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