removed pause during redirect when there are no new settings
[moodle.git] / mod / lesson / locallib.php
CommitLineData
5e7856af 1<?php
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
4b55d2af 210/**
211 * Given some question info and some data about the the answers
212 * this function parses, organises and saves the question
213 *
214 * This is only used when IMPORTING questions and is only called
215 * from format.php
216 * Lifted from mod/quiz/lib.php -
217 * 1. all reference to oldanswers removed
218 * 2. all reference to quiz_multichoice table removed
219 * 3. In SHORTANSWER questions usecase is store in the qoption field
220 * 4. In NUMERIC questions store the range as two answers
221 * 5. TRUEFALSE options are ignored
222 * 6. For MULTICHOICE questions with more than one answer the qoption field is true
223 *
224 * @param opject $question Contains question data like question, type and answers.
225 * @return object Returns $result->error or $result->notice.
226 **/
5e7856af 227function lesson_save_question_options($question) {
5e7856af 228
229 $timenow = time();
230 switch ($question->qtype) {
231 case LESSON_SHORTANSWER:
232
233 $answers = array();
234 $maxfraction = -1;
235
236 // Insert all the new answers
237 foreach ($question->answer as $key => $dataanswer) {
238 if ($dataanswer != "") {
f7ffb898 239 $answer = new stdClass;
5e7856af 240 $answer->lessonid = $question->lessonid;
241 $answer->pageid = $question->id;
242 if ($question->fraction[$key] >=0.5) {
243 $answer->jumpto = LESSON_NEXTPAGE;
244 }
245 $answer->timecreated = $timenow;
246 $answer->grade = $question->fraction[$key] * 100;
247 $answer->answer = $dataanswer;
c87a341c 248 $answer->response = $question->feedback[$key];
5e7856af 249 if (!$answer->id = insert_record("lesson_answers", $answer)) {
250 $result->error = "Could not insert shortanswer quiz answer!";
251 return $result;
252 }
253 $answers[] = $answer->id;
254 if ($question->fraction[$key] > $maxfraction) {
255 $maxfraction = $question->fraction[$key];
256 }
257 }
258 }
259
260
261 /// Perform sanity checks on fractional grades
262 if ($maxfraction != 1) {
263 $maxfraction = $maxfraction * 100;
264 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
265 return $result;
266 }
267 break;
268
269 case LESSON_NUMERICAL: // Note similarities to SHORTANSWER
270
271 $answers = array();
272 $maxfraction = -1;
273
274
275 // for each answer store the pair of min and max values even if they are the same
276 foreach ($question->answer as $key => $dataanswer) {
277 if ($dataanswer != "") {
f7ffb898 278 $answer = new stdClass;
5e7856af 279 $answer->lessonid = $question->lessonid;
280 $answer->pageid = $question->id;
281 $answer->jumpto = LESSON_NEXTPAGE;
282 $answer->timecreated = $timenow;
283 $answer->grade = $question->fraction[$key] * 100;
dd192b26 284 $min = $question->answer[$key] - $question->tolerance[$key];
285 $max = $question->answer[$key] + $question->tolerance[$key];
286 $answer->answer = $min.":".$max;
287 // $answer->answer = $question->min[$key].":".$question->max[$key]; original line for min/max
5e7856af 288 $answer->response = $question->feedback[$key];
289 if (!$answer->id = insert_record("lesson_answers", $answer)) {
290 $result->error = "Could not insert numerical quiz answer!";
291 return $result;
292 }
293
294 $answers[] = $answer->id;
295 if ($question->fraction[$key] > $maxfraction) {
296 $maxfraction = $question->fraction[$key];
297 }
298 }
299 }
300
301 /// Perform sanity checks on fractional grades
302 if ($maxfraction != 1) {
303 $maxfraction = $maxfraction * 100;
304 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
305 return $result;
306 }
307 break;
308
309
310 case LESSON_TRUEFALSE:
311
312 // the truth
313 $answer->lessonid = $question->lessonid;
314 $answer->pageid = $question->id;
315 $answer->timecreated = $timenow;
316 $answer->answer = get_string("true", "quiz");
317 $answer->grade = $question->answer * 100;
318 if ($answer->grade > 50 ) {
319 $answer->jumpto = LESSON_NEXTPAGE;
320 }
321 if (isset($question->feedbacktrue)) {
322 $answer->response = $question->feedbacktrue;
323 }
324 if (!$true->id = insert_record("lesson_answers", $answer)) {
325 $result->error = "Could not insert quiz answer \"true\")!";
326 return $result;
327 }
328
329 // the lie
f7ffb898 330 $answer = new stdClass;
5e7856af 331 $answer->lessonid = $question->lessonid;
332 $answer->pageid = $question->id;
333 $answer->timecreated = $timenow;
334 $answer->answer = get_string("false", "quiz");
335 $answer->grade = (1 - (int)$question->answer) * 100;
336 if ($answer->grade > 50 ) {
337 $answer->jumpto = LESSON_NEXTPAGE;
338 }
339 if (isset($question->feedbackfalse)) {
340 $answer->response = $question->feedbackfalse;
341 }
342 if (!$false->id = insert_record("lesson_answers", $answer)) {
343 $result->error = "Could not insert quiz answer \"false\")!";
344 return $result;
345 }
346
347 break;
348
349
350 case LESSON_MULTICHOICE:
351
352 $totalfraction = 0;
353 $maxfraction = -1;
354
355 $answers = array();
356
357 // Insert all the new answers
358 foreach ($question->answer as $key => $dataanswer) {
359 if ($dataanswer != "") {
f7ffb898 360 $answer = new stdClass;
5e7856af 361 $answer->lessonid = $question->lessonid;
362 $answer->pageid = $question->id;
363 $answer->timecreated = $timenow;
364 $answer->grade = $question->fraction[$key] * 100;
62eda6ea 365 // changed some defaults
ac8e16be 366 /* Original Code
367 if ($answer->grade > 50 ) {
5e7856af 368 $answer->jumpto = LESSON_NEXTPAGE;
369 }
ac8e16be 370 Replaced with: */
371 if ($answer->grade > 50 ) {
372 $answer->jumpto = LESSON_NEXTPAGE;
5e7856af 373 $answer->score = 1;
374 }
ac8e16be 375 // end Replace
5e7856af 376 $answer->answer = $dataanswer;
377 $answer->response = $question->feedback[$key];
378 if (!$answer->id = insert_record("lesson_answers", $answer)) {
379 $result->error = "Could not insert multichoice quiz answer! ";
380 return $result;
381 }
382 // for Sanity checks
383 if ($question->fraction[$key] > 0) {
384 $totalfraction += $question->fraction[$key];
385 }
386 if ($question->fraction[$key] > $maxfraction) {
387 $maxfraction = $question->fraction[$key];
388 }
389 }
390 }
391
392 /// Perform sanity checks on fractional grades
393 if ($question->single) {
394 if ($maxfraction != 1) {
395 $maxfraction = $maxfraction * 100;
396 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
397 return $result;
398 }
399 } else {
400 $totalfraction = round($totalfraction,2);
401 if ($totalfraction != 1) {
402 $totalfraction = $totalfraction * 100;
403 $result->notice = get_string("fractionsaddwrong", "quiz", $totalfraction);
404 return $result;
405 }
406 }
407 break;
408
409 case LESSON_MATCHING:
410
411 $subquestions = array();
412
413 $i = 0;
414 // Insert all the new question+answer pairs
415 foreach ($question->subquestions as $key => $questiontext) {
90455bb3 416 $answertext = $question->subanswers[$key];
5e7856af 417 if (!empty($questiontext) and !empty($answertext)) {
f7ffb898 418 $answer = new stdClass;
5e7856af 419 $answer->lessonid = $question->lessonid;
420 $answer->pageid = $question->id;
421 $answer->timecreated = $timenow;
422 $answer->answer = $questiontext;
423 $answer->response = $answertext;
424 if ($i == 0) {
425 // first answer contains the correct answer jump
426 $answer->jumpto = LESSON_NEXTPAGE;
427 }
428 if (!$subquestion->id = insert_record("lesson_answers", $answer)) {
429 $result->error = "Could not insert quiz match subquestion!";
430 return $result;
431 }
432 $subquestions[] = $subquestion->id;
433 $i++;
434 }
435 }
436
437 if (count($subquestions) < 3) {
438 $result->notice = get_string("notenoughsubquestions", "quiz");
439 return $result;
440 }
441
442 break;
443
444
445 case LESSON_RANDOMSAMATCH:
446 $options->question = $question->id;
447 $options->choose = $question->choose;
448 if ($existing = get_record("quiz_randomsamatch", "question", $options->question)) {
449 $options->id = $existing->id;
450 if (!update_record("quiz_randomsamatch", $options)) {
451 $result->error = "Could not update quiz randomsamatch options!";
452 return $result;
453 }
454 } else {
455 if (!insert_record("quiz_randomsamatch", $options)) {
456 $result->error = "Could not insert quiz randomsamatch options!";
457 return $result;
458 }
459 }
460 break;
461
462 case LESSON_MULTIANSWER:
463 if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id, "id ASC")) {
464 $oldmultianswers = array();
465 }
466
467 // Insert all the new multi answers
468 foreach ($question->answers as $dataanswer) {
469 if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it
470 $multianswer = $oldmultianswer;
471 $multianswer->positionkey = $dataanswer->positionkey;
472 $multianswer->norm = $dataanswer->norm;
473 $multianswer->answertype = $dataanswer->answertype;
474
475 if (! $multianswer->answers = quiz_save_multianswer_alternatives
476 ($question->id, $dataanswer->answertype,
477 $dataanswer->alternatives, $oldmultianswer->answers))
478 {
479 $result->error = "Could not update multianswer alternatives! (id=$multianswer->id)";
480 return $result;
481 }
482 if (!update_record("quiz_multianswers", $multianswer)) {
483 $result->error = "Could not update quiz multianswer! (id=$multianswer->id)";
484 return $result;
485 }
486 } else { // This is a completely new answer
f7ffb898 487 $multianswer = new stdClass;
5e7856af 488 $multianswer->question = $question->id;
489 $multianswer->positionkey = $dataanswer->positionkey;
490 $multianswer->norm = $dataanswer->norm;
491 $multianswer->answertype = $dataanswer->answertype;
492
493 if (! $multianswer->answers = quiz_save_multianswer_alternatives
494 ($question->id, $dataanswer->answertype,
495 $dataanswer->alternatives))
496 {
497 $result->error = "Could not insert multianswer alternatives! (questionid=$question->id)";
498 return $result;
499 }
500 if (!insert_record("quiz_multianswers", $multianswer)) {
501 $result->error = "Could not insert quiz multianswer!";
502 return $result;
503 }
504 }
505 }
506 break;
507
508 case LESSON_RANDOM:
509 break;
510
511 case LESSON_DESCRIPTION:
512 break;
513
514 default:
515 $result->error = "Unsupported question type ($question->qtype)!";
516 return $result;
517 break;
518 }
519 return true;
520}
4b55d2af 521
522/**
523 * Given an array of value, creates a popup menu to be part of a form.
524 *
525 * @param array $options Used to create the popup menu values ( $options["value"]["label"] ).
526 * @param string $name Name of the select form element.
527 * @param string $selected Current value selected in the popup menu.
528 * @param string $nothing If set, used as the first value in the popup menu.
529 * @param string $script OnChange javascript code.
530 * @param string|int $nothingvalue Value of the $nothing parameter.
531 * @param boolean $return False: Print out the popup menu automatically True: Return the popup menu.
532 * @return string May return the popup menu as a string.
533 * @todo replace the use of this function with choose_from_menu in lib/weblib.php
534 **/
535function lesson_choose_from_menu ($options, $name, $selected="", $nothing="choose", $script="", $nothingvalue="0", $return=false) {
5e7856af 536 if ($nothing == "choose") {
537 $nothing = get_string("choose")."...";
538 }
539
540 if ($script) {
541 $javascript = "onChange=\"$script\"";
542 } else {
543 $javascript = "";
544 }
545
62eda6ea 546 $output = "<label for=$name class=hidden-label>$name</label><SELECT id=$name NAME=$name $javascript>\n";
5e7856af 547 if ($nothing) {
548 $output .= " <OPTION VALUE=\"$nothingvalue\"\n";
549 if ($nothingvalue == $selected) {
550 $output .= " SELECTED";
551 }
552 $output .= ">$nothing</OPTION>\n";
553 }
554 if (!empty($options)) {
555 foreach ($options as $value => $label) {
556 $output .= " <OPTION VALUE=\"$value\"";
557 if ($value == $selected) {
558 $output .= " SELECTED";
559 }
ac8e16be 560 // stop zero label being replaced by array index value
5e7856af 561 // if ($label) {
562 // $output .= ">$label</OPTION>\n";
563 // } else {
564 // $output .= ">$value</OPTION>\n";
ac8e16be 565 // }
566 $output .= ">$label</OPTION>\n";
5e7856af 567
568 }
569 }
570 $output .= "</SELECT>\n";
571
572 if ($return) {
573 return $output;
574 } else {
575 echo $output;
576 }
577}
578
4b55d2af 579/**
580 * Determins if a jumpto value is correct or not.
581 *
582 * returns true if jumpto page is (logically) after the pageid page or
583 * if the jumpto value is a special value. Returns false in all other cases.
584 *
585 * @param int $pageid Id of the page from which you are jumping from.
586 * @param int $jumpto The jumpto number.
587 * @return boolean True or false after a series of tests.
588 * @todo Can be optimized to only have to make 1 or 2 database calls instead of 1 foreach page in the lesson
589 **/
5e7856af 590function lesson_iscorrect($pageid, $jumpto) {
5e7856af 591
592 // first test the special values
593 if (!$jumpto) {
594 // same page
595 return false;
596 } elseif ($jumpto == LESSON_NEXTPAGE) {
597 return true;
ac8e16be 598 } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE) {
5e7856af 599 return true;
600 } elseif ($jumpto == LESSON_RANDOMPAGE) {
601 return true;
602 } elseif ($jumpto == LESSON_CLUSTERJUMP) {
603 return true;
5e7856af 604 } elseif ($jumpto == LESSON_EOL) {
605 return true;
606 }
607 // we have to run through the pages from pageid looking for jumpid
608 $apageid = get_field("lesson_pages", "nextpageid", "id", $pageid);
609 while (true) {
610 if ($jumpto == $apageid) {
611 return true;
612 }
613 if ($apageid) {
614 $apageid = get_field("lesson_pages", "nextpageid", "id", $apageid);
615 } else {
616 return false;
617 }
618 }
619 return false; // should never be reached
620}
621
4b55d2af 622/**
623 * Checks to see if a page is a branch table or is
624 * a page that is enclosed by a branch table and an end of branch or end of lesson.
625 * May call this function: {@link lesson_is_page_in_branch()}
626 *
627 * @param int $lesson_id Id of the lesson to which the page belongs.
628 * @param int $pageid Id of the page.
629 * @return boolean True or false.
630 * @todo Change $lesson_id param to $lessonid.
631 **/
5e7856af 632function lesson_display_branch_jumps($lesson_id, $pageid) {
ac8e16be 633 if($pageid == 0) {
634 // first page
635 return false;
636 }
637 // get all of the lesson pages
638 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson_id")) {
639 // adding first page
640 return false;
641 }
642
643 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
644 return true;
645 }
646
647 return lesson_is_page_in_branch($lessonpages, $pageid);
5e7856af 648}
649
4b55d2af 650/**
651 * Checks to see if a page is a cluster page or is
652 * a page that is enclosed by a cluster page and an end of cluster or end of lesson
653 * May call this function: {@link lesson_is_page_in_cluster()}
654 *
655 * @param int $lesson_id Id of the lesson to which the page belongs.
656 * @param int $pageid Id of the page.
657 * @return boolean True or false.
658 * @todo Change $lesson_id param to $lessonid.
659 **/
5e7856af 660function lesson_display_cluster_jump($lesson_id, $pageid) {
ac8e16be 661 if($pageid == 0) {
662 // first page
663 return false;
664 }
665 // get all of the lesson pages
666 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson_id")) {
667 // adding first page
668 return false;
669 }
670
671 if ($lessonpages[$pageid]->qtype == LESSON_CLUSTER) {
672 return true;
673 }
674
675 return lesson_is_page_in_cluster($lessonpages, $pageid);
5e7856af 676
677}
678
4b55d2af 679/**
680 * Checks to see if a LESSON_CLUSTERJUMP or
681 * a LESSON_UNSEENBRANCHPAGE is used in a lesson.
682 *
683 * This function is only executed when a teacher is
684 * checking the navigation for a lesson.
685 *
06469639 686 * @param int $lesson Id of the lesson that is to be checked.
4b55d2af 687 * @return boolean True or false.
688 **/
06469639 689function lesson_display_teacher_warning($lesson) {
ac8e16be 690 // get all of the lesson answers
691 if (!$lessonanswers = get_records_select("lesson_answers", "lessonid = $lesson")) {
692 // no answers, then not useing cluster or unseen
693 return false;
694 }
695 // just check for the first one that fulfills the requirements
696 foreach ($lessonanswers as $lessonanswer) {
697 if ($lessonanswer->jumpto == LESSON_CLUSTERJUMP || $lessonanswer->jumpto == LESSON_UNSEENBRANCHPAGE) {
698 return true;
699 }
700 }
701
702 // if no answers use either of the two jumps
703 return false;
5e7856af 704}
705
706
4b55d2af 707/**
708 * Interprets LESSON_CLUSTERJUMP jumpto value.
709 *
710 * This will select a page randomly
711 * and the page selected will be inbetween a cluster page and end of cluter or end of lesson
712 * and the page selected will be a page that has not been viewed already
713 * and if any pages are within a branch table or end of branch then only 1 page within
714 * the branch table or end of branch will be randomly selected (sub clustering).
715 *
716 * @param int $lesson Id of the lesson.
717 * @param int $user Id of the user.
718 * @param int $pageid Id of the current page from which we are jumping from.
719 * @return int The id of the next page.
720 * @todo Change $lesson param to $lessonid and $user param to $userid
721 **/
5e7856af 722function lesson_cluster_jump($lesson, $user, $pageid) {
ac8e16be 723 // get the number of retakes
5e7856af 724 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 725 $retakes = 0;
726 }
727
728 // get all the lesson_attempts aka what the user has seen
729 if ($seen = get_records_select("lesson_attempts", "lessonid = $lesson AND userid = $user AND retry = $retakes", "timeseen DESC")) {
730 foreach ($seen as $value) { // load it into an array that I can more easily use
731 $seenpages[$value->pageid] = $value->pageid;
732 }
733 } else {
734 $seenpages = array();
735 }
736
737 // get the lesson pages
738 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
739 error("Error: could not find records in lesson_pages table");
740 }
741 // find the start of the cluster
742 while ($pageid != 0) { // this condition should not be satisfied... should be a cluster page
743 if ($lessonpages[$pageid]->qtype == LESSON_CLUSTER) {
744 break;
745 }
746 $pageid = $lessonpages[$pageid]->prevpageid;
747 }
748
749 $pageid = $lessonpages[$pageid]->nextpageid; // move down from the cluster page
750
751 $clusterpages = array();
752 while (true) { // now load all the pages into the cluster that are not already inside of a branch table.
753 if ($lessonpages[$pageid]->qtype == LESSON_ENDOFCLUSTER) {
754 // store the endofcluster page's jump
755 $exitjump = get_field("lesson_answers", "jumpto", "pageid", $pageid, "lessonid", $lesson);
756 if ($exitjump == LESSON_NEXTPAGE) {
757 $exitjump = $lessonpages[$pageid]->nextpageid;
758 }
759 if ($exitjump == 0) {
760 $exitjump = LESSON_EOL;
761 }
762 break;
763 } elseif (!lesson_is_page_in_branch($lessonpages, $pageid) && $lessonpages[$pageid]->qtype != LESSON_ENDOFBRANCH) {
764 // load page into array when it is not in a branch table and when it is not an endofbranch
765 $clusterpages[] = $lessonpages[$pageid];
766 }
767 if ($lessonpages[$pageid]->nextpageid == 0) {
768 // shouldn't ever get here... should be using endofcluster
769 $exitjump = LESSON_EOL;
770 break;
771 } else {
772 $pageid = $lessonpages[$pageid]->nextpageid;
773 }
774 }
775
776 // filter out the ones we have seen
777 $unseen = array();
778 foreach ($clusterpages as $clusterpage) {
779 if ($clusterpage->qtype == LESSON_BRANCHTABLE) { // if branchtable, check to see if any pages inside have been viewed
780 $branchpages = lesson_pages_in_branch($lessonpages, $clusterpage->id); // get the pages in the branchtable
781 $flag = true;
782 foreach ($branchpages as $branchpage) {
783 if (array_key_exists($branchpage->id, $seenpages)) { // check if any of the pages have been viewed
784 $flag = false;
785 }
786 }
787 if ($flag && count($branchpages) > 0) {
788 // add branch table
789 $unseen[] = $clusterpage;
790 }
791 } else {
792 // add any other type of page that has not already been viewed
793 if (!array_key_exists($clusterpage->id, $seenpages)) {
794 $unseen[] = $clusterpage;
795 }
796 }
797 }
798
799 if (count($unseen) > 0) { // it does not contain elements, then use exitjump, otherwise find out next page/branch
800 $nextpage = $unseen[rand(0, count($unseen)-1)];
801 } else {
802 return $exitjump; // seen all there is to see, leave the cluster
803 }
804
805 if ($nextpage->qtype == LESSON_BRANCHTABLE) { // if branch table, then pick a random page inside of it
806 $branchpages = lesson_pages_in_branch($lessonpages, $nextpage->id);
807 return $branchpages[rand(0, count($branchpages)-1)]->id;
808 } else { // otherwise, return the page's id
809 return $nextpage->id;
810 }
5e7856af 811}
812
4b55d2af 813/**
814 * Returns pages that are within a branch table and another branch table, end of branch or end of lesson
815 *
816 * @param array $lessonpages An array of lesson page objects.
817 * @param int $branchid The id of the branch table that we would like the containing pages for.
818 * @return array An array of lesson page objects.
819 **/
5e7856af 820function lesson_pages_in_branch($lessonpages, $branchid) {
ac8e16be 821 $pageid = $lessonpages[$branchid]->nextpageid; // move to the first page after the branch table
822 $pagesinbranch = array();
823
824 while (true) {
825 if ($pageid == 0) { // EOL
826 break;
827 } elseif ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
828 break;
829 } elseif ($lessonpages[$pageid]->qtype == LESSON_ENDOFBRANCH) {
830 break;
831 }
832 $pagesinbranch[] = $lessonpages[$pageid];
833 $pageid = $lessonpages[$pageid]->nextpageid;
834 }
835
836 return $pagesinbranch;
5e7856af 837}
838
4b55d2af 839/**
840 * Interprets the LESSON_UNSEENBRANCHPAGE jump.
841 *
842 * will return the pageid of a random unseen page that is within a branch
843 *
844 * @see lesson_pages_in_branch()
845 * @param int $lesson Id of the lesson.
846 * @param int $user Id of the user.
847 * @param int $pageid Id of the page from which we are jumping.
848 * @return int Id of the next page.
849 * @todo Change params $lesson to $lessonid and $user to $userid.
850 **/
5e7856af 851function lesson_unseen_question_jump($lesson, $user, $pageid) {
ac8e16be 852 // get the number of retakes
5e7856af 853 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 854 $retakes = 0;
855 }
856
857 // get all the lesson_attempts aka what the user has seen
858 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson AND userid = $user AND retry = $retakes", "timeseen DESC")) {
859 foreach($viewedpages as $viewed) {
860 $seenpages[] = $viewed->pageid;
861 }
862 } else {
863 $seenpages = array();
864 }
865
866 // get the lesson pages
867 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
868 error("Error: could not find records in lesson_pages table");
869 }
870
871 if ($pageid == LESSON_UNSEENBRANCHPAGE) { // this only happens when a student leaves in the middle of an unseen question within a branch series
872 $pageid = $seenpages[0]; // just change the pageid to the last page viewed inside the branch table
873 }
874
875 // go up the pages till branch table
876 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
877 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
878 break;
879 }
880 $pageid = $lessonpages[$pageid]->prevpageid;
881 }
882
883 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
884
885 // this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array
886 $unseen = array();
887 foreach($pagesinbranch as $page) {
888 if (!in_array($page->id, $seenpages)) {
889 $unseen[] = $page->id;
890 }
891 }
892
893 if(count($unseen) == 0) {
894 if(isset($pagesinbranch)) {
895 $temp = end($pagesinbranch);
896 $nextpage = $temp->nextpageid; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL
897 } else {
898 // there are no pages inside the branch, so return the next page
899 $nextpage = $lessonpages[$pageid]->nextpageid;
900 }
901 if ($nextpage == 0) {
902 return LESSON_EOL;
903 } else {
904 return $nextpage;
905 }
906 } else {
907 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
908 }
5e7856af 909}
910
4b55d2af 911/**
912 * Handles the unseen branch table jump.
913 *
914 * @param int $lesson Lesson id.
915 * @param int $user User id.
916 * @return int Will return the page id of a branch table or end of lesson
917 * @todo Change $lesson param to $lessonid and change $user param to $userid.
918 **/
5e7856af 919function lesson_unseen_branch_jump($lesson, $user) {
5e7856af 920 if (!$retakes = count_records("lesson_grades", "lessonid", $lesson, "userid", $user)) {
ac8e16be 921 $retakes = 0;
922 }
923
924 if (!$seenbranches = get_records_select("lesson_branch", "lessonid = $lesson AND userid = $user AND retry = $retakes",
925 "timeseen DESC")) {
926 error("Error: could not find records in lesson_branch table");
927 }
928
929 // get the lesson pages
930 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
931 error("Error: could not find records in lesson_pages table");
932 }
933
934 // this loads all the viewed branch tables into $seen untill it finds the branch table with the flag
935 // which is the branch table that starts the unseenbranch function
936 $seen = array();
937 foreach ($seenbranches as $seenbranch) {
938 if (!$seenbranch->flag) {
939 $seen[$seenbranch->pageid] = $seenbranch->pageid;
940 } else {
941 $start = $seenbranch->pageid;
942 break;
943 }
944 }
945 // this function searches through the lesson pages to find all the branch tables
946 // that follow the flagged branch table
947 $pageid = $lessonpages[$start]->nextpageid; // move down from the flagged branch table
948 while ($pageid != 0) { // grab all of the branch table till eol
949 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
950 $branchtables[] = $lessonpages[$pageid]->id;
951 }
952 $pageid = $lessonpages[$pageid]->nextpageid;
953 }
954 $unseen = array();
955 foreach ($branchtables as $branchtable) {
956 // load all of the unseen branch tables into unseen
957 if (!array_key_exists($branchtable, $seen)) {
958 $unseen[] = $branchtable;
959 }
960 }
961 if (count($unseen) > 0) {
962 return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
963 } else {
964 return LESSON_EOL; // has viewed all of the branch tables
965 }
5e7856af 966}
967
4b55d2af 968/**
969 * Handles the random jump between a branch table and end of branch or end of lesson (LESSON_RANDOMPAGE).
970 *
971 * @param int $lesson Lesson id.
972 * @param int $pageid The id of the page that we are jumping from (?)
973 * @return int The pageid of a random page that is within a branch table
974 * @todo Change $lesson param to $lessonid.
975 **/
5e7856af 976function lesson_random_question_jump($lesson, $pageid) {
ac8e16be 977 // get the lesson pages
978 if (!$lessonpages = get_records_select("lesson_pages", "lessonid = $lesson")) {
979 error("Error: could not find records in lesson_pages table");
980 }
981
982 // go up the pages till branch table
983 while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
984
985 if ($lessonpages[$pageid]->qtype == LESSON_BRANCHTABLE) {
986 break;
987 }
988 $pageid = $lessonpages[$pageid]->prevpageid;
989 }
990
991 // get the pages within the branch
992 $pagesinbranch = lesson_pages_in_branch($lessonpages, $pageid);
993
994 if(count($pagesinbranch) == 0) {
995 // there are no pages inside the branch, so return the next page
996 return $lessonpages[$pageid]->nextpageid;
997 } else {
998 return $pagesinbranch[rand(0, count($pagesinbranch)-1)]->id; // returns a random page id for the next page
999 }
5e7856af 1000}
1001
4b55d2af 1002/**
1003 * Check to see if a page is below a branch table (logically).
1004 *
1005 * Will return true if a branch table is found logically above the page.
1006 * Will return false if an end of branch, cluster or the beginning
1007 * of the lesson is found before a branch table.
1008 *
1009 * @param array $pages An array of lesson page objects.
1010 * @param int $pageid Id of the page for testing.
1011 * @return boolean
1012 */
5e7856af 1013function lesson_is_page_in_branch($pages, $pageid) {
ac8e16be 1014 $pageid = $pages[$pageid]->prevpageid; // move up one
1015
1016 // go up the pages till branch table
1017 while (true) {
1018 if ($pageid == 0) { // ran into the beginning of the lesson
1019 return false;
1020 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFBRANCH) { // ran into the end of another branch table
1021 return false;
1022 } elseif ($pages[$pageid]->qtype == LESSON_CLUSTER) { // do not look beyond a cluster
1023 return false;
1024 } elseif ($pages[$pageid]->qtype == LESSON_BRANCHTABLE) { // hit a branch table
1025 return true;
1026 }
1027 $pageid = $pages[$pageid]->prevpageid;
1028 }
5e7856af 1029
1030}
1031
4b55d2af 1032/**
1033 * Check to see if a page is below a cluster page (logically).
1034 *
1035 * Will return true if a cluster is found logically above the page.
1036 * Will return false if an end of cluster or the beginning
1037 * of the lesson is found before a cluster page.
1038 *
1039 * @param array $pages An array of lesson page objects.
1040 * @param int $pageid Id of the page for testing.
1041 * @return boolean
1042 */
5e7856af 1043function lesson_is_page_in_cluster($pages, $pageid) {
ac8e16be 1044 $pageid = $pages[$pageid]->prevpageid; // move up one
1045
1046 // go up the pages till branch table
1047 while (true) {
1048 if ($pageid == 0) { // ran into the beginning of the lesson
1049 return false;
1050 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFCLUSTER) { // ran into the end of another branch table
1051 return false;
1052 } elseif ($pages[$pageid]->qtype == LESSON_CLUSTER) { // hit a branch table
1053 return true;
1054 }
1055 $pageid = $pages[$pageid]->prevpageid;
1056 }
5e7856af 1057}
1058
4b55d2af 1059/**
1060 * Prints the contents for the left menu
1061 *
1062 * Runs through all of the lesson pages and calls {@link lesson_print_tree_link_menu()}
1063 * to print out the link.
1064 *
1065 * @see lesson_print_tree_link_menu()
1066 * @param int $lessonid Lesson id.
1067 * @param int $pageid Page id of the first page of the lesson.
1068 * @param int $id The cmid of the lesson.
1069 * @param boolean $showpages An optional paramater to show question pages as well as branch tables in the left menu (NYI)
1070 * @todo change $id param to $cmid. Finnish implementing the $showpages feature.
1071 * Not necessary to pass $pageid, we can find that out in the function.
1072 * Also, no real need for {@link lesson_print_tree_link_menu()}. Everything can be handled in this function.
1073 */
b9869082 1074function lesson_print_tree_menu($lessonid, $pageid, $id, $showpages=false) {
4b55d2af 1075 if (!$pages = get_records_select("lesson_pages", "lessonid = $lessonid")) {
ac8e16be 1076 error("Error: could not find lesson pages");
1077 }
d6941aff 1078 echo '<ul>';
ac8e16be 1079 while ($pageid != 0) {
1080 lesson_print_tree_link_menu($pages[$pageid], $id, true);
1081 $pageid = $pages[$pageid]->nextpageid;
1082 }
d6941aff 1083 echo '</ul>';
5e7856af 1084}
1085
4b55d2af 1086/**
1087 * Prints the actual link for the left menu
1088 *
1089 * Only prints branch tables that have display set to on.
1090 *
1091 * @param object $page Lesson page object.
1092 * @param int $id The cmid of the lesson.
1093 * @param boolean $showpages An optional paramater to show question pages as well as branch tables in the left menu (NYI)
1094 * @todo change $id param to $cmid. Finnish implementing the $showpages feature.
1095 */
b9869082 1096function lesson_print_tree_link_menu($page, $id, $showpages=false) {
ac8e16be 1097 if ($page->qtype == LESSON_BRANCHTABLE && !$page->display) {
1098 return false;
1099 } elseif ($page->qtype != LESSON_BRANCHTABLE) {
1100 return false;
1101 }
1102
1103 /*elseif ($page->qtype != LESSON_BRANCHTABLE && !$showpages) {
1104 return false;
1105 } elseif (!in_array($page->qtype, $LESSON_QUESTION_TYPE)) {
1106 return false;
1107 }*/
1108
1109 // set up some variables NoticeFix changed whole function
1110 $output = "";
d6941aff 1111 $class = ' class="leftmenu_not_selected_link" ';
ac8e16be 1112
97d2756c 1113 if($page->id == optional_param('pageid', 0, PARAM_INT)) {
1114 $class = ' class="leftmenu_selected_link" ';
1115 }
ac8e16be 1116
d6941aff 1117 $output .= '<li>';
ac8e16be 1118
d6941aff 1119 $output .= "<a $class href=\"view.php?id=$id&amp;action=navigation&amp;pageid=$page->id\">".format_string($page->title,true)."</a>\n";
1120
1121 $output .= "</li>";
5e7856af 1122
d6941aff 1123 echo $output;
5e7856af 1124}
1125
4b55d2af 1126/**
1127 * Prints out the tree view list.
1128 *
1129 * Each page in the lesson is printed out as a link. If the page is a branch table
1130 * or an end of branch then the link color changes and the answer jumps are also printed
1131 * alongside the links. Also, the editing buttons (move, update, delete) are printed
1132 * next to the links.
1133 *
1134 * @uses $USER
a1d0b3eb 1135 * @uses $CFG
4b55d2af 1136 * @param int $pageid Page id of the first page of the lesson.
a1d0b3eb 1137 * @param object $lesson Object of the current lesson.
4b55d2af 1138 * @param int $cmid The course module id of the lesson.
1139 * @param string $pixpath Path to the pictures.
a1d0b3eb 1140 * @todo $pageid does not need to be passed. Can be found in the function.
97d2756c 1141 * This function is only called once. It should be removed and the code inside it moved to view.php
4b55d2af 1142 */
a1d0b3eb 1143function lesson_print_tree($pageid, $lesson, $cmid) {
1144 global $USER, $CFG;
ac8e16be 1145
a1d0b3eb 1146 if(!$pages = get_records_select("lesson_pages", "lessonid = $lesson->id")) {
ac8e16be 1147 error("Error: could not find lesson pages");
1148 }
1149 echo "<table>";
1150 while ($pageid != 0) {
1151 echo "<tr><td>";
1152 if(($pages[$pageid]->qtype != LESSON_BRANCHTABLE) && ($pages[$pageid]->qtype != LESSON_ENDOFBRANCH)) {
68dfc4de 1153 $output = "<a style='color:#DF041E;' href=\"view.php?id=$cmid&display=".$pages[$pageid]->id."\">".format_string($pages[$pageid]->title,true)."</a>\n";
ac8e16be 1154 } else {
68dfc4de 1155 $output = "<a href=\"view.php?id=$cmid&display=".$pages[$pageid]->id."\">".format_string($pages[$pageid]->title,true)."</a>\n";
ac8e16be 1156
a1d0b3eb 1157 if($answers = get_records_select("lesson_answers", "lessonid = $lesson->id and pageid = $pageid")) {
ac8e16be 1158 $output .= "Jumps to: ";
1159 $end = end($answers);
1160 foreach ($answers as $answer) {
1161 if ($answer->jumpto == 0) {
1162 $output .= get_string("thispage", "lesson");
1163 } elseif ($answer->jumpto == LESSON_NEXTPAGE) {
1164 $output .= get_string("nextpage", "lesson");
1165 } elseif ($answer->jumpto == LESSON_EOL) {
1166 $output .= get_string("endoflesson", "lesson");
1167 } elseif ($answer->jumpto == LESSON_UNSEENBRANCHPAGE) {
1168 $output .= get_string("unseenpageinbranch", "lesson");
1169 } elseif ($answer->jumpto == LESSON_PREVIOUSPAGE) {
1170 $output .= get_string("previouspage", "lesson");
1171 } elseif ($answer->jumpto == LESSON_RANDOMPAGE) {
1172 $output .= get_string("randompageinbranch", "lesson");
1173 } elseif ($answer->jumpto == LESSON_RANDOMBRANCH) {
1174 $output .= get_string("randombranch", "lesson");
1175 } elseif ($answer->jumpto == LESSON_CLUSTERJUMP) {
1176 $output .= get_string("clusterjump", "lesson");
1177 } else {
68dfc4de 1178 $output .= format_string($pages[$answer->jumpto]->title);
ac8e16be 1179 }
1180 if ($answer->id != $end->id) {
1181 $output .= ", ";
1182 }
1183 }
1184 }
1185 }
1186
1187 echo $output;
a1d0b3eb 1188 if (isteacheredit($lesson->course)) {
1189 if (count($pages) > 1) {
1190 echo "<a title=\"move\" href=\"lesson.php?id=$cmid&action=move&pageid=".$pages[$pageid]->id."\">\n".
1191 "<img src=\"$CFG->pixpath/t/move.gif\" hspace=\"2\" height=11 width=11 alt=\"move\" border=0></a>\n";
1192 }
1193 echo "<a title=\"update\" href=\"lesson.php?id=$cmid&amp;action=editpage&amp;pageid=".$pages[$pageid]->id."\">\n".
1194 "<img src=\"$CFG->pixpath/t/edit.gif\" hspace=\"2\" height=11 width=11 alt=\"edit\" border=0></a>\n".
1195 "<a title=\"delete\" href=\"lesson.php?id=$cmid&amp;sesskey=".$USER->sesskey."&amp;action=confirmdelete&amp;pageid=".$pages[$pageid]->id."\">\n".
1196 "<img src=\"$CFG->pixpath/t/delete.gif\" hspace=\"2\" height=11 width=11 alt=\"delete\" border=0></a>\n";
ac8e16be 1197 }
ac8e16be 1198 echo "</tr></td>";
1199 $pageid = $pages[$pageid]->nextpageid;
1200 }
1201 echo "</table>";
5e7856af 1202}
1203
4b55d2af 1204/**
1205 * Calculates a user's grade for a lesson.
1206 *
4b55d2af 1207 * @param object $lesson The lesson that the user is taking.
4b55d2af 1208 * @param int $retries The attempt number.
88427c07 1209 * @param int $userid Id of the user (optinal, default current user).
1210 * @return object { nquestions => number of questions answered
1211 attempts => number of question attempts
1212 total => max points possible
1213 earned => points earned by student
1214 grade => calculated percentage grade
1215 nmanual => number of manually graded questions
1216 manualpoints => point value for manually graded questions }
4b55d2af 1217 */
88427c07 1218function lesson_grade($lesson, $ntries, $userid = 0) {
1219 global $USER;
ac8e16be 1220
88427c07 1221 if (empty($userid)) {
1222 $userid = $USER->id;
1223 }
1224
1225 // Zero out everything
1226 $ncorrect = 0;
1227 $nviewed = 0;
1228 $score = 0;
1229 $nmanual = 0;
1230 $manualpoints = 0;
1231 $thegrade = 0;
1232 $nquestions = 0;
1233 $total = 0;
1234 $earned = 0;
1235
1236 if ($useranswers = get_records_select("lesson_attempts", "lessonid = $lesson->id AND
1237 userid = $userid AND retry = $ntries", "timeseen")) {
1238 // group each try with its page
1239 $attemptset = array();
1240 foreach ($useranswers as $useranswer) {
1241 $attemptset[$useranswer->pageid][] = $useranswer;
ac8e16be 1242 }
88427c07 1243
1244 // Drop all attempts that go beyond max attempts for the lesson
1245 foreach ($attemptset as $key => $set) {
1246 $attemptset[$key] = array_slice($set, 0, $lesson->maxattempts);
1247 }
1248
1249 $pageids = implode(",", array_keys($attemptset));
1250
1251 // get only the pages and their answers that the user answered
1252 $pages = get_records_select("lesson_pages", "lessonid = $lesson->id AND id IN($pageids)");
1253 $answers = get_records_select("lesson_answers", "lessonid = $lesson->id AND pageid IN($pageids)");
1254
1255 // Number of pages answered
1256 $nquestions = count($pages);
1257
1258 foreach ($attemptset as $attempts) {
1259 if ($lesson->custom) {
1260 $attempt = end($attempts);
1261 // If essay question, handle it, otherwise add to score
1262 if ($pages[$attempt->pageid]->qtype == LESSON_ESSAY) {
ac8e16be 1263 $essayinfo = unserialize($attempt->useranswer);
88427c07 1264 $earned += $essayinfo->score;
1265 $nmanual++;
1266 $manualpoints += $answers[$attempt->answerid]->score;
ac8e16be 1267 } else {
88427c07 1268 $earned += $answers[$attempt->answerid]->score;
1269 }
1270 } else {
1271 foreach ($attempts as $attempt) {
1272 $earned += $attempt->correct;
1273 }
1274 $attempt = end($attempts); // doesn't matter which one
1275 // If essay question, increase numbers
1276 if ($pages[$attempt->pageid]->qtype == LESSON_ESSAY) {
1277 $nmanual++;
1278 $manualpoints++;
ac8e16be 1279 }
1280 }
88427c07 1281 // Number of times answered
1282 $nviewed += count($attempts);
1283 }
1284
1285 if ($lesson->custom) {
ac8e16be 1286 $bestscores = array();
88427c07 1287 // Find the highest possible score per page to get our total
1288 foreach ($answers as $answer) {
46341ab7 1289 if(!isset($bestscores[$answer->pageid])) {
88427c07 1290 $bestscores[$answer->pageid] = $answer->score;
46341ab7 1291 } else if ($bestscores[$answer->pageid] < $answer->score) {
88427c07 1292 $bestscores[$answer->pageid] = $answer->score;
ac8e16be 1293 }
1294 }
88427c07 1295 $total = array_sum($bestscores);
1296 } else {
1297 // Check to make sure the student has answered the minimum questions
1298 if ($lesson->minquestions and $nquestions < $lesson->minquestions) {
1299 // Nope, increase number viewed by the amount of unanswered questions
1300 $total = $nviewed + ($lesson->minquestions - $nquestions);
1301 } else {
1302 $total = $nviewed;
1303 }
ac8e16be 1304 }
88427c07 1305 }
1306
1307 if ($total) { // not zero
1308 $thegrade = round(100 * $earned / $total, 5);
1309 }
1310
1311 // Build the grade information object
1312 $gradeinfo = new stdClass;
1313 $gradeinfo->nquestions = $nquestions;
1314 $gradeinfo->attempts = $nviewed;
1315 $gradeinfo->total = $total;
1316 $gradeinfo->earned = $earned;
1317 $gradeinfo->grade = $thegrade;
1318 $gradeinfo->nmanual = $nmanual;
1319 $gradeinfo->manualpoints = $manualpoints;
1320
1321 return $gradeinfo;
1322}
1323
1324/**
1325 * Prints the on going message to the user.
1326 *
1327 * With custom grading On, displays points
1328 * earned out of total points possible thus far.
1329 * With custom grading Off, displays number of correct
1330 * answers out of total attempted.
1331 *
1332 * @param object $lesson The lesson that the user is taking.
1333 * @return void
1334 **/
1335function lesson_print_ongoing_score($lesson) {
1336 global $USER;
1337
1338 if (isteacher($lesson->course)) {
1339 echo "<p align=\"center\">".get_string('teacherongoingwarning', 'lesson').'</p>';
1340 } else {
1341 $ntries = count_records("lesson_grades", "lessonid", $lesson->id, "userid", $USER->id);
1342 if (isset($USER->modattempts[$lesson->id])) {
1343 $ntries--;
1344 }
1345 $gradeinfo = lesson_grade($lesson, $ntries);
ac8e16be 1346
88427c07 1347 $a = new stdClass;
1348 if ($lesson->custom) {
1349 $a->score = $gradeinfo->earned;
1350 $a->currenthigh = $gradeinfo->total;
1351 print_simple_box(get_string("ongoingcustom", "lesson", $a), "center");
ac8e16be 1352 } else {
88427c07 1353 $a->correct = $gradeinfo->earned;
1354 $a->viewed = $gradeinfo->attempts;
1355 print_simple_box(get_string("ongoingnormal", "lesson", $a), "center");
ac8e16be 1356 }
1357 }
5e7856af 1358}
1359
4b55d2af 1360/**
1361 * Prints tabs for the editing and adding pages. Each tab is a question type.
1362 *
1363 * @param array $qtypes The question types array (may not need to pass this because it is defined in this file)
1364 * @param string $selected Current selected tab
1365 * @param string $link The base href value of the link for the tab
1366 * @param string $onclick Javascript for the tab link
1367 * @return void
1368 */
5e7856af 1369function lesson_qtype_menu($qtypes, $selected="", $link="", $onclick="") {
271fea97 1370 $tabs = array();
1371 $tabrows = array();
1372
9638a1f4 1373 foreach ($qtypes as $qtype => $qtypename) {
271fea97 1374 $tabrows[] = new tabobject($qtype, "$link&amp;qtype=$qtype\" onClick=\"$onclick\"", $qtypename);
ac8e16be 1375 }
271fea97 1376 $tabs[] = $tabrows;
1377 print_tabs($tabs, $selected);
1378 echo "<input type=\"hidden\" name=\"qtype\" value=\"$selected\" /> \n";
9638a1f4 1379
5e7856af 1380}
1381
4b55d2af 1382/**
1383 * Checks to see if the nickname is naughty or not.
1384 *
1385 * @todo Move this to highscores.php
1386 */
5e7856af 1387function lesson_check_nickname($name) {
5e7856af 1388
832b9f82 1389 if (empty($name)) {
ac8e16be 1390 return false;
1391 }
1392
832b9f82 1393 $filterwords = explode(',', get_string('censorbadwords'));
ac8e16be 1394
1395 foreach ($filterwords as $filterword) {
1396 if (strstr($name, $filterword)) {
1397 return false;
1398 }
1399 }
1400 return true;
5e7856af 1401}
57bfe93d 1402
62eda6ea 1403/**
1404 * Prints out a Progress Bar which depicts a user's progress within a lesson.
1405 *
1406 * Currently works best with a linear lesson. Clusters are counted as a single page.
1407 * Also, only viewed branch tables and questions that have been answered correctly count
1408 * toward lesson completion (or progress). Only Students can see the Progress bar as well.
1409 *
1410 * @param object $lesson The lesson that the user is currently taking.
1411 * @param object $course The course that the to which the lesson belongs.
1412 * @return boolean The return is not significant as of yet. Will return true/false.
1413 * @author Mark Nielsen
1414 **/
1415function lesson_print_progress_bar($lesson, $course) {
1416 global $CFG, $USER;
1417
1418 // lesson setting to turn progress bar on or off
1419 if (!$lesson->progressbar) {
1420 return false;
1421 }
1422
1423 // catch teachers
1424 if (isteacher($course->id)) {
1425 notify(get_string('progressbarteacherwarning', 'lesson', $course->teachers));
1426 return false;
1427 }
acab9099 1428 if (!isset($USER->modattempts[$lesson->id])) {
1429 // all of the lesson pages
1430 if (!$pages = get_records('lesson_pages', 'lessonid', $lesson->id)) {
1431 return false;
1432 } else {
1433 foreach ($pages as $page) {
1434 if ($page->prevpageid == 0) {
1435 $pageid = $page->id; // find the first page id
1436 break;
1437 }
62eda6ea 1438 }
1439 }
62eda6ea 1440
acab9099 1441 // current attempt number
1442 if (!$ntries = count_records("lesson_grades", "lessonid", $lesson->id, "userid", $USER->id)) {
1443 $ntries = 0; // may not be necessary
1444 }
62eda6ea 1445
acab9099 1446 $viewedpageids = array();
62eda6ea 1447
acab9099 1448 // collect all of the correctly answered questions
1449 if ($viewedpages = get_records_select("lesson_attempts", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries AND correct = 1", 'timeseen DESC', 'pageid, id')) {
1450 $viewedpageids = array_keys($viewedpages);
1451 }
1452 // collect all of the branch tables viewed
1453 if ($viewedbranches = get_records_select("lesson_branch", "lessonid = $lesson->id AND userid = $USER->id AND retry = $ntries", 'timeseen DESC', 'pageid, id')) {
1454 $viewedpageids = array_merge($viewedpageids, array_keys($viewedbranches));
1455 }
1456
1457 // Filter out the following pages:
1458 // End of Cluster
1459 // End of Branch
1460 // Pages found inside of Clusters
1461 // Do not filter out Cluster Page(s) because we count a cluster as one.
1462 // By keeping the cluster page, we get our 1
1463 $validpages = array();
1464 while ($pageid != 0) {
1465 if ($pages[$pageid]->qtype == LESSON_CLUSTER) {
1466 $clusterpageid = $pageid; // copy it
1467 $validpages[$clusterpageid] = 1; // add the cluster page as a valid page
1468 $pageid = $pages[$pageid]->nextpageid; // get next page
62eda6ea 1469
acab9099 1470 // now, remove all necessary viewed paged ids from the viewedpageids array.
1471 while ($pages[$pageid]->qtype != LESSON_ENDOFCLUSTER and $pageid != 0) {
1472 if (in_array($pageid, $viewedpageids)) {
1473 unset($viewedpageids[array_search($pageid, $viewedpageids)]); // remove it
1474 // since the user did see one page in the cluster, add the cluster pageid to the viewedpageids
1475 if (!in_array($clusterpageid, $viewedpageids)) {
1476 $viewedpageids[] = $clusterpageid;
1477 }
62eda6ea 1478 }
acab9099 1479 $pageid = $pages[$pageid]->nextpageid;
62eda6ea 1480 }
acab9099 1481 } elseif ($pages[$pageid]->qtype == LESSON_ENDOFCLUSTER or $pages[$pageid]->qtype == LESSON_ENDOFBRANCH) {
1482 // dont count these, just go to next
1483 $pageid = $pages[$pageid]->nextpageid;
1484 } else {
1485 // a counted page
1486 $validpages[$pageid] = 1;
62eda6ea 1487 $pageid = $pages[$pageid]->nextpageid;
1488 }
acab9099 1489 }
62eda6ea 1490
acab9099 1491 // progress calculation as a percent
1492 $progress = round(count($viewedpageids)/count($validpages), 2) * 100;
1493 } else {
1494 $progress = 100;
1495 }
62eda6ea 1496
1497 // print out the Progress Bar. Attempted to put as much as possible in the style sheets.
1498 echo '<div class="progress_bar" align="center">';
1499 echo '<table class="progress_bar_table"><tr>';
1500 if ($progress != 0) { // some browsers do not repsect the 0 width.
1501 echo '<td width="'.$progress.'%" class="progress_bar_completed">';
1502 echo '</td>';
1503 }
1504 echo '<td class="progress_bar_todo">';
1505 echo '<div class="progress_bar_token"></div>';
1506 echo '</td>';
1507 echo '</tr></table>';
1508 echo '</div>';
1509
1510 return true;
1511}
1512
1513/**
1514 * Determines if a user can view the left menu. The determining factor
1515 * is whether a user has a grade greater than or equal to the lesson setting
1516 * of displayleftif
1517 *
1518 * @param object $lesson Lesson object of the current lesson
1519 * @return boolean 0 if the user cannot see, or $lesson->displayleft to keep displayleft unchanged
1520 * @author Mark Nielsen
1521 **/
1522function lesson_displayleftif($lesson) {
1523 global $CFG, $USER;
1524
1525 if (!empty($lesson->displayleftif)) {
1526 // get the current user's max grade for this lesson
1527 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')) {
1528 if ($maxgrade->maxgrade < $lesson->displayleftif) {
1529 return 0; // turn off the displayleft
1530 }
1531 } else {
1532 return 0; // no grades
1533 }
1534 }
1535
1536 // if we get to here, keep the original state of displayleft lesson setting
1537 return $lesson->displayleft;
1538}
5e7856af 1539
2a488ba5 1540?>