Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
online_quiz_question_multichoice.inc
1 <?php
18 require_once SQ_PACKAGES_PATH.'/cms/page_templates/page_online_quiz/online_quiz_question/online_quiz_question.inc';
19 require_once SQ_LIB_PATH.'/html_form/html_form.inc';
20 
21 
39 {
40 
41 
50  function Online_Quiz_Question_Multichoice($assetid=0)
51  {
52  $this->_ser_attrs = TRUE;
53  $this->Online_Quiz_Question($assetid);
54 
55  }//end constructor
56 
57 
67  function setAttrValue($name, $value)
68  {
69  switch ($name) {
70  case 'max_option_selections':
71  if ($value < 1) $value = 1;
72  break;
73  }
74  return parent::setAttrValue($name, $value);
75 
76  }//end setAttrValue()
77 
78 
94  {
95  $prefix = $this->getPrefix();
96 
97  switch ($this->attr('option_style')) {
98  case 'list':
99  if ($this->attr('max_option_selections') <= 1) {
100  $value = array_get_index($_REQUEST, $prefix.'_response', NULL);
101  } else {
102  // Request format: Array(
103  // 0 => option_key,
104  // )
105  $value = array_get_index($_REQUEST, $prefix.'_response', Array());
106  }
107  break;
108 
109  case 'radio':
110  if ($this->attr('max_option_selections') <= 1) {
111  $value = array_get_index($_REQUEST, $prefix.'_response', NULL);
112  } else {
113  // Request format: Array(
114  // option_key => is_checked
115  // )
116  $value = array_keys(array_get_index($_REQUEST, $prefix.'_response', Array()));
117  }
118  break;
119  }//end switch (option_style)
120 
121  $valid = $this->validateValue($value);
122  $errors = array_get_index($valid, 'errors', Array());
123  if (empty($errors)) {
124  $this->_value = $value;
125  $this->saveQuestion();
126  }
127 
128  // return the error stack
129  return $valid;
130 
131  }//end processResponseForm()
132 
133 
153  function validateValue($value=NULL)
154  {
155  if (is_null($value)) $value = $this->_value;
156 
157  $errors = Array(
158  'errors' => Array(),
159  'warnings' => Array(),
160  );
161 
162  if (empty($value)) {
163  $errors['warnings'][] = translate('online_quiz_question_warning_empty_question', $this->getPosition());
164  }
165 
166  return $errors;
167 
168  }//end validateValue()
169 
170 
177  function getValidationJS()
178  {
179  static $printed_fn = FALSE;
180 
181  $prefix = $this->getPrefix();
182 
183  ob_start();
184  if (!$printed_fn) {
185  // the "< echo translate('string', '%s'); >".replace(/%s/, question) is
186  // a hack to insert the question number in a translation string
187  // without using js_translate (which is unsupported on the frontend)
188  ?>
189  function <?php echo $this->type(); ?>_validate(error_stack, question, answer)
190  {
191  if (!answer.length) {
192  error_stack["warnings"][error_stack["warnings"].length] = "<?php echo translate('online_quiz_question_warning_empty_question', '%s'); ?>".replace('%s', question);
193  }
194  }
195  <?php
196  $printed_fn = TRUE;
197  }
198 
199  $options = $this->attr('response_form');
200 
201  // figure out which option(s) are selected
202  ?>
203  var selected = new Array();
204  <?php
205 
206  if ($this->attr('option_style') == 'radio') {
207  // checkboxes and radio buttons use the same format for their IDs
208  foreach ($options as $option_key => $option) {
209  ?>
210  if (document.getElementById("<?php echo $prefix.'_response_'.$option_key; ?>").checked) selected[selected.length] = "<?php echo $option_key; ?>";
211  <?php
212  }
213  } else {
214  ?>
215  select_box = document.getElementById("<?php echo $prefix.'_response'; ?>");
216  selected[selected.length] = select_box.selectedIndex;
217  <?php
218  }
219  ?>
220  <?php echo $this->type(); ?>_validate(error_stack, "<?php echo $this->getPosition(); ?>", selected);
221  <?php
222 
223  return ob_get_clean();
224 
225  }//end getValidationJS()
226 
227 
237  function getSummary()
238  {
239  $value = $this->getValue();
240  $options = $this->attr('response_form');
241 
242  if ($this->attr('max_option_selections') <= 1) {
243  $selected_option = array_get_index($options, $value, Array());
244  return array_get_index($selected_option, 'text', '');
245  } else {
246  $summary = Array();
247  if (!empty($value)) {
248  foreach ($value as $response) {
249  $selected_option = array_get_index($options, $response, Array());
250  $summary[] = array_get_index($selected_option, 'text', '');
251  }
252  }
253  return $summary;
254  }
255 
256  }//end getSummary()
257 
258 
266  {
267  // figure out which answers return points
268 
269  $options = $this->attr('response_form');
270  $correct_keys = Array();
271  foreach ($options as $option_key => $option) {
272  if ($option['points'] > 0) {
273  $correct_keys[] = $option_key;
274  }
275  }
276 
277  return $correct_keys;
278 
279  }//end _getCorrectOptionKeys()
280 
281 
288  function getCorrectValue()
289  {
290  return $this->_getCorrectOptionKeys();
291 
292  }//end getCorrectValue()
293 
294 
301  function getCorrectSummary()
302  {
303  $options = $this->attr('response_form');
304  $correct_keys = $this->_getCorrectOptionKeys();
305  $summary = Array();
306 
307  foreach ($correct_keys as $key) {
308  if (!empty($options[$key]['text'])) {
309  $summary[] = $options[$key]['text'];
310  }
311  }
312 
313  return $summary;
314 
315  }//end getCorrectSummary()
316 
317 
326  function getPoints($value=NULL)
327  {
328  if (is_null($value)) $value = $this->getValue();
329 
330  $total_points = 0;
331 
332  $options = $this->attr('response_form');
333  $max_selections = $this->attr('max_option_selections');
334  $forfeit_penalty = $this->attr('forfeit_penalty');
335 
336  if (empty($options) || empty($max_selections)) {
337  // invalid, nothing to select; bail without docking points
338  return 0;
339  }
340 
341  // if the nothing was selected, apply the forfeit penalty and bail
342  if (empty($value)) return ($forfeit_penalty * -1);
343 
344  if ($max_selections == 1) {
345 
346  // if an invalid answer was selected - dock points as if they hadn't answered at all
347  if (!isset($options[$value]['points'])) {
348  return ($forfeit_penalty * -1);
349  }
350 
351  $total_points += $options[$value]['points'];
352 
353  } else {
354 
355  // make sure only $max_selections or less options have been selected - otherwise
356  // dock points as if they hadn't answered at all
357  // (this prevents the user just selecting everything and getting the answer right)
358  if (count($value) > $max_selections) {
359  return ($forfeit_penalty * -1);
360  }
361 
362  foreach ($options as $option_key => $option) {
363  if (in_array($option_key, $value)) {
364  $total_points += $option['points'];
365  }
366  }
367 
368  }
369 
370  return $total_points;
371 
372  }//end getPoints()
373 
374 
382  {
383  $total_points = 0;
384 
385  $options = $this->attr('response_form');
386  $max_selections = $this->attr('max_option_selections');
387 
388  if (empty($options) || empty($max_selections)) {
389  // invalid, nothing to select
390  return 0;
391  }
392 
393  // order the questions by points value, then choose the
394  // first $this->attr('max_option_selections') and add them up
395 
396  $option_points = Array();
397  foreach ($options as $option_key => $option) {
398  $option_points[] = $option['points'];
399  }
400  rsort($option_points, SORT_NUMERIC);
401 
402  $slice_min = 0;
403  $slice_max = min(count($option_points), $max_selections);
404 
405  $points_to_count = array_slice($option_points, $slice_min, $slice_max);
406 
407  foreach ($points_to_count as $points_value) {
408  if ($points_value > 0) $total_points += $points_value;
409  }
410 
411  return $total_points;
412 
413  }//end getAvailablePoints()
414 
415 
435  function getResults($flatten=TRUE)
436  {
437  $value = $this->getValue();
438  $correct_value = $this->getCorrectValue();
439  $summary = $this->getSummary();
440  $correct_summary = $this->getCorrectSummary();
441 
442  $points = $this->getPoints();
443  $available = $this->getAvailablePoints();
444 
445  $results = Array(
446  'assetid' => $this->id,
447  'value' => $value,
448  'summary' => $summary,
449  'correct_value' => $correct_value,
450  'correct_summary' => $correct_summary,
451  'points' => $points,
452  'available_points' => $available,
453  );
454 
455  // flatten out the value and summary fields
456  foreach (Array('value', 'correct_value') as $field) {
457  $value = $results[$field];
458  if ($flatten && is_array($value)) {
459  $results[$field] = implode(', ', $value);
460  }
461  }
462 
463  foreach (Array('summary', 'correct_summary') as $field) {
464  $summary = $results[$field];
465  if ($flatten && is_array($summary)) {
466  $results[$field] = implode("\n", $summary);
467  }
468  }
469 
470  return $results;
471 
472  }//end getResults()
473 
474 
475 //-- Keyword Replacements --//
476 
477 
489  function getResponseFormKeywordReplacement($option_format='', $feedback_mode=FALSE)
490  {
491  $prefix = $this->getPrefix();
492 
493  $options = $this->attr('response_form');
494  $use_html_options = $this->attr('use_html_options');
495 
496  $value = $this->getValue();
497  $correct_value = $this->getCorrectValue();
498 
499  ob_start();
500  $class = 'sq-'.str_replace('_', '-', $this->type());
501  $class = $feedback_mode ? $class.' '.$class.'-feedback' : $class;
502  ?><div class="<?php echo $class; ?>"><?php
503 
504  if (empty($options)) {
505  echo translate('online_quiz_question_multichoice_empty_options');
506  } else {
507  switch ($this->attr('option_style')) {
508  case 'list':
509  $selection_contents = Array();
510  foreach ($options as $option_key => $option) {
511  $selection_contents[$option_key] = htmlentities($option['text']);
512  }
513  $extras = ($feedback_mode) ? 'disabled="disabled"' : '';
514  combo_box($prefix.'_response', $selection_contents, ($this->attr('max_option_selections') > 1), $value, 0, $extras);
515  if ($feedback_mode) {
516  foreach ($options as $option_key => $option) {
517  $response_supplement = $this->getResponseSupplement($option_key);
518  if (!empty($response_supplement)){
519  if (in_array($option_key, $correct_value)){
520  ?><div class="online_quiz_response_supplement_correct"><?php echo $response_supplement ?></div><?php
521  } else {
522  ?><div class="online_quiz_response_supplement_incorrect"><?php echo $response_supplement ?></div><?php
523  }
524  }
525  }
526  }
527  break;
528 
529  case 'radio':
530  default:
531  ?><ul><?php
532  $option_number = 1;
533  foreach ($options as $option_key => $option) {
534 
535  if ($feedback_mode){
536  $is_selected = ((is_array($value) && in_array($option_key, $value)) || $option_key == $value);
537  if (in_array($option_key, $correct_value)){
538  if ($is_selected){
539  ?><li class="online_quiz_correct_answer online_quiz_user_selection"><?php
540  } else {
541  ?><li class="online_quiz_correct_answer"><?php
542  }
543  } else {
544  if ($is_selected){
545  ?><li class="online_quiz_incorrect_answer online_quiz_user_selection"><?php
546  } else {
547  ?><li class="online_quiz_incorrect_answer"><?php
548  }
549  }
550  } else {
551  ?><li><?php
552  }
553 
554  $extras = 'id="'.$prefix.'_response_'.$option_key.'"';
555  if ($feedback_mode) $extras .= ' disabled="disabled"';
556 
557  ob_start();
558  if ($this->attr('max_option_selections') <= 1) {
559  radio_button($prefix.'_response', $option_key, ($value == $option_key), '', $extras);
560  } else {
561  if (!is_array($value)) $value = Array();
562  check_box($prefix.'_response['.$option_key.']', '1', (in_array($option_key, $value)), '', $extras);
563  }
564  $option_input_field = ob_get_clean();
565 
566  ob_start();
567  if ($use_html_options) {
568  $matches = Array();
569  preg_match_all('|\./\?a=([0-9]+)|', $option['text'], $matches);
570  $urls = $GLOBALS['SQ_SYSTEM']->am->getAssetURL(array_unique($matches[1]));
571  foreach ($urls as $assetid => $url) {
572  $option['text'] = preg_replace('|\./\?a='.$assetid.'([^0-9])|', $url.'\\1', $option['text']);
573  }
574 
575  label($option['text'], $prefix.'_response_'.$option_key);
576  } else {
577  label(htmlentities($option['text']), $prefix.'_response_'.$option_key);
578  }
579  $option_label = ob_get_clean();
580 
581  ob_start();
582  if ($feedback_mode) {
583  $response_supplement = $this->getResponseSupplement($option_key);
584  if (!empty($response_supplement)){
585  if (in_array($option_key, $correct_value)){
586  ?><div class="online_quiz_response_supplement_correct"><?php echo $response_supplement ?></div><?php
587  } else {
588  ?><div class="online_quiz_response_supplement_incorrect"><?php echo $response_supplement ?></div><?php
589  }
590  }
591  }
592  $option_response_supplement = ob_get_clean();
593 
594  if (!empty($option_format)){
595  $option_format_output = $option_format;
596  $option_format_output = str_replace('%option_number%', $option_number, $option_format_output);
597  $option_format_output = str_replace('%option_input_field%', $option_input_field, $option_format_output);
598  $option_format_output = str_replace('%option_label%', $option_label, $option_format_output);
599  $option_format_output = str_replace('%option_text%', $option['text'], $option_format_output);
600  $option_format_output = str_replace('%option_response_supplement%', $option_response_supplement, $option_format_output);
601  echo $option_format_output;
602  } else {
603  echo $option_input_field.$option_label.$option_response_supplement;
604  }
605  ?></li><?php
606  $option_number++;
607  }
608  ?></ul><?php
609  break;
610  }//end switch (option_style)
611  }//end else (empty($options))
612 
613  ?></div><?php
614 
615  return ob_get_clean();
616 
617  }//end getResponseFormKeywordReplacement()
618 
619 
628  function getResponseSupplement($selected_option=NULL)
629  {
630  $response_supplement = '';
631  $option = Array();
632 
633  $response_options = $this->attr('response_form');
634  if (empty($selected_option)) {
635  $selected_option = $this->getValue();
636  }
637  //we can only provide the response supplement for one selection
638  if (!is_array($selected_option) && isset($response_options[$selected_option])) {
639  $option = $response_options[$selected_option];
640  }
641 
642  if (isset($option['response_supplement'])) {
643  $response_supplement = $option['response_supplement'];
644 
645  // replace internal links to pages and images with their full URLs
646  $matches = Array();
647  preg_match_all('|\./\?a=([0-9]+)|', $response_supplement, $matches);
648  $urls = $GLOBALS['SQ_SYSTEM']->am->getAssetURL(array_unique($matches[1]));
649  foreach ($urls as $assetid => $url) {
650  $response_supplement = preg_replace('|\./\?a='.$assetid.'([^0-9])|', $url.'\\1', $response_supplement);
651  }
652  }
653 
654  return $response_supplement;
655 
656  }//end getResponseSupplement()
657 
658 
659 }//end class
660 ?>