Today we continue a series of articles on the creation of powerful chat system. In our sevenths lesson I have added a very important feature – private messaging. Now you can send your private messages to another members (optional – to online members). Functionality looks like facebook’s chat. We have to click small icon near recipient’s name in the list of members to start talking in private mode. Welcome to start reading.
Today I will publish updated sources of our growing project. Whole project is well structured: system classes is in ‘classes’ folder, all javascript files in ‘js’ folder, stylesheets in ‘css’ folder, all custom avatars in ‘data’ folder, images in ‘images’ folder, template files in ‘templates’ folder (as usual).
Now – download the source files and lets start coding !
Step 1. HTML
I updated two template files:
templates/main_page.html
templates/profile_page.html
Please add just 2 lines (as at code below) right before closed BODY tag (for both template files):
2 |
< div class = "priv_dock_wrap" ></ div > |
It is our new private messages dock (fixed element at the bottom of page).
Step 2. CSS
I updated the second half of our main CSS file (from line 180 till the end of file):
css/main.css
004 |
border : 1px solid #888 ; |
008 |
.chat_messages a, .priv_conv a { |
011 |
.chat_messages a img, .priv_conv a img { |
013 |
vertical-align : middle ; |
016 |
.chat_messages .message, .priv_conv .message { |
017 |
background-color : #fff ; |
020 |
-moz-border-radius: 5px ; |
021 |
-ms-border-radius: 5px ; |
022 |
-o-border-radius: 5px ; |
023 |
-webkit-border-radius: 5px ; |
026 |
.chat_messages .message span, .priv_conv .message span { |
035 |
.chat_submit_form .error, .chat_submit_form .success, .chat_submit_form .protect { |
038 |
.chat_submit_form .error { |
041 |
.chat_submit_form .success { |
044 |
.chat_submit_form .protect { |
060 |
padding : 2px 22px 2px 10px ; |
063 |
.profiles div a:hover { |
064 |
background-color : #E0E4EE ; |
065 |
box-shadow: 2px 0 2px -2px #B2B9C9 inset ; |
074 |
.profiles div img.pchat { |
086 |
text- overflow : ellipsis; |
089 |
.profiles div img.status_img { |
100 |
.customizer_buttons #preview, .customizer_buttons #pick { |
101 |
border : 1px solid #888 ; |
102 |
border-radius: 3px 3px 3px 3px ; |
103 |
box-shadow: 2px 3px 3px #888 ; |
117 |
background-color : #FFFFFF ; |
118 |
border : 1px solid #000000 ; |
127 |
background-color : #6D84B4 ; |
133 |
padding : 3px 15px 4px ; |
135 |
text- overflow : ellipsis; |
148 |
border-top : 1px solid #888888 ; |
150 |
.priv_input input[type=text] { |
157 |
outline : medium none ; |
160 |
padding : 5px 4px 3px 20px ; |
163 |
-moz-border-radius: 0 ; |
164 |
-ms-border-radius: 0 ; |
166 |
-webkit-border-radius: 0 ; |
Now it contains new styles for our private messaging system.
Step 3. PHP
Now, its time to check changes in our PHP sources.
index.php
03 |
if (version_compare(phpversion(), '5.3.0' , '>=' ) == 1) |
04 |
error_reporting (E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
06 |
error_reporting (E_ALL & ~E_NOTICE); |
07 |
require_once ( 'classes/Services_JSON.php' ); |
08 |
require_once ( 'classes/CMySQL.php' ); |
09 |
require_once ( 'classes/CLogin.php' ); |
10 |
require_once ( 'classes/CProfiles.php' ); |
13 |
if (! isset( $_SESSION [ 'member_id' ]) && $_POST [ 'Join' ] == 'Join' ) { |
14 |
$GLOBALS [ 'CProfiles' ]->registerProfile(); |
17 |
$sLoginForm = $GLOBALS [ 'CLogin' ]->getLoginBox(); |
18 |
$sChat = '<h2>You do not have rights to use chat</h2>' ; |
19 |
$sInput = $sPrivChatJs = '' ; |
20 |
if ( $_SESSION [ 'member_id' ] && $_SESSION [ 'member_status' ] == 'active' && $_SESSION [ 'member_role' ]) { |
21 |
if ( $_GET [ 'action' ] == 'update_last_nav' ) { |
22 |
$iPid = (int) $_SESSION [ 'member_id' ]; |
24 |
$GLOBALS [ 'MySQL' ]->res( "UPDATE `cs_profiles` SET `date_nav` = NOW() WHERE `id` = '{$iPid}'" ); |
28 |
require_once ( 'classes/CChat.php' ); |
29 |
if ( $_GET [ 'action' ] == 'check_new_messages' ) { |
30 |
$iPid = (int) $_SESSION [ 'member_id' ]; |
31 |
$iSender = $GLOBALS [ 'MainChat' ]->getRecentMessage( $iPid ); |
33 |
$aSender = $GLOBALS [ 'CProfiles' ]->getProfileInfo( $iSender ); |
34 |
$sName = ( $aSender [ 'first_name' ] && $aSender [ 'last_name' ]) ? $aSender [ 'first_name' ] . ' ' . $aSender [ 'last_name' ] : $aSender [ 'name' ]; |
35 |
$oJson = new Services_JSON(); |
36 |
header( 'Content-type: application/json' ); |
37 |
echo $oJson ->encode( array ( 'id' => $iSender , 'name' => $sName )); |
41 |
if ( $_GET [ 'action' ] == 'get_private_messages' ) { |
42 |
$sChat = $GLOBALS [ 'MainChat' ]->getMessages((int) $_GET [ 'recipient' ]); |
43 |
$oJson = new Services_JSON(); |
44 |
header( 'Content-type: application/json' ); |
45 |
echo $oJson ->encode( array ( 'messages' => $sChat )); |
49 |
$sChat = $GLOBALS [ 'MainChat' ]->getMessages(); |
50 |
if ( $_GET [ 'action' ] == 'get_last_messages' ) { |
51 |
$oJson = new Services_JSON(); |
52 |
header( 'Content-type: application/json' ); |
53 |
echo $oJson ->encode( array ( 'messages' => $sChat )); |
57 |
if ( $_POST [ 'action' ] == 'add_avatar' ) { |
58 |
$iAvRes = $GLOBALS [ 'CProfiles' ]->addAvatar(); |
59 |
header( 'Content-Type: text/html; charset=utf-8' ); |
60 |
echo ( $iAvRes == 1) ? '<h2 style="text-align:center">New avatar has been accepted, refresh main window to see it</h2>' : '' ; |
64 |
$sInput = $GLOBALS [ 'MainChat' ]->getInputForm(); |
65 |
if ( $_POST [ 'message' ]) { |
66 |
$iRes = $GLOBALS [ 'MainChat' ]->acceptMessage(); |
67 |
$oJson = new Services_JSON(); |
68 |
header( 'Content-type: application/json' ); |
69 |
echo $oJson ->encode( array ( 'result' => $iRes )); |
72 |
if ( $_POST [ 'priv_message' ]) { |
73 |
$iRes = $GLOBALS [ 'MainChat' ]->acceptPrivMessage(); |
74 |
$oJson = new Services_JSON(); |
75 |
header( 'Content-type: application/json' ); |
76 |
echo $oJson ->encode( array ( 'result' => $iRes )); |
79 |
$sPrivChatJs = '<script src="js/priv_chat.js"></script>' ; |
82 |
$sProfiles = $GLOBALS [ 'CProfiles' ]->getProfilesBlock(); |
83 |
$sOnlineMembers = $GLOBALS [ 'CProfiles' ]->getProfilesBlock(10, true); |
85 |
$sAvatar = $GLOBALS [ 'CProfiles' ]->getProfileAvatarBlock(); |
88 |
'{form}' => $sLoginForm . $sErrors , |
91 |
'{profiles}' => $sProfiles , |
92 |
'{online_members}' => $sOnlineMembers , |
93 |
'{avatar}' => $sAvatar , |
94 |
'{priv_js}' => $sPrivChatJs |
96 |
echo strtr ( file_get_contents ( 'templates/main_page.html' ), $aKeys ); |
I added here several more cases for our chat: checking for fresh messages, get private messages, posting of private messages. Our next updated file is profile view file (where I had to add our new js file priv_chat.js):
profile.php
03 |
if (version_compare(phpversion(), '5.3.0' , '>=' ) == 1) |
04 |
error_reporting (E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
06 |
error_reporting (E_ALL & ~E_NOTICE); |
07 |
require_once ( 'classes/CMySQL.php' ); |
08 |
require_once ( 'classes/CLogin.php' ); |
09 |
require_once ( 'classes/CProfiles.php' ); |
10 |
$iPid = (int) $_GET [ 'id' ]; |
12 |
if ( $_SESSION [ 'member_id' ] && $_SESSION [ 'member_status' ] == 'active' && $_SESSION [ 'member_role' ]) { |
13 |
if ( $_GET [ 'action' ] == 'change_color' ) { |
14 |
$iRes = $GLOBALS [ 'CProfiles' ]->changeColor( $_GET [ 'color' ]); |
15 |
header( 'Content-Type: text/html; charset=utf-8' ); |
16 |
echo ( $iRes == 1) ? '<h2 style="text-align:center">New color has been accepted, refresh main window to see it</h2>' : '' ; |
19 |
$sPrivChatJs = '<script src="js/priv_chat.js"></script>' ; |
21 |
$aInfo = $GLOBALS [ 'CProfiles' ]->getProfileInfo( $iPid ); |
22 |
$sName = $aInfo [ 'name' ]; |
23 |
$sFName = $aInfo [ 'first_name' ]; |
24 |
$sLName = $aInfo [ 'last_name' ]; |
25 |
$sAbout = $aInfo [ 'about' ]; |
26 |
$sDate = $aInfo [ 'date_reg' ]; |
27 |
$sRole = $GLOBALS [ 'CProfiles' ]->getRoleName( $aInfo [ 'role' ]); |
28 |
$sAvatar = $GLOBALS [ 'CProfiles' ]->getProfileAvatar( $iPid ); |
29 |
$sCustomBG = ( $aInfo [ 'color' ]) ? 'background-color:#' . $aInfo [ 'color' ] : '' ; |
31 |
$sProfiles = $GLOBALS [ 'CProfiles' ]->getProfilesBlock(); |
32 |
$sOnlineMembers = $GLOBALS [ 'CProfiles' ]->getProfilesBlock(10, true); |
40 |
'{datereg}' => $sDate , |
42 |
'{avatar}' => $sAvatar , |
43 |
'{custom_styles}' => $sCustomBG , |
44 |
'{cust_visible}' => ( $_SESSION [ 'member_id' ] == $iPid ) ? '' : 'style="display:none"' , |
45 |
'{profiles}' => $sProfiles , |
46 |
'{online_members}' => $sOnlineMembers , |
47 |
'{priv_js}' => $sPrivChatJs |
49 |
echo strtr ( file_get_contents ( 'templates/profile_page.html' ), $aKeys ); |
The next updated file:
classes/CChat.php
06 |
function acceptMessage() { |
07 |
$sName = $GLOBALS [ 'MySQL' ]->escape( $_SESSION [ 'member_name' ]); |
08 |
$iPid = (int) $_SESSION [ 'member_id' ]; |
09 |
$sMessage = $GLOBALS [ 'MySQL' ]->escape( $_POST [ 'message' ]); |
10 |
if ( $iPid && $sName != '' && $sMessage != '' ) { |
14 |
WHERE `sender` = '{$iPid}' AND UNIX_TIMESTAMP( ) - `when` < 5 |
17 |
$iLastId = $GLOBALS [ 'MySQL' ]->getOne( $sSQL ); |
18 |
if ( $iLastId ) return 2; |
19 |
$bRes = $GLOBALS [ 'MySQL' ]->res( "INSERT INTO `cs_messages` SET `sender` = '{$iPid}', `message` = '{$sMessage}', `when` = UNIX_TIMESTAMP()" ); |
20 |
return ( $bRes ) ? 1 : 3; |
24 |
function acceptPrivMessage() { |
25 |
$sName = $GLOBALS [ 'MySQL' ]->escape( $_SESSION [ 'member_name' ]); |
26 |
$iPid = (int) $_SESSION [ 'member_id' ]; |
27 |
$iRecipient = (int) $_POST [ 'recipient' ]; |
28 |
$sMessage = $GLOBALS [ 'MySQL' ]->escape( $_POST [ 'priv_message' ]); |
29 |
if ( $iPid && $iRecipient && $sName != '' && $sMessage != '' ) { |
33 |
WHERE `sender` = '{$iPid}' AND `recipient` = '{$iRecipient}' AND UNIX_TIMESTAMP( ) - `when` < 5 |
36 |
$iLastId = $GLOBALS [ 'MySQL' ]->getOne( $sSQL ); |
37 |
if ( $iLastId ) return 2; |
38 |
$bRes = $GLOBALS [ 'MySQL' ]->res( "INSERT INTO `cs_messages` SET `sender` = '{$iPid}', `recipient` = '{$iRecipient}', `message` = '{$sMessage}', `when` = UNIX_TIMESTAMP()" ); |
39 |
return ( $bRes ) ? 1 : 3; |
43 |
function getInputForm() { |
44 |
return file_get_contents ( 'templates/chat.html' ); |
47 |
function getMessages( $iRecipient = 0) { |
48 |
$sRecipientSQL = 'WHERE `recipient` = 0' ; |
49 |
if ( $iRecipient > 0) { |
50 |
$iPid = (int) $_SESSION [ 'member_id' ]; |
51 |
$sRecipientSQL = "WHERE (`sender` = '{$iRecipient}' && `recipient` = '{$iPid}') || (`recipient` = '{$iRecipient}' && `sender` = '{$iPid}')" ; |
54 |
SELECT `a` . * , `cs_profiles`.`name`, `cs_profiles`.`id` as 'pid' , UNIX_TIMESTAMP( ) - `a`.`when` AS 'diff' |
55 |
FROM `cs_messages` AS `a` |
56 |
INNER JOIN `cs_profiles` ON `cs_profiles`.`id` = `a`.`sender` |
58 |
ORDER BY `a`.`id` DESC |
61 |
$aMessages = $GLOBALS [ 'MySQL' ]->getAll( $sSQL ); |
65 |
foreach ( $aMessages as $i => $aMessage ) { |
66 |
$sExStyles = $sExJS = '' ; |
67 |
$iDiff = (int) $aMessage [ 'diff' ]; |
69 |
$sExStyles = 'style="display:none;"' ; |
70 |
$sExJS = "<script> $('#message_{$aMessage['id']}').fadeIn('slow'); </script>" ; |
72 |
$sWhen = date ( "H:i:s" , $aMessage [ 'when' ]); |
73 |
$sAvatar = $GLOBALS [ 'CProfiles' ]->getProfileAvatar( $aMessage [ 'pid' ]); |
74 |
$sMessages .= '<div class="message" id="message_' . $aMessage [ 'id' ]. '" ' . $sExStyles . '><b><a href="profile.php?id=' . $aMessage [ 'pid' ]. '" target="_blank"><img src="' . $sAvatar . '">' . $aMessage [ 'name' ] . ':</a></b> ' . $aMessage [ 'message' ] . '<span>(' . $sWhen . ')</span></div>' . $sExJS ; |
78 |
function getRecentMessage( $iPid ) { |
81 |
SELECT `a` . * , `cs_profiles`.`name`, `cs_profiles`.`id` as 'pid' , UNIX_TIMESTAMP( ) - `a`.`when` AS 'diff' |
82 |
FROM `cs_messages` AS `a` |
83 |
INNER JOIN `cs_profiles` ON `cs_profiles`.`id` = `a`.`sender` |
84 |
WHERE `recipient` = '{$iPid}' |
85 |
ORDER BY `a`.`id` DESC |
88 |
$aMessage = $GLOBALS [ 'MySQL' ]->getRow( $sSQL ); |
89 |
$iDiff = (int) $aMessage [ 'diff' ]; |
91 |
return (int) $aMessage [ 'sender' ]; |
97 |
$GLOBALS [ 'MainChat' ] = new CChat(); |
I made up my mind to publish whole code of this file. Because I made several changes in existed functions, plus I added two new functions: acceptPrivMessage (to accept private messages) and getRecentMessage (to find, if the member has any fresh messages or hasn’t. If he has any fresh messages and if he doesn’t have opened private chat window – we will initialize new private chat session).
The next updated file:
classes/CProfiles.php
01 |
function getProfilesBlock( $iLim = 10, $bOnlineOnly = false) { |
02 |
$iPLimit = PROFILE_TIMEOUT; |
03 |
$sOnlineSQL = ( $bOnlineOnly ) ? 'AND (`date_nav` > SUBDATE(NOW(), INTERVAL ' . $iPLimit . ' MINUTE))' : '' ; |
05 |
SELECT `cs_profiles`.*, |
06 |
if (`date_nav` > SUBDATE(NOW(), INTERVAL { $iPLimit } MINUTE ), 1, 0) AS `is_online` |
08 |
WHERE `status` = 'active' |
10 |
ORDER BY `date_reg` DESC |
13 |
$aProfiles = $GLOBALS [ 'MySQL' ]->getAll( $sSQL ); |
14 |
$bCanChat = ( $_SESSION [ 'member_id' ] && $_SESSION [ 'member_status' ] == 'active' && $_SESSION [ 'member_role' ]); |
17 |
foreach ( $aProfiles as $i => $aProfile ) { |
18 |
$sName = ( $aProfile [ 'first_name' ] && $aProfile [ 'last_name' ]) ? $aProfile [ 'first_name' ] . ' ' . $aProfile [ 'last_name' ] : $aProfile [ 'name' ]; |
19 |
$sSName = ( strlen ( $sName ) > 32) ? mb_substr( $sName , 0, 28) . '...' : $sName ; |
20 |
$iPid = $aProfile [ 'id' ]; |
21 |
$sAvatar = $this ->getProfileAvatar( $iPid ); |
22 |
$sOnline = ( $aProfile [ 'is_online' ] == 1) ? '<img alt="Powerful Chat System – Lesson 7" src="images/online.png" class="status_img" />' : '' ; |
23 |
$sChat = ( $bCanChat ) ? '<img id="' . $iPid . '" alt="chat" src="images/chat.png" class="pchat" title="' . $sName . '" />' : '' ; |
24 |
$sCode .= '<div id="' . $iPid . '" title="' . $sName . '"><a href="profile.php?id=' . $iPid . '"><img src="' . $sAvatar . '" alt="' . $sName . '"><p>' . $sSName . $sChat . '</p>' . $sOnline . '</a></div>' ; |
26 |
$sClass = ( $bOnlineOnly ) ? 'profiles online_profiles' : 'profiles' ; |
27 |
return '<div class="' . $sClass . '">' . $sCode . '</div>' ; |
I updated only single function: getProfilesBlock here. Now it draws new icon chat.png. We can click at it to start private conversation. Pay attention to commented code (/*&& $aProfile[‘is_online’] == 1*/). You can uncomment it in order to give possibility to chat only for logged in members.
Step 5. Javascript
js/priv_chat.js
New javascript for our private chat feature:
03 |
var aPChatTimers = []; |
05 |
closePchat = function (id) { |
06 |
$( '.priv_dock_wrap .priv_chat_tab#pcid' +id).remove(); |
09 |
initiatePrivateChat = function (id, name) { |
10 |
var oPChat = $( '.priv_dock_wrap .priv_chat_tab#pcid' +id); |
11 |
if (! oPChat.length) { |
12 |
var sPCTemplate = '<div class="priv_chat_tab" id="pcid' +id+ '">' + |
13 |
' <div class="priv_title">' +name+ '<img src="images/close.png" /></div>' + |
14 |
' <div class="priv_conv"></div>' + |
15 |
' <div class="priv_input">' + |
16 |
' <form class="priv_chat_submit_form">' + |
17 |
' <input type="hidden" name="recipient" value="' +id+ '" />' + |
18 |
' <input type="text" name="message" />' + |
22 |
$( '.priv_dock_wrap' ).append(sPCTemplate); |
24 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_title img' ).bind( 'click' , function () { |
25 |
clearTimeout(aPChatTimers[id]) |
26 |
$( '.priv_dock_wrap .priv_chat_tab#pcid' +id).remove(); |
29 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form' ).bind( 'submit' , function () { |
30 |
$.post( 'index.php' , { priv_message: $( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form input[name=message]' ).val(), |
31 |
recipient: $( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form input[name=recipient]' ).val() }, |
33 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form input[name=message]' ).val( '' ); |
34 |
if (data.result == 1) { |
35 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form .success' ).fadeIn( 'slow' , function () { |
36 |
$( this ).delay(1000).fadeOut( 'slow' ); |
38 |
} else if (data.result == 2) { |
39 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form .protect' ).fadeIn( 'slow' , function () { |
40 |
$( this ).delay(1000).fadeOut( 'slow' ); |
43 |
$( '.priv_chat_tab#pcid' +id+ ' .priv_chat_submit_form .error' ).fadeIn( 'slow' , function () { |
44 |
$( this ).delay(1000).fadeOut( 'slow' ); |
53 |
getPrivateMessages(id); |
56 |
getPrivateMessages = function (iRecipient) { |
57 |
$.getJSON( 'index.php?action=get_private_messages&recipient=' + iRecipient, function (data) { |
59 |
$( '.priv_chat_tab#pcid' +iRecipient+ ' .priv_conv' ).html(data.messages); |
62 |
aPChatTimers[iRecipient] = setTimeout( function () { |
63 |
getPrivateMessages(iRecipient); |
68 |
$( '.profiles .pchat' ).click( function (event) { |
69 |
event.stopPropagation(); |
70 |
event.preventDefault(); |
71 |
initiatePrivateChat( this .id, this .title); |
73 |
initiateNewChatsPeriodically = function () { |
74 |
$.getJSON( 'index.php?action=check_new_messages' , function (data) { |
75 |
if (data != undefined && data.id) { |
76 |
initiatePrivateChat(data.id, data.name); |
79 |
setTimeout( function (){ |
80 |
initiateNewChatsPeriodically(); |
84 |
initiateNewChatsPeriodically(); |
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!