Jump to content

Hooking ProcessPageAdd::getAllowedTemplates -- Odd behavior


Recommended Posts

Backstory: I'm working on expanding access to permissions via a specific role, by using a FieldTypeTags on both user accounts and pages alike, thereby giving me (and others) the ability to control access per-page. The logic therein hasn't been fully fleshed out, but I keep getting stuck; for instance: on the Page List (admin home) screen, the "Add" button on hover shows up, but clicking on it threw an error that I didn't have permission (it was a parent-child relationship that the child was only allowed on that parent, and there's only ever allowed to be one parent). The role doesn't have page-create (or page-add) permission because the only template that is currently access-controlled is the home template...so theoretically, all templates can be created (I'd think?). I was having trouble where template permissions were seemingly overriding my hooks that were supposed to deny access, so I removed all but "home" role permissions. Anyhow... After finding Robin's TemplatesChildPages module it let me find a hook that I could use to forcibly override template permissions (saving/publishing does work).

Unfortunately, I've only been able to get the "Add New" form to render when I've created the array in the ProcessPageAdd::getAllowedTemplates (after) hook when I create the return value's (template) array manually. (In this case it's a "job-list" parent template [indexes employment offerings], and 'job-listing' child template that describes the employment position.)

So the following code allows the form to render and work as expected:

$this->addHookAfter('ProcessPageAdd::getAllowedTemplates', $this, 'allowTemplates');

// ...

public function allowTemplates($event) {
	$p = $this->wire->templates->get('job-list');
	$t = $this->wire->templates->get('job-listing');
	$event->return = [
		$p->id => $p,
		$t->id => $t
	];
	ksort($event->return);
}

Obviously that will only work for my explicit example above while I test adding that particular template. So I went back and adjusted the hook to make it dynamic. Unfortunately, the following code throws an exception.

public function allowTemplates($event) {
	$parent_t = $this->wire->templates->get($event->arguments('parent')->template);
	$child_t  = $parent_t->childTemplates;
	$allowed_templates = [];
	$allowed_templates[$parent_t->id] = $parent_t;
	foreach ($child_t as $t) {
		$t = $this->wire->templates->get($t);
		$allowed_templates[$t->id] = $t;
	}
	ksort($allowed_templates);
	$event->return = $allowed_templates;
}

I've taken the $event_return value and processed it through a good old fashioned echo '<pre>'; var_dump($event->return); die('</pre>'); then taken the output (copy/paste) and saved it to a file, for each version above. I used WinMerge (to quickly compare) and the files are identical. The exception that is thrown, for the dynamic version, reads:

ProcessWire: ProcessPageAdd: Template &quot;job-listing&quot; is not allowed here.

But, as far as I can tell, the parent and child values exist in $event->return after my hook is run in both examples...yet only one seems to be working. Does anyone have any possible thoughts on this??

Link to comment
Share on other sites

It seems as though ProcessPageAdd::getAllowedTemplates is called so many times that it doesn't actually always have a $parent value on each call (because of how my authentication is configured). I had to test for null on that value, and exit gracefully if null, and if not then I could enter into my logic. The manual assignment of values worked because it forced a value each time regardless of what was passed into the method as a $parent value for the $event object.

EDIT: This now works for a superuser account, but not for a standard account. It seems there's something else in permissions that I'm missing.

Updated hook method, without any added business logic of my own, is now:

public function allowTemplates($event) {
	// Not all calls pass in an argument value
	if (is_null($event->arguments('parent'))) return;

	// Identify the intended parent template and fetch its allowed (template) children
	$parent_t = $this->wire->templates->get($event->arguments('parent')->template);
	$child_t  = $parent_t->childTemplates;

	// Assign the allowed children to an array we can set to return
	$allowed_templates = [];
	$allowed_templates[$parent_t->id] = $parent_t;
	foreach ($child_t as $t) {
		$t = $this->wire->templates->get($t);
		$allowed_templates[$t->id] = $t;
	}
	// I like sorted values ^_^
	ksort($allowed_templates);

	// Return the expected template array back to the calling process
	$event->return = $allowed_templates;
}

If I give "Edit Pages", and "Create Page" permission to the role for the child template, and "Add Children" permission to the parent template for the role, then I can create a Job Listing (child) under the Job List (parent). However, once I do that, everyone with the role can successfully do this, despite me trying to send FALSE (or some similar value) back through any available hooked method. I can't seem to override a strict parent/child relationship.

Link to comment
Share on other sites

  • BrendonKoz changed the title to Hooking ProcessPageAdd::getAllowedTemplates -- Odd behavior

Similar to my suggestion to your earlier question, I think you should give your roles the necessary permissions to edit/create/add children for all the templates they will be working with. If you don't do this you're going to be fighting against what PW thinks that users with those roles are allowed to do and it will be more difficult to achieve what you want. Once those roles have the generic permissions to do what they need to do then focus on selectively taking away those permissions in your custom hooks according to your test on $user, $page, $parent, etc.

Also, I've never understood the advantage to not explicitly setting access to every template. "Inheriting" access just seems like a recipe for confusion and accidental oversight. It only takes a short while to set the template access you want roles to have, and even less time if you use the Template Access module.

Below is some code with demo logic for determining the allowed templates. You would replace that with your own logic which I expect would use some combination of $parent and $user.

$wire->addHookAfter('ProcessPageAdd::getAllowedTemplates', function(HookEvent $event) {
	$allowed = $event->return;
	$parent_id = (int) $event->wire()->input->get('parent_id');
	if(!$parent_id) return;
	$parent = $event->wire()->pages->get($parent_id);

	// Your custom logic here to remove any disallowed templates
	if($parent->template == 'basic_page' && $event->wire()->user->name === 'test-editor') {
		$allowed_template_ids = [29, 78];
		foreach($allowed as $template_id => $template) {
			if(!in_array($template_id, $allowed_template_ids)) {
				unset($allowed[$template_id]);
			}
		}
		$event->return = $allowed;
	}
});

 

  • Like 2
Link to comment
Share on other sites

  • 1 month later...

I somehow managed to miss this reply. Thank you for both of your replies, @Robin S! I did manage to get this working, with some oddities here and there that had to be worked through due to how I was checking/setting access permissions. I chose not to explicitly set permission for each and every template per role since I'm using a field that's added to the templates to determine access, so it seemed doubly difficult to do that, and my hope was that inheritance would work properly most of the time. So far that's fairly true, with occasional exceptions (ex: nested repeaters since repeaters have their own system templates). It felt a little hack'ish but I checked against the template name and if strpos() found a match to "repeater_" against $page->template->name, then I'd allow access to the edit method (add didn't cause an issue the way I had it written). I wasn't sure how else to check if a template of a page being checked for access belonged to a repeatable fieldtype.

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