Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This file contains functions used by the log reports 19 * 20 * This files lists the functions that are used during the log report generation. 21 * 22 * @package report_log 23 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die; 28 29 if (!defined('REPORT_LOG_MAX_DISPLAY')) { 30 define('REPORT_LOG_MAX_DISPLAY', 150); // days 31 } 32 33 require_once (__DIR__.'/lib.php'); 34 35 /** 36 * This function is used to generate and display the log activity graph 37 * 38 * @global stdClass $CFG 39 * @param stdClass $course course instance 40 * @param int|stdClass $user id/object of the user whose logs are needed 41 * @param string $typeormode type of logs graph needed (usercourse.png/userday.png) or the mode (today, all). 42 * @param int $date timestamp in GMT (seconds since epoch) 43 * @param string $logreader Log reader. 44 * @return void 45 */ 46 function report_log_print_graph($course, $user, $typeormode, $date=0, $logreader='') { 47 global $CFG, $OUTPUT; 48 49 if (!is_object($user)) { 50 $user = core_user::get_user($user); 51 } 52 53 $logmanager = get_log_manager(); 54 $readers = $logmanager->get_readers(); 55 56 if (empty($logreader)) { 57 $reader = reset($readers); 58 } else { 59 $reader = $readers[$logreader]; 60 } 61 // If reader is not a sql_internal_table_reader and not legacy store then don't show graph. 62 if (!($reader instanceof \core\log\sql_internal_table_reader)) { 63 return array(); 64 } 65 $coursecontext = context_course::instance($course->id); 66 67 $a = new stdClass(); 68 $a->coursename = format_string($course->shortname, true, array('context' => $coursecontext)); 69 $a->username = fullname($user, true); 70 71 if ($typeormode == 'today' || $typeormode == 'userday.png') { 72 $logs = report_log_usertoday_data($course, $user, $date, $logreader); 73 $title = get_string("hitsoncoursetoday", "", $a); 74 } else if ($typeormode == 'all' || $typeormode == 'usercourse.png') { 75 $logs = report_log_userall_data($course, $user, $logreader); 76 $title = get_string("hitsoncourse", "", $a); 77 } 78 79 if (!empty($CFG->preferlinegraphs)) { 80 $chart = new \core\chart_line(); 81 } else { 82 $chart = new \core\chart_bar(); 83 } 84 85 $series = new \core\chart_series(get_string("hits"), $logs['series']); 86 $chart->add_series($series); 87 $chart->set_title($title); 88 $chart->set_labels($logs['labels']); 89 $yaxis = $chart->get_yaxis(0, true); 90 $yaxis->set_label(get_string("hits")); 91 $yaxis->set_stepsize(max(1, round(max($logs['series']) / 10))); 92 93 echo $OUTPUT->render($chart); 94 } 95 96 /** 97 * Select all log records for a given course and user 98 * 99 * @param int $userid The id of the user as found in the 'user' table. 100 * @param int $courseid The id of the course as found in the 'course' table. 101 * @param string $coursestart unix timestamp representing course start date and time. 102 * @param string $logreader log reader to use. 103 * @return array 104 */ 105 function report_log_usercourse($userid, $courseid, $coursestart, $logreader = '') { 106 global $DB; 107 108 $logmanager = get_log_manager(); 109 $readers = $logmanager->get_readers(); 110 if (empty($logreader)) { 111 $reader = reset($readers); 112 } else { 113 $reader = $readers[$logreader]; 114 } 115 116 // If reader is not a sql_internal_table_reader and not legacy store then return. 117 if (!($reader instanceof \core\log\sql_internal_table_reader)) { 118 return array(); 119 } 120 121 $coursestart = (int)$coursestart; // Note: unfortunately pg complains if you use name parameter or column alias in GROUP BY. 122 $logtable = $reader->get_internal_log_table_name(); 123 $timefield = 'timecreated'; 124 $coursefield = 'courseid'; 125 $nonanonymous = 'AND anonymous = 0'; 126 127 $params = array(); 128 $courseselect = ''; 129 if ($courseid) { 130 $courseselect = "AND $coursefield = :courseid"; 131 $params['courseid'] = $courseid; 132 } 133 $params['userid'] = $userid; 134 return $DB->get_records_sql("SELECT FLOOR(($timefield - $coursestart)/" . DAYSECS . ") AS day, COUNT(*) AS num 135 FROM {" . $logtable . "} 136 WHERE userid = :userid 137 AND $timefield > $coursestart $courseselect $nonanonymous 138 GROUP BY FLOOR(($timefield - $coursestart)/" . DAYSECS .")", $params); 139 } 140 141 /** 142 * Select all log records for a given course, user, and day 143 * 144 * @param int $userid The id of the user as found in the 'user' table. 145 * @param int $courseid The id of the course as found in the 'course' table. 146 * @param string $daystart unix timestamp of the start of the day for which the logs needs to be retrived 147 * @param string $logreader log reader to use. 148 * @return array 149 */ 150 function report_log_userday($userid, $courseid, $daystart, $logreader = '') { 151 global $DB; 152 $logmanager = get_log_manager(); 153 $readers = $logmanager->get_readers(); 154 if (empty($logreader)) { 155 $reader = reset($readers); 156 } else { 157 $reader = $readers[$logreader]; 158 } 159 160 // If reader is not a sql_internal_table_reader and not legacy store then return. 161 if (!($reader instanceof \core\log\sql_internal_table_reader)) { 162 return array(); 163 } 164 165 $daystart = (int)$daystart; // Note: unfortunately pg complains if you use name parameter or column alias in GROUP BY. 166 167 $logtable = $reader->get_internal_log_table_name(); 168 $timefield = 'timecreated'; 169 $coursefield = 'courseid'; 170 $nonanonymous = 'AND anonymous = 0'; 171 $params = array('userid' => $userid); 172 173 $courseselect = ''; 174 if ($courseid) { 175 $courseselect = "AND $coursefield = :courseid"; 176 $params['courseid'] = $courseid; 177 } 178 return $DB->get_records_sql("SELECT FLOOR(($timefield - $daystart)/" . HOURSECS . ") AS hour, COUNT(*) AS num 179 FROM {" . $logtable . "} 180 WHERE userid = :userid 181 AND $timefield > $daystart $courseselect $nonanonymous 182 GROUP BY FLOOR(($timefield - $daystart)/" . HOURSECS . ") ", $params); 183 } 184 185 /** 186 * This function is used to generate and display Mnet selector form 187 * 188 * @global stdClass $USER 189 * @global stdClass $CFG 190 * @global stdClass $SITE 191 * @global moodle_database $DB 192 * @global core_renderer $OUTPUT 193 * @global stdClass $SESSION 194 * @uses CONTEXT_SYSTEM 195 * @uses COURSE_MAX_COURSES_PER_DROPDOWN 196 * @uses CONTEXT_COURSE 197 * @uses SEPARATEGROUPS 198 * @param int $hostid host id 199 * @param stdClass $course course instance 200 * @param int $selecteduser id of the selected user 201 * @param string $selecteddate Date selected 202 * @param string $modname course_module->id 203 * @param string $modid number or 'site_errors' 204 * @param string $modaction an action as recorded in the logs 205 * @param int $selectedgroup Group to display 206 * @param int $showcourses whether to show courses if we're over our limit. 207 * @param int $showusers whether to show users if we're over our limit. 208 * @param string $logformat Format of the logs (downloadascsv, showashtml, downloadasods, downloadasexcel) 209 * @return void 210 */ 211 function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0, $selecteddate='today', 212 $modname="", $modid=0, $modaction='', $selectedgroup=-1, $showcourses=0, $showusers=0, $logformat='showashtml') { 213 214 global $USER, $CFG, $SITE, $DB, $OUTPUT, $SESSION; 215 require_once $CFG->dirroot.'/mnet/peer.php'; 216 217 $mnet_peer = new mnet_peer(); 218 $mnet_peer->set_id($hostid); 219 220 $sql = "SELECT DISTINCT course, hostid, coursename FROM {mnet_log}"; 221 $courses = $DB->get_records_sql($sql); 222 $remotecoursecount = count($courses); 223 224 // first check to see if we can override showcourses and showusers 225 $numcourses = $remotecoursecount + $DB->count_records('course'); 226 if ($numcourses < COURSE_MAX_COURSES_PER_DROPDOWN && !$showcourses) { 227 $showcourses = 1; 228 } 229 230 $sitecontext = context_system::instance(); 231 232 // Context for remote data is always SITE 233 // Groups for remote data are always OFF 234 if ($hostid == $CFG->mnet_localhost_id) { 235 $context = context_course::instance($course->id); 236 237 /// Setup for group handling. 238 if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 239 $selectedgroup = -1; 240 $showgroups = false; 241 } else if ($course->groupmode) { 242 $showgroups = true; 243 } else { 244 $selectedgroup = 0; 245 $showgroups = false; 246 } 247 248 if ($selectedgroup === -1) { 249 if (isset($SESSION->currentgroup[$course->id])) { 250 $selectedgroup = $SESSION->currentgroup[$course->id]; 251 } else { 252 $selectedgroup = groups_get_all_groups($course->id, $USER->id); 253 if (is_array($selectedgroup)) { 254 $selectedgroup = array_shift(array_keys($selectedgroup)); 255 $SESSION->currentgroup[$course->id] = $selectedgroup; 256 } else { 257 $selectedgroup = 0; 258 } 259 } 260 } 261 262 } else { 263 $context = $sitecontext; 264 } 265 266 // Get all the possible users 267 $users = array(); 268 269 // Define limitfrom and limitnum for queries below 270 // If $showusers is enabled... don't apply limitfrom and limitnum 271 $limitfrom = empty($showusers) ? 0 : ''; 272 $limitnum = empty($showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : ''; 273 274 // If looking at a different host, we're interested in all our site users 275 if ($hostid == $CFG->mnet_localhost_id && $course->id != SITEID) { 276 $userfieldsapi = \core_user\fields::for_name(); 277 $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, ' . 278 $userfieldsapi->get_sql('u', false, '', '', false)->selects, 279 null, $limitfrom, $limitnum); 280 } else { 281 // this may be a lot of users :-( 282 $userfieldsapi = \core_user\fields::for_name(); 283 $courseusers = $DB->get_records('user', array('deleted' => 0), 'lastaccess DESC', 'id, ' . 284 $userfieldsapi->get_sql('', false, '', '', false)->selects, 285 $limitfrom, $limitnum); 286 } 287 288 if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$showusers) { 289 $showusers = 1; 290 } 291 292 if ($showusers) { 293 if ($courseusers) { 294 foreach ($courseusers as $courseuser) { 295 $users[$courseuser->id] = fullname($courseuser, has_capability('moodle/site:viewfullnames', $context)); 296 } 297 } 298 $users[$CFG->siteguest] = get_string('guestuser'); 299 } 300 301 // Get all the hosts that have log records 302 $sql = "select distinct 303 h.id, 304 h.name 305 from 306 {mnet_host} h, 307 {mnet_log} l 308 where 309 h.id = l.hostid 310 order by 311 h.name"; 312 313 if ($hosts = $DB->get_records_sql($sql)) { 314 foreach($hosts as $host) { 315 $hostarray[$host->id] = $host->name; 316 } 317 } 318 319 $hostarray[$CFG->mnet_localhost_id] = $SITE->fullname; 320 asort($hostarray); 321 322 $dropdown = array(); 323 324 foreach($hostarray as $hostid => $name) { 325 $courses = array(); 326 $sites = array(); 327 if ($CFG->mnet_localhost_id == $hostid) { 328 if (has_capability('report/log:view', $sitecontext) && $showcourses) { 329 if ($ccc = $DB->get_records("course", null, "fullname","id,shortname,fullname,category")) { 330 foreach ($ccc as $cc) { 331 if ($cc->id == SITEID) { 332 $sites["$hostid/$cc->id"] = format_string($cc->fullname).' ('.get_string('site').')'; 333 } else { 334 $courses["$hostid/$cc->id"] = format_string(get_course_display_name_for_list($cc)); 335 } 336 } 337 } 338 } 339 } else { 340 if (has_capability('report/log:view', $sitecontext) && $showcourses) { 341 $sql = "SELECT DISTINCT course, coursename FROM {mnet_log} where hostid = ?"; 342 if ($ccc = $DB->get_records_sql($sql, array($hostid))) { 343 foreach ($ccc as $cc) { 344 if (1 == $cc->course) { // TODO: this might be wrong - site course may have another id 345 $sites["$hostid/$cc->course"] = $cc->coursename.' ('.get_string('site').')'; 346 } else { 347 $courses["$hostid/$cc->course"] = $cc->coursename; 348 } 349 } 350 } 351 } 352 } 353 354 asort($courses); 355 $dropdown[] = array($name=>($sites + $courses)); 356 } 357 358 359 $activities = array(); 360 $selectedactivity = ""; 361 362 $modinfo = get_fast_modinfo($course); 363 if (!empty($modinfo->cms)) { 364 $section = 0; 365 $thissection = array(); 366 foreach ($modinfo->cms as $cm) { 367 // Exclude activities that aren't visible or have no view link (e.g. label). Account for folder being displayed inline. 368 if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) { 369 continue; 370 } 371 if ($cm->sectionnum > 0 and $section <> $cm->sectionnum) { 372 $activities[] = $thissection; 373 $thissection = array(); 374 } 375 $section = $cm->sectionnum; 376 $modname = strip_tags($cm->get_formatted_name()); 377 if (core_text::strlen($modname) > 55) { 378 $modname = core_text::substr($modname, 0, 50)."..."; 379 } 380 if (!$cm->visible) { 381 $modname = "(".$modname.")"; 382 } 383 $key = get_section_name($course, $cm->sectionnum); 384 if (!isset($thissection[$key])) { 385 $thissection[$key] = array(); 386 } 387 $thissection[$key][$cm->id] = $modname; 388 389 if ($cm->id == $modid) { 390 $selectedactivity = "$cm->id"; 391 } 392 } 393 if (!empty($thissection)) { 394 $activities[] = $thissection; 395 } 396 } 397 398 if (has_capability('report/log:view', $sitecontext) && !$course->category) { 399 $activities["site_errors"] = get_string("siteerrors"); 400 if ($modid === "site_errors") { 401 $selectedactivity = "site_errors"; 402 } 403 } 404 405 $strftimedate = get_string("strftimedate"); 406 $strftimedaydate = get_string("strftimedaydate"); 407 408 asort($users); 409 410 // Prepare the list of action options. 411 $actions = array( 412 'view' => get_string('view'), 413 'add' => get_string('add'), 414 'update' => get_string('update'), 415 'delete' => get_string('delete'), 416 '-view' => get_string('allchanges') 417 ); 418 419 // Get all the possible dates 420 // Note that we are keeping track of real (GMT) time and user time 421 // User time is only used in displays - all calcs and passing is GMT 422 423 $timenow = time(); // GMT 424 425 // What day is it now for the user, and when is midnight that day (in GMT). 426 $timemidnight = $today = usergetmidnight($timenow); 427 428 // Put today up the top of the list 429 $dates = array( 430 "0" => get_string('alldays'), 431 "$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) 432 ); 433 434 if (!$course->startdate or ($course->startdate > $timenow)) { 435 $course->startdate = $course->timecreated; 436 } 437 438 $numdates = 1; 439 while ($timemidnight > $course->startdate and $numdates < 365) { 440 $timemidnight = $timemidnight - 86400; 441 $timenow = $timenow - 86400; 442 $dates["$timemidnight"] = userdate($timenow, $strftimedaydate); 443 $numdates++; 444 } 445 446 if ($selecteddate === "today") { 447 $selecteddate = $today; 448 } 449 450 echo "<form class=\"logselectform\" action=\"$CFG->wwwroot/report/log/index.php\" method=\"get\">\n"; 451 echo "<div>\n";//invisible fieldset here breaks wrapping 452 echo "<input type=\"hidden\" name=\"chooselog\" value=\"1\" />\n"; 453 echo "<input type=\"hidden\" name=\"showusers\" value=\"$showusers\" />\n"; 454 echo "<input type=\"hidden\" name=\"showcourses\" value=\"$showcourses\" />\n"; 455 if (has_capability('report/log:view', $sitecontext) && $showcourses) { 456 $cid = empty($course->id)? '1' : $course->id; 457 echo html_writer::label(get_string('selectacoursesite'), 'menuhost_course', false, array('class' => 'accesshide')); 458 echo html_writer::select($dropdown, "host_course", $hostid.'/'.$cid); 459 } else { 460 $courses = array(); 461 $courses[$course->id] = get_course_display_name_for_list($course) . ((empty($course->category)) ? ' ('.get_string('site').') ' : ''); 462 echo html_writer::label(get_string('selectacourse'), 'menuid', false, array('class' => 'accesshide')); 463 echo html_writer::select($courses,"id",$course->id, false); 464 if (has_capability('report/log:view', $sitecontext)) { 465 $a = new stdClass(); 466 $a->url = "$CFG->wwwroot/report/log/index.php?chooselog=0&group=$selectedgroup&user=$selecteduser" 467 ."&id=$course->id&date=$selecteddate&modid=$selectedactivity&showcourses=1&showusers=$showusers"; 468 print_string('logtoomanycourses','moodle',$a); 469 } 470 } 471 472 if ($showgroups) { 473 if ($cgroups = groups_get_all_groups($course->id)) { 474 foreach ($cgroups as $cgroup) { 475 $groups[$cgroup->id] = $cgroup->name; 476 } 477 } 478 else { 479 $groups = array(); 480 } 481 echo html_writer::label(get_string('selectagroup'), 'menugroup', false, array('class' => 'accesshide')); 482 echo html_writer::select($groups, "group", $selectedgroup, get_string("allgroups")); 483 } 484 485 if ($showusers) { 486 echo html_writer::label(get_string('participantslist'), 'menuuser', false, array('class' => 'accesshide')); 487 echo html_writer::select($users, "user", $selecteduser, get_string("allparticipants")); 488 } 489 else { 490 $users = array(); 491 if (!empty($selecteduser)) { 492 $user = $DB->get_record('user', array('id'=>$selecteduser)); 493 $users[$selecteduser] = fullname($user); 494 } 495 else { 496 $users[0] = get_string('allparticipants'); 497 } 498 echo html_writer::label(get_string('participantslist'), 'menuuser', false, array('class' => 'accesshide')); 499 echo html_writer::select($users, "user", $selecteduser, false); 500 $a = new stdClass(); 501 $a->url = "$CFG->wwwroot/report/log/index.php?chooselog=0&group=$selectedgroup&user=$selecteduser" 502 ."&id=$course->id&date=$selecteddate&modid=$selectedactivity&showusers=1&showcourses=$showcourses"; 503 print_string('logtoomanyusers','moodle',$a); 504 } 505 506 echo html_writer::label(get_string('date'), 'menudate', false, array('class' => 'accesshide')); 507 echo html_writer::select($dates, "date", $selecteddate, false); 508 echo html_writer::label(get_string('showreports'), 'menumodid', false, array('class' => 'accesshide')); 509 echo html_writer::select($activities, "modid", $selectedactivity, get_string("allactivities")); 510 echo html_writer::label(get_string('actions'), 'menumodaction', false, array('class' => 'accesshide')); 511 echo html_writer::select($actions, 'modaction', $modaction, get_string("allactions")); 512 513 $logformats = array('showashtml' => get_string('displayonpage'), 514 'downloadascsv' => get_string('downloadtext'), 515 'downloadasods' => get_string('downloadods'), 516 'downloadasexcel' => get_string('downloadexcel')); 517 echo html_writer::label(get_string('logsformat', 'report_log'), 'menulogformat', false, array('class' => 'accesshide')); 518 echo html_writer::select($logformats, 'logformat', $logformat, false); 519 echo '<input type="submit" value="'.get_string('gettheselogs').'" />'; 520 echo '</div>'; 521 echo '</form>'; 522 } 523 524 /** 525 * Fetch logs since the start of the courses and structure in series and labels to be sent to Chart API. 526 * 527 * @param stdClass $course the course object 528 * @param stdClass $user user object 529 * @param string $logreader the log reader where the logs are. 530 * @return array structured array to be sent to chart API, split in two indexes (series and labels). 531 */ 532 function report_log_userall_data($course, $user, $logreader) { 533 global $CFG; 534 $site = get_site(); 535 $timenow = time(); 536 $logs = []; 537 if ($course->id == $site->id) { 538 $courseselect = 0; 539 } else { 540 $courseselect = $course->id; 541 } 542 543 $maxseconds = REPORT_LOG_MAX_DISPLAY * 3600 * 24; // Seconds. 544 if ($timenow - $course->startdate > $maxseconds) { 545 $course->startdate = $timenow - $maxseconds; 546 } 547 548 if (!empty($CFG->loglifetime)) { 549 $maxseconds = $CFG->loglifetime * 3600 * 24; // Seconds. 550 if ($timenow - $course->startdate > $maxseconds) { 551 $course->startdate = $timenow - $maxseconds; 552 } 553 } 554 555 $timestart = $coursestart = usergetmidnight($course->startdate); 556 557 $i = 0; 558 $logs['series'][$i] = 0; 559 $logs['labels'][$i] = 0; 560 while ($timestart < $timenow) { 561 $timefinish = $timestart + 86400; 562 $logs['labels'][$i] = userdate($timestart, "%a %d %b"); 563 $logs['series'][$i] = 0; 564 $i++; 565 $timestart = $timefinish; 566 } 567 $rawlogs = report_log_usercourse($user->id, $courseselect, $coursestart, $logreader); 568 569 foreach ($rawlogs as $rawlog) { 570 if (isset($logs['labels'][$rawlog->day])) { 571 $logs['series'][$rawlog->day] = $rawlog->num; 572 } 573 } 574 575 return $logs; 576 } 577 578 /** 579 * Fetch logs of the current day and structure in series and labels to be sent to Chart API. 580 * 581 * @param stdClass $course the course object 582 * @param stdClass $user user object 583 * @param int $date A time of a day (in GMT). 584 * @param string $logreader the log reader where the logs are. 585 * @return array $logs structured array to be sent to chart API, split in two indexes (series and labels). 586 */ 587 function report_log_usertoday_data($course, $user, $date, $logreader) { 588 $site = get_site(); 589 $logs = []; 590 591 if ($course->id == $site->id) { 592 $courseselect = 0; 593 } else { 594 $courseselect = $course->id; 595 } 596 597 if ($date) { 598 $daystart = usergetmidnight($date); 599 } else { 600 $daystart = usergetmidnight(time()); 601 } 602 603 for ($i = 0; $i <= 23; $i++) { 604 $hour = $daystart + $i * 3600; 605 $logs['series'][$i] = 0; 606 $logs['labels'][$i] = userdate($hour, "%H:00"); 607 } 608 609 $rawlogs = report_log_userday($user->id, $courseselect, $daystart, $logreader); 610 611 foreach ($rawlogs as $rawlog) { 612 if (isset($logs['labels'][$rawlog->hour])) { 613 $logs['series'][$rawlog->hour] = $rawlog->num; 614 } 615 } 616 617 return $logs; 618 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body