MDL-52252 Tags: Add tags to modules (Resources and Activities)
[moodle.git] / course / moodleform_mod.php
CommitLineData
aa6c1ced 1<?php
e24b7f85 2require_once ($CFG->libdir.'/formslib.php');
516c5eca 3require_once($CFG->libdir.'/completionlib.php');
61fceb86 4
e24b7f85 5/**
6 * This class adds extra methods to form wrapper specific to be used for module
cce2762c 7 * add / update forms mod/{modname}/mod_form.php replaced deprecated mod/{modname}/mod.html
e24b7f85 8 */
7b5bd060 9abstract class moodleform_mod extends moodleform {
9a105989 10 /** Current data */
11 protected $current;
e24b7f85 12 /**
13 * Instance of the module that is being updated. This is the id of the {prefix}{modulename}
14 * record. Can be used in form definition. Will be "" if this is an 'add' form and not an
15 * update one.
16 *
17 * @var mixed
18 */
42f103be 19 protected $_instance;
e24b7f85 20 /**
21 * Section of course that module instance will be put in or is in.
24f41672 22 * This is always the section number itself (column 'section' from 'course_sections' table).
e24b7f85 23 *
24 * @var mixed
25 */
42f103be 26 protected $_section;
e24b7f85 27 /**
7e85563d 28 * Course module record of the module that is being updated. Will be null if this is an 'add' form and not an
e24b7f85 29 * update one.
30 *
31 * @var mixed
32 */
42f103be 33 protected $_cm;
4b86bb08 34 /**
35 * List of modform features
36 */
42f103be 37 protected $_features;
4e781c7b 38 /**
39 * @var array Custom completion-rule elements, if enabled
40 */
42f103be 41 protected $_customcompletionelements;
42 /**
43 * @var string name of module
44 */
45 protected $_modname;
09f8d75e 46 /** current context, course or module depends if already exists*/
47 protected $context;
e24b7f85 48
1d50dd13
AD
49 /** a flag indicating whether outcomes are being used*/
50 protected $_outcomesused;
51
fa358a83 52 /**
c3037116
DW
53 * @var bool A flag used to indicate that this module should lock settings
54 * based on admin settings flags in definition_after_data.
fa358a83 55 */
c3037116 56 protected $applyadminlockedflags = false;
fa358a83 57
d9203fb7
FM
58 /** @var object The course format of the current course. */
59 protected $courseformat;
60
1a0df553 61 public function __construct($current, $section, $cm, $course) {
d9203fb7
FM
62 global $CFG;
63
9a105989 64 $this->current = $current;
65 $this->_instance = $current->instance;
66 $this->_section = $section;
67 $this->_cm = $cm;
09f8d75e 68 if ($this->_cm) {
9a5e297b 69 $this->context = context_module::instance($this->_cm->id);
09f8d75e 70 } else {
9a5e297b 71 $this->context = context_course::instance($course->id);
09f8d75e 72 }
aa6c1ced 73
d9203fb7
FM
74 // Set the course format.
75 require_once($CFG->dirroot . '/course/format/lib.php');
76 $this->courseformat = course_get_format($course);
77
42f103be 78 // Guess module name
79 $matches = array();
80 if (!preg_match('/^mod_([^_]+)_mod_form$/', get_class($this), $matches)) {
81 debugging('Use $modname parameter or rename form to mod_xx_mod_form, where xx is name of your module');
82 print_error('unknownmodulename');
83 }
84 $this->_modname = $matches[1];
85 $this->init_features();
1a0df553
MG
86 parent::__construct('modedit.php');
87 }
88
89 /**
90 * Old syntax of class constructor. Deprecated in PHP7.
91 *
92 * @deprecated since Moodle 3.1
93 */
94 public function moodleform_mod($current, $section, $cm, $course) {
95 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
96 self::__construct($current, $section, $cm, $course);
e24b7f85 97 }
71ee4471 98
42f103be 99 protected function init_features() {
100 global $CFG;
101
fbaea88f 102 $this->_features = new stdClass();
42f103be 103 $this->_features->groups = plugin_supports('mod', $this->_modname, FEATURE_GROUPS, true);
104 $this->_features->groupings = plugin_supports('mod', $this->_modname, FEATURE_GROUPINGS, false);
42f103be 105 $this->_features->outcomes = (!empty($CFG->enableoutcomes) and plugin_supports('mod', $this->_modname, FEATURE_GRADE_OUTCOMES, true));
106 $this->_features->hasgrades = plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false);
107 $this->_features->idnumber = plugin_supports('mod', $this->_modname, FEATURE_IDNUMBER, true);
dc5c2bd9 108 $this->_features->introeditor = plugin_supports('mod', $this->_modname, FEATURE_MOD_INTRO, true);
42f103be 109 $this->_features->defaultcompletion = plugin_supports('mod', $this->_modname, FEATURE_MODEDIT_DEFAULT_COMPLETION, true);
16306628 110 $this->_features->rating = plugin_supports('mod', $this->_modname, FEATURE_RATE, false);
8c40662e 111 $this->_features->showdescription = plugin_supports('mod', $this->_modname, FEATURE_SHOW_DESCRIPTION, false);
42f103be 112 $this->_features->gradecat = ($this->_features->outcomes or $this->_features->hasgrades);
b11f9da6 113 $this->_features->advancedgrading = plugin_supports('mod', $this->_modname, FEATURE_ADVANCED_GRADING, false);
d629c601 114 $this->_features->canrescale = (component_callback_exists('mod_' . $this->_modname, 'rescale_activity_grades') !== false);
42f103be 115 }
116
e24b7f85 117 /**
a7f7e52f 118 * Only available on moodleform_mod.
e24b7f85 119 *
120 * @param array $default_values passed by reference
121 */
a7f7e52f 122 function data_preprocessing(&$default_values){
a09aeee4
AD
123 if (empty($default_values['scale'])) {
124 $default_values['assessed'] = 0;
125 }
126
127 if (empty($default_values['assessed'])){
a09aeee4
AD
128 $default_values['ratingtime'] = 0;
129 } else {
a09aeee4
AD
130 $default_values['ratingtime']=
131 ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0;
132 }
e24b7f85 133 }
71ee4471 134
4b86bb08 135 /**
136 * Each module which defines definition_after_data() must call this method using parent::definition_after_data();
137 */
71ee4471 138 function definition_after_data() {
4b86bb08 139 global $CFG, $COURSE;
71ee4471 140 $mform =& $this->_form;
141
142 if ($id = $mform->getElementValue('update')) {
143 $modulename = $mform->getElementValue('modulename');
144 $instance = $mform->getElementValue('instance');
145
4b86bb08 146 if ($this->_features->gradecat) {
147 $gradecat = false;
148 if (!empty($CFG->enableoutcomes) and $this->_features->outcomes) {
16306628
AD
149 $outcomes = grade_outcome::fetch_all_available($COURSE->id);
150 if (!empty($outcomes)) {
4b86bb08 151 $gradecat = true;
152 }
153 }
16306628 154
fc55273e 155 $hasgradeitems = false;
16306628
AD
156 $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,'iteminstance'=>$instance, 'courseid'=>$COURSE->id));
157 //will be no items if, for example, this activity supports ratings but rating aggregate type == no ratings
158 if (!empty($items)) {
4b86bb08 159 foreach ($items as $item) {
160 if (!empty($item->outcomeid)) {
161 $elname = 'outcome_'.$item->outcomeid;
162 if ($mform->elementExists($elname)) {
163 $mform->hardFreeze($elname); // prevent removing of existing outcomes
164 }
fc55273e
MG
165 } else {
166 $hasgradeitems = true;
4b86bb08 167 }
168 }
16306628 169
4b86bb08 170 foreach ($items as $item) {
171 if (is_bool($gradecat)) {
172 $gradecat = $item->categoryid;
173 continue;
71ee4471 174 }
4b86bb08 175 if ($gradecat != $item->categoryid) {
176 //mixed categories
177 $gradecat = false;
178 break;
179 }
180 }
181 }
182
fc55273e
MG
183 if (!$hasgradeitems && $mform->elementExists('gradepass')) {
184 // Remove form element 'Grade to pass' since there are no grade items (when rating not selected).
185 $mform->removeElement('gradepass');
186 }
187
4b86bb08 188 if ($gradecat === false) {
189 // items and outcomes in different categories - remove the option
16306628 190 // TODO: add a "Mixed categories" text instead of removing elements with no explanation
4b86bb08 191 if ($mform->elementExists('gradecat')) {
192 $mform->removeElement('gradecat');
7dc5dac6 193 if ($this->_features->rating && !$mform->elementExists('gradepass')) {
16306628
AD
194 //if supports ratings then the max grade dropdown wasnt added so the grade box can be removed entirely
195 $mform->removeElement('modstandardgrade');
196 }
71ee4471 197 }
198 }
199 }
200 }
24f41672 201
a78890d5 202 if ($COURSE->groupmodeforce) {
203 if ($mform->elementExists('groupmode')) {
24f41672 204 $mform->hardFreeze('groupmode'); // groupmode can not be changed if forced from course settings
205 }
206 }
a104debf 207
6d02b265 208 // Don't disable/remove groupingid if it is currently set to something,
209 // otherwise you cannot turn it off at same time as turning off other
210 // option (MDL-30764)
211 if (empty($this->_cm) || !$this->_cm->groupingid) {
4717a5fc 212 if ($mform->elementExists('groupmode') && empty($COURSE->groupmodeforce)) {
6d02b265 213 $mform->disabledIf('groupingid', 'groupmode', 'eq', NOGROUPS);
214
4717a5fc 215 } else if (!$mform->elementExists('groupmode')) {
216 // Groupings have no use without groupmode.
6d02b265 217 if ($mform->elementExists('groupingid')) {
218 $mform->removeElement('groupingid');
219 }
a104debf 220 }
221 }
4e781c7b 222
223 // Completion: If necessary, freeze fields
61fceb86 224 $completion = new completion_info($COURSE);
225 if ($completion->is_enabled()) {
4e781c7b 226 // If anybody has completed the activity, these options will be 'locked'
227 $completedcount = empty($this->_cm)
228 ? 0
229 : $completion->count_user_data($this->_cm);
230
61fceb86 231 $freeze = false;
232 if (!$completedcount) {
233 if ($mform->elementExists('unlockcompletion')) {
4e781c7b 234 $mform->removeElement('unlockcompletion');
235 }
91d0bb0a
SM
236 // Automatically set to unlocked (note: this is necessary
237 // in order to make it recalculate completion once the option
238 // is changed, maybe someone has completed it now)
239 $mform->getElement('completionunlocked')->setValue(1);
4e781c7b 240 } else {
17109731 241 // Has the element been unlocked, either by the button being pressed
242 // in this request, or the field already being set from a previous one?
243 if ($mform->exportValue('unlockcompletion') ||
244 $mform->exportValue('completionunlocked')) {
4e781c7b 245 // Yes, add in warning text and set the hidden variable
246 $mform->insertElementBefore(
61fceb86 247 $mform->createElement('static', 'completedunlocked',
248 get_string('completedunlocked', 'completion'),
249 get_string('completedunlockedtext', 'completion')),
4e781c7b 250 'unlockcompletion');
251 $mform->removeElement('unlockcompletion');
252 $mform->getElement('completionunlocked')->setValue(1);
253 } else {
254 // No, add in the warning text with the count (now we know
255 // it) before the unlock button
256 $mform->insertElementBefore(
61fceb86 257 $mform->createElement('static', 'completedwarning',
258 get_string('completedwarning', 'completion'),
259 get_string('completedwarningtext', 'completion', $completedcount)),
4e781c7b 260 'unlockcompletion');
61fceb86 261 $freeze = true;
4e781c7b 262 }
aa6c1ced 263 }
4e781c7b 264
61fceb86 265 if ($freeze) {
4e781c7b 266 $mform->freeze('completion');
61fceb86 267 if ($mform->elementExists('completionview')) {
4e781c7b 268 $mform->freeze('completionview'); // don't use hardFreeze or checkbox value gets lost
269 }
61fceb86 270 if ($mform->elementExists('completionusegrade')) {
4e781c7b 271 $mform->freeze('completionusegrade');
272 }
273 $mform->freeze($this->_customcompletionelements);
aa6c1ced 274 }
4e781c7b 275 }
82bd6a5e 276
fa358a83 277 // Freeze admin defaults if required (and not different from default)
c3037116 278 $this->apply_admin_locked_flags();
71ee4471 279 }
280
60243313 281 // form verification
a78890d5 282 function validation($data, $files) {
400c0fd2 283 global $COURSE, $DB, $CFG;
a78890d5 284 $errors = parent::validation($data, $files);
60243313 285
273eb2f5 286 $mform =& $this->_form;
287
60243313 288 $errors = array();
289
273eb2f5 290 if ($mform->elementExists('name')) {
291 $name = trim($data['name']);
292 if ($name == '') {
293 $errors['name'] = get_string('required');
294 }
e04ff2d5 295 }
296
60243313 297 $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$data['modulename'],
298 'iteminstance'=>$data['instance'], 'itemnumber'=>0, 'courseid'=>$COURSE->id));
299 if ($data['coursemodule']) {
32648682 300 $cm = $DB->get_record('course_modules', array('id'=>$data['coursemodule']));
60243313 301 } else {
302 $cm = null;
303 }
304
273eb2f5 305 if ($mform->elementExists('cmidnumber')) {
306 // verify the idnumber
204175c5 307 if (!grade_verify_idnumber($data['cmidnumber'], $COURSE->id, $grade_item, $cm)) {
273eb2f5 308 $errors['cmidnumber'] = get_string('idnumbertaken');
309 }
60243313 310 }
aa6c1ced 311
d28e0a2f
SH
312 // Ratings: Don't let them select an aggregate type without selecting a scale.
313 // If the user has selected to use ratings but has not chosen a scale or set max points then the form is
314 // invalid. If ratings have been selected then the user must select either a scale or max points.
315 // This matches (horrible) logic in data_preprocessing.
316 if (isset($data['assessed']) && $data['assessed'] > 0 && empty($data['scale'])) {
317 $errors['assessed'] = get_string('scaleselectionrequired', 'rating');
318 }
319
9758da6e
MN
320 // Check that the grade pass is a valid number.
321 $gradepassvalid = false;
322 if (isset($data['gradepass'])) {
323 if (unformat_float($data['gradepass'], true) === false) {
324 $errors['gradepass'] = get_string('err_numeric', 'form');
325 } else {
326 $gradepassvalid = true;
327 }
328 }
329
8164fad4
GF
330 // Grade to pass: ensure that the grade to pass is valid for points and scales.
331 // If we are working with a scale, convert into a positive number for validation.
9758da6e 332 if ($gradepassvalid && isset($data['gradepass']) && (!empty($data['grade']) || !empty($data['scale']))) {
a824e60a 333 $scale = !empty($data['grade']) ? $data['grade'] : $data['scale'];
c977b350
MG
334 if ($scale < 0) {
335 $scalevalues = $DB->get_record('scale', array('id' => -$scale));
336 $grade = count(explode(',', $scalevalues->scale));
8164fad4 337 } else {
c977b350 338 $grade = $scale;
8164fad4 339 }
c977b350
MG
340 if ($data['gradepass'] > $grade) {
341 $errors['gradepass'] = get_string('gradepassgreaterthangrade', 'grades', $grade);
8164fad4
GF
342 }
343 }
344
4e781c7b 345 // Completion: Don't let them choose automatic completion without turning
a9f5fc15 346 // on some conditions. Ignore this check when completion settings are
347 // locked, as the options are then disabled.
348 if (array_key_exists('completion', $data) &&
349 $data['completion'] == COMPLETION_TRACKING_AUTOMATIC &&
350 !empty($data['completionunlocked'])) {
61fceb86 351 if (empty($data['completionview']) && empty($data['completionusegrade']) &&
4e781c7b 352 !$this->completion_rule_enabled($data)) {
61fceb86 353 $errors['completion'] = get_string('badautocompletion', 'completion');
4e781c7b 354 }
355 }
60243313 356
400c0fd2 357 // Availability: Check availability field does not have errors.
358 if (!empty($CFG->enableavailability)) {
359 \core_availability\frontend::report_validation_errors($data, $errors);
a6b538a8
MN
360 }
361
a78890d5 362 return $errors;
60243313 363 }
364
e24b7f85 365 /**
366 * Load in existing data as form defaults. Usually new entry defaults are stored directly in
367 * form definition (new entry form); this function is used to load in data where values
368 * already exist and data is being edited (edit entry form).
369 *
370 * @param mixed $default_values object or array of default values
371 */
32db0d42 372 function set_data($default_values) {
e24b7f85 373 if (is_object($default_values)) {
374 $default_values = (array)$default_values;
375 }
82bd6a5e 376
aa6c1ced 377 $this->data_preprocessing($default_values);
82bd6a5e 378 parent::set_data($default_values);
e24b7f85 379 }
71ee4471 380
e24b7f85 381 /**
382 * Adds all the standard elements to a form to edit the settings for an activity module.
e24b7f85 383 */
42f103be 384 function standard_coursemodule_elements(){
c18269c7 385 global $COURSE, $CFG, $DB;
e24b7f85 386 $mform =& $this->_form;
f3b783f4 387
1d50dd13 388 $this->_outcomesused = false;
42f103be 389 if ($this->_features->outcomes) {
f3b783f4 390 if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
1d50dd13 391 $this->_outcomesused = true;
f3b783f4 392 $mform->addElement('header', 'modoutcomes', get_string('outcomes', 'grades'));
393 foreach($outcomes as $outcome) {
394 $mform->addElement('advcheckbox', 'outcome_'.$outcome->id, $outcome->get_name());
395 }
396 }
397 }
398
36f3228c 399
16306628 400 if ($this->_features->rating) {
63e87951 401 require_once($CFG->dirroot.'/rating/lib.php');
b47dcdf0 402 $rm = new rating_manager();
63e87951
AD
403
404 $mform->addElement('header', 'modstandardratings', get_string('ratings', 'rating'));
a09aeee4 405
750925e2 406 $permission=CAP_ALLOW;
f9cd798c 407 $rolenamestring = null;
664d8be7 408 $isupdate = false;
f9cd798c 409 if (!empty($this->_cm)) {
664d8be7 410 $isupdate = true;
9a5e297b 411 $context = context_module::instance($this->_cm->id);
0d54a398 412
f9cd798c
AD
413 $rolenames = get_role_names_with_caps_in_context($context, array('moodle/rating:rate', 'mod/'.$this->_cm->modname.':rate'));
414 $rolenamestring = implode(', ', $rolenames);
415 } else {
416 $rolenamestring = get_string('capabilitychecknotavailable','rating');
0d54a398 417 }
f9cd798c 418 $mform->addElement('static', 'rolewarning', get_string('rolewarning','rating'), $rolenamestring);
bbf27733 419 $mform->addHelpButton('rolewarning', 'rolewarning', 'rating');
a09aeee4 420
63e87951 421 $mform->addElement('select', 'assessed', get_string('aggregatetype', 'rating') , $rm->get_aggregate_types());
a09aeee4 422 $mform->setDefault('assessed', 0);
d90dd973 423 $mform->addHelpButton('assessed', 'aggregatetype', 'rating');
a09aeee4 424
664d8be7
MN
425 $gradeoptions = array('isupdate' => $isupdate,
426 'currentgrade' => false,
427 'hasgrades' => false,
428 'canrescale' => $this->_features->canrescale,
429 'useratings' => $this->_features->rating);
430 if ($isupdate) {
431 $gradeitem = grade_item::fetch(array('itemtype' => 'mod',
432 'itemmodule' => $this->_cm->modname,
433 'iteminstance' => $this->_cm->instance,
434 'itemnumber' => 0,
435 'courseid' => $COURSE->id));
436 if ($gradeitem) {
437 $gradeoptions['currentgrade'] = $gradeitem->grademax;
438 $gradeoptions['currentgradetype'] = $gradeitem->gradetype;
439 $gradeoptions['currentscaleid'] = $gradeitem->scaleid;
440 $gradeoptions['hasgrades'] = $gradeitem->has_grades();
441 }
442 }
443 $mform->addElement('modgrade', 'scale', get_string('scale'), $gradeoptions);
a09aeee4 444 $mform->disabledIf('scale', 'assessed', 'eq', 0);
59766233
JM
445 $mform->addHelpButton('scale', 'modgrade', 'grades');
446 $mform->setDefault('scale', $CFG->gradepointdefault);
a09aeee4 447
d90dd973 448 $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'rating'));
a09aeee4
AD
449 $mform->disabledIf('ratingtime', 'assessed', 'eq', 0);
450
451 $mform->addElement('date_time_selector', 'assesstimestart', get_string('from'));
452 $mform->disabledIf('assesstimestart', 'assessed', 'eq', 0);
453 $mform->disabledIf('assesstimestart', 'ratingtime');
454
455 $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to'));
456 $mform->disabledIf('assesstimefinish', 'assessed', 'eq', 0);
457 $mform->disabledIf('assesstimefinish', 'ratingtime');
458 }
459
16306628
AD
460 //doing this here means splitting up the grade related settings on the lesson settings page
461 //$this->standard_grading_coursemodule_elements();
462
24e25bc1 463 $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form'));
207c737d
FM
464
465 $mform->addElement('modvisible', 'visible', get_string('visible'));
466 if (!empty($this->_cm)) {
467 $context = context_module::instance($this->_cm->id);
468 if (!has_capability('moodle/course:activityvisibility', $context)) {
469 $mform->hardFreeze('visible');
470 }
471 }
472
473 if ($this->_features->idnumber) {
474 $mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
8af0dffb 475 $mform->setType('cmidnumber', PARAM_RAW);
207c737d
FM
476 $mform->addHelpButton('cmidnumber', 'idnumbermod');
477 }
478
4b86bb08 479 if ($this->_features->groups) {
a78890d5 480 $options = array(NOGROUPS => get_string('groupsnone'),
481 SEPARATEGROUPS => get_string('groupsseparate'),
482 VISIBLEGROUPS => get_string('groupsvisible'));
ddd9f926
DM
483 $mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $options, NOGROUPS);
484 $mform->addHelpButton('groupmode', 'groupmode', 'group');
e24b7f85 485 }
24f41672 486
4717a5fc 487 if ($this->_features->groupings) {
488 // Groupings selector - used to select grouping for groups in activity.
98da6021 489 $options = array();
98da6021
PS
490 if ($groupings = $DB->get_records('groupings', array('courseid'=>$COURSE->id))) {
491 foreach ($groupings as $grouping) {
492 $options[$grouping->id] = format_string($grouping->name);
24f41672 493 }
494 }
3ba63534
MG
495 core_collator::asort($options);
496 $options = array(0 => get_string('none')) + $options;
98da6021 497 $mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options);
ddd9f926 498 $mform->addHelpButton('groupingid', 'grouping', 'group');
98da6021 499 }
e04ff2d5 500
82bd6a5e 501 if (!empty($CFG->enableavailability)) {
a4c31832 502 // Add special button to end of previous section if groups/groupings
503 // are enabled.
504 if ($this->_features->groups || $this->_features->groupings) {
505 $mform->addElement('static', 'restrictgroupbutton', '',
506 html_writer::tag('button', get_string('restrictbygroup', 'availability'),
507 array('id' => 'restrictbygroup', 'disabled' => 'disabled')));
508 }
509
400c0fd2 510 // Availability field. This is just a textarea; the user interface
511 // interaction is all implemented in JavaScript.
6282381d 512 $mform->addElement('header', 'availabilityconditionsheader',
400c0fd2 513 get_string('restrictaccess', 'availability'));
514 // Note: This field cannot be named 'availability' because that
515 // conflicts with fields in existing modules (such as assign).
516 // So it uses a long name that will not conflict.
517 $mform->addElement('textarea', 'availabilityconditionsjson',
518 get_string('accessrestrictions', 'availability'));
519 // The _cm variable may not be a proper cm_info, so get one from modinfo.
520 if ($this->_cm) {
42f103be 521 $modinfo = get_fast_modinfo($COURSE);
400c0fd2 522 $cm = $modinfo->get_cm($this->_cm->id);
523 } else {
524 $cm = null;
82bd6a5e 525 }
400c0fd2 526 \core_availability\frontend::include_all_javascript($COURSE, $cm);
82bd6a5e 527 }
528
aa6c1ced 529 // Conditional activities: completion tracking section
82bd6a5e 530 if(!isset($completion)) {
531 $completion = new completion_info($COURSE);
532 }
61fceb86 533 if ($completion->is_enabled()) {
3fbe0e8a 534 $mform->addElement('header', 'activitycompletionheader', get_string('activitycompletion', 'completion'));
4e781c7b 535
536 // Unlock button for if people have completed it (will
537 // be removed in definition_after_data if they haven't)
61fceb86 538 $mform->addElement('submit', 'unlockcompletion', get_string('unlockcompletion', 'completion'));
4e781c7b 539 $mform->registerNoSubmitButton('unlockcompletion');
61fceb86 540 $mform->addElement('hidden', 'completionunlocked', 0);
d18e0fe6 541 $mform->setType('completionunlocked', PARAM_INT);
82bd6a5e 542
4ed60499
EM
543 $trackingdefault = COMPLETION_TRACKING_NONE;
544 // If system and activity default is on, set it.
545 if ($CFG->completiondefault && $this->_features->defaultcompletion) {
546 $trackingdefault = COMPLETION_TRACKING_MANUAL;
547 }
548
aa6c1ced
PS
549 $mform->addElement('select', 'completion', get_string('completion', 'completion'),
550 array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'),
61fceb86 551 COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion')));
4ed60499 552 $mform->setDefault('completion', $trackingdefault);
c8dcb793 553 $mform->addHelpButton('completion', 'completion', 'completion');
4e781c7b 554
555 // Automatic completion once you view it
61fceb86 556 $gotcompletionoptions = false;
9593d8b8 557 if (plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
61fceb86 558 $mform->addElement('checkbox', 'completionview', get_string('completionview', 'completion'),
ddd9f926 559 get_string('completionview_desc', 'completion'));
61fceb86 560 $mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
561 $gotcompletionoptions = true;
4e781c7b 562 }
563
564 // Automatic completion once it's graded
9593d8b8 565 if (plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false)) {
61fceb86 566 $mform->addElement('checkbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
ddd9f926 567 get_string('completionusegrade_desc', 'completion'));
61fceb86 568 $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
af455aa0 569 $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
61fceb86 570 $gotcompletionoptions = true;
97c3c2c2 571
572 // If using the rating system, there is no grade unless ratings are enabled.
573 if ($this->_features->rating) {
574 $mform->disabledIf('completionusegrade', 'assessed', 'eq', 0);
575 }
4e781c7b 576 }
577
578 // Automatic completion according to module-specific rules
579 $this->_customcompletionelements = $this->add_completion_rules();
61fceb86 580 foreach ($this->_customcompletionelements as $element) {
aa6c1ced 581 $mform->disabledIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
4e781c7b 582 }
583
584 $gotcompletionoptions = $gotcompletionoptions ||
585 count($this->_customcompletionelements)>0;
586
587 // Automatic option only appears if possible
61fceb86 588 if ($gotcompletionoptions) {
4e781c7b 589 $mform->getElement('completion')->addOption(
61fceb86 590 get_string('completion_automatic', 'completion'),
4e781c7b 591 COMPLETION_TRACKING_AUTOMATIC);
aa6c1ced 592 }
4e781c7b 593
594 // Completion expected at particular date? (For progress tracking)
61fceb86 595 $mform->addElement('date_selector', 'completionexpected', get_string('completionexpected', 'completion'), array('optional'=>true));
ddd9f926 596 $mform->addHelpButton('completionexpected', 'completionexpected', 'completion');
aa6c1ced 597 $mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
4e781c7b 598 }
599
dffcf46f
NK
600 // Populate module tags.
601 $categorycontext = context_coursecat::instance($COURSE->category);
602 $coursecontext = context_course::instance($COURSE->id);
603 if (core_tag_tag::is_enabled('core', 'course_modules')) {
604 $mform->addElement('header', 'tagshdr', get_string('tags', 'tag'));
605 $mform->addElement('tags', 'tags', get_string('tags'), array('itemtype' => 'course_modules', 'component' => 'core'));
606 if ($this->_cm) {
607 $modinfo = get_fast_modinfo($COURSE);
608 $cm = $modinfo->get_cm($this->_cm->id);
609 $tags = core_tag_tag::get_item_tags_array('core', 'course_modules', $cm->id);
610 $mform->setDefault('tags', $tags);
611 }
612 }
613
e24b7f85 614 $this->standard_hidden_coursemodule_elements();
615 }
aa6c1ced 616
4e781c7b 617 /**
618 * Can be overridden to add custom completion rules if the module wishes
619 * them. If overriding this, you should also override completion_rule_enabled.
620 * <p>
621 * Just add elements to the form as needed and return the list of IDs. The
622 * system will call disabledIf and handle other behaviour for each returned
623 * ID.
624 * @return array Array of string IDs of added items, empty array if none
625 */
626 function add_completion_rules() {
627 return array();
628 }
629
630 /**
631 * Called during validation. Override to indicate, based on the data, whether
632 * a custom completion rule is enabled (selected).
633 *
634 * @param array $data Input data (not yet validated)
635 * @return bool True if one or more rules is enabled, false if none are;
636 * default returns false
637 */
54352ac9 638 function completion_rule_enabled($data) {
4e781c7b 639 return false;
640 }
e24b7f85 641
642 function standard_hidden_coursemodule_elements(){
643 $mform =& $this->_form;
644 $mform->addElement('hidden', 'course', 0);
645 $mform->setType('course', PARAM_INT);
646
647 $mform->addElement('hidden', 'coursemodule', 0);
648 $mform->setType('coursemodule', PARAM_INT);
649
650 $mform->addElement('hidden', 'section', 0);
651 $mform->setType('section', PARAM_INT);
652
653 $mform->addElement('hidden', 'module', 0);
654 $mform->setType('module', PARAM_INT);
655
656 $mform->addElement('hidden', 'modulename', '');
aff24313 657 $mform->setType('modulename', PARAM_PLUGIN);
e24b7f85 658
659 $mform->addElement('hidden', 'instance', 0);
660 $mform->setType('instance', PARAM_INT);
661
662 $mform->addElement('hidden', 'add', 0);
663 $mform->setType('add', PARAM_ALPHA);
664
665 $mform->addElement('hidden', 'update', 0);
666 $mform->setType('update', PARAM_INT);
19110c57 667
668 $mform->addElement('hidden', 'return', 0);
669 $mform->setType('return', PARAM_BOOL);
a41b1d96
FM
670
671 $mform->addElement('hidden', 'sr', 0);
672 $mform->setType('sr', PARAM_INT);
e24b7f85 673 }
674
60d5a2e4 675 public function standard_grading_coursemodule_elements() {
16306628 676 global $COURSE, $CFG;
1d50dd13 677 $mform =& $this->_form;
d629c601
DW
678 $isupdate = !empty($this->_cm);
679 $gradeoptions = array('isupdate' => $isupdate,
680 'currentgrade' => false,
681 'hasgrades' => false,
664d8be7
MN
682 'canrescale' => $this->_features->canrescale,
683 'useratings' => $this->_features->rating);
1d50dd13 684
16306628 685 if ($this->_features->hasgrades) {
1d50dd13 686
16306628
AD
687 if (!$this->_features->rating || $this->_features->gradecat) {
688 $mform->addElement('header', 'modstandardgrade', get_string('grade'));
689 }
690
691 //if supports grades and grades arent being handled via ratings
692 if (!$this->_features->rating) {
d629c601
DW
693
694 if ($isupdate) {
695 $gradeitem = grade_item::fetch(array('itemtype' => 'mod',
696 'itemmodule' => $this->_cm->modname,
697 'iteminstance' => $this->_cm->instance,
698 'itemnumber' => 0,
699 'courseid' => $COURSE->id));
664d8be7
MN
700 if ($gradeitem) {
701 $gradeoptions['currentgrade'] = $gradeitem->grademax;
702 $gradeoptions['currentgradetype'] = $gradeitem->gradetype;
703 $gradeoptions['currentscaleid'] = $gradeitem->scaleid;
704 $gradeoptions['hasgrades'] = $gradeitem->has_grades();
705 }
d629c601
DW
706 }
707 $mform->addElement('modgrade', 'grade', get_string('grade'), $gradeoptions);
59766233
JM
708 $mform->addHelpButton('grade', 'modgrade', 'grades');
709 $mform->setDefault('grade', $CFG->gradepointdefault);
16306628 710 }
1d50dd13 711
b11f9da6
DM
712 if ($this->_features->advancedgrading
713 and !empty($this->current->_advancedgradingdata['methods'])
714 and !empty($this->current->_advancedgradingdata['areas'])) {
715
716 if (count($this->current->_advancedgradingdata['areas']) == 1) {
717 // if there is just one gradable area (most cases), display just the selector
718 // without its name to make UI simplier
719 $areadata = reset($this->current->_advancedgradingdata['areas']);
720 $areaname = key($this->current->_advancedgradingdata['areas']);
721 $mform->addElement('select', 'advancedgradingmethod_'.$areaname,
722 get_string('gradingmethod', 'core_grading'), $this->current->_advancedgradingdata['methods']);
6832a102 723 $mform->addHelpButton('advancedgradingmethod_'.$areaname, 'gradingmethod', 'core_grading');
c977b350
MG
724 if (!$this->_features->rating) {
725 $mform->disabledIf('advancedgradingmethod_'.$areaname, 'grade[modgrade_type]', 'eq', 'none');
726 }
b11f9da6
DM
727
728 } else {
729 // the module defines multiple gradable areas, display a selector
730 // for each of them together with a name of the area
731 $areasgroup = array();
732 foreach ($this->current->_advancedgradingdata['areas'] as $areaname => $areadata) {
733 $areasgroup[] = $mform->createElement('select', 'advancedgradingmethod_'.$areaname,
734 $areadata['title'], $this->current->_advancedgradingdata['methods']);
735 $areasgroup[] = $mform->createElement('static', 'advancedgradingareaname_'.$areaname, '', $areadata['title']);
736 }
737 $mform->addGroup($areasgroup, 'advancedgradingmethodsgroup', get_string('gradingmethods', 'core_grading'),
738 array(' ', '<br />'), false);
739 }
740 }
741
1d50dd13 742 if ($this->_features->gradecat) {
71c4154a
TH
743 $mform->addElement('select', 'gradecat',
744 get_string('gradecategoryonmodform', 'grades'),
745 grade_get_categories_menu($COURSE->id, $this->_outcomesused));
746 $mform->addHelpButton('gradecat', 'gradecategoryonmodform', 'grades');
c977b350
MG
747 if (!$this->_features->rating) {
748 $mform->disabledIf('gradecat', 'grade[modgrade_type]', 'eq', 'none');
749 }
1d50dd13 750 }
c977b350
MG
751
752 // Grade to pass.
753 $mform->addElement('text', 'gradepass', get_string('gradepass', 'grades'));
754 $mform->addHelpButton('gradepass', 'gradepass', 'grades');
755 $mform->setDefault('gradepass', '');
9758da6e 756 $mform->setType('gradepass', PARAM_RAW);
c977b350 757 if (!$this->_features->rating) {
8164fad4 758 $mform->disabledIf('gradepass', 'grade[modgrade_type]', 'eq', 'none');
7080b38f
MN
759 } else {
760 $mform->disabledIf('gradepass', 'assessed', 'eq', '0');
8164fad4 761 }
1d50dd13
AD
762 }
763 }
764
6398ff53
AH
765 /**
766 * Add an editor for an activity's introduction field.
767 * @deprecated since MDL-49101 - use moodleform_mod::standard_intro_elements() instead.
768 * @param null $required Override system default for requiremodintro
769 * @param null $customlabel Override default label for editor
770 * @throws coding_exception
771 */
772 protected function add_intro_editor($required=null, $customlabel=null) {
773 $str = "Function moodleform_mod::add_intro_editor() is deprecated, use moodleform_mod::standard_intro_elements() instead.";
774 debugging($str, DEBUG_DEVELOPER);
775
776 $this->standard_intro_elements($customlabel);
777 }
778
779
780 /**
781 * Add an editor for an activity's introduction field.
782 *
783 * @param null $customlabel Override default label for editor
784 * @throws coding_exception
785 */
786 protected function standard_intro_elements($customlabel=null) {
787 global $CFG;
788
789 $required = $CFG->requiremodintro;
dc5c2bd9 790
791 $mform = $this->_form;
792 $label = is_null($customlabel) ? get_string('moduleintro') : $customlabel;
793
7829d5d7 794 $mform->addElement('editor', 'introeditor', $label, array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES,
f9beaf44 795 'noclean' => true, 'context' => $this->context, 'subdirs' => true));
dc5c2bd9 796 $mform->setType('introeditor', PARAM_RAW); // no XSS prevention here, users must be trusted
797 if ($required) {
798 $mform->addRule('introeditor', get_string('required'), 'required', null, 'client');
799 }
8c40662e 800
d9203fb7
FM
801 // If the 'show description' feature is enabled, this checkbox appears below the intro.
802 // We want to hide that when using the singleactivity course format because it is confusing.
803 if ($this->_features->showdescription && $this->courseformat->has_view_page()) {
8c40662e 804 $mform->addElement('checkbox', 'showdescription', get_string('showdescription'));
805 $mform->addHelpButton('showdescription', 'showdescription');
806 }
dc5c2bd9 807 }
808
6b467109 809 /**
810 * Overriding formslib's add_action_buttons() method, to add an extra submit "save changes and return" button.
b5ab55dd 811 *
812 * @param bool $cancel show cancel button
813 * @param string $submitlabel null means default, false means none, string is label text
814 * @param string $submit2label null means default, false means none, string is label text
6b467109 815 * @return void
b5ab55dd 816 */
6b467109 817 function add_action_buttons($cancel=true, $submitlabel=null, $submit2label=null) {
818 if (is_null($submitlabel)) {
819 $submitlabel = get_string('savechangesanddisplay');
820 }
b5ab55dd 821
6b467109 822 if (is_null($submit2label)) {
823 $submit2label = get_string('savechangesandreturntocourse');
824 }
b5ab55dd 825
dc5c2bd9 826 $mform = $this->_form;
b5ab55dd 827
828 // elements in a row need a group
829 $buttonarray = array();
830
d9203fb7
FM
831 // Label for the submit button to return to the course.
832 // Ignore this button in single activity format because it is confusing.
833 if ($submit2label !== false && $this->courseformat->has_view_page()) {
b5ab55dd 834 $buttonarray[] = &$mform->createElement('submit', 'submitbutton2', $submit2label);
835 }
836
837 if ($submitlabel !== false) {
838 $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
839 }
840
6b467109 841 if ($cancel) {
842 $buttonarray[] = &$mform->createElement('cancel');
843 }
844
845 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
de6f158f 846 $mform->setType('buttonar', PARAM_RAW);
6b467109 847 $mform->closeHeaderBefore('buttonar');
848 }
3c88a678
DW
849
850 /**
fa358a83
DW
851 * Get the list of admin settings for this module and apply any locked settings.
852 * This cannot happen in apply_admin_defaults because we do not the current values of the settings
853 * in that function because set_data has not been called yet.
3c88a678 854 *
3c88a678
DW
855 * @return void
856 */
c3037116 857 protected function apply_admin_locked_flags() {
3c88a678
DW
858 global $OUTPUT;
859
c3037116 860 if (!$this->applyadminlockedflags) {
fa358a83
DW
861 return;
862 }
fa358a83 863
3c88a678
DW
864 $settings = get_config($this->_modname);
865 $mform = $this->_form;
866 $lockedicon = html_writer::tag('span',
867 $OUTPUT->pix_icon('t/locked', get_string('locked', 'admin')),
868 array('class' => 'action-icon'));
54106942 869 $isupdate = !empty($this->_cm);
3c88a678
DW
870
871 foreach ($settings as $name => $value) {
872 if (strpos('_', $name) !== false) {
873 continue;
874 }
875 if ($mform->elementExists($name)) {
876 $element = $mform->getElement($name);
3c88a678
DW
877 $lockedsetting = $name . '_locked';
878 if (!empty($settings->$lockedsetting)) {
54106942
DW
879 // Always lock locked settings for new modules,
880 // for updates, only lock them if the current value is the same as the default (or there is no current value).
881 $value = $settings->$name;
882 if ($isupdate && isset($this->current->$name)) {
883 $value = $this->current->$name;
884 }
fa358a83
DW
885 if ($value == $settings->$name) {
886 $mform->setConstant($name, $settings->$name);
887 $element->setLabel($element->getLabel() . $lockedicon);
888 // Do not use hardfreeze because we need the hidden input to check dependencies.
889 $element->freeze();
890 }
3c88a678
DW
891 }
892 }
893 }
894 }
fa358a83
DW
895
896 /**
897 * Get the list of admin settings for this module and apply any defaults/advanced/locked settings.
898 *
899 * @param $datetimeoffsets array - If passed, this is an array of fieldnames => times that the
900 * default date/time value should be relative to. If not passed, all
901 * date/time fields are set relative to the users current midnight.
902 * @return void
903 */
904 public function apply_admin_defaults($datetimeoffsets = array()) {
c3037116
DW
905 // This flag triggers the settings to be locked in apply_admin_locked_flags().
906 $this->applyadminlockedflags = true;
907
908 $settings = get_config($this->_modname);
909 $mform = $this->_form;
910 $usermidnight = usergetmidnight(time());
911 $isupdate = !empty($this->_cm);
912
913 foreach ($settings as $name => $value) {
914 if (strpos('_', $name) !== false) {
915 continue;
916 }
917 if ($mform->elementExists($name)) {
918 $element = $mform->getElement($name);
919 if (!$isupdate) {
920 if ($element->getType() == 'date_time_selector') {
921 $enabledsetting = $name . '_enabled';
922 if (empty($settings->$enabledsetting)) {
923 $mform->setDefault($name, 0);
924 } else {
925 $relativetime = $usermidnight;
926 if (isset($datetimeoffsets[$name])) {
927 $relativetime = $datetimeoffsets[$name];
928 }
929 $mform->setDefault($name, $relativetime + $settings->$name);
930 }
931 } else {
932 $mform->setDefault($name, $settings->$name);
933 }
934 }
935 $advancedsetting = $name . '_adv';
936 if (!empty($settings->$advancedsetting)) {
937 $mform->setAdvanced($name);
938 }
939 }
940 }
fa358a83 941 }
e24b7f85 942}
943
aa6c1ced 944