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