Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 defined('MOODLE_INTERNAL') || die; 18 19 require_once($CFG->libdir.'/formslib.php'); 20 require_once($CFG->dirroot.'/course/modlib.php'); 21 22 /** 23 * Base form for changing completion rules. Used in bulk editing activity completion and editing default activity completion 24 * 25 * @package core_completion 26 * @copyright 2017 Marina Glancy 27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 */ 29 abstract class core_completion_edit_base_form extends moodleform { 30 31 use \core_completion\form\form_trait; 32 33 /** @var moodleform_mod Do not use directly, call $this->get_module_form() */ 34 protected $_moduleform = null; 35 /** @var bool */ 36 protected $hascustomrules = false; 37 /** @var stdClass */ 38 protected $course; 39 40 /** 41 * Returns list of types of selected module types 42 * 43 * @return array modname=>modfullname 44 */ 45 abstract protected function get_module_names(); 46 47 /** 48 * Get the module name. If the form have more than one modules, it will return the first one. 49 * 50 * @return string|null The module name or null if there is no modules associated to this form. 51 */ 52 protected function get_module_name(): ?string { 53 $modnames = $this->get_module_names(); 54 if (empty($modnames)) { 55 return null; 56 } 57 58 $modnamekeys = array_keys($modnames); 59 return reset($modnamekeys); 60 } 61 62 /** 63 * Returns true if all selected modules support tracking view. 64 * 65 * @return bool 66 */ 67 protected function support_views() { 68 foreach ($this->get_module_names() as $modname => $modfullname) { 69 if (!plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) { 70 return false; 71 } 72 } 73 return true; 74 } 75 76 /** 77 * Returns true if all selected modules support grading. 78 * 79 * @return bool 80 */ 81 protected function support_grades() { 82 foreach ($this->get_module_names() as $modname => $modfullname) { 83 if (!plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) { 84 return false; 85 } 86 } 87 return true; 88 } 89 90 /** 91 * Returns an instance of component-specific module form for the first selected module 92 * 93 * @return moodleform_mod|null 94 */ 95 abstract protected function get_module_form(); 96 97 /** 98 * If all selected modules are of the same module type, adds custom completion rules from this module type 99 * 100 * @return array 101 */ 102 protected function add_custom_completion(string $function): array { 103 $modnames = array_keys($this->get_module_names()); 104 105 if (count($modnames) != 1 || !plugin_supports('mod', $modnames[0], FEATURE_COMPLETION_HAS_RULES, false)) { 106 return [false, []]; 107 } 108 109 $component = "mod_{$modnames[0]}"; 110 $itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component); 111 $hascustomrules = count($itemnames) > 1; 112 113 try { 114 // Add completion rules from the module form to this form. 115 $moduleform = $this->get_module_form(); 116 $moduleform->_form = $this->_form; 117 if ($customcompletionelements = $moduleform->{$function}()) { 118 $hascustomrules = true; 119 foreach ($customcompletionelements as $customcompletionelement) { 120 // Instead of checking for the suffix at the end of the element name, we need to check for its presence 121 // because some modules, like SCORM, are adding things at the end. 122 if (!str_contains($customcompletionelement, $this->get_suffix())) { 123 debugging( 124 'Custom completion rule ' . $customcompletionelement . ' of module ' . $modnames[0] . 125 ' has wrong suffix and has been removed from the form. This has to be fixed by the developer', 126 DEBUG_DEVELOPER 127 ); 128 if ($moduleform->_form->elementExists($customcompletionelement)) { 129 $moduleform->_form->removeElement($customcompletionelement); 130 } 131 } 132 } 133 } 134 return [$hascustomrules, $customcompletionelements]; 135 } catch (Exception $e) { 136 debugging('Could not add custom completion rule of module ' . $modnames[0] . 137 ' to this form, this has to be fixed by the developer', DEBUG_DEVELOPER); 138 return [$hascustomrules, $customcompletionelements]; 139 } 140 } 141 142 /** 143 * If all selected modules are of the same module type, adds custom completion rules from this module type 144 * 145 * @return array 146 */ 147 protected function add_completion_rules() { 148 list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completion_rules'); 149 if (!$this->hascustomrules && $hascustomrules) { 150 $this->hascustomrules = true; 151 } 152 153 $component = "mod_{$this->get_module_name()}"; 154 $itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component); 155 if (count($itemnames) > 1) { 156 $customcompletionelements[] = 'completiongradeitemnumber'; 157 } 158 159 return $customcompletionelements; 160 } 161 162 /** 163 * Checks if at least one of the custom completion rules is enabled 164 * 165 * @param array $data Input data (not yet validated) 166 * @return bool True if one or more rules is enabled, false if none are; 167 * default returns false 168 */ 169 protected function completion_rule_enabled($data) { 170 if ($this->hascustomrules) { 171 return $this->get_module_form()->completion_rule_enabled($data); 172 } 173 return false; 174 } 175 176 /** 177 * If all selected modules are of the same module type, adds custom completion rules from this module type 178 * 179 * @return array 180 */ 181 public function add_completiongrade_rules(): array { 182 list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completiongrade_rules'); 183 if (!$this->hascustomrules && $hascustomrules) { 184 $this->hascustomrules = true; 185 } 186 187 return $customcompletionelements; 188 } 189 190 /** 191 * Returns list of modules that have automatic completion rules that are not shown on this form 192 * (because they are not present in at least one other selected module). 193 * 194 * @return array 195 */ 196 protected function get_modules_with_hidden_rules() { 197 $modnames = $this->get_module_names(); 198 if (count($modnames) <= 1) { 199 // No rules definitions conflicts if there is only one module type. 200 return []; 201 } 202 203 $conflicts = []; 204 205 if (!$this->support_views()) { 206 // If we don't display views rule but at least one module supports it - we have conflicts. 207 foreach ($modnames as $modname => $modfullname) { 208 if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) { 209 $conflicts[$modname] = $modfullname; 210 } 211 } 212 } 213 214 if (!$this->support_grades()) { 215 // If we don't display grade rule but at least one module supports it - we have conflicts. 216 foreach ($modnames as $modname => $modfullname) { 217 if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) { 218 $conflicts[$modname] = $modfullname; 219 } 220 } 221 } 222 223 foreach ($modnames as $modname => $modfullname) { 224 // We do not display any custom completion rules, find modules that define them and add to conflicts list. 225 if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_HAS_RULES, false)) { 226 $conflicts[$modname] = $modfullname; 227 } 228 } 229 230 return $conflicts; 231 } 232 233 /** 234 * Form definition 235 */ 236 public function definition() { 237 $mform = $this->_form; 238 239 // Course id. 240 $mform->addElement('hidden', 'id', $this->course->id); 241 $mform->setType('id', PARAM_INT); 242 243 // Add the completion elements to the form. 244 $this->add_completion_elements( 245 $this->get_module_name(), 246 $this->support_views(), 247 $this->support_grades(), 248 false, 249 $this->course->id 250 ); 251 252 if ($conflicts = $this->get_modules_with_hidden_rules()) { 253 $mform->addElement('static', 'qwerty', '', get_string('hiddenrules', 'completion', join(', ', $conflicts))); 254 } 255 256 // Whether to show the cancel button or not in the form. 257 $displaycancel = $this->_customdata['displaycancel'] ?? true; 258 $this->add_action_buttons($displaycancel); 259 } 260 261 /** 262 * Return the course module of the form, if any. 263 * 264 * @return cm_info|null 265 */ 266 protected function get_cm(): ?cm_info { 267 return null; 268 } 269 270 /** 271 * Each module which defines definition_after_data() must call this method. 272 */ 273 public function definition_after_data() { 274 $this->definition_after_data_completion($this->get_cm()); 275 } 276 277 /** 278 * Form validation 279 * 280 * @param array $data array of ("fieldname"=>value) of submitted data 281 * @param array $files array of uploaded files "element_name"=>tmp_file_path 282 * @return array of "element_name"=>"error_description" if there are errors, 283 * or an empty array if everything is OK (true allowed for backwards compatibility too). 284 */ 285 public function validation($data, $files) { 286 $errors = parent::validation($data, $files); 287 288 // Completion: Check completion fields don't have errors. 289 $errors = array_merge($errors, $this->validate_completion($data)); 290 291 return $errors; 292 } 293 294 /** 295 * Returns if this form has custom completion rules. This is only possible if all selected modules have the same 296 * module type and this module type supports custom completion rules 297 * 298 * @return bool 299 */ 300 public function has_custom_completion_rules() { 301 return $this->hascustomrules; 302 } 303 304 /** 305 * Return submitted data if properly submitted or returns NULL if validation fails or 306 * if there is no submitted data. 307 * 308 * @return object submitted data; NULL if not valid or not submitted or cancelled 309 */ 310 public function get_data() { 311 $data = parent::get_data(); 312 if ($data && $this->hascustomrules) { 313 $this->get_module_form()->data_postprocessing($data); 314 } 315 return $data; 316 } 317 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body