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