Главная > Чаты > Мощная система для организации чата - Часть 2

Мощная система для организации чата - Часть 2


3 февраля 2012, 16:50. Разместил: Design FactoRy
Сегодня мы продолжим нашу работу над чатом. Наш второй урок будет посвящен следующим элементам: smart-чату (ajax, фильтрация спама – предотвращение отправки множественных сообщений). Регистрацию мы решили отложить до следующего урока. Надеемся, что вы не сильно расстроитесь.

Мощная система для организации чата - Часть 2

Сегодня мы будем работать над процессором чата, а также внесем некоторые изменения в код, который мы написали в предыдущем уроке. Весь проект у нас аккуратно структурирован: системные классы будут в папке «classes», таблицы стилей в папке «css», файлы шаблона в папке «templates», и сегодня мы добавим новую папку «js» для, как вы уже догадались, файлов javascript.

Пока что скачивайте исходник, и дальше мы можем приступать.

Этап 1 - SQL

В эту таблицу мы внесем незначительные изменения. Мы изменили тип поля «when».

CREATE TABLE `cs_messages` (
`id` int(11) unsigned NOT NULL auto_increment,
`sender` int(11) unsigned NOT NULL,
`recipient` int(11) unsigned NOT NULL default '0',
`message` VARCHAR(255) NOT NULL,
`when` int(11) NOT NULL default '0',
`room` int(5) unsigned NOT NULL default '0',
`type` tinyint(1) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Этап 2 - HTML-код

Здесь у нас html-разметка основной страницы (index):

templates/main_page.html

<!DOCTYPE html>
<html lang="en" >
<head>
<title>Powerful Chat System - Lesson 2</title>
<link href="css/main.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<header>
<h2>Powerful Chat System - Lesson 2</h2>
<a href="http://www.script-tutorials.com/powerful-chat-system-lesson-2/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
</header>
<div class="container">
{form}
</div>
<div class="container">
<h2>Main Chat Block</h2>
<div class="chat_messages">
{chat}
</div>
{input}
</div>
</body>
</html>

Как видно, мы изменили второй контейнер (который содержит наш чат на ajax).

templates/login_form.html и templates/logout_form.html

Эти файлы не претерпели изменений. Дальше давайте рассмотрим один новый шаблон – форму нашего чата:

templates/chat.html

<form class="chat_submit_form">
<div><input type="text" name="message" /><input type="submit" value="Submit" name="Submit" /></div>
<div>
<h3 class="error">Some error occurs during sending message</h3>
<h3 class="success">Message successfully sent</h3>
<h3 class="protect">Please wait 5 secs before adding next message</h3>
</div>
</form>
<script src="js/chat.js"></script>

Этап 3 - CSS-код

Данный файл содержит обновленные стили нашего чата

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;
}
.column h3 {
color: #fff;
}
.login_form input,.login_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: 30px;
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;
}

Этап 4 - JS

Мы создали первый js-файл для нашего чата. В самом начале он содержит функцию для регулярного обновления сообщений, а также контроллер для формы подтверждения.

js/chat.js

$(function() {
getMessages = function() {
$.getJSON('index.php?action=get_last_messages', function(data){
if (data.messages) {
$('.chat_messages').html(data.messages);
}

// get recent chat messages in loop
setTimeout(function(){
getMessages();
}, 5000);
});
}
getMessages();

$('.chat_submit_form').submit(function() {
$.post('index.php', { message: $('.chat_submit_form input[name=message]').val() },
function(data){
if (data.result == 1) {
$('.chat_submit_form .success').fadeIn('slow', function () {
$(this).delay(1000).fadeOut('slow');
});
} else if (data.result == 2) {
$('.chat_submit_form .protect').fadeIn('slow', function () {
$(this).delay(1000).fadeOut('slow');
});
} else {
$('.chat_submit_form .error').fadeIn('slow', function () {
$(this).delay(1000).fadeOut('slow');
});
}
}
);
return false;
});
});

Этап 5 - PHP-код

Теперь давайте рассмотрим исходный код PHP:

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

// 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, '{chat}' => $sChat, '{input}' => $sInput));

Как видно, сегодня мы добавили сюда функционал чата.

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['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();

Данный класс также был обновлен. Мы решили добавить в сессии и 'member_id'. Позже он будет полезен нам.

classes/CMySQL.php

Это класс работы с базой данных. Он есть в архиве. Обратите внимание, настройки базы данных для нашего проекта находятся в этом файле.

classes/Services_JSON.php

Это класс работы с JSON. Он есть в архиве.

classes/CChat.php

<?php

class CChat {

// constructor
function CChat() {}

// add to DB message
function acceptMessage() {
$sName = $GLOBALS['MySQL']->escape($_SESSION['member_name']);
$iPid = (int)$_SESSION['member_id'];
$sMessage = $GLOBALS['MySQL']->escape($_POST['message']);

if ($iPid && $sName != '' && $sMessage != '') {
$sSQL = "
SELECT `id`
FROM `cs_messages`
WHERE `sender` = '{$iPid}' AND UNIX_TIMESTAMP( ) - `when` < 5
LIMIT 1
";
$iLastId = $GLOBALS['MySQL']->getOne($sSQL);
if ($iLastId) return 2; // as protection from very often messages

$bRes = $GLOBALS['MySQL']->res("INSERT INTO `cs_messages` SET `sender` = '{$iPid}', `message` = '{$sMessage}', `when` = UNIX_TIMESTAMP()");
return ($bRes) ? 1 : 3;
}
}

// return input text form
function getInputForm() {
return file_get_contents('templates/chat.html');
}

// get last 10 messages
function getMessages() {
$sSQL = "
SELECT `a` . * , `cs_profiles`.`name` , UNIX_TIMESTAMP( ) - `a`.`when` AS 'diff'
FROM `cs_messages` AS `a`
INNER JOIN `cs_profiles` ON `cs_profiles`.`id` = `a`.`sender`
ORDER BY `a`.`id` DESC
LIMIT 10
";
$aMessages = $GLOBALS['MySQL']->getAll($sSQL);
asort($aMessages);

// create list of messages
$sMessages = '';
foreach ($aMessages as $i => $aMessage) {
$sExStyles = $sExJS = '';
$iDiff = (int)$aMessage['diff'];
if ($iDiff < 7) { // less than 7 seconds
$sExStyles = 'style="display:none;"';
$sExJS = "<script> $('#message_{$aMessage['id']}').fadeIn('slow'); </script>";
}

$sWhen = date("H:i:s", $aMessage['when']);
$sMessages .= '<div class="message" id="message_'.$aMessage['id'].'" '.$sExStyles.'><b>' . $aMessage['name'] . ':</b> ' . $aMessage['message'] . '<span>(' . $sWhen . ')</span></div>' . $sExJS;
}
return $sMessages;
}
}

$GLOBALS['MainChat'] = new CChat();

Здесь у нас новый класс для процессора нашего чата. На данный момент он содержит всего 3 функции: acceptMessage, getInputForm и getMessages.

Посмотреть демо | Скачать архив
Внимание! У вас нет прав для просмотра скрытого текста.


Заключение

Надеемся, что наше руководство будет полезным для вас, и вы используете его в своих будущих проектах. Удачи! И не забываем следить за новостями!
Вернуться назад