MDL-29902: Course contacts display cleanup
[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();
e52a8ebd 2457 $rusers = array();
9d5a4b23 2458
e52a8ebd
DP
2459 if (!isset($course->managers)) {
2460 $rusers = get_role_users($managerroles, $context, true,
2461 'ra.id AS raid, u.id, u.username, u.firstname, u.lastname,
2462 r.name AS rolename, r.sortorder, r.id AS roleid',
2463 'r.sortorder ASC, u.lastname ASC');
2464 } else {
2465 // use the managers array if we have it for perf reasosn
2466 // populate the datastructure like output of get_role_users();
2467 foreach ($course->managers as $manager) {
2468 $u = new stdClass();
2469 $u = $manager->user;
2470 $u->roleid = $manager->roleid;
2471 $u->rolename = $manager->rolename;
4f0c2d00 2472
e52a8ebd 2473 $rusers[] = $u;
4dde1463 2474 }
e52a8ebd 2475 }
165d25cc 2476
e52a8ebd
DP
2477 /// Rename some of the role names if needed
2478 if (isset($context)) {
2479 $aliasnames = $DB->get_records('role_names', array('contextid'=>$context->id), '', 'roleid,contextid,name');
2480 }
165d25cc 2481
e52a8ebd
DP
2482 $namesarray = array();
2483 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2484 foreach ($rusers as $ra) {
2485 if (isset($namesarray[$ra->id])) {
2486 // only display a user once with the higest sortorder role
2487 continue;
2488 }
165d25cc 2489
e52a8ebd
DP
2490 if (isset($aliasnames[$ra->roleid])) {
2491 $ra->rolename = $aliasnames[$ra->roleid]->name;
431cad0d 2492 }
e52a8ebd
DP
2493
2494 $fullname = fullname($ra, $canviewfullnames);
2495 $namesarray[$ra->id] = format_string($ra->rolename).': '.
2496 html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->id, 'course'=>SITEID)), $fullname);
c2cb4545 2497 }
431cad0d 2498
d42c64ba 2499 if (!empty($namesarray)) {
002fc5ba
SH
2500 echo html_writer::start_tag('ul', array('class'=>'teachers'));
2501 foreach ($namesarray as $name) {
2502 echo html_writer::tag('li', $name);
2503 }
2504 echo html_writer::end_tag('ul');
88768091 2505 }
c2cb4545 2506 }
002fc5ba 2507 echo html_writer::end_tag('div'); // End of info div
238c0dd9 2508
002fc5ba 2509 echo html_writer::start_tag('div', array('class'=>'summary'));
9f39c190 2510 $options = NULL;
2511 $options->noclean = true;
34b5847a 2512 $options->para = false;
367a75fa 2513 $options->overflowdiv = true;
8bdc9cac
SH
2514 if (!isset($course->summaryformat)) {
2515 $course->summaryformat = FORMAT_MOODLE;
2516 }
2517 echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options, $course->id));
e877160d 2518 if ($icons = enrol_get_course_info_icons($course)) {
002fc5ba
SH
2519 echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
2520 foreach ($icons as $icon) {
2521 echo $OUTPUT->render($icon);
2522 }
2523 echo html_writer::end_tag('div'); // End of enrolmenticons div
2524 }
2525 echo html_writer::end_tag('div'); // End of summary div
2526 echo html_writer::end_tag('div'); // End of coursebox div
c2cb4545 2527}
2528
cb6fec1f 2529/**
2530 * Prints custom user information on the home page.
2531 * Over time this can include all sorts of information
2532 */
c2cb4545 2533function print_my_moodle() {
e6db3026 2534 global $USER, $CFG, $DB, $OUTPUT;
c2cb4545 2535
4f0c2d00 2536 if (!isloggedin() or isguestuser()) {
ba6018a9 2537 print_error('nopermissions', '', '', 'See My Moodle');
c2cb4545 2538 }
2539
df997f84 2540 $courses = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
0a127169 2541 $rhosts = array();
2542 $rcourses = array();
2543 if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2544 $rcourses = get_my_remotecourses($USER->id);
2545 $rhosts = get_my_remotehosts();
2546 }
2547
2548 if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2549
2550 if (!empty($courses)) {
2551 echo '<ul class="unlist">';
2552 foreach ($courses as $course) {
2553 if ($course->id == SITEID) {
2554 continue;
2555 }
2556 echo '<li>';
2557 print_course($course);
2558 echo "</li>\n";
86dd62a7 2559 }
0a127169 2560 echo "</ul>\n";
86dd62a7 2561 }
2562
0a127169 2563 // MNET
2564 if (!empty($rcourses)) {
2565 // at the IDP, we know of all the remote courses
2566 foreach ($rcourses as $course) {
2567 print_remote_course($course, "100%");
2568 }
2569 } elseif (!empty($rhosts)) {
2570 // non-IDP, we know of all the remote servers, but not courses
2571 foreach ($rhosts as $host) {
2572 print_remote_host($host, "100%");
2573 }
2574 }
86dd62a7 2575 unset($course);
0a127169 2576 unset($host);
38a10939 2577
cb6fec1f 2578 if ($DB->count_records("course") > (count($courses) + 1) ) { // Some courses not being displayed
7f989948 2579 echo "<table width=\"100%\"><tr><td align=\"center\">";
2580 print_course_search("", false, "short");
2581 echo "</td><td align=\"center\">";
5c2ed7e2 2582 echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
7f989948 2583 echo "</td></tr></table>\n";
2584 }
86dd62a7 2585
26330001 2586 } else {
cb6fec1f 2587 if ($DB->count_records("course_categories") > 1) {
e6db3026 2588 echo $OUTPUT->box_start("categorybox");
26330001 2589 print_whole_category_list();
e6db3026 2590 echo $OUTPUT->box_end();
26330001 2591 } else {
35d0244a 2592 print_courses(0);
26330001 2593 }
607809b3 2594 }
2b8cef80 2595}
2596
11b0c469 2597
a8b56716 2598function print_course_search($value="", $return=false, $format="plain") {
38a10939 2599 global $CFG;
1e0fb105 2600 static $count = 0;
2601
2602 $count++;
2603
2604 $id = 'coursesearch';
2605
2606 if ($count > 1) {
2607 $id .= $count;
2608 }
38a10939 2609
2610 $strsearchcourses= get_string("searchcourses");
2611
1c919752 2612 if ($format == 'plain') {
1e0fb105 2613 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2614 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
e42f4d92 2615 $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2616 $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
e42f4d92 2617 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2618 $output .= '</fieldset></form>';
1c919752 2619 } else if ($format == 'short') {
1e0fb105 2620 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2621 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2622 $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2623 $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2624 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2625 $output .= '</fieldset></form>';
1c919752 2626 } else if ($format == 'navbar') {
fcf9577a 2627 $output = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2628 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2629 $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2630 $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2631 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2632 $output .= '</fieldset></form>';
a8b56716 2633 }
2634
2635 if ($return) {
2636 return $output;
2637 }
2638 echo $output;
38a10939 2639}
11b0c469 2640
86dd62a7 2641function print_remote_course($course, $width="100%") {
86dd62a7 2642 global $CFG, $USER;
2643
2644 $linkcss = '';
2645
2646 $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}";
2647
7cd266e9 2648 echo '<div class="coursebox remotecoursebox clearfix">';
86dd62a7 2649 echo '<div class="info">';
2650 echo '<div class="name"><a title="'.get_string('entercourse').'"'.
2651 $linkcss.' href="'.$url.'">'
6ba65fa0 2652 . format_string($course->fullname) .'</a><br />'
2653 . format_string($course->hostname) . ' : '
238c0dd9 2654 . format_string($course->cat_name) . ' : '
2655 . format_string($course->shortname). '</div>';
86dd62a7 2656 echo '</div><div class="summary">';
2657 $options = NULL;
2658 $options->noclean = true;
2659 $options->para = false;
367a75fa 2660 $options->overflowdiv = true;
152a2273 2661 echo format_text($course->summary, $course->summaryformat, $options);
86dd62a7 2662 echo '</div>';
2663 echo '</div>';
86dd62a7 2664}
2665
643b67b8 2666function print_remote_host($host, $width="100%") {
6b608f8f 2667 global $OUTPUT;
643b67b8 2668
2669 $linkcss = '';
2670
7cd266e9 2671 echo '<div class="coursebox clearfix">';
643b67b8 2672 echo '<div class="info">';
2673 echo '<div class="name">';
b5d0cafc 2674 echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
643b67b8 2675 echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
2676 . s($host['name']).'</a> - ';
1fd80ad3 2677 echo $host['count'] . ' ' . get_string('courses');
643b67b8 2678 echo '</div>';
2679 echo '</div>';
caa90d56 2680 echo '</div>';
643b67b8 2681}
2682
86dd62a7 2683
11b0c469 2684/// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
2685
2686function add_course_module($mod) {
cb6fec1f 2687 global $DB;
11b0c469 2688
e5dfd0f3 2689 $mod->added = time();
53f4ad2c 2690 unset($mod->id);
11b0c469 2691
cb6fec1f 2692 return $DB->insert_record("course_modules", $mod);
11b0c469 2693}
2694
97928ddf 2695/**
2696 * Returns course section - creates new if does not exist yet.
2697 * @param int $relative section number
2698 * @param int $courseid
2699 * @return object $course_section object
2700 */
2701function get_course_section($section, $courseid) {
cb6fec1f 2702 global $DB;
2703
2704 if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
97928ddf 2705 return $cw;
2706 }
fbaea88f 2707 $cw = new stdClass();
cb6fec1f 2708 $cw->course = $courseid;
2709 $cw->section = $section;
2710 $cw->summary = "";
09eb2151 2711 $cw->summaryformat = FORMAT_HTML;
97928ddf 2712 $cw->sequence = "";
cb6fec1f 2713 $id = $DB->insert_record("course_sections", $cw);
dc5af91a 2714 return $DB->get_record("course_sections", array("id"=>$id));
97928ddf 2715}
ece966f0 2716/**
2717 * Given a full mod object with section and course already defined, adds this module to that section.
2718 *
2719 * @param object $mod
2720 * @param int $beforemod An existing ID which we will insert the new module before
2721 * @return int The course_sections ID where the mod is inserted
2722 */
7977cffd 2723function add_mod_to_section($mod, $beforemod=NULL) {
cb6fec1f 2724 global $DB;
11b0c469 2725
cb6fec1f 2726 if ($section = $DB->get_record("course_sections", array("course"=>$mod->course, "section"=>$mod->section))) {
7977cffd 2727
2728 $section->sequence = trim($section->sequence);
2729
2730 if (empty($section->sequence)) {
11b0c469 2731 $newsequence = "$mod->coursemodule";
7977cffd 2732
2733 } else if ($beforemod) {
2734 $modarray = explode(",", $section->sequence);
2735
d857e8b6 2736 if ($key = array_keys($modarray, $beforemod->id)) {
7977cffd 2737 $insertarray = array($mod->id, $beforemod->id);
2738 array_splice($modarray, $key[0], 1, $insertarray);
2739 $newsequence = implode(",", $modarray);
2740
2741 } else { // Just tack it on the end anyway
2742 $newsequence = "$section->sequence,$mod->coursemodule";
2743 }
2744
2745 } else {
2746 $newsequence = "$section->sequence,$mod->coursemodule";
11b0c469 2747 }
89adb174 2748
f685e830
PS
2749 $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
2750 return $section->id; // Return course_sections ID that was used.
89adb174 2751
11b0c469 2752 } else { // Insert a new record
cb6fec1f 2753 $section->course = $mod->course;
2754 $section->section = $mod->section;
2755 $section->summary = "";
09eb2151 2756 $section->summaryformat = FORMAT_HTML;
e5dfd0f3 2757 $section->sequence = $mod->coursemodule;
cb6fec1f 2758 return $DB->insert_record("course_sections", $section);
11b0c469 2759 }
2760}
2761
48e535bc 2762function set_coursemodule_groupmode($id, $groupmode) {
cb6fec1f 2763 global $DB;
a5d424df 2764 return $DB->set_field("course_modules", "groupmode", $groupmode, array("id"=>$id));
3d575e6f 2765}
2766
177d4abf 2767function set_coursemodule_idnumber($id, $idnumber) {
cb6fec1f 2768 global $DB;
238c0dd9 2769 return $DB->set_field("course_modules", "idnumber", $idnumber, array("id"=>$id));
177d4abf 2770}
4e781c7b 2771
02f66c42 2772/**
2773* $prevstateoverrides = true will set the visibility of the course module
2774* to what is defined in visibleold. This enables us to remember the current
2775* visibility when making a whole section hidden, so that when we toggle
2776* that section back to visible, we are able to return the visibility of
2777* the course module back to what it was originally.
2778*/
2779function set_coursemodule_visible($id, $visible, $prevstateoverrides=false) {
f5e2602a 2780 global $DB, $CFG;
0f078024
DM
2781 require_once($CFG->libdir.'/gradelib.php');
2782
cb6fec1f 2783 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
978abb42 2784 return false;
2785 }
cb6fec1f 2786 if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
978abb42 2787 return false;
2788 }
cb6fec1f 2789 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2790 foreach($events as $event) {
48e535bc 2791 if ($visible) {
2792 show_event($event);
2793 } else {
2794 hide_event($event);
2795 }
dcd338ff 2796 }
2797 }
f5e2602a 2798
0f078024
DM
2799 // hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there
2800 $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
2801 if ($grade_items) {
2802 foreach ($grade_items as $grade_item) {
2803 $grade_item->set_hidden(!$visible);
2804 }
f5e2602a 2805 }
f685e830 2806
02f66c42 2807 if ($prevstateoverrides) {
2808 if ($visible == '0') {
2809 // Remember the current visible state so we can toggle this back.
cb6fec1f 2810 $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id'=>$id));
02f66c42 2811 } else {
2812 // Get the previous saved visible states.
cb6fec1f 2813 return $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
02f66c42 2814 }
2815 }
cb6fec1f 2816 return $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
1acfbce5 2817}
2818
cb6fec1f 2819/**
290130b3 2820 * Delete a course module and any associated data at the course level (events)
264867fd 2821 * Until 1.5 this function simply marked a deleted flag ... now it
290130b3 2822 * deletes it completely.
2823 *
2824 */
48e535bc 2825function delete_course_module($id) {
cb6fec1f 2826 global $CFG, $DB;
f615fbab 2827 require_once($CFG->libdir.'/gradelib.php');
cae83708 2828 require_once($CFG->dirroot.'/blog/lib.php');
f615fbab 2829
cb6fec1f 2830 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
290130b3 2831 return true;
2832 }
cb6fec1f 2833 $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
f615fbab 2834 //delete events from calendar
cb6fec1f 2835 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2836 foreach($events as $event) {
0ea03696 2837 delete_event($event->id);
dcd338ff 2838 }
2839 }
f615fbab 2840 //delete grade items, outcome items and grades attached to modules
2841 if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
2842 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
2843 foreach ($grade_items as $grade_item) {
2844 $grade_item->delete('moddelete');
2845 }
f615fbab 2846 }
46e12372
SM
2847 // Delete completion and availability data; it is better to do this even if the
2848 // features are not turned on, in case they were turned on previously (these will be
2849 // very quick on an empty table)
2850 $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
2851 $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
06b3a6b2 2852
2853 delete_context(CONTEXT_MODULE, $cm->id);
bf66a674 2854 return $DB->delete_records('course_modules', array('id'=>$cm->id));
11b0c469 2855}
2856
2857function delete_mod_from_section($mod, $section) {
cb6fec1f 2858 global $DB;
11b0c469 2859
cb6fec1f 2860 if ($section = $DB->get_record("course_sections", array("id"=>$section)) ) {
11b0c469 2861
e5dfd0f3 2862 $modarray = explode(",", $section->sequence);
11b0c469 2863
2864 if ($key = array_keys ($modarray, $mod)) {
2865 array_splice($modarray, $key[0], 1);
2866 $newsequence = implode(",", $modarray);
cb6fec1f 2867 return $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
11b0c469 2868 } else {
2869 return false;
2870 }
89adb174 2871
11b0c469 2872 }
7977cffd 2873 return false;
11b0c469 2874}
2875
3440ec12 2876/**
2877 * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
2878 *
2879 * @param object $course
2880 * @param int $section
2881 * @param int $move (-1 or 1)
2882 */
12905134 2883function move_section($course, $section, $move) {
2884/// Moves a whole course section up and down within the course
cb6fec1f 2885 global $USER, $DB;
12905134 2886
2887 if (!$move) {
2888 return true;
2889 }
2890
2891 $sectiondest = $section + $move;
2892
2893 if ($sectiondest > $course->numsections or $sectiondest < 1) {
2894 return false;
2895 }
2896
cb6fec1f 2897 if (!$sectionrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$section))) {
12905134 2898 return false;
2899 }
2900
cb6fec1f 2901 if (!$sectiondestrecord = $DB->get_record("course_sections", array("course"=>$course->id, "section"=>$sectiondest))) {
12905134 2902 return false;
2903 }
2904
f685e830
PS
2905 $DB->set_field("course_sections", "section", $sectiondest, array("id"=>$sectionrecord->id));
2906 $DB->set_field("course_sections", "section", $section, array("id"=>$sectiondestrecord->id));
2907
798b70a1 2908 // if the focus is on the section that is being moved, then move the focus along
13801a49 2909 if (course_get_display($course->id) == $section) {
798b70a1 2910 course_set_display($course->id, $sectiondest);
2911 }
5390cbb7 2912
a987106d 2913 // Check for duplicates and fix order if needed.
5390cbb7 2914 // There is a very rare case that some sections in the same course have the same section id.
cb6fec1f 2915 $sections = $DB->get_records('course_sections', array('course'=>$course->id), 'section ASC');
a987106d 2916 $n = 0;
2917 foreach ($sections as $section) {
2918 if ($section->section != $n) {
f685e830 2919 $DB->set_field('course_sections', 'section', $n, array('id'=>$section->id));
5390cbb7 2920 }
a987106d 2921 $n++;
5390cbb7 2922 }
12905134 2923 return true;
2924}
2925
3440ec12 2926/**
2927 * Moves a section within a course, from a position to another.
2928 * Be very careful: $section and $destination refer to section number,
2929 * not id!.
2930 *
2931 * @param object $course
2932 * @param int $section Section number (not id!!!)
2933 * @param int $destination
2934 * @return boolean Result
2935 */
2936function move_section_to($course, $section, $destination) {
2937/// Moves a whole course section up and down within the course
2938 global $USER, $DB;
2939
ca255392 2940 if (!$destination && $destination != 0) {
3440ec12 2941 return true;
2942 }
2943
ca255392 2944 if ($destination > $course->numsections) {
3440ec12 2945 return false;
2946 }
2947
2948 // Get all sections for this course and re-order them (2 of them should now share the same section number)
2949 if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
2950 'section ASC, id ASC', 'id, section')) {
2951 return false;
2952 }
2953
2954 $sections = reorder_sections($sections, $section, $destination);
2955
2956 // Update all sections
2957 foreach ($sections as $id => $position) {
2958 $DB->set_field('course_sections', 'section', $position, array('id' => $id));
2959 }
2960
2961 // if the focus is on the section that is being moved, then move the focus along
13801a49 2962 if (course_get_display($course->id) == $section) {
3440ec12 2963 course_set_display($course->id, $destination);
2964 }
2965 return true;
2966}
2967
2968/**
2969 * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
2970 * an original position number and a target position number, rebuilds the array so that the
2971 * move is made without any duplication of section positions.
2972 * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
2973 * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
2974 *
2975 * @param array $sections
2976 * @param int $origin_position
2977 * @param int $target_position
2978 * @return array
2979 */
2980function reorder_sections($sections, $origin_position, $target_position) {
2981 if (!is_array($sections)) {
2982 return false;
2983 }
2984
2985 // We can't move section position 0
2986 if ($origin_position < 1) {
2987 echo "We can't move section position 0";
2988 return false;
2989 }
2990
2991 // Locate origin section in sections array
2992 if (!$origin_key = array_search($origin_position, $sections)) {
2993 echo "searched position not in sections array";
2994 return false; // searched position not in sections array
2995 }
2996
2997 // Extract origin section
2998 $origin_section = $sections[$origin_key];
2999 unset($sections[$origin_key]);
3000
3001 // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
3002 $found = false;
3003 $append_array = array();
3004 foreach ($sections as $id => $position) {
3005 if ($found) {
3006 $append_array[$id] = $position;
3007 unset($sections[$id]);
3008 }
3009 if ($position == $target_position) {
3010 $found = true;
3011 }
3012 }
3013
3014 // Append moved section
3015 $sections[$origin_key] = $origin_section;
3016
3017 // Append rest of array (if applicable)
3018 if (!empty($append_array)) {
3019 foreach ($append_array as $id => $position) {
3020 $sections[$id] = $position;
3021 }
3022 }
3023
3024 // Renumber positions
3025 $position = 0;
3026 foreach ($sections as $id => $p) {
3027 $sections[$id] = $position;
3028 $position++;
3029 }
3030
3031 return $sections;
3032
3033}
3034
cb6fec1f 3035/**
3036 * Move the module object $mod to the specified $section
3037 * If $beforemod exists then that is the module
3038 * before which $modid should be inserted
3039 * All parameters are objects
3040 */
7977cffd 3041function moveto_module($mod, $section, $beforemod=NULL) {
e6db3026 3042 global $DB, $OUTPUT;
7977cffd 3043
3044/// Remove original module from original section
7977cffd 3045 if (! delete_mod_from_section($mod->id, $mod->section)) {
e6db3026 3046 echo $OUTPUT->notification("Could not delete module from existing section");
7977cffd 3047 }
3048
3049/// Update module itself if necessary
3050
3051 if ($mod->section != $section->id) {
89adb174 3052 $mod->section = $section->id;
bb4b6010 3053 $DB->update_record("course_modules", $mod);
48e535bc 3054 // if moving to a hidden section then hide module
3055 if (!$section->visible) {
3056 set_coursemodule_visible($mod->id, 0);
3057 }
7977cffd 3058 }
3059
3060/// Add the module into the new section
3061
3062 $mod->course = $section->course;
3063 $mod->section = $section->section; // need relative reference
3064 $mod->coursemodule = $mod->id;
3065
3066 if (! add_mod_to_section($mod, $beforemod)) {
3067 return false;
3068 }
3069
3070 return true;
7977cffd 3071}
3072
7749e187
SH
3073/**
3074 * Produces the editing buttons for a module
3075 *
3076 * @global core_renderer $OUTPUT
3077 * @staticvar type $str
3078 * @param stdClass $mod The module to produce editing buttons for
3079 * @param bool $absolute If true an absolute link is produced (default true)
3080 * @param bool $moveselect If true a move seleciton process is used (default true)
3081 * @param int $indent The current indenting
3082 * @param int $section The section to link back to
3083 * @return string XHTML for the editing buttons
3084 */
3085function make_editing_buttons(stdClass $mod, $absolute = true, $moveselect = true, $indent=-1, $section=-1) {
3086 global $CFG, $OUTPUT;
94361e02 3087
3d575e6f 3088 static $str;
3089
7749e187 3090 $coursecontext = get_context_instance(CONTEXT_COURSE, $mod->course);
217a8ee9 3091 $modcontext = get_context_instance(CONTEXT_MODULE, $mod->id);
7749e187 3092
217a8ee9 3093 // no permission to edit
3094 if (!has_capability('moodle/course:manageactivities', $modcontext)) {
e2cd3ed0 3095 return false;
217a8ee9 3096 }
3097
3d575e6f 3098 if (!isset($str)) {
7749e187 3099 $str = new stdClass;
9534a8cb 3100 $str->assign = get_string("assignroles", 'role');
90ebdf65 3101 $str->delete = get_string("delete");
3102 $str->move = get_string("move");
3103 $str->moveup = get_string("moveup");
3104 $str->movedown = get_string("movedown");
3105 $str->moveright = get_string("moveright");
3106 $str->moveleft = get_string("moveleft");
3107 $str->update = get_string("update");
3108 $str->duplicate = get_string("duplicate");
3109 $str->hide = get_string("hide");
3110 $str->show = get_string("show");
3d575e6f 3111 $str->clicktochange = get_string("clicktochange");
32d03b7b 3112 $str->forcedmode = get_string("forcedmode");
3d575e6f 3113 $str->groupsnone = get_string("groupsnone");
3114 $str->groupsseparate = get_string("groupsseparate");
3115 $str->groupsvisible = get_string("groupsvisible");
1acfbce5 3116 }
94361e02 3117
7749e187
SH
3118 if ($absolute) {
3119 $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
24e1eae4 3120 } else {
7749e187 3121 $baseurl = new moodle_url('mod.php', array('sesskey' => sesskey()));
24e1eae4 3122 }
3123
7749e187
SH
3124 if ($section >= 0) {
3125 $baseurl->param('sr', $section);
3126 }
3127 $actions = array();
3128
3129 // leftright
3130 if (has_capability('moodle/course:update', $coursecontext)) {
3131 if (right_to_left()) { // Exchange arrows on RTL
3132 $rightarrow = 't/left';
3133 $leftarrow = 't/right';
3134 } else {
3135 $rightarrow = 't/right';
3136 $leftarrow = 't/left';
3137 }
3138
3139 if ($indent > 0) {
3140 $actions[] = new action_link(
3141 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
3142 new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall')),
3143 null,
3144 array('class' => 'editing_moveleft', 'title' => $str->moveleft)
3145 );
3146 }
3147 if ($indent >= 0) {
3148 $actions[] = new action_link(
3149 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
3150 new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall')),
3151 null,
3152 array('class' => 'editing_moveright', 'title' => $str->moveright)
3153 );
3154 }
dc0dc7d5 3155 }
7749e187
SH
3156
3157 // move
3158 if (has_capability('moodle/course:update', $coursecontext)) {
3159 if ($moveselect) {
3160 $actions[] = new action_link(
3161 new moodle_url($baseurl, array('copy' => $mod->id)),
3162 new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall')),
3163 null,
3164 array('class' => 'editing_move', 'title' => $str->move)
3165 );
3166 } else {
3167 $actions[] = new action_link(
3168 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '-1')),
3169 new pix_icon('t/up', $str->moveup, 'moodle', array('class' => 'iconsmall')),
3170 null,
3171 array('class' => 'editing_moveup', 'title' => $str->moveup)
3172 );
3173 $actions[] = new action_link(
3174 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '1')),
3175 new pix_icon('t/down', $str->movedown, 'moodle', array('class' => 'iconsmall')),
3176 null,
3177 array('class' => 'editing_movedown', 'title' => $str->movedown)
3178 );
3179 }
3180 }
3181
3182 // Update
3183 $actions[] = new action_link(
3184 new moodle_url($baseurl, array('update' => $mod->id)),
3185 new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall')),
3186 null,
3187 array('class' => 'editing_update', 'title' => $str->update)
3188 );
3189
8645a28f
EL
3190 // Duplicate (require both target import caps to be able to duplicate, see modduplicate.php)
3191 $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
3192 if (has_all_capabilities($dupecaps, $coursecontext)) {
3193 $actions[] = new action_link(