Jump to content
Orkun

Hide uneditable Pages in Admin Tree/ProcessPageList

Recommended Posts

Hi Guys

A long time ago (2-3 years ago) I had written the following hook (based on this gist https://gist.github.com/somatonic/5595081):

public function init() {
	$this->addHookAfter('ProcessPageList::execute', $this, 'hidePages');
}

public function hidePages(HookEvent $event){
		if($this->wire('config')->ajax){
			$excludePages = new PageArray();
			$the_pages = $this->wire('pages')->find("template=newsletter-clinic|sender_2016|sender_2016_collab|inside-news-folder|basic-page|event-clinic|domain_root|brandportal-index|key-figures|jobs|news|media-mirror|worker|xmlexport|weekly-menu|intranet_domain_root|weekly-menu-index|daily-menu|daily-menu-index|benefit|benefits-index|pdiatres-de-garde-index|xml-feed-index|courses-index|help-doc|stocks-index|dictionary|iPad-PDF-Index|pkb-pikettdienst-index|heisser-stuhl-index|heisser-stuhl");

			
			foreach ($the_pages as $the_page) {
				if(!$the_page->editable() && !$the_page->addable()) {
				  $excludePages->add($the_page);
				}
			}

			foreach ($this->wire('pages')->find("template=specialties") as $sp) {
				if($sp->numChildren > 0){
					$editableFound = 0;
					foreach ($sp->children() as $spc) {
						if($spc->editable()){
							$editableFound++;
						}else{
							$excludePages->add($spc);
						}
					}
					if($editableFound == 0){
						$excludePages->add($sp);
					}
				}else{
					$excludePages->add($sp);
				}
			}

			$hidden = explode('|', $excludePages);


			$json = json_decode($event->return, true);
			foreach($json['children'] as $key => $child){
				if(in_array($child['id'],$hidden)) unset($json['children'][$key]);
			}
			$json['children'] = array_values($json['children']);
			$event->return = json_encode($json);
		}
}

It hides all the uneditable and unaddable (can't add children to page) pages for non-superusers. But this code slows down the page tree view heavily.
Because of this I wanted to ask if there is better way (possibly a new added hook in PW3?) to hide uneditable/unaddable pages for non-superusers.

KR
Orkun

Share this post


Link to post
Share on other sites

Can't you just add a CSS class to uneditable pages, and use display: none on these?

Or maybe check with JS for absence of li.PageListActionEdit and then add a hiding class in the parent div.

As for unaddable pages, check for absence of li.PageListActionNew.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, dragan said:

Can't you just add a CSS class to uneditable pages, and use display: none on these?

Or maybe check with JS for absence of li.PageListActionEdit and then add a hiding class in the parent div.

As for unaddable pages, check for absence of li.PageListActionNew.

Hi @dragan

Wouldn't be there problems when using a css or js solution, because of the ajax loading and pagination?

I mean there is also still the problem of false child count (also with the current code).

KR
Orkun

Share this post


Link to post
Share on other sites

It looks like a hook to ProcessPageList::find is working.

Here is the code

$this->addHookAfter('ProcessPageList::find', function(HookEvent $event) {
                    // Get the object the event occurred on, if needed
                    $ProcessPageList = $event->object;
                  
                    // An 'after' hook can retrieve and/or modify the return value
                    $return = $event->return;
                  
                    // Get values of arguments sent to hook (if needed)
                    $selectorString = $event->arguments(0);
                    $page = $event->arguments(1);

                    $excludePagesByTemplate = array(
                        "newsletter-clinic",
                        "sender_2016",
                        "sender_2016_collab",
                        "inside-news-folder",
                        "basic-page",
                        "event-clinic",
                        "domain_root",
                        "brandportal-index",
                        "key-figures",
                        "jobs",
                        "news",
                        "media-mirror",
                        "worker",
                        "xmlexport",
                        "weekly-menu",
                        "intranet_domain_root",
                        "weekly-menu-index",
                        "daily-menu",
                        "daily-menu-index",
                        "benefit",
                        "benefits-index",
                        "pdiatres-de-garde-index",
                        "xml-feed-index",
                        "courses-index",
                        "help-doc",
                        "stocks-index",
                        "dictionary",
                        "iPad-PDF-Index",
                        "pkb-pikettdienst-index",
                        "heisser-stuhl-index",
                        "heisser-stuhl"
                    );
    
                    if($return instanceof PageArray){
                        foreach($return as $p){
                            if(in_array($p->template->name, $excludePagesByTemplate)){
                                if(!$p->editable() && !$p->addable()) {
                                    $return->remove($p);
                                }
                            }
                            
                        }
                    }
                  
                    $event->return = $return;
});

KR

Orkun

 

  • Like 2

Share this post


Link to post
Share on other sites

Just played around with your example and did a little refactoring 🙂 Thx for sharing your code!

$this->addHookAfter('ProcessPageList::find', function(HookEvent $event) {
  $pages = $event->return;
  $excludePagesByTemplate = array('admin', 'basic-page');
  
  $pages->each(function($p) use($pages, $excludePagesByTemplate) {
    if(!in_array($p->template, $excludePagesByTemplate)) return;
    if(!$p->editable() && !$p->addable()) $pages->remove($p);
  });
  
  $event->return = $pages;
});
  • Like 4

Share this post


Link to post
Share on other sites
12 minutes ago, bernhard said:

Just played around with your example and did a little refactoring 🙂 Thx for sharing your code!


$this->addHookAfter('ProcessPageList::find', function(HookEvent $event) {
  $pages = $event->return;
  $excludePagesByTemplate = array('admin', 'basic-page');
  
  $pages->each(function($p) use($pages, $excludePagesByTemplate) {
    if(!in_array($p->template, $excludePagesByTemplate)) return;
    if(!$p->editable() && !$p->addable()) $pages->remove($p);
  });
  
  $event->return = $pages;
});

Hi Bernhard, Thank you for refactoring :)

KR
Orkun

Share this post


Link to post
Share on other sites

@Orkun, do you know about $page->listable()? It determines whether a page may appear in Page List (and also PageListSelect inputfields, which is probably something you want too).

So you could do:

$wire->addHookAfter('Page::listable', function(HookEvent $event) {
	/* @var Page $page */
	$page = $event->object;
	if(!$page->editable() && !$page->addable()) $event->return = false;
});

 

10 hours ago, Orkun said:

I mean there is also still the problem of false child count

You can hook ProcessPageListRender::getNumChildren to customise the child count. But in some cases depending on what is determining a page's listable status you might decide it's not worth the trouble.

Here's one way you might do it (in /site/ready.php):

// Work out which templates are editable or addable for the user and store them on the $user object
// Doing this here to avoid doing it repeatedly in the getNumChildren hook
if(!$user->isSuperuser() && $page->template == 'admin') {
	$user_roles_ids = $user->roles->explode('id');
	$allowed_templates = [];
	foreach($templates as $template) {
		$edit_add_roles = array_merge($template->editRoles, $template->addRoles);
		if(count(array_intersect($user_roles_ids, $edit_add_roles))) $allowed_templates[] = $template->name;
	}
	$user->allowed_templates = $allowed_templates;
}

// Set a selector to correct the children count
$wire->addHookBefore('ProcessPageListRender::getNumChildren', function(HookEvent $event) {
	$user = $event->wire('user');
	if($user->isSuperuser()) return;
	$selector = 'template=' . implode('|', $user->allowed_templates);
	$event->arguments(1, $selector);
});

Be aware that there are some other areas you probably want to consider if you are hiding pages in Page List. Here are some pointers to get you started:

  • Pages that can be found in the admin search - look at manipulating the selector by hooking ProcessPageSearch::findReady
  • Pages that can be found in Lister - look at manipulating the selector by hooking ProcessPageLister::getSelector
  • Like 3

Share this post


Link to post
Share on other sites
On 10/5/2019 at 12:28 AM, Robin S said:

@Orkun, do you know about $page->listable()? It determines whether a page may appear in Page List (and also PageListSelect inputfields, which is probably something you want too).

So you could do:


$wire->addHookAfter('Page::listable', function(HookEvent $event) {
	/* @var Page $page */
	$page = $event->object;
	if(!$page->editable() && !$page->addable()) $event->return = false;
});

 

You can hook ProcessPageListRender::getNumChildren to customise the child count. But in some cases depending on what is determining a page's listable status you might decide it's not worth the trouble.

Here's one way you might do it (in /site/ready.php):


// Work out which templates are editable or addable for the user and store them on the $user object
// Doing this here to avoid doing it repeatedly in the getNumChildren hook
if(!$user->isSuperuser() && $page->template == 'admin') {
	$user_roles_ids = $user->roles->explode('id');
	$allowed_templates = [];
	foreach($templates as $template) {
		$edit_add_roles = array_merge($template->editRoles, $template->addRoles);
		if(count(array_intersect($user_roles_ids, $edit_add_roles))) $allowed_templates[] = $template->name;
	}
	$user->allowed_templates = $allowed_templates;
}

// Set a selector to correct the children count
$wire->addHookBefore('ProcessPageListRender::getNumChildren', function(HookEvent $event) {
	$user = $event->wire('user');
	if($user->isSuperuser()) return;
	$selector = 'template=' . implode('|', $user->allowed_templates);
	$event->arguments(1, $selector);
});

Be aware that there are some other areas you probably want to consider if you are hiding pages in Page List. Here are some pointers to get you started:

  • Pages that can be found in the admin search - look at manipulating the selector by hooking ProcessPageSearch::findReady
  • Pages that can be found in Lister - look at manipulating the selector by hooking ProcessPageLister::getSelector

Hi @Robin S

Thank you for the tip! 
I didn't know about the listable method. I am using now the listable hook to hide pages in the admin tree (and also in PageListSelect Inputfields).

About the false child count problem. You are probably right, that this is not worth the trouble so I will leave it as it is, since I didn't heard any complains from the users concerning false child count.

What is the benefit from hooking Page::listable() instead of ProcessPageList::find()?

KR
Orkun

Share this post


Link to post
Share on other sites
On 10/4/2019 at 3:02 PM, bernhard said:

Just played around with your example and did a little refactoring 🙂 Thx for sharing your code!


$this->addHookAfter('ProcessPageList::find', function(HookEvent $event) {
  $pages = $event->return;
  $excludePagesByTemplate = array('admin', 'basic-page');
  
  $pages->each(function($p) use($pages, $excludePagesByTemplate) {
    if(!in_array($p->template, $excludePagesByTemplate)) return;
    if(!$p->editable() && !$p->addable()) $pages->remove($p);
  });
  
  $event->return = $pages;
});

I refactored your refactored code 😉 One could also remove the pages that should be excluded beforehand…

$this->addHookAfter('ProcessPageList::find', function (HookEvent $event) {
	$excludePagesByTemplate = ['admin', 'basic-page'];

	$event->return->find(["template!=" => $excludePagesByTemplate])->each(function ($p) use ($event) {
		if (!$p->editable() && !$p->addable()) {
			$event->return->remove($p);
		}
	});
});

 

  • Like 2

Share this post


Link to post
Share on other sites
58 minutes ago, Noel Boss said:

I refactored your refactored code 😉

🤪😅 At first I thought my version was better readable. But I quite like this one:

$this->addHookAfter('ProcessPageList::find', function (HookEvent $event) {
	$event->return->find([
		"template!=" => ['admin', 'basic-page'], // keep them
	])->each(function($p) use ($event) {
		if(!$p->editable() AND !$p->addable()) $event->return->remove($p);
	});
});
  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...