Merge branch 'MDL-52570-master' of git://github.com/jleyva/moodle
[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 mod_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 defined('MOODLE_INTERNAL') || die();
28 /* Do not include any libraries here! */
30 /**
31  * Given an object containing all the necessary data,
32  * (defined by the form in mod_form.php) this function
33  * will create a new instance and return the id number
34  * of the new instance.
35  *
36  * @global object
37  * @global object
38  * @param object $lesson Lesson post data from the form
39  * @return int
40  **/
41 function lesson_add_instance($data, $mform) {
42     global $DB;
44     $cmid = $data->coursemodule;
45     $draftitemid = $data->mediafile;
46     $context = context_module::instance($cmid);
48     lesson_process_pre_save($data);
50     unset($data->mediafile);
51     $lessonid = $DB->insert_record("lesson", $data);
52     $data->id = $lessonid;
54     lesson_update_media_file($lessonid, $context, $draftitemid);
56     lesson_process_post_save($data);
58     lesson_grade_item_update($data);
60     return $lessonid;
61 }
63 /**
64  * Given an object containing all the necessary data,
65  * (defined by the form in mod_form.php) this function
66  * will update an existing instance with new data.
67  *
68  * @global object
69  * @param object $lesson Lesson post data from the form
70  * @return boolean
71  **/
72 function lesson_update_instance($data, $mform) {
73     global $DB;
75     $data->id = $data->instance;
76     $cmid = $data->coursemodule;
77     $draftitemid = $data->mediafile;
78     $context = context_module::instance($cmid);
80     lesson_process_pre_save($data);
82     unset($data->mediafile);
83     $DB->update_record("lesson", $data);
85     lesson_update_media_file($data->id, $context, $draftitemid);
87     lesson_process_post_save($data);
89     // update grade item definition
90     lesson_grade_item_update($data);
92     // update grades - TODO: do it only when grading style changes
93     lesson_update_grades($data, 0, false);
95     return true;
96 }
98 /**
99  * This function updates the events associated to the lesson.
100  * If $override is non-zero, then it updates only the events
101  * associated with the specified override.
102  *
103  * @uses LESSON_MAX_EVENT_LENGTH
104  * @param object $lesson the lesson object.
105  * @param object $override (optional) limit to a specific override
106  */
107 function lesson_update_events($lesson, $override = null) {
108     global $CFG, $DB;
110     require_once($CFG->dirroot . '/calendar/lib.php');
112     // Load the old events relating to this lesson.
113     $conds = array('modulename' => 'lesson',
114                    'instance' => $lesson->id);
115     if (!empty($override)) {
116         // Only load events for this override.
117         if (isset($override->userid)) {
118             $conds['userid'] = $override->userid;
119         } else {
120             $conds['groupid'] = $override->groupid;
121         }
122     }
123     $oldevents = $DB->get_records('event', $conds);
125     // Now make a todo list of all that needs to be updated.
126     if (empty($override)) {
127         // We are updating the primary settings for the lesson, so we
128         // need to add all the overrides.
129         $overrides = $DB->get_records('lesson_overrides', array('lessonid' => $lesson->id));
130         // As well as the original lesson (empty override).
131         $overrides[] = new stdClass();
132     } else {
133         // Just do the one override.
134         $overrides = array($override);
135     }
137     foreach ($overrides as $current) {
138         $groupid   = isset($current->groupid) ? $current->groupid : 0;
139         $userid    = isset($current->userid) ? $current->userid : 0;
140         $available  = isset($current->available) ? $current->available : $lesson->available;
141         $deadline = isset($current->deadline) ? $current->deadline : $lesson->deadline;
143         // Only add open/close events for an override if they differ from the lesson default.
144         $addopen  = empty($current->id) || !empty($current->available);
145         $addclose = empty($current->id) || !empty($current->deadline);
147         if (!empty($lesson->coursemodule)) {
148             $cmid = $lesson->coursemodule;
149         } else {
150             $cmid = get_coursemodule_from_instance('lesson', $lesson->id, $lesson->course)->id;
151         }
153         $event = new stdClass();
154         $event->description = format_module_intro('lesson', $lesson, $cmid);
155         // Events module won't show user events when the courseid is nonzero.
156         $event->courseid    = ($userid) ? 0 : $lesson->course;
157         $event->groupid     = $groupid;
158         $event->userid      = $userid;
159         $event->modulename  = 'lesson';
160         $event->instance    = $lesson->id;
161         $event->timestart   = $available;
162         $event->timeduration = max($deadline - $available, 0);
163         $event->visible     = instance_is_visible('lesson', $lesson);
164         $event->eventtype   = 'open';
166         // Determine the event name.
167         if ($groupid) {
168             $params = new stdClass();
169             $params->lesson = $lesson->name;
170             $params->group = groups_get_group_name($groupid);
171             if ($params->group === false) {
172                 // Group doesn't exist, just skip it.
173                 continue;
174             }
175             $eventname = get_string('overridegroupeventname', 'lesson', $params);
176         } else if ($userid) {
177             $params = new stdClass();
178             $params->lesson = $lesson->name;
179             $eventname = get_string('overrideusereventname', 'lesson', $params);
180         } else {
181             $eventname = $lesson->name;
182         }
183         if ($addopen or $addclose) {
184             if ($deadline and $available and $event->timeduration <= LESSON_MAX_EVENT_LENGTH) {
185                 // Single event for the whole lesson.
186                 if ($oldevent = array_shift($oldevents)) {
187                     $event->id = $oldevent->id;
188                 } else {
189                     unset($event->id);
190                 }
191                 $event->name = $eventname;
192                 // The method calendar_event::create will reuse a db record if the id field is set.
193                 calendar_event::create($event);
194             } else {
195                 // Separate start and end events.
196                 $event->timeduration  = 0;
197                 if ($available && $addopen) {
198                     if ($oldevent = array_shift($oldevents)) {
199                         $event->id = $oldevent->id;
200                     } else {
201                         unset($event->id);
202                     }
203                     $event->name = $eventname.' ('.get_string('lessonopens', 'lesson').')';
204                     // The method calendar_event::create will reuse a db record if the id field is set.
205                     calendar_event::create($event);
206                 }
207                 if ($deadline && $addclose) {
208                     if ($oldevent = array_shift($oldevents)) {
209                         $event->id = $oldevent->id;
210                     } else {
211                         unset($event->id);
212                     }
213                     $event->name      = $eventname.' ('.get_string('lessoncloses', 'lesson').')';
214                     $event->timestart = $deadline;
215                     $event->eventtype = 'close';
216                     calendar_event::create($event);
217                 }
218             }
219         }
220     }
222     // Delete any leftover events.
223     foreach ($oldevents as $badevent) {
224         $badevent = calendar_event::load($badevent);
225         $badevent->delete();
226     }
229 /**
230  * This standard function will check all instances of this module
231  * and make sure there are up-to-date events created for each of them.
232  * If courseid = 0, then every lesson event in the site is checked, else
233  * only lesson events belonging to the course specified are checked.
234  * This function is used, in its new format, by restore_refresh_events()
235  *
236  * @param int $courseid
237  * @return bool
238  */
239 function lesson_refresh_events($courseid = 0) {
240     global $DB;
242     if ($courseid == 0) {
243         if (!$lessons = $DB->get_records('lessons')) {
244             return true;
245         }
246     } else {
247         if (!$lessons = $DB->get_records('lesson', array('course' => $courseid))) {
248             return true;
249         }
250     }
252     foreach ($lessons as $lesson) {
253         lesson_update_events($lesson);
254     }
256     return true;
259 /**
260  * Given an ID of an instance of this module,
261  * this function will permanently delete the instance
262  * and any data that depends on it.
263  *
264  * @global object
265  * @param int $id
266  * @return bool
267  */
268 function lesson_delete_instance($id) {
269     global $DB, $CFG;
270     require_once($CFG->dirroot . '/mod/lesson/locallib.php');
272     $lesson = $DB->get_record("lesson", array("id"=>$id), '*', MUST_EXIST);
273     $lesson = new lesson($lesson);
274     return $lesson->delete();
277 /**
278  * Given a course object, this function will clean up anything that
279  * would be leftover after all the instances were deleted
280  *
281  * @global object
282  * @param object $course an object representing the course that is being deleted
283  * @param boolean $feedback to specify if the process must output a summary of its work
284  * @return boolean
285  */
286 function lesson_delete_course($course, $feedback=true) {
287     return true;
290 /**
291  * Return a small object with summary information about what a
292  * user has done with a given particular instance of this module
293  * Used for user activity reports.
294  * $return->time = the time they did it
295  * $return->info = a short text description
296  *
297  * @global object
298  * @param object $course
299  * @param object $user
300  * @param object $mod
301  * @param object $lesson
302  * @return object
303  */
304 function lesson_user_outline($course, $user, $mod, $lesson) {
305     global $CFG;
307     require_once("$CFG->libdir/gradelib.php");
308     $grades = grade_get_grades($course->id, 'mod', 'lesson', $lesson->id, $user->id);
310     $return = new stdClass();
311     if (empty($grades->items[0]->grades)) {
312         $return->info = get_string("no")." ".get_string("attempts", "lesson");
313     } else {
314         $grade = reset($grades->items[0]->grades);
315         $return->info = get_string("grade") . ': ' . $grade->str_long_grade;
317         //datesubmitted == time created. dategraded == time modified or time overridden
318         //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
319         //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
320         if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
321             $return->time = $grade->dategraded;
322         } else {
323             $return->time = $grade->datesubmitted;
324         }
325     }
326     return $return;
329 /**
330  * Print a detailed representation of what a  user has done with
331  * a given particular instance of this module, for user activity reports.
332  *
333  * @global object
334  * @param object $course
335  * @param object $user
336  * @param object $mod
337  * @param object $lesson
338  * @return bool
339  */
340 function lesson_user_complete($course, $user, $mod, $lesson) {
341     global $DB, $OUTPUT, $CFG;
343     require_once("$CFG->libdir/gradelib.php");
345     $grades = grade_get_grades($course->id, 'mod', 'lesson', $lesson->id, $user->id);
347     // Display the grade and feedback.
348     if (empty($grades->items[0]->grades)) {
349         echo $OUTPUT->container(get_string("nolessonattempts", "lesson"));
350     } else {
351         $grade = reset($grades->items[0]->grades);
352         if (empty($grade->grade)) {
353             // Check to see if it an ungraded / incomplete attempt.
354             $sql = "SELECT *
355                       FROM {lesson_timer}
356                      WHERE lessonid = :lessonid
357                        AND userid = :userid
358                      ORDER by starttime desc";
359             $params = array('lessonid' => $lesson->id, 'userid' => $user->id);
361             if ($attempt = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE)) {
362                 if ($attempt->completed) {
363                     $status = get_string("completed", "lesson");
364                 } else {
365                     $status = get_string("notyetcompleted", "lesson");
366                 }
367             } else {
368                 $status = get_string("nolessonattempts", "lesson");
369             }
370         } else {
371             $status = get_string("grade") . ': ' . $grade->str_long_grade;
372         }
374         // Display the grade or lesson status if there isn't one.
375         echo $OUTPUT->container($status);
377         if ($grade->str_feedback) {
378             echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
379         }
380     }
382     // Display the lesson progress.
383     // Attempt, pages viewed, questions answered, correct answers, time.
384     $params = array ("lessonid" => $lesson->id, "userid" => $user->id);
385     $attempts = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND userid = :userid", $params, "retry, timeseen");
386     $branches = $DB->get_records_select("lesson_branch", "lessonid = :lessonid AND userid = :userid", $params, "retry, timeseen");
387     if (!empty($attempts) or !empty($branches)) {
388         echo $OUTPUT->box_start();
389         $table = new html_table();
390         // Table Headings.
391         $table->head = array (get_string("attemptheader", "lesson"),
392             get_string("totalpagesviewedheader", "lesson"),
393             get_string("numberofpagesviewedheader", "lesson"),
394             get_string("numberofcorrectanswersheader", "lesson"),
395             get_string("time"));
396         $table->width = "100%";
397         $table->align = array ("center", "center", "center", "center", "center");
398         $table->size = array ("*", "*", "*", "*", "*");
399         $table->cellpadding = 2;
400         $table->cellspacing = 0;
402         $retry = 0;
403         $nquestions = 0;
404         $npages = 0;
405         $ncorrect = 0;
407         // Filter question pages (from lesson_attempts).
408         foreach ($attempts as $attempt) {
409             if ($attempt->retry == $retry) {
410                 $npages++;
411                 $nquestions++;
412                 if ($attempt->correct) {
413                     $ncorrect++;
414                 }
415                 $timeseen = $attempt->timeseen;
416             } else {
417                 $table->data[] = array($retry + 1, $npages, $nquestions, $ncorrect, userdate($timeseen));
418                 $retry++;
419                 $nquestions = 1;
420                 $npages = 1;
421                 if ($attempt->correct) {
422                     $ncorrect = 1;
423                 } else {
424                     $ncorrect = 0;
425                 }
426             }
427         }
429         // Filter content pages (from lesson_branch).
430         foreach ($branches as $branch) {
431             if ($branch->retry == $retry) {
432                 $npages++;
434                 $timeseen = $branch->timeseen;
435             } else {
436                 $table->data[] = array($retry + 1, $npages, $nquestions, $ncorrect, userdate($timeseen));
437                 $retry++;
438                 $npages = 1;
439             }
440         }
441         if ($npages > 0) {
442                 $table->data[] = array($retry + 1, $npages, $nquestions, $ncorrect, userdate($timeseen));
443         }
444         echo html_writer::table($table);
445         echo $OUTPUT->box_end();
446     }
448     return true;
451 /**
452  * Prints lesson summaries on MyMoodle Page
453  *
454  * Prints lesson name, due date and attempt information on
455  * lessons that have a deadline that has not already passed
456  * and it is available for taking.
457  *
458  * @global object
459  * @global stdClass
460  * @global object
461  * @uses CONTEXT_MODULE
462  * @param array $courses An array of course objects to get lesson instances from
463  * @param array $htmlarray Store overview output array( course ID => 'lesson' => HTML output )
464  * @return void
465  */
466 function lesson_print_overview($courses, &$htmlarray) {
467     global $USER, $CFG, $DB, $OUTPUT;
469     if (!$lessons = get_all_instances_in_courses('lesson', $courses)) {
470         return;
471     }
473     // Get all of the current users attempts on all lessons.
474     $params = array($USER->id);
475     $sql = 'SELECT lessonid, userid, count(userid) as attempts
476               FROM {lesson_grades}
477              WHERE userid = ?
478           GROUP BY lessonid, userid';
479     $allattempts = $DB->get_records_sql($sql, $params);
480     $completedattempts = array();
481     foreach ($allattempts as $myattempt) {
482         $completedattempts[$myattempt->lessonid] = $myattempt->attempts;
483     }
485     // Get the current course ID.
486     $listoflessons = array();
487     foreach ($lessons as $lesson) {
488         $listoflessons[] = $lesson->id;
489     }
490     // Get the last page viewed by the current user for every lesson in this course.
491     list($insql, $inparams) = $DB->get_in_or_equal($listoflessons, SQL_PARAMS_NAMED);
492     $dbparams = array_merge($inparams, array('userid' => $USER->id));
494     // Get the lesson attempts for the user that have the maximum 'timeseen' value.
495     $select = "SELECT l.id, l.timeseen, l.lessonid, l.userid, l.retry, l.pageid, l.answerid as nextpageid, p.qtype ";
496     $from = "FROM {lesson_attempts} l
497              JOIN (
498                    SELECT idselect.lessonid, idselect.userid, MAX(idselect.id) AS id
499                      FROM {lesson_attempts} idselect
500                      JOIN (
501                            SELECT lessonid, userid, MAX(timeseen) AS timeseen
502                              FROM {lesson_attempts}
503                             WHERE userid = :userid
504                               AND lessonid $insql
505                          GROUP BY userid, lessonid
506                            ) timeselect
507                        ON timeselect.timeseen = idselect.timeseen
508                       AND timeselect.userid = idselect.userid
509                       AND timeselect.lessonid = idselect.lessonid
510                  GROUP BY idselect.userid, idselect.lessonid
511                    ) aid
512                ON l.id = aid.id
513              JOIN {lesson_pages} p
514                ON l.pageid = p.id ";
515     $lastattempts = $DB->get_records_sql($select . $from, $dbparams);
517     // Now, get the lesson branches for the user that have the maximum 'timeseen' value.
518     $select = "SELECT l.id, l.timeseen, l.lessonid, l.userid, l.retry, l.pageid, l.nextpageid, p.qtype ";
519     $from = str_replace('{lesson_attempts}', '{lesson_branch}', $from);
520     $lastbranches = $DB->get_records_sql($select . $from, $dbparams);
522     $lastviewed = array();
523     foreach ($lastattempts as $lastattempt) {
524         $lastviewed[$lastattempt->lessonid] = $lastattempt;
525     }
527     // Go through the branch times and record the 'timeseen' value if it doesn't exist
528     // for the lesson, or replace it if it exceeds the current recorded time.
529     foreach ($lastbranches as $lastbranch) {
530         if (!isset($lastviewed[$lastbranch->lessonid])) {
531             $lastviewed[$lastbranch->lessonid] = $lastbranch;
532         } else if ($lastviewed[$lastbranch->lessonid]->timeseen < $lastbranch->timeseen) {
533             $lastviewed[$lastbranch->lessonid] = $lastbranch;
534         }
535     }
537     // Since we have lessons in this course, now include the constants we need.
538     require_once($CFG->dirroot . '/mod/lesson/locallib.php');
540     $now = time();
541     foreach ($lessons as $lesson) {
542         if ($lesson->deadline != 0                                         // The lesson has a deadline
543             and $lesson->deadline >= $now                                  // And it is before the deadline has been met
544             and ($lesson->available == 0 or $lesson->available <= $now)) { // And the lesson is available
546             // Visibility.
547             $class = (!$lesson->visible) ? 'dimmed' : '';
549             // Context.
550             $context = context_module::instance($lesson->coursemodule);
552             // Link to activity.
553             $url = new moodle_url('/mod/lesson/view.php', array('id' => $lesson->coursemodule));
554             $url = html_writer::link($url, format_string($lesson->name, true, array('context' => $context)), array('class' => $class));
555             $str = $OUTPUT->box(get_string('lessonname', 'lesson', $url), 'name');
557             // Deadline.
558             $str .= $OUTPUT->box(get_string('lessoncloseson', 'lesson', userdate($lesson->deadline)), 'info');
560             // Attempt information.
561             if (has_capability('mod/lesson:manage', $context)) {
562                 // This is a teacher, Get the Number of user attempts.
563                 $attempts = $DB->count_records('lesson_grades', array('lessonid' => $lesson->id));
564                 $str     .= $OUTPUT->box(get_string('xattempts', 'lesson', $attempts), 'info');
565                 $str      = $OUTPUT->box($str, 'lesson overview');
566             } else {
567                 // This is a student, See if the user has at least started the lesson.
568                 if (isset($lastviewed[$lesson->id]->timeseen)) {
569                     // See if the user has finished this attempt.
570                     if (isset($completedattempts[$lesson->id]) &&
571                              ($completedattempts[$lesson->id] == ($lastviewed[$lesson->id]->retry + 1))) {
572                         // Are additional attempts allowed?
573                         if ($lesson->retake) {
574                             // User can retake the lesson.
575                             $str .= $OUTPUT->box(get_string('additionalattemptsremaining', 'lesson'), 'info');
576                             $str = $OUTPUT->box($str, 'lesson overview');
577                         } else {
578                             // User has completed the lesson and no retakes are allowed.
579                             $str = '';
580                         }
582                     } else {
583                         // The last attempt was not finished or the lesson does not contain questions.
584                         // See if the last page viewed was a branchtable.
585                         require_once($CFG->dirroot . '/mod/lesson/pagetypes/branchtable.php');
586                         if ($lastviewed[$lesson->id]->qtype == LESSON_PAGE_BRANCHTABLE) {
587                             // See if the next pageid is the end of lesson.
588                             if ($lastviewed[$lesson->id]->nextpageid == LESSON_EOL) {
589                                 // The last page viewed was the End of Lesson.
590                                 if ($lesson->retake) {
591                                     // User can retake the lesson.
592                                     $str .= $OUTPUT->box(get_string('additionalattemptsremaining', 'lesson'), 'info');
593                                     $str = $OUTPUT->box($str, 'lesson overview');
594                                 } else {
595                                     // User has completed the lesson and no retakes are allowed.
596                                     $str = '';
597                                 }
599                             } else {
600                                 // The last page viewed was NOT the end of lesson.
601                                 $str .= $OUTPUT->box(get_string('notyetcompleted', 'lesson'), 'info');
602                                 $str = $OUTPUT->box($str, 'lesson overview');
603                             }
605                         } else {
606                             // Last page was a question page, so the attempt is not completed yet.
607                             $str .= $OUTPUT->box(get_string('notyetcompleted', 'lesson'), 'info');
608                             $str = $OUTPUT->box($str, 'lesson overview');
609                         }
610                     }
612                 } else {
613                     // User has not yet started this lesson.
614                     $str .= $OUTPUT->box(get_string('nolessonattempts', 'lesson'), 'info');
615                     $str = $OUTPUT->box($str, 'lesson overview');
616                 }
617             }
618             if (!empty($str)) {
619                 if (empty($htmlarray[$lesson->course]['lesson'])) {
620                     $htmlarray[$lesson->course]['lesson'] = $str;
621                 } else {
622                     $htmlarray[$lesson->course]['lesson'] .= $str;
623                 }
624             }
625         }
626     }
629 /**
630  * Function to be run periodically according to the moodle cron
631  * This function searches for things that need to be done, such
632  * as sending out mail, toggling flags etc ...
633  * @global stdClass
634  * @return bool true
635  */
636 function lesson_cron () {
637     global $CFG;
639     return true;
642 /**
643  * Return grade for given user or all users.
644  *
645  * @global stdClass
646  * @global object
647  * @param int $lessonid id of lesson
648  * @param int $userid optional user id, 0 means all users
649  * @return array array of grades, false if none
650  */
651 function lesson_get_user_grades($lesson, $userid=0) {
652     global $CFG, $DB;
654     $params = array("lessonid" => $lesson->id,"lessonid2" => $lesson->id);
656     if (!empty($userid)) {
657         $params["userid"] = $userid;
658         $params["userid2"] = $userid;
659         $user = "AND u.id = :userid";
660         $fuser = "AND uu.id = :userid2";
661     }
662     else {
663         $user="";
664         $fuser="";
665     }
667     if ($lesson->retake) {
668         if ($lesson->usemaxgrade) {
669             $sql = "SELECT u.id, u.id AS userid, MAX(g.grade) AS rawgrade
670                       FROM {user} u, {lesson_grades} g
671                      WHERE u.id = g.userid AND g.lessonid = :lessonid
672                            $user
673                   GROUP BY u.id";
674         } else {
675             $sql = "SELECT u.id, u.id AS userid, AVG(g.grade) AS rawgrade
676                       FROM {user} u, {lesson_grades} g
677                      WHERE u.id = g.userid AND g.lessonid = :lessonid
678                            $user
679                   GROUP BY u.id";
680         }
681         unset($params['lessonid2']);
682         unset($params['userid2']);
683     } else {
684         // use only first attempts (with lowest id in lesson_grades table)
685         $firstonly = "SELECT uu.id AS userid, MIN(gg.id) AS firstcompleted
686                         FROM {user} uu, {lesson_grades} gg
687                        WHERE uu.id = gg.userid AND gg.lessonid = :lessonid2
688                              $fuser
689                        GROUP BY uu.id";
691         $sql = "SELECT u.id, u.id AS userid, g.grade AS rawgrade
692                   FROM {user} u, {lesson_grades} g, ($firstonly) f
693                  WHERE u.id = g.userid AND g.lessonid = :lessonid
694                        AND g.id = f.firstcompleted AND g.userid=f.userid
695                        $user";
696     }
698     return $DB->get_records_sql($sql, $params);
701 /**
702  * Update grades in central gradebook
703  *
704  * @category grade
705  * @param object $lesson
706  * @param int $userid specific user only, 0 means all
707  * @param bool $nullifnone
708  */
709 function lesson_update_grades($lesson, $userid=0, $nullifnone=true) {
710     global $CFG, $DB;
711     require_once($CFG->libdir.'/gradelib.php');
713     if ($lesson->grade == 0 || $lesson->practice) {
714         lesson_grade_item_update($lesson);
716     } else if ($grades = lesson_get_user_grades($lesson, $userid)) {
717         lesson_grade_item_update($lesson, $grades);
719     } else if ($userid and $nullifnone) {
720         $grade = new stdClass();
721         $grade->userid   = $userid;
722         $grade->rawgrade = null;
723         lesson_grade_item_update($lesson, $grade);
725     } else {
726         lesson_grade_item_update($lesson);
727     }
730 /**
731  * Create grade item for given lesson
732  *
733  * @category grade
734  * @uses GRADE_TYPE_VALUE
735  * @uses GRADE_TYPE_NONE
736  * @param object $lesson object with extra cmidnumber
737  * @param array|object $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
738  * @return int 0 if ok, error code otherwise
739  */
740 function lesson_grade_item_update($lesson, $grades=null) {
741     global $CFG;
742     if (!function_exists('grade_update')) { //workaround for buggy PHP versions
743         require_once($CFG->libdir.'/gradelib.php');
744     }
746     if (array_key_exists('cmidnumber', $lesson)) { //it may not be always present
747         $params = array('itemname'=>$lesson->name, 'idnumber'=>$lesson->cmidnumber);
748     } else {
749         $params = array('itemname'=>$lesson->name);
750     }
752     if (!$lesson->practice and $lesson->grade > 0) {
753         $params['gradetype']  = GRADE_TYPE_VALUE;
754         $params['grademax']   = $lesson->grade;
755         $params['grademin']   = 0;
756     } else if (!$lesson->practice and $lesson->grade < 0) {
757         $params['gradetype']  = GRADE_TYPE_SCALE;
758         $params['scaleid']   = -$lesson->grade;
760         // Make sure current grade fetched correctly from $grades
761         $currentgrade = null;
762         if (!empty($grades)) {
763             if (is_array($grades)) {
764                 $currentgrade = reset($grades);
765             } else {
766                 $currentgrade = $grades;
767             }
768         }
770         // When converting a score to a scale, use scale's grade maximum to calculate it.
771         if (!empty($currentgrade) && $currentgrade->rawgrade !== null) {
772             $grade = grade_get_grades($lesson->course, 'mod', 'lesson', $lesson->id, $currentgrade->userid);
773             $params['grademax']   = reset($grade->items)->grademax;
774         }
775     } else {
776         $params['gradetype']  = GRADE_TYPE_NONE;
777     }
779     if ($grades  === 'reset') {
780         $params['reset'] = true;
781         $grades = null;
782     } else if (!empty($grades)) {
783         // Need to calculate raw grade (Note: $grades has many forms)
784         if (is_object($grades)) {
785             $grades = array($grades->userid => $grades);
786         } else if (array_key_exists('userid', $grades)) {
787             $grades = array($grades['userid'] => $grades);
788         }
789         foreach ($grades as $key => $grade) {
790             if (!is_array($grade)) {
791                 $grades[$key] = $grade = (array) $grade;
792             }
793             //check raw grade isnt null otherwise we erroneously insert a grade of 0
794             if ($grade['rawgrade'] !== null) {
795                 $grades[$key]['rawgrade'] = ($grade['rawgrade'] * $params['grademax'] / 100);
796             } else {
797                 //setting rawgrade to null just in case user is deleting a grade
798                 $grades[$key]['rawgrade'] = null;
799             }
800         }
801     }
803     return grade_update('mod/lesson', $lesson->course, 'mod', 'lesson', $lesson->id, 0, $grades, $params);
806 /**
807  * List the actions that correspond to a view of this module.
808  * This is used by the participation report.
809  *
810  * Note: This is not used by new logging system. Event with
811  *       crud = 'r' and edulevel = LEVEL_PARTICIPATING will
812  *       be considered as view action.
813  *
814  * @return array
815  */
816 function lesson_get_view_actions() {
817     return array('view','view all');
820 /**
821  * List the actions that correspond to a post of this module.
822  * This is used by the participation report.
823  *
824  * Note: This is not used by new logging system. Event with
825  *       crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
826  *       will be considered as post action.
827  *
828  * @return array
829  */
830 function lesson_get_post_actions() {
831     return array('end','start');
834 /**
835  * Runs any processes that must run before
836  * a lesson insert/update
837  *
838  * @global object
839  * @param object $lesson Lesson form data
840  * @return void
841  **/
842 function lesson_process_pre_save(&$lesson) {
843     global $DB;
845     $lesson->timemodified = time();
847     if (empty($lesson->timelimit)) {
848         $lesson->timelimit = 0;
849     }
850     if (empty($lesson->timespent) or !is_numeric($lesson->timespent) or $lesson->timespent < 0) {
851         $lesson->timespent = 0;
852     }
853     if (!isset($lesson->completed)) {
854         $lesson->completed = 0;
855     }
856     if (empty($lesson->gradebetterthan) or !is_numeric($lesson->gradebetterthan) or $lesson->gradebetterthan < 0) {
857         $lesson->gradebetterthan = 0;
858     } else if ($lesson->gradebetterthan > 100) {
859         $lesson->gradebetterthan = 100;
860     }
862     if (empty($lesson->width)) {
863         $lesson->width = 640;
864     }
865     if (empty($lesson->height)) {
866         $lesson->height = 480;
867     }
868     if (empty($lesson->bgcolor)) {
869         $lesson->bgcolor = '#FFFFFF';
870     }
872     // Conditions for dependency
873     $conditions = new stdClass;
874     $conditions->timespent = $lesson->timespent;
875     $conditions->completed = $lesson->completed;
876     $conditions->gradebetterthan = $lesson->gradebetterthan;
877     $lesson->conditions = serialize($conditions);
878     unset($lesson->timespent);
879     unset($lesson->completed);
880     unset($lesson->gradebetterthan);
882     if (empty($lesson->password)) {
883         unset($lesson->password);
884     }
887 /**
888  * Runs any processes that must be run
889  * after a lesson insert/update
890  *
891  * @global object
892  * @param object $lesson Lesson form data
893  * @return void
894  **/
895 function lesson_process_post_save(&$lesson) {
896     // Update the events relating to this lesson.
897     lesson_update_events($lesson);
901 /**
902  * Implementation of the function for printing the form elements that control
903  * whether the course reset functionality affects the lesson.
904  *
905  * @param $mform form passed by reference
906  */
907 function lesson_reset_course_form_definition(&$mform) {
908     $mform->addElement('header', 'lessonheader', get_string('modulenameplural', 'lesson'));
909     $mform->addElement('advcheckbox', 'reset_lesson', get_string('deleteallattempts','lesson'));
910     $mform->addElement('advcheckbox', 'reset_lesson_user_overrides',
911             get_string('removealluseroverrides', 'lesson'));
912     $mform->addElement('advcheckbox', 'reset_lesson_group_overrides',
913             get_string('removeallgroupoverrides', 'lesson'));
916 /**
917  * Course reset form defaults.
918  * @param object $course
919  * @return array
920  */
921 function lesson_reset_course_form_defaults($course) {
922     return array('reset_lesson' => 1,
923             'reset_lesson_group_overrides' => 1,
924             'reset_lesson_user_overrides' => 1);
927 /**
928  * Removes all grades from gradebook
929  *
930  * @global stdClass
931  * @global object
932  * @param int $courseid
933  * @param string optional type
934  */
935 function lesson_reset_gradebook($courseid, $type='') {
936     global $CFG, $DB;
938     $sql = "SELECT l.*, cm.idnumber as cmidnumber, l.course as courseid
939               FROM {lesson} l, {course_modules} cm, {modules} m
940              WHERE m.name='lesson' AND m.id=cm.module AND cm.instance=l.id AND l.course=:course";
941     $params = array ("course" => $courseid);
942     if ($lessons = $DB->get_records_sql($sql,$params)) {
943         foreach ($lessons as $lesson) {
944             lesson_grade_item_update($lesson, 'reset');
945         }
946     }
949 /**
950  * Actual implementation of the reset course functionality, delete all the
951  * lesson attempts for course $data->courseid.
952  *
953  * @global stdClass
954  * @global object
955  * @param object $data the data submitted from the reset course.
956  * @return array status array
957  */
958 function lesson_reset_userdata($data) {
959     global $CFG, $DB;
961     $componentstr = get_string('modulenameplural', 'lesson');
962     $status = array();
964     if (!empty($data->reset_lesson)) {
965         $lessonssql = "SELECT l.id
966                          FROM {lesson} l
967                         WHERE l.course=:course";
969         $params = array ("course" => $data->courseid);
970         $lessons = $DB->get_records_sql($lessonssql, $params);
972         // Get rid of attempts files.
973         $fs = get_file_storage();
974         if ($lessons) {
975             foreach ($lessons as $lessonid => $unused) {
976                 if (!$cm = get_coursemodule_from_instance('lesson', $lessonid)) {
977                     continue;
978                 }
979                 $context = context_module::instance($cm->id);
980                 $fs->delete_area_files($context->id, 'mod_lesson', 'essay_responses');
981             }
982         }
984         $DB->delete_records_select('lesson_timer', "lessonid IN ($lessonssql)", $params);
985         $DB->delete_records_select('lesson_grades', "lessonid IN ($lessonssql)", $params);
986         $DB->delete_records_select('lesson_attempts', "lessonid IN ($lessonssql)", $params);
987         $DB->delete_records_select('lesson_branch', "lessonid IN ($lessonssql)", $params);
989         // remove all grades from gradebook
990         if (empty($data->reset_gradebook_grades)) {
991             lesson_reset_gradebook($data->courseid);
992         }
994         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallattempts', 'lesson'), 'error'=>false);
995     }
997     // Remove user overrides.
998     if (!empty($data->reset_lesson_user_overrides)) {
999         $DB->delete_records_select('lesson_overrides',
1000                 'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND userid IS NOT NULL', array($data->courseid));
1001         $status[] = array(
1002         'component' => $componentstr,
1003         'item' => get_string('useroverridesdeleted', 'lesson'),
1004         'error' => false);
1005     }
1006     // Remove group overrides.
1007     if (!empty($data->reset_lesson_group_overrides)) {
1008         $DB->delete_records_select('lesson_overrides',
1009         'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND groupid IS NOT NULL', array($data->courseid));
1010         $status[] = array(
1011         'component' => $componentstr,
1012         'item' => get_string('groupoverridesdeleted', 'lesson'),
1013         'error' => false);
1014     }
1015     /// updating dates - shift may be negative too
1016     if ($data->timeshift) {
1017         $DB->execute("UPDATE {lesson_overrides}
1018                          SET available = available + ?
1019                        WHERE lessonid IN (SELECT id FROM {lesson} WHERE course = ?)
1020                          AND available <> 0", array($data->timeshift, $data->courseid));
1021         $DB->execute("UPDATE {lesson_overrides}
1022                          SET deadline = deadline + ?
1023                        WHERE lessonid IN (SELECT id FROM {lesson} WHERE course = ?)
1024                          AND deadline <> 0", array($data->timeshift, $data->courseid));
1026         shift_course_mod_dates('lesson', array('available', 'deadline'), $data->timeshift, $data->courseid);
1027         $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
1028     }
1030     return $status;
1033 /**
1034  * Returns all other caps used in module
1035  * @return array
1036  */
1037 function lesson_get_extra_capabilities() {
1038     return array('moodle/site:accessallgroups');
1041 /**
1042  * @uses FEATURE_GROUPS
1043  * @uses FEATURE_GROUPINGS
1044  * @uses FEATURE_MOD_INTRO
1045  * @uses FEATURE_COMPLETION_TRACKS_VIEWS
1046  * @uses FEATURE_GRADE_HAS_GRADE
1047  * @uses FEATURE_GRADE_OUTCOMES
1048  * @param string $feature FEATURE_xx constant for requested feature
1049  * @return mixed True if module supports feature, false if not, null if doesn't know
1050  */
1051 function lesson_supports($feature) {
1052     switch($feature) {
1053         case FEATURE_GROUPS:
1054             return true;
1055         case FEATURE_GROUPINGS:
1056             return true;
1057         case FEATURE_MOD_INTRO:
1058             return true;
1059         case FEATURE_COMPLETION_TRACKS_VIEWS:
1060             return true;
1061         case FEATURE_GRADE_HAS_GRADE:
1062             return true;
1063         case FEATURE_COMPLETION_HAS_RULES:
1064             return true;
1065         case FEATURE_GRADE_OUTCOMES:
1066             return true;
1067         case FEATURE_BACKUP_MOODLE2:
1068             return true;
1069         case FEATURE_SHOW_DESCRIPTION:
1070             return true;
1071         default:
1072             return null;
1073     }
1076 /**
1077  * Obtains the automatic completion state for this lesson based on any conditions
1078  * in lesson settings.
1079  *
1080  * @param object $course Course
1081  * @param object $cm course-module
1082  * @param int $userid User ID
1083  * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
1084  * @return bool True if completed, false if not, $type if conditions not set.
1085  */
1086 function lesson_get_completion_state($course, $cm, $userid, $type) {
1087     global $CFG, $DB;
1089     // Get lesson details.
1090     $lesson = $DB->get_record('lesson', array('id' => $cm->instance), '*',
1091             MUST_EXIST);
1093     $result = $type; // Default return value.
1094     // If completion option is enabled, evaluate it and return true/false.
1095     if ($lesson->completionendreached) {
1096         $value = $DB->record_exists('lesson_timer', array(
1097                 'lessonid' => $lesson->id, 'userid' => $userid, 'completed' => 1));
1098         if ($type == COMPLETION_AND) {
1099             $result = $result && $value;
1100         } else {
1101             $result = $result || $value;
1102         }
1103     }
1104     if ($lesson->completiontimespent != 0) {
1105         $duration = $DB->get_field_sql(
1106                         "SELECT SUM(lessontime - starttime)
1107                                FROM {lesson_timer}
1108                               WHERE lessonid = :lessonid
1109                                 AND userid = :userid",
1110                         array('userid' => $userid, 'lessonid' => $lesson->id));
1111         if (!$duration) {
1112             $duration = 0;
1113         }
1114         if ($type == COMPLETION_AND) {
1115             $result = $result && ($lesson->completiontimespent < $duration);
1116         } else {
1117             $result = $result || ($lesson->completiontimespent < $duration);
1118         }
1119     }
1120     return $result;
1122 /**
1123  * This function extends the settings navigation block for the site.
1124  *
1125  * It is safe to rely on PAGE here as we will only ever be within the module
1126  * context when this is called
1127  *
1128  * @param settings_navigation $settings
1129  * @param navigation_node $lessonnode
1130  */
1131 function lesson_extend_settings_navigation($settings, $lessonnode) {
1132     global $PAGE, $DB;
1134     // We want to add these new nodes after the Edit settings node, and before the
1135     // Locally assigned roles node. Of course, both of those are controlled by capabilities.
1136     $keys = $lessonnode->get_children_key_list();
1137     $beforekey = null;
1138     $i = array_search('modedit', $keys);
1139     if ($i === false and array_key_exists(0, $keys)) {
1140         $beforekey = $keys[0];
1141     } else if (array_key_exists($i + 1, $keys)) {
1142         $beforekey = $keys[$i + 1];
1143     }
1145     if (has_capability('mod/lesson:manageoverrides', $PAGE->cm->context)) {
1146         $url = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $PAGE->cm->id));
1147         $node = navigation_node::create(get_string('groupoverrides', 'lesson'),
1148                 new moodle_url($url, array('mode' => 'group')),
1149                 navigation_node::TYPE_SETTING, null, 'mod_lesson_groupoverrides');
1150         $lessonnode->add_node($node, $beforekey);
1152         $node = navigation_node::create(get_string('useroverrides', 'lesson'),
1153                 new moodle_url($url, array('mode' => 'user')),
1154                 navigation_node::TYPE_SETTING, null, 'mod_lesson_useroverrides');
1155         $lessonnode->add_node($node, $beforekey);
1156     }
1158     if (has_capability('mod/lesson:edit', $PAGE->cm->context)) {
1159         $url = new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id));
1160         $lessonnode->add(get_string('preview', 'lesson'), $url);
1161         $editnode = $lessonnode->add(get_string('edit', 'lesson'));
1162         $url = new moodle_url('/mod/lesson/edit.php', array('id' => $PAGE->cm->id, 'mode' => 'collapsed'));
1163         $editnode->add(get_string('collapsed', 'lesson'), $url);
1164         $url = new moodle_url('/mod/lesson/edit.php', array('id' => $PAGE->cm->id, 'mode' => 'full'));
1165         $editnode->add(get_string('full', 'lesson'), $url);
1166     }
1168     if (has_capability('mod/lesson:viewreports', $PAGE->cm->context)) {
1169         $reportsnode = $lessonnode->add(get_string('reports', 'lesson'));
1170         $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportoverview'));
1171         $reportsnode->add(get_string('overview', 'lesson'), $url);
1172         $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportdetail'));
1173         $reportsnode->add(get_string('detailedstats', 'lesson'), $url);
1174     }
1176     if (has_capability('mod/lesson:grade', $PAGE->cm->context)) {
1177         $url = new moodle_url('/mod/lesson/essay.php', array('id'=>$PAGE->cm->id));
1178         $lessonnode->add(get_string('manualgrading', 'lesson'), $url);
1179     }
1183 /**
1184  * Get list of available import or export formats
1185  *
1186  * Copied and modified from lib/questionlib.php
1187  *
1188  * @param string $type 'import' if import list, otherwise export list assumed
1189  * @return array sorted list of import/export formats available
1190  */
1191 function lesson_get_import_export_formats($type) {
1192     global $CFG;
1193     $fileformats = core_component::get_plugin_list("qformat");
1195     $fileformatname=array();
1196     foreach ($fileformats as $fileformat=>$fdir) {
1197         $format_file = "$fdir/format.php";
1198         if (file_exists($format_file) ) {
1199             require_once($format_file);
1200         } else {
1201             continue;
1202         }
1203         $classname = "qformat_$fileformat";
1204         $format_class = new $classname();
1205         if ($type=='import') {
1206             $provided = $format_class->provide_import();
1207         } else {
1208             $provided = $format_class->provide_export();
1209         }
1210         if ($provided) {
1211             $fileformatnames[$fileformat] = get_string('pluginname', 'qformat_'.$fileformat);
1212         }
1213     }
1214     natcasesort($fileformatnames);
1216     return $fileformatnames;
1219 /**
1220  * Serves the lesson attachments. Implements needed access control ;-)
1221  *
1222  * @package mod_lesson
1223  * @category files
1224  * @param stdClass $course course object
1225  * @param stdClass $cm course module object
1226  * @param stdClass $context context object
1227  * @param string $filearea file area
1228  * @param array $args extra arguments
1229  * @param bool $forcedownload whether or not force download
1230  * @param array $options additional options affecting the file serving
1231  * @return bool false if file not found, does not return if found - justsend the file
1232  */
1233 function lesson_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
1234     global $CFG, $DB;
1236     if ($context->contextlevel != CONTEXT_MODULE) {
1237         return false;
1238     }
1240     $fileareas = lesson_get_file_areas();
1241     if (!array_key_exists($filearea, $fileareas)) {
1242         return false;
1243     }
1245     if (!$lesson = $DB->get_record('lesson', array('id'=>$cm->instance))) {
1246         return false;
1247     }
1249     require_course_login($course, true, $cm);
1251     if ($filearea === 'page_contents') {
1252         $pageid = (int)array_shift($args);
1253         if (!$page = $DB->get_record('lesson_pages', array('id'=>$pageid))) {
1254             return false;
1255         }
1256         $fullpath = "/$context->id/mod_lesson/$filearea/$pageid/".implode('/', $args);
1258     } else if ($filearea === 'page_answers' || $filearea === 'page_responses') {
1259         $itemid = (int)array_shift($args);
1260         if (!$pageanswers = $DB->get_record('lesson_answers', array('id' => $itemid))) {
1261             return false;
1262         }
1263         $fullpath = "/$context->id/mod_lesson/$filearea/$itemid/".implode('/', $args);
1265     } else if ($filearea === 'essay_responses') {
1266         $itemid = (int)array_shift($args);
1267         if (!$attempt = $DB->get_record('lesson_attempts', array('id' => $itemid))) {
1268             return false;
1269         }
1270         $fullpath = "/$context->id/mod_lesson/$filearea/$itemid/".implode('/', $args);
1272     } else if ($filearea === 'mediafile') {
1273         if (count($args) > 1) {
1274             // Remove the itemid when it appears to be part of the arguments. If there is only one argument
1275             // then it is surely the file name. The itemid is sometimes used to prevent browser caching.
1276             array_shift($args);
1277         }
1278         $fullpath = "/$context->id/mod_lesson/$filearea/0/".implode('/', $args);
1280     } else {
1281         return false;
1282     }
1284     $fs = get_file_storage();
1285     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1286         return false;
1287     }
1289     // finally send the file
1290     send_stored_file($file, 0, 0, $forcedownload, $options); // download MUST be forced - security!
1293 /**
1294  * Returns an array of file areas
1295  *
1296  * @package  mod_lesson
1297  * @category files
1298  * @return array a list of available file areas
1299  */
1300 function lesson_get_file_areas() {
1301     $areas = array();
1302     $areas['page_contents'] = get_string('pagecontents', 'mod_lesson');
1303     $areas['mediafile'] = get_string('mediafile', 'mod_lesson');
1304     $areas['page_answers'] = get_string('pageanswers', 'mod_lesson');
1305     $areas['page_responses'] = get_string('pageresponses', 'mod_lesson');
1306     $areas['essay_responses'] = get_string('essayresponses', 'mod_lesson');
1307     return $areas;
1310 /**
1311  * Returns a file_info_stored object for the file being requested here
1312  *
1313  * @package  mod_lesson
1314  * @category files
1315  * @global stdClass $CFG
1316  * @param file_browse $browser file browser instance
1317  * @param array $areas file areas
1318  * @param stdClass $course course object
1319  * @param stdClass $cm course module object
1320  * @param stdClass $context context object
1321  * @param string $filearea file area
1322  * @param int $itemid item ID
1323  * @param string $filepath file path
1324  * @param string $filename file name
1325  * @return file_info_stored
1326  */
1327 function lesson_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
1328     global $CFG, $DB;
1330     if (!has_capability('moodle/course:managefiles', $context)) {
1331         // No peaking here for students!
1332         return null;
1333     }
1335     // Mediafile area does not have sub directories, so let's select the default itemid to prevent
1336     // the user from selecting a directory to access the mediafile content.
1337     if ($filearea == 'mediafile' && is_null($itemid)) {
1338         $itemid = 0;
1339     }
1341     if (is_null($itemid)) {
1342         return new mod_lesson_file_info($browser, $course, $cm, $context, $areas, $filearea);
1343     }
1345     $fs = get_file_storage();
1346     $filepath = is_null($filepath) ? '/' : $filepath;
1347     $filename = is_null($filename) ? '.' : $filename;
1348     if (!$storedfile = $fs->get_file($context->id, 'mod_lesson', $filearea, $itemid, $filepath, $filename)) {
1349         return null;
1350     }
1352     $itemname = $filearea;
1353     if ($filearea == 'page_contents') {
1354         $itemname = $DB->get_field('lesson_pages', 'title', array('lessonid' => $cm->instance, 'id' => $itemid));
1355         $itemname = format_string($itemname, true, array('context' => $context));
1356     } else {
1357         $areas = lesson_get_file_areas();
1358         if (isset($areas[$filearea])) {
1359             $itemname = $areas[$filearea];
1360         }
1361     }
1363     $urlbase = $CFG->wwwroot . '/pluginfile.php';
1364     return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemname, $itemid, true, true, false);
1368 /**
1369  * Return a list of page types
1370  * @param string $pagetype current page type
1371  * @param stdClass $parentcontext Block's parent context
1372  * @param stdClass $currentcontext Current context of block
1373  */
1374 function lesson_page_type_list($pagetype, $parentcontext, $currentcontext) {
1375     $module_pagetype = array(
1376         'mod-lesson-*'=>get_string('page-mod-lesson-x', 'lesson'),
1377         'mod-lesson-view'=>get_string('page-mod-lesson-view', 'lesson'),
1378         'mod-lesson-edit'=>get_string('page-mod-lesson-edit', 'lesson'));
1379     return $module_pagetype;
1382 /**
1383  * Update the lesson activity to include any file
1384  * that was uploaded, or if there is none, set the
1385  * mediafile field to blank.
1386  *
1387  * @param int $lessonid the lesson id
1388  * @param stdClass $context the context
1389  * @param int $draftitemid the draft item
1390  */
1391 function lesson_update_media_file($lessonid, $context, $draftitemid) {
1392     global $DB;
1394     // Set the filestorage object.
1395     $fs = get_file_storage();
1396     // Save the file if it exists that is currently in the draft area.
1397     file_save_draft_area_files($draftitemid, $context->id, 'mod_lesson', 'mediafile', 0);
1398     // Get the file if it exists.
1399     $files = $fs->get_area_files($context->id, 'mod_lesson', 'mediafile', 0, 'itemid, filepath, filename', false);
1400     // Check that there is a file to process.
1401     if (count($files) == 1) {
1402         // Get the first (and only) file.
1403         $file = reset($files);
1404         // Set the mediafile column in the lessons table.
1405         $DB->set_field('lesson', 'mediafile', '/' . $file->get_filename(), array('id' => $lessonid));
1406     } else {
1407         // Set the mediafile column in the lessons table.
1408         $DB->set_field('lesson', 'mediafile', '', array('id' => $lessonid));
1409     }