fill_blanks.class.php 49 KB

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