MDL-23510 lib.php files have to be as small as possible, the actual implementation...
[moodle.git] / mod / lesson / 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  * Standard library of functions and constants for lesson
20  *
21  * @package   lesson
22  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  **/
26 /* Do not include any libraries here! */
28 /**
29  * Given an object containing all the necessary data,
30  * (defined by the form in mod_form.php) this function
31  * will create a new instance and return the id number
32  * of the new instance.
33  *
34  * @global object
35  * @global object
36  * @param object $lesson Lesson post data from the form
37  * @return int
38  **/
39 function lesson_add_instance($data, $mform) {
40     global $DB;
42     $cmid = $data->coursemodule;
44     lesson_process_pre_save($data);
46     unset($data->mediafile);
47     $lessonid = $DB->insert_record("lesson", $data);
48     $data->id = $lessonid;
50     $context = get_context_instance(CONTEXT_MODULE, $cmid);
51     $lesson = $DB->get_record('lesson', array('id'=>$lessonid), '*', MUST_EXIST);
53     if ($filename = $mform->get_new_filename('mediafile')) {
54         if ($file = $mform->save_stored_file('mediafile', $context->id, 'mod_lesson', 'media_file', $lesson->id, '/', $filename)) {
55             $DB->set_field('lesson', 'mediafile', $file->get_filename(), array('id'=>$lesson->id));
56         }
57     }
59     lesson_process_post_save($data);
61     lesson_grade_item_update($data);
63     return $lesson->id;
64 }
66 /**
67  * Given an object containing all the necessary data,
68  * (defined by the form in mod_form.php) this function
69  * will update an existing instance with new data.
70  *
71  * @global object
72  * @param object $lesson Lesson post data from the form
73  * @return boolean
74  **/
75 function lesson_update_instance($data, $mform) {
76     global $DB;
78     $data->id = $data->instance;
79     $cmid = $data->coursemodule;
81     lesson_process_pre_save($data);
83     unset($data->mediafile);
84     $DB->update_record("lesson", $data);
86     $context = get_context_instance(CONTEXT_MODULE, $cmid);
87     if ($filename = $mform->get_new_filename('mediafile')) {
88         if ($file = $mform->save_stored_file('mediafile', $context->id, 'mod_lesson', 'media_file', $data->id, '/', $filename, true)) {
89             $DB->set_field('lesson', 'mediafile', $file->get_filename(), array('id'=>$data->id));
90         }
91     }
93     lesson_process_post_save($data);
95     // update grade item definition
96     lesson_grade_item_update($data);
98     // update grades - TODO: do it only when grading style changes
99     lesson_update_grades($data, 0, false);
101     return true;
105 /**
106  * Given an ID of an instance of this module,
107  * this function will permanently delete the instance
108  * and any data that depends on it.
109  *
110  * @global object
111  * @param int $id
112  * @return bool
113  */
114 function lesson_delete_instance($id) {
115     global $DB;
116     $lesson = $DB->get_record("lesson", array("id"=>$id), '*', MUST_EXIST);
117     $lesson = new lesson($lesson);
118     return $lesson->delete();
121 /**
122  * Given a course object, this function will clean up anything that
123  * would be leftover after all the instances were deleted
124  *
125  * @global object
126  * @param object $course an object representing the course that is being deleted
127  * @param boolean $feedback to specify if the process must output a summary of its work
128  * @return boolean
129  */
130 function lesson_delete_course($course, $feedback=true) {
131     return true;
134 /**
135  * Return a small object with summary information about what a
136  * user has done with a given particular instance of this module
137  * Used for user activity reports.
138  * $return->time = the time they did it
139  * $return->info = a short text description
140  *
141  * @global object
142  * @param object $course
143  * @param object $user
144  * @param object $mod
145  * @param object $lesson
146  * @return object
147  */
148 function lesson_user_outline($course, $user, $mod, $lesson) {
149     global $DB;
151     global $CFG;
152     require_once("$CFG->libdir/gradelib.php");
153     $grades = grade_get_grades($course->id, 'mod', 'lesson', $lesson->id, $user->id);
155     if (empty($grades->items[0]->grades)) {
156         $return->info = get_string("no")." ".get_string("attempts", "lesson");
157     } else {
158         $grade = reset($grades->items[0]->grades);
159         $return->info = get_string("grade") . ': ' . $grade->str_long_grade;
160         $return->time = $grade->dategraded;
161         $return->info = get_string("no")." ".get_string("attempts", "lesson");
162     }
163     return $return;
166 /**
167  * Print a detailed representation of what a  user has done with
168  * a given particular instance of this module, for user activity reports.
169  *
170  * @global object
171  * @param object $course
172  * @param object $user
173  * @param object $mod
174  * @param object $lesson
175  * @return bool
176  */
177 function lesson_user_complete($course, $user, $mod, $lesson) {
178     global $DB, $OUTPUT, $CFG;
180     require_once("$CFG->libdir/gradelib.php");
182     $grades = grade_get_grades($course->id, 'mod', 'lesson', $lesson->id, $user->id);
183     if (!empty($grades->items[0]->grades)) {
184         $grade = reset($grades->items[0]->grades);
185         echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
186         if ($grade->str_feedback) {
187             echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
188         }
189     }
191     $params = array ("lessonid" => $lesson->id, "userid" => $user->id);
192     if ($attempts = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND userid = :userid", $params,
193                 "retry, timeseen")) {
194         echo $OUTPUT->box_start();
195         $table = new html_table();
196         $table->head = array (get_string("attempt", "lesson"),  get_string("numberofpagesviewed", "lesson"),
197             get_string("numberofcorrectanswers", "lesson"), get_string("time"));
198         $table->width = "100%";
199         $table->align = array ("center", "center", "center", "center");
200         $table->size = array ("*", "*", "*", "*");
201         $table->cellpadding = 2;
202         $table->cellspacing = 0;
204         $retry = 0;
205         $npages = 0;
206         $ncorrect = 0;
208         foreach ($attempts as $attempt) {
209             if ($attempt->retry == $retry) {
210                 $npages++;
211                 if ($attempt->correct) {
212                     $ncorrect++;
213                 }
214                 $timeseen = $attempt->timeseen;
215             } else {
216                 $table->data[] = array($retry + 1, $npages, $ncorrect, userdate($timeseen));
217                 $retry++;
218                 $npages = 1;
219                 if ($attempt->correct) {
220                     $ncorrect = 1;
221                 } else {
222                     $ncorrect = 0;
223                 }
224             }
225         }
226         if ($npages) {
227                 $table->data[] = array($retry + 1, $npages, $ncorrect, userdate($timeseen));
228         }
229         echo html_writer::table($table);
230         echo $OUTPUT->box_end();
231     }
233     return true;
236 /**
237  * Prints lesson summaries on MyMoodle Page
238  *
239  * Prints lesson name, due date and attempt information on
240  * lessons that have a deadline that has not already passed
241  * and it is available for taking.
242  *
243  * @global object
244  * @global stdClass
245  * @global object
246  * @uses CONTEXT_MODULE
247  * @param array $courses An array of course objects to get lesson instances from
248  * @param array $htmlarray Store overview output array( course ID => 'lesson' => HTML output )
249  * @return void
250  */
251 function lesson_print_overview($courses, &$htmlarray) {
252     global $USER, $CFG, $DB, $OUTPUT;
254     if (!$lessons = get_all_instances_in_courses('lesson', $courses)) {
255         return;
256     }
258 /// Get Necessary Strings
259     $strlesson       = get_string('modulename', 'lesson');
260     $strnotattempted = get_string('nolessonattempts', 'lesson');
261     $strattempted    = get_string('lessonattempted', 'lesson');
263     $now = time();
264     foreach ($lessons as $lesson) {
265         if ($lesson->deadline != 0                                         // The lesson has a deadline
266             and $lesson->deadline >= $now                                  // And it is before the deadline has been met
267             and ($lesson->available == 0 or $lesson->available <= $now)) { // And the lesson is available
269             // Lesson name
270             if (!$lesson->visible) {
271                 $class = ' class="dimmed"';
272             } else {
273                 $class = '';
274             }
275             $str = $OUTPUT->box("$strlesson: <a$class href=\"$CFG->wwwroot/mod/lesson/view.php?id=$lesson->coursemodule\">".
276                              format_string($lesson->name).'</a>', 'name');
278             // Deadline
279             $str .= $OUTPUT->box(get_string('lessoncloseson', 'lesson', userdate($lesson->deadline)), 'info');
281             // Attempt information
282             if (has_capability('mod/lesson:manage', get_context_instance(CONTEXT_MODULE, $lesson->coursemodule))) {
283                 // Number of user attempts
284                 $attempts = $DB->count_records('lesson_attempts', array('lessonid'=>$lesson->id));
285                 $str     .= $OUTPUT->box(get_string('xattempts', 'lesson', $attempts), 'info');
286             } else {
287                 // Determine if the user has attempted the lesson or not
288                 if ($DB->count_records('lesson_attempts', array('lessonid'=>$lesson->id, 'userid'=>$USER->id))) {
289                     $str .= $OUTPUT->box($strattempted, 'info');
290                 } else {
291                     $str .= $OUTPUT->box($strnotattempted, 'info');
292                 }
293             }
294             $str = $OUTPUT->box($str, 'lesson overview');
296             if (empty($htmlarray[$lesson->course]['lesson'])) {
297                 $htmlarray[$lesson->course]['lesson'] = $str;
298             } else {
299                 $htmlarray[$lesson->course]['lesson'] .= $str;
300             }
301         }
302     }
305 /**
306  * Function to be run periodically according to the moodle cron
307  * This function searches for things that need to be done, such
308  * as sending out mail, toggling flags etc ...
309  * @global stdClass
310  * @return bool true
311  */
312 function lesson_cron () {
313     global $CFG;
315     return true;
318 /**
319  * Return grade for given user or all users.
320  *
321  * @global stdClass
322  * @global object
323  * @param int $lessonid id of lesson
324  * @param int $userid optional user id, 0 means all users
325  * @return array array of grades, false if none
326  */
327 function lesson_get_user_grades($lesson, $userid=0) {
328     global $CFG, $DB;
330     $params = array("lessonid" => $lesson->id,"lessonid2" => $lesson->id);
332     if (isset($userid)) {
333         $params["userid"] = $userid;
334         $params["userid2"] = $userid;
335         $user = "AND u.id = :userid";
336         $fuser = "AND uu.id = :userid2";
337     }
338     else {
339         $user="";
340         $fuser="";
341     }
343     if ($lesson->retake) {
344         if ($lesson->usemaxgrade) {
345             $sql = "SELECT u.id, u.id AS userid, MAX(g.grade) AS rawgrade
346                       FROM {user} u, {lesson_grades} g
347                      WHERE u.id = g.userid AND g.lessonid = :lessonid
348                            $user
349                   GROUP BY u.id";
350         } else {
351             $sql = "SELECT u.id, u.id AS userid, AVG(g.grade) AS rawgrade
352                       FROM {user} u, {lesson_grades} g
353                      WHERE u.id = g.userid AND g.lessonid = :lessonid
354                            $user
355                   GROUP BY u.id";
356         }
357         unset($params['lessonid2']);
358         unset($params['userid2']);
359     } else {
360         // use only first attempts (with lowest id in lesson_grades table)
361         $firstonly = "SELECT uu.id AS userid, MIN(gg.id) AS firstcompleted
362                         FROM {user} uu, {lesson_grades} gg
363                        WHERE uu.id = gg.userid AND gg.lessonid = :lessonid2
364                              $fuser
365                        GROUP BY uu.id";
367         $sql = "SELECT u.id, u.id AS userid, g.grade AS rawgrade
368                   FROM {user} u, {lesson_grades} g, ($firstonly) f
369                  WHERE u.id = g.userid AND g.lessonid = :lessonid
370                        AND g.id = f.firstcompleted AND g.userid=f.userid
371                        $user";
372     }
374     return $DB->get_records_sql($sql, $params);
377 /**
378  * Update grades in central gradebook
379  *
380  * @global stdclass
381  * @global object
382  * @param object $lesson
383  * @param int $userid specific user only, 0 means all
384  * @param bool $nullifnone
385  */
386 function lesson_update_grades($lesson, $userid=0, $nullifnone=true) {
387     global $CFG, $DB;
388     require_once($CFG->libdir.'/gradelib.php');
390     if ($lesson->grade == 0) {
391         lesson_grade_item_update($lesson);
393     } else if ($grades = lesson_get_user_grades($lesson, $userid)) {
394         lesson_grade_item_update($lesson, $grades);
396     } else if ($userid and $nullifnone) {
397         $grade = new object();
398         $grade->userid   = $userid;
399         $grade->rawgrade = NULL;
400         lesson_grade_item_update($lesson, $grade);
402     } else {
403         lesson_grade_item_update($lesson);
404     }
407 /**
408  * Update all grades in gradebook.
409  *
410  * @global object
411  */
412 function lesson_upgrade_grades() {
413     global $DB;
415     $sql = "SELECT COUNT('x')
416               FROM {lesson} l, {course_modules} cm, {modules} m
417              WHERE m.name='lesson' AND m.id=cm.module AND cm.instance=l.id";
418     $count = $DB->count_records_sql($sql);
420     $sql = "SELECT l.*, cm.idnumber AS cmidnumber, l.course AS courseid
421               FROM {lesson} l, {course_modules} cm, {modules} m
422              WHERE m.name='lesson' AND m.id=cm.module AND cm.instance=l.id";
423     if ($rs = $DB->get_recordset_sql($sql)) {
424         $pbar = new progress_bar('lessonupgradegrades', 500, true);
425         $i=0;
426         foreach ($rs as $lesson) {
427             $i++;
428             upgrade_set_timeout(60*5); // set up timeout, may also abort execution
429             lesson_update_grades($lesson, 0, false);
430             $pbar->update($i, $count, "Updating Lesson grades ($i/$count).");
431         }
432         $rs->close();
433     }
436 /**
437  * Create grade item for given lesson
438  *
439  * @global stdClass
440  * @uses GRADE_TYPE_VALUE
441  * @uses GRADE_TYPE_NONE
442  * @param object $lesson object with extra cmidnumber
443  * @param array|object $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
444  * @return int 0 if ok, error code otherwise
445  */
446 function lesson_grade_item_update($lesson, $grades=NULL) {
447     global $CFG;
448     if (!function_exists('grade_update')) { //workaround for buggy PHP versions
449         require_once($CFG->libdir.'/gradelib.php');
450     }
452     if (array_key_exists('cmidnumber', $lesson)) { //it may not be always present
453         $params = array('itemname'=>$lesson->name, 'idnumber'=>$lesson->cmidnumber);
454     } else {
455         $params = array('itemname'=>$lesson->name);
456     }
458     if ($lesson->grade > 0) {
459         $params['gradetype']  = GRADE_TYPE_VALUE;
460         $params['grademax']   = $lesson->grade;
461         $params['grademin']   = 0;
463     } else {
464         $params['gradetype']  = GRADE_TYPE_NONE;
465     }
467     if ($grades  === 'reset') {
468         $params['reset'] = true;
469         $grades = NULL;
470     } else if (!empty($grades)) {
471         // Need to calculate raw grade (Note: $grades has many forms)
472         if (is_object($grades)) {
473             $grades = array($grades->userid => $grades);
474         } else if (array_key_exists('userid', $grades)) {
475             $grades = array($grades['userid'] => $grades);
476         }
477         foreach ($grades as $key => $grade) {
478             if (!is_array($grade)) {
479                 $grades[$key] = $grade = (array) $grade;
480             }
481             $grades[$key]['rawgrade'] = ($grade['rawgrade'] * $lesson->grade / 100);
482         }
483     }
485     return grade_update('mod/lesson', $lesson->course, 'mod', 'lesson', $lesson->id, 0, $grades, $params);
488 /**
489  * Delete grade item for given lesson
490  *
491  * @global stdClass
492  * @param object $lesson object
493  * @return object lesson
494  */
495 function lesson_grade_item_delete($lesson) {
496     global $CFG;
501 /**
502  * Must return an array of user records (all data) who are participants
503  * for a given instance of lesson. Must include every user involved
504  * in the instance, independent of his role (student, teacher, admin...)
505  *
506  * @global stdClass
507  * @global object
508  * @param int $lessonid
509  * @return array
510  */
511 function lesson_get_participants($lessonid) {
512     global $CFG, $DB;
514     //Get students
515     $params = array ("lessonid" => $lessonid);
516     $students = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
517                                  FROM {user} u,
518                                       {lesson_attempts} a
519                                  WHERE a.lessonid = :lessonid and
520                                        u.id = a.userid", $params);
522     //Return students array (it contains an array of unique users)
523     return ($students);
526 /**
527  * @return array
528  */
529 function lesson_get_view_actions() {
530     return array('view','view all');
533 /**
534  * @return array
535  */
536 function lesson_get_post_actions() {
537     return array('end','start');
540 /**
541  * Runs any processes that must run before
542  * a lesson insert/update
543  *
544  * @global object
545  * @param object $lesson Lesson form data
546  * @return void
547  **/
548 function lesson_process_pre_save(&$lesson) {
549     global $DB;
551     $lesson->timemodified = time();
553     if (empty($lesson->timed)) {
554         $lesson->timed = 0;
555     }
556     if (empty($lesson->timespent) or !is_numeric($lesson->timespent) or $lesson->timespent < 0) {
557         $lesson->timespent = 0;
558     }
559     if (!isset($lesson->completed)) {
560         $lesson->completed = 0;
561     }
562     if (empty($lesson->gradebetterthan) or !is_numeric($lesson->gradebetterthan) or $lesson->gradebetterthan < 0) {
563         $lesson->gradebetterthan = 0;
564     } else if ($lesson->gradebetterthan > 100) {
565         $lesson->gradebetterthan = 100;
566     }
568     if (empty($lesson->width)) {
569         $lesson->width = 640;
570     }
571     if (empty($lesson->height)) {
572         $lesson->height = 480;
573     }
574     if (empty($lesson->bgcolor)) {
575         $lesson->bgcolor = '#FFFFFF';
576     }
578     // Conditions for dependency
579     $conditions = new stdClass;
580     $conditions->timespent = $lesson->timespent;
581     $conditions->completed = $lesson->completed;
582     $conditions->gradebetterthan = $lesson->gradebetterthan;
583     $lesson->conditions = serialize($conditions);
584     unset($lesson->timespent);
585     unset($lesson->completed);
586     unset($lesson->gradebetterthan);
588     if (empty($lesson->password)) {
589         unset($lesson->password);
590     }
593 /**
594  * Runs any processes that must be run
595  * after a lesson insert/update
596  *
597  * @global object
598  * @param object $lesson Lesson form data
599  * @return void
600  **/
601 function lesson_process_post_save(&$lesson) {
602     global $DB, $CFG;
603     require_once($CFG->dirroot.'/calendar/lib.php');
604     require_once($CFG->dirroot . '/mod/lesson/locallib.php');
606     if ($events = $DB->get_records('event', array('modulename'=>'lesson', 'instance'=>$lesson->id))) {
607         foreach($events as $event) {
608             $event = calendar_event::load($event->id);
609             $event->delete();
610         }
611     }
613     $event = new stdClass;
614     $event->description = $lesson->name;
615     $event->courseid    = $lesson->course;
616     $event->groupid     = 0;
617     $event->userid      = 0;
618     $event->modulename  = 'lesson';
619     $event->instance    = $lesson->id;
620     $event->eventtype   = 'open';
621     $event->timestart   = $lesson->available;
623     $event->visible     = instance_is_visible('lesson', $lesson);
625     $event->timeduration = ($lesson->deadline - $lesson->available);
627     if ($lesson->deadline and $lesson->available and $event->timeduration <= LESSON_MAX_EVENT_LENGTH) {
628         // Single event for the whole lesson.
629         $event->name = $lesson->name;
630         calendar_event::create(clone($event));
631     } else {
632         // Separate start and end events.
633         $event->timeduration  = 0;
634         if ($lesson->available) {
635             $event->name = $lesson->name.' ('.get_string('lessonopens', 'lesson').')';
636             calendar_event::create(clone($event));
637         } else if ($lesson->deadline) {
638             $event->name      = $lesson->name.' ('.get_string('lessoncloses', 'lesson').')';
639             $event->timestart = $lesson->deadline;
640             $event->eventtype = 'close';
641             calendar_event::create(clone($event));
642         }
643     }
647 /**
648  * Implementation of the function for printing the form elements that control
649  * whether the course reset functionality affects the lesson.
650  *
651  * @param $mform form passed by reference
652  */
653 function lesson_reset_course_form_definition(&$mform) {
654     $mform->addElement('header', 'lessonheader', get_string('modulenameplural', 'lesson'));
655     $mform->addElement('advcheckbox', 'reset_lesson', get_string('deleteallattempts','lesson'));
658 /**
659  * Course reset form defaults.
660  * @param object $course
661  * @return array
662  */
663 function lesson_reset_course_form_defaults($course) {
664     return array('reset_lesson'=>1);
667 /**
668  * Removes all grades from gradebook
669  *
670  * @global stdClass
671  * @global object
672  * @param int $courseid
673  * @param string optional type
674  */
675 function lesson_reset_gradebook($courseid, $type='') {
676     global $CFG, $DB;
678     $sql = "SELECT l.*, cm.idnumber as cmidnumber, l.course as courseid
679               FROM {lesson} l, {course_modules} cm, {modules} m
680              WHERE m.name='lesson' AND m.id=cm.module AND cm.instance=l.id AND l.course=:course";
681     $params = array ("course" => $courseid);
682     if ($lessons = $DB->get_records_sql($sql,$params)) {
683         foreach ($lessons as $lesson) {
684             lesson_grade_item_update($lesson, 'reset');
685         }
686     }
689 /**
690  * Actual implementation of the rest coures functionality, delete all the
691  * lesson attempts for course $data->courseid.
692  *
693  * @global stdClass
694  * @global object
695  * @param object $data the data submitted from the reset course.
696  * @return array status array
697  */
698 function lesson_reset_userdata($data) {
699     global $CFG, $DB;
701     $componentstr = get_string('modulenameplural', 'lesson');
702     $status = array();
704     if (!empty($data->reset_lesson)) {
705         $lessonssql = "SELECT l.id
706                          FROM {lesson} l
707                         WHERE l.course=:course";
709         $params = array ("course" => $data->courseid);
710         $DB->delete_records_select('lesson_timer', "lessonid IN ($lessonssql)", $params);
711         $DB->delete_records_select('lesson_high_scores', "lessonid IN ($lessonssql)", $params);
712         $DB->delete_records_select('lesson_grades', "lessonid IN ($lessonssql)", $params);
713         $DB->delete_records_select('lesson_attempts', "lessonid IN ($lessonssql)", $params);
715         // remove all grades from gradebook
716         if (empty($data->reset_gradebook_grades)) {
717             lesson_reset_gradebook($data->courseid);
718         }
720         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallattempts', 'lesson'), 'error'=>false);
721     }
723     /// updating dates - shift may be negative too
724     if ($data->timeshift) {
725         shift_course_mod_dates('lesson', array('available', 'deadline'), $data->timeshift, $data->courseid);
726         $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
727     }
729     return $status;
732 /**
733  * Returns all other caps used in module
734  * @return array
735  */
736 function lesson_get_extra_capabilities() {
737     return array('moodle/site:accessallgroups');
740 /**
741  * @uses FEATURE_GROUPS
742  * @uses FEATURE_GROUPINGS
743  * @uses FEATURE_GROUPMEMBERSONLY
744  * @uses FEATURE_MOD_INTRO
745  * @uses FEATURE_COMPLETION_TRACKS_VIEWS
746  * @uses FEATURE_GRADE_HAS_GRADE
747  * @uses FEATURE_GRADE_OUTCOMES
748  * @param string $feature FEATURE_xx constant for requested feature
749  * @return mixed True if module supports feature, false if not, null if doesn't know
750  */
751 function lesson_supports($feature) {
752     switch($feature) {
753         case FEATURE_GROUPS:                  return false;
754         case FEATURE_GROUPINGS:               return false;
755         case FEATURE_GROUPMEMBERSONLY:        return true;
756         case FEATURE_MOD_INTRO:               return false;
757         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
758         case FEATURE_GRADE_HAS_GRADE:         return true;
759         case FEATURE_GRADE_OUTCOMES:          return true;
760         case FEATURE_BACKUP_MOODLE2:          return true;
761         default: return null;
762     }
765 /**
766  * This function extends the global navigation for the site.
767  * It is important to note that you should not rely on PAGE objects within this
768  * body of code as there is no guarantee that during an AJAX request they are
769  * available
770  *
771  * @param navigation_node $navigation The lesson node within the global navigation
772  * @param stdClass $course The course object returned from the DB
773  * @param stdClass $module The module object returned from the DB
774  * @param stdClass $cm The course module instance returned from the DB
775  */
776 function lesson_extend_navigation($navigation, $course, $module, $cm) {
777     /**
778      * This is currently just a stub so  that it can be easily expanded upon.
779      * When expanding just remove this comment and the line below and then add
780      * you content.
781      */
782     $navigation->nodetype = navigation_node::NODETYPE_LEAF;
785 /**
786  * This function extends the settings navigation block for the site.
787  *
788  * It is safe to rely on PAGE here as we will only ever be within the module
789  * context when this is called
790  *
791  * @param settings_navigation $settings
792  * @param navigation_node $lessonnode
793  */
794 function lesson_extend_settings_navigation($settings, $lessonnode) {
795     global $PAGE, $DB;
797     $canedit = has_capability('mod/lesson:edit', $PAGE->cm->context);
799     $url = new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id));
800     $lessonnode->add(get_string('preview', 'lesson'), $url);
802     if ($canedit) {
803         $url = new moodle_url('/mod/lesson/edit.php', array('id'=>$PAGE->cm->id));
804         $lessonnode->add(get_string('edit', 'lesson'), $url);
805     }
807     if (has_capability('mod/lesson:manage', $PAGE->cm->context)) {
808         $reportsnode = $lessonnode->add(get_string('reports', 'lesson'));
809         $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportoverview'));
810         $reportsnode->add(get_string('overview', 'lesson'), $url);
811         $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportdetail'));
812         $reportsnode->add(get_string('detailedstats', 'lesson'), $url);
813     }
815     if ($canedit) {
816         $url = new moodle_url('/mod/lesson/essay.php', array('id'=>$PAGE->cm->id));
817         $lessonnode->add(get_string('manualgrading', 'lesson'), $url);
818     }
820     if ($PAGE->activityrecord->highscores) {
821         $url = new moodle_url('/mod/lesson/highscores.php', array('id'=>$PAGE->cm->id));
822         $lessonnode->add(get_string('highscores', 'lesson'), $url);
823     }
826 /**
827  * Get list of available import or export formats
828  *
829  * Copied and modified from lib/questionlib.php
830  *
831  * @param string $type 'import' if import list, otherwise export list assumed
832  * @return array sorted list of import/export formats available
833  */
834 function lesson_get_import_export_formats($type) {
835     global $CFG;
836     $fileformats = get_plugin_list("qformat");
838     $fileformatname=array();
839     foreach ($fileformats as $fileformat=>$fdir) {
840         $format_file = "$fdir/format.php";
841         if (file_exists($format_file) ) {
842             require_once($format_file);
843         } else {
844             continue;
845         }
846         $classname = "qformat_$fileformat";
847         $format_class = new $classname();
848         if ($type=='import') {
849             $provided = $format_class->provide_import();
850         } else {
851             $provided = $format_class->provide_export();
852         }
853         if ($provided) {
854             //TODO: do NOT rely on [[]] any more!!
855             $formatname = get_string($fileformat, 'quiz');
856             if ($formatname == "[[$fileformat]]") {
857                 $formatname = get_string($fileformat, 'qformat_'.$fileformat);
858                 if ($formatname == "[[$fileformat]]") {
859                     $formatname = $fileformat;  // Just use the raw folder name
860                 }
861             }
862             $fileformatnames[$fileformat] = $formatname;
863         }
864     }
865     natcasesort($fileformatnames);
867     return $fileformatnames;
870 /**
871  * Serves the lesson attachments. Implements needed access control ;-)
872  *
873  * @param object $course
874  * @param object $cm
875  * @param object $context
876  * @param string $filearea
877  * @param array $args
878  * @param bool $forcedownload
879  * @return bool false if file not found, does not return if found - justsend the file
880  */
881 function lesson_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
882     global $CFG, $DB;
884     if ($context->contextlevel != CONTEXT_MODULE) {
885         return false;
886     }
888     $fileareas = lesson_get_file_areas();
889     if (!array_key_exists($filearea, $fileareas)) {
890         return false;
891     }
893     if (!$lesson = $DB->get_record('lesson', array('id'=>$cm->instance))) {
894         return false;
895     }
897     require_course_login($course, true, $cm);
899     if ($filearea === 'page_contents') {
900         $pageid = (int)array_shift($args);
901         if (!$page = $DB->get_record('lesson_pages', array('id'=>$pageid))) {
902             return false;
903         }
904         $fullpath = "/$context->id/mod_lesson/$filearea/$pageid/".implode('/', $args);
905         $forcedownload = true; //TODO: this is strange (skodak)
906     } else {
908         $fullpath = "/$context->id/mod_lesson/$filearea/".implode('/', $args);
909     }
911     $fs = get_file_storage();
912     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
913         return false;
914     }
916     // finally send the file
917     send_stored_file($file, 0, 0, $forcedownload); // download MUST be forced - security!
920 /**
921  * Returns an array of file areas
922  * @return array
923  */
924 function lesson_get_file_areas() {
925     $areas = array();
926     $areas['page_contents'] = 'page_contents'; //TODO: localize!!!!
927     $areas['media_files'] = 'media_files'; //TODO: localize!!!!
930 /**
931  * Returns a file_info_stored object for the file being requested here
932  *
933  * @global <type> $CFG
934  * @param file_browse $browser
935  * @param array $areas
936  * @param object $course
937  * @param object $cm
938  * @param object $context
939  * @param string $filearea
940  * @param int $itemid
941  * @param string $filepath
942  * @param string $filename
943  * @return file_info_stored
944  */
945 function lesson_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
946     global $CFG;
947     if (has_capability('moodle/course:managefiles', $context)) {
948         // no peaking here for students!!
949         return null;
950     }
952     $fs = get_file_storage();
953     $filepath = is_null($filepath) ? '/' : $filepath;
954     $filename = is_null($filename) ? '.' : $filename;
955     $urlbase = $CFG->wwwroot.'/pluginfile.php';
956     if (!$storedfile = $fs->get_file($context->id, 'mod_lesson', $filearea, $itemid, $filepath, $filename)) {
957         return null;
958     }
959     return new file_info_stored($browser, $context, $storedfile, $urlbase, $filearea, $itemid, true, true, false);