See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
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|null 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 class="text-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 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 '<tr><td>'. $OUTPUT->render($chart) . '</td></tr>'; 245 echo "</table>"; 246 } 247 } 248 249 public function excelprint_item(&$worksheet, $row_offset, 250 $xls_formats, $item, 251 $groupid, $courseid = false) { 252 253 $analysed_item = $this->get_analysed($item, $groupid, $courseid); 254 if (!$analysed_item) { 255 return $row_offset; 256 } 257 258 $data = $analysed_item[2]; 259 260 //frage schreiben 261 $worksheet->write_string($row_offset, 0, $item->label, $xls_formats->head2); 262 $worksheet->write_string($row_offset, 1, $analysed_item[1], $xls_formats->head2); 263 if (is_array($data)) { 264 $sizeofdata = count($data); 265 for ($i = 0; $i < $sizeofdata; $i++) { 266 $analysed_data = $data[$i]; 267 268 $worksheet->write_string($row_offset, 269 $i + 2, 270 trim($analysed_data->answertext), 271 $xls_formats->head2); 272 273 $worksheet->write_number($row_offset + 1, 274 $i + 2, 275 $analysed_data->answercount, 276 $xls_formats->default); 277 278 $worksheet->write_number($row_offset + 2, 279 $i + 2, 280 $analysed_data->quotient, 281 $xls_formats->procent); 282 } 283 } 284 $row_offset += 3; 285 return $row_offset; 286 } 287 288 /** 289 * Options for the multichoice element 290 * @param stdClass $item 291 * @return array 292 */ 293 protected function get_options($item) { 294 $info = $this->get_info($item); 295 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 296 $options = array(); 297 foreach ($presentation as $idx => $optiontext) { 298 $options[$idx + 1] = format_text($optiontext, FORMAT_HTML, array('noclean' => true, 'para' => false)); 299 } 300 if ($info->subtype === 'r' && !$this->hidenoselect($item)) { 301 $options = array(0 => get_string('not_selected', 'feedback')) + $options; 302 } 303 304 return $options; 305 } 306 307 /** 308 * Adds an input element to the complete form 309 * 310 * This element has many options - it can be displayed as group or radio elements, 311 * group of checkboxes or a dropdown list. 312 * 313 * @param stdClass $item 314 * @param mod_feedback_complete_form $form 315 */ 316 public function complete_form_element($item, $form) { 317 $info = $this->get_info($item); 318 $name = $this->get_display_name($item); 319 $class = 'multichoice-' . $info->subtype; 320 $inputname = $item->typ . '_' . $item->id; 321 $options = $this->get_options($item); 322 $separator = !empty($info->horizontal) ? ' ' : '<br>'; 323 $tmpvalue = $form->get_item_value($item) ?? 0; // Used for element defaults, so must be a valid value (not null). 324 325 // Subtypes: 326 // r = radio 327 // c = checkbox 328 // d = dropdown. 329 if ($info->subtype === 'd' || ($info->subtype === 'r' && $form->is_frozen())) { 330 // Display as a dropdown in the complete form or a single value in the response view. 331 $element = $form->add_form_element($item, 332 ['select', $inputname, $name, array(0 => '') + $options, array('class' => $class)], 333 false, false); 334 $form->set_element_default($inputname, $tmpvalue); 335 $form->set_element_type($inputname, PARAM_INT); 336 } else if ($info->subtype === 'c' && $form->is_frozen()) { 337 // Display list of checkbox values in the response view. 338 $objs = []; 339 foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $form->get_item_value($item)) as $v) { 340 $objs[] = ['static', $inputname."[$v]", '', isset($options[$v]) ? $options[$v] : '']; 341 } 342 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 343 } else { 344 // Display group or radio or checkbox elements. 345 $class .= ' multichoice-' . ($info->horizontal ? 'horizontal' : 'vertical'); 346 $objs = []; 347 if ($info->subtype === 'c') { 348 // Checkboxes. 349 $objs[] = ['hidden', $inputname.'[0]', 0]; 350 $form->set_element_type($inputname.'[0]', PARAM_INT); 351 foreach ($options as $idx => $label) { 352 $objs[] = ['advcheckbox', $inputname.'['.$idx.']', '', $label, null, array(0, $idx)]; 353 $form->set_element_type($inputname.'['.$idx.']', PARAM_INT); 354 } 355 // Span to hold the element id. The id is used for drag and drop reordering. 356 $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])]; 357 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 358 if ($tmpvalue) { 359 foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $tmpvalue) as $v) { 360 $form->set_element_default($inputname.'['.$v.']', $v); 361 } 362 } 363 } else { 364 // Radio. 365 if (!array_key_exists(0, $options)) { 366 // Always add a hidden element to the group to guarantee we get a value in the submit data. 367 $objs[] = ['hidden', $inputname, 0]; 368 } 369 foreach ($options as $idx => $label) { 370 $objs[] = ['radio', $inputname, '', $label, $idx]; 371 } 372 // Span to hold the element id. The id is used for drag and drop reordering. 373 $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])]; 374 $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class); 375 $form->set_element_default($inputname, $tmpvalue); 376 $form->set_element_type($inputname, PARAM_INT); 377 } 378 } 379 380 // Process 'required' rule. 381 if ($item->required) { 382 $elementname = $element->getName(); 383 $form->add_validation_rule(function($values) use ($elementname, $item) { 384 $inputname = $item->typ . '_' . $item->id; 385 return empty($values[$inputname]) || (is_array($values[$inputname]) && !array_filter($values[$inputname])) ? 386 array($elementname => get_string('required')) : true; 387 }); 388 } 389 } 390 391 /** 392 * Prepares value that user put in the form for storing in DB 393 * @param array $value 394 * @return string 395 */ 396 public function create_value($value) { 397 // Could be an array (multichoice checkbox) or single value (multichoice radio or dropdown). 398 $value = is_array($value) ? $value : [$value]; 399 400 $value = array_unique(array_filter($value)); 401 return join(FEEDBACK_MULTICHOICE_LINE_SEP, $value); 402 } 403 404 /** 405 * Compares the dbvalue with the dependvalue 406 * 407 * @param stdClass $item 408 * @param string $dbvalue is the value input by user in the format as it is stored in the db 409 * @param string $dependvalue is the value that it needs to be compared against 410 */ 411 public function compare_value($item, $dbvalue, $dependvalue) { 412 413 if (is_array($dbvalue)) { 414 $dbvalues = $dbvalue; 415 } else { 416 $dbvalues = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $dbvalue); 417 } 418 419 $info = $this->get_info($item); 420 $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation); 421 $index = 1; 422 foreach ($presentation as $pres) { 423 foreach ($dbvalues as $dbval) { 424 if ($dbval == $index AND trim($pres) == $dependvalue) { 425 return true; 426 } 427 } 428 $index++; 429 } 430 return false; 431 } 432 433 public function get_info($item) { 434 $presentation = empty($item->presentation) ? '' : $item->presentation; 435 436 $info = new stdClass(); 437 //check the subtype of the multichoice 438 //it can be check(c), radio(r) or dropdown(d) 439 $info->subtype = ''; 440 $info->presentation = ''; 441 $info->horizontal = false; 442 443 $parts = explode(FEEDBACK_MULTICHOICE_TYPE_SEP, $item->presentation); 444 $info->subtype = $parts[0]; 445 if (count($parts) > 1) { 446 $info->presentation = $parts[1]; 447 } 448 if (!isset($info->subtype)) { 449 $info->subtype = 'r'; 450 } 451 452 if ($info->subtype != 'd') { 453 $parts = explode(FEEDBACK_MULTICHOICE_ADJUST_SEP, $info->presentation); 454 $info->presentation = $parts[0]; 455 if (count($parts) > 1) { 456 $info->horizontal = $parts[1]; 457 } 458 if (isset($info->horizontal) AND $info->horizontal == 1) { 459 $info->horizontal = true; 460 } else { 461 $info->horizontal = false; 462 } 463 } 464 return $info; 465 } 466 467 public function set_ignoreempty($item, $ignoreempty=true) { 468 $item->options = str_replace(FEEDBACK_MULTICHOICE_IGNOREEMPTY, '', $item->options); 469 if ($ignoreempty) { 470 $item->options .= FEEDBACK_MULTICHOICE_IGNOREEMPTY; 471 } 472 } 473 474 public function ignoreempty($item) { 475 if (strstr($item->options, FEEDBACK_MULTICHOICE_IGNOREEMPTY)) { 476 return true; 477 } 478 return false; 479 } 480 481 public function set_hidenoselect($item, $hidenoselect=true) { 482 $item->options = str_replace(FEEDBACK_MULTICHOICE_HIDENOSELECT, '', $item->options); 483 if ($hidenoselect) { 484 $item->options .= FEEDBACK_MULTICHOICE_HIDENOSELECT; 485 } 486 } 487 488 public function hidenoselect($item) { 489 if (strstr($item->options, FEEDBACK_MULTICHOICE_HIDENOSELECT)) { 490 return true; 491 } 492 return false; 493 } 494 495 /** 496 * Return the analysis data ready for external functions. 497 * 498 * @param stdClass $item the item (question) information 499 * @param int $groupid the group id to filter data (optional) 500 * @param int $courseid the course id (optional) 501 * @return array an array of data with non scalar types json encoded 502 * @since Moodle 3.3 503 */ 504 public function get_analysed_for_external($item, $groupid = false, $courseid = false) { 505 506 $externaldata = array(); 507 $data = $this->get_analysed($item, $groupid, $courseid); 508 509 if ($data && !empty($data[2]) && is_array($data[2])) { 510 foreach ($data[2] as $d) { 511 $externaldata[] = json_encode($d); 512 } 513 } 514 return $externaldata; 515 } 516 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body