MDL-33063 course: Reuse get_all_sections()
[moodle.git] / course / lib.php
CommitLineData
d9cb06dc 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Library of useful functions
20 *
21 * @copyright 1999 Martin Dougiamas http://dougiamas.com
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
23 * @package core
24 * @subpackage course
d9cb06dc 25 */
f9903ed0 26
df997f84
PS
27defined('MOODLE_INTERNAL') || die;
28
4e781c7b 29require_once($CFG->libdir.'/completionlib.php');
8bdc9cac 30require_once($CFG->libdir.'/filelib.php');
f9903ed0 31
92890025 32define('COURSE_MAX_LOGS_PER_PAGE', 1000); // records
92890025 33define('COURSE_MAX_RECENT_PERIOD', 172800); // Two days, in seconds
34define('COURSE_MAX_SUMMARIES_PER_PAGE', 10); // courses
950c35a9 35define('COURSE_MAX_COURSES_PER_DROPDOWN',1000); // max courses in log dropdown before switching to optional
92890025 36define('COURSE_MAX_USERS_PER_DROPDOWN',1000); // max users in log dropdown before switching to optional
220a90c5 37define('FRONTPAGENEWS', '0');
38define('FRONTPAGECOURSELIST', '1');
39define('FRONTPAGECATEGORYNAMES', '2');
40define('FRONTPAGETOPICONLY', '3');
41define('FRONTPAGECATEGORYCOMBO', '4');
6f24e48e 42define('FRONTPAGECOURSELIMIT', 200); // maximum number of courses displayed on the frontpage
43define('EXCELROWS', 65535);
44define('FIRSTUSEDEXCELROW', 3);
60fdc714 45
89bfeee0 46define('MOD_CLASS_ACTIVITY', 0);
47define('MOD_CLASS_RESOURCE', 1);
48
600149be 49function make_log_url($module, $url) {
50 switch ($module) {
bd7be234 51 case 'course':
d48f6068 52 if (strpos($url, 'report/') === 0) {
3ce3bb88 53 // there is only one report type, course reports are deprecated
d48f6068
PS
54 $url = "/$url";
55 break;
56 }
bd7be234 57 case 'file':
58 case 'login':
59 case 'lib':
60 case 'admin':
bd7be234 61 case 'calendar':
bd5d0ce5 62 case 'mnet course':
11003188 63 if (strpos($url, '../') === 0) {
64 $url = ltrim($url, '.');
65 } else {
66 $url = "/course/$url";
67 }
bd5d0ce5 68 break;
01d5d399 69 case 'user':
bd7be234 70 case 'blog':
11003188 71 $url = "/$module/$url";
600149be 72 break;
bd7be234 73 case 'upload':
11003188 74 $url = $url;
c80b7585 75 break;
38fb8190 76 case 'coursetags':
11003188 77 $url = '/'.$url;
38fb8190 78 break;
bd7be234 79 case 'library':
80 case '':
11003188 81 $url = '/';
de2dfe68 82 break;
4597d533 83 case 'message':
11003188 84 $url = "/message/$url";
85 break;
86 case 'notes':
87 $url = "/notes/$url";
4597d533 88 break;
b89e4ad8 89 case 'tag':
90 $url = "/tag/$url";
91 break;
dbcf271b 92 case 'role':
93 $url = '/'.$url;
94 break;
600149be 95 default:
11003188 96 $url = "/mod/$module/$url";
600149be 97 break;
98 }
11003188 99
100 //now let's sanitise urls - there might be some ugly nasties:-(
101 $parts = explode('?', $url);
102 $script = array_shift($parts);
103 if (strpos($script, 'http') === 0) {
104 $script = clean_param($script, PARAM_URL);
105 } else {
106 $script = clean_param($script, PARAM_PATH);
107 }
108
109 $query = '';
110 if ($parts) {
111 $query = implode('', $parts);
112 $query = str_replace('&amp;', '&', $query); // both & and &amp; are stored in db :-|
113 $parts = explode('&', $query);
114 $eq = urlencode('=');
115 foreach ($parts as $key=>$part) {
116 $part = urlencode(urldecode($part));
117 $part = str_replace($eq, '=', $part);
118 $parts[$key] = $part;
119 }
120 $query = '?'.implode('&amp;', $parts);
121 }
122
123 return $script.$query;
600149be 124}
125
92890025 126
c215b32b 127function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
128 $modname="", $modid=0, $modaction="", $groupid=0) {
cb6fec1f 129 global $CFG, $DB;
c215b32b 130
131 // It is assumed that $date is the GMT time of midnight for that day,
132 // and so the next 86400 seconds worth of logs are printed.
133
134 /// Setup for group handling.
238c0dd9 135
136 // TODO: I don't understand group/context/etc. enough to be able to do
c215b32b 137 // something interesting with it here
138 // What is the context of a remote course?
238c0dd9 139
c215b32b 140 /// If the group mode is separate, and this user does not have editing privileges,
141 /// then only the user's group can be viewed.
142 //if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
143 // $groupid = get_current_group($course->id);
144 //}
145 /// If this course doesn't have groups, no groupid can be specified.
146 //else if (!$course->groupmode) {
147 // $groupid = 0;
148 //}
cb6fec1f 149
c215b32b 150 $groupid = 0;
151
152 $joins = array();
92701024 153 $where = '';
c215b32b 154
cb6fec1f 155 $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
156 FROM {mnet_log} l
157 LEFT JOIN {user} u ON l.userid = u.id
158 WHERE ";
159 $params = array();
160
161 $where .= "l.hostid = :hostid";
162 $params['hostid'] = $hostid;
c215b32b 163
164 // TODO: Is 1 really a magic number referring to the sitename?
cb6fec1f 165 if ($course != SITEID || $modid != 0) {
166 $where .= " AND l.course=:courseid";
167 $params['courseid'] = $course;
c215b32b 168 }
169
170 if ($modname) {
cb6fec1f 171 $where .= " AND l.module = :modname";
172 $params['modname'] = $modname;
c215b32b 173 }
174
175 if ('site_errors' === $modid) {
cb6fec1f 176 $where .= " AND ( l.action='error' OR l.action='infected' )";
c215b32b 177 } else if ($modid) {
238c0dd9 178 //TODO: This assumes that modids are the same across sites... probably
c215b32b 179 //not true
cb6fec1f 180 $where .= " AND l.cmid = :modid";
181 $params['modid'] = $modid;
c215b32b 182 }
183
184 if ($modaction) {
185 $firstletter = substr($modaction, 0, 1);
8086b083 186 if ($firstletter == '-') {
47586394 187 $where .= " AND ".$DB->sql_like('l.action', ':modaction', false, true, true);
8086b083
PS
188 $params['modaction'] = '%'.substr($modaction, 1).'%';
189 } else {
190 $where .= " AND ".$DB->sql_like('l.action', ':modaction', false);
191 $params['modaction'] = '%'.$modaction.'%';
c215b32b 192 }
193 }
194
195 if ($user) {
cb6fec1f 196 $where .= " AND l.userid = :user";
197 $params['user'] = $user;
c215b32b 198 }
199
200 if ($date) {
201 $enddate = $date + 86400;
cb6fec1f 202 $where .= " AND l.time > :date AND l.time < :enddate";
203 $params['date'] = $date;
204 $params['enddate'] = $enddate;
c215b32b 205 }
206
207 $result = array();
cb6fec1f 208 $result['totalcount'] = $DB->count_records_sql("SELECT COUNT('x') FROM {mnet_log} l WHERE $where", $params);
c215b32b 209 if(!empty($result['totalcount'])) {
cb6fec1f 210 $where .= " ORDER BY $order";
211 $result['logs'] = $DB->get_records_sql("$qry $where", $params, $limitfrom, $limitnum);
c215b32b 212 } else {
213 $result['logs'] = array();
214 }
215 return $result;
216}
217
92890025 218function build_logs_array($course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
219 $modname="", $modid=0, $modaction="", $groupid=0) {
d5abe1b5 220 global $DB, $SESSION, $USER;
e0161bff 221 // It is assumed that $date is the GMT time of midnight for that day,
222 // and so the next 86400 seconds worth of logs are printed.
f9903ed0 223
69c76405 224 /// Setup for group handling.
264867fd 225
69c76405 226 /// If the group mode is separate, and this user does not have editing privileges,
227 /// then only the user's group can be viewed.
3924b988 228 if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
928d4738 229 if (isset($SESSION->currentgroup[$course->id])) {
230 $groupid = $SESSION->currentgroup[$course->id];
231 } else {
232 $groupid = groups_get_all_groups($course->id, $USER->id);
233 if (is_array($groupid)) {
234 $groupid = array_shift(array_keys($groupid));
235 $SESSION->currentgroup[$course->id] = $groupid;
236 } else {
237 $groupid = 0;
238 }
239 }
69c76405 240 }
241 /// If this course doesn't have groups, no groupid can be specified.
242 else if (!$course->groupmode) {
243 $groupid = 0;
244 }
245
e0161bff 246 $joins = array();
18f8a34f 247 $params = array();
a2ab3b05 248
e15ef260 249 if ($course->id != SITEID || $modid != 0) {
c3df0901 250 $joins[] = "l.course = :courseid";
251 $params['courseid'] = $course->id;
e15ef260 252 }
f9903ed0 253
c469a7ef 254 if ($modname) {
c3df0901 255 $joins[] = "l.module = :modname";
238c0dd9 256 $params['modname'] = $modname;
f24cffb9 257 }
258
e21922f0 259 if ('site_errors' === $modid) {
bf35eb15 260 $joins[] = "( l.action='error' OR l.action='infected' )";
e21922f0 261 } else if ($modid) {
c3df0901 262 $joins[] = "l.cmid = :modid";
263 $params['modid'] = $modid;
69d79bc3 264 }
265
266 if ($modaction) {
ee35e0b8 267 $firstletter = substr($modaction, 0, 1);
8086b083 268 if ($firstletter == '-') {
47586394 269 $joins[] = $DB->sql_like('l.action', ':modaction', false, true, true);
c3df0901 270 $params['modaction'] = '%'.substr($modaction, 1).'%';
8086b083
PS
271 } else {
272 $joins[] = $DB->sql_like('l.action', ':modaction', false);
273 $params['modaction'] = '%'.$modaction.'%';
ee35e0b8 274 }
f24cffb9 275 }
276
238c0dd9 277
69c76405 278 /// Getting all members of a group.
279 if ($groupid and !$user) {
62d63838 280 if ($gusers = groups_get_members($groupid)) {
281 $gusers = array_keys($gusers);
1e95d7b7 282 $joins[] = 'l.userid IN (' . implode(',', $gusers) . ')';
283 } else {
05a33439 284 $joins[] = 'l.userid = 0'; // No users in groups, so we want something that will always be false.
69c76405 285 }
286 }
287 else if ($user) {
c3df0901 288 $joins[] = "l.userid = :userid";
289 $params['userid'] = $user;
f9903ed0 290 }
291
292 if ($date) {
293 $enddate = $date + 86400;
c3df0901 294 $joins[] = "l.time > :date AND l.time < :enddate";
295 $params['date'] = $date;
296 $params['enddate'] = $enddate;
f9903ed0 297 }
298
1e95d7b7 299 $selector = implode(' AND ', $joins);
e0161bff 300
d09f3c80 301 $totalcount = 0; // Initialise
92890025 302 $result = array();
c3df0901 303 $result['logs'] = get_logs($selector, $params, $order, $limitfrom, $limitnum, $totalcount);
92890025 304 $result['totalcount'] = $totalcount;
305 return $result;
306}
264867fd 307
308
92890025 309function print_log($course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
310 $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
264867fd 311
d60c1124 312 global $CFG, $DB, $OUTPUT;
264867fd 313
92890025 314 if (!$logs = build_logs_array($course, $user, $date, $order, $page*$perpage, $perpage,
315 $modname, $modid, $modaction, $groupid)) {
e6db3026 316 echo $OUTPUT->notification("No logs found!");
d60c1124 317 echo $OUTPUT->footer();
f9903ed0 318 exit;
319 }
264867fd 320
ea49a66c 321 $courses = array();
322
92890025 323 if ($course->id == SITEID) {
324 $courses[0] = '';
bf221aca 325 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
92890025 326 foreach ($ccc as $cc) {
bf221aca 327 $courses[$cc->id] = $cc->shortname;
92890025 328 }
329 }
ea49a66c 330 } else {
bf221aca 331 $courses[$course->id] = $course->shortname;
92890025 332 }
264867fd 333
92890025 334 $totalcount = $logs['totalcount'];
f9903ed0 335 $count=0;
2eb68e6f 336 $ldcache = array();
f9903ed0 337 $tt = getdate(time());
338 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
1c0200e0 339
dcde9f02 340 $strftimedatetime = get_string("strftimedatetime");
341
5577ceb3 342 echo "<div class=\"info\">\n";
519d369f 343 print_string("displayingrecords", "", $totalcount);
5577ceb3 344 echo "</div>\n";
1c0200e0 345
929d7a83 346 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
519d369f 347
337203a4 348 $table = new html_table();
90723839 349 $table->classes = array('logtable','generalbox');
337203a4
SH
350 $table->align = array('right', 'left', 'left');
351 $table->head = array(
352 get_string('time'),
353 get_string('ip_address'),
af27c69e 354 get_string('fullnameuser'),
337203a4
SH
355 get_string('action'),
356 get_string('info')
357 );
358 $table->data = array();
1b048629 359
1548978d 360 if ($course->id == SITEID) {
337203a4
SH
361 array_unshift($table->align, 'left');
362 array_unshift($table->head, get_string('course'));
1548978d 363 }
1548978d 364
1e95d7b7 365 // Make sure that the logs array is an array, even it is empty, to avoid warnings from the foreach.
2b2d182a 366 if (empty($logs['logs'])) {
1e95d7b7 367 $logs['logs'] = array();
2b2d182a 368 }
238c0dd9 369
92890025 370 foreach ($logs['logs'] as $log) {
600149be 371
2eb68e6f 372 if (isset($ldcache[$log->module][$log->action])) {
373 $ld = $ldcache[$log->module][$log->action];
374 } else {
cb6fec1f 375 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
2eb68e6f 376 $ldcache[$log->module][$log->action] = $ld;
377 }
edf3ef00 378 if ($ld && is_numeric($log->info)) {
181b888e 379 // ugly hack to make sure fullname is shown correctly
337203a4 380 if ($ld->mtable == 'user' && $ld->field == $DB->sql_concat('firstname', "' '" , 'lastname')) {
cb6fec1f 381 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
181b888e 382 } else {
cb6fec1f 383 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
181b888e 384 }
600149be 385 }
386
264867fd 387 //Filter log->info
c8b0a50b 388 $log->info = format_string($log->info);
389
6c5a2108 390 // If $log->url has been trimmed short by the db size restriction
391 // code in add_to_log, keep a note so we don't add a link to a broken url
f8311def 392 $brokenurl=(textlib::strlen($log->url)==100 && textlib::substr($log->url,97)=='...');
6c5a2108 393
337203a4 394 $row = array();
1548978d 395 if ($course->id == SITEID) {
bd5d0ce5 396 if (empty($log->course)) {
337203a4 397 $row[] = get_string('site');
bd5d0ce5 398 } else {
337203a4 399 $row[] = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">". format_string($courses[$log->course])."</a>";
bd5d0ce5 400 }
720a43ce 401 }
1b048629 402
337203a4 403 $row[] = userdate($log->time, '%a').' '.userdate($log->time, $strftimedatetime);
1b048629 404
75015e5f
PS
405 $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
406 $row[] = $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 440, 'width' => 700)));
337203a4 407
c63923bd 408 $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))));
337203a4 409
6c5a2108 410 $displayaction="$log->module $log->action";
337203a4
SH
411 if ($brokenurl) {
412 $row[] = $displayaction;
6c5a2108 413 } else {
75015e5f
PS
414 $link = make_log_url($log->module,$log->url);
415 $row[] = $OUTPUT->action_link($link, $displayaction, new popup_action('click', $link, 'fromloglive'), array('height' => 440, 'width' => 700));
6c5a2108 416 }
337203a4 417 $row[] = $log->info;
755ee6d8 418 $table->data[] = $row;
f9903ed0 419 }
519d369f 420
16be8974 421 echo html_writer::table($table);
929d7a83 422 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
f9903ed0 423}
424
425
c215b32b 426function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
427 $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
238c0dd9 428
d60c1124 429 global $CFG, $DB, $OUTPUT;
238c0dd9 430
c215b32b 431 if (!$logs = build_mnet_logs_array($hostid, $course, $user, $date, $order, $page*$perpage, $perpage,
432 $modname, $modid, $modaction, $groupid)) {
e6db3026 433 echo $OUTPUT->notification("No logs found!");
d60c1124 434 echo $OUTPUT->footer();
c215b32b 435 exit;
436 }
238c0dd9 437
c215b32b 438 if ($course->id == SITEID) {
439 $courses[0] = '';
440 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname,c.visible')) {
441 foreach ($ccc as $cc) {
442 $courses[$cc->id] = $cc->shortname;
443 }
444 }
445 }
238c0dd9 446
c215b32b 447 $totalcount = $logs['totalcount'];
448 $count=0;
449 $ldcache = array();
450 $tt = getdate(time());
451 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
452
453 $strftimedatetime = get_string("strftimedatetime");
454
5577ceb3 455 echo "<div class=\"info\">\n";
c215b32b 456 print_string("displayingrecords", "", $totalcount);
5577ceb3 457 echo "</div>\n";
c215b32b 458
929d7a83 459 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
c215b32b 460
5577ceb3 461 echo "<table class=\"logtable\" cellpadding=\"3\" cellspacing=\"0\">\n";
c215b32b 462 echo "<tr>";
463 if ($course->id == SITEID) {
464 echo "<th class=\"c0 header\">".get_string('course')."</th>\n";
465 }
466 echo "<th class=\"c1 header\">".get_string('time')."</th>\n";
467 echo "<th class=\"c2 header\">".get_string('ip_address')."</th>\n";
af27c69e 468 echo "<th class=\"c3 header\">".get_string('fullnameuser')."</th>\n";
c215b32b 469 echo "<th class=\"c4 header\">".get_string('action')."</th>\n";
470 echo "<th class=\"c5 header\">".get_string('info')."</th>\n";
471 echo "</tr>\n";
472
473 if (empty($logs['logs'])) {
474 echo "</table>\n";
475 return;
476 }
477
478 $row = 1;
479 foreach ($logs['logs'] as $log) {
238c0dd9 480
c215b32b 481 $log->info = $log->coursename;
482 $row = ($row + 1) % 2;
483
484 if (isset($ldcache[$log->module][$log->action])) {
485 $ld = $ldcache[$log->module][$log->action];
486 } else {
6bb08163 487 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
c215b32b 488 $ldcache[$log->module][$log->action] = $ld;
489 }
490 if (0 && $ld && !empty($log->info)) {
491 // ugly hack to make sure fullname is shown correctly
cb6fec1f 492 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
493 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
c215b32b 494 } else {
cb6fec1f 495 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
c215b32b 496 }
497 }
498
238c0dd9 499 //Filter log->info
c215b32b 500 $log->info = format_string($log->info);
501
c215b32b 502 echo '<tr class="r'.$row.'">';
503 if ($course->id == SITEID) {
8ebbb06a 504 $courseshortname = format_string($courses[$log->course], true, array('context' => get_context_instance(CONTEXT_COURSE, SITEID)));
5577ceb3 505 echo "<td class=\"r$row c0\" >\n";
8ebbb06a 506 echo " <a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">".$courseshortname."</a>\n";
c215b32b 507 echo "</td>\n";
508 }
5577ceb3 509 echo "<td class=\"r$row c1\" align=\"right\">".userdate($log->time, '%a').
c215b32b 510 ' '.userdate($log->time, $strftimedatetime)."</td>\n";
5577ceb3 511 echo "<td class=\"r$row c2\" >\n";
75015e5f
PS
512 $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
513 echo $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 400, 'width' => 700)));
c215b32b 514 echo "</td>\n";
515 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
5577ceb3 516 echo "<td class=\"r$row c3\" >\n";
c215b32b 517 echo " <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
518 echo "</td>\n";
5577ceb3 519 echo "<td class=\"r$row c4\">\n";
c215b32b 520 echo $log->action .': '.$log->module;
521 echo "</td>\n";;
5577ceb3 522 echo "<td class=\"r$row c5\">{$log->info}</td>\n";
c215b32b 523 echo "</tr>\n";
524 }
525 echo "</table>\n";
526
929d7a83 527 echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
c215b32b 528}
529
530
92890025 531function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
532 $modid, $modaction, $groupid) {
cb6fec1f 533 global $DB;
4068bedb 534
954fdb42 535 $text = get_string('course')."\t".get_string('time')."\t".get_string('ip_address')."\t".
af27c69e 536 get_string('fullnameuser')."\t".get_string('action')."\t".get_string('info');
264867fd 537
954fdb42 538 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
92890025 539 $modname, $modid, $modaction, $groupid)) {
540 return false;
541 }
264867fd 542
ea49a66c 543 $courses = array();
544
92890025 545 if ($course->id == SITEID) {
546 $courses[0] = '';
547 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
548 foreach ($ccc as $cc) {
549 $courses[$cc->id] = $cc->shortname;
550 }
551 }
ea49a66c 552 } else {
553 $courses[$course->id] = $course->shortname;
92890025 554 }
264867fd 555
92890025 556 $count=0;
557 $ldcache = array();
558 $tt = getdate(time());
559 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
560
561 $strftimedatetime = get_string("strftimedatetime");
92890025 562
4248b15c 563 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
954fdb42 564 $filename .= '.txt';
264867fd 565 header("Content-Type: application/download\n");
954fdb42 566 header("Content-Disposition: attachment; filename=$filename");
567 header("Expires: 0");
568 header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
569 header("Pragma: public");
570
571 echo get_string('savedat').userdate(time(), $strftimedatetime)."\n";
d0cdf8ec 572 echo $text."\n";
954fdb42 573
2b2d182a 574 if (empty($logs['logs'])) {
575 return true;
576 }
577
954fdb42 578 foreach ($logs['logs'] as $log) {
579 if (isset($ldcache[$log->module][$log->action])) {
580 $ld = $ldcache[$log->module][$log->action];
581 } else {
6bb08163 582 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
954fdb42 583 $ldcache[$log->module][$log->action] = $ld;
584 }
585 if ($ld && !empty($log->info)) {
586 // ugly hack to make sure fullname is shown correctly
cb6fec1f 587 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
588 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
954fdb42 589 } else {
cb6fec1f 590 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
954fdb42 591 }
592 }
593
264867fd 594 //Filter log->info
954fdb42 595 $log->info = format_string($log->info);
954fdb42 596 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
954fdb42 597
8ebbb06a
SH
598 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
599 $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
600 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
954fdb42 601 $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action, $log->info);
602 $text = implode("\t", $row);
603 echo $text." \n";
604 }
605 return true;
92890025 606}
607
608
609function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
610 $modid, $modaction, $groupid) {
264867fd 611
cb6fec1f 612 global $CFG, $DB;
92890025 613
954fdb42 614 require_once("$CFG->libdir/excellib.class.php");
264867fd 615
954fdb42 616 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
92890025 617 $modname, $modid, $modaction, $groupid)) {
618 return false;
619 }
264867fd 620
ea49a66c 621 $courses = array();
622
92890025 623 if ($course->id == SITEID) {
624 $courses[0] = '';
625 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
626 foreach ($ccc as $cc) {
627 $courses[$cc->id] = $cc->shortname;
628 }
629 }
ea49a66c 630 } else {
631 $courses[$course->id] = $course->shortname;
92890025 632 }
264867fd 633
92890025 634 $count=0;
635 $ldcache = array();
636 $tt = getdate(time());
637 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
638
639 $strftimedatetime = get_string("strftimedatetime");
92890025 640
954fdb42 641 $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
4248b15c 642 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
954fdb42 643 $filename .= '.xls';
264867fd 644
92890025 645 $workbook = new MoodleExcelWorkbook('-');
646 $workbook->send($filename);
264867fd 647
954fdb42 648 $worksheet = array();
649 $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
af27c69e 650 get_string('fullnameuser'), get_string('action'), get_string('info'));
264867fd 651
954fdb42 652 // Creating worksheets
653 for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
0a013367 654 $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
954fdb42 655 $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
656 $worksheet[$wsnumber]->set_column(1, 1, 30);
657 $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
658 userdate(time(), $strftimedatetime));
659 $col = 0;
660 foreach ($headers as $item) {
661 $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
662 $col++;
663 }
664 }
665
2b2d182a 666 if (empty($logs['logs'])) {
667 $workbook->close();
668 return true;
669 }
670
954fdb42 671 $formatDate =& $workbook->add_format();
672 $formatDate->set_num_format(get_string('log_excel_date_format'));
673
674 $row = FIRSTUSEDEXCELROW;
675 $wsnumber = 1;
676 $myxls =& $worksheet[$wsnumber];
677 foreach ($logs['logs'] as $log) {
678 if (isset($ldcache[$log->module][$log->action])) {
679 $ld = $ldcache[$log->module][$log->action];
680 } else {
cb6fec1f 681 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
954fdb42 682 $ldcache[$log->module][$log->action] = $ld;
683 }
684 if ($ld && !empty($log->info)) {
685 // ugly hack to make sure fullname is shown correctly
cb6fec1f 686 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
687 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
954fdb42 688 } else {
cb6fec1f 689 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
954fdb42 690 }
691 }
692
693 // Filter log->info
694 $log->info = format_string($log->info);
695 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
696
697 if ($nroPages>1) {
698 if ($row > EXCELROWS) {
699 $wsnumber++;
700 $myxls =& $worksheet[$wsnumber];
701 $row = FIRSTUSEDEXCELROW;
702 }
703 }
264867fd 704
8ebbb06a
SH
705 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
706
707 $myxls->write($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)), '');
5c34c4ba 708 $myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
954fdb42 709 $myxls->write($row, 2, $log->ip, '');
8ebbb06a 710 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
954fdb42 711 $myxls->write($row, 3, $fullname, '');
712 $myxls->write($row, 4, $log->module.' '.$log->action, '');
713 $myxls->write($row, 5, $log->info, '');
264867fd 714
954fdb42 715 $row++;
716 }
717
718 $workbook->close();
ea49a66c 719 return true;
720}
721
722function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
723 $modid, $modaction, $groupid) {
724
cb6fec1f 725 global $CFG, $DB;
ea49a66c 726
727 require_once("$CFG->libdir/odslib.class.php");
728
729 if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
730 $modname, $modid, $modaction, $groupid)) {
731 return false;
732 }
733
734 $courses = array();
735
736 if ($course->id == SITEID) {
737 $courses[0] = '';
738 if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
739 foreach ($ccc as $cc) {
740 $courses[$cc->id] = $cc->shortname;
741 }
742 }
743 } else {
744 $courses[$course->id] = $course->shortname;
745 }
746
747 $count=0;
748 $ldcache = array();
749 $tt = getdate(time());
750 $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
751
752 $strftimedatetime = get_string("strftimedatetime");
753
754 $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
4248b15c 755 $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
ea49a66c 756 $filename .= '.ods';
757
758 $workbook = new MoodleODSWorkbook('-');
759 $workbook->send($filename);
760
761 $worksheet = array();
762 $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
af27c69e 763 get_string('fullnameuser'), get_string('action'), get_string('info'));
ea49a66c 764
765 // Creating worksheets
766 for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
0a013367 767 $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
ea49a66c 768 $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
769 $worksheet[$wsnumber]->set_column(1, 1, 30);
770 $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
771 userdate(time(), $strftimedatetime));
772 $col = 0;
773 foreach ($headers as $item) {
774 $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
775 $col++;
776 }
777 }
778
779 if (empty($logs['logs'])) {
780 $workbook->close();
781 return true;
782 }
783
784 $formatDate =& $workbook->add_format();
785 $formatDate->set_num_format(get_string('log_excel_date_format'));
786
787 $row = FIRSTUSEDEXCELROW;
788 $wsnumber = 1;
789 $myxls =& $worksheet[$wsnumber];
790 foreach ($logs['logs'] as $log) {
791 if (isset($ldcache[$log->module][$log->action])) {
792 $ld = $ldcache[$log->module][$log->action];
793 } else {
cb6fec1f 794 $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
ea49a66c 795 $ldcache[$log->module][$log->action] = $ld;
796 }
797 if ($ld && !empty($log->info)) {
798 // ugly hack to make sure fullname is shown correctly
7e60297f 799 if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
cb6fec1f 800 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
ea49a66c 801 } else {
cb6fec1f 802 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
ea49a66c 803 }
804 }
805
806 // Filter log->info
807 $log->info = format_string($log->info);
808 $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
809
810 if ($nroPages>1) {
811 if ($row > EXCELROWS) {
812 $wsnumber++;
813 $myxls =& $worksheet[$wsnumber];
814 $row = FIRSTUSEDEXCELROW;
815 }
816 }
817
8ebbb06a
SH
818 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
819
dc767040 820 $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)));
d81b7ffb 821 $myxls->write_date($row, 1, $log->time);
822 $myxls->write_string($row, 2, $log->ip);
8ebbb06a 823 $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
d81b7ffb 824 $myxls->write_string($row, 3, $fullname);
825 $myxls->write_string($row, 4, $log->module.' '.$log->action);
826 $myxls->write_string($row, 5, $log->info);
ea49a66c 827
828 $row++;
829 }
830
831 $workbook->close();
954fdb42 832 return true;
92890025 833}
834
92890025 835
45e87759 836function print_overview($courses, array $remote_courses=array()) {
7c5286cd 837 global $CFG, $USER, $DB, $OUTPUT;
0d6b9d4f 838
185cfb09 839 $htmlarray = array();
6bb08163 840 if ($modules = $DB->get_records('modules')) {
f8716988 841 foreach ($modules as $mod) {
842 if (file_exists(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php')) {
f461e8ec 843 include_once(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php');
f8716988 844 $fname = $mod->name.'_print_overview';
0d6b9d4f 845 if (function_exists($fname)) {
185cfb09 846 $fname($courses,$htmlarray);
0d6b9d4f 847 }
848 }
849 }
850 }
185cfb09 851 foreach ($courses as $course) {
91d284c1 852 $fullname = format_string($course->fullname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
45e87759 853 echo $OUTPUT->box_start('coursebox');
91d284c1 854 $attributes = array('title' => s($fullname));
185cfb09 855 if (empty($course->visible)) {
45e87759 856 $attributes['class'] = 'dimmed';
185cfb09 857 }
45e87759 858 echo $OUTPUT->heading(html_writer::link(
91d284c1 859 new moodle_url('/course/view.php', array('id' => $course->id)), $fullname, $attributes), 3);
185cfb09 860 if (array_key_exists($course->id,$htmlarray)) {
861 foreach ($htmlarray[$course->id] as $modname => $html) {
862 echo $html;
863 }
864 }
e6db3026 865 echo $OUTPUT->box_end();
185cfb09 866 }
45e87759
DM
867
868 if (!empty($remote_courses)) {
869 echo $OUTPUT->heading(get_string('remotecourses', 'mnet'));
870 }
871 foreach ($remote_courses as $course) {
872 echo $OUTPUT->box_start('coursebox');
873 $attributes = array('title' => s($course->fullname));
874 echo $OUTPUT->heading(html_writer::link(
875 new moodle_url('/auth/mnet/jump.php', array('hostid' => $course->hostid, 'wantsurl' => '/course/view.php?id='.$course->remoteid)),
876 format_string($course->shortname),
877 $attributes) . ' (' . format_string($course->hostname) . ')', 3);
878 echo $OUTPUT->box_end();
879 }
0d6b9d4f 880}
881
882
cb6fec1f 883/**
884 * This function trawls through the logs looking for
885 * anything new since the user's last login
886 */
600149be 887function print_recent_activity($course) {
888 // $course is an object
008350b5 889 global $CFG, $USER, $SESSION, $DB, $OUTPUT;
600149be 890
e2a3a0e7 891 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2ac64806 892
dd97c328 893 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
894
895 $timestart = round(time() - COURSE_MAX_RECENT_PERIOD, -2); // better db caching for guests - 100 seconds
0f87cb1d 896
4f0c2d00 897 if (!isguestuser()) {
e2a3a0e7 898 if (!empty($USER->lastcourseaccess[$course->id])) {
899 if ($USER->lastcourseaccess[$course->id] > $timestart) {
900 $timestart = $USER->lastcourseaccess[$course->id];
901 }
9e51847a 902 }
3d891989 903 }
0f87cb1d 904
de785682 905 echo '<div class="activitydate">';
27bf9e20 906 echo get_string('activitysince', '', userdate($timestart));
de785682 907 echo '</div>';
908 echo '<div class="activityhead">';
0f87cb1d 909
de785682 910 echo '<a href="'.$CFG->wwwroot.'/course/recent.php?id='.$course->id.'">'.get_string('recentactivityreport').'</a>';
0f87cb1d 911
5fc835a5 912 echo "</div>\n";
0f87cb1d 913
600149be 914 $content = false;
1b5910c4 915
dd97c328 916/// Firstly, have there been any new enrolments?
917
6c38b7e0 918 $users = get_recent_enrolments($course->id, $timestart);
1b5910c4 919
5fc835a5 920 //Accessibility: new users now appear in an <OL> list.
6c38b7e0 921 if ($users) {
27bf9e20 922 echo '<div class="newusers">';
008350b5 923 echo $OUTPUT->heading(get_string("newusers").':', 3);
dd97c328 924 $content = true;
5fc835a5 925 echo "<ol class=\"list\">\n";
6c38b7e0 926 foreach ($users as $user) {
dd97c328 927 $fullname = fullname($user, $viewfullnames);
928 echo '<li class="name"><a href="'."$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a></li>\n";
600149be 929 }
5fc835a5 930 echo "</ol>\n</div>\n";
600149be 931 }
932
dd97c328 933/// Next, have there been any modifications to the course structure?
934
f20edd52 935 $modinfo = get_fast_modinfo($course);
dd97c328 936
937 $changelist = array();
1b5910c4 938
cb6fec1f 939 $logs = $DB->get_records_select('log', "time > ? AND course = ? AND
940 module = 'course' AND
941 (action = 'add mod' OR action = 'update mod' OR action = 'delete mod')",
942 array($timestart, $course->id), "id ASC");
1b5910c4 943
944 if ($logs) {
dd97c328 945 $actions = array('add mod', 'update mod', 'delete mod');
946 $newgones = array(); // added and later deleted items
1b5910c4 947 foreach ($logs as $key => $log) {
dd97c328 948 if (!in_array($log->action, $actions)) {
949 continue;
950 }
c71f3265 951 $info = explode(' ', $log->info);
c9f6251e 952
0d8b6a69 953 // note: in most cases I replaced hardcoding of label with use of
954 // $cm->has_view() but it was not possible to do this here because
955 // we don't necessarily have the $cm for it
dd97c328 956 if ($info[0] == 'label') { // Labels are ignored in recent activity
c9f6251e 957 continue;
958 }
959
dd97c328 960 if (count($info) != 2) {
961 debugging("Incorrect log entry info: id = ".$log->id, DEBUG_DEVELOPER);
962 continue;
963 }
964
965 $modname = $info[0];
966 $instanceid = $info[1];
967
968 if ($log->action == 'delete mod') {
969 // unfortunately we do not know if the mod was visible
970 if (!array_key_exists($log->info, $newgones)) {
971 $strdeleted = get_string('deletedactivity', 'moodle', get_string('modulename', $modname));
972 $changelist[$log->info] = array ('operation' => 'delete', 'text' => $strdeleted);
973 }
974 } else {
975 if (!isset($modinfo->instances[$modname][$instanceid])) {
976 if ($log->action == 'add mod') {
977 // do not display added and later deleted activities
978 $newgones[$log->info] = true;
979 }
980 continue;
981 }
982 $cm = $modinfo->instances[$modname][$instanceid];
983 if (!$cm->uservisible) {
ff96219d 984 continue;
dd97c328 985 }
986
987 if ($log->action == 'add mod') {
988 $stradded = get_string('added', 'moodle', get_string('modulename', $modname));
989 $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>");
990
991 } else if ($log->action == 'update mod' and empty($changelist[$log->info])) {
992 $strupdated = get_string('updated', 'moodle', get_string('modulename', $modname));
993 $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>");
600149be 994 }
ef25340c 995 }
996 }
997 }
998
9c9f7d77 999 if (!empty($changelist)) {
008350b5 1000 echo $OUTPUT->heading(get_string("courseupdates").':', 3);
dd97c328 1001 $content = true;
ef25340c 1002 foreach ($changelist as $changeinfo => $change) {
dd97c328 1003 echo '<p class="activity">'.$change['text'].'</p>';
600149be 1004 }
89adb174 1005 }
bf40f9c1 1006
dd97c328 1007/// Now display new things from each module
0fd7da81 1008
dd97c328 1009 $usedmodules = array();
1010 foreach($modinfo->cms as $cm) {
1011 if (isset($usedmodules[$cm->modname])) {
1012 continue;
1013 }
1014 if (!$cm->uservisible) {
1015 continue;
1016 }
1017 $usedmodules[$cm->modname] = $cm->modname;
1018 }
e2a3a0e7 1019
dd97c328 1020 foreach ($usedmodules as $modname) { // Each module gets it's own logs and prints them
1021 if (file_exists($CFG->dirroot.'/mod/'.$modname.'/lib.php')) {
1022 include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php');
1023 $print_recent_activity = $modname.'_print_recent_activity';
296c6ac2 1024 if (function_exists($print_recent_activity)) {
dd97c328 1025 // NOTE: original $isteacher (second parameter below) was replaced with $viewfullnames!
1026 $content = $print_recent_activity($course, $viewfullnames, $timestart) || $content;
600149be 1027 }
296c6ac2 1028 } else {
238c0dd9 1029 debugging("Missing lib.php in lib/{$modname} - please reinstall files or uninstall the module");
600149be 1030 }
1031 }
1032
1033 if (! $content) {
27bf9e20 1034 echo '<p class="message">'.get_string('nothingnew').'</p>';
600149be 1035 }
600149be 1036}
1037
cb6fec1f 1038/**
1039 * For a given course, returns an array of course activity objects
1040 * Each item in the array contains he following properties:
1041 */
d897cae4 1042function get_array_of_activities($courseid) {
d897cae4 1043// cm - course module id
1044// mod - name of the module (eg forum)
1045// section - the number of the section (eg week or topic)
1046// name - the name of the instance
5867bfb5 1047// visible - is the instance visible or not
13534ef7
ML
1048// groupingid - grouping id
1049// groupmembersonly - is this instance visible to group members only
86aa7ccf 1050// extra - contains extra string to include in any link
cb6fec1f 1051 global $CFG, $DB;
82bd6a5e 1052 if(!empty($CFG->enableavailability)) {
1053 require_once($CFG->libdir.'/conditionlib.php');
1054 }
8dddba42 1055
a0c30e1b 1056 $course = $DB->get_record('course', array('id'=>$courseid));
1057
1058 if (empty($course)) {
1059 throw new moodle_exception('courseidnotfound');
1060 }
1061
d897cae4 1062 $mod = array();
1063
a0c30e1b 1064 $rawmods = get_course_mods($courseid);
1065 if (empty($rawmods)) {
dd97c328 1066 return $mod; // always return array
d897cae4 1067 }
1068
66b49477 1069 if ($sections = get_all_sections($courseid)) {
d897cae4 1070 foreach ($sections as $section) {
74666583 1071 if (!empty($section->sequence)) {
d897cae4 1072 $sequence = explode(",", $section->sequence);
1073 foreach ($sequence as $seq) {
7af6281f 1074 if (empty($rawmods[$seq])) {
1075 continue;
1076 }
b85b25eb 1077 $mod[$seq] = new stdClass();
dd97c328 1078 $mod[$seq]->id = $rawmods[$seq]->instance;
1079 $mod[$seq]->cm = $rawmods[$seq]->id;
1080 $mod[$seq]->mod = $rawmods[$seq]->modname;
adaeccb6 1081
1082 // Oh dear. Inconsistent names left here for backward compatibility.
dd97c328 1083 $mod[$seq]->section = $section->section;
adaeccb6 1084 $mod[$seq]->sectionid = $rawmods[$seq]->section;
1085
1086 $mod[$seq]->module = $rawmods[$seq]->module;
1087 $mod[$seq]->added = $rawmods[$seq]->added;
1088 $mod[$seq]->score = $rawmods[$seq]->score;
66b250fd 1089 $mod[$seq]->idnumber = $rawmods[$seq]->idnumber;
dd97c328 1090 $mod[$seq]->visible = $rawmods[$seq]->visible;
adaeccb6 1091 $mod[$seq]->visibleold = $rawmods[$seq]->visibleold;
dd97c328 1092 $mod[$seq]->groupmode = $rawmods[$seq]->groupmode;
1093 $mod[$seq]->groupingid = $rawmods[$seq]->groupingid;
13534ef7 1094 $mod[$seq]->groupmembersonly = $rawmods[$seq]->groupmembersonly;
82bd6a5e 1095 $mod[$seq]->indent = $rawmods[$seq]->indent;
1096 $mod[$seq]->completion = $rawmods[$seq]->completion;
dd97c328 1097 $mod[$seq]->extra = "";
adaeccb6 1098 $mod[$seq]->completiongradeitemnumber =
1099 $rawmods[$seq]->completiongradeitemnumber;
1100 $mod[$seq]->completionview = $rawmods[$seq]->completionview;
1101 $mod[$seq]->completionexpected = $rawmods[$seq]->completionexpected;
1102 $mod[$seq]->availablefrom = $rawmods[$seq]->availablefrom;
1103 $mod[$seq]->availableuntil = $rawmods[$seq]->availableuntil;
1104 $mod[$seq]->showavailability = $rawmods[$seq]->showavailability;
8c40662e 1105 $mod[$seq]->showdescription = $rawmods[$seq]->showdescription;
adaeccb6 1106 if (!empty($CFG->enableavailability)) {
82bd6a5e 1107 condition_info::fill_availability_conditions($rawmods[$seq]);
82bd6a5e 1108 $mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
1109 $mod[$seq]->conditionsgrade = $rawmods[$seq]->conditionsgrade;
1110 }
8dddba42 1111
1112 $modname = $mod[$seq]->mod;
1113 $functionname = $modname."_get_coursemodule_info";
1114
3a37b3f8 1115 if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
1116 continue;
1117 }
1118
8dddba42 1119 include_once("$CFG->dirroot/mod/$modname/lib.php");
1120
b3a89232 1121 if ($hasfunction = function_exists($functionname)) {
9d361034 1122 if ($info = $functionname($rawmods[$seq])) {
9d361034 1123 if (!empty($info->icon)) {
1124 $mod[$seq]->icon = $info->icon;
1125 }
9a9012dc
PS
1126 if (!empty($info->iconcomponent)) {
1127 $mod[$seq]->iconcomponent = $info->iconcomponent;
1128 }
1ea543df 1129 if (!empty($info->name)) {
9a9012dc 1130 $mod[$seq]->name = $info->name;
1ea543df 1131 }
0d8b6a69 1132 if ($info instanceof cached_cm_info) {
1133 // When using cached_cm_info you can include three new fields
1134 // that aren't available for legacy code
1135 if (!empty($info->content)) {
1136 $mod[$seq]->content = $info->content;
1137 }
1138 if (!empty($info->extraclasses)) {
1139 $mod[$seq]->extraclasses = $info->extraclasses;
1140 }
c443a1cd
EL
1141 if (!empty($info->iconurl)) {
1142 $mod[$seq]->iconurl = $info->iconurl;
1143 }
0d8b6a69 1144 if (!empty($info->onclick)) {
1145 $mod[$seq]->onclick = $info->onclick;
1146 }
1147 if (!empty($info->customdata)) {
1148 $mod[$seq]->customdata = $info->customdata;
1149 }
1150 } else {
1151 // When using a stdclass, the (horrible) deprecated ->extra field
1152 // is available for BC
1153 if (!empty($info->extra)) {
1154 $mod[$seq]->extra = $info->extra;
1155 }
1156 }
c9f6251e 1157 }
1158 }
b3a89232 1159 // When there is no modname_get_coursemodule_info function,
1160 // but showdescriptions is enabled, then we use the 'intro'
1161 // and 'introformat' fields in the module table
1162 if (!$hasfunction && $rawmods[$seq]->showdescription) {
1163 if ($modvalues = $DB->get_record($rawmods[$seq]->modname,
1164 array('id' => $rawmods[$seq]->instance), 'name, intro, introformat')) {
1165 // Set content from intro and introformat. Filters are disabled
1166 // because we filter it with format_text at display time
1167 $mod[$seq]->content = format_module_intro($rawmods[$seq]->modname,
1168 $modvalues, $rawmods[$seq]->id, false);
1169
1170 // To save making another query just below, put name in here
1171 $mod[$seq]->name = $modvalues->name;
1172 }
1173 }
1ea543df 1174 if (!isset($mod[$seq]->name)) {
9a9012dc 1175 $mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
1ea543df 1176 }
0d8b6a69 1177
1178 // Minimise the database size by unsetting default options when they are
1179 // 'empty'. This list corresponds to code in the cm_info constructor.
8c40662e 1180 foreach (array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
c443a1cd 1181 'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content',
adaeccb6 1182 'icon', 'iconcomponent', 'customdata', 'showavailability', 'availablefrom',
1183 'availableuntil', 'conditionscompletion', 'conditionsgrade',
8c40662e 1184 'completionview', 'completionexpected', 'score', 'showdescription')
1185 as $property) {
0d8b6a69 1186 if (property_exists($mod[$seq], $property) &&
1187 empty($mod[$seq]->{$property})) {
1188 unset($mod[$seq]->{$property});
1189 }
1190 }
adaeccb6 1191 // Special case: this value is usually set to null, but may be 0
1192 if (property_exists($mod[$seq], 'completiongradeitemnumber') &&
1193 is_null($mod[$seq]->completiongradeitemnumber)) {
1194 unset($mod[$seq]->completiongradeitemnumber);
1195 }
d897cae4 1196 }
1197 }
1198 }
1199 }
1200 return $mod;
1201}
1202
1203
cb6fec1f 1204/**
1205 * Returns a number of useful structures for course displays
1206 */
90845098 1207function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
0edb4aad 1208 global $CFG, $DB, $COURSE;
7468bf01 1209
dd97c328 1210 $mods = array(); // course modules indexed by id
1211 $modnames = array(); // all course module names (except resource!)
1212 $modnamesplural= array(); // all course module names (plural form)
1213 $modnamesused = array(); // course module names used
7468bf01 1214
cb6fec1f 1215 if ($allmods = $DB->get_records("modules")) {
90845098 1216 foreach ($allmods as $mod) {
0edb4aad
PS
1217 if (!file_exists("$CFG->dirroot/mod/$mod->name/lib.php")) {
1218 continue;
1219 }
5867bfb5 1220 if ($mod->visible) {
1221 $modnames[$mod->name] = get_string("modulename", "$mod->name");
1222 $modnamesplural[$mod->name] = get_string("modulenameplural", "$mod->name");
1223 }
90845098 1224 }
d609d962 1225 collatorlib::asort($modnames);
90845098 1226 } else {
ba6018a9 1227 print_error("nomodules", 'debug');
90845098 1228 }
1229
82bd6a5e 1230 $course = ($courseid==$COURSE->id) ? $COURSE : $DB->get_record('course',array('id'=>$courseid));
1231 $modinfo = get_fast_modinfo($course);
1232
1233 if ($rawmods=$modinfo->cms) {
7468bf01 1234 foreach($rawmods as $mod) { // Index the mods
959ae824 1235 if (empty($modnames[$mod->modname])) {
1236 continue;
1237 }
dd97c328 1238 $mods[$mod->id] = $mod;
1239 $mods[$mod->id]->modfullname = $modnames[$mod->modname];
1240 if (!$mod->visible and !has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $courseid))) {
1241 continue;
1242 }
13534ef7
ML
1243 // Check groupings
1244 if (!groups_course_module_visible($mod)) {
1245 continue;
1246 }
dd97c328 1247 $modnamesused[$mod->modname] = $modnames[$mod->modname];
7468bf01 1248 }
c7da6f7a 1249 if ($modnamesused) {
d609d962 1250 collatorlib::asort($modnamesused);
c7da6f7a 1251 }
7468bf01 1252 }
7468bf01 1253}
1254
7487c856
SH
1255/**
1256 * Returns an array of sections for the requested course id
1257 *
1258 * This function stores the sections against the course id within a staticvar encase
1259 * of subsequent requests. This is used all over + in some standard libs and course
1260 * format callbacks so subsequent requests are a reality.
1261 *
1262 * @staticvar array $coursesections
1263 * @param int $courseid
1264 * @return array Array of sections
1265 */
7468bf01 1266function get_all_sections($courseid) {
cb6fec1f 1267 global $DB;
7487c856
SH
1268 static $coursesections = array();
1269 if (!array_key_exists($courseid, $coursesections)) {
1270 $coursesections[$courseid] = $DB->get_records("course_sections", array("course"=>"$courseid"), "section",
1271 "section, id, course, name, summary, summaryformat, sequence, visible");
1272 }
1273 return $coursesections[$courseid];
7468bf01 1274}
1275
13801a49 1276/**
cc10c0b9
PS
1277 * Returns the course section to display or 0 meaning show all sections. Returns 0 for guests.
1278 * It also sets the $USER->display cache to array($courseid=>return value)
1279 *
13801a49 1280 * @param int $courseid The course id
cc10c0b9 1281 * @return int Course section to display, 0 means all
13801a49
AB
1282 */
1283function course_get_display($courseid) {
1284 global $USER, $DB;
1285
1286 if (!isloggedin() or isguestuser()) {
1287 //do not get settings in db for guests
1288 return 0; //return the implicit setting
1289 }
1290
1291 if (!isset($USER->display[$courseid])) {
cc10c0b9
PS
1292 if (!$display = $DB->get_field('course_display', 'display', array('userid' => $USER->id, 'course'=>$courseid))) {
1293 $display = 0; // all sections option is not stored in DB, this makes the table much smaller
13801a49 1294 }
cc10c0b9
PS
1295 //use display cache for one course only - we need to keep session small
1296 $USER->display = array($courseid => $display);
13801a49 1297 }
cc10c0b9 1298
13801a49
AB
1299 return $USER->display[$courseid];
1300}
1301
cc10c0b9
PS
1302/**
1303 * Show one section only or all sections.
1304 *
1305 * @param int $courseid The course id
1306 * @param mixed $display show only this section, 0 or 'all' means show all sections
1307 * @return int Course section to display, 0 means all
1308 */
1309function course_set_display($courseid, $display) {
cb6fec1f 1310 global $USER, $DB;
b86fc0e2 1311
cc10c0b9 1312 if ($display === 'all' or empty($display)) {
b86fc0e2 1313 $display = 0;
1314 }
1315
4f0c2d00 1316 if (!isloggedin() or isguestuser()) {
7b678e0a 1317 //do not store settings in db for guests
cc10c0b9
PS
1318 return 0;
1319 }
1320
1321 if ($display == 0) {
1322 //show all, do not store anything in database
1323 $DB->delete_records('course_display', array('userid' => $USER->id, 'course' => $courseid));
1324
b86fc0e2 1325 } else {
cc10c0b9
PS
1326 if ($DB->record_exists('course_display', array('userid' => $USER->id, 'course' => $courseid))) {
1327 $DB->set_field('course_display', 'display', $display, array('userid' => $USER->id, 'course' => $courseid));
13801a49 1328 } else {
cc10c0b9
PS
1329 $record = new stdClass();
1330 $record->userid = $USER->id;
1331 $record->course = $courseid;
1332 $record->display = $display;
1333 $DB->insert_record('course_display', $record);
13801a49 1334 }
b86fc0e2 1335 }
1336
cc10c0b9
PS
1337 //use display cache for one course only - we need to keep session small
1338 $USER->display = array($courseid => $display);
1339
1340 return $display;
b86fc0e2 1341}
1342
93d46f48
RK
1343/**
1344 * Set highlighted section. Only one section can be highlighted at the time.
1345 *
1346 * @param int $courseid course id
1347 * @param int $marker highlight section with this number, 0 means remove higlightin
1348 * @return void
1349 */
1350function course_set_marker($courseid, $marker) {
1351 global $DB;
1352 $DB->set_field("course", "marker", $marker, array('id' => $courseid));
1353}
1354
cb6fec1f 1355/**
7e85563d 1356 * For a given course section, marks it visible or hidden,
cb6fec1f 1357 * and does the same for every activity in that section
ebaa29d1
ARN
1358 *
1359 * @param int $courseid course id
1360 * @param int $sectionnumber The section number to adjust
1361 * @param int $visibility The new visibility
1362 * @return array A list of resources which were hidden in the section
cb6fec1f 1363 */
7d99d695 1364function set_section_visible($courseid, $sectionnumber, $visibility) {
cb6fec1f 1365 global $DB;
7d99d695 1366
ebaa29d1 1367 $resourcestotoggle = array();
cb6fec1f 1368 if ($section = $DB->get_record("course_sections", array("course"=>$courseid, "section"=>$sectionnumber))) {
1369 $DB->set_field("course_sections", "visible", "$visibility", array("id"=>$section->id));
7d99d695 1370 if (!empty($section->sequence)) {
1371 $modules = explode(",", $section->sequence);
1372 foreach ($modules as $moduleid) {
02f66c42 1373 set_coursemodule_visible($moduleid, $visibility, true);
7d99d695 1374 }
1375 }
5867bfb5 1376 rebuild_course_cache($courseid);
ebaa29d1
ARN
1377
1378 // Determine which modules are visible for AJAX update
1379 if (!empty($modules)) {
1380 list($insql, $params) = $DB->get_in_or_equal($modules);
1381 $select = 'id ' . $insql . ' AND visible = ?';
1382 array_push($params, $visibility);
1383 if (!$visibility) {
1384 $select .= ' AND visibleold = 1';
1385 }
1386 $resourcestotoggle = $DB->get_fieldset_select('course_modules', 'id', $select, $params);
1387 }
7d99d695 1388 }
ebaa29d1 1389 return $resourcestotoggle;
7d99d695 1390}
ba2e5d73 1391
0d8b6a69 1392/**
1393 * Obtains shared data that is used in print_section when displaying a
1394 * course-module entry.
1395 *
1396 * Calls format_text or format_string as appropriate, and obtains the correct icon.
1397 *
1398 * This data is also used in other areas of the code.
1399 * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
1400 * @param object $course Moodle course object
1401 * @return array An array with the following values in this order:
1402 * $content (optional extra content for after link),
1403 * $instancename (text of link)
1404 */
1405function get_print_section_cm_text(cm_info $cm, $course) {
1406 global $OUTPUT;
1407
0d8b6a69 1408 // Get content from modinfo if specified. Content displays either
1409 // in addition to the standard link (below), or replaces it if
1410 // the link is turned off by setting ->url to null.
1411 if (($content = $cm->get_content()) !== '') {
371fbe1c 1412 // Improve filter performance by preloading filter setttings for all
1413 // activities on the course (this does nothing if called multiple
1414 // times)
1415 filter_preload_activities($cm->get_modinfo());
1416
1417 // Get module context
1418 $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
0d8b6a69 1419 $labelformatoptions = new stdClass();
1420 $labelformatoptions->noclean = true;
1421 $labelformatoptions->overflowdiv = true;
371fbe1c 1422 $labelformatoptions->context = $modulecontext;
0d8b6a69 1423 $content = format_text($content, FORMAT_HTML, $labelformatoptions);
1424 } else {
1425 $content = '';
1426 }
1427
371fbe1c 1428 // Get course context
1429 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
0d8b6a69 1430 $stringoptions = new stdClass;
1431 $stringoptions->context = $coursecontext;
1432 $instancename = format_string($cm->name, true, $stringoptions);
1433 return array($content, $instancename);
1434}
1435
cb6fec1f 1436/**
1437 * Prints a section full of activity modules
1438 */
4e781c7b 1439function print_section($course, $section, $mods, $modnamesused, $absolute=false, $width="100%", $hidecompletion=false) {
e63f88c9 1440 global $CFG, $USER, $DB, $PAGE, $OUTPUT;
7977cffd 1441
dd97c328 1442 static $initialised;
1443
3d575e6f 1444 static $groupbuttons;
32d03b7b 1445 static $groupbuttonslink;
52dcc2f9 1446 static $isediting;
7977cffd 1447 static $ismoving;
1448 static $strmovehere;
1449 static $strmovefull;
54669989 1450 static $strunreadpostsone;
dd97c328 1451 static $groupings;
0d8b6a69 1452 static $modulenames;
110a32e2 1453
dd97c328 1454 if (!isset($initialised)) {
9fd9c29b 1455 $groupbuttons = ($course->groupmode or (!$course->groupmodeforce));
32d03b7b 1456 $groupbuttonslink = (!$course->groupmodeforce);
830dd6e9 1457 $isediting = $PAGE->user_is_editing();
dd97c328 1458 $ismoving = $isediting && ismoving($course->id);
3d575e6f 1459 if ($ismoving) {
dd97c328 1460 $strmovehere = get_string("movehere");
1461 $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
3d575e6f 1462 }
0d8b6a69 1463 $modulenames = array();
dd97c328 1464 $initialised = true;
7977cffd 1465 }
dd97c328 1466
dd97c328 1467 $modinfo = get_fast_modinfo($course);
984baa77 1468 $completioninfo = new completion_info($course);
94361e02 1469
7e85563d 1470 //Accessibility: replace table with list <ul>, but don't output empty list.
74666583 1471 if (!empty($section->sequence)) {
94361e02 1472
f2d660dc 1473 // Fix bug #5027, don't want style=\"width:$width\".
6285f8a8 1474 echo "<ul class=\"section img-text\">\n";
94361e02 1475 $sectionmods = explode(",", $section->sequence);
1476
1477 foreach ($sectionmods as $modnumber) {
9ae687af 1478 if (empty($mods[$modnumber])) {
1479 continue;
1480 }
dd97c328 1481
0d8b6a69 1482 /**
1483 * @var cm_info
1484 */
94361e02 1485 $mod = $mods[$modnumber];
c9f6251e 1486
dd97c328 1487 if ($ismoving and $mod->id == $USER->activitycopy) {
1488 // do not display moving mod
1489 continue;
1490 }
c9f6251e 1491
dd97c328 1492 if (isset($modinfo->cms[$modnumber])) {
85f55ba2 1493 // We can continue (because it will not be displayed at all)
1494 // if:
1495 // 1) The activity is not visible to users
1496 // and
1497 // 2a) The 'showavailability' option is not set (if that is set,
2f48819b 1498 // we need to display the activity so we can show
85f55ba2 1499 // availability info)
1500 // or
2f48819b 1501 // 2b) The 'availableinfo' is empty, i.e. the activity was
1502 // hidden in a way that leaves no info, such as using the
85f55ba2 1503 // eye icon.
82bd6a5e 1504 if (!$modinfo->cms[$modnumber]->uservisible &&
85f55ba2 1505 (empty($modinfo->cms[$modnumber]->showavailability) ||
1506 empty($modinfo->cms[$modnumber]->availableinfo))) {
dd97c328 1507 // visibility shortcut
1508 continue;
86aa7ccf 1509 }
dd97c328 1510 } else {
0f56c9da 1511 if (!file_exists("$CFG->dirroot/mod/$mod->modname/lib.php")) {
1512 // module not installed
1513 continue;
1514 }
82bd6a5e 1515 if (!coursemodule_visible_for_user($mod) &&
1516 empty($mod->showavailability)) {
dd97c328 1517 // full visibility check
1518 continue;
9d361034 1519 }
dd97c328 1520 }
1521
0d8b6a69 1522 if (!isset($modulenames[$mod->modname])) {
1523 $modulenames[$mod->modname] = get_string('modulename', $mod->modname);
1524 }
1525 $modulename = $modulenames[$mod->modname];
1526
2f48819b 1527 // In some cases the activity is visible to user, but it is
e8e50986 1528 // dimmed. This is done if viewhiddenactivities is true and if:
1529 // 1. the activity is not visible, or
1530 // 2. the activity has dates set which do not include current, or
1531 // 3. the activity has any other conditions set (regardless of whether
1532 // current user meets them)
1533 $canviewhidden = has_capability(
1534 'moodle/course:viewhiddenactivities',
1535 get_context_instance(CONTEXT_MODULE, $mod->id));
1536 $accessiblebutdim = false;
1537 if ($canviewhidden) {
1538 $accessiblebutdim = !$mod->visible;
1539 if (!empty($CFG->enableavailability)) {
1540 $accessiblebutdim = $accessiblebutdim ||
1541 $mod->availablefrom > time() ||
1542 ($mod->availableuntil && $mod->availableuntil < time()) ||
2f48819b 1543 count($mod->conditionsgrade) > 0 ||
e8e50986 1544 count($mod->conditionscompletion) > 0;
1545 }
1546 }
1547
060cd0c8
SH
1548 $liclasses = array();
1549 $liclasses[] = 'activity';
1550 $liclasses[] = $mod->modname;
1551 $liclasses[] = 'modtype_'.$mod->modname;
0d8b6a69 1552 $extraclasses = $mod->get_extra_classes();
1553 if ($extraclasses) {
1554 $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
1555 }
060cd0c8 1556 echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
dd97c328 1557 if ($ismoving) {
1558 echo '<a title="'.$strmovefull.'"'.
d4a1fcaf 1559 ' href="'.$CFG->wwwroot.'/course/mod.php?moveto='.$mod->id.'&amp;sesskey='.sesskey().'">'.
b5d0cafc 1560 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
dd97c328 1561 ' alt="'.$strmovehere.'" /></a><br />
1562 ';
1563 }
9d361034 1564
060cd0c8
SH
1565 $classes = array('mod-indent');
1566 if (!empty($mod->indent)) {
1567 $classes[] = 'mod-indent-'.$mod->indent;
1568 if ($mod->indent > 15) {
1569 $classes[] = 'mod-indent-huge';
1570 }
dd97c328 1571 }
060cd0c8 1572 echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
dd97c328 1573
0d8b6a69 1574 // Get data about this course-module
1575 list($content, $instancename) =
1576 get_print_section_cm_text($modinfo->cms[$modnumber], $course);
1577
1578 //Accessibility: for files get description via icon, this is very ugly hack!
1579 $altname = '';
1580 $altname = $mod->modfullname;
1581 if (!empty($customicon)) {
1582 $archetype = plugin_supports('mod', $mod->modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
1583 if ($archetype == MOD_ARCHETYPE_RESOURCE) {
1584 $mimetype = mimeinfo_from_icon('type', $customicon);
1585 $altname = get_mimetype_description($mimetype);
1586 }
1587 }
1588 // Avoid unnecessary duplication: if e.g. a forum name already
1589 // includes the word forum (or Forum, etc) then it is unhelpful
1590 // to include that in the accessible description that is added.
f8311def
PS
1591 if (false !== strpos(textlib::strtolower($instancename),
1592 textlib::strtolower($altname))) {
0d8b6a69 1593 $altname = '';
1594 }
1595 // File type after name, for alphabetic lists (screen reader).
1596 if ($altname) {
1597 $altname = get_accesshide(' '.$altname);
554606c7 1598 }
dd97c328 1599
0d8b6a69 1600 // We may be displaying this just in order to show information
1601 // about visibility, without the actual link
1602 $contentpart = '';
1603 if ($mod->uservisible) {
1604 // Nope - in this case the link is fully working for user
1605 $linkclasses = '';
1606 $textclasses = '';
1607 if ($accessiblebutdim) {
1608 $linkclasses .= ' dimmed';
1609 $textclasses .= ' dimmed_text';
1610 $accesstext = '<span class="accesshide">'.
1611 get_string('hiddenfromstudents').': </span>';
722b4ce1 1612 } else {
0d8b6a69 1613 $accesstext = '';
dd97c328 1614 }
0d8b6a69 1615 if ($linkclasses) {
1616 $linkcss = 'class="' . trim($linkclasses) . '" ';
1617 } else {
1618 $linkcss = '';
1619 }
1620 if ($textclasses) {
1621 $textcss = 'class="' . trim($textclasses) . '" ';
1622 } else {
1623 $textcss = '';
c4d989a1 1624 }
aac94fd0 1625
0d8b6a69 1626 // Get on-click attribute value if specified
1627 $onclick = $mod->get_on_click();
1628 if ($onclick) {
1629 $onclick = ' onclick="' . $onclick . '"';
1630 }
c9f6251e 1631
0d8b6a69 1632 if ($url = $mod->get_url()) {
1633 // Display link itself
1634 echo '<a ' . $linkcss . $mod->extra . $onclick .
1635 ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
1636 '" class="activityicon" alt="' .
1637 $modulename . '" /> ' .
1638 $accesstext . '<span class="instancename">' .
1639 $instancename . $altname . '</span></a>';
1640
1641 // If specified, display extra content after link
1642 if ($content) {
ce2a7a91 1643 $contentpart = '<div class="' . trim('contentafterlink' . $textclasses) .
1644 '">' . $content . '</div>';
c93dae38 1645 }
dd97c328 1646 } else {
0d8b6a69 1647 // No link, so display only content
1648 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1649 $accesstext . $content . '</div>';
dd97c328 1650 }
e0b033d5 1651
0d8b6a69 1652 if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
1653 if (!isset($groupings)) {
1654 $groupings = groups_get_all_groupings($course->id);
6285f8a8 1655 }
0d8b6a69 1656 echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
dd97c328 1657 }
0d8b6a69 1658 } else {
1659 $textclasses = $extraclasses;
1660 $textclasses .= ' dimmed_text';
1661 if ($textclasses) {
1662 $textcss = 'class="' . trim($textclasses) . '" ';
1663 } else {
1664 $textcss = '';
dd97c328 1665 }
0d8b6a69 1666 $accesstext = '<span class="accesshide">' .
1667 get_string('notavailableyet', 'condition') .
1668 ': </span>';
6285f8a8 1669
0d8b6a69 1670 if ($url = $mod->get_url()) {
82bd6a5e 1671 // Display greyed-out text of link
0d8b6a69 1672 echo '<div ' . $textcss . $mod->extra .
1673 ' >' . '<img src="' . $mod->get_icon_url() .
1674 '" class="activityicon" alt="' .
1675 $modulename .
1676 '" /> <span>'. $instancename . $altname .
1677 '</span></div>';
1678
1679 // Do not display content after link when it is greyed out like this.
1680 } else {
1681 // No link, so display only content (also greyed)
1682 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1683 $accesstext . $content . '</div>';
f37da850 1684 }
dd97c328 1685 }
f37da850 1686
0d8b6a69 1687 // Module can put text after the link (e.g. forum unread)
1688 echo $mod->get_after_link();
1689
611684a8 1690 // If there is content but NO link (eg label), then display the
1691 // content here (BEFORE any icons). In this case cons must be
1692 // displayed after the content so that it makes more sense visually
1693 // and for accessibility reasons, e.g. if you have a one-line label
1694 // it should work similarly (at least in terms of ordering) to an
1695 // activity.
1696 if (empty($url)) {
1697 echo $contentpart;
1698 }
1699
dd97c328 1700 if ($isediting) {
525e16ce 1701 if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
dd97c328 1702 if (! $mod->groupmodelink = $groupbuttonslink) {
1703 $mod->groupmode = $course->groupmode;
3d575e6f 1704 }
dd97c328 1705
1706 } else {
1707 $mod->groupmode = false;
c9f6251e 1708 }
dd97c328 1709 echo '&nbsp;&nbsp;';
1710 echo make_editing_buttons($mod, $absolute, true, $mod->indent, $section->section);
0d8b6a69 1711 echo $mod->get_after_edit_icons();
94361e02 1712 }
4e781c7b 1713
1714 // Completion
3982f0f8 1715 $completion = $hidecompletion
4e781c7b 1716 ? COMPLETION_TRACKING_NONE
1717 : $completioninfo->is_enabled($mod);
2f48819b 1718 if ($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
82bd6a5e 1719 !isguestuser() && $mod->uservisible) {
3982f0f8 1720 $completiondata = $completioninfo->get_data($mod,true);
1721 $completionicon = '';
1722 if ($isediting) {
1723 switch ($completion) {
ca255392 1724 case COMPLETION_TRACKING_MANUAL :
3982f0f8 1725 $completionicon = 'manual-enabled'; break;
ca255392 1726 case COMPLETION_TRACKING_AUTOMATIC :
3982f0f8 1727 $completionicon = 'auto-enabled'; break;
4e781c7b 1728 default: // wtf
1729 }
3982f0f8 1730 } else if ($completion==COMPLETION_TRACKING_MANUAL) {
4e781c7b 1731 switch($completiondata->completionstate) {
1732 case COMPLETION_INCOMPLETE:
3982f0f8 1733 $completionicon = 'manual-n'; break;
4e781c7b 1734 case COMPLETION_COMPLETE:
3982f0f8 1735 $completionicon = 'manual-y'; break;
4e781c7b 1736 }
1737 } else { // Automatic
1738 switch($completiondata->completionstate) {
1739 case COMPLETION_INCOMPLETE:
3982f0f8 1740 $completionicon = 'auto-n'; break;
4e781c7b 1741 case COMPLETION_COMPLETE:
3982f0f8 1742 $completionicon = 'auto-y'; break;
4e781c7b 1743 case COMPLETION_COMPLETE_PASS:
3982f0f8 1744 $completionicon = 'auto-pass'; break;
4e781c7b 1745 case COMPLETION_COMPLETE_FAIL:
3982f0f8 1746 $completionicon = 'auto-fail'; break;
4e781c7b 1747 }
1748 }
3982f0f8 1749 if ($completionicon) {
b5d0cafc 1750 $imgsrc = $OUTPUT->pix_url('i/completion-'.$completionicon);
9e4baaf9 1751 $imgalt = s(get_string('completion-alt-'.$completionicon, 'completion', $mod->name));
76c0123b 1752 if ($completion == COMPLETION_TRACKING_MANUAL && !$isediting) {
9e4baaf9 1753 $imgtitle = s(get_string('completion-title-'.$completionicon, 'completion', $mod->name));
3982f0f8 1754 $newstate =
4e781c7b 1755 $completiondata->completionstate==COMPLETION_COMPLETE
ca255392 1756 ? COMPLETION_INCOMPLETE
1757 : COMPLETION_COMPLETE;
a343b13f 1758 // In manual mode the icon is a toggle form...
1759
1760 // If this completion state is used by the
1761 // conditional activities system, we need to turn
1762 // off the JS.
2f48819b 1763 if (!empty($CFG->enableavailability) &&
76c0123b 1764 condition_info::completion_value_used_as_condition($course, $mod)) {
a343b13f 1765 $extraclass = ' preventjs';
1766 } else {
1767 $extraclass = '';
1768 }
4e781c7b 1769 echo "
d5842f7a 1770<form class='togglecompletion$extraclass' method='post' action='".$CFG->wwwroot."/course/togglecompletion.php'><div>
4e781c7b 1771<input type='hidden' name='id' value='{$mod->id}' />
9e4baaf9 1772<input type='hidden' name='modulename' value='".s($mod->name)."' />
8c194133 1773<input type='hidden' name='sesskey' value='".sesskey()."' />
4e781c7b 1774<input type='hidden' name='completionstate' value='$newstate' />
1775<input type='image' src='$imgsrc' alt='$imgalt' title='$imgtitle' />
1776</div></form>";
1777 } else {
1778 // In auto mode, or when editing, the icon is just an image
f17a0360 1779 echo "<span class='autocompletion'>";
f17a0360 1780 echo "<img src='$imgsrc' alt='$imgalt' title='$imgalt' /></span>";
4e781c7b 1781 }
1782 }
1783 }
1784
611684a8 1785 // If there is content AND a link, then display the content here
1786 // (AFTER any icons). Otherwise it was displayed before
1787 if (!empty($url)) {
1788 echo $contentpart;
1789 }
0d8b6a69 1790
2f48819b 1791 // Show availability information (for someone who isn't allowed to
82bd6a5e 1792 // see the activity itself, or for staff)
3982f0f8 1793 if (!$mod->uservisible) {
82bd6a5e 1794 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
e8e50986 1795 } else if ($canviewhidden && !empty($CFG->enableavailability)) {
82bd6a5e 1796 $ci = new condition_info($mod);
3982f0f8 1797 $fullinfo = $ci->get_full_information();
82bd6a5e 1798 if($fullinfo) {
2f48819b 1799 echo '<div class="availabilityinfo">'.get_string($mod->showavailability
82bd6a5e 1800 ? 'userrestriction_visible'
1801 : 'userrestriction_hidden','condition',
1802 $fullinfo).'</div>';
1803 }
1804 }
1805
060cd0c8
SH
1806 echo html_writer::end_tag('div');
1807 echo html_writer::end_tag('li')."\n";
94361e02 1808 }
dd97c328 1809
f2d660dc 1810 } elseif ($ismoving) {
1811 echo "<ul class=\"section\">\n";
264867fd 1812 }
dd97c328 1813
7977cffd 1814 if ($ismoving) {
64fdc686 1815 echo '<li><a title="'.$strmovefull.'"'.
d4a1fcaf 1816 ' href="'.$CFG->wwwroot.'/course/mod.php?movetosection='.$section->id.'&amp;sesskey='.sesskey().'">'.
b5d0cafc 1817 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
c6a55371 1818 ' alt="'.$strmovehere.'" /></a></li>
1c919752 1819 ';
7977cffd 1820 }
c6a55371 1821 if (!empty($section->sequence) || $ismoving) {
1822 echo "</ul><!--class='section'-->\n\n";
1823 }
a7ad3ea6 1824}
1825
89bfeee0 1826/**
1827 * Prints the menus to add activities and resources.
1828 */
cb57e6f4 1829function print_section_add_menus($course, $section, $modnames, $vertical=false, $return=false) {
64e12bb7 1830 global $CFG, $OUTPUT;
e0161bff 1831
217a8ee9 1832 // check to see if user can add menus
1833 if (!has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id))) {
e2cd3ed0 1834 return false;
217a8ee9 1835 }
1836
59155a3f
ARN
1837 // Retrieve all modules with associated metadata
1838 $modules = get_module_metadata($course, $modnames);
31dadc6f 1839
59155a3f 1840 // We'll sort resources and activities into two lists
017ab27c
DP
1841 $resources = array();
1842 $activities = array();
6da4b261 1843
59155a3f
ARN
1844 // We need to add the section section to the link for each module
1845 $sectionlink = '&section=' . $section;
6da4b261 1846
59155a3f
ARN
1847 foreach ($modules as $module) {
1848 if (isset($module->types)) {
1849 // This module has a subtype
017ab27c 1850 // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
59155a3f
ARN
1851 $subtypes = array();
1852 foreach ($module->types as $subtype) {
1853 $subtypes[$subtype->link . $sectionlink] = $subtype->title;
1854 }
1855
1856 // Sort module subtypes into the list
1857 if (!empty($module->title)) {
1858 // This grouping has a name
1859 if ($module->archetype == MOD_CLASS_RESOURCE) {
1860 $resources[] = array($module->title=>$subtypes);
1861 } else {
1862 $activities[] = array($module->title=>$subtypes);
89bfeee0 1863 }
59155a3f
ARN
1864 } else {
1865 // This grouping does not have a name
1866 if ($module->archetype == MOD_CLASS_RESOURCE) {
1867 $resources = array_merge($resources, $subtypes);
aa54ed7b 1868 } else {
59155a3f 1869 $activities = array_merge($activities, $subtypes);
aa54ed7b 1870 }
89bfeee0 1871 }
017ab27c 1872 } else {
59155a3f
ARN
1873 // This module has no subtypes
1874 if ($module->archetype == MOD_ARCHETYPE_RESOURCE) {
1875 $resources[$module->link . $sectionlink] = $module->title;
1876 } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) {
5813c7f7 1877 // System modules cannot be added by user, do not add to dropdown
017ab27c 1878 } else {
59155a3f 1879 $activities[$module->link . $sectionlink] = $module->title;
017ab27c 1880 }
0705ff84 1881 }
e0161bff 1882 }
1883
89bfeee0 1884 $straddactivity = get_string('addactivity');
1885 $straddresource = get_string('addresource');
1886
4f24b3e3 1887 $output = '<div class="section_add_menus">';
1888
1889 if (!$vertical) {
1890 $output .= '<div class="horizontal">';
1891 }
82fcab32 1892
89bfeee0 1893 if (!empty($resources)) {
31dadc6f 1894 $select = new url_select($resources, '', array(''=>$straddresource), "ressection$section");
d2265f13 1895 $select->set_help_icon('resources');
31dadc6f 1896 $output .= $OUTPUT->render($select);
0705ff84 1897 }
cb57e6f4 1898
89bfeee0 1899 if (!empty($activities)) {
31dadc6f 1900 $select = new url_select($activities, '', array(''=>$straddactivity), "section$section");
d2265f13 1901 $select->set_help_icon('activities');
31dadc6f 1902 $output .= $OUTPUT->render($select);
0705ff84 1903 }
1904
4f24b3e3 1905 if (!$vertical) {
d33d0cda 1906 $output .= '</div>';
1907 }
1908
cb57e6f4 1909 $output .= '</div>';
1910
1911 if ($return) {
1912 return $output;
1913 } else {
1914 echo $output;
1915 }
e0161bff 1916}
1917
59155a3f
ARN
1918/**
1919 * Retrieve all metadata for the requested modules
1920 *
1921 * @param object $course The Course
1922 * @param array $modnames An array containing the list of modules and their
1923 * names
1924 * @return array A list of stdClass objects containing metadata about each
1925 * module
1926 */
1927function get_module_metadata($course, $modnames) {
1928 global $CFG, $OUTPUT;
1929
1930 // get_module_metadata will be called once per section on the page and courses may show
1931 // different modules to one another
1932 static $modlist = array();
1933 if (!isset($modlist[$course->id])) {
1934 $modlist[$course->id] = array();
1935 }
1936
1937 $return = array();
1938 $urlbase = "/course/mod.php?id=$course->id&sesskey=".sesskey().'&add=';
1939 foreach($modnames as $modname => $modnamestr) {
1940 if (!course_allowed_module($course, $modname)) {
1941 continue;
1942 }
1943 if (isset($modlist[$modname])) {
1944 // This module is already cached
1945 $return[$modname] = $modlist[$course->id][$modname];
1946 continue;
1947 }
1948
1949 // Include the module lib
1950 $libfile = "$CFG->dirroot/mod/$modname/lib.php";
1951 if (!file_exists($libfile)) {
1952 continue;
1953 }
1954 include_once($libfile);
1955
1956 // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
1957 $gettypesfunc = $modname.'_get_types';
1958 if (function_exists($gettypesfunc)) {
1959 if ($types = $gettypesfunc()) {
1960 $group = new stdClass();
1961 $group->name = $modname;
1962 $group->icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
1963 foreach($types as $type) {
1964 if ($type->typestr === '--') {
1965 continue;
1966 }
1967 if (strpos($type->typestr, '--') === 0) {
1968 $group->title = str_replace('--', '', $type->typestr);
1969 continue;
1970 }
1971 // Set the Sub Type metadata
1972 $subtype = new stdClass();
1973 $subtype->title = $type->typestr;
1974 $subtype->type = str_replace('&amp;', '&', $type->type);
1975 $subtype->name = preg_replace('/.*type=/', '', $subtype->type);
1976 $subtype->archetype = $type->modclass;
1977
1978 // The group archetype should match the subtype archetypes and all subtypes
1979 // should have the same archetype
1980 $group->archetype = $subtype->archetype;
1981
1982 if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
1983 $subtype->help = get_string('help' . $subtype->name, $modname);
1984 }
772685e9 1985 $subtype->link = $urlbase . $subtype->type;
59155a3f
ARN
1986 $group->types[] = $subtype;
1987 }
1988 $modlist[$course->id][$modname] = $group;
1989 }
1990 } else {
1991 $module = new stdClass();
1992 $module->title = get_string('modulename', $modname);
1993 $module->name = $modname;
1994 $module->link = $urlbase . $modname;
1995 $module->icon = $OUTPUT->pix_icon('icon', '', $module->name, array('class' => 'icon'));
1996 if (get_string_manager()->string_exists('modulename_help', $modname)) {
1997 $module->help = get_string('modulename_help', $modname);
1998 }
1999 $module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
2000 $modlist[$course->id][$modname] = $module;
2001 }
2002 $return[$modname] = $modlist[$course->id][$modname];
2003 }
2004
2005 return $return;
2006}
2007
8ed5dd63 2008/**
2009 * Return the course category context for the category with id $categoryid, except
2010 * that if $categoryid is 0, return the system context.
2011 *
2012 * @param integer $categoryid a category id or 0.
2013 * @return object the corresponding context
2014 */
2015function get_category_or_system_context($categoryid) {
2016 if ($categoryid) {
2017 return get_context_instance(CONTEXT_COURSECAT, $categoryid);
2018 } else {
2019 return get_context_instance(CONTEXT_SYSTEM);
2020 }
2021}
2022
cb6fec1f 2023/**
7e85563d 2024 * Gets the child categories of a given courses category. Uses a static cache
8ed5dd63 2025 * to make repeat calls efficient.
2026 *
e92c39ca 2027 * @param int $parentid the id of a course category.
8ed5dd63 2028 * @return array all the child course categories.
cb6fec1f 2029 */
8ed5dd63 2030function get_child_categories($parentid) {
9bb19e58 2031 static $allcategories = null;
2032
2033 // only fill in this variable the first time
2034 if (null == $allcategories) {
2035 $allcategories = array();
2036
2037 $categories = get_categories();
2038 foreach ($categories as $category) {
2039 if (empty($allcategories[$category->parent])) {
2040 $allcategories[$category->parent] = array();
2041 }
2042 $allcategories[$category->parent][] = $category;
2043 }
2044 }
2045
8ed5dd63 2046 if (empty($allcategories[$parentid])) {
9bb19e58 2047 return array();
2048 } else {
8ed5dd63 2049 return $allcategories[$parentid];
9bb19e58 2050 }
2051}
2052
cb6fec1f 2053/**
8ed5dd63 2054 * This function recursively travels the categories, building up a nice list
2055 * for display. It also makes an array that list all the parents for each
2056 * category.
2057 *
2058 * For example, if you have a tree of categories like:
2059 * Miscellaneous (id = 1)
2060 * Subcategory (id = 2)
2061 * Sub-subcategory (id = 4)
2062 * Other category (id = 3)
2063 * Then after calling this function you will have
2064 * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
2065 * 4 => 'Miscellaneous / Subcategory / Sub-subcategory',
2066 * 3 => 'Other category');
2067 * $parents = array(2 => array(1), 4 => array(1, 2));
2068 *
2069 * If you specify $requiredcapability, then only categories where the current
2070 * user has that capability will be added to $list, although all categories
2071 * will still be added to $parents, and if you only have $requiredcapability
2072 * in a child category, not the parent, then the child catgegory will still be
2073 * included.
2074 *
2075 * If you specify the option $excluded, then that category, and all its children,
2076 * are omitted from the tree. This is useful when you are doing something like
2077 * moving categories, where you do not want to allow people to move a category
2078 * to be the child of itself.
2079 *
2080 * @param array $list For output, accumulates an array categoryid => full category path name
2081 * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
8a1b1c32 2082 * @param string/array $requiredcapability if given, only categories where the current
2083 * user has this capability will be added to $list. Can also be an array of capabilities,
2084 * in which case they are all required.
8ed5dd63 2085 * @param integer $excludeid Omit this category and its children from the lists built.
2086 * @param object $category Build the tree starting at this category - otherwise starts at the top level.
2087 * @param string $path For internal use, as part of recursive calls.
cb6fec1f 2088 */
8ed5dd63 2089function make_categories_list(&$list, &$parents, $requiredcapability = '',
2090 $excludeid = 0, $category = NULL, $path = "") {
2091
9d866ae0 2092 // initialize the arrays if needed
2093 if (!is_array($list)) {
264867fd 2094 $list = array();
9d866ae0 2095 }
2096 if (!is_array($parents)) {
264867fd 2097 $parents = array();
9d866ae0 2098 }
2099
8ed5dd63 2100 if (empty($category)) {
2101 // Start at the top level.
2102 $category = new stdClass;
2103 $category->id = 0;
2104 } else {
2105 // This is the excluded category, don't include it.
2106 if ($excludeid > 0 && $excludeid == $category->id) {
2107 return;
2108 }
2109
63390481
SH
2110 $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
2111 $categoryname = format_string($category->name, true, array('context' => $context));
2112
8ed5dd63 2113 // Update $path.
c2cb4545 2114 if ($path) {
63390481 2115 $path = $path.' / '.$categoryname;
c2cb4545 2116 } else {
63390481 2117 $path = $categoryname;
c2cb4545 2118 }
8ed5dd63 2119
2120 // Add this category to $list, if the permissions check out.
3ce50127 2121 if (empty($requiredcapability)) {
8ed5dd63 2122 $list[$category->id] = $path;
3ce50127 2123
2124 } else {
3ce50127 2125 $requiredcapability = (array)$requiredcapability;
63390481 2126 if (has_all_capabilities($requiredcapability, $context)) {
3ce50127 2127 $list[$category->id] = $path;
2128 }
8ed5dd63 2129 }
c2cb4545 2130 }
2131
8ed5dd63 2132 // Add all the children recursively, while updating the parents array.
2133 if ($categories = get_child_categories($category->id)) {
c2cb4545 2134 foreach ($categories as $cat) {
2135 if (!empty($category->id)) {
3bd4de22 2136 if (isset($parents[$category->id])) {
2832badf 2137 $parents[$cat->id] = $parents[$category->id];
2138 }
c2cb4545 2139 $parents[$cat->id][] = $category->id;
2140 }
8ed5dd63 2141 make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
c2cb4545 2142 }
2143 }
2144}
2145
24e27ac0
SH
2146/**
2147 * This function generates a structured array of courses and categories.
2148 *
2149 * The depth of categories is limited by $CFG->maxcategorydepth however there
2150 * is no limit on the number of courses!
2151 *
2152 * Suitable for use with the course renderers course_category_tree method:
2153 * $renderer = $PAGE->get_renderer('core','course');
2154 * echo $renderer->course_category_tree(get_course_category_tree());
2155 *
2156 * @global moodle_database $DB
2157 * @param int $id
2158 * @param int $depth
2159 */
2160function get_course_category_tree($id = 0, $depth = 0) {
cf41dc37 2161 global $DB, $CFG;
24e27ac0
SH
2162 $viewhiddencats = has_capability('moodle/category:viewhiddencategories', get_context_instance(CONTEXT_SYSTEM));
2163 $categories = get_child_categories($id);
2164 $categoryids = array();
2165 foreach ($categories as $key => &$category) {
2166 if (!$category->visible && !$viewhiddencats) {
2167 unset($categories[$key]);
2168 continue;
2169 }
2170 $categoryids[$category->id] = $category;
2171 if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
2172 list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
3ebc548f
SH
2173 foreach ($subcategories as $subid=>$subcat) {
2174 $categoryids[$subid] = $subcat;
2175 }
24e27ac0
SH
2176 $category->courses = array();
2177 }
2178 }
2179
2180 if ($depth > 0) {
2181 // This is a recursive call so return the required array
2182 return array($categories, $categoryids);
2183 }
2184
2185 // The depth is 0 this function has just been called so we can finish it off
2186
2187 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
2188 list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
2189 $sql = "SELECT
df997f84 2190 c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
24e27ac0
SH
2191 $ccselect
2192 FROM {course} c
2193 $ccjoin
2194 WHERE c.category $catsql ORDER BY c.sortorder ASC";
2195 if ($courses = $DB->get_records_sql($sql, $catparams)) {
2196 // loop throught them
2197 foreach ($courses as $course) {
2198 if ($course->id == SITEID) {
2199 continue;
2200 }
2201 context_instance_preload($course);
2202 if (!empty($course->visible) || has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
2203 $categoryids[$course->category]->courses[$course->id] = $course;
2204 }
2205 }
2206 }
2207 return $categories;
2208}
c2cb4545 2209
cb6fec1f 2210/**
2211 * Recursive function to print out all the categories in a nice format
2212 * with or without courses included
2213 */
8ed5dd63 2214function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) {
9ff5310a 2215 global $CFG;
e05bcf2f 2216
beeee4d2 2217 // maxcategorydepth == 0 meant no limit
2218 if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
e05bcf2f 2219 return;
9ff5310a 2220 }
c2cb4545 2221
2222 if (!$displaylist) {
e92fe848 2223 make_categories_list($displaylist, $parentslist);
c2cb4545 2224 }
2225
2226 if ($category) {
8ed5dd63 2227 if ($category->visible or has_capability('moodle/category:viewhiddencategories', get_context_instance(CONTEXT_SYSTEM))) {
2228 print_category_info($category, $depth, $showcourses);
c2cb4545 2229 } else {
2230 return; // Don't bother printing children of invisible categories
2231 }
89adb174 2232
c2cb4545 2233 } else {
b85b25eb 2234 $category = new stdClass();
c2cb4545 2235 $category->id = "0";
2236 }
2237
9bb19e58 2238 if ($categories = get_child_categories($category->id)) { // Print all the children recursively
c2cb4545 2239 $countcats = count($categories);
2240 $count = 0;
2241 $first = true;
2242 $last = false;
2243 foreach ($categories as $cat) {
2244 $count++;
2245 if ($count == $countcats) {
2246 $last = true;
2247 }
2248 $up = $first ? false : true;
2249 $down = $last ? false : true;
2250 $first = false;
2251
8ed5dd63 2252 print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses);
c2cb4545 2253 }
2254 }
c2cb4545 2255}
2256
cb6fec1f 2257/**
af90698b 2258 * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
cb6fec1f 2259 */
0705ff84 2260function make_categories_options() {
2261 make_categories_list($cats,$parents);
2262 foreach ($cats as $key => $value) {
2263 if (array_key_exists($key,$parents)) {
2264 if ($indent = count($parents[$key])) {
2265 for ($i = 0; $i < $indent; $i++) {
2266 $cats[$key] = '&nbsp;'.$cats[$key];
2267 }
2268 }
2269 }
2270 }
2271 return $cats;
2272}
c2cb4545 2273
7fb46992 2274/**
2275 * Gets the name of a course to be displayed when showing a list of courses.
2276 * By default this is just $course->fullname but user can configure it. The
2277 * result of this function should be passed through print_string.
2278 * @param object $course Moodle course object
2279 * @return string Display name of course (either fullname or short + fullname)
2280 */
2281function get_course_display_name_for_list($course) {
2282 global $CFG;
2283 if (!empty($CFG->courselistshortnames)) {
2284 return $course->shortname . ' ' .$course->fullname;
2285 } else {
2286 return $course->fullname;
2287 }
2288}
2289
cb6fec1f 2290/**
2291 * Prints the category info in indented fashion
2292 * This function is only used by print_whole_category_list() above
2293 */
f3e5bf86 2294function print_category_info($category, $depth=0, $showcourses = false) {
6b608f8f 2295 global $CFG, $DB, $OUTPUT;
c2cb4545 2296
df997f84 2297 $strsummary = get_string('summary');
ba2e5d73 2298
ea831ceb
RW
2299 $catlinkcss = null;
2300 if (!$category->visible) {
2301 $catlinkcss = array('class'=>'dimmed');
2302 }
dc247e52 2303 static $coursecount = null;
2304 if (null === $coursecount) {
2305 // only need to check this once
2306 $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT;
2307 }
2308
8ed5dd63 2309 if ($showcourses and $coursecount) {
b5d0cafc 2310 $catimage = '<img src="'.$OUTPUT->pix_url('i/course') . '" alt="" />';
b48f834c 2311 } else {
7b0b5c14 2312 $catimage = "&nbsp;";
8ef9cb56 2313 }
2afcfc44 2314
df997f84 2315 $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
63390481
SH
2316 $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
2317 $fullname = format_string($category->name, true, array('context' => $context));
2318
8ed5dd63 2319 if ($showcourses and $coursecount) {
2a63b636 2320 echo '<div class="categorylist clearfix">';
2afcfc44 2321 $cat = '';
2a63b636 2322 $cat .= html_writer::tag('div', $catimage, array('class'=>'image'));
63390481 2323 $catlink = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
2afcfc44
RW
2324 $cat .= html_writer::tag('div', $catlink, array('class'=>'name'));
2325
ea831ceb 2326 $html = '';
2afcfc44
RW
2327 if ($depth > 0) {
2328 for ($i=0; $i< $depth; $i++) {
ea831ceb 2329 $html = html_writer::tag('div', $html . $cat, array('class'=>'indentation'));
2afcfc44 2330 $cat = '';
cb184beb 2331 }
2afcfc44 2332 } else {
ea831ceb 2333 $html = $cat;
2afcfc44 2334 }
2a63b636 2335 echo html_writer::tag('div', $html, array('class'=>'category'));
2afcfc44 2336 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
b48f834c 2337
beeee4d2 2338 // does the depth exceed maxcategorydepth
2afcfc44 2339 // maxcategorydepth == 0 or unset meant no limit
beeee4d2 2340 $limit = !(isset($CFG->maxcategorydepth) && ($depth >= $CFG->maxcategorydepth-1));
beeee4d2 2341 if ($courses && ($limit || $CFG->maxcategorydepth == 0)) {
c2cb4545 2342 foreach ($courses as $course) {
ea831ceb
RW
2343 $linkcss = null;
2344 if (!$course->visible) {
2345 $linkcss = array('class'=>'dimmed');
2346 }
2a63b636 2347
7fb46992 2348 $coursename = get_course_display_name_for_list($course);
2349 $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($coursename), $linkcss);
2afcfc44 2350
bf423bb1 2351 // print enrol info
f3e5bf86 2352 $courseicon = '';
bf423bb1
PS
2353 if ($icons = enrol_get_course_info_icons($course)) {
2354 foreach ($icons as $pix_icon) {
f3e5bf86 2355 $courseicon = $OUTPUT->render($pix_icon).' ';
bf423bb1
PS
2356 }
2357 }
2358
f3e5bf86
SH
2359 $coursecontent = html_writer::tag('div', $courseicon.$courselink, array('class'=>'name'));
2360
b48f834c 2361 if ($course->summary) {
75015e5f 2362 $link = new moodle_url('/course/info.php?id='.$course->id);
2afcfc44 2363 $actionlink = $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
75015e5f
PS
2364 new popup_action('click', $link, 'courseinfo', array('height' => 400, 'width' => 500)),
2365 array('title'=>$strsummary));
2afcfc44
RW
2366
2367 $coursecontent .= html_writer::tag('div', $actionlink, array('class'=>'info'));
2368 }
2369
ea831ceb 2370 $html = '';
f3e5bf86 2371 for ($i=0; $i <= $depth; $i++) {
ea831ceb 2372 $html = html_writer::tag('div', $html . $coursecontent , array('class'=>'indentation'));
2afcfc44 2373 $coursecontent = '';
0c656181 2374 }
ea831ceb 2375 echo html_writer::tag('div', $html, array('class'=>'course clearfloat'));
2afcfc44 2376 }
ba2e5d73 2377 }
2afcfc44
RW
2378 echo '</div>';
2379 } else {
2a63b636 2380 echo '<div class="categorylist">';
ea831ceb 2381 $html = '';
63390481 2382 $cat = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
eea78ebc
DP
2383 if (count($courses) > 0) {
2384 $cat .= html_writer::tag('span', ' ('.count($courses).')', array('title'=>get_string('numberofcourses'), 'class'=>'numberofcourse'));
2385 }
2a63b636 2386
ea831ceb
RW
2387 if ($depth > 0) {
2388 for ($i=0; $i< $depth; $i++) {
2389 $html = html_writer::tag('div', $html .$cat, array('class'=>'indentation'));
2390 $cat = '';
2391 }
2392 } else {
2393 $html = $cat;
2394 }
2a63b636
PS
2395
2396 echo html_writer::tag('div', $html, array('class'=>'category'));
ea831ceb 2397 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
cb184beb 2398 echo '</div>';
2afcfc44 2399 }
c2cb4545 2400}
2401
77eddcd5 2402/**
2403 * Print the buttons relating to course requests.
2404 *
2405 * @param object $systemcontext the system context.
2406 */
2407function print_course_request_buttons($systemcontext) {
b4531207 2408 global $CFG, $DB, $OUTPUT;
77eddcd5 2409 if (empty($CFG->enablecourserequests)) {
2410 return;
2411 }
4f0c2d00 2412 if (!has_capability('moodle/course:create', $systemcontext) && has_capability('moodle/course:request', $systemcontext)) {
77eddcd5 2413 /// Print a button to request a new course
5c2ed7e2 2414 echo $OUTPUT->single_button('request.php', get_string('requestcourse'), 'get');
77eddcd5 2415 }
2416 /// Print a button to manage pending requests
2417 if (has_capability('moodle/site:approvecourse', $systemcontext)) {
5c2ed7e2
PS
2418 $disabled = !$DB->record_exists('course_request', array());
2419 echo $OUTPUT->single_button('pending.php', get_string('coursespending'), 'get', array('disabled'=>$disabled));
77eddcd5 2420 }
2421}
2422
5048e034 2423/**
2424 * Does the user have permission to edit things in this category?
2425 *
2426 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2427 * @return boolean has_any_capability(array(...), ...); in the appropriate context.
2428 */
2429function can_edit_in_category($categoryid = 0) {
2430 $context = get_category_or_system_context($categoryid);
2431 return has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context);
2432}
2433
8ed5dd63 2434/**
2435 * Prints the turn editing on/off button on course/index.php or course/category.php.
2436 *
2437 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2438 * @return string HTML of the editing button, or empty string, if this user is not allowed
2439 * to see it.
2440 */
2441function update_category_button($categoryid = 0) {
b4531207 2442 global $CFG, $PAGE, $OUTPUT;
8ed5dd63 2443
2444 // Check permissions.
5048e034 2445 if (!can_edit_in_category($categoryid)) {
8ed5dd63 2446 return '';
2447 }
2448
2449 // Work out the appropriate action.
830dd6e9 2450 if ($PAGE->user_is_editing()) {
8ed5dd63 2451 $label = get_string('turneditingoff');
2452 $edit = 'off';
2453 } else {
2454 $label = get_string('turneditingon');
2455 $edit = 'on';
2456 }
c2cb4545 2457
8ed5dd63 2458 // Generate the button HTML.
2459 $options = array('categoryedit' => $edit, 'sesskey' => sesskey());
2460 if ($categoryid) {
2461 $options['id'] = $categoryid;
2462 $page = 'category.php';
2463 } else {
2464 $page = 'index.php';
2465 }
a6855934 2466 return $OUTPUT->single_button(new moodle_url('/course/' . $page, $options), $label, 'get');
8ed5dd63 2467}
e0b033d5 2468
cb6fec1f 2469/**
2470 * Category is 0 (for all courses) or an object
2471 */
6c54240a 2472function print_courses($category) {
2f48819b 2473 global $CFG, $OUTPUT;
c2cb4545 2474
4dde1463 2475 if (!is_object($category) && $category==0) {
9bb19e58 2476 $categories = get_child_categories(0); // Parent = 0 ie top-level categories only
4dde1463 2477 if (is_array($categories) && count($categories) == 1) {
90c2ca2e 2478 $category = array_shift($categories);
238c0dd9 2479 $courses = get_courses_wmanagers($category->id,
2480 'c.sortorder ASC',
df997f84 2481 array('summary','summaryformat'));
90c2ca2e 2482 } else {
238c0dd9 2483 $courses = get_courses_wmanagers('all',
2484 'c.sortorder ASC',
df997f84 2485 array('summary','summaryformat'));
90c2ca2e 2486 }
2487 unset($categories);
607809b3 2488 } else {
238c0dd9 2489 $courses = get_courses_wmanagers($category->id,
2490 'c.sortorder ASC',
df997f84 2491 array('summary','summaryformat'));
c2cb4545 2492 }
2493
49cd4d79 2494 if ($courses) {
002fc5ba 2495 echo html_writer::start_tag('ul', array('class'=>'unlist'));
c2cb4545 2496 foreach ($courses as $course) {
4f0c2d00
PS
2497 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
2498 if ($course->visible == 1 || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
002fc5ba 2499 echo html_writer::start_tag('li');
4dde1463 2500 print_course($course);
002fc5ba 2501 echo html_writer::end_tag('li');
4dde1463 2502 }
c2cb4545 2503 }
002fc5ba 2504 echo html_writer::end_tag('ul');
c2cb4545 2505 } else {
7c5286cd 2506 echo $OUTPUT->heading(get_string("nocoursesyet"));
8e480396 2507 $context = get_context_instance(CONTEXT_SYSTEM);
0468976c 2508 if (has_capability('moodle/course:create', $context)) {
255d1033 2509 $options = array();
4868e95f
DM
2510 if (!empty($category->id)) {
2511 $options['category'] = $category->id;
2512 } else {
2513 $options['category'] = $CFG->defaultrequestcategory;
2514 }
002fc5ba 2515 echo html_writer::start_tag('div', array('class'=>'addcoursebutton'));
a6855934 2516 echo $OUTPUT->single_button(new moodle_url('/course/edit.php', $options), get_string("addnewcourse"));
002fc5ba 2517 echo html_writer::end_tag('div');
255d1033 2518 }
c2cb4545 2519 }
c2cb4545 2520}
2521
04c53106 2522/**
2523 * Print a description of a course, suitable for browsing in a list.
2524 *
2525 * @param object $course the course object.
2526 * @param string $highlightterms (optional) some search terms that should be highlighted in the display.
2527 */
2528function print_course($course, $highlightterms = '') {
666e8458 2529 global $CFG, $USER, $DB, $OUTPUT;
c2cb4545 2530
4f0c2d00 2531 $context = get_context_instance(CONTEXT_COURSE, $course->id);
146bbb8f 2532
8bdc9cac 2533 // Rewrite file URLs so that they are correct
64f93798 2534 $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
8bdc9cac 2535
002fc5ba
SH
2536 echo html_writer::start_tag('div', array('class'=>'coursebox clearfix'));
2537 echo html_writer::start_tag('div', array('class'=>'info'));
2538 echo html_writer::start_tag('h3', array('class'=>'name'));
22288704 2539
002fc5ba 2540 $linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
7fb46992 2541
2542 $coursename = get_course_display_name_for_list($course);
2543 $linktext = highlight($highlightterms, format_string($coursename));
002fc5ba
SH
2544 $linkparams = array('title'=>get_string('entercourse'));
2545 if (empty($course->visible)) {
2546 $linkparams['class'] = 'dimmed';
2547 }
2548 echo html_writer::link($linkhref, $linktext, $linkparams);
2549 echo html_writer::end_tag('h3');
238c0dd9 2550
d42c64ba 2551 /// first find all roles that are supposed to be displayed
df997f84 2552 if (!empty($CFG->coursecontact)) {
c71f3265 2553 $managerroles = explode(',', $CFG->coursecontact);
4dde1463 2554 $namesarray = array();
e52a8ebd 2555 $rusers = array();
9d5a4b23 2556
e52a8ebd
DP
2557 if (!isset($course->managers)) {
2558 $rusers = get_role_users($managerroles, $context, true,
2559 'ra.id AS raid, u.id, u.username, u.firstname, u.lastname,
2560 r.name AS rolename, r.sortorder, r.id AS roleid',
2561 'r.sortorder ASC, u.lastname ASC');
2562 } else {
2563 // use the managers array if we have it for perf reasosn
2564 // populate the datastructure like output of get_role_users();
2565 foreach ($course->managers as $manager) {
2566 $u = new stdClass();
2567 $u = $manager->user;
2568 $u->roleid = $manager->roleid;
2569 $u->rolename = $manager->rolename;
4f0c2d00 2570
e52a8ebd 2571 $rusers[] = $u;
4dde1463 2572 }
e52a8ebd 2573 }
165d25cc 2574
e52a8ebd
DP
2575 /// Rename some of the role names if needed
2576 if (isset($context)) {
2577 $aliasnames = $DB->get_records('role_names', array('contextid'=>$context->id), '', 'roleid,contextid,name');
2578 }
165d25cc 2579
e52a8ebd
DP
2580 $namesarray = array();
2581 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2582 foreach ($rusers as $ra) {
2583 if (isset($namesarray[$ra->id])) {
2584 // only display a user once with the higest sortorder role
2585 continue;
2586 }
165d25cc 2587
e52a8ebd
DP
2588 if (isset($aliasnames[$ra->roleid])) {
2589 $ra->rolename = $aliasnames[$ra->roleid]->name;
431cad0d 2590 }
e52a8ebd
DP
2591
2592 $fullname = fullname($ra, $canviewfullnames);
2593 $namesarray[$ra->id] = format_string($ra->rolename).': '.
2594 html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->id, 'course'=>SITEID)), $fullname);
c2cb4545 2595 }
431cad0d 2596
d42c64ba 2597 if (!empty($namesarray)) {
002fc5ba
SH
2598 echo html_writer::start_tag('ul', array('class'=>'teachers'));
2599 foreach ($namesarray as $name) {
2600 echo html_writer::tag('li', $name);
2601 }
2602 echo html_writer::end_tag('ul');
88768091 2603 }
c2cb4545 2604 }
002fc5ba 2605 echo html_writer::end_tag('div'); // End of info div
238c0dd9 2606
002fc5ba 2607 echo html_writer::start_tag('div', array('class'=>'summary'));
b85b25eb 2608 $options = new stdClass();
9f39c190 2609 $options->noclean = true;
34b5847a 2610 $options->para = false;
367a75fa 2611 $options->overflowdiv = true;
8bdc9cac
SH
2612 if (!isset($course->summaryformat)) {
2613 $course->summaryformat = FORMAT_MOODLE;
2614 }
2615 echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options, $course->id));
e877160d 2616 if ($icons = enrol_get_course_info_icons($course)) {
002fc5ba
SH
2617 echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
2618 foreach ($icons as $icon) {
2619 echo $OUTPUT->render($icon);
2620 }
2621 echo html_writer::end_tag('div'); // End of enrolmenticons div
2622 }
2623 echo html_writer::end_tag('div'); // End of summary div
2624 echo html_writer::end_tag('div'); // End of coursebox div
c2cb4545 2625}
2626
cb6fec1f 2627/**
2628 * Prints custom user information on the home page.
2629 * Over time this can include all sorts of information
2630 */
c2cb4545 2631function print_my_moodle() {
e6db3026 2632 global $USER, $CFG, $DB, $OUTPUT;
c2cb4545 2633
4f0c2d00 2634 if (!isloggedin() or isguestuser()) {
ba6018a9 2635 print_error('nopermissions', '', '', 'See My Moodle');
c2cb4545 2636 }
2637
df997f84 2638 $courses = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
0a127169 2639 $rhosts = array();
2640 $rcourses = array();
2641 if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2642 $rcourses = get_my_remotecourses($USER->id);
2643 $rhosts = get_my_remotehosts();
2644 }
2645
2646 if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2647
2648 if (!empty($courses)) {
2649 echo '<ul class="unlist">';
2650 foreach ($courses as $course) {
2651 if ($course->id == SITEID) {
2652 continue;
2653 }
2654 echo '<li>';
2655 print_course($course);
2656 echo "</li>\n";
86dd62a7 2657 }
0a127169 2658 echo "</ul>\n";
86dd62a7 2659 }
2660
0a127169 2661 // MNET
2662 if (!empty($rcourses)) {
2663 // at the IDP, we know of all the remote courses
2664 foreach ($rcourses as $course) {
2665 print_remote_course($course, "100%");
2666 }
2667 } elseif (!empty($rhosts)) {
2668 // non-IDP, we know of all the remote servers, but not courses
2669 foreach ($rhosts as $host) {
2670 print_remote_host($host, "100%");
2671 }
2672 }
86dd62a7 2673 unset($course);
0a127169 2674 unset($host);
38a10939 2675
cb6fec1f 2676 if ($DB->count_records("course") > (count($courses) + 1) ) { // Some courses not being displayed
7f989948 2677 echo "<table width=\"100%\"><tr><td align=\"center\">";
2678 print_course_search("", false, "short");
2679 echo "</td><td align=\"center\">";
5c2ed7e2 2680 echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
7f989948 2681 echo "</td></tr></table>\n";
2682 }
86dd62a7 2683
26330001 2684 } else {
cb6fec1f 2685 if ($DB->count_records("course_categories") > 1) {
e6db3026 2686 echo $OUTPUT->box_start("categorybox");
26330001 2687 print_whole_category_list();
e6db3026 2688 echo $OUTPUT->box_end();
26330001 2689 } else {
35d0244a 2690 print_courses(0);
26330001 2691 }
607809b3 2692 }
2b8cef80 2693}
2694
11b0c469 2695
a8b56716 2696function print_course_search($value="", $return=false, $format="plain") {
38a10939 2697 global $CFG;
1e0fb105 2698 static $count = 0;
2699
2700 $count++;
2701
2702 $id = 'coursesearch';
2703
2704 if ($count > 1) {
2705 $id .= $count;
2706 }
38a10939 2707
2708 $strsearchcourses= get_string("searchcourses");
2709
1c919752 2710 if ($format == 'plain') {
1e0fb105 2711 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2712 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
e42f4d92 2713 $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2714 $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
e42f4d92 2715 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2716 $output .= '</fieldset></form>';
1c919752 2717 } else if ($format == 'short') {
1e0fb105 2718 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2719 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2720 $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2721 $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2722 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2723 $output .= '</fieldset></form>';
1c919752 2724 } else if ($format == 'navbar') {
fcf9577a 2725 $output = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2726 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2727 $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2728 $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2729 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2730 $output .= '</fieldset></form>';
a8b56716 2731 }
2732
2733 if ($return) {
2734 return $output;
2735 }
2736 echo $output;
38a10939 2737}
11b0c469 2738
86dd62a7 2739function print_remote_course($course, $width="100%") {
86dd62a7 2740 global $CFG, $USER;
2741
2742 $linkcss = '';
2743
2744 $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}";
2745
7cd266e9 2746 echo '<div class="coursebox remotecoursebox clearfix">';
86dd62a7 2747 echo '<div class="info">';
2748 echo '<div class="name"><a title="'.get_string('entercourse').'"'.
2749 $linkcss.' href="'.$url.'">'
6ba65fa0 2750 . format_string($course->fullname) .'</a><br />'
2751 . format_string($course->hostname) . ' : '
238c0dd9 2752 . format_string($course->cat_name) . ' : '
2753 . format_string($course->shortname). '</div>';
86dd62a7 2754 echo '</div><div class="summary">';
b85b25eb 2755 $options = new stdClass();
86dd62a7 2756 $options->noclean = true;
2757 $options->para = false;
367a75fa 2758 $options->overflowdiv = true;
152a2273 2759 echo format_text($course->summary, $course->summaryformat, $options);
86dd62a7 2760 echo '</div>';
2761 echo '</div>';
86dd62a7 2762}
2763
643b67b8 2764function print_remote_host($host, $width="100%") {
6b608f8f 2765 global $OUTPUT;
643b67b8 2766
2767 $linkcss = '';
2768
7cd266e9 2769 echo '<div class="coursebox clearfix">';
643b67b8 2770 echo '<div class="info">';
2771 echo '<div class="name">';
b5d0cafc 2772 echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
643b67b8 2773 echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
2774 . s($host['name']).'</a> - ';
1fd80ad3 2775 echo $host['count'] . ' ' . get_string('courses');
643b67b8 2776 echo '</div>';
2777 echo '</div>';
caa90d56 2778 echo '</div>';
643b67b8 2779}
2780
86dd62a7 2781
11b0c469 2782/// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
2783
2784function add_course_module($mod) {
cb6fec1f 2785 global $DB;
11b0c469 2786
e5dfd0f3 2787 $mod->added = time();
53f4ad2c 2788 unset($mod->id);
11b0c469 2789
cb6fec1f 2790 return $DB->insert_record("course_modules", $mod);
11b0c469 2791}
2792
97928ddf 2793/**
2794 * Returns course section - creates new if does not exist yet.
2795 * @param int $relative section number
2796 * @param int $courseid
2797 * @return object $course_section object
2798 */
2799function get_course_section($section, $courseid) {
cb6fec1f 2800 global $DB;
2801
2802 if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
97928ddf 2803 return $cw;
2804 }
fbaea88f 2805 $cw = new stdClass();
cb6fec1f 2806 $cw->course = $courseid;
2807 $cw->section = $section;
2808 $cw->summary = "";
09eb2151 2809 $cw->summaryformat = FORMAT_HTML;
97928ddf 2810 $cw->sequence = "";
cb6fec1f 2811 $id = $DB->insert_record("course_sections", $cw);
dc5af91a 2812 return $DB->get_record("course_sections", array("id"=>$id));
97928ddf 2813}
ece966f0 2814/**
2815 * Given a full mod object with section and course already defined, adds this module to that section.
2816 *
2817 * @param object $mod
2818 * @param int $beforemod An existing ID which we will insert the new module before
2819 * @return int The course_sections ID where the mod is inserted
2820 */
7977cffd 2821function add_mod_to_section($mod, $beforemod=NULL) {
cb6fec1f 2822 global $DB;
11b0c469 2823
cb6fec1f 2824 if ($section = $DB->get_record("course_sections", array("course"=>$mod->course, "section"=>$mod->section))) {
7977cffd 2825
2826 $section->sequence = trim($section->sequence);
2827
2828 if (empty($section->sequence)) {
11b0c469 2829 $newsequence = "$mod->coursemodule";
7977cffd 2830
2831 } else if ($beforemod) {
2832 $modarray = explode(",", $section->sequence);
2833
d857e8b6 2834 if ($key = array_keys($modarray, $beforemod->id)) {
7977cffd 2835 $insertarray = array($mod->id, $beforemod->id);
2836 array_splice($modarray, $key[0], 1, $insertarray);
2837 $newsequence = implode(",", $modarray);
2838
2839 } else { // Just tack it on the end anyway
2840 $newsequence = "$section->sequence,$mod->coursemodule";
2841 }
2842
2843 } else {
2844 $newsequence = "$section->sequence,$mod->coursemodule";
11b0c469 2845 }
89adb174 2846
f685e830
PS
2847 $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
2848 return $section->id; // Return course_sections ID that was used.
89adb174 2849
11b0c469 2850 } else { // Insert a new record
c9c20273 2851 $section = new stdClass();
cb6fec1f 2852 $section->course = $mod->course;
2853 $section->section = $mod->section;
2854 $section->summary = "";
09eb2151 2855 $section->summaryformat = FORMAT_HTML;
e5dfd0f3 2856 $section->sequence = $mod->coursemodule;
cb6fec1f 2857 return $DB->insert_record("course_sections", $section);
11b0c469 2858 }
2859}
2860
48e535bc 2861function set_coursemodule_groupmode($id, $groupmode) {
cb6fec1f 2862 global $DB;
a5d424df 2863 return $DB->set_field("course_modules", "groupmode", $groupmode, array("id"=>$id));
3d575e6f 2864}
2865
177d4abf 2866function set_coursemodule_idnumber($id, $idnumber) {
cb6fec1f 2867 global $DB;
238c0dd9 2868 return $DB->set_field("course_modules", "idnumber", $idnumber, array("id"=>$id));
177d4abf 2869}
4e781c7b 2870
02f66c42 2871/**
2872* $prevstateoverrides = true will set the visibility of the course module
2873* to what is defined in visibleold. This enables us to remember the current
2874* visibility when making a whole section hidden, so that when we toggle
2875* that section back to visible, we are able to return the visibility of
2876* the course module back to what it was originally.
2877*/
2878function set_coursemodule_visible($id, $visible, $prevstateoverrides=false) {
f5e2602a 2879 global $DB, $CFG;
0f078024
DM
2880 require_once($CFG->libdir.'/gradelib.php');
2881
cb6fec1f 2882 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
978abb42 2883 return false;
2884 }
cb6fec1f 2885 if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
978abb42 2886 return false;
2887 }
cb6fec1f 2888 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2889 foreach($events as $event) {
48e535bc 2890 if ($visible) {
2891 show_event($event);
2892 } else {
2893 hide_event($event);
2894 }
dcd338ff 2895 }
2896 }
f5e2602a 2897
0f078024
DM
2898 // hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there
2899 $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
2900 if ($grade_items) {
2901 foreach ($grade_items as $grade_item) {
2902 $grade_item->set_hidden(!$visible);
2903 }
f5e2602a 2904 }
f685e830 2905
02f66c42 2906 if ($prevstateoverrides) {
2907 if ($visible == '0') {
2908 // Remember the current visible state so we can toggle this back.
cb6fec1f 2909 $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id'=>$id));
02f66c42 2910 } else {
2911 // Get the previous saved visible states.
cb6fec1f 2912 return $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
02f66c42 2913 }
2914 }
cb6fec1f 2915 return $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
1acfbce5 2916}
2917
cb6fec1f 2918/**
290130b3 2919 * Delete a course module and any associated data at the course level (events)
264867fd 2920 * Until 1.5 this function simply marked a deleted flag ... now it
290130b3 2921 * deletes it completely.
2922 *
2923 */
48e535bc 2924function delete_course_module($id) {
cb6fec1f 2925 global $CFG, $DB;
f615fbab 2926 require_once($CFG->libdir.'/gradelib.php');
cae83708 2927 require_once($CFG->dirroot.'/blog/lib.php');
f615fbab 2928
cb6fec1f 2929 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
290130b3 2930 return true;
2931 }
cb6fec1f 2932 $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
f615fbab 2933 //delete events from calendar
cb6fec1f 2934 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2935 foreach($events as $event) {
0ea03696 2936 delete_event($event->id);
dcd338ff 2937 }
2938 }
f615fbab 2939 //delete grade items, outcome items and grades attached to modules
2940 if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
2941 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
2942 foreach ($grade_items as $grade_item) {
2943 $grade_item->delete('moddelete');
2944 }
f615fbab 2945 }
46e12372
SM
2946 // Delete completion and availability data; it is better to do this even if the
2947 // features are not turned on, in case they were turned on previously (these will be
2948 // very quick on an empty table)
2949 $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
2950 $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
ede323e2
ARN
2951 $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id,
2952 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
06b3a6b2 2953
2954 delete_context(CONTEXT_MODULE, $cm->id);
bf66a674 2955 return $DB->delete_records('course_modules', array('id'=>$cm->id));
11b0c469 2956}
2957
2958function delete_mod_from_section($mod, $section) {
cb6fec1f 2959 global $DB;
11b0c469 2960
cb6fec1f 2961 if ($section = $DB->get_record("course_sections", array("id"=>$section)) ) {
11b0c469 2962
e5dfd0f3 2963 $modarray = explode(",", $section->sequence);
11b0c469 2964
2965 if ($key = array_keys ($modarray, $mod)) {
2966 array_splice($modarray, $key[0], 1);
2967 $newsequence = implode(",", $modarray);
cb6fec1f 2968 return $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
11b0c469 2969 } else {
2970 return false;
2971 }
89adb174 2972
11b0c469 2973 }
7977cffd 2974 return false;
11b0c469 2975}
2976
3440ec12 2977/**
2978 * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
2979 *
93d46f48
RK
2980 * @param object $course course object
2981 * @param int $section Section number (not id!!!)
3440ec12 2982 * @param int $move (-1 or 1)
93d46f48 2983 * @return boolean true if section moved successfully
3440ec12 2984 */
12905134 2985function move_section($course, $section, $move) {
2986/// Moves a whole course section up and down within the course
cb6fec1f 2987 global $USER, $DB;
12905134 2988
2989 if (!$move) {
2990 return true;
2991 }
2992
2993 $sectiondest = $section + $move;
2994
2995 if ($sectiondest > $course->numsections or $sectiondest < 1) {
2996 return false;
2997 }
2998
cb6fec1f 2999 if (!$sectionrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$section))) {
12905134 3000 return false;
3001 }
3002
cb6fec1f 3003 if (!$sectiondestrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$sectiondest))) {
12905134 3004 return false;
3005 }
3006
cf76b335 3007 // Three-step change ensures that the section always remains unique (there is
3008 // a unique index now)
3009 $DB->set_field("course_sections", "section", -$sectiondest, array("id"=>$sectionrecord->id));
f685e830 3010 $DB->set_field("course_sections", "section", $section, array("id"=>$sectiondestrecord->id));
e7e0f8d2 3011 $DB->set_field("course_sections", "section", $sectiondest, array("id"=>$sectionrecord->id));
f685e830 3012
93d46f48
RK
3013 // Update highlighting if the move affects highlighted section
3014 if ($course->marker == $section) {
3015 course_set_marker($course->id, $sectiondest);
3016 } elseif ($course->marker == $sectiondest) {
3017 course_set_marker($course->id, $section);
3018 }
3019
798b70a1 3020 // if the focus is on the section that is being moved, then move the focus along
13801a49 3021 if (course_get_display($course->id) == $section) {
798b70a1 3022 course_set_display($course->id, $sectiondest);
3023 }
5390cbb7 3024
cf76b335 3025 // Fix order if needed. The database prevents duplicate sections, but it is
3026 // possible there could be a gap in the numbering.
cb6fec1f 3027 $sections = $DB->get_records('course_sections', array('course'=>$course->id), 'section ASC');
a987106d 3028 $n = 0;
3029 foreach ($sections as $section) {
3030 if ($section->section != $n) {
f685e830 3031 $DB->set_field('course_sections', 'section', $n, array('id'=>$section->id));
5390cbb7 3032 }
a987106d 3033 $n++;
5390cbb7 3034 }
12905134 3035 return true;
3036}
3037
3440ec12 3038/**
3039 * Moves a section within a course, from a position to another.
3040 * Be very careful: $section and $destination refer to section number,
3041 * not id!.
3042 *
3043 * @param object $course
3044 * @param int $section Section number (not id!!!)
3045 * @param int $destination
3046 * @return boolean Result
3047 */
3048function move_section_to($course, $section, $destination) {
3049/// Moves a whole course section up and down within the course
3050 global $USER, $DB;
3051
ca255392 3052 if (!$destination && $destination != 0) {
3440ec12 3053 return true;
3054 }
3055
ca255392 3056 if ($destination > $course->numsections) {
3440ec12 3057 return false;
3058 }
3059
3060 // Get all sections for this course and re-order them (2 of them should now share the same section number)
3061 if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
3062 'section ASC, id ASC', 'id, section')) {
3063 return false;
3064 }
3065
cf76b335 3066 $movedsections = reorder_sections($sections, $section, $destination);
3440ec12 3067
cf76b335 3068 // Update all sections. Do this in 2 steps to avoid breaking database
3069 // uniqueness constraint
3070 $transaction = $DB->start_delegated_transaction();
3071 foreach ($movedsections as $id => $position) {
3072 if ($sections[$id] !== $position) {
3073 $DB->set_field('course_sections', 'section', -$position, array('id' => $id));
3074 }
3075 }
e7e0f8d2
DP
3076 foreach ($movedsections as $id => $position) {
3077 if ($sections[$id] !== $position) {
3078 $DB->set_field('course_sections', 'section', $position, array('id' => $id));
3079 }
3440ec12 3080 }
3081
15e2552f
RK
3082 // Adjust destination to reflect the actual section
3083 $moveup = false;
3084 if ($section > $destination) {
3085 $destination++;
3086 $moveup = true;
3087 }
3088
3089 // If we move the highlighted section itself, then just highlight the destination.
3090 // Adjust the higlighted section location if we move something over it either direction.
3091 if ($section == $course->marker) {
3092 course_set_marker($course, $destination);
3093 } elseif ($moveup && $section > $course->marker && $course->marker >= $destination) {
3094 course_set_marker($course, $course->marker+1);
3095 } elseif (!$moveup && $section < $course->marker && $course->marker <= $destination) {
3096 course_set_marker($course, $course->marker-1);
3097 }
3098
3440ec12 3099 // if the focus is on the section that is being moved, then move the focus along
13801a49 3100 if (course_get_display($course->id) == $section) {
3440ec12 3101 course_set_display($course->id, $destination);
3102 }
cf76b335 3103 $transaction->allow_commit();
3440ec12 3104 return true;
3105}
3106
3107/**
3108 * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
3109 * an original position number and a target position number, rebuilds the array so that the
3110 * move is made without any duplication of section positions.
3111 * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
3112 * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
3113 *
3114 * @param array $sections
3115 * @param int $origin_position
3116 * @param int $target_position
3117 * @return array
3118 */
3119function reorder_sections($sections, $origin_position, $target_position) {
3120 if (!is_array($sections)) {
3121 return false;
3122 }
3123
3124 // We can't move section position 0
3125 if ($origin_position < 1) {
3126 echo "We can't move section position 0";
3127 return false;
3128 }
3129
3130 // Locate origin section in sections array
3131 if (!$origin_key = array_search($origin_position, $sections)) {
3132 echo "searched position not in sections array";
3133 return false; // searched position not in sections array
3134 }
3135
3136 // Extract origin section
3137 $origin_section = $sections[$origin_key];
3138 unset($sections[$origin_key]);
3139
3140 // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
3141 $found = false;
3142 $append_array = array();
3143 foreach ($sections as $id => $position) {
3144 if ($found) {
3145 $append_array[$id] = $position;
3146 unset($sections[$id]);
3147 }
3148 if ($position == $target_position) {
3149 $found = true;
3150 }
3151 }
3152
3153 // Append moved section
3154 $sections[$origin_key] = $origin_section;
3155
3156 // Append rest of array (if applicable)
3157 if (!empty($append_array)) {
3158 foreach ($append_array as $id => $position) {
3159 $sections[$id] = $position;
3160 }
3161 }
3162
3163 // Renumber positions
3164 $position = 0;
3165 foreach ($sections as $id => $p) {
3166 $sections[$id] = $position;
3167 $position++;
3168 }
3169
3170 return $sections;
3171
3172}
3173
cb6fec1f 3174/**
3175 * Move the module object $mod to the specified $section
3176 * If $beforemod exists then that is the module
3177 * before which $modid should be inserted
3178 * All parameters are objects
3179 */
7977cffd 3180function moveto_module($mod, $section, $beforemod=NULL) {
e6db3026 3181 global $DB, $OUTPUT;
7977cffd 3182
3183/// Remove original module from original section