"MDL-20221, hide header and navigation in embedded layout"
[moodle.git] / mod / chat / lib.php
CommitLineData
848bb113 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1515a89e 17
848bb113 18/**
19 * Library of functions and constants for module chat
20 *
b2d5a79a 21 * @package mod-chat
848bb113 22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26/** Include portfoliolib.php */
214b1cf7 27require_once($CFG->libdir.'/portfoliolib.php');
c4d588cc 28
1d507186 29$CFG->chat_ajax_debug = false;
30$CFG->chat_use_cache = false;
31
1515a89e 32// The HTML head for the message window to start with (<!-- nix --> is used to get some browsers starting with output
17da2e6f 33global $CHAT_HTMLHEAD;
1f8abb89 34$CHAT_HTMLHEAD = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"><html><head></head>\n<body>\n\n".padding(200);
1515a89e 35
36// The HTML head for the message window to start with (with js scrolling)
17da2e6f 37global $CHAT_HTMLHEAD_JS;
1f8abb89 38$CHAT_HTMLHEAD_JS = <<<EOD
39<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
40<html><head><script type="text/javascript">
41//<![CDATA[
42function move(){
43 if (scroll_active)
44 window.scroll(1,400000);
45 window.setTimeout("move()",100);
46}
47var scroll_active = true;
48move();
49//]]>
50</script>
51</head>
52<body onBlur="scroll_active = true" onFocus="scroll_active = false">
53EOD;
17da2e6f 54global $CHAT_HTMLHEAD_JS;
1f8abb89 55$CHAT_HTMLHEAD_JS .= padding(200);
1515a89e 56
57// The HTML code for standard empty pages (e.g. if a user was kicked out)
17da2e6f 58global $CHAT_HTMLHEAD_OUT;
1f8abb89 59$CHAT_HTMLHEAD_OUT = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"><html><head><title>You are out!</title></head><body></body></html>";
1515a89e 60
61// The HTML head for the message input page
17da2e6f 62global $CHAT_HTMLHEAD_MSGINPUT;
1f8abb89 63$CHAT_HTMLHEAD_MSGINPUT = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"><html><head><title>Message Input</title></head><body>";
1515a89e 64
65// The HTML code for the message input page, with JavaScript
17da2e6f 66global $CHAT_HTMLHEAD_MSGINPUT_JS;
1d507186 67$CHAT_HTMLHEAD_MSGINPUT_JS = <<<EOD
68<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
69<html>
70 <head><title>Message Input</title>
71 <script type="text/javascript">
72 //<![CDATA[
73 scroll_active = true;
74 function empty_field_and_submit(){
75 document.fdummy.arsc_message.value=document.f.arsc_message.value;
76 document.fdummy.submit();
77 document.f.arsc_message.focus();
78 document.f.arsc_message.select();
79 return false;
80 }
81 //]]>
82 </script>
83 </head><body OnLoad="document.f.arsc_message.focus();document.f.arsc_message.select();">;
84EOD;
1515a89e 85
fbabbd23 86// Dummy data that gets output to the browser as needed, in order to make it show output
17da2e6f 87global $CHAT_DUMMY_DATA;
6e5f40ea 88$CHAT_DUMMY_DATA = padding(200);
5c2f6a7f 89
848bb113 90/**
91 * @param int $n
92 * @return string
93 */
6e5f40ea 94function padding($n){
5c2f6a7f 95 $str = '';
633c3341 96 for($i=0; $i<$n; $i++){
1f8abb89 97 $str.="<!-- nix -->\n";
5c2f6a7f 98 }
99 return $str;
100}
1515a89e 101
848bb113 102/**
103 * Given an object containing all the necessary data,
104 * (defined by the form in mod_form.php) this function
105 * will create a new instance and return the id number
106 * of the new instance.
107 *
108 * @global object
109 * @param object $chat
110 * @return int
111 */
1515a89e 112function chat_add_instance($chat) {
c18269c7 113 global $DB;
1515a89e 114
115 $chat->timemodified = time();
116
c18269c7 117 if ($returnid = $DB->insert_record("chat", $chat)) {
8496c4af 118
119 $event = NULL;
120 $event->name = $chat->name;
9b010a10 121 $event->description = format_module_intro('chat', $chat, $chat->coursemodule);
8496c4af 122 $event->courseid = $chat->course;
123 $event->groupid = 0;
124 $event->userid = 0;
125 $event->modulename = 'chat';
126 $event->instance = $returnid;
127 $event->eventtype = $chat->schedule;
128 $event->timestart = $chat->chattime;
129 $event->timeduration = 0;
130
131 add_event($event);
132 }
133
134 return $returnid;
1515a89e 135}
136
848bb113 137/**
138 * Given an object containing all the necessary data,
139 * (defined by the form in mod_form.php) this function
140 * will update an existing instance with new data.
141 *
142 * @global object
143 * @param object $chat
144 * @return int
145 */
1515a89e 146function chat_update_instance($chat) {
c18269c7 147 global $DB;
1515a89e 148
149 $chat->timemodified = time();
150 $chat->id = $chat->instance;
151
1515a89e 152
c18269c7 153 if ($returnid = $DB->update_record("chat", $chat)) {
8496c4af 154
264867fd 155 $event = new object();
8496c4af 156
c18269c7 157 if ($event->id = $DB->get_field('event', 'id', array('modulename'=>'chat', 'instance'=>$chat->id))) {
8496c4af 158
159 $event->name = $chat->name;
9b010a10 160 $event->description = format_module_intro('chat', $chat, $chat->coursemodule);
8496c4af 161 $event->timestart = $chat->chattime;
162
163 update_event($event);
164 }
165 }
166
167 return $returnid;
1515a89e 168}
169
848bb113 170/**
171 * Given an ID of an instance of this module,
172 * this function will permanently delete the instance
173 * and any data that depends on it.
174 *
175 * @global object
176 * @param int $id
177 * @return bool
178 */
1515a89e 179function chat_delete_instance($id) {
c18269c7 180 global $DB;
848bb113 181
1515a89e 182
c18269c7 183 if (! $chat = $DB->get_record('chat', array('id'=>$id))) {
1515a89e 184 return false;
185 }
186
187 $result = true;
188
189 # Delete any dependent records here #
190
c18269c7 191 if (! $DB->delete_records('chat', array('id'=>$chat->id))) {
a71efae3 192 $result = false;
193 }
c18269c7 194 if (! $DB->delete_records('chat_messages', array('chatid'=>$chat->id))) {
a71efae3 195 $result = false;
196 }
6e5f40ea 197 if (! $DB->delete_records('chat_messages_current', array('chatid'=>$chat->id))) {
198 $result = false;
199 }
c18269c7 200 if (! $DB->delete_records('chat_users', array('chatid'=>$chat->id))) {
1515a89e 201 $result = false;
202 }
203
c18269c7 204 if (! $DB->delete_records('event', array('modulename'=>'chat', 'instance'=>$chat->id))) {
36eb856f 205 $result = false;
206 }
207
1515a89e 208 return $result;
209}
210
848bb113 211/**
212 * Return a small object with summary information about what a
213 * user has done with a given particular instance of this module
214 * Used for user activity reports.
215 * <code>
216 * $return->time = the time they did it
217 * $return->info = a short text description
218 * </code>
219 *
220 * @param object $course
221 * @param object $user
222 * @param object $mod
223 * @param object $chat
224 * @return void
225 */
1515a89e 226function chat_user_outline($course, $user, $mod, $chat) {
d3bf6f92 227 return NULL;
1515a89e 228}
229
848bb113 230/**
231 * Print a detailed representation of what a user has done with
232 * a given particular instance of this module, for user activity reports.
233 *
234 * @param object $course
235 * @param object $user
236 * @param object $mod
237 * @param object $chat
238 * @return bool
239 */
1515a89e 240function chat_user_complete($course, $user, $mod, $chat) {
1515a89e 241 return true;
242}
243
848bb113 244/**
245 * Given a course and a date, prints a summary of all chat rooms past and present
246 * This function is called from course/lib.php: print_recent_activity()
247 *
248 * @global object
249 * @global object
250 * @global object
251 * @param object $course
252 * @param array $viewfullnames
253 * @param int|string $timestart Timestamp
254 * @return bool
255 */
dd97c328 256function chat_print_recent_activity($course, $viewfullnames, $timestart) {
e09fc68b 257 global $CFG, $USER, $DB, $OUTPUT;
dd97c328 258
259 // this is approximate only, but it is really fast ;-)
260 $timeout = $CFG->chat_old_ping * 10;
261
d3bf6f92 262 if (!$mcms = $DB->get_records_sql("SELECT cm.id, MAX(chm.timestamp) AS lasttime
263 FROM {course_modules} cm
264 JOIN {modules} md ON md.id = cm.module
265 JOIN {chat} ch ON ch.id = cm.instance
266 JOIN {chat_messages} chm ON chm.chatid = ch.id
267 WHERE chm.timestamp > ? AND ch.course = ? AND md.name = 'chat'
268 GROUP BY cm.id
269 ORDER BY lasttime ASC", array($timestart, $course->id))) {
dd97c328 270 return false;
271 }
272
273 $past = array();
274 $current = array();
275 $modinfo =& get_fast_modinfo($course); // reference needed because we might load the groups
276
fd4d41c3 277 foreach ($mcms as $cmid=>$mcm) {
278 if (!array_key_exists($cmid, $modinfo->cms)) {
dd97c328 279 continue;
280 }
fd4d41c3 281 $cm = $modinfo->cms[$cmid];
282 $cm->lasttime = $mcm->lasttime;
dd97c328 283 if (!$modinfo->cms[$cm->id]->uservisible) {
284 continue;
285 }
b7602a11 286
dd97c328 287 if (groups_get_activity_groupmode($cm) != SEPARATEGROUPS
288 or has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
289 if ($timeout > time() - $cm->lasttime) {
290 $current[] = $cm;
291 } else {
292 $past[] = $cm;
293 }
294
295 continue;
296 }
297
298 if (is_null($modinfo->groups)) {
299 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
300 }
c5a05b95 301
dd97c328 302 // verify groups in separate mode
303 if (!$mygroupids = $modinfo->groups[$cm->groupingid]) {
304 continue;
305 }
0469cccf 306
dd97c328 307 // ok, last post was not for my group - we have to query db to get last message from one of my groups
308 // only minor problem is that the order will not be correct
309 $mygroupids = implode(',', $mygroupids);
310 $cm->mygroupids = $mygroupids;
311
d3bf6f92 312 if (!$mcm = $DB->get_record_sql("SELECT cm.id, MAX(chm.timestamp) AS lasttime
313 FROM {course_modules} cm
314 JOIN {chat} ch ON ch.id = cm.instance
6e5f40ea 315 JOIN {chat_messages_current} chm ON chm.chatid = ch.id
d3bf6f92 316 WHERE chm.timestamp > ? AND cm.id = ? AND
317 (chm.groupid IN ($mygroupids) OR chm.groupid = 0)
318 GROUP BY cm.id", array($timestart, $cm->id))) {
dd97c328 319 continue;
320 }
fd4d41c3 321
322 $cm->lasttime = $mcm->lasttime;
dd97c328 323 if ($timeout > time() - $cm->lasttime) {
324 $current[] = $cm;
325 } else {
326 $past[] = $cm;
327 }
328 }
329
330 if (!$past and !$current) {
b7602a11 331 return false;
332 }
1515a89e 333
dd97c328 334 $strftimerecent = get_string('strftimerecent');
335
336 if ($past) {
e09fc68b 337 echo $OUTPUT->heading(get_string("pastchats", 'chat').':');
dd97c328 338
339 foreach ($past as $cm) {
340 $link = $CFG->wwwroot.'/mod/chat/view.php?id='.$cm->id;
341 $date = userdate($cm->lasttime, $strftimerecent);
342 echo '<div class="head"><div class="date">'.$date.'</div></div>';
343 echo '<div class="info"><a href="'.$link.'">'.format_string($cm->name,true).'</a></div>';
b7602a11 344 }
8f7dc7f1 345 }
346
347 if ($current) {
e09fc68b 348 echo $OUTPUT->heading(get_string("currentchats", 'chat').':');
dd97c328 349
350 $oldest = floor((time()-$CFG->chat_old_ping)/10)*10; // better db caching
351
352 $timeold = time() - $CFG->chat_old_ping;
353 $timeold = floor($timeold/10)*10; // better db caching
354 $timeoldext = time() - ($CFG->chat_old_ping*10); // JSless gui_basic needs much longer timeouts
355 $timeoldext = floor($timeoldext/10)*10; // better db caching
356
d3bf6f92 357 $params = array('timeold'=>$timeold, 'timeoldext'=>$timeoldext, 'cmid'=>$cm->id);
358
359 $timeout = "AND (chu.version<>'basic' AND chu.lastping>:timeold) OR (chu.version='basic' AND chu.lastping>:timeoldext)";
dd97c328 360
361 foreach ($current as $cm) {
362 //count users first
363 if (isset($cm->mygroupids)) {
364 $groupselect = "AND (chu.groupid IN ({$cm->mygroupids}) OR chu.groupid = 0)";
365 } else {
366 $groupselect = "";
367 }
fd4d41c3 368
d3bf6f92 369 if (!$users = $DB->get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email, u.picture
370 FROM {course_modules} cm
371 JOIN {chat} ch ON ch.id = cm.instance
372 JOIN {chat_users} chu ON chu.chatid = ch.id
373 JOIN {user} u ON u.id = chu.userid
374 WHERE cm.id = :cmid $timeout $groupselect
375 GROUP BY u.id, u.firstname, u.lastname, u.email, u.picture", $params)) {
dd97c328 376 }
377
378 $link = $CFG->wwwroot.'/mod/chat/view.php?id='.$cm->id;
379 $date = userdate($cm->lasttime, $strftimerecent);
380
381 echo '<div class="head"><div class="date">'.$date.'</div></div>';
382 echo '<div class="info"><a href="'.$link.'">'.format_string($cm->name,true).'</a></div>';
383 echo '<div class="userlist">';
384 if ($users) {
385 echo '<ul>';
386 foreach ($users as $user) {
387 echo '<li>'.fullname($user, $viewfullnames).'</li>';
388 }
389 echo '</ul>';
390 }
391 echo '</div>';
392 }
b7602a11 393 }
394
395 return true;
1515a89e 396}
397
848bb113 398/**
399 * Function to be run periodically according to the moodle cron
400 * This function searches for things that need to be done, such
401 * as sending out mail, toggling flags etc ...
402 *
403 * @global object
404 * @return bool
405 */
1515a89e 406function chat_cron () {
d3bf6f92 407 global $DB;
1515a89e 408
fcd3a1ee 409 chat_update_chat_times();
410
7d792369 411 chat_delete_old_users();
412
319038c3 413 /// Delete old messages with a
414 /// single SQL query.
415 $subselect = "SELECT c.keepdays
d3bf6f92 416 FROM {chat} c
417 WHERE c.id = {chat_messages}.chatid";
4388027c 418
319038c3 419 $sql = "DELETE
d3bf6f92 420 FROM {chat_messages}
6e5f40ea 421 WHERE ($subselect) > 0 AND timestamp < ( ".time()." -($subselect) * 24 * 3600)";
422
423 $DB->execute($sql);
424
425 $sql = "DELETE
426 FROM {chat_messages_current}
427 WHERE timestamp < ( ".time()." - 8 * 3600)";
4388027c 428
d3bf6f92 429 $DB->execute($sql);
22a4491a 430
1515a89e 431 return true;
432}
433
848bb113 434/**
435 * Returns the users with data in one chat
436 * (users with records in chat_messages, students)
437 *
438 * @global object
439 * @param int $chatid
440 * @param int $groupid
441 * @return array
442 */
84a2fdd7 443function chat_get_participants($chatid, $groupid=0) {
d3bf6f92 444 global $DB;
05855091 445
d3bf6f92 446 $params = array('groupid'=>$groupid, 'chatid'=>$chatid);
05855091 447
84a2fdd7 448 if ($groupid) {
d3bf6f92 449 $groupselect = " AND (c.groupid=:groupid OR c.groupid='0')";
84a2fdd7 450 } else {
451 $groupselect = "";
452 }
453
05855091 454 //Get students
d3bf6f92 455 $students = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
456 FROM {user} u, {chat_messages} c
457 WHERE c.chatid = :chatid $groupselect
458 AND u.id = c.userid", $params);
1515a89e 459
05855091 460 //Return students array (it contains an array of unique users)
461 return ($students);
462}
1515a89e 463
848bb113 464/**
465 * This standard function will check all instances of this module
466 * and make sure there are up-to-date events created for each of them.
467 * If courseid = 0, then every chat event in the site is checked, else
468 * only chat events belonging to the course specified are checked.
469 * This function is used, in its new format, by restore_refresh_events()
470 *
471 * @global object
472 * @param int $courseid
473 * @return bool
474 */
8496c4af 475function chat_refresh_events($courseid = 0) {
d3bf6f92 476 global $DB;
8496c4af 477
478 if ($courseid) {
d3bf6f92 479 if (! $chats = $DB->get_records("chat", array("course"=>$courseid))) {
8496c4af 480 return true;
481 }
482 } else {
d3bf6f92 483 if (! $chats = $DB->get_records("chat")) {
8496c4af 484 return true;
485 }
486 }
d3bf6f92 487 $moduleid = $DB->get_field('modules', 'id', array('name'=>'chat'));
8496c4af 488
489 foreach ($chats as $chat) {
9b010a10 490 $cm = get_coursemodule_from_id('chat', $chat->id);
491 $event = new object();
d3bf6f92 492 $event->name = $chat->name;
9b010a10 493 $event->description = format_module_intro('chat', $chat, $cm->id);
8496c4af 494 $event->timestart = $chat->chattime;
495
d3bf6f92 496 if ($event->id = $DB->get_field('event', 'id', array('modulename'=>'chat', 'instance'=>$chat->id))) {
8496c4af 497 update_event($event);
498
499 } else {
500 $event->courseid = $chat->course;
501 $event->groupid = 0;
502 $event->userid = 0;
503 $event->modulename = 'chat';
504 $event->instance = $chat->id;
505 $event->eventtype = $chat->schedule;
506 $event->timeduration = 0;
d3bf6f92 507 $event->visible = $DB->get_field('course_modules', 'visible', array('module'=>$moduleid, 'instance'=>$chat->id));
b5de723d 508
8496c4af 509 add_event($event);
510 }
511 }
512 return true;
513}
514
516121bd 515
1515a89e 516//////////////////////////////////////////////////////////////////////
517/// Functions that require some SQL
518
848bb113 519/**
520 * @global object
521 * @param int $chatid
522 * @param int $groupid
523 * @param int $groupingid
524 * @return array
525 */
a12e11c1 526function chat_get_users($chatid, $groupid=0, $groupingid=0) {
d3bf6f92 527 global $DB;
1515a89e 528
d3bf6f92 529 $params = array('chatid'=>$chatid, 'groupid'=>$groupid, 'groupingid'=>$groupingid);
84a2fdd7 530
531 if ($groupid) {
d3bf6f92 532 $groupselect = " AND (c.groupid=:groupid OR c.groupid='0')";
84a2fdd7 533 } else {
534 $groupselect = "";
535 }
6e5f40ea 536
a12e11c1 537 if (!empty($CFG->enablegroupings) && !(empty($groupingid))) {
d3bf6f92 538 $groupingjoin = "JOIN {groups_members} gm ON u.id = gm.userid
539 JOIN {groupings_groups} gg ON gm.groupid = gg.groupid AND gg.groupingid = :groupingid ";
6e5f40ea 540
a12e11c1 541 } else {
542 $groupingjoin = '';
543 }
b5de723d 544
547ac664 545 return $DB->get_records_sql("SELECT
546 DISTINCT u.id, u.firstname, u.lastname, u.picture, c.lastmessageping, c.firstping, u.imagealt
547 FROM {chat_users} c JOIN {user} u ON u.id = c.userid $groupingjoin
548 WHERE c.chatid = :chatid $groupselect
549 ORDER BY c.firstping ASC", $params);
1515a89e 550}
551
848bb113 552/**
553 * @global object
554 * @param int $chatid
555 * @param int $groupid
556 * @return array
557 */
84a2fdd7 558function chat_get_latest_message($chatid, $groupid=0) {
f33e1ed4 559 global $DB;
1515a89e 560
d3bf6f92 561 $params = array('chatid'=>$chatid, 'groupid'=>$groupid);
1515a89e 562
84a2fdd7 563 if ($groupid) {
d3bf6f92 564 $groupselect = "AND (groupid=:groupid OR groupid=0)";
84a2fdd7 565 } else {
566 $groupselect = "";
567 }
568
547ac664 569 $sql = "SELECT *
570 FROM {chat_messages_current} WHERE chatid = :chatid $groupselect
571 ORDER BY timestamp DESC";
03cedd62 572
547ac664 573 // return the lastest one message
f33e1ed4 574 return $DB->get_record_sql($sql, $params, true);
1515a89e 575}
576
5a8625e4 577
1515a89e 578//////////////////////////////////////////////////////////////////////
516121bd 579// login if not already logged in
1515a89e 580
848bb113 581/**
582 * login if not already logged in
583 *
584 * @global object
585 * @global object
586 * @param int $chatid
587 * @param string $version
588 * @param int $groupid
589 * @param object $course
590 * @return bool|int Returns the chat users sid or false
591 */
a32c7772 592function chat_login_user($chatid, $version, $groupid, $course) {
d3bf6f92 593 global $USER, $DB;
594
595 if (($version != 'sockets') and $chatuser = $DB->get_record('chat_users', array('chatid'=>$chatid, 'userid'=>$USER->id, 'groupid'=>$groupid))) {
7f0483f6 596 // this will update logged user information
516121bd 597 $chatuser->version = $version;
d96466d2 598 $chatuser->ip = $USER->lastip;
516121bd 599 $chatuser->lastping = time();
600 $chatuser->lang = current_language();
1515a89e 601
d96466d2 602 // Sometimes $USER->lastip is not setup properly
d13ef2fb 603 // during login. Update with current value if possible
f83edcb1 604 // or provide a dummy value for the db
d13ef2fb 605 if (empty($chatuser->ip)) {
606 $chatuser->ip = getremoteaddr();
607 if (empty($chatuser->ip)) {
f83edcb1 608 $chatuser->ip = '';
d13ef2fb 609 }
610 }
611
7f0483f6 612 if (($chatuser->course != $course->id) or ($chatuser->userid != $USER->id)) {
516121bd 613 return false;
614 }
a8c31db2 615 $DB->update_record('chat_users', $chatuser);
616
516121bd 617 } else {
6ee78cee 618 $chatuser = new object();
516121bd 619 $chatuser->chatid = $chatid;
620 $chatuser->userid = $USER->id;
621 $chatuser->groupid = $groupid;
622 $chatuser->version = $version;
d96466d2 623 $chatuser->ip = $USER->lastip;
516121bd 624 $chatuser->lastping = $chatuser->firstping = $chatuser->lastmessageping = time();
625 $chatuser->sid = random_string(32);
3dfd307f 626 $chatuser->course = $course->id; //caching - needed for current_language too
627 $chatuser->lang = current_language(); //caching - to resource intensive to find out later
516121bd 628
d96466d2 629 // Sometimes $USER->lastip is not setup properly
274f0091 630 // during login. Update with current value if possible
631 // or provide a dummy value for the db
632 if (empty($chatuser->ip)) {
633 $chatuser->ip = getremoteaddr();
634 if (empty($chatuser->ip)) {
635 $chatuser->ip = '';
636 }
637 }
638
639
a8c31db2 640 $DB->insert_record('chat_users', $chatuser);
516121bd 641
a32c7772 642 if ($version == 'sockets') {
643 // do not send 'enter' message, chatd will do it
644 } else {
6ee78cee 645 $message = new object();
2ac0d13b 646 $message->chatid = $chatuser->chatid;
647 $message->userid = $chatuser->userid;
648 $message->groupid = $groupid;
649 $message->message = 'enter';
650 $message->system = 1;
651 $message->timestamp = time();
652
7826abc7 653 $DB->insert_record('chat_messages', $message);
654 $DB->insert_record('chat_messages_current', $message);
516121bd 655 }
1515a89e 656 }
657
658 return $chatuser->sid;
659}
660
848bb113 661/**
662 * Delete the old and in the way
663 *
664 * @global object
665 * @global object
666 */
7d792369 667function chat_delete_old_users() {
668// Delete the old and in the way
d3bf6f92 669 global $CFG, $DB;
b5012f3e 670
e7fbd0b3 671 $timeold = time() - $CFG->chat_old_ping;
953eb6f3 672 $timeoldext = time() - ($CFG->chat_old_ping*10); // JSless gui_basic needs much longer timeouts
a32c7772 673
d3bf6f92 674 $query = "(version<>'basic' AND lastping<?) OR (version='basic' AND lastping<?)";
675 $params = array($timeold, $timeoldext);
7d792369 676
d3bf6f92 677 if ($oldusers = $DB->get_records_select('chat_users', $query, $params) ) {
678 $DB->delete_records_select('chat_users', $query, $params);
7d792369 679 foreach ($oldusers as $olduser) {
6ee78cee 680 $message = new object();
516121bd 681 $message->chatid = $olduser->chatid;
682 $message->userid = $olduser->userid;
683 $message->groupid = $olduser->groupid;
684 $message->message = 'exit';
685 $message->system = 1;
7d792369 686 $message->timestamp = time();
b5de723d 687
7826abc7 688 $DB->insert_record('chat_messages', $message);
689 $DB->insert_record('chat_messages_current', $message);
7d792369 690 }
691 }
692}
1515a89e 693
848bb113 694/**
695 * Updates chat records so that the next chat time is correct
696 *
697 * @global object
698 * @param int $chatid
699 * @return void
700 */
fcd3a1ee 701function chat_update_chat_times($chatid=0) {
702/// Updates chat records so that the next chat time is correct
d3bf6f92 703 global $DB;
fcd3a1ee 704
705 $timenow = time();
d3bf6f92 706
707 $params = array('timenow'=>$timenow, 'chatid'=>$chatid);
708
fcd3a1ee 709 if ($chatid) {
d3bf6f92 710 if (!$chats[] = $DB->get_record_select("chat", "id = :chatid AND chattime <= :timenow AND schedule > 0", $params)) {
fcd3a1ee 711 return;
712 }
713 } else {
d3bf6f92 714 if (!$chats = $DB->get_records_select("chat", "chattime <= :timenow AND schedule > 0", $params)) {
fcd3a1ee 715 return;
716 }
717 }
718
719 foreach ($chats as $chat) {
720 switch ($chat->schedule) {
721 case 1: // Single event - turn off schedule and disable
722 $chat->chattime = 0;
723 $chat->schedule = 0;
724 break;
725 case 2: // Repeat daily
f0d3bb9e 726 while ($chat->chattime <= $timenow) {
727 $chat->chattime += 24 * 3600;
728 }
fcd3a1ee 729 break;
730 case 3: // Repeat weekly
f0d3bb9e 731 while ($chat->chattime <= $timenow) {
732 $chat->chattime += 7 * 24 * 3600;
733 }
fcd3a1ee 734 break;
735 }
d3bf6f92 736 $DB->update_record("chat", $chat);
737
738 $event = new object(); // Update calendar too
8496c4af 739
d3bf6f92 740 $cond = "modulename='chat' AND instance = :chatid AND timestart <> :chattime";
741 $params = array('chattime'=>$chat->chattime, 'chatid'=>$chatid);
742
743 if ($event->id = $DB->get_field_select('event', 'id', $cond, $params)) {
8496c4af 744 $event->timestart = $chat->chattime;
745 update_event($event);
746 }
fcd3a1ee 747 }
748}
749
848bb113 750/**
751 * @global object
752 * @global object
753 * @param object $message
754 * @param int $courseid
755 * @param object $sender
756 * @param object $currentuser
757 * @param string $chat_lastrow
758 * @return bool|string Returns HTML or false
759 */
aa5c32fd 760function chat_format_message_manually($message, $courseid, $sender, $currentuser, $chat_lastrow=NULL) {
af7bad79 761 global $CFG, $USER, $OUTPUT;
1515a89e 762
6ee78cee 763 $output = new object();
516121bd 764 $output->beep = false; // by default
765 $output->refreshusers = false; // by default
7d792369 766
72989350 767 // Use get_user_timezone() to find the correct timezone for displaying this message:
768 // It's either the current user's timezone or else decided by some Moodle config setting
970f144e 769 // First, "reset" $USER->timezone (which could have been set by a previous call to here)
770 // because otherwise the value for the previous $currentuser will take precedence over $CFG->timezone
771 $USER->timezone = 99;
72989350 772 $tz = get_user_timezone($currentuser->timezone);
b5de723d 773
72989350 774 // Before formatting the message time string, set $USER->timezone to the above.
775 // This will allow dst_offset_on (called by userdate) to work correctly, otherwise the
776 // message times appear off because DST is not taken into account when it should be.
777 $USER->timezone = $tz;
b5de723d 778 $message->strtime = userdate($message->timestamp, get_string('strftimemessage', 'chat'), $tz);
779
af7bad79 780 $message->picture = $OUTPUT->user_picture(moodle_user_picture::make($sender, $courseid));
582de679 781 if ($courseid) {
d3981e38 782 $message->picture = "<a onclick=\"window.open('$CFG->wwwroot/user/view.php?id=$sender->id&amp;course=$courseid')\" href=\"$CFG->wwwroot/user/view.php?id=$sender->id&amp;course=$courseid\">$message->picture</a>";
582de679 783 }
1515a89e 784
aa5c32fd 785 //Calculate the row class
786 if ($chat_lastrow !== NULL) {
787 $rowclass = ' class="r'.$chat_lastrow.'" ';
788 } else {
789 $rowclass = '';
790 }
791
b5de723d 792 // Start processing the message
1515a89e 793
b5de723d 794 if(!empty($message->system)) {
795 // System event
796 $output->text = $message->strtime.': '.get_string('message'.$message->message, 'chat', fullname($sender));
aa5c32fd 797 $output->html = '<table class="chat-event"><tr'.$rowclass.'><td class="picture">'.$message->picture.'</td><td class="text">';
798 $output->html .= '<span class="event">'.$output->text.'</span></td></tr></table>';
6e5f40ea 799 $output->basic = '<dl><dt class="event">'.$message->strtime.': '.get_string('message'.$message->message, 'chat', fullname($sender)).'</dt></dl>';
7d792369 800
516121bd 801 if($message->message == 'exit' or $message->message == 'enter') {
802 $output->refreshusers = true; //force user panel refresh ASAP
803 }
1515a89e 804 return $output;
805 }
806
82a524ef 807 // It's not a system event
b5de723d 808
809 $text = $message->message;
82a524ef 810
811 /// Parse the text to clean and filter it
812
6ee78cee 813 $options = new object();
82a524ef 814 $options->para = false;
815 $text = format_text($text, FORMAT_MOODLE, $options, $courseid);
927a7808 816
b5de723d 817 // And now check for special cases
927a7808 818 $special = false;
819
b5de723d 820 if (substr($text, 0, 5) == 'beep ') {
6e5f40ea 821 /// It's a beep!
927a7808 822 $special = true;
7d792369 823 $beepwho = trim(substr($text, 5));
9f85bed4 824
b5de723d 825 if ($beepwho == 'all') { // everyone
826 $outinfo = $message->strtime.': '.get_string('messagebeepseveryone', 'chat', fullname($sender));
827 $outmain = '';
828 $output->beep = true; // (eventually this should be set to
7d792369 829 // to a filename uploaded by the user)
830
82a524ef 831 } else if ($beepwho == $currentuser->id) { // current user
b5de723d 832 $outinfo = $message->strtime.': '.get_string('messagebeepsyou', 'chat', fullname($sender));
833 $outmain = '';
7d792369 834 $output->beep = true;
264867fd 835
0eda0a46 836 } else { //something is not caught?
7d792369 837 return false;
838 }
b5de723d 839 } else if (substr($text, 0, 1) == '/') { /// It's a user command
1d507186 840 // support some IRC commands
841 $pattern = '#(^\/)(\w+).*#';
842 preg_match($pattern, trim($text), $matches);
843 $command = $matches[2];
844 switch ($command){
845 case 'me':
927a7808 846 $special = true;
b5de723d 847 $outinfo = $message->strtime;
1d507186 848 $outmain = '*** <b>'.$sender->firstname.' '.substr($text, 4).'</b>';
849 break;
1515a89e 850 }
7f0483f6 851 } elseif (substr($text, 0, 2) == 'To') {
852 $pattern = '#To[[:space:]](.*):(.*)#';
853 preg_match($pattern, trim($text), $matches);
854 $special = true;
855 $outinfo = $message->strtime;
856 $outmain = $sender->firstname.' '.get_string('saidto', 'chat').' <i>'.$matches[1].'</i>: '.$matches[2];
927a7808 857 }
9f85bed4 858
927a7808 859 if(!$special) {
b5de723d 860 $outinfo = $message->strtime.' '.$sender->firstname;
7d792369 861 $outmain = $text;
1515a89e 862 }
264867fd 863
9f85bed4 864 /// Format the message as a small table
1515a89e 865
b5de723d 866 $output->text = strip_tags($outinfo.': '.$outmain);
7d792369 867
5379d249 868 $output->html = "<table class=\"chat-message\"><tr$rowclass><td class=\"picture\" valign=\"top\">$message->picture</td><td class=\"text\">";
aa5c32fd 869 $output->html .= "<span class=\"title\">$outinfo</span>";
7d792369 870 if ($outmain) {
871 $output->html .= ": $outmain";
6e5f40ea 872 $output->basic = '<dl><dt class="title">'.$outinfo.':</dt><dd class="text">'.$outmain.'</dd></dl>';
6ee78cee 873 } else {
6e5f40ea 874 $output->basic = '<dl><dt class="title">'.$outinfo.'</dt></dl>';
7d792369 875 }
aa5c32fd 876 $output->html .= "</td></tr></table>";
7d792369 877 return $output;
b5de723d 878}
879
848bb113 880/**
881 * @global object
882 * @param object $message
883 * @param int $courseid
884 * @param object $currentuser
885 * @param string $chat_lastrow
886 * @return bool|string Returns HTML or false
887 */
aa5c32fd 888function chat_format_message($message, $courseid, $currentuser, $chat_lastrow=NULL) {
b5de723d 889/// Given a message object full of information, this function
890/// formats it appropriately into text and html, then
891/// returns the formatted data.
d3bf6f92 892 global $DB;
b5de723d 893
78c98892 894 static $users; // Cache user lookups
895
896 if (isset($users[$message->userid])) {
897 $user = $users[$message->userid];
d3bf6f92 898 } else if ($user = $DB->get_record('user', array('id'=>$message->userid), 'id,picture,firstname,lastname')) {
78c98892 899 $users[$message->userid] = $user;
900 } else {
901 return NULL;
b5de723d 902 }
aa5c32fd 903 return chat_format_message_manually($message, $courseid, $user, $currentuser, $chat_lastrow);
1515a89e 904}
905
848bb113 906/**
907 * @return array
908 */
f3221af9 909function chat_get_view_actions() {
910 return array('view','view all','report');
911}
912
848bb113 913/**
914 * @return array
915 */
f3221af9 916function chat_get_post_actions() {
917 return array('talk');
918}
919
848bb113 920/**
921 * @global object
922 * @global object
923 * @param array $courses
924 * @param array $htmlarray Passed by reference
925 */
9ca0187e 926function chat_print_overview($courses, &$htmlarray) {
927 global $USER, $CFG;
928
929 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
930 return array();
931 }
932
933 if (!$chats = get_all_instances_in_courses('chat',$courses)) {
934 return;
935 }
936
937 $strchat = get_string('modulename', 'chat');
938 $strnextsession = get_string('nextsession', 'chat');
9ca0187e 939
940 foreach ($chats as $chat) {
9ca0187e 941 if ($chat->chattime and $chat->schedule) { // A chat is scheduled
a2a37336 942 $str = '<div class="chat overview"><div class="name">'.
943 $strchat.': <a '.($chat->visible?'':' class="dimmed"').
944 ' href="'.$CFG->wwwroot.'/mod/chat/view.php?id='.$chat->coursemodule.'">'.
945 $chat->name.'</a></div>';
946 $str .= '<div class="info">'.$strnextsession.': '.userdate($chat->chattime).'</div></div>';
947
948 if (empty($htmlarray[$chat->course]['chat'])) {
949 $htmlarray[$chat->course]['chat'] = $str;
950 } else {
951 $htmlarray[$chat->course]['chat'] .= $str;
952 }
9ca0187e 953 }
9ca0187e 954 }
955}
956
0b5a80a1 957
958/**
959 * Implementation of the function for printing the form elements that control
960 * whether the course reset functionality affects the chat.
848bb113 961 *
962 * @param object $mform form passed by reference
0b5a80a1 963 */
964function chat_reset_course_form_definition(&$mform) {
965 $mform->addElement('header', 'chatheader', get_string('modulenameplural', 'chat'));
966 $mform->addElement('advcheckbox', 'reset_chat', get_string('removemessages','chat'));
967}
968
969/**
970 * Course reset form defaults.
848bb113 971 *
972 * @param object $course
973 * @return array
0b5a80a1 974 */
975function chat_reset_course_form_defaults($course) {
976 return array('reset_chat'=>1);
977}
978
979/**
980 * Actual implementation of the rest coures functionality, delete all the
981 * chat messages for course $data->courseid.
848bb113 982 *
983 * @global object
984 * @global object
985 * @param object $data the data submitted from the reset course.
0b5a80a1 986 * @return array status array
987 */
988function chat_reset_userdata($data) {
d3bf6f92 989 global $CFG, $DB;
0b5a80a1 990
991 $componentstr = get_string('modulenameplural', 'chat');
992 $status = array();
993
994 if (!empty($data->reset_chat)) {
995 $chatessql = "SELECT ch.id
d3bf6f92 996 FROM {chat} ch
997 WHERE ch.course=?";
998 $params = array($data->courseid);
0b5a80a1 999
d3bf6f92 1000 $DB->delete_records_select('chat_messages', "chatid IN ($chatessql)", $params);
6e5f40ea 1001 $DB->delete_records_select('chat_messages_current', "chatid IN ($chatessql)", $params);
d3bf6f92 1002 $DB->delete_records_select('chat_users', "chatid IN ($chatessql)", $params);
0b5a80a1 1003 $status[] = array('component'=>$componentstr, 'item'=>get_string('removemessages', 'chat'), 'error'=>false);
1004 }
1005
1006 /// updating dates - shift may be negative too
1007 if ($data->timeshift) {
1008 shift_course_mod_dates('chat', array('chattime'), $data->timeshift, $data->courseid);
1009 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
1010 }
1011
1012 return $status;
1013}
1014
f432bebf 1015/**
1016 * Returns all other caps used in module
848bb113 1017 *
1018 * @return array
f432bebf 1019 */
1020function chat_get_extra_capabilities() {
1021 return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames');
1022}
1023
848bb113 1024/**
b2d5a79a 1025 * @package mod-chat
848bb113 1026 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
1027 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1028 */
47cfd331 1029class chat_portfolio_caller extends portfolio_module_caller_base {
848bb113 1030 /** @var object */
47cfd331 1031 private $chat;
848bb113 1032 /** @var int Timestamp */
0d06b6fd 1033 protected $start;
848bb113 1034 /** @var int Timestamp */
0d06b6fd 1035 protected $end;
848bb113 1036 /**
1037 * @return array
1038 */
0d06b6fd 1039 public static function expected_callbackargs() {
1040 return array(
1041 'id' => true,
1042 'start' => false,
1043 'end' => false,
1044 );
1045 }
848bb113 1046 /**
1047 * @global object
1048 */
0d06b6fd 1049 public function load_data() {
1050 global $DB;
47cfd331 1051
0d06b6fd 1052 if (!$this->cm = get_coursemodule_from_id('chat', $this->id)) {
34035201 1053 throw new portfolio_caller_exception('invalidid', 'chat');
47cfd331 1054 }
1055 $this->chat = $DB->get_record('chat', array('id' => $this->cm->instance));
1056 $select = 'chatid = ?';
1057 $params = array($this->chat->id);
0d06b6fd 1058 if ($this->start && $this->end) {
47cfd331 1059 $select .= ' AND timestamp >= ? AND timestamp <= ?';
0d06b6fd 1060 $params[] = $this->start;
1061 $params[] = $this->end;
47cfd331 1062 }
1063 $this->messages = $DB->get_records_select(
1064 'chat_messages',
1065 $select,
1066 $params,
1067 'timestamp ASC'
1068 );
1069 $select .= ' AND userid = ?';
0d06b6fd 1070 $params[] = $this->user->id;
47cfd331 1071 $this->participated = $DB->record_exists_select(
1072 'chat_messages',
1073 $select,
1074 $params
1075 );
0d06b6fd 1076 }
848bb113 1077 /**
1078 * @return array
1079 */
a7d90683 1080 public static function supported_formats() {
6be1dcae 1081 return array(PORTFOLIO_FORMAT_PLAINHTML);
47cfd331 1082 }
848bb113 1083 /**
1084 *
1085 */
47cfd331 1086 public function expected_time() {
d8606b20 1087 return portfolio_expected_time_db(count($this->messages));
47cfd331 1088 }
848bb113 1089 /**
1090 * @return string
1091 */
47cfd331 1092 public function get_sha1() {
1093 $str = '';
1094 ksort($this->messages);
1095 foreach ($this->messages as $m) {
1096 $str .= implode('', (array)$m);
1097 }
1098 return sha1($str);
1099 }
1100
848bb113 1101 /**
1102 * @return bool
1103 */
47cfd331 1104 public function check_permissions() {
1105 $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
1106 return has_capability('mod/chat:exportsession', $context)
1107 || ($this->participated
1108 && has_capability('mod/chat:exportparticipatedsession', $context));
1109 }
848bb113 1110
1111 /**
1112 * @todo Document this function
1113 */
47cfd331 1114 public function prepare_package() {
1115 $content = '';
5a04379a 1116 $lasttime = 0;
1117 $sessiongap = 5 * 60; // 5 minutes silence means a new session
47cfd331 1118 foreach ($this->messages as $message) { // We are walking FORWARDS through messages
5a04379a 1119 $m = clone $message; // grrrrrr - this causes the sha1 to change as chat_format_message changes what it's passed.
47cfd331 1120 $formatmessage = chat_format_message($m, null, $this->user);
1121 if (!isset($formatmessage->html)) {
1122 continue;
1123 }
5a04379a 1124 if (empty($lasttime) || (($message->timestamp - $lasttime) > $sessiongap)) {
1125 $content .= '<hr />';
1126 $content .= userdate($message->timestamp);
1127 }
47cfd331 1128 $content .= $formatmessage->html;
5a04379a 1129 $lasttime = $message->timestamp;
47cfd331 1130 }
1131 $content = preg_replace('/\<img[^>]*\>/', '', $content);
1132
6be1dcae 1133 $this->exporter->write_new_file($content, clean_filename($this->cm->name . '-session.html'), false);
47cfd331 1134 }
1135
848bb113 1136 /**
1137 * @return string
1138 */
47cfd331 1139 public static function display_name() {
1140 return get_string('modulename', 'chat');
1141 }
1142
848bb113 1143 /**
1144 * @global object
1145 * @return string
1146 */
47cfd331 1147 public function get_return_url() {
1148 global $CFG;
1149
1150 return $CFG->wwwroot . '/mod/chat/report.php?id='
1151 . $this->cm->id . ((isset($this->start))
1152 ? '&start=' . $this->start . '&end=' . $this->end
1153 : '');
1154 }
1155}
1156
42f103be 1157/**
1158 * @param string $feature FEATURE_xx constant for requested feature
1159 * @return mixed True if module supports feature, null if doesn't know
1160 */
1161function chat_supports($feature) {
1162 switch($feature) {
1163 case FEATURE_GROUPS: return true;
1164 case FEATURE_GROUPINGS: return true;
1165 case FEATURE_GROUPMEMBERSONLY: return true;
dc5c2bd9 1166 case FEATURE_MOD_INTRO: return true;
42f103be 1167 case FEATURE_COMPLETION_TRACKS_VIEWS: return false;
1168 case FEATURE_GRADE_HAS_GRADE: return false;
1169 case FEATURE_GRADE_OUTCOMES: return true;
1170
1171 default: return null;
1172 }
1173}