quiz editing: MDL-17398 made question grading field smaller to accommodate a bigger...
[moodle.git] / course / moodleform_mod.php
CommitLineData
61fceb86 1<?php //$Id$
e24b7f85 2require_once ($CFG->libdir.'/formslib.php');
61fceb86 3require_once($CFG->libdir.'/completionlib.php');
4
e24b7f85 5/**
6 * This class adds extra methods to form wrapper specific to be used for module
7cac0c4b 7 * add / update forms (mod/{modname}.mod_form.php replaces deprecated mod/{modname}/mod_form.php
e24b7f85 8 *
9 */
10class moodleform_mod extends moodleform {
11 /**
12 * Instance of the module that is being updated. This is the id of the {prefix}{modulename}
13 * record. Can be used in form definition. Will be "" if this is an 'add' form and not an
14 * update one.
15 *
16 * @var mixed
17 */
18 var $_instance;
19 /**
20 * Section of course that module instance will be put in or is in.
24f41672 21 * This is always the section number itself (column 'section' from 'course_sections' table).
e24b7f85 22 *
23 * @var mixed
24 */
25 var $_section;
26 /**
27 * Coursemodle record of the module that is being updated. Will be null if this is an 'add' form and not an
28 * update one.
29 *
30 * @var mixed
31 */
32 var $_cm;
4b86bb08 33 /**
34 * List of modform features
35 */
36 var $_features;
4e781c7b 37
38 /**
39 * @var array Custom completion-rule elements, if enabled
40 */
41 var $_customcompletionelements;
e24b7f85 42
43 function moodleform_mod($instance, $section, $cm) {
44 $this->_instance = $instance;
45 $this->_section = $section;
46 $this->_cm = $cm;
47 parent::moodleform('modedit.php');
48 }
71ee4471 49
e24b7f85 50 /**
a7f7e52f 51 * Only available on moodleform_mod.
e24b7f85 52 *
53 * @param array $default_values passed by reference
54 */
a7f7e52f 55 function data_preprocessing(&$default_values){
e24b7f85 56 }
71ee4471 57
4b86bb08 58 /**
59 * Each module which defines definition_after_data() must call this method using parent::definition_after_data();
60 */
71ee4471 61 function definition_after_data() {
4b86bb08 62 global $CFG, $COURSE;
71ee4471 63 $mform =& $this->_form;
64
65 if ($id = $mform->getElementValue('update')) {
66 $modulename = $mform->getElementValue('modulename');
67 $instance = $mform->getElementValue('instance');
68
4b86bb08 69 if ($this->_features->gradecat) {
70 $gradecat = false;
71 if (!empty($CFG->enableoutcomes) and $this->_features->outcomes) {
72 if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
73 $gradecat = true;
74 }
75 }
76 if ($items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
77 'iteminstance'=>$instance, 'courseid'=>$COURSE->id))) {
78 foreach ($items as $item) {
79 if (!empty($item->outcomeid)) {
80 $elname = 'outcome_'.$item->outcomeid;
81 if ($mform->elementExists($elname)) {
82 $mform->hardFreeze($elname); // prevent removing of existing outcomes
83 }
84 }
85 }
86 foreach ($items as $item) {
87 if (is_bool($gradecat)) {
88 $gradecat = $item->categoryid;
89 continue;
71ee4471 90 }
4b86bb08 91 if ($gradecat != $item->categoryid) {
92 //mixed categories
93 $gradecat = false;
94 break;
95 }
96 }
97 }
98
99 if ($gradecat === false) {
100 // items and outcomes in different categories - remove the option
101 // TODO: it might be better to add a "Mixed categories" text instead
102 if ($mform->elementExists('gradecat')) {
103 $mform->removeElement('gradecat');
71ee4471 104 }
105 }
106 }
107 }
24f41672 108
a78890d5 109 if ($COURSE->groupmodeforce) {
110 if ($mform->elementExists('groupmode')) {
24f41672 111 $mform->hardFreeze('groupmode'); // groupmode can not be changed if forced from course settings
112 }
113 }
a104debf 114
f2fdc8eb 115 if ($mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly') and empty($COURSE->groupmodeforce)) {
116 $mform->disabledIf('groupingid', 'groupmode', 'eq', NOGROUPS);
117
118 } else if (!$mform->elementExists('groupmode') and $mform->elementExists('groupmembersonly')) {
119 $mform->disabledIf('groupingid', 'groupmembersonly', 'notchecked');
4b86bb08 120
f2fdc8eb 121 } else if (!$mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly')) {
122 // groupings have no use without groupmode or groupmembersonly
a104debf 123 if ($mform->elementExists('groupingid')) {
124 $mform->removeElement('groupingid');
125 }
126 }
4e781c7b 127
128 // Completion: If necessary, freeze fields
61fceb86 129 $completion = new completion_info($COURSE);
130 if ($completion->is_enabled()) {
4e781c7b 131 // If anybody has completed the activity, these options will be 'locked'
132 $completedcount = empty($this->_cm)
133 ? 0
134 : $completion->count_user_data($this->_cm);
135
61fceb86 136 $freeze = false;
137 if (!$completedcount) {
138 if ($mform->elementExists('unlockcompletion')) {
4e781c7b 139 $mform->removeElement('unlockcompletion');
140 }
141 } else {
142 // Has the element been unlocked?
61fceb86 143 if ($mform->exportValue('unlockcompletion')) {
4e781c7b 144 // Yes, add in warning text and set the hidden variable
145 $mform->insertElementBefore(
61fceb86 146 $mform->createElement('static', 'completedunlocked',
147 get_string('completedunlocked', 'completion'),
148 get_string('completedunlockedtext', 'completion')),
4e781c7b 149 'unlockcompletion');
150 $mform->removeElement('unlockcompletion');
151 $mform->getElement('completionunlocked')->setValue(1);
152 } else {
153 // No, add in the warning text with the count (now we know
154 // it) before the unlock button
155 $mform->insertElementBefore(
61fceb86 156 $mform->createElement('static', 'completedwarning',
157 get_string('completedwarning', 'completion'),
158 get_string('completedwarningtext', 'completion', $completedcount)),
4e781c7b 159 'unlockcompletion');
160 $mform->setHelpButton('completedwarning', array('completionlocked', get_string('help_completionlocked', 'completion'), 'completion'));
161
61fceb86 162 $freeze = true;
4e781c7b 163 }
164 }
165
61fceb86 166 if ($freeze) {
4e781c7b 167 $mform->freeze('completion');
61fceb86 168 if ($mform->elementExists('completionview')) {
4e781c7b 169 $mform->freeze('completionview'); // don't use hardFreeze or checkbox value gets lost
170 }
61fceb86 171 if ($mform->elementExists('completionusegrade')) {
4e781c7b 172 $mform->freeze('completionusegrade');
173 }
174 $mform->freeze($this->_customcompletionelements);
175 }
176 }
71ee4471 177 }
178
60243313 179 // form verification
a78890d5 180 function validation($data, $files) {
32648682 181 global $COURSE, $DB;
a78890d5 182 $errors = parent::validation($data, $files);
60243313 183
273eb2f5 184 $mform =& $this->_form;
185
60243313 186 $errors = array();
187
273eb2f5 188 if ($mform->elementExists('name')) {
189 $name = trim($data['name']);
190 if ($name == '') {
191 $errors['name'] = get_string('required');
192 }
e04ff2d5 193 }
194
60243313 195 $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$data['modulename'],
196 'iteminstance'=>$data['instance'], 'itemnumber'=>0, 'courseid'=>$COURSE->id));
197 if ($data['coursemodule']) {
32648682 198 $cm = $DB->get_record('course_modules', array('id'=>$data['coursemodule']));
60243313 199 } else {
200 $cm = null;
201 }
202
273eb2f5 203 if ($mform->elementExists('cmidnumber')) {
204 // verify the idnumber
204175c5 205 if (!grade_verify_idnumber($data['cmidnumber'], $COURSE->id, $grade_item, $cm)) {
273eb2f5 206 $errors['cmidnumber'] = get_string('idnumbertaken');
207 }
60243313 208 }
4e781c7b 209
210 // Completion: Don't let them choose automatic completion without turning
211 // on some conditions
61fceb86 212 if (array_key_exists('completion', $data) && $data['completion']==COMPLETION_TRACKING_AUTOMATIC) {
213 if (empty($data['completionview']) && empty($data['completionusegrade']) &&
4e781c7b 214 !$this->completion_rule_enabled($data)) {
61fceb86 215 $errors['completion'] = get_string('badautocompletion', 'completion');
4e781c7b 216 }
217 }
60243313 218
a78890d5 219 return $errors;
60243313 220 }
221
e24b7f85 222 /**
223 * Load in existing data as form defaults. Usually new entry defaults are stored directly in
224 * form definition (new entry form); this function is used to load in data where values
225 * already exist and data is being edited (edit entry form).
226 *
227 * @param mixed $default_values object or array of default values
228 */
32db0d42 229 function set_data($default_values) {
e24b7f85 230 if (is_object($default_values)) {
231 $default_values = (array)$default_values;
232 }
ab6803a9 233 $this->data_preprocessing($default_values);
24f41672 234 parent::set_data($default_values); //never slashed for moodleform_mod
e24b7f85 235 }
71ee4471 236
e24b7f85 237 /**
238 * Adds all the standard elements to a form to edit the settings for an activity module.
239 *
4e781c7b 240 * @param mixed $features array or object describing supported features - groups, groupings, groupmembersonly, etc.
241 * @param string $modname Name of module e.g. 'label'
e24b7f85 242 */
61fceb86 243 function standard_coursemodule_elements($features=null, $modname=null){
c18269c7 244 global $COURSE, $CFG, $DB;
e24b7f85 245 $mform =& $this->_form;
f3b783f4 246
4e781c7b 247 // Guess module name if not supplied
61fceb86 248 if (!$modname) {
4e781c7b 249 $matches=array();
61fceb86 250 if (!preg_match('/^mod_([^_]+)_mod_form$/', $this->_formname, $matches)) {
4e781c7b 251 debugging('Use $modname parameter or rename form to mod_xx_mod_form, where xx is name of your module');
9b34dc6e 252 print_error('unknownmodulename');
4e781c7b 253 }
254 $modname=$matches[1];
255 }
256
e04ff2d5 257 // deal with legacy $supportgroups param
258 if ($features === true or $features === false) {
259 $groupmode = $features;
4b86bb08 260 $this->_features = new object();
261 $this->_features->groups = $groupmode;
e04ff2d5 262
263 } else if (is_array($features)) {
4b86bb08 264 $this->_features = (object)$features;
e04ff2d5 265
266 } else if (empty($features)) {
4b86bb08 267 $this->_features = new object();
268
269 } else {
270 $this->_features = $features;
e04ff2d5 271 }
272
4b86bb08 273 if (!isset($this->_features->groups)) {
274 $this->_features->groups = true;
e04ff2d5 275 }
276
4b86bb08 277 if (!isset($this->_features->groupings)) {
278 $this->_features->groupings = false;
e04ff2d5 279 }
280
4b86bb08 281 if (!isset($this->_features->groupmembersonly)) {
282 $this->_features->groupmembersonly = false;
e04ff2d5 283 }
284
4b86bb08 285 if (!isset($this->_features->outcomes)) {
286 $this->_features->outcomes = true;
287 }
288
289 if (!isset($this->_features->gradecat)) {
290 $this->_features->gradecat = true;
291 }
292
b5ab55dd 293 if (!isset($this->_features->idnumber)) {
294 $this->_features->idnumber = true;
295 }
4e781c7b 296
61fceb86 297 if (!isset($this->_features->defaultcompletion)) {
4e781c7b 298 $this->_features->defaultcompletion = true;
299 }
b5ab55dd 300
301 $outcomesused = false;
4b86bb08 302 if (!empty($CFG->enableoutcomes) and $this->_features->outcomes) {
f3b783f4 303 if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
b5ab55dd 304 $outcomesused = true;
f3b783f4 305 $mform->addElement('header', 'modoutcomes', get_string('outcomes', 'grades'));
306 foreach($outcomes as $outcome) {
307 $mform->addElement('advcheckbox', 'outcome_'.$outcome->id, $outcome->get_name());
308 }
309 }
310 }
311
24e25bc1 312 $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form'));
4b86bb08 313 if ($this->_features->groups) {
a78890d5 314 $options = array(NOGROUPS => get_string('groupsnone'),
315 SEPARATEGROUPS => get_string('groupsseparate'),
316 VISIBLEGROUPS => get_string('groupsvisible'));
317 $mform->addElement('select', 'groupmode', get_string('groupmode'), $options, NOGROUPS);
318 $mform->setHelpButton('groupmode', array('groupmode', get_string('groupmode')));
e24b7f85 319 }
24f41672 320
321 if (!empty($CFG->enablegroupings)) {
4b86bb08 322 if ($this->_features->groupings or $this->_features->groupmembersonly) {
e04ff2d5 323 //groupings selector - used for normal grouping mode or also when restricting access with groupmembersonly
324 $options = array();
325 $options[0] = get_string('none');
c18269c7 326 if ($groupings = $DB->get_records('groupings', array('courseid'=>$COURSE->id))) {
e04ff2d5 327 foreach ($groupings as $grouping) {
328 $options[$grouping->id] = format_string($grouping->name);
329 }
24f41672 330 }
e04ff2d5 331 $mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options);
0143840c 332 $mform->setHelpButton('groupingid', array('grouping', get_string('grouping', 'group')));
e04ff2d5 333 $mform->setAdvanced('groupingid');
24f41672 334 }
e04ff2d5 335
4b86bb08 336 if ($this->_features->groupmembersonly) {
f2fdc8eb 337 $mform->addElement('checkbox', 'groupmembersonly', get_string('groupmembersonly', 'group'));
0143840c 338 $mform->setHelpButton('groupmembersonly', array('groupmembersonly', get_string('groupmembersonly', 'group')));
a104debf 339 $mform->setAdvanced('groupmembersonly');
340 }
24f41672 341 }
342
e24b7f85 343 $mform->addElement('modvisible', 'visible', get_string('visible'));
b5ab55dd 344
345 if ($this->_features->idnumber) {
346 $mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
347 $mform->setHelpButton('cmidnumber', array('cmidnumber', get_string('idnumbermod')), true);
348 }
71ee4471 349
4b86bb08 350 if ($this->_features->gradecat) {
57956368 351 $categories = grade_get_categories_menu($COURSE->id, $outcomesused);
4b86bb08 352 $mform->addElement('select', 'gradecat', get_string('gradecategory', 'grades'), $categories);
353 }
354
4e781c7b 355 // Conditional activities: completion tracking section
61fceb86 356 $completion = new completion_info($COURSE);
357 if ($completion->is_enabled()) {
4e781c7b 358 $mform->addElement('header', '', get_string('activitycompletion', 'completion'));
359
360 // Unlock button for if people have completed it (will
361 // be removed in definition_after_data if they haven't)
61fceb86 362 $mform->addElement('submit', 'unlockcompletion', get_string('unlockcompletion', 'completion'));
4e781c7b 363 $mform->registerNoSubmitButton('unlockcompletion');
61fceb86 364 $mform->addElement('hidden', 'completionunlocked', 0);
4e781c7b 365
61fceb86 366 $mform->addElement('select', 'completion', get_string('completion', 'completion'),
367 array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'),
368 COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion')));
4e781c7b 369 $mform->setHelpButton('completion', array('completion', get_string('help_completion', 'completion'), 'completion'));
61fceb86 370 $mform->setDefault('completion', $this->_features->defaultcompletion
4e781c7b 371 ? COMPLETION_TRACKING_MANUAL
372 : COMPLETION_TRACKING_NONE);
373
374 // Automatic completion once you view it
61fceb86 375 $gotcompletionoptions = false;
376 if (plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
377 $mform->addElement('checkbox', 'completionview', get_string('completionview', 'completion'),
378 get_string('completionview_text', 'completion'));
4e781c7b 379 $mform->setHelpButton('completionview', array('completionview', get_string('help_completionview', 'completion'), 'completion'));
61fceb86 380 $mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
381 $gotcompletionoptions = true;
4e781c7b 382 }
383
384 // Automatic completion once it's graded
61fceb86 385 if (plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
386 $mform->addElement('checkbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
387 get_string('completionusegrade_text', 'completion'));
4e781c7b 388 $mform->setHelpButton('completionusegrade', array('completionusegrade', get_string('help_completionusegrade', 'completion'), 'completion'));
61fceb86 389 $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
390 $gotcompletionoptions = true;
4e781c7b 391 }
392
393 // Automatic completion according to module-specific rules
394 $this->_customcompletionelements = $this->add_completion_rules();
61fceb86 395 foreach ($this->_customcompletionelements as $element) {
396 $mform->disabledIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
4e781c7b 397 }
398
399 $gotcompletionoptions = $gotcompletionoptions ||
400 count($this->_customcompletionelements)>0;
401
402 // Automatic option only appears if possible
61fceb86 403 if ($gotcompletionoptions) {
4e781c7b 404 $mform->getElement('completion')->addOption(
61fceb86 405 get_string('completion_automatic', 'completion'),
4e781c7b 406 COMPLETION_TRACKING_AUTOMATIC);
407 }
408
409 // Completion expected at particular date? (For progress tracking)
61fceb86 410 $mform->addElement('date_selector', 'completionexpected', get_string('completionexpected', 'completion'), array('optional'=>true));
4e781c7b 411 $mform->setHelpButton('completionexpected', array('completionexpected', get_string('help_completionexpected', 'completion'), 'completion'));
61fceb86 412 $mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
4e781c7b 413 }
414
e24b7f85 415 $this->standard_hidden_coursemodule_elements();
416 }
4e781c7b 417
418 /**
419 * Can be overridden to add custom completion rules if the module wishes
420 * them. If overriding this, you should also override completion_rule_enabled.
421 * <p>
422 * Just add elements to the form as needed and return the list of IDs. The
423 * system will call disabledIf and handle other behaviour for each returned
424 * ID.
425 * @return array Array of string IDs of added items, empty array if none
426 */
427 function add_completion_rules() {
428 return array();
429 }
430
431 /**
432 * Called during validation. Override to indicate, based on the data, whether
433 * a custom completion rule is enabled (selected).
434 *
435 * @param array $data Input data (not yet validated)
436 * @return bool True if one or more rules is enabled, false if none are;
437 * default returns false
438 */
439 function completion_rule_enabled(&$data) {
440 return false;
441 }
e24b7f85 442
443 function standard_hidden_coursemodule_elements(){
444 $mform =& $this->_form;
445 $mform->addElement('hidden', 'course', 0);
446 $mform->setType('course', PARAM_INT);
447
448 $mform->addElement('hidden', 'coursemodule', 0);
449 $mform->setType('coursemodule', PARAM_INT);
450
451 $mform->addElement('hidden', 'section', 0);
452 $mform->setType('section', PARAM_INT);
453
454 $mform->addElement('hidden', 'module', 0);
455 $mform->setType('module', PARAM_INT);
456
457 $mform->addElement('hidden', 'modulename', '');
458 $mform->setType('modulename', PARAM_SAFEDIR);
459
460 $mform->addElement('hidden', 'instance', 0);
461 $mform->setType('instance', PARAM_INT);
462
463 $mform->addElement('hidden', 'add', 0);
464 $mform->setType('add', PARAM_ALPHA);
465
466 $mform->addElement('hidden', 'update', 0);
467 $mform->setType('update', PARAM_INT);
19110c57 468
469 $mform->addElement('hidden', 'return', 0);
470 $mform->setType('return', PARAM_BOOL);
e24b7f85 471 }
472
6b467109 473 /**
474 * Overriding formslib's add_action_buttons() method, to add an extra submit "save changes and return" button.
b5ab55dd 475 *
476 * @param bool $cancel show cancel button
477 * @param string $submitlabel null means default, false means none, string is label text
478 * @param string $submit2label null means default, false means none, string is label text
6b467109 479 * @return void
b5ab55dd 480 */
6b467109 481 function add_action_buttons($cancel=true, $submitlabel=null, $submit2label=null) {
482 if (is_null($submitlabel)) {
483 $submitlabel = get_string('savechangesanddisplay');
484 }
b5ab55dd 485
6b467109 486 if (is_null($submit2label)) {
487 $submit2label = get_string('savechangesandreturntocourse');
488 }
b5ab55dd 489
6b467109 490 $mform =& $this->_form;
b5ab55dd 491
492 // elements in a row need a group
493 $buttonarray = array();
494
495 if ($submit2label !== false) {
496 $buttonarray[] = &$mform->createElement('submit', 'submitbutton2', $submit2label);
497 }
498
499 if ($submitlabel !== false) {
500 $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
501 }
502
6b467109 503 if ($cancel) {
504 $buttonarray[] = &$mform->createElement('cancel');
505 }
506
507 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
de6f158f 508 $mform->setType('buttonar', PARAM_RAW);
6b467109 509 $mform->closeHeaderBefore('buttonar');
510 }
e24b7f85 511}
512
f3b783f4 513?>