Сегодня мы продолжим нашу эпопею, посвященную разработке чата. Наш 8-й урок посвящен разработке нескольких функций: мы добавили возможность администраторам и модераторам блокировать (или разблокировать) пользователей (теперь они могут осуществлять естественную модерацию – банить пользователей), а также добавили рейтинги пользователей. Теперь каждый пользователь может проголосовать за других.
Сегодня мы выложим для вас обновленные исходники нашего растущего проекта. Весь проект разложен по полочкам: системные классы будут в папке «classes», javascript-файлы в папке «js», таблицы стилей в папке «css», файлы шаблона в папке «templates», а все пользовательские аватары будут помещены в папку под названием «data».
Скачивайте исходники, и давайте приступать к разработке!
Этап 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 { {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, '{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); } } ); }); });
Завершение
Надеемся, что вы следите за нашей серией статей о создании чата, и также надеемся, что вам интересно. Если у вас есть какие-либо идеи, то мы обязательно обсудим их с вами и другими читателями.
Посмотреть демо | Скачать архивом
Внимание! У вас нет прав для просмотра скрытого текста.
Вернуться назад
|