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