3 * Local library file for Lesson. These are non-standard functions that are used
7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
12 * Next page -> any page not seen before
14 if (!defined("LESSON_UNSEENPAGE")) {
15 define("LESSON_UNSEENPAGE", 1); // Next page -> any page not seen before
18 * Next page -> any page not answered correctly
20 if (!defined("LESSON_UNANSWEREDPAGE")) {
21 define("LESSON_UNANSWEREDPAGE", 2); // Next page -> any page not answered correctly
25 * Define different lesson flows for next page
27 $LESSON_NEXTPAGE_ACTION = array (0 => get_string("normal", "lesson"),
28 LESSON_UNSEENPAGE
=> get_string("showanunseenpage", "lesson"),
29 LESSON_UNANSWEREDPAGE
=> get_string("showanunansweredpage", "lesson") );
31 // Lesson jump types defined
32 // TODO: instead of using define statements, create an array with all the jump values
37 if (!defined("LESSON_NEXTPAGE")) {
38 define("LESSON_NEXTPAGE", -1);
43 if (!defined("LESSON_EOL")) {
44 define("LESSON_EOL", -9);
47 * Jump to an unseen page within a branch and end of branch or end of lesson
49 if (!defined("LESSON_UNSEENBRANCHPAGE")) {
50 define("LESSON_UNSEENBRANCHPAGE", -50);
53 * Jump to Previous Page
55 if (!defined("LESSON_PREVIOUSPAGE")) {
56 define("LESSON_PREVIOUSPAGE", -40);
59 * Jump to a random page within a branch and end of branch or end of lesson
61 if (!defined("LESSON_RANDOMPAGE")) {
62 define("LESSON_RANDOMPAGE", -60);
65 * Jump to a random Branch
67 if (!defined("LESSON_RANDOMBRANCH")) {
68 define("LESSON_RANDOMBRANCH", -70);
73 if (!defined("LESSON_CLUSTERJUMP")) {
74 define("LESSON_CLUSTERJUMP", -80);
79 if (!defined("LESSON_UNDEFINED")) {
80 define("LESSON_UNDEFINED", -99);
83 // Lesson question types defined
86 * Short answer question type
88 if (!defined("LESSON_SHORTANSWER")) {
89 define("LESSON_SHORTANSWER", "1");
92 * True/False question type
94 if (!defined("LESSON_TRUEFALSE")) {
95 define("LESSON_TRUEFALSE", "2");
98 * Multichoice question type
100 * If you change the value of this then you need
101 * to change it in restorelib.php as well.
103 if (!defined("LESSON_MULTICHOICE")) {
104 define("LESSON_MULTICHOICE", "3");
107 * Random question type - not used
109 if (!defined("LESSON_RANDOM")) {
110 define("LESSON_RANDOM", "4");
113 * Matching question type
115 * If you change the value of this then you need
116 * to change it in restorelib.php, in mysql.php
117 * and postgres7.php as well.
119 if (!defined("LESSON_MATCHING")) {
120 define("LESSON_MATCHING", "5");
123 * Not sure - not used
125 if (!defined("LESSON_RANDOMSAMATCH")) {
126 define("LESSON_RANDOMSAMATCH", "6");
129 * Not sure - not used
131 if (!defined("LESSON_DESCRIPTION")) {
132 define("LESSON_DESCRIPTION", "7");
135 * Numerical question type
137 if (!defined("LESSON_NUMERICAL")) {
138 define("LESSON_NUMERICAL", "8");
141 * Multichoice with multianswer question type
143 if (!defined("LESSON_MULTIANSWER")) {
144 define("LESSON_MULTIANSWER", "9");
147 * Essay question type
149 if (!defined("LESSON_ESSAY")) {
150 define("LESSON_ESSAY", "10");
154 * Lesson question type array.
155 * Contains all question types used
157 $LESSON_QUESTION_TYPE = array ( LESSON_MULTICHOICE
=> get_string("multichoice", "quiz"),
158 LESSON_TRUEFALSE
=> get_string("truefalse", "quiz"),
159 LESSON_SHORTANSWER
=> get_string("shortanswer", "quiz"),
160 LESSON_NUMERICAL
=> get_string("numerical", "quiz"),
161 LESSON_MATCHING
=> get_string("match", "quiz"),
162 LESSON_ESSAY
=> get_string("essay", "lesson")
163 // LESSON_DESCRIPTION => get_string("description", "quiz"),
164 // LESSON_RANDOM => get_string("random", "quiz"),
165 // LESSON_RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
166 // LESSON_MULTIANSWER => get_string("multianswer", "quiz"),
169 // Non-question page types
174 if (!defined("LESSON_BRANCHTABLE")) {
175 define("LESSON_BRANCHTABLE", "20");
180 if (!defined("LESSON_ENDOFBRANCH")) {
181 define("LESSON_ENDOFBRANCH", "21");
184 * Start of Cluster page
186 if (!defined("LESSON_CLUSTER")) {
187 define("LESSON_CLUSTER", "30");
190 * End of Cluster page
192 if (!defined("LESSON_ENDOFCLUSTER")) {
193 define("LESSON_ENDOFCLUSTER", "31");
196 // other variables...
199 * Flag for the editor for the answer textarea.
201 if (!defined("LESSON_ANSWER_EDITOR")) {
202 define("LESSON_ANSWER_EDITOR", "1");
205 * Flag for the editor for the response textarea.
207 if (!defined("LESSON_RESPONSE_EDITOR")) {
208 define("LESSON_RESPONSE_EDITOR", "2");
211 //////////////////////////////////////////////////////////////////////////////////////
212 /// Any other lesson functions go here. Each of them must have a name that
213 /// starts with lesson_
216 * Print the standard header for lesson module
220 * @param object $cm Course module record object
221 * @param object $course Couse record object
222 * @param object $lesson Lesson module record object
223 * @param string $currenttab Current tab for the lesson tabs
226 function lesson_print_header($cm, $course, $lesson, $currenttab = '') {
229 $strlesson = get_string('modulename', 'lesson');
230 $strname = format_string($lesson->name
, true, $course->id
);
232 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
234 if (has_capability('mod/lesson:edit', $context)) {
235 $button = update_module_button($cm->id
, $course->id
, $strlesson);
241 $navigation = build_navigation('', $cm);
243 /// Print header, heading, tabs and messages
244 print_header("$course->shortname: $strname", $course->fullname
, $navigation,
245 '', '', true, $button, navmenu($course, $cm));
247 if (has_capability('mod/lesson:manage', $context)) {
248 print_heading_with_help($strname, "overview", "lesson");
250 if (!empty($currenttab)) {
251 include($CFG->dirroot
.'/mod/lesson/tabs.php');
254 print_heading($strname);
257 lesson_print_messages();
263 * Returns course module, course and module instance given
264 * either the course module ID or a lesson module ID.
266 * @param int $cmid Course Module ID
267 * @param int $lessonid Lesson module instance ID
268 * @return array array($cm, $course, $lesson)
270 function lesson_get_basics($cmid = 0, $lessonid = 0) {
272 if (!$cm = get_coursemodule_from_id('lesson', $cmid)) {
273 error('Course Module ID was incorrect');
275 if (!$course = get_record('course', 'id', $cm->course
)) {
276 error('Course is misconfigured');
278 if (!$lesson = get_record('lesson', 'id', $cm->instance
)) {
279 error('Course module is incorrect');
281 } else if ($lessonid) {
282 if (!$lesson = get_record('lesson', 'id', $lessonid)) {
283 error('Course module is incorrect');
285 if (!$course = get_record('course', 'id', $lesson->course
)) {
286 error('Course is misconfigured');
288 if (!$cm = get_coursemodule_from_instance('lesson', $lesson->id
, $course->id
)) {
289 error('Course Module ID was incorrect');
292 error('No course module ID or lesson ID were passed');
295 return array($cm, $course, $lesson);
299 * Sets a message to be printed. Messages are printed
300 * by calling {@link lesson_print_messages()}.
303 * @param string $message The message to be printed
304 * @param string $class Class to be passed to {@link notify()}. Usually notifyproblem or notifysuccess.
305 * @param string $align Alignment of the message
308 function lesson_set_message($message, $class="notifyproblem", $align='center') {
311 if (empty($SESSION->lesson_messages
) or !is_array($SESSION->lesson_messages
)) {
312 $SESSION->lesson_messages
= array();
315 $SESSION->lesson_messages
[] = array($message, $class, $align);
321 * Print all set messages.
323 * See {@link lesson_set_message()} for setting messages.
325 * Uses {@link notify()} to print the messages.
330 function lesson_print_messages() {
333 if (empty($SESSION->lesson_messages
)) {
334 // No messages to print
338 foreach($SESSION->lesson_messages
as $message) {
339 notify($message[0], $message[1], $message[2]);
343 unset($SESSION->lesson_messages
);
349 * Prints a lesson link that submits a form.
351 * If Javascript is disabled, then a regular submit button is printed
353 * @param string $name Name of the link or button
354 * @param string $form The name of the form to be submitted
355 * @param string $align Alignment of the button
356 * @param string $class Class names to add to the div wrapper
357 * @param string $title Title for the link (Not used if javascript is disabled)
358 * @param string $id ID tag
359 * @param boolean $return Return flag
360 * @return mixed boolean/html
362 function lesson_print_submit_link($name, $form, $align = 'center', $class='standardbutton', $title = '', $id = '', $return = false) {
363 if (!empty($align)) {
364 $align = " style=\"text-align:$align\"";
373 $output = "<div class=\"lessonbutton $class\" $align>\n";
374 $output .= "<input type=\"submit\" value=\"$name\" $align $id />";
375 $output .= "</div>\n";
386 * Prints a time remaining in the following format: H:MM:SS
388 * @param int $starttime Time when the lesson started
389 * @param int $maxtime Length of the lesson
390 * @param boolean $return Return output switch
391 * @return mixed boolean/string
393 function lesson_print_time_remaining($starttime, $maxtime, $return = false) {
394 // Calculate hours, minutes and seconds
395 $timeleft = $starttime +
$maxtime * 60 - time();
396 $hours = floor($timeleft/3600);
397 $timeleft = $timeleft - ($hours * 3600);
398 $minutes = floor($timeleft/60);
399 $secs = $timeleft - ($minutes * 60);
402 $minutes = "0$minutes";
409 $output[] = $minutes;
412 $output = implode(':', $output);
423 * Prints the page action buttons
425 * Move/Edit/Preview/Delete
428 * @param int $cmid Course Module ID
429 * @param object $page Page record
430 * @param boolean $printmove Flag to print the move button or not
431 * @param boolean $printaddpage Flag to print the add page drop-down or not
432 * @param boolean $return Return flag
433 * @return mixed boolean/string
435 function lesson_print_page_actions($cmid, $page, $printmove, $printaddpage = false, $return = false) {
438 $context = get_context_instance(CONTEXT_MODULE
, $cmid);
441 if (has_capability('mod/lesson:edit', $context)) {
443 $actions[] = "<a title=\"".get_string('move')."\" href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&action=move&pageid=$page->id\">
444 <img src=\"$CFG->pixpath/t/move.gif\" class=\"iconsmall\" alt=\"".get_string('move')."\" /></a>\n";
446 $actions[] = "<a title=\"".get_string('update')."\" href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&action=editpage&pageid=$page->id\">
447 <img src=\"$CFG->pixpath/t/edit.gif\" class=\"iconsmall\" alt=\"".get_string('update')."\" /></a>\n";
449 $actions[] = "<a title=\"".get_string('preview')."\" href=\"$CFG->wwwroot/mod/lesson/view.php?id=$cmid&pageid=$page->id\">
450 <img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"".get_string('preview')."\" /></a>\n";
452 $actions[] = "<a title=\"".get_string('delete')."\" href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&sesskey=".sesskey()."&action=confirmdelete&pageid=$page->id\">
453 <img src=\"$CFG->pixpath/t/delete.gif\" class=\"iconsmall\" alt=\"".get_string('delete')."\" /></a>\n";
456 // Add page drop-down
458 $options['addcluster&sesskey='.sesskey()] = get_string('clustertitle', 'lesson');
459 $options['addendofcluster&sesskey='.sesskey()] = get_string('endofclustertitle', 'lesson');
460 $options['addbranchtable'] = get_string('branchtable', 'lesson');
461 $options['addendofbranch&sesskey='.sesskey()] = get_string('endofbranch', 'lesson');
462 $options['addpage'] = get_string('question', 'lesson');
464 $common = "$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&pageid=$page->id&action=";
466 $actions[] = popup_form($common, $options, "addpage_$page->id", '', get_string('addpage', 'lesson').'...', '', '', true);
470 $actions = implode(' ', $actions);
481 * Prints the add links in expanded view or single view when editing
484 * @param int $cmid Course Module ID
485 * @param int $prevpageid Previous page id
486 * @param boolean $return Return flag
487 * @return mixed boolean/string
488 * @todo &pageid does not make sense, it is prevpageid
490 function lesson_print_add_links($cmid, $prevpageid, $return = false) {
493 $context = get_context_instance(CONTEXT_MODULE
, $cmid);
496 if (has_capability('mod/lesson:edit', $context)) {
498 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/import.php?id=$cmid&pageid=$prevpageid\">".
499 get_string('importquestions', 'lesson').'</a>';
501 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&sesskey=".sesskey()."&action=addcluster&pageid=$prevpageid\">".
502 get_string('addcluster', 'lesson').'</a>';
504 if ($prevpageid != 0) {
505 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&sesskey=".sesskey()."&action=addendofcluster&pageid=$prevpageid\">".
506 get_string('addendofcluster', 'lesson').'</a>';
508 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&action=addbranchtable&pageid=$prevpageid\">".
509 get_string('addabranchtable', 'lesson').'</a>';
511 if ($prevpageid != 0) {
512 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&sesskey=".sesskey()."&action=addendofbranch&pageid=$prevpageid\">".
513 get_string('addanendofbranch', 'lesson').'</a>';
516 $links[] = "<a href=\"$CFG->wwwroot/mod/lesson/lesson.php?id=$cmid&action=addpage&pageid=$prevpageid\">".
517 get_string('addaquestionpagehere', 'lesson').'</a>';
519 $links = implode(" | \n", $links);
520 $links = "\n<div class=\"addlinks\">\n$links\n</div>\n";
532 * Returns the string for a page type
534 * @uses $LESSON_QUESTION_TYPE
535 * @param int $qtype Page type
538 function lesson_get_qtype_name($qtype) {
539 global $LESSON_QUESTION_TYPE;
542 case LESSON_SHORTANSWER
:
543 case LESSON_MULTICHOICE
:
544 case LESSON_MATCHING
:
545 case LESSON_TRUEFALSE
:
546 case LESSON_NUMERICAL
:
547 return $LESSON_QUESTION_TYPE[$qtype];
549 case LESSON_BRANCHTABLE
:
550 return get_string("branchtable", "lesson");
552 case LESSON_ENDOFBRANCH
:
553 return get_string("endofbranch", "lesson");
555 case LESSON_CLUSTER
:
556 return get_string("clustertitle", "lesson");
558 case LESSON_ENDOFCLUSTER
:
559 return get_string("endofclustertitle", "lesson");
568 * Returns the string for a jump name
570 * @param int $jumpto Jump code or page ID
573 function lesson_get_jump_name($jumpto) {
575 $jumptitle = get_string('thispage', 'lesson');
576 } elseif ($jumpto == LESSON_NEXTPAGE
) {
577 $jumptitle = get_string('nextpage', 'lesson');
578 } elseif ($jumpto == LESSON_EOL
) {
579 $jumptitle = get_string('endoflesson', 'lesson');
580 } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE
) {
581 $jumptitle = get_string('unseenpageinbranch', 'lesson');
582 } elseif ($jumpto == LESSON_PREVIOUSPAGE
) {
583 $jumptitle = get_string('previouspage', 'lesson');
584 } elseif ($jumpto == LESSON_RANDOMPAGE
) {
585 $jumptitle = get_string('randompageinbranch', 'lesson');
586 } elseif ($jumpto == LESSON_RANDOMBRANCH
) {
587 $jumptitle = get_string('randombranch', 'lesson');
588 } elseif ($jumpto == LESSON_CLUSTERJUMP
) {
589 $jumptitle = get_string('clusterjump', 'lesson');
591 if (!$jumptitle = get_field('lesson_pages', 'title', 'id', $jumpto)) {
592 $jumptitle = '<strong>'.get_string('notdefined', 'lesson').'</strong>';
596 return format_string($jumptitle,true);
600 * Given some question info and some data about the the answers
601 * this function parses, organises and saves the question
603 * This is only used when IMPORTING questions and is only called
605 * Lifted from mod/quiz/lib.php -
606 * 1. all reference to oldanswers removed
607 * 2. all reference to quiz_multichoice table removed
608 * 3. In SHORTANSWER questions usecase is store in the qoption field
609 * 4. In NUMERIC questions store the range as two answers
610 * 5. TRUEFALSE options are ignored
611 * 6. For MULTICHOICE questions with more than one answer the qoption field is true
613 * @param opject $question Contains question data like question, type and answers.
614 * @return object Returns $result->error or $result->notice.
616 function lesson_save_question_options($question) {
619 switch ($question->qtype
) {
620 case LESSON_SHORTANSWER
:
625 // Insert all the new answers
626 foreach ($question->answer
as $key => $dataanswer) {
627 if ($dataanswer != "") {
628 $answer = new stdClass
;
629 $answer->lessonid
= $question->lessonid
;
630 $answer->pageid
= $question->id
;
631 if ($question->fraction
[$key] >=0.5) {
632 $answer->jumpto
= LESSON_NEXTPAGE
;
634 $answer->timecreated
= $timenow;
635 $answer->grade
= $question->fraction
[$key] * 100;
636 $answer->answer
= $dataanswer;
637 $answer->response
= $question->feedback
[$key];
638 if (!$answer->id
= insert_record("lesson_answers", $answer)) {
639 $result->error
= "Could not insert shortanswer quiz answer!";
642 $answers[] = $answer->id
;
643 if ($question->fraction
[$key] > $maxfraction) {
644 $maxfraction = $question->fraction
[$key];
650 /// Perform sanity checks on fractional grades
651 if ($maxfraction != 1) {
652 $maxfraction = $maxfraction * 100;
653 $result->notice
= get_string("fractionsnomax", "quiz", $maxfraction);
658 case LESSON_NUMERICAL
: // Note similarities to SHORTANSWER
664 // for each answer store the pair of min and max values even if they are the same
665 foreach ($question->answer
as $key => $dataanswer) {
666 if ($dataanswer != "") {
667 $answer = new stdClass
;
668 $answer->lessonid
= $question->lessonid
;
669 $answer->pageid
= $question->id
;
670 $answer->jumpto
= LESSON_NEXTPAGE
;
671 $answer->timecreated
= $timenow;
672 $answer->grade
= $question->fraction
[$key] * 100;
673 $min = $question->answer
[$key] - $question->tolerance
[$key];
674 $max = $question->answer
[$key] +
$question->tolerance
[$key];
675 $answer->answer
= $min.":".$max;
676 // $answer->answer = $question->min[$key].":".$question->max[$key]; original line for min/max
677 $answer->response
= $question->feedback
[$key];
678 if (!$answer->id
= insert_record("lesson_answers", $answer)) {
679 $result->error
= "Could not insert numerical quiz answer!";
683 $answers[] = $answer->id
;
684 if ($question->fraction
[$key] > $maxfraction) {
685 $maxfraction = $question->fraction
[$key];
690 /// Perform sanity checks on fractional grades
691 if ($maxfraction != 1) {
692 $maxfraction = $maxfraction * 100;
693 $result->notice
= get_string("fractionsnomax", "quiz", $maxfraction);
699 case LESSON_TRUEFALSE
:
702 $answer->lessonid
= $question->lessonid
;
703 $answer->pageid
= $question->id
;
704 $answer->timecreated
= $timenow;
705 $answer->answer
= get_string("true", "quiz");
706 $answer->grade
= $question->answer
* 100;
707 if ($answer->grade
> 50 ) {
708 $answer->jumpto
= LESSON_NEXTPAGE
;
710 if (isset($question->feedbacktrue
)) {
711 $answer->response
= $question->feedbacktrue
;
713 if (!$true->id
= insert_record("lesson_answers", $answer)) {
714 $result->error
= "Could not insert quiz answer \"true\")!";
719 $answer = new stdClass
;
720 $answer->lessonid
= $question->lessonid
;
721 $answer->pageid
= $question->id
;
722 $answer->timecreated
= $timenow;
723 $answer->answer
= get_string("false", "quiz");
724 $answer->grade
= (1 - (int)$question->answer
) * 100;
725 if ($answer->grade
> 50 ) {
726 $answer->jumpto
= LESSON_NEXTPAGE
;
728 if (isset($question->feedbackfalse
)) {
729 $answer->response
= $question->feedbackfalse
;
731 if (!$false->id
= insert_record("lesson_answers", $answer)) {
732 $result->error
= "Could not insert quiz answer \"false\")!";
739 case LESSON_MULTICHOICE
:
746 // Insert all the new answers
747 foreach ($question->answer
as $key => $dataanswer) {
748 if ($dataanswer != "") {
749 $answer = new stdClass
;
750 $answer->lessonid
= $question->lessonid
;
751 $answer->pageid
= $question->id
;
752 $answer->timecreated
= $timenow;
753 $answer->grade
= $question->fraction
[$key] * 100;
754 // changed some defaults
756 if ($answer->grade > 50 ) {
757 $answer->jumpto = LESSON_NEXTPAGE;
760 if ($answer->grade
> 50 ) {
761 $answer->jumpto
= LESSON_NEXTPAGE
;
765 $answer->answer
= $dataanswer;
766 $answer->response
= $question->feedback
[$key];
767 if (!$answer->id
= insert_record("lesson_answers", $answer)) {
768 $result->error
= "Could not insert multichoice quiz answer! ";
772 if ($question->fraction
[$key] > 0) {
773 $totalfraction +
= $question->fraction
[$key];
775 if ($question->fraction
[$key] > $maxfraction) {
776 $maxfraction = $question->fraction
[$key];
781 /// Perform sanity checks on fractional grades
782 if ($question->single
) {
783 if ($maxfraction != 1) {
784 $maxfraction = $maxfraction * 100;
785 $result->notice
= get_string("fractionsnomax", "quiz", $maxfraction);
789 $totalfraction = round($totalfraction,2);
790 if ($totalfraction != 1) {
791 $totalfraction = $totalfraction * 100;
792 $result->notice
= get_string("fractionsaddwrong", "quiz", $totalfraction);
798 case LESSON_MATCHING
:
800 $subquestions = array();
803 // Insert all the new question+answer pairs
804 foreach ($question->subquestions
as $key => $questiontext) {
805 $answertext = $question->subanswers
[$key];
806 if (!empty($questiontext) and !empty($answertext)) {
807 $answer = new stdClass
;
808 $answer->lessonid
= $question->lessonid
;
809 $answer->pageid
= $question->id
;
810 $answer->timecreated
= $timenow;
811 $answer->answer
= $questiontext;
812 $answer->response
= $answertext;
814 // first answer contains the correct answer jump
815 $answer->jumpto
= LESSON_NEXTPAGE
;
817 if (!$subquestion->id
= insert_record("lesson_answers", $answer)) {
818 $result->error
= "Could not insert quiz match subquestion!";
821 $subquestions[] = $subquestion->id
;
826 if (count($subquestions) < 3) {
827 $result->notice
= get_string("notenoughsubquestions", "quiz");
834 case LESSON_RANDOMSAMATCH
:
835 $options->question
= $question->id
;
836 $options->choose
= $question->choose
;
837 if ($existing = get_record("quiz_randomsamatch", "question", $options->question
)) {
838 $options->id
= $existing->id
;
839 if (!update_record("quiz_randomsamatch", $options)) {
840 $result->error
= "Could not update quiz randomsamatch options!";
844 if (!insert_record("quiz_randomsamatch", $options)) {
845 $result->error
= "Could not insert quiz randomsamatch options!";
851 case LESSON_MULTIANSWER
:
852 if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id
, "id ASC")) {
853 $oldmultianswers = array();
856 // Insert all the new multi answers
857 foreach ($question->answers
as $dataanswer) {
858 if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it
859 $multianswer = $oldmultianswer;
860 $multianswer->positionkey
= $dataanswer->positionkey
;
861 $multianswer->norm
= $dataanswer->norm
;
862 $multianswer->answertype
= $dataanswer->answertype
;
864 if (! $multianswer->answers
= quiz_save_multianswer_alternatives
865 ($question->id
, $dataanswer->answertype
,
866 $dataanswer->alternatives
, $oldmultianswer->answers
))
868 $result->error
= "Could not update multianswer alternatives! (id=$multianswer->id)";
871 if (!update_record("quiz_multianswers", $multianswer)) {
872 $result->error
= "Could not update quiz multianswer! (id=$multianswer->id)";
875 } else { // This is a completely new answer
876 $multianswer = new stdClass
;
877 $multianswer->question
= $question->id
;
878 $multianswer->positionkey
= $dataanswer->positionkey
;
879 $multianswer->norm
= $dataanswer->norm
;
880 $multianswer->answertype
= $dataanswer->answertype
;
882 if (! $multianswer->answers
= quiz_save_multianswer_alternatives
883 ($question->id
, $dataanswer->answertype
,
884 $dataanswer->alternatives
))
886 $result->error
= "Could not insert multianswer alternatives! (questionid=$question->id)";
889 if (!insert_record("quiz_multianswers", $multianswer)) {
890 $result->error
= "Could not insert quiz multianswer!";
900 case LESSON_DESCRIPTION
:
904 $result->error
= "Unsupported question type ($question->qtype)!";
912 * Determins if a jumpto value is correct or not.
914 * returns true if jumpto page is (logically) after the pageid page or
915 * if the jumpto value is a special value. Returns false in all other cases.
917 * @param int $pageid Id of the page from which you are jumping from.
918 * @param int $jumpto The jumpto number.
919 * @return boolean True or false after a series of tests.
921 function lesson_iscorrect($pageid, $jumpto) {
923 // first test the special values
927 } elseif ($jumpto == LESSON_NEXTPAGE
) {
929 } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE
) {
931 } elseif ($jumpto == LESSON_RANDOMPAGE
) {
933 } elseif ($jumpto == LESSON_CLUSTERJUMP
) {
935 } elseif ($jumpto == LESSON_EOL
) {
938 // we have to run through the pages from pageid looking for jumpid
939 if ($lessonid = get_field('lesson_pages', 'lessonid', 'id', $pageid)) {
940 if ($pages = get_records('lesson_pages', 'lessonid', $lessonid, '', 'id, nextpageid')) {
941 $apageid = $pages[$pageid]->nextpageid
;
942 while ($apageid != 0) {
943 if ($jumpto == $apageid) {
946 $apageid = $pages[$apageid]->nextpageid
;
954 * Checks to see if a page is a branch table or is
955 * a page that is enclosed by a branch table and an end of branch or end of lesson.
956 * May call this function: {@link lesson_is_page_in_branch()}
958 * @param int $lesson Id of the lesson to which the page belongs.
959 * @param int $pageid Id of the page.
960 * @return boolean True or false.
962 function lesson_display_branch_jumps($lessonid, $pageid) {
967 // get all of the lesson pages
968 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
973 if ($lessonpages[$pageid]->qtype
== LESSON_BRANCHTABLE
) {
977 return lesson_is_page_in_branch($lessonpages, $pageid);
981 * Checks to see if a page is a cluster page or is
982 * a page that is enclosed by a cluster page and an end of cluster or end of lesson
983 * May call this function: {@link lesson_is_page_in_cluster()}
985 * @param int $lesson Id of the lesson to which the page belongs.
986 * @param int $pageid Id of the page.
987 * @return boolean True or false.
989 function lesson_display_cluster_jump($lesson, $pageid) {
994 // get all of the lesson pages
995 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
1000 if ($lessonpages[$pageid]->qtype
== LESSON_CLUSTER
) {
1004 return lesson_is_page_in_cluster($lessonpages, $pageid);
1009 * Checks to see if a LESSON_CLUSTERJUMP or
1010 * a LESSON_UNSEENBRANCHPAGE is used in a lesson.
1012 * This function is only executed when a teacher is
1013 * checking the navigation for a lesson.
1015 * @param int $lesson Id of the lesson that is to be checked.
1016 * @return boolean True or false.
1018 function lesson_display_teacher_warning($lesson) {
1019 // get all of the lesson answers
1020 if (!$lessonanswers = get_records_select("lesson_answers", "lessonid = $lesson")) {
1021 // no answers, then not useing cluster or unseen
1024 // just check for the first one that fulfills the requirements
1025 foreach ($lessonanswers as $lessonanswer) {
1026 if ($lessonanswer->jumpto
== LESSON_CLUSTERJUMP ||
$lessonanswer->jumpto
== LESSON_UNSEENBRANCHPAGE
) {
1031 // if no answers use either of the two jumps
1037 * Interprets LESSON_CLUSTERJUMP jumpto value.
1039 * This will select a page randomly
1040 * and the page selected will be inbetween a cluster page and end of cluter or end of lesson
1041 * and the page selected will be a page that has not been viewed already
1042 * and if any pages are within a branch table or end of branch then only 1 page within
1043 * the branch table or end of branch will be randomly selected (sub clustering).
1045 * @param int $lessonid Id of the lesson.
1046 * @param int $userid Id of the user.
1047 * @param int $pageid Id of the current page from which we are jumping from.
1048 * @return int The id of the next page.
1050 function lesson_cluster_jump($lessonid, $userid, $pageid) {
1051 // get the number of retakes
1052 if (!$retakes = count_records("lesson_grades", "lessonid", $lessonid, "userid", $userid)) {
1056 // get all the lesson_attempts aka what the user has seen
1057 if ($seen = get_records_select("lesson_attempts", "lessonid = $lessonid AND userid = $userid AND retry = $retakes", "timeseen DESC")) {
1058 foreach ($seen as $value) { // load it into an array that I can more easily use
1059 $seenpages[$value->pageid
] = $value->pageid
;
1062 $seenpages = array();
1065 // get the lesson pages
1066 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
1067 error("Error: could not find records in lesson_pages table");
1069 // find the start of the cluster
1070 while ($pageid != 0) { // this condition should not be satisfied... should be a cluster page
1071 if ($lessonpages[$pageid]->qtype
== LESSON_CLUSTER
) {
1074 $pageid = $lessonpages[$pageid]->prevpageid
;
1077 $pageid = $lessonpages[$pageid]->nextpageid
; // move down from the cluster page
1079 $clusterpages = array();
1080 while (true) { // now load all the pages into the cluster that are not already inside of a branch table.
1081 if ($lessonpages[$pageid]->qtype
== LESSON_ENDOFCLUSTER
) {
1082 // store the endofcluster page's jump
1083 $exitjump = get_field("lesson_answers", "jumpto", "pageid", $pageid, "lessonid", $lessonid);
1084 if ($exitjump == LESSON_NEXTPAGE
) {
1085 $exitjump = $lessonpages[$pageid]->nextpageid
;
1087 if ($exitjump == 0) {
1088 $exitjump = LESSON_EOL
;
1091 } elseif (!lesson_is_page_in_branch($lessonpages, $pageid) && $lessonpages[$pageid]->qtype
!= LESSON_ENDOFBRANCH
) {
1092 // load page into array when it is not in a branch table and when it is not an endofbranch
1093 $clusterpages[] = $lessonpages[$pageid];
1095 if ($lessonpages[$pageid]->nextpageid
== 0) {
1096 // shouldn't ever get here... should be using endofcluster
1097 $exitjump = LESSON_EOL
;
1100 $pageid = $lessonpages[$pageid]->nextpageid
;
1104 // filter out the ones we have seen
1106 foreach ($clusterpages as $clusterpage) {
1107 if ($clusterpage->qtype
== LESSON_BRANCHTABLE
) { // if branchtable, check to see if any pages inside have been viewed
1108 $branchpages = lesson_pages_in_branch($lessonpages, $clusterpage->id
); // get the pages in the branchtable
1110 foreach ($branchpages as $branchpage) {
1111 if (array_key_exists($branchpage->id
, $seenpages)) { // check if any of the pages have been viewed
1115 if ($flag && count($branchpages) > 0) {
1117 $unseen[] = $clusterpage;
1120 // add any other type of page that has not already been viewed
1121 if (!array_key_exists($clusterpage->id
, $seenpages)) {
1122 $unseen[] = $clusterpage;
1127 if (count($unseen) > 0) { // it does not contain elements, then use exitjump, otherwise find out next page/branch
1128 $nextpage = $unseen[rand(0, count($unseen)-1)];
1130 return $exitjump; // seen all there is to see, leave the cluster
1133 if ($nextpage->qtype
== LESSON_BRANCHTABLE
) { // if branch table, then pick a random page inside of it
1134 $branchpages = lesson_pages_in_branch($lessonpages, $nextpage->id
);
1135 return $branchpages[rand(0, count($branchpages)-1)]->id
;
1136 } else { // otherwise, return the page's id
1137 return $nextpage->id
;
1142 * Returns pages that are within a branch table and another branch table, end of branch or end of lesson
1144 * @param array $lessonpages An array of lesson page objects.
1145 * @param int $branchid The id of the branch table that we would like the containing pages for.
1146 * @return array An array of lesson page objects.
1148 function lesson_pages_in_branch($lessonpages, $branchid) {
1149 $pageid = $lessonpages[$branchid]->nextpageid
; // move to the first page after the branch table
1150 $pagesinbranch = array();
1153 if ($pageid == 0) { // EOL
1155 } elseif ($lessonpages[$pageid]->qtype
== LESSON_BRANCHTABLE
) {
1157 } elseif ($lessonpages[$pageid]->qtype
== LESSON_ENDOFBRANCH
) {
1160 $pagesinbranch[] = $lessonpages[$pageid];
1161 $pageid = $lessonpages[$pageid]->nextpageid
;
1164 return $pagesinbranch;
1168 * Interprets the LESSON_UNSEENBRANCHPAGE jump.
1170 * will return the pageid of a random unseen page that is within a branch
1172 * @see lesson_pages_in_branch()
1173 * @param int $lesson Id of the lesson.
1174 * @param int $userid Id of the user.
1175 * @param int $pageid Id of the page from which we are jumping.
1176 * @return int Id of the next page.
1178 function lesson_unseen_question_jump($lesson, $user, $pageid) {
1179 // get the number of retakes
1180 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
1184 // get all the lesson_attempts aka what the user has seen
1185 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson AND userid = $user AND retry = $retakes", "timeseen DESC")) {
1186 foreach($viewedpages as $viewed) {
1187 $seenpages[] = $viewed->pageid
;
1190 $seenpages = array();
1193 // get the lesson pages
1194 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
1195 error("Error: could not find records in lesson_pages table");
1198 if ($pageid == LESSON_UNSEENBRANCHPAGE
) { // this only happens when a student leaves in the middle of an unseen question within a branch series
1199 $pageid = $seenpages[0]; // just change the pageid to the last page viewed inside the branch table
1202 // go up the pages till branch table
1203 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
1204 if ($lessonpages[$pageid]->qtype
== LESSON_BRANCHTABLE
) {
1207 $pageid = $lessonpages[$pageid]->prevpageid
;
1210 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
1212 // this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array
1214 foreach($pagesinbranch as $page) {
1215 if (!in_array($page->id
, $seenpages)) {
1216 $unseen[] = $page->id
;
1220 if(count($unseen) == 0) {
1221 if(isset($pagesinbranch)) {
1222 $temp = end($pagesinbranch);
1223 $nextpage = $temp->nextpageid
; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL
1225 // there are no pages inside the branch, so return the next page
1226 $nextpage = $lessonpages[$pageid]->nextpageid
;
1228 if ($nextpage == 0) {
1234 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
1239 * Handles the unseen branch table jump.
1241 * @param int $lessonid Lesson id.
1242 * @param int $userid User id.
1243 * @return int Will return the page id of a branch table or end of lesson
1245 function lesson_unseen_branch_jump($lessonid, $userid) {
1246 if (!$retakes = count_records("lesson_grades", "lessonid", $lessonid, "userid", $userid)) {
1250 if (!$seenbranches = get_records_select("lesson_branch", "lessonid = $lessonid AND userid = $userid AND retry = $retakes",
1252 error("Error: could not find records in lesson_branch table");
1255 // get the lesson pages
1256 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
1257 error("Error: could not find records in lesson_pages table");
1260 // this loads all the viewed branch tables into $seen untill it finds the branch table with the flag
1261 // which is the branch table that starts the unseenbranch function
1263 foreach ($seenbranches as $seenbranch) {
1264 if (!$seenbranch->flag
) {
1265 $seen[$seenbranch->pageid
] = $seenbranch->pageid
;
1267 $start = $seenbranch->pageid
;
1271 // this function searches through the lesson pages to find all the branch tables
1272 // that follow the flagged branch table
1273 $pageid = $lessonpages[$start]->nextpageid
; // move down from the flagged branch table
1274 while ($pageid != 0) { // grab all of the branch table till eol
1275 if ($lessonpages[$pageid]->qtype
== LESSON_BRANCHTABLE
) {
1276 $branchtables[] = $lessonpages[$pageid]->id
;
1278 $pageid = $lessonpages[$pageid]->nextpageid
;
1281 foreach ($branchtables as $branchtable) {
1282 // load all of the unseen branch tables into unseen
1283 if (!array_key_exists($branchtable, $seen)) {
1284 $unseen[] = $branchtable;
1287 if (count($unseen) > 0) {
1288 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
1290 return LESSON_EOL
; // has viewed all of the branch tables
1295 * Handles the random jump between a branch table and end of branch or end of lesson (LESSON_RANDOMPAGE).
1297 * @param int $lessonid Lesson id.
1298 * @param int $pageid The id of the page that we are jumping from (?)
1299 * @return int The pageid of a random page that is within a branch table
1301 function lesson_random_question_jump($lessonid, $pageid) {
1302 // get the lesson pages
1303 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
1304 error("Error: could not find records in lesson_pages table");
1307 // go up the pages till branch table
1308 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
1310 if ($lessonpages[$pageid]->qtype
== LESSON_BRANCHTABLE
) {
1313 $pageid = $lessonpages[$pageid]->prevpageid
;
1316 // get the pages within the branch
1317 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
1319 if(count($pagesinbranch) == 0) {
1320 // there are no pages inside the branch, so return the next page
1321 return $lessonpages[$pageid]->nextpageid
;
1323 return $pagesinbranch[rand(0, count($pagesinbranch)-1)]->id
; // returns a random page id for the next page
1328 * Check to see if a page is below a branch table (logically).
1330 * Will return true if a branch table is found logically above the page.
1331 * Will return false if an end of branch, cluster or the beginning
1332 * of the lesson is found before a branch table.
1334 * @param array $pages An array of lesson page objects.
1335 * @param int $pageid Id of the page for testing.
1338 function lesson_is_page_in_branch($pages, $pageid) {
1339 $pageid = $pages[$pageid]->prevpageid
; // move up one
1341 // go up the pages till branch table
1343 if ($pageid == 0) { // ran into the beginning of the lesson
1345 } elseif ($pages[$pageid]->qtype
== LESSON_ENDOFBRANCH
) { // ran into the end of another branch table
1347 } elseif ($pages[$pageid]->qtype
== LESSON_CLUSTER
) { // do not look beyond a cluster
1349 } elseif ($pages[$pageid]->qtype
== LESSON_BRANCHTABLE
) { // hit a branch table
1352 $pageid = $pages[$pageid]->prevpageid
;
1358 * Check to see if a page is below a cluster page (logically).
1360 * Will return true if a cluster is found logically above the page.
1361 * Will return false if an end of cluster or the beginning
1362 * of the lesson is found before a cluster page.
1364 * @param array $pages An array of lesson page objects.
1365 * @param int $pageid Id of the page for testing.
1368 function lesson_is_page_in_cluster($pages, $pageid) {
1369 $pageid = $pages[$pageid]->prevpageid
; // move up one
1371 // go up the pages till branch table
1373 if ($pageid == 0) { // ran into the beginning of the lesson
1375 } elseif ($pages[$pageid]->qtype
== LESSON_ENDOFCLUSTER
) { // ran into the end of another branch table
1377 } elseif ($pages[$pageid]->qtype
== LESSON_CLUSTER
) { // hit a branch table
1380 $pageid = $pages[$pageid]->prevpageid
;
1385 * Calculates a user's grade for a lesson.
1387 * @param object $lesson The lesson that the user is taking.
1388 * @param int $retries The attempt number.
1389 * @param int $userid Id of the user (optinal, default current user).
1390 * @return object { nquestions => number of questions answered
1391 attempts => number of question attempts
1392 total => max points possible
1393 earned => points earned by student
1394 grade => calculated percentage grade
1395 nmanual => number of manually graded questions
1396 manualpoints => point value for manually graded questions }
1398 function lesson_grade($lesson, $ntries, $userid = 0) {
1401 if (empty($userid)) {
1402 $userid = $USER->id
;
1405 // Zero out everything
1416 if ($useranswers = get_records_select("lesson_attempts", "lessonid = $lesson->id AND
1417 userid = $userid AND retry = $ntries", "timeseen")) {
1418 // group each try with its page
1419 $attemptset = array();
1420 foreach ($useranswers as $useranswer) {
1421 $attemptset[$useranswer->pageid
][] = $useranswer;
1424 // Drop all attempts that go beyond max attempts for the lesson
1425 foreach ($attemptset as $key => $set) {
1426 $attemptset[$key] = array_slice($set, 0, $lesson->maxattempts
);
1429 $pageids = implode(",", array_keys($attemptset));
1431 // get only the pages and their answers that the user answered
1432 $pages = get_records_select("lesson_pages", "lessonid = $lesson->id AND id IN($pageids)");
1433 $answers = get_records_select("lesson_answers", "lessonid = $lesson->id AND pageid IN($pageids)");
1435 // Number of pages answered
1436 $nquestions = count($pages);
1438 foreach ($attemptset as $attempts) {
1439 if ($lesson->custom
) {
1440 $attempt = end($attempts);
1441 // If essay question, handle it, otherwise add to score
1442 if ($pages[$attempt->pageid
]->qtype
== LESSON_ESSAY
) {
1443 $essayinfo = unserialize($attempt->useranswer
);
1444 $earned +
= $essayinfo->score
;
1446 $manualpoints +
= $answers[$attempt->answerid
]->score
;
1448 $earned +
= $answers[$attempt->answerid
]->score
;
1451 foreach ($attempts as $attempt) {
1452 $earned +
= $attempt->correct
;
1454 $attempt = end($attempts); // doesn't matter which one
1455 // If essay question, increase numbers
1456 if ($pages[$attempt->pageid
]->qtype
== LESSON_ESSAY
) {
1461 // Number of times answered
1462 $nviewed +
= count($attempts);
1465 if ($lesson->custom
) {
1466 $bestscores = array();
1467 // Find the highest possible score per page to get our total
1468 foreach ($answers as $answer) {
1469 if(!isset($bestscores[$answer->pageid
])) {
1470 $bestscores[$answer->pageid
] = $answer->score
;
1471 } else if ($bestscores[$answer->pageid
] < $answer->score
) {
1472 $bestscores[$answer->pageid
] = $answer->score
;
1475 $total = array_sum($bestscores);
1477 // Check to make sure the student has answered the minimum questions
1478 if ($lesson->minquestions
and $nquestions < $lesson->minquestions
) {
1479 // Nope, increase number viewed by the amount of unanswered questions
1480 $total = $nviewed +
($lesson->minquestions
- $nquestions);
1487 if ($total) { // not zero
1488 $thegrade = round(100 * $earned / $total, 5);
1491 // Build the grade information object
1492 $gradeinfo = new stdClass
;
1493 $gradeinfo->nquestions
= $nquestions;
1494 $gradeinfo->attempts
= $nviewed;
1495 $gradeinfo->total
= $total;
1496 $gradeinfo->earned
= $earned;
1497 $gradeinfo->grade
= $thegrade;
1498 $gradeinfo->nmanual
= $nmanual;
1499 $gradeinfo->manualpoints
= $manualpoints;
1505 * Prints the on going message to the user.
1507 * With custom grading On, displays points
1508 * earned out of total points possible thus far.
1509 * With custom grading Off, displays number of correct
1510 * answers out of total attempted.
1512 * @param object $lesson The lesson that the user is taking.
1515 function lesson_print_ongoing_score($lesson) {
1517 $cm = get_coursemodule_from_instance('lesson', $lesson->id
);
1518 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
1520 if (has_capability('mod/lesson:manage', $context)) {
1521 echo "<p align=\"center\">".get_string('teacherongoingwarning', 'lesson').'</p>';
1523 $ntries = count_records("lesson_grades", "lessonid", $lesson->id
, "userid", $USER->id
);
1524 if (isset($USER->modattempts
[$lesson->id
])) {
1527 $gradeinfo = lesson_grade($lesson, $ntries);
1530 if ($lesson->custom
) {
1531 $a->score
= $gradeinfo->earned
;
1532 $a->currenthigh
= $gradeinfo->total
;
1533 print_simple_box(get_string("ongoingcustom", "lesson", $a), "center");
1535 $a->correct
= $gradeinfo->earned
;
1536 $a->viewed
= $gradeinfo->attempts
;
1537 print_simple_box(get_string("ongoingnormal", "lesson", $a), "center");
1543 * Prints tabs for the editing and adding pages. Each tab is a question type.
1545 * @param array $qtypes The question types array (may not need to pass this because it is defined in this file)
1546 * @param string $selected Current selected tab
1547 * @param string $link The base href value of the link for the tab
1548 * @param string $onclick Javascript for the tab link
1551 function lesson_qtype_menu($qtypes, $selected="", $link="", $onclick="") {
1555 foreach ($qtypes as $qtype => $qtypename) {
1556 $tabrows[] = new tabobject($qtype, "$link&qtype=$qtype\" onclick=\"$onclick", $qtypename);
1559 print_tabs($tabs, $selected);
1560 echo "<input type=\"hidden\" name=\"qtype\" value=\"$selected\" /> \n";
1565 * Prints out a Progress Bar which depicts a user's progress within a lesson.
1567 * Currently works best with a linear lesson. Clusters are counted as a single page.
1568 * Also, only viewed branch tables and questions that have been answered correctly count
1569 * toward lesson completion (or progress). Only Students can see the Progress bar as well.
1571 * @param object $lesson The lesson that the user is currently taking.
1572 * @param object $course The course that the to which the lesson belongs.
1573 * @return boolean The return is not significant as of yet. Will return true/false.
1575 function lesson_print_progress_bar($lesson, $course) {
1577 $cm = get_coursemodule_from_instance('lesson', $lesson->id
);
1578 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
1580 // lesson setting to turn progress bar on or off
1581 if (!$lesson->progressbar
) {
1586 if (has_capability('mod/lesson:manage', $context)) {
1587 notify(get_string('progressbarteacherwarning', 'lesson', $course->teachers
));
1590 if (!isset($USER->modattempts
[$lesson->id
])) {
1591 // all of the lesson pages
1592 if (!$pages = get_records('lesson_pages', 'lessonid', $lesson->id
)) {
1595 foreach ($pages as $page) {
1596 if ($page->prevpageid
== 0) {
1597 $pageid = $page->id
; // find the first page id
1603 // current attempt number
1604 if (!$ntries = count_records("lesson_grades", "lessonid", $lesson->id
, "userid", $USER->id
)) {
1605 $ntries = 0; // may not be necessary
1608 $viewedpageids = array();
1610 // collect all of the correctly answered questions
1611 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries AND correct = 1", 'timeseen DESC', 'pageid, id')) {
1612 $viewedpageids = array_keys($viewedpages);
1614 // collect all of the branch tables viewed
1615 if ($viewedbranches = get_records_select("lesson_branch", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries", 'timeseen DESC', 'pageid, id')) {
1616 $viewedpageids = array_merge($viewedpageids, array_keys($viewedbranches));
1619 // Filter out the following pages:
1622 // Pages found inside of Clusters
1623 // Do not filter out Cluster Page(s) because we count a cluster as one.
1624 // By keeping the cluster page, we get our 1
1625 $validpages = array();
1626 while ($pageid != 0) {
1627 if ($pages[$pageid]->qtype
== LESSON_CLUSTER
) {
1628 $clusterpageid = $pageid; // copy it
1629 $validpages[$clusterpageid] = 1; // add the cluster page as a valid page
1630 $pageid = $pages[$pageid]->nextpageid
; // get next page
1632 // now, remove all necessary viewed paged ids from the viewedpageids array.
1633 while ($pages[$pageid]->qtype
!= LESSON_ENDOFCLUSTER
and $pageid != 0) {
1634 if (in_array($pageid, $viewedpageids)) {
1635 unset($viewedpageids[array_search($pageid, $viewedpageids)]); // remove it
1636 // since the user did see one page in the cluster, add the cluster pageid to the viewedpageids
1637 if (!in_array($clusterpageid, $viewedpageids)) {
1638 $viewedpageids[] = $clusterpageid;
1641 $pageid = $pages[$pageid]->nextpageid
;
1643 } elseif ($pages[$pageid]->qtype
== LESSON_ENDOFCLUSTER
or $pages[$pageid]->qtype
== LESSON_ENDOFBRANCH
) {
1644 // dont count these, just go to next
1645 $pageid = $pages[$pageid]->nextpageid
;
1648 $validpages[$pageid] = 1;
1649 $pageid = $pages[$pageid]->nextpageid
;
1653 // progress calculation as a percent
1654 $progress = round(count($viewedpageids)/count($validpages), 2) * 100;
1659 // print out the Progress Bar. Attempted to put as much as possible in the style sheets.
1660 echo '<div class="progress_bar" align="center">';
1661 echo '<table class="progress_bar_table"><tr>';
1662 if ($progress != 0) { // some browsers do not repsect the 0 width.
1663 echo '<td style="width:'.$progress.'%;" class="progress_bar_completed">';
1666 echo '<td class="progress_bar_todo">';
1667 echo '<div class="progress_bar_token"></div>';
1669 echo '</tr></table>';
1676 * Determines if a user can view the left menu. The determining factor
1677 * is whether a user has a grade greater than or equal to the lesson setting
1680 * @param object $lesson Lesson object of the current lesson
1681 * @return boolean 0 if the user cannot see, or $lesson->displayleft to keep displayleft unchanged
1683 function lesson_displayleftif($lesson) {
1686 if (!empty($lesson->displayleftif
)) {
1687 // get the current user's max grade for this lesson
1688 if ($maxgrade = get_record_sql('SELECT userid, MAX(grade) AS maxgrade FROM '.$CFG->prefix
.'lesson_grades WHERE userid = '.$USER->id
.' AND lessonid = '.$lesson->id
.' GROUP BY userid')) {
1689 if ($maxgrade->maxgrade
< $lesson->displayleftif
) {
1690 return 0; // turn off the displayleft
1693 return 0; // no grades
1697 // if we get to here, keep the original state of displayleft lesson setting
1698 return $lesson->displayleft
;
1702 * If there is a media file associated with this
1703 * lesson, then print it in a block.
1705 * @param int $cmid Course Module ID for this lesson
1706 * @param object $lesson Full lesson record object
1709 function lesson_print_mediafile_block($cmid, $lesson) {
1710 if (!empty($lesson->mediafile
)) {
1711 $url = '/mod/lesson/mediafile.php?id='.$cmid;
1712 $options = 'menubar=0,location=0,left=5,top=5,scrollbars,resizable,width='. $lesson->mediawidth
.',height='. $lesson->mediaheight
;
1713 $name = 'lessonmediafile';
1715 $content = link_to_popup_window ($url, $name, get_string('mediafilepopup', 'lesson'), '', '', get_string('mediafilepopup', 'lesson'), $options, true);
1716 $content .= helpbutton("mediafilestudent", get_string("mediafile", "lesson"), "lesson", true, false, '', true);
1718 print_side_block(get_string('linkedmedia', 'lesson'), $content, NULL, NULL, '', array('class' => 'mediafile'), get_string('linkedmedia', 'lesson'));
1723 * If a timed lesson and not a teacher, then
1726 * @param int $cmid Course Module ID for this lesson
1727 * @param object $lesson Full lesson record object
1728 * @param object $timer Full timer record object
1731 function lesson_print_clock_block($cmid, $lesson, $timer) {
1734 $context = get_context_instance(CONTEXT_MODULE
, $cmid);
1736 // Display for timed lessons and for students only
1737 if($lesson->timed
and !has_capability('mod/lesson:manage', $context) and !empty($timer)) {
1738 $content = '<script type="text/javascript" charset="utf-8">'."\n";
1739 $content .= "<!--\n";
1740 $content .= ' var starttime = '.$timer->starttime
.";\n";
1741 $content .= ' var servertime = '.time().";\n";
1742 $content .= ' var testlength = '.($lesson->maxtime
* 60).";\n";
1743 $content .= ' document.write(\'<script type="text/javascript" src="'.$CFG->wwwroot
.'/mod/lesson/timer.js" charset="utf-8"><\/script>\');'."\n";
1744 $content .= " window.onload = function () { show_clock(); };\n";
1745 $content .= "// -->\n";
1746 $content .= "</script>\n";
1747 $content .= "<noscript>\n";
1748 $content .= lesson_print_time_remaining($timer->starttime
, $lesson->maxtime
, true)."\n";
1749 $content .= "</noscript>\n";
1751 print_side_block(get_string('timeremaining', 'lesson'), $content, NULL, NULL, '', array('class' => 'clock'), get_string('timeremaining', 'lesson'));
1756 * If left menu is turned on, then this will
1757 * print the menu in a block
1759 * @param int $cmid Course Module ID for this lesson
1760 * @param object $lesson Full lesson record object
1763 function lesson_print_menu_block($cmid, $lesson) {
1766 if ($lesson->displayleft
) {
1767 $pageid = get_field('lesson_pages', 'id', 'lessonid', $lesson->id
, 'prevpageid', 0);
1768 $pages = get_records_select('lesson_pages', "lessonid = $lesson->id");
1769 $currentpageid = optional_param('pageid', $pageid, PARAM_INT
);
1771 if ($pageid and $pages) {
1772 $content = '<a href="#maincontent" class="skip">'.get_string('skip', 'lesson')."</a>\n<div class=\"menuwrapper\">\n<ul>\n";
1774 while ($pageid != 0) {
1775 $page = $pages[$pageid];
1777 // Only process branch tables with display turned on
1778 if ($page->qtype
== LESSON_BRANCHTABLE
and $page->display
) {
1779 if ($page->id
== $currentpageid) {
1780 $content .= '<li class="selected">'.format_string($page->title
,true)."</a></li>\n";
1782 $content .= "<li class=\"notselected\"><a href=\"$CFG->wwwroot/mod/lesson/view.php?id=$cmid&pageid=$page->id\">".format_string($page->title
,true)."</a></li>\n";
1786 $pageid = $page->nextpageid
;
1788 $content .= "</ul>\n</div>\n";
1789 print_side_block(get_string('lessonmenu', 'lesson'), $content, NULL, NULL, '', array('class' => 'menu'), get_string('lessonmenu', 'lesson'));
1795 * This is not ideal, but checks to see if a
1796 * column has "block" content.
1798 * In the future, it would be nice if the lesson
1799 * media file, timer and navigation were blocks
1800 * then this would be unnecessary.
1804 * @param object $lesson Full lesson record object
1805 * @param array $pageblocks An array of block instances organized by left and right columns
1806 * @param string $column Pass either BLOCK_POS_RIGHT or BLOCK_POS_LEFT constants
1809 function lesson_blocks_have_content($lesson, $pageblocks, $column) {
1812 // First check lesson conditions
1813 if ($column == BLOCK_POS_RIGHT
) {
1815 if ($cm = get_coursemodule_from_instance('lesson', $lesson->id
, $lesson->course
)) {
1816 $managecap = has_capability('mod/lesson:manage', get_context_instance(CONTEXT_MODULE
, $cm->id
));
1818 if (($lesson->timed
and !$managecap) or !empty($lesson->mediafile
)) {
1821 } else if ($column == BLOCK_POS_LEFT
) {
1822 if ($lesson->displayleft
) {
1826 if (!empty($CFG->showblocksonmodpages
)) {
1827 if ((blocks_have_content($pageblocks, $column) ||
$PAGE->user_is_editing())) {