Ultra-Simple Membership
When you need a way to quickly implement an easy-to-maintain membership functionality for your website, Ultra-Simple Membership may be exactly what you are looking for.
This is not sophisticated software with a myriad of options. It is a simple thing.
It is intended for use when you want to let some people see certain web pages, perhaps your family or your customers. You manually add them to the member roster. They log in with their username and password. (Their username may be their email address.)
To install Ultra-Simple Membership, upload two PHP scripts. One of the scripts is the dashboard and the other is the membership page monitor.
To use Ultra-Simple Membership:
-
Log into your dashboard and provide:
-
The cookie name for the membership and how long the cookie shall last.
-
A list of usernames and passwords for member accounts.
-
Insert a line of PHP code into every web page that is to be exclusively a member page.
When someone tries to load a member page and they are not logged in, they will see a log-in form instead of the member page. When logged in, the page comes up.
Use your dashboard to add and remove members. Also, if needed, to change the log-in cookie name and/or how long the cookie shall last.
With the line of PHP code to block non-members, member pages can't be seen unless the member cookie is present in the browser.
Installing the Membership Software
There are two PHP scripts. Each has a bit of customization.
-
The dashboard script has 3 places to customize — data file location, dashboard username, dashboard password. The file may have any legitimate *.php file name. In this article,
USM_dashboard.php
is assumed to be its file name. -
The page monitor script has 1 place to customize — data file location. As is the case for the dashboard script, this file may have any legitimate *.php file name. Here,
USM_monitor.php
is assumed to be its file name.
The dashboard looks something like this (screenshot may be resized to display in your device).
Determine where on your domain server the scripts will be uploaded to. They will both be uploaded to the same directory. (Make a note of this directory as the data file location may rely on it.)
Because both PHP scripts need to be customized with the data file location, the location should be decided first.
Generally, the data file would be a subdomain of where the scripts are installed, but it can be anywhere. In this article, subdomain USMdata
with location USMdata/USM.txt
is assumed for the file location.
If the data file location is a subdomain of the PHP scripts, it can be specified as indicated in the previous paragraph. Otherwise, specify the location as relative to the document root directory. Example: /subdirectory/USMdata/USM.txt
USM_monitor.php
Let's customize USM_monitor.php
first because it has only one place to customize — to specify the data file location. Here is the PHP script source code.
<?php
/*
Ultra-Simple Membership
Monitor Unit
Version 1.0
November 24, 2019
Will Bontrager Software LLC
https://www.willmaster.com/
*/
/* *** Customizations *** */
// One place to customize.
// Specify the location of the membership data file.
// This must be identical to the location specified
// in the USM_dashboard.php dashboard file.
// Specify the file's server location, not its URL.
// The directory and file must be writable by PHP.
$MemberDataFile = "USMdata/USM.txt";
/* *** Customization End *** */
mb_regex_encoding('UTF-8');
mb_internal_encoding('UTF-8');
ini_set('display_errors',1);
error_reporting(E_ALL);
if( ! ini_get('date.timezone') ) { date_default_timezone_set('UTC'); }
$MemberDataFile = trim($MemberDataFile);
if( strpos($MemberDataFile,'/')===0 )
{
$MemberDataFile = preg_replace( '/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/', $_SERVER['DOCUMENT_ROOT'], $MemberDataFile );
$MemberDataFile = "{$_SERVER['DOCUMENT_ROOT']}$MemberDataFile";
$MemberDataFile = preg_replace('!//+!','/',$MemberDataFile);
}
$Members = @file($MemberDataFile);
if( count($Members) < 2 )
{
echo '<p>Blank data file or unable to find members in it.</p>';
exit;
}
$Expire = false;
$CookieName = trim(array_shift($Members));
if( preg_match('/\s/',$CookieName) ) { list($CookieName,$Expire) = preg_split('/\s/',$CookieName,2); }
if( empty($_COOKIE[$CookieName]) )
{
if( isset($_POST['un']) and strlen($_POST['un'])>0 and $_POST['pw'] and strlen($_POST['pw'])>0 )
{
$un = strtolower($_POST['un']);
$sha1pw = sha1(trim($_POST['pw']));
$isOK = false;
foreach( $Members as $line )
{
list($u,$p) = explode("\t",$line,2);
$u = strtolower($u);
$p = trim($p);
if( $un == $u and $sha1pw == $p )
{
$isOK = true;
$exp = 0;
if( $Expire )
{
$Expire = preg_replace('/\s*/','',strtolower($Expire));
$match = array();
$expires = 0;
preg_match('/^(\d+)/',$Expire,$match);
if( isset($match[1]) )
{
$units = $match[1];
$match = array();
$days = 0;
preg_match('/([a-z]+)$/',$Expire,$match);
if( isset($match[1]) )
{
$increment = $match[1];
switch($increment)
{
case 'd' : $days = $units; break;
case 'w' : $days = $units * 7; break;
case 'm' : $days = $units * 30; break;
case 'h' : $days = $units / 24; break;
case 'y' : $days = $units * 365.25; break;
}
}
if( $days < 0 ) { $days = 0; }
if( $days ) { $expires = time() + ( $days * 24 * 60 * 60 ); }
}
}
$time = time();
if( headers_sent() )
{
$exp = $expires>0 ? ('; expires='.preg_replace('/\s[\+\-]*\d*$/',' GMT',date('r',$expires))) : '';
echo "<script>document.cookie='$CookieName=$time$exp;'</script>";
}
else { setcookie($CookieName,time(),$expires,'/'); }
}
}
if( ! $isOK ) { ShowLogInForm(); }
}
else { ShowLogInForm(); }
}
function ShowLogInForm()
{
echo <<<LOGINFORM
<form method="post" enctype="multipart/form-data" action="{$_SERVER['PHP_SELF']}">
<div style="display:table; margin:.5in auto;">
<h3>
Log In
</h3>
<p>
Username<br><input type="text" name="un" style="width:200px; box-sizing:border-box;">
</p>
<p>
Password<br><input type="password" name="pw" style="width:200px; box-sizing:border-box;">
</p>
<p>
<input type="submit" value="Log In" style="width:200px; box-sizing:border-box;">
</p>
</div>
</form>
LOGINFORM;
exit;
} // function ShowLogInForm()
?>
Customization:
If the data file location you have decided on is not USMdata/USM.txt
, then replace USMdata/USM.txt with the correct location.
Customization of USM_monitor.php
is complete.
USM_dashboard.php
Now, let's customize USM_dashboard.php
. Here is the PHP script source code.
<?php /* Ultra-Simple Membership Dashboard Unit Version 1.0 November 24, 2019 Will Bontrager Software LLC https://www.willmaster.com/ */ /* *** Customizations *** */ // Three places to customize. // Place 1. // Specify the location of the membership data file. // This must be identical to the location specified // in the USM_monitor.php web page access monitor file. // Specify the file's server location, not its URL. // The directory and file must be writable by PHP. $MemberDataFile = "USMdata/USM.txt"; // Places 2 and 3. // Specify the username and the password for logging // into the dashboard. // The password may be encrypted. If not encrypted, // the password may not be exactly 40 characters // long. If encrypted, it muste be sha1 encrypted. // (The tool at // https://www.willmaster.com/secure/encrypt.php // may be used to encrypt the password.) $Username = "change_username"; $Password = "e9c28e8b9cabca75ef30a4150c80dd1c7725306d"; /* *** Customization End *** */ mb_regex_encoding('UTF-8'); mb_internal_encoding('UTF-8'); ini_set('display_errors',1); error_reporting(E_ALL); if( ! ini_get('date.timezone') ) { date_default_timezone_set('UTC'); } $LoginForm = false; $Dashboardmcookiename = 'USM_dashboard_cookie'; $DashboardCookieExpire = time() + intval( 365.25 * 24 * 60 * 60 ); if( empty($_COOKIE[$Dashboardmcookiename]) ) { if( isset($_POST['un']) and strlen($_POST['un'])>0 and $_POST['pw'] and strlen($_POST['pw'])>0 ) { $_POST['un'] = trim($_POST['un']); $_POST['pw'] = trim($_POST['pw']); $Username = strtolower($Username); $un = strtolower($_POST['un']); if( $Username != $un ) { $LoginForm = true; } else { if( strlen($Password) != 40 ) { if( $Password != $_POST['pw'] ) { $LoginForm = true; } } else { $sha1pw = sha1(trim($_POST['pw'])); if( $Password != $sha1pw ) { $LoginForm = true; } } } if( ! $LoginForm ) { setcookie($Dashboardmcookiename,time(),$DashboardCookieExpire); } } else { $LoginForm = true; } } $Global = array(); $Global['memlist'] = ''; $Global['mcookiename'] = ''; $Global['mcookielife'] = ''; $Global['mlifeperiod'] = 'd'; $Global['Members'] = array(); $Global['notice'] = array(); if( ! $LoginForm ) { $MemberDataFile = trim($MemberDataFile); if( strpos($MemberDataFile,'/')===0 ) { $MemberDataFile = preg_replace( '/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/', $_SERVER['DOCUMENT_ROOT'], $MemberDataFile ); $MemberDataFile = "{$_SERVER['DOCUMENT_ROOT']}$MemberDataFile"; $MemberDataFile = preg_replace('!//+!','/',$MemberDataFile); } $Global['Members'] = MakeDataFromMemberInfo($Global['memlist'],@file($MemberDataFile)); if( isset($_POST['updated']) ) { $Global['notice'][] = 'Updating …'; $Global['NewMembers'] = array(); $Global['mcookiename'] = preg_replace('/\W/','',$_POST['mcookiename']); $Global['mcookielife'] = intval(preg_replace('/\D/','',$_POST['mcookielife'])); $Global['mlifeperiod'] = $_POST['mlifeperiod']; $Global['NewMembers'][] = "{$Global['mcookiename']}\t{$Global['mcookielife']}{$Global['mlifeperiod']}"; foreach( preg_split('/[\r\n]+/',trim($_POST['memlist'])) as $line ) { $line = trim($line); if( preg_match('/\s/',$line) < 1 ) { if( isset($Global['Members'][$line]) ) { $Global['NewMembers'][] = "$line\t{$Global['Members'][$line]}"; } else { $Global['notice'][] = "New member username $line not recorded because no password provided."; } continue; } list($un,$pw) = preg_split('/\s+/',$line,2); $pw = sha1($pw); $Global['NewMembers'][] = "$un\t$pw"; } $Global['notice'][] = '… done.'; file_put_contents($MemberDataFile,implode("\n",$Global['NewMembers'])); $Global['Members'] = MakeDataFromMemberInfo($Global['memlist'],$Global['NewMembers']); } } function MakeDataFromMemberInfo(&$memlist,$memarray) { global $Global; $expire = ''; $list = array(); $memlist = array(); if( ! is_array($memarray) ) { $memarray = array(); } if( count($memarray) ) { $Global['mcookiename'] = trim(array_shift($memarray)); } if( preg_match('/\s/',$Global['mcookiename']) ) { list($Global['mcookiename'],$expire) = preg_split('/\s/',$Global['mcookiename'],2); } if( ! count($memarray) ) { $memarray = array(); } $match = array(); preg_match('/^(\d+)/',$expire,$match); if( isset($match[1]) ) { $Global['mcookielife'] = $match[1]; $match = array(); if( preg_match('/([a-z]+)$/',$expire,$match) ) { $Global['mlifeperiod'] = $match[1]; } } $retval = array(); if( count($memarray) ) { foreach( $memarray as $line ) { if( strpos($line,"\t") === false ) { continue; } list($un,$pw) = explode("\t",trim($line),2); $list[] = $un; $retval[$un] = $pw; } } $memlist = implode("\r\n",$list); return $retval; } # function MakeDataFromMemberInfo() ?><!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Ultra-Simple Membership Dashboard</title> <style type="text/css"> * { box-sizing:border-box; } html, body { font-size:100%; font-family:sans-serif; } .nowrap { white-space:nowrap; } .bold { font-weight:bold; } .italic { font-style:italic; } .underline { text-decoration:underline; } input, textarea { width:100%; font-size:1em; } input[type="text"], input[type="password"], textarea { border:1px solid #ccc; padding:3px; border-radius:3px; font-family:sans-serif; } #content { max-width:650px; margin:.5in auto; background-color:transparent; } </style> </head> <body><div id="content"> <form method="post" enctype="multipart/form-data" action="<?php echo($_SERVER['PHP_SELF']); ?>"> <div style="float:left; margin-right:1em;"><a href="https://www.willmaster.com/"><img src="https://www.willmaster.com/images/wmlogo_icon.gif"></a></div> <h1 style="padding-top:5px; margin-bottom:2em;">Ultra-Simple Membership</h1> <div style="clear:left;"></div> <?php if( count($Global['notice']) ): ?> <div style="border:3px double red; padding:1em; margin:.5in 0;"> <p class="bold">Notice:</p> <ul> <li><?php echo(implode('</li><li>',$Global['notice'])) ?></li> </ul> </div> <?php endif; ?> <?php if( $LoginForm ): ?> <h3> Log In </h3> <p> Username<br><input type="text" name="un"> </p> <p> Password<br><input type="password" name="pw"> </p> <p> <input type="submit" value="Log In"> </p> <?php else: ?> <h1 style="position:relative; top:-1em;">Dashboard</h1> <h3> Member Cookie </h3> <p> Member cookie name<br><input type="text" name="mcookiename" value="<?php echo($Global['mcookiename']) ?>" </p> <p> Member cookie expires in (0 or blank for expiration when browser is exited)<br><input type="text" name="mcookielife" style="width:3em; text-align:center;" value="<?php echo($Global['mcookielife']) ?>"> <select name="mlifeperiod"> <option value="h"<?php if($Global['mlifeperiod']=='h'){echo(' selected="selected"');} ?>>hours</option> <option value="d"<?php if($Global['mlifeperiod']=='d'){echo(' selected="selected"');} ?>>days</option> <option value="w"<?php if($Global['mlifeperiod']=='w'){echo(' selected="selected"');} ?>>weeks</option> <option value="m"<?php if($Global['mlifeperiod']=='m'){echo(' selected="selected"');} ?>>months</option> <option value="y"<?php if($Global['mlifeperiod']=='y'){echo(' selected="selected"');} ?>>years</option> </select> </p> <h3> Member List </h3> <p> Specify member usernames and passwords, both on the same line separated with one or more spaces and/or tabs. Usernames may not have any spaces. Passwords may be any series of characters. When updating the member list, previously assigned passwords will be invisible and won't be changed unless you type something in for a new password. <br> <textarea style="height:2in;" name="memlist"><?php echo($Global['memlist']."\n") ?></textarea> </p> <h3> Update </h3> <p> <input type="Submit" value="Update Information" name="updated"> </p> <?php endif; ?> </form> <p style="margin-top:3em; border-top:1px solid #999; padding-top:.25em; display:table;">Copyright 2019 <a href="https://www.willmaster.com/">Will Bontrager Software LLC</a></p> </div> </html>
Customizations:
There are three places to customize.
Place 1 —
If the data file location you have decided on is not USMdata/USM.txt
, then replace USMdata/USM.txt with the correct location. This must be the same value as specified when the previous script was customized.
Place 2 —
Replace change_username
with a username to log into the Ultra-Simple Membership dashboard.
Place 3 —
Replace e9c28e8b9cabca75ef30a4150c80dd1c7725306d
with either a plain text password or a password encrypted with SHA1. Plain text passwords may not be exactly 40 characters long because that is the length of a SHA1 encryption. To optionally encrypt your password, you may use this 40-character sha1 encryption form.
Customization of USM_dashboard.php
is complete.
Uploading USM_monitor.php
and USM_dashboard.php
Upload the two PHP scripts into the directory you decided on earlier.
Verify the subdirectory for the location of the data file exists. It must be writable by PHP. Generally, writable is the default, but some servers need subdirectories to have 777 permissions.
Using the Membership Software
With the installation complete, inserting one line of PHP code into web pages will change those page into member pages.
Here is the line (needs one customization).
<?php include($_SERVER["DOCUMENT_ROOT"] . "/one/two/USM_monitor.php") ?>
Replace one/two
with the subdirectory path to where USM_monitor.php
is installed.
That's all it takes to convert a regular web page into a member page.
Once the two PHP scripts are installed, it is both easy and simple to add or delete members through the dashboard. And one line of code converts a public web page into a member-only page.
(This article first appeared with an issue of the Possibilities newsletter.)
Will Bontrager