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