]> git.mjollnir.org Git - moodle.git/commitdiff
mod/chat: Normal method - introducing "Stream" updates.
authormartinlanghoff <martinlanghoff>
Wed, 19 Apr 2006 02:20:48 +0000 (02:20 +0000)
committermartinlanghoff <martinlanghoff>
Wed, 19 Apr 2006 02:20:48 +0000 (02:20 +0000)
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.

lang/en_utf8/chat.php
mod/chat/config.html
mod/chat/gui_header_js/index.php
mod/chat/gui_header_js/jsupdated.php [new file with mode: 0644]
mod/chat/lib.php

index 7eba9371b797dcd6b0e71e93edae7ffda8b56579..2c8e39c94bb41989370454b019343214df17c75b 100644 (file)
@@ -6,8 +6,9 @@ $string['chatname'] = 'Name of this chat room';
 $string['chatreport'] = 'Chat sessions';
 $string['chattime'] = 'Next chat time';
 $string['configmethod'] = 'The normal chat method involves the clients regularly contacting the server for updates. It requires no configuration and works everywhere, but it can create a large load on the server with many chatters.  Using a server daemon requires shell access to Unix, but it results in a fast scalable chat environment.';
+$string['confignormalupdatemode'] = 'Chatroom updates are normally served efficiently using the <em>Keep-Alive</em> feature of HTTP 1.1, but this is still quite heavy on the server. A more advanced method is to use the <em>Stream</em> strategy to feed updates to the users. Using <em>Stream</em> scales much better (similar to the chatd method) but may not be supported by your server.';
 $string['configoldping'] = 'What is the maximum time that may pass before we detect that a user has disconnected (in seconds)? This is just an upper limit, as usually disconnects are detected very quickly. Lower values will be more demanding on your server. If you are using the normal method, <strong>never</strong> set this lower than 2 * chat_refresh_room.';
-$string['configrefreshroom'] = 'How often should the chat room itself be refreshed? (in seconds).  Setting this low will make the chat room seem quicker, but it may place a higher load on your web server when many people are chatting';
+$string['configrefreshroom'] = 'How often should the chat room itself be refreshed? (in seconds).  Setting this low will make the chat room seem quicker, but it may place a higher load on your web server when many people are chatting. If you are using <em>Stream</em> updates, you can select higher refresh frequencies -- try with 2.';
 $string['configrefreshuserlist'] = 'How often should the list of users be refreshed? (in seconds)';
 $string['configserverhost'] = 'The hostname of the computer where the server daemon is';
 $string['configserverip'] = 'The numerical IP address that matches the above hostname';
@@ -39,6 +40,8 @@ $string['neverdeletemessages'] = 'Never delete messages';
 $string['nextsession'] = 'Next scheduled session';
 $string['noguests'] = 'The chat is not open to guests';
 $string['nomessages'] = 'No messages yet';
+$string['normalstream'] = 'Stream';
+$string['normalkeepalive'] = 'KeepAlive';
 $string['noscheduledsession'] = 'No scheduled session';
 $string['repeatdaily'] = 'At the same time every day';
 $string['repeatnone'] = 'No repeats - publish the specified time only';
index 8baa10b18534ccbb9270d6cb5e9997daa08a94fa..8d262c10f760c4c0fb5011d01a90219a450c0ce5 100644 (file)
     </td>
 </tr>
 
+<tr valign="top">
+  <td align="right">chat_normal_updatemode:</td>
+  <td>
+<?php
+       unset($options);
+       $options['jsupdate']     = get_string('normalkeepalive', 'chat');
+       $options['jsupdated']    = get_string('normalstream', 'chat');
+       choose_from_menu ($options, "chat_normal_updatemode", $CFG->chat_normal_updatemode, "", "", "");
+?>
+    </td>
+    <td>
+    <?php print_string("confignormalupdatemode", "chat") ?>
+    </td>
+</tr>
+
 <tr>
   <td colspan="3" align="center"><hr />
      <div><b><?php print_string('methoddaemon', 'chat')?></b></div>
index f59e0d93450ccf51c84c21fe8be48a276083a61d..6e0227acefa8e8823dd8113d2b6a69adc4a0f532 100644 (file)
 
     $params = "chat_sid=$chat_sid";
 
+    // fallback to the old jsupdate, but allow other update modes
+    $updatemode = 'jsupdate';
+    if (!empty($CFG->chat_normal_updatemode)) {
+        $updatemode = $CFG->chat_normal_updatemode;
+    }
 ?>
 
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
@@ -65,7 +70,7 @@
  <frameset cols="*,200" border="5" framespacing="no" frameborder="yes" marginwidth="2" marginheight="1">
   <frameset rows="0,0,*,50" border="0" framespacing="no" frameborder="no" marginwidth="2" marginheight="1">
    <frame src="../empty.php" name="empty" scrolling="no" marginwidth="0" marginheight="0">
-   <frame src="jsupdate.php?<?php echo $params ?>" name="jsupdate" scrolling="no" marginwidth="0" marginheight="0">
+   <frame src="<?php echo $updatemode ?>.php?<?php echo $params ?>" name="jsupdate" scrolling="no" marginwidth="0" marginheight="0">
    <frame src="chatmsg.php?<?php echo $params ?>" name="msg" scrolling="auto" marginwidth="2" marginheight="1">
    <frame src="chatinput.php?<?php echo $params ?>" name="input" scrolling="no" marginwidth="2" marginheight="1">
   </frameset>
diff --git a/mod/chat/gui_header_js/jsupdated.php b/mod/chat/gui_header_js/jsupdated.php
new file mode 100644 (file)
index 0000000..0558cf4
--- /dev/null
@@ -0,0 +1,223 @@
+<?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>
index 12c4c5a51b9a214b837bf748774bb94a5ca6704a..dad87133a0b7d9cfb2e92fe6e44597d9db8738b9 100644 (file)
@@ -16,6 +16,9 @@ if (!isset($CFG->chat_old_ping)) {
 if (!isset($CFG->chat_method)) {
     set_config("chat_method", "header_js");
 }
+if (!isset($CFG->chat_normal_updatemode)) {
+    set_config("chat_normal_updatemode", 'jsupdate');
+}
 if (!isset($CFG->chat_serverhost)) {
     set_config("chat_serverhost", $_SERVER['HTTP_HOST']);
 }