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