See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 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. 8 // 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. 13 // 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/>. 16 17 defined('MOODLE_INTERNAL') OR die('not allowed'); 18 require_once($CFG->dirroot.'/mod/feedback/item/feedback_item_class.php'); 19 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'); 25 26 class feedback_item_multichoice extends feedback_item_base { 27 protected $type = "multichoice"; 28 29 public function build_editform($item, $feedback, $cm) { 30 global $DB, $CFG; 31 require_once ('multichoice_form.php'); 32 33 //get the lastposition number of the feedback_items 34 $position = $item->position; 35 $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedback->id)); 36 if ($position == -1) { 37 $i_formselect_last = $lastposition + 1; 38 $i_formselect_value = $lastposition + 1; 39 $item->position = $lastposition + 1; 40 } else { 41 $i_formselect_last = $lastposition; 42 $i_formselect_value = $item->position; 43 } 44 //the elements for position dropdownlist 45 $positionlist = array_slice(range(0, $i_formselect_last), 1, $i_formselect_last, true); 46 47 $item->presentation = empty($item->presentation) ? '' : $item->presentation; 48 $info = $this->get_info($item); 49 50 $item->ignoreempty = $this->ignoreempty($item); 51 $item->hidenoselect = $this->hidenoselect($item); 52 53 //all items for dependitem 54 $feedbackitems = feedback_get_depend_candidates_for_item($feedback, $item); 55 $commonparams = array('cmid'=>$cm->id, 56 'id'=>isset($item->id) ? $item->id : null, 57 'typ'=>$item->typ, 58 'items'=>$feedbackitems, 59 'feedback'=>$feedback->id); 60 61 //build the form 62 $customdata = array('item' => $item, 63 'common' => $commonparams, 64 'positionlist' => $positionlist, 65 'position' => $position, 66 'info' => $info); 67 68 $this->item_form = new feedback_multichoice_form('edit_item.php', $customdata); 69 } 70 71 public function save_item() { 72 global $DB; 73 74 if (!$this->get_data()) { 75 return false; 76 } 77 $item = $this->item; 78 79 if (isset($item->clone_item) AND $item->clone_item) { 80 $item->id = ''; //to clone this item 81 $item->position++; 82 } 83 84 $this->set_ignoreempty($item, $item->ignoreempty); 85 $this->set_hidenoselect($item, $item->hidenoselect); 86 87 $item->hasvalue = $this->get_hasvalue(); 88 if (!$item->id) { 89 $item->id = $DB->insert_record('feedback_item', $item); 90 } else { 91 $DB->update_record('feedback_item', $item); 92 } 93 94 return $DB->get_record('feedback_item', array('id'=>$item->id)); 95 } 96 97 98 //gets an array with three values(typ, name, XXX) 99 //XXX is an object with answertext, answercount and quotient 100 101 /** 102 * Helper function for collected data, both for analysis page and export to excel 103 * 104 * @param stdClass $item the db-object from feedback_item 105 * @param int $groupid 106 * @param int $courseid 107 * @return array 108 */ 109 protected function get_analysed($item, $groupid = false, $courseid = false) { 110 $info = $this->get_info($item); 111 112 $analysed_item = array(); 113 $analysed_item[] = $item->typ; 114 $analysed_item[] = format_string($item->name); 115 116 //get the possible answers 117 $answers = null; 118 $answers = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 119 if (!is_array($answers)) { 120 return null; 121 } 122 123 //get the values 124 $values = feedback_get_group_values($item, $groupid, $courseid, $this->ignoreempty($item)); 125 if (!$values) { 126 return null; 127 } 128 129 //get answertext, answercount and quotient for each answer 130 $analysed_answer = array(); 131 if ($info->subtype == 'c') { 132 $sizeofanswers = count($answers); 133 for ($i = 1; $i <= $sizeofanswers; $i++) { 134 $ans = new stdClass(); 135 $ans->answertext = $answers[$i-1]; 136 $ans->answercount = 0; 137 foreach ($values as $value) { 138 //ist die Antwort gleich dem index der Antworten + 1? 139 $vallist = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value->value); 140 foreach ($vallist as $val) { 141 if ($val == $i) { 142 $ans->answercount++; 143 } 144 } 145 } 146 $ans->quotient = $ans->answercount / count($values); 147 $analysed_answer[] = $ans; 148 } 149 } else { 150 $sizeofanswers = count($answers); 151 for ($i = 1; $i <= $sizeofanswers; $i++) { 152 $ans = new stdClass(); 153 $ans->answertext = $answers[$i-1]; 154 $ans->answercount = 0; 155 foreach ($values as $value) { 156 //ist die Antwort gleich dem index der Antworten + 1? 157 if ($value->value == $i) { 158 $ans->answercount++; 159 } 160 } 161 $ans->quotient = $ans->answercount / count($values); 162 $analysed_answer[] = $ans; 163 } 164 } 165 $analysed_item[] = $analysed_answer; 166 return $analysed_item; 167 } 168 169 public function get_printval($item, $value) { 170 $info = $this->get_info($item); 171 172 $printval = ''; 173 174 if (!isset($value->value)) { 175 return $printval; 176 } 177 178 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 179 180 if ($info->subtype == 'c') { 181 $vallist = array_values(explode (FEEDBACK_MULTICHOICE_LINE_SEP, $value->value)); 182 $sizeofvallist = count($vallist); 183 $sizeofpresentation = count($presentation); 184 for ($i = 0; $i < $sizeofvallist; $i++) { 185 for ($k = 0; $k < $sizeofpresentation; $k++) { 186 if ($vallist[$i] == ($k + 1)) {//Die Werte beginnen bei 1, das Array aber mit 0 187 $printval .= trim(format_string($presentation[$k])) . chr(10); 188 break; 189 } 190 } 191 } 192 } else { 193 $index = 1; 194 foreach ($presentation as $pres) { 195 if ($value->value == $index) { 196 $printval = format_string($pres); 197 break; 198 } 199 $index++; 200 } 201 } 202 return $printval; 203 } 204 205 public function print_analysed($item, $itemnr = '', $groupid = false, $courseid = false) { 206 global $OUTPUT; 207 208 $analysed_item = $this->get_analysed($item, $groupid, $courseid); 209 if ($analysed_item) { 210 $itemname = $analysed_item[1]; 211 echo "<table class=\"analysis itemtype_{$item->typ}\">"; 212 echo '<tr><th colspan="2" align="left">'; 213 echo $itemnr . ' '; 214 if (strval($item->label) !== '') { 215 echo '('. format_string($item->label).') '; 216 } 217 echo format_string($itemname); 218 echo '</th></tr>'; 219 echo "</table>"; 220 $analysed_vals = $analysed_item[2]; 221 $count = 0; 222 $data = []; 223 foreach ($analysed_vals as $val) { 224 $quotient = format_float($val->quotient * 100, 2); 225 $strquotient = ''; 226 if ($val->quotient > 0) { 227 $strquotient = ' ('. $quotient . ' %)'; 228 } 229 $answertext = format_text(trim($val->answertext), FORMAT_HTML, 230 array('noclean' => true, 'para' => false)); 231 232 $data['labels'][$count] = $answertext; 233 $data['series'][$count] = $val->answercount; 234 $data['series_labels'][$count] = $val->answercount . $strquotient; 235 $count++; 236 } 237 $chart = new \core\chart_bar(); 238 $chart->set_horizontal(true); 239 $series = new \core\chart_series(format_string(get_string("responses", "feedback")), $data['series']); 240 $series->set_labels($data['series_labels']); 241 $chart->add_series($series); 242 $chart->set_labels($data['labels']); 243 244 echo $OUTPUT->render($chart); 245 } 246 } 247 248 public function excelprint_item(&$worksheet, $row_offset, 249 $xls_formats, $item, 250 $groupid, $courseid = false) { 251 252 $analysed_item = $this->get_analysed($item, $groupid, $courseid); 253 254 $data = $analysed_item[2]; 255 256 //frage schreiben 257 $worksheet->write_string($row_offset, 0, $item->label, $xls_formats->head2); 258 $worksheet->write_string($row_offset, 1, $analysed_item[1], $xls_formats->head2); 259 if (is_array($data)) { 260 $sizeofdata = count($data); 261 for ($i = 0; $i < $sizeofdata; $i++) { 262 $analysed_data = $data[$i]; 263 264 $worksheet->write_string($row_offset, 265 $i + 2, 266 trim($analysed_data->answertext), 267 $xls_formats->head2); 268 269 $worksheet->write_number($row_offset + 1, 270 $i + 2, 271 $analysed_data->answercount, 272 $xls_formats->default); 273 274 $worksheet->write_number($row_offset + 2, 275 $i + 2, 276 $analysed_data->quotient, 277 $xls_formats->procent); 278 } 279 } 280 $row_offset += 3; 281 return $row_offset; 282 } 283 284 /** 285 * Options for the multichoice element 286 * @param stdClass $item 287 * @return array 288 */ 289 protected function get_options($item) { 290 $info = $this->get_info($item); 291 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 292 $options = array(); 293 foreach ($presentation as $idx => $optiontext) { 294 $options[$idx + 1] = format_text($optiontext, FORMAT_HTML, array('noclean' => true, 'para' => false)); 295 } 296 if ($info->subtype === 'r' && !$this->hidenoselect($item)) { 297 $options = array(0 => get_string('not_selected', 'feedback')) + $options; 298 } 299 300 return $options; 301 } 302 303 /** 304 * Adds an input element to the complete form 305 * 306 * This element has many options - it can be displayed as group or radio elements, 307 * group of checkboxes or a dropdown list. 308 * 309 * @param stdClass $item 310 * @param mod_feedback_complete_form $form 311 */ 312 public function complete_form_element($item, $form) { 313 $info = $this->get_info($item); 314 $name = $this->get_display_name($item); 315 $class = 'multichoice-' . $info->subtype; 316 $inputname = $item->typ . '_' . $item->id; 317 $options = $this->get_options($item); 318 $separator = !empty($info->horizontal) ? ' ' : '<br>'; 319 $tmpvalue = $form->get_item_value($item) ?? 0; // Used for element defaults, so must be a valid value (not null). 320 321 // Subtypes: 322 // r = radio 323 // c = checkbox 324 // d = dropdown. 325 if ($info->subtype === 'd' || ($info->subtype === 'r' && $form->is_frozen())) { 326 // Display as a dropdown in the complete form or a single value in the response view. 327 $element = $form->add_form_element($item, 328 ['select', $inputname, $name, array(0 => '') + $options, array('class' => $class)], 329 false, false); 330 $form->set_element_default($inputname, $tmpvalue); 331 $form->set_element_type($inputname, PARAM_INT); 332 } else if ($info->subtype === 'c' && $form->is_frozen()) { 333 // Display list of checkbox values in the response view. 334 $objs = []; 335 foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $form->get_item_value($item)) as $v) { 336 $objs[] = ['static', $inputname."[$v]", '', isset($options[$v]) ? $options[$v] : '']; 337 } 338 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 339 } else { 340 // Display group or radio or checkbox elements. 341 $class .= ' multichoice-' . ($info->horizontal ? 'horizontal' : 'vertical'); 342 $objs = []; 343 if ($info->subtype === 'c') { 344 // Checkboxes. 345 $objs[] = ['hidden', $inputname.'[0]', 0]; 346 $form->set_element_type($inputname.'[0]', PARAM_INT); 347 foreach ($options as $idx => $label) { 348 $objs[] = ['advcheckbox', $inputname.'['.$idx.']', '', $label, null, array(0, $idx)]; 349 $form->set_element_type($inputname.'['.$idx.']', PARAM_INT); 350 } 351 // Span to hold the element id. The id is used for drag and drop reordering. 352 $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])]; 353 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 354 if ($tmpvalue) { 355 foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $tmpvalue) as $v) { 356 $form->set_element_default($inputname.'['.$v.']', $v); 357 } 358 } 359 } else { 360 // Radio. 361 if (!array_key_exists(0, $options)) { 362 // Always add a hidden element to the group to guarantee we get a value in the submit data. 363 $objs[] = ['hidden', $inputname, 0]; 364 } 365 foreach ($options as $idx => $label) { 366 $objs[] = ['radio', $inputname, '', $label, $idx]; 367 } 368 // Span to hold the element id. The id is used for drag and drop reordering. 369 $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])]; 370 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 371 $form->set_element_default($inputname, $tmpvalue); 372 $form->set_element_type($inputname, PARAM_INT); 373 } 374 } 375 376 // Process 'required' rule. 377 if ($item->required) { 378 $elementname = $element->getName(); 379 $form->add_validation_rule(function($values) use ($elementname, $item) { 380 $inputname = $item->typ . '_' . $item->id; 381 return empty($values[$inputname]) || (is_array($values[$inputname]) && !array_filter($values[$inputname])) ? 382 array($elementname => get_string('required')) : true; 383 }); 384 } 385 } 386 387 /** 388 * Prepares value that user put in the form for storing in DB 389 * @param array $value 390 * @return string 391 */ 392 public function create_value($value) { 393 // Could be an array (multichoice checkbox) or single value (multichoice radio or dropdown). 394 $value = is_array($value) ? $value : [$value]; 395 396 $value = array_unique(array_filter($value)); 397 return join(FEEDBACK_MULTICHOICE_LINE_SEP, $value); 398 } 399 400 /** 401 * Compares the dbvalue with the dependvalue 402 * 403 * @param stdClass $item 404 * @param string $dbvalue is the value input by user in the format as it is stored in the db 405 * @param string $dependvalue is the value that it needs to be compared against 406 */ 407 public function compare_value($item, $dbvalue, $dependvalue) { 408 409 if (is_array($dbvalue)) { 410 $dbvalues = $dbvalue; 411 } else { 412 $dbvalues = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $dbvalue); 413 } 414 415 $info = $this->get_info($item); 416 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 417 $index = 1; 418 foreach ($presentation as $pres) { 419 foreach ($dbvalues as $dbval) { 420 if ($dbval == $index AND trim($pres) == $dependvalue) { 421 return true; 422 } 423 } 424 $index++; 425 } 426 return false; 427 } 428 429 public function get_info($item) { 430 $presentation = empty($item->presentation) ? '' : $item->presentation; 431 432 $info = new stdClass(); 433 //check the subtype of the multichoice 434 //it can be check(c), radio(r) or dropdown(d) 435 $info->subtype = ''; 436 $info->presentation = ''; 437 $info->horizontal = false; 438 439 $parts = explode(FEEDBACK_MULTICHOICE_TYPE_SEP, $item->presentation); 440 @list($info->subtype, $info->presentation) = $parts; 441 if (!isset($info->subtype)) { 442 $info->subtype = 'r'; 443 } 444 445 if ($info->subtype != 'd') { 446 $parts = explode(FEEDBACK_MULTICHOICE_ADJUST_SEP, $info->presentation); 447 @list($info->presentation, $info->horizontal) = $parts; 448 if (isset($info->horizontal) AND $info->horizontal == 1) { 449 $info->horizontal = true; 450 } else { 451 $info->horizontal = false; 452 } 453 } 454 return $info; 455 } 456 457 public function set_ignoreempty($item, $ignoreempty=true) { 458 $item->options = str_replace(FEEDBACK_MULTICHOICE_IGNOREEMPTY, '', $item->options); 459 if ($ignoreempty) { 460 $item->options .= FEEDBACK_MULTICHOICE_IGNOREEMPTY; 461 } 462 } 463 464 public function ignoreempty($item) { 465 if (strstr($item->options, FEEDBACK_MULTICHOICE_IGNOREEMPTY)) { 466 return true; 467 } 468 return false; 469 } 470 471 public function set_hidenoselect($item, $hidenoselect=true) { 472 $item->options = str_replace(FEEDBACK_MULTICHOICE_HIDENOSELECT, '', $item->options); 473 if ($hidenoselect) { 474 $item->options .= FEEDBACK_MULTICHOICE_HIDENOSELECT; 475 } 476 } 477 478 public function hidenoselect($item) { 479 if (strstr($item->options, FEEDBACK_MULTICHOICE_HIDENOSELECT)) { 480 return true; 481 } 482 return false; 483 } 484 485 /** 486 * Return the analysis data ready for external functions. 487 * 488 * @param stdClass $item the item (question) information 489 * @param int $groupid the group id to filter data (optional) 490 * @param int $courseid the course id (optional) 491 * @return array an array of data with non scalar types json encoded 492 * @since Moodle 3.3 493 */ 494 public function get_analysed_for_external($item, $groupid = false, $courseid = false) { 495 496 $externaldata = array(); 497 $data = $this->get_analysed($item, $groupid, $courseid); 498 499 if (!empty($data[2]) && is_array($data[2])) { 500 foreach ($data[2] as $d) { 501 $externaldata[] = json_encode($d); 502 } 503 } 504 return $externaldata; 505 } 506 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body