Orkun Posted October 4, 2019 Share Posted October 4, 2019 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 Link to comment Share on other sites More sharing options...
dragan Posted October 4, 2019 Share Posted October 4, 2019 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. 1 Link to comment Share on other sites More sharing options...
Orkun Posted October 4, 2019 Author Share Posted October 4, 2019 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 Link to comment Share on other sites More sharing options...
Orkun Posted October 4, 2019 Author Share Posted October 4, 2019 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 2 Link to comment Share on other sites More sharing options...
bernhard Posted October 4, 2019 Share Posted October 4, 2019 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; }); 4 Link to comment Share on other sites More sharing options...
Orkun Posted October 4, 2019 Author Share Posted October 4, 2019 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 Link to comment Share on other sites More sharing options...
Robin S Posted October 4, 2019 Share Posted October 4, 2019 @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 4 Link to comment Share on other sites More sharing options...
Orkun Posted October 7, 2019 Author Share Posted October 7, 2019 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 Link to comment Share on other sites More sharing options...
Noel Boss Posted November 3, 2019 Share Posted November 3, 2019 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); } }); }); 3 Link to comment Share on other sites More sharing options...
bernhard Posted November 3, 2019 Share Posted November 3, 2019 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); }); }); 3 Link to comment Share on other sites More sharing options...
Chris Ernovsky Posted February 23, 2022 Share Posted February 23, 2022 On 11/3/2019 at 1:22 PM, bernhard said: $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); }); }); Dear @bernhard & @Noel Boss, I LOVE this wonderful hook! Works perfectly. Huge thanks! ? 1 Link to comment Share on other sites More sharing options...
bernhard Posted February 23, 2022 Share Posted February 23, 2022 Actually I'd probably do it this way today (refactoring again ? ) <?php $this->addHookAfter('ProcessPageList::find', function (HookEvent $event) { $event->return->each(function($p) use ($event) { if($p->template == 'admin') return; // keep if($p->template == 'basic-page') return; // keep if(!$p->editable() AND !$p->addable()) $event->return->remove($p); }); }); Link to comment Share on other sites More sharing options...
Chris Ernovsky Posted February 23, 2022 Share Posted February 23, 2022 1 hour ago, bernhard said: ...refactoring again ? ?? Just checked it - works properly as well. Any particular reason of resigning from 'find' in $event? My programming skills ends somewhere around 'if' statements and playing with PW selectors... ? Efficiency in queries? Structure of methods? 1 Link to comment Share on other sites More sharing options...
bernhard Posted February 23, 2022 Share Posted February 23, 2022 No - I was just looking at the old version and did not instantly understand it, so I thought I'd change it a little bit. But there are still 1000 other options ? In terms of performance I don't think that there is any noticable difference between both versions. 1 Link to comment Share on other sites More sharing options...
Chris Ernovsky Posted February 23, 2022 Share Posted February 23, 2022 Got it & thanks again! ⬇️⬇️⬇️ 1 Link to comment Share on other sites More sharing options...
TomPich Posted February 10 Share Posted February 10 Hi folks, My research about hiding pages in the page tree led me to this thread. In the ProcessPageList module, there is a "Hide these pages in page list(s)" section where you can "select one or more pages that you do not want to appear in page list(s)." However, when I select a page, it’s still visible in the page tree. Is it normal? In which situation these pages would be hidden? I don’t get that. As always, thanks a lot. Link to comment Share on other sites More sharing options...
Robin S Posted February 10 Share Posted February 10 3 hours ago, TomPich said: However, when I select a page, it’s still visible in the page tree. Is it normal? In which situation these pages would be hidden? I don’t get that. Yeah, I've noticed that too and I think it's a bug. I normally select the "except when superuser" option and it does work then but this shouldn't be necessary. I opened an issue here: https://github.com/processwire/processwire-issues/issues/1879 2 Link to comment Share on other sites More sharing options...
TomPich Posted February 11 Share Posted February 11 11 hours ago, Robin S said: I normally select the "except when superuser" option and it does work then but this shouldn't be necessary. For me, whether I select this option or not, it doesn’t work. Not a big deal though. I just wanted to know if I got this functionality wrong. Link to comment Share on other sites More sharing options...
Robin S Posted February 20 Share Posted February 20 The issue is fixed now in a commit on the dev branch: https://github.com/processwire/processwire/commit/3e90cb74faa46b63ff9b5920d1c4e5b971aac591 1 1 Link to comment Share on other sites More sharing options...
da² Posted February 21 Share Posted February 21 Just to add another solution to the original question, this is how I hide not editable pages in admin tree: $this->addHookAfter('Page::listable', isPageListable(...)); function isPageListable(HookEvent $event): void { if ($event->page->path == "/") $event->return = true; else $event->return = wire()->user->hasPermission("page-edit", $event->page); } 2 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now