Added lang entries for Collapsed editing
[moodle.git] / mod / lesson / locallib.php
CommitLineData
9fcf51d9 1<?php // $Id$
62eda6ea 2/// mnielsen
5e7856af 3/// locallib.php is the new lib file for lesson module.
4/// including locallib.php is the same as including the old lib.php
4b55d2af 5
6/**
7* Next page -> any page not seen before
8*/
5e7856af 9if (!defined("LESSON_UNSEENPAGE")) {
ac8e16be 10 define("LESSON_UNSEENPAGE", 1); // Next page -> any page not seen before
4b55d2af 11}
12/**
13* Next page -> any page not answered correctly
14*/
5e7856af 15if (!defined("LESSON_UNANSWEREDPAGE")) {
ac8e16be 16 define("LESSON_UNANSWEREDPAGE", 2); // Next page -> any page not answered correctly
4b55d2af 17}
5e7856af 18
4b55d2af 19/**
20* Define different lesson flows for next page
21*/
5e7856af 22$LESSON_NEXTPAGE_ACTION = array (0 => get_string("normal", "lesson"),
23 LESSON_UNSEENPAGE => get_string("showanunseenpage", "lesson"),
24 LESSON_UNANSWEREDPAGE => get_string("showanunansweredpage", "lesson") );
25
4b55d2af 26// Lesson jump types defined
27// TODO: instead of using define statements, create an array with all the jump values
5e7856af 28
4b55d2af 29/**
30 * Jump to Next Page
31 */
5e7856af 32if (!defined("LESSON_NEXTPAGE")) {
4b55d2af 33 define("LESSON_NEXTPAGE", -1);
34}
35/**
36 * End of Lesson
37 */
5e7856af 38if (!defined("LESSON_EOL")) {
4b55d2af 39 define("LESSON_EOL", -9);
40}
41/**
42 * Jump to an unseen page within a branch and end of branch or end of lesson
43 */
5e7856af 44if (!defined("LESSON_UNSEENBRANCHPAGE")) {
4b55d2af 45 define("LESSON_UNSEENBRANCHPAGE", -50);
46}
47/**
48 * Jump to Previous Page
49 */
5e7856af 50if (!defined("LESSON_PREVIOUSPAGE")) {
4b55d2af 51 define("LESSON_PREVIOUSPAGE", -40);
52}
53/**
54 * Jump to a random page within a branch and end of branch or end of lesson
55 */
5e7856af 56if (!defined("LESSON_RANDOMPAGE")) {
4b55d2af 57 define("LESSON_RANDOMPAGE", -60);
58}
59/**
60 * Jump to a random Branch
61 */
5e7856af 62if (!defined("LESSON_RANDOMBRANCH")) {
4b55d2af 63 define("LESSON_RANDOMBRANCH", -70);
64}
65/**
66 * Cluster Jump
67 */
5e7856af 68if (!defined("LESSON_CLUSTERJUMP")) {
4b55d2af 69 define("LESSON_CLUSTERJUMP", -80);
70}
71/**
72 * Undefined
73 */
5e7856af 74if (!defined("LESSON_UNDEFINED")) {
4b55d2af 75 define("LESSON_UNDEFINED", -99);
76}
77
78// Lesson question types defined
5e7856af 79
4b55d2af 80/**
81 * Short answer question type
82 */
5e7856af 83if (!defined("LESSON_SHORTANSWER")) {
84 define("LESSON_SHORTANSWER", "1");
85}
4b55d2af 86/**
87 * True/False question type
88 */
5e7856af 89if (!defined("LESSON_TRUEFALSE")) {
90 define("LESSON_TRUEFALSE", "2");
91}
4b55d2af 92/**
93 * Multichoice question type
94 *
95 * If you change the value of this then you need
96 * to change it in restorelib.php as well.
97 */
98if (!defined("LESSON_MULTICHOICE")) {
5e7856af 99 define("LESSON_MULTICHOICE", "3");
100}
4b55d2af 101/**
102 * Random question type - not used
103 */
5e7856af 104if (!defined("LESSON_RANDOM")) {
105 define("LESSON_RANDOM", "4");
106}
4b55d2af 107/**
108 * Matching question type
109 *
110 * If you change the value of this then you need
111 * to change it in restorelib.php, in mysql.php
112 * and postgres7.php as well.
113 */
114if (!defined("LESSON_MATCHING")) {
b9869082 115 define("LESSON_MATCHING", "5");
5e7856af 116}
4b55d2af 117/**
118 * Not sure - not used
119 */
5e7856af 120if (!defined("LESSON_RANDOMSAMATCH")) {
121 define("LESSON_RANDOMSAMATCH", "6");
122}
4b55d2af 123/**
124 * Not sure - not used
125 */
5e7856af 126if (!defined("LESSON_DESCRIPTION")) {
127 define("LESSON_DESCRIPTION", "7");
128}
4b55d2af 129/**
130 * Numerical question type
131 */
5e7856af 132if (!defined("LESSON_NUMERICAL")) {
133 define("LESSON_NUMERICAL", "8");
134}
4b55d2af 135/**
136 * Multichoice with multianswer question type
137 */
5e7856af 138if (!defined("LESSON_MULTIANSWER")) {
139 define("LESSON_MULTIANSWER", "9");
140}
4b55d2af 141/**
142 * Essay question type
143 */
5e7856af 144if (!defined("LESSON_ESSAY")) {
ac8e16be 145 define("LESSON_ESSAY", "10");
5e7856af 146}
5e7856af 147
4b55d2af 148/**
149 * Lesson question type array.
150 * Contains all question types used
151 */
5e7856af 152$LESSON_QUESTION_TYPE = array ( LESSON_MULTICHOICE => get_string("multichoice", "quiz"),
153 LESSON_TRUEFALSE => get_string("truefalse", "quiz"),
154 LESSON_SHORTANSWER => get_string("shortanswer", "quiz"),
155 LESSON_NUMERICAL => get_string("numerical", "quiz"),
156 LESSON_MATCHING => get_string("match", "quiz"),
62eda6ea 157 LESSON_ESSAY => get_string("essay", "lesson")
5e7856af 158// LESSON_DESCRIPTION => get_string("description", "quiz"),
159// LESSON_RANDOM => get_string("random", "quiz"),
160// LESSON_RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
161// LESSON_MULTIANSWER => get_string("multianswer", "quiz"),
162 );
163
4b55d2af 164// Non-question page types
165
166/**
167 * Branch Table page
168 */
5e7856af 169if (!defined("LESSON_BRANCHTABLE")) {
170 define("LESSON_BRANCHTABLE", "20");
171}
4b55d2af 172/**
173 * End of Branch page
174 */
5e7856af 175if (!defined("LESSON_ENDOFBRANCH")) {
176 define("LESSON_ENDOFBRANCH", "21");
177}
4b55d2af 178/**
179 * Start of Cluster page
180 */
181if (!defined("LESSON_CLUSTER")) {
182 define("LESSON_CLUSTER", "30");
183}
184/**
185 * End of Cluster page
186 */
187if (!defined("LESSON_ENDOFCLUSTER")) {
188 define("LESSON_ENDOFCLUSTER", "31");
189}
190
191// other variables...
5e7856af 192
4b55d2af 193/**
194 * Flag for the editor for the answer textarea.
195 */
5e7856af 196if (!defined("LESSON_ANSWER_EDITOR")) {
197 define("LESSON_ANSWER_EDITOR", "1");
198}
4b55d2af 199/**
200 * Flag for the editor for the response textarea.
201 */
5e7856af 202if (!defined("LESSON_RESPONSE_EDITOR")) {
203 define("LESSON_RESPONSE_EDITOR", "2");
204}
205
206//////////////////////////////////////////////////////////////////////////////////////
207/// Any other lesson functions go here. Each of them must have a name that
208/// starts with lesson_
209
9fcf51d9 210/**
211 * Print the standard header for lesson module
212 *
213 * @param object $cm Course module record object
214 * @param object $course Couse record object
215 * @param object $lesson Lesson module record object
216 * @param string $currenttab Current tab for the lesson tabs
217 * @param boolean $printheading Print the a heading with the lesson name
218 * @return void
219 **/
d1b025d0 220function lesson_print_header($cm, $course, $lesson, $currenttab = '') {
9fcf51d9 221 global $CFG, $USER;
fb992d71 222
223 $strlessons = get_string('modulenameplural', 'lesson');
224 $strlesson = get_string('modulename', 'lesson');
225 $strname = format_string($lesson->name, true);
226
6e1ff5c8 227 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
9fcf51d9 228
fb992d71 229 // Changed the update_module_button and added another button when a teacher is checking the navigation of the lesson
230 if (has_capability('mod/lesson:edit', $context)) {
231
232 $button = update_module_button($cm->id, $course->id, $strlesson);
233
234 if ($currenttab == 'view') {
235 if (!$pageid = optional_param('pageid', 0, PARAM_INT)) {
236 $pageid = get_field('lesson_pages', 'id', 'lessonid', $lesson->id, 'prevpageid', 0);
237 }
238 if (!empty($pageid) and $pageid != LESSON_EOL) {
239 $button = '<table><tr><td>'.$button.
240 '</td><td>'.
241 '<form target="'. $CFG->framename .'" method="get" action="'. $CFG->wwwroot .'/mod/lesson/lesson.php">'.
242 '<input type="hidden" name="id" value="'. $cm->id .'" />'.
243 '<input type="hidden" name="action" value="editpage" />'.
244 '<input type="hidden" name="redirect" value="navigation" />'.
245 '<input type="hidden" name="pageid" value="'. $pageid .'" />'.
246 '<input type="submit" value="'. get_string('editpagecontent', 'lesson') .'" /></form>
247 </td></tr></table>';
248 }
249 }
250 } else {
251 $button = '';
252 }
253
5631925a 254 if (!optional_param('pageid', 0, PARAM_INT) and !empty($lesson->mediafile)) {
255 // open our pop-up
256 $url = '/mod/lesson/mediafile.php?id='.$cm->id;
257 $name = 'lessonmediafile';
258 $options = 'menubar=0,location=0,left=5,top=5,scrollbars,resizable,width='. $lesson->mediawidth .',height='. $lesson->mediaheight;
259 $meta = "\n<script language=\"javascript\" type=\"text/javascript\">";
260 $meta .= "\n<!--\n";
261 $meta .= " openpopup('$url', '$name', '$options', 0);";
262 $meta .= "\n-->\n";
263 $meta .= '</script>';
264 } else {
265 $meta = '';
266 }
267
9fcf51d9 268/// Header setup
269 if ($course->category) {
270 $navigation = "<a href=\"$CFG->wwwroot/course/view.php?id=$course->id\" title=\"$course->fullname\">$course->shortname</a> ->";
271 } else {
272 $navigation = '';
273 }
274
9fcf51d9 275
276/// Print header, heading, tabs and messages
277 print_header("$course->shortname: $strname", $course->fullname,
278 "$navigation <a href=\"index.php?id=$course->id\" title=\"$strlessons\">$strlessons</a> -> $strname",
5631925a 279 '', $meta, true, $button, navmenu($course, $cm));
9fcf51d9 280
d1b025d0 281 if (has_capability('mod/lesson:manage')) {
9fcf51d9 282 print_heading_with_help(format_string($lesson->name, true), "overview", "lesson");
d1b025d0 283 } else {
284 print_heading($lesson->name);
9fcf51d9 285 }
286
6e1ff5c8 287 if (!empty($currenttab) and has_capability('mod/lesson:manage', $context)) {
9fcf51d9 288 include($CFG->dirroot.'/mod/lesson/tabs.php');
289 }
f15eb92c 290
291 lesson_print_messages();
9fcf51d9 292}
293
294/**
295 * Returns course module, course and module instance given
296 * either the course module ID or a lesson module ID.
297 *
298 * @param int $cmid Course Module ID
299 * @param int $lessonid Lesson module instance ID
300 * @return array array($cm, $course, $lesson)
301 **/
302function lesson_get_basics($cmid = 0, $lessonid = 0) {
303 if ($cmid) {
f15eb92c 304 if (!$cm = get_coursemodule_from_id('lesson', $cmid)) {
9fcf51d9 305 error('Course Module ID was incorrect');
306 }
307 if (!$course = get_record('course', 'id', $cm->course)) {
308 error('Course is misconfigured');
309 }
310 if (!$lesson = get_record('lesson', 'id', $cm->instance)) {
311 error('Course module is incorrect');
312 }
313 } else if ($lessonid) {
314 if (!$lesson = get_record('lesson', 'id', $lessonid)) {
315 error('Course module is incorrect');
316 }
317 if (!$course = get_record('course', 'id', $lesson->course)) {
318 error('Course is misconfigured');
319 }
320 if (!$cm = get_coursemodule_from_instance('lesson', $lesson->id, $course->id)) {
321 error('Course Module ID was incorrect');
322 }
323 } else {
324 error('No course module ID or lesson ID were passed');
325 }
326
327 return array($cm, $course, $lesson);
328}
329
6e1ff5c8 330/**
331 * Sets a message to be printed. Messages are printed
332 * by calling {@link lesson_print_messages()}.
333 *
334 * @uses $SESSION
335 * @param string $message The message to be printed
336 * @param string $class Class to be passed to {@link notify()}. Usually notifyproblem or notifysuccess.
337 * @param string $align Alignment of the message
338 * @return boolean
339 **/
340function lesson_set_message($message, $class="notifyproblem", $align='center') {
341 global $SESSION;
342
343 if (empty($SESSION->lesson_messages) or !is_array($SESSION->lesson_messages)) {
344 $SESSION->lesson_messages = array();
345 }
346
347 $SESSION->lesson_messages[] = array($message, $class, $align);
348
349 return true;
350}
351
352/**
353 * Print all set messages.
354 *
355 * See {@link lesson_set_message()} for setting messages.
356 *
357 * Uses {@link notify()} to print the messages.
358 *
359 * @uses $SESSION
360 * @return boolean
361 **/
362function lesson_print_messages() {
363 global $SESSION;
364
365 if (empty($SESSION->lesson_messages)) {
366 // No messages to print
367 return true;
368 }
369
370 foreach($SESSION->lesson_messages as $message) {
371 notify($message[0], $message[1], $message[2]);
372 }
373
374 // Reset
375 unset($SESSION->lesson_messages);
376
377 return true;
378}
379
259990d2 380/**
381 * Prints a lesson link that submits a form.
382 *
383 * If Javascript is disabled, then a regular submit button is printed
384 *
385 * @return mixed boolean/html
386 **/
387function lesson_print_submit_link($name, $form, $align = 'center', $class='standardbutton', $title = '', $id = '', $return = false) {
388 if (!empty($id)) {
389 $id = " id=\"$id\"";
390 }
391 if (empty($title)) {
392 $title = $name;
393 }
394
395 $output = "<div align=\"$align\" class=\"lessonbutton $class\">\n";
396 $output .= "<script type=\"text/javascript\" charset=\"utf-8\">
397 <!--
398 document.write('<a href=\"javascript: document.$form.submit();\" title=\"$title\"$id>$name</a>');
399 // -->
400 </script>
401 <noscript>
402 <input type=\"submit\" value=\"$name\" align=\"$align\"$id />
403 </noscript>\n";
404 $output .= "</div>\n";
405
2cb24b64 406 if ($return) {
407 return $output;
408 } else {
409 echo $output;
410 return true;
411 }
412}
413
414/**
415 * Prints a time remaining in the following format: H:MM:SS
416 *
417 * @param int $starttime Time when the lesson started
418 * @param int $maxtime Length of the lesson
419 * @param boolean $return Return output switch
420 * @return mixed boolean/string
421 **/
2163c226 422function lesson_print_time_remaining($starttime, $maxtime, $return = false) {
2cb24b64 423 // Calculate hours, minutes and seconds
424 $timeleft = $starttime + $maxtime * 60 - time();
425 $hours = floor($timeleft/3600);
426 $timeleft = $timeleft - ($hours * 3600);
427 $minutes = floor($timeleft/60);
428 $secs = $timeleft - ($minutes * 60);
429
430 if ($minutes < 10) {
431 $minutes = "0$minutes";
432 }
433 if ($secs < 10) {
434 $secs = "0$secs";
435 }
436 $output = array();
437 $output[] = $hours;
438 $output[] = $minutes;
439 $output[] = $secs;
440
441 $output = implode(':', $output);
442
259990d2 443 if ($return) {
444 return $output;
445 } else {
446 echo $output;
447 return true;
448 }
449}
450
4b55d2af 451/**
452 * Given some question info and some data about the the answers
453 * this function parses, organises and saves the question
454 *
455 * This is only used when IMPORTING questions and is only called
456 * from format.php
457 * Lifted from mod/quiz/lib.php -
458 * 1. all reference to oldanswers removed
459 * 2. all reference to quiz_multichoice table removed
460 * 3. In SHORTANSWER questions usecase is store in the qoption field
461 * 4. In NUMERIC questions store the range as two answers
462 * 5. TRUEFALSE options are ignored
463 * 6. For MULTICHOICE questions with more than one answer the qoption field is true
464 *
465 * @param opject $question Contains question data like question, type and answers.
466 * @return object Returns $result->error or $result->notice.
467 **/
5e7856af 468function lesson_save_question_options($question) {
5e7856af 469
470 $timenow = time();
471 switch ($question->qtype) {
472 case LESSON_SHORTANSWER:
473
474 $answers = array();
475 $maxfraction = -1;
476
477 // Insert all the new answers
478 foreach ($question->answer as $key => $dataanswer) {
479 if ($dataanswer != "") {
f7ffb898 480 $answer = new stdClass;
5e7856af 481 $answer->lessonid = $question->lessonid;
482 $answer->pageid = $question->id;
483 if ($question->fraction[$key] >=0.5) {
484 $answer->jumpto = LESSON_NEXTPAGE;
485 }
486 $answer->timecreated = $timenow;
487 $answer->grade = $question->fraction[$key] * 100;
488 $answer->answer = $dataanswer;
c87a341c 489 $answer->response = $question->feedback[$key];
5e7856af 490 if (!$answer->id = insert_record("lesson_answers", $answer)) {
491 $result->error = "Could not insert shortanswer quiz answer!";
492 return $result;
493 }
494 $answers[] = $answer->id;
495 if ($question->fraction[$key] > $maxfraction) {
496 $maxfraction = $question->fraction[$key];
497 }
498 }
499 }
500
501
502 /// Perform sanity checks on fractional grades
503 if ($maxfraction != 1) {
504 $maxfraction = $maxfraction * 100;
505 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
506 return $result;
507 }
508 break;
509
510 case LESSON_NUMERICAL: // Note similarities to SHORTANSWER
511
512 $answers = array();
513 $maxfraction = -1;
514
515
516 // for each answer store the pair of min and max values even if they are the same
517 foreach ($question->answer as $key => $dataanswer) {
518 if ($dataanswer != "") {
f7ffb898 519 $answer = new stdClass;
5e7856af 520 $answer->lessonid = $question->lessonid;
521 $answer->pageid = $question->id;
522 $answer->jumpto = LESSON_NEXTPAGE;
523 $answer->timecreated = $timenow;
524 $answer->grade = $question->fraction[$key] * 100;
dd192b26 525 $min = $question->answer[$key] - $question->tolerance[$key];
526 $max = $question->answer[$key] + $question->tolerance[$key];
527 $answer->answer = $min.":".$max;
528 // $answer->answer = $question->min[$key].":".$question->max[$key]; original line for min/max
5e7856af 529 $answer->response = $question->feedback[$key];
530 if (!$answer->id = insert_record("lesson_answers", $answer)) {
531 $result->error = "Could not insert numerical quiz answer!";
532 return $result;
533 }
534
535 $answers[] = $answer->id;
536 if ($question->fraction[$key] > $maxfraction) {
537 $maxfraction = $question->fraction[$key];
538 }
539 }
540 }
541
542 /// Perform sanity checks on fractional grades
543 if ($maxfraction != 1) {
544 $maxfraction = $maxfraction * 100;
545 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
546 return $result;
547 }
548 break;
549
550
551 case LESSON_TRUEFALSE:
552
553 // the truth
554 $answer->lessonid = $question->lessonid;
555 $answer->pageid = $question->id;
556 $answer->timecreated = $timenow;
557 $answer->answer = get_string("true", "quiz");
558 $answer->grade = $question->answer * 100;
559 if ($answer->grade > 50 ) {
560 $answer->jumpto = LESSON_NEXTPAGE;
561 }
562 if (isset($question->feedbacktrue)) {
563 $answer->response = $question->feedbacktrue;
564 }
565 if (!$true->id = insert_record("lesson_answers", $answer)) {
566 $result->error = "Could not insert quiz answer \"true\")!";
567 return $result;
568 }
569
570 // the lie
f7ffb898 571 $answer = new stdClass;
5e7856af 572 $answer->lessonid = $question->lessonid;
573 $answer->pageid = $question->id;
574 $answer->timecreated = $timenow;
575 $answer->answer = get_string("false", "quiz");
576 $answer->grade = (1 - (int)$question->answer) * 100;
577 if ($answer->grade > 50 ) {
578 $answer->jumpto = LESSON_NEXTPAGE;
579 }
580 if (isset($question->feedbackfalse)) {
581 $answer->response = $question->feedbackfalse;
582 }
583 if (!$false->id = insert_record("lesson_answers", $answer)) {
584 $result->error = "Could not insert quiz answer \"false\")!";
585 return $result;
586 }
587
588 break;
589
590
591 case LESSON_MULTICHOICE:
592
593 $totalfraction = 0;
594 $maxfraction = -1;
595
596 $answers = array();
597
598 // Insert all the new answers
599 foreach ($question->answer as $key => $dataanswer) {
600 if ($dataanswer != "") {
f7ffb898 601 $answer = new stdClass;
5e7856af 602 $answer->lessonid = $question->lessonid;
603 $answer->pageid = $question->id;
604 $answer->timecreated = $timenow;
605 $answer->grade = $question->fraction[$key] * 100;
62eda6ea 606 // changed some defaults
ac8e16be 607 /* Original Code
608 if ($answer->grade > 50 ) {
5e7856af 609 $answer->jumpto = LESSON_NEXTPAGE;
610 }
ac8e16be 611 Replaced with: */
612 if ($answer->grade > 50 ) {
613 $answer->jumpto = LESSON_NEXTPAGE;
5e7856af 614 $answer->score = 1;
615 }
ac8e16be 616 // end Replace
5e7856af 617 $answer->answer = $dataanswer;
618 $answer->response = $question->feedback[$key];
619 if (!$answer->id = insert_record("lesson_answers", $answer)) {
620 $result->error = "Could not insert multichoice quiz answer! ";
621 return $result;
622 }
623 // for Sanity checks
624 if ($question->fraction[$key] > 0) {
625 $totalfraction += $question->fraction[$key];
626 }
627 if ($question->fraction[$key] > $maxfraction) {
628 $maxfraction = $question->fraction[$key];
629 }
630 }
631 }
632
633 /// Perform sanity checks on fractional grades
634 if ($question->single) {
635 if ($maxfraction != 1) {
636 $maxfraction = $maxfraction * 100;
637 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
638 return $result;
639 }
640 } else {
641 $totalfraction = round($totalfraction,2);
642 if ($totalfraction != 1) {
643 $totalfraction = $totalfraction * 100;
644 $result->notice = get_string("fractionsaddwrong", "quiz", $totalfraction);
645 return $result;
646 }
647 }
648 break;
649
650 case LESSON_MATCHING:
651
652 $subquestions = array();
653
654 $i = 0;
655 // Insert all the new question+answer pairs
656 foreach ($question->subquestions as $key => $questiontext) {
90455bb3 657 $answertext = $question->subanswers[$key];
5e7856af 658 if (!empty($questiontext) and !empty($answertext)) {
f7ffb898 659 $answer = new stdClass;
5e7856af 660 $answer->lessonid = $question->lessonid;
661 $answer->pageid = $question->id;
662 $answer->timecreated = $timenow;
663 $answer->answer = $questiontext;
664 $answer->response = $answertext;
665 if ($i == 0) {
666 // first answer contains the correct answer jump
667 $answer->jumpto = LESSON_NEXTPAGE;
668 }
669 if (!$subquestion->id = insert_record("lesson_answers", $answer)) {
670 $result->error = "Could not insert quiz match subquestion!";
671 return $result;
672 }
673 $subquestions[] = $subquestion->id;
674 $i++;
675 }
676 }
677
678 if (count($subquestions) < 3) {
679 $result->notice = get_string("notenoughsubquestions", "quiz");
680 return $result;
681 }
682
683 break;
684
685
686 case LESSON_RANDOMSAMATCH:
687 $options->question = $question->id;
688 $options->choose = $question->choose;
689 if ($existing = get_record("quiz_randomsamatch", "question", $options->question)) {
690 $options->id = $existing->id;
691 if (!update_record("quiz_randomsamatch", $options)) {
692 $result->error = "Could not update quiz randomsamatch options!";
693 return $result;
694 }
695 } else {
696 if (!insert_record("quiz_randomsamatch", $options)) {
697 $result->error = "Could not insert quiz randomsamatch options!";
698 return $result;
699 }
700 }
701 break;
702
703 case LESSON_MULTIANSWER:
704 if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id, "id ASC")) {
705 $oldmultianswers = array();
706 }
707
708 // Insert all the new multi answers
709 foreach ($question->answers as $dataanswer) {
710 if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it
711 $multianswer = $oldmultianswer;
712 $multianswer->positionkey = $dataanswer->positionkey;
713 $multianswer->norm = $dataanswer->norm;
714 $multianswer->answertype = $dataanswer->answertype;
715
716 if (! $multianswer->answers = quiz_save_multianswer_alternatives
717 ($question->id, $dataanswer->answertype,
718 $dataanswer->alternatives, $oldmultianswer->answers))
719 {
720 $result->error = "Could not update multianswer alternatives! (id=$multianswer->id)";
721 return $result;
722 }
723 if (!update_record("quiz_multianswers", $multianswer)) {
724 $result->error = "Could not update quiz multianswer! (id=$multianswer->id)";
725 return $result;
726 }
727 } else { // This is a completely new answer
f7ffb898 728 $multianswer = new stdClass;
5e7856af 729 $multianswer->question = $question->id;
730 $multianswer->positionkey = $dataanswer->positionkey;
731 $multianswer->norm = $dataanswer->norm;
732 $multianswer->answertype = $dataanswer->answertype;
733
734 if (! $multianswer->answers = quiz_save_multianswer_alternatives
735 ($question->id, $dataanswer->answertype,
736 $dataanswer->alternatives))
737 {
738 $result->error = "Could not insert multianswer alternatives! (questionid=$question->id)";
739 return $result;
740 }
741 if (!insert_record("quiz_multianswers", $multianswer)) {
742 $result->error = "Could not insert quiz multianswer!";
743 return $result;
744 }
745 }
746 }
747 break;
748
749 case LESSON_RANDOM:
750 break;
751
752 case LESSON_DESCRIPTION:
753 break;
754
755 default:
756 $result->error = "Unsupported question type ($question->qtype)!";
757 return $result;
758 break;
759 }
760 return true;
761}
4b55d2af 762
763/**
764 * Given an array of value, creates a popup menu to be part of a form.
765 *
766 * @param array $options Used to create the popup menu values ( $options["value"]["label"] ).
767 * @param string $name Name of the select form element.
768 * @param string $selected Current value selected in the popup menu.
769 * @param string $nothing If set, used as the first value in the popup menu.
770 * @param string $script OnChange javascript code.
771 * @param string|int $nothingvalue Value of the $nothing parameter.
772 * @param boolean $return False: Print out the popup menu automatically True: Return the popup menu.
773 * @return string May return the popup menu as a string.
774 * @todo replace the use of this function with choose_from_menu in lib/weblib.php
775 **/
776function lesson_choose_from_menu ($options, $name, $selected="", $nothing="choose", $script="", $nothingvalue="0", $return=false) {
5e7856af 777 if ($nothing == "choose") {
778 $nothing = get_string("choose")."...";
779 }
780
781 if ($script) {
782 $javascript = "onChange=\"$script\"";
783 } else {
784 $javascript = "";
785 }
786
62eda6ea 787 $output = "<label for=$name class=hidden-label>$name</label><SELECT id=$name NAME=$name $javascript>\n";
5e7856af 788 if ($nothing) {
789 $output .= " <OPTION VALUE=\"$nothingvalue\"\n";
790 if ($nothingvalue == $selected) {
791 $output .= " SELECTED";
792 }
793 $output .= ">$nothing</OPTION>\n";
794 }
795 if (!empty($options)) {
796 foreach ($options as $value => $label) {
797 $output .= " <OPTION VALUE=\"$value\"";
798 if ($value == $selected) {
799 $output .= " SELECTED";
800 }
ac8e16be 801 // stop zero label being replaced by array index value
5e7856af 802 // if ($label) {
803 // $output .= ">$label</OPTION>\n";
804 // } else {
805 // $output .= ">$value</OPTION>\n";
ac8e16be 806 // }
807 $output .= ">$label</OPTION>\n";
5e7856af 808
809 }
810 }
811 $output .= "</SELECT>\n";
812
813 if ($return) {
814 return $output;
815 } else {
816 echo $output;
817 }
818}
819
4b55d2af 820/**
821 * Determins if a jumpto value is correct or not.
822 *
823 * returns true if jumpto page is (logically) after the pageid page or
824 * if the jumpto value is a special value. Returns false in all other cases.
825 *
826 * @param int $pageid Id of the page from which you are jumping from.
827 * @param int $jumpto The jumpto number.
828 * @return boolean True or false after a series of tests.
829 * @todo Can be optimized to only have to make 1 or 2 database calls instead of 1 foreach page in the lesson
830 **/
5e7856af 831function lesson_iscorrect($pageid, $jumpto) {
5e7856af 832
833 // first test the special values
834 if (!$jumpto) {
835 // same page
836 return false;
837 } elseif ($jumpto == LESSON_NEXTPAGE) {
838 return true;
ac8e16be 839 } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE) {
5e7856af 840 return true;
841 } elseif ($jumpto == LESSON_RANDOMPAGE) {
842 return true;
843 } elseif ($jumpto == LESSON_CLUSTERJUMP) {
844 return true;
5e7856af 845 } elseif ($jumpto == LESSON_EOL) {
846 return true;
847 }
848 // we have to run through the pages from pageid looking for jumpid
849 $apageid = get_field("lesson_pages", "nextpageid", "id", $pageid);
850 while (true) {
851 if ($jumpto == $apageid) {
852 return true;
853 }
854 if ($apageid) {
855 $apageid = get_field("lesson_pages", "nextpageid", "id", $apageid);
856 } else {
857 return false;
858 }
859 }
860 return false; // should never be reached
861}
862
4b55d2af 863/**
864 * Checks to see if a page is a branch table or is
865 * a page that is enclosed by a branch table and an end of branch or end of lesson.
866 * May call this function: {@link lesson_is_page_in_branch()}
867 *
868 * @param int $lesson_id Id of the lesson to which the page belongs.
869 * @param int $pageid Id of the page.
870 * @return boolean True or false.
871 * @todo Change $lesson_id param to $lessonid.
872 **/
5e7856af 873function lesson_display_branch_jumps($lesson_id, $pageid) {
ac8e16be 874 if($pageid == 0) {
875 // first page
876 return false;
877 }
878 // get all of the lesson pages
879 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson_id")) {
880 // adding first page
881 return false;
882 }
883
884 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
885 return true;
886 }
887
888 return lesson_is_page_in_branch($lessonpages, $pageid);
5e7856af 889}
890
4b55d2af 891/**
892 * Checks to see if a page is a cluster page or is
893 * a page that is enclosed by a cluster page and an end of cluster or end of lesson
894 * May call this function: {@link lesson_is_page_in_cluster()}
895 *
896 * @param int $lesson_id Id of the lesson to which the page belongs.
897 * @param int $pageid Id of the page.
898 * @return boolean True or false.
899 * @todo Change $lesson_id param to $lessonid.
900 **/
5e7856af 901function lesson_display_cluster_jump($lesson_id, $pageid) {
ac8e16be 902 if($pageid == 0) {
903 // first page
904 return false;
905 }
906 // get all of the lesson pages
907 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson_id")) {
908 // adding first page
909 return false;
910 }
911
912 if ($lessonpages[$pageid]->qtype == LESSON_CLUSTER) {
913 return true;
914 }
915
916 return lesson_is_page_in_cluster($lessonpages, $pageid);
5e7856af 917
918}
919
4b55d2af 920/**
921 * Checks to see if a LESSON_CLUSTERJUMP or
922 * a LESSON_UNSEENBRANCHPAGE is used in a lesson.
923 *
924 * This function is only executed when a teacher is
925 * checking the navigation for a lesson.
926 *
06469639 927 * @param int $lesson Id of the lesson that is to be checked.
4b55d2af 928 * @return boolean True or false.
929 **/
06469639 930function lesson_display_teacher_warning($lesson) {
ac8e16be 931 // get all of the lesson answers
932 if (!$lessonanswers = get_records_select("lesson_answers", "lessonid = $lesson")) {
933 // no answers, then not useing cluster or unseen
934 return false;
935 }
936 // just check for the first one that fulfills the requirements
937 foreach ($lessonanswers as $lessonanswer) {
938 if ($lessonanswer->jumpto == LESSON_CLUSTERJUMP || $lessonanswer->jumpto == LESSON_UNSEENBRANCHPAGE) {
939 return true;
940 }
941 }
942
943 // if no answers use either of the two jumps
944 return false;
5e7856af 945}
946
947
4b55d2af 948/**
949 * Interprets LESSON_CLUSTERJUMP jumpto value.
950 *
951 * This will select a page randomly
952 * and the page selected will be inbetween a cluster page and end of cluter or end of lesson
953 * and the page selected will be a page that has not been viewed already
954 * and if any pages are within a branch table or end of branch then only 1 page within
955 * the branch table or end of branch will be randomly selected (sub clustering).
956 *
957 * @param int $lesson Id of the lesson.
958 * @param int $user Id of the user.
959 * @param int $pageid Id of the current page from which we are jumping from.
960 * @return int The id of the next page.
961 * @todo Change $lesson param to $lessonid and $user param to $userid
962 **/
5e7856af 963function lesson_cluster_jump($lesson, $user, $pageid) {
ac8e16be 964 // get the number of retakes
5e7856af 965 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 966 $retakes = 0;
967 }
968
969 // get all the lesson_attempts aka what the user has seen
970 if ($seen = get_records_select("lesson_attempts", "lessonid = $lesson AND userid = $user AND retry = $retakes", "timeseen DESC")) {
971 foreach ($seen as $value) { // load it into an array that I can more easily use
972 $seenpages[$value->pageid] = $value->pageid;
973 }
974 } else {
975 $seenpages = array();
976 }
977
978 // get the lesson pages
979 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
980 error("Error: could not find records in lesson_pages table");
981 }
982 // find the start of the cluster
983 while ($pageid != 0) { // this condition should not be satisfied... should be a cluster page
984 if ($lessonpages[$pageid]->qtype == LESSON_CLUSTER) {
985 break;
986 }
987 $pageid = $lessonpages[$pageid]->prevpageid;
988 }
989
990 $pageid = $lessonpages[$pageid]->nextpageid; // move down from the cluster page
991
992 $clusterpages = array();
993 while (true) { // now load all the pages into the cluster that are not already inside of a branch table.
994 if ($lessonpages[$pageid]->qtype == LESSON_ENDOFCLUSTER) {
995 // store the endofcluster page's jump
996 $exitjump = get_field("lesson_answers", "jumpto", "pageid", $pageid, "lessonid", $lesson);
997 if ($exitjump == LESSON_NEXTPAGE) {
998 $exitjump = $lessonpages[$pageid]->nextpageid;
999 }
1000 if ($exitjump == 0) {
1001 $exitjump = LESSON_EOL;
1002 }
1003 break;
1004 } elseif (!lesson_is_page_in_branch($lessonpages, $pageid) && $lessonpages[$pageid]->qtype != LESSON_ENDOFBRANCH) {
1005 // load page into array when it is not in a branch table and when it is not an endofbranch
1006 $clusterpages[] = $lessonpages[$pageid];
1007 }
1008 if ($lessonpages[$pageid]->nextpageid == 0) {
1009 // shouldn't ever get here... should be using endofcluster
1010 $exitjump = LESSON_EOL;
1011 break;
1012 } else {
1013 $pageid = $lessonpages[$pageid]->nextpageid;
1014 }
1015 }
1016
1017 // filter out the ones we have seen
1018 $unseen = array();
1019 foreach ($clusterpages as $clusterpage) {
1020 if ($clusterpage->qtype == LESSON_BRANCHTABLE) { // if branchtable, check to see if any pages inside have been viewed
1021 $branchpages = lesson_pages_in_branch($lessonpages, $clusterpage->id); // get the pages in the branchtable
1022 $flag = true;
1023 foreach ($branchpages as $branchpage) {
1024 if (array_key_exists($branchpage->id, $seenpages)) { // check if any of the pages have been viewed
1025 $flag = false;
1026 }
1027 }
1028 if ($flag && count($branchpages) > 0) {
1029 // add branch table
1030 $unseen[] = $clusterpage;
1031 }
1032 } else {
1033 // add any other type of page that has not already been viewed
1034 if (!array_key_exists($clusterpage->id, $seenpages)) {
1035 $unseen[] = $clusterpage;
1036 }
1037 }
1038 }
1039
1040 if (count($unseen) > 0) { // it does not contain elements, then use exitjump, otherwise find out next page/branch
1041 $nextpage = $unseen[rand(0, count($unseen)-1)];
1042 } else {
1043 return $exitjump; // seen all there is to see, leave the cluster
1044 }
1045
1046 if ($nextpage->qtype == LESSON_BRANCHTABLE) { // if branch table, then pick a random page inside of it
1047 $branchpages = lesson_pages_in_branch($lessonpages, $nextpage->id);
1048 return $branchpages[rand(0, count($branchpages)-1)]->id;
1049 } else { // otherwise, return the page's id
1050 return $nextpage->id;
1051 }
5e7856af 1052}
1053
4b55d2af 1054/**
1055 * Returns pages that are within a branch table and another branch table, end of branch or end of lesson
1056 *
1057 * @param array $lessonpages An array of lesson page objects.
1058 * @param int $branchid The id of the branch table that we would like the containing pages for.
1059 * @return array An array of lesson page objects.
1060 **/
5e7856af 1061function lesson_pages_in_branch($lessonpages, $branchid) {
ac8e16be 1062 $pageid = $lessonpages[$branchid]->nextpageid; // move to the first page after the branch table
1063 $pagesinbranch = array();
1064
1065 while (true) {
1066 if ($pageid == 0) { // EOL
1067 break;
1068 } elseif ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
1069 break;
1070 } elseif ($lessonpages[$pageid]->qtype == LESSON_ENDOFBRANCH) {
1071 break;
1072 }
1073 $pagesinbranch[] = $lessonpages[$pageid];
1074 $pageid = $lessonpages[$pageid]->nextpageid;
1075 }
1076
1077 return $pagesinbranch;
5e7856af 1078}
1079
4b55d2af 1080/**
1081 * Interprets the LESSON_UNSEENBRANCHPAGE jump.
1082 *
1083 * will return the pageid of a random unseen page that is within a branch
1084 *
1085 * @see lesson_pages_in_branch()
1086 * @param int $lesson Id of the lesson.
1087 * @param int $user Id of the user.
1088 * @param int $pageid Id of the page from which we are jumping.
1089 * @return int Id of the next page.
1090 * @todo Change params $lesson to $lessonid and $user to $userid.
1091 **/
5e7856af 1092function lesson_unseen_question_jump($lesson, $user, $pageid) {
ac8e16be 1093 // get the number of retakes
5e7856af 1094 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 1095 $retakes = 0;
1096 }
1097
1098 // get all the lesson_attempts aka what the user has seen
1099 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson AND userid = $user AND retry = $retakes", "timeseen DESC")) {
1100 foreach($viewedpages as $viewed) {
1101 $seenpages[] = $viewed->pageid;
1102 }
1103 } else {
1104 $seenpages = array();
1105 }
1106
1107 // get the lesson pages
1108 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
1109 error("Error: could not find records in lesson_pages table");
1110 }
1111
1112 if ($pageid == LESSON_UNSEENBRANCHPAGE) { // this only happens when a student leaves in the middle of an unseen question within a branch series
1113 $pageid = $seenpages[0]; // just change the pageid to the last page viewed inside the branch table
1114 }
1115
1116 // go up the pages till branch table
1117 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
1118 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
1119 break;
1120 }
1121 $pageid = $lessonpages[$pageid]->prevpageid;
1122 }
1123
1124 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
1125
1126 // this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array
1127 $unseen = array();
1128 foreach($pagesinbranch as $page) {
1129 if (!in_array($page->id, $seenpages)) {
1130 $unseen[] = $page->id;
1131 }
1132 }
1133
1134 if(count($unseen) == 0) {
1135 if(isset($pagesinbranch)) {
1136 $temp = end($pagesinbranch);
1137 $nextpage = $temp->nextpageid; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL
1138 } else {
1139 // there are no pages inside the branch, so return the next page
1140 $nextpage = $lessonpages[$pageid]->nextpageid;
1141 }
1142 if ($nextpage == 0) {
1143 return LESSON_EOL;
1144 } else {
1145 return $nextpage;
1146 }
1147 } else {
1148 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
1149 }
5e7856af 1150}
1151
4b55d2af 1152/**
1153 * Handles the unseen branch table jump.
1154 *
1155 * @param int $lesson Lesson id.
1156 * @param int $user User id.
1157 * @return int Will return the page id of a branch table or end of lesson
1158 * @todo Change $lesson param to $lessonid and change $user param to $userid.
1159 **/
5e7856af 1160function lesson_unseen_branch_jump($lesson, $user) {
5e7856af 1161 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 1162 $retakes = 0;
1163 }
1164
1165 if (!$seenbranches = get_records_select("lesson_branch", "lessonid = $lesson AND userid = $user AND retry = $retakes",
1166 "timeseen DESC")) {
1167 error("Error: could not find records in lesson_branch table");
1168 }
1169
1170 // get the lesson pages
1171 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
1172 error("Error: could not find records in lesson_pages table");
1173 }
1174
1175 // this loads all the viewed branch tables into $seen untill it finds the branch table with the flag
1176 // which is the branch table that starts the unseenbranch function
1177 $seen = array();
1178 foreach ($seenbranches as $seenbranch) {
1179 if (!$seenbranch->flag) {
1180 $seen[$seenbranch->pageid] = $seenbranch->pageid;
1181 } else {
1182 $start = $seenbranch->pageid;
1183 break;
1184 }
1185 }
1186 // this function searches through the lesson pages to find all the branch tables
1187 // that follow the flagged branch table
1188 $pageid = $lessonpages[$start]->nextpageid; // move down from the flagged branch table
1189 while ($pageid != 0) { // grab all of the branch table till eol
1190 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
1191 $branchtables[] = $lessonpages[$pageid]->id;
1192 }
1193 $pageid = $lessonpages[$pageid]->nextpageid;
1194 }
1195 $unseen = array();
1196 foreach ($branchtables as $branchtable) {
1197 // load all of the unseen branch tables into unseen
1198 if (!array_key_exists($branchtable, $seen)) {
1199 $unseen[] = $branchtable;
1200 }
1201 }
1202 if (count($unseen) > 0) {
1203 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
1204 } else {
1205 return LESSON_EOL; // has viewed all of the branch tables
1206 }
5e7856af 1207}
1208
4b55d2af 1209/**
1210 * Handles the random jump between a branch table and end of branch or end of lesson (LESSON_RANDOMPAGE).
1211 *
1212 * @param int $lesson Lesson id.
1213 * @param int $pageid The id of the page that we are jumping from (?)
1214 * @return int The pageid of a random page that is within a branch table
1215 * @todo Change $lesson param to $lessonid.
1216 **/
5e7856af 1217function lesson_random_question_jump($lesson, $pageid) {
ac8e16be 1218 // get the lesson pages
1219 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
1220 error("Error: could not find records in lesson_pages table");
1221 }
1222
1223 // go up the pages till branch table
1224 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
1225
1226 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
1227 break;
1228 }
1229 $pageid = $lessonpages[$pageid]->prevpageid;
1230 }
1231
1232 // get the pages within the branch
1233 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
1234
1235 if(count($pagesinbranch) == 0) {
1236 // there are no pages inside the branch, so return the next page
1237 return $lessonpages[$pageid]->nextpageid;
1238 } else {
1239 return $pagesinbranch[rand(0, count($pagesinbranch)-1)]->id; // returns a random page id for the next page
1240 }
5e7856af 1241}
1242
4b55d2af 1243/**
1244 * Check to see if a page is below a branch table (logically).
1245 *
1246 * Will return true if a branch table is found logically above the page.
1247 * Will return false if an end of branch, cluster or the beginning
1248 * of the lesson is found before a branch table.
1249 *
1250 * @param array $pages An array of lesson page objects.
1251 * @param int $pageid Id of the page for testing.
1252 * @return boolean
1253 */
5e7856af 1254function lesson_is_page_in_branch($pages, $pageid) {
ac8e16be 1255 $pageid = $pages[$pageid]->prevpageid; // move up one
1256
1257 // go up the pages till branch table
1258 while (true) {
1259 if ($pageid == 0) { // ran into the beginning of the lesson
1260 return false;
1261 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFBRANCH) { // ran into the end of another branch table
1262 return false;
1263 } elseif ($pages[$pageid]->qtype == LESSON_CLUSTER) { // do not look beyond a cluster
1264 return false;
1265 } elseif ($pages[$pageid]->qtype == LESSON_BRANCHTABLE) { // hit a branch table
1266 return true;
1267 }
1268 $pageid = $pages[$pageid]->prevpageid;
1269 }
5e7856af 1270
1271}
1272
4b55d2af 1273/**
1274 * Check to see if a page is below a cluster page (logically).
1275 *
1276 * Will return true if a cluster is found logically above the page.
1277 * Will return false if an end of cluster or the beginning
1278 * of the lesson is found before a cluster page.
1279 *
1280 * @param array $pages An array of lesson page objects.
1281 * @param int $pageid Id of the page for testing.
1282 * @return boolean
1283 */
5e7856af 1284function lesson_is_page_in_cluster($pages, $pageid) {
ac8e16be 1285 $pageid = $pages[$pageid]->prevpageid; // move up one
1286
1287 // go up the pages till branch table
1288 while (true) {
1289 if ($pageid == 0) { // ran into the beginning of the lesson
1290 return false;
1291 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFCLUSTER) { // ran into the end of another branch table
1292 return false;
1293 } elseif ($pages[$pageid]->qtype == LESSON_CLUSTER) { // hit a branch table
1294 return true;
1295 }
1296 $pageid = $pages[$pageid]->prevpageid;
1297 }
5e7856af 1298}
1299
4b55d2af 1300/**
1301 * Prints the contents for the left menu
1302 *
1303 * Runs through all of the lesson pages and calls {@link lesson_print_tree_link_menu()}
1304 * to print out the link.
1305 *
1306 * @see lesson_print_tree_link_menu()
1307 * @param int $lessonid Lesson id.
1308 * @param int $pageid Page id of the first page of the lesson.
1309 * @param int $id The cmid of the lesson.
1310 * @param boolean $showpages An optional paramater to show question pages as well as branch tables in the left menu (NYI)
1311 * @todo change $id param to $cmid. Finnish implementing the $showpages feature.
1312 * Not necessary to pass $pageid, we can find that out in the function.
1313 * Also, no real need for {@link lesson_print_tree_link_menu()}. Everything can be handled in this function.
1314 */
b9869082 1315function lesson_print_tree_menu($lessonid, $pageid, $id, $showpages=false) {
4b55d2af 1316 if (!$pages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
ac8e16be 1317 error("Error: could not find lesson pages");
1318 }
d6941aff 1319 echo '<ul>';
ac8e16be 1320 while ($pageid != 0) {
1321 lesson_print_tree_link_menu($pages[$pageid], $id, true);
1322 $pageid = $pages[$pageid]->nextpageid;
1323 }
d6941aff 1324 echo '</ul>';
5e7856af 1325}
1326
4b55d2af 1327/**
1328 * Prints the actual link for the left menu
1329 *
1330 * Only prints branch tables that have display set to on.
1331 *
1332 * @param object $page Lesson page object.
1333 * @param int $id The cmid of the lesson.
1334 * @param boolean $showpages An optional paramater to show question pages as well as branch tables in the left menu (NYI)
1335 * @todo change $id param to $cmid. Finnish implementing the $showpages feature.
1336 */
b9869082 1337function lesson_print_tree_link_menu($page, $id, $showpages=false) {
ac8e16be 1338 if ($page->qtype == LESSON_BRANCHTABLE && !$page->display) {
1339 return false;
1340 } elseif ($page->qtype != LESSON_BRANCHTABLE) {
1341 return false;
1342 }
1343
1344 /*elseif ($page->qtype != LESSON_BRANCHTABLE && !$showpages) {
1345 return false;
1346 } elseif (!in_array($page->qtype, $LESSON_QUESTION_TYPE)) {
1347 return false;
1348 }*/
1349
1350 // set up some variables NoticeFix changed whole function
1351 $output = "";
d6941aff 1352 $class = ' class="leftmenu_not_selected_link" ';
ac8e16be 1353
97d2756c 1354 if($page->id == optional_param('pageid', 0, PARAM_INT)) {
1355 $class = ' class="leftmenu_selected_link" ';
1356 }
ac8e16be 1357
d6941aff 1358 $output .= '<li>';
ac8e16be 1359
c94d3799 1360 $output .= "<a $class href=\"view.php?id=$id&amp;pageid=$page->id\">".format_string($page->title,true)."</a>\n";
d6941aff 1361
1362 $output .= "</li>";
5e7856af 1363
d6941aff 1364 echo $output;
5e7856af 1365}
1366
4b55d2af 1367/**
1368 * Prints out the tree view list.
1369 *
1370 * Each page in the lesson is printed out as a link. If the page is a branch table
1371 * or an end of branch then the link color changes and the answer jumps are also printed
1372 * alongside the links. Also, the editing buttons (move, update, delete) are printed
1373 * next to the links.
1374 *
1375 * @uses $USER
a1d0b3eb 1376 * @uses $CFG
4b55d2af 1377 * @param int $pageid Page id of the first page of the lesson.
a1d0b3eb 1378 * @param object $lesson Object of the current lesson.
4b55d2af 1379 * @param int $cmid The course module id of the lesson.
1380 * @param string $pixpath Path to the pictures.
a1d0b3eb 1381 * @todo $pageid does not need to be passed. Can be found in the function.
97d2756c 1382 * This function is only called once. It should be removed and the code inside it moved to view.php
4b55d2af 1383 */
a1d0b3eb 1384function lesson_print_tree($pageid, $lesson, $cmid) {
1385 global $USER, $CFG;
6e1ff5c8 1386 $context = get_context_instance(CONTEXT_MODULE, $cmid);
ac8e16be 1387
a1d0b3eb 1388 if(!$pages = get_records_select("lesson_pages", "lessonid = $lesson->id")) {
ac8e16be 1389 error("Error: could not find lesson pages");
1390 }
1391 echo "<table>";
1392 while ($pageid != 0) {
1393 echo "<tr><td>";
1394 if(($pages[$pageid]->qtype != LESSON_BRANCHTABLE) && ($pages[$pageid]->qtype != LESSON_ENDOFBRANCH)) {
15f3d44a 1395 $output = "<a style='color:#DF041E;' href=\"$CFG->wwwroot/mod/lesson/edit.php?id=$cmid&display=".$pages[$pageid]->id."\">".format_string($pages[$pageid]->title,true)."</a>\n";
ac8e16be 1396 } else {
15f3d44a 1397 $output = "<a href=\"$CFG->wwwroot/mod/lesson/edit.php?id=$cmid&display=".$pages[$pageid]->id."\">".format_string($pages[$pageid]->title,true)."</a>\n";
ac8e16be 1398
a1d0b3eb 1399 if($answers = get_records_select("lesson_answers", "lessonid = $lesson->id and pageid = $pageid")) {
ac8e16be 1400 $output .= "Jumps to: ";
1401 $end = end($answers);
1402 foreach ($answers as $answer) {
1403 if ($answer->jumpto == 0) {
1404 $output .= get_string("thispage", "lesson");
1405 } elseif ($answer->jumpto == LESSON_NEXTPAGE) {
1406 $output .= get_string("nextpage", "lesson");
1407 } elseif ($answer->jumpto == LESSON_EOL) {
1408 $output .= get_string("endoflesson", "lesson");
1409 } elseif ($answer->jumpto == LESSON_UNSEENBRANCHPAGE) {
1410 $output .= get_string("unseenpageinbranch", "lesson");
1411 } elseif ($answer->jumpto == LESSON_PREVIOUSPAGE) {
1412 $output .= get_string("previouspage", "lesson");
1413 } elseif ($answer->jumpto == LESSON_RANDOMPAGE) {
1414 $output .= get_string("randompageinbranch", "lesson");
1415 } elseif ($answer->jumpto == LESSON_RANDOMBRANCH) {
1416 $output .= get_string("randombranch", "lesson");
1417 } elseif ($answer->jumpto == LESSON_CLUSTERJUMP) {
1418 $output .= get_string("clusterjump", "lesson");
1419 } else {
68dfc4de 1420 $output .= format_string($pages[$answer->jumpto]->title);
ac8e16be 1421 }
1422 if ($answer->id != $end->id) {
1423 $output .= ", ";
1424 }
1425 }
1426 }
1427 }
1428
1429 echo $output;
6e1ff5c8 1430 if (has_capability('mod/lesson:edit', $context)) {
a1d0b3eb 1431 if (count($pages) > 1) {
1432 echo "<a title=\"move\" href=\"lesson.php?id=$cmid&action=move&pageid=".$pages[$pageid]->id."\">\n".
1433 "<img src=\"$CFG->pixpath/t/move.gif\" hspace=\"2\" height=11 width=11 alt=\"move\" border=0></a>\n";
1434 }
1435 echo "<a title=\"update\" href=\"lesson.php?id=$cmid&amp;action=editpage&amp;pageid=".$pages[$pageid]->id."\">\n".
1436 "<img src=\"$CFG->pixpath/t/edit.gif\" hspace=\"2\" height=11 width=11 alt=\"edit\" border=0></a>\n".
1437 "<a title=\"delete\" href=\"lesson.php?id=$cmid&amp;sesskey=".$USER->sesskey."&amp;action=confirmdelete&amp;pageid=".$pages[$pageid]->id."\">\n".
1438 "<img src=\"$CFG->pixpath/t/delete.gif\" hspace=\"2\" height=11 width=11 alt=\"delete\" border=0></a>\n";
ac8e16be 1439 }
ac8e16be 1440 echo "</tr></td>";
1441 $pageid = $pages[$pageid]->nextpageid;
1442 }
1443 echo "</table>";
5e7856af 1444}
1445
4b55d2af 1446/**
1447 * Calculates a user's grade for a lesson.
1448 *
4b55d2af 1449 * @param object $lesson The lesson that the user is taking.
4b55d2af 1450 * @param int $retries The attempt number.
88427c07 1451 * @param int $userid Id of the user (optinal, default current user).
1452 * @return object { nquestions => number of questions answered
1453 attempts => number of question attempts
1454 total => max points possible
1455 earned => points earned by student
1456 grade => calculated percentage grade
1457 nmanual => number of manually graded questions
1458 manualpoints => point value for manually graded questions }
4b55d2af 1459 */
88427c07 1460function lesson_grade($lesson, $ntries, $userid = 0) {
1461 global $USER;
ac8e16be 1462
88427c07 1463 if (empty($userid)) {
1464 $userid = $USER->id;
1465 }
1466
1467 // Zero out everything
1468 $ncorrect = 0;
1469 $nviewed = 0;
1470 $score = 0;
1471 $nmanual = 0;
1472 $manualpoints = 0;
1473 $thegrade = 0;
1474 $nquestions = 0;
1475 $total = 0;
1476 $earned = 0;
1477
1478 if ($useranswers = get_records_select("lesson_attempts", "lessonid = $lesson->id AND
1479 userid = $userid AND retry = $ntries", "timeseen")) {
1480 // group each try with its page
1481 $attemptset = array();
1482 foreach ($useranswers as $useranswer) {
1483 $attemptset[$useranswer->pageid][] = $useranswer;
ac8e16be 1484 }
88427c07 1485
1486 // Drop all attempts that go beyond max attempts for the lesson
1487 foreach ($attemptset as $key => $set) {
1488 $attemptset[$key] = array_slice($set, 0, $lesson->maxattempts);
1489 }
1490
1491 $pageids = implode(",", array_keys($attemptset));
1492
1493 // get only the pages and their answers that the user answered
1494 $pages = get_records_select("lesson_pages", "lessonid = $lesson->id AND id IN($pageids)");
1495 $answers = get_records_select("lesson_answers", "lessonid = $lesson->id AND pageid IN($pageids)");
1496
1497 // Number of pages answered
1498 $nquestions = count($pages);
1499
1500 foreach ($attemptset as $attempts) {
1501 if ($lesson->custom) {
1502 $attempt = end($attempts);
1503 // If essay question, handle it, otherwise add to score
1504 if ($pages[$attempt->pageid]->qtype == LESSON_ESSAY) {
ac8e16be 1505 $essayinfo = unserialize($attempt->useranswer);
88427c07 1506 $earned += $essayinfo->score;
1507 $nmanual++;
1508 $manualpoints += $answers[$attempt->answerid]->score;
ac8e16be 1509 } else {
88427c07 1510 $earned += $answers[$attempt->answerid]->score;
1511 }
1512 } else {
1513 foreach ($attempts as $attempt) {
1514 $earned += $attempt->correct;
1515 }
1516 $attempt = end($attempts); // doesn't matter which one
1517 // If essay question, increase numbers
1518 if ($pages[$attempt->pageid]->qtype == LESSON_ESSAY) {
1519 $nmanual++;
1520 $manualpoints++;
ac8e16be 1521 }
1522 }
88427c07 1523 // Number of times answered
1524 $nviewed += count($attempts);
1525 }
1526
1527 if ($lesson->custom) {
ac8e16be 1528 $bestscores = array();
88427c07 1529 // Find the highest possible score per page to get our total
1530 foreach ($answers as $answer) {
46341ab7 1531 if(!isset($bestscores[$answer->pageid])) {
88427c07 1532 $bestscores[$answer->pageid] = $answer->score;
46341ab7 1533 } else if ($bestscores[$answer->pageid] < $answer->score) {
88427c07 1534 $bestscores[$answer->pageid] = $answer->score;
ac8e16be 1535 }
1536 }
88427c07 1537 $total = array_sum($bestscores);
1538 } else {
1539 // Check to make sure the student has answered the minimum questions
1540 if ($lesson->minquestions and $nquestions < $lesson->minquestions) {
1541 // Nope, increase number viewed by the amount of unanswered questions
1542 $total = $nviewed + ($lesson->minquestions - $nquestions);
1543 } else {
1544 $total = $nviewed;
1545 }
ac8e16be 1546 }
88427c07 1547 }
1548
1549 if ($total) { // not zero
1550 $thegrade = round(100 * $earned / $total, 5);
1551 }
1552
1553 // Build the grade information object
1554 $gradeinfo = new stdClass;
1555 $gradeinfo->nquestions = $nquestions;
1556 $gradeinfo->attempts = $nviewed;
1557 $gradeinfo->total = $total;
1558 $gradeinfo->earned = $earned;
1559 $gradeinfo->grade = $thegrade;
1560 $gradeinfo->nmanual = $nmanual;
1561 $gradeinfo->manualpoints = $manualpoints;
1562
1563 return $gradeinfo;
1564}
1565
1566/**
1567 * Prints the on going message to the user.
1568 *
1569 * With custom grading On, displays points
1570 * earned out of total points possible thus far.
1571 * With custom grading Off, displays number of correct
1572 * answers out of total attempted.
1573 *
1574 * @param object $lesson The lesson that the user is taking.
1575 * @return void
1576 **/
1577function lesson_print_ongoing_score($lesson) {
1578 global $USER;
6e1ff5c8 1579 $cm = get_coursemodule_from_instance('lesson', $lesson->id);
1580 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1581
1582 if (has_capability('mod/lesson:manage', $context)) {
88427c07 1583 echo "<p align=\"center\">".get_string('teacherongoingwarning', 'lesson').'</p>';
1584 } else {
1585 $ntries = count_records("lesson_grades", "lessonid", $lesson->id, "userid", $USER->id);
1586 if (isset($USER->modattempts[$lesson->id])) {
1587 $ntries--;
1588 }
1589 $gradeinfo = lesson_grade($lesson, $ntries);
ac8e16be 1590
88427c07 1591 $a = new stdClass;
1592 if ($lesson->custom) {
1593 $a->score = $gradeinfo->earned;
1594 $a->currenthigh = $gradeinfo->total;
1595 print_simple_box(get_string("ongoingcustom", "lesson", $a), "center");
ac8e16be 1596 } else {
88427c07 1597 $a->correct = $gradeinfo->earned;
1598 $a->viewed = $gradeinfo->attempts;
1599 print_simple_box(get_string("ongoingnormal", "lesson", $a), "center");
ac8e16be 1600 }
1601 }
5e7856af 1602}
1603
4b55d2af 1604/**
1605 * Prints tabs for the editing and adding pages. Each tab is a question type.
1606 *
1607 * @param array $qtypes The question types array (may not need to pass this because it is defined in this file)
1608 * @param string $selected Current selected tab
1609 * @param string $link The base href value of the link for the tab
1610 * @param string $onclick Javascript for the tab link
1611 * @return void
1612 */
5e7856af 1613function lesson_qtype_menu($qtypes, $selected="", $link="", $onclick="") {
271fea97 1614 $tabs = array();
1615 $tabrows = array();
1616
9638a1f4 1617 foreach ($qtypes as $qtype => $qtypename) {
271fea97 1618 $tabrows[] = new tabobject($qtype, "$link&amp;qtype=$qtype\" onClick=\"$onclick\"", $qtypename);
ac8e16be 1619 }
271fea97 1620 $tabs[] = $tabrows;
1621 print_tabs($tabs, $selected);
1622 echo "<input type=\"hidden\" name=\"qtype\" value=\"$selected\" /> \n";
9638a1f4 1623
5e7856af 1624}
1625
4b55d2af 1626/**
1627 * Checks to see if the nickname is naughty or not.
1628 *
1629 * @todo Move this to highscores.php
1630 */
5e7856af 1631function lesson_check_nickname($name) {
5e7856af 1632
832b9f82 1633 if (empty($name)) {
ac8e16be 1634 return false;
1635 }
1636
832b9f82 1637 $filterwords = explode(',', get_string('censorbadwords'));
ac8e16be 1638
1639 foreach ($filterwords as $filterword) {
1640 if (strstr($name, $filterword)) {
1641 return false;
1642 }
1643 }
1644 return true;
5e7856af 1645}
57bfe93d 1646
62eda6ea 1647/**
1648 * Prints out a Progress Bar which depicts a user's progress within a lesson.
1649 *
1650 * Currently works best with a linear lesson. Clusters are counted as a single page.
1651 * Also, only viewed branch tables and questions that have been answered correctly count
1652 * toward lesson completion (or progress). Only Students can see the Progress bar as well.
1653 *
1654 * @param object $lesson The lesson that the user is currently taking.
1655 * @param object $course The course that the to which the lesson belongs.
1656 * @return boolean The return is not significant as of yet. Will return true/false.
1657 * @author Mark Nielsen
1658 **/
1659function lesson_print_progress_bar($lesson, $course) {
1660 global $CFG, $USER;
6e1ff5c8 1661 $cm = get_coursemodule_from_instance('lesson', $lesson->id);
1662 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1663
62eda6ea 1664 // lesson setting to turn progress bar on or off
1665 if (!$lesson->progressbar) {
1666 return false;
1667 }
1668
1669 // catch teachers
6e1ff5c8 1670 if (has_capability('mod/lesson:manage', $context)) {
62eda6ea 1671 notify(get_string('progressbarteacherwarning', 'lesson', $course->teachers));
1672 return false;
1673 }
acab9099 1674 if (!isset($USER->modattempts[$lesson->id])) {
1675 // all of the lesson pages
1676 if (!$pages = get_records('lesson_pages', 'lessonid', $lesson->id)) {
1677 return false;
1678 } else {
1679 foreach ($pages as $page) {
1680 if ($page->prevpageid == 0) {
1681 $pageid = $page->id; // find the first page id
1682 break;
1683 }
62eda6ea 1684 }
1685 }
62eda6ea 1686
acab9099 1687 // current attempt number
1688 if (!$ntries = count_records("lesson_grades", "lessonid", $lesson->id, "userid", $USER->id)) {
1689 $ntries = 0; // may not be necessary
1690 }
62eda6ea 1691
acab9099 1692 $viewedpageids = array();
62eda6ea 1693
acab9099 1694 // collect all of the correctly answered questions
1695 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries AND correct = 1", 'timeseen DESC', 'pageid, id')) {
1696 $viewedpageids = array_keys($viewedpages);
1697 }
1698 // collect all of the branch tables viewed
1699 if ($viewedbranches = get_records_select("lesson_branch", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries", 'timeseen DESC', 'pageid, id')) {
1700 $viewedpageids = array_merge($viewedpageids, array_keys($viewedbranches));
1701 }
1702
1703 // Filter out the following pages:
1704 // End of Cluster
1705 // End of Branch
1706 // Pages found inside of Clusters
1707 // Do not filter out Cluster Page(s) because we count a cluster as one.
1708 // By keeping the cluster page, we get our 1
1709 $validpages = array();
1710 while ($pageid != 0) {
1711 if ($pages[$pageid]->qtype == LESSON_CLUSTER) {
1712 $clusterpageid = $pageid; // copy it
1713 $validpages[$clusterpageid] = 1; // add the cluster page as a valid page
1714 $pageid = $pages[$pageid]->nextpageid; // get next page
62eda6ea 1715
acab9099 1716 // now, remove all necessary viewed paged ids from the viewedpageids array.
1717 while ($pages[$pageid]->qtype != LESSON_ENDOFCLUSTER and $pageid != 0) {
1718 if (in_array($pageid, $viewedpageids)) {
1719 unset($viewedpageids[array_search($pageid, $viewedpageids)]); // remove it
1720 // since the user did see one page in the cluster, add the cluster pageid to the viewedpageids
1721 if (!in_array($clusterpageid, $viewedpageids)) {
1722 $viewedpageids[] = $clusterpageid;
1723 }
62eda6ea 1724 }
acab9099 1725 $pageid = $pages[$pageid]->nextpageid;
62eda6ea 1726 }
acab9099 1727 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFCLUSTER or $pages[$pageid]->qtype == LESSON_ENDOFBRANCH) {
1728 // dont count these, just go to next
1729 $pageid = $pages[$pageid]->nextpageid;
1730 } else {
1731 // a counted page
1732 $validpages[$pageid] = 1;
62eda6ea 1733 $pageid = $pages[$pageid]->nextpageid;
1734 }
acab9099 1735 }
62eda6ea 1736
acab9099 1737 // progress calculation as a percent
1738 $progress = round(count($viewedpageids)/count($validpages), 2) * 100;
1739 } else {
1740 $progress = 100;
1741 }
62eda6ea 1742
1743 // print out the Progress Bar. Attempted to put as much as possible in the style sheets.
1744 echo '<div class="progress_bar" align="center">';
1745 echo '<table class="progress_bar_table"><tr>';
1746 if ($progress != 0) { // some browsers do not repsect the 0 width.
1747 echo '<td width="'.$progress.'%" class="progress_bar_completed">';
1748 echo '</td>';
1749 }
1750 echo '<td class="progress_bar_todo">';
1751 echo '<div class="progress_bar_token"></div>';
1752 echo '</td>';
1753 echo '</tr></table>';
1754 echo '</div>';
1755
1756 return true;
1757}
1758
1759/**
1760 * Determines if a user can view the left menu. The determining factor
1761 * is whether a user has a grade greater than or equal to the lesson setting
1762 * of displayleftif
1763 *
1764 * @param object $lesson Lesson object of the current lesson
1765 * @return boolean 0 if the user cannot see, or $lesson->displayleft to keep displayleft unchanged
1766 * @author Mark Nielsen
1767 **/
1768function lesson_displayleftif($lesson) {
1769 global $CFG, $USER;
1770
1771 if (!empty($lesson->displayleftif)) {
1772 // get the current user's max grade for this lesson
1773 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')) {
1774 if ($maxgrade->maxgrade < $lesson->displayleftif) {
1775 return 0; // turn off the displayleft
1776 }
1777 } else {
1778 return 0; // no grades
1779 }
1780 }
1781
1782 // if we get to here, keep the original state of displayleft lesson setting
1783 return $lesson->displayleft;
1784}
5e7856af 1785
2a488ba5 1786?>