on-demand release 3.10dev+
[moodle.git] / grade / edit / tree / item_form.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 /**
18  * A moodleform allowing the editing of the grade options for an individual grade item
19  *
20  * @package   core_grades
21  * @copyright 2007 Petr Skoda
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 if (!defined('MOODLE_INTERNAL')) {
26     die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
27 }
29 require_once $CFG->libdir.'/formslib.php';
31 class edit_item_form extends moodleform {
32     private $displayoptions;
34     function definition() {
35         global $COURSE, $CFG, $DB;
37         $mform =& $this->_form;
39         $item = $this->_customdata['current'];
41 /// visible elements
42         $mform->addElement('header', 'general', get_string('gradeitem', 'grades'));
44         $mform->addElement('text', 'itemname', get_string('itemname', 'grades'));
45         $mform->setType('itemname', PARAM_TEXT);
46         $mform->addElement('text', 'iteminfo', get_string('iteminfo', 'grades'));
47         $mform->addHelpButton('iteminfo', 'iteminfo', 'grades');
48         $mform->setType('iteminfo', PARAM_TEXT);
50         $mform->addElement('text', 'idnumber', get_string('idnumbermod'));
51         $mform->addHelpButton('idnumber', 'idnumbermod');
52         $mform->setType('idnumber', PARAM_RAW);
54         if (!empty($item->id)) {
55             $gradeitem = new grade_item(array('id' => $item->id, 'courseid' => $item->courseid));
56             // If grades exist set a message so the user knows why they can not alter the grade type or scale.
57             // We could never change the grade type for external items, so only need to show this for manual grade items.
58             if ($gradeitem->has_grades() && !$gradeitem->is_external_item()) {
59                 // Set a message so the user knows why they can not alter the grade type or scale.
60                 if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
61                     $gradesexistmsg = get_string('modgradecantchangegradetyporscalemsg', 'grades');
62                 } else {
63                     $gradesexistmsg = get_string('modgradecantchangegradetypemsg', 'grades');
64                 }
66                 $gradesexisthtml = '<div class=\'alert\'>' . $gradesexistmsg . '</div>';
67                 $mform->addElement('static', 'gradesexistmsg', '', $gradesexisthtml);
68             }
69         }
71         // Manual grade items cannot have grade type GRADE_TYPE_NONE.
72         $options = array(GRADE_TYPE_VALUE => get_string('typevalue', 'grades'),
73                          GRADE_TYPE_SCALE => get_string('typescale', 'grades'),
74                          GRADE_TYPE_TEXT => get_string('typetext', 'grades'));
76         $mform->addElement('select', 'gradetype', get_string('gradetype', 'grades'), $options);
77         $mform->addHelpButton('gradetype', 'gradetype', 'grades');
78         $mform->setDefault('gradetype', GRADE_TYPE_VALUE);
80         //$mform->addElement('text', 'calculation', get_string('calculation', 'grades'));
81         //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_TEXT);
82         //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_NONE);
84         $options = array(0=>get_string('usenoscale', 'grades'));
85         if ($scales = grade_scale::fetch_all_local($COURSE->id)) {
86             foreach ($scales as $scale) {
87                 $options[$scale->id] = $scale->get_name();
88             }
89         }
90         if ($scales = grade_scale::fetch_all_global()) {
91             foreach ($scales as $scale) {
92                 $options[$scale->id] = $scale->get_name();
93             }
94         }
95         // ugly BC hack - it was possible to use custom scale from other courses :-(
96         if (!empty($item->scaleid) and !isset($options[$item->scaleid])) {
97             if ($scale = grade_scale::fetch(array('id'=>$item->scaleid))) {
98                 $options[$scale->id] = $scale->get_name().get_string('incorrectcustomscale', 'grades');
99             }
100         }
101         $mform->addElement('select', 'scaleid', get_string('scale'), $options);
102         $mform->addHelpButton('scaleid', 'typescale', 'grades');
103         $mform->disabledIf('scaleid', 'gradetype', 'noteq', GRADE_TYPE_SCALE);
105         $choices = array();
106         $choices[''] = get_string('choose');
107         $choices['no'] = get_string('no');
108         $choices['yes'] = get_string('yes');
109         $mform->addElement('select', 'rescalegrades', get_string('modgraderescalegrades', 'grades'), $choices);
110         $mform->addHelpButton('rescalegrades', 'modgraderescalegrades', 'grades');
111         $mform->disabledIf('rescalegrades', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
113         $mform->addElement('text', 'grademax', get_string('grademax', 'grades'));
114         $mform->addHelpButton('grademax', 'grademax', 'grades');
115         $mform->disabledIf('grademax', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
116         $mform->setType('grademax', PARAM_RAW);
118         if ((bool) get_config('moodle', 'grade_report_showmin')) {
119             $mform->addElement('text', 'grademin', get_string('grademin', 'grades'));
120             $mform->addHelpButton('grademin', 'grademin', 'grades');
121             $mform->disabledIf('grademin', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
122             $mform->setType('grademin', PARAM_RAW);
123         }
125         $mform->addElement('text', 'gradepass', get_string('gradepass', 'grades'));
126         $mform->addHelpButton('gradepass', 'gradepass', 'grades');
127         $mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_NONE);
128         $mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_TEXT);
129         $mform->setType('gradepass', PARAM_RAW);
131         $mform->addElement('text', 'multfactor', get_string('multfactor', 'grades'));
132         $mform->addHelpButton('multfactor', 'multfactor', 'grades');
133         $mform->setAdvanced('multfactor');
134         $mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_NONE);
135         $mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT);
136         $mform->setType('multfactor', PARAM_RAW);
138         $mform->addElement('text', 'plusfactor', get_string('plusfactor', 'grades'));
139         $mform->addHelpButton('plusfactor', 'plusfactor', 'grades');
140         $mform->setAdvanced('plusfactor');
141         $mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_NONE);
142         $mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT);
143         $mform->setType('plusfactor', PARAM_RAW);
145         /// grade display prefs
146         $default_gradedisplaytype = grade_get_setting($COURSE->id, 'displaytype', $CFG->grade_displaytype);
147         $options = array(GRADE_DISPLAY_TYPE_DEFAULT            => get_string('default', 'grades'),
148                          GRADE_DISPLAY_TYPE_REAL               => get_string('real', 'grades'),
149                          GRADE_DISPLAY_TYPE_PERCENTAGE         => get_string('percentage', 'grades'),
150                          GRADE_DISPLAY_TYPE_LETTER             => get_string('letter', 'grades'),
151                          GRADE_DISPLAY_TYPE_REAL_PERCENTAGE    => get_string('realpercentage', 'grades'),
152                          GRADE_DISPLAY_TYPE_REAL_LETTER        => get_string('realletter', 'grades'),
153                          GRADE_DISPLAY_TYPE_LETTER_REAL        => get_string('letterreal', 'grades'),
154                          GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE  => get_string('letterpercentage', 'grades'),
155                          GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER  => get_string('percentageletter', 'grades'),
156                          GRADE_DISPLAY_TYPE_PERCENTAGE_REAL    => get_string('percentagereal', 'grades')
157                          );
159         asort($options);
161         foreach ($options as $key=>$option) {
162             if ($key == $default_gradedisplaytype) {
163                 $options[GRADE_DISPLAY_TYPE_DEFAULT] = get_string('defaultprev', 'grades', $option);
164                 break;
165             }
166         }
167         $mform->addElement('select', 'display', get_string('gradedisplaytype', 'grades'), $options);
168         $mform->addHelpButton('display', 'gradedisplaytype', 'grades');
169         $mform->disabledIf('display', 'gradetype', 'eq', GRADE_TYPE_TEXT);
171         $default_gradedecimals = grade_get_setting($COURSE->id, 'decimalpoints', $CFG->grade_decimalpoints);
172         $options = array(-1=>get_string('defaultprev', 'grades', $default_gradedecimals), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5);
173         $mform->addElement('select', 'decimals', get_string('decimalpoints', 'grades'), $options);
174         $mform->addHelpButton('decimals', 'decimalpoints', 'grades');
175         $mform->setDefault('decimals', -1);
176         $mform->disabledIf('decimals', 'display', 'eq', GRADE_DISPLAY_TYPE_LETTER);
177         if ($default_gradedisplaytype == GRADE_DISPLAY_TYPE_LETTER) {
178             $mform->disabledIf('decimals', 'display', "eq", GRADE_DISPLAY_TYPE_DEFAULT);
179         }
180         $mform->disabledIf('decimals', 'gradetype', 'eq', GRADE_TYPE_TEXT);
182         /// hiding
183         if ($item->cancontrolvisibility) {
184             // advcheckbox is not compatible with disabledIf!
185             $mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades'));
186             $mform->addElement('date_time_selector', 'hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true));
187             $mform->disabledIf('hidden', 'hiddenuntil[off]', 'notchecked');
188         } else {
189             $mform->addElement('static', 'hidden', get_string('hidden', 'grades'),
190                     get_string('componentcontrolsvisibility', 'grades'));
191             // Unset hidden to avoid data override.
192             unset($item->hidden);
193         }
194         $mform->addHelpButton('hidden', 'hidden', 'grades');
196         /// locking
197         $mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades'));
198         $mform->addHelpButton('locked', 'locked', 'grades');
200         $mform->addElement('date_time_selector', 'locktime', get_string('locktime', 'grades'), array('optional'=>true));
201         $mform->disabledIf('locktime', 'gradetype', 'eq', GRADE_TYPE_NONE);
203 /// parent category related settings
204         $mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades'));
206         $mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades'));
207         $mform->addHelpButton('weightoverride', 'weightoverride', 'grades');
208         $mform->disabledIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_NONE);
209         $mform->disabledIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_TEXT);
211         $mform->addElement('text', 'aggregationcoef2', get_string('weight', 'grades'));
212         $mform->addHelpButton('aggregationcoef2', 'weight', 'grades');
213         $mform->setType('aggregationcoef2', PARAM_RAW);
214         $mform->disabledIf('aggregationcoef2', 'weightoverride');
215         $mform->disabledIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_NONE);
216         $mform->disabledIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_TEXT);
218         $options = array();
219         $coefstring = '';
220         $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id));
222         foreach ($categories as $cat) {
223             $cat->apply_forced_settings();
224             $options[$cat->id] = $cat->get_name();
225         }
227         if (count($categories) > 1) {
228             $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
229         }
231 /// hidden params
232         $mform->addElement('hidden', 'id', 0);
233         $mform->setType('id', PARAM_INT);
235         $mform->addElement('hidden', 'courseid', $COURSE->id);
236         $mform->setType('courseid', PARAM_INT);
238         $mform->addElement('hidden', 'itemtype', 'manual'); // all new items are manual only
239         $mform->setType('itemtype', PARAM_ALPHA);
241 /// add return tracking info
242         $gpr = $this->_customdata['gpr'];
243         $gpr->add_mform_elements($mform);
245 /// mark advanced according to site settings
246         if (isset($CFG->grade_item_advanced)) {
247             $advanced = explode(',', $CFG->grade_item_advanced);
248             foreach ($advanced as $el) {
249                 if ($mform->elementExists($el)) {
250                     $mform->setAdvanced($el);
251                 }
252             }
253         }
254 //-------------------------------------------------------------------------------
255         // buttons
256         $this->add_action_buttons();
257 //-------------------------------------------------------------------------------
258         $this->set_data($item);
259     }
262 /// tweak the form - depending on existing data
263     function definition_after_data() {
264         global $CFG, $COURSE;
266         $mform =& $this->_form;
268         if ($id = $mform->getElementValue('id')) {
269             $gradeitem = grade_item::fetch(array('id' => $id));
270             $parentcategory = $gradeitem->get_parent_category();
271         } else {
272             // If we do not have an id, we are creating a new grade item.
273             $gradeitem = new grade_item(array('courseid' => $COURSE->id, 'itemtype' => 'manual'), false);
275             // Assign the course category to this grade item.
276             $parentcategory = grade_category::fetch_course_category($COURSE->id);
277             $gradeitem->parent_category = $parentcategory;
278         }
280         if (!$gradeitem->is_raw_used()) {
281             $mform->removeElement('plusfactor');
282             $mform->removeElement('multfactor');
283         }
285         if ($gradeitem->is_outcome_item()) {
286             // We have to prevent incompatible modifications of outcomes if outcomes disabled.
287             $mform->removeElement('grademax');
288             if ($mform->elementExists('grademin')) {
289                 $mform->removeElement('grademin');
290             }
291             $mform->removeElement('gradetype');
292             $mform->removeElement('display');
293             $mform->removeElement('decimals');
294             $mform->hardFreeze('scaleid');
296         } else {
297             if ($gradeitem->is_external_item()) {
298                 // Following items are set up from modules and should not be overrided by user.
299                 if ($mform->elementExists('grademin')) {
300                     // The site setting grade_report_showmin may have prevented grademin being added to the form.
301                     $mform->hardFreeze('grademin');
302                 }
303                 $mform->hardFreeze('itemname,gradetype,grademax,scaleid');
304                 if ($gradeitem->itemnumber == 0) {
305                     // The idnumber of grade itemnumber 0 is synced with course_modules.
306                     $mform->hardFreeze('idnumber');
307                 }
309                 // For external items we can not change the grade type, even if no grades exist, so if it is set to
310                 // scale, then remove the grademax and grademin fields from the form - no point displaying them.
311                 if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
312                     $mform->removeElement('grademax');
313                     if ($mform->elementExists('grademin')) {
314                         $mform->removeElement('grademin');
315                     }
316                 } else { // Not using scale, so remove it.
317                     $mform->removeElement('scaleid');
318                 }
320                 // Always remove the rescale grades element if it's an external item.
321                 $mform->removeElement('rescalegrades');
322             } else if ($gradeitem->has_grades()) {
323                 // Can't change the grade type or the scale if there are grades.
324                 $mform->hardFreeze('gradetype, scaleid');
326                 // If we are using scales then remove the unnecessary rescale and grade fields.
327                 if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
328                     $mform->removeElement('rescalegrades');
329                     $mform->removeElement('grademax');
330                     if ($mform->elementExists('grademin')) {
331                         $mform->removeElement('grademin');
332                     }
333                 } else { // Remove the scale field.
334                     $mform->removeElement('scaleid');
335                     // Set the maximum grade to disabled unless a grade is chosen.
336                     $mform->disabledIf('grademax', 'rescalegrades', 'eq', '');
337                 }
338             } else {
339                 // Remove the rescale element if there are no grades.
340                 $mform->removeElement('rescalegrades');
341             }
342         }
344         // If we wanted to change parent of existing item - we would have to verify there are no circular references in parents!!!
345         if ($id && $mform->elementExists('parentcategory')) {
346             $mform->hardFreeze('parentcategory');
347         }
349         $parentcategory->apply_forced_settings();
351         if (!$parentcategory->is_aggregationcoef_used()) {
352             if ($mform->elementExists('aggregationcoef')) {
353                 $mform->removeElement('aggregationcoef');
354             }
356         } else {
357             $coefstring = $gradeitem->get_coefstring();
359             if ($coefstring !== '') {
360                 if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
361                     // The advcheckbox is not compatible with disabledIf!
362                     $coefstring = 'aggregationcoefextrasum';
363                     $element =& $mform->createElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
364                 } else {
365                     $element =& $mform->createElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
366                 }
367                 if ($mform->elementExists('parentcategory')) {
368                     $mform->insertElementBefore($element, 'parentcategory');
369                 } else {
370                     $mform->insertElementBefore($element, 'id');
371                 }
372                 $mform->addHelpButton('aggregationcoef', $coefstring, 'grades');
373             }
374             $mform->disabledIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_NONE);
375             $mform->disabledIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_TEXT);
376             $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $parentcategory->id);
377         }
379         // Remove fields used by natural weighting if the parent category is not using natural weighting.
380         // Or if the item is a scale and scales are not used in aggregation.
381         if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM
382                 || (empty($CFG->grade_includescalesinaggregation) && $gradeitem->gradetype == GRADE_TYPE_SCALE)) {
383             if ($mform->elementExists('weightoverride')) {
384                 $mform->removeElement('weightoverride');
385             }
386             if ($mform->elementExists('aggregationcoef2')) {
387                 $mform->removeElement('aggregationcoef2');
388             }
389         }
391         if ($category = $gradeitem->get_item_category()) {
392             if ($category->aggregation == GRADE_AGGREGATE_SUM) {
393                 if ($mform->elementExists('gradetype')) {
394                     $mform->hardFreeze('gradetype');
395                 }
396                 if ($mform->elementExists('grademin')) {
397                     $mform->hardFreeze('grademin');
398                 }
399                 if ($mform->elementExists('grademax')) {
400                     $mform->hardFreeze('grademax');
401                 }
402                 if ($mform->elementExists('scaleid')) {
403                     $mform->removeElement('scaleid');
404                 }
405             }
406         }
408         // no parent header for course category
409         if (!$mform->elementExists('aggregationcoef') and !$mform->elementExists('parentcategory')) {
410             $mform->removeElement('headerparent');
411         }
412     }
414 /// perform extra validation before submission
415     function validation($data, $files) {
416         global $COURSE;
417         $grade_item = false;
418         if ($data['id']) {
419             $grade_item = new grade_item(array('id' => $data['id'], 'courseid' => $data['courseid']));
420         }
422         $errors = parent::validation($data, $files);
424         if (array_key_exists('idnumber', $data)) {
425             if ($grade_item) {
426                 if ($grade_item->itemtype == 'mod') {
427                     $cm = get_coursemodule_from_instance($grade_item->itemmodule, $grade_item->iteminstance, $grade_item->courseid);
428                 } else {
429                     $cm = null;
430                 }
431             } else {
432                 $grade_item = null;
433                 $cm = null;
434             }
435             if (!grade_verify_idnumber($data['idnumber'], $COURSE->id, $grade_item, $cm)) {
436                 $errors['idnumber'] = get_string('idnumbertaken');
437             }
438         }
440         if (array_key_exists('gradetype', $data) and $data['gradetype'] == GRADE_TYPE_SCALE) {
441             if (empty($data['scaleid'])) {
442                 $errors['scaleid'] = get_string('missingscale', 'grades');
443             }
444         }
446         if (array_key_exists('grademin', $data) and array_key_exists('grademax', $data)) {
447             if ($data['grademax'] == $data['grademin'] or $data['grademax'] < $data['grademin']) {
448                 $errors['grademin'] = get_string('incorrectminmax', 'grades');
449                 $errors['grademax'] = get_string('incorrectminmax', 'grades');
450             }
451         }
453         // We do not want the user to be able to change the grade type or scale for this item if grades exist.
454         if ($grade_item && $grade_item->has_grades()) {
455             // Check that grade type is set - should never not be set unless form has been modified.
456             if (!isset($data['gradetype'])) {
457                 $errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades');
458             } else if ($data['gradetype'] !== $grade_item->gradetype) { // Check if we are changing the grade type.
459                 $errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades');
460             } else if ($data['gradetype'] == GRADE_TYPE_SCALE) {
461                 // Check if we are changing the scale - can't do this when grades exist.
462                 if (isset($data['scaleid']) && ($data['scaleid'] !== $grade_item->scaleid)) {
463                     $errors['scaleid'] = get_string('modgradecantchangescale', 'grades');
464                 }
465             }
466         }
467         if ($grade_item) {
468             if ($grade_item->gradetype == GRADE_TYPE_VALUE) {
469                 if ((((bool) get_config('moodle', 'grade_report_showmin')) &&
470                     grade_floats_different($data['grademin'], $grade_item->grademin)) ||
471                     grade_floats_different($data['grademax'], $grade_item->grademax)) {
472                     if ($grade_item->has_grades() && empty($data['rescalegrades'])) {
473                         $errors['rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades');
474                     }
475                 }
476             }
477         }
479         return $errors;
480     }