MDL-36967 Deprecate function print_overview()
[moodle.git] / course / lib.php
1 <?php
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/>.
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
23  * @package core
24  * @subpackage course
25  */
27 defined('MOODLE_INTERNAL') || die;
29 require_once($CFG->libdir.'/completionlib.php');
30 require_once($CFG->libdir.'/filelib.php');
31 require_once($CFG->dirroot.'/course/dnduploadlib.php');
32 require_once($CFG->dirroot.'/course/format/lib.php');
34 define('COURSE_MAX_LOGS_PER_PAGE', 1000);       // records
35 define('COURSE_MAX_RECENT_PERIOD', 172800);     // Two days, in seconds
37 /**
38  * Number of courses to display when summaries are included.
39  * @var int
40  * @deprecated since 2.4, use $CFG->courseswithsummarieslimit instead.
41  */
42 define('COURSE_MAX_SUMMARIES_PER_PAGE', 10);
44 define('COURSE_MAX_COURSES_PER_DROPDOWN',1000); //  max courses in log dropdown before switching to optional
45 define('COURSE_MAX_USERS_PER_DROPDOWN',1000);   //  max users in log dropdown before switching to optional
46 define('FRONTPAGENEWS',           '0');
47 define('FRONTPAGECOURSELIST',     '1');
48 define('FRONTPAGECATEGORYNAMES',  '2');
49 define('FRONTPAGETOPICONLY',      '3');
50 define('FRONTPAGECATEGORYCOMBO',  '4');
51 define('FRONTPAGECOURSELIMIT',    200);         // maximum number of courses displayed on the frontpage
52 define('EXCELROWS', 65535);
53 define('FIRSTUSEDEXCELROW', 3);
55 define('MOD_CLASS_ACTIVITY', 0);
56 define('MOD_CLASS_RESOURCE', 1);
58 function make_log_url($module, $url) {
59     switch ($module) {
60         case 'course':
61             if (strpos($url, 'report/') === 0) {
62                 // there is only one report type, course reports are deprecated
63                 $url = "/$url";
64                 break;
65             }
66         case 'file':
67         case 'login':
68         case 'lib':
69         case 'admin':
70         case 'calendar':
71         case 'mnet course':
72             if (strpos($url, '../') === 0) {
73                 $url = ltrim($url, '.');
74             } else {
75                 $url = "/course/$url";
76             }
77             break;
78         case 'user':
79         case 'blog':
80             $url = "/$module/$url";
81             break;
82         case 'upload':
83             $url = $url;
84             break;
85         case 'coursetags':
86             $url = '/'.$url;
87             break;
88         case 'library':
89         case '':
90             $url = '/';
91             break;
92         case 'message':
93             $url = "/message/$url";
94             break;
95         case 'notes':
96             $url = "/notes/$url";
97             break;
98         case 'tag':
99             $url = "/tag/$url";
100             break;
101         case 'role':
102             $url = '/'.$url;
103             break;
104         default:
105             $url = "/mod/$module/$url";
106             break;
107     }
109     //now let's sanitise urls - there might be some ugly nasties:-(
110     $parts = explode('?', $url);
111     $script = array_shift($parts);
112     if (strpos($script, 'http') === 0) {
113         $script = clean_param($script, PARAM_URL);
114     } else {
115         $script = clean_param($script, PARAM_PATH);
116     }
118     $query = '';
119     if ($parts) {
120         $query = implode('', $parts);
121         $query = str_replace('&amp;', '&', $query); // both & and &amp; are stored in db :-|
122         $parts = explode('&', $query);
123         $eq = urlencode('=');
124         foreach ($parts as $key=>$part) {
125             $part = urlencode(urldecode($part));
126             $part = str_replace($eq, '=', $part);
127             $parts[$key] = $part;
128         }
129         $query = '?'.implode('&amp;', $parts);
130     }
132     return $script.$query;
136 function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
137                    $modname="", $modid=0, $modaction="", $groupid=0) {
138     global $CFG, $DB;
140     // It is assumed that $date is the GMT time of midnight for that day,
141     // and so the next 86400 seconds worth of logs are printed.
143     /// Setup for group handling.
145     // TODO: I don't understand group/context/etc. enough to be able to do
146     // something interesting with it here
147     // What is the context of a remote course?
149     /// If the group mode is separate, and this user does not have editing privileges,
150     /// then only the user's group can be viewed.
151     //if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
152     //    $groupid = get_current_group($course->id);
153     //}
154     /// If this course doesn't have groups, no groupid can be specified.
155     //else if (!$course->groupmode) {
156     //    $groupid = 0;
157     //}
159     $groupid = 0;
161     $joins = array();
162     $where = '';
164     $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
165               FROM {mnet_log} l
166                LEFT JOIN {user} u ON l.userid = u.id
167               WHERE ";
168     $params = array();
170     $where .= "l.hostid = :hostid";
171     $params['hostid'] = $hostid;
173     // TODO: Is 1 really a magic number referring to the sitename?
174     if ($course != SITEID || $modid != 0) {
175         $where .= " AND l.course=:courseid";
176         $params['courseid'] = $course;
177     }
179     if ($modname) {
180         $where .= " AND l.module = :modname";
181         $params['modname'] = $modname;
182     }
184     if ('site_errors' === $modid) {
185         $where .= " AND ( l.action='error' OR l.action='infected' )";
186     } else if ($modid) {
187         //TODO: This assumes that modids are the same across sites... probably
188         //not true
189         $where .= " AND l.cmid = :modid";
190         $params['modid'] = $modid;
191     }
193     if ($modaction) {
194         $firstletter = substr($modaction, 0, 1);
195         if ($firstletter == '-') {
196             $where .= " AND ".$DB->sql_like('l.action', ':modaction', false, true, true);
197             $params['modaction'] = '%'.substr($modaction, 1).'%';
198         } else {
199             $where .= " AND ".$DB->sql_like('l.action', ':modaction', false);
200             $params['modaction'] = '%'.$modaction.'%';
201         }
202     }
204     if ($user) {
205         $where .= " AND l.userid = :user";
206         $params['user'] = $user;
207     }
209     if ($date) {
210         $enddate = $date + 86400;
211         $where .= " AND l.time > :date AND l.time < :enddate";
212         $params['date'] = $date;
213         $params['enddate'] = $enddate;
214     }
216     $result = array();
217     $result['totalcount'] = $DB->count_records_sql("SELECT COUNT('x') FROM {mnet_log} l WHERE $where", $params);
218     if(!empty($result['totalcount'])) {
219         $where .= " ORDER BY $order";
220         $result['logs'] = $DB->get_records_sql("$qry $where", $params, $limitfrom, $limitnum);
221     } else {
222         $result['logs'] = array();
223     }
224     return $result;
227 function build_logs_array($course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
228                    $modname="", $modid=0, $modaction="", $groupid=0) {
229     global $DB, $SESSION, $USER;
230     // It is assumed that $date is the GMT time of midnight for that day,
231     // and so the next 86400 seconds worth of logs are printed.
233     /// Setup for group handling.
235     /// If the group mode is separate, and this user does not have editing privileges,
236     /// then only the user's group can be viewed.
237     if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
238         if (isset($SESSION->currentgroup[$course->id])) {
239             $groupid =  $SESSION->currentgroup[$course->id];
240         } else {
241             $groupid = groups_get_all_groups($course->id, $USER->id);
242             if (is_array($groupid)) {
243                 $groupid = array_shift(array_keys($groupid));
244                 $SESSION->currentgroup[$course->id] = $groupid;
245             } else {
246                 $groupid = 0;
247             }
248         }
249     }
250     /// If this course doesn't have groups, no groupid can be specified.
251     else if (!$course->groupmode) {
252         $groupid = 0;
253     }
255     $joins = array();
256     $params = array();
258     if ($course->id != SITEID || $modid != 0) {
259         $joins[] = "l.course = :courseid";
260         $params['courseid'] = $course->id;
261     }
263     if ($modname) {
264         $joins[] = "l.module = :modname";
265         $params['modname'] = $modname;
266     }
268     if ('site_errors' === $modid) {
269         $joins[] = "( l.action='error' OR l.action='infected' )";
270     } else if ($modid) {
271         $joins[] = "l.cmid = :modid";
272         $params['modid'] = $modid;
273     }
275     if ($modaction) {
276         $firstletter = substr($modaction, 0, 1);
277         if ($firstletter == '-') {
278             $joins[] = $DB->sql_like('l.action', ':modaction', false, true, true);
279             $params['modaction'] = '%'.substr($modaction, 1).'%';
280         } else {
281             $joins[] = $DB->sql_like('l.action', ':modaction', false);
282             $params['modaction'] = '%'.$modaction.'%';
283         }
284     }
287     /// Getting all members of a group.
288     if ($groupid and !$user) {
289         if ($gusers = groups_get_members($groupid)) {
290             $gusers = array_keys($gusers);
291             $joins[] = 'l.userid IN (' . implode(',', $gusers) . ')';
292         } else {
293             $joins[] = 'l.userid = 0'; // No users in groups, so we want something that will always be false.
294         }
295     }
296     else if ($user) {
297         $joins[] = "l.userid = :userid";
298         $params['userid'] = $user;
299     }
301     if ($date) {
302         $enddate = $date + 86400;
303         $joins[] = "l.time > :date AND l.time < :enddate";
304         $params['date'] = $date;
305         $params['enddate'] = $enddate;
306     }
308     $selector = implode(' AND ', $joins);
310     $totalcount = 0;  // Initialise
311     $result = array();
312     $result['logs'] = get_logs($selector, $params, $order, $limitfrom, $limitnum, $totalcount);
313     $result['totalcount'] = $totalcount;
314     return $result;
318 function print_log($course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
319                    $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
321     global $CFG, $DB, $OUTPUT;
323     if (!$logs = build_logs_array($course, $user, $date, $order, $page*$perpage, $perpage,
324                        $modname, $modid, $modaction, $groupid)) {
325         echo $OUTPUT->notification("No logs found!");
326         echo $OUTPUT->footer();
327         exit;
328     }
330     $courses = array();
332     if ($course->id == SITEID) {
333         $courses[0] = '';
334         if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
335             foreach ($ccc as $cc) {
336                 $courses[$cc->id] = $cc->shortname;
337             }
338         }
339     } else {
340         $courses[$course->id] = $course->shortname;
341     }
343     $totalcount = $logs['totalcount'];
344     $count=0;
345     $ldcache = array();
346     $tt = getdate(time());
347     $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
349     $strftimedatetime = get_string("strftimedatetime");
351     echo "<div class=\"info\">\n";
352     print_string("displayingrecords", "", $totalcount);
353     echo "</div>\n";
355     echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
357     $table = new html_table();
358     $table->classes = array('logtable','generalbox');
359     $table->align = array('right', 'left', 'left');
360     $table->head = array(
361         get_string('time'),
362         get_string('ip_address'),
363         get_string('fullnameuser'),
364         get_string('action'),
365         get_string('info')
366     );
367     $table->data = array();
369     if ($course->id == SITEID) {
370         array_unshift($table->align, 'left');
371         array_unshift($table->head, get_string('course'));
372     }
374     // Make sure that the logs array is an array, even it is empty, to avoid warnings from the foreach.
375     if (empty($logs['logs'])) {
376         $logs['logs'] = array();
377     }
379     foreach ($logs['logs'] as $log) {
381         if (isset($ldcache[$log->module][$log->action])) {
382             $ld = $ldcache[$log->module][$log->action];
383         } else {
384             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
385             $ldcache[$log->module][$log->action] = $ld;
386         }
387         if ($ld && is_numeric($log->info)) {
388             // ugly hack to make sure fullname is shown correctly
389             if ($ld->mtable == 'user' && $ld->field == $DB->sql_concat('firstname', "' '" , 'lastname')) {
390                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
391             } else {
392                 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
393             }
394         }
396         //Filter log->info
397         $log->info = format_string($log->info);
399         // If $log->url has been trimmed short by the db size restriction
400         // code in add_to_log, keep a note so we don't add a link to a broken url
401         $brokenurl=(textlib::strlen($log->url)==100 && textlib::substr($log->url,97)=='...');
403         $row = array();
404         if ($course->id == SITEID) {
405             if (empty($log->course)) {
406                 $row[] = get_string('site');
407             } else {
408                 $row[] = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">". format_string($courses[$log->course])."</a>";
409             }
410         }
412         $row[] = userdate($log->time, '%a').' '.userdate($log->time, $strftimedatetime);
414         $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
415         $row[] = $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 440, 'width' => 700)));
417         $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))));
419         $displayaction="$log->module $log->action";
420         if ($brokenurl) {
421             $row[] = $displayaction;
422         } else {
423             $link = make_log_url($log->module,$log->url);
424             $row[] = $OUTPUT->action_link($link, $displayaction, new popup_action('click', $link, 'fromloglive'), array('height' => 440, 'width' => 700));
425         }
426         $row[] = $log->info;
427         $table->data[] = $row;
428     }
430     echo html_writer::table($table);
431     echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
435 function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
436                    $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
438     global $CFG, $DB, $OUTPUT;
440     if (!$logs = build_mnet_logs_array($hostid, $course, $user, $date, $order, $page*$perpage, $perpage,
441                        $modname, $modid, $modaction, $groupid)) {
442         echo $OUTPUT->notification("No logs found!");
443         echo $OUTPUT->footer();
444         exit;
445     }
447     if ($course->id == SITEID) {
448         $courses[0] = '';
449         if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname,c.visible')) {
450             foreach ($ccc as $cc) {
451                 $courses[$cc->id] = $cc->shortname;
452             }
453         }
454     }
456     $totalcount = $logs['totalcount'];
457     $count=0;
458     $ldcache = array();
459     $tt = getdate(time());
460     $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
462     $strftimedatetime = get_string("strftimedatetime");
464     echo "<div class=\"info\">\n";
465     print_string("displayingrecords", "", $totalcount);
466     echo "</div>\n";
468     echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
470     echo "<table class=\"logtable\" cellpadding=\"3\" cellspacing=\"0\">\n";
471     echo "<tr>";
472     if ($course->id == SITEID) {
473         echo "<th class=\"c0 header\">".get_string('course')."</th>\n";
474     }
475     echo "<th class=\"c1 header\">".get_string('time')."</th>\n";
476     echo "<th class=\"c2 header\">".get_string('ip_address')."</th>\n";
477     echo "<th class=\"c3 header\">".get_string('fullnameuser')."</th>\n";
478     echo "<th class=\"c4 header\">".get_string('action')."</th>\n";
479     echo "<th class=\"c5 header\">".get_string('info')."</th>\n";
480     echo "</tr>\n";
482     if (empty($logs['logs'])) {
483         echo "</table>\n";
484         return;
485     }
487     $row = 1;
488     foreach ($logs['logs'] as $log) {
490         $log->info = $log->coursename;
491         $row = ($row + 1) % 2;
493         if (isset($ldcache[$log->module][$log->action])) {
494             $ld = $ldcache[$log->module][$log->action];
495         } else {
496             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
497             $ldcache[$log->module][$log->action] = $ld;
498         }
499         if (0 && $ld && !empty($log->info)) {
500             // ugly hack to make sure fullname is shown correctly
501             if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
502                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
503             } else {
504                 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
505             }
506         }
508         //Filter log->info
509         $log->info = format_string($log->info);
511         echo '<tr class="r'.$row.'">';
512         if ($course->id == SITEID) {
513             $courseshortname = format_string($courses[$log->course], true, array('context' => context_course::instance(SITEID)));
514             echo "<td class=\"r$row c0\" >\n";
515             echo "    <a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">".$courseshortname."</a>\n";
516             echo "</td>\n";
517         }
518         echo "<td class=\"r$row c1\" align=\"right\">".userdate($log->time, '%a').
519              ' '.userdate($log->time, $strftimedatetime)."</td>\n";
520         echo "<td class=\"r$row c2\" >\n";
521         $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
522         echo $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 400, 'width' => 700)));
523         echo "</td>\n";
524         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', context_course::instance($course->id)));
525         echo "<td class=\"r$row c3\" >\n";
526         echo "    <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
527         echo "</td>\n";
528         echo "<td class=\"r$row c4\">\n";
529         echo $log->action .': '.$log->module;
530         echo "</td>\n";
531         echo "<td class=\"r$row c5\">{$log->info}</td>\n";
532         echo "</tr>\n";
533     }
534     echo "</table>\n";
536     echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
540 function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
541                         $modid, $modaction, $groupid) {
542     global $DB, $CFG;
544     require_once($CFG->libdir . '/csvlib.class.php');
546     $csvexporter = new csv_export_writer('tab');
548     $header = array();
549     $header[] = get_string('course');
550     $header[] = get_string('time');
551     $header[] = get_string('ip_address');
552     $header[] = get_string('fullnameuser');
553     $header[] = get_string('action');
554     $header[] = get_string('info');
556     if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
557                        $modname, $modid, $modaction, $groupid)) {
558         return false;
559     }
561     $courses = array();
563     if ($course->id == SITEID) {
564         $courses[0] = '';
565         if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
566             foreach ($ccc as $cc) {
567                 $courses[$cc->id] = $cc->shortname;
568             }
569         }
570     } else {
571         $courses[$course->id] = $course->shortname;
572     }
574     $count=0;
575     $ldcache = array();
576     $tt = getdate(time());
577     $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
579     $strftimedatetime = get_string("strftimedatetime");
581     $csvexporter->set_filename('logs', '.txt');
582     $title = array(get_string('savedat').userdate(time(), $strftimedatetime));
583     $csvexporter->add_data($title);
584     $csvexporter->add_data($header);
586     if (empty($logs['logs'])) {
587         return true;
588     }
590     foreach ($logs['logs'] as $log) {
591         if (isset($ldcache[$log->module][$log->action])) {
592             $ld = $ldcache[$log->module][$log->action];
593         } else {
594             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
595             $ldcache[$log->module][$log->action] = $ld;
596         }
597         if ($ld && is_numeric($log->info)) {
598             // ugly hack to make sure fullname is shown correctly
599             if (($ld->mtable == 'user') and ($ld->field ==  $DB->sql_concat('firstname', "' '" , 'lastname'))) {
600                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
601             } else {
602                 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
603             }
604         }
606         //Filter log->info
607         $log->info = format_string($log->info);
608         $log->info = strip_tags(urldecode($log->info));    // Some XSS protection
610         $coursecontext = context_course::instance($course->id);
611         $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
612         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
613         $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
614         $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action.' ('.$actionurl.')', $log->info);
615         $csvexporter->add_data($row);
616     }
617     $csvexporter->download_file();
618     return true;
622 function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
623                         $modid, $modaction, $groupid) {
625     global $CFG, $DB;
627     require_once("$CFG->libdir/excellib.class.php");
629     if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
630                        $modname, $modid, $modaction, $groupid)) {
631         return false;
632     }
634     $courses = array();
636     if ($course->id == SITEID) {
637         $courses[0] = '';
638         if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
639             foreach ($ccc as $cc) {
640                 $courses[$cc->id] = $cc->shortname;
641             }
642         }
643     } else {
644         $courses[$course->id] = $course->shortname;
645     }
647     $count=0;
648     $ldcache = array();
649     $tt = getdate(time());
650     $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
652     $strftimedatetime = get_string("strftimedatetime");
654     $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
655     $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
656     $filename .= '.xls';
658     $workbook = new MoodleExcelWorkbook('-');
659     $workbook->send($filename);
661     $worksheet = array();
662     $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
663                         get_string('fullnameuser'),    get_string('action'), get_string('info'));
665     // Creating worksheets
666     for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
667         $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
668         $worksheet[$wsnumber] = $workbook->add_worksheet($sheettitle);
669         $worksheet[$wsnumber]->set_column(1, 1, 30);
670         $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
671                                     userdate(time(), $strftimedatetime));
672         $col = 0;
673         foreach ($headers as $item) {
674             $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
675             $col++;
676         }
677     }
679     if (empty($logs['logs'])) {
680         $workbook->close();
681         return true;
682     }
684     $formatDate =& $workbook->add_format();
685     $formatDate->set_num_format(get_string('log_excel_date_format'));
687     $row = FIRSTUSEDEXCELROW;
688     $wsnumber = 1;
689     $myxls =& $worksheet[$wsnumber];
690     foreach ($logs['logs'] as $log) {
691         if (isset($ldcache[$log->module][$log->action])) {
692             $ld = $ldcache[$log->module][$log->action];
693         } else {
694             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
695             $ldcache[$log->module][$log->action] = $ld;
696         }
697         if ($ld && is_numeric($log->info)) {
698             // ugly hack to make sure fullname is shown correctly
699             if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
700                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
701             } else {
702                 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
703             }
704         }
706         // Filter log->info
707         $log->info = format_string($log->info);
708         $log->info = strip_tags(urldecode($log->info));  // Some XSS protection
710         if ($nroPages>1) {
711             if ($row > EXCELROWS) {
712                 $wsnumber++;
713                 $myxls =& $worksheet[$wsnumber];
714                 $row = FIRSTUSEDEXCELROW;
715             }
716         }
718         $coursecontext = context_course::instance($course->id);
720         $myxls->write($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)), '');
721         $myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
722         $myxls->write($row, 2, $log->ip, '');
723         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
724         $myxls->write($row, 3, $fullname, '');
725         $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
726         $myxls->write($row, 4, $log->module.' '.$log->action.' ('.$actionurl.')', '');
727         $myxls->write($row, 5, $log->info, '');
729         $row++;
730     }
732     $workbook->close();
733     return true;
736 function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
737                         $modid, $modaction, $groupid) {
739     global $CFG, $DB;
741     require_once("$CFG->libdir/odslib.class.php");
743     if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
744                        $modname, $modid, $modaction, $groupid)) {
745         return false;
746     }
748     $courses = array();
750     if ($course->id == SITEID) {
751         $courses[0] = '';
752         if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
753             foreach ($ccc as $cc) {
754                 $courses[$cc->id] = $cc->shortname;
755             }
756         }
757     } else {
758         $courses[$course->id] = $course->shortname;
759     }
761     $count=0;
762     $ldcache = array();
763     $tt = getdate(time());
764     $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
766     $strftimedatetime = get_string("strftimedatetime");
768     $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
769     $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
770     $filename .= '.ods';
772     $workbook = new MoodleODSWorkbook('-');
773     $workbook->send($filename);
775     $worksheet = array();
776     $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
777                         get_string('fullnameuser'),    get_string('action'), get_string('info'));
779     // Creating worksheets
780     for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
781         $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
782         $worksheet[$wsnumber] = $workbook->add_worksheet($sheettitle);
783         $worksheet[$wsnumber]->set_column(1, 1, 30);
784         $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
785                                     userdate(time(), $strftimedatetime));
786         $col = 0;
787         foreach ($headers as $item) {
788             $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
789             $col++;
790         }
791     }
793     if (empty($logs['logs'])) {
794         $workbook->close();
795         return true;
796     }
798     $formatDate =& $workbook->add_format();
799     $formatDate->set_num_format(get_string('log_excel_date_format'));
801     $row = FIRSTUSEDEXCELROW;
802     $wsnumber = 1;
803     $myxls =& $worksheet[$wsnumber];
804     foreach ($logs['logs'] as $log) {
805         if (isset($ldcache[$log->module][$log->action])) {
806             $ld = $ldcache[$log->module][$log->action];
807         } else {
808             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
809             $ldcache[$log->module][$log->action] = $ld;
810         }
811         if ($ld && is_numeric($log->info)) {
812             // ugly hack to make sure fullname is shown correctly
813             if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
814                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
815             } else {
816                 $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
817             }
818         }
820         // Filter log->info
821         $log->info = format_string($log->info);
822         $log->info = strip_tags(urldecode($log->info));  // Some XSS protection
824         if ($nroPages>1) {
825             if ($row > EXCELROWS) {
826                 $wsnumber++;
827                 $myxls =& $worksheet[$wsnumber];
828                 $row = FIRSTUSEDEXCELROW;
829             }
830         }
832         $coursecontext = context_course::instance($course->id);
834         $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)));
835         $myxls->write_date($row, 1, $log->time);
836         $myxls->write_string($row, 2, $log->ip);
837         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
838         $myxls->write_string($row, 3, $fullname);
839         $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
840         $myxls->write_string($row, 4, $log->module.' '.$log->action.' ('.$actionurl.')');
841         $myxls->write_string($row, 5, $log->info);
843         $row++;
844     }
846     $workbook->close();
847     return true;
850 /**
851  * This function trawls through the logs looking for
852  * anything new since the user's last login
853  */
854 function print_recent_activity($course) {
855     // $course is an object
856     global $CFG, $USER, $SESSION, $DB, $OUTPUT;
858     $context = context_course::instance($course->id);
860     $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
862     $timestart = round(time() - COURSE_MAX_RECENT_PERIOD, -2); // better db caching for guests - 100 seconds
864     if (!isguestuser()) {
865         if (!empty($USER->lastcourseaccess[$course->id])) {
866             if ($USER->lastcourseaccess[$course->id] > $timestart) {
867                 $timestart = $USER->lastcourseaccess[$course->id];
868             }
869         }
870     }
872     echo '<div class="activitydate">';
873     echo get_string('activitysince', '', userdate($timestart));
874     echo '</div>';
875     echo '<div class="activityhead">';
877     echo '<a href="'.$CFG->wwwroot.'/course/recent.php?id='.$course->id.'">'.get_string('recentactivityreport').'</a>';
879     echo "</div>\n";
881     $content = false;
883 /// Firstly, have there been any new enrolments?
885     $users = get_recent_enrolments($course->id, $timestart);
887     //Accessibility: new users now appear in an <OL> list.
888     if ($users) {
889         echo '<div class="newusers">';
890         echo $OUTPUT->heading(get_string("newusers").':', 3);
891         $content = true;
892         echo "<ol class=\"list\">\n";
893         foreach ($users as $user) {
894             $fullname = fullname($user, $viewfullnames);
895             echo '<li class="name"><a href="'."$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a></li>\n";
896         }
897         echo "</ol>\n</div>\n";
898     }
900 /// Next, have there been any modifications to the course structure?
902     $modinfo = get_fast_modinfo($course);
904     $changelist = array();
906     $logs = $DB->get_records_select('log', "time > ? AND course = ? AND
907                                             module = 'course' AND
908                                             (action = 'add mod' OR action = 'update mod' OR action = 'delete mod')",
909                                     array($timestart, $course->id), "id ASC");
911     if ($logs) {
912         $actions  = array('add mod', 'update mod', 'delete mod');
913         $newgones = array(); // added and later deleted items
914         foreach ($logs as $key => $log) {
915             if (!in_array($log->action, $actions)) {
916                 continue;
917             }
918             $info = explode(' ', $log->info);
920             // note: in most cases I replaced hardcoding of label with use of
921             // $cm->has_view() but it was not possible to do this here because
922             // we don't necessarily have the $cm for it
923             if ($info[0] == 'label') {     // Labels are ignored in recent activity
924                 continue;
925             }
927             if (count($info) != 2) {
928                 debugging("Incorrect log entry info: id = ".$log->id, DEBUG_DEVELOPER);
929                 continue;
930             }
932             $modname    = $info[0];
933             $instanceid = $info[1];
935             if ($log->action == 'delete mod') {
936                 // unfortunately we do not know if the mod was visible
937                 if (!array_key_exists($log->info, $newgones)) {
938                     $strdeleted = get_string('deletedactivity', 'moodle', get_string('modulename', $modname));
939                     $changelist[$log->info] = array ('operation' => 'delete', 'text' => $strdeleted);
940                 }
941             } else {
942                 if (!isset($modinfo->instances[$modname][$instanceid])) {
943                     if ($log->action == 'add mod') {
944                         // do not display added and later deleted activities
945                         $newgones[$log->info] = true;
946                     }
947                     continue;
948                 }
949                 $cm = $modinfo->instances[$modname][$instanceid];
950                 if (!$cm->uservisible) {
951                     continue;
952                 }
954                 if ($log->action == 'add mod') {
955                     $stradded = get_string('added', 'moodle', get_string('modulename', $modname));
956                     $changelist[$log->info] = array('operation' => 'add', 'text' => "$stradded:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
958                 } else if ($log->action == 'update mod' and empty($changelist[$log->info])) {
959                     $strupdated = get_string('updated', 'moodle', get_string('modulename', $modname));
960                     $changelist[$log->info] = array('operation' => 'update', 'text' => "$strupdated:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
961                 }
962             }
963         }
964     }
966     if (!empty($changelist)) {
967         echo $OUTPUT->heading(get_string("courseupdates").':', 3);
968         $content = true;
969         foreach ($changelist as $changeinfo => $change) {
970             echo '<p class="activity">'.$change['text'].'</p>';
971         }
972     }
974 /// Now display new things from each module
976     $usedmodules = array();
977     foreach($modinfo->cms as $cm) {
978         if (isset($usedmodules[$cm->modname])) {
979             continue;
980         }
981         if (!$cm->uservisible) {
982             continue;
983         }
984         $usedmodules[$cm->modname] = $cm->modname;
985     }
987     foreach ($usedmodules as $modname) {      // Each module gets it's own logs and prints them
988         if (file_exists($CFG->dirroot.'/mod/'.$modname.'/lib.php')) {
989             include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php');
990             $print_recent_activity = $modname.'_print_recent_activity';
991             if (function_exists($print_recent_activity)) {
992                 // NOTE: original $isteacher (second parameter below) was replaced with $viewfullnames!
993                 $content = $print_recent_activity($course, $viewfullnames, $timestart) || $content;
994             }
995         } else {
996             debugging("Missing lib.php in lib/{$modname} - please reinstall files or uninstall the module");
997         }
998     }
1000     if (! $content) {
1001         echo '<p class="message">'.get_string('nothingnew').'</p>';
1002     }
1005 /**
1006  * For a given course, returns an array of course activity objects
1007  * Each item in the array contains he following properties:
1008  */
1009 function get_array_of_activities($courseid) {
1010 //  cm - course module id
1011 //  mod - name of the module (eg forum)
1012 //  section - the number of the section (eg week or topic)
1013 //  name - the name of the instance
1014 //  visible - is the instance visible or not
1015 //  groupingid - grouping id
1016 //  groupmembersonly - is this instance visible to group members only
1017 //  extra - contains extra string to include in any link
1018     global $CFG, $DB;
1019     if(!empty($CFG->enableavailability)) {
1020         require_once($CFG->libdir.'/conditionlib.php');
1021     }
1023     $course = $DB->get_record('course', array('id'=>$courseid));
1025     if (empty($course)) {
1026         throw new moodle_exception('courseidnotfound');
1027     }
1029     $mod = array();
1031     $rawmods = get_course_mods($courseid);
1032     if (empty($rawmods)) {
1033         return $mod; // always return array
1034     }
1036     if ($sections = $DB->get_records("course_sections", array("course"=>$courseid), "section ASC")) {
1037        foreach ($sections as $section) {
1038            if (!empty($section->sequence)) {
1039                $sequence = explode(",", $section->sequence);
1040                foreach ($sequence as $seq) {
1041                    if (empty($rawmods[$seq])) {
1042                        continue;
1043                    }
1044                    $mod[$seq] = new stdClass();
1045                    $mod[$seq]->id               = $rawmods[$seq]->instance;
1046                    $mod[$seq]->cm               = $rawmods[$seq]->id;
1047                    $mod[$seq]->mod              = $rawmods[$seq]->modname;
1049                     // Oh dear. Inconsistent names left here for backward compatibility.
1050                    $mod[$seq]->section          = $section->section;
1051                    $mod[$seq]->sectionid        = $rawmods[$seq]->section;
1053                    $mod[$seq]->module           = $rawmods[$seq]->module;
1054                    $mod[$seq]->added            = $rawmods[$seq]->added;
1055                    $mod[$seq]->score            = $rawmods[$seq]->score;
1056                    $mod[$seq]->idnumber         = $rawmods[$seq]->idnumber;
1057                    $mod[$seq]->visible          = $rawmods[$seq]->visible;
1058                    $mod[$seq]->visibleold       = $rawmods[$seq]->visibleold;
1059                    $mod[$seq]->groupmode        = $rawmods[$seq]->groupmode;
1060                    $mod[$seq]->groupingid       = $rawmods[$seq]->groupingid;
1061                    $mod[$seq]->groupmembersonly = $rawmods[$seq]->groupmembersonly;
1062                    $mod[$seq]->indent           = $rawmods[$seq]->indent;
1063                    $mod[$seq]->completion       = $rawmods[$seq]->completion;
1064                    $mod[$seq]->extra            = "";
1065                    $mod[$seq]->completiongradeitemnumber =
1066                            $rawmods[$seq]->completiongradeitemnumber;
1067                    $mod[$seq]->completionview   = $rawmods[$seq]->completionview;
1068                    $mod[$seq]->completionexpected = $rawmods[$seq]->completionexpected;
1069                    $mod[$seq]->availablefrom    = $rawmods[$seq]->availablefrom;
1070                    $mod[$seq]->availableuntil   = $rawmods[$seq]->availableuntil;
1071                    $mod[$seq]->showavailability = $rawmods[$seq]->showavailability;
1072                    $mod[$seq]->showdescription  = $rawmods[$seq]->showdescription;
1073                    if (!empty($CFG->enableavailability)) {
1074                        condition_info::fill_availability_conditions($rawmods[$seq]);
1075                        $mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
1076                        $mod[$seq]->conditionsgrade  = $rawmods[$seq]->conditionsgrade;
1077                        $mod[$seq]->conditionsfield  = $rawmods[$seq]->conditionsfield;
1078                    }
1080                    $modname = $mod[$seq]->mod;
1081                    $functionname = $modname."_get_coursemodule_info";
1083                    if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
1084                        continue;
1085                    }
1087                    include_once("$CFG->dirroot/mod/$modname/lib.php");
1089                    if ($hasfunction = function_exists($functionname)) {
1090                        if ($info = $functionname($rawmods[$seq])) {
1091                            if (!empty($info->icon)) {
1092                                $mod[$seq]->icon = $info->icon;
1093                            }
1094                            if (!empty($info->iconcomponent)) {
1095                                $mod[$seq]->iconcomponent = $info->iconcomponent;
1096                            }
1097                            if (!empty($info->name)) {
1098                                $mod[$seq]->name = $info->name;
1099                            }
1100                            if ($info instanceof cached_cm_info) {
1101                                // When using cached_cm_info you can include three new fields
1102                                // that aren't available for legacy code
1103                                if (!empty($info->content)) {
1104                                    $mod[$seq]->content = $info->content;
1105                                }
1106                                if (!empty($info->extraclasses)) {
1107                                    $mod[$seq]->extraclasses = $info->extraclasses;
1108                                }
1109                                if (!empty($info->iconurl)) {
1110                                    $mod[$seq]->iconurl = $info->iconurl;
1111                                }
1112                                if (!empty($info->onclick)) {
1113                                    $mod[$seq]->onclick = $info->onclick;
1114                                }
1115                                if (!empty($info->customdata)) {
1116                                    $mod[$seq]->customdata = $info->customdata;
1117                                }
1118                            } else {
1119                                // When using a stdclass, the (horrible) deprecated ->extra field
1120                                // is available for BC
1121                                if (!empty($info->extra)) {
1122                                    $mod[$seq]->extra = $info->extra;
1123                                }
1124                            }
1125                        }
1126                    }
1127                    // When there is no modname_get_coursemodule_info function,
1128                    // but showdescriptions is enabled, then we use the 'intro'
1129                    // and 'introformat' fields in the module table
1130                    if (!$hasfunction && $rawmods[$seq]->showdescription) {
1131                        if ($modvalues = $DB->get_record($rawmods[$seq]->modname,
1132                                array('id' => $rawmods[$seq]->instance), 'name, intro, introformat')) {
1133                            // Set content from intro and introformat. Filters are disabled
1134                            // because we  filter it with format_text at display time
1135                            $mod[$seq]->content = format_module_intro($rawmods[$seq]->modname,
1136                                    $modvalues, $rawmods[$seq]->id, false);
1138                            // To save making another query just below, put name in here
1139                            $mod[$seq]->name = $modvalues->name;
1140                        }
1141                    }
1142                    if (!isset($mod[$seq]->name)) {
1143                        $mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
1144                    }
1146                    // Minimise the database size by unsetting default options when they are
1147                    // 'empty'. This list corresponds to code in the cm_info constructor.
1148                    foreach (array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
1149                            'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content',
1150                            'icon', 'iconcomponent', 'customdata', 'showavailability', 'availablefrom',
1151                            'availableuntil', 'conditionscompletion', 'conditionsgrade',
1152                            'completionview', 'completionexpected', 'score', 'showdescription')
1153                            as $property) {
1154                        if (property_exists($mod[$seq], $property) &&
1155                                empty($mod[$seq]->{$property})) {
1156                            unset($mod[$seq]->{$property});
1157                        }
1158                    }
1159                    // Special case: this value is usually set to null, but may be 0
1160                    if (property_exists($mod[$seq], 'completiongradeitemnumber') &&
1161                            is_null($mod[$seq]->completiongradeitemnumber)) {
1162                        unset($mod[$seq]->completiongradeitemnumber);
1163                    }
1164                }
1165             }
1166         }
1167     }
1168     return $mod;
1171 /**
1172  * Returns the localised human-readable names of all used modules
1173  *
1174  * @param bool $plural if true returns the plural forms of the names
1175  * @return array where key is the module name (component name without 'mod_') and
1176  *     the value is the human-readable string. Array sorted alphabetically by value
1177  */
1178 function get_module_types_names($plural = false) {
1179     static $modnames = null;
1180     global $DB, $CFG;
1181     if ($modnames === null) {
1182         $modnames = array(0 => array(), 1 => array());
1183         if ($allmods = $DB->get_records("modules")) {
1184             foreach ($allmods as $mod) {
1185                 if (file_exists("$CFG->dirroot/mod/$mod->name/lib.php") && $mod->visible) {
1186                     $modnames[0][$mod->name] = get_string("modulename", "$mod->name");
1187                     $modnames[1][$mod->name] = get_string("modulenameplural", "$mod->name");
1188                 }
1189             }
1190             collatorlib::asort($modnames[0]);
1191             collatorlib::asort($modnames[1]);
1192         }
1193     }
1194     return $modnames[(int)$plural];
1197 /**
1198  * Set highlighted section. Only one section can be highlighted at the time.
1199  *
1200  * @param int $courseid course id
1201  * @param int $marker highlight section with this number, 0 means remove higlightin
1202  * @return void
1203  */
1204 function course_set_marker($courseid, $marker) {
1205     global $DB;
1206     $DB->set_field("course", "marker", $marker, array('id' => $courseid));
1207     format_base::reset_course_cache($courseid);
1210 /**
1211  * For a given course section, marks it visible or hidden,
1212  * and does the same for every activity in that section
1213  *
1214  * @param int $courseid course id
1215  * @param int $sectionnumber The section number to adjust
1216  * @param int $visibility The new visibility
1217  * @return array A list of resources which were hidden in the section
1218  */
1219 function set_section_visible($courseid, $sectionnumber, $visibility) {
1220     global $DB;
1222     $resourcestotoggle = array();
1223     if ($section = $DB->get_record("course_sections", array("course"=>$courseid, "section"=>$sectionnumber))) {
1224         $DB->set_field("course_sections", "visible", "$visibility", array("id"=>$section->id));
1225         if (!empty($section->sequence)) {
1226             $modules = explode(",", $section->sequence);
1227             foreach ($modules as $moduleid) {
1228                 if ($cm = $DB->get_record('course_modules', array('id' => $moduleid), 'visible, visibleold')) {
1229                     if ($visibility) {
1230                         // As we unhide the section, we use the previously saved visibility stored in visibleold.
1231                         set_coursemodule_visible($moduleid, $cm->visibleold);
1232                     } else {
1233                         // We hide the section, so we hide the module but we store the original state in visibleold.
1234                         set_coursemodule_visible($moduleid, 0);
1235                         $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id' => $moduleid));
1236                     }
1237                 }
1238             }
1239         }
1240         rebuild_course_cache($courseid, true);
1242         // Determine which modules are visible for AJAX update
1243         if (!empty($modules)) {
1244             list($insql, $params) = $DB->get_in_or_equal($modules);
1245             $select = 'id ' . $insql . ' AND visible = ?';
1246             array_push($params, $visibility);
1247             if (!$visibility) {
1248                 $select .= ' AND visibleold = 1';
1249             }
1250             $resourcestotoggle = $DB->get_fieldset_select('course_modules', 'id', $select, $params);
1251         }
1252     }
1253     return $resourcestotoggle;
1256 /**
1257  * Obtains shared data that is used in print_section when displaying a
1258  * course-module entry.
1259  *
1260  * Calls format_text or format_string as appropriate, and obtains the correct icon.
1261  *
1262  * This data is also used in other areas of the code.
1263  * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
1264  * @param object $course Moodle course object
1265  * @return array An array with the following values in this order:
1266  *   $content (optional extra content for after link),
1267  *   $instancename (text of link)
1268  */
1269 function get_print_section_cm_text(cm_info $cm, $course) {
1270     global $OUTPUT;
1272     // Get content from modinfo if specified. Content displays either
1273     // in addition to the standard link (below), or replaces it if
1274     // the link is turned off by setting ->url to null.
1275     if (($content = $cm->get_content()) !== '') {
1276         // Improve filter performance by preloading filter setttings for all
1277         // activities on the course (this does nothing if called multiple
1278         // times)
1279         filter_preload_activities($cm->get_modinfo());
1281         // Get module context
1282         $modulecontext = context_module::instance($cm->id);
1283         $labelformatoptions = new stdClass();
1284         $labelformatoptions->noclean = true;
1285         $labelformatoptions->overflowdiv = true;
1286         $labelformatoptions->context = $modulecontext;
1287         $content = format_text($content, FORMAT_HTML, $labelformatoptions);
1288     } else {
1289         $content = '';
1290     }
1292     // Get course context
1293     $coursecontext = context_course::instance($course->id);
1294     $stringoptions = new stdClass;
1295     $stringoptions->context = $coursecontext;
1296     $instancename = format_string($cm->name, true,  $stringoptions);
1297     return array($content, $instancename);
1300 /**
1301  * Prints a section full of activity modules
1302  *
1303  * @param stdClass $course The course
1304  * @param stdClass|section_info $section The section object containing properties id and section
1305  * @param array $mods (argument not used)
1306  * @param array $modnamesused (argument not used)
1307  * @param bool $absolute All links are absolute
1308  * @param string $width Width of the container
1309  * @param bool $hidecompletion Hide completion status
1310  * @param int $sectionreturn The section to return to
1311  * @return void
1312  */
1313 function print_section($course, $section, $mods, $modnamesused, $absolute=false, $width="100%", $hidecompletion=false, $sectionreturn=null) {
1314     global $CFG, $USER, $DB, $PAGE, $OUTPUT;
1316     static $initialised;
1318     static $groupbuttons;
1319     static $groupbuttonslink;
1320     static $isediting;
1321     static $ismoving;
1322     static $strmovehere;
1323     static $strmovefull;
1324     static $strunreadpostsone;
1326     if (!isset($initialised)) {
1327         $groupbuttons     = ($course->groupmode or (!$course->groupmodeforce));
1328         $groupbuttonslink = (!$course->groupmodeforce);
1329         $isediting        = $PAGE->user_is_editing();
1330         $ismoving         = $isediting && ismoving($course->id);
1331         if ($ismoving) {
1332             $strmovehere  = get_string("movehere");
1333             $strmovefull  = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
1334         }
1335         $initialised = true;
1336     }
1338     $modinfo = get_fast_modinfo($course);
1339     $completioninfo = new completion_info($course);
1341     //Accessibility: replace table with list <ul>, but don't output empty list.
1342     if (!empty($modinfo->sections[$section->section])) {
1344         // Fix bug #5027, don't want style=\"width:$width\".
1345         echo "<ul class=\"section img-text\">\n";
1347         foreach ($modinfo->sections[$section->section] as $modnumber) {
1348             $mod = $modinfo->cms[$modnumber];
1350             if ($ismoving and $mod->id == $USER->activitycopy) {
1351                 // do not display moving mod
1352                 continue;
1353             }
1355             // We can continue (because it will not be displayed at all)
1356             // if:
1357             // 1) The activity is not visible to users
1358             // and
1359             // 2a) The 'showavailability' option is not set (if that is set,
1360             //     we need to display the activity so we can show
1361             //     availability info)
1362             // or
1363             // 2b) The 'availableinfo' is empty, i.e. the activity was
1364             //     hidden in a way that leaves no info, such as using the
1365             //     eye icon.
1366             if (!$mod->uservisible &&
1367                 (empty($mod->showavailability) ||
1368                   empty($mod->availableinfo))) {
1369                 // visibility shortcut
1370                 continue;
1371             }
1373             // In some cases the activity is visible to user, but it is
1374             // dimmed. This is done if viewhiddenactivities is true and if:
1375             // 1. the activity is not visible, or
1376             // 2. the activity has dates set which do not include current, or
1377             // 3. the activity has any other conditions set (regardless of whether
1378             //    current user meets them)
1379             $modcontext = context_module::instance($mod->id);
1380             $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
1381             $accessiblebutdim = false;
1382             $conditionalhidden = false;
1383             if ($canviewhidden) {
1384                 $accessiblebutdim = !$mod->visible;
1385                 if (!empty($CFG->enableavailability)) {
1386                     $conditionalhidden = $mod->availablefrom > time() ||
1387                         ($mod->availableuntil && $mod->availableuntil < time()) ||
1388                         count($mod->conditionsgrade) > 0 ||
1389                         count($mod->conditionscompletion) > 0;
1390                 }
1391                 $accessiblebutdim = $conditionalhidden || $accessiblebutdim;
1392             }
1394             $liclasses = array();
1395             $liclasses[] = 'activity';
1396             $liclasses[] = $mod->modname;
1397             $liclasses[] = 'modtype_'.$mod->modname;
1398             $extraclasses = $mod->get_extra_classes();
1399             if ($extraclasses) {
1400                 $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
1401             }
1402             echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
1403             if ($ismoving) {
1404                 echo '<a title="'.$strmovefull.'"'.
1405                      ' href="'.$CFG->wwwroot.'/course/mod.php?moveto='.$mod->id.'&amp;sesskey='.sesskey().'">'.
1406                      '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
1407                      ' alt="'.$strmovehere.'" /></a><br />
1408                      ';
1409             }
1411             $classes = array('mod-indent');
1412             if (!empty($mod->indent)) {
1413                 $classes[] = 'mod-indent-'.$mod->indent;
1414                 if ($mod->indent > 15) {
1415                     $classes[] = 'mod-indent-huge';
1416                 }
1417             }
1418             echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
1420             // Get data about this course-module
1421             list($content, $instancename) =
1422                     get_print_section_cm_text($modinfo->cms[$modnumber], $course);
1424             //Accessibility: for files get description via icon, this is very ugly hack!
1425             $altname = '';
1426             $altname = $mod->modfullname;
1427             // Avoid unnecessary duplication: if e.g. a forum name already
1428             // includes the word forum (or Forum, etc) then it is unhelpful
1429             // to include that in the accessible description that is added.
1430             if (false !== strpos(textlib::strtolower($instancename),
1431                     textlib::strtolower($altname))) {
1432                 $altname = '';
1433             }
1434             // File type after name, for alphabetic lists (screen reader).
1435             if ($altname) {
1436                 $altname = get_accesshide(' '.$altname);
1437             }
1439             // Start the div for the activity title, excluding the edit icons.
1440             echo html_writer::start_tag('div', array('class' => 'activityinstance'));
1442             // We may be displaying this just in order to show information
1443             // about visibility, without the actual link
1444             $contentpart = '';
1445             if ($mod->uservisible) {
1446                 // Nope - in this case the link is fully working for user
1447                 $linkclasses = '';
1448                 $textclasses = '';
1449                 if ($accessiblebutdim) {
1450                     $linkclasses .= ' dimmed';
1451                     $textclasses .= ' dimmed_text';
1452                     if ($conditionalhidden) {
1453                         $linkclasses .= ' conditionalhidden';
1454                         $textclasses .= ' conditionalhidden';
1455                     }
1456                     $accesstext = get_accesshide(get_string('hiddenfromstudents').': ');
1457                 } else {
1458                     $accesstext = '';
1459                 }
1460                 if ($linkclasses) {
1461                     $linkcss = trim($linkclasses) . ' ';
1462                 } else {
1463                     $linkcss = '';
1464                 }
1465                 if ($textclasses) {
1466                     $textcss = trim($textclasses) . ' ';
1467                 } else {
1468                     $textcss = '';
1469                 }
1471                 // Get on-click attribute value if specified and decode the onclick - it
1472                 // has already been encoded for display (puke).
1473                 $onclick = htmlspecialchars_decode($mod->get_on_click(), ENT_QUOTES);
1475                 $groupinglabel = '';
1476                 if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
1477                     $groupings = groups_get_all_groupings($course->id);
1478                     $groupinglabel = html_writer::tag('span', '('.format_string($groupings[$mod->groupingid]->name).')',
1479                             array('class' => 'groupinglabel'));
1480                 }
1482                 if ($url = $mod->get_url()) {
1483                     // Display link itself.
1484                     $activitylink = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(),
1485                             'class' => 'iconlarge activityicon', 'alt' => $mod->modfullname)) . $accesstext .
1486                             html_writer::tag('span', $instancename . $altname, array('class' => 'instancename'));
1487                     echo html_writer::link($url, $activitylink, array('class' => $linkcss, 'onclick' => $onclick)) .
1488                             $groupinglabel;
1490                     // If specified, display extra content after link.
1491                     if ($content) {
1492                         $contentpart = html_writer::tag('div', $content, array('class' =>
1493                                 trim('contentafterlink ' . $textclasses)));
1494                     }
1495                 } else {
1496                     // No link, so display only content.
1497                     $contentpart = html_writer::tag('div', $accesstext . $content, array('class' => $textcss));
1498                 }
1500             } else {
1501                 $textclasses = $extraclasses;
1502                 $textclasses .= ' dimmed_text';
1503                 if ($textclasses) {
1504                     $textcss = 'class="' . trim($textclasses) . '" ';
1505                 } else {
1506                     $textcss = '';
1507                 }
1508                 $accesstext = '<span class="accesshide">' .
1509                         get_string('notavailableyet', 'condition') .
1510                         ': </span>';
1512                 if ($url = $mod->get_url()) {
1513                     // Display greyed-out text of link
1514                     echo '<div ' . $textcss . $mod->extra .
1515                             ' >' . '<img src="' . $mod->get_icon_url() .
1516                             '" class="activityicon" alt="" /> <span>'. $instancename . $altname .
1517                             '</span></div>';
1519                     // Do not display content after link when it is greyed out like this.
1520                 } else {
1521                     // No link, so display only content (also greyed)
1522                     $contentpart = '<div ' . $textcss . $mod->extra . '>' .
1523                             $accesstext . $content . '</div>';
1524                 }
1525             }
1527             // Module can put text after the link (e.g. forum unread)
1528             echo $mod->get_after_link();
1530             // Closing the tag which contains everything but edit icons. $contentpart should not be part of this.
1531             echo html_writer::end_tag('div');
1533             // If there is content but NO link (eg label), then display the
1534             // content here (BEFORE any icons). In this case cons must be
1535             // displayed after the content so that it makes more sense visually
1536             // and for accessibility reasons, e.g. if you have a one-line label
1537             // it should work similarly (at least in terms of ordering) to an
1538             // activity.
1539             if (empty($url)) {
1540                 echo $contentpart;
1541             }
1543             if ($isediting) {
1544                 if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
1545                     if (! $mod->groupmodelink = $groupbuttonslink) {
1546                         $mod->groupmode = $course->groupmode;
1547                     }
1549                 } else {
1550                     $mod->groupmode = false;
1551                 }
1552                 echo make_editing_buttons($mod, $absolute, true, $mod->indent, $sectionreturn);
1553                 echo $mod->get_after_edit_icons();
1554             }
1556             // Completion
1557             $completion = $hidecompletion
1558                 ? COMPLETION_TRACKING_NONE
1559                 : $completioninfo->is_enabled($mod);
1560             if ($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
1561                 !isguestuser() && $mod->uservisible) {
1562                 $completiondata = $completioninfo->get_data($mod,true);
1563                 $completionicon = '';
1564                 if ($isediting) {
1565                     switch ($completion) {
1566                         case COMPLETION_TRACKING_MANUAL :
1567                             $completionicon = 'manual-enabled'; break;
1568                         case COMPLETION_TRACKING_AUTOMATIC :
1569                             $completionicon = 'auto-enabled'; break;
1570                         default: // wtf
1571                     }
1572                 } else if ($completion==COMPLETION_TRACKING_MANUAL) {
1573                     switch($completiondata->completionstate) {
1574                         case COMPLETION_INCOMPLETE:
1575                             $completionicon = 'manual-n'; break;
1576                         case COMPLETION_COMPLETE:
1577                             $completionicon = 'manual-y'; break;
1578                     }
1579                 } else { // Automatic
1580                     switch($completiondata->completionstate) {
1581                         case COMPLETION_INCOMPLETE:
1582                             $completionicon = 'auto-n'; break;
1583                         case COMPLETION_COMPLETE:
1584                             $completionicon = 'auto-y'; break;
1585                         case COMPLETION_COMPLETE_PASS:
1586                             $completionicon = 'auto-pass'; break;
1587                         case COMPLETION_COMPLETE_FAIL:
1588                             $completionicon = 'auto-fail'; break;
1589                     }
1590                 }
1591                 if ($completionicon) {
1592                     $imgsrc = $OUTPUT->pix_url('i/completion-'.$completionicon);
1593                     $formattedname = format_string($mod->name, true, array('context' => $modcontext));
1594                     $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname);
1595                     if ($completion == COMPLETION_TRACKING_MANUAL && !$isediting) {
1596                         $imgtitle = get_string('completion-title-' . $completionicon, 'completion', $formattedname);
1597                         $newstate =
1598                             $completiondata->completionstate==COMPLETION_COMPLETE
1599                             ? COMPLETION_INCOMPLETE
1600                             : COMPLETION_COMPLETE;
1601                         // In manual mode the icon is a toggle form...
1603                         // If this completion state is used by the
1604                         // conditional activities system, we need to turn
1605                         // off the JS.
1606                         if (!empty($CFG->enableavailability) &&
1607                             condition_info::completion_value_used_as_condition($course, $mod)) {
1608                             $extraclass = ' preventjs';
1609                         } else {
1610                             $extraclass = '';
1611                         }
1612                         echo html_writer::start_tag('form', array(
1613                                 'class' => 'togglecompletion' . $extraclass,
1614                                 'method' => 'post',
1615                                 'action' => $CFG->wwwroot . '/course/togglecompletion.php'));
1616                         echo html_writer::start_tag('div');
1617                         echo html_writer::empty_tag('input', array(
1618                                 'type' => 'hidden', 'name' => 'id', 'value' => $mod->id));
1619                         echo html_writer::empty_tag('input', array(
1620                                 'type' => 'hidden', 'name' => 'modulename',
1621                                 'value' => $mod->name));
1622                         echo html_writer::empty_tag('input', array(
1623                                 'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
1624                         echo html_writer::empty_tag('input', array(
1625                                 'type' => 'hidden', 'name' => 'completionstate',
1626                                 'value' => $newstate));
1627                         echo html_writer::empty_tag('input', array(
1628                                 'type' => 'image', 'src' => $imgsrc, 'alt' => $imgalt, 'title' => $imgtitle));
1629                         echo html_writer::end_tag('div');
1630                         echo html_writer::end_tag('form');
1631                     } else {
1632                         // In auto mode, or when editing, the icon is just an image
1633                         echo "<span class='autocompletion'>";
1634                         echo "<img src='$imgsrc' alt='$imgalt' title='$imgalt' /></span>";
1635                     }
1636                 }
1637             }
1639             // If there is content AND a link, then display the content here
1640             // (AFTER any icons). Otherwise it was displayed before
1641             if (!empty($url)) {
1642                 echo $contentpart;
1643             }
1645             // Show availability information (for someone who isn't allowed to
1646             // see the activity itself, or for staff)
1647             if (!$mod->uservisible) {
1648                 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
1649             } else if ($canviewhidden && !empty($CFG->enableavailability)) {
1650                 // Don't add availability information if user is not editing and activity is hidden.
1651                 if ($mod->visible || $PAGE->user_is_editing()) {
1652                     $hidinfoclass = '';
1653                     if (!$mod->visible) {
1654                         $hidinfoclass = 'hide';
1655                     }
1656                     $ci = new condition_info($mod);
1657                     $fullinfo = $ci->get_full_information();
1658                     if($fullinfo) {
1659                         echo '<div class="availabilityinfo '.$hidinfoclass.'">'.get_string($mod->showavailability
1660                             ? 'userrestriction_visible'
1661                             : 'userrestriction_hidden','condition',
1662                             $fullinfo).'</div>';
1663                     }
1664                 }
1665             }
1667             echo html_writer::end_tag('div');
1668             echo html_writer::end_tag('li')."\n";
1669         }
1671     } elseif ($ismoving) {
1672         echo "<ul class=\"section\">\n";
1673     }
1675     if ($ismoving) {
1676         echo '<li><a title="'.$strmovefull.'"'.
1677              ' href="'.$CFG->wwwroot.'/course/mod.php?movetosection='.$section->id.'&amp;sesskey='.sesskey().'">'.
1678              '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
1679              ' alt="'.$strmovehere.'" /></a></li>
1680              ';
1681     }
1682     if (!empty($modinfo->sections[$section->section]) || $ismoving) {
1683         echo "</ul><!--class='section'-->\n\n";
1684     }
1687 /**
1688  * Prints the menus to add activities and resources.
1689  *
1690  * @param stdClass $course The course
1691  * @param int $section relative section number (field course_sections.section)
1692  * @param null|array $modnames An array containing the list of modules and their names
1693  *     if omitted will be taken from get_module_types_names()
1694  * @param bool $vertical Vertical orientation
1695  * @param bool $return Return the menus or send them to output
1696  * @param int $sectionreturn The section to link back to
1697  * @return void|string depending on $return
1698  */
1699 function print_section_add_menus($course, $section, $modnames = null, $vertical=false, $return=false, $sectionreturn=null) {
1700     global $CFG, $OUTPUT;
1702     if ($modnames === null) {
1703         $modnames = get_module_types_names();
1704     }
1706     // check to see if user can add menus and there are modules to add
1707     if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))
1708             || empty($modnames)) {
1709         if ($return) {
1710             return '';
1711         } else {
1712             return false;
1713         }
1714     }
1716     // Retrieve all modules with associated metadata
1717     $modules = get_module_metadata($course, $modnames, $sectionreturn);
1719     // We'll sort resources and activities into two lists
1720     $resources = array();
1721     $activities = array();
1723     // We need to add the section section to the link for each module
1724     $sectionlink = '&section=' . $section . '&sr=' . $sectionreturn;
1726     foreach ($modules as $module) {
1727         if (isset($module->types)) {
1728             // This module has a subtype
1729             // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
1730             $subtypes = array();
1731             foreach ($module->types as $subtype) {
1732                 $subtypes[$subtype->link . $sectionlink] = $subtype->title;
1733             }
1735             // Sort module subtypes into the list
1736             if (!empty($module->title)) {
1737                 // This grouping has a name
1738                 if ($module->archetype == MOD_CLASS_RESOURCE) {
1739                     $resources[] = array($module->title=>$subtypes);
1740                 } else {
1741                     $activities[] = array($module->title=>$subtypes);
1742                 }
1743             } else {
1744                 // This grouping does not have a name
1745                 if ($module->archetype == MOD_CLASS_RESOURCE) {
1746                     $resources = array_merge($resources, $subtypes);
1747                 } else {
1748                     $activities = array_merge($activities, $subtypes);
1749                 }
1750             }
1751         } else {
1752             // This module has no subtypes
1753             if ($module->archetype == MOD_ARCHETYPE_RESOURCE) {
1754                 $resources[$module->link . $sectionlink] = $module->title;
1755             } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) {
1756                 // System modules cannot be added by user, do not add to dropdown
1757             } else {
1758                 $activities[$module->link . $sectionlink] = $module->title;
1759             }
1760         }
1761     }
1763     $straddactivity = get_string('addactivity');
1764     $straddresource = get_string('addresource');
1765     $sectionname = get_section_name($course, $section);
1766     $strresourcelabel = get_string('addresourcetosection', null, $sectionname);
1767     $stractivitylabel = get_string('addactivitytosection', null, $sectionname);
1769     $output = html_writer::start_tag('div', array('class' => 'section_add_menus', 'id' => 'add_menus-section-' . $section));
1771     if (!$vertical) {
1772         $output .= html_writer::start_tag('div', array('class' => 'horizontal'));
1773     }
1775     if (!empty($resources)) {
1776         $select = new url_select($resources, '', array(''=>$straddresource), "ressection$section");
1777         $select->set_help_icon('resources');
1778         $select->set_label($strresourcelabel, array('class' => 'accesshide'));
1779         $output .= $OUTPUT->render($select);
1780     }
1782     if (!empty($activities)) {
1783         $select = new url_select($activities, '', array(''=>$straddactivity), "section$section");
1784         $select->set_help_icon('activities');
1785         $select->set_label($stractivitylabel, array('class' => 'accesshide'));
1786         $output .= $OUTPUT->render($select);
1787     }
1789     if (!$vertical) {
1790         $output .= html_writer::end_tag('div');
1791     }
1793     $output .= html_writer::end_tag('div');
1795     if (course_ajax_enabled($course)) {
1796         $straddeither = get_string('addresourceoractivity');
1797         // The module chooser link
1798         $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
1799         $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
1800         $icon = $OUTPUT->pix_icon('t/add', '');
1801         $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
1802         $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
1803         $modchooser.= html_writer::end_tag('div');
1804         $modchooser.= html_writer::end_tag('div');
1806         // Wrap the normal output in a noscript div
1807         $usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault);
1808         if ($usemodchooser) {
1809             $output = html_writer::tag('div', $output, array('class' => 'hiddenifjs addresourcedropdown'));
1810             $modchooser = html_writer::tag('div', $modchooser, array('class' => 'visibleifjs addresourcemodchooser'));
1811         } else {
1812             // If the module chooser is disabled, we need to ensure that the dropdowns are shown even if javascript is disabled
1813             $output = html_writer::tag('div', $output, array('class' => 'show addresourcedropdown'));
1814             $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hide addresourcemodchooser'));
1815         }
1816         $output = $modchooser . $output;
1817     }
1819     if ($return) {
1820         return $output;
1821     } else {
1822         echo $output;
1823     }
1826 /**
1827  * Retrieve all metadata for the requested modules
1828  *
1829  * @param object $course The Course
1830  * @param array $modnames An array containing the list of modules and their
1831  * names
1832  * @param int $sectionreturn The section to return to
1833  * @return array A list of stdClass objects containing metadata about each
1834  * module
1835  */
1836 function get_module_metadata($course, $modnames, $sectionreturn = null) {
1837     global $CFG, $OUTPUT;
1839     // get_module_metadata will be called once per section on the page and courses may show
1840     // different modules to one another
1841     static $modlist = array();
1842     if (!isset($modlist[$course->id])) {
1843         $modlist[$course->id] = array();
1844     }
1846     $return = array();
1847     $urlbase = "/course/mod.php?id=$course->id&sesskey=".sesskey().'&sr='.$sectionreturn.'&add=';
1848     foreach($modnames as $modname => $modnamestr) {
1849         if (!course_allowed_module($course, $modname)) {
1850             continue;
1851         }
1852         if (isset($modlist[$course->id][$modname])) {
1853             // This module is already cached
1854             $return[$modname] = $modlist[$course->id][$modname];
1855             continue;
1856         }
1858         // Include the module lib
1859         $libfile = "$CFG->dirroot/mod/$modname/lib.php";
1860         if (!file_exists($libfile)) {
1861             continue;
1862         }
1863         include_once($libfile);
1865         // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
1866         $gettypesfunc =  $modname.'_get_types';
1867         if (function_exists($gettypesfunc)) {
1868             if ($types = $gettypesfunc()) {
1869                 $group = new stdClass();
1870                 $group->name = $modname;
1871                 $group->icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
1872                 foreach($types as $type) {
1873                     if ($type->typestr === '--') {
1874                         continue;
1875                     }
1876                     if (strpos($type->typestr, '--') === 0) {
1877                         $group->title = str_replace('--', '', $type->typestr);
1878                         continue;
1879                     }
1880                     // Set the Sub Type metadata
1881                     $subtype = new stdClass();
1882                     $subtype->title = $type->typestr;
1883                     $subtype->type = str_replace('&amp;', '&', $type->type);
1884                     $subtype->name = preg_replace('/.*type=/', '', $subtype->type);
1885                     $subtype->archetype = $type->modclass;
1887                     // The group archetype should match the subtype archetypes and all subtypes
1888                     // should have the same archetype
1889                     $group->archetype = $subtype->archetype;
1891                     if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
1892                         $subtype->help = get_string('help' . $subtype->name, $modname);
1893                     }
1894                     $subtype->link = $urlbase . $subtype->type;
1895                     $group->types[] = $subtype;
1896                 }
1897                 $modlist[$course->id][$modname] = $group;
1898             }
1899         } else {
1900             $module = new stdClass();
1901             $module->title = get_string('modulename', $modname);
1902             $module->name = $modname;
1903             $module->link = $urlbase . $modname;
1904             $module->icon = $OUTPUT->pix_icon('icon', '', $module->name, array('class' => 'icon'));
1905             $sm = get_string_manager();
1906             if ($sm->string_exists('modulename_help', $modname)) {
1907                 $module->help = get_string('modulename_help', $modname);
1908                 if ($sm->string_exists('modulename_link', $modname)) {  // Link to further info in Moodle docs
1909                     $link = get_string('modulename_link', $modname);
1910                     $linktext = get_string('morehelp');
1911                     $module->help .= html_writer::tag('div', $OUTPUT->doc_link($link, $linktext, true), array('class' => 'helpdoclink'));
1912                 }
1913             }
1914             $module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
1915             $modlist[$course->id][$modname] = $module;
1916         }
1917         $return[$modname] = $modlist[$course->id][$modname];
1918     }
1920     return $return;
1923 /**
1924  * Return the course category context for the category with id $categoryid, except
1925  * that if $categoryid is 0, return the system context.
1926  *
1927  * @param integer $categoryid a category id or 0.
1928  * @return object the corresponding context
1929  */
1930 function get_category_or_system_context($categoryid) {
1931     if ($categoryid) {
1932         return context_coursecat::instance($categoryid, IGNORE_MISSING);
1933     } else {
1934         return context_system::instance();
1935     }
1938 /**
1939  * Gets the child categories of a given courses category. Uses a static cache
1940  * to make repeat calls efficient.
1941  *
1942  * @param int $parentid the id of a course category.
1943  * @return array all the child course categories.
1944  */
1945 function get_child_categories($parentid) {
1946     static $allcategories = null;
1948     // only fill in this variable the first time
1949     if (null == $allcategories) {
1950         $allcategories = array();
1952         $categories = get_categories();
1953         foreach ($categories as $category) {
1954             if (empty($allcategories[$category->parent])) {
1955                 $allcategories[$category->parent] = array();
1956             }
1957             $allcategories[$category->parent][] = $category;
1958         }
1959     }
1961     if (empty($allcategories[$parentid])) {
1962         return array();
1963     } else {
1964         return $allcategories[$parentid];
1965     }
1968 /**
1969  * This function recursively travels the categories, building up a nice list
1970  * for display. It also makes an array that list all the parents for each
1971  * category.
1972  *
1973  * For example, if you have a tree of categories like:
1974  *   Miscellaneous (id = 1)
1975  *      Subcategory (id = 2)
1976  *         Sub-subcategory (id = 4)
1977  *   Other category (id = 3)
1978  * Then after calling this function you will have
1979  * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
1980  *      4 => 'Miscellaneous / Subcategory / Sub-subcategory',
1981  *      3 => 'Other category');
1982  * $parents = array(2 => array(1), 4 => array(1, 2));
1983  *
1984  * If you specify $requiredcapability, then only categories where the current
1985  * user has that capability will be added to $list, although all categories
1986  * will still be added to $parents, and if you only have $requiredcapability
1987  * in a child category, not the parent, then the child catgegory will still be
1988  * included.
1989  *
1990  * If you specify the option $excluded, then that category, and all its children,
1991  * are omitted from the tree. This is useful when you are doing something like
1992  * moving categories, where you do not want to allow people to move a category
1993  * to be the child of itself.
1994  *
1995  * @param array $list For output, accumulates an array categoryid => full category path name
1996  * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
1997  * @param string/array $requiredcapability if given, only categories where the current
1998  *      user has this capability will be added to $list. Can also be an array of capabilities,
1999  *      in which case they are all required.
2000  * @param integer $excludeid Omit this category and its children from the lists built.
2001  * @param object $category Build the tree starting at this category - otherwise starts at the top level.
2002  * @param string $path For internal use, as part of recursive calls.
2003  */
2004 function make_categories_list(&$list, &$parents, $requiredcapability = '',
2005         $excludeid = 0, $category = NULL, $path = "") {
2007     // initialize the arrays if needed
2008     if (!is_array($list)) {
2009         $list = array();
2010     }
2011     if (!is_array($parents)) {
2012         $parents = array();
2013     }
2015     if (empty($category)) {
2016         // Start at the top level.
2017         $category = new stdClass;
2018         $category->id = 0;
2019     } else {
2020         // This is the excluded category, don't include it.
2021         if ($excludeid > 0 && $excludeid == $category->id) {
2022             return;
2023         }
2025         $context = context_coursecat::instance($category->id);
2026         $categoryname = format_string($category->name, true, array('context' => $context));
2028         // Update $path.
2029         if ($path) {
2030             $path = $path.' / '.$categoryname;
2031         } else {
2032             $path = $categoryname;
2033         }
2035         // Add this category to $list, if the permissions check out.
2036         if (empty($requiredcapability)) {
2037             $list[$category->id] = $path;
2039         } else {
2040             $requiredcapability = (array)$requiredcapability;
2041             if (has_all_capabilities($requiredcapability, $context)) {
2042                 $list[$category->id] = $path;
2043             }
2044         }
2045     }
2047     // Add all the children recursively, while updating the parents array.
2048     if ($categories = get_child_categories($category->id)) {
2049         foreach ($categories as $cat) {
2050             if (!empty($category->id)) {
2051                 if (isset($parents[$category->id])) {
2052                     $parents[$cat->id]   = $parents[$category->id];
2053                 }
2054                 $parents[$cat->id][] = $category->id;
2055             }
2056             make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
2057         }
2058     }
2061 /**
2062  * This function generates a structured array of courses and categories.
2063  *
2064  * The depth of categories is limited by $CFG->maxcategorydepth however there
2065  * is no limit on the number of courses!
2066  *
2067  * Suitable for use with the course renderers course_category_tree method:
2068  * $renderer = $PAGE->get_renderer('core','course');
2069  * echo $renderer->course_category_tree(get_course_category_tree());
2070  *
2071  * @global moodle_database $DB
2072  * @param int $id
2073  * @param int $depth
2074  */
2075 function get_course_category_tree($id = 0, $depth = 0) {
2076     global $DB, $CFG;
2077     $viewhiddencats = has_capability('moodle/category:viewhiddencategories', context_system::instance());
2078     $categories = get_child_categories($id);
2079     $categoryids = array();
2080     foreach ($categories as $key => &$category) {
2081         if (!$category->visible && !$viewhiddencats) {
2082             unset($categories[$key]);
2083             continue;
2084         }
2085         $categoryids[$category->id] = $category;
2086         if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
2087             list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
2088             foreach ($subcategories as $subid=>$subcat) {
2089                 $categoryids[$subid] = $subcat;
2090             }
2091             $category->courses = array();
2092         }
2093     }
2095     if ($depth > 0) {
2096         // This is a recursive call so return the required array
2097         return array($categories, $categoryids);
2098     }
2100     if (empty($categoryids)) {
2101         // No categories available (probably all hidden).
2102         return array();
2103     }
2105     // The depth is 0 this function has just been called so we can finish it off
2107     list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
2108     list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
2109     $sql = "SELECT
2110             c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
2111             $ccselect
2112             FROM {course} c
2113             $ccjoin
2114             WHERE c.category $catsql ORDER BY c.sortorder ASC";
2115     if ($courses = $DB->get_records_sql($sql, $catparams)) {
2116         // loop throught them
2117         foreach ($courses as $course) {
2118             if ($course->id == SITEID) {
2119                 continue;
2120             }
2121             context_instance_preload($course);
2122             if (!empty($course->visible) || has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
2123                 $categoryids[$course->category]->courses[$course->id] = $course;
2124             }
2125         }
2126     }
2127     return $categories;
2130 /**
2131  * Recursive function to print out all the categories in a nice format
2132  * with or without courses included
2133  */
2134 function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) {
2135     global $CFG;
2137     // maxcategorydepth == 0 meant no limit
2138     if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
2139         return;
2140     }
2142     if (!$displaylist) {
2143         make_categories_list($displaylist, $parentslist);
2144     }
2146     if ($category) {
2147         if ($category->visible or has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
2148             print_category_info($category, $depth, $showcourses);
2149         } else {
2150             return;  // Don't bother printing children of invisible categories
2151         }
2153     } else {
2154         $category = new stdClass();
2155         $category->id = "0";
2156     }
2158     if ($categories = get_child_categories($category->id)) {   // Print all the children recursively
2159         $countcats = count($categories);
2160         $count = 0;
2161         $first = true;
2162         $last = false;
2163         foreach ($categories as $cat) {
2164             $count++;
2165             if ($count == $countcats) {
2166                 $last = true;
2167             }
2168             $up = $first ? false : true;
2169             $down = $last ? false : true;
2170             $first = false;
2172             print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses);
2173         }
2174     }
2177 /**
2178  * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
2179  */
2180 function make_categories_options() {
2181     make_categories_list($cats,$parents);
2182     foreach ($cats as $key => $value) {
2183         if (array_key_exists($key,$parents)) {
2184             if ($indent = count($parents[$key])) {
2185                 for ($i = 0; $i < $indent; $i++) {
2186                     $cats[$key] = '&nbsp;'.$cats[$key];
2187                 }
2188             }
2189         }
2190     }
2191     return $cats;
2194 /**
2195  * Prints the category info in indented fashion
2196  * This function is only used by print_whole_category_list() above
2197  */
2198 function print_category_info($category, $depth=0, $showcourses = false) {
2199     global $CFG, $DB, $OUTPUT;
2201     $strsummary = get_string('summary');
2203     $catlinkcss = null;
2204     if (!$category->visible) {
2205         $catlinkcss = array('class'=>'dimmed');
2206     }
2207     static $coursecount = null;
2208     if (null === $coursecount) {
2209         // only need to check this once
2210         $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT;
2211     }
2213     if ($showcourses and $coursecount) {
2214         $catimage = '<img src="'.$OUTPUT->pix_url('i/course') . '" alt="" />';
2215     } else {
2216         $catimage = "&nbsp;";
2217     }
2219     $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
2220     $context = context_coursecat::instance($category->id);
2221     $fullname = format_string($category->name, true, array('context' => $context));
2223     if ($showcourses and $coursecount) {
2224         echo '<div class="categorylist clearfix">';
2225         $cat = '';
2226         $cat .= html_writer::tag('div', $catimage, array('class'=>'image'));
2227         $catlink = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
2228         $cat .= html_writer::tag('div', $catlink, array('class'=>'name'));
2230         $html = '';
2231         if ($depth > 0) {
2232             for ($i=0; $i< $depth; $i++) {
2233                 $html = html_writer::tag('div', $html . $cat, array('class'=>'indentation'));
2234                 $cat = '';
2235             }
2236         } else {
2237             $html = $cat;
2238         }
2239         echo html_writer::tag('div', $html, array('class'=>'category'));
2240         echo html_writer::tag('div', '', array('class'=>'clearfloat'));
2242         // does the depth exceed maxcategorydepth
2243         // maxcategorydepth == 0 or unset meant no limit
2244         $limit = !(isset($CFG->maxcategorydepth) && ($depth >= $CFG->maxcategorydepth-1));
2245         if ($courses && ($limit || $CFG->maxcategorydepth == 0)) {
2246             foreach ($courses as $course) {
2247                 $linkcss = null;
2248                 if (!$course->visible) {
2249                     $linkcss = array('class'=>'dimmed');
2250                 }
2252                 $coursename = get_course_display_name_for_list($course);
2253                 $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($coursename), $linkcss);
2255                 // print enrol info
2256                 $courseicon = '';
2257                 if ($icons = enrol_get_course_info_icons($course)) {
2258                     foreach ($icons as $pix_icon) {
2259                         $courseicon = $OUTPUT->render($pix_icon);
2260                     }
2261                 }
2263                 $coursecontent = html_writer::tag('div', $courseicon.$courselink, array('class'=>'name'));
2265                 if ($course->summary) {
2266                     $link = new moodle_url('/course/info.php?id='.$course->id);
2267                     $actionlink = $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
2268                         new popup_action('click', $link, 'courseinfo', array('height' => 400, 'width' => 500)),
2269                         array('title'=>$strsummary));
2271                     $coursecontent .= html_writer::tag('div', $actionlink, array('class'=>'info'));
2272                 }
2274                 $html = '';
2275                 for ($i=0; $i <= $depth; $i++) {
2276                     $html = html_writer::tag('div', $html . $coursecontent , array('class'=>'indentation'));
2277                     $coursecontent = '';
2278                 }
2279                 echo html_writer::tag('div', $html, array('class'=>'course clearfloat'));
2280             }
2281         }
2282         echo '</div>';
2283     } else {
2284         echo '<div class="categorylist">';
2285         $html = '';
2286         $cat = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
2287         if (count($courses) > 0) {
2288             $cat .= html_writer::tag('span', ' ('.count($courses).')', array('title'=>get_string('numberofcourses'), 'class'=>'numberofcourse'));
2289         }
2291         if ($depth > 0) {
2292             for ($i=0; $i< $depth; $i++) {
2293                 $html = html_writer::tag('div', $html .$cat, array('class'=>'indentation'));
2294                 $cat = '';
2295             }
2296         } else {
2297             $html = $cat;
2298         }
2300         echo html_writer::tag('div', $html, array('class'=>'category'));
2301         echo html_writer::tag('div', '', array('class'=>'clearfloat'));
2302         echo '</div>';
2303     }
2306 /**
2307  * Print the buttons relating to course requests.
2308  *
2309  * @param object $systemcontext the system context.
2310  */
2311 function print_course_request_buttons($systemcontext) {
2312     global $CFG, $DB, $OUTPUT;
2313     if (empty($CFG->enablecourserequests)) {
2314         return;
2315     }
2316     if (!has_capability('moodle/course:create', $systemcontext) && has_capability('moodle/course:request', $systemcontext)) {
2317     /// Print a button to request a new course
2318         echo $OUTPUT->single_button('request.php', get_string('requestcourse'), 'get');
2319     }
2320     /// Print a button to manage pending requests
2321     if (has_capability('moodle/site:approvecourse', $systemcontext)) {
2322         $disabled = !$DB->record_exists('course_request', array());
2323         echo $OUTPUT->single_button('pending.php', get_string('coursespending'), 'get', array('disabled'=>$disabled));
2324     }
2327 /**
2328  * Does the user have permission to edit things in this category?
2329  *
2330  * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2331  * @return boolean has_any_capability(array(...), ...); in the appropriate context.
2332  */
2333 function can_edit_in_category($categoryid = 0) {
2334     $context = get_category_or_system_context($categoryid);
2335     return has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context);
2338 /**
2339  * Prints the turn editing on/off button on course/index.php or course/category.php.
2340  *
2341  * @param integer $categoryid The id of the category we are showing, or 0 for system context.
2342  * @return string HTML of the editing button, or empty string, if this user is not allowed
2343  *      to see it.
2344  */
2345 function update_category_button($categoryid = 0) {
2346     global $CFG, $PAGE, $OUTPUT;
2348     // Check permissions.
2349     if (!can_edit_in_category($categoryid)) {
2350         return '';
2351     }
2353     // Work out the appropriate action.
2354     if ($PAGE->user_is_editing()) {
2355         $label = get_string('turneditingoff');
2356         $edit = 'off';
2357     } else {
2358         $label = get_string('turneditingon');
2359         $edit = 'on';
2360     }
2362     // Generate the button HTML.
2363     $options = array('categoryedit' => $edit, 'sesskey' => sesskey());
2364     if ($categoryid) {
2365         $options['id'] = $categoryid;
2366         $page = 'category.php';
2367     } else {
2368         $page = 'index.php';
2369     }
2370     return $OUTPUT->single_button(new moodle_url('/course/' . $page, $options), $label, 'get');
2373 /**
2374  * Category is 0 (for all courses) or an object
2375  */
2376 function print_courses($category) {
2377     global $CFG, $OUTPUT;
2379     if (!is_object($category) && $category==0) {
2380         $categories = get_child_categories(0);  // Parent = 0   ie top-level categories only
2381         if (is_array($categories) && count($categories) == 1) {
2382             $category   = array_shift($categories);
2383             $courses    = get_courses_wmanagers($category->id,
2384                                                 'c.sortorder ASC',
2385                                                 array('summary','summaryformat'));
2386         } else {
2387             $courses    = get_courses_wmanagers('all',
2388                                                 'c.sortorder ASC',
2389                                                 array('summary','summaryformat'));
2390         }
2391         unset($categories);
2392     } else {
2393         $courses    = get_courses_wmanagers($category->id,
2394                                             'c.sortorder ASC',
2395                                             array('summary','summaryformat'));
2396     }
2398     if ($courses) {
2399         echo html_writer::start_tag('ul', array('class'=>'unlist'));
2400         foreach ($courses as $course) {
2401             $coursecontext = context_course::instance($course->id);
2402             if ($course->visible == 1 || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
2403                 echo html_writer::start_tag('li');
2404                 print_course($course);
2405                 echo html_writer::end_tag('li');
2406             }
2407         }
2408         echo html_writer::end_tag('ul');
2409     } else {
2410         echo $OUTPUT->heading(get_string("nocoursesyet"));
2411         $context = context_system::instance();
2412         if (has_capability('moodle/course:create', $context)) {
2413             $options = array();
2414             if (!empty($category->id)) {
2415                 $options['category'] = $category->id;
2416             } else {
2417                 $options['category'] = $CFG->defaultrequestcategory;
2418             }
2419             echo html_writer::start_tag('div', array('class'=>'addcoursebutton'));
2420             echo $OUTPUT->single_button(new moodle_url('/course/edit.php', $options), get_string("addnewcourse"));
2421             echo html_writer::end_tag('div');
2422         }
2423     }
2426 /**
2427  * Print a description of a course, suitable for browsing in a list.
2428  *
2429  * @param object $course the course object.
2430  * @param string $highlightterms (optional) some search terms that should be highlighted in the display.
2431  */
2432 function print_course($course, $highlightterms = '') {
2433     global $CFG, $USER, $DB, $OUTPUT;
2435     $context = context_course::instance($course->id);
2437     // Rewrite file URLs so that they are correct
2438     $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
2440     echo html_writer::start_tag('div', array('class'=>'coursebox clearfix'));
2441     echo html_writer::start_tag('div', array('class'=>'info'));
2442     echo html_writer::start_tag('h3', array('class'=>'name'));
2444     $linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
2446     $coursename = get_course_display_name_for_list($course);
2447     $linktext = highlight($highlightterms, format_string($coursename));
2448     $linkparams = array('title'=>get_string('entercourse'));
2449     if (empty($course->visible)) {
2450         $linkparams['class'] = 'dimmed';
2451     }
2452     echo html_writer::link($linkhref, $linktext, $linkparams);
2453     echo html_writer::end_tag('h3');
2455     /// first find all roles that are supposed to be displayed
2456     if (!empty($CFG->coursecontact)) {
2457         $managerroles = explode(',', $CFG->coursecontact);
2458         $rusers = array();
2460         if (!isset($course->managers)) {
2461             list($sort, $sortparams) = users_order_by_sql('u');
2462             $rusers = get_role_users($managerroles, $context, true,
2463                 'ra.id AS raid, u.id, u.username, u.firstname, u.lastname, rn.name AS rolecoursealias,
2464                  r.name AS rolename, r.sortorder, r.id AS roleid, r.shortname AS roleshortname',
2465                 'r.sortorder ASC, ' . $sort, null, '', '', '', '', $sortparams);
2466         } else {
2467             //  use the managers array if we have it for perf reasosn
2468             //  populate the datastructure like output of get_role_users();
2469             foreach ($course->managers as $manager) {
2470                 $user = clone($manager->user);
2471                 $user->roleid = $manager->roleid;
2472                 $user->rolename = $manager->rolename;
2473                 $user->roleshortname = $manager->roleshortname;
2474                 $user->rolecoursealias = $manager->rolecoursealias;
2475                 $rusers[$user->id] = $user;
2476             }
2477         }
2479         $namesarray = array();
2480         $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
2481         foreach ($rusers as $ra) {
2482             if (isset($namesarray[$ra->id])) {
2483                 //  only display a user once with the higest sortorder role
2484                 continue;
2485             }
2487             $role = new stdClass();
2488             $role->id = $ra->roleid;
2489             $role->name = $ra->rolename;
2490             $role->shortname = $ra->roleshortname;
2491             $role->coursealias = $ra->rolecoursealias;
2492             $rolename = role_get_name($role, $context, ROLENAME_ALIAS);
2494             $fullname = fullname($ra, $canviewfullnames);
2495             $namesarray[$ra->id] = $rolename.': '.
2496                 html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->id, 'course'=>SITEID)), $fullname);
2497         }
2499         if (!empty($namesarray)) {
2500             echo html_writer::start_tag('ul', array('class'=>'teachers'));
2501             foreach ($namesarray as $name) {
2502                 echo html_writer::tag('li', $name);
2503             }
2504             echo html_writer::end_tag('ul');
2505         }
2506     }
2507     echo html_writer::end_tag('div'); // End of info div
2509     echo html_writer::start_tag('div', array('class'=>'summary'));
2510     $options = new stdClass();
2511     $options->noclean = true;
2512     $options->para = false;
2513     $options->overflowdiv = true;
2514     if (!isset($course->summaryformat)) {
2515         $course->summaryformat = FORMAT_MOODLE;
2516     }
2517     echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options,  $course->id));
2518     if ($icons = enrol_get_course_info_icons($course)) {
2519         echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
2520         foreach ($icons as $icon) {
2521             echo $OUTPUT->render($icon);
2522         }
2523         echo html_writer::end_tag('div'); // End of enrolmenticons div
2524     }
2525     echo html_writer::end_tag('div'); // End of summary div
2526     echo html_writer::end_tag('div'); // End of coursebox div
2529 /**
2530  * Prints custom user information on the home page.
2531  * Over time this can include all sorts of information
2532  */
2533 function print_my_moodle() {
2534     global $USER, $CFG, $DB, $OUTPUT;
2536     if (!isloggedin() or isguestuser()) {
2537         print_error('nopermissions', '', '', 'See My Moodle');
2538     }
2540     $courses  = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
2541     $rhosts   = array();
2542     $rcourses = array();
2543     if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2544         $rcourses = get_my_remotecourses($USER->id);
2545         $rhosts   = get_my_remotehosts();
2546     }
2548     if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2550         if (!empty($courses)) {
2551             echo '<ul class="unlist">';
2552             foreach ($courses as $course) {
2553                 if ($course->id == SITEID) {
2554                     continue;
2555                 }
2556                 echo '<li>';
2557                 print_course($course);
2558                 echo "</li>\n";
2559             }
2560             echo "</ul>\n";
2561         }
2563         // MNET
2564         if (!empty($rcourses)) {
2565             // at the IDP, we know of all the remote courses
2566             foreach ($rcourses as $course) {
2567                 print_remote_course($course, "100%");
2568             }
2569         } elseif (!empty($rhosts)) {
2570             // non-IDP, we know of all the remote servers, but not courses
2571             foreach ($rhosts as $host) {
2572                 print_remote_host($host, "100%");
2573             }
2574         }
2575         unset($course);
2576         unset($host);
2578         if ($DB->count_records("course") > (count($courses) + 1) ) {  // Some courses not being displayed
2579             echo "<table width=\"100%\"><tr><td align=\"center\">";
2580             print_course_search("", false, "short");
2581             echo "</td><td align=\"center\">";
2582             echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
2583             echo "</td></tr></table>\n";
2584         }
2586     } else {
2587         if ($DB->count_records("course_categories") > 1) {
2588             echo $OUTPUT->box_start("categorybox");
2589             print_whole_category_list();
2590             echo $OUTPUT->box_end();
2591         } else {
2592             print_courses(0);
2593         }
2594     }
2598 function print_course_search($value="", $return=false, $format="plain") {
2599     global $CFG;
2600     static $count = 0;
2602     $count++;
2604     $id = 'coursesearch';
2606     if ($count > 1) {
2607         $id .= $count;
2608     }
2610     $strsearchcourses= get_string("searchcourses");
2612     if ($format == 'plain') {
2613         $output  = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2614         $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2615         $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
2616         $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
2617         $output .= '<input type="submit" value="'.get_string('go').'" />';
2618         $output .= '</fieldset></form>';
2619     } else if ($format == 'short') {
2620         $output  = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2621         $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2622         $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
2623         $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
2624         $output .= '<input type="submit" value="'.get_string('go').'" />';
2625         $output .= '</fieldset></form>';
2626     } else if ($format == 'navbar') {
2627         $output  = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
2628         $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
2629         $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
2630         $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
2631         $output .= '<input type="submit" value="'.get_string('go').'" />';
2632         $output .= '</fieldset></form>';
2633     }
2635     if ($return) {
2636         return $output;
2637     }
2638     echo $output;
2641 function print_remote_course($course, $width="100%") {
2642     global $CFG, $USER;
2644     $linkcss = '';
2646     $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}";
2648     echo '<div class="coursebox remotecoursebox clearfix">';
2649     echo '<div class="info">';
2650     echo '<div class="name"><a title="'.get_string('entercourse').'"'.
2651          $linkcss.' href="'.$url.'">'
2652         .  format_string($course->fullname) .'</a><br />'
2653         . format_string($course->hostname) . ' : '
2654         . format_string($course->cat_name) . ' : '
2655         . format_string($course->shortname). '</div>';
2656     echo '</div><div class="summary">';
2657     $options = new stdClass();
2658     $options->noclean = true;
2659     $options->para = false;
2660     $options->overflowdiv = true;
2661     echo format_text($course->summary, $course->summaryformat, $options);
2662     echo '</div>';
2663     echo '</div>';
2666 function print_remote_host($host, $width="100%") {
2667     global $OUTPUT;
2669     $linkcss = '';
2671     echo '<div class="coursebox clearfix">';
2672     echo '<div class="info">';
2673     echo '<div class="name">';
2674     echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
2675     echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
2676         . s($host['name']).'</a> - ';
2677     echo $host['count'] . ' ' . get_string('courses');
2678     echo '</div>';
2679     echo '</div>';
2680     echo '</div>';
2684 /// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
2686 function add_course_module($mod) {
2687     global $DB;
2689     $mod->added = time();
2690     unset($mod->id);
2692     $cmid = $DB->insert_record("course_modules", $mod);
2693     rebuild_course_cache($mod->course, true);
2694     return $cmid;
2697 /**
2698  * Creates missing course section(s) and rebuilds course cache
2699  *
2700  * @param int|stdClass $courseorid course id or course object
2701  * @param int|array $sections list of relative section numbers to create
2702  * @return bool if there were any sections created
2703  */
2704 function course_create_sections_if_missing($courseorid, $sections) {
2705     global $DB;
2706     if (!is_array($sections)) {
2707         $sections = array($sections);
2708     }
2709     $existing = array_keys(get_fast_modinfo($courseorid)->get_section_info_all());
2710     if (is_object($courseorid)) {
2711         $courseorid = $courseorid->id;
2712     }
2713     $coursechanged = false;
2714     foreach ($sections as $sectionnum) {
2715         if (!in_array($sectionnum, $existing)) {
2716             $cw = new stdClass();
2717             $cw->course   = $courseorid;
2718             $cw->section  = $sectionnum;
2719             $cw->summary  = '';
2720             $cw->summaryformat = FORMAT_HTML;
2721             $cw->sequence = '';
2722             $id = $DB->insert_record("course_sections", $cw);
2723             $coursechanged = true;
2724         }
2725     }
2726     if ($coursechanged) {
2727         rebuild_course_cache($courseorid, true);
2728     }
2729     return $coursechanged;
2732 /**
2733  * Adds an existing module to the section
2734  *
2735  * Updates both tables {course_sections} and {course_modules}
2736  *
2737  * @param int|stdClass $courseorid course id or course object
2738  * @param int $cmid id of the module already existing in course_modules table
2739  * @param int $sectionnum relative number of the section (field course_sections.section)
2740  *     If section does not exist it will be created
2741  * @param int|stdClass $beforemod id or object with field id corresponding to the module
2742  *     before which the module needs to be included. Null for inserting in the
2743  *     end of the section
2744  * @return int The course_sections ID where the module is inserted
2745  */
2746 function course_add_cm_to_section($courseorid, $cmid, $sectionnum, $beforemod = null) {
2747     global $DB, $COURSE;
2748     if (is_object($beforemod)) {
2749         $beforemod = $beforemod->id;
2750     }
2751     if (is_object($courseorid)) {
2752         $courseid = $courseorid->id;
2753     } else {
2754         $courseid = $courseorid;
2755     }
2756     course_create_sections_if_missing($courseorid, $sectionnum);
2757     // Do not try to use modinfo here, there is no guarantee it is valid!
2758     $section = $DB->get_record('course_sections', array('course'=>$courseid, 'section'=>$sectionnum), '*', MUST_EXIST);
2759     $modarray = explode(",", trim($section->sequence));
2760     if (empty($section->sequence)) {
2761         $newsequence = "$cmid";
2762     } else if ($beforemod && ($key = array_keys($modarray, $beforemod))) {
2763         $insertarray = array($cmid, $beforemod);
2764         array_splice($modarray, $key[0], 1, $insertarray);
2765         $newsequence = implode(",", $modarray);
2766     } else {
2767         $newsequence = "$section->sequence,$cmid";
2768     }
2769     $DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
2770     $DB->set_field('course_modules', 'section', $section->id, array('id' => $cmid));
2771     if (is_object($courseorid)) {
2772         rebuild_course_cache($courseorid->id, true);
2773     } else {
2774         rebuild_course_cache($courseorid, true);
2775     }
2776     return $section->id;     // Return course_sections ID that was used.
2779 function set_coursemodule_groupmode($id, $groupmode) {
2780     global $DB;
2781     $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,groupmode', MUST_EXIST);
2782     if ($cm->groupmode != $groupmode) {
2783         $DB->set_field('course_modules', 'groupmode', $groupmode, array('id' => $cm->id));
2784         rebuild_course_cache($cm->course, true);
2785     }
2786     return ($cm->groupmode != $groupmode);
2789 function set_coursemodule_idnumber($id, $idnumber) {
2790     global $DB;
2791     $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,idnumber', MUST_EXIST);
2792     if ($cm->idnumber != $idnumber) {
2793         $DB->set_field('course_modules', 'idnumber', $idnumber, array('id' => $cm->id));
2794         rebuild_course_cache($cm->course, true);
2795     }
2796     return ($cm->idnumber != $idnumber);
2799 /**
2800  * Set the visibility of a module and inherent properties.
2801  *
2802  * From 2.4 the parameter $prevstateoverrides has been removed, the logic it triggered
2803  * has been moved to {@link set_section_visible()} which was the only place from which
2804  * the parameter was used.
2805  *
2806  * @param int $id of the module
2807  * @param int $visible state of the module
2808  * @return bool false when the module was not found, true otherwise
2809  */
2810 function set_coursemodule_visible($id, $visible) {
2811     global $DB, $CFG;
2812     require_once($CFG->libdir.'/gradelib.php');
2814     // Trigger developer's attention when using the previously removed argument.
2815     if (func_num_args() > 2) {
2816         debugging('Wrong number of arguments passed to set_coursemodule_visible(), $prevstateoverrides
2817             has been removed.', DEBUG_DEVELOPER);
2818     }
2820     if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
2821         return false;
2822     }
2823     if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
2824         return false;
2825     }
2826     if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
2827         foreach($events as $event) {
2828             if ($visible) {
2829                 show_event($event);
2830             } else {
2831                 hide_event($event);
2832             }
2833         }
2834     }
2836     // hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there
2837     $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
2838     if ($grade_items) {
2839         foreach ($grade_items as $grade_item) {
2840             $grade_item->set_hidden(!$visible);
2841         }
2842     }
2844     // Updating visible and visibleold to keep them in sync. Only changing a section visibility will
2845     // affect visibleold to allow for an original visibility restore. See set_section_visible().
2846     $cminfo = new stdClass();
2847     $cminfo->id = $id;
2848     $cminfo->visible = $visible;
2849     $cminfo->visibleold = $visible;
2850     $DB->update_record('course_modules', $cminfo);
2852     rebuild_course_cache($cm->course, true);
2853     return true;
2856 /**
2857  * Delete a course module and any associated data at the course level (events)
2858  * Until 1.5 this function simply marked a deleted flag ... now it
2859  * deletes it completely.
2860  *
2861  */
2862 function delete_course_module($id) {
2863     global $CFG, $DB;
2864     require_once($CFG->libdir.'/gradelib.php');
2865     require_once($CFG->dirroot.'/blog/lib.php');
2867     if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
2868         return true;
2869     }
2870     $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
2871     //delete events from calendar
2872     if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
2873         foreach($events as $event) {
2874             delete_event($event->id);
2875         }
2876     }
2877     //delete grade items, outcome items and grades attached to modules
2878     if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
2879                                                    'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
2880         foreach ($grade_items as $grade_item) {
2881             $grade_item->delete('moddelete');
2882         }
2883     }
2884     // Delete completion and availability data; it is better to do this even if the
2885     // features are not turned on, in case they were turned on previously (these will be
2886     // very quick on an empty table)
2887     $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
2888     $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
2889     $DB->delete_records('course_modules_avail_fields', array('coursemoduleid' => $cm->id));
2890     $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id,
2891                                                             'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
2893     delete_context(CONTEXT_MODULE, $cm->id);
2894     $DB->delete_records('course_modules', array('id'=>$cm->id));
2895     rebuild_course_cache($cm->course, true);
2896     return true;
2899 function delete_mod_from_section($modid, $sectionid) {
2900     global $DB;
2902     if ($section = $DB->get_record("course_sections", array("id"=>$sectionid)) ) {
2904         $modarray = explode(",", $section->sequence);
2906         if ($key = array_keys ($modarray, $modid)) {
2907             array_splice($modarray, $key[0], 1);
2908             $newsequence = implode(",", $modarray);
2909             $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
2910             rebuild_course_cache($section->course, true);
2911             return true;
2912         } else {
2913             return false;
2914         }
2916     }
2917     return false;
2920 /**
2921  * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
2922  *
2923  * @param object $course course object
2924  * @param int $section Section number (not id!!!)
2925  * @param int $move (-1 or 1)
2926  * @return boolean true if section moved successfully
2927  * @todo MDL-33379 remove this function in 2.5
2928  */
2929 function move_section($course, $section, $move) {
2930     debugging('This function will be removed before 2.5 is released please use move_section_to', DEBUG_DEVELOPER);
2932 /// Moves a whole course section up and down within the course
2933     global $USER;
2935     if (!$move) {
2936         return true;
2937     }
2939     $sectiondest = $section + $move;
2941     // compartibility with course formats using field 'numsections'
2942     $courseformatoptions = course_get_format($course)->get_format_options();
2943     if (array_key_exists('numsections', $courseformatoptions) &&
2944             $sectiondest > $courseformatoptions['numsections'] or $sectiondest < 1) {
2945         return false;
2946     }
2948     $retval = move_section_to($course, $section, $sectiondest);
2949     return $retval;
2952 /**
2953  * Moves a section within a course, from a position to another.
2954  * Be very careful: $section and $destination refer to section number,
2955  * not id!.
2956  *
2957  * @param object $course
2958  * @param int $section Section number (not id!!!)
2959  * @param int $destination
2960  * @return boolean Result
2961  */
2962 function move_section_to($course, $section, $destination) {
2963 /// Moves a whole course section up and down within the course
2964     global $USER, $DB;
2966     if (!$destination && $destination != 0) {
2967         return true;
2968     }
2970     // compartibility with course formats using field 'numsections'
2971     $courseformatoptions = course_get_format($course)->get_format_options();
2972     if ((array_key_exists('numsections', $courseformatoptions) &&
2973             ($destination > $courseformatoptions['numsections'])) || ($destination < 1)) {
2974         return false;
2975     }
2977     // Get all sections for this course and re-order them (2 of them should now share the same section number)
2978     if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
2979             'section ASC, id ASC', 'id, section')) {
2980         return false;
2981     }
2983     $movedsections = reorder_sections($sections, $section, $destination);
2985     // Update all sections. Do this in 2 steps to avoid breaking database
2986     // uniqueness constraint
2987     $transaction = $DB->start_delegated_transaction();
2988     foreach ($movedsections as $id => $position) {
2989         if ($sections[$id] !== $position) {
2990             $DB->set_field('course_sections', 'section', -$position, array('id' => $id));
2991         }
2992     }
2993     foreach ($movedsections as $id => $position) {
2994         if ($sections[$id] !== $position) {
2995             $DB->set_field('course_sections', 'section', $position, array('id' => $id));
2996         }
2997     }
2999     // If we move the highlighted section itself, then just highlight the destination.
3000     // Adjust the higlighted section location if we move something over it either direction.
3001     if ($section == $course->marker) {
3002         course_set_marker($course->id, $destination);
3003     } elseif ($section > $course->marker && $course->marker >= $destination) {
3004         course_set_marker($course->id, $course->marker+1);
3005     } elseif ($section < $course->marker && $course->marker <= $destination) {
3006         course_set_marker($course->id, $course->marker-1);
3007     }
3009     $transaction->allow_commit();
3010     rebuild_course_cache($course->id, true);
3011     return true;
3014 /**
3015  * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
3016  * an original position number and a target position number, rebuilds the array so that the
3017  * move is made without any duplication of section positions.
3018  * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
3019  * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
3020  *
3021  * @param array $sections
3022  * @param int $origin_position
3023  * @param int $target_position
3024  * @return array
3025  */
3026 function reorder_sections($sections, $origin_position, $target_position) {
3027     if (!is_array($sections)) {
3028         return false;
3029     }
3031     // We can't move section position 0
3032     if ($origin_position < 1) {
3033         echo "We can't move section position 0";
3034         return false;
3035     }
3037     // Locate origin section in sections array
3038     if (!$origin_key = array_search($origin_position, $sections)) {
3039         echo "searched position not in sections array";
3040         return false; // searched position not in sections array
3041     }
3043     // Extract origin section
3044     $origin_section = $sections[$origin_key];
3045     unset($sections[$origin_key]);
3047     // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
3048     $found = false;
3049     $append_array = array();
3050     foreach ($sections as $id => $position) {
3051         if ($found) {
3052             $append_array[$id] = $position;
3053             unset($sections[$id]);
3054         }
3055         if ($position == $target_position) {
3056             if ($target_position < $origin_position) {
3057                 $append_array[$id] = $position;
3058                 unset($sections[$id]);
3059             }
3060             $found = true;
3061         }
3062     }
3064     // Append moved section
3065     $sections[$origin_key] = $origin_section;
3067     // Append rest of array (if applicable)
3068     if (!empty($append_array)) {
3069         foreach ($append_array as $id => $position) {
3070             $sections[$id] = $position;
3071         }
3072     }
3074     // Renumber positions
3075     $position = 0;
3076     foreach ($sections as $id => $p) {
3077         $sections[$id] = $position;
3078         $position++;
3079     }
3081     return $sections;
3085 /**
3086  * Move the module object $mod to the specified $section
3087  * If $beforemod exists then that is the module
3088  * before which $modid should be inserted
3089  * All parameters are objects
3090  */
3091 function moveto_module($mod, $section, $beforemod=NULL) {
3092     global $OUTPUT;
3094 /// Remove original module from original section
3095     if (! delete_mod_from_section($mod->id, $mod->section)) {
3096         echo $OUTPUT->notification("Could not delete module from existing section");
3097     }
3099     // if moving to a hidden section then hide module
3100     if (!$section->visible && $mod->visible) {
3101         set_coursemodule_visible($mod->id, 0);
3102     }
3104 /// Add the module into the new section
3105     course_add_cm_to_section($section->course, $mod->id, $section->section, $beforemod);
3106     return true;
3109 /**
3110  * Produces the editing buttons for a module
3111  *
3112  * @global core_renderer $OUTPUT
3113  * @staticvar type $str
3114  * @param stdClass $mod The module to produce editing buttons for
3115  * @param bool $absolute_ignored ignored - all links are absolute
3116  * @param bool $moveselect If true a move seleciton process is used (default true)
3117  * @param int $indent The current indenting
3118  * @param int $section The section to link back to
3119  * @return string XHTML for the editing buttons
3120  */
3121 function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $moveselect = true, $indent=-1, $section=null) {
3122     global $CFG, $OUTPUT, $COURSE;
3124     static $str;
3126     $coursecontext = context_course::instance($mod->course);
3127     $modcontext = context_module::instance($mod->id);
3129     $editcaps = array('moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/role:assign');
3130     $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
3132     // no permission to edit anything
3133     if (!has_any_capability($editcaps, $modcontext) and !has_all_capabilities($dupecaps, $coursecontext)) {
3134         return false;
3135     }
3137     $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
3139     if (!isset($str)) {
3140         $str = new stdClass;
3141         $str->assign         = get_string("assignroles", 'role');
3142         $str->delete         = get_string("delete");
3143         $str->move           = get_string("move");
3144         $str->moveup         = get_string("moveup");
3145         $str->movedown       = get_string("movedown");
3146         $str->moveright      = get_string("moveright");
3147         $str->moveleft       = get_string("moveleft");
3148         $str->update         = get_string("update");
3149         $str->duplicate      = get_string("duplicate");
3150         $str->hide           = get_string("hide");
3151         $str->show           = get_string("show");
3152         $str->groupsnone     = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsnone"));
3153         $str->groupsseparate = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsseparate"));
3154         $str->groupsvisible  = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsvisible"));
3155         $str->forcedgroupsnone     = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsnone"));
3156         $str->forcedgroupsseparate = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsseparate"));
3157         $str->forcedgroupsvisible  = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsvisible"));
3158         $str->edittitle = get_string('edittitle', 'moodle');
3159     }
3161     $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
3163     if ($section !== null) {
3164         $baseurl->param('sr', $section);
3165     }
3166     $actions = array();
3168     // AJAX edit title
3169     if ($mod->modname !== 'label' && $hasmanageactivities && course_ajax_enabled($COURSE)) {
3170         $actions[] = new action_link(
3171             new moodle_url($baseurl, array('update' => $mod->id)),
3172             new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')),
3173             null,
3174             array('class' => 'editing_title', 'title' => $str->edittitle)
3175         );
3176     }
3178     // leftright
3179     if ($hasmanageactivities) {
3180         if (right_to_left()) {   // Exchange arrows on RTL
3181             $rightarrow = 't/left';
3182             $leftarrow  = 't/right';
3183         } else {
3184             $rightarrow = 't/right';
3185             $leftarrow  = 't/left';
3186         }
3188         if ($indent > 0) {
3189             $actions[] = new action_link(
3190                 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
3191                 new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3192                 null,
3193                 array('class' => 'editing_moveleft', 'title' => $str->moveleft)
3194             );
3195         }
3196         if ($indent >= 0) {
3197             $actions[] = new action_link(
3198                 new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
3199                 new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3200                 null,
3201                 array('class' => 'editing_moveright', 'title' => $str->moveright)
3202             );
3203         }
3204     }
3206     // move
3207     if ($hasmanageactivities) {
3208         if ($moveselect) {
3209             $actions[] = new action_link(
3210                 new moodle_url($baseurl, array('copy' => $mod->id)),
3211                 new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3212                 null,
3213                 array('class' => 'editing_move', 'title' => $str->move)
3214             );
3215         } else {
3216             $actions[] = new action_link(
3217                 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '-1')),
3218                 new pix_icon('t/up', $str->moveup, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3219                 null,
3220                 array('class' => 'editing_moveup', 'title' => $str->moveup)
3221             );
3222             $actions[] = new action_link(
3223                 new moodle_url($baseurl, array('id' => $mod->id, 'move' => '1')),
3224                 new pix_icon('t/down', $str->movedown, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3225                 null,
3226                 array('class' => 'editing_movedown', 'title' => $str->movedown)
3227             );
3228         }
3229     }
3231     // Update
3232     if ($hasmanageactivities) {
3233         $actions[] = new action_link(
3234             new moodle_url($baseurl, array('update' => $mod->id)),
3235             new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3236             null,
3237             array('class' => 'editing_update', 'title' => $str->update)
3238         );
3239     }
3241     // Duplicate (require both target import caps to be able to duplicate and backup2 support, see modduplicate.php)
3242     if (has_all_capabilities($dupecaps, $coursecontext) && plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) {
3243         $actions[] = new action_link(
3244             new moodle_url($baseurl, array('duplicate' => $mod->id)),
3245             new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3246             null,
3247             array('class' => 'editing_duplicate', 'title' => $str->duplicate)
3248         );
3249     }
3251     // Delete
3252     if ($hasmanageactivities) {
3253         $actions[] = new action_link(
3254             new moodle_url($baseurl, array('delete' => $mod->id)),
3255             new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3256             null,
3257             array('class' => 'editing_delete', 'title' => $str->delete)
3258         );
3259     }
3261     // hideshow
3262     if (has_capability('moodle/course:activityvisibility', $modcontext)) {
3263         if ($mod->visible) {
3264             $actions[] = new action_link(
3265                 new moodle_url($baseurl, array('hide' => $mod->id)),
3266                 new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3267                 null,
3268                 array('class' => 'editing_hide', 'title' => $str->hide)
3269             );
3270         } else {
3271             $actions[] = new action_link(
3272                 new moodle_url($baseurl, array('show' => $mod->id)),
3273                 new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3274                 null,
3275                 array('class' => 'editing_show', 'title' => $str->show)
3276             );
3277         }
3278     }
3280     // groupmode
3281     if ($hasmanageactivities and $mod->groupmode !== false) {
3282         if ($mod->groupmode == SEPARATEGROUPS) {
3283             $groupmode = 0;
3284             $grouptitle = $str->groupsseparate;
3285             $forcedgrouptitle = $str->forcedgroupsseparate;
3286             $groupclass = 'editing_groupsseparate';
3287             $groupimage = 't/groups';
3288         } else if ($mod->groupmode == VISIBLEGROUPS) {
3289             $groupmode = 1;
3290             $grouptitle = $str->groupsvisible;
3291             $forcedgrouptitle = $str->forcedgroupsvisible;
3292             $groupclass = 'editing_groupsvisible';
3293             $groupimage = 't/groupv';
3294         } else {
3295             $groupmode = 2;
3296             $grouptitle = $str->groupsnone;
3297             $forcedgrouptitle = $str->forcedgroupsnone;
3298             $groupclass = 'editing_groupsnone';
3299             $groupimage = 't/groupn';
3300         }
3301         if ($mod->groupmodelink) {
3302             $actions[] = new action_link(
3303                 new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
3304                 new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3305                 null,
3306                 array('class' => $groupclass, 'title' => $grouptitle)
3307             );
3308         } else {
3309             $actions[] = new pix_icon($groupimage, $forcedgrouptitle, 'moodle', array('title' => $forcedgrouptitle, 'class' => 'iconsmall'));
3310         }
3311     }
3313     // Assign
3314     if (has_capability('moodle/role:assign', $modcontext)){
3315         $actions[] = new action_link(
3316             new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid' => $modcontext->id)),
3317             new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
3318             null,
3319             array('class' => 'editing_assign', 'title' => $str->assign)
3320         );
3321     }
3323     // The space added before the <span> is a ugly hack but required to set the CSS property white-space: nowrap
3324     // and having it to work without attaching the preceding text along with it. Hopefully the refactoring of
3325     // the course page HTML will allow this to be removed.
3326     $output = ' ' . html_writer::start_tag('span', array('class' => 'commands'));
3327     foreach ($actions as $action) {
3328         if ($action instanceof renderable) {
3329             $output .= $OUTPUT->render($action);
3330         } else {
3331             $output .= $action;
3332         }
3333     }
3334     $output .= html_writer::end_tag('span');
3335     return $output;
3338 /**
3339  * given a course object with shortname & fullname, this function will
3340  * truncate the the number of chars allowed and add ... if it was too long
3341  */
3342 function course_format_name ($course,$max=100) {
3344     $context = context_course::instance($course->id);
3345     $shortname = format_string($course->shortname, true, array('context' => $context));
3346     $fullname = format_string($course->fullname, true, array('context' => context_course::instance($course->id)));
3347     $str = $shortname.': '. $fullname;
3348     if (textlib::strlen($str) <= $max) {
3349         return $str;
3350     }
3351     else {
3352         return textlib::substr($str,0,$max-3).'...';
3353     }
3356 /**
3357  * Is the user allowed to add this type of module to this course?
3358  * @param object $course the course settings. Only $course->id is used.
3359  * @param string $modname the module name. E.g. 'forum' or 'quiz'.
3360  * @return bool whether the current user is allowed to add this type of module to this course.
3361  */
3362 function course_allowed_module($course, $modname) {
3363     if (is_numeric($modname)) {
3364         throw new coding_exception('Function course_allowed_module no longer
3365                 supports numeric module ids. Please update your code to pass the module name.');
3366     }
3368     $capability = 'mod/' . $modname . ':addinstance';
3369     if (!get_capability_info($capability)) {
3370         // Debug warning that the capability does not exist, but no more than once per page.
3371         static $warned = array();
3372         $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
3373         if (!isset($warned[$modname]) && $archetype !== MOD_ARCHETYPE_SYSTEM) {
3374             debugging('The module ' . $modname . ' does not define the standard capability ' .
3375                     $capability , DEBUG_DEVELOPER);
3376             $warned[$modname] = 1;
3377         }
3379         // If the capability does not exist, the module can always be added.
3380         return true;
3381     }
3383     $coursecontext = context_course::instance($course->id);
3384     return has_capability($capability, $coursecontext);
3387 /**
3388  * Recursively delete category including all subcategories and courses.
3389  * @param stdClass $category
3390  * @param boolean $showfeedback display some notices
3391  * @return array return deleted courses
3392  */
3393 function category_delete_full($category, $showfeedback=true) {
3394     global $CFG, $DB;
3395     require_once($CFG->libdir.'/gradelib.php');
3396     require_once($CFG->libdir.'/questionlib.php');
3397     require_once($CFG->dirroot.'/cohort/lib.php');
3399     if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
3400         foreach ($children as $childcat) {
3401             category_delete_full($childcat, $showfeedback);
3402         }
3403     }
3405     $deletedcourses = array();
3406     if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC')) {
3407         foreach ($courses as $course) {
3408             if (!delete_course($course, false)) {
3409                 throw new moodle_exception('cannotdeletecategorycourse','','',$course->shortname);
3410             }
3411             $deletedcourses[] = $course;
3412         }
3413     }
3415     // move or delete cohorts in this context
3416     cohort_delete_category($category);
3418     // now delete anything that may depend on course category context
3419     grade_course_category_delete($category->id, 0, $showfeedback);
3420     if (!question_delete_course_category($category, 0, $showfeedback)) {
3421         throw new moodle_exception('cannotdeletecategoryquestions','','',$category->name);
3422     }
3424     // finally delete the category and it's context
3425     $DB->delete_records('course_categories', array('id'=>$category->id));
3426     delete_context(CONTEXT_COURSECAT, $category->id);
3428     events_trigger('course_category_deleted', $category);
3430     return $deletedcourses;
3433 /**
3434  * Delete category, but move contents to another category.
3435  * @param object $ccategory
3436  * @param int $newparentid category id
3437  * @return bool status
3438  */
3439 function category_delete_move($category, $newparentid, $showfeedback=true) {
3440     global $CFG, $DB, $OUTPUT;
3441     require_once($CFG->libdir.'/gradelib.php');
3442     require_once($CFG->libdir.'/questionlib.php');
3443     require_once($CFG->dirroot.'/cohort/lib.php');
3445     if (!$newparentcat = $DB->get_record('course_categories', array('id'=>$newparentid))) {
3446         return false;
3447     }
3449     if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
3450         foreach ($children as $childcat) {
3451             move_category($childcat, $newparentcat);
3452         }
3453     }
3455     if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC', 'id')) {
3456         if (!move_courses(array_keys($courses), $newparentid)) {
3457             if ($showfeedback) {
3458                 echo $OUTPUT->notification("Error moving courses");
3459             }
3460             return false;
3461         }
3462         if ($showfeedback) {
3463             echo $OUTPUT->notification(get_string('coursesmovedout', '', format_string($category->name)), 'notifysuccess');
3464         }
3465     }
3467     // move or delete cohorts in this context