Creating a blog from scratch with PHP

Creating a blog from scratch with PHP

·

23 min read

Blog Series


This tutorial will cover creating a very simple blog. It will only consist of posts. The front-end will only be two pages, an index page to list all posts and a view page to view a post.

There will be a backend control panel for managing posts and admins, this guide will also include a user authentication system to login administrators.

Demo: demos.dcblog.dev/simpleblog

Download: github.com/daveismynamecom/simple-blog-part..

admin demo

username: demo
password: demo

I will reinstall the demo every hour.

The file structure will be setup as follows:

Blog folders structure

There will be two mysql tables, blog_posts and blog_members.

blog_posts structure:

blog_posts structure

blog_members structure

blog_members structure

Every page will need a database connection, this will be opened in config.php:

includes/config.php

Start output buffering, then headers can be used anywhere. Start sessions this will be needed for the admin area.

Define the database connection details then open a PDO connection.

Also set the timezone, adjust this as needed.

<?php
ob_start();
session_start();

//database credentials
define('DBHOST','localhost');
define('DBUSER','database username');
define('DBPASS','database password');
define('DBNAME','database name');

$db = new PDO("mysql:host=".DBHOST.";port=8889;dbname=".DBNAME, DBUSER, DBPASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

//set timezone
date_default_timezone_set('Europe/London');

Next create an autoload function this will include any class as it is called this stops you having to manually include all classes, for this project there is only a single class but this set up the feature for more classes in future developments.

Inside the function, the name passed to is is converted into lowercase then check to see if the file exists if it does it is then included. 

Lastly the user class is instantiated and passed the database connection ($db) so the class has access to the database.

//load classes as needed
function __autoload($class) {

   $class = strtolower($class);

   $classpath = 'classes/class.'.$class . '.php';
   if ( file_exists($classpath)) {
      require_once $classpath;
    }     

   $classpath = '../classes/class.'.$class . '.php';
   if ( file_exists($classpath)) {
      require_once $classpath;
    }


}

$user = new User($db);

index.php

The index file will list all posts from the posts table.
A query is ran to select the columns from blog_posts then ordered by the postID in descending order.
Then the posts are looped through on each loop display the title, description date posted and a link to read the full post.

The query is wrapped inside a try catch statement so if there is any errors a PDO Exception will be used to display them. 

The id for the post is past to the next page in what's called a query string ?id will become a variable in the url = assignes the value.

<?php
    try {

        $stmt = $db->query('SELECT postID, postTitle, postDesc, postDate FROM blog_posts ORDER BY postID DESC');
        while($row = $stmt->fetch()){

            echo '<div>';
                echo '<h1><a href="viewpost.php?id='.$row['postID'].'">'.$row['postTitle'].'</a></h1>';
                echo '<p>Posted on '.date('jS M Y H:i:s', strtotime($row['postDate'])).'</p>';
                echo '<p>'.$row['postDesc'].'</p>';                
                echo '<p><a href="viewpost.php?id='.$row['postID'].'">Read More</a></p>';                
            echo '</div>';

        }

    } catch(PDOException $e) {
        echo $e->getMessage();
    }
?>

viewpost.php

viewpost.php is used to display any post that has been clicked on.

This query will use a prepared statement, the record to be selected is based upon the id been passed from a $_GET['id'] request, as such a normally query is not recommended, a prepared statement is much better. The prepare will 'prepare' the database for query to be run then when $stmt->execute is ran the items from the array will be bound and sent to the database server, at no point does the two connect to there is no way to tamper with the database.

$stmt = $db->prepare('SELECT postID, postTitle, postCont, postDate FROM blog_posts WHERE postID = :postID');
$stmt->execute(array(':postID' => $_GET['id']));
$row = $stmt->fetch();

If there is no postID coming from the database, their is no record so redirect the user to the index page.

if($row['postID'] == ''){
    header('Location: ./');
    exit;
}

Lastly display the select post in full:

echo '<div>';
    echo '<h1>'.$row['postTitle'].'</h1>';
    echo '<p>Posted on '.date('jS M Y', strtotime($row['postDate'])).'</p>';
    echo '<p>'.$row['postCont'].'</p>';                
echo '</div>';

That's it for the front end! its very simple but its also very easy to expand and add more features.

Admin

Every page in the admin area will start by including the config file and checking if the admin is logged in, otherwise they are redirect to the login page. 

//include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }

admin/login.php

The login page will check if the user is already logged in, if they are they will be redirect to the main admin area.

The login page is very simple there is a login form which accepts a username and password.

<form action="" method="post">
<p><label>Username</label><input type="text" name="username" value=""  /></p>
<p><label>Password</label><input type="password" name="password" value=""  /></p>
<p><label></label><input type="submit" name="submit" value="Login"  /></p>
</form>

Once submitted the username and password are collected from the form then passed to a login method in the user class (I'll come to that shortly) if this returns true, they have logged in are are taken to the admin otherwise they are shown an error.

//process login form if submitted
if(isset($_POST['submit'])){

    $username = trim($_POST['username']);
    $password = trim($_POST['password']);

    if($user->login($username,$password)){ 

        //logged in return to index page
        header('Location: index.php');
        exit;


    } else {
        $message = '<p class="error">Wrong username or password</p>';
    }

}//end if submit

if(isset($message)){ echo $message; }

classes/class.user.php

The user class is used to login and logout users verify their password and create a hash of their password.

The first function that will get called as soon as the class is ran is an automatic function called __construct this method is passed a database connection this is then assigned to a variable within the class so all methods will have access to it.

private $db;

public function __construct($db){
    $this->db = $db; 
}

To check if a user is logged in a method is_logged_in() looks for a session called loggedin if its set and is true their is a logged in user and returns true otherwise is would return nothing.

public function is_logged_in(){
    if(isset($_SESSION['loggedin']) &amp;&amp; $_SESSION['loggedin'] == true){
        return true;
    }        
}

The get_user_hash method is used to get the columns from the database and return them.

private function get_user_hash($username){

    try {

        $stmt = $this->_db->prepare('SELECT MemberID, username, password FROM blog_members WHERE username = :username');
        $stmt->execute(array('username' => $username));

        return $stmt->fetch();

    } catch(PDOException $e) {
        echo '<p class="error">'.$e->getMessage().'</p>';
    }
}

To verify a hash, the below method is used. pass the password from the form and the hashed password from the database this should equal to 1 otherwise they do not match.

if($this->password_verify($password,$user['password']) == 1){
//match
}

In order to verify a password matched a password given on login the hashed password needs to be fetched from the database, the username is passed to the database and the hashed password is returned.

The login method expects the users username and password then the fetches users password based on their username from the get_user_hash method in order to use the verify_hash method. 
If a match is found a session is set and the method returns true.

public function login($username,$password){ 

    $hashed = $this->get_user_hash($username);

    if($this->password_verify($password,$hashed) == 1){

        $_SESSION['loggedin'] = true;
        $_SESSION['memberID'] = $user['memberID'];
        $_SESSION['username'] = $user['username'];
        return true;
    }       
}

admin/index.php

The blog posts are listed in a table, again using a query to select all records and display them ordered by the postID in descending order, then looped through to list all posts, each post has an edit and delete link the edit link passes the postID to edit-post.php in order to edit the selected post.
The delete link calls a javascript function (delpost) it expects the id of the post and the title of the post, when clicked the javascript function is executed which will run a confirmation popup asking to confirm to delete the post.

<table>
<tr>
    <th>Title</th>
    <th>Date</th>
    <th>Action</th>
</tr>
<?php
    try {

        $stmt = $db->query('SELECT postID, postTitle, postDate FROM blog_posts ORDER BY postID DESC');
        while($row = $stmt->fetch()){

            echo '<tr>';
            echo '<td>'.$row['postTitle'].'</td>';
            echo '<td>'.date('jS M Y', strtotime($row['postDate'])).'</td>';
            ?>

            <td>
                <a href="edit-post.php?id=<?php echo $row['postID'];?>">Edit</a> | 
                <a href="javascript:delpost('<?php echo $row['postID'];?>','<?php echo $row['postTitle'];?>')">Delete</a>
            </td>

            <?php 
            echo '</tr>';

        }

    } catch(PDOException $e) {
        echo $e->getMessage();
    }
?>
</table>

The javascript confirm function, once confirmed a command window.locaation.href is ran which will redirect the page, in this case it goes to index.php again but appends ?delpost= and the id of the post to be deleted, which in turn will execute a php function.

<script language="JavaScript" type="text/javascript">
function delpost(id, title)
{
  if (confirm("Are you sure you want to delete '" + title + "'"))
  {
      window.location.href = 'index.php?delpost=' + id;
  }
}
</script>

PHP Delete function

If the get request delpost has been sent then a prepared statement is ran to delete the post where the postID matches the id passed in the array. Then the page is reloaded passing a status to the url in indexphp?action= the action is used on the page to confirm the deletion.

if(isset($_GET['delpost'])){ 

    $stmt = $db->prepare('DELETE FROM blog_posts WHERE postID = :postID') ;
    $stmt->execute(array(':postID' => $_GET['delpost']));

    header('Location: index.php?action=deleted');
    exit;
}

if there has been an action passed in a $_GET request then display it.

if(isset($_GET['action'])){ 
    echo '<h3>Post '.$_GET['action'].'.</h3>'; 
}

There is an admin menu that will be display on every page, whilst this could be added to each page it makes more sense to add links to a separate file called menu.php that can be included into every file, that way any changes only need to be applied once.

<?php include('menu.php');?>

admin/menu.php

The menu is very simple for this site the links are inside a ul list, the view website links back to the root of the project by going back a directory using ../ in the href path.

<h1>Blog</h1>
<p>Logged in as <?=$_SESSION['username'];?></p>
<ul id='adminmenu'>
    <li><a href='index.php'>Blog</a></li>
    <li><a href='users.php'>Users</a></li>
    <li><a href="../" target="_blank">View Website</a></li>
    <li><a href='logout.php'>Logout</a></li>
</ul>
<div class='clear'></div>
<hr />

admin/add-post.php

The form to add a post is made up of input's and textareas, each section has a name which will become a variable in php when the form is submitted.
The forms also use what's called sticky forms meaning if validation fails then show all content entered into the form.

<form action='' method='post'>

    <p><label>Title</label><br />
    <input type='text' name='postTitle' value='<?php if(isset($error)){ echo $_POST['postTitle'];}?>'></p>

    <p><label>Description</label><br />
    <textarea name='postDesc' cols='60' rows='10'><?php if(isset($error)){ echo $_POST['postDesc'];}?></textarea></p>

    <p><label>Content</label><br />
    <textarea name='postCont' cols='60' rows='10'><?php if(isset($error)){ echo $_POST['postCont'];}?></textarea></p>

    <p><input type='submit' name='submit' value='Submit'></p>

</form>

For textarea's rather then making the admins enter the html for the text themselves its better to use an editor, I've chosen to use a popular one called TinyMCE To use it you would normally have to download the files from tinyMCE's website upload them and configure the config, thankfully they have recently released a CDN version so you can include tinyMCE by simply referencing the CDN and then your setup options:

This will convert all textarea's into editors.

<script src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
<script>
        tinymce.init({
            selector: "textarea",
            plugins: [
                "advlist autolink lists link image charmap print preview anchor",
                "searchreplace visualblocks code fullscreen",
                "insertdatetime media table contextmenu paste"
            ],
            toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
        });
</script>

To process the form data once its been submitted is a simple process first make sure the form has been submitted. Then remove any slashed in the $_POST array. Then extract all posts items inside $_POST by using extract($_POST) any post element is then accessible by using just its name so $_POST['postTitle'] becomes $postTitle.

Next validate the data, these are very basic validation rules. These can be improved upon, if any of the if statements are true then an error is needed, adding an error to an array called error is a simple way to collect multiple errors.

//if form has been submitted process it
if(isset($_POST['submit'])){

    $_POST = array_map( 'stripslashes', $_POST );

    //collect form data
    extract($_POST);

    //very basic validation
    if($postTitle ==''){
        $error[] = 'Please enter the title.';
    }

    if($postDesc ==''){
        $error[] = 'Please enter the description.';
    }

    if($postCont ==''){
        $error[] = 'Please enter the content.';
    }

Next if no error has been set then insert the data into the database, this is using prepared statements the place holders :postTitle, :postDesc etc are using to bind the matching array elements when execute to add the data into the correct columns. Once inserted the user is redirected back to the admin a action status is appended to the url ?action=added.

if(!isset($error)){

    try {

        //insert into database
        $stmt = $db->prepare('INSERT INTO blog_posts (postTitle,postDesc,postCont,postDate) VALUES (:postTitle, :postDesc, :postCont, :postDate)') ;
        $stmt->execute(array(
            ':postTitle' => $postTitle,
            ':postDesc' => $postDesc,
            ':postCont' => $postCont,
            ':postDate' => date('Y-m-d H:i:s')
        ));

        //redirect to index page
        header('Location: index.php?action=added');
        exit;

    } catch(PDOException $e) {
        echo $e->getMessage();
    }

}

If there has been any errors set then loop through the error array and display them.

if(isset($error)){
    foreach($error as $error){
        echo '<p class="error">'.$error.'</p>';
    }
}

admin/edit-post.php

This is very much like the add page except before the menu a query must be ran to select the correct post from the database to populate the form with.

The query needs to select the record where the postID matched the id passed in the $_GET['id'] request, as this can be manipulated a prepared statement is used.
The form also has an hidden field with the name postID and a value from the database this is used when updating the record to determine which post to make the changes to.

<?php 
try {

    $stmt = $db->prepare('SELECT postID, postTitle, postDesc, postCont FROM blog_posts WHERE postID = :postID');
    $stmt->execute(array(':postID' => $_GET['id']));
    $row = $stmt->fetch(); 

} catch(PDOException $e) {
    echo $e->getMessage();
}
?>

<form action='' method='post'>
    <input type='hidden' name='postID' value='<?php echo $row['postID'];?>'>

    <p><label>Title</label><br />
    <input type='text' name='postTitle' value='<?php echo $row['postTitle'];?>'></p>

    <p><label>Description</label><br />
    <textarea name='postDesc' cols='60' rows='10'><?php echo $row['postDesc'];?></textarea></p>

    <p><label>Content</label><br />
    <textarea name='postCont' cols='60' rows='10'><?php echo $row['postCont'];?></textarea></p>

    <p><input type='submit' name='submit' value='Update'></p>

</form>

When the form is submitted the same checks are done that were done on the add post page:

if(isset($_POST['submit'])){

    $_POST = array_map( 'stripslashes', $_POST );

    //collect form data
    extract($_POST);

    //very basic validation
    if($postID ==''){
        $error[] = 'This post is missing a valid id!.';
    }

    if($postTitle ==''){
        $error[] = 'Please enter the title.';
    }

    if($postDesc ==''){
        $error[] = 'Please enter the description.';
    }

    if($postCont ==''){
        $error[] = 'Please enter the content.';
    }

    if(!isset($error)){

To update the post a prepared statement is ran this time an update command is used updating the specified columns by the matching place holders/ executed array. Once completed the user is redirected to index with an action status.

try {

    //insert into database
    $stmt = $db->prepare('UPDATE blog_posts SET postTitle = :postTitle, postDesc = :postDesc, postCont = :postCont WHERE postID = :postID') ;
    $stmt->execute(array(
        ':postTitle' => $postTitle,
        ':postDesc' => $postDesc,
        ':postCont' => $postCont,
        ':postID' => $postID
    ));

    //redirect to index page
    header('Location: index.php?action=updated');
    exit;

} catch(PDOException $e) {
    echo $e->getMessage();
}

admin/users.php

This page lists all administrators of the blog.

While looping through the admins edit and delete links are created, the first admin will have an id of 1 this user should not be deleted, therefor an if statement is used to make sure the delete link is not shown for the first admin.

$stmt = $db->query('SELECT memberID, username, email FROM blog_members ORDER BY username');
while($row = $stmt->fetch()){

    echo '<tr>';
    echo '<td>'.$row['username'].'</td>';
    echo '<td>'.$row['email'].'</td>';
    ?>

    <td>
        <a href="edit-user.php?id=<?php echo $row['memberID'];?>">Edit</a> 
        <?php if($row['memberID'] != 1){?>
            | <a href="javascript:deluser('<?php echo $row['memberID'];?>','<?php echo $row['username'];?>')">Delete</a>
        <?php } ?>
    </td>

    <?php 
    echo '</tr>';

}

Deleting a user is the same as a post:

<script language="JavaScript" type="text/javascript">
function deluser(id, title)
{
  if (confirm("Are you sure you want to delete '" + title + "'"))
  {
      window.location.href = 'users.php?deluser=' + id;
  }
}
</script>
if(isset($_GET['deluser'])){ 

    //if user id is 1 ignore
    if($_GET['deluser'] !='1'){

        $stmt = $db->prepare('DELETE FROM blog_members WHERE memberID = :memberID') ;
        $stmt->execute(array(':memberID' => $_GET['deluser']));

        header('Location: users.php?action=deleted');
        exit;

    }
}

admin/add-user.php

Adding and editing users is very similar to posts I will go over only what's different.

Present the form to be filled in, notice the password have a type of password this stops the password being show in the form.

<form action='' method='post'>

    <p><label>Username</label><br />
    <input type='text' name='username' value='<?php if(isset($error)){ echo $_POST['username'];}?>'></p>

    <p><label>Password</label><br />
    <input type='password' name='password' value='<?php if(isset($error)){ echo $_POST['password'];}?>'></p>

    <p><label>Confirm Password</label><br />
    <input type='password' name='passwordConfirm' value='<?php if(isset($error)){ echo $_POST['passwordConfirm'];}?>'></p>

    <p><label>Email</label><br />
    <input type='text' name='email' value='<?php if(isset($error)){ echo $_POST['email'];}?>'></p>

    <p><input type='submit' name='submit' value='Add User'></p>

</form>

As part of processing the form new users will need a hash to be created from the user class this is done by passing the password from the form into the class object and use the create_hash method.
Next a normal insert statement is ran in the array the password is not used but instead the hashedpassword is given.

$hashedpassword = $user->create_hash($password);

try {

    //insert into database
    $stmt = $db->prepare('INSERT INTO blog_members (username,password,email) VALUES (:username, :password, :email)') ;
    $stmt->execute(array(
        ':username' => $username,
        ':password' => $hashedpassword,
        ':email' => $email
    ));

    //redirect to index page
    header('Location: users.php?action=added');
    exit;

} catch(PDOException $e) {
    echo $e->getMessage();
}

admin/edit-user.php

To edit a user first the user needs to be retrieved from the database using a prepared statement where the memberID matches the id passed in the get request.

The password fields are not populated, this are only filled in to change the password.

<?php
 try {

        $stmt = $db->prepare('SELECT memberID, username, email FROM blog_members WHERE memberID = :memberID') ;
        $stmt->execute(array(':memberID' => $_GET['id']));
        $row = $stmt->fetch(); 

    } catch(PDOException $e) {
        echo $e->getMessage();
    }
?>

<form action='' method='post'>
    <input type='hidden' name='memberID' value='<?php echo $row['memberID'];?>'>

    <p><label>Username</label><br />
    <input type='text' name='username' value='<?php echo $row['username'];?>'></p>

    <p><label>Password (only to change)</label><br />
    <input type='password' name='password' value=''></p>

    <p><label>Confirm Password</label><br />
    <input type='password' name='passwordConfirm' value=''></p>

    <p><label>Email</label><br />
    <input type='text' name='email' value='<?php echo $row['email'];?>'></p>

    <p><input type='submit' name='submit' value='Update User'></p>

</form>

 

When running validation the password checks should only run if a password has been entered.

if( strlen($password) > 0){

    if($password ==''){
        $error[] = 'Please enter the password.';
    }

    if($passwordConfirm ==''){
        $error[] = 'Please confirm the password.';
    }

    if($password != $passwordConfirm){
        $error[] = 'Passwords do not match.';
    }

}

When updating the database a check is made to see if the password has been set, if so then the password is updated otherwise another update is ran without updating the password.
When the password is to be updated a new hash is created from the user object.

if(isset($password)){

    $hashedpassword = $user->create_hash($password);

    //update into database
    $stmt = $db->prepare('UPDATE blog_members SET username = :username, password = :password, email = :email WHERE memberID = :memberID') ;
    $stmt->execute(array(
        ':username' => $username,
        ':password' => $hashedpassword,
        ':email' => $email,
        ':memberID' => $memberID
    ));


} else {

    //update database
    $stmt = $db->prepare('UPDATE blog_members SET username = :username, email = :email WHERE memberID = :memberID') ;
    $stmt->execute(array(
        ':username' => $username,
        ':email' => $email,
        ':memberID' => $memberID
    ));

}

That's all the notable differences the files in full are available in the download I'll also list the full files below:

Source Files

index.php

<?php require('includes/config.php'); ?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Blog</title>
    <link rel="stylesheet" href="style/normalize.css">
    <link rel="stylesheet" href="style/main.css">
</head>
<body>

    <div id="wrapper">

        <h1>Blog</h1>
        <hr />

        <?php
            try {

                $stmt = $db->query('SELECT postID, postTitle, postDesc, postDate FROM blog_posts ORDER BY postID DESC');
                while($row = $stmt->fetch()){

                    echo '<div>';
                        echo '<h1><a href="viewpost.php?id='.$row['postID'].'">'.$row['postTitle'].'</a></h1>';
                        echo '<p>Posted on '.date('jS M Y H:i:s', strtotime($row['postDate'])).'</p>';
                        echo '<p>'.$row['postDesc'].'</p>';                
                        echo '<p><a href="viewpost.php?id='.$row['postID'].'">Read More</a></p>';                
                    echo '</div>';

                }

            } catch(PDOException $e) {
                echo $e->getMessage();
            }
        ?>

    </div>


</body>
</html>

viewpost.php

<?php require('includes/config.php'); 

$stmt = $db->prepare('SELECT postID, postTitle, postCont, postDate FROM blog_posts WHERE postID = :postID');
$stmt->execute(array(':postID' => $_GET['id']));
$row = $stmt->fetch();

//if post does not exists redirect user.
if($row['postID'] == ''){
    header('Location: ./');
    exit;
}

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Blog - <?php echo $row['postTitle'];?></title>
    <link rel="stylesheet" href="style/normalize.css">
    <link rel="stylesheet" href="style/main.css">
</head>
<body>

    <div id="wrapper">

        <h1>Blog</h1>
        <hr />
        <p><a href="./">Blog Index</a></p>


        <?php    
            echo '<div>';
                echo '<h1>'.$row['postTitle'].'</h1>';
                echo '<p>Posted on '.date('jS M Y', strtotime($row['postDate'])).'</p>';
                echo '<p>'.$row['postCont'].'</p>';                
            echo '</div>';
        ?>

    </div>


</body>
</html>

includes/config.php

<?php
ob_start();
session_start();

//database credentials
define('DBHOST','localhost');
define('DBUSER','database username');
define('DBPASS','database password');
define('DBNAME','database name');

$db = new PDO("mysql:host=".DBHOST.";port=8889;dbname=".DBNAME, DBUSER, DBPASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


//set timezone
date_default_timezone_set('Europe/London');

//load classes as needed
function __autoload($class) {

   $class = strtolower($class);

    //if call from within /assets adjust the path
   $classpath = 'classes/class.'.$class . '.php';
   if ( file_exists($classpath)) {
      require_once $classpath;
    }     

    //if call from within admin adjust the path
   $classpath = '../classes/class.'.$class . '.php';
   if ( file_exists($classpath)) {
      require_once $classpath;
    }

    //if call from within admin adjust the path
   $classpath = '../../classes/class.'.$class . '.php';
   if ( file_exists($classpath)) {
      require_once $classpath;
    }         

}

$user = new User($db); 
?>

classes/class.user.php

<?php

class User{

    private $db;

    public function __construct($db){
        $this->db = $db; 
    }


    public function is_logged_in(){
        if(isset($_SESSION['loggedin']) &amp;&amp; $_SESSION['loggedin'] == true){
            return true;
        }        
    }

    public function create_hash($value)
    {
        return $hash = crypt($value, '$2a$12$'.substr(str_replace('+', '.', base64_encode(sha1(microtime(true), true))), 0, 22));
    }

    private function verify_hash($password,$hash)
    {
        return $hash == crypt($password, $hash);
    }

    private function get_user_hash($username){    

        try {

            //echo $this->create_hash('demo');

            $stmt = $this->db->prepare('SELECT password FROM blog_members WHERE username = :username');
            $stmt->execute(array('username' => $username));

            $row = $stmt->fetch();
            return $row['password'];

        } catch(PDOException $e) {
            echo '<p class="error">'.$e->getMessage().'</p>';
        }
    }


    public function login($username,$password){    

        $hashed = $this->get_user_hash($username);

        if($this->verify_hash($password,$hashed) == 1){

            $_SESSION['loggedin'] = true;
            return true;
        }        
    }


    public function logout(){
        session_destroy();
    }

}

?>

admin/index.php

<?php
//include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }

//show message from add / edit page
if(isset($_GET['delpost'])){ 

    $stmt = $db->prepare('DELETE FROM blog_posts WHERE postID = :postID') ;
    $stmt->execute(array(':postID' => $_GET['delpost']));

    header('Location: index.php?action=deleted');
    exit;
} 

?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
  <script language="JavaScript" type="text/javascript">
  function delpost(id, title)
  {
      if (confirm("Are you sure you want to delete '" + title + "'"))
      {
          window.location.href = 'index.php?delpost=' + id;
      }
  }
  </script>
</head>
<body>

    <div id="wrapper">

    <?php include('menu.php');?>

    <?php 
    //show message from add / edit page
    if(isset($_GET['action'])){ 
        echo '<h3>Post '.$_GET['action'].'.</h3>'; 
    } 
    ?>

    <table>
    <tr>
        <th>Title</th>
        <th>Date</th>
        <th>Action</th>
    </tr>
    <?php
        try {

            $stmt = $db->query('SELECT postID, postTitle, postDate FROM blog_posts ORDER BY postID DESC');
            while($row = $stmt->fetch()){

                echo '<tr>';
                echo '<td>'.$row['postTitle'].'</td>';
                echo '<td>'.date('jS M Y', strtotime($row['postDate'])).'</td>';
                ?>

                <td>
                    <a href="edit-post.php?id=<?php echo $row['postID'];?>">Edit</a> | 
                    <a href="javascript:delpost('<?php echo $row['postID'];?>','<?php echo $row['postTitle'];?>')">Delete</a>
                </td>

                <?php 
                echo '</tr>';

            }

        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    ?>
    </table>

    <p><a href='add-post.php'>Add Post</a></p>

</div>

</body>
</html>

admin/login.php

<?php
//include config
require_once('../includes/config.php');


//check if already logged in
if( $user->is_logged_in() ){ header('Location: index.php'); } 
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin Login</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
</head>
<body>

<div id="login">

    <?php

    //process login form if submitted
    if(isset($_POST['submit'])){

        $username = trim($_POST['username']);
        $password = trim($_POST['password']);

        if($user->login($username,$password)){ 

            //logged in return to index page
            header('Location: index.php');
            exit;


        } else {
            $message = '<p class="error">Wrong username or password</p>';
        }

    }//end if submit

    if(isset($message)){ echo $message; }
    ?>

    <form action="" method="post">
    <p><label>Username</label><input type="text" name="username" value=""  /></p>
    <p><label>Password</label><input type="password" name="password" value=""  /></p>
    <p><label></label><input type="submit" name="submit" value="Login"  /></p>
    </form>

</div>
</body>
</html>

admin/logout.php

<?php
//include config
require_once('../includes/config.php');

//log user out
$user->logout();
header('Location: index.php'); 

?>

admin/menu.php

<h1>Blog</h1>
<ul id='adminmenu'>
    <li><a href='index.php'>Blog</a></li>
    <li><a href='users.php'>Users</a></li>
    <li><a href="../" target="_blank">View Website</a></li>
    <li><a href='logout.php'>Logout</a></li>
</ul>
<div class='clear'></div>
<hr />

admin/add-post.php

<?php //include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin - Add Post</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
  <script src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
  <script>
          tinymce.init({
              selector: "textarea",
              plugins: [
                  "advlist autolink lists link image charmap print preview anchor",
                  "searchreplace visualblocks code fullscreen",
                  "insertdatetime media table contextmenu paste"
              ],
              toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
          });
  </script>
</head>
<body>

<div id="wrapper">

    <?php include('menu.php');?>
    <p><a href="./">Blog Admin Index</a></p>

    <h2>Add Post</h2>

    <?php

    //if form has been submitted process it
    if(isset($_POST['submit'])){

        $_POST = array_map( 'stripslashes', $_POST );

        //collect form data
        extract($_POST);

        //very basic validation
        if($postTitle ==''){
            $error[] = 'Please enter the title.';
        }

        if($postDesc ==''){
            $error[] = 'Please enter the description.';
        }

        if($postCont ==''){
            $error[] = 'Please enter the content.';
        }

        if(!isset($error)){

            try {

                //insert into database
                $stmt = $db->prepare('INSERT INTO blog_posts (postTitle,postDesc,postCont,postDate) VALUES (:postTitle, :postDesc, :postCont, :postDate)') ;
                $stmt->execute(array(
                    ':postTitle' => $postTitle,
                    ':postDesc' => $postDesc,
                    ':postCont' => $postCont,
                    ':postDate' => date('Y-m-d H:i:s')
                ));

                //redirect to index page
                header('Location: index.php?action=added');
                exit;

            } catch(PDOException $e) {
                echo $e->getMessage();
            }

        }

    }

    //check for any errors
    if(isset($error)){
        foreach($error as $error){
            echo '<p class="error">'.$error.'</p>';
        }
    }
    ?>

    <form action='' method='post'>

        <p><label>Title</label><br />
        <input type='text' name='postTitle' value='<?php if(isset($error)){ echo $_POST['postTitle'];}?>'></p>

        <p><label>Description</label><br />
        <textarea name='postDesc' cols='60' rows='10'><?php if(isset($error)){ echo $_POST['postDesc'];}?></textarea></p>

        <p><label>Content</label><br />
        <textarea name='postCont' cols='60' rows='10'><?php if(isset($error)){ echo $_POST['postCont'];}?></textarea></p>

        <p><input type='submit' name='submit' value='Submit'></p>

    </form>

</div>

admin/edit-post.php

<?php //include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin - Edit Post</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
  <script src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
  <script>
          tinymce.init({
              selector: "textarea",
              plugins: [
                  "advlist autolink lists link image charmap print preview anchor",
                  "searchreplace visualblocks code fullscreen",
                  "insertdatetime media table contextmenu paste"
              ],
              toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
          });
  </script>
</head>
<body>

<div id="wrapper">

    <?php include('menu.php');?>
    <p><a href="./">Blog Admin Index</a></p>

    <h2>Edit Post</h2>


    <?php

    //if form has been submitted process it
    if(isset($_POST['submit'])){

        $_POST = array_map( 'stripslashes', $_POST );

        //collect form data
        extract($_POST);

        //very basic validation
        if($postID ==''){
            $error[] = 'This post is missing a valid id!.';
        }

        if($postTitle ==''){
            $error[] = 'Please enter the title.';
        }

        if($postDesc ==''){
            $error[] = 'Please enter the description.';
        }

        if($postCont ==''){
            $error[] = 'Please enter the content.';
        }

        if(!isset($error)){

            try {

                //insert into database
                $stmt = $db->prepare('UPDATE blog_posts SET postTitle = :postTitle, postDesc = :postDesc, postCont = :postCont WHERE postID = :postID') ;
                $stmt->execute(array(
                    ':postTitle' => $postTitle,
                    ':postDesc' => $postDesc,
                    ':postCont' => $postCont,
                    ':postID' => $postID
                ));

                //redirect to index page
                header('Location: index.php?action=updated');
                exit;

            } catch(PDOException $e) {
                echo $e->getMessage();
            }

        }

    }

    ?>


    <?php
    //check for any errors
    if(isset($error)){
        foreach($error as $error){
            echo $error.'<br />';
        }
    }

        try {

            $stmt = $db->prepare('SELECT postID, postTitle, postDesc, postCont FROM blog_posts WHERE postID = :postID') ;
            $stmt->execute(array(':postID' => $_GET['id']));
            $row = $stmt->fetch(); 

        } catch(PDOException $e) {
            echo $e->getMessage();
        }

    ?>

    <form action='' method='post'>
        <input type='hidden' name='postID' value='<?php echo $row['postID'];?>'>

        <p><label>Title</label><br />
        <input type='text' name='postTitle' value='<?php echo $row['postTitle'];?>'></p>

        <p><label>Description</label><br />
        <textarea name='postDesc' cols='60' rows='10'><?php echo $row['postDesc'];?></textarea></p>

        <p><label>Content</label><br />
        <textarea name='postCont' cols='60' rows='10'><?php echo $row['postCont'];?></textarea></p>

        <p><input type='submit' name='submit' value='Update'></p>

    </form>

</div>

</body>
</html>

admin/users.php

<?php
//include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }

//show message from add / edit page
if(isset($_GET['deluser'])){ 

    //if user id is 1 ignore
    if($_GET['deluser'] !='1'){

        $stmt = $db->prepare('DELETE FROM blog_members WHERE memberID = :memberID') ;
        $stmt->execute(array(':memberID' => $_GET['deluser']));

        header('Location: users.php?action=deleted');
        exit;

    }
} 

?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin - Users</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
  <script language="JavaScript" type="text/javascript">
  function deluser(id, title)
  {
      if (confirm("Are you sure you want to delete '" + title + "'"))
      {
          window.location.href = 'users.php?deluser=' + id;
      }
  }
  </script>
</head>
<body>

    <div id="wrapper">

    <?php include('menu.php');?>

    <?php 
    //show message from add / edit page
    if(isset($_GET['action'])){ 
        echo '<h3>User '.$_GET['action'].'.</h3>'; 
    } 
    ?>

    <table>
    <tr>
        <th>Username</th>
        <th>Email</th>
        <th>Action</th>
    </tr>
    <?php
        try {

            $stmt = $db->query('SELECT memberID, username, email FROM blog_members ORDER BY username');
            while($row = $stmt->fetch()){

                echo '<tr>';
                echo '<td>'.$row['username'].'</td>';
                echo '<td>'.$row['email'].'</td>';
                ?>

                <td>
                    <a href="edit-user.php?id=<?php echo $row['memberID'];?>">Edit</a> 
                    <?php if($row['memberID'] != 1){?>
                        | <a href="javascript:deluser('<?php echo $row['memberID'];?>','<?php echo $row['username'];?>')">Delete</a>
                    <?php } ?>
                </td>

                <?php 
                echo '</tr>';

            }

        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    ?>
    </table>

    <p><a href='add-user.php'>Add User</a></p>

</div>

</body>
</html>

admin/add-user.php

<?php //include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin - Add User</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
</head>
<body>

<div id="wrapper">

    <?php include('menu.php');?>
    <p><a href="users.php">User Admin Index</a></p>

    <h2>Add User</h2>

    <?php

    //if form has been submitted process it
    if(isset($_POST['submit'])){

        //collect form data
        extract($_POST);

        //very basic validation
        if($username ==''){
            $error[] = 'Please enter the username.';
        }

        if($password ==''){
            $error[] = 'Please enter the password.';
        }

        if($passwordConfirm ==''){
            $error[] = 'Please confirm the password.';
        }

        if($password != $passwordConfirm){
            $error[] = 'Passwords do not match.';
        }

        if($email ==''){
            $error[] = 'Please enter the email address.';
        }

        if(!isset($error)){

            $hashedpassword = $user->create_hash($password);

            try {

                //insert into database
                $stmt = $db->prepare('INSERT INTO blog_members (username,password,email) VALUES (:username, :password, :email)') ;
                $stmt->execute(array(
                    ':username' => $username,
                    ':password' => $hashedpassword,
                    ':email' => $email
                ));

                //redirect to index page
                header('Location: users.php?action=added');
                exit;

            } catch(PDOException $e) {
                echo $e->getMessage();
            }

        }

    }

    //check for any errors
    if(isset($error)){
        foreach($error as $error){
            echo '<p class="error">'.$error.'</p>';
        }
    }
    ?>

    <form action='' method='post'>

        <p><label>Username</label><br />
        <input type='text' name='username' value='<?php if(isset($error)){ echo $_POST['username'];}?>'></p>

        <p><label>Password</label><br />
        <input type='password' name='password' value='<?php if(isset($error)){ echo $_POST['password'];}?>'></p>

        <p><label>Confirm Password</label><br />
        <input type='password' name='passwordConfirm' value='<?php if(isset($error)){ echo $_POST['passwordConfirm'];}?>'></p>

        <p><label>Email</label><br />
        <input type='text' name='email' value='<?php if(isset($error)){ echo $_POST['email'];}?>'></p>

        <p><input type='submit' name='submit' value='Add User'></p>

    </form>

</div>

admin/edit-user.php

<?php //include config
require_once('../includes/config.php');

//if not logged in redirect to login page
if(!$user->is_logged_in()){ header('Location: login.php'); }
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Admin - Edit User</title>
  <link rel="stylesheet" href="../style/normalize.css">
  <link rel="stylesheet" href="../style/main.css">
</head>
<body>

<div id="wrapper">

    <?php include('menu.php');?>
    <p><a href="users.php">User Admin Index</a></p>

    <h2>Edit User</h2>


    <?php

    //if form has been submitted process it
    if(isset($_POST['submit'])){

        //collect form data
        extract($_POST);

        //very basic validation
        if($username ==''){
            $error[] = 'Please enter the username.';
        }

        if( strlen($password) > 0){

            if($password ==''){
                $error[] = 'Please enter the password.';
            }

            if($passwordConfirm ==''){
                $error[] = 'Please confirm the password.';
            }

            if($password != $passwordConfirm){
                $error[] = 'Passwords do not match.';
            }

        }


        if($email ==''){
            $error[] = 'Please enter the email address.';
        }

        if(!isset($error)){

            try {

                if(isset($password)){

                    $hashedpassword = $user->create_hash($password);

                    //update into database
                    $stmt = $db->prepare('UPDATE blog_members SET username = :username, password = :password, email = :email WHERE memberID = :memberID') ;
                    $stmt->execute(array(
                        ':username' => $username,
                        ':password' => $hashedpassword,
                        ':email' => $email,
                        ':memberID' => $memberID
                    ));


                } else {

                    //update database
                    $stmt = $db->prepare('UPDATE blog_members SET username = :username, email = :email WHERE memberID = :memberID') ;
                    $stmt->execute(array(
                        ':username' => $username,
                        ':email' => $email,
                        ':memberID' => $memberID
                    ));

                }


                //redirect to index page
                header('Location: users.php?action=updated');
                exit;

            } catch(PDOException $e) {
                echo $e->getMessage();
            }

        }

    }

    ?>


    <?php
    //check for any errors
    if(isset($error)){
        foreach($error as $error){
            echo $error.'<br />';
        }
    }

        try {

            $stmt = $db->prepare('SELECT memberID, username, email FROM blog_members WHERE memberID = :memberID') ;
            $stmt->execute(array(':memberID' => $_GET['id']));
            $row = $stmt->fetch(); 

        } catch(PDOException $e) {
            echo $e->getMessage();
        }

    ?>

    <form action='' method='post'>
        <input type='hidden' name='memberID' value='<?php echo $row['memberID'];?>'>

        <p><label>Username</label><br />
        <input type='text' name='username' value='<?php echo $row['username'];?>'></p>

        <p><label>Password (only to change)</label><br />
        <input type='password' name='password' value=''></p>

        <p><label>Confirm Password</label><br />
        <input type='password' name='passwordConfirm' value=''></p>

        <p><label>Email</label><br />
        <input type='text' name='email' value='<?php echo $row['email'];?>'></p>

        <p><input type='submit' name='submit' value='Update User'></p>

    </form>

</div>

</body>
</html>

 

Did you find this article valuable?

Support David Carr by becoming a sponsor. Any amount is appreciated!