fill_blanks.class.php 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Class FillBlanks
  5. *
  6. * @author Eric Marguin
  7. * @author Julio Montoya multiple fill in blank option added
  8. * @package chamilo.exercise
  9. **/
  10. class FillBlanks extends Question
  11. {
  12. public static $typePicture = 'fill_in_blanks.png';
  13. public static $explanationLangVar = 'FillBlanks';
  14. const FILL_THE_BLANK_STANDARD = 0;
  15. const FILL_THE_BLANK_MENU = 1;
  16. const FILL_THE_BLANK_SEVERAL_ANSWER = 2;
  17. /**
  18. * Constructor
  19. */
  20. public function __construct()
  21. {
  22. parent::__construct();
  23. $this->type = FILL_IN_BLANKS;
  24. $this->isContent = $this->getIsContent();
  25. }
  26. /**
  27. * function which redefines Question::createAnswersForm
  28. * @param FormValidator $form
  29. */
  30. public function createAnswersForm($form)
  31. {
  32. $defaults = array();
  33. if (!empty($this->id)) {
  34. $objectAnswer = new Answer($this->id);
  35. $answer = $objectAnswer->selectAnswer(1);
  36. $listAnswersInfo = FillBlanks::getAnswerInfo($answer);
  37. if ($listAnswersInfo['switchable']) {
  38. $defaults['multiple_answer'] = 1;
  39. } else {
  40. $defaults['multiple_answer'] = 0;
  41. }
  42. //take the complete string except after the last '::'
  43. $defaults['answer'] = $listAnswersInfo['text'];
  44. $defaults['select_separator'] = $listAnswersInfo['blankseparatornumber'];
  45. $blankSeparatorNumber = $listAnswersInfo['blankseparatornumber'];
  46. } else {
  47. $defaults['answer'] = get_lang('DefaultTextInBlanks');
  48. $defaults['select_separator'] = 0;
  49. $blankSeparatorNumber = 0;
  50. }
  51. $blankSeparatorStart = self::getStartSeparator($blankSeparatorNumber);
  52. $blankSeparatorEnd = self::getEndSeparator($blankSeparatorNumber);
  53. $setWeightAndSize = '';
  54. if (isset($listAnswersInfo) && count($listAnswersInfo['tabweighting']) > 0) {
  55. foreach ($listAnswersInfo['tabweighting'] as $i => $weighting) {
  56. $setWeightAndSize .= 'document.getElementById("weighting['.$i.']").value = "'.$weighting.'";';
  57. }
  58. foreach ($listAnswersInfo['tabinputsize'] as $i => $sizeOfInput) {
  59. $setWeightAndSize .= 'document.getElementById("sizeofinput['.$i.']").value = "'.$sizeOfInput.'";';
  60. $setWeightAndSize .= 'document.getElementById("samplesize['.$i.']").style.width = "'.$sizeOfInput.'px";';
  61. }
  62. }
  63. echo '<script>
  64. var firstTime = true;
  65. var originalOrder = new Array();
  66. var blankSeparatorStart = "'.$blankSeparatorStart.'";
  67. var blankSeparatorEnd = "'.$blankSeparatorEnd.'";
  68. var blankSeparatorStartRegexp = getBlankSeparatorRegexp(blankSeparatorStart);
  69. var blankSeparatorEndRegexp = getBlankSeparatorRegexp(blankSeparatorEnd);
  70. var blanksRegexp = "/"+blankSeparatorStartRegexp+"[^"+blankSeparatorStartRegexp+"]*"+blankSeparatorEndRegexp+"/g";
  71. CKEDITOR.on("instanceCreated", function(e) {
  72. if (e.editor.name === "answer") {
  73. //e.editor.on("change", updateBlanks);
  74. e.editor.on("change", function(){
  75. updateBlanks();
  76. });
  77. }
  78. });
  79. function updateBlanks()
  80. {
  81. var answer;
  82. if (firstTime) {
  83. var field = document.getElementById("answer");
  84. answer = field.value;
  85. } else {
  86. answer = CKEDITOR.instances["answer"].getData();
  87. }
  88. // disable the save button, if not blanks have been created
  89. $("button").attr("disabled", "disabled");
  90. $("#defineoneblank").show();
  91. var blanks = answer.match(eval(blanksRegexp));
  92. var fields = "<div class=\"form-group \">";
  93. fields += "<label class=\"col-sm-2 control-label\">'.get_lang('Weighting').'</label>";
  94. fields += "<div class=\"col-sm-8\">";
  95. fields += "<table>";
  96. fields += "<tr><th style=\"padding:0 20px\">'.get_lang("WordTofind").'</th><th style=\"padding:0 20px\">'.get_lang("QuestionWeighting").'</th><th style=\"padding:0 20px\">'.get_lang("BlankInputSize").'</th></tr>";
  97. if (blanks != null) {
  98. for (var i=0; i < blanks.length; i++) {
  99. // remove forbidden characters that causes bugs
  100. blanks[i] = removeForbiddenChars(blanks[i]);
  101. // trim blanks between brackets
  102. blanks[i] = trimBlanksBetweenSeparator(blanks[i], blankSeparatorStart, blankSeparatorEnd);
  103. // if the word is empty []
  104. if (blanks[i] == blankSeparatorStartRegexp+blankSeparatorEndRegexp) {
  105. break;
  106. }
  107. // get input size
  108. var inputSize = 100;
  109. var textValue = blanks[i].substr(1, blanks[i].length - 2);
  110. var btoaValue = textValue.hashCode();
  111. if (firstTime == false) {
  112. var element = document.getElementById("samplesize["+i+"]");
  113. if (element) {
  114. inputSize = document.getElementById("sizeofinput["+i+"]").value;
  115. }
  116. }
  117. if (document.getElementById("weighting["+i+"]")) {
  118. var value = document.getElementById("weighting["+i+"]").value;
  119. } else {
  120. var value = "1";
  121. }
  122. fields += "<tr>";
  123. fields += "<td>"+blanks[i]+"</td>";
  124. fields += "<td><input style=\"width:35px\" value=\""+value+"\" type=\"text\" id=\"weighting["+i+"]\" name=\"weighting["+i+"]\" /></td>";
  125. fields += "<td>";
  126. fields += "<input class=\"btn btn-default\" type=\"button\" value=\"-\" onclick=\"changeInputSize(-1, "+i+")\">&nbsp;";
  127. fields += "<input class=\"btn btn-default\" type=\"button\" value=\"+\" onclick=\"changeInputSize(1, "+i+")\">&nbsp;";
  128. fields += "<input class=\"sample\" id=\"samplesize["+i+"]\" data-btoa=\""+btoaValue+"\" type=\"text\" value=\""+textValue+"\" style=\"width:"+inputSize+"px\" disabled=disabled />";
  129. fields += "<input id=\"sizeofinput["+i+"]\" type=\"hidden\" value=\""+inputSize+"\" name=\"sizeofinput["+i+"]\" />";
  130. fields += "</td>";
  131. fields += "</tr>";
  132. // enable the save button
  133. $("button").removeAttr("disabled");
  134. $("#defineoneblank").hide();
  135. }
  136. }
  137. document.getElementById("blanks_weighting").innerHTML = fields + "</table></div></div>";
  138. $(originalOrder).each(function(i, data) {
  139. if (firstTime == false) {
  140. value = data.value;
  141. var d = $("input.sample[data-btoa=\'"+value+"\']");
  142. var id = d.attr("id");
  143. if (id) {
  144. var sizeInputId = id.replace("samplesize", "sizeofinput");
  145. var sizeInputId = sizeInputId.replace("[", "\\\[");
  146. var sizeInputId = sizeInputId.replace("]", "\\\]");
  147. $("#"+sizeInputId).val(data.width);
  148. d.outerWidth(data.width+"px");
  149. }
  150. }
  151. });
  152. updateOrder(blanks);
  153. if (firstTime) {
  154. firstTime = false;
  155. '.$setWeightAndSize.'
  156. }
  157. }
  158. window.onload = updateBlanks;
  159. String.prototype.hashCode = function() {
  160. var hash = 0, i, chr, len;
  161. if (this.length === 0) return hash;
  162. for (i = 0, len = this.length; i < len; i++) {
  163. chr = this.charCodeAt(i);
  164. hash = ((hash << 5) - hash) + chr;
  165. hash |= 0; // Convert to 32bit integer
  166. }
  167. return hash;
  168. };
  169. function updateOrder(blanks)
  170. {
  171. originalOrder = new Array();
  172. if (blanks != null) {
  173. for (var i=0; i < blanks.length; i++) {
  174. // remove forbidden characters that causes bugs
  175. blanks[i] = removeForbiddenChars(blanks[i]);
  176. // trim blanks between brackets
  177. blanks[i] = trimBlanksBetweenSeparator(blanks[i], blankSeparatorStart, blankSeparatorEnd);
  178. // if the word is empty []
  179. if (blanks[i] == blankSeparatorStartRegexp+blankSeparatorEndRegexp) {
  180. break;
  181. }
  182. var textValue = blanks[i].substr(1, blanks[i].length - 2);
  183. var btoaValue = textValue.hashCode();
  184. if (firstTime == false) {
  185. var element = document.getElementById("samplesize["+i+"]");
  186. if (element) {
  187. inputSize = document.getElementById("sizeofinput["+i+"]").value;
  188. originalOrder.push({ "width" : inputSize, "value": btoaValue });
  189. }
  190. }
  191. }
  192. }
  193. }
  194. function changeInputSize(coef, inIdNum)
  195. {
  196. if (firstTime) {
  197. var field = document.getElementById("answer");
  198. answer = field.value;
  199. } else {
  200. answer = CKEDITOR.instances["answer"].getData();
  201. }
  202. var blanks = answer.match(eval(blanksRegexp));
  203. var currentWidth = $("#samplesize\\\["+inIdNum+"\\\]").width();
  204. var newWidth = currentWidth + coef * 20;
  205. newWidth = Math.max(20, newWidth);
  206. newWidth = Math.min(newWidth, 600);
  207. $("#samplesize\\\["+inIdNum+"\\\]").outerWidth(newWidth);
  208. $("#sizeofinput\\\["+inIdNum+"\\\]").attr("value", newWidth);
  209. updateOrder(blanks);
  210. }
  211. function removeForbiddenChars(inTxt)
  212. {
  213. outTxt = inTxt;
  214. outTxt = outTxt.replace(/&quot;/g, ""); // remove the char
  215. outTxt = outTxt.replace(/\x22/g, ""); // remove the char
  216. outTxt = outTxt.replace(/"/g, ""); // remove the char
  217. outTxt = outTxt.replace(/\\\\/g, ""); // remove the \ char
  218. outTxt = outTxt.replace(/&nbsp;/g, " ");
  219. outTxt = outTxt.replace(/^ +/, "");
  220. outTxt = outTxt.replace(/ +$/, "");
  221. return outTxt;
  222. }
  223. function changeBlankSeparator()
  224. {
  225. var separatorNumber = $("#select_separator").val();
  226. var tabSeparator = getSeparatorFromNumber(separatorNumber);
  227. blankSeparatorStart = tabSeparator[0];
  228. blankSeparatorEnd = tabSeparator[1];
  229. blankSeparatorStartRegexp = getBlankSeparatorRegexp(blankSeparatorStart);
  230. blankSeparatorEndRegexp = getBlankSeparatorRegexp(blankSeparatorEnd);
  231. blanksRegexp = "/"+blankSeparatorStartRegexp+"[^"+blankSeparatorStartRegexp+"]*"+blankSeparatorEndRegexp+"/g";
  232. updateBlanks();
  233. }
  234. // this function is the same than the PHP one
  235. // if modify it modify the php one escapeForRegexp
  236. function getBlankSeparatorRegexp(inTxt)
  237. {
  238. var tabSpecialChar = new Array(".", "+", "*", "?", "[", "^", "]", "$", "(", ")",
  239. "{", "}", "=", "!", "<", ">", "|", ":", "-", ")");
  240. for (var i=0; i < tabSpecialChar.length; i++) {
  241. if (inTxt == tabSpecialChar[i]) {
  242. return "\\\"+inTxt;
  243. }
  244. }
  245. return inTxt;
  246. }
  247. // this function is the same than the PHP one
  248. // if modify it modify the php one getAllowedSeparator
  249. function getSeparatorFromNumber(innumber)
  250. {
  251. tabSeparator = new Array();
  252. tabSeparator[0] = new Array("[", "]");
  253. tabSeparator[1] = new Array("{", "}");
  254. tabSeparator[2] = new Array("(", ")");
  255. tabSeparator[3] = new Array("*", "*");
  256. tabSeparator[4] = new Array("#", "#");
  257. tabSeparator[5] = new Array("%", "%");
  258. tabSeparator[6] = new Array("$", "$");
  259. return tabSeparator[innumber];
  260. }
  261. function trimBlanksBetweenSeparator(inTxt, inSeparatorStart, inSeparatorEnd)
  262. {
  263. var result = inTxt
  264. result = result.replace(inSeparatorStart, "");
  265. result = result.replace(inSeparatorEnd, "");
  266. result = result.trim();
  267. return inSeparatorStart+result+inSeparatorEnd;
  268. }
  269. </script>';
  270. // answer
  271. $form->addLabel(
  272. null,
  273. get_lang('TypeTextBelow').', '.get_lang('And').' '.get_lang('UseTagForBlank')
  274. );
  275. $form->addElement(
  276. 'html_editor',
  277. 'answer',
  278. Display::return_icon('fill_field.png'),
  279. ['id' => 'answer'],
  280. array('ToolbarSet' => 'TestQuestionDescription')
  281. );
  282. $form->addRule('answer', get_lang('GiveText'), 'required');
  283. //added multiple answers
  284. $form->addElement('checkbox', 'multiple_answer', '', get_lang('FillInBlankSwitchable'));
  285. $form->addElement(
  286. 'select',
  287. 'select_separator',
  288. get_lang("SelectFillTheBlankSeparator"),
  289. self::getAllowedSeparatorForSelect(),
  290. ' id="select_separator" style="width:150px" onchange="changeBlankSeparator()" '
  291. );
  292. $form->addLabel(
  293. null,
  294. '<input type="button" onclick="updateBlanks()" value="'.get_lang('RefreshBlanks').'" class="btn btn-default" />'
  295. );
  296. $form->addHtml('<div id="blanks_weighting"></div>');
  297. global $text;
  298. // setting the save button here and not in the question class.php
  299. $form->addHtml('<div id="defineoneblank" style="color:#D04A66; margin-left:160px">'.get_lang('DefineBlanks').'</div>');
  300. $form->addButtonSave($text, 'submitQuestion');
  301. if (!empty($this->id)) {
  302. $form->setDefaults($defaults);
  303. } else {
  304. if ($this->isContent == 1) {
  305. $form->setDefaults($defaults);
  306. }
  307. }
  308. }
  309. /**
  310. * Function which creates the form to create/edit the answers of the question
  311. * @param FormValidator $form
  312. */
  313. public function processAnswersCreation($form)
  314. {
  315. $answer = $form->getSubmitValue('answer');
  316. // Due the ckeditor transform the elements to their HTML value
  317. //$answer = api_html_entity_decode($answer, ENT_QUOTES, $charset);
  318. //$answer = htmlentities(api_utf8_encode($answer));
  319. // remove the :: eventually written by the user
  320. $answer = str_replace('::', '', $answer);
  321. // remove starting and ending space and &nbsp;
  322. $answer = api_preg_replace("/\xc2\xa0/", " ", $answer);
  323. // start and end separator
  324. $blankStartSeparator = self::getStartSeparator($form->getSubmitValue('select_separator'));
  325. $blankEndSeparator = self::getEndSeparator($form->getSubmitValue('select_separator'));
  326. $blankStartSeparatorRegexp = self::escapeForRegexp($blankStartSeparator);
  327. $blankEndSeparatorRegexp = self::escapeForRegexp($blankEndSeparator);
  328. // remove spaces at the beginning and the end of text in square brackets
  329. $answer = preg_replace_callback(
  330. "/".$blankStartSeparatorRegexp."[^]]+".$blankEndSeparatorRegexp."/",
  331. function ($matches) use ($blankStartSeparator, $blankEndSeparator) {
  332. $matchingResult = $matches[0];
  333. $matchingResult = trim($matchingResult, $blankStartSeparator);
  334. $matchingResult = trim($matchingResult, $blankEndSeparator);
  335. $matchingResult = trim($matchingResult);
  336. // remove forbidden chars
  337. $matchingResult = str_replace("/\\/", "", $matchingResult);
  338. $matchingResult = str_replace('/"/', "", $matchingResult);
  339. return $blankStartSeparator.$matchingResult.$blankEndSeparator;
  340. },
  341. $answer
  342. );
  343. // get the blanks weightings
  344. $nb = preg_match_all(
  345. '/'.$blankStartSeparatorRegexp.'[^'.$blankStartSeparatorRegexp.']*'.$blankEndSeparatorRegexp.'/',
  346. $answer,
  347. $blanks
  348. );
  349. if (isset($_GET['editQuestion'])) {
  350. $this->weighting = 0;
  351. }
  352. /* if we have some [tobefound] in the text
  353. build the string to save the following in the answers table
  354. <p>I use a [computer] and a [pen].</p>
  355. becomes
  356. <p>I use a [computer] and a [pen].</p>::100,50:100,50@1
  357. ++++++++-------**
  358. --- -- --- -- -
  359. A B (C) (D)(E)
  360. +++++++ : required, weighting of each words
  361. ------- : optional, input width to display, 200 if not present
  362. ** : equal @1 if "Allow answers order switches" has been checked, @ otherwise
  363. A : weighting for the word [computer]
  364. B : weighting for the word [pen]
  365. C : input width for the word [computer]
  366. D : input width for the word [pen]
  367. E : equal @1 if "Allow answers order switches" has been checked, @ otherwise
  368. */
  369. if ($nb > 0) {
  370. $answer .= '::';
  371. // weighting
  372. for ($i=0; $i < $nb; ++$i) {
  373. // enter the weighting of word $i
  374. $answer .= $form->getSubmitValue('weighting['.$i.']');
  375. // not the last word, add ","
  376. if ($i != $nb - 1) {
  377. $answer .= ",";
  378. }
  379. // calculate the global weighting for the question
  380. $this -> weighting += $form->getSubmitValue('weighting['.$i.']');
  381. }
  382. // input width
  383. $answer .= ":";
  384. for ($i=0; $i < $nb; ++$i) {
  385. // enter the width of input for word $i
  386. $answer .= $form->getSubmitValue('sizeofinput['.$i.']');
  387. // not the last word, add ","
  388. if ($i != $nb - 1) {
  389. $answer .= ",";
  390. }
  391. }
  392. }
  393. // write the blank separator code number
  394. // see function getAllowedSeparator
  395. /*
  396. 0 [...]
  397. 1 {...}
  398. 2 (...)
  399. 3 *...*
  400. 4 #...#
  401. 5 %...%
  402. 6 $...$
  403. */
  404. $answer .= ":".$form->getSubmitValue('select_separator');
  405. // Allow answers order switches
  406. $is_multiple = $form -> getSubmitValue('multiple_answer');
  407. $answer.= '@'.$is_multiple;
  408. $this->save();
  409. $objAnswer = new Answer($this->id);
  410. $objAnswer->createAnswer($answer, 0, '', 0, 1);
  411. $objAnswer->save();
  412. }
  413. /**
  414. * @param null $feedback_type
  415. * @param null $counter
  416. * @param null $score
  417. * @return string
  418. */
  419. public function return_header($feedback_type = null, $counter = null, $score = null)
  420. {
  421. $header = parent::return_header($feedback_type, $counter, $score);
  422. $header .= '<table class="'.$this->question_table_class .'">
  423. <tr>
  424. <th>'.get_lang("Answer").'</th>
  425. </tr>';
  426. return $header;
  427. }
  428. /**
  429. * @param string $separatorStartRegexp
  430. * @param string $separatorEndRegexp
  431. * @param string $correctItemRegexp
  432. * @param integer $questionId
  433. * @param $correctItem
  434. * @param $attributes
  435. * @param string $answer
  436. * @param $listAnswersInfo
  437. * @param boolean $displayForStudent
  438. * @param integer $inBlankNumber
  439. * @return string
  440. */
  441. public static function getFillTheBlankHtml(
  442. $separatorStartRegexp,
  443. $separatorEndRegexp,
  444. $correctItemRegexp,
  445. $questionId,
  446. $correctItem,
  447. $attributes,
  448. $answer,
  449. $listAnswersInfo,
  450. $displayForStudent,
  451. $inBlankNumber
  452. ) {
  453. $result = '';
  454. $inTabTeacherSolution = $listAnswersInfo['tabwords'];
  455. $inTeacherSolution = $inTabTeacherSolution[$inBlankNumber];
  456. switch (self::getFillTheBlankAnswerType($inTeacherSolution)) {
  457. case self::FILL_THE_BLANK_MENU:
  458. $selected = '';
  459. // the blank menu
  460. $optionMenu = '';
  461. // display a menu from answer separated with |
  462. // if display for student, shuffle the correct answer menu
  463. $listMenu = self::getFillTheBlankMenuAnswers($inTeacherSolution, $displayForStudent);
  464. $result .= '<select name="choice['.$questionId.'][]">';
  465. for ($k=0; $k < count($listMenu); $k++) {
  466. $selected = "";
  467. if ($correctItem == $listMenu[$k]) {
  468. $selected = " selected=selected ";
  469. }
  470. // if in teacher view, display the first item by default, which is the right answer
  471. if ($k==0 && !$displayForStudent) {
  472. $selected = " selected=selected ";
  473. }
  474. $optionMenu .= '<option '.$selected.' value="'.$listMenu[$k].'">'.$listMenu[$k].'</option>';
  475. }
  476. if ($selected == "") {
  477. // no good answer have been found...
  478. $selected = " selected=selected ";
  479. }
  480. $result .= "<option $selected value=''>--</option>";
  481. $result .= $optionMenu;
  482. $result .= '</select>';
  483. break;
  484. case self::FILL_THE_BLANK_SEVERAL_ANSWER:
  485. //no break
  486. case self::FILL_THE_BLANK_STANDARD:
  487. default:
  488. $result = Display::input('text', "choice[$questionId][]", $correctItem, $attributes);
  489. break;
  490. }
  491. return $result;
  492. }
  493. /**
  494. * Return an array with the different choices available
  495. * when the answers between bracket show as a menu
  496. * @param string $correctAnswer
  497. * @param bool $displayForStudent true if we want to shuffle the choices of the menu for students
  498. *
  499. * @return array
  500. */
  501. public static function getFillTheBlankMenuAnswers($correctAnswer, $displayForStudent)
  502. {
  503. // if $inDisplayForStudent, then shuffle the result array
  504. /*$items = explode('|', $correctAnswer);
  505. if ($displayForStudent) {
  506. shuffle($items);
  507. }
  508. return $items;*/
  509. $listChoises = api_preg_split("/\|/", $correctAnswer);
  510. if ($displayForStudent) {
  511. shuffle($listChoises);
  512. }
  513. return $listChoises;
  514. }
  515. /**
  516. * Return the array index of the student answer
  517. * @param string $correctAnswer the menu Choice1|Choice2|Choice3
  518. * @param string $studentAnswer the student answer must be Choice1 or Choice2 or Choice3
  519. *
  520. * @return int in the example 0 1 or 2 depending of the choice of the student
  521. */
  522. public static function getFillTheBlankMenuAnswerNum($correctAnswer, $studentAnswer)
  523. {
  524. $listChoices = self::getFillTheBlankMenuAnswers($correctAnswer, false);
  525. foreach ($listChoices as $num => $value) {
  526. if ($value == $studentAnswer) {
  527. return $num;
  528. }
  529. }
  530. // should not happened, because student choose the answer in a menu of possible answers
  531. return -1;
  532. }
  533. /**
  534. * Return the possible answer if the answer between brackets is a multiple choice menu
  535. * @param string $correctAnswer
  536. *
  537. * @return array
  538. */
  539. public static function getFillTheBlankSeveralAnswers($correctAnswer)
  540. {
  541. // is answer||Answer||response||Response , mean answer or Answer ...
  542. $listSeveral = api_preg_split("/\|\|/", $correctAnswer);
  543. return $listSeveral;
  544. }
  545. /**
  546. * Return true if student answer is right according to the correctAnswer
  547. * it is not as simple as equality, because of the type of Fill The Blank question
  548. * eg : studentAnswer = 'Un' and correctAnswer = 'Un||1||un'
  549. * @param string $studentAnswer [studentanswer] of the info array of the answer field
  550. * @param string $correctAnswer [tabwords] of the info array of the answer field
  551. *
  552. * @return bool
  553. */
  554. public static function isGoodStudentAnswer($studentAnswer, $correctAnswer)
  555. {
  556. switch (self::getFillTheBlankAnswerType($correctAnswer)) {
  557. case self::FILL_THE_BLANK_MENU:
  558. $listMenu = self::getFillTheBlankMenuAnswers($correctAnswer, false);
  559. $result = $listMenu[0] == $studentAnswer;
  560. break;
  561. case self::FILL_THE_BLANK_SEVERAL_ANSWER:
  562. // the answer must be one of the choice made
  563. $listSeveral = self::getFillTheBlankSeveralAnswers($correctAnswer);
  564. $result = in_array($studentAnswer, $listSeveral);
  565. break;
  566. case self::FILL_THE_BLANK_STANDARD:
  567. default:
  568. $result = $studentAnswer == $correctAnswer;
  569. break;
  570. }
  571. return $result;
  572. }
  573. /**
  574. * @param string $correctAnswer
  575. *
  576. * @return int
  577. */
  578. public static function getFillTheBlankAnswerType($correctAnswer)
  579. {
  580. if (api_strpos($correctAnswer, "|") && !api_strpos($correctAnswer, "||")) {
  581. return self::FILL_THE_BLANK_MENU;
  582. } elseif (api_strpos($correctAnswer, "||")) {
  583. return self::FILL_THE_BLANK_SEVERAL_ANSWER;
  584. } else {
  585. return self::FILL_THE_BLANK_STANDARD;
  586. }
  587. }
  588. /**
  589. * Return information about the answer
  590. * @param string $userAnswer the text of the answer of the question
  591. * @param bool $isStudentAnswer true if it's a student answer false the empty question model
  592. *
  593. * @return array of information about the answer
  594. */
  595. public static function getAnswerInfo($userAnswer = "", $isStudentAnswer = false)
  596. {
  597. $listAnswerResults = array();
  598. $listAnswerResults['text'] = '';
  599. $listAnswerResults['wordsCount'] = 0;
  600. $listAnswerResults['tabwordsbracket'] = array();
  601. $listAnswerResults['tabwords'] = array();
  602. $listAnswerResults['tabweighting'] = array();
  603. $listAnswerResults['tabinputsize'] = array();
  604. $listAnswerResults['switchable'] = '';
  605. $listAnswerResults['studentanswer'] = array();
  606. $listAnswerResults['studentscore'] = array();
  607. $listAnswerResults['blankseparatornumber'] = 0;
  608. $listDoubleColon = array();
  609. api_preg_match("/(.*)::(.*)$/s", $userAnswer, $listResult);
  610. if (count($listResult) < 2) {
  611. $listDoubleColon[] = '';
  612. $listDoubleColon[] = '';
  613. } else {
  614. $listDoubleColon[] = $listResult[1];
  615. $listDoubleColon[] = $listResult[2];
  616. }
  617. $listAnswerResults['systemstring'] = $listDoubleColon[1];
  618. // make sure we only take the last bit to find special marks
  619. $listArobaseSplit = explode('@', $listDoubleColon[1]);
  620. if (count($listArobaseSplit) < 2) {
  621. $listArobaseSplit[1] = '';
  622. }
  623. // take the complete string except after the last '::'
  624. $listDetails = explode(":", $listArobaseSplit[0]);
  625. // < number of item after the ::[score]:[size]:[separator_id]@ , here there are 3
  626. if (count($listDetails) < 3) {
  627. $listWeightings = explode(',', $listDetails[0]);
  628. $listSizeOfInput = array();
  629. for ($i=0; $i < count($listWeightings); $i++) {
  630. $listSizeOfInput[] = 200;
  631. }
  632. $blankSeparatorNumber = 0; // 0 is [...]
  633. } else {
  634. $listWeightings = explode(',', $listDetails[0]);
  635. $listSizeOfInput = explode(',', $listDetails[1]);
  636. $blankSeparatorNumber = $listDetails[2];
  637. }
  638. $listAnswerResults['text'] = $listDoubleColon[0];
  639. $listAnswerResults['tabweighting'] = $listWeightings;
  640. $listAnswerResults['tabinputsize'] = $listSizeOfInput;
  641. $listAnswerResults['switchable'] = $listArobaseSplit[1];
  642. $listAnswerResults['blankseparatorstart'] = self::getStartSeparator($blankSeparatorNumber);
  643. $listAnswerResults['blankseparatorend'] = self::getEndSeparator($blankSeparatorNumber);
  644. $listAnswerResults['blankseparatornumber'] = $blankSeparatorNumber;
  645. $blankCharStart = self::getStartSeparator($blankSeparatorNumber);
  646. $blankCharEnd = self::getEndSeparator($blankSeparatorNumber);
  647. $blankCharStartForRegexp = self::escapeForRegexp($blankCharStart);
  648. $blankCharEndForRegexp = self::escapeForRegexp($blankCharEnd);
  649. // get all blanks words
  650. $listAnswerResults['wordsCount'] = api_preg_match_all(
  651. '/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
  652. $listDoubleColon[0],
  653. $listWords
  654. );
  655. if ($listAnswerResults['wordsCount'] > 0) {
  656. $listAnswerResults['tabwordsbracket'] = $listWords[0];
  657. // remove [ and ] in string
  658. array_walk(
  659. $listWords[0],
  660. function (&$value, $key, $tabBlankChar) {
  661. $trimChars = '';
  662. for ($i=0; $i < count($tabBlankChar); $i++) {
  663. $trimChars .= $tabBlankChar[$i];
  664. }
  665. $value = trim($value, $trimChars);
  666. },
  667. array($blankCharStart, $blankCharEnd)
  668. );
  669. $listAnswerResults['tabwords'] = $listWords[0];
  670. }
  671. // get all common words
  672. $commonWords = api_preg_replace(
  673. '/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
  674. "::",
  675. $listDoubleColon[0]
  676. );
  677. // if student answer, the second [] is the student answer,
  678. // the third is if student scored or not
  679. $listBrackets = array();
  680. $listWords = array();
  681. if ($isStudentAnswer) {
  682. for ($i=0; $i < count($listAnswerResults['tabwords']); $i++) {
  683. $listBrackets[] = $listAnswerResults['tabwordsbracket'][$i];
  684. $listWords[] = $listAnswerResults['tabwords'][$i];
  685. if ($i+1 < count($listAnswerResults['tabwords'])) {
  686. // should always be
  687. $i++;
  688. }
  689. $listAnswerResults['studentanswer'][] = $listAnswerResults['tabwords'][$i];
  690. if ($i+1 < count($listAnswerResults['tabwords'])) {
  691. // should always be
  692. $i++;
  693. }
  694. $listAnswerResults['studentscore'][] = $listAnswerResults['tabwords'][$i];
  695. }
  696. $listAnswerResults['tabwords'] = $listWords;
  697. $listAnswerResults['tabwordsbracket'] = $listBrackets;
  698. // if we are in student view, we've got 3 times :::::: for common words
  699. $commonWords = api_preg_replace("/::::::/", "::", $commonWords);
  700. }
  701. $listAnswerResults['commonwords'] = explode("::", $commonWords);
  702. return $listAnswerResults;
  703. }
  704. /**
  705. * Return an array of student state answers for fill the blank questions
  706. * for each students that answered the question
  707. * -2 : didn't answer
  708. * -1 : student answer is wrong
  709. * 0 : student answer is correct
  710. * >0 : for fill the blank question with choice menu, is the index of the student answer (right answer indice is 0)
  711. *
  712. * @param integer $testId
  713. * @param integer $questionId
  714. * @param $studentsIdList
  715. * @param string $startDate
  716. * @param string $endDate
  717. * @param bool $useLastAnswerredAttempt
  718. * @return array
  719. * (
  720. * [student_id] => Array
  721. * (
  722. * [first fill the blank for question] => -1
  723. * [second fill the blank for question] => 2
  724. * [third fill the blank for question] => -1
  725. * )
  726. * )
  727. */
  728. public static function getFillTheBlankTabResult(
  729. $testId,
  730. $questionId,
  731. $studentsIdList,
  732. $startDate,
  733. $endDate,
  734. $useLastAnswerredAttempt = true
  735. ) {
  736. $tblTrackEAttempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  737. $tblTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  738. $courseId = api_get_course_int_id();
  739. // If no user has answered questions, no need to go further. Return empty array.
  740. if (empty($studentsIdList)) {
  741. return array();
  742. }
  743. // request to have all the answers of student for this question
  744. // student may have doing it several time
  745. // student may have not answered the bracket id, in this case, is result of the answer is empty
  746. // we got the less recent attempt first
  747. $sql = 'SELECT * FROM '.$tblTrackEAttempt.' tea
  748. LEFT JOIN '.$tblTrackEExercise.' tee
  749. ON
  750. tee.exe_id = tea.exe_id AND
  751. tea.c_id = '.$courseId.' AND
  752. exe_exo_id = '.$testId.'
  753. WHERE
  754. tee.c_id = '.$courseId.' AND
  755. question_id = '.$questionId.' AND
  756. tea.user_id IN ('.implode(',', $studentsIdList).') AND
  757. tea.tms >= "'.$startDate.'" AND
  758. tea.tms <= "'.$endDate.'"
  759. ORDER BY user_id, tea.exe_id;
  760. ';
  761. $res = Database::query($sql);
  762. $tabUserResult = array();
  763. // foreach attempts for all students starting with his older attempt
  764. while ($data = Database::fetch_array($res)) {
  765. $tabAnswer = FillBlanks::getAnswerInfo($data['answer'], true);
  766. // for each bracket to find in this question
  767. foreach ($tabAnswer['studentanswer'] as $bracketNumber => $studentAnswer) {
  768. if ($tabAnswer['studentanswer'][$bracketNumber] != '') {
  769. // student has answered this bracket, cool
  770. switch (FillBlanks::getFillTheBlankAnswerType($tabAnswer['tabwords'][$bracketNumber])) {
  771. case self::FILL_THE_BLANK_MENU:
  772. // get the indice of the choosen answer in the menu
  773. // we know that the right answer is the first entry of the menu, ie 0
  774. // (remember, menu entries are shuffled when taking the test)
  775. $tabUserResult[$data['user_id']][$bracketNumber] = FillBlanks::getFillTheBlankMenuAnswerNum(
  776. $tabAnswer['tabwords'][$bracketNumber],
  777. $tabAnswer['studentanswer'][$bracketNumber]
  778. );
  779. break;
  780. default:
  781. if (FillBlanks::isGoodStudentAnswer(
  782. $tabAnswer['studentanswer'][$bracketNumber],
  783. $tabAnswer['tabwords'][$bracketNumber]
  784. )
  785. ) {
  786. $tabUserResult[$data['user_id']][$bracketNumber] = 0; // right answer
  787. } else {
  788. $tabUserResult[$data['user_id']][$bracketNumber] = -1; // wrong answer
  789. }
  790. }
  791. } else {
  792. // student didn't answer this bracket
  793. if ($useLastAnswerredAttempt) {
  794. // if we take into account the last answered attempt
  795. if (!isset($tabUserResult[$data['user_id']][$bracketNumber])) {
  796. $tabUserResult[$data['user_id']][$bracketNumber] = -2; // not answered
  797. }
  798. } else {
  799. // we take the last attempt, even if the student answer the question before
  800. $tabUserResult[$data['user_id']][$bracketNumber] = -2; // not answered
  801. }
  802. }
  803. }
  804. }
  805. return $tabUserResult;
  806. }
  807. /**
  808. * Return the number of student that give at leat an answer in the fill the blank test
  809. * @param array $resultList
  810. * @return int
  811. */
  812. public static function getNbResultFillBlankAll($resultList)
  813. {
  814. $outRes = 0;
  815. // for each student in group
  816. foreach ($resultList as $userId => $tabValue) {
  817. $found = false;
  818. // for each bracket, if student has at least one answer ( choice > -2) then he pass the question
  819. foreach ($tabValue as $i => $choice) {
  820. if ($choice > -2 && !$found) {
  821. $outRes++;
  822. $found = true;
  823. }
  824. }
  825. }
  826. return $outRes;
  827. }
  828. /**
  829. * Replace the occurrence of blank word with [correct answer][student answer][answer is correct]
  830. * @param array $listWithStudentAnswer
  831. *
  832. * @return string
  833. */
  834. public static function getAnswerInStudentAttempt($listWithStudentAnswer)
  835. {
  836. $separatorStart = $listWithStudentAnswer['blankseparatorstart'];
  837. $separatorEnd = $listWithStudentAnswer['blankseparatorend'];
  838. // lets rebuild the sentence with [correct answer][student answer][answer is correct]
  839. $result = "";
  840. for ($i=0; $i < count($listWithStudentAnswer['commonwords']) - 1; $i++) {
  841. $result .= $listWithStudentAnswer['commonwords'][$i];
  842. $result .= $listWithStudentAnswer['tabwordsbracket'][$i];
  843. $result .= $separatorStart.$listWithStudentAnswer['studentanswer'][$i].$separatorEnd;
  844. $result .= $separatorStart.$listWithStudentAnswer['studentscore'][$i].$separatorEnd;
  845. }
  846. $result .= $listWithStudentAnswer['commonwords'][$i];
  847. $result .= "::";
  848. // add the system string
  849. $result .= $listWithStudentAnswer['systemstring'];
  850. return $result;
  851. }
  852. /**
  853. * This function is the same than the js one above getBlankSeparatorRegexp
  854. * @param string $inChar
  855. *
  856. * @return string
  857. */
  858. public static function escapeForRegexp($inChar)
  859. {
  860. $listChars = [
  861. ".",
  862. "+",
  863. "*",
  864. "?",
  865. "[",
  866. "^",
  867. "]",
  868. "$",
  869. "(",
  870. ")",
  871. "{",
  872. "}",
  873. "=",
  874. "!",
  875. ">",
  876. "|",
  877. ":",
  878. "-",
  879. ")",
  880. ];
  881. if (in_array($inChar, $listChars)) {
  882. return "\\".$inChar;
  883. } else {
  884. return $inChar;
  885. }
  886. }
  887. /**
  888. * return $text protected for use in regexp
  889. * @param string $text
  890. *
  891. * @return string
  892. */
  893. public static function getRegexpProtected($text)
  894. {
  895. $listRegexpCharacters = [
  896. "/",
  897. ".",
  898. "+",
  899. "*",
  900. "?",
  901. "[",
  902. "^",
  903. "]",
  904. "$",
  905. "(",
  906. ")",
  907. "{",
  908. "}",
  909. "=",
  910. "!",
  911. ">",
  912. "|",
  913. ":",
  914. "-",
  915. ")",
  916. ];
  917. $result = $text;
  918. for ($i=0; $i < count($listRegexpCharacters); $i++) {
  919. $result = str_replace($listRegexpCharacters[$i], "\\".$listRegexpCharacters[$i], $result);
  920. }
  921. return $result;
  922. }
  923. /**
  924. * This function must be the same than the js one getSeparatorFromNumber above
  925. * @return array
  926. */
  927. public static function getAllowedSeparator()
  928. {
  929. $fillBlanksAllowedSeparator = array(
  930. array('[', ']'),
  931. array('{', '}'),
  932. array('(', ')'),
  933. array('*', '*'),
  934. array('#', '#'),
  935. array('%', '%'),
  936. array('$', '$'),
  937. );
  938. return $fillBlanksAllowedSeparator;
  939. }
  940. /**
  941. * return the start separator for answer
  942. * @param string $number
  943. *
  944. * @return string
  945. */
  946. public static function getStartSeparator($number)
  947. {
  948. $listSeparators = self::getAllowedSeparator();
  949. return $listSeparators[$number][0];
  950. }
  951. /**
  952. * return the end separator for answer
  953. * @param string $number
  954. *
  955. * @return string
  956. */
  957. public static function getEndSeparator($number)
  958. {
  959. $listSeparators = self::getAllowedSeparator();
  960. return $listSeparators[$number][1];
  961. }
  962. /**
  963. * Return as a description text, array of allowed separators for question
  964. * eg: array("[...]", "(...)")
  965. * @return array
  966. */
  967. public static function getAllowedSeparatorForSelect()
  968. {
  969. $listResults = array();
  970. $fillBlanksAllowedSeparator = self::getAllowedSeparator();
  971. for ($i=0; $i < count($fillBlanksAllowedSeparator); $i++) {
  972. $listResults[] = $fillBlanksAllowedSeparator[$i][0]."...".$fillBlanksAllowedSeparator[$i][1];
  973. }
  974. return $listResults;
  975. }
  976. /**
  977. * return the code number of the separator for the question
  978. * @param string $startSeparator
  979. * @param string $endSeparator
  980. *
  981. * @return int
  982. */
  983. public function getDefaultSeparatorNumber($startSeparator, $endSeparator)
  984. {
  985. $listSeparators = self::getAllowedSeparator();
  986. $result = 0;
  987. for ($i=0; $i < count($listSeparators); $i++) {
  988. if ($listSeparators[$i][0] == $startSeparator &&
  989. $listSeparators[$i][1] == $endSeparator
  990. ) {
  991. $result = $i;
  992. }
  993. }
  994. return $result;
  995. }
  996. /**
  997. * return the HTML display of the answer
  998. * @param string $answer
  999. * @param bool $resultsDisabled
  1000. * @param bool $showTotalScoreAndUserChoices
  1001. *
  1002. * @return string
  1003. */
  1004. public static function getHtmlDisplayForAnswer(
  1005. $answer,
  1006. $resultsDisabled = false,
  1007. $showTotalScoreAndUserChoices = false
  1008. ) {
  1009. $result = '';
  1010. $listStudentAnswerInfo = self::getAnswerInfo($answer, true);
  1011. if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
  1012. if ($showTotalScoreAndUserChoices) {
  1013. $resultsDisabled = false;
  1014. } else {
  1015. $resultsDisabled = true;
  1016. }
  1017. }
  1018. // rebuild the answer with good HTML style
  1019. // this is the student answer, right or wrong
  1020. for ($i=0; $i < count($listStudentAnswerInfo['studentanswer']); $i++) {
  1021. if ($listStudentAnswerInfo['studentscore'][$i] == 1) {
  1022. $listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlRightAnswer(
  1023. $listStudentAnswerInfo['studentanswer'][$i],
  1024. $listStudentAnswerInfo['tabwords'][$i],
  1025. $resultsDisabled
  1026. );
  1027. } else {
  1028. $listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlWrongAnswer(
  1029. $listStudentAnswerInfo['studentanswer'][$i],
  1030. $listStudentAnswerInfo['tabwords'][$i],
  1031. $resultsDisabled
  1032. );
  1033. }
  1034. }
  1035. // rebuild the sentence with student answer inserted
  1036. for ($i=0; $i < count($listStudentAnswerInfo['commonwords']); $i++) {
  1037. $result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
  1038. $result .= isset($listStudentAnswerInfo['studentanswer'][$i]) ? $listStudentAnswerInfo['studentanswer'][$i] : '';
  1039. }
  1040. // the last common word (should be </p>)
  1041. $result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
  1042. return $result;
  1043. }
  1044. /**
  1045. * return the HTML code of answer for correct and wrong answer
  1046. * @param string $answer
  1047. * @param string $correct
  1048. * @param string $right
  1049. * @param bool $resultsDisabled
  1050. *
  1051. * @return string
  1052. */
  1053. public static function getHtmlAnswer($answer, $correct, $right, $resultsDisabled = false)
  1054. {
  1055. $style = "color: green";
  1056. if (!$right) {
  1057. $style = "color: red; text-decoration: line-through;";
  1058. }
  1059. $type = FillBlanks::getFillTheBlankAnswerType($correct);
  1060. switch ($type) {
  1061. case self::FILL_THE_BLANK_MENU:
  1062. $correctAnswerHtml = '';
  1063. $listPossibleAnswers = FillBlanks::getFillTheBlankMenuAnswers($correct, false);
  1064. $correctAnswerHtml .= "<span style='color: green'>".$listPossibleAnswers[0]."</span>";
  1065. $correctAnswerHtml .= " <span style='font-weight:normal'>(";
  1066. for ($i=1; $i < count($listPossibleAnswers); $i++) {
  1067. $correctAnswerHtml .= $listPossibleAnswers[$i];
  1068. if ($i != count($listPossibleAnswers) - 1) {
  1069. $correctAnswerHtml .= " | ";
  1070. }
  1071. }
  1072. $correctAnswerHtml .= ")</span>";
  1073. break;
  1074. case self::FILL_THE_BLANK_SEVERAL_ANSWER:
  1075. $listCorrects = explode("||", $correct);
  1076. $firstCorrect = $correct;
  1077. if (count($listCorrects) > 0) {
  1078. $firstCorrect = $listCorrects[0];
  1079. }
  1080. $correctAnswerHtml = "<span style='color: green'>".$firstCorrect."</span>";
  1081. break;
  1082. case self::FILL_THE_BLANK_STANDARD:
  1083. default:
  1084. $correctAnswerHtml = "<span style='color: green'>".$correct."</span>";
  1085. }
  1086. if ($resultsDisabled) {
  1087. $correctAnswerHtml = "<span title='".get_lang("ExerciseWithFeedbackWithoutCorrectionComment")."'> - </span>";
  1088. }
  1089. $result = "<span style='border:1px solid black; border-radius:5px; padding:2px; font-weight:bold;'>";
  1090. $result .= "<span style='$style'>".$answer."</span>";
  1091. $result .= "&nbsp;<span style='font-size:120%;'>/</span>&nbsp;";
  1092. $result .= $correctAnswerHtml;
  1093. $result .= "</span>";
  1094. return $result;
  1095. }
  1096. /**
  1097. * return HTML code for correct answer
  1098. * @param string $answer
  1099. * @param string $correct
  1100. * @param bool $resultsDisabled
  1101. *
  1102. * @return string
  1103. */
  1104. public static function getHtmlRightAnswer($answer, $correct, $resultsDisabled = false)
  1105. {
  1106. return self::getHtmlAnswer($answer, $correct, true, $resultsDisabled);
  1107. }
  1108. /**
  1109. * return HTML code for wrong answer
  1110. * @param string $answer
  1111. * @param string $correct
  1112. * @param bool $resultsDisabled
  1113. *
  1114. * @return string
  1115. */
  1116. public static function getHtmlWrongAnswer($answer, $correct, $resultsDisabled = false)
  1117. {
  1118. return self::getHtmlAnswer($answer, $correct, false, $resultsDisabled);
  1119. }
  1120. /**
  1121. * Check if a answer is correct by its text
  1122. * @param string $answerText
  1123. * @return bool
  1124. */
  1125. public static function isCorrect($answerText)
  1126. {
  1127. $answerInfo = FillBlanks::getAnswerInfo($answerText, true);
  1128. $correctAnswerList = $answerInfo['tabwords'];
  1129. $studentAnswer = $answerInfo['studentanswer'];
  1130. $isCorrect = true;
  1131. foreach ($correctAnswerList as $i => $correctAnswer) {
  1132. $isGoodStudentAnswer = FillBlanks::isGoodStudentAnswer($studentAnswer[$i], $correctAnswer);
  1133. $isCorrect = $isCorrect && $isGoodStudentAnswer;
  1134. }
  1135. return $isCorrect;
  1136. }
  1137. }