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

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


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

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

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

Скачивайте исходники, и давайте приступать к разработке!

Этап 1 – SQL

Добавляем новую таблицу в базу данных «cs_profiles_vote_track». Эта таблица будет содержать записи о рейтингах. Пожалуйста, выполните следующий SQL-запрос:

CREATE TABLE `cs_profiles_vote_track` (
`pid` int(11) unsigned NOT NULL default '0',
`ip` varchar(20) default NULL,
`date` datetime default NULL,
KEY `uip` (`ip`,`pid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Этап 2 – HTML

Обновляем шаблон нашей страницы профайла. Теперь он содержит дополнительную кнопку (функцию блокировки) и блок с рейтингом:

templates/profile_page.html

<html lang="en" >
<head>
<title>Powerful Chat System - Lesson 8</title>
<link href="css/main.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="js/customizer.js"></script>

<style>
.container {
&#123;custom_styles}
}
</style>
</head>
<body>
<header>
<h2>Powerful Chat System - Lesson 8</h2>
<a href="http://www.script-tutorials.com/powerful-chat-system-lesson-8/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
</header>
<div class="clear"></div>
<div class="container">
<div class="column">
<h3>Name: {name}</h3>
<h3>First name: {fname}</h3>
<h3>Last name: {lname}</h3>
<h3>About: {about}</h3>
<h3>Date Reg: {datereg}</h3>
<h3>Role: {role}</h3>
<h3>Avatar: <img src="{avatar}" style="vertical-align:middle" /></h3>
</div>
<div class="column">
<p><a href="index.php">Back to chat</a>
{actions}
</p>

</div>
</div>
{rate}
<div class="container" {cust_visible}>
<h2>You can customize your profile page</h2>
<div class="column">
<canvas id="color_canvas" width="370" height="60"></canvas>
</div>
<div class="column">
<div class="customizer_buttons">
<div id="preview"></div>
</div>

<form action="profile.php" method="GET" target="change_color_result">
<input type="hidden" value="{id}" name="id">
<input type="hidden" value="color" name="color" id="color">
<input type="hidden" value="change_color" name="action">
<input id="submit" type="submit" name="submit" value="Apply">
</form>
<iframe class="avatar_iframe" name="change_color_result"></iframe>
</div>
</div>

<div class="sidebar">
<div>
<h2>Online Members Block</h2>
{online_members}
</div>
<div>
<h2>Last Members</h2>
{profiles}
</div>
</div>

<div class="priv_dock_wrap"></div>
{priv_js}

</body>
</html>

И теперь у нас есть отдельный шаблон блока рейтинга:

templates/vote.html

<link href="css/vote.css" rel="stylesheet" type="text/css" />
<script src="js/vote.js"></script>

<div class="clear"></div>
<div class="container">
<div class="column">
<div class="votes_main">
<div class="votes_gray" style="width:{width}px;">
<div class="votes_buttons" id="{pid}" cnt="{rate_cnt}" val="{rate_avg}">
{votes}
</div>
<div class="votes_active" style="width:{act_width}px;"></div>
</div>
<span><b>{rate_cnt}</b> votes</span>
</div>
</div>
</div>

Этап 3 - CSS

Мы добавили новый css-файл для хранения стилей нашего элемента голосования:

css/vote.css

.votes_main {
margin: 10px auto;
overflow: hidden;
width: 450px;
}
.votes_gray {
background-image: url("../images/star_gray.png");
float: left;
height: 64px;
position: relative;
}
.votes_active {
background-image: url("../images/star.png");
height: 64px;
left: 0;
position: absolute;
top: 0;
z-index: 1;
}
.votes_buttons {
left: 0;
position: absolute;
top: 0;
z-index: 2;
}
.votes_button {
border: medium none;
height: 64px;
margin: 0;
padding: 0;
width: 64px;
}
.votes_main span {
color: #333333;
display: block;
float: left;
font-weight: bold;
font-size: 18px;
line-height: 64px;
margin-left: 10px;
}

Этап 4 - PHP

Также был обновлен файл профайла: мы добавили всего пару строк кода в самом конце (в массив), поэтому нет необходимости публиковать здесь весь файл. Демонстрируем лишь обновленный код массива:

profile.php

// draw common page
$aKeys = array(
'{id}' => $iPid,
'{name}' => $sName,
'{fname}' => $sFName,
'{lname}' => $sLName,
'{about}' => $sAbout,
'{datereg}' => $sDate,
'{role}' => $sRole,
'{avatar}' => $sAvatar,
'&#123;custom_styles}' => $sCustomBG,
'{cust_visible}' => ($_SESSION['member_id'] == $iPid) ? '' : 'style="display:none"',
'{profiles}' => $sProfiles,
'{online_members}' => $sOnlineMembers,
'{priv_js}' => $sPrivChatJs,
'{actions}' => $GLOBALS['CProfiles']->getBlockMemberAction($iPid),
'{rate}' => $GLOBALS['CProfiles']->getBlockRate($iPid),
);

Наш следующий обновленный файл. Здесь у нас три новых функции (вы можете добавить их в самый конец нашего класса):

classes/CProfiles.php

// get block member action button
function getBlockMemberAction($iPid) {
if ($_SESSION['member_id'] != $iPid && $_SESSION['member_status'] == 'active' && in_array($_SESSION['member_role'], array(4, 5))) {
$aMyInfo = $this->getProfileInfo($_SESSION['member_id']);
$aInfo = $this->getProfileInfo($iPid);

if ($aMyInfo['role'] > $aInfo['role']) {
$sStatus = $aInfo['status'];
$sDescDesc = ($sStatus == 'active') ? 'Block this member' : 'Unblock this member';

return '<font style="float:right"><button id="block" pid="'.$iPid.'">'.$sDescDesc.'</button></font><script src="js/admin_utils.js"></script>';
}
}
}

// block member
function blockMember($iPid) {
if ($iPid) {
$aInfo = $this->getProfileInfo($iPid);
$sStatus = $aInfo['status'];
$sUpStatus = ($sStatus == 'active') ? 'passive' : 'active';
$sSQL = "
UPDATE `cs_profiles` SET
`status` = '{$sUpStatus}'
WHERE `id` = '{$iPid}'
";
$GLOBALS['MySQL']->res($sSQL);
return ($sStatus == 'active') ? 2 : 1;
}
return;
}

// get block member action button
function getBlockRate($iPid) {
if ($_SESSION['member_id'] != $iPid && $_SESSION['member_status'] == 'active') {
// $aMyInfo = $this->getProfileInfo($_SESSION['member_id']);
$aInfo = $this->getProfileInfo($iPid);

// vote element
$iIconSize = 64;
$iMax = 5;
$iRate = $aInfo['rate'];
$iRateCnt = $aInfo['rate_count'];
$fRateAvg = ($iRate && $iRateCnt) ? $iRate / $iRateCnt : 0;
$iWidth = $iIconSize*$iMax;
$iActiveWidth = round($fRateAvg*($iMax ? $iWidth/$iMax : 0));

$sVot = '';
for ($i=1 ; $i<=$iMax ; $i++) {
$sVot .= '<a href="#" id="'.$i.'"><img class="votes_button" src="images/empty.gif" alt="" /></a>';
}

$aKeys = array(
'{pid}' => $iPid,
'{width}' => $iWidth,
'{rate_cnt}' => $iRateCnt,
'{rate_avg}' => $fRateAvg,
'{votes}' => $sVot,
'{act_width}' => $iActiveWidth,
);
return strtr(file_get_contents('templates/vote.html'), $aKeys);
}
}

Первая функция – getBlockMemberAction – возвращает одну небольшую кнопку на страницу профайла. Клик по этой кнопке приведет к блокировке/разблокировке пользователя. Эта функция доступна только модераторам и администраторам. К тому же, модератор не может заблокировать администратора, а вот наоборот всё очень даже возможно. Вторая функция – blockMember – обновляет статус профайла (блок). И последняя – getBlockRate – возвращает блок со звездочками рейтинга, с помощью которых мы можем голосовать за пользователей.

Для того чтобы выполнять различные действия, мы добавили новый PHP-файл:

actions.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/CMySQL.php');
require_once('classes/CLogin.php');
require_once('classes/CProfiles.php');

// for logged-in members only
if ($_SESSION['member_id'] && $_SESSION['member_status'] == 'active') {
if (in_array($_SESSION['member_role'], array(4, 5))) { // for moderators and admins only
if ($_POST['action'] == 'block') { // Block action
$iRes = $GLOBALS['CProfiles']->blockMember((int)$_POST['pid']);
header('Content-Type: text/html; charset=utf-8');
echo $iRes;
exit;
}
}

if ($_POST['action'] == 'put_vote') { // Put vote action
$iPid = (int)$_POST['id'];
$iVote = (int)$_POST['vote'];
$sIp = getVisitorIP();

// we can vote once per week (protection)
$iOldId = $GLOBALS['MySQL']->getOne("SELECT `pid` FROM `cs_profiles_vote_track` WHERE `pid` = '{$iPid}' AND `ip` = '{$sIp}' AND (`date` >= NOW() - INTERVAL 7 DAY) LIMIT 1");
if (! $iOldId) {
$GLOBALS['MySQL']->res("INSERT INTO `cs_profiles_vote_track` SET `pid` = '{$iPid}', `ip` = '{$sIp}', `date` = NOW()");
$GLOBALS['MySQL']->res("UPDATE `cs_profiles` SET `rate` = `rate` + {$iVote}, `rate_count` = `rate_count` + 1 WHERE `id` = '{$iPid}'");
header('Content-Type: text/html; charset=utf-8');
echo 1;
exit;
}
}
}

function getVisitorIP() {
$ip = "0.0.0.0";
if( ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) && ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif( ( isset( $_SERVER['HTTP_CLIENT_IP'])) && (!empty($_SERVER['HTTP_CLIENT_IP'] ) ) ) {
$ip = explode(".",$_SERVER['HTTP_CLIENT_IP']);
$ip = $ip[3].".".$ip[2].".".$ip[1].".".$ip[0];
} elseif((!isset( $_SERVER['HTTP_X_FORWARDED_FOR'])) || (empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
if ((!isset( $_SERVER['HTTP_CLIENT_IP'])) && (empty($_SERVER['HTTP_CLIENT_IP']))) {
$ip = $_SERVER['REMOTE_ADDR'];
}
}
return $ip;
}

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

Этап 5 – javascript

Мы добавили новый Js-файл, который будет производить различные действия администратора. Первое заключается в блокировке пользователей.

js/admin_utils.js

$(function() {

// block member
$('#block').click(function(event) {
var oBtn = $(this);
$.post('actions.php', { pid: oBtn.attr('pid'), action: 'block' },
function(data){
if (data != undefined) {
oBtn[0].innerHTML = (data == 2) ? 'Unblock this member' : 'Block this member';
}
}
);
});
});

Еще один JS-файл нужен для работы с рейтингами

js/vote.js

$(function(){
var width = 0;

$('.votes_buttons a').hover(
function () {
width = $(this).attr('id') * 64;
$('.votes_active').width(width + 'px');
},
function () {
width = $(this).parent().attr('val') * 64;
$('.votes_active').width(width + 'px');
}
);

$('.votes_buttons a').click(function () {
var idVal = $(this).parent().attr('id');
var iCnt = $(this).parent().attr('cnt');
var voteVal = $(this).attr('id');
var iSelWidth = voteVal * 64;

$.post('actions.php', { id: idVal, vote: voteVal, action: 'put_vote' },
function(data){
if (data == 1) {
width = iSelWidth;
$('.votes_active').width(iSelWidth + 'px');
iCnt = parseInt(iCnt) + 1;
$('.votes_main span b').text(iCnt);
$('.votes_buttons').attr('val', voteVal);
}
}
);
});
});

Завершение

Надеемся, что вы следите за нашей серией статей о создании чата, и также надеемся, что вам интересно. Если у вас есть какие-либо идеи, то мы обязательно обсудим их с вами и другими читателями.

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

Вернуться назад