Jump to content

How to add drop down menu in the backend with icons


DrQuincy
 Share

Recommended Posts

I thought this would be really easy but can't figure it out.

In the backend at the top you have Setup, Modules, etc that have drop downs with icons. You can see that these correspond to pages that use the admin template when looking under Admin in the site tree.

What I want to do is add an entirely new menu with drop downs and icons that look exactly the same as this. I have a redirect template that I will use to send them to specific pages within the CMS — or even the front end.

  1. Can someone point me in the right direction for how to do this (if it is possible)? Including, if possible, setting menu icons. When I add the pages in the site tree the child pages don't show as a drop down.
  2. When you link to a CMS page is it possible to use a GET var in the URL to have the Children tab open instead of Content? In my drop down I want to link to sections that show all child pages.

I hope that makes sense.

Many thanks.

Link to comment
Share on other sites

5 hours ago, DrQuincy said:

Can someone point me in the right direction for how to do this (if it is possible)? Including, if possible, setting menu icons. When I add the pages in the site tree the child pages don't show as a drop down.

Pages will only appear in the dropdown menu if those pages have a Process assigned. See here. In effect this usually means "pages that use the admin template".

You could possibly create a custom Process module that you use to redirect to other pages in the module's execute() method. Then create new pages using the admin template and select your Process. See here for an easy way to set the menu icon of admin pages.

But also consider using the "nav" item in the getModuleInfo() method of a custom Process module. This lets you define some dropdown menu items for the Process when the page is in the top menu. ProcessModule is an example.

Unfortunately you cannot set the first level of dropdown items dynamically. https://github.com/processwire/processwire-requests/issues/189

The top menu is cached (unnecessary in my opinion) so if you are making changes and not seeing them reflected in the menu then do a Modules > Refresh (and Tracy has a handy shortcut).

 

6 hours ago, DrQuincy said:

When you link to a CMS page is it possible to use a GET var in the URL to have the Children tab open instead of Content? In my drop down I want to link to sections that show all child pages.

Add "#ProcessPageEditChildren" to the URL.

/your-admin-page/page/edit/?id=1234#ProcessPageEditChildren

 

  • Like 3
Link to comment
Share on other sites

Thanks for the detailed response! 🙂

I did think it was going to require an admin Process when looking at existing pages. From the looks of it then I could create my own Process class and add the nav there. My knowledge of PW isn't wide enough to know much about how this works but it looks like I should be able to figure it out based off the example you linked to.

In this case it is fine to have it hard-coded in getModuleInfo(). It is just for some specific site options such as Settings, Config, Emails that are in the site tree. I just thought it'd be nice to have it in the top nav so it appears separate from everything else.

Thanks also for the #ProcessPageEditChildren tip.

Just one more question please: is it any easier to to add items to existing menus? For example, could I add some extra options to the Setup menu or does this require custom Process classes as well?

Link to comment
Share on other sites

I did a very quick text and it seems to work! 🙂

I just created a new Process class based off one that was already in there and placed it in site/modules and added the nav, stripped back what I thought I didn't need and installed the module and it pretty much worked first time. It will take a bit of tweaking but the bit I was struggling with now works.

Thanks again!

  • Like 1
Link to comment
Share on other sites

3 hours ago, DrQuincy said:

it pretty much worked first time.

Great! I did the same and it refused to work. No errors, nothing. Nothing in the nav is getting picked up and nothing in the markup. How did you go about this? Thanks.

Link to comment
Share on other sites

On 9/3/2021 at 12:30 PM, kongondo said:

Great! I did the same and it refused to work. No errors, nothing. Nothing in the nav is getting picked up and nothing in the markup. How did you go about this? Thanks.

Bear in mind each time you make a change to your .module file you need to refresh modules.

Here's a working sample that should get you started.

namespace ProcessWire;

class ProcessSiteOptions extends Process {

private $cmsPath = null;

public static function getModuleInfo() {
	
	return [

		'title'			=> __('Site options', __FILE__),        
		'summary'		=> __('Custom site options', __FILE__),
		'version' 		=> '0.0.1', 
		'permanent'		=> false, 
		'useNavJSON' 	=> true,
		'permission'	=> 'page-edit',
		'nav' 			=> [

			[
				
				// URls relative to where this admin page is added
				'url'		=> '../page/edit/?id=1023',
				'label' 	=> 'Settings', // These labels are HTML entity encoded
				'icon' 		=> 'cog'
				
			],
			[
				
				'url' 		=> '../page/edit/?id=6601#ProcessPageEditChildren',
				'label' 	=> 'Project filters', 
				'icon' 		=> 'filter',
				'navJSON' 	=> 'navJSON'
				
			]
		
		]

	];
	
}


public function ___execute() {
	
	return $this->render();
	
}
	

protected function render() {
	
	$html = '<p>Manage site options.</p><ul>';
	
	$sanitizer = wire('sanitizer');
	
	$nav = self::getModuleInfo()['nav'];
	
	foreach ($nav as $item) {
		
		$html .= '<li><a href="' . $sanitizer->entities($item['url']) . '">' . $sanitizer->entities($item['label']) . '</a></li>';
		
	}
	
	return $html . '</ul>';
	
}


public function ___executeNavJSON($options = []) {
	
	$options = [];
	
	$options['list'] = [];
	
	// Unlike nav children above these can be dynamic
	$options['list'] = self::getChildArray();
	
	return json_encode($options);
	
}


private function getCMSPath() {
	
	// Caches the CMS path
	return $this->cmsPath === null ? wire('pages')->get(2)->path : $this->cmsPath;
	
}


private function getChildArray() {
	
	$page = wire('pages')->get('/');
	
	$sanitizer = wire('sanitizer');
	
	return [
		
		['url' => $this->getCMSPath() . 'foo-bar', 'label' => 'Arbitrary CMS link', 'icon' => 'smile-o'],
		['url' => '/about/', 'label' => 'Arbitrary site link', 'icon' => 'user-o'],
		['url' => $page->path, 'label' => $sanitizer->entities($page->title), 'icon' => 'home'] // Dynamic; these labels are not HTML entity encoded
		
	];
	
}

A few points:

  • Don't put any comments before the namespace declaration as PW can't pick up on the module info properly
  • As @Robin S pointed out the first level of items are static, the rest (navJSON) can be dynamic

Questions (just me being picky/trying to understand):

  • I don't know how you add third-level items, i.e. children on navJSON but I probably don't need these anyway
  • The labels on navJSON aren't HTML encoded but the labels on 'nav' are — why is this?  Is it so you can add HTML into the dynamic options? (strong, em) — just wondering why they are different
  • How can you make a link in 'nav' not do anything when clicked — i.e. just act as a parent for the child links (not a big deal, just curious)
  • How can you add a link (in either 'nav' or navJSON) that goes to an external site? I.e. to a different HTTP host
  • Like 1
Link to comment
Share on other sites

1 hour ago, DrQuincy said:

each time you make a change to your .module file you need to refresh modules.

I did, only like a a hundred times 😁. Maybe I was refreshing the wrong site! I'll give this a go.

1 hour ago, DrQuincy said:

Here's a working sample that should get you started.

Thanks for sharing! And the pointers as well.

  • Like 1
Link to comment
Share on other sites

5 hours ago, kongondo said:

I did, only like a a hundred times 😁.

I've found something similar in a few cases. Doing a Modules > Refresh is supposed to both clear the module info cache and the admin menu cache (AdminThemeUikit hooks Modules::refresh) but sometimes it doesn't work and you have to either logout/login or (easier) use the Tracy Debugger "Clear Session & Cookies" link in the "ProcessWire Info" panel. The whole cached admin menu thing seems a bit pointless to me, so if you want to effectively disable that you can put the following in /site/ready.php

// Prevent admin menu from being cached
$wire->addHookBefore('ProcessController::execute', function(HookEvent $event) {
	$event->wire()->session->removeFor('AdminThemeUikit', 'prnav');
});

But you'll still need to do a Modules > Refresh if you make changes to the "nav" item in a Process module's getModuleInfo method.

 

7 hours ago, DrQuincy said:

I don't know how you add third-level items, i.e. children on navJSON but I probably don't need these anyway

Have a look at ProcessModule and I think you'll be able to work it out. Within getModuleInfo see "useNavJSON" and the "navJSON" items within the "nav" array, and the executeNavJSON method.

7 hours ago, DrQuincy said:

How can you make a link in 'nav' not do anything when clicked — i.e. just act as a parent for the child links (not a big deal, just curious)

You probably can't. Probably best to make a corresponding executeSomething() method in your Process module that simply lists the child links.

7 hours ago, DrQuincy said:

How can you add a link (in either 'nav' or navJSON) that goes to an external site? I.e. to a different HTTP host

Yeah, unfortunately you can't because AdminThemeFramework forces the Process page URL before the link for items in the "nav" array. It seems this array is really only intended to be used for executeSomething() methods within the Process module and not for general purpose links.

 

Recently I've been playing around with an alternative approach to custom admin menus which involves hooking AdminThemeFramework::getPrimaryNavArray (only possible in PW 3.0.178 or newer). If you use Tracy to dump the event return you can work out how the menus can be manipulated. And I've put together a module that lets you add custom admin menus without writing code - will release soon.

  • 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...