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