MDL-35339 Deprecate add_mod_to_section(), create new function course_add_cm_to_section()
[moodle.git] / course / lib.php
CommitLineData
d9cb06dc 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Library of useful functions
20 *
21 * @copyright 1999 Martin Dougiamas http://dougiamas.com
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
23 * @package core
24 * @subpackage course
d9cb06dc 25 */
f9903ed0 26
df997f84
PS
27defined('MOODLE_INTERNAL') || die;
28
4e781c7b 29require_once($CFG->libdir.'/completionlib.php');
8bdc9cac 30require_once($CFG->libdir.'/filelib.php');
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
1212
cb6fec1f 1213/**
1214 * Returns a number of useful structures for course displays
1215 */
90845098 1216function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
0edb4aad 1217 global $CFG, $DB, $COURSE;
7468bf01 1218
dd97c328 1219 $mods = array(); // course modules indexed by id
1220 $modnames = array(); // all course module names (except resource!)
1221 $modnamesplural= array(); // all course module names (plural form)
1222 $modnamesused = array(); // course module names used
7468bf01 1223
cb6fec1f 1224 if ($allmods = $DB->get_records("modules")) {
90845098 1225 foreach ($allmods as $mod) {
0edb4aad
PS
1226 if (!file_exists("$CFG->dirroot/mod/$mod->name/lib.php")) {
1227 continue;
1228 }
5867bfb5 1229 if ($mod->visible) {
1230 $modnames[$mod->name] = get_string("modulename", "$mod->name");
1231 $modnamesplural[$mod->name] = get_string("modulenameplural", "$mod->name");
1232 }
90845098 1233 }
d609d962 1234 collatorlib::asort($modnames);
90845098 1235 } else {
ba6018a9 1236 print_error("nomodules", 'debug');
90845098 1237 }
1238
82bd6a5e 1239 $course = ($courseid==$COURSE->id) ? $COURSE : $DB->get_record('course',array('id'=>$courseid));
1240 $modinfo = get_fast_modinfo($course);
1241
1242 if ($rawmods=$modinfo->cms) {
7468bf01 1243 foreach($rawmods as $mod) { // Index the mods
959ae824 1244 if (empty($modnames[$mod->modname])) {
1245 continue;
1246 }
dd97c328 1247 $mods[$mod->id] = $mod;
1248 $mods[$mod->id]->modfullname = $modnames[$mod->modname];
9a5e297b 1249 if (!$mod->visible and !has_capability('moodle/course:viewhiddenactivities', context_course::instance($courseid))) {
dd97c328 1250 continue;
1251 }
13534ef7
ML
1252 // Check groupings
1253 if (!groups_course_module_visible($mod)) {
1254 continue;
1255 }
dd97c328 1256 $modnamesused[$mod->modname] = $modnames[$mod->modname];
7468bf01 1257 }
c7da6f7a 1258 if ($modnamesused) {
d609d962 1259 collatorlib::asort($modnamesused);
c7da6f7a 1260 }
7468bf01 1261 }
7468bf01 1262}
1263
93d46f48
RK
1264/**
1265 * Set highlighted section. Only one section can be highlighted at the time.
1266 *
1267 * @param int $courseid course id
1268 * @param int $marker highlight section with this number, 0 means remove higlightin
1269 * @return void
1270 */
1271function course_set_marker($courseid, $marker) {
1272 global $DB;
1273 $DB->set_field("course", "marker", $marker, array('id' => $courseid));
1274}
1275
cb6fec1f 1276/**
7e85563d 1277 * For a given course section, marks it visible or hidden,
cb6fec1f 1278 * and does the same for every activity in that section
ebaa29d1
ARN
1279 *
1280 * @param int $courseid course id
1281 * @param int $sectionnumber The section number to adjust
1282 * @param int $visibility The new visibility
1283 * @return array A list of resources which were hidden in the section
cb6fec1f 1284 */
7d99d695 1285function set_section_visible($courseid, $sectionnumber, $visibility) {
cb6fec1f 1286 global $DB;
7d99d695 1287
ebaa29d1 1288 $resourcestotoggle = array();
cb6fec1f 1289 if ($section = $DB->get_record("course_sections", array("course"=>$courseid, "section"=>$sectionnumber))) {
1290 $DB->set_field("course_sections", "visible", "$visibility", array("id"=>$section->id));
7d99d695 1291 if (!empty($section->sequence)) {
1292 $modules = explode(",", $section->sequence);
1293 foreach ($modules as $moduleid) {
02f66c42 1294 set_coursemodule_visible($moduleid, $visibility, true);
7d99d695 1295 }
1296 }
5867bfb5 1297 rebuild_course_cache($courseid);
ebaa29d1
ARN
1298
1299 // Determine which modules are visible for AJAX update
1300 if (!empty($modules)) {
1301 list($insql, $params) = $DB->get_in_or_equal($modules);
1302 $select = 'id ' . $insql . ' AND visible = ?';
1303 array_push($params, $visibility);
1304 if (!$visibility) {
1305 $select .= ' AND visibleold = 1';
1306 }
1307 $resourcestotoggle = $DB->get_fieldset_select('course_modules', 'id', $select, $params);
1308 }
7d99d695 1309 }
ebaa29d1 1310 return $resourcestotoggle;
7d99d695 1311}
ba2e5d73 1312
0d8b6a69 1313/**
1314 * Obtains shared data that is used in print_section when displaying a
1315 * course-module entry.
1316 *
1317 * Calls format_text or format_string as appropriate, and obtains the correct icon.
1318 *
1319 * This data is also used in other areas of the code.
1320 * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
1321 * @param object $course Moodle course object
1322 * @return array An array with the following values in this order:
1323 * $content (optional extra content for after link),
1324 * $instancename (text of link)
1325 */
1326function get_print_section_cm_text(cm_info $cm, $course) {
1327 global $OUTPUT;
1328
0d8b6a69 1329 // Get content from modinfo if specified. Content displays either
1330 // in addition to the standard link (below), or replaces it if
1331 // the link is turned off by setting ->url to null.
1332 if (($content = $cm->get_content()) !== '') {
371fbe1c 1333 // Improve filter performance by preloading filter setttings for all
1334 // activities on the course (this does nothing if called multiple
1335 // times)
1336 filter_preload_activities($cm->get_modinfo());
1337
1338 // Get module context
9a5e297b 1339 $modulecontext = context_module::instance($cm->id);
0d8b6a69 1340 $labelformatoptions = new stdClass();
1341 $labelformatoptions->noclean = true;
1342 $labelformatoptions->overflowdiv = true;
371fbe1c 1343 $labelformatoptions->context = $modulecontext;
0d8b6a69 1344 $content = format_text($content, FORMAT_HTML, $labelformatoptions);
1345 } else {
1346 $content = '';
1347 }
1348
371fbe1c 1349 // Get course context
9a5e297b 1350 $coursecontext = context_course::instance($course->id);
0d8b6a69 1351 $stringoptions = new stdClass;
1352 $stringoptions->context = $coursecontext;
1353 $instancename = format_string($cm->name, true, $stringoptions);
1354 return array($content, $instancename);
1355}
1356
cb6fec1f 1357/**
1358 * Prints a section full of activity modules
5946d376
FM
1359 *
1360 * @param stdClass $course The course
1361 * @param stdClass $section The section
1362 * @param array $mods The modules in the section
1363 * @param array $modnamesused An array containing the list of modules and their names
1364 * @param bool $absolute All links are absolute
1365 * @param string $width Width of the container
1366 * @param bool $hidecompletion Hide completion status
b8514b6a 1367 * @param int $sectionreturn The section to return to
5946d376 1368 * @return void
cb6fec1f 1369 */
923451c5 1370function print_section($course, $section, $mods, $modnamesused, $absolute=false, $width="100%", $hidecompletion=false, $sectionreturn=null) {
e63f88c9 1371 global $CFG, $USER, $DB, $PAGE, $OUTPUT;
7977cffd 1372
dd97c328 1373 static $initialised;
1374
3d575e6f 1375 static $groupbuttons;
32d03b7b 1376 static $groupbuttonslink;
52dcc2f9 1377 static $isediting;
7977cffd 1378 static $ismoving;
1379 static $strmovehere;
1380 static $strmovefull;
54669989 1381 static $strunreadpostsone;
0d8b6a69 1382 static $modulenames;
110a32e2 1383
dd97c328 1384 if (!isset($initialised)) {
9fd9c29b 1385 $groupbuttons = ($course->groupmode or (!$course->groupmodeforce));
32d03b7b 1386 $groupbuttonslink = (!$course->groupmodeforce);
830dd6e9 1387 $isediting = $PAGE->user_is_editing();
dd97c328 1388 $ismoving = $isediting && ismoving($course->id);
3d575e6f 1389 if ($ismoving) {
dd97c328 1390 $strmovehere = get_string("movehere");
1391 $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
3d575e6f 1392 }
0d8b6a69 1393 $modulenames = array();
dd97c328 1394 $initialised = true;
7977cffd 1395 }
dd97c328 1396
dd97c328 1397 $modinfo = get_fast_modinfo($course);
984baa77 1398 $completioninfo = new completion_info($course);
94361e02 1399
7e85563d 1400 //Accessibility: replace table with list <ul>, but don't output empty list.
74666583 1401 if (!empty($section->sequence)) {
94361e02 1402
f2d660dc 1403 // Fix bug #5027, don't want style=\"width:$width\".
6285f8a8 1404 echo "<ul class=\"section img-text\">\n";
94361e02 1405 $sectionmods = explode(",", $section->sequence);
1406
1407 foreach ($sectionmods as $modnumber) {
9ae687af 1408 if (empty($mods[$modnumber])) {
1409 continue;
1410 }
dd97c328 1411
0d8b6a69 1412 /**
1413 * @var cm_info
1414 */
94361e02 1415 $mod = $mods[$modnumber];
c9f6251e 1416
dd97c328 1417 if ($ismoving and $mod->id == $USER->activitycopy) {
1418 // do not display moving mod
1419 continue;
1420 }
c9f6251e 1421
dd97c328 1422 if (isset($modinfo->cms[$modnumber])) {
85f55ba2 1423 // We can continue (because it will not be displayed at all)
1424 // if:
1425 // 1) The activity is not visible to users
1426 // and
1427 // 2a) The 'showavailability' option is not set (if that is set,
2f48819b 1428 // we need to display the activity so we can show
85f55ba2 1429 // availability info)
1430 // or
2f48819b 1431 // 2b) The 'availableinfo' is empty, i.e. the activity was
1432 // hidden in a way that leaves no info, such as using the
85f55ba2 1433 // eye icon.
82bd6a5e 1434 if (!$modinfo->cms[$modnumber]->uservisible &&
85f55ba2 1435 (empty($modinfo->cms[$modnumber]->showavailability) ||
1436 empty($modinfo->cms[$modnumber]->availableinfo))) {
dd97c328 1437 // visibility shortcut
1438 continue;
86aa7ccf 1439 }
dd97c328 1440 } else {
0f56c9da 1441 if (!file_exists("$CFG->dirroot/mod/$mod->modname/lib.php")) {
1442 // module not installed
1443 continue;
1444 }
82bd6a5e 1445 if (!coursemodule_visible_for_user($mod) &&
1446 empty($mod->showavailability)) {
dd97c328 1447 // full visibility check
1448 continue;
9d361034 1449 }
dd97c328 1450 }
1451
0d8b6a69 1452 if (!isset($modulenames[$mod->modname])) {
1453 $modulenames[$mod->modname] = get_string('modulename', $mod->modname);
1454 }
1455 $modulename = $modulenames[$mod->modname];
1456
2f48819b 1457 // In some cases the activity is visible to user, but it is
e8e50986 1458 // dimmed. This is done if viewhiddenactivities is true and if:
1459 // 1. the activity is not visible, or
1460 // 2. the activity has dates set which do not include current, or
1461 // 3. the activity has any other conditions set (regardless of whether
1462 // current user meets them)
110f73ad 1463 $modcontext = context_module::instance($mod->id);
1464 $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
e8e50986 1465 $accessiblebutdim = false;
1466 if ($canviewhidden) {
1467 $accessiblebutdim = !$mod->visible;
1468 if (!empty($CFG->enableavailability)) {
1469 $accessiblebutdim = $accessiblebutdim ||
1470 $mod->availablefrom > time() ||
1471 ($mod->availableuntil && $mod->availableuntil < time()) ||
2f48819b 1472 count($mod->conditionsgrade) > 0 ||
e8e50986 1473 count($mod->conditionscompletion) > 0;
1474 }
1475 }
1476
060cd0c8
SH
1477 $liclasses = array();
1478 $liclasses[] = 'activity';
1479 $liclasses[] = $mod->modname;
1480 $liclasses[] = 'modtype_'.$mod->modname;
0d8b6a69 1481 $extraclasses = $mod->get_extra_classes();
1482 if ($extraclasses) {
1483 $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
1484 }
060cd0c8 1485 echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
dd97c328 1486 if ($ismoving) {
1487 echo '<a title="'.$strmovefull.'"'.
d4a1fcaf 1488 ' href="'.$CFG->wwwroot.'/course/mod.php?moveto='.$mod->id.'&amp;sesskey='.sesskey().'">'.
b5d0cafc 1489 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
dd97c328 1490 ' alt="'.$strmovehere.'" /></a><br />
1491 ';
1492 }
9d361034 1493
060cd0c8
SH
1494 $classes = array('mod-indent');
1495 if (!empty($mod->indent)) {
1496 $classes[] = 'mod-indent-'.$mod->indent;
1497 if ($mod->indent > 15) {
1498 $classes[] = 'mod-indent-huge';
1499 }
dd97c328 1500 }
060cd0c8 1501 echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
dd97c328 1502
0d8b6a69 1503 // Get data about this course-module
1504 list($content, $instancename) =
1505 get_print_section_cm_text($modinfo->cms[$modnumber], $course);
1506
1507 //Accessibility: for files get description via icon, this is very ugly hack!
1508 $altname = '';
1509 $altname = $mod->modfullname;
0d8b6a69 1510 // Avoid unnecessary duplication: if e.g. a forum name already
1511 // includes the word forum (or Forum, etc) then it is unhelpful
1512 // to include that in the accessible description that is added.
f8311def
PS
1513 if (false !== strpos(textlib::strtolower($instancename),
1514 textlib::strtolower($altname))) {
0d8b6a69 1515 $altname = '';
1516 }
1517 // File type after name, for alphabetic lists (screen reader).
1518 if ($altname) {
1519 $altname = get_accesshide(' '.$altname);
554606c7 1520 }
dd97c328 1521
0d8b6a69 1522 // We may be displaying this just in order to show information
1523 // about visibility, without the actual link
1524 $contentpart = '';
1525 if ($mod->uservisible) {
1526 // Nope - in this case the link is fully working for user
1527 $linkclasses = '';
1528 $textclasses = '';
1529 if ($accessiblebutdim) {
1530 $linkclasses .= ' dimmed';
1531 $textclasses .= ' dimmed_text';
1532 $accesstext = '<span class="accesshide">'.
1533 get_string('hiddenfromstudents').': </span>';
722b4ce1 1534 } else {
0d8b6a69 1535 $accesstext = '';
dd97c328 1536 }
0d8b6a69 1537 if ($linkclasses) {
1538 $linkcss = 'class="' . trim($linkclasses) . '" ';
1539 } else {
1540 $linkcss = '';
1541 }
1542 if ($textclasses) {
1543 $textcss = 'class="' . trim($textclasses) . '" ';
1544 } else {
1545 $textcss = '';
c4d989a1 1546 }
aac94fd0 1547
0d8b6a69 1548 // Get on-click attribute value if specified
1549 $onclick = $mod->get_on_click();
1550 if ($onclick) {
1551 $onclick = ' onclick="' . $onclick . '"';
1552 }
c9f6251e 1553
0d8b6a69 1554 if ($url = $mod->get_url()) {
1555 // Display link itself
1556 echo '<a ' . $linkcss . $mod->extra . $onclick .
1557 ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
ddb16bdd 1558 '" class="activityicon" alt="' . $modulename . '" /> ' .
0d8b6a69 1559 $accesstext . '<span class="instancename">' .
1560 $instancename . $altname . '</span></a>';
1561
1562 // If specified, display extra content after link
1563 if ($content) {
ce2a7a91 1564 $contentpart = '<div class="' . trim('contentafterlink' . $textclasses) .
1565 '">' . $content . '</div>';
c93dae38 1566 }
dd97c328 1567 } else {
0d8b6a69 1568 // No link, so display only content
1569 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1570 $accesstext . $content . '</div>';
dd97c328 1571 }
e0b033d5 1572
9a5e297b 1573 if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
ae3fbf7b 1574 $groupings = groups_get_all_groupings($course->id);
0d8b6a69 1575 echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
dd97c328 1576 }
0d8b6a69 1577 } else {
1578 $textclasses = $extraclasses;
1579 $textclasses .= ' dimmed_text';
1580 if ($textclasses) {
1581 $textcss = 'class="' . trim($textclasses) . '" ';
1582 } else {
1583 $textcss = '';
dd97c328 1584 }
0d8b6a69 1585 $accesstext = '<span class="accesshide">' .
1586 get_string('notavailableyet', 'condition') .
1587 ': </span>';
6285f8a8 1588
0d8b6a69 1589 if ($url = $mod->get_url()) {
82bd6a5e 1590 // Display greyed-out text of link
0d8b6a69 1591 echo '<div ' . $textcss . $mod->extra .
1592 ' >' . '<img src="' . $mod->get_icon_url() .
aad982aa 1593 '" class="activityicon" alt="" /> <span>'. $instancename . $altname .
0d8b6a69 1594 '</span></div>';
1595
1596 // Do not display content after link when it is greyed out like this.
1597 } else {
1598 // No link, so display only content (also greyed)
1599 $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1600 $accesstext . $content . '</div>';
f37da850 1601 }
dd97c328 1602 }
f37da850 1603
0d8b6a69 1604 // Module can put text after the link (e.g. forum unread)
1605 echo $mod->get_after_link();
1606
611684a8 1607 // If there is content but NO link (eg label), then display the
1608 // content here (BEFORE any icons). In this case cons must be
1609 // displayed after the content so that it makes more sense visually
1610 // and for accessibility reasons, e.g. if you have a one-line label
1611 // it should work similarly (at least in terms of ordering) to an
1612 // activity.
1613 if (empty($url)) {
1614 echo $contentpart;
1615 }
1616
dd97c328 1617 if ($isediting) {
525e16ce 1618 if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
dd97c328 1619 if (! $mod->groupmodelink = $groupbuttonslink) {
1620 $mod->groupmode = $course->groupmode;
3d575e6f 1621 }
dd97c328 1622
1623 } else {
1624 $mod->groupmode = false;
c9f6251e 1625 }
dd97c328 1626 echo '&nbsp;&nbsp;';
b8514b6a 1627 echo make_editing_buttons($mod, $absolute, true, $mod->indent, $sectionreturn);
0d8b6a69 1628 echo $mod->get_after_edit_icons();
94361e02 1629 }
4e781c7b 1630
1631 // Completion
3982f0f8 1632 $completion = $hidecompletion
4e781c7b 1633 ? COMPLETION_TRACKING_NONE
1634 : $completioninfo->is_enabled($mod);
2f48819b 1635 if ($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
82bd6a5e 1636 !isguestuser() && $mod->uservisible) {
3982f0f8 1637 $completiondata = $completioninfo->get_data($mod,true);
1638 $completionicon = '';
1639 if ($isediting) {
1640 switch ($completion) {
ca255392 1641 case COMPLETION_TRACKING_MANUAL :
3982f0f8 1642 $completionicon = 'manual-enabled'; break;
ca255392 1643 case COMPLETION_TRACKING_AUTOMATIC :
3982f0f8 1644 $completionicon = 'auto-enabled'; break;
4e781c7b 1645 default: // wtf
1646 }
3982f0f8 1647 } else if ($completion==COMPLETION_TRACKING_MANUAL) {
4e781c7b 1648 switch($completiondata->completionstate) {
1649 case COMPLETION_INCOMPLETE:
3982f0f8 1650 $completionicon = 'manual-n'; break;
4e781c7b 1651 case COMPLETION_COMPLETE:
3982f0f8 1652 $completionicon = 'manual-y'; break;
4e781c7b 1653 }
1654 } else { // Automatic
1655 switch($completiondata->completionstate) {
1656 case COMPLETION_INCOMPLETE:
3982f0f8 1657 $completionicon = 'auto-n'; break;
4e781c7b 1658 case COMPLETION_COMPLETE:
3982f0f8 1659 $completionicon = 'auto-y'; break;
4e781c7b 1660 case COMPLETION_COMPLETE_PASS:
3982f0f8 1661 $completionicon = 'auto-pass'; break;
4e781c7b 1662 case COMPLETION_COMPLETE_FAIL:
3982f0f8 1663 $completionicon = 'auto-fail'; break;
4e781c7b 1664 }
1665 }
3982f0f8 1666 if ($completionicon) {
b5d0cafc 1667 $imgsrc = $OUTPUT->pix_url('i/completion-'.$completionicon);
110f73ad 1668 $formattedname = format_string($mod->name, true, array('context' => $modcontext));
1669 $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname);
76c0123b 1670 if ($completion == COMPLETION_TRACKING_MANUAL && !$isediting) {
110f73ad 1671 $imgtitle = get_string('completion-title-' . $completionicon, 'completion', $formattedname);
3982f0f8 1672 $newstate =
4e781c7b 1673 $completiondata->completionstate==COMPLETION_COMPLETE
ca255392 1674 ? COMPLETION_INCOMPLETE
1675 : COMPLETION_COMPLETE;
a343b13f 1676 // In manual mode the icon is a toggle form...
1677
1678 // If this completion state is used by the
1679 // conditional activities system, we need to turn
1680 // off the JS.
2f48819b 1681 if (!empty($CFG->enableavailability) &&
76c0123b 1682 condition_info::completion_value_used_as_condition($course, $mod)) {
a343b13f 1683 $extraclass = ' preventjs';
1684 } else {
1685 $extraclass = '';
1686 }
4e781c7b 1687 echo "
d5842f7a 1688<form class='togglecompletion$extraclass' method='post' action='".$CFG->wwwroot."/course/togglecompletion.php'><div>
4e781c7b 1689<input type='hidden' name='id' value='{$mod->id}' />
9e4baaf9 1690<input type='hidden' name='modulename' value='".s($mod->name)."' />
8c194133 1691<input type='hidden' name='sesskey' value='".sesskey()."' />
4e781c7b 1692<input type='hidden' name='completionstate' value='$newstate' />
1693<input type='image' src='$imgsrc' alt='$imgalt' title='$imgtitle' />
1694</div></form>";
1695 } else {
1696 // In auto mode, or when editing, the icon is just an image
f17a0360 1697 echo "<span class='autocompletion'>";
f17a0360 1698 echo "<img src='$imgsrc' alt='$imgalt' title='$imgalt' /></span>";
4e781c7b 1699 }
1700 }
1701 }
1702
611684a8 1703 // If there is content AND a link, then display the content here
1704 // (AFTER any icons). Otherwise it was displayed before
1705 if (!empty($url)) {
1706 echo $contentpart;
1707 }
0d8b6a69 1708
2f48819b 1709 // Show availability information (for someone who isn't allowed to
82bd6a5e 1710 // see the activity itself, or for staff)
3982f0f8 1711 if (!$mod->uservisible) {
82bd6a5e 1712 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
540f89d7 1713 } else if ($canviewhidden && !empty($CFG->enableavailability) && $mod->visible) {
82bd6a5e 1714 $ci = new condition_info($mod);
3982f0f8 1715 $fullinfo = $ci->get_full_information();
82bd6a5e 1716 if($fullinfo) {
2f48819b 1717 echo '<div class="availabilityinfo">'.get_string($mod->showavailability
82bd6a5e 1718 ? 'userrestriction_visible'
1719 : 'userrestriction_hidden','condition',
1720 $fullinfo).'</div>';
1721 }
1722 }
1723
060cd0c8
SH
1724 echo html_writer::end_tag('div');
1725 echo html_writer::end_tag('li')."\n";
94361e02 1726 }
dd97c328 1727
f2d660dc 1728 } elseif ($ismoving) {
1729 echo "<ul class=\"section\">\n";
264867fd 1730 }
dd97c328 1731
7977cffd 1732 if ($ismoving) {
64fdc686 1733 echo '<li><a title="'.$strmovefull.'"'.
d4a1fcaf 1734 ' href="'.$CFG->wwwroot.'/course/mod.php?movetosection='.$section->id.'&amp;sesskey='.sesskey().'">'.
b5d0cafc 1735 '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
c6a55371 1736 ' alt="'.$strmovehere.'" /></a></li>
1c919752 1737 ';
7977cffd 1738 }
c6a55371 1739 if (!empty($section->sequence) || $ismoving) {
1740 echo "</ul><!--class='section'-->\n\n";
1741 }
a7ad3ea6 1742}
1743
89bfeee0 1744/**
1745 * Prints the menus to add activities and resources.
5946d376
FM
1746 *
1747 * @param stdClass $course The course
923451c5 1748 * @param int $section relative section number (field course_sections.section)
5946d376
FM
1749 * @param array $modnames An array containing the list of modules and their names
1750 * @param bool $vertical Vertical orientation
1751 * @param bool $return Return the menus or send them to output
b8514b6a 1752 * @param int $sectionreturn The section to link back to
5946d376 1753 * @return void|string depending on $return
89bfeee0 1754 */
923451c5 1755function print_section_add_menus($course, $section, $modnames, $vertical=false, $return=false, $sectionreturn=null) {
64e12bb7 1756 global $CFG, $OUTPUT;
e0161bff 1757
217a8ee9 1758 // check to see if user can add menus
9a5e297b 1759 if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))) {
e2cd3ed0 1760 return false;
217a8ee9 1761 }
1762
59155a3f 1763 // Retrieve all modules with associated metadata
923451c5 1764 $modules = get_module_metadata($course, $modnames, $sectionreturn);
31dadc6f 1765
59155a3f 1766 // We'll sort resources and activities into two lists
017ab27c
DP
1767 $resources = array();
1768 $activities = array();
6da4b261 1769
59155a3f 1770 // We need to add the section section to the link for each module
b8514b6a 1771 $sectionlink = '&section=' . $section . '&sr=' . $sectionreturn;
a41b1d96 1772
59155a3f
ARN
1773 foreach ($modules as $module) {
1774 if (isset($module->types)) {
1775 // This module has a subtype
017ab27c 1776 // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
59155a3f
ARN
1777 $subtypes = array();
1778 foreach ($module->types as $subtype) {
b8514b6a 1779 $subtypes[$subtype->link . $sectionlink] = $subtype->title;
59155a3f
ARN
1780 }
1781
1782 // Sort module subtypes into the list
1783 if (!empty($module->title)) {
1784 // This grouping has a name
1785 if ($module->archetype == MOD_CLASS_RESOURCE) {
1786 $resources[] = array($module->title=>$subtypes);
1787 } else {
1788 $activities[] = array($module->title=>$subtypes);
89bfeee0 1789 }
59155a3f
ARN
1790 } else {
1791 // This grouping does not have a name
1792 if ($module->archetype == MOD_CLASS_RESOURCE) {
1793 $resources = array_merge($resources, $subtypes);
aa54ed7b 1794 } else {
59155a3f 1795 $activities = array_merge($activities, $subtypes);
aa54ed7b 1796 }
89bfeee0 1797 }
017ab27c 1798 } else {
59155a3f
ARN
1799 // This module has no subtypes
1800 if ($module->archetype == MOD_ARCHETYPE_RESOURCE) {
b8514b6a 1801 $resources[$module->link . $sectionlink] = $module->title;
59155a3f 1802 } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) {
5813c7f7 1803 // System modules cannot be added by user, do not add to dropdown
017ab27c 1804 } else {
b8514b6a 1805 $activities[$module->link . $sectionlink] = $module->title;
017ab27c 1806 }
0705ff84 1807 }
e0161bff 1808 }
1809
89bfeee0 1810 $straddactivity = get_string('addactivity');
1811 $straddresource = get_string('addresource');
1812
01e0e704 1813 $output = html_writer::start_tag('div', array('class' => 'section_add_menus', 'id' => 'add_menus-section-' . $section));
4f24b3e3 1814
1815 if (!$vertical) {
01e0e704 1816 $output .= html_writer::start_tag('div', array('class' => 'horizontal'));
4f24b3e3 1817 }
82fcab32 1818
89bfeee0 1819 if (!empty($resources)) {
31dadc6f 1820 $select = new url_select($resources, '', array(''=>$straddresource), "ressection$section");
d2265f13 1821 $select->set_help_icon('resources');
31dadc6f 1822 $output .= $OUTPUT->render($select);
0705ff84 1823 }
cb57e6f4 1824
89bfeee0 1825 if (!empty($activities)) {
31dadc6f 1826 $select = new url_select($activities, '', array(''=>$straddactivity), "section$section");
d2265f13 1827 $select->set_help_icon('activities');
31dadc6f 1828 $output .= $OUTPUT->render($select);
0705ff84 1829 }
1830
4f24b3e3 1831 if (!$vertical) {
01e0e704 1832 $output .= html_writer::end_tag('div');
d33d0cda 1833 }
1834
01e0e704
ARN
1835 $output .= html_writer::end_tag('div');
1836
1837 if (course_ajax_enabled($course)) {
1838 $straddeither = get_string('addresourceoractivity');
1839 // The module chooser link
46e046b7
DP
1840 $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
1841 $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
1842 $icon = $OUTPUT->pix_icon('t/add', $straddeither);
1843 $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
928cc791 1844 $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
46e046b7
DP
1845 $modchooser.= html_writer::end_tag('div');
1846 $modchooser.= html_writer::end_tag('div');
01e0e704
ARN
1847
1848 // Wrap the normal output in a noscript div
945a81a9 1849 $usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault);
01e0e704
ARN
1850 if ($usemodchooser) {
1851 $output = html_writer::tag('div', $output, array('class' => 'hiddenifjs addresourcedropdown'));
1852 $modchooser = html_writer::tag('div', $modchooser, array('class' => 'visibleifjs addresourcemodchooser'));
1853 } else {
1854 $output = html_writer::tag('div', $output, array('class' => 'visibleifjs addresourcedropdown'));
1855 $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hiddenifjs addresourcemodchooser'));
1856 }
1857 $output = $modchooser . $output;
1858 }
cb57e6f4 1859
1860 if ($return) {
1861 return $output;
1862 } else {
1863 echo $output;
1864 }
e0161bff 1865}
1866
59155a3f
ARN
1867/**
1868 * Retrieve all metadata for the requested modules
1869 *
1870 * @param object $course The Course
1871 * @param array $modnames An array containing the list of modules and their
1872 * names
a41b1d96 1873 * @param int $sectionreturn The section to return to
59155a3f
ARN
1874 * @return array A list of stdClass objects containing metadata about each
1875 * module
1876 */
923451c5 1877function get_module_metadata($course, $modnames, $sectionreturn = null) {
59155a3f
ARN
1878 global $CFG, $OUTPUT;
1879
1880 // get_module_metadata will be called once per section on the page and courses may show
1881 // different modules to one another
1882 static $modlist = array();
1883 if (!isset($modlist[$course->id])) {
1884 $modlist[$course->id] = array();
1885 }
1886
1887 $return = array();
a41b1d96 1888 $urlbase = "/course/mod.php?id=$course->id&sesskey=".sesskey().'&sr='.$sectionreturn.'&add=';
59155a3f
ARN
1889 foreach($modnames as $modname => $modnamestr) {
1890 if (!course_allowed_module($course, $modname)) {
1891 continue;
1892 }
1893 if (isset($modlist[$modname])) {
1894 // This module is already cached
1895 $return[$modname] = $modlist[$course->id][$modname];
1896 continue;
1897 }
1898
1899 // Include the module lib
1900 $libfile = "$CFG->dirroot/mod/$modname/lib.php";
1901 if (!file_exists($libfile)) {
1902 continue;
1903 }
1904 include_once($libfile);
1905
1906 // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
1907 $gettypesfunc = $modname.'_get_types';
1908 if (function_exists($gettypesfunc)) {
1909 if ($types = $gettypesfunc()) {
1910 $group = new stdClass();
1911 $group->name = $modname;
1912 $group->icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
1913 foreach($types as $type) {
1914 if ($type->typestr === '--') {
1915 continue;
1916 }
1917 if (strpos($type->typestr, '--') === 0) {
1918 $group->title = str_replace('--', '', $type->typestr);
1919 continue;
1920 }
1921 // Set the Sub Type metadata
1922 $subtype = new stdClass();
1923 $subtype->title = $type->typestr;
1924 $subtype->type = str_replace('&amp;', '&', $type->type);
1925 $subtype->name = preg_replace('/.*type=/', '', $subtype->type);
1926 $subtype->archetype = $type->modclass;
1927
1928 // The group archetype should match the subtype archetypes and all subtypes
1929 // should have the same archetype
1930 $group->archetype = $subtype->archetype;
1931
1932 if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
1933 $subtype->help = get_string('help' . $subtype->name, $modname);
1934 }
772685e9 1935 $subtype->link = $urlbase . $subtype->type;
59155a3f
ARN
1936 $group->types[] = $subtype;
1937 }
1938 $modlist[$course->id][$modname] = $group;
1939 }
1940 } else {
1941 $module = new stdClass();
1942 $module->title = get_string('modulename', $modname);
1943 $module->name = $modname;
1944 $module->link = $urlbase . $modname;
1945 $module->icon = $OUTPUT->pix_icon('icon', '', $module->name, array('class' => 'icon'));
01e0e704
ARN
1946 $sm = get_string_manager();
1947 if ($sm->string_exists('modulename_help', $modname)) {
59155a3f 1948 $module->help = get_string('modulename_help', $modname);
01e0e704
ARN
1949 if ($sm->string_exists('modulename_link', $modname)) { // Link to further info in Moodle docs
1950 $link = get_string('modulename_link', $modname);
1951 $linktext = get_string('morehelp');
1952 $module->help .= html_writer::tag('div', $OUTPUT->doc_link($link, $linktext), array('class' => 'helpdoclink'));
1953 }
59155a3f
ARN
1954 }
1955 $module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
1956 $modlist[$course->id][$modname] = $module;
1957 }
1958 $return[$modname] = $modlist[$course->id][$modname];
1959 }
1960
1961 return $return;
1962}
1963
8ed5dd63 1964/**
1965 * Return the course category context for the category with id $categoryid, except
1966 * that if $categoryid is 0, return the system context.
1967 *
1968 * @param integer $categoryid a category id or 0.
1969 * @return object the corresponding context
1970 */
1971function get_category_or_system_context($categoryid) {
1972 if ($categoryid) {
4658aec5 1973 return context_coursecat::instance($categoryid, IGNORE_MISSING);
8ed5dd63 1974 } else {
9a5e297b 1975 return context_system::instance();
8ed5dd63 1976 }
1977}
1978
cb6fec1f 1979/**
7e85563d 1980 * Gets the child categories of a given courses category. Uses a static cache
8ed5dd63 1981 * to make repeat calls efficient.
1982 *
e92c39ca 1983 * @param int $parentid the id of a course category.
8ed5dd63 1984 * @return array all the child course categories.
cb6fec1f 1985 */
8ed5dd63 1986function get_child_categories($parentid) {
9bb19e58 1987 static $allcategories = null;
1988
1989 // only fill in this variable the first time
1990 if (null == $allcategories) {
1991 $allcategories = array();
1992
1993 $categories = get_categories();
1994 foreach ($categories as $category) {
1995 if (empty($allcategories[$category->parent])) {
1996 $allcategories[$category->parent] = array();
1997 }
1998 $allcategories[$category->parent][] = $category;
1999 }
2000 }
2001
8ed5dd63 2002 if (empty($allcategories[$parentid])) {
9bb19e58 2003 return array();
2004 } else {
8ed5dd63 2005 return $allcategories[$parentid];
9bb19e58 2006 }
2007}
2008
cb6fec1f 2009/**
8ed5dd63 2010 * This function recursively travels the categories, building up a nice list
2011 * for display. It also makes an array that list all the parents for each
2012 * category.
2013 *
2014 * For example, if you have a tree of categories like:
2015 * Miscellaneous (id = 1)
2016 * Subcategory (id = 2)
2017 * Sub-subcategory (id = 4)
2018 * Other category (id = 3)
2019 * Then after calling this function you will have
2020 * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
2021 * 4 => 'Miscellaneous / Subcategory / Sub-subcategory',
2022 * 3 => 'Other category');
2023 * $parents = array(2 => array(1), 4 => array(1, 2));
2024 *
2025 * If you specify $requiredcapability, then only categories where the current
2026 * user has that capability will be added to $list, although all categories
2027 * will still be added to $parents, and if you only have $requiredcapability
2028 * in a child category, not the parent, then the child catgegory will still be
2029 * included.
2030 *
2031 * If you specify the option $excluded, then that category, and all its children,
2032 * are omitted from the tree. This is useful when you are doing something like
2033 * moving categories, where you do not want to allow people to move a category
2034 * to be the child of itself.
2035 *
2036 * @param array $list For output, accumulates an array categoryid => full category path name
2037 * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
8a1b1c32 2038 * @param string/array $requiredcapability if given, only categories where the current
2039 * user has this capability will be added to $list. Can also be an array of capabilities,
2040 * in which case they are all required.
8ed5dd63 2041 * @param integer $excludeid Omit this category and its children from the lists built.
2042 * @param object $category Build the tree starting at this category - otherwise starts at the top level.
2043 * @param string $path For internal use, as part of recursive calls.
cb6fec1f 2044 */
8ed5dd63 2045function make_categories_list(&$list, &$parents, $requiredcapability = '',
2046 $excludeid = 0, $category = NULL, $path = "") {
2047
9d866ae0 2048 // initialize the arrays if needed
2049 if (!is_array($list)) {
264867fd 2050 $list = array();
9d866ae0 2051 }
2052 if (!is_array($parents)) {
264867fd 2053 $parents = array();
9d866ae0 2054 }
2055
8ed5dd63 2056 if (empty($category)) {
2057 // Start at the top level.
2058 $category = new stdClass;
2059 $category->id = 0;
2060 } else {
2061 // This is the excluded category, don't include it.
2062 if ($excludeid > 0 && $excludeid == $category->id) {
2063 return;
2064 }
2065
9a5e297b 2066 $context = context_coursecat::instance($category->id);
63390481
SH
2067 $categoryname = format_string($category->name, true, array('context' => $context));
2068
8ed5dd63 2069 // Update $path.
c2cb4545 2070 if ($path) {
63390481 2071 $path = $path.' / '.$categoryname;
c2cb4545 2072 } else {
63390481 2073 $path = $categoryname;
c2cb4545 2074 }
8ed5dd63 2075
2076 // Add this category to $list, if the permissions check out.
3ce50127 2077 if (empty($requiredcapability)) {
8ed5dd63 2078 $list[$category->id] = $path;
3ce50127 2079
2080 } else {
3ce50127 2081 $requiredcapability = (array)$requiredcapability;
63390481 2082 if (has_all_capabilities($requiredcapability, $context)) {
3ce50127 2083 $list[$category->id] = $path;
2084 }
8ed5dd63 2085 }
c2cb4545 2086 }
2087
8ed5dd63 2088 // Add all the children recursively, while updating the parents array.
2089 if ($categories = get_child_categories($category->id)) {
c2cb4545 2090 foreach ($categories as $cat) {
2091 if (!empty($category->id)) {
3bd4de22 2092 if (isset($parents[$category->id])) {
2832badf 2093 $parents[$cat->id] = $parents[$category->id];
2094 }
c2cb4545 2095 $parents[$cat->id][] = $category->id;
2096 }
8ed5dd63 2097 make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
c2cb4545 2098 }
2099 }
2100}
2101
24e27ac0
SH
2102/**
2103 * This function generates a structured array of courses and categories.
2104 *
2105 * The depth of categories is limited by $CFG->maxcategorydepth however there
2106 * is no limit on the number of courses!
2107 *
2108 * Suitable for use with the course renderers course_category_tree method:
2109 * $renderer = $PAGE->get_renderer('core','course');
2110 * echo $renderer->course_category_tree(get_course_category_tree());
2111 *
2112 * @global moodle_database $DB
2113 * @param int $id
2114 * @param int $depth
2115 */
2116function get_course_category_tree($id = 0, $depth = 0) {
cf41dc37 2117 global $DB, $CFG;
9a5e297b 2118 $viewhiddencats = has_capability('moodle/category:viewhiddencategories', context_system::instance());
24e27ac0
SH
2119 $categories = get_child_categories($id);
2120 $categoryids = array();
2121 foreach ($categories as $key => &$category) {
2122 if (!$category->visible && !$viewhiddencats) {
2123 unset($categories[$key]);
2124 continue;
2125 }
2126 $categoryids[$category->id] = $category;
2127 if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
2128 list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
3ebc548f
SH
2129 foreach ($subcategories as $subid=>$subcat) {
2130 $categoryids[$subid] = $subcat;
2131 }
24e27ac0
SH
2132 $category->courses = array();
2133 }
2134 }
2135
2136 if ($depth > 0) {
2137 // This is a recursive call so return the required array
2138 return array($categories, $categoryids);
2139 }
2140
7f88c426
DP
2141 if (empty($categoryids)) {
2142 // No categories available (probably all hidden).
2143 return array();
2144 }
2145
24e27ac0
SH
2146 // The depth is 0 this function has just been called so we can finish it off
2147
2148 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
2149 list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
2150 $sql = "SELECT
df997f84 2151 c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
24e27ac0
SH
2152 $ccselect
2153 FROM {course} c
2154 $ccjoin
2155 WHERE c.category $catsql ORDER BY c.sortorder ASC";
2156 if ($courses = $DB->get_records_sql($sql, $catparams)) {
2157 // loop throught them
2158 foreach ($courses as $course) {
2159 if ($course->id == SITEID) {
2160 continue;
2161 }
2162 context_instance_preload($course);
9a5e297b 2163 if (!empty($course->visible) || has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
24e27ac0
SH
2164 $categoryids[$course->category]->courses[$course->id] = $course;
2165 }
2166 }
2167 }
2168 return $categories;
2169}
c2cb4545 2170
cb6fec1f 2171/**
2172 * Recursive function to print out all the categories in a nice format
2173 * with or without courses included
2174 */
8ed5dd63 2175function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) {
9ff5310a 2176 global $CFG;
e05bcf2f 2177
beeee4d2 2178 // maxcategorydepth == 0 meant no limit
2179 if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
e05bcf2f 2180 return;
9ff5310a 2181 }
c2cb4545 2182
2183 if (!$displaylist) {
e92fe848 2184 make_categories_list($displaylist, $parentslist);
c2cb4545 2185 }
2186
2187 if ($category) {
9a5e297b 2188 if ($category->visible or has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
8ed5dd63 2189 print_category_info($category, $depth, $showcourses);
c2cb4545 2190 } else {
2191 return; // Don't bother printing children of invisible categories
2192 }
89adb174 2193
c2cb4545 2194 } else {
b85b25eb 2195 $category = new stdClass();
c2cb4545 2196 $category->id = "0";
2197 }
2198
9bb19e58 2199 if ($categories = get_child_categories($category->id)) { // Print all the children recursively
c2cb4545 2200 $countcats = count($categories);
2201 $count = 0;
2202 $first = true;
2203 $last = false;
2204 foreach ($categories as $cat) {
2205 $count++;
2206 if ($count == $countcats) {
2207 $last = true;
2208 }
2209 $up = $first ? false : true;
2210 $down = $last ? false : true;
2211 $first = false;
2212
8ed5dd63 2213 print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses);
c2cb4545 2214 }
2215 }
c2cb4545 2216}
2217
cb6fec1f 2218/**
af90698b 2219 * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
cb6fec1f 2220 */
0705ff84 2221function make_categories_options() {
2222 make_categories_list($cats,$parents);
2223 foreach ($cats as $key => $value) {
2224 if (array_key_exists($key,$parents)) {
2225 if ($indent = count($parents[$key])) {
2226 for ($i = 0; $i < $indent; $i++) {
2227 $cats[$key] = '&nbsp;'.$cats[$key];
2228 }
2229 }
2230 }
2231 }
2232 return $cats;
2233}
c2cb4545 2234
cb6fec1f 2235/**
2236 * Prints the category info in indented fashion
2237 * This function is only used by print_whole_category_list() above
2238 */
f3e5bf86 2239function print_category_info($category, $depth=0, $showcourses = false) {
6b608f8f 2240 global $CFG, $DB, $OUTPUT;
c2cb4545 2241
df997f84 2242 $strsummary = get_string('summary');
ba2e5d73 2243
ea831ceb
RW
2244 $catlinkcss = null;
2245 if (!$category->visible) {
2246 $catlinkcss = array('class'=>'dimmed');
2247 }
dc247e52 2248 static $coursecount = null;
2249 if (null === $coursecount) {
2250 // only need to check this once
2251 $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT;
2252 }
2253
8ed5dd63 2254 if ($showcourses and $coursecount) {
b5d0cafc 2255 $catimage = '<img src="'.$OUTPUT->pix_url('i/course') . '" alt="" />';
b48f834c 2256 } else {
7b0b5c14 2257 $catimage = "&nbsp;";
8ef9cb56 2258 }
2afcfc44 2259
df997f84 2260 $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
9a5e297b 2261 $context = context_coursecat::instance($category->id);
63390481
SH
2262 $fullname = format_string($category->name, true, array('context' => $context));
2263
8ed5dd63 2264 if ($showcourses and $coursecount) {
2a63b636 2265 echo '<div class="categorylist clearfix">';
2afcfc44 2266 $cat = '';
2a63b636 2267 $cat .= html_writer::tag('div', $catimage, array('class'=>'image'));
63390481 2268 $catlink = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
2afcfc44
RW
2269 $cat .= html_writer::tag('div', $catlink, array('class'=>'name'));
2270
ea831ceb 2271 $html = '';
2afcfc44
RW
2272 if ($depth > 0) {
2273 for ($i=0; $i< $depth; $i++) {
ea831ceb 2274 $html = html_writer::tag('div', $html . $cat, array('class'=>'indentation'));
2afcfc44 2275 $cat = '';
cb184beb 2276 }
2afcfc44 2277 } else {
ea831ceb 2278 $html = $cat;
2afcfc44 2279 }
2a63b636 2280 echo html_writer::tag('div', $html, array('class'=>'category'));
2afcfc44 2281 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
b48f834c 2282
beeee4d2 2283 // does the depth exceed maxcategorydepth
2afcfc44 2284 // maxcategorydepth == 0 or unset meant no limit
beeee4d2 2285 $limit = !(isset($CFG->maxcategorydepth) && ($depth >= $CFG->maxcategorydepth-1));
beeee4d2 2286 if ($courses && ($limit || $CFG->maxcategorydepth == 0)) {
c2cb4545 2287 foreach ($courses as $course) {
ea831ceb
RW
2288 $linkcss = null;
2289 if (!$course->visible) {
2290 $linkcss = array('class'=>'dimmed');
2291 }
2a63b636 2292
7fb46992 2293 $coursename = get_course_display_name_for_list($course);
2294 $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($coursename), $linkcss);
2afcfc44 2295
bf423bb1 2296 // print enrol info
f3e5bf86 2297 $courseicon = '';
bf423bb1
PS
2298 if ($icons = enrol_get_course_info_icons($course)) {
2299 foreach ($icons as $pix_icon) {
f3e5bf86 2300 $courseicon = $OUTPUT->render($pix_icon).' ';
bf423bb1
PS
2301 }
2302 }
2303
f3e5bf86
SH
2304 $coursecontent = html_writer::tag('div', $courseicon.$courselink, array('class'=>'name'));
2305
b48f834c 2306 if ($course->summary) {
75015e5f 2307 $link = new moodle_url('/course/info.php?id='.$course->id);
2afcfc44 2308 $actionlink = $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
75015e5f
PS
2309 new popup_action('click', $link, 'courseinfo', array('height' => 400, 'width' => 500)),
2310 array('title'=>$strsummary));
2afcfc44
RW
2311
2312 $coursecontent .= html_writer::tag('div', $actionlink, array('class'=>'info'));
2313 }
2314
ea831ceb 2315 $html = '';
f3e5bf86 2316 for ($i=0; $i <= $depth; $i++) {
ea831ceb 2317 $html = html_writer::tag('div', $html . $coursecontent , array('class'=>'indentation'));
2afcfc44 2318 $coursecontent = '';
0c656181 2319 }
ea831ceb 2320 echo html_writer::tag('div', $html, array('class'=>'course clearfloat'));
2afcfc44 2321 }
ba2e5d73 2322 }
2afcfc44
RW
2323 echo '</div>';
2324 } else {
2a63b636 2325 echo '<div class="categorylist">';
ea831ceb 2326 $html = '';
63390481 2327 $cat = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
eea78ebc
DP
2328 if (count($courses) > 0) {
2329 $cat .= html_writer::tag('span', ' ('.count($courses).')', array('title'=>get_string('numberofcourses'), 'class'=>'numberofcourse'));
2330 }
2a63b636 2331
ea831ceb
RW
2332 if ($depth > 0) {
2333 for ($i=0; $i< $depth; $i++) {
2334 $html = html_writer::tag('div', $html .$cat, array('class'=>'indentation'));
2335 $cat = '';
2336 }
2337 } else {
2338 $html = $cat;
2339 }
2a63b636
PS
2340
2341 echo html_writer::tag('div', $html, array('class'=>'category'));
ea831ceb 2342 echo html_writer::tag('div', '', array('class'=>'clearfloat'));
cb184beb 2343 echo '</div>';
2afcfc44 2344 }
c2cb4545 2345}
2346
77eddcd5 2347/**
2348 * Print the buttons relating to course requests.
2349 *
2350 * @param object $systemcontext the system context.
2351 */
2352function print_course_request_buttons($systemcontext) {
b4531207 2353 global $CFG, $DB, $OUTPUT;
77eddcd5 2354 if (empty($CFG->enablecourserequests)) {
2355 return;
2356 }
4f0c2d00 2357 if (!has_capability('moodle/course:create', $systemcontext) && has_capability('moodle/course:request', $systemcontext)) {
77eddcd5 2358 /// Print a button to request a new course
5c2ed7e2 2359 echo $OUTPUT->single_button('request.php', get_string('requestcourse'), 'get');
77eddcd5 2360 }
2361 /// Print a button to manage pending requests
2362 if (has_capability('moodle/site:approvecourse', $systemcontext)) {
5c2ed7e2
PS
2363 $disabled = !$DB->record_exists('course_request', array());
2364 echo $OUTPUT->single_button('pending.php', get_string('coursespending'), 'get', array('disabled'=>$disabled));
77eddcd5 2365 }
2366}
2367
5048e034 2368/**
2369 * Does the user have permission to edit things in this category?
2370 *
2371 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2372 * @return boolean has_any_capability(array(...), ...); in the appropriate context.
2373 */
2374function can_edit_in_category($categoryid = 0) {
2375 $context = get_category_or_system_context($categoryid);
2376 return has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context);
2377}
2378
8ed5dd63 2379/**
2380 * Prints the turn editing on/off button on course/index.php or course/category.php.
2381 *
2382 * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2383 * @return string HTML of the editing button, or empty string, if this user is not allowed
2384 * to see it.
2385 */
2386function update_category_button($categoryid = 0) {
b4531207 2387 global $CFG, $PAGE, $OUTPUT;
8ed5dd63 2388
2389 // Check permissions.
5048e034 2390 if (!can_edit_in_category($categoryid)) {
8ed5dd63 2391 return '';
2392 }
2393
2394 // Work out the appropriate action.
830dd6e9 2395 if ($PAGE->user_is_editing()) {
8ed5dd63 2396 $label = get_string('turneditingoff');
2397 $edit = 'off';
2398 } else {
2399 $label = get_string('turneditingon');
2400 $edit = 'on';
2401 }
c2cb4545 2402
8ed5dd63 2403 // Generate the button HTML.
2404 $options = array('categoryedit' => $edit, 'sesskey' => sesskey());
2405 if ($categoryid) {
2406 $options['id'] = $categoryid;
2407 $page = 'category.php';
2408 } else {
2409 $page = 'index.php';
2410 }
a6855934 2411 return $OUTPUT->single_button(new moodle_url('/course/' . $page, $options), $label, 'get');
8ed5dd63 2412}
e0b033d5 2413
cb6fec1f 2414/**
2415 * Category is 0 (for all courses) or an object
2416 */
6c54240a 2417function print_courses($category) {
2f48819b 2418 global $CFG, $OUTPUT;
c2cb4545 2419
4dde1463 2420 if (!is_object($category) && $category==0) {
9bb19e58 2421 $categories = get_child_categories(0); // Parent = 0 ie top-level categories only
4dde1463 2422 if (is_array($categories) && count($categories) == 1) {
90c2ca2e 2423 $category = array_shift($categories);
238c0dd9 2424 $courses = get_courses_wmanagers($category->id,
2425 'c.sortorder ASC',
df997f84 2426 array('summary','summaryformat'));
90c2ca2e 2427 } else {
238c0dd9 2428 $courses = get_courses_wmanagers('all',
2429 'c.sortorder ASC',
df997f84 2430 array('summary','summaryformat'));
90c2ca2e 2431 }
2432 unset($categories);
607809b3 2433 } else {
238c0dd9 2434 $courses = get_courses_wmanagers($category->id,
2435 'c.sortorder ASC',
df997f84 2436 array('summary','summaryformat'));
c2cb4545 2437 }
2438
49cd4d79 2439 if ($courses) {
002fc5ba 2440 echo html_writer::start_tag('ul', array('class'=>'unlist'));
c2cb4545 2441 foreach ($courses as $course) {
9a5e297b 2442 $coursecontext = context_course::instance($course->id);
4f0c2d00 2443 if ($course->visible == 1 || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
002fc5ba 2444 echo html_writer::start_tag('li');
4dde1463 2445 print_course($course);
002fc5ba 2446 echo html_writer::end_tag('li');
4dde1463 2447 }
c2cb4545 2448 }
002fc5ba 2449 echo html_writer::end_tag('ul');
c2cb4545 2450 } else {
7c5286cd 2451 echo $OUTPUT->heading(get_string("nocoursesyet"));
9a5e297b 2452 $context = context_system::instance();
0468976c 2453 if (has_capability('moodle/course:create', $context)) {
255d1033 2454 $options = array();
4868e95f
DM
2455 if (!empty($category->id)) {
2456 $options['category'] = $category->id;
2457 } else {
2458 $options['category'] = $CFG->defaultrequestcategory;
2459 }
002fc5ba 2460 echo html_writer::start_tag('div', array('class'=>'addcoursebutton'));
a6855934 2461 echo $OUTPUT->single_button(new moodle_url('/course/edit.php', $options), get_string("addnewcourse"));
002fc5ba 2462 echo html_writer::end_tag('div');
255d1033 2463 }
c2cb4545 2464 }
c2cb4545 2465}
2466
04c53106 2467/**
2468 * Print a description of a course, suitable for browsing in a list.
2469 *
2470 * @param object $course the course object.
2471 * @param string $highlightterms (optional) some search terms that should be highlighted in the display.
2472 */
2473function print_course($course, $highlightterms = '') {
666e8458 2474 global $CFG, $USER, $DB, $OUTPUT;
c2cb4545 2475
9a5e297b 2476 $context = context_course::instance($course->id);
146bbb8f 2477
8bdc9cac 2478 // Rewrite file URLs so that they are correct
64f93798 2479 $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
8bdc9cac 2480
002fc5ba
SH
2481 echo html_writer::start_tag('div', array('class'=>'coursebox clearfix'));
2482 echo html_writer::start_tag('div', array('class'=>'info'));
2483 echo html_writer::start_tag('h3', array('class'=>'name'));
22288704 2484
002fc5ba 2485 $linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
7fb46992 2486
2487 $coursename = get_course_display_name_for_list($course);
2488 $linktext = highlight($highlightterms, format_string($coursename));
002fc5ba
SH
2489 $linkparams = array('title'=>get_string('entercourse'));
2490 if (empty($course->visible)) {
2491 $linkparams['class'] = 'dimmed';
2492 }
2493 echo html_writer::link($linkhref, $linktext, $linkparams);
2494 echo html_writer::end_tag('h3');
238c0dd9 2495
d42c64ba 2496 /// first find all roles that are supposed to be displayed
df997f84 2497 if (!empty($CFG->coursecontact)) {
c71f3265 2498 $managerroles = explode(',', $CFG->coursecontact);
e52a8ebd 2499 $rusers = array();
9d5a4b23 2500
e52a8ebd 2501 if (!isset($course->managers)) {
9695ff81 2502 list($sort, $sortparams) = users_order_by_sql('u');
e52a8ebd 2503 $rusers = get_role_users($managerroles, $context, true,
c52551dc
PS
2504 'ra.id AS raid, u.id, u.username, u.firstname, u.lastname, rn.name AS rolecoursealias,
2505 r.name AS rolename, r.sortorder, r.id AS roleid, r.shortname AS roleshortname',
9695ff81 2506 'r.sortorder ASC, ' . $sort, null, '', '', '', '', $sortparams);
e52a8ebd
DP
2507 } else {
2508 // use the managers array if we have it for perf reasosn
2509 // populate the datastructure like output of get_role_users();
2510 foreach ($course->managers as $manager) {
c52551dc
PS
2511 $user = clone($manager->user);
2512 $user->roleid = $manager->roleid;
2513 $user->rolename = $manager->rolename;
2514 $user->roleshortname = $manager->roleshortname;
2515 $user->rolecoursealias = $manager->rolecoursealias;
2516 $rusers[$user->id] = $user;
4dde1463 2517 }
e52a8ebd 2518 }
165d25cc 2519
e52a8ebd
DP
2520 $namesarray = array();
2521 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2522 foreach ($rusers as $ra) {
2523 if (isset($namesarray[$ra->id])) {
2524 // only display a user once with the higest sortorder role
2525 continue;
2526 }
165d25cc 2527
c52551dc
PS
2528 $role = new stdClass();
2529 $role->id = $ra->roleid;
2530 $role->name = $ra->rolename;
2531 $role->shortname = $ra->roleshortname;
2532 $role->coursealias = $ra->rolecoursealias;
2533 $rolename = role_get_name($role, $context, ROLENAME_ALIAS);
e52a8ebd
DP
2534
2535 $fullname = fullname($ra, $canviewfullnames);
c52551dc 2536 $namesarray[$ra->id] = $rolename.': '.
e52a8ebd 2537 html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->id, 'course'=>SITEID)), $fullname);
c2cb4545 2538 }
431cad0d 2539
d42c64ba 2540 if (!empty($namesarray)) {
002fc5ba
SH
2541 echo html_writer::start_tag('ul', array('class'=>'teachers'));
2542 foreach ($namesarray as $name) {
2543 echo html_writer::tag('li', $name);
2544 }
2545 echo html_writer::end_tag('ul');
88768091 2546 }
c2cb4545 2547 }
002fc5ba 2548 echo html_writer::end_tag('div'); // End of info div
238c0dd9 2549
002fc5ba 2550 echo html_writer::start_tag('div', array('class'=>'summary'));
b85b25eb 2551 $options = new stdClass();
9f39c190 2552 $options->noclean = true;
34b5847a 2553 $options->para = false;
367a75fa 2554 $options->overflowdiv = true;
8bdc9cac
SH
2555 if (!isset($course->summaryformat)) {
2556 $course->summaryformat = FORMAT_MOODLE;
2557 }
2558 echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options, $course->id));
e877160d 2559 if ($icons = enrol_get_course_info_icons($course)) {
002fc5ba
SH
2560 echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
2561 foreach ($icons as $icon) {
2562 echo $OUTPUT->render($icon);
2563 }
2564 echo html_writer::end_tag('div'); // End of enrolmenticons div
2565 }
2566 echo html_writer::end_tag('div'); // End of summary div
2567 echo html_writer::end_tag('div'); // End of coursebox div
c2cb4545 2568}
2569
cb6fec1f 2570/**
2571 * Prints custom user information on the home page.
2572 * Over time this can include all sorts of information
2573 */
c2cb4545 2574function print_my_moodle() {
e6db3026 2575 global $USER, $CFG, $DB, $OUTPUT;
c2cb4545 2576
4f0c2d00 2577 if (!isloggedin() or isguestuser()) {
ba6018a9 2578 print_error('nopermissions', '', '', 'See My Moodle');
c2cb4545 2579 }
2580
df997f84 2581 $courses = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
0a127169 2582 $rhosts = array();
2583 $rcourses = array();
2584 if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2585 $rcourses = get_my_remotecourses($USER->id);
2586 $rhosts = get_my_remotehosts();
2587 }
2588
2589 if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2590
2591 if (!empty($courses)) {
2592 echo '<ul class="unlist">';
2593 foreach ($courses as $course) {
2594 if ($course->id == SITEID) {
2595 continue;
2596 }
2597 echo '<li>';
2598 print_course($course);
2599 echo "</li>\n";
86dd62a7 2600 }
0a127169 2601 echo "</ul>\n";
86dd62a7 2602 }
2603
0a127169 2604 // MNET
2605 if (!empty($rcourses)) {
2606 // at the IDP, we know of all the remote courses
2607 foreach ($rcourses as $course) {
2608 print_remote_course($course, "100%");
2609 }
2610 } elseif (!empty($rhosts)) {
2611 // non-IDP, we know of all the remote servers, but not courses
2612 foreach ($rhosts as $host) {
2613 print_remote_host($host, "100%");
2614 }
2615 }
86dd62a7 2616 unset($course);
0a127169 2617 unset($host);
38a10939 2618
cb6fec1f 2619 if ($DB->count_records("course") > (count($courses) + 1) ) { // Some courses not being displayed
7f989948 2620 echo "<table width=\"100%\"><tr><td align=\"center\">";
2621 print_course_search("", false, "short");
2622 echo "</td><td align=\"center\">";
5c2ed7e2 2623 echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
7f989948 2624 echo "</td></tr></table>\n";
2625 }
86dd62a7 2626
26330001 2627 } else {
cb6fec1f 2628 if ($DB->count_records("course_categories") > 1) {
e6db3026 2629 echo $OUTPUT->box_start("categorybox");
26330001 2630 print_whole_category_list();
e6db3026 2631 echo $OUTPUT->box_end();
26330001 2632 } else {
35d0244a 2633 print_courses(0);
26330001 2634 }
607809b3 2635 }
2b8cef80 2636}
2637
11b0c469 2638
a8b56716 2639function print_course_search($value="", $return=false, $format="plain") {
38a10939 2640 global $CFG;
1e0fb105 2641 static $count = 0;
2642
2643 $count++;
2644
2645 $id = 'coursesearch';
2646
2647 if ($count > 1) {
2648 $id .= $count;
2649 }
38a10939 2650
2651 $strsearchcourses= get_string("searchcourses");
2652
1c919752 2653 if ($format == 'plain') {
1e0fb105 2654 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2655 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
e42f4d92 2656 $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2657 $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
e42f4d92 2658 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2659 $output .= '</fieldset></form>';
1c919752 2660 } else if ($format == 'short') {
1e0fb105 2661 $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
fcf9577a 2662 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2663 $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2664 $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2665 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2666 $output .= '</fieldset></form>';
1c919752 2667 } else if ($format == 'navbar') {
fcf9577a 2668 $output = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2669 $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
b1f97418 2670 $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
cb6fec1f 2671 $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
e42f4d92 2672 $output .= '<input type="submit" value="'.get_string('go').'" />';
fcf9577a 2673 $output .= '</fieldset></form>';
a8b56716 2674 }
2675
2676 if ($return) {
2677 return $output;
2678 }
2679 echo $output;
38a10939 2680}
11b0c469 2681
86dd62a7 2682function print_remote_course($course, $width="100%") {
86dd62a7 2683 global $CFG, $USER;
2684
2685 $linkcss = '';
2686
2687 $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}";
2688
7cd266e9 2689 echo '<div class="coursebox remotecoursebox clearfix">';
86dd62a7 2690 echo '<div class="info">';
2691 echo '<div class="name"><a title="'.get_string('entercourse').'"'.
2692 $linkcss.' href="'.$url.'">'
6ba65fa0 2693 . format_string($course->fullname) .'</a><br />'
2694 . format_string($course->hostname) . ' : '
238c0dd9 2695 . format_string($course->cat_name) . ' : '
2696 . format_string($course->shortname). '</div>';
86dd62a7 2697 echo '</div><div class="summary">';
b85b25eb 2698 $options = new stdClass();
86dd62a7 2699 $options->noclean = true;
2700 $options->para = false;
367a75fa 2701 $options->overflowdiv = true;
152a2273 2702 echo format_text($course->summary, $course->summaryformat, $options);
86dd62a7 2703 echo '</div>';
2704 echo '</div>';
86dd62a7 2705}
2706
643b67b8 2707function print_remote_host($host, $width="100%") {
6b608f8f 2708 global $OUTPUT;
643b67b8 2709
2710 $linkcss = '';
2711
7cd266e9 2712 echo '<div class="coursebox clearfix">';
643b67b8 2713 echo '<div class="info">';
2714 echo '<div class="name">';
b5d0cafc 2715 echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
643b67b8 2716 echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
2717 . s($host['name']).'</a> - ';
1fd80ad3 2718 echo $host['count'] . ' ' . get_string('courses');
643b67b8 2719 echo '</div>';
2720 echo '</div>';
caa90d56 2721 echo '</div>';
643b67b8 2722}
2723
86dd62a7 2724
11b0c469 2725/// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
2726
2727function add_course_module($mod) {
cb6fec1f 2728 global $DB;
11b0c469 2729
e5dfd0f3 2730 $mod->added = time();
53f4ad2c 2731 unset($mod->id);
11b0c469 2732
cb6fec1f 2733 return $DB->insert_record("course_modules", $mod);
11b0c469 2734}
2735
97928ddf 2736/**
99e9f9a6
MG
2737 * Returns course section - creates new if does not exist yet
2738 *
2739 * @param int $section relative section number (field course_sections.section)
97928ddf 2740 * @param int $courseid
99e9f9a6 2741 * @return stdClass record from table {course_sections}
97928ddf 2742 */
2743function get_course_section($section, $courseid) {
cb6fec1f 2744 global $DB;
2745
2746 if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
97928ddf 2747 return $cw;
2748 }
fbaea88f 2749 $cw = new stdClass();
cb6fec1f 2750 $cw->course = $courseid;
2751 $cw->section = $section;
2752 $cw->summary = "";
09eb2151 2753 $cw->summaryformat = FORMAT_HTML;
97928ddf 2754 $cw->sequence = "";
cb6fec1f 2755 $id = $DB->insert_record("course_sections", $cw);
52f14061 2756 rebuild_course_cache($courseid, true);
dc5af91a 2757 return $DB->get_record("course_sections", array("id"=>$id));
97928ddf 2758}
52f14061 2759
ece966f0 2760/**
722e6ba9 2761 * Adds an existing module to the section
ece966f0 2762 *
722e6ba9
MG
2763 * Updates both tables {course_sections} and {course_modules}
2764 *
2765 * @param int|stdClass $courseorid course id or course object
2766 * @param int $modid id of the module already existing in course_modules table
2767 * @param int $sectionnum relative number of the section (field course_sections.section)
2768 * If section does not exist it will be created
2769 * @param int|stdClass $beforemod id or object with field id corresponding to the module
2770 * before which the module needs to be included. Null for inserting in the
2771 * end of the section
2772 * @return int The course_sections ID where the module is inserted
ece966f0 2773 */
722e6ba9
MG
2774function course_add_cm_to_section($courseorid, $modid, $sectionnum, $beforemod = NULL) {
2775 global $DB, $COURSE;
2776 if (is_object($beforemod)) {
2777 $beforemod = $beforemod->id;
2778 }
2779 if (is_object($courseorid)) {
2780 $course = &$courseorid;
2781 } else {
2782 if (isset($COURSE->id) && $COURSE->id == $courseorid) {
2783 $course = &$COURSE;
7977cffd 2784 } else {
722e6ba9 2785 $course = $DB->get_record('course', array('id' => $courseorid), '*', MUST_EXIST);
11b0c469 2786 }
11b0c469 2787 }
722e6ba9
MG
2788 $section = get_course_section($sectionnum, $courseid);
2789 $modarray = explode(",", trim($section->sequence));
2790 if (empty($modarray)) {
2791 $newsequence = "$modid";
2792 } else if ($beforemod && ($key = array_keys($modarray, $beforemod))) {
2793 $insertarray = array($modid, $beforemod);
2794 array_splice($modarray, $key[0], 1, $insertarray);
2795 $newsequence = implode(",", $modarray);
2796 } else {
2797 $newsequence = "$section->sequence,$modid";
2798 }
2799 $DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
2800 $DB->set_field('course_modules', 'section', $section->id, array('id' => $modid));
2801 rebuild_course_cache($course->id, true);
2802 $course->modinfo = null;
2803 $course->sectioncache = null;
2804 return $section->id; // Return course_sections ID that was used.
11b0c469 2805}
2806
48e535bc 2807function set_coursemodule_groupmode($id, $groupmode) {
cb6fec1f 2808 global $DB;
a5d424df 2809 return $DB->set_field("course_modules", "groupmode", $groupmode, array("id"=>$id));
3d575e6f 2810}
2811
177d4abf 2812function set_coursemodule_idnumber($id, $idnumber) {
cb6fec1f 2813 global $DB;
238c0dd9 2814 return $DB->set_field("course_modules", "idnumber", $idnumber, array("id"=>$id));
177d4abf 2815}
4e781c7b 2816
02f66c42 2817/**
2818* $prevstateoverrides = true will set the visibility of the course module
2819* to what is defined in visibleold. This enables us to remember the current
2820* visibility when making a whole section hidden, so that when we toggle
2821* that section back to visible, we are able to return the visibility of
2822* the course module back to what it was originally.
2823*/
2824function set_coursemodule_visible($id, $visible, $prevstateoverrides=false) {
f5e2602a 2825 global $DB, $CFG;
0f078024
DM
2826 require_once($CFG->libdir.'/gradelib.php');
2827
cb6fec1f 2828 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
978abb42 2829 return false;
2830 }
cb6fec1f 2831 if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
978abb42 2832 return false;
2833 }
cb6fec1f 2834 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2835 foreach($events as $event) {
48e535bc 2836 if ($visible) {
2837 show_event($event);
2838 } else {
2839 hide_event($event);
2840 }
dcd338ff 2841 }
2842 }
f5e2602a 2843
0f078024
DM
2844 // hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there
2845 $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
2846 if ($grade_items) {
2847 foreach ($grade_items as $grade_item) {
2848 $grade_item->set_hidden(!$visible);
2849 }
f5e2602a 2850 }
f685e830 2851
02f66c42 2852 if ($prevstateoverrides) {
2853 if ($visible == '0') {
2854 // Remember the current visible state so we can toggle this back.
cb6fec1f 2855 $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id'=>$id));
02f66c42 2856 } else {
2857 // Get the previous saved visible states.
cb6fec1f 2858 return $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
02f66c42 2859 }
2860 }
cb6fec1f 2861 return $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
1acfbce5 2862}
2863
cb6fec1f 2864/**
290130b3 2865 * Delete a course module and any associated data at the course level (events)
264867fd 2866 * Until 1.5 this function simply marked a deleted flag ... now it
290130b3 2867 * deletes it completely.
2868 *
2869 */
48e535bc 2870function delete_course_module($id) {
cb6fec1f 2871 global $CFG, $DB;
f615fbab 2872 require_once($CFG->libdir.'/gradelib.php');
cae83708 2873 require_once($CFG->dirroot.'/blog/lib.php');
f615fbab 2874
cb6fec1f 2875 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
290130b3 2876 return true;
2877 }
cb6fec1f 2878 $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
f615fbab 2879 //delete events from calendar
cb6fec1f 2880 if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
dcd338ff 2881 foreach($events as $event) {
0ea03696 2882 delete_event($event->id);
dcd338ff 2883 }
2884 }
f615fbab 2885 //delete grade items, outcome items and grades attached to modules
2886 if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
2887 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
2888 foreach ($grade_items as $grade_item) {
2889 $grade_item->delete('moddelete');
2890 }
f615fbab 2891 }
46e12372
SM
2892 // Delete completion and availability data; it is better to do this even if the
2893 // features are not turned on, in case they were turned on previously (these will be
2894 // very quick on an empty table)
2895 $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
2896 $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
a8f84c28 2897 $DB->delete_records('course_modules_avail_fields', array('coursemoduleid' => $cm->id));
ede323e2
ARN
2898 $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id,
2899 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
06b3a6b2 2900
2901 delete_context(CONTEXT_MODULE, $cm->id);
bf66a674 2902 return $DB->delete_records('course_modules', array('id'=>$cm->id));
11b0c469 2903}
2904
722e6ba9 2905function delete_mod_from_section($modid, $sectionid) {
cb6fec1f 2906 global $DB;
11b0c469 2907
722e6ba9 2908 if ($section = $DB->get_record("course_sections", array("id"=>$sectionid)) ) {
11b0c469 2909
e5dfd0f3 2910 $modarray = explode(",", $section->sequence);
11b0c469 2911
722e6ba9 2912 if ($key = array_keys ($modarray, $modid)) {
11b0c469 2913 array_splice($modarray, $key[0], 1);
2914 $newsequence = implode(",", $modarray);
cb6fec1f 2915 return $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
11b0c469 2916 } else {
2917 return false;
2918 }
89adb174 2919
11b0c469 2920 }
7977cffd 2921 return false;
11b0c469 2922}
2923
3440ec12 2924/**
2925 * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
2926 *
93d46f48
RK
2927 * @param object $course course object
2928 * @param int $section Section number (not id!!!)
3440ec12 2929 * @param int $move (-1 or 1)
93d46f48 2930 * @return boolean true if section moved successfully
ac5f6414 2931 * @todo MDL-33379 remove this function in 2.5
3440ec12 2932 */
12905134 2933function move_section($course, $section, $move) {
ac5f6414
RT
2934 debugging('This function will be removed before 2.5 is released please use move_section_to', DEBUG_DEVELOPER);
2935
12905134 2936/// Moves a whole course section up and down within the course
cb6fec1f 2937 global $USER, $DB;
12905134 2938
2939 if (!$move) {
2940 return true;
2941 }
2942
2943 $sectiondest = $section + $move;
2944
2945 if ($sectiondest > $course->numsections or $sectiondest < 1) {
2946 return false;
2947 }
2948
eb01aa2c
RT
2949 $retval = move_section_to($course, $section, $sectiondest);
2950 // If section moved, then rebuild course cache.
2951 if ($retval) {
2952 rebuild_course_cache($course->id, true);
93d46f48 2953 }
eb01aa2c 2954 return $retval;
12905134 2955}
2956
3440ec12 2957/**
2958 * Moves a section within a course, from a position to another.
2959 * Be very careful: $section and $destination refer to section number,
2960 * not id!.
2961 *
2962 * @param object $course
2963 * @param int $section Section number (not id!!!)
2964 * @param int $destination
2965 * @return boolean Result
2966 */
2967function move_section_to($course, $section, $destination) {
2968/// Moves a whole course section up and down within the course
2969 global $USER, $DB;
2970
ca255392 2971 if (!$destination && $destination != 0) {
3440ec12 2972 return true;
2973 }
2974
ac5f6414 2975 if (($destination > $course->numsections) || ($destination < 1)) {
3440ec12 2976 return false;
2977 }
2978
2979 // Get all sections for this course and re-order them (2 of them should now share the same section number)
2980 if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
2981 'section ASC, id ASC', 'id, section')) {
2982 return false;
2983 }
2984
cf76b335 2985 $movedsections = reorder_sections($sections, $section, $destination);
3440ec12 2986
cf76b335 2987 // Update all sections. Do this in 2 steps to avoid breaking database
2988 // uniqueness constraint
2989 $transaction = $DB->start_delegated_transaction();
2990 foreach ($movedsections as $id => $position) {
2991 if ($sections[$id] !== $position) {
2992 $DB->set_field('course_sections', 'section', -$position, array('id' => $id));
2993 }
2994 }
e7e0f8d2
DP
2995 foreach ($movedsections as $id => $position) {
2996 if ($sections[$id] !== $position) {
2997 $DB->set_field('course_sections', 'section', $position, array('id' => $id));
2998 }
3440ec12 2999 }
3000
15e2552f
RK
3001 // If we move the highlighted section itself, then just highlight the destination.
3002 // Adjust the higlighted section location if we move something over it either direction.
3003 if ($section == $course->marker) {
e7b6e6b9 3004 course_set_marker($course->id, $destination);
2365213f 3005 } elseif ($section > $course->marker && $course->marker >= $destination) {
e7b6e6b9 3006 course_set_marker($course->id, $course->marker+1);
2365213f 3007 } elseif ($section < $course->marker && $course->marker <= $destination) {
e7b6e6b9 3008 course_set_marker($course->id, $course->marker-1);
15e2552f
RK
3009 }
3010
cf76b335 3011 $transaction->allow_commit();
3440ec12 3012 return true;
3013}
3014
3015/**
3016 * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
3017 * an original position number and a target position number, rebuilds the array so that the
3018 * move is made without any duplication of section positions.
3019 * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
3020 * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
3021 *
3022 * @param array $sections
3023 * @param int $origin_position
3024 * @param int $target_position
3025 * @return array
3026 */
3027function reorder_sections($sections, $origin_position, $target_position) {
3028 if (!is_array($sections)) {
3029 return false;
3030 }
3031
3032 // We can't move section position 0
3033 if ($origin_position < 1) {
3034 echo "We can't move section position 0";
3035 return false;
3036 }
3037
3038 // Locate origin section in sections array
3039 if (!$origin_key = array_search($origin_position, $sections)) {
3040 echo "searched position not in sections array";
3041 return false; // searched position not in sections array
3042 }
3043
3044 // Extract origin section
3045 $origin_section = $sections[$origin_key];
3046 unset($sections[$origin_key]);
3047
3048 // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
3049 $found = false;
3050 $append_array = array();
3051 foreach ($sections as $id => $position) {
3052 if ($found) {
3053 $append_array[$id] = $position;
3054 unset($sections[$id]);
3055 }
3056 if ($position == $target_position) {
eb01aa2c
RT
3057 if ($target_position < $origin_position) {
3058 $append_array[$id] = $position;
3059 unset($sections[$id]);
3060 }
3440ec12 3061 $found = true;
3062 }
3063 }
3064
3065 // Append moved section
3066 $sections[$origin_key] = $origin_section;
3067
3068 // Append rest of array (if applicable)
3069 if (!empty($append_array)) {
3070 foreach ($append_array as $id => $position) {
3071 $sections[$id] = $position;
3072 }
3073 }
3074
3075 // Renumber positions
3076 $position = 0;
3077 foreach ($sections as $id => $p) {
3078 $sections[$id] = $position;
3079 $position++;
3080 }
3081
3082 return $sections;
3083
3084}
3085
cb6fec1f 3086/**
3087 * Move the module object $mod to the specified $section
3088 * If $beforemod exists then that is the module
3089 * before which $modid should be inserted
3090 * All parameters are objects
3091 */
7977cffd 3092function moveto_module($mod, $section, $beforemod=NULL) {
e6db3026 3093 global $DB, $OUTPUT;
7977cffd 3094
3095/// Remove original module from original section
7977cffd 3096 if (! delete_mod_from_section($mod->id, $mod->section)) {
e6db3026 3097 echo $OUTPUT->notification("Could not delete module from existing section");
7977cffd 3098 }
3099
722e6ba9
MG
3100 // if moving to a hidden section then hide module
3101 if (!$section->visible && $mod->visible) {
3102 set_coursemodule_visible($mod->id, 0);
7977cffd 3103 }
3104
3105/// Add the module into the new section
722e6ba9 3106 course_add_cm_to_section($section->course, $mod->id, $section->section, $beforemod);
7977cffd 3107 return true;
7977cffd 3108}
3109
7749e187
SH
3110/**
3111 * Produces the editing buttons for a module
3112 *
3113 * @global core_renderer $OUTPUT
3114 * @staticvar type $str
3115 * @param stdClass $mod The module to produce editing buttons for
af189935 3116 * @param bool $absolute_ignored ignored - all links are absolute
7749e187
SH
3117 * @param bool $moveselect If true a move seleciton process is used (default true)
3118 * @param int $indent The current indenting
3119 * @param int $section The section to link back to
3120 * @return string XHTML for the editing buttons
3121 */
923451c5 3122function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $moveselect = true, $indent=-1, $section=null) {
7a9a07d2 3123 global $CFG, $OUTPUT, $COURSE;
94361e02 3124
3d575e6f 3125 static $str;
3126
9a5e297b
AA
3127 $coursecontext = context_course::instance($mod->course);
3128 $modcontext = context_module::instance($mod->id);
7749e187 3129
af189935
PS
3130 $editcaps = array('moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/role:assign');
3131 $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
3132