Building a CMS Part 1

Please note: This post was written 7 years ago, which is an age in programming terms, so please don't treat this as gospel! I'll be updating all posts soon enough, but if you'd especially like this one to be updated let me know.

cms-1

A content management system (or CMS) is the best way (I think) to maintain a site. This site, like many others uses WordPress, a blogging CMS. There are many options available to us web designers out there, but I think creating your own CMS is a really good way of learning a lot of new techniques. So let me tell you what you will learn in this series. We will be starting from scratch, laying out our plans for what we want to achieve with our CMS and how we are going to go about doing it. We will then set up a database for our CMS and make it accessible to a bunch of PHP based pages. We will then create a site that relies on the CMS to display content, and at the same time create the back-end of the CMS where we will be able to add pages, change the content, and upload images. At the end of these tutorials you should have a good idea of what it takes to create a CMS and hopefully appreciate even more, the work of those who maintain services such as WordPress.

What do I need to know?

  1. General Understanding of HTML / CSS
  2. Basic knowledge of PHP and MySQL syntax
  3. Access to a server with a MySQL Database & PHPMyAdmin

Once you have all of the above we should be able to get going.

Note: This tutorial was initially published in 2010, and was updated in June 2013 to reflect newer technologies and better practises, primarily the use of PDO.

Let's do this thing!

Our CMS is going to be rather simple, but will allow for user sign-up, content editing and a few other little features. The backbone of our CMS is going to be our database, and we are going to use PHPMyAdmin to create one. If you have a hosting plan you will need to find out if you have PHPMyAdmin installed on your server - most hosts should offer the service - including Media Temple (not a referral link). Many services require you to create the database outside of PHPMyAdmin, so find out how to do so and create one called "main" (all lower-case). Then head over into PHPMyAdmin and select the database on the left. Click "Create New Table" with 6 fields named "content". These fields will be id, page, title, text, last update, updater - exactly like that. Make them all VARCHARs with appropriate lengths.

The next table is to be even more in-depth, it's the users table. Go ahead and create a new table named "users" with 13 fields. They will be - id, username, password, email,  hash, active, firstName, lastName, firstLogin, lastLogin, secretQuestion, secretAnswer, photo. Don't worry - all will be explained with a couple of handy lists below.

  • id - for internal referencing, which is a necessity
  • username - what users will use to login
  • password - what users will use to authenticate themselves
  • email - for communication with the user
  • active - to make sure the user has activated their email
  • firstName - so we know what to call them informally
  • lastName - so we know who we have on record
  • firstLogin - just a nice little piece of information
  • lastLogin - to find out if they are using their account
  • secretQuestion - if they forget their password
  • secretAnswer - the answer to the one above
  • photo - so we can see them!

Right, now we know what is what in the land of databases, we need to connect to it. So make sure you have the required credentials to access the database. These are your database user name, your database password and your database name (and very rarely your hostname). Once you've got all of that good stuff head over into your code editor of choice and let the mayhem begin!

Our Connect File

To have a CMS work, we need to be able to communicate with our database, and to do so we will first have to connect to it. And if we want multiple pages of our CMS to be able to communicate with our database we are going to need to create a separate file (the alternative to this, is copying and pasting the code onto every page - but that's a lot of work, and if we happen to change any of the details, a whole lot more). The file itself is really simple, below is the code for the file. Create a new file called connect.php and paste in the code below.

<?php
define(DB_HOST, 'localhost'); // You shouldn't need to change this
define(DB_NAME, 'my_database'); // The name of your database
define(DB_USER, 'userName'); // Your username
define(DB_PASS, 'password'); // Your password

$db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);

?>

The only thing you might be unfamiliar with is the PDO function. Unfortunately a trap that many beginners fall into because of the plethora of poor content published, is using the simple mysql_* functions, however these old and insecure (like a retired actor), and the deprecation process has started - eek! So you heard it from me first: never use the mysql_* functions again! They leave you vulnerable to attack and are a terrible practise. Got it? Good, let's move on.

We will now be able to use the PHP function of require to include this in all the files we create. Before we continue, make sure that the file can successfully connect to the database, do this by simply visiting the page. If successful you should see a blank page, if not, you should see an error message. Make sure it works before continuing on.

User Authentication

We want to be able to edit the content of our site right? But what we don't want, is everybody being able to edit the content, or else we could be subject to spammers, hackers, and all kinds of other nasty stuff. So we are going to have to create a user system. You might think this is a long and complex process, but really it's quite simple. We will need to create a login page, followed by one to authenticate the credentials, followed by a "Members Only" area where we will eventually place the main body of our CMS. So let's get started.

Create a new page named login.php and insert a basic page structure. Now we need to create a form that users can use to login. The form is going to be very simple with inputs for username, password, and then a submit button.

<form id="login" action="userauth.php" method="post">
<label for="username">Username: </label><br />
<input class="textbox" type="text" name="username" placeholder="Username" />
<br />
<br />
<label for="password">Password: </label><br />
<input class="textbox" type="password" name="password" placeholder="Password" />
<br />
<br />
<input type="submit" class="submit" value="Submit" />
</form>

As you can see from the code above we are going to be needing another file named userauth.php, this will be the file that will compare what the user inputs to what we have in our database, so go ahead and create it. So what do we have at the moment? Well we have a login form that posts the information to another page to validate it. Right now if you fill in the form you will be taken to a black page, and that's no good is it?! So let's create the authentication page. Hopefully you've already created the file, open it up and insert the following code (a basic page structure fo this page isn't required).

<?php
// Make sure we can connect to our DB
require("connect.php");
session_start();

$iusername = $_POST['username'];
$ipassword = sha1($_POST['password']); // Best to use a salt remember!

// Have we got any input to process?
if(empty($iusername) || empty($ipassword)){ 
	echo "<meta http-equiv=\"refresh\" content=\"0;url=http://yoursite.com/login.php?m=1\">"; 
	die();
} // if

// Fetch the user from the DB
$query = $db->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$query->bindValue(':username', $iusername);
$query->bindValue(':password', $ipassword)
$query->execute();

// Do we have a valid user?
if($row = $query->fetch(PDO::FETCH_ASSOC)){	
	if($row['active']==0){
		echo "<meta http-equiv=\"refresh\" content=\"0;url=http://yoursite.com/login.php?status=noneactive\">"; 
		session_destroy();
	} // if
	
	// Keep our session up to date
	$_SESSION['loggedin']  = true;	
	$_SESSION['userid']    = $row['id'];
	
	// Do we need to insert a first login value?
	if($row['firstLogin'] == 0){
		$query = $db->prepare("UPDATE users set firstLogin = :firstLogin WHERE id = :id");
		$query->bindValue(':firstLogin', gmdate('U'), PDO::PARAM_INT);
		$query->bindValue(':id', $_SESSION['id'], PDO::PARAM_INT);
		$query->execute();
	} // if
	
	// Update the last login time
	$query = $db->prepare("UPDATE users set lastLogin = :lastLogin WHERE id = :id");
	$query->bindValue(':lastLogin', gmdate('U'), PDO::PARAM_INT);
	$query->bindValue(':id', $_SESSION['id'], PDO::PARAM_INT);
	$query->execute();

	// Finally redirect the user	
	echo "<meta http-equiv=\"refresh\" content=\"0;url=http://yoursite.com/index.php\">";
}else {
	// Something has gone wrong, sent 'em away!
	echo "<meta http-equiv=\"refresh\" content=\"0;url=http://yoursite.com/login.php?m=1\">";
	session_destroy();
} // if
?>

So what's going on here? Well first up we pull in connect.php to ensure we can access our database (if this file doesn't exist or there's a problem with it our page will die!), we then start our session so we can keep track of the current user. After this we create 2 variables to store the input, notice the password input goes through a function called sha1() - this hashes the value - think of it as one-way encryption which is nice and secure as we the "owners" of the database cannot directly read the user's password. There's a note about adding a "salt" - essentially to improve security it's a good idea to prepend or append a random string to the input before hashing it - you just have to make sure to use the same one when writing your login script! Final note here: don't use md5() despite what anyone says, it's creator said it's no longer secure!

Ok up next we check to ensure we have input for both the username and password, and if not we redirect our user. Then we get to use some funky PDO syntax. If you've not encountered PDO before this can be daunting, but after some practise you'll learn to love it! Here we select all users who's username and password matches our input, if we've got proper input then this will return exactly 1 row, otherwise we'll get nothing back. To check this we ask for the $row variable and wrap it in an if statement - if this fails then again we redirect the user.

Once inside the if we know we have a valid login. So first up we check to see if the user has activated their account (more on that later), and then set up some $_SESSION variables. Penultimately we check to see if we need to assign a first time login date, and then update the last login date, before finally redirecting the user. Phew! Quite a lot to take in there, but providing you've made it this far we should be getting somewhere.

Now the more astute among you may have realised that we haven't actually got any records in our database, meaning that even if we wanted to, we wouldn't be able to login. So let's get to creating that signup form.

The Signup Form

We are going to create a really simple form so that users can signup, you should be aware that more time should be spent perfecting the form such as adding validation to certain fields and maybe some sort of terms of service agreement, but for now let's just go ahead and create a simple signup form.

<form id="signupform" action="addaccount.php" method="post">
	<label for="username">Username: </label>
	<input name="username" type="text" placeholder="joebloggs" /><br>
	
	<label for="password">Password: </label>
	<input name="password" type="password" placeholder="password" /><br>
	
	<label for="email">Email Address: </label>
	<input name="email" type="text" placeholder="joe@bloggs.com" /><br>
	
	<label for="firstname">First Name: </label>
	<input name="firstname" type="text" placeholder="Joseph" /><br>
	
	<label for="secondname">Second Name: </label>
	<input name="secondname" type="text" placeholder="Bloggs" /><br>
	
	<label for="secretquestion">Write a secret Question: </label>
	<input name="secretquestion" type="text" placeholder="Favourite Place" /><br>
	
	<label for="secretanswer">Answer to secret Question: </label>
	<input name="secretanswer" type="text" placeholder="New York" /><br>
	
	<input class="submit" type="submit" />
</form>

So the above code simple gives us a form whereby users can enter their info and submit it to our system. Again this form posts the information to another file called addaccount.php, and we need to create this file. so go ahead and create addaccount.php and paste in the following code.

<?php
// Make sure we can connect to the DB
require("connect.php");

// Prepare our insert query
$query = $db->prepare("INSERT INTO users (username ,password, email, active, firstName, lastName, firstLogin, lastLogin, secretQuestion, secretAnswer, photo) VALUES (:user, :pass, :email, '0', :firstname, :secondname, '0', '0', :secretq, :secreta, '0')");

$query->bindParam(':user', $_POST['username']);
$query->bindParam(':pass', sha1($_POST['password']));
$query->bindParam(':email', $_POST['email']);
$query->bindParam(':firstname', $_POST['firstname']);
$query->bindParam(':secondname', $_POST['secondname']);
$query->bindParam(':secretq', $_POST['secretquestion']);
$query->bindParam(':secreta', $_POST['secretanswer']);

// Execute it
$query->execute();

/*
  Now send a confirmation email
  
  To make this more secure we include a secret hash in the link 
  users must click based on their email address
*/
$hash = sha1($email . 'my_amazing_salt'); // **** Remember this salt! ****

$message = '
<html>
<body bgcolor="#FFF">
<p style="font-family:Calibri, Arial, Helvetica, sans-serif; color:#000; font-size:16px;">Hi ' . $firstname . ', You have just signed up for your account on my website. To activate the account please click, or copy and paste into a browser, the link below.</p><br /><br />
<p>http://example.com/activate.php?i='.$hash.'&email='.$email.'</p>
</body>
</html>';

// Send the email
$subject = 'My CMS – Please Activate your Account';
$headers = 'From:My Site!' . "\r\nContent-Type: text/html\n";
mail($_POST['email'], $subject, $message, $headers);

// Finally redirect to the login page
echo "<meta http-equiv=\"refresh\" content=\"0;url=login.php?m=1\">";

?>

So what's happening here? Although there's quite a bit of code, we can break it into 2 simple parts: first we insert all the data we get from the new user into our database - we aren't validating the input, so you might want to look into such things! When we've finished inserting the data we want to confirm the user's email address, so we first create a hash. As I said before this is a one-way encryption and in this case will allow us to prevent the user activating their account unless they have the exact hash; we once again refer to the handy sha1() function and this time add a salt - just incase some unscrupulous character realised we were using the function, they'd be hard-pushed to guess a random string.

When we're happy with our hash we go ahead and create a HTML email, set the subject and headers, and send it on it's way! I should point out that HTML email is an entirely different beast, so I'd suggest you try and keep it simple as most email clients will render your code completely differently, and there is much variation. When we're finished we once again redirect the user.

If the code is successful and all is well, the user should receive an email with a link to validate the page. Note: if you're running this on your localhost then you may be unable to send emails as this tends to be disabled in various ways, if that's the case, try outputting the content of the email to your browser and verifying it is all working correctly. You will need to replace the site URL with your own and create a new page called activate.php. Copy and paste the following code into this page.

<?php
// Make sure we can access our DB
require("connect.php");

// Collect the URL params
$hash = $_GET['i'];
$email = $_GET['email'];

// Get the user row
$query = $db->prepare("SELECT active FROM users WHERE email = :email");
$query->bindValue(':email', $email);
$query->execute();
$row = $query->fetch(PDO::FETCH_ASSOC);

if($row['active'] == 1){

	// The account has already been activated
	echo "<h1>This account has already been activated!</h1>";
	echo "<meta http-equiv=\"refresh\" content=\"2;url=login.php\">";
	
} else if(sha1($email . 'my_amazing_salt') == $hash){ // **** Make sure you use the same salt! ****

	// Time to activate the account
	$query = $db->prepare("UPDATE users SET active = '1' WHERE email = :email");
	$query->bindValue(':email', $email);
	$query->execute();

	// Tell the user and redirect them
	echo "<h1>Account Activated!</h1>";
	echo "<a>You will now need to login</a>";
	echo "<meta http-equiv=\"refresh\" content=\"2;url=login.php\">";
	
} else{
	// Something has gone wrong 🙁
	echo "<h1>Invalid verification</h1>";
	echo "<meta http-equiv=\"refresh\" content=\"2;url=login.php\">";
}
?>

This page is self explanatory, it checks to see if the account has been activated, if not then it activates it, and if it can't find the email address, or the hash is wrong, then it doesn't activate anything! Also notice the 2 second delay on the redirects, allowing the user to read whatever message is outputted by the system. Remember to use the same salt as used in the sign-up process or this won't ever work!

And that's it for this first part, in the next tutorial we'll look at the member's area and how we can allow the users to edit the content of a site, as well as organising your files and making the whole thing look nice - stay tuned!

***

If you found this particularly useful and want to share the ♥ you can donate here.

***

6 Responses

  1. Ian

    A nice simple explanation of the processes involved. Does this site use your CMS? I am developing a new website that could possibly use a CMS and await the next article with interest.

    Reply
    • Tom

      Hi Ian, no this site uses WordPress, but I had a client recently who required that I build a CMS - these tutorials are based on my experience with them, and I'm glad you like the article 🙂

      Reply
  2. Marjun

    I like this article but I need full tutorial on how to make a simple CMS for my blog portfolio website.

    Reply
  3. Ryan

    Very helpful! Was just thinking about how to do this and am glad I found your article. Gonna go look for part two now. Thank!

    Reply
  4. Tanzeel Niazi

    Yeah, very helpful, thanks for such a nice Post

    Reply


~ Comments are now closed ~

Get in touch here