Files
moodle/mod/chat/gui_header_js/jsupdated.php
martinlanghoff 930413b133 mod/chat: Normal method - introducing "Stream" updates.
This is an alternative version of jsupdate.php that acts
as a long-running daemon. It will feed/stall/feed JS updates
to the client. From the module configuration select "Stream"
updates.

The client connection is not forever though. Once we reach
CHAT_MAX_CLIENT_UPDATES (currently 1000), it will force
the client to re-fetch it.

This buys us all the benefits that chatd has, minus the setup,
as we are using apache to do the daemon handling.

Chat still defaults to the normal update method, which is now
optimised to take advantage of keepalives -- so this change is
safe. The instructions in the config page also indicate that this
mode may not be well supported everywhere. It hasn't been
tested on IIS for starters.

In terms of relative cost -- if each hit on jsupdate.php incurs
on ~20 db queries and delivers one update to the client, each hit
on jsupdate takes ~20 queries, and then roughly 2~3 queries to
serve each of the next 1000 updates. On busy sites, the difference
is huge.

There is still room for enhancements in both keepalive and stream
update methods. I am pretty sure we can trim DB queries more.
2006-04-19 02:20:48 +00:00

224 lines
8.4 KiB
PHP

<?php // $Id$
/** jsupdated.php - notes by Martin Langhoff <martin@catalyst.net.nz>
**
** This is an alternative version of jsupdate.php that acts
** as a long-running daemon. It will feed/stall/feed JS updates
** to the client. From the module configuration select "Stream"
** updates.
**
** The client connection is not forever though. Once we reach
** CHAT_MAX_CLIENT_UPDATES, it will force the client to re-fetch it.
**
** This buys us all the benefits that chatd has, minus the setup,
** as we are using apache to do the daemon handling.
**
**/
define('CHAT_MAX_CLIENT_UPDATES', 1000);
$nomoodlecookie = true; // Session not needed!
require('../../../config.php');
require('../lib.php');
// we are going to run for a long time
// avoid being terminated by php
@set_time_limit(0);
$chat_sid = required_param('chat_sid', PARAM_ALPHANUM);
$chat_lasttime = optional_param('chat_lasttime', 0, PARAM_INT);
$chat_lastrow = optional_param('chat_lastrow', 1, PARAM_INT);
$chat_lastid = optional_param('chat_lastid', 0, PARAM_INT);
if (!$chatuser = get_record('chat_users', 'sid', $chat_sid)) {
error('Not logged in!');
}
//Get the course theme
$course = get_record('course','id',$chatuser->course,'','','','','id,theme');
//Set the course theme if necessary
if (!empty($course->theme)) {
if (!empty($CFG->allowcoursethemes)) {
$CFG->coursetheme = $course->theme;
}
}
//Get the user theme and enough info to be used in chat_format_message() which passes it along to
// chat_format_message_manually() -- and only id and timezone are used.
if (!$USER = get_record('user','id',$chatuser->userid,'','','','','id, theme, username, timezone')) {
error('User does not exist!');
}
$USER->description = '';
//Adjust the prefered theme (main, course, user)
theme_setup();
chat_force_language($chatuser->lang);
// force deleting of timed out users if there is a silence in room or just entering
if ((time() - $chat_lasttime) > $CFG->chat_old_ping) {
// must be done before chat_get_latest_message!!!
chat_delete_old_users();
}
//
// Time to send headers, and lay out the basic JS updater page
//
header('Expires: Sun, 28 Dec 1997 09:32:45 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
header('Content-Type: text/html');
/// required stylesheets
$stylesheetshtml = '';
foreach ($CFG->stylesheets as $stylesheet) {
$stylesheetshtml .= '<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"'.$stylesheet.'\\" />';
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=<?php echo current_charset(); ?>" />
<script type="text/javascript">
<!--
if (parent.msg.document.getElementById("msgStarted") == null) {
parent.msg.document.close();
parent.msg.document.open("text/html","replace");
parent.msg.document.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
parent.msg.document.write("<html><head>");
parent.msg.document.write("<meta http-equiv=\"content-type\" content=\"text/html; charset=<?php echo current_charset(); ?>\" />");
parent.msg.document.write("<base target=\"_blank\" />");
parent.msg.document.write("<?php echo $stylesheetshtml ?>");
parent.msg.document.write("</head><body class=\"mod-chat-gui_header_js course-<?php echo $chatuser->course ?>\" id=\"mod-chat-gui_header_js-jsupdate\"><div style=\"display: none\" id=\"msgStarted\">&nbsp;</div>");
}
// -->
</script>
</head>
<body>
<?php
// Ensure the HTML head makes it out there
echo $CHAT_DUMMY_DATA;
@ob_end_flush();
for ($n=0; $n <= CHAT_MAX_CLIENT_UPDATES; $n++) {
// ping first so we can later shortcut as needed.
$chatuser->lastping = time();
set_field('chat_users', 'lastping', $chatuser->lastping, 'id', $chatuser->id );
if ($message = chat_get_latest_message($chatuser->chatid, $chatuser->groupid)) {
$chat_newlasttime = $message->timestamp;
$chat_newlastid = $message->id;
} else {
$chat_newlasttime = 0;
$chat_newlastid = 0;
print " \n";
print $CHAT_DUMMY_DATA;
@ob_end_flush();
sleep($CFG->chat_refresh_room);
continue;
}
$timenow = time();
$groupselect = $chatuser->groupid ? " AND (groupid='".$chatuser->groupid."' OR groupid='0') " : "";
$newcriteria = '';
if ($chat_lastid > 0) {
$newcriteria = "id > $chat_lastid";
} else {
if ($chat_lasttime == 0) { //display some previous messages
$chat_lasttime = $timenow - $CFG->chat_old_ping; //TO DO - any better value??
}
$newcriteria = "timestamp > $chat_lasttime";
}
$messages = get_records_select("chat_messages",
"chatid = '$chatuser->chatid' AND $newcriteria $groupselect",
"timestamp ASC");
if ($messages) {
$num = count($messages);
} else {
print " \n";
print $CHAT_DUMMY_DATA;
@ob_end_flush();
sleep($CFG->chat_refresh_room);
continue;
$num = 0;
}
print '<script type="text/javascript">' . "\n";
print "<!-- \n\n";
$chat_newrow = ($chat_lastrow + $num) % 2;
$refreshusers = false;
$us = array ();
if (($chat_lasttime != $chat_newlasttime) and $messages) {
$beep = false;
$refreshusers = false;
foreach ($messages as $message) {
$chat_lastrow = ($chat_lastrow + 1) % 2;
$formatmessage = chat_format_message($message, $chatuser->course, $USER, $chat_lastrow);
if ($formatmessage->beep) {
$beep = true;
}
if ($formatmessage->refreshusers) {
$refreshusers = true;
}
$us[$message->userid] = $timenow - $message->timestamp;
echo "parent.msg.document.write('".addslashes($formatmessage->html )."\\n');\n";
}
// from the last message printed...
// a strange case where lack of closures is useful!
$chat_lasttime = $message->timestamp;
$chat_lastid = $message->id;
}
if ($refreshusers) {
echo "if (parent.users.document.anchors[0] != null) {" .
"parent.users.location.href = parent.users.document.anchors[0].href;}\n";
} else {
foreach($us as $uid=>$lastping) {
$min = (int) ($lastping/60);
$sec = $lastping - ($min*60);
$min = $min < 10 ? '0'.$min : $min;
$sec = $sec < 10 ? '0'.$sec : $sec;
$idle = $min.':'.$sec;
echo "if (parent.users.document.getElementById('uidle{$uid}') != null) {".
"parent.users.document.getElementById('uidle{$uid}').innerHTML = '$idle';}\n";
}
}
print 'parent.msg.scroll(1,5000000);' . "\n\n";
print "// -->\n";
print '</script>' . "\n\n";
if ($beep) {
print '<embed src="../beep.wav" autostart="true" hidden="true" name="beep" />';
}
print $CHAT_DUMMY_DATA;
@ob_end_flush();
sleep($CFG->chat_refresh_room);
} // here ends the for() loop
// here & should be written & :-D
$refreshurl = "{$CFG->wwwroot}/mod/chat/gui_header_js/jsupdated.php?chat_sid=$chat_sid&chat_lasttime=$chat_lasttime&chat_lastrow=$chat_newrow&chat_lastid=$chat_lastid";
print '<script type="text/javascript">' . "\n";
print "<!-- \n\n";
print "location.href = '$refreshurl';\n";
print "// -->\n";
print '</script>' . "\n\n";
?>
</body>
</html>