2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 defined('MOODLE_INTERNAL') OR die('not allowed');
18 require_once($CFG->dirroot.'/mod/feedback/item/feedback_item_class.php');
20 define('FEEDBACK_MULTICHOICE_TYPE_SEP', '>>>>>');
21 define('FEEDBACK_MULTICHOICE_LINE_SEP', '|');
22 define('FEEDBACK_MULTICHOICE_ADJUST_SEP', '<<<<<');
23 define('FEEDBACK_MULTICHOICE_IGNOREEMPTY', 'i');
24 define('FEEDBACK_MULTICHOICE_HIDENOSELECT', 'h');
26 class feedback_item_multichoice extends feedback_item_base {
27 protected $type = "multichoice";
28 private $commonparams;
32 public function init() {
36 public function build_editform($item, $feedback, $cm) {
38 require_once('multichoice_form.php');
40 //get the lastposition number of the feedback_items
41 $position = $item->position;
42 $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
43 if ($position == -1) {
44 $i_formselect_last = $lastposition + 1;
45 $i_formselect_value = $lastposition + 1;
46 $item->position = $lastposition + 1;
48 $i_formselect_last = $lastposition;
49 $i_formselect_value = $item->position;
51 //the elements for position dropdownlist
52 $positionlist = array_slice(range(0, $i_formselect_last), 1, $i_formselect_last, true);
54 $item->presentation = empty($item->presentation) ? '' : $item->presentation;
55 $info = $this->get_info($item);
57 $item->ignoreempty = $this->ignoreempty($item);
58 $item->hidenoselect = $this->hidenoselect($item);
60 //all items for dependitem
61 $feedbackitems = feedback_get_depend_candidates_for_item($feedback, $item);
62 $commonparams = array('cmid'=>$cm->id,
63 'id'=>isset($item->id) ? $item->id : null,
65 'items'=>$feedbackitems,
66 'feedback'=>$feedback->id);
69 $customdata = array('item' => $item,
70 'common' => $commonparams,
71 'positionlist' => $positionlist,
72 'position' => $position,
75 $this->item_form = new feedback_multichoice_form('edit_item.php', $customdata);
78 //this function only can used after the call of build_editform()
79 public function show_editform() {
80 $this->item_form->display();
83 public function is_cancelled() {
84 return $this->item_form->is_cancelled();
87 public function get_data() {
88 if ($this->item = $this->item_form->get_data()) {
94 public function save_item() {
97 if (!$item = $this->item_form->get_data()) {
101 if (isset($item->clone_item) AND $item->clone_item) {
102 $item->id = ''; //to clone this item
106 $this->set_ignoreempty($item, $item->ignoreempty);
107 $this->set_hidenoselect($item, $item->hidenoselect);
109 $item->hasvalue = $this->get_hasvalue();
111 $item->id = $DB->insert_record('feedback_item', $item);
113 $DB->update_record('feedback_item', $item);
116 return $DB->get_record('feedback_item', array('id'=>$item->id));
120 //gets an array with three values(typ, name, XXX)
121 //XXX is an object with answertext, answercount and quotient
122 public function get_analysed($item, $groupid = false, $courseid = false) {
123 $info = $this->get_info($item);
125 $analysed_item = array();
126 $analysed_item[] = $item->typ;
127 $analysed_item[] = $item->name;
129 //get the possible answers
131 $answers = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
132 if (!is_array($answers)) {
137 $values = feedback_get_group_values($item, $groupid, $courseid, $this->ignoreempty($item));
142 //get answertext, answercount and quotient for each answer
143 $analysed_answer = array();
144 if ($info->subtype == 'c') {
145 $sizeofanswers = count($answers);
146 for ($i = 1; $i <= $sizeofanswers; $i++) {
147 $ans = new stdClass();
148 $ans->answertext = $answers[$i-1];
149 $ans->answercount = 0;
150 foreach ($values as $value) {
151 //ist die Antwort gleich dem index der Antworten + 1?
152 $vallist = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value->value);
153 foreach ($vallist as $val) {
159 $ans->quotient = $ans->answercount / count($values);
160 $analysed_answer[] = $ans;
163 $sizeofanswers = count($answers);
164 for ($i = 1; $i <= $sizeofanswers; $i++) {
165 $ans = new stdClass();
166 $ans->answertext = $answers[$i-1];
167 $ans->answercount = 0;
168 foreach ($values as $value) {
169 //ist die Antwort gleich dem index der Antworten + 1?
170 if ($value->value == $i) {
174 $ans->quotient = $ans->answercount / count($values);
175 $analysed_answer[] = $ans;
178 $analysed_item[] = $analysed_answer;
179 return $analysed_item;
182 public function get_printval($item, $value) {
183 $info = $this->get_info($item);
187 if (!isset($value->value)) {
191 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
193 if ($info->subtype == 'c') {
194 $vallist = array_values(explode (FEEDBACK_MULTICHOICE_LINE_SEP, $value->value));
195 $sizeofvallist = count($vallist);
196 $sizeofpresentation = count($presentation);
197 for ($i = 0; $i < $sizeofvallist; $i++) {
198 for ($k = 0; $k < $sizeofpresentation; $k++) {
199 if ($vallist[$i] == ($k + 1)) {//Die Werte beginnen bei 1, das Array aber mit 0
200 $printval .= trim($presentation[$k]) . chr(10);
207 foreach ($presentation as $pres) {
208 if ($value->value == $index) {
218 public function print_analysed($item, $itemnr = '', $groupid = false, $courseid = false) {
220 $sep_dec = get_string('separator_decimal', 'feedback');
221 if (substr($sep_dec, 0, 2) == '[[') {
222 $sep_dec = FEEDBACK_DECIMAL;
225 $sep_thous = get_string('separator_thousand', 'feedback');
226 if (substr($sep_thous, 0, 2) == '[[') {
227 $sep_thous = FEEDBACK_THOUSAND;
230 $analysed_item = $this->get_analysed($item, $groupid, $courseid);
231 if ($analysed_item) {
232 $itemname = $analysed_item[1];
233 echo '<tr><th colspan="2" align="left">';
235 if (strval($item->label) !== '') {
236 echo '('. format_string($item->label).') ';
241 $analysed_vals = $analysed_item[2];
243 foreach ($analysed_vals as $val) {
244 $intvalue = $pixnr % 10;
245 $pix = $OUTPUT->pix_url('multichoice/' . $intvalue, 'feedback');
247 $pixwidth = intval($val->quotient * FEEDBACK_MAX_PIX_LENGTH);
248 $quotient = number_format(($val->quotient * 100), 2, $sep_dec, $sep_thous);
250 if ($val->quotient > 0) {
251 $str_quotient = ' ('. $quotient . ' %)';
254 echo '<td align="left" valign="top">
255 - '.trim($val->answertext).':
257 <td align="left" style="width:'.FEEDBACK_MAX_PIX_LENGTH.';">
258 <img class="feedback_bar_image" alt="'.$intvalue.'" src="'.$pix.'" height="5" width="'.$pixwidth.'" />
259 '.$val->answercount.$str_quotient.'
266 public function excelprint_item(&$worksheet, $row_offset,
268 $groupid, $courseid = false) {
270 $analysed_item = $this->get_analysed($item, $groupid, $courseid);
272 $data = $analysed_item[2];
275 $worksheet->write_string($row_offset, 0, $item->label, $xls_formats->head2);
276 $worksheet->write_string($row_offset, 1, $analysed_item[1], $xls_formats->head2);
277 if (is_array($data)) {
278 $sizeofdata = count($data);
279 for ($i = 0; $i < $sizeofdata; $i++) {
280 $analysed_data = $data[$i];
282 $worksheet->write_string($row_offset,
284 trim($analysed_data->answertext),
285 $xls_formats->head2);
287 $worksheet->write_number($row_offset + 1,
289 $analysed_data->answercount,
290 $xls_formats->default);
292 $worksheet->write_number($row_offset + 2,
294 $analysed_data->quotient,
295 $xls_formats->procent);
303 * print the item at the edit-page of feedback
306 * @param object $item
309 public function print_item_preview($item) {
311 $info = $this->get_info($item);
312 $align = right_to_left() ? 'right' : 'left';
314 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
315 $strrequiredmark = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.
316 get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />';
318 //test if required and no value is set so we have to mark this item
319 //we have to differ check and the other subtypes
320 $requiredmark = ($item->required == 1) ? $strrequiredmark : '';
322 //print the question and label
323 echo '<div class="feedback_item_label_'.$align.'">';
324 if ($info->subtype == 'd') {
325 echo '<label for="'. $item->typ . '_' . $item->id .'">';
327 if (strval($item->label) !== '') {
328 echo '('. format_string($item->label).') ';
330 echo format_text($item->name . $requiredmark, FORMAT_HTML, array('noclean' => true, 'para' => false));
331 if ($item->dependitem) {
332 if ($dependitem = $DB->get_record('feedback_item', array('id'=>$item->dependitem))) {
333 echo ' <span class="feedback_depend">';
334 echo '('.format_string($dependitem->label).'->'.$item->dependvalue.')';
338 if ($info->subtype == 'd') {
343 //print the presentation
344 echo '<div class="feedback_item_presentation_'.$align.'">';
347 if ($info->subtype == 'r' || $info->subtype == 'c') {
348 // if (r)adio buttons or (c)heckboxes
353 if ($info->horizontal) {
359 if ($info->subtype == 'r' AND !$this->hidenoselect($item)) {
360 //print the "not_selected" item on radiobuttons
362 <li class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
363 <span class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
365 echo '<input type="radio" '.
366 'name="'.$item->typ.'_'.$item->id.'[]" '.
367 'id="'.$item->typ.'_'.$item->id.'_xxx" '.
368 'value="" checked="checked" />';
371 <span class="feedback_item_radiolabel_<?php echo $hv.'_'.$align;?>">
372 <label for="<?php echo $item->typ . '_' . $item->id.'_xxx';?>">
373 <?php print_string('not_selected', 'feedback');?>
380 switch($info->subtype) {
382 $this->print_item_radio($presentation, $item, false, $info, $align);
385 $this->print_item_check($presentation, $item, false, $info, $align);
388 $this->print_item_dropdown($presentation, $item, false, $info, $align);
391 if ($info->subtype == 'r' || $info->subtype == 'c') {
392 // if (r)adio buttons or (c)heckboxes
400 * print the item at the complete-page of feedback
403 * @param object $item
404 * @param string $value
405 * @param bool $highlightrequire
408 public function print_item_complete($item, $value = null, $highlightrequire = false) {
410 $info = $this->get_info($item);
411 $align = right_to_left() ? 'right' : 'left';
413 if ($value == null) {
416 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
417 $strrequiredmark = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.
418 get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />';
420 //test if required and no value is set so we have to mark this item
421 //we have to differ check and the other subtypes
422 if (is_array($value)) {
425 $values = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value);
427 $requiredmark = ($item->required == 1) ? $strrequiredmark : '';
429 //print the question and label
430 $inputname = $item->typ . '_' . $item->id;
431 echo '<div class="feedback_item_label_'.$align.'">';
432 if ($info->subtype == 'd') {
433 echo '<label for="'. $inputname .'">';
434 echo format_text($item->name . $requiredmark, FORMAT_HTML, array('noclean' => true, 'para' => false));
435 if ($highlightrequire AND $item->required AND (count($values) == 0 OR $values[0] == '' OR $values[0] == 0)) {
436 echo '<br class="error"><span id="id_error_'.$inputname.'" class="error"> '.get_string('err_required', 'form').
437 '</span><br id="id_error_break_'.$inputname.'" class="error" >';
441 echo format_text($item->name . $requiredmark, FORMAT_HTML, array('noclean' => true, 'para' => false));
442 if ($highlightrequire AND $item->required AND (count($values) == 0 OR $values[0] == '' OR $values[0] == 0)) {
443 echo '<br class="error"><span id="id_error_'.$inputname.'" class="error"> '.get_string('err_required', 'form').
444 '</span><br id="id_error_break_'.$inputname.'" class="error" >';
449 //print the presentation
450 echo '<div class="feedback_item_presentation_'.$align.'">';
452 if ($info->subtype == 'r' || $info->subtype == 'c') {
453 // if (r)adio buttons or (c)heckboxes
457 if ($info->horizontal) {
462 //print the "not_selected" item on radiobuttons
463 if ($info->subtype == 'r' AND !$this->hidenoselect($item)) {
465 <li class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
466 <span class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
470 // $checked = 'checked="checked"';
472 if (count($values) == 0 OR $values[0] == '' OR $values[0] == 0) {
473 $checked = 'checked="checked"';
475 echo '<input type="radio" '.
476 'name="'.$item->typ.'_'.$item->id.'[]" '.
477 'id="'.$item->typ.'_'.$item->id.'_xxx" '.
478 'value="" '.$checked.' />';
481 <span class="feedback_item_radiolabel_<?php echo $hv.'_'.$align;?>">
482 <label for="<?php echo $item->typ.'_'.$item->id.'_xxx';?>">
483 <?php print_string('not_selected', 'feedback');?>
490 switch($info->subtype) {
492 $this->print_item_radio($presentation, $item, $value, $info, $align);
495 $this->print_item_check($presentation, $item, $value, $info, $align);
498 $this->print_item_dropdown($presentation, $item, $value, $info, $align);
501 if ($info->subtype == 'r' || $info->subtype == 'c') {
502 // if (r)adio buttons or (c)heckboxes
510 * print the item at the complete-page of feedback
513 * @param object $item
514 * @param string $value
517 public function print_item_show_value($item, $value = null) {
519 $info = $this->get_info($item);
520 $align = right_to_left() ? 'right' : 'left';
522 if ($value == null) {
526 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
528 //test if required and no value is set so we have to mark this item
529 //we have to differ check and the other subtypes
530 if ($info->subtype == 'c') {
531 if (is_array($value)) {
534 $values = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value);
538 if ($item->required == 1) {
539 $requiredmark = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.
540 get_string('requiredelement', 'form').'" src="'.$OUTPUT->pix_url('req') .'" />';
543 //print the question and label
544 echo '<div class="feedback_item_label_'.$align.'">';
545 if (strval($item->label) !== '') {
546 echo '('. format_string($item->label).') ';
548 echo format_text($item->name . $requiredmark, FORMAT_HTML, array('noclean' => true, 'para' => false));
551 //print the presentation
552 echo '<div class="feedback_item_presentation_'.$align.'">';
554 if ($info->subtype == 'c') {
555 echo $OUTPUT->box_start('generalbox boxalign'.$align);
556 foreach ($presentation as $pres) {
557 foreach ($values as $val) {
558 if ($val == $index) {
559 echo '<div class="feedback_item_multianswer">';
560 echo format_text($pres, FORMAT_HTML, array('noclean' => true, 'para' => false));
567 echo $OUTPUT->box_end();
569 foreach ($presentation as $pres) {
570 if ($value == $index) {
571 echo $OUTPUT->box_start('generalbox boxalign'.$align);
572 echo format_text($pres, FORMAT_HTML, array('noclean' => true, 'para' => false));
573 echo $OUTPUT->box_end();
582 public function check_value($value, $item) {
583 $info = $this->get_info($item);
585 if ($item->required != 1) {
589 if (empty($value) OR !is_array($value) OR !array_filter($value)) {
596 public function create_value($data) {
598 if (is_array($vallist)) {
599 $vallist = array_unique(array_filter($vallist));
601 return trim($this->item_array_to_string($vallist));
604 //compares the dbvalue with the dependvalue
605 //dbvalue is the number of one selection
606 //dependvalue is the presentation of one selection
607 public function compare_value($item, $dbvalue, $dependvalue) {
609 if (is_array($dbvalue)) {
610 $dbvalues = $dbvalue;
612 $dbvalues = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $dbvalue);
615 $info = $this->get_info($item);
616 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
618 foreach ($presentation as $pres) {
619 foreach ($dbvalues as $dbval) {
620 if ($dbval == $index AND trim($pres) == $dependvalue) {
629 public function get_presentation($data) {
630 $present = str_replace("\n", FEEDBACK_MULTICHOICE_LINE_SEP, trim($data->itemvalues));
631 if (!isset($data->subtype)) {
634 $subtype = substr($data->subtype, 0, 1);
636 if (isset($data->horizontal) AND $data->horizontal == 1 AND $subtype != 'd') {
637 $present .= FEEDBACK_MULTICHOICE_ADJUST_SEP.'1';
639 return $subtype.FEEDBACK_MULTICHOICE_TYPE_SEP.$present;
642 public function get_hasvalue() {
646 public function get_info($item) {
647 $presentation = empty($item->presentation) ? '' : $item->presentation;
649 $info = new stdClass();
650 //check the subtype of the multichoice
651 //it can be check(c), radio(r) or dropdown(d)
653 $info->presentation = '';
654 $info->horizontal = false;
656 $parts = explode(FEEDBACK_MULTICHOICE_TYPE_SEP, $item->presentation);
657 @list($info->subtype, $info->presentation) = $parts;
658 if (!isset($info->subtype)) {
659 $info->subtype = 'r';
662 if ($info->subtype != 'd') {
663 $parts = explode(FEEDBACK_MULTICHOICE_ADJUST_SEP, $info->presentation);
664 @list($info->presentation, $info->horizontal) = $parts;
665 if (isset($info->horizontal) AND $info->horizontal == 1) {
666 $info->horizontal = true;
668 $info->horizontal = false;
674 private function item_array_to_string($value) {
675 if (!is_array($value)) {
682 $arrvals = array_values($value);
683 $arrvals = clean_param_array($arrvals, PARAM_INT); //prevent sql-injection
684 $retval = $arrvals[0];
685 $sizeofarrvals = count($arrvals);
686 for ($i = 1; $i < $sizeofarrvals; $i++) {
687 $retval .= FEEDBACK_MULTICHOICE_LINE_SEP.$arrvals[$i];
692 private function print_item_radio($presentation, $item, $value, $info, $align) {
696 if (is_array($value)) {
699 $values = array($value);
702 if ($info->horizontal) {
708 foreach ($presentation as $radio) {
709 foreach ($values as $val) {
710 if ($val == $index) {
711 $checked = 'checked="checked"';
717 $inputname = $item->typ . '_' . $item->id;
718 $inputid = $inputname.'_'.$index;
720 <li class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
721 <span class="feedback_item_radio_<?php echo $hv.'_'.$align;?>">
723 echo '<input type="radio" '.
724 'name="'.$inputname.'[]" '.
725 'id="'.$inputid.'" '.
726 'value="'.$index.'" '.$checked.' />';
729 <span class="feedback_item_radiolabel_<?php echo $hv.'_'.$align;?>">
730 <label for="<?php echo $inputid;?>">
731 <?php echo format_text($radio, FORMAT_HTML, array('noclean' => true, 'para' => false));?>
740 private function print_item_check($presentation, $item, $value, $info, $align) {
742 if (is_array($value)) {
745 $values = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value);
748 if ($info->horizontal) {
756 $inputname = $item->typ. '_' . $item->id;
757 echo '<input type="hidden" name="'.$inputname.'[]" value="0" />';
758 foreach ($presentation as $check) {
759 foreach ($values as $val) {
760 if ($val == $index) {
761 $checked = 'checked="checked"';
767 $inputid = $item->typ. '_' . $item->id.'_'.$index;
769 <li class="feedback_item_check_<?php echo $hv.'_'.$align;?>">
770 <span class="feedback_item_check_<?php echo $hv.'_'.$align;?>">
772 echo '<input type="checkbox" '.
773 'name="'.$inputname.'[]" '.
774 'id="'.$inputid.'" '.
775 'value="'.$index.'" '.$checked.' />';
778 <span class="feedback_item_radiolabel_<?php echo $hv.'_'.$align;?>">
779 <label for="<?php echo $inputid;?>">
780 <?php echo format_text($check, FORMAT_HTML, array('noclean' => true, 'para' => false));?>
789 private function print_item_dropdown($presentation, $item, $value, $info, $align) {
790 if (is_array($value)) {
793 $values = array($value);
796 if ($info->horizontal) {
803 <div class="feedback_item_select_<?php echo $hv.'_'.$align;?>">
804 <select id="<?php echo $item->typ .'_' . $item->id;?>" name="<?php echo $item->typ .'_' . $item->id;?>[]" size="1">
805 <option value="0"> </option>
809 foreach ($presentation as $dropdown) {
810 foreach ($values as $val) {
811 if ($val == $index) {
812 $selected = 'selected="selected"';
819 <option value="<?php echo $index;?>" <?php echo $selected;?>>
820 <?php echo format_text($dropdown, FORMAT_HTML, array('noclean' => true, 'para' => false));?>
831 public function set_ignoreempty($item, $ignoreempty=true) {
832 $item->options = str_replace(FEEDBACK_MULTICHOICE_IGNOREEMPTY, '', $item->options);
834 $item->options .= FEEDBACK_MULTICHOICE_IGNOREEMPTY;
838 public function ignoreempty($item) {
839 if (strstr($item->options, FEEDBACK_MULTICHOICE_IGNOREEMPTY)) {
845 public function set_hidenoselect($item, $hidenoselect=true) {
846 $item->options = str_replace(FEEDBACK_MULTICHOICE_HIDENOSELECT, '', $item->options);
848 $item->options .= FEEDBACK_MULTICHOICE_HIDENOSELECT;
852 public function hidenoselect($item) {
853 if (strstr($item->options, FEEDBACK_MULTICHOICE_HIDENOSELECT)) {
859 public function can_switch_require() {
863 public function value_type() {
867 public function value_is_array() {
871 public function clean_input_value($value) {
872 return clean_param_array($value, $this->value_type());