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