Commit | Line | Data |
---|---|---|
aa6c1ced | 1 | <?php |
fcc88fdd AN |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * Moodleform. | |
19 | * | |
20 | * @package core_course | |
21 | * @copyright Andrew Nicols <andrew@nicols.co.uk> | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
23 | */ | |
24 | ||
25 | require_once($CFG->libdir.'/formslib.php'); | |
516c5eca | 26 | require_once($CFG->libdir.'/completionlib.php'); |
06cdda46 | 27 | require_once($CFG->libdir.'/gradelib.php'); |
7f53e8aa | 28 | require_once($CFG->libdir.'/plagiarismlib.php'); |
61fceb86 | 29 | |
fcc88fdd AN |
30 | use core_grades\component_gradeitems; |
31 | ||
e24b7f85 | 32 | /** |
fcc88fdd AN |
33 | * This class adds extra methods to form wrapper specific to be used for module add / update forms |
34 | * mod/{modname}/mod_form.php replaced deprecated mod/{modname}/mod.html Moodleform. | |
35 | * | |
36 | * @package core_course | |
37 | * @copyright Andrew Nicols <andrew@nicols.co.uk> | |
e24b7f85 | 38 | */ |
7b5bd060 | 39 | abstract class moodleform_mod extends moodleform { |
9a105989 | 40 | /** Current data */ |
41 | protected $current; | |
e24b7f85 | 42 | /** |
43 | * Instance of the module that is being updated. This is the id of the {prefix}{modulename} | |
44 | * record. Can be used in form definition. Will be "" if this is an 'add' form and not an | |
45 | * update one. | |
46 | * | |
47 | * @var mixed | |
48 | */ | |
42f103be | 49 | protected $_instance; |
e24b7f85 | 50 | /** |
51 | * Section of course that module instance will be put in or is in. | |
24f41672 | 52 | * This is always the section number itself (column 'section' from 'course_sections' table). |
e24b7f85 | 53 | * |
8341055e | 54 | * @var int |
e24b7f85 | 55 | */ |
42f103be | 56 | protected $_section; |
e24b7f85 | 57 | /** |
7e85563d | 58 | * Course module record of the module that is being updated. Will be null if this is an 'add' form and not an |
e24b7f85 | 59 | * update one. |
60 | * | |
61 | * @var mixed | |
62 | */ | |
42f103be | 63 | protected $_cm; |
8995c270 DW |
64 | |
65 | /** | |
66 | * Current course. | |
07fc0ec3 | 67 | * |
8995c270 DW |
68 | * @var mixed |
69 | */ | |
70 | protected $_course; | |
71 | ||
4b86bb08 | 72 | /** |
73 | * List of modform features | |
74 | */ | |
42f103be | 75 | protected $_features; |
4e781c7b | 76 | /** |
77 | * @var array Custom completion-rule elements, if enabled | |
78 | */ | |
42f103be | 79 | protected $_customcompletionelements; |
80 | /** | |
f9a7a98a | 81 | * @var string name of module. |
42f103be | 82 | */ |
83 | protected $_modname; | |
09f8d75e | 84 | /** current context, course or module depends if already exists*/ |
85 | protected $context; | |
e24b7f85 | 86 | |
1d50dd13 AD |
87 | /** a flag indicating whether outcomes are being used*/ |
88 | protected $_outcomesused; | |
89 | ||
fa358a83 | 90 | /** |
c3037116 DW |
91 | * @var bool A flag used to indicate that this module should lock settings |
92 | * based on admin settings flags in definition_after_data. | |
fa358a83 | 93 | */ |
c3037116 | 94 | protected $applyadminlockedflags = false; |
fa358a83 | 95 | |
d9203fb7 FM |
96 | /** @var object The course format of the current course. */ |
97 | protected $courseformat; | |
98 | ||
fcc88fdd AN |
99 | /** @var string Whether this is graded or rated. */ |
100 | private $gradedorrated = null; | |
101 | ||
1a0df553 | 102 | public function __construct($current, $section, $cm, $course) { |
d9203fb7 FM |
103 | global $CFG; |
104 | ||
9a105989 | 105 | $this->current = $current; |
106 | $this->_instance = $current->instance; | |
107 | $this->_section = $section; | |
108 | $this->_cm = $cm; | |
8995c270 | 109 | $this->_course = $course; |
09f8d75e | 110 | if ($this->_cm) { |
9a5e297b | 111 | $this->context = context_module::instance($this->_cm->id); |
09f8d75e | 112 | } else { |
9a5e297b | 113 | $this->context = context_course::instance($course->id); |
09f8d75e | 114 | } |
aa6c1ced | 115 | |
d9203fb7 FM |
116 | // Set the course format. |
117 | require_once($CFG->dirroot . '/course/format/lib.php'); | |
118 | $this->courseformat = course_get_format($course); | |
119 | ||
f9a7a98a TW |
120 | // Guess module name if not set. |
121 | if (is_null($this->_modname)) { | |
122 | $matches = array(); | |
123 | if (!preg_match('/^mod_([^_]+)_mod_form$/', get_class($this), $matches)) { | |
124 | debugging('Rename form to mod_xx_mod_form, where xx is name of your module'); | |
125 | print_error('unknownmodulename'); | |
126 | } | |
127 | $this->_modname = $matches[1]; | |
42f103be | 128 | } |
42f103be | 129 | $this->init_features(); |
1a0df553 MG |
130 | parent::__construct('modedit.php'); |
131 | } | |
132 | ||
133 | /** | |
134 | * Old syntax of class constructor. Deprecated in PHP7. | |
135 | * | |
136 | * @deprecated since Moodle 3.1 | |
137 | */ | |
138 | public function moodleform_mod($current, $section, $cm, $course) { | |
139 | debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); | |
140 | self::__construct($current, $section, $cm, $course); | |
e24b7f85 | 141 | } |
71ee4471 | 142 | |
8995c270 DW |
143 | /** |
144 | * Get the current data for the form. | |
145 | * @return stdClass|null | |
146 | */ | |
91e54642 | 147 | public function get_current() { |
8995c270 DW |
148 | return $this->current; |
149 | } | |
150 | ||
151 | /** | |
152 | * Get the DB record for the current instance. | |
153 | * @return stdClass|null | |
154 | */ | |
91e54642 | 155 | public function get_instance() { |
8995c270 DW |
156 | return $this->_instance; |
157 | } | |
158 | ||
159 | /** | |
160 | * Get the course section number (relative). | |
161 | * @return int | |
162 | */ | |
91e54642 | 163 | public function get_section() { |
8995c270 DW |
164 | return $this->_section; |
165 | } | |
166 | ||
167 | /** | |
168 | * Get the course id. | |
169 | * @return int | |
170 | */ | |
91e54642 | 171 | public function get_course() { |
8995c270 DW |
172 | return $this->_course; |
173 | } | |
174 | ||
175 | /** | |
176 | * Get the course module object. | |
177 | * @return stdClass|null | |
178 | */ | |
91e54642 | 179 | public function get_coursemodule() { |
8995c270 DW |
180 | return $this->_cm; |
181 | } | |
182 | ||
183 | /** | |
184 | * Return the course context for new modules, or the module context for existing modules. | |
185 | * @return context | |
186 | */ | |
91e54642 | 187 | public function get_context() { |
8995c270 DW |
188 | return $this->context; |
189 | } | |
190 | ||
191 | /** | |
192 | * Return the features this module supports. | |
193 | * @return stdClass | |
194 | */ | |
91e54642 | 195 | public function get_features() { |
8995c270 DW |
196 | return $this->_features; |
197 | } | |
198 | ||
199 | ||
42f103be | 200 | protected function init_features() { |
201 | global $CFG; | |
202 | ||
fbaea88f | 203 | $this->_features = new stdClass(); |
ea191cf1 | 204 | $this->_features->groups = plugin_supports('mod', $this->_modname, FEATURE_GROUPS, false); |
42f103be | 205 | $this->_features->groupings = plugin_supports('mod', $this->_modname, FEATURE_GROUPINGS, false); |
42f103be | 206 | $this->_features->outcomes = (!empty($CFG->enableoutcomes) and plugin_supports('mod', $this->_modname, FEATURE_GRADE_OUTCOMES, true)); |
207 | $this->_features->hasgrades = plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false); | |
208 | $this->_features->idnumber = plugin_supports('mod', $this->_modname, FEATURE_IDNUMBER, true); | |
dc5c2bd9 | 209 | $this->_features->introeditor = plugin_supports('mod', $this->_modname, FEATURE_MOD_INTRO, true); |
42f103be | 210 | $this->_features->defaultcompletion = plugin_supports('mod', $this->_modname, FEATURE_MODEDIT_DEFAULT_COMPLETION, true); |
16306628 | 211 | $this->_features->rating = plugin_supports('mod', $this->_modname, FEATURE_RATE, false); |
8c40662e | 212 | $this->_features->showdescription = plugin_supports('mod', $this->_modname, FEATURE_SHOW_DESCRIPTION, false); |
42f103be | 213 | $this->_features->gradecat = ($this->_features->outcomes or $this->_features->hasgrades); |
b11f9da6 | 214 | $this->_features->advancedgrading = plugin_supports('mod', $this->_modname, FEATURE_ADVANCED_GRADING, false); |
d629c601 | 215 | $this->_features->canrescale = (component_callback_exists('mod_' . $this->_modname, 'rescale_activity_grades') !== false); |
42f103be | 216 | } |
217 | ||
e24b7f85 | 218 | /** |
06cdda46 MG |
219 | * Allows module to modify data returned by get_moduleinfo_data() or prepare_new_moduleinfo_data() before calling set_data() |
220 | * This method is also called in the bulk activity completion form. | |
221 | * | |
a7f7e52f | 222 | * Only available on moodleform_mod. |
e24b7f85 | 223 | * |
224 | * @param array $default_values passed by reference | |
225 | */ | |
a7f7e52f | 226 | function data_preprocessing(&$default_values){ |
a09aeee4 AD |
227 | if (empty($default_values['scale'])) { |
228 | $default_values['assessed'] = 0; | |
229 | } | |
230 | ||
231 | if (empty($default_values['assessed'])){ | |
a09aeee4 AD |
232 | $default_values['ratingtime'] = 0; |
233 | } else { | |
a09aeee4 AD |
234 | $default_values['ratingtime']= |
235 | ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0; | |
236 | } | |
e24b7f85 | 237 | } |
71ee4471 | 238 | |
4b86bb08 | 239 | /** |
240 | * Each module which defines definition_after_data() must call this method using parent::definition_after_data(); | |
241 | */ | |
71ee4471 | 242 | function definition_after_data() { |
4b86bb08 | 243 | global $CFG, $COURSE; |
71ee4471 | 244 | $mform =& $this->_form; |
245 | ||
246 | if ($id = $mform->getElementValue('update')) { | |
247 | $modulename = $mform->getElementValue('modulename'); | |
248 | $instance = $mform->getElementValue('instance'); | |
fcc88fdd | 249 | $component = "mod_{$modulename}"; |
71ee4471 | 250 | |
4b86bb08 | 251 | if ($this->_features->gradecat) { |
fc55273e | 252 | $hasgradeitems = false; |
fcc88fdd AN |
253 | $items = grade_item::fetch_all([ |
254 | 'itemtype' => 'mod', | |
255 | 'itemmodule' => $modulename, | |
256 | 'iteminstance' => $instance, | |
257 | 'courseid' => $COURSE->id, | |
258 | ]); | |
259 | ||
260 | $gradecategories = []; | |
261 | $removecategories = []; | |
16306628 AD |
262 | //will be no items if, for example, this activity supports ratings but rating aggregate type == no ratings |
263 | if (!empty($items)) { | |
4b86bb08 | 264 | foreach ($items as $item) { |
265 | if (!empty($item->outcomeid)) { | |
266 | $elname = 'outcome_'.$item->outcomeid; | |
267 | if ($mform->elementExists($elname)) { | |
268 | $mform->hardFreeze($elname); // prevent removing of existing outcomes | |
269 | } | |
fc55273e MG |
270 | } else { |
271 | $hasgradeitems = true; | |
4b86bb08 | 272 | } |
273 | } | |
16306628 | 274 | |
4b86bb08 | 275 | foreach ($items as $item) { |
fcc88fdd AN |
276 | $gradecatfieldname = component_gradeitems::get_field_name_for_itemnumber( |
277 | $component, | |
278 | $item->itemnumber, | |
279 | 'gradecat' | |
280 | ); | |
281 | ||
282 | if (!isset($gradecategories[$gradecatfieldname])) { | |
283 | $gradecategories[$gradecatfieldname] = $item->categoryid; | |
284 | } else if ($gradecategories[$gradecatfieldname] != $item->categoryid) { | |
285 | $removecategories[$gradecatfieldname] = true; | |
4b86bb08 | 286 | } |
287 | } | |
288 | } | |
289 | ||
fcc88fdd AN |
290 | foreach ($removecategories as $toremove) { |
291 | if ($mform->elementExists($toremove)) { | |
292 | $mform->removeElement($toremove); | |
71ee4471 | 293 | } |
294 | } | |
295 | } | |
296 | } | |
24f41672 | 297 | |
a78890d5 | 298 | if ($COURSE->groupmodeforce) { |
299 | if ($mform->elementExists('groupmode')) { | |
fcc88fdd | 300 | // The groupmode can not be changed if forced from course settings. |
fcc88fdd | 301 | $mform->hardFreeze('groupmode'); |
24f41672 | 302 | } |
303 | } | |
a104debf | 304 | |
fcc88fdd AN |
305 | // Don't disable/remove groupingid if it is currently set to something, otherwise you cannot turn it off at same |
306 | // time as turning off other option (MDL-30764). | |
6d02b265 | 307 | if (empty($this->_cm) || !$this->_cm->groupingid) { |
4717a5fc | 308 | if ($mform->elementExists('groupmode') && empty($COURSE->groupmodeforce)) { |
87c7fb6e | 309 | $mform->hideIf('groupingid', 'groupmode', 'eq', NOGROUPS); |
6d02b265 | 310 | |
4717a5fc | 311 | } else if (!$mform->elementExists('groupmode')) { |
312 | // Groupings have no use without groupmode. | |
6d02b265 | 313 | if ($mform->elementExists('groupingid')) { |
314 | $mform->removeElement('groupingid'); | |
315 | } | |
de60737d JD |
316 | // Nor does the group restrictions button. |
317 | if ($mform->elementExists('restrictgroupbutton')) { | |
318 | $mform->removeElement('restrictgroupbutton'); | |
319 | } | |
a104debf | 320 | } |
321 | } | |
4e781c7b | 322 | |
323 | // Completion: If necessary, freeze fields | |
61fceb86 | 324 | $completion = new completion_info($COURSE); |
325 | if ($completion->is_enabled()) { | |
4e781c7b | 326 | // If anybody has completed the activity, these options will be 'locked' |
327 | $completedcount = empty($this->_cm) | |
328 | ? 0 | |
329 | : $completion->count_user_data($this->_cm); | |
330 | ||
61fceb86 | 331 | $freeze = false; |
332 | if (!$completedcount) { | |
333 | if ($mform->elementExists('unlockcompletion')) { | |
4e781c7b | 334 | $mform->removeElement('unlockcompletion'); |
335 | } | |
91d0bb0a SM |
336 | // Automatically set to unlocked (note: this is necessary |
337 | // in order to make it recalculate completion once the option | |
338 | // is changed, maybe someone has completed it now) | |
339 | $mform->getElement('completionunlocked')->setValue(1); | |
4e781c7b | 340 | } else { |
17109731 | 341 | // Has the element been unlocked, either by the button being pressed |
342 | // in this request, or the field already being set from a previous one? | |
343 | if ($mform->exportValue('unlockcompletion') || | |
344 | $mform->exportValue('completionunlocked')) { | |
4e781c7b | 345 | // Yes, add in warning text and set the hidden variable |
346 | $mform->insertElementBefore( | |
61fceb86 | 347 | $mform->createElement('static', 'completedunlocked', |
348 | get_string('completedunlocked', 'completion'), | |
349 | get_string('completedunlockedtext', 'completion')), | |
4e781c7b | 350 | 'unlockcompletion'); |
351 | $mform->removeElement('unlockcompletion'); | |
352 | $mform->getElement('completionunlocked')->setValue(1); | |
353 | } else { | |
354 | // No, add in the warning text with the count (now we know | |
355 | // it) before the unlock button | |
356 | $mform->insertElementBefore( | |
61fceb86 | 357 | $mform->createElement('static', 'completedwarning', |
358 | get_string('completedwarning', 'completion'), | |
359 | get_string('completedwarningtext', 'completion', $completedcount)), | |
4e781c7b | 360 | 'unlockcompletion'); |
61fceb86 | 361 | $freeze = true; |
4e781c7b | 362 | } |
aa6c1ced | 363 | } |
4e781c7b | 364 | |
61fceb86 | 365 | if ($freeze) { |
4e781c7b | 366 | $mform->freeze('completion'); |
61fceb86 | 367 | if ($mform->elementExists('completionview')) { |
4e781c7b | 368 | $mform->freeze('completionview'); // don't use hardFreeze or checkbox value gets lost |
369 | } | |
61fceb86 | 370 | if ($mform->elementExists('completionusegrade')) { |
4e781c7b | 371 | $mform->freeze('completionusegrade'); |
372 | } | |
fe795b59 AN |
373 | if ($mform->elementExists('completiongradeitemnumber')) { |
374 | $mform->freeze('completiongradeitemnumber'); | |
375 | } | |
4e781c7b | 376 | $mform->freeze($this->_customcompletionelements); |
aa6c1ced | 377 | } |
4e781c7b | 378 | } |
82bd6a5e | 379 | |
fa358a83 | 380 | // Freeze admin defaults if required (and not different from default) |
c3037116 | 381 | $this->apply_admin_locked_flags(); |
71ee4471 | 382 | } |
383 | ||
60243313 | 384 | // form verification |
a78890d5 | 385 | function validation($data, $files) { |
400c0fd2 | 386 | global $COURSE, $DB, $CFG; |
a78890d5 | 387 | $errors = parent::validation($data, $files); |
60243313 | 388 | |
273eb2f5 | 389 | $mform =& $this->_form; |
390 | ||
60243313 | 391 | $errors = array(); |
392 | ||
273eb2f5 | 393 | if ($mform->elementExists('name')) { |
394 | $name = trim($data['name']); | |
395 | if ($name == '') { | |
396 | $errors['name'] = get_string('required'); | |
397 | } | |
e04ff2d5 | 398 | } |
399 | ||
60243313 | 400 | $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$data['modulename'], |
401 | 'iteminstance'=>$data['instance'], 'itemnumber'=>0, 'courseid'=>$COURSE->id)); | |
402 | if ($data['coursemodule']) { | |
32648682 | 403 | $cm = $DB->get_record('course_modules', array('id'=>$data['coursemodule'])); |
60243313 | 404 | } else { |
405 | $cm = null; | |
406 | } | |
407 | ||
273eb2f5 | 408 | if ($mform->elementExists('cmidnumber')) { |
409 | // verify the idnumber | |
204175c5 | 410 | if (!grade_verify_idnumber($data['cmidnumber'], $COURSE->id, $grade_item, $cm)) { |
273eb2f5 | 411 | $errors['cmidnumber'] = get_string('idnumbertaken'); |
412 | } | |
60243313 | 413 | } |
aa6c1ced | 414 | |
fcc88fdd AN |
415 | $component = "mod_{$this->_modname}"; |
416 | $itemnames = component_gradeitems::get_itemname_mapping_for_component($component); | |
417 | foreach ($itemnames as $itemnumber => $itemname) { | |
418 | $gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade'); | |
419 | $gradepassfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradepass'); | |
420 | $assessedfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'assessed'); | |
421 | $scalefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'scale'); | |
422 | ||
423 | // Ratings: Don't let them select an aggregate type without selecting a scale. | |
424 | // If the user has selected to use ratings but has not chosen a scale or set max points then the form is | |
425 | // invalid. If ratings have been selected then the user must select either a scale or max points. | |
426 | // This matches (horrible) logic in data_preprocessing. | |
427 | if (isset($data[$assessedfieldname]) && $data[$assessedfieldname] > 0 && empty($data[$scalefieldname])) { | |
428 | $errors[$assessedfieldname] = get_string('scaleselectionrequired', 'rating'); | |
9758da6e | 429 | } |
9758da6e | 430 | |
fcc88fdd AN |
431 | // Check that the grade pass is a valid number. |
432 | $gradepassvalid = false; | |
433 | if (isset($data[$gradepassfieldname])) { | |
434 | if (unformat_float($data[$gradepassfieldname], true) === false) { | |
435 | $errors[$gradepassfieldname] = get_string('err_numeric', 'form'); | |
436 | } else { | |
437 | $gradepassvalid = true; | |
438 | } | |
8164fad4 | 439 | } |
fcc88fdd AN |
440 | |
441 | // Grade to pass: ensure that the grade to pass is valid for points and scales. | |
442 | // If we are working with a scale, convert into a positive number for validation. | |
443 | if ($gradepassvalid && isset($data[$gradepassfieldname]) && (!empty($data[$gradefieldname]) || !empty($data[$scalefieldname]))) { | |
444 | $scale = !empty($data[$gradefieldname]) ? $data[$gradefieldname] : $data[$scalefieldname]; | |
445 | if ($scale < 0) { | |
446 | $scalevalues = $DB->get_record('scale', array('id' => -$scale)); | |
447 | $grade = count(explode(',', $scalevalues->scale)); | |
448 | } else { | |
449 | $grade = $scale; | |
450 | } | |
451 | if (unformat_float($data[$gradepassfieldname]) > $grade) { | |
452 | $errors[$gradepassfieldname] = get_string('gradepassgreaterthangrade', 'grades', $grade); | |
453 | } | |
8164fad4 | 454 | } |
a492f69c | 455 | |
fe795b59 AN |
456 | // We have a grade if there is a non-falsey value for: |
457 | // - the assessedfieldname for Ratings there; or | |
458 | // - the gradefieldname for Ratings there. | |
459 | if (empty($data[$assessedfieldname]) && empty($data[$gradefieldname])) { | |
460 | // There are no grades set therefore completion is not allowed. | |
461 | if (isset($data['completiongradeitemnumber']) && $data['completiongradeitemnumber'] == (string) $itemnumber) { | |
462 | $errors['completiongradeitemnumber'] = get_string( | |
463 | 'badcompletiongradeitemnumber', | |
464 | 'completion', | |
465 | get_string("grade_{$itemname}_name", $component) | |
466 | ); | |
467 | } | |
468 | } | |
8164fad4 GF |
469 | } |
470 | ||
4e781c7b | 471 | // Completion: Don't let them choose automatic completion without turning |
a9f5fc15 | 472 | // on some conditions. Ignore this check when completion settings are |
473 | // locked, as the options are then disabled. | |
fe795b59 AN |
474 | $automaticcompletion = array_key_exists('completion', $data); |
475 | $automaticcompletion = $automaticcompletion && $data['completion'] == COMPLETION_TRACKING_AUTOMATIC; | |
476 | $automaticcompletion = $automaticcompletion && !empty($data['completionunlocked']); | |
477 | ||
478 | if ($automaticcompletion) { | |
479 | // View to complete. | |
480 | $rulesenabled = !empty($data['completionview']); | |
481 | ||
482 | // Use grade to complete (only one grade item). | |
483 | $rulesenabled = $rulesenabled || !empty($data['completionusegrade']); | |
484 | ||
485 | // Use grade to complete (specific grade item). | |
486 | if (!$rulesenabled && isset($data['completiongradeitemnumber'])) { | |
487 | $rulesenabled = $data['completiongradeitemnumber'] != ''; | |
488 | } | |
489 | ||
490 | // Module-specific completion rules. | |
491 | $rulesenabled = $rulesenabled || $this->completion_rule_enabled($data); | |
492 | ||
493 | if (!$rulesenabled) { | |
494 | // No rules are enabled. Can't set automatically completed without rules. | |
61fceb86 | 495 | $errors['completion'] = get_string('badautocompletion', 'completion'); |
4e781c7b | 496 | } |
497 | } | |
60243313 | 498 | |
400c0fd2 | 499 | // Availability: Check availability field does not have errors. |
500 | if (!empty($CFG->enableavailability)) { | |
501 | \core_availability\frontend::report_validation_errors($data, $errors); | |
a6b538a8 MN |
502 | } |
503 | ||
8995c270 DW |
504 | $pluginerrors = $this->plugin_extend_coursemodule_validation($data); |
505 | if (!empty($pluginerrors)) { | |
506 | $errors = array_merge($errors, $pluginerrors); | |
507 | } | |
508 | ||
509 | return $errors; | |
510 | } | |
511 | ||
512 | /** | |
513 | * Extend the validation function from any other plugin. | |
514 | * | |
515 | * @param stdClass $data The form data. | |
516 | * @return array $errors The list of errors keyed by element name. | |
517 | */ | |
91e54642 | 518 | protected function plugin_extend_coursemodule_validation($data) { |
8995c270 DW |
519 | $errors = array(); |
520 | ||
521 | $callbacks = get_plugins_with_function('coursemodule_validation', 'lib.php'); | |
522 | foreach ($callbacks as $type => $plugins) { | |
523 | foreach ($plugins as $plugin => $pluginfunction) { | |
524 | // We have exposed all the important properties with public getters - the errors array should be pass by reference. | |
525 | $pluginerrors = $pluginfunction($this, $data); | |
526 | if (!empty($pluginerrors)) { | |
527 | $errors = array_merge($errors, $pluginerrors); | |
528 | } | |
529 | } | |
530 | } | |
a78890d5 | 531 | return $errors; |
60243313 | 532 | } |
533 | ||
e24b7f85 | 534 | /** |
535 | * Load in existing data as form defaults. Usually new entry defaults are stored directly in | |
536 | * form definition (new entry form); this function is used to load in data where values | |
537 | * already exist and data is being edited (edit entry form). | |
538 | * | |
539 | * @param mixed $default_values object or array of default values | |
540 | */ | |
32db0d42 | 541 | function set_data($default_values) { |
e24b7f85 | 542 | if (is_object($default_values)) { |
543 | $default_values = (array)$default_values; | |
544 | } | |
82bd6a5e | 545 | |
aa6c1ced | 546 | $this->data_preprocessing($default_values); |
82bd6a5e | 547 | parent::set_data($default_values); |
e24b7f85 | 548 | } |
71ee4471 | 549 | |
e24b7f85 | 550 | /** |
551 | * Adds all the standard elements to a form to edit the settings for an activity module. | |
e24b7f85 | 552 | */ |
fcc88fdd | 553 | protected function standard_coursemodule_elements() { |
c18269c7 | 554 | global $COURSE, $CFG, $DB; |
e24b7f85 | 555 | $mform =& $this->_form; |
f3b783f4 | 556 | |
1d50dd13 | 557 | $this->_outcomesused = false; |
42f103be | 558 | if ($this->_features->outcomes) { |
f3b783f4 | 559 | if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) { |
1d50dd13 | 560 | $this->_outcomesused = true; |
f3b783f4 | 561 | $mform->addElement('header', 'modoutcomes', get_string('outcomes', 'grades')); |
562 | foreach($outcomes as $outcome) { | |
563 | $mform->addElement('advcheckbox', 'outcome_'.$outcome->id, $outcome->get_name()); | |
564 | } | |
565 | } | |
566 | } | |
567 | ||
16306628 | 568 | if ($this->_features->rating) { |
fcc88fdd | 569 | $this->add_rating_settings($mform, 0); |
a09aeee4 AD |
570 | } |
571 | ||
24e25bc1 | 572 | $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form')); |
207c737d | 573 | |
8341055e MG |
574 | $section = get_fast_modinfo($COURSE)->get_section_info($this->_section); |
575 | $allowstealth = !empty($CFG->allowstealth) && $this->courseformat->allow_stealth_module_visibility($this->_cm, $section); | |
125c4c4c MG |
576 | if ($allowstealth && $section->visible) { |
577 | $modvisiblelabel = 'modvisiblewithstealth'; | |
578 | } else if ($section->visible) { | |
579 | $modvisiblelabel = 'modvisible'; | |
580 | } else { | |
581 | $modvisiblelabel = 'modvisiblehiddensection'; | |
582 | } | |
583 | $mform->addElement('modvisible', 'visible', get_string($modvisiblelabel), null, | |
8341055e | 584 | array('allowstealth' => $allowstealth, 'sectionvisible' => $section->visible, 'cm' => $this->_cm)); |
125c4c4c | 585 | $mform->addHelpButton('visible', $modvisiblelabel); |
207c737d FM |
586 | if (!empty($this->_cm)) { |
587 | $context = context_module::instance($this->_cm->id); | |
588 | if (!has_capability('moodle/course:activityvisibility', $context)) { | |
589 | $mform->hardFreeze('visible'); | |
590 | } | |
591 | } | |
592 | ||
593 | if ($this->_features->idnumber) { | |
594 | $mform->addElement('text', 'cmidnumber', get_string('idnumbermod')); | |
8af0dffb | 595 | $mform->setType('cmidnumber', PARAM_RAW); |
207c737d FM |
596 | $mform->addHelpButton('cmidnumber', 'idnumbermod'); |
597 | } | |
598 | ||
4b86bb08 | 599 | if ($this->_features->groups) { |
a78890d5 | 600 | $options = array(NOGROUPS => get_string('groupsnone'), |
601 | SEPARATEGROUPS => get_string('groupsseparate'), | |
602 | VISIBLEGROUPS => get_string('groupsvisible')); | |
ddd9f926 DM |
603 | $mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $options, NOGROUPS); |
604 | $mform->addHelpButton('groupmode', 'groupmode', 'group'); | |
e24b7f85 | 605 | } |
24f41672 | 606 | |
4717a5fc | 607 | if ($this->_features->groupings) { |
608 | // Groupings selector - used to select grouping for groups in activity. | |
98da6021 | 609 | $options = array(); |
98da6021 PS |
610 | if ($groupings = $DB->get_records('groupings', array('courseid'=>$COURSE->id))) { |
611 | foreach ($groupings as $grouping) { | |
612 | $options[$grouping->id] = format_string($grouping->name); | |
24f41672 | 613 | } |
614 | } | |
3ba63534 MG |
615 | core_collator::asort($options); |
616 | $options = array(0 => get_string('none')) + $options; | |
98da6021 | 617 | $mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options); |
ddd9f926 | 618 | $mform->addHelpButton('groupingid', 'grouping', 'group'); |
98da6021 | 619 | } |
e04ff2d5 | 620 | |
82bd6a5e | 621 | if (!empty($CFG->enableavailability)) { |
a4c31832 | 622 | // Add special button to end of previous section if groups/groupings |
623 | // are enabled. | |
624 | if ($this->_features->groups || $this->_features->groupings) { | |
625 | $mform->addElement('static', 'restrictgroupbutton', '', | |
626 | html_writer::tag('button', get_string('restrictbygroup', 'availability'), | |
29df1cad | 627 | array('id' => 'restrictbygroup', 'disabled' => 'disabled', 'class' => 'btn btn-secondary'))); |
a4c31832 | 628 | } |
629 | ||
400c0fd2 | 630 | // Availability field. This is just a textarea; the user interface |
631 | // interaction is all implemented in JavaScript. | |
6282381d | 632 | $mform->addElement('header', 'availabilityconditionsheader', |
400c0fd2 | 633 | get_string('restrictaccess', 'availability')); |
634 | // Note: This field cannot be named 'availability' because that | |
635 | // conflicts with fields in existing modules (such as assign). | |
636 | // So it uses a long name that will not conflict. | |
637 | $mform->addElement('textarea', 'availabilityconditionsjson', | |
638 | get_string('accessrestrictions', 'availability')); | |
639 | // The _cm variable may not be a proper cm_info, so get one from modinfo. | |
640 | if ($this->_cm) { | |
42f103be | 641 | $modinfo = get_fast_modinfo($COURSE); |
400c0fd2 | 642 | $cm = $modinfo->get_cm($this->_cm->id); |
643 | } else { | |
644 | $cm = null; | |
82bd6a5e | 645 | } |
400c0fd2 | 646 | \core_availability\frontend::include_all_javascript($COURSE, $cm); |
82bd6a5e | 647 | } |
648 | ||
aa6c1ced | 649 | // Conditional activities: completion tracking section |
82bd6a5e | 650 | if(!isset($completion)) { |
651 | $completion = new completion_info($COURSE); | |
652 | } | |
61fceb86 | 653 | if ($completion->is_enabled()) { |
3fbe0e8a | 654 | $mform->addElement('header', 'activitycompletionheader', get_string('activitycompletion', 'completion')); |
4e781c7b | 655 | // Unlock button for if people have completed it (will |
656 | // be removed in definition_after_data if they haven't) | |
61fceb86 | 657 | $mform->addElement('submit', 'unlockcompletion', get_string('unlockcompletion', 'completion')); |
4e781c7b | 658 | $mform->registerNoSubmitButton('unlockcompletion'); |
61fceb86 | 659 | $mform->addElement('hidden', 'completionunlocked', 0); |
d18e0fe6 | 660 | $mform->setType('completionunlocked', PARAM_INT); |
82bd6a5e | 661 | |
4ed60499 EM |
662 | $trackingdefault = COMPLETION_TRACKING_NONE; |
663 | // If system and activity default is on, set it. | |
664 | if ($CFG->completiondefault && $this->_features->defaultcompletion) { | |
a0a6762e JP |
665 | $hasrules = plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_HAS_RULES, true); |
666 | $tracksviews = plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, true); | |
667 | if ($hasrules || $tracksviews) { | |
668 | $trackingdefault = COMPLETION_TRACKING_AUTOMATIC; | |
669 | } else { | |
670 | $trackingdefault = COMPLETION_TRACKING_MANUAL; | |
671 | } | |
4ed60499 EM |
672 | } |
673 | ||
aa6c1ced PS |
674 | $mform->addElement('select', 'completion', get_string('completion', 'completion'), |
675 | array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'), | |
61fceb86 | 676 | COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion'))); |
4ed60499 | 677 | $mform->setDefault('completion', $trackingdefault); |
c8dcb793 | 678 | $mform->addHelpButton('completion', 'completion', 'completion'); |
4e781c7b | 679 | |
680 | // Automatic completion once you view it | |
61fceb86 | 681 | $gotcompletionoptions = false; |
9593d8b8 | 682 | if (plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) { |
61fceb86 | 683 | $mform->addElement('checkbox', 'completionview', get_string('completionview', 'completion'), |
ddd9f926 | 684 | get_string('completionview_desc', 'completion')); |
87c7fb6e | 685 | $mform->hideIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC); |
a0a6762e JP |
686 | // Check by default if automatic completion tracking is set. |
687 | if ($trackingdefault == COMPLETION_TRACKING_AUTOMATIC) { | |
688 | $mform->setDefault('completionview', 1); | |
689 | } | |
61fceb86 | 690 | $gotcompletionoptions = true; |
4e781c7b | 691 | } |
692 | ||
9593d8b8 | 693 | if (plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false)) { |
fe795b59 | 694 | // This activity supports grading. |
61fceb86 | 695 | $gotcompletionoptions = true; |
fe795b59 | 696 | |
a492f69c RW |
697 | $component = "mod_{$this->_modname}"; |
698 | $itemnames = component_gradeitems::get_itemname_mapping_for_component($component); | |
699 | ||
700 | if (count($itemnames) === 1) { | |
fe795b59 AN |
701 | // Only one gradeitem in this activity. |
702 | // We use the completionusegrade field here. | |
703 | $mform->addElement( | |
704 | 'checkbox', | |
705 | 'completionusegrade', | |
706 | get_string('completionusegrade', 'completion'), | |
707 | get_string('completionusegrade_desc', 'completion') | |
708 | ); | |
709 | $mform->hideIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC); | |
710 | $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion'); | |
711 | ||
712 | // The disabledIf logic differs between ratings and other grade items due to different field types. | |
a492f69c RW |
713 | if ($this->_features->rating) { |
714 | // If using the rating system, there is no grade unless ratings are enabled. | |
715 | $mform->disabledIf('completionusegrade', 'assessed', 'eq', 0); | |
716 | } else { | |
fe795b59 | 717 | // All other field types use the '$gradefieldname' field's modgrade_type. |
a492f69c RW |
718 | $itemnumbers = array_keys($itemnames); |
719 | $itemnumber = array_shift($itemnumbers); | |
720 | $gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade'); | |
721 | $mform->disabledIf('completionusegrade', "{$gradefieldname}[modgrade_type]", 'eq', 'none'); | |
722 | } | |
fe795b59 AN |
723 | } else if (count($itemnames) > 1) { |
724 | // There are multiple grade items in this activity. | |
725 | // Show them all. | |
726 | $options = [ | |
727 | '' => get_string('activitygradenotrequired', 'completion'), | |
728 | ]; | |
729 | foreach ($itemnames as $itemnumber => $itemname) { | |
730 | $options[$itemnumber] = get_string("grade_{$itemname}_name", $component); | |
731 | } | |
732 | ||
733 | $mform->addElement( | |
734 | 'select', | |
735 | 'completiongradeitemnumber', | |
736 | get_string('completionusegrade', 'completion'), | |
737 | $options | |
738 | ); | |
739 | $mform->hideIf('completiongradeitemnumber', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC); | |
97c3c2c2 | 740 | } |
4e781c7b | 741 | } |
742 | ||
743 | // Automatic completion according to module-specific rules | |
744 | $this->_customcompletionelements = $this->add_completion_rules(); | |
61fceb86 | 745 | foreach ($this->_customcompletionelements as $element) { |
87c7fb6e | 746 | $mform->hideIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC); |
4e781c7b | 747 | } |
748 | ||
749 | $gotcompletionoptions = $gotcompletionoptions || | |
750 | count($this->_customcompletionelements)>0; | |
751 | ||
752 | // Automatic option only appears if possible | |
61fceb86 | 753 | if ($gotcompletionoptions) { |
4e781c7b | 754 | $mform->getElement('completion')->addOption( |
61fceb86 | 755 | get_string('completion_automatic', 'completion'), |
4e781c7b | 756 | COMPLETION_TRACKING_AUTOMATIC); |
aa6c1ced | 757 | } |
4e781c7b | 758 | |
759 | // Completion expected at particular date? (For progress tracking) | |
dccd9540 SB |
760 | $mform->addElement('date_time_selector', 'completionexpected', get_string('completionexpected', 'completion'), |
761 | array('optional' => true)); | |
ddd9f926 | 762 | $mform->addHelpButton('completionexpected', 'completionexpected', 'completion'); |
87c7fb6e | 763 | $mform->hideIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE); |
4e781c7b | 764 | } |
765 | ||
dffcf46f | 766 | // Populate module tags. |
dffcf46f NK |
767 | if (core_tag_tag::is_enabled('core', 'course_modules')) { |
768 | $mform->addElement('header', 'tagshdr', get_string('tags', 'tag')); | |
769 | $mform->addElement('tags', 'tags', get_string('tags'), array('itemtype' => 'course_modules', 'component' => 'core')); | |
770 | if ($this->_cm) { | |
1ea2f7d6 | 771 | $tags = core_tag_tag::get_item_tags_array('core', 'course_modules', $this->_cm->id); |
dffcf46f NK |
772 | $mform->setDefault('tags', $tags); |
773 | } | |
774 | } | |
775 | ||
e24b7f85 | 776 | $this->standard_hidden_coursemodule_elements(); |
8995c270 DW |
777 | |
778 | $this->plugin_extend_coursemodule_standard_elements(); | |
779 | } | |
780 | ||
fcc88fdd AN |
781 | /** |
782 | * Add rating settings. | |
783 | * | |
784 | * @param moodleform_mod $mform | |
785 | * @param int $itemnumber | |
786 | */ | |
787 | protected function add_rating_settings($mform, int $itemnumber) { | |
788 | global $CFG, $COURSE; | |
789 | ||
790 | if ($this->gradedorrated && $this->gradedorrated !== 'rated') { | |
791 | return; | |
792 | } | |
793 | $this->gradedorrated = 'rated'; | |
794 | ||
795 | require_once("{$CFG->dirroot}/rating/lib.php"); | |
796 | $rm = new rating_manager(); | |
797 | ||
798 | $component = "mod_{$this->_modname}"; | |
799 | $gradecatfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradecat'); | |
800 | $gradepassfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradepass'); | |
801 | $assessedfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'assessed'); | |
802 | $scalefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'scale'); | |
803 | ||
804 | $mform->addElement('header', 'modstandardratings', get_string('ratings', 'rating')); | |
805 | ||
806 | $isupdate = !empty($this->_cm); | |
807 | ||
808 | $rolenamestring = null; | |
809 | if ($isupdate) { | |
810 | $context = context_module::instance($this->_cm->id); | |
811 | $capabilities = ['moodle/rating:rate', "mod/{$this->_cm->modname}:rate"]; | |
812 | $rolenames = get_role_names_with_caps_in_context($context, $capabilities); | |
813 | $rolenamestring = implode(', ', $rolenames); | |
814 | } else { | |
815 | $rolenamestring = get_string('capabilitychecknotavailable', 'rating'); | |
816 | } | |
817 | ||
818 | $mform->addElement('static', 'rolewarning', get_string('rolewarning', 'rating'), $rolenamestring); | |
819 | $mform->addHelpButton('rolewarning', 'rolewarning', 'rating'); | |
820 | ||
821 | $mform->addElement('select', $assessedfieldname, get_string('aggregatetype', 'rating') , $rm->get_aggregate_types()); | |
822 | $mform->setDefault($assessedfieldname, 0); | |
823 | $mform->addHelpButton($assessedfieldname, 'aggregatetype', 'rating'); | |
824 | ||
825 | $gradeoptions = [ | |
826 | 'isupdate' => $isupdate, | |
827 | 'currentgrade' => false, | |
828 | 'hasgrades' => false, | |
829 | 'canrescale' => false, | |
830 | 'useratings' => true, | |
831 | ]; | |
832 | if ($isupdate) { | |
833 | $gradeitem = grade_item::fetch([ | |
834 | 'itemtype' => 'mod', | |
835 | 'itemmodule' => $this->_cm->modname, | |
836 | 'iteminstance' => $this->_cm->instance, | |
837 | 'itemnumber' => $itemnumber, | |
838 | 'courseid' => $COURSE->id, | |
839 | ]); | |
840 | if ($gradeitem) { | |
841 | $gradeoptions['currentgrade'] = $gradeitem->grademax; | |
842 | $gradeoptions['currentgradetype'] = $gradeitem->gradetype; | |
843 | $gradeoptions['currentscaleid'] = $gradeitem->scaleid; | |
844 | $gradeoptions['hasgrades'] = $gradeitem->has_grades(); | |
845 | } | |
846 | } | |
847 | ||
848 | $mform->addElement('modgrade', $scalefieldname, get_string('scale'), $gradeoptions); | |
849 | $mform->hideIf($scalefieldname, $assessedfieldname, 'eq', 0); | |
850 | $mform->addHelpButton($scalefieldname, 'modgrade', 'grades'); | |
851 | $mform->setDefault($scalefieldname, $CFG->gradepointdefault); | |
852 | ||
853 | $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'rating')); | |
854 | $mform->hideIf('ratingtime', $assessedfieldname, 'eq', 0); | |
855 | ||
856 | $mform->addElement('date_time_selector', 'assesstimestart', get_string('from')); | |
857 | $mform->hideIf('assesstimestart', $assessedfieldname, 'eq', 0); | |
858 | $mform->hideIf('assesstimestart', 'ratingtime'); | |
859 | ||
860 | $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); | |
861 | $mform->hideIf('assesstimefinish', $assessedfieldname, 'eq', 0); | |
862 | $mform->hideIf('assesstimefinish', 'ratingtime'); | |
863 | ||
864 | if ($this->_features->gradecat) { | |
865 | $mform->addElement( | |
866 | 'select', | |
867 | $gradecatfieldname, | |
868 | get_string('gradecategoryonmodform', 'grades'), | |
869 | grade_get_categories_menu($COURSE->id, $this->_outcomesused) | |
870 | ); | |
871 | $mform->addHelpButton($gradecatfieldname, 'gradecategoryonmodform', 'grades'); | |
872 | $mform->hideIf($gradecatfieldname, $assessedfieldname, 'eq', 0); | |
873 | $mform->hideIf($gradecatfieldname, "{$scalefieldname}[modgrade_type]", 'eq', 'none'); | |
874 | } | |
875 | ||
876 | // Grade to pass. | |
877 | $mform->addElement('text', $gradepassfieldname, get_string('gradepass', 'grades')); | |
878 | $mform->addHelpButton($gradepassfieldname, 'gradepass', 'grades'); | |
879 | $mform->setDefault($gradepassfieldname, ''); | |
880 | $mform->setType($gradepassfieldname, PARAM_RAW); | |
881 | $mform->hideIf($gradepassfieldname, $assessedfieldname, 'eq', '0'); | |
882 | $mform->hideIf($gradepassfieldname, "{$scalefieldname}[modgrade_type]", 'eq', 'none'); | |
883 | } | |
884 | ||
8995c270 DW |
885 | /** |
886 | * Plugins can extend the coursemodule settings form. | |
887 | */ | |
91e54642 | 888 | protected function plugin_extend_coursemodule_standard_elements() { |
8995c270 DW |
889 | $callbacks = get_plugins_with_function('coursemodule_standard_elements', 'lib.php'); |
890 | foreach ($callbacks as $type => $plugins) { | |
891 | foreach ($plugins as $plugin => $pluginfunction) { | |
892 | // We have exposed all the important properties with public getters - and the callback can manipulate the mform | |
893 | // directly. | |
894 | $pluginfunction($this, $this->_form); | |
895 | } | |
896 | } | |
e24b7f85 | 897 | } |
aa6c1ced | 898 | |
4e781c7b | 899 | /** |
900 | * Can be overridden to add custom completion rules if the module wishes | |
901 | * them. If overriding this, you should also override completion_rule_enabled. | |
902 | * <p> | |
903 | * Just add elements to the form as needed and return the list of IDs. The | |
904 | * system will call disabledIf and handle other behaviour for each returned | |
905 | * ID. | |
906 | * @return array Array of string IDs of added items, empty array if none | |
907 | */ | |
908 | function add_completion_rules() { | |
909 | return array(); | |
910 | } | |
911 | ||
912 | /** | |
913 | * Called during validation. Override to indicate, based on the data, whether | |
914 | * a custom completion rule is enabled (selected). | |
915 | * | |
916 | * @param array $data Input data (not yet validated) | |
917 | * @return bool True if one or more rules is enabled, false if none are; | |
918 | * default returns false | |
919 | */ | |
54352ac9 | 920 | function completion_rule_enabled($data) { |
4e781c7b | 921 | return false; |
922 | } | |
e24b7f85 | 923 | |
924 | function standard_hidden_coursemodule_elements(){ | |
925 | $mform =& $this->_form; | |
926 | $mform->addElement('hidden', 'course', 0); | |
927 | $mform->setType('course', PARAM_INT); | |
928 | ||
929 | $mform->addElement('hidden', 'coursemodule', 0); | |
930 | $mform->setType('coursemodule', PARAM_INT); | |
931 | ||
932 | $mform->addElement('hidden', 'section', 0); | |
933 | $mform->setType('section', PARAM_INT); | |
934 | ||
935 | $mform->addElement('hidden', 'module', 0); | |
936 | $mform->setType('module', PARAM_INT); | |
937 | ||
938 | $mform->addElement('hidden', 'modulename', ''); | |
aff24313 | 939 | $mform->setType('modulename', PARAM_PLUGIN); |
e24b7f85 | 940 | |
941 | $mform->addElement('hidden', 'instance', 0); | |
942 | $mform->setType('instance', PARAM_INT); | |
943 | ||
944 | $mform->addElement('hidden', 'add', 0); | |
945 | $mform->setType('add', PARAM_ALPHA); | |
946 | ||
947 | $mform->addElement('hidden', 'update', 0); | |
948 | $mform->setType('update', PARAM_INT); | |
19110c57 | 949 | |
950 | $mform->addElement('hidden', 'return', 0); | |
951 | $mform->setType('return', PARAM_BOOL); | |
a41b1d96 FM |
952 | |
953 | $mform->addElement('hidden', 'sr', 0); | |
954 | $mform->setType('sr', PARAM_INT); | |
e24b7f85 | 955 | } |
956 | ||
60d5a2e4 | 957 | public function standard_grading_coursemodule_elements() { |
16306628 | 958 | global $COURSE, $CFG; |
fcc88fdd AN |
959 | |
960 | if ($this->gradedorrated && $this->gradedorrated !== 'graded') { | |
961 | return; | |
962 | } | |
963 | if ($this->_features->rating) { | |
964 | return; | |
965 | } | |
966 | $this->gradedorrated = 'graded'; | |
967 | ||
968 | $itemnumber = 0; | |
969 | $component = "mod_{$this->_modname}"; | |
970 | $gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade'); | |
971 | $gradecatfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradecat'); | |
972 | $gradepassfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradepass'); | |
973 | ||
1d50dd13 | 974 | $mform =& $this->_form; |
d629c601 DW |
975 | $isupdate = !empty($this->_cm); |
976 | $gradeoptions = array('isupdate' => $isupdate, | |
977 | 'currentgrade' => false, | |
978 | 'hasgrades' => false, | |
664d8be7 MN |
979 | 'canrescale' => $this->_features->canrescale, |
980 | 'useratings' => $this->_features->rating); | |
1d50dd13 | 981 | |
16306628 | 982 | if ($this->_features->hasgrades) { |
fcc88fdd | 983 | if ($this->_features->gradecat) { |
16306628 AD |
984 | $mform->addElement('header', 'modstandardgrade', get_string('grade')); |
985 | } | |
986 | ||
987 | //if supports grades and grades arent being handled via ratings | |
fcc88fdd AN |
988 | if ($isupdate) { |
989 | $gradeitem = grade_item::fetch(array('itemtype' => 'mod', | |
990 | 'itemmodule' => $this->_cm->modname, | |
991 | 'iteminstance' => $this->_cm->instance, | |
992 | 'itemnumber' => 0, | |
993 | 'courseid' => $COURSE->id)); | |
994 | if ($gradeitem) { | |
995 | $gradeoptions['currentgrade'] = $gradeitem->grademax; | |
996 | $gradeoptions['currentgradetype'] = $gradeitem->gradetype; | |
997 | $gradeoptions['currentscaleid'] = $gradeitem->scaleid; | |
998 | $gradeoptions['hasgrades'] = $gradeitem->has_grades(); | |
d629c601 | 999 | } |
16306628 | 1000 | } |
fcc88fdd AN |
1001 | $mform->addElement('modgrade', $gradefieldname, get_string('grade'), $gradeoptions); |
1002 | $mform->addHelpButton($gradefieldname, 'modgrade', 'grades'); | |
1003 | $mform->setDefault($gradefieldname, $CFG->gradepointdefault); | |
1d50dd13 | 1004 | |
b11f9da6 DM |
1005 | if ($this->_features->advancedgrading |
1006 | and !empty($this->current->_advancedgradingdata['methods']) | |
1007 | and !empty($this->current->_advancedgradingdata['areas'])) { | |
1008 | ||
1009 | if (count($this->current->_advancedgradingdata['areas']) == 1) { | |
1010 | // if there is just one gradable area (most cases), display just the selector | |
1011 | // without its name to make UI simplier | |
1012 | $areadata = reset($this->current->_advancedgradingdata['areas']); | |
1013 | $areaname = key($this->current->_advancedgradingdata['areas']); | |
1014 | $mform->addElement('select', 'advancedgradingmethod_'.$areaname, | |
1015 | get_string('gradingmethod', 'core_grading'), $this->current->_advancedgradingdata['methods']); | |
6832a102 | 1016 | $mform->addHelpButton('advancedgradingmethod_'.$areaname, 'gradingmethod', 'core_grading'); |
fcc88fdd | 1017 | $mform->hideIf('advancedgradingmethod_'.$areaname, "{$gradefieldname}[modgrade_type]", 'eq', 'none'); |
b11f9da6 DM |
1018 | |
1019 | } else { | |
1020 | // the module defines multiple gradable areas, display a selector | |
1021 | // for each of them together with a name of the area | |
1022 | $areasgroup = array(); | |
1023 | foreach ($this->current->_advancedgradingdata['areas'] as $areaname => $areadata) { | |
1024 | $areasgroup[] = $mform->createElement('select', 'advancedgradingmethod_'.$areaname, | |
1025 | $areadata['title'], $this->current->_advancedgradingdata['methods']); | |
1026 | $areasgroup[] = $mform->createElement('static', 'advancedgradingareaname_'.$areaname, '', $areadata['title']); | |
1027 | } | |
1028 | $mform->addGroup($areasgroup, 'advancedgradingmethodsgroup', get_string('gradingmethods', 'core_grading'), | |
1029 | array(' ', '<br />'), false); | |
1030 | } | |
1031 | } | |
1032 | ||
1d50dd13 | 1033 | if ($this->_features->gradecat) { |
fcc88fdd | 1034 | $mform->addElement('select', $gradecatfieldname, |
71c4154a TH |
1035 | get_string('gradecategoryonmodform', 'grades'), |
1036 | grade_get_categories_menu($COURSE->id, $this->_outcomesused)); | |
fcc88fdd AN |
1037 | $mform->addHelpButton($gradecatfieldname, 'gradecategoryonmodform', 'grades'); |
1038 | $mform->hideIf($gradecatfieldname, "{$gradefieldname}[modgrade_type]", 'eq', 'none'); | |
1d50dd13 | 1039 | } |
c977b350 MG |
1040 | |
1041 | // Grade to pass. | |
fcc88fdd AN |
1042 | $mform->addElement('text', $gradepassfieldname, get_string($gradepassfieldname, 'grades')); |
1043 | $mform->addHelpButton($gradepassfieldname, $gradepassfieldname, 'grades'); | |
1044 | $mform->setDefault($gradepassfieldname, ''); | |
1045 | $mform->setType($gradepassfieldname, PARAM_RAW); | |
1046 | $mform->hideIf($gradepassfieldname, "{$gradefieldname}[modgrade_type]", 'eq', 'none'); | |
1d50dd13 AD |
1047 | } |
1048 | } | |
1049 | ||
6398ff53 AH |
1050 | /** |
1051 | * Add an editor for an activity's introduction field. | |
1052 | * @deprecated since MDL-49101 - use moodleform_mod::standard_intro_elements() instead. | |
1053 | * @param null $required Override system default for requiremodintro | |
1054 | * @param null $customlabel Override default label for editor | |
1055 | * @throws coding_exception | |
1056 | */ | |
1057 | protected function add_intro_editor($required=null, $customlabel=null) { | |
1058 | $str = "Function moodleform_mod::add_intro_editor() is deprecated, use moodleform_mod::standard_intro_elements() instead."; | |
1059 | debugging($str, DEBUG_DEVELOPER); | |
1060 | ||
1061 | $this->standard_intro_elements($customlabel); | |
1062 | } | |
1063 | ||
1064 | ||
1065 | /** | |
1066 | * Add an editor for an activity's introduction field. | |
1067 | * | |
1068 | * @param null $customlabel Override default label for editor | |
1069 | * @throws coding_exception | |
1070 | */ | |
bc577c54 | 1071 | protected function standard_intro_elements($customlabel=null) { |
6398ff53 AH |
1072 | global $CFG; |
1073 | ||
1074 | $required = $CFG->requiremodintro; | |
dc5c2bd9 | 1075 | |
1076 | $mform = $this->_form; | |
1077 | $label = is_null($customlabel) ? get_string('moduleintro') : $customlabel; | |
1078 | ||
bc577c54 | 1079 | $mform->addElement('editor', 'introeditor', $label, array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES, |
f9beaf44 | 1080 | 'noclean' => true, 'context' => $this->context, 'subdirs' => true)); |
dc5c2bd9 | 1081 | $mform->setType('introeditor', PARAM_RAW); // no XSS prevention here, users must be trusted |
1082 | if ($required) { | |
1083 | $mform->addRule('introeditor', get_string('required'), 'required', null, 'client'); | |
1084 | } | |
8c40662e | 1085 | |
d9203fb7 FM |
1086 | // If the 'show description' feature is enabled, this checkbox appears below the intro. |
1087 | // We want to hide that when using the singleactivity course format because it is confusing. | |
1088 | if ($this->_features->showdescription && $this->courseformat->has_view_page()) { | |
7f53e8aa | 1089 | $mform->addElement('advcheckbox', 'showdescription', get_string('showdescription')); |
8c40662e | 1090 | $mform->addHelpButton('showdescription', 'showdescription'); |
1091 | } | |
dc5c2bd9 | 1092 | } |
1093 | ||
6b467109 | 1094 | /** |
1095 | * Overriding formslib's add_action_buttons() method, to add an extra submit "save changes and return" button. | |
b5ab55dd | 1096 | * |
1097 | * @param bool $cancel show cancel button | |
1098 | * @param string $submitlabel null means default, false means none, string is label text | |
1099 | * @param string $submit2label null means default, false means none, string is label text | |
6b467109 | 1100 | * @return void |
b5ab55dd | 1101 | */ |
6b467109 | 1102 | function add_action_buttons($cancel=true, $submitlabel=null, $submit2label=null) { |
1103 | if (is_null($submitlabel)) { | |
1104 | $submitlabel = get_string('savechangesanddisplay'); | |
1105 | } | |
b5ab55dd | 1106 | |
6b467109 | 1107 | if (is_null($submit2label)) { |
1108 | $submit2label = get_string('savechangesandreturntocourse'); | |
1109 | } | |
b5ab55dd | 1110 | |
dc5c2bd9 | 1111 | $mform = $this->_form; |
b5ab55dd | 1112 | |
1113 | // elements in a row need a group | |
1114 | $buttonarray = array(); | |
1115 | ||
d9203fb7 FM |
1116 | // Label for the submit button to return to the course. |
1117 | // Ignore this button in single activity format because it is confusing. | |
1118 | if ($submit2label !== false && $this->courseformat->has_view_page()) { | |
b5ab55dd | 1119 | $buttonarray[] = &$mform->createElement('submit', 'submitbutton2', $submit2label); |
1120 | } | |
1121 | ||
1122 | if ($submitlabel !== false) { | |
1123 | $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel); | |
1124 | } | |
1125 | ||
6b467109 | 1126 | if ($cancel) { |
1127 | $buttonarray[] = &$mform->createElement('cancel'); | |
1128 | } | |
1129 | ||
1130 | $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); | |
de6f158f | 1131 | $mform->setType('buttonar', PARAM_RAW); |
6b467109 | 1132 | $mform->closeHeaderBefore('buttonar'); |
1133 | } | |
3c88a678 DW |
1134 | |
1135 | /** | |
fa358a83 DW |
1136 | * Get the list of admin settings for this module and apply any locked settings. |
1137 | * This cannot happen in apply_admin_defaults because we do not the current values of the settings | |
1138 | * in that function because set_data has not been called yet. | |
3c88a678 | 1139 | * |
3c88a678 DW |
1140 | * @return void |
1141 | */ | |
c3037116 | 1142 | protected function apply_admin_locked_flags() { |
3c88a678 DW |
1143 | global $OUTPUT; |
1144 | ||
c3037116 | 1145 | if (!$this->applyadminlockedflags) { |
fa358a83 DW |
1146 | return; |
1147 | } | |
fa358a83 | 1148 | |
3c88a678 DW |
1149 | $settings = get_config($this->_modname); |
1150 | $mform = $this->_form; | |
1151 | $lockedicon = html_writer::tag('span', | |
1152 | $OUTPUT->pix_icon('t/locked', get_string('locked', 'admin')), | |
1153 | array('class' => 'action-icon')); | |
54106942 | 1154 | $isupdate = !empty($this->_cm); |
3c88a678 DW |
1155 | |
1156 | foreach ($settings as $name => $value) { | |
1157 | if (strpos('_', $name) !== false) { | |
1158 | continue; | |
1159 | } | |
1160 | if ($mform->elementExists($name)) { | |
1161 | $element = $mform->getElement($name); | |
3c88a678 DW |
1162 | $lockedsetting = $name . '_locked'; |
1163 | if (!empty($settings->$lockedsetting)) { | |
54106942 DW |
1164 | // Always lock locked settings for new modules, |
1165 | // for updates, only lock them if the current value is the same as the default (or there is no current value). | |
1166 | $value = $settings->$name; | |
1167 | if ($isupdate && isset($this->current->$name)) { | |
1168 | $value = $this->current->$name; | |
1169 | } | |
fa358a83 DW |
1170 | if ($value == $settings->$name) { |
1171 | $mform->setConstant($name, $settings->$name); | |
1172 | $element->setLabel($element->getLabel() . $lockedicon); | |
1173 | // Do not use hardfreeze because we need the hidden input to check dependencies. | |
1174 | $element->freeze(); | |
1175 | } | |
3c88a678 DW |
1176 | } |
1177 | } | |
1178 | } | |
1179 | } | |
fa358a83 DW |
1180 | |
1181 | /** | |
948a951a | 1182 | * Get the list of admin settings for this module and apply any defaults/advanced/locked/required settings. |
fa358a83 DW |
1183 | * |
1184 | * @param $datetimeoffsets array - If passed, this is an array of fieldnames => times that the | |
1185 | * default date/time value should be relative to. If not passed, all | |
1186 | * date/time fields are set relative to the users current midnight. | |
1187 | * @return void | |
1188 | */ | |
1189 | public function apply_admin_defaults($datetimeoffsets = array()) { | |
c3037116 DW |
1190 | // This flag triggers the settings to be locked in apply_admin_locked_flags(). |
1191 | $this->applyadminlockedflags = true; | |
1192 | ||
1193 | $settings = get_config($this->_modname); | |
1194 | $mform = $this->_form; | |
1195 | $usermidnight = usergetmidnight(time()); | |
1196 | $isupdate = !empty($this->_cm); | |
1197 | ||
1198 | foreach ($settings as $name => $value) { | |
1199 | if (strpos('_', $name) !== false) { | |
1200 | continue; | |
1201 | } | |
1202 | if ($mform->elementExists($name)) { | |
1203 | $element = $mform->getElement($name); | |
1204 | if (!$isupdate) { | |
1205 | if ($element->getType() == 'date_time_selector') { | |
1206 | $enabledsetting = $name . '_enabled'; | |
1207 | if (empty($settings->$enabledsetting)) { | |
1208 | $mform->setDefault($name, 0); | |
1209 | } else { | |
1210 | $relativetime = $usermidnight; | |
1211 | if (isset($datetimeoffsets[$name])) { | |
1212 | $relativetime = $datetimeoffsets[$name]; | |
1213 | } | |
1214 | $mform->setDefault($name, $relativetime + $settings->$name); | |
1215 | } | |
1216 | } else { | |
1217 | $mform->setDefault($name, $settings->$name); | |
1218 | } | |
1219 | } | |
1220 | $advancedsetting = $name . '_adv'; | |
1221 | if (!empty($settings->$advancedsetting)) { | |
1222 | $mform->setAdvanced($name); | |
1223 | } | |
948a951a AM |
1224 | $requiredsetting = $name . '_required'; |
1225 | if (!empty($settings->$requiredsetting)) { | |
1226 | $mform->addRule($name, null, 'required', null, 'client'); | |
1227 | } | |
c3037116 DW |
1228 | } |
1229 | } | |
fa358a83 | 1230 | } |
06cdda46 MG |
1231 | |
1232 | /** | |
1233 | * Allows modules to modify the data returned by form get_data(). | |
1234 | * This method is also called in the bulk activity completion form. | |
1235 | * | |
1236 | * Only available on moodleform_mod. | |
1237 | * | |
1238 | * @param stdClass $data passed by reference | |
1239 | */ | |
273d3106 | 1240 | public function data_postprocessing($data) { |
06cdda46 MG |
1241 | } |
1242 | ||
1243 | /** | |
1244 | * Return submitted data if properly submitted or returns NULL if validation fails or | |
1245 | * if there is no submitted data. | |
1246 | * | |
1247 | * Do not override this method, override data_postprocessing() instead. | |
1248 | * | |
1249 | * @return object submitted data; NULL if not valid or not submitted or cancelled | |
1250 | */ | |
1251 | public function get_data() { | |
1252 | $data = parent::get_data(); | |
1253 | if ($data) { | |
1254 | // Convert the grade pass value - we may be using a language which uses commas, | |
1255 | // rather than decimal points, in numbers. These need to be converted so that | |
1256 | // they can be added to the DB. | |
1257 | if (isset($data->gradepass)) { | |
1258 | $data->gradepass = unformat_float($data->gradepass); | |
1259 | } | |
1260 | ||
1261 | $this->data_postprocessing($data); | |
1262 | } | |
1263 | return $data; | |
1264 | } | |
e24b7f85 | 1265 | } |