Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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 /** 18 * Library of functions specific to course/modedit.php and course API functions. 19 * The course API function calling them are course/lib.php:create_module() and update_module(). 20 * This file has been created has an alternative solution to a full refactor of course/modedit.php 21 * in order to create the course API functions. 22 * 23 * @copyright 2013 Jerome Mouneyrac 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 * @package core_course 26 */ 27 28 defined('MOODLE_INTERNAL') || die; 29 30 use \core_grades\component_gradeitems; 31 32 require_once($CFG->dirroot.'/course/lib.php'); 33 34 /** 35 * Add course module. 36 * 37 * The function does not check user capabilities. 38 * The function creates course module, module instance, add the module to the correct section. 39 * It also trigger common action that need to be done after adding/updating a module. 40 * 41 * @param object $moduleinfo the moudle data 42 * @param object $course the course of the module 43 * @param object $mform this is required by an existing hack to deal with files during MODULENAME_add_instance() 44 * @return object the updated module info 45 */ 46 function add_moduleinfo($moduleinfo, $course, $mform = null) { 47 global $DB, $CFG; 48 49 // Attempt to include module library before we make any changes to DB. 50 include_modulelib($moduleinfo->modulename); 51 52 $moduleinfo->course = $course->id; 53 $moduleinfo = set_moduleinfo_defaults($moduleinfo); 54 55 if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { 56 $moduleinfo->groupmode = 0; // Do not set groupmode. 57 } 58 59 // First add course_module record because we need the context. 60 $newcm = new stdClass(); 61 $newcm->course = $course->id; 62 $newcm->module = $moduleinfo->module; 63 $newcm->instance = 0; // Not known yet, will be updated later (this is similar to restore code). 64 $newcm->visible = $moduleinfo->visible; 65 $newcm->visibleold = $moduleinfo->visible; 66 $newcm->visibleoncoursepage = $moduleinfo->visibleoncoursepage; 67 if (isset($moduleinfo->cmidnumber)) { 68 $newcm->idnumber = $moduleinfo->cmidnumber; 69 } 70 $newcm->groupmode = $moduleinfo->groupmode; 71 $newcm->groupingid = $moduleinfo->groupingid; 72 $completion = new completion_info($course); 73 if ($completion->is_enabled()) { 74 $newcm->completion = $moduleinfo->completion; 75 if ($moduleinfo->completiongradeitemnumber === '') { 76 $newcm->completiongradeitemnumber = null; 77 } else { 78 $newcm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; 79 } 80 $newcm->completionview = $moduleinfo->completionview; 81 $newcm->completionexpected = $moduleinfo->completionexpected; 82 } 83 if(!empty($CFG->enableavailability)) { 84 // This code is used both when submitting the form, which uses a long 85 // name to avoid clashes, and by unit test code which uses the real 86 // name in the table. 87 $newcm->availability = null; 88 if (property_exists($moduleinfo, 'availabilityconditionsjson')) { 89 if ($moduleinfo->availabilityconditionsjson !== '') { 90 $newcm->availability = $moduleinfo->availabilityconditionsjson; 91 } 92 } else if (property_exists($moduleinfo, 'availability')) { 93 $newcm->availability = $moduleinfo->availability; 94 } 95 // If there is any availability data, verify it. 96 if ($newcm->availability) { 97 $tree = new \core_availability\tree(json_decode($newcm->availability)); 98 // Save time and database space by setting null if the only data 99 // is an empty tree. 100 if ($tree->is_empty()) { 101 $newcm->availability = null; 102 } 103 } 104 } 105 if (isset($moduleinfo->showdescription)) { 106 $newcm->showdescription = $moduleinfo->showdescription; 107 } else { 108 $newcm->showdescription = 0; 109 } 110 111 // From this point we make database changes, so start transaction. 112 $transaction = $DB->start_delegated_transaction(); 113 114 if (!$moduleinfo->coursemodule = add_course_module($newcm)) { 115 print_error('cannotaddcoursemodule'); 116 } 117 118 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true) && 119 isset($moduleinfo->introeditor)) { 120 $introeditor = $moduleinfo->introeditor; 121 unset($moduleinfo->introeditor); 122 $moduleinfo->intro = $introeditor['text']; 123 $moduleinfo->introformat = $introeditor['format']; 124 } 125 126 $addinstancefunction = $moduleinfo->modulename."_add_instance"; 127 try { 128 $returnfromfunc = $addinstancefunction($moduleinfo, $mform); 129 } catch (moodle_exception $e) { 130 $returnfromfunc = $e; 131 } 132 if (!$returnfromfunc or !is_number($returnfromfunc)) { 133 // Undo everything we can. This is not necessary for databases which 134 // support transactions, but improves consistency for other databases. 135 context_helper::delete_instance(CONTEXT_MODULE, $moduleinfo->coursemodule); 136 $DB->delete_records('course_modules', array('id'=>$moduleinfo->coursemodule)); 137 138 if ($returnfromfunc instanceof moodle_exception) { 139 throw $returnfromfunc; 140 } else if (!is_number($returnfromfunc)) { 141 print_error('invalidfunction', '', course_get_url($course, $moduleinfo->section)); 142 } else { 143 print_error('cannotaddnewmodule', '', course_get_url($course, $moduleinfo->section), $moduleinfo->modulename); 144 } 145 } 146 147 $moduleinfo->instance = $returnfromfunc; 148 149 $DB->set_field('course_modules', 'instance', $returnfromfunc, array('id'=>$moduleinfo->coursemodule)); 150 151 // Update embedded links and save files. 152 $modcontext = context_module::instance($moduleinfo->coursemodule); 153 if (!empty($introeditor)) { 154 // This will respect a module that has set a value for intro in it's modname_add_instance() function. 155 $introeditor['text'] = $moduleinfo->intro; 156 157 $moduleinfo->intro = file_save_draft_area_files($introeditor['itemid'], $modcontext->id, 158 'mod_'.$moduleinfo->modulename, 'intro', 0, 159 array('subdirs'=>true), $introeditor['text']); 160 $DB->set_field($moduleinfo->modulename, 'intro', $moduleinfo->intro, array('id'=>$moduleinfo->instance)); 161 } 162 163 // Add module tags. 164 if (core_tag_tag::is_enabled('core', 'course_modules') && isset($moduleinfo->tags)) { 165 core_tag_tag::set_item_tags('core', 'course_modules', $moduleinfo->coursemodule, $modcontext, $moduleinfo->tags); 166 } 167 168 // Course_modules and course_sections each contain a reference to each other. 169 // So we have to update one of them twice. 170 $sectionid = course_add_cm_to_section($course, $moduleinfo->coursemodule, $moduleinfo->section); 171 172 // Trigger event based on the action we did. 173 // Api create_from_cm expects modname and id property, and we don't want to modify $moduleinfo since we are returning it. 174 $eventdata = clone $moduleinfo; 175 $eventdata->modname = $eventdata->modulename; 176 $eventdata->id = $eventdata->coursemodule; 177 $event = \core\event\course_module_created::create_from_cm($eventdata, $modcontext); 178 $event->trigger(); 179 180 $moduleinfo = edit_module_post_actions($moduleinfo, $course); 181 $transaction->allow_commit(); 182 183 return $moduleinfo; 184 } 185 186 /** 187 * Hook for plugins to take action when a module is created or updated. 188 * 189 * @param stdClass $moduleinfo the module info 190 * @param stdClass $course the course of the module 191 * 192 * @return stdClass moduleinfo updated by plugins. 193 */ 194 function plugin_extend_coursemodule_edit_post_actions($moduleinfo, $course) { 195 $callbacks = get_plugins_with_function('coursemodule_edit_post_actions', 'lib.php'); 196 foreach ($callbacks as $type => $plugins) { 197 foreach ($plugins as $plugin => $pluginfunction) { 198 $moduleinfo = $pluginfunction($moduleinfo, $course); 199 } 200 } 201 return $moduleinfo; 202 } 203 204 /** 205 * Common create/update module module actions that need to be processed as soon as a module is created/updaded. 206 * For example:create grade parent category, add outcomes, rebuild caches, regrade, save plagiarism settings... 207 * Please note this api does not trigger events as of MOODLE 2.6. Please trigger events before calling this api. 208 * 209 * @param object $moduleinfo the module info 210 * @param object $course the course of the module 211 * 212 * @return object moduleinfo update with grading management info 213 */ 214 function edit_module_post_actions($moduleinfo, $course) { 215 global $CFG; 216 require_once($CFG->libdir.'/gradelib.php'); 217 218 $modcontext = context_module::instance($moduleinfo->coursemodule); 219 $hasgrades = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_HAS_GRADE, false); 220 $hasoutcomes = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_OUTCOMES, true); 221 222 $items = grade_item::fetch_all([ 223 'itemtype' => 'mod', 224 'itemmodule' => $moduleinfo->modulename, 225 'iteminstance' => $moduleinfo->instance, 226 'courseid' => $course->id, 227 ]); 228 229 // Create parent category if requested and move to correct parent category. 230 $component = "mod_{$moduleinfo->modulename}"; 231 if ($items) { 232 foreach ($items as $item) { 233 $update = false; 234 235 // Sync idnumber with grade_item. 236 // Note: This only happens for itemnumber 0 at this time. 237 if ($item->itemnumber == 0 && ($item->idnumber != $moduleinfo->cmidnumber)) { 238 $item->idnumber = $moduleinfo->cmidnumber; 239 $update = true; 240 } 241 242 // Determine the grade category. 243 $gradecatfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $item->itemnumber, 'gradecat'); 244 if (property_exists($moduleinfo, $gradecatfieldname)) { 245 $gradecat = $moduleinfo->$gradecatfieldname; 246 if ($gradecat == -1) { 247 $gradecategory = new grade_category(); 248 $gradecategory->courseid = $course->id; 249 $gradecategory->fullname = $moduleinfo->name; 250 $gradecategory->insert(); 251 252 $parent = $item->get_parent_category(); 253 $gradecategory->set_parent($parent->id); 254 $gradecat = $gradecategory->id; 255 } 256 257 $oldgradecat = null; 258 if ($parent = $item->get_parent_category()) { 259 $oldgradecat = $parent->id; 260 } 261 if ($oldgradecat != $gradecat) { 262 $item->set_parent($gradecat); 263 $update = true; 264 } 265 } 266 267 // Determine the gradepass. 268 $gradepassfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $item->itemnumber, 'gradepass'); 269 if (isset($moduleinfo->{$gradepassfieldname})) { 270 $gradepass = $moduleinfo->{$gradepassfieldname}; 271 if (null !== $gradepass && $gradepass != $item->gradepass) { 272 $item->gradepass = $gradepass; 273 $update = true; 274 } 275 } 276 277 if ($update) { 278 $item->update(); 279 } 280 281 if (!empty($moduleinfo->add)) { 282 $gradecategory = $item->get_parent_category(); 283 if ($item->set_aggregation_fields_for_aggregation(0, $gradecategory->aggregation)) { 284 $item->update(); 285 } 286 } 287 } 288 } 289 290 require_once($CFG->libdir.'/grade/grade_outcome.php'); 291 // Add outcomes if requested. 292 if ($hasoutcomes && $outcomes = grade_outcome::fetch_all_available($course->id)) { 293 // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes. 294 $max_itemnumber = 999; 295 if ($items) { 296 foreach($items as $item) { 297 if ($item->itemnumber > $max_itemnumber) { 298 $max_itemnumber = $item->itemnumber; 299 } 300 } 301 } 302 303 foreach($outcomes as $outcome) { 304 $elname = 'outcome_'.$outcome->id; 305 306 if (property_exists($moduleinfo, $elname) and $moduleinfo->$elname) { 307 // Check if this is a new outcome grade item. 308 $outcomeexists = false; 309 if ($items) { 310 foreach($items as $item) { 311 if ($item->outcomeid == $outcome->id) { 312 $outcomeexists = true; 313 break; 314 } 315 } 316 if ($outcomeexists) { 317 continue; 318 } 319 } 320 321 $max_itemnumber++; 322 323 $outcomeitem = new grade_item(); 324 $outcomeitem->courseid = $course->id; 325 $outcomeitem->itemtype = 'mod'; 326 $outcomeitem->itemmodule = $moduleinfo->modulename; 327 $outcomeitem->iteminstance = $moduleinfo->instance; 328 $outcomeitem->itemnumber = $max_itemnumber; 329 $outcomeitem->itemname = $outcome->fullname; 330 $outcomeitem->outcomeid = $outcome->id; 331 $outcomeitem->gradetype = GRADE_TYPE_SCALE; 332 $outcomeitem->scaleid = $outcome->scaleid; 333 $outcomeitem->insert(); 334 335 if ($items) { 336 // Move the new outcome into the same category and immediately after the first grade item. 337 $item = reset($items); 338 $outcomeitem->set_parent($item->categoryid); 339 $outcomeitem->move_after_sortorder($item->sortorder); 340 } else if (isset($moduleinfo->gradecat)) { 341 $outcomeitem->set_parent($moduleinfo->gradecat); 342 } 343 344 if (!$outcomeexists) { 345 $gradecategory = $outcomeitem->get_parent_category(); 346 if ($outcomeitem->set_aggregation_fields_for_aggregation(0, $gradecategory->aggregation)) { 347 $outcomeitem->update(); 348 } 349 } 350 } 351 } 352 } 353 354 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_ADVANCED_GRADING, false) 355 and has_capability('moodle/grade:managegradingforms', $modcontext)) { 356 require_once($CFG->dirroot.'/grade/grading/lib.php'); 357 $gradingman = get_grading_manager($modcontext, 'mod_'.$moduleinfo->modulename); 358 $showgradingmanagement = false; 359 foreach ($gradingman->get_available_areas() as $areaname => $aretitle) { 360 $formfield = 'advancedgradingmethod_'.$areaname; 361 if (isset($moduleinfo->{$formfield})) { 362 $gradingman->set_area($areaname); 363 $methodchanged = $gradingman->set_active_method($moduleinfo->{$formfield}); 364 if (empty($moduleinfo->{$formfield})) { 365 // Going back to the simple direct grading is not a reason to open the management screen. 366 $methodchanged = false; 367 } 368 $showgradingmanagement = $showgradingmanagement || $methodchanged; 369 } 370 } 371 // Update grading management information. 372 $moduleinfo->gradingman = $gradingman; 373 $moduleinfo->showgradingmanagement = $showgradingmanagement; 374 } 375 376 rebuild_course_cache($course->id, true); 377 if ($hasgrades) { 378 grade_regrade_final_grades($course->id); 379 } 380 381 // To be removed (deprecated) with MDL-67526 (both lines). 382 require_once($CFG->libdir.'/plagiarismlib.php'); 383 plagiarism_save_form_elements($moduleinfo); 384 385 // Allow plugins to extend the course module form. 386 $moduleinfo = plugin_extend_coursemodule_edit_post_actions($moduleinfo, $course); 387 388 return $moduleinfo; 389 } 390 391 /** 392 * Set module info default values for the unset module attributs. 393 * 394 * @param object $moduleinfo the current known data of the module 395 * @return object the completed module info 396 */ 397 function set_moduleinfo_defaults($moduleinfo) { 398 399 if (empty($moduleinfo->coursemodule)) { 400 // Add. 401 $cm = null; 402 $moduleinfo->instance = ''; 403 $moduleinfo->coursemodule = ''; 404 } else { 405 // Update. 406 $cm = get_coursemodule_from_id('', $moduleinfo->coursemodule, 0, false, MUST_EXIST); 407 $moduleinfo->instance = $cm->instance; 408 $moduleinfo->coursemodule = $cm->id; 409 } 410 // For safety. 411 $moduleinfo->modulename = clean_param($moduleinfo->modulename, PARAM_PLUGIN); 412 413 if (!isset($moduleinfo->groupingid)) { 414 $moduleinfo->groupingid = 0; 415 } 416 417 if (!isset($moduleinfo->name)) { // Label. 418 $moduleinfo->name = $moduleinfo->modulename; 419 } 420 421 if (!isset($moduleinfo->completion)) { 422 $moduleinfo->completion = COMPLETION_DISABLED; 423 } 424 if (!isset($moduleinfo->completionview)) { 425 $moduleinfo->completionview = COMPLETION_VIEW_NOT_REQUIRED; 426 } 427 if (!isset($moduleinfo->completionexpected)) { 428 $moduleinfo->completionexpected = 0; 429 } 430 431 // Convert the 'use grade' checkbox into a grade-item number: 0 if checked, null if not. 432 if (isset($moduleinfo->completionusegrade) && $moduleinfo->completionusegrade) { 433 $moduleinfo->completiongradeitemnumber = 0; 434 } else if (!isset($moduleinfo->completiongradeitemnumber)) { 435 $moduleinfo->completiongradeitemnumber = null; 436 } 437 438 if (!isset($moduleinfo->conditiongradegroup)) { 439 $moduleinfo->conditiongradegroup = array(); 440 } 441 if (!isset($moduleinfo->conditionfieldgroup)) { 442 $moduleinfo->conditionfieldgroup = array(); 443 } 444 if (!isset($moduleinfo->visibleoncoursepage)) { 445 $moduleinfo->visibleoncoursepage = 1; 446 } 447 448 return $moduleinfo; 449 } 450 451 /** 452 * Check that the user can add a module. Also returns some information like the module, context and course section info. 453 * The fucntion create the course section if it doesn't exist. 454 * 455 * @param object $course the course of the module 456 * @param object $modulename the module name 457 * @param object $section the section of the module 458 * @return array list containing module, context, course section. 459 * @throws moodle_exception if user is not allowed to perform the action or module is not allowed in this course 460 */ 461 function can_add_moduleinfo($course, $modulename, $section) { 462 global $DB; 463 464 $module = $DB->get_record('modules', array('name'=>$modulename), '*', MUST_EXIST); 465 466 $context = context_course::instance($course->id); 467 require_capability('moodle/course:manageactivities', $context); 468 469 course_create_sections_if_missing($course, $section); 470 $cw = get_fast_modinfo($course)->get_section_info($section); 471 472 if (!course_allowed_module($course, $module->name)) { 473 print_error('moduledisable'); 474 } 475 476 return array($module, $context, $cw); 477 } 478 479 /** 480 * Check if user is allowed to update module info and returns related item/data to the module. 481 * 482 * @param object $cm course module 483 * @return array - list of course module, context, module, moduleinfo, and course section. 484 * @throws moodle_exception if user is not allowed to perform the action 485 */ 486 function can_update_moduleinfo($cm) { 487 global $DB; 488 489 // Check the $USER has the right capability. 490 $context = context_module::instance($cm->id); 491 require_capability('moodle/course:manageactivities', $context); 492 493 // Check module exists. 494 $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST); 495 496 // Check the moduleinfo exists. 497 $data = $DB->get_record($module->name, array('id'=>$cm->instance), '*', MUST_EXIST); 498 499 // Check the course section exists. 500 $cw = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST); 501 502 return array($cm, $context, $module, $data, $cw); 503 } 504 505 506 /** 507 * Update the module info. 508 * This function doesn't check the user capabilities. It updates the course module and the module instance. 509 * Then execute common action to create/update module process (trigger event, rebuild cache, save plagiarism settings...). 510 * 511 * @param object $cm course module 512 * @param object $moduleinfo module info 513 * @param object $course course of the module 514 * @param object $mform - the mform is required by some specific module in the function MODULE_update_instance(). This is due to a hack in this function. 515 * @return array list of course module and module info. 516 */ 517 function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) { 518 global $DB, $CFG; 519 520 $data = new stdClass(); 521 if ($mform) { 522 $data = $mform->get_data(); 523 } 524 525 // Attempt to include module library before we make any changes to DB. 526 include_modulelib($moduleinfo->modulename); 527 528 $moduleinfo->course = $course->id; 529 $moduleinfo = set_moduleinfo_defaults($moduleinfo); 530 531 if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { 532 $moduleinfo->groupmode = $cm->groupmode; // Keep original. 533 } 534 535 // Update course module first. 536 $cm->groupmode = $moduleinfo->groupmode; 537 if (isset($moduleinfo->groupingid)) { 538 $cm->groupingid = $moduleinfo->groupingid; 539 } 540 541 $completion = new completion_info($course); 542 if ($completion->is_enabled()) { 543 // Completion settings that would affect users who have already completed 544 // the activity may be locked; if so, these should not be updated. 545 if (!empty($moduleinfo->completionunlocked)) { 546 $cm->completion = $moduleinfo->completion; 547 if ($moduleinfo->completiongradeitemnumber === '') { 548 $cm->completiongradeitemnumber = null; 549 } else { 550 $cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; 551 } 552 $cm->completionview = $moduleinfo->completionview; 553 } 554 // The expected date does not affect users who have completed the activity, 555 // so it is safe to update it regardless of the lock status. 556 $cm->completionexpected = $moduleinfo->completionexpected; 557 } 558 if (!empty($CFG->enableavailability)) { 559 // This code is used both when submitting the form, which uses a long 560 // name to avoid clashes, and by unit test code which uses the real 561 // name in the table. 562 if (property_exists($moduleinfo, 'availabilityconditionsjson')) { 563 if ($moduleinfo->availabilityconditionsjson !== '') { 564 $cm->availability = $moduleinfo->availabilityconditionsjson; 565 } else { 566 $cm->availability = null; 567 } 568 } else if (property_exists($moduleinfo, 'availability')) { 569 $cm->availability = $moduleinfo->availability; 570 } 571 // If there is any availability data, verify it. 572 if ($cm->availability) { 573 $tree = new \core_availability\tree(json_decode($cm->availability)); 574 // Save time and database space by setting null if the only data 575 // is an empty tree. 576 if ($tree->is_empty()) { 577 $cm->availability = null; 578 } 579 } 580 } 581 if (isset($moduleinfo->showdescription)) { 582 $cm->showdescription = $moduleinfo->showdescription; 583 } else { 584 $cm->showdescription = 0; 585 } 586 587 $DB->update_record('course_modules', $cm); 588 589 $modcontext = context_module::instance($moduleinfo->coursemodule); 590 591 // Update embedded links and save files. 592 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) { 593 $moduleinfo->intro = file_save_draft_area_files($moduleinfo->introeditor['itemid'], $modcontext->id, 594 'mod_'.$moduleinfo->modulename, 'intro', 0, 595 array('subdirs'=>true), $moduleinfo->introeditor['text']); 596 $moduleinfo->introformat = $moduleinfo->introeditor['format']; 597 unset($moduleinfo->introeditor); 598 } 599 // Get the a copy of the grade_item before it is modified incase we need to scale the grades. 600 $oldgradeitem = null; 601 $newgradeitem = null; 602 if (!empty($data->grade_rescalegrades) && $data->grade_rescalegrades == 'yes') { 603 // Fetch the grade item before it is updated. 604 $oldgradeitem = grade_item::fetch(array('itemtype' => 'mod', 605 'itemmodule' => $moduleinfo->modulename, 606 'iteminstance' => $moduleinfo->instance, 607 'itemnumber' => 0, 608 'courseid' => $moduleinfo->course)); 609 } 610 611 $updateinstancefunction = $moduleinfo->modulename."_update_instance"; 612 if (!$updateinstancefunction($moduleinfo, $mform)) { 613 print_error('cannotupdatemod', '', course_get_url($course, $cm->section), $moduleinfo->modulename); 614 } 615 616 // This needs to happen AFTER the grademin/grademax have already been updated. 617 if (!empty($data->grade_rescalegrades) && $data->grade_rescalegrades == 'yes') { 618 // Get the grade_item after the update call the activity to scale the grades. 619 $newgradeitem = grade_item::fetch(array('itemtype' => 'mod', 620 'itemmodule' => $moduleinfo->modulename, 621 'iteminstance' => $moduleinfo->instance, 622 'itemnumber' => 0, 623 'courseid' => $moduleinfo->course)); 624 if ($newgradeitem && $oldgradeitem->gradetype == GRADE_TYPE_VALUE && $newgradeitem->gradetype == GRADE_TYPE_VALUE) { 625 $params = array( 626 $course, 627 $cm, 628 $oldgradeitem->grademin, 629 $oldgradeitem->grademax, 630 $newgradeitem->grademin, 631 $newgradeitem->grademax 632 ); 633 if (!component_callback('mod_' . $moduleinfo->modulename, 'rescale_activity_grades', $params)) { 634 print_error('cannotreprocessgrades', '', course_get_url($course, $cm->section), $moduleinfo->modulename); 635 } 636 } 637 } 638 639 // Make sure visibility is set correctly (in particular in calendar). 640 if (has_capability('moodle/course:activityvisibility', $modcontext)) { 641 set_coursemodule_visible($moduleinfo->coursemodule, $moduleinfo->visible, $moduleinfo->visibleoncoursepage); 642 } 643 644 if (isset($moduleinfo->cmidnumber)) { // Label. 645 // Set cm idnumber - uniqueness is already verified by form validation. 646 set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber); 647 } 648 649 // Update module tags. 650 if (core_tag_tag::is_enabled('core', 'course_modules') && isset($moduleinfo->tags)) { 651 core_tag_tag::set_item_tags('core', 'course_modules', $moduleinfo->coursemodule, $modcontext, $moduleinfo->tags); 652 } 653 654 // Now that module is fully updated, also update completion data if required. 655 // (this will wipe all user completion data and recalculate it) 656 if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) { 657 $completion->reset_all_state($cm); 658 } 659 $cm->name = $moduleinfo->name; 660 \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 661 662 $moduleinfo = edit_module_post_actions($moduleinfo, $course); 663 664 return array($cm, $moduleinfo); 665 } 666 667 /** 668 * Include once the module lib file. 669 * 670 * @param string $modulename module name of the lib to include 671 * @throws moodle_exception if lib.php file for the module does not exist 672 */ 673 function include_modulelib($modulename) { 674 global $CFG; 675 $modlib = "$CFG->dirroot/mod/$modulename/lib.php"; 676 if (file_exists($modlib)) { 677 include_once($modlib); 678 } else { 679 throw new moodle_exception('modulemissingcode', '', '', $modlib); 680 } 681 } 682 683 /** 684 * Get module information data required for updating the module. 685 * 686 * @param stdClass $cm course module object 687 * @param stdClass $course course object 688 * @return array required data for updating a module 689 * @since Moodle 3.2 690 */ 691 function get_moduleinfo_data($cm, $course) { 692 global $CFG; 693 694 list($cm, $context, $module, $data, $cw) = can_update_moduleinfo($cm); 695 696 $data->coursemodule = $cm->id; 697 $data->section = $cw->section; // The section number itself - relative!!! (section column in course_sections) 698 $data->visible = $cm->visible; //?? $cw->visible ? $cm->visible : 0; // section hiding overrides 699 $data->visibleoncoursepage = $cm->visibleoncoursepage; 700 $data->cmidnumber = $cm->idnumber; // The cm IDnumber 701 $data->groupmode = groups_get_activity_groupmode($cm); // locked later if forced 702 $data->groupingid = $cm->groupingid; 703 $data->course = $course->id; 704 $data->module = $module->id; 705 $data->modulename = $module->name; 706 $data->instance = $cm->instance; 707 $data->completion = $cm->completion; 708 $data->completionview = $cm->completionview; 709 $data->completionexpected = $cm->completionexpected; 710 $data->completionusegrade = is_null($cm->completiongradeitemnumber) ? 0 : 1; 711 $data->completiongradeitemnumber = $cm->completiongradeitemnumber; 712 $data->showdescription = $cm->showdescription; 713 $data->tags = core_tag_tag::get_item_tags_array('core', 'course_modules', $cm->id); 714 if (!empty($CFG->enableavailability)) { 715 $data->availabilityconditionsjson = $cm->availability; 716 } 717 718 if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) { 719 $draftid_editor = file_get_submitted_draft_itemid('introeditor'); 720 $currentintro = file_prepare_draft_area($draftid_editor, $context->id, 'mod_'.$data->modulename, 'intro', 0, array('subdirs'=>true), $data->intro); 721 $data->introeditor = array('text'=>$currentintro, 'format'=>$data->introformat, 'itemid'=>$draftid_editor); 722 } 723 724 if (plugin_supports('mod', $data->modulename, FEATURE_ADVANCED_GRADING, false) 725 and has_capability('moodle/grade:managegradingforms', $context)) { 726 require_once($CFG->dirroot.'/grade/grading/lib.php'); 727 $gradingman = get_grading_manager($context, 'mod_'.$data->modulename); 728 $data->_advancedgradingdata['methods'] = $gradingman->get_available_methods(); 729 $areas = $gradingman->get_available_areas(); 730 731 foreach ($areas as $areaname => $areatitle) { 732 $gradingman->set_area($areaname); 733 $method = $gradingman->get_active_method(); 734 $data->_advancedgradingdata['areas'][$areaname] = array( 735 'title' => $areatitle, 736 'method' => $method, 737 ); 738 $formfield = 'advancedgradingmethod_'.$areaname; 739 $data->{$formfield} = $method; 740 } 741 } 742 743 $component = "mod_{$data->modulename}"; 744 $items = grade_item::fetch_all([ 745 'itemtype' => 'mod', 746 'itemmodule' => $data->modulename, 747 'iteminstance' => $data->instance, 748 'courseid' => $course->id, 749 ]); 750 751 if ($items) { 752 // Add existing outcomes. 753 foreach ($items as $item) { 754 if (!empty($item->outcomeid)) { 755 $data->{'outcome_' . $item->outcomeid} = 1; 756 } else if (isset($item->gradepass)) { 757 $gradepassfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $item->itemnumber, 'gradepass'); 758 $data->{$gradepassfieldname} = format_float($item->gradepass, $item->get_decimals()); 759 } 760 761 } 762 763 // set category if present 764 $gradecat = []; 765 foreach ($items as $item) { 766 if (!isset($gradecat[$item->itemnumber])) { 767 $gradecat[$item->itemnumber] = $item->categoryid; 768 } 769 if ($gradecat[$item->itemnumber] != $item->categoryid) { 770 // Mixed categories. 771 $gradecat[$item->itemnumber] = false; 772 } 773 } 774 foreach ($gradecat as $itemnumber => $cat) { 775 if ($cat !== false) { 776 $gradecatfieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'gradecat'); 777 // Do not set if mixed categories present. 778 $data->{$gradecatfieldname} = $cat; 779 } 780 } 781 } 782 return array($cm, $context, $module, $data, $cw); 783 } 784 785 /** 786 * Prepare the standard module information for a new module instance. 787 * 788 * @param stdClass $course course object 789 * @param string $modulename module name 790 * @param int $section section number 791 * @return array module information about other required data 792 * @since Moodle 3.2 793 */ 794 function prepare_new_moduleinfo_data($course, $modulename, $section) { 795 global $CFG; 796 797 list($module, $context, $cw) = can_add_moduleinfo($course, $modulename, $section); 798 799 $cm = null; 800 801 $data = new stdClass(); 802 $data->section = $section; // The section number itself - relative!!! (section column in course_sections) 803 $data->visible = $cw->visible; 804 $data->course = $course->id; 805 $data->module = $module->id; 806 $data->modulename = $module->name; 807 $data->groupmode = $course->groupmode; 808 $data->groupingid = $course->defaultgroupingid; 809 $data->id = ''; 810 $data->instance = ''; 811 $data->coursemodule = ''; 812 813 // Apply completion defaults. 814 $defaults = \core_completion\manager::get_default_completion($course, $module); 815 foreach ($defaults as $key => $value) { 816 $data->$key = $value; 817 } 818 819 if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) { 820 $draftid_editor = file_get_submitted_draft_itemid('introeditor'); 821 file_prepare_draft_area($draftid_editor, null, null, null, null, array('subdirs'=>true)); 822 $data->introeditor = array('text'=>'', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); // TODO: add better default 823 } 824 825 if (plugin_supports('mod', $data->modulename, FEATURE_ADVANCED_GRADING, false) 826 and has_capability('moodle/grade:managegradingforms', $context)) { 827 require_once($CFG->dirroot.'/grade/grading/lib.php'); 828 829 $data->_advancedgradingdata['methods'] = grading_manager::available_methods(); 830 $areas = grading_manager::available_areas('mod_'.$module->name); 831 832 foreach ($areas as $areaname => $areatitle) { 833 $data->_advancedgradingdata['areas'][$areaname] = array( 834 'title' => $areatitle, 835 'method' => '', 836 ); 837 $formfield = 'advancedgradingmethod_'.$areaname; 838 $data->{$formfield} = ''; 839 } 840 } 841 842 return array($module, $context, $cw, $cm, $data); 843 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body