Replacing the […] in WordPress

Some of you might be wondering what I mean by the [...], well I am of course referring to the symbol generated by WordPress upon calling the function the_excerpt(). Now personally I really dislike the symbol which is generated as it often doesn't fit with my design, and I always prefer to have an ellipse - ... followed by a link to continue reading - but how on earth could we go about doing this? Does the answer lie in using jQuery to find and replace this horrendous symbol? Perhaps we need to add some arguments to the the_excerpt() function?

No, it's far simpler than that.

All that needs to be done is the addition of some code to the functions.php file in your theme directory. So head over to this file (if it doesn't exist, create it) and go to your next free line, and copy and paste the code from below.

function new_excerpt_more($post) {
	return '...</p><a href="'. get_permalink($post->ID) . '">' . 'Continue Reading...' . '</a>';
}
add_filter('excerpt_more', 'new_excerpt_more');

So the above code replaces the default [...] with something that looks like "... Continue Reading...", where the continue reading text is a link to the article itself. You could replace this with whatever code you like and have bundles of fun if you like that kind of thing. Just remember to not leave any blank lines at the bottom of your functions.php file as this can cause errors later down the line.

Building a CMS Part 3

And so we've arrived at the last in this set of tutorials; if you've been following along, by now you should have a user system as well as a basic CMS. At the moment we have a very strong base for our CMS but there are still a few things that we need to talk about. Firstly style. I like to think of myself as a stylish kind of guy 😉 and I like to keep my work stylish to, so now were are going to implement some CSS to make our CMS at least moderately aesthetically pleasing. We are going to keep it simple and if you would like to change it feel free to do so, this is just a suggestion; in fact if you are intending on integrating this CMS into your own site, why not give it the same look?

Now before we get to the CSS you will need to copy the tiny bit of code below between your <head> and </head> tags of every page that you want styled.

<link href="style.css" rel="stylesheet" media="screen"/>

That's the code for linking the CSS to the page. Right then let's just get this CSS out of the way. So go ahead and create a new file called style.css and paste in the following code.

* {
	margin: 0px;
	padding: 0px;
}

/*---Login Page---*/

#login {
	width:500px;
	height:250px;
	background-color:#fff;
	-moz-border-radius: 10px;
	-webkit-border-radius:10px;
	display: block;
	top:50%;
	left:50%;
	margin-left:-250px;
	margin-top: -125px;
	position: absolute;
}

input.textbox, input.edit {
	width:450px;
	margin:5px 24px 0px 24px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:35px;
	font-family: Georgia, "Times New Roman", Times, serif;
	color: #7f7f7f;
	padding:5px;
	font-size: 20px;
}

input.submit, input.editbutton, #submit {
	width:460px;
	height:45px;
	cursor:pointer;
	font-family:Calibri, Arial, Helvetica, sans-serif;
	font-size:19px;
	color:#FFF;
	background-color:#bfbfbf;
	border:#cfcfcf;
	border-style:solid;
	border-width:1px;
	padding:3px 4px;
	-webkit-border-radius:3px;
	-moz-border-radius:3px;	
	margin:5px 24px 0;
}

input.submit:hover, input.editbutton:hover, #submit:hover {
	background-color:#cfcfcf;
}

label {
	font-family:Georgia, "Times New Roman", Times, serif;
	font-size:18px;
	color:#9f9f9f;
	padding:5px 24px 0;
}



/*---Index Page---*/

h1 {
	font-family:Georgia, "Times New Roman", Times, serif;
	font-size: 24px;
	color:#777;
	padding:5px;
}

ul.pagelist1 {
	background-color: #cfcfcf;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	padding:10px;
	width:680px;
}

li.pageoption {
	background-color: #afafaf;
	width:640px;
	padding:10px;
	-moz-border-radius: 5px;
	-webkit-border-radius:5px;
	display: block;
	margin:10px;
}

li.pageoption:hover {
	background-color: #efefef;
}

a.pagelink {
	font-family:Calibri, Arial, Helvetica, sans-serif;
	color:#000;
	text-decoration: none;
}

span.floatright {
	float: right;
}

/*---Edit Page---*/

textarea.edit {
	width:450px;
	margin:5px 24px 0px 24px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:200px;
	font-family: Calibri, Arial, Helvetica, sans-serif;
	color: #000;
	padding:5px;
	font-size: 20px;
}

input.edit {
	width:450px;
	margin:5px 24px 0px 24px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:35px;
	font-family: Calibri, Arial, Helvetica, sans-serif;
	color: #000;
	padding:5px;
	font-size: 20px;
}

/*---Add New Page, Page---*/

#title {
	width:450px;
	margin:5px 24px 0px 24px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:35px;
	font-family: Georgia, "Times New Roman", Times, serif;
	color: #7f7f7f;
	padding:5px;
	font-size: 20px;
}

#url {
	width:170px;
	margin:5px 0px 0px 0px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:35px;
	font-family: Georgia, "Times New Roman", Times, serif;
	color: #7f7f7f;
	padding:5px;
	font-size: 20px;
}

.infomsg {
	display:block;
	background-color:#fff9d7;
	border-width:1px;
	border-color:#e2c822;
	border-style:solid;
	width:445px;
	color:#000;
	font-family:Calibri, Arial, Helvetica, sans-serif;
	font-size:16px;
	text-align:center;
	margin:10px 0px 10px 20px;
	padding:10px;
}

/*---Signup Page---*/

#signupform input {
	width:450px;
	margin:5px 24px 0px 24px;
	border:1px solid #cfcfcf;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	height:35px;
	font-family: Georgia, "Times New Roman", Times, serif;
	color: #7f7f7f;
	padding:5px;
	font-size: 20px;
}

Now that is quite a lot of code indeed. But really all it does is make our site more workable. Next I would like to talk about extending the functionality of this CMS. I am going to be using real world examples that I recently used on a client's site, so they should be relevant.

So let's take a step back and look at what we have done. We have created a CMS that allows users to signup and login. From the index page they can create new pages and edit existing pages. The changes that they make in a friendly noob-based environment as I like to call it, will be reflected on the live site. That's quite impressive. Visit any one of the site's pages and you are viewing dynamically generated content. This approach is excellent for us web designers, and the only people that love it more than we do, are the clients. Think about it, if you had a site that was supposed to display the latest technology news and you had to go through your web designer first, it would take some time, therefore making your site out of date quickly. Clients love the idea that they can easily edit and update the information on their site without needing to know anything technical. But wait! Before you go pitching to a client with the CMS above there are a few things you should know. If I bought a website with the CMS above I wouldn't be happy. Why not? Well because there are some things missing. Firstly think about the navigation in the backend. There is none! Moreover how do I add images and links? Remember I know nothing about code. To overcome such difficulties that you will inevitably face I recommend you work on their solutions.

When the client came to me and asked for a myriad of features I had a number of problems to sort out. Many of the problems I faced were solved using solutions listed in this series of tutorials. But instead of providing you with the exact solutions to some of them, I urge you to try to figure them out for yourself. Let's look at the image and link adding feature for example. Realistically it can be tackled head on very easily. What would we need? Well, we want the URL of the page to be linked to, and what the user wants to display as the link text. Then all we have to do is generate a bit of HTML with that information in it. Go ahead and try and combat the problem. You will need a form and a tip - I used some really simple Javascript to generate the HTML. Once the code is generated the user need only paste it into the editing field of the page.

Now let's discuss an advanced security feature. You could use this for a client who was particularly wary of being hacked. Look at the code below.

       $ip=$_SERVER['REMOTE_ADDR'];
       
               if(!filter_var($ip, FILTER_VALIDATE_IP))
               {
                       throw new InvalidArgumentException("IP is not valid");
               }

               $response = file_get_contents('http://www.netip.de/search?query='.$ip);
               if (empty($response))
               {
                       throw new InvalidArgumentException("Error contacting Geo-IP-Server");
               }

               $patterns=array();
               $patterns["domain"] = '#Domain: (.*?) #i';
               $patterns["country"] = '#Country: (.*?) #i';
               $patterns["town"] = '#City: (.*?) $pattern)
               {
                       $ipInfo[$key] = preg_match($pattern,$response,$value) && !empty($value[1]) ? $value[1] : 'not found';
               }

The code above gets the user's IP address and then uses a free service to retrieve some information. With that information you could compare the country the user is in to the country they usually login from, and if it differs ask them to provide some more information like an extra security code. Clients will be impressed by this sort of stuff, especially if you can make it work well and make it slick.

Lastly I want to talk about user details. This is a really simple subject area, but it is important. If your user signs up and accidentally spells their name wrong then they won't be able to change it, and that really isn't great. If you think about it to implement a profile editing page would be easy. I'll take you through the theory. You would create a new page and add a form not dissimilar to that of the form used to signup. You would then populate the form with $_SESSION variables, leaving the user free to edit them. When the submit button is clicked the database is updated and everything is great!

The things listed above may seem simple, but adding polish to a site's CMS is a good idea and a requirement if you are working on one for a client. In the end my client's CMS turned into a very powerful one, it looks lovely with a load of fancy icons and animations. I was able to work with the client and help them to achieve their goals because at the end of the day the CMS that I built - much like the one above - was extensible and flexible.

I hope you have enjoyed this series, I have worked hard to iron out any kinks in the code, but if you find one leave a comment below - I'll be most grateful. The same goes for any issues you have with the tutorial. If you have any questions at all, feel free to ask away below.

Now that's over, time for a coffee me thinks!

Building a CMS Part 2

Note: This post is currently undergoing review and will be updated within the next few days to reflect more recent standards and better code quality.

So if you're following this set of tutorials you should now have your very own login system that allows users to sign up and login. However these tutorials aren't about building a user base, so we need to add the functionality of a CMS. At the moment if a user logs in they will be presented with a blank screen which isn't very useful at all, so now we will create a rather friendly member's area. But before we do, let's assess what we need in the member's area. Well to begin with we want to be presented with a list of editable pages right? So we will need to produce a list of all of these pages from the database. With this in mind, create a file called index.php, not forgetting to add the normal document base structure, and then copy the code below into the file.

<?php require("connect.php");  session_start(); ?>
echo '<h1>Member\'s Area</h1> <ul>';
<?php
$sql = mysql_query('SELECT * FROM content');
$pages = mysql_num_rows($sql);
$pagesedit = $pages + 1;
$pageselector = 1;
echo '<a href="http://mywebsite.com/addpage.php"><li>[+] <strong>Add new page</strong></li></a>';
while($pages >= $pageselector){
$contentresult = mysql_query("SELECT * FROM content WHERE id = '".$pageselector."'");
$contentrow = mysql_fetch_array($contentresult);
echo '<a href="http://mywebsite/edit.php?page='. $contentrow['page'] .'"><li><strong>'. $contentrow['title']   .'</strong><span>Last updated on <strong>'. $contentrow['lastupdate'] .'</strong> by <strong>'. $contentrow['updater']   .'</strong></span></li></a>';
$pageselector++;
}
?>
</ul>

Now the middle bit with all the PHP might seem a little complex, but if we break it down it makes more sense. At the top we simply select all the records from our "content" table that we created earlier, and then count how many we have with the function mysql_num_rows(). Then we echo out a link to a page that we will later use to create new pages. After that we create a while loop which loops through the content of the table, and for each record, it echos a link to another page we haven't yet created, that we will be able to edit pages of the site with. Now I know it might seem like a lot to take in, but if you take a moment to look at it logically it makes perfect sense!

Now if you were to visit that page right now you would likely get an error because we don't have anything in our table yet. To stop this from happening we are going to create a new file called addpage.php which we will use to create pages. Now I warn you there is a lot of code for this page, so much in fact I've given it it's own special type of container! But don't worry because all will be explained!

<?php
require("connect.php");
session_start();
mysql_connect($dbhost, $dbuser, $dbpass) or die('>>Error connecting to Database<br />');
mysql_select_db($dbname) or die('>>Unable to select Database');
if(!empty($_POST['title'])){
$filename = str_replace(" ", "-",$_POST['url']);
$filename1 =  "root/folder/" . $filename . ".php"; //EDIT THIS LINE!
$filehandle = fopen($filename1, 'w') or die("Can't open file");
$blankPage = '
<?php
$currentPage = "'.$_POST[url].'";
require("connect.php");
$result = mysql_query("SELECT * FROM content WHERE page = \'$currentPage\'");
$row = mysql_fetch_array($result);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><?php echo $row[title] ?></title>
</head>
<body>
<?php echo "<h1>" . $row[title] . "</h1>" ?>
<?php echo "<p class=\"text\">" . nl2br($row[text]) . "</p>"; ?>
</body>
</html>
';
fwrite($filehandle, $blankPage);
fclose($filehandle);
mysql_query("INSERT INTO content(page,title,text,lastupdate,updater) VALUES('$filename','".ucwords($_POST[title])."','TBC','".gmdate('d-m-y')."','$_SESSION[firstname] $_SESSION[lastname]')") or die(mysql_error());
$file = "http://mywebsite.com/".$filename.".php";
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Add New Page</title>
</head>
<body>
<?php if(!empty($file)){ echo '<span class="infomsg"><a>Here is the URL for the new page: '. $file .'<br />To edit <a href="http://mywebsite.com/edit.php?page='.str_replace("/", "",$filename).'">click here</a></a></span>'; }; ?>
<form id="editor" action="addpage.php"  method="post">
<label id="top" for="title">Page Title: </label><br />
<input type="text" id="title" name="title" value="New Page" />
<br />
<br/>
<label for="text">Path to Page: </label><br /><br />
<label for="text">http://mywebsite.com/</label>
<input type="text" id="url" value="new-page" name="url" /><label for="text">.php</label>
<br />
<br />
<input id="submit" type="submit" value="Add Page" />
<br />
<br />
</form>
</div>
</div>
</body>
</html>

Now I know that's a lot of code, but bare with me here, I'm about to go through it all with you now (again if you get it, feel free to skip). So at the top of the code we make our usual connection to our database and then there's a load of code. For the time being skip the rest of the PHP code until you get down to the actual page entitled "Add new Page". On this page we first have an if statement to check if a variable is empty, and if not, then we echo a message to the user so that they know where their newly created file is. Then we have a form into which users can enter a title and a URL for a new page, but what you may have noticed is that this page doesn't post the information to another, but instead to itself - and that's why it is so long. So once the form is submitted we can jump back to the top of the page to examine the rest of the code. After the connection we can see an "if" statement which checks to see if the form has been submitted, if it has, then it executes a large block of code.

At the top of this block of code we have 4 variables. Our $filename variable takes the URL that the user has supplied and replaces any spaces with a hyphen - this is because we don't want page URL with spaces in them - one of my pet hates! After this we create an actual URL for the file, now it is important that you get this part right, you cannot have a URL such as "http://yoursite.com/", instead you have to have your folder root, so if your site is inside a folder such as "public_html" then you will put "public_html/", it's basically the furthest back you can go on your server in terms of folders - just keep going up until you can't go up no more! Next we create a file handle which inserts the URL into a more useful variable to be used later on. Next we have our blank page which is a generic skeleton to be used for every page of the site. Notice that the only variable that changes is the that of $currentPage, this is the variable used to communicate with the database, so it is imperative that it is correct.

Lastly we have the fwrite() function which attempts to write to a file using the URL we supply, and the information we give it with $blankPage. If the function cannot find the file specified, then it simply creates it, which is very useful indeed! Then, we close the function and insert all the information into the database, leaving the text field with simply "TBC" or to be confirmed. If you want to change the default text feel free to do so. And finally we set the value of the $file variable so that the user can be presented with the URL of the file they just created!

Phew, that was a lot of code!

Now I must warn you that if the page already exists this page wil overwrite it, so be careful when naming pages. But if everything is working visit the page and create a brand new page, call it anything you want and submit the form! After you have done this, go to the index page and you should see the page title, when it was last updated, and who it was last updated by in the list! Great stuff, but we're not done yet, because if you click the link you are going to end up with an error because we haven't yet created the editing page - don't worry it's really simple, so lets do it now. Go ahead and create a new page called edit.php and paste the following code into it.

<?php session_start(); $currentPage = $_GET['page'];
$result = mysql_query("SELECT * FROM content WHERE page = '".$currentPage."'");
$row = mysql_fetch_array($result);
if($row['title'] == ""){ echo "<script type=\"text/javascript\">window.location = \"http://yoursite.com/\"</script>"; die(); };

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Editing: <?php echo $row['title']; ?></title>
</head>
<body>
<h1>Editing Page: <?php echo $row['title'] ?></h1>
<form id="editor" action="process.php?page=<?php echo $currentPage ?>"  method="post">
<?php if($_GET['status'] == "success"){ echo "<div id=\"infomsg\"><p>Page successfully updated!</p></div>"; } ?>
<label id="top" for="title">Title: </label><br />
<input type="text" id="title" name="title" value="<?php echo $row['title'] ?>" />
<br />
<br/>
<label for="text">Text: </label><br />
<textarea name="text" id="text"><?php echo $row[text] ?></textarea>
<br />
<br />
<input id="submit" type="submit" value="Submit Changes" />
<br />
<br />
</form>
</body>
</html>

So although it may not look it, this page is really simple. At the top we connect to our database and fetch the page supplied by the URL. We then grab all the page's information from the database and fill up some variables with that information. You may have noticed that we use Javascript this time to redirect the user if they aren't logged in, why? Well because variety is the spice of a CMS! We then fill a text box and a text area with the values from our database. Now again this form submits to another page, and so we are going to have to create it. So create a new page named process.php and paste the following code into it.

<?php $currentPage = $_GET['page']; require("connect.php"); session_start(); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Editing</title>
</head>
<body>
<?php
$title = $_POST['title'];
$text = $_POST['text'];
if($title == "" || $text == ""){ echo "<h1>No input Recieved!</h1>"; die(); }
mysql_query("UPDATE content SET title= '$title' WHERE page='$currentPage'") or die('Unable to change Title');
mysql_query("UPDATE content SET text='$text' WHERE page='$currentPage'") or die('Unable to change Text');
mysql_query("UPDATE content SET lastupdate='". date("d-m-y")."' WHERE page='$currentPage'") or die('Unable to insert Date');
mysql_query("UPDATE content SET updater='$_SESSION[firstname] $_SESSION[lastname]' WHERE page='$currentPage'") or die('Unable to insert Name');
?>
<script type="text/javascript">
document.location = "http://yoursite.com/edit.php?page=<?php echo $currentPage ?>&status=success";
</script>
</body>
</html>

So the above is a really simple page, basically all it does is take what the user enters into the form and updates the record in the database. Notice the helpful error messages as well; using relevant error messages can help you to track down an error faster, so I defiantly recommend it. Now if the page exists, and process.php is able to update it, it should redirect users back to the editing page with a success message. If it has worked you should now see your changes reflected on the page that you were editing.

Now to finish off this part of the tutorial we are going to create the simpelest page ever that will allow you to destroy the session variables and ultimately log out. We'll call it logout.php (unexpected eh?). Create the file and paste in the code below.

<?php session_start(); session_destroy() ?>

And that's it for this tutorial, in the next part we'll look at how we can allow the user to add images and links, as well as the all important styling so that it looks pretty! Stay tuned!

Building a CMS Part 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!