Merge branch 'MDL-29269' of git://github.com/stronk7/moodle
[moodle.git] / course / lib.php
blobfe8630fb5fac7b969987002dc1279aa0b8c44350
1 <?php
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.
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/>.
18 /**
19 * Library of useful functions
21 * @copyright 1999 Martin Dougiamas http://dougiamas.com
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 * @package core
24 * @subpackage course
27 defined('MOODLE_INTERNAL') || die;
29 require_once($CFG->libdir.'/completionlib.php');
30 require_once($CFG->libdir.'/filelib.php');
32 define('COURSE_MAX_LOG_DISPLAY', 150); // days
33 define('COURSE_MAX_LOGS_PER_PAGE', 1000); // records
34 define('COURSE_LIVELOG_REFRESH', 60); // Seconds
35 define('COURSE_MAX_RECENT_PERIOD', 172800); // Two days, in seconds
36 define('COURSE_MAX_SUMMARIES_PER_PAGE', 10); // courses
37 define('COURSE_MAX_COURSES_PER_DROPDOWN',1000); // max courses in log dropdown before switching to optional
38 define('COURSE_MAX_USERS_PER_DROPDOWN',1000); // max users in log dropdown before switching to optional
39 define('FRONTPAGENEWS', '0');
40 define('FRONTPAGECOURSELIST', '1');
41 define('FRONTPAGECATEGORYNAMES', '2');
42 define('FRONTPAGETOPICONLY', '3');
43 define('FRONTPAGECATEGORYCOMBO', '4');
44 define('FRONTPAGECOURSELIMIT', 200); // maximum number of courses displayed on the frontpage
45 define('EXCELROWS', 65535);
46 define('FIRSTUSEDEXCELROW', 3);
48 define('MOD_CLASS_ACTIVITY', 0);
49 define('MOD_CLASS_RESOURCE', 1);
51 function make_log_url($module, $url) {
52 switch ($module) {
53 case 'course':
54 case 'file':
55 case 'login':
56 case 'lib':
57 case 'admin':
58 case 'calendar':
59 case 'mnet course':
60 if (strpos($url, '../') === 0) {
61 $url = ltrim($url, '.');
62 } else {
63 $url = "/course/$url";
65 break;
66 case 'user':
67 case 'blog':
68 $url = "/$module/$url";
69 break;
70 case 'upload':
71 $url = $url;
72 break;
73 case 'coursetags':
74 $url = '/'.$url;
75 break;
76 case 'library':
77 case '':
78 $url = '/';
79 break;
80 case 'message':
81 $url = "/message/$url";
82 break;
83 case 'notes':
84 $url = "/notes/$url";
85 break;
86 case 'tag':
87 $url = "/tag/$url";
88 break;
89 case 'role':
90 $url = '/'.$url;
91 break;
92 default:
93 $url = "/mod/$module/$url";
94 break;
97 //now let's sanitise urls - there might be some ugly nasties:-(
98 $parts = explode('?', $url);
99 $script = array_shift($parts);
100 if (strpos($script, 'http') === 0) {
101 $script = clean_param($script, PARAM_URL);
102 } else {
103 $script = clean_param($script, PARAM_PATH);
106 $query = '';
107 if ($parts) {
108 $query = implode('', $parts);
109 $query = str_replace('&amp;', '&', $query); // both & and &amp; are stored in db :-|
110 $parts = explode('&', $query);
111 $eq = urlencode('=');
112 foreach ($parts as $key=>$part) {
113 $part = urlencode(urldecode($part));
114 $part = str_replace($eq, '=', $part);
115 $parts[$key] = $part;
117 $query = '?'.implode('&amp;', $parts);
120 return $script.$query;
124 function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
125 $modname="", $modid=0, $modaction="", $groupid=0) {
126 global $CFG, $DB;
128 // It is assumed that $date is the GMT time of midnight for that day,
129 // and so the next 86400 seconds worth of logs are printed.
131 /// Setup for group handling.
133 // TODO: I don't understand group/context/etc. enough to be able to do
134 // something interesting with it here
135 // What is the context of a remote course?
137 /// If the group mode is separate, and this user does not have editing privileges,
138 /// then only the user's group can be viewed.
139 //if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
140 // $groupid = get_current_group($course->id);
142 /// If this course doesn't have groups, no groupid can be specified.
143 //else if (!$course->groupmode) {
144 // $groupid = 0;
147 $groupid = 0;
149 $joins = array();
151 $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
152 FROM {mnet_log} l
153 LEFT JOIN {user} u ON l.userid = u.id
154 WHERE ";
155 $params = array();
157 $where .= "l.hostid = :hostid";
158 $params['hostid'] = $hostid;
160 // TODO: Is 1 really a magic number referring to the sitename?
161 if ($course != SITEID || $modid != 0) {
162 $where .= " AND l.course=:courseid";
163 $params['courseid'] = $course;
166 if ($modname) {
167 $where .= " AND l.module = :modname";
168 $params['modname'] = $modname;
171 if ('site_errors' === $modid) {
172 $where .= " AND ( l.action='error' OR l.action='infected' )";
173 } else if ($modid) {
174 //TODO: This assumes that modids are the same across sites... probably
175 //not true
176 $where .= " AND l.cmid = :modid";
177 $params['modid'] = $modid;
180 if ($modaction) {
181 $firstletter = substr($modaction, 0, 1);
182 if ($firstletter == '-') {
183 $where .= " AND ".$DB->sql_like('l.action', ':modaction', false, true, true);
184 $params['modaction'] = '%'.substr($modaction, 1).'%';
185 } else {
186 $where .= " AND ".$DB->sql_like('l.action', ':modaction', false);
187 $params['modaction'] = '%'.$modaction.'%';
191 if ($user) {
192 $where .= " AND l.userid = :user";
193 $params['user'] = $user;
196 if ($date) {
197 $enddate = $date + 86400;
198 $where .= " AND l.time > :date AND l.time < :enddate";
199 $params['date'] = $date;
200 $params['enddate'] = $enddate;
203 $result = array();
204 $result['totalcount'] = $DB->count_records_sql("SELECT COUNT('x') FROM {mnet_log} l WHERE $where", $params);
205 if(!empty($result['totalcount'])) {
206 $where .= " ORDER BY $order";
207 $result['logs'] = $DB->get_records_sql("$qry $where", $params, $limitfrom, $limitnum);
208 } else {
209 $result['logs'] = array();
211 return $result;
214 function build_logs_array($course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
215 $modname="", $modid=0, $modaction="", $groupid=0) {
216 global $DB, $SESSION, $USER;
217 // It is assumed that $date is the GMT time of midnight for that day,
218 // and so the next 86400 seconds worth of logs are printed.
220 /// Setup for group handling.
222 /// If the group mode is separate, and this user does not have editing privileges,
223 /// then only the user's group can be viewed.
224 if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
225 if (isset($SESSION->currentgroup[$course->id])) {
226 $groupid = $SESSION->currentgroup[$course->id];
227 } else {
228 $groupid = groups_get_all_groups($course->id, $USER->id);
229 if (is_array($groupid)) {
230 $groupid = array_shift(array_keys($groupid));
231 $SESSION->currentgroup[$course->id] = $groupid;
232 } else {
233 $groupid = 0;
237 /// If this course doesn't have groups, no groupid can be specified.
238 else if (!$course->groupmode) {
239 $groupid = 0;
242 $joins = array();
243 $params = array();
245 if ($course->id != SITEID || $modid != 0) {
246 $joins[] = "l.course = :courseid";
247 $params['courseid'] = $course->id;
250 if ($modname) {
251 $joins[] = "l.module = :modname";
252 $params['modname'] = $modname;
255 if ('site_errors' === $modid) {
256 $joins[] = "( l.action='error' OR l.action='infected' )";
257 } else if ($modid) {
258 $joins[] = "l.cmid = :modid";
259 $params['modid'] = $modid;
262 if ($modaction) {
263 $firstletter = substr($modaction, 0, 1);
264 if ($firstletter == '-') {
265 $joins[] = $DB->sql_like('l.action', ':modaction', false, true, true);
266 $params['modaction'] = '%'.substr($modaction, 1).'%';
267 } else {
268 $joins[] = $DB->sql_like('l.action', ':modaction', false);
269 $params['modaction'] = '%'.$modaction.'%';
274 /// Getting all members of a group.
275 if ($groupid and !$user) {
276 if ($gusers = groups_get_members($groupid)) {
277 $gusers = array_keys($gusers);
278 $joins[] = 'l.userid IN (' . implode(',', $gusers) . ')';
279 } else {
280 $joins[] = 'l.userid = 0'; // No users in groups, so we want something that will always be false.
283 else if ($user) {
284 $joins[] = "l.userid = :userid";
285 $params['userid'] = $user;
288 if ($date) {
289 $enddate = $date + 86400;
290 $joins[] = "l.time > :date AND l.time < :enddate";
291 $params['date'] = $date;
292 $params['enddate'] = $enddate;
295 $selector = implode(' AND ', $joins);
297 $totalcount = 0; // Initialise
298 $result = array();
299 $result['logs'] = get_logs($selector, $params, $order, $limitfrom, $limitnum, $totalcount);
300 $result['totalcount'] = $totalcount;
301 return $result;
305 function print_log($course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
306 $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
308 global $CFG, $DB, $OUTPUT;
310 if (!$logs = build_logs_array($course, $user, $date, $order, $page*$perpage, $perpage,
311 $modname, $modid, $modaction, $groupid)) {
312 echo $OUTPUT->notification("No logs found!");
313 echo $OUTPUT->footer();
314 exit;
317 $courses = array();
319 if ($course->id == SITEID) {
320 $courses[0] = '';
321 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
322 foreach ($ccc as $cc) {
323 $courses[$cc->id] = $cc->shortname;
326 } else {
327 $courses[$course->id] = $course->shortname;
330 $totalcount = $logs['totalcount'];
331 $count=0;
332 $ldcache = array();
333 $tt = getdate(time());
334 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
336 $strftimedatetime = get_string("strftimedatetime");
338 echo "<div class=\"info\">\n";
339 print_string("displayingrecords", "", $totalcount);
340 echo "</div>\n";
342 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
344 $table = new html_table();
345 $table->classes = array('logtable','generalbox');
346 $table->align = array('right', 'left', 'left');
347 $table->head = array(
348 get_string('time'),
349 get_string('ip_address'),
350 get_string('fullnameuser'),
351 get_string('action'),
352 get_string('info')
354 $table->data = array();
356 if ($course->id == SITEID) {
357 array_unshift($table->align, 'left');
358 array_unshift($table->head, get_string('course'));
361 // Make sure that the logs array is an array, even it is empty, to avoid warnings from the foreach.
362 if (empty($logs['logs'])) {
363 $logs['logs'] = array();
366 foreach ($logs['logs'] as $log) {
368 if (isset($ldcache[$log->module][$log->action])) {
369 $ld = $ldcache[$log->module][$log->action];
370 } else {
371 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
372 $ldcache[$log->module][$log->action] = $ld;
374 if ($ld && is_numeric($log->info)) {
375 // ugly hack to make sure fullname is shown correctly
376 if ($ld->mtable == 'user' && $ld->field == $DB->sql_concat('firstname', "' '" , 'lastname')) {
377 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
378 } else {
379 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
383 //Filter log->info
384 $log->info = format_string($log->info);
386 // If $log->url has been trimmed short by the db size restriction
387 // code in add_to_log, keep a note so we don't add a link to a broken url
388 $tl=textlib_get_instance();
389 $brokenurl=($tl->strlen($log->url)==100 && $tl->substr($log->url,97)=='...');
391 $row = array();
392 if ($course->id == SITEID) {
393 if (empty($log->course)) {
394 $row[] = get_string('site');
395 } else {
396 $row[] = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">". format_string($courses[$log->course])."</a>";
400 $row[] = userdate($log->time, '%a').' '.userdate($log->time, $strftimedatetime);
402 $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
403 $row[] = $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 440, 'width' => 700)));
405 $row[] = html_writer::link(new moodle_url("/user/view.php?id={$log->userid}&course={$log->course}"), fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id))));
407 $displayaction="$log->module $log->action";
408 if ($brokenurl) {
409 $row[] = $displayaction;
410 } else {
411 $link = make_log_url($log->module,$log->url);
412 $row[] = $OUTPUT->action_link($link, $displayaction, new popup_action('click', $link, 'fromloglive'), array('height' => 440, 'width' => 700));
414 $row[] = $log->info;
415 $table->data[] = $row;
418 echo html_writer::table($table);
419 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
423 function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
424 $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
426 global $CFG, $DB, $OUTPUT;
428 if (!$logs = build_mnet_logs_array($hostid, $course, $user, $date, $order, $page*$perpage, $perpage,
429 $modname, $modid, $modaction, $groupid)) {
430 echo $OUTPUT->notification("No logs found!");
431 echo $OUTPUT->footer();
432 exit;
435 if ($course->id == SITEID) {
436 $courses[0] = '';
437 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname,c.visible')) {
438 foreach ($ccc as $cc) {
439 $courses[$cc->id] = $cc->shortname;
444 $totalcount = $logs['totalcount'];
445 $count=0;
446 $ldcache = array();
447 $tt = getdate(time());
448 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
450 $strftimedatetime = get_string("strftimedatetime");
452 echo "<div class=\"info\">\n";
453 print_string("displayingrecords", "", $totalcount);
454 echo "</div>\n";
456 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
458 echo "<table class=\"logtable\" cellpadding=\"3\" cellspacing=\"0\">\n";
459 echo "<tr>";
460 if ($course->id == SITEID) {
461 echo "<th class=\"c0 header\">".get_string('course')."</th>\n";
463 echo "<th class=\"c1 header\">".get_string('time')."</th>\n";
464 echo "<th class=\"c2 header\">".get_string('ip_address')."</th>\n";
465 echo "<th class=\"c3 header\">".get_string('fullnameuser')."</th>\n";
466 echo "<th class=\"c4 header\">".get_string('action')."</th>\n";
467 echo "<th class=\"c5 header\">".get_string('info')."</th>\n";
468 echo "</tr>\n";
470 if (empty($logs['logs'])) {
471 echo "</table>\n";
472 return;
475 $row = 1;
476 foreach ($logs['logs'] as $log) {
478 $log->info = $log->coursename;
479 $row = ($row + 1) % 2;
481 if (isset($ldcache[$log->module][$log->action])) {
482 $ld = $ldcache[$log->module][$log->action];
483 } else {
484 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
485 $ldcache[$log->module][$log->action] = $ld;
487 if (0 && $ld && !empty($log->info)) {
488 // ugly hack to make sure fullname is shown correctly
489 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
490 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
491 } else {
492 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
496 //Filter log->info
497 $log->info = format_string($log->info);
499 echo '<tr class="r'.$row.'">';
500 if ($course->id == SITEID) {
501 echo "<td class=\"r$row c0\" >\n";
502 echo " <a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">".$courses[$log->course]."</a>\n";
503 echo "</td>\n";
505 echo "<td class=\"r$row c1\" align=\"right\">".userdate($log->time, '%a').
506 ' '.userdate($log->time, $strftimedatetime)."</td>\n";
507 echo "<td class=\"r$row c2\" >\n";
508 $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
509 echo $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 400, 'width' => 700)));
510 echo "</td>\n";
511 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
512 echo "<td class=\"r$row c3\" >\n";
513 echo " <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
514 echo "</td>\n";
515 echo "<td class=\"r$row c4\">\n";
516 echo $log->action .': '.$log->module;
517 echo "</td>\n";;
518 echo "<td class=\"r$row c5\">{$log->info}</td>\n";
519 echo "</tr>\n";
521 echo "</table>\n";
523 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
527 function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
528 $modid, $modaction, $groupid) {
529 global $DB;
531 $text = get_string('course')."\t".get_string('time')."\t".get_string('ip_address')."\t".
532 get_string('fullnameuser')."\t".get_string('action')."\t".get_string('info');
534 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
535 $modname, $modid, $modaction, $groupid)) {
536 return false;
539 $courses = array();
541 if ($course->id == SITEID) {
542 $courses[0] = '';
543 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
544 foreach ($ccc as $cc) {
545 $courses[$cc->id] = $cc->shortname;
548 } else {
549 $courses[$course->id] = $course->shortname;
552 $count=0;
553 $ldcache = array();
554 $tt = getdate(time());
555 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
557 $strftimedatetime = get_string("strftimedatetime");
559 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
560 $filename .= '.txt';
561 header("Content-Type: application/download\n");
562 header("Content-Disposition: attachment; filename=$filename");
563 header("Expires: 0");
564 header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
565 header("Pragma: public");
567 echo get_string('savedat').userdate(time(), $strftimedatetime)."\n";
568 echo $text."\n";
570 if (empty($logs['logs'])) {
571 return true;
574 foreach ($logs['logs'] as $log) {
575 if (isset($ldcache[$log->module][$log->action])) {
576 $ld = $ldcache[$log->module][$log->action];
577 } else {
578 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
579 $ldcache[$log->module][$log->action] = $ld;
581 if ($ld && !empty($log->info)) {
582 // ugly hack to make sure fullname is shown correctly
583 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
584 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
585 } else {
586 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
590 //Filter log->info
591 $log->info = format_string($log->info);
592 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
594 $firstField = $courses[$log->course];
595 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
596 $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action, $log->info);
597 $text = implode("\t", $row);
598 echo $text." \n";
600 return true;
604 function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
605 $modid, $modaction, $groupid) {
607 global $CFG, $DB;
609 require_once("$CFG->libdir/excellib.class.php");
611 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
612 $modname, $modid, $modaction, $groupid)) {
613 return false;
616 $courses = array();
618 if ($course->id == SITEID) {
619 $courses[0] = '';
620 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
621 foreach ($ccc as $cc) {
622 $courses[$cc->id] = $cc->shortname;
625 } else {
626 $courses[$course->id] = $course->shortname;
629 $count=0;
630 $ldcache = array();
631 $tt = getdate(time());
632 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
634 $strftimedatetime = get_string("strftimedatetime");
636 $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
637 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
638 $filename .= '.xls';
640 $workbook = new MoodleExcelWorkbook('-');
641 $workbook->send($filename);
643 $worksheet = array();
644 $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
645 get_string('fullnameuser'), get_string('action'), get_string('info'));
647 // Creating worksheets
648 for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
649 $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
650 $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
651 $worksheet[$wsnumber]->set_column(1, 1, 30);
652 $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
653 userdate(time(), $strftimedatetime));
654 $col = 0;
655 foreach ($headers as $item) {
656 $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
657 $col++;
661 if (empty($logs['logs'])) {
662 $workbook->close();
663 return true;
666 $formatDate =& $workbook->add_format();
667 $formatDate->set_num_format(get_string('log_excel_date_format'));
669 $row = FIRSTUSEDEXCELROW;
670 $wsnumber = 1;
671 $myxls =& $worksheet[$wsnumber];
672 foreach ($logs['logs'] as $log) {
673 if (isset($ldcache[$log->module][$log->action])) {
674 $ld = $ldcache[$log->module][$log->action];
675 } else {
676 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
677 $ldcache[$log->module][$log->action] = $ld;
679 if ($ld && !empty($log->info)) {
680 // ugly hack to make sure fullname is shown correctly
681 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
682 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
683 } else {
684 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
688 // Filter log->info
689 $log->info = format_string($log->info);
690 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
692 if ($nroPages>1) {
693 if ($row > EXCELROWS) {
694 $wsnumber++;
695 $myxls =& $worksheet[$wsnumber];
696 $row = FIRSTUSEDEXCELROW;
700 $myxls->write($row, 0, $courses[$log->course], '');
701 $myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
702 $myxls->write($row, 2, $log->ip, '');
703 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
704 $myxls->write($row, 3, $fullname, '');
705 $myxls->write($row, 4, $log->module.' '.$log->action, '');
706 $myxls->write($row, 5, $log->info, '');
708 $row++;
711 $workbook->close();
712 return true;
715 function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
716 $modid, $modaction, $groupid) {
718 global $CFG, $DB;
720 require_once("$CFG->libdir/odslib.class.php");
722 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
723 $modname, $modid, $modaction, $groupid)) {
724 return false;
727 $courses = array();
729 if ($course->id == SITEID) {
730 $courses[0] = '';
731 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
732 foreach ($ccc as $cc) {
733 $courses[$cc->id] = $cc->shortname;
736 } else {
737 $courses[$course->id] = $course->shortname;
740 $count=0;
741 $ldcache = array();
742 $tt = getdate(time());
743 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
745 $strftimedatetime = get_string("strftimedatetime");
747 $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
748 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
749 $filename .= '.ods';
751 $workbook = new MoodleODSWorkbook('-');
752 $workbook->send($filename);
754 $worksheet = array();
755 $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
756 get_string('fullnameuser'), get_string('action'), get_string('info'));
758 // Creating worksheets
759 for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
760 $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
761 $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
762 $worksheet[$wsnumber]->set_column(1, 1, 30);
763 $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
764 userdate(time(), $strftimedatetime));
765 $col = 0;
766 foreach ($headers as $item) {
767 $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
768 $col++;
772 if (empty($logs['logs'])) {
773 $workbook->close();
774 return true;
777 $formatDate =& $workbook->add_format();
778 $formatDate->set_num_format(get_string('log_excel_date_format'));
780 $row = FIRSTUSEDEXCELROW;
781 $wsnumber = 1;
782 $myxls =& $worksheet[$wsnumber];
783 foreach ($logs['logs'] as $log) {
784 if (isset($ldcache[$log->module][$log->action])) {
785 $ld = $ldcache[$log->module][$log->action];
786 } else {
787 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
788 $ldcache[$log->module][$log->action] = $ld;
790 if ($ld && !empty($log->info)) {
791 // ugly hack to make sure fullname is shown correctly
792 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
793 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
794 } else {
795 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
799 // Filter log->info
800 $log->info = format_string($log->info);
801 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
803 if ($nroPages>1) {
804 if ($row > EXCELROWS) {
805 $wsnumber++;
806 $myxls =& $worksheet[$wsnumber];
807 $row = FIRSTUSEDEXCELROW;
811 $myxls->write_string($row, 0, $courses[$log->course]);
812 $myxls->write_date($row, 1, $log->time);
813 $myxls->write_string($row, 2, $log->ip);
814 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
815 $myxls->write_string($row, 3, $fullname);
816 $myxls->write_string($row, 4, $log->module.' '.$log->action);
817 $myxls->write_string($row, 5, $log->info);
819 $row++;
822 $workbook->close();
823 return true;
827 function print_log_graph($course, $userid=0, $type="course.png", $date=0) {
828 global $CFG, $USER;
829 if (empty($CFG->gdversion)) {
830 echo "(".get_string("gdneed").")";
831 } else {
832 // MDL-10818, do not display broken graph when user has no permission to view graph
833 if (has_capability('coursereport/log:view', get_context_instance(CONTEXT_COURSE, $course->id)) ||
834 ($course->showreports and $USER->id == $userid)) {
835 echo '<img src="'.$CFG->wwwroot.'/course/report/log/graph.php?id='.$course->id.
836 '&amp;user='.$userid.'&amp;type='.$type.'&amp;date='.$date.'" alt="" />';
842 function print_overview($courses, array $remote_courses=array()) {
843 global $CFG, $USER, $DB, $OUTPUT;
845 $htmlarray = array();
846 if ($modules = $DB->get_records('modules')) {
847 foreach ($modules as $mod) {
848 if (file_exists(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php')) {
849 include_once(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php');
850 $fname = $mod->name.'_print_overview';
851 if (function_exists($fname)) {
852 $fname($courses,$htmlarray);
857 foreach ($courses as $course) {
858 echo $OUTPUT->box_start('coursebox');
859 $attributes = array('title' => s($course->fullname));
860 if (empty($course->visible)) {
861 $attributes['class'] = 'dimmed';
863 echo $OUTPUT->heading(html_writer::link(
864 new moodle_url('/course/view.php', array('id' => $course->id)), format_string($course->fullname), $attributes), 3);
865 if (array_key_exists($course->id,$htmlarray)) {
866 foreach ($htmlarray[$course->id] as $modname => $html) {
867 echo $html;
870 echo $OUTPUT->box_end();
873 if (!empty($remote_courses)) {
874 echo $OUTPUT->heading(get_string('remotecourses', 'mnet'));
876 foreach ($remote_courses as $course) {
877 echo $OUTPUT->box_start('coursebox');
878 $attributes = array('title' => s($course->fullname));
879 echo $OUTPUT->heading(html_writer::link(
880 new moodle_url('/auth/mnet/jump.php', array('hostid' => $course->hostid, 'wantsurl' => '/course/view.php?id='.$course->remoteid)),
881 format_string($course->shortname),
882 $attributes) . ' (' . format_string($course->hostname) . ')', 3);
883 echo $OUTPUT->box_end();
889 * This function trawls through the logs looking for
890 * anything new since the user's last login
892 function print_recent_activity($course) {
893 // $course is an object
894 global $CFG, $USER, $SESSION, $DB, $OUTPUT;
896 $context = get_context_instance(CONTEXT_COURSE, $course->id);
898 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
900 $timestart = round(time() - COURSE_MAX_RECENT_PERIOD, -2); // better db caching for guests - 100 seconds
902 if (!isguestuser()) {
903 if (!empty($USER->lastcourseaccess[$course->id])) {
904 if ($USER->lastcourseaccess[$course->id] > $timestart) {
905 $timestart = $USER->lastcourseaccess[$course->id];
910 echo '<div class="activitydate">';
911 echo get_string('activitysince', '', userdate($timestart));
912 echo '</div>';
913 echo '<div class="activityhead">';
915 echo '<a href="'.$CFG->wwwroot.'/course/recent.php?id='.$course->id.'">'.get_string('recentactivityreport').'</a>';
917 echo "</div>\n";
919 $content = false;
921 /// Firstly, have there been any new enrolments?
923 $users = get_recent_enrolments($course->id, $timestart);
925 //Accessibility: new users now appear in an <OL> list.
926 if ($users) {
927 echo '<div class="newusers">';
928 echo $OUTPUT->heading(get_string("newusers").':', 3);
929 $content = true;
930 echo "<ol class=\"list\">\n";
931 foreach ($users as $user) {
932 $fullname = fullname($user, $viewfullnames);
933 echo '<li class="name"><a href="'."$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a></li>\n";
935 echo "</ol>\n</div>\n";
938 /// Next, have there been any modifications to the course structure?
940 $modinfo =& get_fast_modinfo($course);
942 $changelist = array();
944 $logs = $DB->get_records_select('log', "time > ? AND course = ? AND
945 module = 'course' AND
946 (action = 'add mod' OR action = 'update mod' OR action = 'delete mod')",
947 array($timestart, $course->id), "id ASC");
949 if ($logs) {
950 $actions = array('add mod', 'update mod', 'delete mod');
951 $newgones = array(); // added and later deleted items
952 foreach ($logs as $key => $log) {
953 if (!in_array($log->action, $actions)) {
954 continue;
956 $info = explode(' ', $log->info);
958 // note: in most cases I replaced hardcoding of label with use of
959 // $cm->has_view() but it was not possible to do this here because
960 // we don't necessarily have the $cm for it
961 if ($info[0] == 'label') { // Labels are ignored in recent activity
962 continue;
965 if (count($info) != 2) {
966 debugging("Incorrect log entry info: id = ".$log->id, DEBUG_DEVELOPER);
967 continue;
970 $modname = $info[0];
971 $instanceid = $info[1];
973 if ($log->action == 'delete mod') {
974 // unfortunately we do not know if the mod was visible
975 if (!array_key_exists($log->info, $newgones)) {
976 $strdeleted = get_string('deletedactivity', 'moodle', get_string('modulename', $modname));
977 $changelist[$log->info] = array ('operation' => 'delete', 'text' => $strdeleted);
979 } else {
980 if (!isset($modinfo->instances[$modname][$instanceid])) {
981 if ($log->action == 'add mod') {
982 // do not display added and later deleted activities
983 $newgones[$log->info] = true;
985 continue;
987 $cm = $modinfo->instances[$modname][$instanceid];
988 if (!$cm->uservisible) {
989 continue;
992 if ($log->action == 'add mod') {
993 $stradded = get_string('added', 'moodle', get_string('modulename', $modname));
994 $changelist[$log->info] = array('operation' => 'add', 'text' => "$stradded:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
996 } else if ($log->action == 'update mod' and empty($changelist[$log->info])) {
997 $strupdated = get_string('updated', 'moodle', get_string('modulename', $modname));
998 $changelist[$log->info] = array('operation' => 'update', 'text' => "$strupdated:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
1004 if (!empty($changelist)) {
1005 echo $OUTPUT->heading(get_string("courseupdates").':', 3);
1006 $content = true;
1007 foreach ($changelist as $changeinfo => $change) {
1008 echo '<p class="activity">'.$change['text'].'</p>';
1012 /// Now display new things from each module
1014 $usedmodules = array();
1015 foreach($modinfo->cms as $cm) {
1016 if (isset($usedmodules[$cm->modname])) {
1017 continue;
1019 if (!$cm->uservisible) {
1020 continue;
1022 $usedmodules[$cm->modname] = $cm->modname;
1025 foreach ($usedmodules as $modname) { // Each module gets it's own logs and prints them
1026 if (file_exists($CFG->dirroot.'/mod/'.$modname.'/lib.php')) {
1027 include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php');
1028 $print_recent_activity = $modname.'_print_recent_activity';
1029 if (function_exists($print_recent_activity)) {
1030 // NOTE: original $isteacher (second parameter below) was replaced with $viewfullnames!
1031 $content = $print_recent_activity($course, $viewfullnames, $timestart) || $content;
1033 } else {
1034 debugging("Missing lib.php in lib/{$modname} - please reinstall files or uninstall the module");
1038 if (! $content) {
1039 echo '<p class="message">'.get_string('nothingnew').'</p>';
1044 * For a given course, returns an array of course activity objects
1045 * Each item in the array contains he following properties:
1047 function get_array_of_activities($courseid) {
1048 // cm - course module id
1049 // mod - name of the module (eg forum)
1050 // section - the number of the section (eg week or topic)
1051 // name - the name of the instance
1052 // visible - is the instance visible or not
1053 // groupingid - grouping id
1054 // groupmembersonly - is this instance visible to group members only
1055 // extra - contains extra string to include in any link
1056 global $CFG, $DB;
1057 if(!empty($CFG->enableavailability)) {
1058 require_once($CFG->libdir.'/conditionlib.php');
1061 $course = $DB->get_record('course', array('id'=>$courseid));
1063 if (empty($course)) {
1064 throw new moodle_exception('courseidnotfound');
1067 $mod = array();
1069 $rawmods = get_course_mods($courseid);
1070 if (empty($rawmods)) {
1071 return $mod; // always return array
1074 if ($sections = $DB->get_records("course_sections", array("course"=>$courseid), "section ASC")) {
1075 foreach ($sections as $section) {
1076 if (!empty($section->sequence)) {
1077 $sequence = explode(",", $section->sequence);
1078 foreach ($sequence as $seq) {
1079 if (empty($rawmods[$seq])) {
1080 continue;
1082 $mod[$seq]->id = $rawmods[$seq]->instance;
1083 $mod[$seq]->cm = $rawmods[$seq]->id;
1084 $mod[$seq]->mod = $rawmods[$seq]->modname;
1086 // Oh dear. Inconsistent names left here for backward compatibility.
1087 $mod[$seq]->section = $section->section;
1088 $mod[$seq]->sectionid = $rawmods[$seq]->section;
1090 $mod[$seq]->module = $rawmods[$seq]->module;
1091 $mod[$seq]->added = $rawmods[$seq]->added;
1092 $mod[$seq]->score = $rawmods[$seq]->score;
1093 $mod[$seq]->idnumber = $rawmods[$seq]->idnumber;
1094 $mod[$seq]->visible = $rawmods[$seq]->visible;
1095 $mod[$seq]->visibleold = $rawmods[$seq]->visibleold;
1096 $mod[$seq]->groupmode = $rawmods[$seq]->groupmode;
1097 $mod[$seq]->groupingid = $rawmods[$seq]->groupingid;
1098 $mod[$seq]->groupmembersonly = $rawmods[$seq]->groupmembersonly;
1099 $mod[$seq]->indent = $rawmods[$seq]->indent;
1100 $mod[$seq]->completion = $rawmods[$seq]->completion;
1101 $mod[$seq]->extra = "";
1102 $mod[$seq]->completiongradeitemnumber =
1103 $rawmods[$seq]->completiongradeitemnumber;
1104 $mod[$seq]->completionview = $rawmods[$seq]->completionview;
1105 $mod[$seq]->completionexpected = $rawmods[$seq]->completionexpected;
1106 $mod[$seq]->availablefrom = $rawmods[$seq]->availablefrom;
1107 $mod[$seq]->availableuntil = $rawmods[$seq]->availableuntil;
1108 $mod[$seq]->showavailability = $rawmods[$seq]->showavailability;
1109 $mod[$seq]->showdescription = $rawmods[$seq]->showdescription;
1110 if (!empty($CFG->enableavailability)) {
1111 condition_info::fill_availability_conditions($rawmods[$seq]);
1112 $mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
1113 $mod[$seq]->conditionsgrade = $rawmods[$seq]->conditionsgrade;
1116 $modname = $mod[$seq]->mod;
1117 $functionname = $modname."_get_coursemodule_info";
1119 if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
1120 continue;
1123 include_once("$CFG->dirroot/mod/$modname/lib.php");
1125 if ($hasfunction = function_exists($functionname)) {
1126 if ($info = $functionname($rawmods[$seq])) {
1127 if (!empty($info->icon)) {
1128 $mod[$seq]->icon = $info->icon;
1130 if (!empty($info->iconcomponent)) {
1131 $mod[$seq]->iconcomponent = $info->iconcomponent;
1133 if (!empty($info->name)) {
1134 $mod[$seq]->name = $info->name;
1136 if ($info instanceof cached_cm_info) {
1137 // When using cached_cm_info you can include three new fields
1138 // that aren't available for legacy code
1139 if (!empty($info->content)) {
1140 $mod[$seq]->content = $info->content;
1142 if (!empty($info->extraclasses)) {
1143 $mod[$seq]->extraclasses = $info->extraclasses;
1145 if (!empty($info->onclick)) {
1146 $mod[$seq]->onclick = $info->onclick;
1148 if (!empty($info->customdata)) {
1149 $mod[$seq]->customdata = $info->customdata;
1151 } else {
1152 // When using a stdclass, the (horrible) deprecated ->extra field
1153 // is available for BC
1154 if (!empty($info->extra)) {
1155 $mod[$seq]->extra = $info->extra;
1160 // When there is no modname_get_coursemodule_info function,
1161 // but showdescriptions is enabled, then we use the 'intro'
1162 // and 'introformat' fields in the module table
1163 if (!$hasfunction && $rawmods[$seq]->showdescription) {
1164 if ($modvalues = $DB->get_record($rawmods[$seq]->modname,
1165 array('id' => $rawmods[$seq]->instance), 'name, intro, introformat')) {
1166 // Set content from intro and introformat. Filters are disabled
1167 // because we filter it with format_text at display time
1168 $mod[$seq]->content = format_module_intro($rawmods[$seq]->modname,
1169 $modvalues, $rawmods[$seq]->id, false);
1171 // To save making another query just below, put name in here
1172 $mod[$seq]->name = $modvalues->name;
1175 if (!isset($mod[$seq]->name)) {
1176 $mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
1179 // Minimise the database size by unsetting default options when they are
1180 // 'empty'. This list corresponds to code in the cm_info constructor.
1181 foreach (array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
1182 'indent', 'completion', 'extra', 'extraclasses', 'onclick', 'content',
1183 'icon', 'iconcomponent', 'customdata', 'showavailability', 'availablefrom',
1184 'availableuntil', 'conditionscompletion', 'conditionsgrade',
1185 'completionview', 'completionexpected', 'score', 'showdescription')
1186 as $property) {
1187 if (property_exists($mod[$seq], $property) &&
1188 empty($mod[$seq]->{$property})) {
1189 unset($mod[$seq]->{$property});
1192 // Special case: this value is usually set to null, but may be 0
1193 if (property_exists($mod[$seq], 'completiongradeitemnumber') &&
1194 is_null($mod[$seq]->completiongradeitemnumber)) {
1195 unset($mod[$seq]->completiongradeitemnumber);
1201 return $mod;
1206 * Returns a number of useful structures for course displays
1208 function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
1209 global $CFG, $DB, $COURSE;
1211 $mods = array(); // course modules indexed by id
1212 $modnames = array(); // all course module names (except resource!)
1213 $modnamesplural= array(); // all course module names (plural form)
1214 $modnamesused = array(); // course module names used
1216 if ($allmods = $DB->get_records("modules")) {
1217 foreach ($allmods as $mod) {
1218 if (!file_exists("$CFG->dirroot/mod/$mod->name/lib.php")) {
1219 continue;
1221 if ($mod->visible) {
1222 $modnames[$mod->name] = get_string("modulename", "$mod->name");
1223 $modnamesplural[$mod->name] = get_string("modulenameplural", "$mod->name");
1226 collatorlib::asort($modnames);
1227 } else {
1228 print_error("nomodules", 'debug');
1231 $course = ($courseid==$COURSE->id) ? $COURSE : $DB->get_record('course',array('id'=>$courseid));
1232 $modinfo = get_fast_modinfo($course);
1234 if ($rawmods=$modinfo->cms) {
1235 foreach($rawmods as $mod) { // Index the mods
1236 if (empty($modnames[$mod->modname])) {
1237 continue;
1239 $mods[$mod->id] = $mod;
1240 $mods[$mod->id]->modfullname = $modnames[$mod->modname];
1241 if (!$mod->visible and !has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $courseid))) {
1242 continue;
1244 // Check groupings
1245 if (!groups_course_module_visible($mod)) {
1246 continue;
1248 $modnamesused[$mod->modname] = $modnames[$mod->modname];
1250 if ($modnamesused) {
1251 collatorlib::asort($modnamesused);
1257 * Returns an array of sections for the requested course id
1259 * This function stores the sections against the course id within a staticvar encase
1260 * of subsequent requests. This is used all over + in some standard libs and course
1261 * format callbacks so subsequent requests are a reality.
1263 * @staticvar array $coursesections
1264 * @param int $courseid
1265 * @return array Array of sections
1267 function get_all_sections($courseid) {
1268 global $DB;
1269 static $coursesections = array();
1270 if (!array_key_exists($courseid, $coursesections)) {
1271 $coursesections[$courseid] = $DB->get_records("course_sections", array("course"=>"$courseid"), "section",
1272 "section, id, course, name, summary, summaryformat, sequence, visible");
1274 return $coursesections[$courseid];
1278 * Returns the course section to display or 0 meaning show all sections. Returns 0 for guests.
1279 * It also sets the $USER->display cache to array($courseid=>return value)
1281 * @param int $courseid The course id
1282 * @return int Course section to display, 0 means all
1284 function course_get_display($courseid) {
1285 global $USER, $DB;
1287 if (!isloggedin() or isguestuser()) {
1288 //do not get settings in db for guests
1289 return 0; //return the implicit setting
1292 if (!isset($USER->display[$courseid])) {
1293 if (!$display = $DB->get_field('course_display', 'display', array('userid' => $USER->id, 'course'=>$courseid))) {
1294 $display = 0; // all sections option is not stored in DB, this makes the table much smaller
1296 //use display cache for one course only - we need to keep session small
1297 $USER->display = array($courseid => $display);
1300 return $USER->display[$courseid];
1304 * Show one section only or all sections.
1306 * @param int $courseid The course id
1307 * @param mixed $display show only this section, 0 or 'all' means show all sections
1308 * @return int Course section to display, 0 means all
1310 function course_set_display($courseid, $display) {
1311 global $USER, $DB;
1313 if ($display === 'all' or empty($display)) {
1314 $display = 0;
1317 if (!isloggedin() or isguestuser()) {
1318 //do not store settings in db for guests
1319 return 0;
1322 if ($display == 0) {
1323 //show all, do not store anything in database
1324 $DB->delete_records('course_display', array('userid' => $USER->id, 'course' => $courseid));
1326 } else {
1327 if ($DB->record_exists('course_display', array('userid' => $USER->id, 'course' => $courseid))) {
1328 $DB->set_field('course_display', 'display', $display, array('userid' => $USER->id, 'course' => $courseid));
1329 } else {
1330 $record = new stdClass();
1331 $record->userid = $USER->id;
1332 $record->course = $courseid;
1333 $record->display = $display;
1334 $DB->insert_record('course_display', $record);
1338 //use display cache for one course only - we need to keep session small
1339 $USER->display = array($courseid => $display);
1341 return $display;
1345 * For a given course section, marks it visible or hidden,
1346 * and does the same for every activity in that section
1348 function set_section_visible($courseid, $sectionnumber, $visibility) {
1349 global $DB;
1351 if ($section = $DB->get_record("course_sections", array("course"=>$courseid, "section"=>$sectionnumber))) {
1352 $DB->set_field("course_sections", "visible", "$visibility", array("id"=>$section->id));
1353 if (!empty($section->sequence)) {
1354 $modules = explode(",", $section->sequence);
1355 foreach ($modules as $moduleid) {
1356 set_coursemodule_visible($moduleid, $visibility, true);
1359 rebuild_course_cache($courseid);
1364 * Obtains shared data that is used in print_section when displaying a
1365 * course-module entry.
1367 * Calls format_text or format_string as appropriate, and obtains the correct icon.
1369 * This data is also used in other areas of the code.
1370 * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
1371 * @param object $course Moodle course object
1372 * @return array An array with the following values in this order:
1373 * $content (optional extra content for after link),
1374 * $instancename (text of link)
1376 function get_print_section_cm_text(cm_info $cm, $course) {
1377 global $OUTPUT;
1379 // Get course context
1380 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
1382 // Get content from modinfo if specified. Content displays either
1383 // in addition to the standard link (below), or replaces it if
1384 // the link is turned off by setting ->url to null.
1385 if (($content = $cm->get_content()) !== '') {
1386 $labelformatoptions = new stdClass();
1387 $labelformatoptions->noclean = true;
1388 $labelformatoptions->overflowdiv = true;
1389 $labelformatoptions->context = $coursecontext;
1390 $content = format_text($content, FORMAT_HTML, $labelformatoptions);
1391 } else {
1392 $content = '';
1395 $stringoptions = new stdClass;
1396 $stringoptions->context = $coursecontext;
1397 $instancename = format_string($cm->name, true, $stringoptions);
1398 return array($content, $instancename);
1402 * Prints a section full of activity modules
1404 function print_section($course, $section, $mods, $modnamesused, $absolute=false, $width="100%", $hidecompletion=false) {
1405 global $CFG, $USER, $DB, $PAGE, $OUTPUT;
1407 static $initialised;
1409 static $groupbuttons;
1410 static $groupbuttonslink;
1411 static $isediting;
1412 static $ismoving;
1413 static $strmovehere;
1414 static $strmovefull;
1415 static $strunreadpostsone;
1416 static $groupings;
1417 static $modulenames;
1419 if (!isset($initialised)) {
1420 $groupbuttons = ($course->groupmode or (!$course->groupmodeforce));
1421 $groupbuttonslink = (!$course->groupmodeforce);
1422 $isediting = $PAGE->user_is_editing();
1423 $ismoving = $isediting && ismoving($course->id);
1424 if ($ismoving) {
1425 $strmovehere = get_string("movehere");
1426 $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
1428 $modulenames = array();
1429 $initialised = true;
1432 $tl = textlib_get_instance();
1434 $modinfo = get_fast_modinfo($course);
1435 $completioninfo = new completion_info($course);
1437 //Accessibility: replace table with list <ul>, but don't output empty list.
1438 if (!empty($section->sequence)) {
1440 // Fix bug #5027, don't want style=\"width:$width\".
1441 echo "<ul class=\"section img-text\">\n";
1442 $sectionmods = explode(",", $section->sequence);
1444 foreach ($sectionmods as $modnumber) {
1445 if (empty($mods[$modnumber])) {
1446 continue;
1450 * @var cm_info
1452 $mod = $mods[$modnumber];
1454 if ($ismoving and $mod->id == $USER->activitycopy) {
1455 // do not display moving mod
1456 continue;
1459 if (isset($modinfo->cms[$modnumber])) {
1460 // We can continue (because it will not be displayed at all)
1461 // if:
1462 // 1) The activity is not visible to users
1463 // and
1464 // 2a) The 'showavailability' option is not set (if that is set,
1465 // we need to display the activity so we can show
1466 // availability info)
1467 // or
1468 // 2b) The 'availableinfo' is empty, i.e. the activity was
1469 // hidden in a way that leaves no info, such as using the
1470 // eye icon.
1471 if (!$modinfo->cms[$modnumber]->uservisible &&
1472 (empty($modinfo->cms[$modnumber]->showavailability) ||
1473 empty($modinfo->cms[$modnumber]->availableinfo))) {
1474 // visibility shortcut
1475 continue;
1477 } else {
1478 if (!file_exists("$CFG->dirroot/mod/$mod->modname/lib.php")) {
1479 // module not installed
1480 continue;
1482 if (!coursemodule_visible_for_user($mod) &&
1483 empty($mod->showavailability)) {
1484 // full visibility check
1485 continue;
1489 if (!isset($modulenames[$mod->modname])) {
1490 $modulenames[$mod->modname] = get_string('modulename', $mod->modname);
1492 $modulename = $modulenames[$mod->modname];
1494 // In some cases the activity is visible to user, but it is
1495 // dimmed. This is done if viewhiddenactivities is true and if:
1496 // 1. the activity is not visible, or
1497 // 2. the activity has dates set which do not include current, or
1498 // 3. the activity has any other conditions set (regardless of whether
1499 // current user meets them)
1500 $canviewhidden = has_capability(
1501 'moodle/course:viewhiddenactivities',
1502 get_context_instance(CONTEXT_MODULE, $mod->id));
1503 $accessiblebutdim = false;
1504 if ($canviewhidden) {
1505 $accessiblebutdim = !$mod->visible;
1506 if (!empty($CFG->enableavailability)) {
1507 $accessiblebutdim = $accessiblebutdim ||
1508 $mod->availablefrom > time() ||
1509 ($mod->availableuntil && $mod->availableuntil < time()) ||
1510 count($mod->conditionsgrade) > 0 ||
1511 count($mod->conditionscompletion) > 0;
1515 $liclasses = array();
1516 $liclasses[] = 'activity';
1517 $liclasses[] = $mod->modname;
1518 $liclasses[] = 'modtype_'.$mod->modname;
1519 $extraclasses = $mod->get_extra_classes();
1520 if ($extraclasses) {
1521 $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
1523 echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
1524 if ($ismoving) {
1525 echo '<a title="'.$strmovefull.'"'.
1526 ' href="'.$CFG->wwwroot.'/course/mod.php?moveto='.$mod->id.'&amp;sesskey='.sesskey().'">'.
1527 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
1528 ' alt="'.$strmovehere.'" /></a><br />
1532 $classes = array('mod-indent');
1533 if (!empty($mod->indent)) {
1534 $classes[] = 'mod-indent-'.$mod->indent;
1535 if ($mod->indent > 15) {
1536 $classes[] = 'mod-indent-huge';
1539 echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
1541 // Get data about this course-module
1542 list($content, $instancename) =
1543 get_print_section_cm_text($modinfo->cms[$modnumber], $course);
1545 //Accessibility: for files get description via icon, this is very ugly hack!
1546 $altname = '';
1547 $altname = $mod->modfullname;
1548 if (!empty($customicon)) {
1549 $archetype = plugin_supports('mod', $mod->modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
1550 if ($archetype == MOD_ARCHETYPE_RESOURCE) {
1551 $mimetype = mimeinfo_from_icon('type', $customicon);
1552 $altname = get_mimetype_description($mimetype);
1555 // Avoid unnecessary duplication: if e.g. a forum name already
1556 // includes the word forum (or Forum, etc) then it is unhelpful
1557 // to include that in the accessible description that is added.
1558 if (false !== strpos($tl->strtolower($instancename),
1559 $tl->strtolower($altname))) {
1560 $altname = '';
1562 // File type after name, for alphabetic lists (screen reader).
1563 if ($altname) {
1564 $altname = get_accesshide(' '.$altname);
1567 // We may be displaying this just in order to show information
1568 // about visibility, without the actual link
1569 $contentpart = '';
1570 if ($mod->uservisible) {
1571 // Nope - in this case the link is fully working for user
1572 $linkclasses = '';
1573 $textclasses = '';
1574 if ($accessiblebutdim) {
1575 $linkclasses .= ' dimmed';
1576 $textclasses .= ' dimmed_text';
1577 $accesstext = '<span class="accesshide">'.
1578 get_string('hiddenfromstudents').': </span>';
1579 } else {
1580 $accesstext = '';
1582 if ($linkclasses) {
1583 $linkcss = 'class="' . trim($linkclasses) . '" ';
1584 } else {
1585 $linkcss = '';
1587 if ($textclasses) {
1588 $textcss = 'class="' . trim($textclasses) . '" ';
1589 } else {
1590 $textcss = '';
1593 // Get on-click attribute value if specified
1594 $onclick = $mod->get_on_click();
1595 if ($onclick) {
1596 $onclick = ' onclick="' . $onclick . '"';
1599 if ($url = $mod->get_url()) {
1600 // Display link itself
1601 echo '<a ' . $linkcss . $mod->extra . $onclick .
1602 ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
1603 '" class="activityicon" alt="' .
1604 $modulename . '" /> ' .
1605 $accesstext . '<span class="instancename">' .
1606 $instancename . $altname . '</span></a>';
1608 // If specified, display extra content after link
1609 if ($content) {
1610 $contentpart = '<div class="contentafterlink' .
1611 trim($textclasses) . '">' . $content . '</div>';
1613 } else {
1614 // No link, so display only content
1615 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1616 $accesstext . $content . '</div>';
1619 if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
1620 if (!isset($groupings)) {
1621 $groupings = groups_get_all_groupings($course->id);
1623 echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
1625 } else {
1626 $textclasses = $extraclasses;
1627 $textclasses .= ' dimmed_text';
1628 if ($textclasses) {
1629 $textcss = 'class="' . trim($textclasses) . '" ';
1630 } else {
1631 $textcss = '';
1633 $accesstext = '<span class="accesshide">' .
1634 get_string('notavailableyet', 'condition') .
1635 ': </span>';
1637 if ($url = $mod->get_url()) {
1638 // Display greyed-out text of link
1639 echo '<div ' . $textcss . $mod->extra .
1640 ' >' . '<img src="' . $mod->get_icon_url() .
1641 '" class="activityicon" alt="' .
1642 $modulename .
1643 '" /> <span>'. $instancename . $altname .
1644 '</span></div>';
1646 // Do not display content after link when it is greyed out like this.
1647 } else {
1648 // No link, so display only content (also greyed)
1649 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1650 $accesstext . $content . '</div>';
1654 // Module can put text after the link (e.g. forum unread)
1655 echo $mod->get_after_link();
1657 // If there is content but NO link (eg label), then display the
1658 // content here (BEFORE any icons). In this case cons must be
1659 // displayed after the content so that it makes more sense visually
1660 // and for accessibility reasons, e.g. if you have a one-line label
1661 // it should work similarly (at least in terms of ordering) to an
1662 // activity.
1663 if (empty($url)) {
1664 echo $contentpart;
1667 if ($isediting) {
1668 if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
1669 if (! $mod->groupmodelink = $groupbuttonslink) {
1670 $mod->groupmode = $course->groupmode;
1673 } else {
1674 $mod->groupmode = false;
1676 echo '&nbsp;&nbsp;';
1677 echo make_editing_buttons($mod, $absolute, true, $mod->indent, $section->section);
1678 echo $mod->get_after_edit_icons();
1681 // Completion
1682 $completion = $hidecompletion
1683 ? COMPLETION_TRACKING_NONE
1684 : $completioninfo->is_enabled($mod);
1685 if ($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
1686 !isguestuser() && $mod->uservisible) {
1687 $completiondata = $completioninfo->get_data($mod,true);
1688 $completionicon = '';
1689 if ($isediting) {
1690 switch ($completion) {
1691 case COMPLETION_TRACKING_MANUAL :
1692 $completionicon = 'manual-enabled'; break;
1693 case COMPLETION_TRACKING_AUTOMATIC :
1694 $completionicon = 'auto-enabled'; break;
1695 default: // wtf
1697 } else if ($completion==COMPLETION_TRACKING_MANUAL) {
1698 switch($completiondata->completionstate) {
1699 case COMPLETION_INCOMPLETE:
1700 $completionicon = 'manual-n'; break;
1701 case COMPLETION_COMPLETE:
1702 $completionicon = 'manual-y'; break;
1704 } else { // Automatic
1705 switch($completiondata->completionstate) {
1706 case COMPLETION_INCOMPLETE:
1707 $completionicon = 'auto-n'; break;
1708 case COMPLETION_COMPLETE:
1709 $completionicon = 'auto-y'; break;
1710 case COMPLETION_COMPLETE_PASS:
1711 $completionicon = 'auto-pass'; break;
1712 case COMPLETION_COMPLETE_FAIL:
1713 $completionicon = 'auto-fail'; break;
1716 if ($completionicon) {
1717 $imgsrc = $OUTPUT->pix_url('i/completion-'.$completionicon);
1718 $imgalt = s(get_string('completion-alt-'.$completionicon, 'completion'));
1719 if ($completion == COMPLETION_TRACKING_MANUAL && !$isediting) {
1720 $imgtitle = s(get_string('completion-title-'.$completionicon, 'completion'));
1721 $newstate =
1722 $completiondata->completionstate==COMPLETION_COMPLETE
1723 ? COMPLETION_INCOMPLETE
1724 : COMPLETION_COMPLETE;
1725 // In manual mode the icon is a toggle form...
1727 // If this completion state is used by the
1728 // conditional activities system, we need to turn
1729 // off the JS.
1730 if (!empty($CFG->enableavailability) &&
1731 condition_info::completion_value_used_as_condition($course, $mod)) {
1732 $extraclass = ' preventjs';
1733 } else {
1734 $extraclass = '';
1736 echo "
1737 <form class='togglecompletion$extraclass' method='post' action='".$CFG->wwwroot."/course/togglecompletion.php'><div>
1738 <input type='hidden' name='id' value='{$mod->id}' />
1739 <input type='hidden' name='sesskey' value='".sesskey()."' />
1740 <input type='hidden' name='completionstate' value='$newstate' />
1741 <input type='image' src='$imgsrc' alt='$imgalt' title='$imgtitle' />
1742 </div></form>";
1743 } else {
1744 // In auto mode, or when editing, the icon is just an image
1745 echo "<span class='autocompletion'>";
1746 echo "<img src='$imgsrc' alt='$imgalt' title='$imgalt' /></span>";
1751 // If there is content AND a link, then display the content here
1752 // (AFTER any icons). Otherwise it was displayed before
1753 if (!empty($url)) {
1754 echo $contentpart;
1757 // Show availability information (for someone who isn't allowed to
1758 // see the activity itself, or for staff)
1759 if (!$mod->uservisible) {
1760 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
1761 } else if ($canviewhidden && !empty($CFG->enableavailability)) {
1762 $ci = new condition_info($mod);
1763 $fullinfo = $ci->get_full_information();
1764 if($fullinfo) {
1765 echo '<div class="availabilityinfo">'.get_string($mod->showavailability
1766 ? 'userrestriction_visible'
1767 : 'userrestriction_hidden','condition',
1768 $fullinfo).'</div>';
1772 echo html_writer::end_tag('div');
1773 echo html_writer::end_tag('li')."\n";
1776 } elseif ($ismoving) {
1777 echo "<ul class=\"section\">\n";
1780 if ($ismoving) {
1781 echo '<li><a title="'.$strmovefull.'"'.
1782 ' href="'.$CFG->wwwroot.'/course/mod.php?movetosection='.$section->id.'&amp;sesskey='.sesskey().'">'.
1783 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
1784 ' alt="'.$strmovehere.'" /></a></li>
1787 if (!empty($section->sequence) || $ismoving) {
1788 echo "</ul><!--class='section'-->\n\n";
1793 * Prints the menus to add activities and resources.
1795 function print_section_add_menus($course, $section, $modnames, $vertical=false, $return=false) {
1796 global $CFG, $OUTPUT;
1798 // check to see if user can add menus
1799 if (!has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id))) {
1800 return false;
1803 $urlbase = "/course/mod.php?id=$course->id&section=$section&sesskey=".sesskey().'&add=';
1805 $resources = array();
1806 $activities = array();
1808 foreach($modnames as $modname=>$modnamestr) {
1809 if (!course_allowed_module($course, $modname)) {
1810 continue;
1813 $libfile = "$CFG->dirroot/mod/$modname/lib.php";
1814 if (!file_exists($libfile)) {
1815 continue;
1817 include_once($libfile);
1818 $gettypesfunc = $modname.'_get_types';
1819 if (function_exists($gettypesfunc)) {
1820 // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
1821 if ($types = $gettypesfunc()) {
1822 $menu = array();
1823 $atype = null;
1824 $groupname = null;
1825 foreach($types as $type) {
1826 if ($type->typestr === '--') {
1827 continue;
1829 if (strpos($type->typestr, '--') === 0) {
1830 $groupname = str_replace('--', '', $type->typestr);
1831 continue;
1833 $type->type = str_replace('&amp;', '&', $type->type);
1834 if ($type->modclass == MOD_CLASS_RESOURCE) {
1835 $atype = MOD_CLASS_RESOURCE;
1837 $menu[$urlbase.$type->type] = $type->typestr;
1839 if (!is_null($groupname)) {
1840 if ($atype == MOD_CLASS_RESOURCE) {
1841 $resources[] = array($groupname=>$menu);
1842 } else {
1843 $activities[] = array($groupname=>$menu);
1845 } else {
1846 if ($atype == MOD_CLASS_RESOURCE) {
1847 $resources = array_merge($resources, $menu);
1848 } else {
1849 $activities = array_merge($activities, $menu);
1853 } else {
1854 $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
1855 if ($archetype == MOD_ARCHETYPE_RESOURCE) {
1856 $resources[$urlbase.$modname] = $modnamestr;
1857 } else {
1858 // all other archetypes are considered activity
1859 $activities[$urlbase.$modname] = $modnamestr;
1864 $straddactivity = get_string('addactivity');
1865 $straddresource = get_string('addresource');
1867 $output = '<div class="section_add_menus">';
1869 if (!$vertical) {
1870 $output .= '<div class="horizontal">';
1873 if (!empty($resources)) {
1874 $select = new url_select($resources, '', array(''=>$straddresource), "ressection$section");
1875 $select->set_help_icon('resources');
1876 $output .= $OUTPUT->render($select);
1879 if (!empty($activities)) {
1880 $select = new url_select($activities, '', array(''=>$straddactivity), "section$section");
1881 $select->set_help_icon('activities');
1882 $output .= $OUTPUT->render($select);
1885 if (!$vertical) {
1886 $output .= '</div>';
1889 $output .= '</div>';
1891 if ($return) {
1892 return $output;
1893 } else {
1894 echo $output;
1899 * Return the course category context for the category with id $categoryid, except
1900 * that if $categoryid is 0, return the system context.
1902 * @param integer $categoryid a category id or 0.
1903 * @return object the corresponding context
1905 function get_category_or_system_context($categoryid) {
1906 if ($categoryid) {
1907 return get_context_instance(CONTEXT_COURSECAT, $categoryid);
1908 } else {
1909 return get_context_instance(CONTEXT_SYSTEM);
1914 * Gets the child categories of a given courses category. Uses a static cache
1915 * to make repeat calls efficient.
1917 * @param int $parentid the id of a course category.
1918 * @return array all the child course categories.
1920 function get_child_categories($parentid) {
1921 static $allcategories = null;
1923 // only fill in this variable the first time
1924 if (null == $allcategories) {
1925 $allcategories = array();
1927 $categories = get_categories();
1928 foreach ($categories as $category) {
1929 if (empty($allcategories[$category->parent])) {
1930 $allcategories[$category->parent] = array();
1932 $allcategories[$category->parent][] = $category;
1936 if (empty($allcategories[$parentid])) {
1937 return array();
1938 } else {
1939 return $allcategories[$parentid];
1944 * This function recursively travels the categories, building up a nice list
1945 * for display. It also makes an array that list all the parents for each
1946 * category.
1948 * For example, if you have a tree of categories like:
1949 * Miscellaneous (id = 1)
1950 * Subcategory (id = 2)
1951 * Sub-subcategory (id = 4)
1952 * Other category (id = 3)
1953 * Then after calling this function you will have
1954 * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
1955 * 4 => 'Miscellaneous / Subcategory / Sub-subcategory',
1956 * 3 => 'Other category');
1957 * $parents = array(2 => array(1), 4 => array(1, 2));
1959 * If you specify $requiredcapability, then only categories where the current
1960 * user has that capability will be added to $list, although all categories
1961 * will still be added to $parents, and if you only have $requiredcapability
1962 * in a child category, not the parent, then the child catgegory will still be
1963 * included.
1965 * If you specify the option $excluded, then that category, and all its children,
1966 * are omitted from the tree. This is useful when you are doing something like
1967 * moving categories, where you do not want to allow people to move a category
1968 * to be the child of itself.
1970 * @param array $list For output, accumulates an array categoryid => full category path name
1971 * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
1972 * @param string/array $requiredcapability if given, only categories where the current
1973 * user has this capability will be added to $list. Can also be an array of capabilities,
1974 * in which case they are all required.
1975 * @param integer $excludeid Omit this category and its children from the lists built.
1976 * @param object $category Build the tree starting at this category - otherwise starts at the top level.
1977 * @param string $path For internal use, as part of recursive calls.
1979 function make_categories_list(&$list, &$parents, $requiredcapability = '',
1980 $excludeid = 0, $category = NULL, $path = "") {
1982 // initialize the arrays if needed
1983 if (!is_array($list)) {
1984 $list = array();
1986 if (!is_array($parents)) {
1987 $parents = array();
1990 if (empty($category)) {
1991 // Start at the top level.
1992 $category = new stdClass;
1993 $category->id = 0;
1994 } else {
1995 // This is the excluded category, don't include it.
1996 if ($excludeid > 0 && $excludeid == $category->id) {
1997 return;
2000 // Update $path.
2001 if ($path) {
2002 $path = $path.' / '.format_string($category->name);
2003 } else {
2004 $path = format_string($category->name);
2007 // Add this category to $list, if the permissions check out.
2008 if (empty($requiredcapability)) {
2009 $list[$category->id] = $path;
2011 } else {
2012 ensure_context_subobj_present($category, CONTEXT_COURSECAT);
2013 $requiredcapability = (array)$requiredcapability;
2014 if (has_all_capabilities($requiredcapability, $category->context)) {
2015 $list[$category->id] = $path;
2020 // Add all the children recursively, while updating the parents array.
2021 if ($categories = get_child_categories($category->id)) {
2022 foreach ($categories as $cat) {
2023 if (!empty($category->id)) {
2024 if (isset($parents[$category->id])) {
2025 $parents[$cat->id] = $parents[$category->id];
2027 $parents[$cat->id][] = $category->id;
2029 make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
2035 * This function generates a structured array of courses and categories.
2037 * The depth of categories is limited by $CFG->maxcategorydepth however there
2038 * is no limit on the number of courses!
2040 * Suitable for use with the course renderers course_category_tree method:
2041 * $renderer = $PAGE->get_renderer('core','course');
2042 * echo $renderer->course_category_tree(get_course_category_tree());
2044 * @global moodle_database $DB
2045 * @param int $id
2046 * @param int $depth
2048 function get_course_category_tree($id = 0, $depth = 0) {
2049 global $DB, $CFG;
2050 $viewhiddencats = has_capability('moodle/category:viewhiddencategories', get_context_instance(CONTEXT_SYSTEM));
2051 $categories = get_child_categories($id);
2052 $categoryids = array();
2053 foreach ($categories as $key => &$category) {
2054 if (!$category->visible && !$viewhiddencats) {
2055 unset($categories[$key]);
2056 continue;
2058 $categoryids[$category->id] = $category;
2059 if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
2060 list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
2061 foreach ($subcategories as $subid=>$subcat) {
2062 $categoryids[$subid] = $subcat;
2064 $category->courses = array();
2068 if ($depth > 0) {
2069 // This is a recursive call so return the required array
2070 return array($categories, $categoryids);
2073 // The depth is 0 this function has just been called so we can finish it off
2075 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
2076 list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
2077 $sql = "SELECT
2078 c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
2079 $ccselect
2080 FROM {course} c
2081 $ccjoin
2082 WHERE c.category $catsql ORDER BY c.sortorder ASC";
2083 if ($courses = $DB->get_records_sql($sql, $catparams)) {
2084 // loop throught them
2085 foreach ($courses as $course) {
2086 if ($course->id == SITEID) {
2087 continue;
2089 context_instance_preload($course);
2090 if (!empty($course->visible) || has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
2091 $categoryids[$course->category]->courses[$course->id] = $course;
2095 return $categories;
2099 * Recursive function to print out all the categories in a nice format
2100 * with or without courses included
2102 function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) {
2103 global $CFG;
2105 // maxcategorydepth == 0 meant no limit
2106 if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
2107 return;
2110 if (!$displaylist) {
2111 make_categories_list($displaylist, $parentslist);
2114 if ($category) {
2115 if ($category->visible or has_capability('moodle/category:viewhiddencategories', get_context_instance(CONTEXT_SYSTEM))) {
2116 print_category_info($category, $depth, $showcourses);
2117 } else {
2118 return; // Don't bother printing children of invisible categories
2121 } else {
2122 $category->id = "0";
2125 if ($categories = get_child_categories($category->id)) { // Print all the children recursively
2126 $countcats = count($categories);
2127 $count = 0;
2128 $first = true;
2129 $last = false;
2130 foreach ($categories as $cat) {
2131 $count++;
2132 if ($count == $countcats) {
2133 $last = true;
2135 $up = $first ? false : true;
2136 $down = $last ? false : true;
2137 $first = false;
2139 print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses);
2145 * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
2147 function make_categories_options() {
2148 make_categories_list($cats,$parents);
2149 foreach ($cats as $key => $value) {
2150 if (array_key_exists($key,$parents)) {
2151 if ($indent = count($parents[$key])) {
2152 for ($i = 0; $i < $indent; $i++) {
2153 $cats[$key] = '&nbsp;'.$cats[$key];
2158 return $cats;
2162 * Prints the category info in indented fashion
2163 * This function is only used by print_whole_category_list() above
2165 function print_category_info($category, $depth=0, $showcourses = false) {
2166 global $CFG, $DB, $OUTPUT;
2168 $strsummary = get_string('summary');
2170 $catlinkcss = null;
2171 if (!$category->visible) {
2172 $catlinkcss = array('class'=>'dimmed');
2174 static $coursecount = null;
2175 if (null === $coursecount) {
2176 // only need to check this once
2177 $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT;
2180 if ($showcourses and $coursecount) {
2181 $catimage = '<img src="'.$OUTPUT->pix_url('i/course') . '" alt="" />';
2182 } else {
2183 $catimage = "&nbsp;";
2186 $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
2187 if ($showcourses and $coursecount) {
2188 echo '<div class="categorylist clearfix">';
2189 $cat = '';
2190 $cat .= html_writer::tag('div', $catimage, array('class'=>'image'));
2191 $catlink = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), format_string($category->name), $catlinkcss);
2192 $cat .= html_writer::tag('div', $catlink, array('class'=>'name'));
2194 $html = '';
2195 if ($depth > 0) {
2196 for ($i=0; $i< $depth; $i++) {
2197 $html = html_writer::tag('div', $html . $cat, array('class'=>'indentation'));
2198 $cat = '';
2200 } else {
2201 $html = $cat;
2203 echo html_writer::tag('div', $html, array('class'=>'category'));
2204 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
2206 // does the depth exceed maxcategorydepth
2207 // maxcategorydepth == 0 or unset meant no limit
2208 $limit = !(isset($CFG->maxcategorydepth) && ($depth >= $CFG->maxcategorydepth-1));
2209 if ($courses && ($limit || $CFG->maxcategorydepth == 0)) {
2210 foreach ($courses as $course) {
2211 $linkcss = null;
2212 if (!$course->visible) {
2213 $linkcss = array('class'=>'dimmed');
2216 $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($course->fullname), $linkcss);
2218 // print enrol info
2219 $courseicon = '';
2220 if ($icons = enrol_get_course_info_icons($course)) {
2221 foreach ($icons as $pix_icon) {
2222 $courseicon = $OUTPUT->render($pix_icon).' ';
2226 $coursecontent = html_writer::tag('div', $courseicon.$courselink, array('class'=>'name'));
2228 if ($course->summary) {
2229 $link = new moodle_url('/course/info.php?id='.$course->id);
2230 $actionlink = $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
2231 new popup_action('click', $link, 'courseinfo', array('height' => 400, 'width' => 500)),
2232 array('title'=>$strsummary));
2234 $coursecontent .= html_writer::tag('div', $actionlink, array('class'=>'info'));
2237 $html = '';
2238 for ($i=0; $i <= $depth; $i++) {
2239 $html = html_writer::tag('div', $html . $coursecontent , array('class'=>'indentation'));
2240 $coursecontent = '';
2242 echo html_writer::tag('div', $html, array('class'=>'course clearfloat'));
2245 echo '</div>';
2246 } else {
2247 echo '<div class="categorylist">';
2248 $html = '';
2249 $cat = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), format_string($category->name), $catlinkcss);
2250 $cat .= html_writer::tag('span', ' ('.count($courses).')', array('title'=>get_string('numberofcourses'), 'class'=>'numberofcourse'));
2252 if ($depth > 0) {
2253 for ($i=0; $i< $depth; $i++) {
2254 $html = html_writer::tag('div', $html .$cat, array('class'=>'indentation'));
2255 $cat = '';
2257 } else {
2258 $html = $cat;
2261 echo html_writer::tag('div', $html, array('class'=>'category'));
2262 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
2263 echo '</div>';
2268 * Print the buttons relating to course requests.
2270 * @param object $systemcontext the system context.
2272 function print_course_request_buttons($systemcontext) {
2273 global $CFG, $DB, $OUTPUT;
2274 if (empty($CFG->enablecourserequests)) {
2275 return;
2277 if (!has_capability('moodle/course:create', $systemcontext) && has_capability('moodle/course:request', $systemcontext)) {
2278 /// Print a button to request a new course
2279 echo $OUTPUT->single_button('request.php', get_string('requestcourse'), 'get');
2281 /// Print a button to manage pending requests
2282 if (has_capability('moodle/site:approvecourse', $systemcontext)) {
2283 $disabled = !$DB->record_exists('course_request', array());
2284 echo $OUTPUT->single_button('pending.php', get_string('coursespending'), 'get', array('disabled'=>$disabled));
2289 * Does the user have permission to edit things in this category?
2291 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2292 * @return boolean has_any_capability(array(...), ...); in the appropriate context.
2294 function can_edit_in_category($categoryid = 0) {
2295 $context = get_category_or_system_context($categoryid);
2296 return has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context);
2300 * Prints the turn editing on/off button on course/index.php or course/category.php.
2302 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2303 * @return string HTML of the editing button, or empty string, if this user is not allowed
2304 * to see it.
2306 function update_category_button($categoryid = 0) {
2307 global $CFG, $PAGE, $OUTPUT;
2309 // Check permissions.
2310 if (!can_edit_in_category($categoryid)) {
2311 return '';
2314 // Work out the appropriate action.
2315 if ($PAGE->user_is_editing()) {
2316 $label = get_string('turneditingoff');
2317 $edit = 'off';
2318 } else {
2319 $label = get_string('turneditingon');
2320 $edit = 'on';
2323 // Generate the button HTML.
2324 $options = array('categoryedit' => $edit, 'sesskey' => sesskey());
2325 if ($categoryid) {
2326 $options['id'] = $categoryid;
2327 $page = 'category.php';
2328 } else {
2329 $page = 'index.php';
2331 return $OUTPUT->single_button(new moodle_url('/course/' . $page, $options), $label, 'get');
2335 * Category is 0 (for all courses) or an object
2337 function print_courses($category) {
2338 global $CFG, $OUTPUT;
2340 if (!is_object($category) && $category==0) {
2341 $categories = get_child_categories(0); // Parent = 0 ie top-level categories only
2342 if (is_array($categories) && count($categories) == 1) {
2343 $category = array_shift($categories);
2344 $courses = get_courses_wmanagers($category->id,
2345 'c.sortorder ASC',
2346 array('summary','summaryformat'));
2347 } else {
2348 $courses = get_courses_wmanagers('all',
2349 'c.sortorder ASC',
2350 array('summary','summaryformat'));
2352 unset($categories);
2353 } else {
2354 $courses = get_courses_wmanagers($category->id,
2355 'c.sortorder ASC',
2356 array('summary','summaryformat'));
2359 if ($courses) {
2360 echo html_writer::start_tag('ul', array('class'=>'unlist'));
2361 foreach ($courses as $course) {
2362 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
2363 if ($course->visible == 1 || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
2364 echo html_writer::start_tag('li');
2365 print_course($course);
2366 echo html_writer::end_tag('li');
2369 echo html_writer::end_tag('ul');
2370 } else {
2371 echo $OUTPUT->heading(get_string("nocoursesyet"));
2372 $context = get_context_instance(CONTEXT_SYSTEM);
2373 if (has_capability('moodle/course:create', $context)) {
2374 $options = array();
2375 if (!empty($category->id)) {
2376 $options['category'] = $category->id;
2377 } else {
2378 $options['category'] = $CFG->defaultrequestcategory;
2380 echo html_writer::start_tag('div', array('class'=>'addcoursebutton'));
2381 echo $OUTPUT->single_button(new moodle_url('/course/edit.php', $options), get_string("addnewcourse"));
2382 echo html_writer::end_tag('div');
2388 * Print a description of a course, suitable for browsing in a list.
2390 * @param object $course the course object.
2391 * @param string $highlightterms (optional) some search terms that should be highlighted in the display.
2393 function print_course($course, $highlightterms = '') {
2394 global $CFG, $USER, $DB, $OUTPUT;
2396 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2398 // Rewrite file URLs so that they are correct
2399 $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
2401 echo html_writer::start_tag('div', array('class'=>'coursebox clearfix'));
2402 echo html_writer::start_tag('div', array('class'=>'info'));
2403 echo html_writer::start_tag('h3', array('class'=>'name'));
2405 $linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
2406 $linktext = highlight($highlightterms, format_string($course->fullname));
2407 $linkparams = array('title'=>get_string('entercourse'));
2408 if (empty($course->visible)) {
2409 $linkparams['class'] = 'dimmed';
2411 echo html_writer::link($linkhref, $linktext, $linkparams);
2412 echo html_writer::end_tag('h3');
2414 /// first find all roles that are supposed to be displayed
2415 if (!empty($CFG->coursecontact)) {
2416 $managerroles = explode(',', $CFG->coursecontact);
2417 $namesarray = array();
2418 if (isset($course->managers)) {
2419 if (count($course->managers)) {
2420 $rusers = $course->managers;
2421 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2423 /// Rename some of the role names if needed
2424 if (isset($context)) {
2425 $aliasnames = $DB->get_records('role_names', array('contextid'=>$context->id), '', 'roleid,contextid,name');
2428 // keep a note of users displayed to eliminate duplicates
2429 $usersshown = array();
2430 foreach ($rusers as $ra) {
2432 // if we've already displayed user don't again
2433 if (in_array($ra->user->id,$usersshown)) {
2434 continue;
2436 $usersshown[] = $ra->user->id;
2438 $fullname = fullname($ra->user, $canviewfullnames);
2440 if (isset($aliasnames[$ra->roleid])) {
2441 $ra->rolename = $aliasnames[$ra->roleid]->name;
2444 $namesarray[] = format_string($ra->rolename).': '.
2445 html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->user->id, 'course'=>SITEID)), $fullname);
2448 } else {
2449 $rusers = get_role_users($managerroles, $context,
2450 true, '', 'r.sortorder ASC, u.lastname ASC');
2451 if (is_array($rusers) && count($rusers)) {
2452 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2454 /// Rename some of the role names if needed
2455 if (isset($context)) {
2456 $aliasnames = $DB->get_records('role_names', array('contextid'=>$context->id), '', 'roleid,contextid,name');
2459 foreach ($rusers as $teacher) {
2460 $fullname = fullname($teacher, $canviewfullnames);
2462 /// Apply role names
2463 if (isset($aliasnames[$teacher->roleid])) {
2464 $teacher->rolename = $aliasnames[$teacher->roleid]->name;
2467 $namesarray[] = format_string($teacher->rolename).': '.
2468 html_writer::link(new moodle_url('/user/view.php', array('id'=>$teacher->id, 'course'=>SITEID)), $fullname);
2473 if (!empty($namesarray)) {
2474 echo html_writer::start_tag('ul', array('class'=>'teachers'));
2475 foreach ($namesarray as $name) {
2476 echo html_writer::tag('li', $name);
2478 echo html_writer::end_tag('ul');
2481 echo html_writer::end_tag('div'); // End of info div
2483 echo html_writer::start_tag('div', array('class'=>'summary'));
2484 $options = NULL;
2485 $options->noclean = true;
2486 $options->para = false;
2487 $options->overflowdiv = true;
2488 if (!isset($course->summaryformat)) {
2489 $course->summaryformat = FORMAT_MOODLE;
2491 echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options, $course->id));
2492 if ($icons = enrol_get_course_info_icons($course)) {
2493 echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
2494 foreach ($icons as $icon) {
2495 echo $OUTPUT->render($icon);
2497 echo html_writer::end_tag('div'); // End of enrolmenticons div
2499 echo html_writer::end_tag('div'); // End of summary div
2500 echo html_writer::end_tag('div'); // End of coursebox div
2504 * Prints custom user information on the home page.
2505 * Over time this can include all sorts of information
2507 function print_my_moodle() {
2508 global $USER, $CFG, $DB, $OUTPUT;
2510 if (!isloggedin() or isguestuser()) {
2511 print_error('nopermissions', '', '', 'See My Moodle');
2514 $courses = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
2515 $rhosts = array();
2516 $rcourses = array();
2517 if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2518 $rcourses = get_my_remotecourses($USER->id);
2519 $rhosts = get_my_remotehosts();
2522 if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2524 if (!empty($courses)) {
2525 echo '<ul class="unlist">';
2526 foreach ($courses as $course) {
2527 if ($course->id == SITEID) {
2528 continue;
2530 echo '<li>';
2531 print_course($course);
2532 echo "</li>\n";
2534 echo "</ul>\n";
2537 // MNET
2538 if (!empty($rcourses)) {
2539 // at the IDP, we know of all the remote courses
2540 foreach ($rcourses as $course) {
2541 print_remote_course($course, "100%");
2543 } elseif (!empty($rhosts)) {
2544 // non-IDP, we know of all the remote servers, but not courses
2545 foreach ($rhosts as $host) {
2546 print_remote_host($host, "100%");
2549 unset($course);
2550 unset($host);
2552 if ($DB->count_records("course") > (count($courses) + 1) ) { // Some courses not being displayed
2553 echo "<table width=\"100%\"><tr><td align=\"center\">";
2554 print_course_search("", false, "short");
2555 echo "</td><td align=\"center\">";
2556 echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
2557 echo "</td></tr></table>\n";
2560 } else {
2561 if ($DB->count_records("course_categories") > 1) {
2562 echo $OUTPUT->box_start("categorybox");
2563 print_whole_category_list();
2564 echo $OUTPUT->box_end();
2565 } else {
2566 print_courses(0);
2572 function print_course_search($value="", $return=false, $format="plain") {
2573 global $CFG;
2574 static $count = 0;
2576 $count++;
2578 $id = 'coursesearch';
2580 if ($count > 1) {
2581 $id .= $count;
2584 $strsearchcourses= get_string("searchcourses");
2586 if ($format == 'plain') {
2587 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2588 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2589 $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
2590 $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
2591 $output .= '<input type="submit" value="'.get_string('go').'" />';
2592 $output .= '</fieldset></form>';
2593 } else if ($format == 'short') {
2594 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2595 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2596 $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
2597 $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
2598 $output .= '<input type="submit" value="'.get_string('go').'" />';
2599 $output .= '</fieldset></form>';
2600 } else if ($format == 'navbar') {
2601 $output = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2602 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2603 $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
2604 $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
2605 $output .= '<input type="submit" value="'.get_string('go').'" />';
2606 $output .= '</fieldset></form>';
2609 if ($return) {
2610 return $output;
2612 echo $output;
2615 function print_remote_course($course, $width="100%") {
2616 global $CFG, $USER;
2618 $linkcss = '';
2620 $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}";
2622 echo '<div class="coursebox remotecoursebox clearfix">';
2623 echo '<div class="info">';
2624 echo '<div class="name"><a title="'.get_string('entercourse').'"'.
2625 $linkcss.' href="'.$url.'">'
2626 . format_string($course->fullname) .'</a><br />'
2627 . format_string($course->hostname) . ' : '
2628 . format_string($course->cat_name) . ' : '
2629 . format_string($course->shortname). '</div>';
2630 echo '</div><div class="summary">';
2631 $options = NULL;
2632 $options->noclean = true;
2633 $options->para = false;
2634 $options->overflowdiv = true;
2635 echo format_text($course->summary, $course->summaryformat, $options);
2636 echo '</div>';
2637 echo '</div>';
2640 function print_remote_host($host, $width="100%") {
2641 global $OUTPUT;
2643 $linkcss = '';
2645 echo '<div class="coursebox clearfix">';
2646 echo '<div class="info">';
2647 echo '<div class="name">';
2648 echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
2649 echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
2650 . s($host['name']).'</a> - ';
2651 echo $host['count'] . ' ' . get_string('courses');
2652 echo '</div>';
2653 echo '</div>';
2654 echo '</div>';
2658 /// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
2660 function add_course_module($mod) {
2661 global $DB;
2663 $mod->added = time();
2664 unset($mod->id);
2666 return $DB->insert_record("course_modules", $mod);
2670 * Returns course section - creates new if does not exist yet.
2671 * @param int $relative section number
2672 * @param int $courseid
2673 * @return object $course_section object
2675 function get_course_section($section, $courseid) {
2676 global $DB;
2678 if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
2679 return $cw;
2681 $cw = new stdClass();
2682 $cw->course = $courseid;
2683 $cw->section = $section;
2684 $cw->summary = "";
2685 $cw->summaryformat = FORMAT_HTML;
2686 $cw->sequence = "";
2687 $id = $DB->insert_record("course_sections", $cw);
2688 return $DB->get_record("course_sections", array("id"=>$id));
2691 * Given a full mod object with section and course already defined, adds this module to that section.
2693 * @param object $mod
2694 * @param int $beforemod An existing ID which we will insert the new module before
2695 * @return int The course_sections ID where the mod is inserted
2697 function add_mod_to_section($mod, $beforemod=NULL) {
2698 global $DB;
2700 if ($section = $DB->get_record("course_sections", array("course"=>$mod->course, "section"=>$mod->section))) {
2702 $section->sequence = trim($section->sequence);
2704 if (empty($section->sequence)) {
2705 $newsequence = "$mod->coursemodule";
2707 } else if ($beforemod) {
2708 $modarray = explode(",", $section->sequence);
2710 if ($key = array_keys($modarray, $beforemod->id)) {
2711 $insertarray = array($mod->id, $beforemod->id);
2712 array_splice($modarray, $key[0], 1, $insertarray);
2713 $newsequence = implode(",", $modarray);
2715 } else { // Just tack it on the end anyway
2716 $newsequence = "$section->sequence,$mod->coursemodule";
2719 } else {
2720 $newsequence = "$section->sequence,$mod->coursemodule";
2723 $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
2724 return $section->id; // Return course_sections ID that was used.
2726 } else { // Insert a new record
2727 $section->course = $mod->course;
2728 $section->section = $mod->section;
2729 $section->summary = "";
2730 $section->summaryformat = FORMAT_HTML;
2731 $section->sequence = $mod->coursemodule;
2732 return $DB->insert_record("course_sections", $section);
2736 function set_coursemodule_groupmode($id, $groupmode) {
2737 global $DB;
2738 return $DB->set_field("course_modules", "groupmode", $groupmode, array("id"=>$id));
2741 function set_coursemodule_idnumber($id, $idnumber) {
2742 global $DB;
2743 return $DB->set_field("course_modules", "idnumber", $idnumber, array("id"=>$id));
2747 * $prevstateoverrides = true will set the visibility of the course module
2748 * to what is defined in visibleold. This enables us to remember the current
2749 * visibility when making a whole section hidden, so that when we toggle
2750 * that section back to visible, we are able to return the visibility of
2751 * the course module back to what it was originally.
2753 function set_coursemodule_visible($id, $visible, $prevstateoverrides=false) {
2754 global $DB, $CFG;
2755 require_once($CFG->libdir.'/gradelib.php');
2757 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
2758 return false;
2760 if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
2761 return false;
2763 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
2764 foreach($events as $event) {
2765 if ($visible) {
2766 show_event($event);
2767 } else {
2768 hide_event($event);
2773 // hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there
2774 $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
2775 if ($grade_items) {
2776 foreach ($grade_items as $grade_item) {
2777 $grade_item->set_hidden(!$visible);
2781 if ($prevstateoverrides) {
2782 if ($visible == '0') {
2783 // Remember the current visible state so we can toggle this back.
2784 $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id'=>$id));
2785 } else {
2786 // Get the previous saved visible states.
2787 return $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
2790 return $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
2794 * Delete a course module and any associated data at the course level (events)
2795 * Until 1.5 this function simply marked a deleted flag ... now it
2796 * deletes it completely.
2799 function delete_course_module($id) {
2800 global $CFG, $DB;
2801 require_once($CFG->libdir.'/gradelib.php');
2802 require_once($CFG->dirroot.'/blog/lib.php');
2804 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
2805 return true;
2807 $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
2808 //delete events from calendar
2809 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
2810 foreach($events as $event) {
2811 delete_event($event->id);
2814 //delete grade items, outcome items and grades attached to modules
2815 if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
2816 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
2817 foreach ($grade_items as $grade_item) {
2818 $grade_item->delete('moddelete');
2821 // Delete completion and availability data; it is better to do this even if the
2822 // features are not turned on, in case they were turned on previously (these will be
2823 // very quick on an empty table)
2824 $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
2825 $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
2827 delete_context(CONTEXT_MODULE, $cm->id);
2828 return $DB->delete_records('course_modules', array('id'=>$cm->id));
2831 function delete_mod_from_section($mod, $section) {
2832 global $DB;
2834 if ($section = $DB->get_record("course_sections", array("id"=>$section)) ) {
2836 $modarray = explode(",", $section->sequence);
2838 if ($key = array_keys ($modarray, $mod)) {
2839 array_splice($modarray, $key[0], 1);
2840 $newsequence = implode(",", $modarray);
2841 return $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
2842 } else {
2843 return false;
2847 return false;
2851 * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
2853 * @param object $course
2854 * @param int $section
2855 * @param int $move (-1 or 1)
2857 function move_section($course, $section, $move) {
2858 /// Moves a whole course section up and down within the course
2859 global $USER, $DB;
2861 if (!$move) {
2862 return true;
2865 $sectiondest = $section + $move;
2867 if ($sectiondest > $course->numsections or $sectiondest < 1) {
2868 return false;
2871 if (!$sectionrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$section))) {
2872 return false;
2875 if (!$sectiondestrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$sectiondest))) {
2876 return false;
2879 $DB->set_field("course_sections", "section", $sectiondest, array("id"=>$sectionrecord->id));
2880 $DB->set_field("course_sections", "section", $section, array("id"=>$sectiondestrecord->id));
2882 // if the focus is on the section that is being moved, then move the focus along
2883 if (course_get_display($course->id) == $section) {
2884 course_set_display($course->id, $sectiondest);
2887 // Check for duplicates and fix order if needed.
2888 // There is a very rare case that some sections in the same course have the same section id.
2889 $sections = $DB->get_records('course_sections', array('course'=>$course->id), 'section ASC');
2890 $n = 0;
2891 foreach ($sections as $section) {
2892 if ($section->section != $n) {
2893 $DB->set_field('course_sections', 'section', $n, array('id'=>$section->id));
2895 $n++;
2897 return true;
2901 * Moves a section within a course, from a position to another.
2902 * Be very careful: $section and $destination refer to section number,
2903 * not id!.
2905 * @param object $course
2906 * @param int $section Section number (not id!!!)
2907 * @param int $destination
2908 * @return boolean Result
2910 function move_section_to($course, $section, $destination) {
2911 /// Moves a whole course section up and down within the course
2912 global $USER, $DB;
2914 if (!$destination && $destination != 0) {
2915 return true;
2918 if ($destination > $course->numsections) {
2919 return false;
2922 // Get all sections for this course and re-order them (2 of them should now share the same section number)
2923 if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
2924 'section ASC, id ASC', 'id, section')) {
2925 return false;
2928 $sections = reorder_sections($sections, $section, $destination);
2930 // Update all sections
2931 foreach ($sections as $id => $position) {
2932 $DB->set_field('course_sections', 'section', $position, array('id' => $id));
2935 // if the focus is on the section that is being moved, then move the focus along
2936 if (course_get_display($course->id) == $section) {
2937 course_set_display($course->id, $destination);
2939 return true;
2943 * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
2944 * an original position number and a target position number, rebuilds the array so that the
2945 * move is made without any duplication of section positions.
2946 * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
2947 * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
2949 * @param array $sections
2950 * @param int $origin_position
2951 * @param int $target_position
2952 * @return array
2954 function reorder_sections($sections, $origin_position, $target_position) {
2955 if (!is_array($sections)) {
2956 return false;
2959 // We can't move section position 0
2960 if ($origin_position < 1) {
2961 echo "We can't move section position 0";
2962 return false;
2965 // Locate origin section in sections array
2966 if (!$origin_key = array_search($origin_position, $sections)) {
2967 echo "searched position not in sections array";
2968 return false; // searched position not in sections array
2971 // Extract origin section
2972 $origin_section = $sections[$origin_key];
2973 unset($sections[$origin_key]);
2975 // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
2976 $found = false;
2977 $append_array = array();
2978 foreach ($sections as $id => $position) {
2979 if ($found) {
2980 $append_array[$id] = $position;
2981 unset($sections[$id]);
2983 if ($position == $target_position) {
2984 $found = true;
2988 // Append moved section
2989 $sections[$origin_key] = $origin_section;
2991 // Append rest of array (if applicable)
2992 if (!empty($append_array)) {
2993 foreach ($append_array as $id => $position) {
2994 $sections[$id] = $position;
2998 // Renumber positions
2999 $position = 0;
3000 foreach ($sections as $id => $p) {
3001 $sections[$id] = $position;
3002 $position++;
3005 return $sections;
3010 * Move the module object $mod to the specified $section
3011 * If $beforemod exists then that is the module
3012 * before which $modid should be inserted
3013 * All parameters are objects
3015 function moveto_module($mod, $section, $beforemod=NULL) {
3016 global $DB, $OUTPUT;
3018 /// Remove original module from original section
3019 if (! delete_mod_from_section($mod->id, $mod->section)) {
3020 echo $OUTPUT->notification("Could not delete module from existing section");
3023 /// Update module itself if necessary
3025 if ($mod->section != $section->id) {
3026 $mod->section = $section->id;
3027 $DB->update_record("course_modules", $mod);
3028 // if moving to a hidden section then hide module
3029 if (!$section->visible) {
3030 set_coursemodule_visible($mod->id, 0);
3034 /// Add the module into the new section
3036 $mod->course = $section->course;
3037 $mod->section = $section->section; // need relative reference
3038 $mod->coursemodule = $mod->id;
3040 if (! add_mod_to_section($mod, $beforemod)) {
3041 return false;
3044 return true;
3048 * Produces the editing buttons for a module
3050 * @global core_renderer $OUTPUT
3051 * @staticvar type $str
3052 * @param stdClass $mod The module to produce editing buttons for
3053 * @param bool $absolute If true an absolute link is produced (default true)
3054 * @param bool $moveselect If true a move seleciton process is used (default true)
3055 * @param int $indent The current indenting
3056 * @param int $section The section to link back to
3057 * @return string XHTML for the editing buttons
3059 function make_editing_buttons(stdClass $mod, $absolute = true, $moveselect = true, $indent=-1, $section=-1) {
3060 global $CFG, $OUTPUT;
3062 static $str;
3064 $coursecontext = get_context_instance(CONTEXT_COURSE, $mod->course);
3065 $modcontext = get_context_instance(CONTEXT_MODULE, $mod->id);
3067 // no permission to edit
3068 if (!has_capability('moodle/course:manageactivities', $modcontext)) {
3069 return false;
3072 if (!isset($str)) {
3073 $str = new stdClass;
3074 $str->assign = get_string("assignroles", 'role');
3075 $str->delete = get_string("delete");
3076 $str->move = get_string("move");
3077 $str->moveup = get_string("moveup");
3078 $str->movedown = get_string("movedown");
3079 $str->moveright = get_string("moveright");
3080 $str->moveleft = get_string("moveleft");
3081 $str->update = get_string("update");
3082 $str->duplicate = get_string("duplicate");
3083 $str->hide = get_string("hide");
3084 $str->show = get_string("show");
3085 $str->clicktochange = get_string("clicktochange");
3086 $str->forcedmode = get_string("forcedmode");
3087 $str->groupsnone = get_string("groupsnone");
3088 $str->groupsseparate = get_string("groupsseparate");
3089 $str->groupsvisible = get_string("groupsvisible");
3092 if ($absolute) {
3093 $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
3094 } else {
3095 $baseurl = new moodle_url('mod.php', array('sesskey' => sesskey()));
3098 if ($section >= 0) {
3099 $baseurl->param('sr', $section);
3101 $actions = array();
3103 // leftright
3104 if (has_capability('moodle/course:update', $coursecontext)) {
3105 if (right_to_left()) { // Exchange arrows on RTL
3106 $rightarrow = 't/left';
3107 $leftarrow = 't/right';
3108 } else {
3109 $rightarrow = 't/right';
3110 $leftarrow = 't/left';
3113 if ($indent > 0) {
3114 $actions[] = new action_link(
3115 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
3116 new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall')),
3117 null,
3118 array('class' => 'editing_moveleft', 'title' => $str->moveleft)
3121 if ($indent >= 0) {
3122 $actions[] = new action_link(
3123 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
3124 new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall')),
3125 null,
3126 array('class' => 'editing_moveright', 'title' => $str->moveright)
3131 // move
3132 if (has_capability('moodle/course:update', $coursecontext)) {
3133 if ($moveselect) {
3134 $actions[] = new action_link(
3135 new moodle_url($baseurl, array('copy' => $mod->id)),
3136 new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall')),
3137 null,
3138 array('class' => 'editing_move', 'title' => $str->move)
3140 } else {
3141 $actions[] = new action_link(
3142 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '-1')),
3143 new pix_icon('t/up', $str->moveup, 'moodle', array('class' => 'iconsmall')),
3144 null,
3145 array('class' => 'editing_moveup', 'title' => $str->moveup)
3147 $actions[] = new action_link(
3148 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '1')),
3149 new pix_icon('t/down', $str->movedown, 'moodle', array('class' => 'iconsmall')),
3150 null,
3151 array('class' => 'editing_movedown', 'title' => $str->movedown)
3156 // Update
3157 $actions[] = new action_link(
3158 new moodle_url($baseurl, array('update' => $mod->id)),
3159 new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall')),
3160 null,
3161 array('class' => 'editing_update', 'title' => $str->update)
3164 // Duplicate
3165 $actions[] = new action_link(
3166 new moodle_url($baseurl, array('duplicate' => $mod->id)),
3167 new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall')),
3168 null,
3169 array('class' => 'editing_duplicate', 'title' => $str->duplicate)
3172 // Delete
3173 $actions[] = new action_link(
3174 new moodle_url($baseurl, array('delete' => $mod->id)),
3175 new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall')),
3176 null,
3177 array('class' => 'editing_delete', 'title' => $str->delete)
3180 // hideshow
3181 if (has_capability('moodle/course:activityvisibility', $modcontext)) {
3182 if ($mod->visible) {
3183 $actions[] = new action_link(
3184 new moodle_url($baseurl, array('hide' => $mod->id)),
3185 new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall')),
3186 null,
3187 array('class' => 'editing_hide', 'title' => $str->hide)
3189 } else {
3190 $actions[] = new action_link(
3191 new moodle_url($baseurl, array('show' => $mod->id)),
3192 new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall')),
3193 null,
3194 array('class' => 'editing_show', 'title' => $str->show)
3199 // groupmode
3200 if ($mod->groupmode !== false) {
3201 if ($mod->groupmode == SEPARATEGROUPS) {
3202 $groupmode = 0;
3203 $grouptitle = $str->groupsseparate;
3204 $groupclass = 'editing_groupsseparate';
3205 $groupimage = 't/groups';
3206 } else if ($mod->groupmode == VISIBLEGROUPS) {
3207 $groupmode = 1;
3208 $grouptitle = $str->groupsvisible;
3209 $groupclass = 'editing_groupsvisible';
3210 $groupimage = 't/groupv';
3211 } else {
3212 $groupmode = 2;
3213 $grouptitle = $str->groupsnone;
3214 $groupclass = 'editing_groupsnone';
3215 $groupimage = 't/groupn';
3217 if ($mod->groupmodelink) {
3218 $actions[] = new action_link(
3219 new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
3220 new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall')),
3221 null,
3222 array('class' => $groupclass, 'title' => $grouptitle.' ('.$str->clicktochange.')')
3224 } else {
3225 $actions[] = new pix_icon($groupimage, $grouptitle, 'moodle', array('title' => $grouptitle.' ('.$str->forcedmode.')', 'class' => 'iconsmall'));
3229 // Assign
3230 if (has_capability('moodle/course:managegroups', $modcontext)){
3231 $actions[] = new action_link(
3232 new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid' => $modcontext->id)),
3233 new pix_icon('i/roles', $str->assign, 'moodle', array('class' => 'iconsmall')),
3234 null,
3235 array('class' => 'editing_assign', 'title' => $str->assign)
3239 $output = html_writer::start_tag('span', array('class' => 'commands'));
3240 foreach ($actions as $action) {
3241 if ($action instanceof renderable) {
3242 $output .= $OUTPUT->render($action);
3243 } else {
3244 $output .= $action;
3247 $output .= html_writer::end_tag('span');
3248 return $output;
3252 * given a course object with shortname & fullname, this function will
3253 * truncate the the number of chars allowed and add ... if it was too long
3255 function course_format_name ($course,$max=100) {
3257 $str = $course->shortname.': '. $course->fullname;
3258 if (strlen($str) <= $max) {
3259 return $str;
3261 else {
3262 return substr($str,0,$max-3).'...';
3266 function update_restricted_mods($course, $mods) {
3267 global $DB;
3269 /// Delete all the current restricted list
3270 $DB->delete_records('course_allowed_modules', array('course'=>$course->id));
3272 if (empty($course->restrictmodules)) {
3273 return; // We're done
3276 /// Insert the new list of restricted mods
3277 foreach ($mods as $mod) {
3278 if ($mod == 0) {
3279 continue; // this is the 'allow none' option
3281 $am = new stdClass();
3282 $am->course = $course->id;
3283 $am->module = $mod;
3284 $DB->insert_record('course_allowed_modules',$am);
3289 * This function will take an int (module id) or a string (module name)
3290 * and return true or false, whether it's allowed in the given course (object)
3291 * $mod is not allowed to be an object, as the field for the module id is inconsistent
3292 * depending on where in the code it's called from (sometimes $mod->id, sometimes $mod->module)
3295 function course_allowed_module($course,$mod) {
3296 global $DB;
3298 if (empty($course->restrictmodules)) {
3299 return true;
3302 // Admins and admin-like people who can edit everything can also add anything.
3303 // Originally there was a course:update test only, but it did not match the test in course edit form
3304 if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
3305 return true;
3308 if (is_numeric($mod)) {
3309 $modid = $mod;
3310 } else if (is_string($mod)) {
3311 $modid = $DB->get_field('modules', 'id', array('name'=>$mod));
3313 if (empty($modid)) {
3314 return false;
3317 return $DB->record_exists('course_allowed_modules', array('course'=>$course->id, 'module'=>$modid));
3321 * Recursively delete category including all subcategories and courses.
3322 * @param stdClass $category
3323 * @param boolean $showfeedback display some notices
3324 * @return array return deleted courses
3326 function category_delete_full($category, $showfeedback=true) {
3327 global $CFG, $DB;
3328 require_once($CFG->libdir.'/gradelib.php');
3329 require_once($CFG->libdir.'/questionlib.php');
3330 require_once($CFG->dirroot.'/cohort/lib.php');
3332 if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
3333 foreach ($children as $childcat) {
3334 category_delete_full($childcat, $showfeedback);
3338 $deletedcourses = array();
3339 if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC')) {
3340 foreach ($courses as $course) {
3341 if (!delete_course($course, false)) {
3342 throw new moodle_exception('cannotdeletecategorycourse','','',$course->shortname);
3344 $deletedcourses[] = $course;
3348 // move or delete cohorts in this context
3349 cohort_delete_category($category);
3351 // now delete anything that may depend on course category context
3352 grade_course_category_delete($category->id, 0, $showfeedback);
3353 if (!question_delete_course_category($category, 0, $showfeedback)) {
3354 throw new moodle_exception('cannotdeletecategoryquestions','','',$category->name);
3357 // finally delete the category and it's context
3358 $DB->delete_records('course_categories', array('id'=>$category->id));
3359 delete_context(CONTEXT_COURSECAT, $category->id);
3361 events_trigger('course_category_deleted', $category);
3363 return $deletedcourses;
3367 * Delete category, but move contents to another category.
3368 * @param object $ccategory
3369 * @param int $newparentid category id
3370 * @return bool status
3372 function category_delete_move($category, $newparentid, $showfeedback=true) {
3373 global $CFG, $DB, $OUTPUT;
3374 require_once($CFG->libdir.'/gradelib.php');
3375 require_once($CFG->libdir.'/questionlib.php');
3376 require_once($CFG->dirroot.'/cohort/lib.php');
3378 if (!$newparentcat = $DB->get_record('course_categories', array('id'=>$newparentid))) {
3379 return false;
3382 if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
3383 foreach ($children as $childcat) {
3384 move_category($childcat, $newparentcat);
3388 if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC', 'id')) {
3389 if (!move_courses(array_keys($courses), $newparentid)) {
3390 echo $OUTPUT->notification("Error moving courses");
3391 return false;
3393 echo $OUTPUT->notification(get_string('coursesmovedout', '', format_string($category->name)), 'notifysuccess');
3396 // move or delete cohorts in this context
3397 cohort_delete_category($category);
3399 // now delete anything that may depend on course category context
3400 grade_course_category_delete($category->id, $newparentid, $showfeedback);
3401 if (!question_delete_course_category($category, $newparentcat, $showfeedback)) {
3402 echo $OUTPUT->notification(get_string('errordeletingquestionsfromcategory', 'question', $category), 'notifysuccess');
3403 return false;
3406 // finally delete the category and it's context
3407 $DB->delete_records('course_categories', array('id'=>$category->id));
3408 delete_context(CONTEXT_COURSECAT, $category->id);
3410 events_trigger('course_category_deleted', $category);
3412 echo $OUTPUT->notification(get_string('coursecategorydeleted', '', format_string($category->name)), 'notifysuccess');
3414 return true;
3418 * Efficiently moves many courses around while maintaining
3419 * sortorder in order.
3421 * @param array $courseids is an array of course ids
3422 * @param int $categoryid
3423 * @return bool success
3425 function move_courses($courseids, $categoryid) {
3426 global $CFG, $DB, $OUTPUT;
3428 if (empty($courseids)) {
3429 // nothing to do
3430 return;
3433 if (!$category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
3434 return false;
3437 $courseids = array_reverse($courseids);
3438 $newparent = get_context_instance(CONTEXT_COURSECAT, $category->id);
3439 $i = 1;
3441 foreach ($courseids as $courseid) {
3442 if ($course = $DB->get_record('course', array('id'=>$courseid), 'id, category')) {
3443 $course = new stdClass();
3444 $course->id = $courseid;
3445 $course->category = $category->id;
3446 $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
3447 if ($category->visible == 0) {
3448 // hide the course when moving into hidden category,
3449 // do not update the visibleold flag - we want to get to previous state if somebody unhides the category
3450 $course->visible = 0;
3453 $DB->update_record('course', $course);
3455 $context = get_context_instance(CONTEXT_COURSE, $course->id);
3456 context_moved($context, $newparent);
3459 fix_course_sortorder();
3461 return true;
3465 * Hide course category and child course and subcategories
3466 * @param stdClass $category
3467 * @return void
3469 function course_category_hide($category) {
3470 global $DB;
3472 $category->visible = 0;
3473 $DB->set_field('course_categories', 'visible', 0, array('id'=>$category->id));
3474 $DB->set_field('course_categories', 'visibleold', 0, array('id'=>$category->id));
3475 $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($category->id)); // store visible flag so that we can return to it if we immediately unhide
3476 $DB->set_field('course', 'visible', 0, array('category' => $category->id));
3477 // get all child categories and hide too
3478 if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
3479 foreach ($subcats as $cat) {
3480 $DB->set_field('course_categories', 'visibleold', $cat->visible, array('id'=>$cat->id));
3481 $DB->set_field('course_categories', 'visible', 0, array('id'=>$cat->id));
3482 $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($cat->id));
3483 $DB->set_field('course', 'visible', 0, array('category' => $cat->id));
3489 * Show course category and child course and subcategories
3490 * @param stdClass $category
3491 * @return void
3493 function course_category_show($category) {
3494 global $DB;
3496 $category->visible = 1;
3497 $DB->set_field('course_categories', 'visible', 1, array('id'=>$category->id));
3498 $DB->set_field('course_categories', 'visibleold', 1, array('id'=>$category->id));
3499 $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($category->id));
3500 // get all child categories and unhide too
3501 if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
3502 foreach ($subcats as $cat) {
3503 if ($cat->visibleold) {
3504 $DB->set_field('course_categories', 'visible', 1, array('id'=>$cat->id));
3506 $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($cat->id));
3512 * Efficiently moves a category - NOTE that this can have
3513 * a huge impact access-control-wise...
3515 function move_category($category, $newparentcat) {
3516 global $CFG, $DB;
3518 $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
3520 $hidecat = false;
3521 if (empty($newparentcat->id)) {
3522 $DB->set_field('course_categories', 'parent', 0, array('id'=>$category->id));
3524 $newparent = get_context_instance(CONTEXT_SYSTEM);
3526 } else {
3527 $DB->set_field('course_categories', 'parent', $newparentcat->id, array('id'=>$category->id));
3528 $newparent = get_context_instance(CONTEXT_COURSECAT, $newparentcat->id);
3530 if (!$newparentcat->visible and $category->visible) {
3531 // better hide category when moving into hidden category, teachers may unhide afterwards and the hidden children will be restored properly
3532 $hidecat = true;
3536 context_moved($context, $newparent);
3538 // now make it last in new category
3539 $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('id'=>$category->id));
3541 // and fix the sortorders
3542 fix_course_sortorder();
3544 if ($hidecat) {
3545 course_category_hide($category);
3550 * Returns the display name of the given section that the course prefers.
3552 * This function utilizes a callback that can be implemented within the course
3553 * formats lib.php file to customize the display name that is used to reference
3554 * the section.
3556 * By default (if callback is not defined) the method
3557 * {@see get_numeric_section_name} is called instead.
3559 * @param stdClass $course The course to get the section name for
3560 * @param stdClass $section Section object from database
3561 * @return Display name that the course format prefers, e.g. "Week 2"
3563 * @see get_generic_section_name
3565 function get_section_name(stdClass $course, stdClass $section) {
3566 global $CFG;
3568 /// Inelegant hack for bug 3408
3569 if ($course->format == 'site') {
3570 return get_string('site');
3573 // Use course formatter callback if it exists
3574 $namingfile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php';
3575 $namingfunction = 'callback_'.$course->format.'_get_section_name';
3576 if (!function_exists($namingfunction) && file_exists($namingfile)) {
3577 require_once $namingfile;
3579 if (function_exists($namingfunction)) {
3580 return $namingfunction($course, $section);
3583 // else, default behavior:
3584 return get_generic_section_name($course->format, $section);
3588 * Gets the generic section name for a courses section.
3590 * @param string $format Course format ID e.g. 'weeks' $course->format
3591 * @param stdClass $section Section object from database
3592 * @return Display name that the course format prefers, e.g. "Week 2"
3594 function get_generic_section_name($format, stdClass $section) {
3595 return get_string('sectionname', "format_$format") . ' ' . $section->section;
3599 function course_format_uses_sections($format) {
3600 global $CFG;
3602 $featurefile = $CFG->dirroot.'/course/format/'.$format.'/lib.php';
3603 $featurefunction = 'callback_'.$format.'_uses_sections';
3604 if (!function_exists($featurefunction) && file_exists($featurefile)) {
3605 require_once $featurefile;
3607 if (function_exists($featurefunction)) {
3608 return $featurefunction();
3611 return false;
3615 * Returns the information about the ajax support in the given source format
3617 * The returned object's property (boolean)capable indicates that
3618 * the course format supports Moodle course ajax features.
3619 * The property (array)testedbrowsers can be used as a parameter for {@see ajaxenabled()}.
3621 * @param string $format
3622 * @return stdClass
3624 function course_format_ajax_support($format) {
3625 global $CFG;
3627 // set up default values
3628 $ajaxsupport = new stdClass();
3629 $ajaxsupport->capable = false;
3630 $ajaxsupport->testedbrowsers = array();
3632 // get the information from the course format library
3633 $featurefile = $CFG->dirroot.'/course/format/'.$format.'/lib.php';
3634 $featurefunction = 'callback_'.$format.'_ajax_support';
3635 if (!function_exists($featurefunction) && file_exists($featurefile)) {
3636 require_once $featurefile;
3638 if (function_exists($featurefunction)) {
3639 $formatsupport = $featurefunction();
3640 if (isset($formatsupport->capable)) {
3641 $ajaxsupport->capable = $formatsupport->capable;
3643 if (is_array($formatsupport->testedbrowsers)) {
3644 $ajaxsupport->testedbrowsers = $formatsupport->testedbrowsers;
3648 return $ajaxsupport;
3652 * Can the current user delete this course?
3653 * Course creators have exception,
3654 * 1 day after the creation they can sill delete the course.
3655 * @param int $courseid
3656 * @return boolean
3658 function can_delete_course($courseid) {
3659 global $USER, $DB;
3661 $context = get_context_instance(CONTEXT_COURSE, $courseid);
3663 if (has_capability('moodle/course:delete', $context)) {
3664 return true;
3667 // hack: now try to find out if creator created this course recently (1 day)
3668 if (!has_capability('moodle/course:create', $context)) {
3669 return false;
3672 $since = time() - 60*60*24;
3674 $params = array('userid'=>$USER->id, 'url'=>"view.php?id=$courseid", 'since'=>$since);
3675 $select = "module = 'course' AND action = 'new' AND userid = :userid AND url = :url AND time > :since";
3677 return $DB->record_exists_select('log', $select, $params);
3681 * Save the Your name for 'Some role' strings.
3683 * @param integer $courseid the id of this course.
3684 * @param array $data the data that came from the course settings form.
3686 function save_local_role_names($courseid, $data) {
3687 global $DB;
3688 $context = get_context_instance(CONTEXT_COURSE, $courseid);
3690 foreach ($data as $fieldname => $value) {
3691 if (strpos($fieldname, 'role_') !== 0) {
3692 continue;
3694 list($ignored, $roleid) = explode('_', $fieldname);
3696 // make up our mind whether we want to delete, update or insert
3697 if (!$value) {
3698 $DB->delete_records('role_names', array('contextid' => $context->id, 'roleid' => $roleid));
3700 } else if ($rolename = $DB->get_record('role_names', array('contextid' => $context->id, 'roleid' => $roleid))) {
3701 $rolename->name = $value;
3702 $DB->update_record('role_names', $rolename);
3704 } else {
3705 $rolename = new stdClass;
3706 $rolename->contextid = $context->id;
3707 $rolename->roleid = $roleid;
3708 $rolename->name = $value;
3709 $DB->insert_record('role_names', $rolename);
3715 * Create a course and either return a $course object
3717 * Please note this functions does not verify any access control,
3718 * the calling code is responsible for all validation (usually it is the form definition).
3720 * @param array $editoroptions course description editor options
3721 * @param object $data - all the data needed for an entry in the 'course' table
3722 * @return object new course instance
3724 function create_course($data, $editoroptions = NULL) {
3725 global $CFG, $DB;
3727 //check the categoryid - must be given for all new courses
3728 $category = $DB->get_record('course_categories', array('id'=>$data->category), '*', MUST_EXIST);
3730 //check if the shortname already exist
3731 if (!empty($data->shortname)) {
3732 if ($DB->record_exists('course', array('shortname' => $data->shortname))) {
3733 throw new moodle_exception('shortnametaken');
3737 //check if the id number already exist
3738 if (!empty($data->idnumber)) {
3739 if ($DB->record_exists('course', array('idnumber' => $data->idnumber))) {
3740 throw new moodle_exception('idnumbertaken');
3744 $data->timecreated = time();
3745 $data->timemodified = $data->timecreated;
3747 // place at beginning of any category
3748 $data->sortorder = 0;
3750 if ($editoroptions) {
3751 // summary text is updated later, we need context to store the files first
3752 $data->summary = '';
3753 $data->summary_format = FORMAT_HTML;
3756 if (!isset($data->visible)) {
3757 // data not from form, add missing visibility info
3758 $data->visible = $category->visible;
3760 $data->visibleold = $data->visible;
3762 $newcourseid = $DB->insert_record('course', $data);
3763 $context = get_context_instance(CONTEXT_COURSE, $newcourseid, MUST_EXIST);
3765 if ($editoroptions) {
3766 // Save the files used in the summary editor and store
3767 $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'summary', 0);
3768 $DB->set_field('course', 'summary', $data->summary, array('id'=>$newcourseid));
3769 $DB->set_field('course', 'summaryformat', $data->summary_format, array('id'=>$newcourseid));
3772 $course = $DB->get_record('course', array('id'=>$newcourseid));
3774 // Setup the blocks
3775 blocks_add_default_course_blocks($course);
3777 $section = new stdClass();
3778 $section->course = $course->id; // Create a default section.
3779 $section->section = 0;
3780 $section->summaryformat = FORMAT_HTML;
3781 $DB->insert_record('course_sections', $section);
3783 fix_course_sortorder();
3785 // update module restrictions
3786 if ($course->restrictmodules) {
3787 if (isset($data->allowedmods)) {
3788 update_restricted_mods($course, $data->allowedmods);
3789 } else {
3790 if (!empty($CFG->defaultallowedmodules)) {
3791 update_restricted_mods($course, explode(',', $CFG->defaultallowedmodules));
3796 // new context created - better mark it as dirty
3797 mark_context_dirty($context->path);
3799 // Save any custom role names.
3800 save_local_role_names($course->id, (array)$data);
3802 // set up enrolments
3803 enrol_course_updated(true, $course, $data);
3805 add_to_log(SITEID, 'course', 'new', 'view.php?id='.$course->id, $data->fullname.' (ID '.$course->id.')');
3807 // Trigger events
3808 events_trigger('course_created', $course);
3810 return $course;
3814 * Update a course.
3816 * Please note this functions does not verify any access control,
3817 * the calling code is responsible for all validation (usually it is the form definition).
3819 * @param object $data - all the data needed for an entry in the 'course' table
3820 * @param array $editoroptions course description editor options
3821 * @return void
3823 function update_course($data, $editoroptions = NULL) {
3824 global $CFG, $DB;
3826 $data->timemodified = time();
3828 $oldcourse = $DB->get_record('course', array('id'=>$data->id), '*', MUST_EXIST);
3829 $context = get_context_instance(CONTEXT_COURSE, $oldcourse->id);
3831 if ($editoroptions) {
3832 $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'summary', 0);
3835 if (!isset($data->category) or empty($data->category)) {
3836 // prevent nulls and 0 in category field
3837 unset($data->category);
3839 $movecat = (isset($data->category) and $oldcourse->category != $data->category);
3841 if (!isset($data->visible)) {
3842 // data not from form, add missing visibility info
3843 $data->visible = $oldcourse->visible;
3846 if ($data->visible != $oldcourse->visible) {
3847 // reset the visibleold flag when manually hiding/unhiding course
3848 $data->visibleold = $data->visible;
3849 } else {
3850 if ($movecat) {
3851 $newcategory = $DB->get_record('course_categories', array('id'=>$data->category));
3852 if (empty($newcategory->visible)) {
3853 // make sure when moving into hidden category the course is hidden automatically
3854 $data->visible = 0;
3859 // Update with the new data
3860 $DB->update_record('course', $data);
3862 $course = $DB->get_record('course', array('id'=>$data->id));
3864 if ($movecat) {
3865 $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
3866 context_moved($context, $newparent);
3869 fix_course_sortorder();
3871 // Test for and remove blocks which aren't appropriate anymore
3872 blocks_remove_inappropriate($course);
3874 // update module restrictions
3875 if (isset($data->allowedmods)) {
3876 update_restricted_mods($course, $data->allowedmods);
3879 // Save any custom role names.
3880 save_local_role_names($course->id, $data);
3882 // update enrol settings
3883 enrol_course_updated(false, $course, $data);
3885 add_to_log($course->id, "course", "update", "edit.php?id=$course->id", $course->id);
3887 // Trigger events
3888 events_trigger('course_updated', $course);
3892 * Average number of participants
3893 * @return integer
3895 function average_number_of_participants() {
3896 global $DB, $SITE;
3898 //count total of enrolments for visible course (except front page)
3899 $sql = 'SELECT COUNT(*) FROM (
3900 SELECT DISTINCT ue.userid, e.courseid
3901 FROM {user_enrolments} ue, {enrol} e, {course} c
3902 WHERE ue.enrolid = e.id
3903 AND e.courseid <> :siteid
3904 AND c.id = e.courseid
3905 AND c.visible = 1) as total';
3906 $params = array('siteid' => $SITE->id);
3907 $enrolmenttotal = $DB->count_records_sql($sql, $params);
3910 //count total of visible courses (minus front page)
3911 $coursetotal = $DB->count_records('course', array('visible' => 1));
3912 $coursetotal = $coursetotal - 1 ;
3914 //average of enrolment
3915 if (empty($coursetotal)) {
3916 $participantaverage = 0;
3917 } else {
3918 $participantaverage = $enrolmenttotal / $coursetotal;
3921 return $participantaverage;
3925 * Average number of course modules
3926 * @return integer
3928 function average_number_of_courses_modules() {
3929 global $DB, $SITE;
3931 //count total of visible course module (except front page)
3932 $sql = 'SELECT COUNT(*) FROM (
3933 SELECT cm.course, cm.module
3934 FROM {course} c, {course_modules} cm
3935 WHERE c.id = cm.course
3936 AND c.id <> :siteid
3937 AND cm.visible = 1
3938 AND c.visible = 1) as total';
3939 $params = array('siteid' => $SITE->id);
3940 $moduletotal = $DB->count_records_sql($sql, $params);
3943 //count total of visible courses (minus front page)
3944 $coursetotal = $DB->count_records('course', array('visible' => 1));
3945 $coursetotal = $coursetotal - 1 ;
3947 //average of course module
3948 if (empty($coursetotal)) {
3949 $coursemoduleaverage = 0;
3950 } else {
3951 $coursemoduleaverage = $moduletotal / $coursetotal;
3954 return $coursemoduleaverage;
3958 * This class pertains to course requests and contains methods associated with
3959 * create, approving, and removing course requests.
3961 * Please note we do not allow embedded images here because there is no context
3962 * to store them with proper access control.
3964 * @copyright 2009 Sam Hemelryk
3965 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3966 * @since Moodle 2.0
3968 * @property-read int $id
3969 * @property-read string $fullname
3970 * @property-read string $shortname
3971 * @property-read string $summary
3972 * @property-read int $summaryformat
3973 * @property-read int $summarytrust
3974 * @property-read string $reason
3975 * @property-read int $requester
3977 class course_request {
3980 * This is the stdClass that stores the properties for the course request
3981 * and is externally accessed through the __get magic method
3982 * @var stdClass
3984 protected $properties;
3987 * An array of options for the summary editor used by course request forms.
3988 * This is initially set by {@link summary_editor_options()}
3989 * @var array
3990 * @static
3992 protected static $summaryeditoroptions;
3995 * Static function to prepare the summary editor for working with a course
3996 * request.
3998 * @static
3999 * @param null|stdClass $data Optional, an object containing the default values
4000 * for the form, these may be modified when preparing the
4001 * editor so this should be called before creating the form
4002 * @return stdClass An object that can be used to set the default values for
4003 * an mforms form
4005 public static function prepare($data=null) {
4006 if ($data === null) {
4007 $data = new stdClass;
4009 $data = file_prepare_standard_editor($data, 'summary', self::summary_editor_options());
4010 return $data;
4014 * Static function to create a new course request when passed an array of properties
4015 * for it.
4017 * This function also handles saving any files that may have been used in the editor
4019 * @static
4020 * @param stdClass $data
4021 * @return course_request The newly created course request
4023 public static function create($data) {
4024 global $USER, $DB, $CFG;
4025 $data->requester = $USER->id;
4027 // Summary is a required field so copy the text over
4028 $data->summary = $data->summary_editor['text'];
4029 $data->summaryformat = $data->summary_editor['format'];
4031 $data->id = $DB->insert_record('course_request', $data);
4033 // Create a new course_request object and return it
4034 $request = new course_request($data);
4036 // Notify the admin if required.
4037 if ($users = get_users_from_config($CFG->courserequestnotify, 'moodle/site:approvecourse')) {
4039 $a = new stdClass;
4040 $a->link = "$CFG->wwwroot/course/pending.php";
4041 $a->user = fullname($USER);
4042 $subject = get_string('courserequest');
4043 $message = get_string('courserequestnotifyemail', 'admin', $a);
4044 foreach ($users as $user) {
4045 $request->notify($user, $USER, 'courserequested', $subject, $message);
4049 return $request;
4053 * Returns an array of options to use with a summary editor
4055 * @uses course_request::$summaryeditoroptions
4056 * @return array An array of options to use with the editor
4058 public static function summary_editor_options() {
4059 global $CFG;
4060 if (self::$summaryeditoroptions === null) {
4061 self::$summaryeditoroptions = array('maxfiles' => 0, 'maxbytes'=>0);
4063 return self::$summaryeditoroptions;
4067 * Loads the properties for this course request object. Id is required and if
4068 * only id is provided then we load the rest of the properties from the database
4070 * @param stdClass|int $properties Either an object containing properties
4071 * or the course_request id to load
4073 public function __construct($properties) {
4074 global $DB;
4075 if (empty($properties->id)) {
4076 if (empty($properties)) {
4077 throw new coding_exception('You must provide a course request id when creating a course_request object');
4079 $id = $properties;
4080 $properties = new stdClass;
4081 $properties->id = (int)$id;
4082 unset($id);
4084 if (empty($properties->requester)) {
4085 if (!($this->properties = $DB->get_record('course_request', array('id' => $properties->id)))) {
4086 print_error('unknowncourserequest');
4088 } else {
4089 $this->properties = $properties;
4091 $this->properties->collision = null;
4095 * Returns the requested property
4097 * @param string $key
4098 * @return mixed
4100 public function __get($key) {
4101 return $this->properties->$key;
4105 * Override this to ensure empty($request->blah) calls return a reliable answer...
4107 * This is required because we define the __get method
4109 * @param mixed $key
4110 * @return bool True is it not empty, false otherwise
4112 public function __isset($key) {
4113 return (!empty($this->properties->$key));
4117 * Returns the user who requested this course
4119 * Uses a static var to cache the results and cut down the number of db queries
4121 * @staticvar array $requesters An array of cached users
4122 * @return stdClass The user who requested the course
4124 public function get_requester() {
4125 global $DB;
4126 static $requesters= array();
4127 if (!array_key_exists($this->properties->requester, $requesters)) {
4128 $requesters[$this->properties->requester] = $DB->get_record('user', array('id'=>$this->properties->requester));
4130 return $requesters[$this->properties->requester];
4134 * Checks that the shortname used by the course does not conflict with any other
4135 * courses that exist
4137 * @param string|null $shortnamemark The string to append to the requests shortname
4138 * should a conflict be found
4139 * @return bool true is there is a conflict, false otherwise
4141 public function check_shortname_collision($shortnamemark = '[*]') {
4142 global $DB;
4144 if ($this->properties->collision !== null) {
4145 return $this->properties->collision;
4148 if (empty($this->properties->shortname)) {
4149 debugging('Attempting to check a course request shortname before it has been set', DEBUG_DEVELOPER);
4150 $this->properties->collision = false;
4151 } else if ($DB->record_exists('course', array('shortname' => $this->properties->shortname))) {
4152 if (!empty($shortnamemark)) {
4153 $this->properties->shortname .= ' '.$shortnamemark;
4155 $this->properties->collision = true;
4156 } else {
4157 $this->properties->collision = false;
4159 return $this->properties->collision;
4163 * This function approves the request turning it into a course
4165 * This function converts the course request into a course, at the same time
4166 * transferring any files used in the summary to the new course and then removing
4167 * the course request and the files associated with it.
4169 * @return int The id of the course that was created from this request
4171 public function approve() {
4172 global $CFG, $DB, $USER;
4174 $user = $DB->get_record('user', array('id' => $this->properties->requester, 'deleted'=>0), '*', MUST_EXIST);
4176 $category = get_course_category($CFG->defaultrequestcategory);
4177 $courseconfig = get_config('moodlecourse');
4179 // Transfer appropriate settings
4180 $data = clone($this->properties);
4181 unset($data->id);
4182 unset($data->reason);
4183 unset($data->requester);
4185 // Set category
4186 $data->category = $category->id;
4187 $data->sortorder = $category->sortorder; // place as the first in category
4189 // Set misc settings
4190 $data->requested = 1;
4191 if (!empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor != 'none' && !empty($CFG->restrictbydefault)) {
4192 $data->restrictmodules = 1;
4195 // Apply course default settings
4196 $data->format = $courseconfig->format;
4197 $data->numsections = $courseconfig->numsections;
4198 $data->hiddensections = $courseconfig->hiddensections;
4199 $data->newsitems = $courseconfig->newsitems;
4200 $data->showgrades = $courseconfig->showgrades;
4201 $data->showreports = $courseconfig->showreports;
4202 $data->maxbytes = $courseconfig->maxbytes;
4203 $data->groupmode = $courseconfig->groupmode;
4204 $data->groupmodeforce = $courseconfig->groupmodeforce;
4205 $data->visible = $courseconfig->visible;
4206 $data->visibleold = $data->visible;
4207 $data->lang = $courseconfig->lang;
4209 $course = create_course($data);
4210 $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
4212 // add enrol instances
4213 if (!$DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {
4214 if ($manual = enrol_get_plugin('manual')) {
4215 $manual->add_default_instance($course);
4219 // enrol the requester as teacher if necessary
4220 if (!empty($CFG->creatornewroleid) and !is_viewing($context, $user, 'moodle/role:assign') and !is_enrolled($context, $user, 'moodle/role:assign')) {
4221 enrol_try_internal_enrol($course->id, $user->id, $CFG->creatornewroleid);
4224 $this->delete();
4226 $a = new stdClass();
4227 $a->name = $course->fullname;
4228 $a->url = $CFG->wwwroot.'/course/view.php?id=' . $course->id;
4229 $this->notify($user, $USER, 'courserequestapproved', get_string('courseapprovedsubject'), get_string('courseapprovedemail2', 'moodle', $a));
4231 return $course->id;
4235 * Reject a course request
4237 * This function rejects a course request, emailing the requesting user the
4238 * provided notice and then removing the request from the database
4240 * @param string $notice The message to display to the user
4242 public function reject($notice) {
4243 global $USER, $DB;
4244 $user = $DB->get_record('user', array('id' => $this->properties->requester), '*', MUST_EXIST);
4245 $this->notify($user, $USER, 'courserequestrejected', get_string('courserejectsubject'), get_string('courserejectemail', 'moodle', $notice));
4246 $this->delete();
4250 * Deletes the course request and any associated files
4252 public function delete() {
4253 global $DB;
4254 $DB->delete_records('course_request', array('id' => $this->properties->id));
4258 * Send a message from one user to another using events_trigger
4260 * @param object $touser
4261 * @param object $fromuser
4262 * @param string $name
4263 * @param string $subject
4264 * @param string $message
4266 protected function notify($touser, $fromuser, $name='courserequested', $subject, $message) {
4267 $eventdata = new stdClass();
4268 $eventdata->component = 'moodle';
4269 $eventdata->name = $name;
4270 $eventdata->userfrom = $fromuser;
4271 $eventdata->userto = $touser;
4272 $eventdata->subject = $subject;
4273 $eventdata->fullmessage = $message;
4274 $eventdata->fullmessageformat = FORMAT_PLAIN;
4275 $eventdata->fullmessagehtml = '';
4276 $eventdata->smallmessage = '';
4277 $eventdata->notification = 1;
4278 message_send($eventdata);
4283 * Return a list of page types
4284 * @param string $pagetype current page type
4285 * @param stdClass $parentcontext Block's parent context
4286 * @param stdClass $currentcontext Current context of block
4288 function course_page_type_list($pagetype, $parentcontext, $currentcontext) {
4289 // if above course context ,display all course fomats
4290 list($currentcontext, $course, $cm) = get_context_info_array($currentcontext->id);
4291 if ($course->id == SITEID) {
4292 return array('*'=>get_string('page-x', 'pagetype'));
4293 } else {
4294 return array('*'=>get_string('page-x', 'pagetype'),
4295 'course-*'=>get_string('page-course-x', 'pagetype'),
4296 'course-view-*'=>get_string('page-course-view-x', 'pagetype')