Powerful Chat System – Lesson 3

Powerful Chat System – Lesson 3

Powerful Chat System

Powerful Chat System – Lesson 3

Today we continue a series of articles on the creation of powerful chat system. Our third lesson will contain next elements: join form (with html5 validation). A long ago, I have already made similar validation. So you can refresh your memories if you like.

Today I will publish updated sources of our growing project (several files was modified, plus I expanded structure of database slightly). All project is well structured: system classes is in ‘classes’ folder, stylesheets in ‘css’ folder, template files in ‘templates’ folder, and – new folder ‘js’ for javascript files.

Live Demo
download in package

Now – download the source files and lets start coding !


Step 1. SQL

I have added new field into table of profiles where we will store its first names. So, please execute next SQL:

ALTER TABLE `cs_profiles` ADD `first_name` varchar(255) NOT NULL default '' AFTER `name`;

Step 2. HTML

I have updated template with login form. As you can see – I have expanded this markup and added join form here:

templates/logout_form.html

<div class="column">
    <h3>Powerful Chat System Demonstration</h3>
    <p>Our chat will contain next features: registration, main chat, profiles with avatars, ranking system, private messaging, custom rooms, moderation / administration and possible something else (in future).</p>
</div>
<div class="column">
    <div class="tabs_container">
        <ul class="tabs">
            <li class="active"><h3>Log In</h3></li>
            <li><h3>Join</h3></li>
        </ul>
    </div>
    <div class="nav_container">
        <form class="login_form" action="index.php" method="post" id="1">
            <label>Username:</label><input type="text" name="username">
            <label>Password:</label><input type="password" name="password">
            <input type="submit" name="Login" value="Login">
        </form>
        <form class="join_form" action="index.php" method="post" id="2" style="display: none;">
            <label>Full name:</label>
            <input type="text" id="username" name="username" placeholder="Your name" maxlength="128" title="Your name" required>
            <label>First name:</label>
            <input type="text" id="firstname" name="firstname" placeholder="Your first name" maxlength="128" title="Your first name">
            <label>Last name:</label>
            <input type="text" id="lastname" name="lastname" placeholder="Your last name" maxlength="128" title="Your last name">
            <label>Email:</label>
            <input type="text" id="email" name="email" placeholder="Your email" maxlength="128" title="Format: *@gmail.com" pattern=".*@gmail\.com" required>
            <label>Password:</label>
            <input type="text" id="password" name="password" maxlength="128" title="Password" required>
            <label>Repeat Password:</label>
            <input type="text" id="password" name="password" maxlength="128" title="Repeat password" required oninput="checkpass(this)">
            <input type="submit" name="Join" value="Join">
        </form>
    </div>
</div>
<script>
$(document).ready(function(){
    $('.tabs li h3').click(function () {
      $('.tabs li').removeClass('active');
      $(this).parent().addClass('active');
      $('.nav_container form').hide();
      var index = $('.tabs li h3').index(this);
      $('.nav_container form').eq(index).show();
      return false;
    });
});
function checkpass(input) {
    if (input.value != document.getElementById('password').value) {
        input.setCustomValidity('Password must match.');
    } else {
        input.setCustomValidity('');
    }
}
</script>

Please pay attention to HTML structure of join form, it contain several new attributes for html5 validation: placeholder, required and pattern. This is pretty interesting.

Step 3. CSS

This file has updated too

css/main.css

/* page layout */
*{
    margin:0;
    padding:0;
}
body {
    background-color:#eee;
    color:#fff;
    font:14px/1.3 Arial,sans-serif;
}
header {
    background-color:#212121;
    box-shadow: 0 -1px 2px #111111;
    display:block;
    height:70px;
    position:relative;
    width:100%;
    z-index:100;
}
header h2{
    font-size:22px;
    font-weight:normal;
    left:50%;
    margin-left:-400px;
    padding:22px 0;
    position:absolute;
    width:540px;
}
header a.stuts,a.stuts:visited{
    border:none;
    text-decoration:none;
    color:#fcfcfc;
    font-size:14px;
    left:50%;
    line-height:31px;
    margin:23px 0 0 110px;
    position:absolute;
    top:0;
}
header .stuts span {
    font-size:22px;
    font-weight:bold;
    margin-left:5px;
}
/* main styles */
.container {
    background-color: #222;
    color: #bbb;
    margin: 20px auto;
    overflow: hidden;
    padding: 20px;
    position: relative;
    width: 800px;
}
.container h2 {
    color: #fff;
    margin-bottom: 10px;
}
.column {
    float: left;
    width: 48%;
}
.column:first-child {
    margin-right: 4%;
}
.column > * {
    color: #ddd;
    margin-bottom: 10px;
}
/* tabs section */
.tabs_container {
    margin: 0;
}
.tabs {
    overflow: hidden;
}
.tabs li {
    float: left;
    list-style: none;
}
.tabs li h3:first-child {
    margin-left: 10px
}
.tabs li h3 {
    border: 1px solid #ddd;
    border-bottom-width: 0;
    display: block;
    margin: 0 2px 0 0;
    padding: 6px 10px 4px
}
.tabs li.active h3 {
    background-color: #555;
    border: 1px solid #ddd;
    border-bottom-width: 0;
    -moz-border-radius: 4px 4px 0 0;
    -ms-border-radius: 4px 4px 0 0;
    -o-border-radius: 4px 4px 0 0;
    -webkit-border-radius: 4px 4px 0 0;
    border-radius: 4px 4px 0 0;
}
.tabs li h3:hover {
    background-color: #666;
    cursor: pointer;
}
.tabs li.active h3:hover {
    background-color: #555;
    cursor: normal;
}
.nav_container form {
    background-color: #555;
    display: block;
    padding: 15px;
}
.column h3 {
    color: #fff;
}
.login_form input,.login_form label,
.join_form input,.join_form label {
    display: block;
    margin-bottom: 10px;
}
input[type=text], input[type=password], input[type=submit] {
    -moz-border-radius: 5px;
    -ms-border-radius: 5px;
    -o-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
}
input[type=text], input[type=password] {
    font-size: 16px;
    height: 25px;
    margin-right: 10px;
    width: 200px;
}
input[type=submit]{
    cursor: pointer;
    font-size: 16px;
    font-weight: bold;
    height: 35px;
    padding: 5px;
}
/* chat block */
.chat_messages {
    border: 1px solid #888;
    box-shadow: 0 0 5px #AAA;
    color: #000;
    padding: 10px;
}
.chat_messages h2 {
    color: #fff;
}
.chat_messages .message {
    background-color: #fff;
    margin: 5px;
    padding: 5px;
    -moz-border-radius: 5px;
    -ms-border-radius: 5px;
    -o-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
}
.chat_messages .message span {
    color: #444;
    font-size: 10px;
    margin-left: 10px;
}
.chat_submit_form {
    margin: 10px 0px;
}
.chat_submit_form div {
    float: left;
    width: 49%;
}
.chat_submit_form .error, .chat_submit_form .success, .chat_submit_form .protect {
    display: none;
}
.chat_submit_form .error {
    color: #f55;
}
.chat_submit_form .success {
    color: #5f5;
}
.chat_submit_form .protect {
    color: #55f;
}

Step 4. PHP

Now, lets review php sources. Our index.php file has updated. I have added here join processing functional. Before actual joining, we have to check if someone else was joined with same email (this field should be unique of course)

index.php

<?php
// set error reporting level
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);
require_once('classes/Services_JSON.php');
require_once('classes/CMySQL.php'); // including service class to work with database
require_once('classes/CLogin.php'); // including service class to work with login processing
$sErrors = '';
// join processing
if (! isset($_SESSION['member_id']) && $_POST['Join'] == 'Join') {
    $sUsername = $GLOBALS['MySQL']->escape($_POST['username']);
    $sFirstname = $GLOBALS['MySQL']->escape($_POST['firstname']);
    $sLastname = $GLOBALS['MySQL']->escape($_POST['lastname']);
    $sEmail = $GLOBALS['MySQL']->escape($_POST['email']);
    $sPassword = $GLOBALS['MySQL']->escape($_POST['password']);
    if ($sUsername && $sEmail && $sPassword) {
        // check if already exist
        $aProfile = $GLOBALS['MySQL']->getRow("SELECT * FROM `cs_profiles` WHERE `email`='{$sEmail}'");
        if ($aProfile['id'] > 0) {
            $sErrors = '<h2>Another profile with same email already exist</h2>';
        } else {
            // generate Salt and Cached password
            $sSalt = 'testing'; // TODO - we will add generation of salt in future
            $sPass = sha1(md5($sPassword) . $sSalt);
            // add new member into database
            $sSQL = "
                INSERT INTO `cs_profiles` SET
                `name` = '{$sUsername}',
                `first_name` = '{$sFirstname}',
                `last_name` = '{$sLastname}',
                `email` = '{$sEmail}',
                `password` = '{$sPass}',
                `salt` = '{$sSalt}',
                `status` = 'active',
                `role` = '1',
                `date_reg` = NOW();
            ";
            $GLOBALS['MySQL']->res($sSQL);
            // autologin
            $GLOBALS['CLogin']->performLogin($sUsername, $sPassword);
        }
    }
}
// login system init and generation code
$sLoginForm = $GLOBALS['CLogin']->getLoginBox();
$sChat = '<h2>You do not have rights to use chat</h2>';
$sInput = '';
if ($_SESSION['member_id'] && $_SESSION['member_status'] == 'active' && $_SESSION['member_role']) {
    require_once('classes/CChat.php'); // including service class to work with chat
    // get last messages
    $sChat = $GLOBALS['MainChat']->getMessages();
    if ($_GET['action'] == 'get_last_messages') { // regular updating of messages in chat
        $oJson = new Services_JSON();
        header('Content-type: application/json');
        echo $oJson->encode(array('messages' => $sChat));
        exit;
    }
    // get input form
    $sInput = $GLOBALS['MainChat']->getInputForm();
    if ($_POST['message']) { // POST-ing of message
        $iRes = $GLOBALS['MainChat']->acceptMessage();
        $oJson = new Services_JSON();
        header('Content-type: application/json');
        echo $oJson->encode(array('result' => $iRes));
        exit;
    }
}
// draw common page
echo strtr(file_get_contents('templates/main_page.html'), array('{form}' => $sLoginForm . $sErrors, '{chat}' => $sChat, '{input}' => $sInput));

As you can see – I have added chat functionality today.

classes/CLogin.php

<?php
class CLogin {
    // constructor
    function CLogin() {
        session_start();
    }
    // get login box function
    function getLoginBox() {
        if (isset($_GET['logout'])) { // logout processing
            if (isset($_SESSION['member_name']) && isset($_SESSION['member_pass']))
                $this->performLogout();
        }
        if ($_POST && $_POST['Login'] == 'Login' && $_POST['username'] && $_POST['password']) { // login processing
            if ($this->checkLogin($_POST['username'], $_POST['password'], false)) { // successful login
                $this->performLogin($_POST['username'], $_POST['password']);
                header( "Location:{$_SERVER['REQUEST_URI']}" );
                exit;
            } else { // wrong login
                return file_get_contents('templates/login_form.html') . '<h2>Username or Password is incorrect</h2>';
            }
        } else { // in case if we already logged (on refresh page):
            if (isset($_SESSION['member_name']) && $_SESSION['member_name'] && $_SESSION['member_pass']) {
                $aReplaces = array(
                    '{name}' => $_SESSION['member_name'],
                    '{status}' => $_SESSION['member_status'],
                    '{role}' => $_SESSION['member_role'],
                );
                return strtr(file_get_contents('templates/logout_form.html'), $aReplaces);
            }
            // otherwise - draw login form
            return file_get_contents('templates/login_form.html');
        }
    }
    // perform login
    function performLogin($sName, $sPass) {
        $this->performLogout();
        // make variables safe
        $sName = $GLOBALS['MySQL']->escape($sName);
        $aProfile = $GLOBALS['MySQL']->getRow("SELECT * FROM `cs_profiles` WHERE `name`='{$sName}'");
        // $sPassEn = $aProfile['password'];
        $iPid = $aProfile['id'];
        $sSalt = $aProfile['salt'];
        $sStatus = $aProfile['status'];
        $sRole = $aProfile['role'];
        $sPass = sha1(md5($sPass) . $sSalt);
        $_SESSION['member_id'] = $iPid;
        $_SESSION['member_name'] = $sName;
        $_SESSION['member_pass'] = $sPass;
        $_SESSION['member_status'] = $sStatus;
        $_SESSION['member_role'] = $sRole;
    }
    // perform logout
    function performLogout() {
        unset($_SESSION['member_id']);
        unset($_SESSION['member_name']);
        unset($_SESSION['member_pass']);
        unset($_SESSION['member_status']);
        unset($_SESSION['member_role']);
    }
    // check login
    function checkLogin($sName, $sPass, $isHash = true) {
        // make variables safe
        $sName = $GLOBALS['MySQL']->escape($sName);
        $sPass = $GLOBALS['MySQL']->escape($sPass);
        $aProfile = $GLOBALS['MySQL']->getRow("SELECT * FROM `cs_profiles` WHERE `name`='{$sName}'");
        $sPassEn = $aProfile['password'];
        if ($sName && $sPass && $sPassEn) {
            if (! $isHash) {
                $sSalt = $aProfile['salt'];
                $sPass = sha1(md5($sPass) . $sSalt);
            }
            return ($sPass == $sPassEn);
        }
        return false;
    }
}
$GLOBALS['CLogin'] = new CLogin();

Minor changes were made in this file too. The rest of the functional was not changed. We will continue enhance our chat soon.


Live Demo
download in archive

Conclusion

I hope that our new series of articles of chat system creation is useful and interesting for you. If you want to share your ideas, or you noticed any weakness – don’t hesitate to contact us. Good luck and welcome back!

SIMILAR ARTICLES

Polaroid gallery

0 23875

NO COMMENTS

Leave a Reply