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