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