Create Protected Pages the Easy Way

Learn how to protect members-only pages in MODX quickly and easily with a simple custom Snippet.

By Bob Ray  |  November 30, 2021  |  2 min read
Create Protected Pages the Easy Way

Suppose you want to make a section of your site private so that only logged-in users can see the Resources. As with most things in MODX, there are several ways to protect Resources.

Contexts

One method is to put all the private Resources in a separate context and protect it by creating a Context Access ACL entry that links the private Context to one or more User Groups. Once you do that, the Resources in the Context will be hidden from everyone who is not a member of the specified group or groups. I try to keep from using Contexts if I can, because they are difficult to set up correctly and they introduce complexities that I’d rather avoid. Links to Resources in other Contexts, for example, can be tricky to implement and users often get sent to the error (page-not-found) page rather than the unauthorized page when trying to access a forbidden Resource.

Resource Groups

This is probably the most commonly used method. It involves putting the private Resources in a Resource Group and protecting them with a Resource Group Access ACL entry linking the Resource Group to the User Group(s) of the members who should have access to them. The down side of this method is that you have to remember to put all new private Resources in the Resource Group, and by default, users still get sent to the error page when trying to access private Resources.

The Easy Way—A Custom Snippet

This method is my favorite for simple situations where you want to protect a section of your site, either from users who are not logged in, or from users who don’t belong to one or more User Groups. It’s quick and easy, and it allows you to send the user wherever you like when they try to access a forbidden page.

The heart of this method is to put a Snippet tag for a very simple Snippet in the Template used by the private pages. The Snippet checks the user’s status, and redirects them as necessary. Let’s call the Snippet “PrivatePage.”

This is the Snippet tag for the Template:

[[!PrivatePage]]

This is the code of the PrivatePage Snippet:

<?php
/* PrivatePage snippet */
/* Redirect anyone who is not logged in */

$key = $modx->context->get('key');
if (! $modx->user->hasSessionContext($key)) {
   $modx->sendUnauthorizedPage();
}
return '';

For users who are logged in to the current Context (usually 'web'), the Snippet does nothing at all. Users who are not logged in, however, get sent to the unauthorized page. You can set the ID of that page in the unauthorized_page System Setting. Just go to System (gear icon) -> System Settings on the Main Menu and put “unauthorized” in the search box at the upper right and press the Enter key. Find the unauthorized_page setting and double-click in the value field. Change the ID to the page you want to send user’s to. By default, it’s the home page, but it can be any page on your site. It could be the Login page, for example.

Suppose you want to restrict the private pages to members of a particular User Group. This version of the Snippet will do that. Just change SomeUserGroup to the name of an actual User Group.

<?php
/* PrivatePage snippet */
/* Redirect anyone who is not a
 * member of a particular user group */

if (! $modx->user->isMember('SomeUserGroup')) {
$modx->sendUnauthorizedPage();
}
return '';

What if the private pages should be available to users in more than one User Group? Just change the test line to this:

if (! $modx->user->isMember( array( 'SomeUserGroup', 'SomeOtherUserGroup'))) {

You can list as many User Groups here as you like, just make sure that each group name is surrounded by single quotes and note that there is an extra set of parentheses in this version.

Send Them Somewhere Else

You might already be using the unauthorized page for something else or maybe you just feel like sending the users to some other page (e.g., the Login page). That’s easy enough to do. Just change the sendUnauthorized() line to this (Change 12 to the ID of the page you want to send them to):

$url = $modx->makeUrl(12, "", "", "full");
$modx->sendRedirect($url);

Putting It All Together

Our Snippet isn’t very flexible. The actions it takes, the group names, and the page to redirect to are all hard-coded into the Snippet. It would be nice if this were configurable in the Snippet tag so you could use it in different ways on different sites or different pages (the tag can go in the page content rather than the Template). Here’s a more generic version:

<?php
/* PrivatePage snippet */
/* Redirect users who shouldn't see this page */

/** @param $redirectTo string -- (optional) ID of page to redirect to;
 *      default: unauthorized page
 *  @param $userGroups string -- (optional) Comma-separated list of
 *      User Groups that are authorized;
 *      if not set, users who are not logged in are redirected
 * */

$output = '';

function forward($id, &$modx) {

    if (empty($id)) {
        $modx->sendUnauthorizedPage();
    }
    $url = $modx->makeUrl((integer) $id, "", "", "full");
    if (empty($url)) {
        die('MakeUrl failed. ID: ' . $id);
    }
    $modx->sendRedirect($url);
}

$redirectTo = $modx->getOption( 'redirectTo', $scriptProperties, '');
$userGroups = $modx->getOption( 'userGroups', $scriptProperties, '');

if ((!empty($userGroups)) && strpos( $userGroups, ',') !== false) {
    $userGroups = explode( ',', $userGroups);
}

if (empty($userGroups)) {
    /* Redirect anyone who is not logged in */
    $key = $modx->context->get('key');
    if (!$modx->user->hasSessionContext($key)) {
        forward($redirectTo, $modx);
    }
} else {
    /* redirect if not a member of specified group(s) */
    if (!$modx->user->isMember($userGroups)) {
        forward($redirectTo, $modx);
    }
}

return $output;

This version has two properties for the Snippet tag. Both are optional. If no properties are sent, users who are not logged in will be sent to the unauthorized page. The &userGroups property can contain a single User Group name or a comma-separated list of User Groups. Users who are not members will be forwarded. If the &redirectTo property is not set, they’ll go to the unauthorized page. If it’s set, they’ll go to the ID set in that property.

Examples

All versions of the tag will redirect users who are not logged in.

/* Redirect anyone who is not logged in to the unauthorized page */
[[!PrivatePage]]

/* Redirect anyone who is not logged in to Resource 12 */
[[!PrivatePage? &redirectTo=`12`]]

/* Redirect anyone who is not a member of the Subscribers user group
   to the unauthorized page */
[[!PrivatePage? &userGroups=`Subscribers`]]

/* Redirect anyone who is not a member of the Subscribers or Administrator
   user group to Resource 12 */
[[!PrivatePage? &userGroups=`Subscribers,Administrator` &redirectTo=`12`]]

Important: be sure the unauthorized page (or any other page you forward to) is published, and make sure the PrivatePage Snippet will not execute on that page!


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.