MDL-27675 - Feedback module abuses data_submitted
[moodle.git] / mod / feedback / item / numeric / lib.php
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/>.
17 defined('MOODLE_INTERNAL') OR die('not allowed');
18 require_once($CFG->dirroot.'/mod/feedback/item/feedback_item_class.php');
20 class feedback_item_numeric extends feedback_item_base {
21     protected $type = "numeric";
22     public $sep_dec, $sep_thous;
23     private $commonparams;
24     private $item_form;
25     private $item;
27     public function init() {
28         $this->sep_dec = get_string('separator_decimal', 'feedback');
29         if (substr($this->sep_dec, 0, 2) == '[[') {
30             $this->sep_dec = FEEDBACK_DECIMAL;
31         }
33         $this->sep_thous = get_string('separator_thousand', 'feedback');
34         if (substr($this->sep_thous, 0, 2) == '[[') {
35             $this->sep_thous = FEEDBACK_THOUSAND;
36         }
37     }
39     public function build_editform($item, $feedback, $cm) {
40         global $DB, $CFG;
41         require_once('numeric_form.php');
43         //get the lastposition number of the feedback_items
44         $position = $item->position;
45         $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
46         if ($position == -1) {
47             $i_formselect_last = $lastposition + 1;
48             $i_formselect_value = $lastposition + 1;
49             $item->position = $lastposition + 1;
50         } else {
51             $i_formselect_last = $lastposition;
52             $i_formselect_value = $item->position;
53         }
54         //the elements for position dropdownlist
55         $positionlist = array_slice(range(0, $i_formselect_last), 1, $i_formselect_last, true);
57         $item->presentation = empty($item->presentation) ? '' : $item->presentation;
59         $range_from_to = explode('|', $item->presentation);
60         if (isset($range_from_to[0]) AND is_numeric($range_from_to[0])) {
61             $range_from = str_replace(FEEDBACK_DECIMAL,
62                                 $this->sep_dec,
63                                 floatval($range_from_to[0]));
64         } else {
65             $range_from = '-';
66         }
68         if (isset($range_from_to[1]) AND is_numeric($range_from_to[1])) {
69             $range_to = str_replace(FEEDBACK_DECIMAL,
70                                 $this->sep_dec,
71                                 floatval($range_from_to[1]));
72         } else {
73             $range_to = '-';
74         }
76         $item->rangefrom = $range_from;
77         $item->rangeto = $range_to;
79         //all items for dependitem
80         $feedbackitems = feedback_get_depend_candidates_for_item($feedback, $item);
81         $commonparams = array('cmid'=>$cm->id,
82                              'id'=>isset($item->id) ? $item->id : null,
83                              'typ'=>$item->typ,
84                              'items'=>$feedbackitems,
85                              'feedback'=>$feedback->id);
87         //build the form
88         $customdata = array('item' => $item,
89                             'common' => $commonparams,
90                             'positionlist' => $positionlist,
91                             'position' => $position);
93         $this->item_form = new feedback_numeric_form('edit_item.php', $customdata);
94     }
96     //this function only can used after the call of build_editform()
97     public function show_editform() {
98         $this->item_form->display();
99     }
101     public function is_cancelled() {
102         return $this->item_form->is_cancelled();
103     }
105     public function get_data() {
106         if ($this->item = $this->item_form->get_data()) {
107             return true;
108         }
109         return false;
110     }
112     public function save_item() {
113         global $DB;
115         if (!$item = $this->item_form->get_data()) {
116             return false;
117         }
119         if (isset($item->clone_item) AND $item->clone_item) {
120             $item->id = ''; //to clone this item
121             $item->position++;
122         }
124         $item->hasvalue = $this->get_hasvalue();
125         if (!$item->id) {
126             $item->id = $DB->insert_record('feedback_item', $item);
127         } else {
128             $DB->update_record('feedback_item', $item);
129         }
131         return $DB->get_record('feedback_item', array('id'=>$item->id));
132     }
135     //liefert eine Struktur ->name, ->data = array(mit Antworten)
136     public function get_analysed($item, $groupid = false, $courseid = false) {
137         global $DB;
139         $analysed = new stdClass();
140         $analysed->data = array();
141         $analysed->name = $item->name;
142         $values = feedback_get_group_values($item, $groupid, $courseid);
144         $avg = 0.0;
145         $counter = 0;
146         if ($values) {
147             $data = array();
148             foreach ($values as $value) {
149                 if (is_numeric($value->value)) {
150                     $data[] = $value->value;
151                     $avg += $value->value;
152                     $counter++;
153                 }
154             }
155             $avg = $counter > 0 ? $avg / $counter : 0;
156             $analysed->data = $data;
157             $analysed->avg = $avg;
158         }
159         return $analysed;
160     }
162     public function get_printval($item, $value) {
163         if (!isset($value->value)) {
164             return '';
165         }
167         return $value->value;
168     }
170     public function print_analysed($item, $itemnr = '', $groupid = false, $courseid = false) {
172         $values = $this->get_analysed($item, $groupid, $courseid);
174         if (isset($values->data) AND is_array($values->data)) {
175             echo '<tr><th colspan="2" align="left">';
176             echo $itemnr.'&nbsp;('.$item->label.') '.$item->name;
177             echo '</th></tr>';
179             foreach ($values->data as $value) {
180                 echo '<tr><td colspan="2" valign="top" align="left">';
181                 echo '-&nbsp;&nbsp;'.number_format($value, 2, $this->sep_dec, $this->sep_thous);
182                 echo '</td></tr>';
183             }
185             if (isset($values->avg)) {
186                 $avg = number_format($values->avg, 2, $this->sep_dec, $this->sep_thous);
187             } else {
188                 $avg = number_format(0, 2, $this->sep_dec, $this->sep_thous);
189             }
190             echo '<tr><td align="left" colspan="2"><b>';
191             echo get_string('average', 'feedback').': '.$avg;
192             echo '</b></td></tr>';
193         }
194     }
196     public function excelprint_item(&$worksheet, $row_offset,
197                              $xls_formats, $item,
198                              $groupid, $courseid = false) {
200         $analysed_item = $this->get_analysed($item, $groupid, $courseid);
202         $worksheet->write_string($row_offset, 0, $item->label, $xls_formats->head2);
203         $worksheet->write_string($row_offset, 1, $item->name, $xls_formats->head2);
204         $data = $analysed_item->data;
205         if (is_array($data)) {
207             //mittelwert anzeigen
208             $worksheet->write_string($row_offset,
209                                      2,
210                                      get_string('average', 'feedback'),
211                                      $xls_formats->value_bold);
213             $worksheet->write_number($row_offset + 1,
214                                      2,
215                                      $analysed_item->avg,
216                                      $xls_formats->value_bold);
217             $row_offset++;
218         }
219         $row_offset++;
220         return $row_offset;
221     }
223     /**     
224      * print the item at the edit-page of feedback
225      *
226      * @global object
227      * @param object $item
228      * @return void
229      */
230     public function print_item_preview($item) {
231         global $OUTPUT, $DB;
233         $align = right_to_left() ? 'right' : 'left';
234         $str_required_mark = '<span class="feedback_required_mark">*</span>';
236         //get the range
237         $range_from_to = explode('|', $item->presentation);
239         //get the min-value
240         if (isset($range_from_to[0]) AND is_numeric($range_from_to[0])) {
241             $range_from = floatval($range_from_to[0]);
242         } else {
243             $range_from = 0;
244         }
246         //get the max-value
247         if (isset($range_from_to[1]) AND is_numeric($range_from_to[1])) {
248             $range_to = floatval($range_from_to[1]);
249         } else {
250             $range_to = 0;
251         }
253         $requiredmark =  ($item->required == 1) ? $str_required_mark : '';
254         //print the question and label
255         echo '<div class="feedback_item_label_'.$align.'">';
256         echo '('.$item->label.') ';
257         echo format_text($item->name . $requiredmark, true, false, false);
258         if ($item->dependitem) {
259             $params = array('id'=>$item->dependitem);
260             if ($dependitem = $DB->get_record('feedback_item', $params)) {
261                 echo ' <span class="feedback_depend">';
262                 echo '('.$dependitem->label.'-&gt;'.$item->dependvalue.')';
263                 echo '</span>';
264             }
265         }
266         echo '<span class="feedback_item_numinfo">';
267         switch(true) {
268             case ($range_from === '-' AND is_numeric($range_to)):
269                 echo ' ('.get_string('maximal', 'feedback').
270                         ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
271                 break;
272             case (is_numeric($range_from) AND $range_to === '-'):
273                 echo ' ('.get_string('minimal', 'feedback').
274                         ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).')';
275                 break;
276             case ($range_from === '-' AND $range_to === '-'):
277                 break;
278             default:
279                 echo ' ('.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).
280                         ' - '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
281                 break;
282         }
283         echo '</span>';
284         echo '</div>';
286         //print the presentation
287         echo '<div class="feedback_item_presentation_'.$align.'">';
288         echo '<span class="feedback_item_textfield">';
289         echo '<input type="text" '.
290                     'name="'.$item->typ.'_'.$item->id.'" '.
291                     'size="10" '.
292                     'maxlength="10" '.
293                     'value="" />';
295         echo '</span>';
296         echo '</div>';
297     }
299     /**     
300      * print the item at the complete-page of feedback
301      *
302      * @global object
303      * @param object $item
304      * @param string $value
305      * @param bool $highlightrequire
306      * @return void
307      */
308     public function print_item_complete($item, $value = '', $highlightrequire = false) {
309         global $OUTPUT;
310         $align = right_to_left() ? 'right' : 'left';
311         $str_required_mark = '<span class="feedback_required_mark">*</span>';
313         //get the range
314         $range_from_to = explode('|', $item->presentation);
316         //get the min-value
317         if (isset($range_from_to[0]) AND is_numeric($range_from_to[0])) {
318             $range_from = floatval($range_from_to[0]);
319         } else {
320             $range_from = 0;
321         }
323         //get the max-value
324         if (isset($range_from_to[1]) AND is_numeric($range_from_to[1])) {
325             $range_to = floatval($range_from_to[1]);
326         } else {
327             $range_to = 0;
328         }
330         if ($highlightrequire AND (!$this->check_value($value, $item))) {
331             $highlight = ' missingrequire';
332         } else {
333             $highlight = '';
334         }
335         $requiredmark = ($item->required == 1) ? $str_required_mark : '';
337         //print the question and label
338         echo '<div class="feedback_item_label_'.$align.$highlight.'">';
339         echo format_text($item->name . $requiredmark, true, false, false);
340         echo '<span class="feedback_item_numinfo">';
341         switch(true) {
342             case ($range_from === '-' AND is_numeric($range_to)):
343                 echo ' ('.get_string('maximal', 'feedback').
344                         ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
345                 break;
346             case (is_numeric($range_from) AND $range_to === '-'):
347                 echo ' ('.get_string('minimal', 'feedback').
348                         ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).')';
349                 break;
350             case ($range_from === '-' AND $range_to === '-'):
351                 break;
352             default:
353                 echo ' ('.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).
354                         ' - '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
355                 break;
356         }
357         echo '</span>';
358         echo '</div>';
360         //print the presentation
361         echo '<div class="feedback_item_presentation_'.$align.$highlight.'">';
362         echo '<span class="feedback_item_textfield">';
363         echo '<input type="text" '.
364                      'name="'.$item->typ.'_'.$item->id.'" '.
365                      'size="10" '.
366                      'maxlength="10" '.
367                      'value="'.$value.'" />';
369         echo '</span>';
370         echo '</div>';
371     }
373     /**     
374      * print the item at the complete-page of feedback
375      *
376      * @global object
377      * @param object $item
378      * @param string $value
379      * @return void
380      */
381     public function print_item_show_value($item, $value = '') {
382         global $OUTPUT;
383         $align = right_to_left() ? 'right' : 'left';
384         $str_required_mark = '<span class="feedback_required_mark">*</span>';
386         //get the range
387         $range_from_to = explode('|', $item->presentation);
388         //get the min-value
389         if (isset($range_from_to[0]) AND is_numeric($range_from_to[0])) {
390             $range_from = floatval($range_from_to[0]);
391         } else {
392             $range_from = 0;
393         }
394         //get the max-value
395         if (isset($range_from_to[1]) AND is_numeric($range_from_to[1])) {
396             $range_to = floatval($range_from_to[1]);
397         } else {
398             $range_to = 0;
399         }
400         $requiredmark = ($item->required == 1) ? $str_required_mark : '';
402         //print the question and label
403         echo '<div class="feedback_item_label_'.$align.'">';
404         echo '('.$item->label.') ';
405         echo format_text($item->name . $requiredmark, true, false, false);
406         switch(true) {
407             case ($range_from === '-' AND is_numeric($range_to)):
408                 echo ' ('.get_string('maximal', 'feedback').
409                     ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
410                 break;
411             case (is_numeric($range_from) AND $range_to === '-'):
412                 echo ' ('.get_string('minimal', 'feedback').
413                     ': '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).')';
414                 break;
415             case ($range_from === '-' AND $range_to === '-'):
416                 break;
417             default:
418                 echo ' ('.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_from).
419                     ' - '.str_replace(FEEDBACK_DECIMAL, $this->sep_dec, $range_to).')';
420                 break;
421         }
422         echo '</div>';
424         //print the presentation
425         echo '<div class="feedback_item_presentation_'.$align.'">';
426         echo $OUTPUT->box_start('generalbox boxalign'.$align);
427         if (is_numeric($value)) {
428             $str_num_value = number_format($value, 2, $this->sep_dec, $this->sep_thous);
429         } else {
430             $str_num_value = '&nbsp;';
431         }
432         echo $str_num_value;
433         echo $OUTPUT->box_end();
434         echo '</div>';
435     }
437     public function check_value($value, $item) {
438         $value = str_replace($this->sep_dec, FEEDBACK_DECIMAL, $value);
439         //if the item is not required, so the check is true if no value is given
440         if ((!isset($value) OR $value == '') AND $item->required != 1) {
441             return true;
442         }
443         if (!is_numeric($value)) {
444             return false;
445         }
447         $range_from_to = explode('|', $item->presentation);
448         if (isset($range_from_to[0]) AND is_numeric($range_from_to[0])) {
449             $range_from = floatval($range_from_to[0]);
450         } else {
451             $range_from = '-';
452         }
453         if (isset($range_from_to[1]) AND is_numeric($range_from_to[1])) {
454             $range_to = floatval($range_from_to[1]);
455         } else {
456             $range_to = '-';
457         }
459         switch(true) {
460             case ($range_from === '-' AND is_numeric($range_to)):
461                 if (floatval($value) <= $range_to) {
462                     return true;
463                 }
464                 break;
465             case (is_numeric($range_from) AND $range_to === '-'):
466                 if (floatval($value) >= $range_from) {
467                     return true;
468                 }
469                 break;
470             case ($range_from === '-' AND $range_to === '-'):
471                 return true;
472                 break;
473             default:
474                 if (floatval($value) >= $range_from AND floatval($value) <= $range_to) {
475                     return true;
476                 }
477                 break;
478         }
480         return false;
481     }
483     public function create_value($data) {
484         $data = str_replace($this->sep_dec, FEEDBACK_DECIMAL, $data);
486         if (is_numeric($data)) {
487             $data = floatval($data);
488         } else {
489             $data = '';
490         }
491         return $data;
492     }
494     //compares the dbvalue with the dependvalue
495     //dbvalue is the number put in by the user
496     //dependvalue is the value that is compared
497     public function compare_value($item, $dbvalue, $dependvalue) {
498         if ($dbvalue == $dependvalue) {
499             return true;
500         }
501         return false;
502     }
504     public function get_presentation($data) {
505         $num1 = str_replace($this->sep_dec, FEEDBACK_DECIMAL, $data->numericrangefrom);
506         if (is_numeric($num1)) {
507             $num1 = floatval($num1);
508         } else {
509             $num1 = '-';
510         }
512         $num2 = str_replace($this->sep_dec, FEEDBACK_DECIMAL, $data->numericrangeto);
513         if (is_numeric($num2)) {
514             $num2 = floatval($num2);
515         } else {
516             $num2 = '-';
517         }
519         if ($num1 === '-' OR $num2 === '-') {
520             return $num1 . '|'. $num2;
521         }
523         if ($num1 > $num2) {
524             return $num2 . '|'. $num1;
525         } else {
526             return $num1 . '|'. $num2;
527         }
528     }
530     public function get_hasvalue() {
531         return 1;
532     }
534     public function can_switch_require() {
535         return true;
536     }
537     
538     public function value_type() {
539         return PARAM_FLOAT;
540     }
542     function clean_input_value($value) {
543         if (!is_numeric($value)) {
544             return null;
545         }
546         return clean_param($value, $this->value_type());
547     }