Building a Textpattern-powered public login form
Often, your client’s website will need more than just public-facing content. Having a simple login system, where content is locked behind authentication, is useful for boards of directors needing meeting minutes, customers needing proprietary support information, or a hundred other uses. Textpattern, through its own strong user security and a combination of plugins, can support exactly that.
This article walks through planning, building and testing a login area. In our case, it will be a repository of information (meeting minutes, marketing material, etc) for a board of directors at a non-profit. We’ll cover the following:
- Setting up an area that will require authentication.
- Extending the user table to support more information beyond name, email and password.
- Modifying permissions to ensure people that have access to the content do not have access to Textpattern itself.
- Building a public-facing login that uses Textpattern’s authentication model.
- Enabling the ability for members to independently change or reset their own password.
- Customizing the invitation email.
Requirements for this tutorial:
- A site running Textpattern 4.4.1 (preferably not a production environment)
- The plugins smd_user_manager and smd_bio
- The plugins cbe_frontauth and cbe_members
- Some content you want to lock up
1. Prepare content for password-protection
The first thing to do is figure out what content is going to be locked up behind the username/password login fields. In our case, we’re building an area for a non-profit’s board of directors to access information that does not need to be exposed to the public, such as meeting minutes, event invitee lists, marketing material like logos, and more. This content is not customized per user. That is, we have one general repository, and everyone sees the same thing.
The first thing we need to do is set up a new page template. We’ll call it “authenticate”. Right now, the code is simple, with just the page’s header, footer and content stuff. No login code yet.
<txp:output_form form="meta" />
<txp:output_form form="header" />
<div class="main" role="main">
<txp:if_individual_article>
<txp:article />
<txp:else />
<txp:article status="sticky" />
</txp:if_individual_article>
</div>
<txp:output_form form="footer" />
Next, set up a new section that will house the locked-up content. In our case, it will be titled “Board”. Assign the section to the “authenticate” page template, and select “no” for all options. (We do not want any of this content to be exposed in a public-facing search.)
Finally, assign some content to this section. In our case, we’ll have three pages: meeting minutes, event invitee lists, and marketing materials.
2. Set up user table and permissions
Install the smd_user_manager and smd_bio plugins. These plugins give us two new pieces of power regarding user management. First, smd_bio provides the ability to extend user information beyond the default name, email, password set. Second, with smd_user_manager, we can easily modify and add new permission groups which provides a more semantic level of control.
Add fields for additional user data
Navigate to Extensions > Bio config. Here you can add the fields of additional user data you want to capture and possibly later use in a meaningful way. For this example, we’re going to add two new fields: the person’s role on the board (a dropdown with either “Member” or “Officer” as the options) and the person’s title (an open text field since this is different for every person). We’ll use this information later in the tutorial.
Adjust role labels and group privileges
Once the plugin is installed, navigate to Admin > User manager > Roles. You will see Textpattern’s default user roles. These are tailored for a traditional editorial workflow, but we need to rename them to something that makes a bit more sense. For our website, we need three specific permission levels:
- Publisher.
- Contributor.
- Board Member.
Then we go to Admin > User manager > Privs to actually modify permissions. We will leave Publisher alone, since that is the site’s super admin. For Contributor, we’ll tweak the permission roles of Copy Editor or Staff Writer to make sure they have access to content and not much else.
For Board Member, however, we want to strip away all Textpattern permissions. We want them to be able to log into the public pages, but not the backend content management area. Inside the Privs area, deselect everything. If they do happen to find /textpattern/ or any of its subdirectories, or even type in a command via URL query string, they will be rejected. All that matters is that they can use their credentials to log into the public-facing page.
3. Build login form
Install and activate the cbe_frontauth plugin. This plugin provides a rich set of capability for client-side user management, including login/logout functionality, redirection, password management, and more. It is well worth reading the documentation.
Returning to our “authenticate” page template, we’ll add the cbe_frontauth_protect tags:
<txp:output_form form="meta" />
<txp:output_form form="header" />
<div class="main" role="main">
<txp:cbe_frontauth_protect>
<txp:if_individual_article>
<txp:article />
<txp:else />
<txp:article status="sticky" />
</txp:if_individual_article>
</txp:cbe_frontauth_protect>
</div>
<txp:output_form form="footer" />
Anything inside these tags will be hidden until the user logs in.
Then we’ll add a few more lines of code to call in our login form:
<txp:output_form form="meta" />
<txp:output_form form="header" />
<div class="main" role="main">
<txp:cbe_frontauth_if_connected>
<txp:else />
<h1>Please Log In</h1>
<txp:output_form form="form_login" />
</txp:cbe_frontauth_if_connected>
<txp:cbe_frontauth_protect>
<txp:if_individual_article>
<txp:article />
<txp:else />
<txp:article status="sticky" />
</txp:if_individual_article>
</txp:cbe_frontauth_protect>
</div>
<txp:output_form form="footer" />
The tag cbe_frontauth_if_connected is a simple conditional that executes its contents based on whether the user is logged in. We’ll add a bit more to this code in a bit, but for now, we’ve added a line that says if the user is not logged in, display the login form, which we’ve separated to a Textpattern form called form_login. The contents of that form use more tags from cbe_frontauth:
<txp:cbe_frontauth_login invite="">
<ol class='login'>
<txp:cbe_frontauth_logname label="Username" wraptag="li" />
<txp:cbe_frontauth_password label="Password" wraptag="li" />
<txp:cbe_frontauth_stay label="Remember me" wraptag="li" class="checkbox" />
<txp:cbe_frontauth_submit label="Login" wraptag="li" class="submit" />
</ol>
</txp:cbe_frontauth_login>
In place of this code you could also simply use <txp:cbe_frontauth_box />
, which displays the form, but I prefer laying out each component individually to have better control over the markup. As you can see, I’m wrapping my fields in my own <ol>
, which provides slightly more semantic value and great hooks for styling. Since you guys are equally awesome, I thought you might like to do something similar.
Once the user is authenticated, the login form will disappear, and they will see the secret content as expected. And now that they’re in, we also want to provide a welcome message along with a mechanism to log out. Inside the conditional statement, we’ll add the following:
<txp:cbe_frontauth_if_connected>
<h1>Welcome <txp:cbe_frontauth_whois type="RealName" /></h1>
<txp:cbe_frontauth_logout type="link">
<txp:cbe_frontauth_link link="logout=1" target="_get" label="Log out" wraptag="p" class="logout-link" />
</txp:cbe_frontauth_logout>
<txp:else />
<h1>Please Log In</h1>
<txp:output_form form="form_login" />
</txp:cbe_frontauth_if_connected>
We’re using two new tags. cbe_frontauth_logout is an explicit tag for rendering a logout button as either a button or link. (The default is a button, which would require simply placing <txp:cbe_frontauth_logout />
on the page, but I prefer the HTML markup of a text link I can better control, which is why the code is a bit heavier.) Secondly, cbe_frontauth_whois outputs whatever information is stored in the Textpattern user database; I’m using it to print their real name, but it could also be their username, last access date or email address.
Bonus exposure!
Sometimes, you want a little more information about your users to be shown. By default, Textpattern’s user info is spartan: real name, username, email, password (which can’t be exposed) and last access date. Above, we mentioned using the plugin smd_bio to extend user information. In this case, we want to expose each member’s board position (either a Member or an Officer) and their specific title (such as Vice President, Treasurer, Committee Chairman, and so forth). Using a combination of smd_bio and cbe_frontauth_whois, we can do exactly that. We’ll replace the code under the conditional with this:
<txp:cbe_frontauth_if_connected>
<txp:smd_bio_author author='<txp:cbe_frontauth_whois />'>
<txp:smd_bio_info fields="RealName,title,position" form="smd_bio_login-info" />
</txp:smd_bio_author>
<txp:else />
<h1>Please Log In</h1>
<txp:output_form form="form_login" />
</txp:cbe_frontauth_if_connected>
The Textpattern form smd_bio_login-info would render their name, title, position and the log out functionality using the plugin’s replacement tags:
<h1>Welcome, {smd_bio_RealName}</h1>
<p>{smd_bio_position}, {smd_bio_title}</p>
<txp:cbe_frontauth_logout type="link">
<txp:cbe_frontauth_link link="logout=1" target="_get" label="Log out" wraptag="p" class="logout-link" />
</txp:cbe_frontauth_logout>
As a side note, you could use almost identical code to output a list of all board members, their positions, and their titles. Dive into smd_bio’s documentation to see the unique capabilities this plugin brings to the table.
4. Password retrieval and modification
One of the keys to a professional authenticated system is providing users the ability to retrieve and modify their password. Thanks to cbe_members, this is as simple as activating the plugin. When combined with cbe_frontauth, it automatically renders a “retrieve password” link for non-logged users, and a “change password” link for logged users. Clicking either link reloads the page with a form.
The plugin, as of this writing, is functionally sound but very raw. There are two caveats:
- It provides no options. It works in a binary state: when it’s activated, it renders the links and code; when deactivated, nothing.
- The markup cannot be modified with extra classes or wraptags, so some agility with CSS is required to style components correctly.
5. Serve content conditionally
Using smd_user_manager, you can set up multiple permission levels, and serve content based on user’s role. Using our scenario above, you might have a role of Board Member, Board Officer and Associate, where the Officer role is exposed to additional information. Your code might look like this:
<txp:cbe_frontauth_protect level="6,7,8">
<!-- stuff for Board Officers, Board Members and Associates -->
<txp:article_custom section="protected-stuff" category="associates" />
</txp:cbe_frontauth_protect>
<txp:cbe_frontauth_protect level="6,7">
<!-- stuff for Board Officers and Board Members -->
<txp:article_custom section="protected-stuff" category="board-members" />
</txp:cbe_frontauth_protect>
<txp:cbe_frontauth_protect level="6">
<!-- stuff for just Board Officers -->
<txp:article_custom section="protected-stuff" category="board-officers" />
</txp:cbe_frontauth_protect>
<txp:cbe_frontauth_protect />
also supports the name
attribute for serving content for specific users.
Conclusion
Hopefully, this simple means of providing authentication can help smaller site admins lock down content with confidence. Using further conditionals, this model could be easily extended to much richer functionality, and with some real trickery, extend into other systems like forum usernames.
comments powered by Disqus