[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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 require_once($CFG->dirroot.'/course/lib.php'); 31 32 /** 33 * Add course module. 34 * 35 * The function does not check user capabilities. 36 * The function creates course module, module instance, add the module to the correct section. 37 * It also trigger common action that need to be done after adding/updating a module. 38 * 39 * @param object $moduleinfo the moudle data 40 * @param object $course the course of the module 41 * @param object $mform this is required by an existing hack to deal with files during MODULENAME_add_instance() 42 * @return object the updated module info 43 */ 44 function add_moduleinfo($moduleinfo, $course, $mform = null) { 45 global $DB, $CFG; 46 47 // Attempt to include module library before we make any changes to DB. 48 include_modulelib($moduleinfo->modulename); 49 50 $moduleinfo->course = $course->id; 51 $moduleinfo = set_moduleinfo_defaults($moduleinfo); 52 53 if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { 54 $moduleinfo->groupmode = 0; // Do not set groupmode. 55 } 56 57 // First add course_module record because we need the context. 58 $newcm = new stdClass(); 59 $newcm->course = $course->id; 60 $newcm->module = $moduleinfo->module; 61 $newcm->instance = 0; // Not known yet, will be updated later (this is similar to restore code). 62 $newcm->visible = $moduleinfo->visible; 63 $newcm->visibleold = $moduleinfo->visible; 64 if (isset($moduleinfo->cmidnumber)) { 65 $newcm->idnumber = $moduleinfo->cmidnumber; 66 } 67 $newcm->groupmode = $moduleinfo->groupmode; 68 $newcm->groupingid = $moduleinfo->groupingid; 69 $completion = new completion_info($course); 70 if ($completion->is_enabled()) { 71 $newcm->completion = $moduleinfo->completion; 72 $newcm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; 73 $newcm->completionview = $moduleinfo->completionview; 74 $newcm->completionexpected = $moduleinfo->completionexpected; 75 } 76 if(!empty($CFG->enableavailability)) { 77 // This code is used both when submitting the form, which uses a long 78 // name to avoid clashes, and by unit test code which uses the real 79 // name in the table. 80 $newcm->availability = null; 81 if (property_exists($moduleinfo, 'availabilityconditionsjson')) { 82 if ($moduleinfo->availabilityconditionsjson !== '') { 83 $newcm->availability = $moduleinfo->availabilityconditionsjson; 84 } 85 } else if (property_exists($moduleinfo, 'availability')) { 86 $newcm->availability = $moduleinfo->availability; 87 } 88 // If there is any availability data, verify it. 89 if ($newcm->availability) { 90 $tree = new \core_availability\tree(json_decode($newcm->availability)); 91 // Save time and database space by setting null if the only data 92 // is an empty tree. 93 if ($tree->is_empty()) { 94 $newcm->availability = null; 95 } 96 } 97 } 98 if (isset($moduleinfo->showdescription)) { 99 $newcm->showdescription = $moduleinfo->showdescription; 100 } else { 101 $newcm->showdescription = 0; 102 } 103 104 // From this point we make database changes, so start transaction. 105 $transaction = $DB->start_delegated_transaction(); 106 107 if (!$moduleinfo->coursemodule = add_course_module($newcm)) { 108 print_error('cannotaddcoursemodule'); 109 } 110 111 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true) && 112 isset($moduleinfo->introeditor)) { 113 $introeditor = $moduleinfo->introeditor; 114 unset($moduleinfo->introeditor); 115 $moduleinfo->intro = $introeditor['text']; 116 $moduleinfo->introformat = $introeditor['format']; 117 } 118 119 $addinstancefunction = $moduleinfo->modulename."_add_instance"; 120 try { 121 $returnfromfunc = $addinstancefunction($moduleinfo, $mform); 122 } catch (moodle_exception $e) { 123 $returnfromfunc = $e; 124 } 125 if (!$returnfromfunc or !is_number($returnfromfunc)) { 126 // Undo everything we can. This is not necessary for databases which 127 // support transactions, but improves consistency for other databases. 128 $modcontext = context_module::instance($moduleinfo->coursemodule); 129 context_helper::delete_instance(CONTEXT_MODULE, $moduleinfo->coursemodule); 130 $DB->delete_records('course_modules', array('id'=>$moduleinfo->coursemodule)); 131 132 if ($e instanceof moodle_exception) { 133 throw $e; 134 } else if (!is_number($returnfromfunc)) { 135 print_error('invalidfunction', '', course_get_url($course, $moduleinfo->section)); 136 } else { 137 print_error('cannotaddnewmodule', '', course_get_url($course, $moduleinfo->section), $moduleinfo->modulename); 138 } 139 } 140 141 $moduleinfo->instance = $returnfromfunc; 142 143 $DB->set_field('course_modules', 'instance', $returnfromfunc, array('id'=>$moduleinfo->coursemodule)); 144 145 // Update embedded links and save files. 146 $modcontext = context_module::instance($moduleinfo->coursemodule); 147 if (!empty($introeditor)) { 148 $moduleinfo->intro = file_save_draft_area_files($introeditor['itemid'], $modcontext->id, 149 'mod_'.$moduleinfo->modulename, 'intro', 0, 150 array('subdirs'=>true), $introeditor['text']); 151 $DB->set_field($moduleinfo->modulename, 'intro', $moduleinfo->intro, array('id'=>$moduleinfo->instance)); 152 } 153 154 // Course_modules and course_sections each contain a reference to each other. 155 // So we have to update one of them twice. 156 $sectionid = course_add_cm_to_section($course, $moduleinfo->coursemodule, $moduleinfo->section); 157 158 // Trigger event based on the action we did. 159 // Api create_from_cm expects modname and id property, and we don't want to modify $moduleinfo since we are returning it. 160 $eventdata = clone $moduleinfo; 161 $eventdata->modname = $eventdata->modulename; 162 $eventdata->id = $eventdata->coursemodule; 163 $event = \core\event\course_module_created::create_from_cm($eventdata, $modcontext); 164 $event->trigger(); 165 166 $moduleinfo = edit_module_post_actions($moduleinfo, $course); 167 $transaction->allow_commit(); 168 169 return $moduleinfo; 170 } 171 172 173 /** 174 * Common create/update module module actions that need to be processed as soon as a module is created/updaded. 175 * For example:create grade parent category, add outcomes, rebuild caches, regrade, save plagiarism settings... 176 * Please note this api does not trigger events as of MOODLE 2.6. Please trigger events before calling this api. 177 * 178 * @param object $moduleinfo the module info 179 * @param object $course the course of the module 180 * 181 * @return object moduleinfo update with grading management info 182 */ 183 function edit_module_post_actions($moduleinfo, $course) { 184 global $CFG; 185 require_once($CFG->libdir.'/gradelib.php'); 186 187 $modcontext = context_module::instance($moduleinfo->coursemodule); 188 $hasgrades = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_HAS_GRADE, false); 189 $hasoutcomes = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_OUTCOMES, true); 190 191 // Sync idnumber with grade_item. 192 if ($hasgrades && $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename, 193 'iteminstance'=>$moduleinfo->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) { 194 if ($grade_item->idnumber != $moduleinfo->cmidnumber) { 195 $grade_item->idnumber = $moduleinfo->cmidnumber; 196 $grade_item->update(); 197 } 198 } 199 200 if ($hasgrades) { 201 $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename, 202 'iteminstance'=>$moduleinfo->instance, 'courseid'=>$course->id)); 203 } else { 204 $items = array(); 205 } 206 207 // Create parent category if requested and move to correct parent category. 208 if ($items and isset($moduleinfo->gradecat)) { 209 if ($moduleinfo->gradecat == -1) { 210 $grade_category = new grade_category(); 211 $grade_category->courseid = $course->id; 212 $grade_category->fullname = $moduleinfo->name; 213 $grade_category->insert(); 214 if ($grade_item) { 215 $parent = $grade_item->get_parent_category(); 216 $grade_category->set_parent($parent->id); 217 } 218 $moduleinfo->gradecat = $grade_category->id; 219 } 220 $gradecategory = $grade_item->get_parent_category(); 221 foreach ($items as $itemid=>$unused) { 222 $items[$itemid]->set_parent($moduleinfo->gradecat); 223 if ($itemid == $grade_item->id) { 224 // Use updated grade_item. 225 $grade_item = $items[$itemid]; 226 } 227 if (!empty($moduleinfo->add)) { 228 if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) { 229 if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) { 230 $grade_item->aggregationcoef = 1; 231 } else { 232 $grade_item->aggregationcoef = 0; 233 } 234 $grade_item->update(); 235 } 236 } 237 } 238 } 239 240 require_once($CFG->libdir.'/grade/grade_outcome.php'); 241 // Add outcomes if requested. 242 if ($hasoutcomes && $outcomes = grade_outcome::fetch_all_available($course->id)) { 243 $grade_items = array(); 244 245 // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes. 246 $max_itemnumber = 999; 247 if ($items) { 248 foreach($items as $item) { 249 if ($item->itemnumber > $max_itemnumber) { 250 $max_itemnumber = $item->itemnumber; 251 } 252 } 253 } 254 255 foreach($outcomes as $outcome) { 256 $elname = 'outcome_'.$outcome->id; 257 258 if (property_exists($moduleinfo, $elname) and $moduleinfo->$elname) { 259 // So we have a request for new outcome grade item? 260 if ($items) { 261 $outcomeexists = false; 262 foreach($items as $item) { 263 if ($item->outcomeid == $outcome->id) { 264 $outcomeexists = true; 265 break; 266 } 267 } 268 if ($outcomeexists) { 269 continue; 270 } 271 } 272 273 $max_itemnumber++; 274 275 $outcome_item = new grade_item(); 276 $outcome_item->courseid = $course->id; 277 $outcome_item->itemtype = 'mod'; 278 $outcome_item->itemmodule = $moduleinfo->modulename; 279 $outcome_item->iteminstance = $moduleinfo->instance; 280 $outcome_item->itemnumber = $max_itemnumber; 281 $outcome_item->itemname = $outcome->fullname; 282 $outcome_item->outcomeid = $outcome->id; 283 $outcome_item->gradetype = GRADE_TYPE_SCALE; 284 $outcome_item->scaleid = $outcome->scaleid; 285 $outcome_item->insert(); 286 287 // Move the new outcome into correct category and fix sortorder if needed. 288 if ($grade_item) { 289 $outcome_item->set_parent($grade_item->categoryid); 290 $outcome_item->move_after_sortorder($grade_item->sortorder); 291 292 } else if (isset($moduleinfo->gradecat)) { 293 $outcome_item->set_parent($moduleinfo->gradecat); 294 } 295 $gradecategory = $outcome_item->get_parent_category(); 296 if ($outcomeexists == false) { 297 if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) { 298 if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) { 299 $outcome_item->aggregationcoef = 1; 300 } else { 301 $outcome_item->aggregationcoef = 0; 302 } 303 $outcome_item->update(); 304 } 305 } 306 } 307 } 308 } 309 310 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_ADVANCED_GRADING, false) 311 and has_capability('moodle/grade:managegradingforms', $modcontext)) { 312 require_once($CFG->dirroot.'/grade/grading/lib.php'); 313 $gradingman = get_grading_manager($modcontext, 'mod_'.$moduleinfo->modulename); 314 $showgradingmanagement = false; 315 foreach ($gradingman->get_available_areas() as $areaname => $aretitle) { 316 $formfield = 'advancedgradingmethod_'.$areaname; 317 if (isset($moduleinfo->{$formfield})) { 318 $gradingman->set_area($areaname); 319 $methodchanged = $gradingman->set_active_method($moduleinfo->{$formfield}); 320 if (empty($moduleinfo->{$formfield})) { 321 // Going back to the simple direct grading is not a reason to open the management screen. 322 $methodchanged = false; 323 } 324 $showgradingmanagement = $showgradingmanagement || $methodchanged; 325 } 326 } 327 // Update grading management information. 328 $moduleinfo->gradingman = $gradingman; 329 $moduleinfo->showgradingmanagement = $showgradingmanagement; 330 } 331 332 rebuild_course_cache($course->id, true); 333 if ($hasgrades) { 334 grade_regrade_final_grades($course->id); 335 } 336 require_once($CFG->libdir.'/plagiarismlib.php'); 337 plagiarism_save_form_elements($moduleinfo); 338 339 return $moduleinfo; 340 } 341 342 343 /** 344 * Set module info default values for the unset module attributs. 345 * 346 * @param object $moduleinfo the current known data of the module 347 * @return object the completed module info 348 */ 349 function set_moduleinfo_defaults($moduleinfo) { 350 351 if (empty($moduleinfo->coursemodule)) { 352 // Add. 353 $cm = null; 354 $moduleinfo->instance = ''; 355 $moduleinfo->coursemodule = ''; 356 } else { 357 // Update. 358 $cm = get_coursemodule_from_id('', $moduleinfo->coursemodule, 0, false, MUST_EXIST); 359 $moduleinfo->instance = $cm->instance; 360 $moduleinfo->coursemodule = $cm->id; 361 } 362 // For safety. 363 $moduleinfo->modulename = clean_param($moduleinfo->modulename, PARAM_PLUGIN); 364 365 if (!isset($moduleinfo->groupingid)) { 366 $moduleinfo->groupingid = 0; 367 } 368 369 if (!isset($moduleinfo->name)) { // Label. 370 $moduleinfo->name = $moduleinfo->modulename; 371 } 372 373 if (!isset($moduleinfo->completion)) { 374 $moduleinfo->completion = COMPLETION_DISABLED; 375 } 376 if (!isset($moduleinfo->completionview)) { 377 $moduleinfo->completionview = COMPLETION_VIEW_NOT_REQUIRED; 378 } 379 if (!isset($moduleinfo->completionexpected)) { 380 $moduleinfo->completionexpected = 0; 381 } 382 383 // Convert the 'use grade' checkbox into a grade-item number: 0 if checked, null if not. 384 if (isset($moduleinfo->completionusegrade) && $moduleinfo->completionusegrade) { 385 $moduleinfo->completiongradeitemnumber = 0; 386 } else { 387 $moduleinfo->completiongradeitemnumber = null; 388 } 389 390 if (!isset($moduleinfo->conditiongradegroup)) { 391 $moduleinfo->conditiongradegroup = array(); 392 } 393 if (!isset($moduleinfo->conditionfieldgroup)) { 394 $moduleinfo->conditionfieldgroup = array(); 395 } 396 397 return $moduleinfo; 398 } 399 400 /** 401 * Check that the user can add a module. Also returns some information like the module, context and course section info. 402 * The fucntion create the course section if it doesn't exist. 403 * 404 * @param object $course the course of the module 405 * @param object $modulename the module name 406 * @param object $section the section of the module 407 * @return array list containing module, context, course section. 408 * @throws moodle_exception if user is not allowed to perform the action or module is not allowed in this course 409 */ 410 function can_add_moduleinfo($course, $modulename, $section) { 411 global $DB; 412 413 $module = $DB->get_record('modules', array('name'=>$modulename), '*', MUST_EXIST); 414 415 $context = context_course::instance($course->id); 416 require_capability('moodle/course:manageactivities', $context); 417 418 course_create_sections_if_missing($course, $section); 419 $cw = get_fast_modinfo($course)->get_section_info($section); 420 421 if (!course_allowed_module($course, $module->name)) { 422 print_error('moduledisable'); 423 } 424 425 return array($module, $context, $cw); 426 } 427 428 /** 429 * Check if user is allowed to update module info and returns related item/data to the module. 430 * 431 * @param object $cm course module 432 * @return array - list of course module, context, module, moduleinfo, and course section. 433 * @throws moodle_exception if user is not allowed to perform the action 434 */ 435 function can_update_moduleinfo($cm) { 436 global $DB; 437 438 // Check the $USER has the right capability. 439 $context = context_module::instance($cm->id); 440 require_capability('moodle/course:manageactivities', $context); 441 442 // Check module exists. 443 $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST); 444 445 // Check the moduleinfo exists. 446 $data = $DB->get_record($module->name, array('id'=>$cm->instance), '*', MUST_EXIST); 447 448 // Check the course section exists. 449 $cw = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST); 450 451 return array($cm, $context, $module, $data, $cw); 452 } 453 454 455 /** 456 * Update the module info. 457 * This function doesn't check the user capabilities. It updates the course module and the module instance. 458 * Then execute common action to create/update module process (trigger event, rebuild cache, save plagiarism settings...). 459 * 460 * @param object $cm course module 461 * @param object $moduleinfo module info 462 * @param object $course course of the module 463 * @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. 464 * @return array list of course module and module info. 465 */ 466 function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) { 467 global $DB, $CFG; 468 469 // Attempt to include module library before we make any changes to DB. 470 include_modulelib($moduleinfo->modulename); 471 472 $moduleinfo->course = $course->id; 473 $moduleinfo = set_moduleinfo_defaults($moduleinfo); 474 475 if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { 476 $moduleinfo->groupmode = $cm->groupmode; // Keep original. 477 } 478 479 // Update course module first. 480 $cm->groupmode = $moduleinfo->groupmode; 481 if (isset($moduleinfo->groupingid)) { 482 $cm->groupingid = $moduleinfo->groupingid; 483 } 484 485 $completion = new completion_info($course); 486 if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) { 487 // Update completion settings. 488 $cm->completion = $moduleinfo->completion; 489 $cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; 490 $cm->completionview = $moduleinfo->completionview; 491 $cm->completionexpected = $moduleinfo->completionexpected; 492 } 493 if (!empty($CFG->enableavailability)) { 494 // This code is used both when submitting the form, which uses a long 495 // name to avoid clashes, and by unit test code which uses the real 496 // name in the table. 497 if (property_exists($moduleinfo, 'availabilityconditionsjson')) { 498 if ($moduleinfo->availabilityconditionsjson !== '') { 499 $cm->availability = $moduleinfo->availabilityconditionsjson; 500 } else { 501 $cm->availability = null; 502 } 503 } else if (property_exists($moduleinfo, 'availability')) { 504 $cm->availability = $moduleinfo->availability; 505 } 506 // If there is any availability data, verify it. 507 if ($cm->availability) { 508 $tree = new \core_availability\tree(json_decode($cm->availability)); 509 // Save time and database space by setting null if the only data 510 // is an empty tree. 511 if ($tree->is_empty()) { 512 $cm->availability = null; 513 } 514 } 515 } 516 if (isset($moduleinfo->showdescription)) { 517 $cm->showdescription = $moduleinfo->showdescription; 518 } else { 519 $cm->showdescription = 0; 520 } 521 522 $DB->update_record('course_modules', $cm); 523 524 $modcontext = context_module::instance($moduleinfo->coursemodule); 525 526 // Update embedded links and save files. 527 if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) { 528 $moduleinfo->intro = file_save_draft_area_files($moduleinfo->introeditor['itemid'], $modcontext->id, 529 'mod_'.$moduleinfo->modulename, 'intro', 0, 530 array('subdirs'=>true), $moduleinfo->introeditor['text']); 531 $moduleinfo->introformat = $moduleinfo->introeditor['format']; 532 unset($moduleinfo->introeditor); 533 } 534 $updateinstancefunction = $moduleinfo->modulename."_update_instance"; 535 if (!$updateinstancefunction($moduleinfo, $mform)) { 536 print_error('cannotupdatemod', '', course_get_url($course, $cw->section), $moduleinfo->modulename); 537 } 538 539 // Make sure visibility is set correctly (in particular in calendar). 540 if (has_capability('moodle/course:activityvisibility', $modcontext)) { 541 set_coursemodule_visible($moduleinfo->coursemodule, $moduleinfo->visible); 542 } 543 544 if (isset($moduleinfo->cmidnumber)) { // Label. 545 // Set cm idnumber - uniqueness is already verified by form validation. 546 set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber); 547 } 548 549 // Now that module is fully updated, also update completion data if required. 550 // (this will wipe all user completion data and recalculate it) 551 if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) { 552 $completion->reset_all_state($cm); 553 } 554 $cm->name = $moduleinfo->name; 555 \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 556 557 $moduleinfo = edit_module_post_actions($moduleinfo, $course); 558 559 return array($cm, $moduleinfo); 560 } 561 562 /** 563 * Include once the module lib file. 564 * 565 * @param string $modulename module name of the lib to include 566 * @throws moodle_exception if lib.php file for the module does not exist 567 */ 568 function include_modulelib($modulename) { 569 global $CFG; 570 $modlib = "$CFG->dirroot/mod/$modulename/lib.php"; 571 if (file_exists($modlib)) { 572 include_once($modlib); 573 } else { 574 throw new moodle_exception('modulemissingcode', '', '', $modlib); 575 } 576 } 577
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |