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