ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç shortanswer.php000064400000054350152267036420007652 0ustar00. /** * Short answer * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Short answer question type */ define("LESSON_PAGE_SHORTANSWER", "1"); class lesson_page_type_shortanswer extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeidstring = 'shortanswer'; protected $typeid = LESSON_PAGE_SHORTANSWER; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function display($renderer, $attempt) { global $USER, $CFG, $PAGE; $mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents(), 'lessonid'=>$this->lesson->id)); $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; if (isset($USER->modattempts[$this->lesson->id])) { $data->answer = s($attempt->useranswer); } $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } /** * Creates answers for this page type. * * @param object $properties The answer properties. */ public function create_answers($properties) { if (isset($properties->enableotheranswers) && $properties->enableotheranswers) { $properties->response_editor = array_values($properties->response_editor); $properties->jumpto = array_values($properties->jumpto); $properties->score = array_values($properties->score); $wrongresponse = end($properties->response_editor); $wrongkey = key($properties->response_editor); $properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS; } parent::create_answers($properties); } /** * Update the answers for this page type. * * @param object $properties The answer properties. * @param context $context The context for this module. * @param int $maxbytes The maximum bytes for any uploades. */ public function update($properties, $context = null, $maxbytes = null) { if ($properties->enableotheranswers) { $properties->response_editor = array_values($properties->response_editor); $properties->jumpto = array_values($properties->jumpto); $properties->score = array_values($properties->score); $wrongresponse = end($properties->response_editor); $wrongkey = key($properties->response_editor); $properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS; } parent::update($properties, $context, $maxbytes); } public function check_answer() { global $CFG; $result = parent::check_answer(); $mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents())); $data = $mform->get_data(); require_sesskey(); $studentanswer = trim($data->answer); if ($studentanswer === '') { $result->noanswer = true; return $result; } $i=0; $answers = $this->get_answers(); foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer, false); $i++; // Applying PARAM_TEXT as it is applied to the answer submitted by the user. $expectedanswer = clean_param($answer->answer, PARAM_TEXT); $ismatch = false; $markit = false; $useregexp = ($this->qoption); if ($useregexp) { //we are using 'normal analysis', which ignores case $ignorecase = ''; if (substr($expectedanswer, -2) == '/i') { $expectedanswer = substr($expectedanswer, 0, -2); $ignorecase = 'i'; } } else { $expectedanswer = str_replace('*', '%@@%@@%', $expectedanswer); $expectedanswer = preg_quote($expectedanswer, '/'); $expectedanswer = str_replace('%@@%@@%', '.*', $expectedanswer); } // see if user typed in any of the correct answers if ((!$this->lesson->custom && $this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) or ($this->lesson->custom && $answer->score > 0) ) { if (!$useregexp) { // we are using 'normal analysis', which ignores case if (preg_match('/^'.$expectedanswer.'$/i',$studentanswer)) { $ismatch = true; } } else { if (preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer)) { $ismatch = true; } } if ($ismatch == true) { $result->correctanswer = true; } } else { if (!$useregexp) { //we are using 'normal analysis' // see if user typed in any of the wrong answers; don't worry about case if (preg_match('/^'.$expectedanswer.'$/i',$studentanswer)) { $ismatch = true; } } else { // we are using regular expressions analysis $startcode = substr($expectedanswer,0,2); switch ($startcode){ //1- check for absence of required string in $studentanswer (coded by initial '--') case "--": $expectedanswer = substr($expectedanswer,2); if (!preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer)) { $ismatch = true; } break; //2- check for code for marking wrong strings (coded by initial '++') case "++": $expectedanswer=substr($expectedanswer,2); $markit = true; //check for one or several matches if (preg_match_all('/'.$expectedanswer.'/'.$ignorecase,$studentanswer, $matches)) { $ismatch = true; $nb = count($matches[0]); $original = array(); $marked = array(); $fontStart = ''; $fontEnd = ''; for ($i = 0; $i < $nb; $i++) { array_push($original,$matches[0][$i]); array_push($marked,$fontStart.$matches[0][$i].$fontEnd); } $studentanswer = str_replace($original, $marked, $studentanswer); } break; //3- check for wrong answers belonging neither to -- nor to ++ categories default: if (preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer, $matches)) { $ismatch = true; } break; } $result->correctanswer = false; } } if ($ismatch) { $result->newpageid = $answer->jumpto; $options = new stdClass(); $options->para = false; $options->noclean = true; $result->response = format_text($answer->response, $answer->responseformat, $options); $result->answerid = $answer->id; break; // quit answer analysis immediately after a match has been found } } // We could check here to see if we have a wrong answer jump to use. if ($result->answerid == 0) { // Use the all other answers jump details if it is set up. $lastanswer = end($answers); // Double check that this is the @#wronganswer#@ answer. if (strpos($lastanswer->answer, LESSON_OTHER_ANSWERS) !== false) { $otheranswers = end($answers); $result->newpageid = $otheranswers->jumpto; $options = new stdClass(); $options->para = false; $result->response = format_text($otheranswers->response, $otheranswers->responseformat, $options); // Does this also need to do the jumpto_is_correct? if ($this->lesson->custom) { $result->correctanswer = ($otheranswers->score > 0); } $result->answerid = $otheranswers->id; } } $result->userresponse = $studentanswer; //clean student answer as it goes to output. $result->studentanswer = s($studentanswer); return $result; } public function option_description_string() { if ($this->properties->qoption) { return " - ".get_string("casesensitive", "lesson"); } return parent::option_description_string(); } public function display_answers(html_table $table) { $answers = $this->get_answers(); $options = new stdClass; $options->noclean = true; $options->para = false; $i = 1; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer, false); $cells = array(); if ($this->lesson->custom && $answer->score > 0) { // if the score is > 0, then it is correct $cells[] = ':'; } else if ($this->lesson->custom) { $cells[] = ':'; } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { // underline correct answers $cells[] = '' . get_string('answer', 'lesson') . ' ' . $i . ':' . "\n"; } else { $cells[] = ':'; } $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = format_text($answer->response, $answer->responseformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); if ($i === 1){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $i++; } return $table; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); if (isset($pagestats[$temp->pageid][$temp->useranswer])) { $pagestats[$temp->pageid][$temp->useranswer]++; } else { $pagestats[$temp->pageid][$temp->useranswer] = 1; } if (isset($pagestats[$temp->pageid]["total"])) { $pagestats[$temp->pageid]["total"]++; } else { $pagestats[$temp->pageid]["total"] = 1; } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { global $PAGE; $answers = $this->get_answers(); $formattextdefoptions = new stdClass; $formattextdefoptions->para = false; //I'll use it widely in this page foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer, false); if ($useranswer == null && $i == 0) { // I have the $i == 0 because it is easier to blast through it all at once. if (isset($pagestats[$this->properties->id])) { $stats = $pagestats[$this->properties->id]; $total = $stats["total"]; unset($stats["total"]); foreach ($stats as $valentered => $ntimes) { $data = ''; $percent = $ntimes / $total * 100; $percent = round($percent, 2); $percent .= "% ".get_string("enteredthis", "lesson"); $answerdata->answers[] = array($data, $percent); } } else { $answerdata->answers[] = array(get_string("nooneansweredthisquestion", "lesson"), " "); } $i++; } else if ($useranswer != null && ($answer->id == $useranswer->answerid || $answer == end($answers))) { // get in here when what the user entered is not one of the answers $data = ''; if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) { $percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100; $percent = round($percent, 2); $percent .= "% ".get_string("enteredthis", "lesson"); } else { $percent = get_string("nooneenteredthis", "lesson"); } $answerdata->answers[] = array($data, $percent); if ($answer->id == $useranswer->answerid) { if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = $answer->response; } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } // We have found the correct answer, do not process any more answers. $answerpage->answerdata = $answerdata; break; } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": 0"; } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } } $answerpage->answerdata = $answerdata; } return $answerpage; } /** * Make updates to the form data if required. In this case to put the all other answer data into the write section of the form. * * @param stdClass $data The form data to update. * @return stdClass The updated fom data. */ public function update_form_data(stdClass $data): stdClass { $answercount = count($this->get_answers()); // Check for other answer entry. $lastanswer = $data->{'answer_editor[' . ($answercount - 1) . ']'}; if (strpos($lastanswer, LESSON_OTHER_ANSWERS) !== false) { $data->{'answer_editor[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'answer_editor[' . ($answercount - 1) . ']'}; $data->{'response_editor[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'response_editor[' . ($answercount - 1) . ']'}; $data->{'jumpto[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'jumpto[' . ($answercount - 1) . ']'}; $data->{'score[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'score[' . ($answercount - 1) . ']'}; $data->enableotheranswers = true; // Unset the old values. unset($data->{'answer_editor[' . ($answercount - 1) . ']'}); unset($data->{'response_editor[' . ($answercount - 1) . ']'}); unset($data->{'jumpto[' . ($answercount - 1) . ']'}); unset($data->{'score[' . ($answercount - 1) . ']'}); } return $data; } } class lesson_add_page_form_shortanswer extends lesson_add_page_form_base { public $qtype = 'shortanswer'; public $qtypestring = 'shortanswer'; protected $answerformat = ''; protected $responseformat = LESSON_ANSWER_HTML; public function custom_definition() { $this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('casesensitive', 'lesson')); //oh my, this is a regex option! $this->_form->setDefault('qoption', 0); $this->_form->addHelpButton('qoption', 'casesensitive', 'lesson'); $answercount = $this->_customdata['lesson']->maxanswers; for ($i = 0; $i < $answercount; $i++) { $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1)); // Only first answer is required. $this->add_answer($i, null, ($i < 1)); $this->add_response($i); $this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE)); $this->add_score($i, null, ($i===0)?1:0); } // Other answer jump. $this->_form->addElement('header', 'wronganswer', get_string('allotheranswers', 'lesson')); $newcount = $answercount + 1; $this->_form->addElement('advcheckbox', 'enableotheranswers', get_string('enabled', 'lesson')); $this->add_response($newcount); $this->add_jumpto($newcount, get_string('allotheranswersjump', 'lesson'), LESSON_NEXTPAGE); $this->add_score($newcount, get_string('allotheranswersscore', 'lesson'), 0); } } class lesson_display_answer_form_shortanswer extends moodleform { public function definition() { global $OUTPUT, $USER; $mform = $this->_form; $contents = $this->_customdata['contents']; $hasattempt = false; $attrs = array('size'=>'50', 'maxlength'=>'200'); if (isset($this->_customdata['lessonid'])) { $lessonid = $this->_customdata['lessonid']; if (isset($USER->modattempts[$lessonid]->useranswer)) { $attrs['readonly'] = 'readonly'; $hasattempt = true; } } $placeholder = false; if (preg_match('/_____+/', $contents, $matches)) { $placeholder = $matches[0]; $contentsparts = explode( $placeholder, $contents, 2); $attrs['size'] = round(strlen($placeholder) * 1.1); } // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); if ($placeholder) { $contentsgroup = array(); $contentsgroup[] = $mform->createElement('static', '', '', $contentsparts[0]); $contentsgroup[] = $mform->createElement('text', 'answer', '', $attrs); $contentsgroup[] = $mform->createElement('static', '', '', $contentsparts[1]); $mform->addGroup($contentsgroup, '', '', '', false); } else { $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $mform->addElement('text', 'answer', get_string('youranswer', 'lesson'), $attrs); } $mform->setType('answer', PARAM_TEXT); if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } } endofbranch.php000064400000020406152267036420007537 0ustar00. /** * End of branch table * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** End of Branch page */ define("LESSON_PAGE_ENDOFBRANCH", "21"); class lesson_page_type_endofbranch extends lesson_page { protected $type = lesson_page::TYPE_STRUCTURE; protected $typeidstring = 'endofbranch'; protected $typeid = LESSON_PAGE_ENDOFBRANCH; protected $string = null; protected $jumpto = null; public function display($renderer, $attempt) { return ''; } public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function callback_on_view($canmanage, $redirect = true) { return (int) $this->redirect_to_first_answer($canmanage, $redirect); } public function redirect_to_first_answer($canmanage, $redirect) { global $USER, $PAGE; $answers = $this->get_answers(); $answer = array_shift($answers); $jumpto = $answer->jumpto; if ($jumpto == LESSON_RANDOMBRANCH) { $jumpto = lesson_unseen_branch_jump($this->lesson, $USER->id); } elseif ($jumpto == LESSON_CLUSTERJUMP) { if (!$canmanage) { $jumpto = $this->lesson->cluster_jump($this->properties->id); } else { if ($this->properties->nextpageid == 0) { $jumpto = LESSON_EOL; } else { $jumpto = $this->properties->nextpageid; } } } else if ($answer->jumpto == LESSON_NEXTPAGE) { if ($this->properties->nextpageid == 0) { $jumpto = LESSON_EOL; } else { $jumpto = $this->properties->nextpageid; } } else if ($jumpto == 0) { $jumpto = $this->properties->id; } else if ($jumpto == LESSON_PREVIOUSPAGE) { $jumpto = $this->properties->prevpageid; } if ($redirect) { redirect(new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id, 'pageid' => $jumpto))); die; } return $jumpto; } public function get_grayout() { return 1; } public function add_page_link($previd) { global $PAGE, $CFG; if ($previd != 0) { $addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_ENDOFBRANCH)); return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_ENDOFBRANCH, 'name'=>get_string('addanendofbranch', 'lesson')); } return false; } public function valid_page_and_view(&$validpages, &$pageviews) { return $this->properties->nextpageid; } /** * Creates answers within the database for this end of cluster page. Usually only ever * called when creating a new page instance. * @param object $properties * @return array */ public function create_answers($properties) { global $DB; $newanswer = new stdClass; $newanswer->lessonid = $this->lesson->id; $newanswer->pageid = $this->properties->id; $newanswer->timecreated = $this->properties->timecreated; if (isset($properties->jumpto[0])) { $newanswer->jumpto = $properties->jumpto[0]; } $newanswer->id = $DB->insert_record('lesson_answers', $newanswer); $answers = [$newanswer->id => new lesson_page_answer($newanswer)]; $this->answers = $answers; return $answers; } } class lesson_add_page_form_endofbranch extends lesson_add_page_form_base { public $qtype = LESSON_PAGE_ENDOFBRANCH; public $qtypestring = 'endofbranch'; protected $standard = false; public function custom_definition() { global $PAGE, $CFG; $mform = $this->_form; $lesson = $this->_customdata['lesson']; $jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson); $mform->addElement('hidden', 'firstpage'); $mform->setType('firstpage', PARAM_BOOL); $mform->addElement('hidden', 'qtype'); $mform->setType('qtype', PARAM_TEXT); $mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]); $mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); if (!empty($CFG->formatstringstriptags)) { $mform->setType('title', PARAM_TEXT); } else { $mform->setType('title', PARAM_CLEANHTML); } $this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes); $mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions); $mform->setType('contents_editor', PARAM_RAW); $this->add_jumpto(0); } public function construction_override($pageid, lesson $lesson) { global $DB, $CFG, $PAGE; require_sesskey(); // first get the preceeding page $timenow = time(); // the new page is not the first page (end of branch always comes after an existing page) if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) { throw new \moodle_exception('cannotfindpagerecord', 'lesson'); } // chain back up to find the (nearest branch table) $btpage = clone($page); $btpageid = $btpage->id; while (($btpage->qtype != LESSON_PAGE_BRANCHTABLE) && ($btpage->prevpageid > 0)) { $btpageid = $btpage->prevpageid; if (!$btpage = $DB->get_record("lesson_pages", array("id" => $btpageid))) { throw new \moodle_exception('cannotfindpagerecord', 'lesson'); } } if ($btpage->qtype == LESSON_PAGE_BRANCHTABLE) { $newpage = new stdClass; $newpage->lessonid = $lesson->id; $newpage->prevpageid = $pageid; $newpage->nextpageid = $page->nextpageid; $newpage->qtype = $this->qtype; $newpage->timecreated = $timenow; $newpage->title = get_string("endofbranch", "lesson"); $newpage->contents = get_string("endofbranch", "lesson"); $newpageid = $DB->insert_record("lesson_pages", $newpage); // update the linked list... $DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid)); if ($page->nextpageid) { // the new page is not the last page $DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid)); } // ..and the single "answer" $newanswer = new stdClass; $newanswer->lessonid = $lesson->id; $newanswer->pageid = $newpageid; $newanswer->timecreated = $timenow; $newanswer->jumpto = $btpageid; $newanswerid = $DB->insert_record("lesson_answers", $newanswer); $lesson->add_message(get_string('addedanendofbranch', 'lesson'), 'notifysuccess'); } else { $lesson->add_message(get_string('nobranchtablefound', 'lesson')); } redirect($CFG->wwwroot."/mod/lesson/edit.php?id=".$PAGE->cm->id); } } truefalse.php000064400000046440152267036420007266 0ustar00. /** * True/false * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** True/False question type */ define("LESSON_PAGE_TRUEFALSE", "2"); class lesson_page_type_truefalse extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeidstring = 'truefalse'; protected $typeid = LESSON_PAGE_TRUEFALSE; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function display($renderer, $attempt) { global $USER, $CFG, $PAGE; $answers = $this->get_answers(); foreach ($answers as $key => $answer) { $answers[$key] = parent::rewrite_answers_urls($answer); } shuffle($answers); $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt); $mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params); $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } public function check_answer() { global $DB, $CFG; $formattextdefoptions = new stdClass(); $formattextdefoptions->noclean = true; $formattextdefoptions->para = false; $answers = $this->get_answers(); shuffle($answers); $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents()); $mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params); $data = $mform->get_data(); require_sesskey(); $result = parent::check_answer(); if (empty($data->answerid)) { $result->noanswer = true; return $result; } $result->answerid = $data->answerid; $answer = $DB->get_record("lesson_answers", array("id" => $result->answerid), '*', MUST_EXIST); $answer = parent::rewrite_answers_urls($answer); if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { $result->correctanswer = true; } if ($this->lesson->custom) { if ($answer->score > 0) { $result->correctanswer = true; } else { $result->correctanswer = false; } } $result->newpageid = $answer->jumpto; $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions); $result->studentanswer = $result->userresponse = $answer->answer; return $result; } public function display_answers(html_table $table) { $answers = $this->get_answers(); $options = new stdClass(); $options->noclean = true; $options->para = false; $i = 1; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer); $cells = array(); if ($this->lesson->custom && $answer->score > 0) { // if the score is > 0, then it is correct $cells[] = ': \n"; } else if ($this->lesson->custom) { $cells[] = ': \n"; } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { // underline correct answers $cells[] = '' . get_string('answer', 'lesson') . " {$i}: \n"; } else { $cells[] = ': \n"; } $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = format_text($answer->response, $answer->responseformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); if ($i === 1){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $i++; } return $table; } /** * Updates the page and its answers * * @global moodle_database $DB * @global moodle_page $PAGE * @param stdClass $properties * @return bool */ public function update($properties, $context = null, $maxbytes = null) { global $DB, $PAGE; $answers = $this->get_answers(); $properties->id = $this->properties->id; $properties->lessonid = $this->lesson->id; $properties->timemodified = time(); $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id); $DB->update_record("lesson_pages", $properties); // Trigger an event: page updated. \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger(); // need to reset offset for correct and wrong responses $this->lesson->maxanswers = 2; for ($i = 0; $i < $this->lesson->maxanswers; $i++) { if (!array_key_exists($i, $this->answers)) { $this->answers[$i] = new stdClass; $this->answers[$i]->lessonid = $this->lesson->id; $this->answers[$i]->pageid = $this->id; $this->answers[$i]->timecreated = $this->timecreated; } if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) { $this->answers[$i]->answer = $properties->answer_editor[$i]['text']; $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format']; } if (!empty($properties->response_editor[$i]) && is_array($properties->response_editor[$i])) { $this->answers[$i]->response = $properties->response_editor[$i]['text']; $this->answers[$i]->responseformat = $properties->response_editor[$i]['format']; } // we don't need to check for isset here because properties called it's own isset method. if ($this->answers[$i]->answer != '') { if (isset($properties->jumpto[$i])) { $this->answers[$i]->jumpto = $properties->jumpto[$i]; } if ($this->lesson->custom && isset($properties->score[$i])) { $this->answers[$i]->score = $properties->score[$i]; } if (!isset($this->answers[$i]->id)) { $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]); } else { $DB->update_record("lesson_answers", $this->answers[$i]->properties()); } // Save files in answers and responses. $this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i], $properties->response_editor[$i]); } else if (isset($this->answers[$i]->id)) { $DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id)); unset($this->answers[$i]); } } return true; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); if ($this->properties->qoption) { $userresponse = explode(",", $temp->useranswer); foreach ($userresponse as $response) { if (isset($pagestats[$temp->pageid][$response])) { $pagestats[$temp->pageid][$response]++; } else { $pagestats[$temp->pageid][$response] = 1; } } } else { if (isset($pagestats[$temp->pageid][$temp->answerid])) { $pagestats[$temp->pageid][$temp->answerid]++; } else { $pagestats[$temp->pageid][$temp->answerid] = 1; } } if (isset($pagestats[$temp->pageid]["total"])) { $pagestats[$temp->pageid]["total"]++; } else { $pagestats[$temp->pageid]["total"] = 1; } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { $answers = $this->get_answers(); $formattextdefoptions = new stdClass(); //I'll use it widely in this page $formattextdefoptions->para = false; $formattextdefoptions->noclean = true; $formattextdefoptions->context = $answerpage->context; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer); $answertext = format_text($answer->answer, $answer->answerformat, $formattextdefoptions); $correctresponsetext = html_writer::div(get_string('correctresponse', 'lesson'), 'badge bg-success text-white'); if ($this->properties->qoption) { if ($useranswer == null) { $userresponse = array(); } else { $userresponse = explode(",", $useranswer->useranswer); } if (in_array($answer->id, $userresponse)) { // make checked $checkboxelement = ""; if (!isset($answerdata->response)) { if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions); } } if (!isset($answerdata->score)) { if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } } else { // unchecked $checkboxelement = ""; } $answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null); if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) { $data = html_writer::div($answercontent, 'text-success') . $correctresponsetext; } else { $data = $answercontent; } } else { if ($useranswer != null and $answer->id == $useranswer->answerid) { // make checked $checkboxelement = ""; if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions); } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } else { // unchecked $checkboxelement = ""; } $answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null); if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) { $data = html_writer::div($answercontent, 'text-success') . $correctresponsetext; } else { $data = $answercontent; } } if (isset($pagestats[$this->properties->id][$answer->id])) { $percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100; $percent = round($percent, 2); $percent .= "% ".get_string("checkedthisone", "lesson"); } else { $percent = get_string("noonecheckedthis", "lesson"); } $answerdata->answers[] = array($data, $percent); $answerpage->answerdata = $answerdata; } return $answerpage; } } class lesson_add_page_form_truefalse extends lesson_add_page_form_base { public $qtype = 'truefalse'; public $qtypestring = 'truefalse'; protected $answerformat = LESSON_ANSWER_HTML; protected $responseformat = LESSON_ANSWER_HTML; public function custom_definition() { $this->_form->addElement('header', 'answertitle0', get_string('correctresponse', 'lesson')); $this->add_answer(0, null, true, $this->get_answer_format()); $this->add_response(0); $this->add_jumpto(0, get_string('correctanswerjump', 'lesson'), LESSON_NEXTPAGE); $this->add_score(0, get_string('correctanswerscore', 'lesson'), 1); $this->_form->addElement('header', 'answertitle1', get_string('wrongresponse', 'lesson')); $this->add_answer(1, null, true, $this->get_answer_format()); $this->add_response(1); $this->add_jumpto(1, get_string('wronganswerjump', 'lesson'), LESSON_THISPAGE); $this->add_score(1, get_string('wronganswerscore', 'lesson'), 0); } } class lesson_display_answer_form_truefalse extends moodleform { public function definition() { global $USER, $OUTPUT; $mform = $this->_form; $answers = $this->_customdata['answers']; $lessonid = $this->_customdata['lessonid']; $contents = $this->_customdata['contents']; if (array_key_exists('attempt', $this->_customdata)) { $attempt = $this->_customdata['attempt']; } else { $attempt = new stdClass(); $attempt->answerid = null; } // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $hasattempt = false; $disabled = ''; if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) { $hasattempt = true; $disabled = array('disabled' => 'disabled'); } $options = new stdClass(); $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); $i = 0; $radiobuttons = array(); foreach ($answers as $answer) { $ansid = 'answerid'; if ($hasattempt) { $ansid = 'answer_id'; } $answer = lesson_page_type_truefalse::rewrite_answers_urls($answer); $radiobuttons[] = $mform->createElement('radio', $ansid, null, format_text($answer->answer, $answer->answerformat, $options), $answer->id, $disabled); $mform->setType($ansid, PARAM_INT); if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) { $mform->setDefault($ansid, $attempt->answerid); $mform->addElement('hidden', 'answerid', $answer->id); $mform->setType('answerid', PARAM_INT); } $i++; } $radiogroup = $mform->addGroup($radiobuttons, $ansid, '', array(''), false); $radiogroup->setAttributes(array('class' => 'answeroptiongroup')); if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } } matching.php000064400000064160152267036420007065 0ustar00. /** * Matching * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Matching question type */ define("LESSON_PAGE_MATCHING", "5"); class lesson_page_type_matching extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeid = LESSON_PAGE_MATCHING; protected $typeidstring = 'matching'; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function display($renderer, $attempt) { global $USER, $CFG, $PAGE; $mform = $this->make_answer_form($attempt); $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } protected function make_answer_form($attempt=null) { global $USER, $CFG; // don't shuffle answers (could be an option??) $getanswers = array_slice($this->get_answers(), 2); $answers = array(); foreach ($getanswers as $getanswer) { $answers[$getanswer->id] = $getanswer; } // Calculate the text for the dropdown, keyed by the non formatted version. $responses = array(); foreach ($answers as $answer) { // Get all the response. if ($answer->response != null) { $responses[trim($answer->response)] = format_text(trim($answer->response)); } } // Now shuffle the answers to randomise the order of the items in the dropdown. $responseoptions = ['' => get_string('choosedots')]; if (!empty($responses)) { $keys = array_keys($responses); shuffle($keys); foreach ($keys as $key) { $responseoptions[$key] = $responses[$key]; } } if (isset($USER->modattempts[$this->lesson->id]) && !empty($attempt->useranswer)) { $useranswers = explode(',', $attempt->useranswer); $t = 0; } else { $useranswers = array(); } $action = $CFG->wwwroot.'/mod/lesson/continue.php'; $params = array('answers'=>$answers, 'useranswers'=>$useranswers, 'responseoptions'=>$responseoptions, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents()); $mform = new lesson_display_answer_form_matching($action, $params); return $mform; } public function create_answers($properties) { global $DB, $PAGE; // now add the answers $newanswer = new stdClass; $newanswer->lessonid = $this->lesson->id; $newanswer->pageid = $this->properties->id; $newanswer->timecreated = $this->properties->timecreated; $cm = get_coursemodule_from_instance('lesson', $this->lesson->id, $this->lesson->course); $context = context_module::instance($cm->id); // Check for duplicate response format. $duplicateresponse = array(); if (is_array($properties->response_editor) && // If there are response_editors to iterate. is_array(reset($properties->response_editor))) { // And they come split into text & format array. foreach ($properties->response_editor as $response) { // Iterate over all them. $duplicateresponse[] = $response['text']; // Picking the text only. This pagetype is that way. } $properties->response_editor = $duplicateresponse; } $answers = array(); // need to add two to offset correct response and wrong response $this->lesson->maxanswers = $this->lesson->maxanswers + 2; for ($i = 0; $i < $this->lesson->maxanswers; $i++) { $answer = clone($newanswer); if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) { $answer->answer = $properties->answer_editor[$i]['text']; $answer->answerformat = $properties->answer_editor[$i]['format']; } if (!empty($properties->response_editor[$i])) { $answer->response = $properties->response_editor[$i]; $answer->responseformat = 0; } if (isset($properties->jumpto[$i])) { $answer->jumpto = $properties->jumpto[$i]; } if ($this->lesson->custom && isset($properties->score[$i])) { $answer->score = $properties->score[$i]; } if (isset($answer->answer) && $answer->answer != '') { $answer->id = $DB->insert_record("lesson_answers", $answer); $this->save_answers_files($context, $PAGE->course->maxbytes, $answer, $properties->answer_editor[$i]); $answers[$answer->id] = new lesson_page_answer($answer); } else if ($i < 2) { $answer->id = $DB->insert_record("lesson_answers", $answer); $answers[$answer->id] = new lesson_page_answer($answer); } else { break; } } $this->answers = $answers; return $answers; } public function check_answer() { global $CFG, $PAGE; $formattextdefoptions = new stdClass(); $formattextdefoptions->noclean = true; $formattextdefoptions->para = false; $result = parent::check_answer(); $mform = $this->make_answer_form(); $data = $mform->get_data(); require_sesskey(); if (!$data) { $result->inmediatejump = true; $result->newpageid = $this->properties->id; return $result; } $response = $data->response; $getanswers = $this->get_answers(); foreach ($getanswers as $key => $answer) { $getanswers[$key] = parent::rewrite_answers_urls($answer); } $correct = array_shift($getanswers); $wrong = array_shift($getanswers); $answers = array(); foreach ($getanswers as $key => $answer) { if ($answer->answer !== '' or $answer->response !== '') { $answers[$answer->id] = $answer; } } // get the user's exact responses for record keeping $hits = 0; $userresponse = array(); $result->studentanswerformat = FORMAT_HTML; foreach ($response as $id => $value) { if ($value == '') { $result->noanswer = true; return $result; } $userresponse[] = $value; // Make sure the user's answer exists in question's answer if (array_key_exists($id, $answers)) { $answer = $answers[$id]; $result->studentanswer .= '
'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions).' = '.$value; if (trim($answer->response) == trim($value)) { $hits++; } } } $result->userresponse = implode(",", $userresponse); if ($hits == count($answers)) { $result->correctanswer = true; $result->response = format_text($correct->answer, $correct->answerformat, $formattextdefoptions); $result->answerid = $correct->id; $result->newpageid = $correct->jumpto; } else { $result->correctanswer = false; $result->response = format_text($wrong->answer, $wrong->answerformat, $formattextdefoptions); $result->answerid = $wrong->id; $result->newpageid = $wrong->jumpto; } return $result; } public function option_description_string() { return get_string("firstanswershould", "lesson"); } public function display_answers(html_table $table) { $answers = $this->get_answers(); $options = new stdClass; $options->noclean = true; $options->para = false; $i = 1; $n = 0; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer); if ($n < 2) { if ($answer->answer != null) { $cells = array(); if ($n == 0) { $cells[] = ''; } else { $cells[] = ''; } $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); } if ($n == 0) { $cells = array(); $cells[] = ': '; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ': '; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); } elseif ($n == 1) { $cells = array(); $cells[] = ': '; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ': '; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); } if ($n === 0){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $n++; $i--; } else { $cells = array(); if ($this->lesson->custom && $answer->score > 0) { // if the score is > 0, then it is correct $cells[] = ': \n"; } else if ($this->lesson->custom) { $cells[] = ': \n"; } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { $cells[] = ': \n"; } else { $cells[] = ': \n"; } $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ': \n"; $cells[] = format_text($answer->response, $answer->responseformat, $options); $table->data[] = new html_table_row($cells); } $i++; } return $table; } /** * Updates the page and its answers * * @global moodle_database $DB * @global moodle_page $PAGE * @param stdClass $properties * @return bool */ public function update($properties, $context = null, $maxbytes = null) { global $DB, $PAGE; $answers = $this->get_answers(); $properties->id = $this->properties->id; $properties->lessonid = $this->lesson->id; $properties->timemodified = time(); $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id); $DB->update_record("lesson_pages", $properties); // Trigger an event: page updated. \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger(); // need to add two to offset correct response and wrong response $this->lesson->maxanswers += 2; for ($i = 0; $i < $this->lesson->maxanswers; $i++) { if (!array_key_exists($i, $this->answers)) { $this->answers[$i] = new stdClass; $this->answers[$i]->lessonid = $this->lesson->id; $this->answers[$i]->pageid = $this->id; $this->answers[$i]->timecreated = $this->timecreated; } if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) { $this->answers[$i]->answer = $properties->answer_editor[$i]['text']; $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format']; } if (!empty($properties->response_editor[$i])) { $this->answers[$i]->response = $properties->response_editor[$i]; $this->answers[$i]->responseformat = 0; } if (isset($properties->jumpto[$i])) { $this->answers[$i]->jumpto = $properties->jumpto[$i]; } if ($this->lesson->custom && isset($properties->score[$i])) { $this->answers[$i]->score = $properties->score[$i]; } // we don't need to check for isset here because properties called it's own isset method. if ($this->answers[$i]->answer != '') { if (!isset($this->answers[$i]->id)) { $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]); } else { $DB->update_record("lesson_answers", $this->answers[$i]->properties()); } // Save files in answers (no response_editor for matching questions). $this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]); } else if ($i < 2) { if (!isset($this->answers[$i]->id)) { $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]); } else { $DB->update_record("lesson_answers", $this->answers[$i]->properties()); } // Save files in answers (no response_editor for matching questions). $this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]); } else if (isset($this->answers[$i]->id)) { $DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id)); unset($this->answers[$i]); } } return true; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); if ($temp->correct) { if (isset($pagestats[$temp->pageid]["correct"])) { $pagestats[$temp->pageid]["correct"]++; } else { $pagestats[$temp->pageid]["correct"] = 1; } } if (isset($pagestats[$temp->pageid]["total"])) { $pagestats[$temp->pageid]["total"]++; } else { $pagestats[$temp->pageid]["total"] = 1; } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { $answers = array(); foreach ($this->get_answers() as $answer) { $answers[$answer->id] = $answer; } $formattextdefoptions = new stdClass; $formattextdefoptions->para = false; //I'll use it widely in this page foreach ($answers as $answer) { if ($n == 0 && $useranswer != null && $useranswer->correct) { if ($answer->response == null && $useranswer != null) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = $answer->response; } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } else { $answerdata->score = get_string("receivedcredit", "lesson"); } } elseif ($n == 1 && $useranswer != null && !$useranswer->correct) { if ($answer->response == null && $useranswer != null) { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } else { $answerdata->response = $answer->response; } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } elseif ($n > 1) { $data = ''; $data .= strip_tags(format_string($answer->answer)) . ' '; if ($useranswer != null) { $userresponse = explode(",", $useranswer->useranswer); $data .= ''; $data .= ""; } else { $data .= ''; $data .= ""; } if ($n == 2) { if (isset($pagestats[$this->properties->id])) { if (!array_key_exists('correct', $pagestats[$this->properties->id])) { $pagestats[$this->properties->id]["correct"] = 0; } $percent = $pagestats[$this->properties->id]["correct"] / $pagestats[$this->properties->id]["total"] * 100; $percent = round($percent, 2); $percent .= "% ".get_string("answeredcorrectly", "lesson"); } else { $percent = get_string("nooneansweredthisquestion", "lesson"); } } else { $percent = ''; } $answerdata->answers[] = array($data, $percent); $i++; } $n++; $answerpage->answerdata = $answerdata; } return $answerpage; } public function get_jumps() { global $DB; // The jumps for matching question type are stored in the 1st and 2nd answer record. $jumps = array(); if ($answers = $DB->get_records("lesson_answers", array("lessonid" => $this->lesson->id, "pageid" => $this->properties->id), 'id', '*', 0, 2)) { foreach ($answers as $answer) { $jumps[] = $this->get_jump_name($answer->jumpto); } } else { $jumps[] = $this->get_jump_name($this->properties->nextpageid); } return $jumps; } } class lesson_add_page_form_matching extends lesson_add_page_form_base { public $qtype = 'matching'; public $qtypestring = 'matching'; protected $answerformat = LESSON_ANSWER_HTML; protected $responseformat = ''; public function custom_definition() { $this->_form->addElement('header', 'correctresponse', get_string('correctresponse', 'lesson')); $this->_form->addElement('editor', 'answer_editor[0]', get_string('correctresponse', 'lesson'), array('rows' => '4', 'columns' => '80'), array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes'])); $this->_form->setType('answer_editor[0]', PARAM_RAW); $this->_form->setDefault('answer_editor[0]', array('text' => '', 'format' => FORMAT_HTML)); $this->add_jumpto(0, get_string('correctanswerjump','lesson'), LESSON_NEXTPAGE); $this->add_score(0, get_string("correctanswerscore", "lesson"), 1); $this->_form->addElement('header', 'wrongresponse', get_string('wrongresponse', 'lesson')); $this->_form->addElement('editor', 'answer_editor[1]', get_string('wrongresponse', 'lesson'), array('rows' => '4', 'columns' => '80'), array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes'])); $this->_form->setType('answer_editor[1]', PARAM_RAW); $this->_form->setDefault('answer_editor[1]', array('text' => '', 'format' => FORMAT_HTML)); $this->add_jumpto(1, get_string('wronganswerjump','lesson'), LESSON_THISPAGE); $this->add_score(1, get_string("wronganswerscore", "lesson"), 0); for ($i = 2; $i < $this->_customdata['lesson']->maxanswers+2; $i++) { $this->_form->addElement('header', 'matchingpair'.($i-1), get_string('matchingpair', 'lesson', $i-1)); $this->add_answer($i, null, ($i < 4), LESSON_ANSWER_HTML); $required = ($i < 4); $label = get_string('matchesanswer','lesson'); $count = $i; $this->_form->addElement('text', 'response_editor['.$count.']', $label, array('size'=>'50')); $this->_form->setType('response_editor['.$count.']', PARAM_NOTAGS); $this->_form->setDefault('response_editor['.$count.']', ''); if ($required) { $this->_form->addRule('response_editor['.$count.']', get_string('required'), 'required', null, 'client'); } } } } class lesson_display_answer_form_matching extends moodleform { public function definition() { global $USER, $OUTPUT, $PAGE; $mform = $this->_form; $answers = $this->_customdata['answers']; $useranswers = $this->_customdata['useranswers']; $responseoptions = $this->_customdata['responseoptions']; $lessonid = $this->_customdata['lessonid']; $contents = $this->_customdata['contents']; // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $hasattempt = false; $disabled = ''; if (isset($useranswers) && !empty($useranswers)) { $hasattempt = true; $disabled = array('disabled' => 'disabled'); } $options = new stdClass; $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); $i = 0; foreach ($answers as $answer) { $mform->addElement('html', '
'); if ($answer->response != null) { $responseid = 'response['.$answer->id.']'; if ($hasattempt) { $responseid = 'response_'.$answer->id; $mform->addElement('hidden', 'response['.$answer->id.']', htmlspecialchars($useranswers[$i], ENT_COMPAT)); // Temporary fixed until MDL-38885 gets integrated $mform->setType('response', PARAM_TEXT); } $answer = lesson_page_type_matching::rewrite_answers_urls($answer); $mform->addElement('select', $responseid, format_text($answer->answer,$answer->answerformat,$options), $responseoptions, $disabled); $mform->setType($responseid, PARAM_TEXT); if ($hasattempt) { $mform->setDefault($responseid, htmlspecialchars(trim($useranswers[$i]), ENT_COMPAT)); } else { $mform->setDefault($responseid, 'answeroption'); } } $mform->addElement('html', '
'); $i++; } if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } } essay.php000064400000043213152267036420006413 0ustar00. /** * Essay * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Essay question type */ define("LESSON_PAGE_ESSAY", "10"); class lesson_page_type_essay extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeidstring = 'essay'; protected $typeid = LESSON_PAGE_ESSAY; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } /** * Unserialize attempt useranswer and add missing responseformat if needed * for compatibility with old records. * * @param string $useranswer serialized object * @return object */ public static function extract_useranswer($useranswer) { $essayinfo = unserialize_object($useranswer); if (!isset($essayinfo->responseformat)) { $essayinfo->response = text_to_html($essayinfo->response ?? '', false, false); $essayinfo->responseformat = FORMAT_HTML; } return $essayinfo; } public function display($renderer, $attempt) { global $PAGE, $CFG, $USER; $context = context_module::instance($PAGE->cm->id); $options = array( 'contents' => $this->get_contents(), 'lessonid' => $this->lesson->id, 'attemptid' => $attempt ? $attempt->id : null, 'editoroptions' => array( 'maxbytes' => $PAGE->course->maxbytes, 'context' => $context, 'noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'enable_filemanagement' => false ) ); $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options); $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; if (isset($USER->modattempts[$this->lesson->id])) { $essayinfo = self::extract_useranswer($attempt->useranswer); $data->answer = $essayinfo->answer; } $data = file_prepare_standard_editor($data, 'answer', $options['editoroptions'], $context, 'mod_lesson', 'essay_answers'); $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } public function create_answers($properties) { global $DB; // now add the answers $newanswer = new stdClass; $newanswer->lessonid = $this->lesson->id; $newanswer->pageid = $this->properties->id; $newanswer->timecreated = $this->properties->timecreated; if (isset($properties->jumpto[0])) { $newanswer->jumpto = $properties->jumpto[0]; } if (isset($properties->score[0])) { $newanswer->score = $properties->score[0]; } $newanswer->id = $DB->insert_record("lesson_answers", $newanswer); $answers = array($newanswer->id => new lesson_page_answer($newanswer)); $this->answers = $answers; return $answers; } /** * Overridden function * * @param object $attempt * @param object $result * @return array */ public function on_after_write_attempt($attempt, $result) { global $PAGE; if ($formdata = $result->postdata) { // Save any linked files if we are using an editor. $editoroptions = array( 'maxbytes' => $PAGE->course->maxbytes, 'context' => context_module::instance($PAGE->cm->id), 'noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'enable_filemanagement' => false, ); $formdata = file_postupdate_standard_editor($formdata, 'answer', $editoroptions, $editoroptions['context'], 'mod_lesson', 'essay_answers', $attempt->id); // Update the student response to have the modified link. $useranswer = unserialize_object($attempt->useranswer); $useranswer->answer = $formdata->answer; $useranswer->answerformat = $formdata->answerformat; $attempt->useranswer = serialize($useranswer); $result->studentanswer = $formdata->answer; $result->studentanswerformat = $formdata->answerformat; return [$attempt, $result]; } return parent::on_after_write_attempt($attempt, $result); } /** * Custom formats the answer to display * * @param string $answer * @param context $context * @param int $answerformat * @param array $options Optional param for additional options. * @return string Returns formatted string */ public function format_answer($answer, $context, $answerformat, $options = []) { $answer = file_rewrite_pluginfile_urls($answer, 'pluginfile.php', $context->id, 'mod_lesson', 'essay_answers', $options->attemptid); return parent::format_answer($answer, $context, $answerformat, $options); } public function check_answer() { global $PAGE, $CFG; $result = parent::check_answer(); $result->isessayquestion = true; $context = context_module::instance($PAGE->cm->id); $options = array( 'contents' => $this->get_contents(), 'editoroptions' => array( 'maxbytes' => $PAGE->course->maxbytes, 'context' => $context, 'noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'enable_filemanagement' => false, ) ); $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options); $data = $mform->get_data(); require_sesskey(); if (!$data) { $result->inmediatejump = true; $result->newpageid = $this->properties->id; return $result; } if (is_array($data->answer_editor) && strlen($data->answer_editor['text'])) { $studentanswer = $data->answer_editor['text']; // Will be reset later. $studentanswerformat = $data->answer_editor['format']; // Will be reset later. } else { $studentanswer = isset($data->answer) ? $data->answer : ''; $studentanswerformat = FORMAT_HTML; } if (trim($studentanswer) === '') { $result->noanswer = true; return $result; } $answers = $this->get_answers(); foreach ($answers as $answer) { $result->answerid = $answer->id; $result->newpageid = $answer->jumpto; } $userresponse = new stdClass; $userresponse->sent=0; $userresponse->graded = 0; $userresponse->score = 0; $userresponse->answer = $studentanswer; $userresponse->answerformat = $studentanswerformat; $userresponse->response = ''; $userresponse->responseformat = FORMAT_HTML; $result->userresponse = serialize($userresponse); $result->studentanswerformat = $studentanswerformat; $result->studentanswer = $studentanswer; $result->postdata = $data; return $result; } public function update($properties, $context = null, $maxbytes = null) { global $DB, $PAGE; $answers = $this->get_answers(); $properties->id = $this->properties->id; $properties->lessonid = $this->lesson->id; $properties->timemodified = time(); $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id); $DB->update_record("lesson_pages", $properties); // Trigger an event: page updated. \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger(); if (!array_key_exists(0, $this->answers)) { $this->answers[0] = new stdClass; $this->answers[0]->lessonid = $this->lesson->id; $this->answers[0]->pageid = $this->id; $this->answers[0]->timecreated = $this->timecreated; } if (isset($properties->jumpto[0])) { $this->answers[0]->jumpto = $properties->jumpto[0]; } if (isset($properties->score[0])) { $this->answers[0]->score = $properties->score[0]; } if (!isset($this->answers[0]->id)) { $this->answers[0]->id = $DB->insert_record("lesson_answers", $this->answers[0]); } else { $DB->update_record("lesson_answers", $this->answers[0]->properties()); } return true; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); $essayinfo = self::extract_useranswer($temp->useranswer); if ($essayinfo->graded) { if (isset($pagestats[$temp->pageid])) { $essaystats = $pagestats[$temp->pageid]; $essaystats->totalscore += $essayinfo->score; $essaystats->total++; $pagestats[$temp->pageid] = $essaystats; } else { $essaystats = new stdClass(); $essaystats->totalscore = $essayinfo->score; $essaystats->total = 1; $pagestats[$temp->pageid] = $essaystats; } } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { global $PAGE, $DB; $formattextdefoptions = new stdClass(); $formattextdefoptions->noclean = true; $formattextdefoptions->para = false; $formattextdefoptions->context = $answerpage->context; $answers = $this->get_answers(); $context = context_module::instance($PAGE->cm->id); foreach ($answers as $answer) { $hasattempts = $DB->record_exists('lesson_attempts', ['answerid' => $answer->id]); if ($useranswer != null) { $essayinfo = self::extract_useranswer($useranswer->useranswer); $essayinfo->answer = file_rewrite_pluginfile_urls($essayinfo->answer, 'pluginfile.php', $context->id, 'mod_lesson', 'essay_answers', $useranswer->id); if ($essayinfo->response == null) { $answerdata->response = get_string("nocommentyet", "lesson"); } else { $essayinfo->response = file_rewrite_pluginfile_urls($essayinfo->response, 'pluginfile.php', $answerpage->context->id, 'mod_lesson', 'essay_responses', $useranswer->id); $answerdata->response = format_text($essayinfo->response, $essayinfo->responseformat, $formattextdefoptions); } if (isset($pagestats[$this->properties->id])) { $percent = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total * 100; $percent = round($percent, 2); $percent = get_string("averagescore", "lesson").": ". $percent ."%"; } else { // dont think this should ever be reached.... $percent = get_string("nooneansweredthisquestion", "lesson"); } if ($essayinfo->graded) { if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": " . $essayinfo->score; } elseif ($essayinfo->score) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } else { $answerdata->score = get_string("havenotgradedyet", "lesson"); } } else { $essayinfo = new stdClass(); if ($hasattempts && has_capability('mod/lesson:grade', $answerpage->context)) { $essayinfo->answer = html_writer::link(new moodle_url("/mod/lesson/essay.php", ['id' => $PAGE->cm->id]), get_string("viewessayanswers", "lesson")); } else { $essayinfo->answer = ""; } $essayinfo->answerformat = null; } // The essay question has been graded. if (isset($pagestats[$this->properties->id])) { $avescore = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total; $avescore = round($avescore, 2); $avescore = get_string("averagescore", "lesson").": ". $avescore ; } else { $avescore = $hasattempts ? get_string("essaynotgradedyet", "lesson") : get_string("nooneansweredthisquestion", "lesson"); } // This is the student's answer so it should be cleaned. $answerdata->answers[] = array(format_text($essayinfo->answer, $essayinfo->answerformat, array('para' => true, 'context' => $answerpage->context)), $avescore); $answerpage->answerdata = $answerdata; } return $answerpage; } public function is_unanswered($nretakes) { global $DB, $USER; if (!$DB->count_records("lesson_attempts", array('pageid'=>$this->properties->id, 'userid'=>$USER->id, 'retry'=>$nretakes))) { return true; } return false; } public function requires_manual_grading() { return true; } public function get_earnedscore($answers, $attempt) { $essayinfo = self::extract_useranswer($attempt->useranswer); return $essayinfo->score; } } class lesson_add_page_form_essay extends lesson_add_page_form_base { public $qtype = 'essay'; public $qtypestring = 'essay'; public function custom_definition() { $this->add_jumpto(0); $this->add_score(0, null, 1); } } class lesson_display_answer_form_essay extends moodleform { public function definition() { global $USER, $OUTPUT; $mform = $this->_form; $contents = $this->_customdata['contents']; $editoroptions = $this->_customdata['editoroptions']; $hasattempt = false; $attrs = ''; $useranswer = ''; $useranswerraw = ''; if (isset($this->_customdata['lessonid'])) { $lessonid = $this->_customdata['lessonid']; if (isset($USER->modattempts[$lessonid]->useranswer) && !empty($USER->modattempts[$lessonid]->useranswer)) { $attrs = array('disabled' => 'disabled'); $hasattempt = true; $useranswertemp = lesson_page_type_essay::extract_useranswer($USER->modattempts[$lessonid]->useranswer); $useranswer = htmlspecialchars_decode($useranswertemp->answer, ENT_QUOTES); $useranswerraw = $useranswertemp->answer; } } // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $options = new stdClass; $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); if ($hasattempt) { $mform->addElement('hidden', 'answer', $useranswerraw); $mform->setType('answer', PARAM_RAW); $mform->addElement('html', $OUTPUT->container(get_string('youranswer', 'lesson'), 'youranswer')); $useranswer = file_rewrite_pluginfile_urls($useranswer, 'pluginfile.php', $editoroptions['context']->id, 'mod_lesson', 'essay_answers', $this->_customdata['attemptid']); $mform->addElement('html', $OUTPUT->container($useranswer, 'reviewessay')); $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $mform->addElement('editor', 'answer_editor', get_string('youranswer', 'lesson'), null, $editoroptions); $mform->setType('answer_editor', PARAM_RAW); $this->add_action_buttons(null, get_string("submit", "lesson")); } } } branchtable.php000064400000034637152267036420007546 0ustar00. /** * Branch Table * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Branch Table page */ define("LESSON_PAGE_BRANCHTABLE", "20"); class lesson_page_type_branchtable extends lesson_page { protected $type = lesson_page::TYPE_STRUCTURE; protected $typeid = LESSON_PAGE_BRANCHTABLE; protected $typeidstring = 'branchtable'; protected $string = null; protected $jumpto = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } /** * Gets an array of the jumps used by the answers of this page * * @return array */ public function get_jumps() { global $DB; $jumps = array(); $params = array ("lessonid" => $this->lesson->id, "pageid" => $this->properties->id); if ($answers = $this->get_answers()) { foreach ($answers as $answer) { if ($answer->answer === '') { // show only jumps for real branches (==have description) continue; } $jumps[] = $this->get_jump_name($answer->jumpto); } } else { // We get here is the lesson was created on a Moodle 1.9 site and // the lesson contains question pages without any answers. $jumps[] = $this->get_jump_name($this->properties->nextpageid); } return $jumps; } public static function get_jumptooptions($firstpage, lesson $lesson) { global $DB, $PAGE; $jump = array(); $jump[0] = get_string("thispage", "lesson"); $jump[LESSON_NEXTPAGE] = get_string("nextpage", "lesson"); $jump[LESSON_PREVIOUSPAGE] = get_string("previouspage", "lesson"); $jump[LESSON_EOL] = get_string("endoflesson", "lesson"); $jump[LESSON_UNSEENBRANCHPAGE] = get_string("unseenpageinbranch", "lesson"); $jump[LESSON_RANDOMPAGE] = get_string("randompageinbranch", "lesson"); $jump[LESSON_RANDOMBRANCH] = get_string("randombranch", "lesson"); if (!$firstpage) { if (!$apageid = $DB->get_field("lesson_pages", "id", array("lessonid" => $lesson->id, "prevpageid" => 0))) { throw new \moodle_exception('cannotfindfirstpage', 'lesson'); } while (true) { if ($apageid) { $title = $DB->get_field("lesson_pages", "title", array("id" => $apageid)); $jump[$apageid] = $title; $apageid = $DB->get_field("lesson_pages", "nextpageid", array("id" => $apageid)); } else { // last page reached break; } } } return $jump; } public function get_idstring() { return $this->typeidstring; } public function display($renderer, $attempt) { global $PAGE, $CFG; $output = ''; $options = new stdClass; $options->para = false; $options->noclean = true; if ($this->lesson->slideshow) { $output .= $renderer->slideshow_start($this->lesson); } // The heading level depends on whether the theme's activity header displays a heading (usually the activity name). $headinglevel = $PAGE->activityheader->get_heading_level(); $output .= $renderer->heading(format_string($this->properties->title), $headinglevel); $output .= $renderer->box($this->get_contents(), 'contents'); $buttons = array(); $i = 0; foreach ($this->get_answers() as $answer) { if ($answer->answer === '') { // not a branch! continue; } $params = array(); $params['id'] = $PAGE->cm->id; $params['pageid'] = $this->properties->id; $params['sesskey'] = sesskey(); $params['jumpto'] = $answer->jumpto; $url = new moodle_url('/mod/lesson/continue.php', $params); $buttons[] = $renderer->single_button($url, strip_tags(format_text($answer->answer, FORMAT_MOODLE, $options))); $i++; } // Set the orientation if ($this->properties->layout) { $buttonshtml = $renderer->box(implode("\n", $buttons), 'branchbuttoncontainer horizontal'); } else { $buttonshtml = $renderer->box(implode("\n", $buttons), 'branchbuttoncontainer vertical'); } $output .= $buttonshtml; if ($this->lesson->slideshow) { $output .= $renderer->slideshow_end(); } // Trigger an event: content page viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id ); $event = \mod_lesson\event\content_page_viewed::create($eventparams); $event->trigger(); return $output; } public function check_answer() { global $USER, $DB, $PAGE, $CFG; $result = parent::check_answer(); require_sesskey(); $newpageid = optional_param('jumpto', null, PARAM_INT); // going to insert into lesson_branch if ($newpageid == LESSON_RANDOMBRANCH) { $branchflag = 1; } else { $branchflag = 0; } if ($grades = $DB->get_records("lesson_grades", array("lessonid" => $this->lesson->id, "userid" => $USER->id), "grade DESC")) { $retries = count($grades); } else { $retries = 0; } // First record this page in lesson_branch. This record may be needed by lesson_unseen_branch_jump. $branch = new stdClass; $branch->lessonid = $this->lesson->id; $branch->userid = $USER->id; $branch->pageid = $this->properties->id; $branch->retry = $retries; $branch->flag = $branchflag; $branch->timeseen = time(); $branch->nextpageid = 0; // Next page id will be set later. $branch->id = $DB->insert_record("lesson_branch", $branch); // this is called when jumping to random from a branch table $context = context_module::instance($PAGE->cm->id); if($newpageid == LESSON_UNSEENBRANCHPAGE) { if (has_capability('mod/lesson:manage', $context)) { $newpageid = LESSON_NEXTPAGE; } else { $newpageid = lesson_unseen_question_jump($this->lesson, $USER->id, $this->properties->id); // this may return 0 } } // convert jumpto page into a proper page id if ($newpageid == 0) { $newpageid = $this->properties->id; } elseif ($newpageid == LESSON_NEXTPAGE) { if (!$newpageid = $this->nextpageid) { // no nextpage go to end of lesson $newpageid = LESSON_EOL; } } elseif ($newpageid == LESSON_PREVIOUSPAGE) { $newpageid = $this->prevpageid; } elseif ($newpageid == LESSON_RANDOMPAGE) { $newpageid = lesson_random_question_jump($this->lesson, $this->properties->id); } elseif ($newpageid == LESSON_RANDOMBRANCH) { $newpageid = lesson_unseen_branch_jump($this->lesson, $USER->id); } // Update record to set nextpageid. $branch->nextpageid = $newpageid; $DB->update_record("lesson_branch", $branch); // This will force to redirect to the newpageid. $result->inmediatejump = true; $result->newpageid = $newpageid; return $result; } public function display_answers(html_table $table) { $answers = $this->get_answers(); $options = new stdClass; $options->noclean = true; $options->para = false; $i = 1; foreach ($answers as $answer) { if ($answer->answer === '') { // not a branch! continue; } $cells = array(); $cells[] = ': '; $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ': '; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); if ($i === 1){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $i++; } return $table; } public function get_grayout() { return 1; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { $answers = $this->get_answers(); $formattextdefoptions = new stdClass; $formattextdefoptions->para = false; //I'll use it widely in this page $formattextdefoptions->context = $answerpage->context; foreach ($answers as $answer) { $data = "id\" " . "value=\"".s(strip_tags(format_text($answer->answer, FORMAT_MOODLE, $formattextdefoptions)))."\" " . "disabled=\"disabled\"> "; $data .= get_string('jumpsto', 'lesson', $this->get_jump_name($answer->jumpto)); $answerdata->answers[] = array($data, ""); $answerpage->answerdata = $answerdata; } return $answerpage; } public function update($properties, $context = null, $maxbytes = null) { if (empty($properties->display)) { $properties->display = '0'; } if (empty($properties->layout)) { $properties->layout = '0'; } return parent::update($properties); } public function add_page_link($previd) { global $PAGE, $CFG; $addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'qtype'=>LESSON_PAGE_BRANCHTABLE)); return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_BRANCHTABLE, 'name'=>get_string('addabranchtable', 'lesson')); } protected function get_displayinmenublock() { return true; } public function is_unseen($param) { global $USER, $DB; if (is_array($param)) { $seenpages = $param; $branchpages = $this->lesson->get_sub_pages_of($this->properties->id, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH)); foreach ($branchpages as $branchpage) { if (array_key_exists($branchpage->id, $seenpages)) { // check if any of the pages have been viewed return false; } } return true; } else { $nretakes = $param; if (!$DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id, "retry"=>$nretakes))) { return true; } return false; } } } class lesson_add_page_form_branchtable extends lesson_add_page_form_base { public $qtype = LESSON_PAGE_BRANCHTABLE; public $qtypestring = 'branchtable'; protected $standard = false; public function custom_definition() { global $PAGE, $CFG; $mform = $this->_form; $lesson = $this->_customdata['lesson']; $firstpage = optional_param('firstpage', false, PARAM_BOOL); $jumptooptions = lesson_page_type_branchtable::get_jumptooptions($firstpage, $lesson); if ($this->_customdata['edit']) { $mform->setDefault('qtypeheading', get_string('editbranchtable', 'lesson')); } else { $mform->setDefault('qtypeheading', get_string('addabranchtable', 'lesson')); } $mform->addElement('hidden', 'firstpage'); $mform->setType('firstpage', PARAM_BOOL); $mform->setDefault('firstpage', $firstpage); $mform->addElement('hidden', 'qtype'); $mform->setType('qtype', PARAM_INT); $mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]); $mform->addRule('title', null, 'required', null, 'client'); $mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); if (!empty($CFG->formatstringstriptags)) { $mform->setType('title', PARAM_TEXT); } else { $mform->setType('title', PARAM_CLEANHTML); } $this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes); $mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions); $mform->setType('contents_editor', PARAM_RAW); $mform->addElement('checkbox', 'layout', null, get_string("arrangebuttonshorizontally", "lesson")); $mform->setDefault('layout', true); $mform->addElement('checkbox', 'display', null, get_string("displayinleftmenu", "lesson")); $mform->setDefault('display', true); for ($i = 0; $i < $lesson->maxanswers; $i++) { $mform->addElement('header', 'headeranswer'.$i, get_string('branch', 'lesson').' '.($i+1)); $this->add_answer($i, get_string("description", "lesson"), $i == 0); $mform->addElement('select', 'jumpto['.$i.']', get_string("jump", "lesson"), $jumptooptions); if ($i === 0) { $mform->setDefault('jumpto['.$i.']', 0); } else { $mform->setDefault('jumpto['.$i.']', LESSON_NEXTPAGE); } } } } endofcluster.php000064400000016312152267036420007764 0ustar00. /** * End of cluster * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** End of Cluster page */ define("LESSON_PAGE_ENDOFCLUSTER", "31"); class lesson_page_type_endofcluster extends lesson_page { protected $type = lesson_page::TYPE_STRUCTURE; protected $typeidstring = 'endofcluster'; protected $typeid = LESSON_PAGE_ENDOFCLUSTER; protected $string = null; protected $jumpto = null; public function display($renderer, $attempt) { return ''; } public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function callback_on_view($canmanage, $redirect = true) { return (int) $this->redirect_to_next_page($canmanage, $redirect); } public function redirect_to_next_page($canmanage, $redirect) { global $PAGE; if ($this->properties->nextpageid == 0) { $nextpageid = LESSON_EOL; } else { $nextpageid = $this->properties->nextpageid; } if ($redirect) { redirect(new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id, 'pageid' => $nextpageid))); die; } return $nextpageid; } public function get_grayout() { return 1; } public function override_next_page() { global $DB; $jump = $DB->get_field("lesson_answers", "jumpto", array("pageid" => $this->properties->id, "lessonid" => $this->lesson->id)); if ($jump == LESSON_NEXTPAGE) { if ($this->properties->nextpageid == 0) { return LESSON_EOL; } else { return $this->properties->nextpageid; } } else { return $jump; } } public function add_page_link($previd) { global $PAGE, $CFG; if ($previd != 0) { $addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_ENDOFCLUSTER)); return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_ENDOFCLUSTER, 'name'=>get_string('addendofcluster', 'lesson')); } return false; } public function valid_page_and_view(&$validpages, &$pageviews) { return $this->properties->nextpageid; } /** * Creates answers within the database for this end of cluster page. Usually only ever * called when creating a new page instance. * @param object $properties * @return array */ public function create_answers($properties) { global $DB; $newanswer = new stdClass; $newanswer->lessonid = $this->lesson->id; $newanswer->pageid = $this->properties->id; $newanswer->timecreated = $this->properties->timecreated; if (isset($properties->jumpto[0])) { $newanswer->jumpto = $properties->jumpto[0]; } $newanswer->id = $DB->insert_record('lesson_answers', $newanswer); $answers = [$newanswer->id => new lesson_page_answer($newanswer)]; $this->answers = $answers; return $answers; } } class lesson_add_page_form_endofcluster extends lesson_add_page_form_base { public $qtype = LESSON_PAGE_ENDOFCLUSTER; public $qtypestring = 'endofcluster'; protected $standard = false; public function custom_definition() { global $PAGE, $CFG; $mform = $this->_form; $lesson = $this->_customdata['lesson']; $jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson); $mform->addElement('hidden', 'firstpage'); $mform->setType('firstpage', PARAM_BOOL); $mform->addElement('hidden', 'qtype'); $mform->setType('qtype', PARAM_TEXT); $mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]); $mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); if (!empty($CFG->formatstringstriptags)) { $mform->setType('title', PARAM_TEXT); } else { $mform->setType('title', PARAM_CLEANHTML); } $this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes); $mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions); $mform->setType('contents_editor', PARAM_RAW); $this->add_jumpto(0); } public function construction_override($pageid, lesson $lesson) { global $CFG, $PAGE, $DB; require_sesskey(); $timenow = time(); // the new page is not the first page (end of cluster always comes after an existing page) if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) { throw new \moodle_exception('cannotfindpages', 'lesson'); } // could put code in here to check if the user really can insert an end of cluster $newpage = new stdClass; $newpage->lessonid = $lesson->id; $newpage->prevpageid = $pageid; $newpage->nextpageid = $page->nextpageid; $newpage->qtype = $this->qtype; $newpage->timecreated = $timenow; $newpage->title = get_string("endofclustertitle", "lesson"); $newpage->contents = get_string("endofclustertitle", "lesson"); $newpageid = $DB->insert_record("lesson_pages", $newpage); // update the linked list... $DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid)); if ($page->nextpageid) { // the new page is not the last page $DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid)); } // ..and the single "answer" $newanswer = new stdClass; $newanswer->lessonid = $lesson->id; $newanswer->pageid = $newpageid; $newanswer->timecreated = $timenow; $newanswer->jumpto = LESSON_NEXTPAGE; $newanswerid = $DB->insert_record("lesson_answers", $newanswer); $lesson->add_message(get_string('addedendofcluster', 'lesson'), 'notifysuccess'); redirect($CFG->wwwroot.'/mod/lesson/edit.php?id='.$PAGE->cm->id); } } cluster.php000064400000017167152267036420006761 0ustar00. /** * Cluster * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Start of Cluster page */ define("LESSON_PAGE_CLUSTER", "30"); class lesson_page_type_cluster extends lesson_page { protected $type = lesson_page::TYPE_STRUCTURE; protected $typeidstring = 'cluster'; protected $typeid = LESSON_PAGE_CLUSTER; protected $string = null; protected $jumpto = null; public function display($renderer, $attempt) { return ''; } public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function get_grayout() { return 1; } public function callback_on_view($canmanage, $redirect = true) { global $USER; if (!$canmanage) { // Get the next page in the lesson cluster jump return (int) $this->lesson->cluster_jump($this->properties->id); } else { // get the next page return (int) $this->properties->nextpageid; } } public function override_next_page() { global $USER; return $this->lesson->cluster_jump($this->properties->id); } public function add_page_link($previd) { global $PAGE, $CFG; $addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_CLUSTER)); return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_CLUSTER, 'name'=>get_string('addcluster', 'lesson')); } public function valid_page_and_view(&$validpages, &$pageviews) { $validpages[$this->properties->id] = 1; // add the cluster page as a valid page foreach ($this->lesson->get_sub_pages_of($this->properties->id, array(LESSON_PAGE_ENDOFCLUSTER)) as $subpage) { if (in_array($subpage->id, $pageviews)) { unset($pageviews[array_search($subpage->id, $pageviews)]); // remove it // since the user did see one page in the cluster, add the cluster pageid to the viewedpageids if (!in_array($this->properties->id, $pageviews)) { $pageviews[] = $this->properties->id; } } } return $this->properties->nextpageid; } /** * Creates answers within the database for this cluster page. Usually only ever * called when creating a new page instance. * @param object $properties * @return array */ public function create_answers($properties) { global $DB; $newanswer = new stdClass; $newanswer->lessonid = $this->lesson->id; $newanswer->pageid = $this->properties->id; $newanswer->timecreated = $this->properties->timecreated; if (isset($properties->jumpto[0])) { $newanswer->jumpto = $properties->jumpto[0]; } $newanswer->id = $DB->insert_record('lesson_answers', $newanswer); $answers = [$newanswer->id => new lesson_page_answer($newanswer)]; $this->answers = $answers; return $answers; } } class lesson_add_page_form_cluster extends lesson_add_page_form_base { public $qtype = LESSON_PAGE_CLUSTER; public $qtypestring = 'cluster'; protected $standard = false; public function custom_definition() { global $PAGE, $CFG; $mform = $this->_form; $lesson = $this->_customdata['lesson']; $jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson); $mform->addElement('hidden', 'firstpage'); $mform->setType('firstpage', PARAM_BOOL); $mform->addElement('hidden', 'qtype'); $mform->setType('qtype', PARAM_TEXT); $mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]); $mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); if (!empty($CFG->formatstringstriptags)) { $mform->setType('title', PARAM_TEXT); } else { $mform->setType('title', PARAM_CLEANHTML); } $this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes); $mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions); $mform->setType('contents_editor', PARAM_RAW); $this->add_jumpto(0); } public function construction_override($pageid, lesson $lesson) { global $PAGE, $CFG, $DB; require_sesskey(); $timenow = time(); if ($pageid == 0) { if ($lesson->has_pages()) { if (!$page = $DB->get_record("lesson_pages", array("prevpageid" => 0, "lessonid" => $lesson->id))) { throw new \moodle_exception('cannotfindpagerecord', 'lesson'); } } else { // This is the ONLY page $page = new stdClass; $page->id = 0; } } else { if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) { throw new \moodle_exception('cannotfindpagerecord', 'lesson'); } } $newpage = new stdClass; $newpage->lessonid = $lesson->id; $newpage->prevpageid = $pageid; if ($pageid != 0) { $newpage->nextpageid = $page->nextpageid; } else { $newpage->nextpageid = $page->id; } $newpage->qtype = $this->qtype; $newpage->timecreated = $timenow; $newpage->title = get_string("clustertitle", "lesson"); $newpage->contents = get_string("clustertitle", "lesson"); $newpageid = $DB->insert_record("lesson_pages", $newpage); // update the linked list... if ($pageid != 0) { $DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid)); } if ($pageid == 0) { $page->nextpageid = $page->id; } if ($page->nextpageid) { // the new page is not the last page $DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid)); } // ..and the single "answer" $newanswer = new stdClass; $newanswer->lessonid = $lesson->id; $newanswer->pageid = $newpageid; $newanswer->timecreated = $timenow; $newanswer->jumpto = LESSON_CLUSTERJUMP; $newanswerid = $DB->insert_record("lesson_answers", $newanswer); $lesson->add_message(get_string('addedcluster', 'lesson'), 'notifysuccess'); redirect($CFG->wwwroot.'/mod/lesson/edit.php?id='.$PAGE->cm->id); } } multichoice.php000064400000060070152267036420007574 0ustar00. /** * Multichoice * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Multichoice question type */ define("LESSON_PAGE_MULTICHOICE", "3"); class lesson_page_type_multichoice extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeidstring = 'multichoice'; protected $typeid = LESSON_PAGE_MULTICHOICE; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } /** * Gets an array of the jumps used by the answers of this page * * @return array */ public function get_jumps() { global $DB; $jumps = array(); if ($answers = $this->get_answers()) { foreach ($answers as $answer) { if ($answer->answer === '') { // show only jumps for real branches (==have description) continue; } $jumps[] = $this->get_jump_name($answer->jumpto); } } else { // We get here is the lesson was created on a Moodle 1.9 site and // the lesson contains question pages without any answers. $jumps[] = $this->get_jump_name($this->properties->nextpageid); } return $jumps; } public function get_used_answers() { $answers = $this->get_answers(); foreach ($answers as $key=>$answer) { if ($answer->answer === '') { unset($answers[$key]); } else { $answers[$key] = parent::rewrite_answers_urls($answer); } } return $answers; } public function display($renderer, $attempt) { global $CFG, $PAGE; $answers = $this->get_used_answers(); shuffle($answers); $action = $CFG->wwwroot.'/mod/lesson/continue.php'; $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt); if ($this->properties->qoption) { $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params); } else { $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params); } $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } public function check_answer() { global $DB, $CFG, $PAGE; $result = parent::check_answer(); $formattextdefoptions = new stdClass(); $formattextdefoptions->noclean = true; $formattextdefoptions->para = false; $answers = $this->get_used_answers(); shuffle($answers); $action = $CFG->wwwroot.'/mod/lesson/continue.php'; $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents()); if ($this->properties->qoption) { $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params); } else { $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params); } $data = $mform->get_data(); require_sesskey(); if (!$data) { $result->inmediatejump = true; $result->newpageid = $this->properties->id; return $result; } if ($this->properties->qoption) { // Multianswer allowed, user's answer is an array if (empty($data->answer) || !is_array($data->answer)) { $result->noanswer = true; return $result; } $studentanswers = array(); foreach ($data->answer as $key=>$value) { $studentanswers[] = (int)$key; } // get what the user answered $result->userresponse = implode(",", $studentanswers); // get the answers in a set order, the id order $answers = $this->get_used_answers(); $ncorrect = 0; $nhits = 0; $responses = array(); $correctanswerid = 0; $wronganswerid = 0; // store student's answers for displaying on feedback page $result->studentanswer = ''; $result->studentanswerformat = FORMAT_HTML; foreach ($answers as $answer) { foreach ($studentanswers as $answerid) { if ($answerid == $answer->id) { $studentanswerarray[] = format_text($answer->answer, $answer->answerformat, $formattextdefoptions); $responses[$answerid] = format_text($answer->response, $answer->responseformat, $formattextdefoptions); } } } $result->studentanswer = implode(self::MULTIANSWER_DELIMITER, $studentanswerarray); $correctpageid = null; $wrongpageid = null; // Iterate over all the possible answers. foreach ($answers as $answer) { if ($this->lesson->custom) { $iscorrectanswer = $answer->score > 0; } else { $iscorrectanswer = $this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto); } // Iterate over all the student answers to check if he selected the current possible answer. foreach ($studentanswers as $answerid) { if ($answerid == $answer->id) { if ($iscorrectanswer) { $nhits++; } else { // Always jump to the page related to the student's first wrong answer. if (!isset($wrongpageid)) { // Leave in its "raw" state - will be converted into a proper page id later. $wrongpageid = $answer->jumpto; } // Save the answer id for scoring. if ($wronganswerid == 0) { $wronganswerid = $answer->id; } } } } if ($iscorrectanswer) { $ncorrect++; // Save the first jumpto page id, may be needed! if (!isset($correctpageid)) { // Leave in its "raw" state - will be converted into a proper page id later. $correctpageid = $answer->jumpto; } // Save the answer id for scoring. if ($correctanswerid == 0) { $correctanswerid = $answer->id; } } } if ((count($studentanswers) == $ncorrect) and ($nhits == $ncorrect)) { $result->correctanswer = true; $result->response = implode(self::MULTIANSWER_DELIMITER, $responses); $result->newpageid = $correctpageid; $result->answerid = $correctanswerid; } else { $result->response = implode(self::MULTIANSWER_DELIMITER, $responses); $result->newpageid = $wrongpageid; $result->answerid = $wronganswerid; } } else { // only one answer allowed if (!isset($data->answerid) || (empty($data->answerid) && !is_int($data->answerid))) { $result->noanswer = true; return $result; } $result->answerid = $data->answerid; if (!$answer = $DB->get_record("lesson_answers", array("id" => $result->answerid))) { throw new \moodle_exception("Continue: answer record not found"); } $answer = parent::rewrite_answers_urls($answer); if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { $result->correctanswer = true; } if ($this->lesson->custom) { if ($answer->score > 0) { $result->correctanswer = true; } else { $result->correctanswer = false; } } $result->newpageid = $answer->jumpto; $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions); $result->userresponse = format_text($answer->answer, $answer->answerformat, $formattextdefoptions); $result->studentanswer = $result->userresponse; } return $result; } public function option_description_string() { if ($this->properties->qoption) { return " - ".get_string("multianswer", "lesson"); } return parent::option_description_string(); } public function display_answers(html_table $table) { $answers = $this->get_used_answers(); $options = new stdClass; $options->noclean = true; $options->para = false; $i = 1; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer); $cells = array(); if ($this->lesson->custom && $answer->score > 0) { // if the score is > 0, then it is correct $cells[] = ': \n"; } else if ($this->lesson->custom) { $cells[] = ': \n"; } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { // underline correct answers $cells[] = '' . get_string('answer', 'lesson') . " {$i}: \n"; } else { $cells[] = ': \n"; } $cells[] = format_text($answer->answer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':\n"; $cells[] = format_text($answer->response, $answer->responseformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); if ($i === 1){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $i++; } return $table; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); if ($this->properties->qoption) { $userresponse = explode(",", $temp->useranswer); foreach ($userresponse as $response) { if (isset($pagestats[$temp->pageid][$response])) { $pagestats[$temp->pageid][$response]++; } else { $pagestats[$temp->pageid][$response] = 1; } } } else { if (isset($pagestats[$temp->pageid][$temp->answerid])) { $pagestats[$temp->pageid][$temp->answerid]++; } else { $pagestats[$temp->pageid][$temp->answerid] = 1; } } if (isset($pagestats[$temp->pageid]["total"])) { $pagestats[$temp->pageid]["total"]++; } else { $pagestats[$temp->pageid]["total"] = 1; } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { $answers = $this->get_used_answers(); $formattextdefoptions = new stdClass; $formattextdefoptions->para = false; //I'll use it widely in this page $formattextdefoptions->context = $answerpage->context; foreach ($answers as $answer) { $answertext = format_text($answer->answer,$answer->answerformat,$formattextdefoptions); $correctresponsetext = html_writer::div(get_string('correctresponse', 'lesson'), 'badge bg-success text-white'); if ($this->properties->qoption) { if ($useranswer == null) { $userresponse = array(); } else { $userresponse = explode(",", $useranswer->useranswer); } if (in_array($answer->id, $userresponse)) { // make checked $checkboxelement = ""; if (!isset($answerdata->response)) { if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = $answer->response; } } if (!isset($answerdata->score)) { if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } } else { // unchecked $checkboxelement = ""; } $answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null); if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) { $data = html_writer::div($answercontent, 'text-success') . $correctresponsetext; } else { $data = $answercontent; } } else { if ($useranswer != null and $answer->id == $useranswer->answerid) { // make checked $checkboxelement = ""; if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = $answer->response; } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } else { // unchecked $checkboxelement = ""; } $answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null); if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) { $data = html_writer::div($answercontent, 'text-success') . $correctresponsetext; } else { $data = $answercontent; } } if (isset($pagestats[$this->properties->id][$answer->id])) { $percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100; $percent = round($percent, 2); $percent .= "% ".get_string("checkedthisone", "lesson"); } else { $percent = get_string("noonecheckedthis", "lesson"); } $answerdata->answers[] = array($data, $percent); $answerpage->answerdata = $answerdata; } return $answerpage; } } class lesson_add_page_form_multichoice extends lesson_add_page_form_base { public $qtype = 'multichoice'; public $qtypestring = 'multichoice'; protected $answerformat = LESSON_ANSWER_HTML; protected $responseformat = LESSON_ANSWER_HTML; public function custom_definition() { $this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('multianswer', 'lesson')); $this->_form->setDefault('qoption', 0); $this->_form->addHelpButton('qoption', 'multianswer', 'lesson'); for ($i = 0; $i < $this->_customdata['lesson']->maxanswers; $i++) { $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1)); $this->add_answer($i, null, ($i<2), $this->get_answer_format()); $this->add_response($i); $this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE)); $this->add_score($i, null, ($i===0)?1:0); } } } class lesson_display_answer_form_multichoice_singleanswer extends moodleform { public function definition() { global $USER, $OUTPUT; $mform = $this->_form; $answers = $this->_customdata['answers']; $lessonid = $this->_customdata['lessonid']; $contents = $this->_customdata['contents']; if (array_key_exists('attempt', $this->_customdata)) { $attempt = $this->_customdata['attempt']; } else { $attempt = new stdClass(); $attempt->answerid = null; } // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $hasattempt = false; $disabled = ''; if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) { $hasattempt = true; $disabled = array('disabled' => 'disabled'); } $options = new stdClass; $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); $i = 0; foreach ($answers as $answer) { $mform->addElement('html', '
'); $answer->answer = preg_replace('#>$#', '> ', $answer->answer); $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id, $disabled); $mform->setType('answer'.$i, PARAM_INT); if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) { $mform->setDefault('answerid', $USER->modattempts[$lessonid]->answerid); } $mform->addElement('html', '
'); $i++; } if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } } class lesson_display_answer_form_multichoice_multianswer extends moodleform { public function definition() { global $USER, $OUTPUT; $mform = $this->_form; $answers = $this->_customdata['answers']; $lessonid = $this->_customdata['lessonid']; $contents = $this->_customdata['contents']; // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $hasattempt = false; $disabled = ''; $useranswers = array(); if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) { $hasattempt = true; $disabled = array('disabled' => 'disabled'); $useranswers = explode(',', $USER->modattempts[$lessonid]->useranswer); } $options = new stdClass; $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); foreach ($answers as $answer) { $mform->addElement('html', '
'); $answerid = 'answer['.$answer->id.']'; if ($hasattempt && in_array($answer->id, $useranswers)) { $answerid = 'answer_'.$answer->id; $mform->addElement('hidden', 'answer['.$answer->id.']', $answer->answer); $mform->setType('answer['.$answer->id.']', PARAM_NOTAGS); $mform->setDefault($answerid, true); $mform->setDefault('answer['.$answer->id.']', true); } // NOTE: our silly checkbox supports only value '1' - we can not use it like the radiobox above!!!!!! $answer->answer = preg_replace('#>$#', '> ', $answer->answer); $mform->addElement('checkbox', $answerid, null, format_text($answer->answer, $answer->answerformat, $options), $disabled); $mform->setType($answerid, PARAM_INT); $mform->addElement('html', '
'); } if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } } numerical.php000064400000051451152267036420007251 0ustar00. /** * Numerical * * @package mod_lesson * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ defined('MOODLE_INTERNAL') || die(); /** Numerical question type */ define("LESSON_PAGE_NUMERICAL", "8"); use mod_lesson\local\numeric\helper; class lesson_page_type_numerical extends lesson_page { protected $type = lesson_page::TYPE_QUESTION; protected $typeidstring = 'numerical'; protected $typeid = LESSON_PAGE_NUMERICAL; protected $string = null; public function get_typeid() { return $this->typeid; } public function get_typestring() { if ($this->string===null) { $this->string = get_string($this->typeidstring, 'lesson'); } return $this->string; } public function get_idstring() { return $this->typeidstring; } public function display($renderer, $attempt) { global $USER, $PAGE; $mform = new lesson_display_answer_form_numerical(new moodle_url('/mod/lesson/continue.php'), array('contents' => $this->get_contents(), 'lessonid' => $this->lesson->id)); $data = new stdClass; $data->id = $PAGE->cm->id; $data->pageid = $this->properties->id; if (isset($USER->modattempts[$this->lesson->id])) { $data->answer = s($attempt->useranswer); } $mform->set_data($data); // Trigger an event question viewed. $eventparams = array( 'context' => context_module::instance($PAGE->cm->id), 'objectid' => $this->properties->id, 'other' => array( 'pagetype' => $this->get_typestring() ) ); $event = \mod_lesson\event\question_viewed::create($eventparams); $event->trigger(); return $mform->display(); } /** * Creates answers for this page type. * * @param object $properties The answer properties. */ public function create_answers($properties) { if (isset($properties->enableotheranswers) && $properties->enableotheranswers) { $properties->response_editor = array_values($properties->response_editor); $properties->jumpto = array_values($properties->jumpto); $properties->score = array_values($properties->score); $wrongresponse = end($properties->response_editor); $wrongkey = key($properties->response_editor); $properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS; } parent::create_answers($properties); } /** * Update the answers for this page type. * * @param object $properties The answer properties. * @param context $context The context for this module. * @param int $maxbytes The maximum bytes for any uploades. */ public function update($properties, $context = null, $maxbytes = null) { if ($properties->enableotheranswers) { $properties->response_editor = array_values($properties->response_editor); $properties->jumpto = array_values($properties->jumpto); $properties->score = array_values($properties->score); $wrongresponse = end($properties->response_editor); $wrongkey = key($properties->response_editor); $properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS; } parent::update($properties, $context, $maxbytes); } public function check_answer() { $result = parent::check_answer(); $mform = new lesson_display_answer_form_numerical(new moodle_url('/mod/lesson/continue.php'), array('contents' => $this->get_contents())); $data = $mform->get_data(); require_sesskey(); $formattextdefoptions = new stdClass(); $formattextdefoptions->noclean = true; $formattextdefoptions->para = false; // set defaults $result->response = ''; $result->newpageid = 0; if (!isset($data->answer)) { $result->noanswer = true; return $result; } else { $result->useranswer = $data->answer; } $result->studentanswer = $result->userresponse = $result->useranswer; $answers = $this->get_answers(); foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer); if (strpos($answer->answer, ':')) { // there's a pairs of values list($min, $max) = explode(':', $answer->answer); $minimum = (float) $min; $maximum = (float) $max; } else { // there's only one value $minimum = (float) $answer->answer; $maximum = $minimum; } if (($result->useranswer >= $minimum) && ($result->useranswer <= $maximum)) { $result->newpageid = $answer->jumpto; $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions); if ($this->lesson->jumpto_is_correct($this->properties->id, $result->newpageid)) { $result->correctanswer = true; } if ($this->lesson->custom) { if ($answer->score > 0) { $result->correctanswer = true; } else { $result->correctanswer = false; } } $result->answerid = $answer->id; return $result; } } // We could check here to see if we have a wrong answer jump to use. if ($result->answerid == 0) { // Use the all other answers jump details if it is set up. $lastanswer = end($answers); // Double check that this is the @#wronganswer#@ answer. if (strpos($lastanswer->answer, LESSON_OTHER_ANSWERS) !== false) { $otheranswers = end($answers); $result->newpageid = $otheranswers->jumpto; $result->response = format_text($otheranswers->response, $otheranswers->responseformat, $formattextdefoptions); // Does this also need to do the jumpto_is_correct? if ($this->lesson->custom) { $result->correctanswer = ($otheranswers->score > 0); } $result->answerid = $otheranswers->id; } } return $result; } public function display_answers(html_table $table) { $answers = $this->get_answers(); $options = new stdClass; $options->noclean = true; $options->para = false; $i = 1; foreach ($answers as $answer) { $answer = parent::rewrite_answers_urls($answer, false); $cells = array(); if ($this->lesson->custom && $answer->score > 0) { // if the score is > 0, then it is correct $cells[] = ':'; } else if ($this->lesson->custom) { $cells[] = ':'; } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) { // underline correct answers $cells[] = '' . get_string('answer', 'lesson') . ' ' . $i . ':' . "\n"; } else { $cells[] = ':'; } $formattedanswer = helper::lesson_format_numeric_value($answer->answer); $cells[] = format_text($formattedanswer, $answer->answerformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = format_text($answer->response, $answer->responseformat, $options); $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $answer->score; $table->data[] = new html_table_row($cells); $cells = array(); $cells[] = ':'; $cells[] = $this->get_jump_name($answer->jumpto); $table->data[] = new html_table_row($cells); if ($i === 1){ $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; } $i++; } return $table; } public function stats(array &$pagestats, $tries) { $temp = $this->lesson->get_last_attempt($tries); if (isset($pagestats[$temp->pageid][$temp->useranswer])) { $pagestats[$temp->pageid][$temp->useranswer]++; } else { $pagestats[$temp->pageid][$temp->useranswer] = 1; } if (isset($pagestats[$temp->pageid]["total"])) { $pagestats[$temp->pageid]["total"]++; } else { $pagestats[$temp->pageid]["total"] = 1; } return true; } public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { $answers = $this->get_answers(); $formattextdefoptions = new stdClass; $formattextdefoptions->para = false; //I'll use it widely in this page foreach ($answers as $answer) { if ($useranswer == null && $i == 0) { // I have the $i == 0 because it is easier to blast through it all at once. if (isset($pagestats[$this->properties->id])) { $stats = $pagestats[$this->properties->id]; $total = $stats["total"]; unset($stats["total"]); foreach ($stats as $valentered => $ntimes) { $valformatted = ''; if (!is_null($valentered) && trim($valentered) !== '') { // Empty response, 0 could be ok. $valformatted = s(format_float($valentered, strlen($valentered), true, true)); } $data = ''; $percent = $ntimes / $total * 100; $percent = round($percent, 2); $percent .= "% ".get_string("enteredthis", "lesson"); $answerdata->answers[] = array($data, $percent); } } else { $answerdata->answers[] = array(get_string("nooneansweredthisquestion", "lesson"), " "); } $i++; } else if ($useranswer != null && ($answer->id == $useranswer->answerid || ($answer == end($answers) && empty($answerdata->answers)))) { // Get in here when the user answered or for the last answer. $valformatted = ''; if (!is_null($useranswer->useranswer) && trim($useranswer->useranswer) !== '') { // Empty response, 0 could be ok. $valformatted = s(format_float($useranswer->useranswer, strlen($useranswer->useranswer), true, true)); } $data = ''; if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) { $percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100; $percent = round($percent, 2); $percent .= "% ".get_string("enteredthis", "lesson"); } else { $percent = get_string("nooneenteredthis", "lesson"); } $answerdata->answers[] = array($data, $percent); if ($answer->id == $useranswer->answerid) { if ($answer->response == null) { if ($useranswer->correct) { $answerdata->response = get_string("thatsthecorrectanswer", "lesson"); } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); } } else { $answerdata->response = $answer->response; } if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score; } elseif ($useranswer->correct) { $answerdata->score = get_string("receivedcredit", "lesson"); } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } else { $answerdata->response = get_string("thatsthewronganswer", "lesson"); if ($this->lesson->custom) { $answerdata->score = get_string("pointsearned", "lesson").": 0"; } else { $answerdata->score = get_string("didnotreceivecredit", "lesson"); } } } $answerpage->answerdata = $answerdata; } return $answerpage; } /** * Make updates to the form data if required. In this case to put the all other answer data into the write section of the form. * * @param stdClass $data The form data to update. * @return stdClass The updated fom data. */ public function update_form_data(stdClass $data): stdClass { $answercount = count($this->get_answers()); // If no answers provided, then we don't need to check anything. if (!$answercount) { return $data; } // Check for other answer entry. $lastanswer = $data->{'answer_editor[' . ($answercount - 1) . ']'}; if (strpos($lastanswer, LESSON_OTHER_ANSWERS) !== false) { $data->{'answer_editor[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'answer_editor[' . ($answercount - 1) . ']'}; $data->{'response_editor[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'response_editor[' . ($answercount - 1) . ']'}; $data->{'jumpto[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'jumpto[' . ($answercount - 1) . ']'}; $data->{'score[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'score[' . ($answercount - 1) . ']'}; $data->enableotheranswers = true; // Unset the old values. unset($data->{'answer_editor[' . ($answercount - 1) . ']'}); unset($data->{'response_editor[' . ($answercount - 1) . ']'}); unset($data->{'jumpto['. ($answercount - 1) . ']'}); unset($data->{'score[' . ($answercount - 1) . ']'}); } return $data; } /** * Custom formats the answer to display * * @param string $answer * @param context $context * @param int $answerformat * @param array $options Optional param for additional options. * @return string Returns formatted string */ public function format_answer($answer, $context, $answerformat, $options = []) { $answer = helper::lesson_format_numeric_value($answer); return parent::format_answer($answer, $context, $answerformat, $options); } } class lesson_add_page_form_numerical extends lesson_add_page_form_base { public $qtype = 'numerical'; public $qtypestring = 'numerical'; protected $answerformat = ''; protected $responseformat = LESSON_ANSWER_HTML; public function custom_definition() { $answercount = $this->_customdata['lesson']->maxanswers; for ($i = 0; $i < $answercount; $i++) { $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1)); $this->add_answer($i, null, ($i < 1), '', [ 'identifier' => 'numericanswer', 'component' => 'mod_lesson' ]); $this->add_response($i); $this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE)); $this->add_score($i, null, ($i===0)?1:0); } // Wrong answer jump. $this->_form->addElement('header', 'wronganswer', get_string('allotheranswers', 'lesson')); $newcount = $answercount + 1; $this->_form->addElement('advcheckbox', 'enableotheranswers', get_string('enabled', 'lesson')); $this->add_response($newcount); $this->add_jumpto($newcount, get_string('allotheranswersjump', 'lesson'), LESSON_NEXTPAGE); $this->add_score($newcount, get_string('allotheranswersscore', 'lesson'), 0); } /** * We call get data when storing the data into the db. Override to format the floats properly * * @return object|void */ public function get_data(): ?stdClass { $data = parent::get_data(); if (!empty($data->answer_editor)) { foreach ($data->answer_editor as $key => $answer) { $data->answer_editor[$key] = helper::lesson_unformat_numeric_value($answer); } } return $data; } /** * Return submitted data if properly submitted or returns NULL if validation fails or * if there is no submitted data with formatted numbers * * @return object submitted data; NULL if not valid or not submitted or cancelled */ public function get_submitted_data(): ?stdClass { $data = parent::get_submitted_data(); if (!empty($data->answer_editor)) { foreach ($data->answer_editor as $key => $answer) { $data->answer_editor[$key] = helper::lesson_unformat_numeric_value($answer); } } return $data; } /** * Load in existing data as form defaults. Usually new entry defaults are stored directly in * form definition (new entry form); this function is used to load in data where values * already exist and data is being edited (edit entry form) after formatting numbers * * * @param stdClass|array $defaults object or array of default values */ public function set_data($defaults) { if (is_object($defaults)) { $defaults = (array) $defaults; } $editor = 'answer_editor'; foreach ($defaults as $key => $answer) { if (substr($key, 0, strlen($editor)) == $editor) { $defaults[$key] = helper::lesson_format_numeric_value($answer); } } parent::set_data($defaults); } } class lesson_display_answer_form_numerical extends moodleform { public function definition() { global $USER, $OUTPUT; $mform = $this->_form; $contents = $this->_customdata['contents']; // Disable shortforms. $mform->setDisableShortforms(); $mform->addElement('header', 'pageheader'); $mform->addElement('html', $OUTPUT->container($contents, 'contents')); $hasattempt = false; $attrs = array('size'=>'50', 'maxlength'=>'200'); if (isset($this->_customdata['lessonid'])) { $lessonid = $this->_customdata['lessonid']; if (isset($USER->modattempts[$lessonid]->useranswer)) { $attrs['readonly'] = 'readonly'; $hasattempt = true; } } $options = new stdClass; $options->para = false; $options->noclean = true; $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'pageid'); $mform->setType('pageid', PARAM_INT); $mform->addElement('float', 'answer', get_string('youranswer', 'lesson'), $attrs); if ($hasattempt) { $this->add_action_buttons(null, get_string("nextpage", "lesson")); } else { $this->add_action_buttons(null, get_string("submit", "lesson")); } } }