Jump to content

Extending template role access with some custom code


Recommended Posts

Hi everyone. I was wondering if the intelligent people on the forum could help me check over a piece of code related to access options. It's based on the template access options, but I need more access options than view/edit etc so I decided to create this so please don't laugh too hard. First of all, this will all be related to the front-end. I plan on restricting the PW admin area to the superuser only so normal users will be unable to change these settings.

Basically I have Page Reference fields attached to certain templates. The Page Reference fields are defined as checkboxes and are linked to user roles. As an example, one of the fields could be called content_pinned. I could then create a new page using the template with that field attached and then my method would determine if a member is allowed to pin a topic for example via the front-end.

Other Page Reference fields can be added to the templates at any time, for example content_delete, content_comment. My method would then check these permissions for the desired output.

Examples of calling the method....

$deleteCheck = permissionCheck($page, "delete");
if($deleteCheck === false) {
    // The user can't delete this content, so maybe we could display a disabled delete button
} else {
    // The user has a role that is allowed to delete this content so we can now add a delete button and some code to
    // allow them to delete it....
}

OR

$commentCheck = permissionCheck($page, "comment");
if($commentCheck === false) {
    // The user can't comment on this content, so display a message telling them how useless they are ;)
} else {
    // The user has a role that is allowed to comment on this content so we can now add a shiny new editor for them
    // to use
}

Below is the method I came up with. It seems to work well, but I'm just a tad worried I have left in a loophole or something stupid.

Also, it's worth noting that this method is designed to check parent pages etc for permissions as well. This will allow me to create categories. If the parent category doesn't allow the user to do something but the current page does, it should take this into account and still disallow the action. This allows me to disable certain permissions at a category level.

function permissionCheck($content, $type) {

    $user = wire('user');

    switch ($type) {
    case "view":
      $fieldName="content_view";
      break;
    case "comment":
      $fieldName="content_comment";
      break;
    case "post":
      $fieldName="content_post";
      break;
    case "delete":
      $fieldName="content_delete";
      break;
    case "pinned":
      $fieldName="content_pinned";
      break;
    }

    // Check if the $content page has the $fieldName field. If it does, check that the user's role
    // is selected - if not, return false
    if($content->hasField($fieldName)) {
        if(!$content->$fieldName->has('id=' . $user->roles)) {
            return false;
        }
    }   

    // Get all the parents for the $content page
    $contentParents = $content->parents();

    foreach($contentParents as $contentParent) {
        // Find out how many parent pages have the $fieldName field
        if($contentParent->hasField($fieldName)) {
            // If the parent page has the field, check it has the user's role selected
            $rolesField = $contentParent->$fieldName;
            if(!$rolesField->has("id=" . $user->roles)) {
                return false;
            }
        }
    }

    return true;
}

Thanks for your time.

Link to comment
Share on other sites

21 hours ago, The Frayed Ends of Sanity said:

Below is the method I came up with. It seems to work well, but I'm just a tad worried I have left in a loophole or something stupid.

If it's working for your purposes then I think it's fine to do it the way you are.

My suggestion is that you have the function return false by default rather than true, and reverse the logic in the conditionals. In general, when permissions are involved I'd say it's best practise that the default state be that the user can do nothing, and then you actively add permissions as needed.

This post did get me thinking about if you could use the core permissions system for your needs. I found that when creating a new custom permission, if you prefix the permission name with "page-" then it gets some special features: namely that you can activate that permission for a role individually for each template.

2018-03-18_092204.thumb.png.976dfc003f1bc91aac1cff1cc712ed91.png

So that sounds promising for your needs, but I found that the downside is that the user must have the "page-edit" permission in order for the individual template activation to work. So that probably rules the option out because it's likely that you would want these permissions to be usable on roles that aren't allowed to edit pages.

2018-03-18_092304.png.51ce6b6caed6b17bbb1159224c8aea54.png

Maybe someone can shed some light on how "page-" prefixed permissions work. Are they mentioned anywhere in the docs or a blog post? Is there any other way to assign permissions on a per-template basis besides making them "page-" permissions? And do folks think it would be worth opening a request for per-template permissions that don't require the page-edit permission?

  • Like 2
Link to comment
Share on other sites

Thanks a lot for the feedback @Robin S

Of course you are totally right about returning false by default, that was bad practice on my part. Providing I haven't missed something silly then I guess my code is good? I'm not sure about how efficient it is, but I doubt it would cause too many issues?

The other suggestion you made seems good,  but for this project I need to limit all permission in the PW admin.

Thanks again ^-^

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Just an update to my code. After looking through the forum for a few hours I changed it to the following which I hope is more efficient.

function permissionCheck($content, $type) {

  $user = wire('user');
  $fields = wire('fields');

  switch ($type) {
    case "view":
      $fieldName="content_view";
      break;
    case "comment":
      $fieldName="content_comment";
      break;
    case "post":
      $fieldName="content_post";
      break;
    case "delete":
      $fieldName="content_delete";
      break;
    case "pinned":
      $fieldName="content_pinned";
      break;
  }

  // Find only the parent pages that have the $fieldName attached to their template 
  $templateWithField = $fields->get($fieldName)->getFieldgroups()->implode('|', 'name');
  $pagesWithField = $content->parents("template=$templateWithField");

  // Check the current $content page also, if it has the $fieldName, append it to the $pagesWithField array.
  if($content->hasField($fieldName))
    $pagesWithField = $pagesWithField->append($content);

  // If the $pagesWithField array contains pages, check for the $user roles within the $fieldName
  if($pagesWithField->count > 0) {
    $i = 0;
    foreach ($pagesWithField as $item) {
      if($item->hasField($fieldName)) {
        if($item->$fieldName->has('id=' . $user->roles)) {
          $i++;
        }
      }
    }
    // Only return true if the $user has a role for all the pages within the $pagesWithField array
    if($pagesWithField->count == $i)
      return true;
  }
  return false;
}

 

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