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