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