See Release Notes
Long Term Support Release
Differences Between: [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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 namespace core_courseformat; 18 19 use core_courseformat\stateupdates; 20 use core\event\course_module_updated; 21 use cm_info; 22 use section_info; 23 use stdClass; 24 use course_modinfo; 25 use moodle_exception; 26 use context_module; 27 use context_course; 28 29 /** 30 * Contains the core course state actions. 31 * 32 * The methods from this class should be executed via "core_courseformat_edit" web service. 33 * 34 * Each format plugin could extend this class to provide new actions to the editor. 35 * Extended classes should be locate in "format_XXX\course" namespace and 36 * extends core_courseformat\stateactions. 37 * 38 * @package core_courseformat 39 * @copyright 2021 Ferran Recio <ferran@moodle.com> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class stateactions { 43 44 /** 45 * Move course modules to another location in the same course. 46 * 47 * @param stateupdates $updates the affected course elements track 48 * @param stdClass $course the course object 49 * @param int[] $ids the list of affected course module ids 50 * @param int $targetsectionid optional target section id 51 * @param int $targetcmid optional target cm id 52 */ 53 public function cm_move( 54 stateupdates $updates, 55 stdClass $course, 56 array $ids, 57 ?int $targetsectionid = null, 58 ?int $targetcmid = null 59 ): void { 60 // Validate target elements. 61 if (!$targetsectionid && !$targetcmid) { 62 throw new moodle_exception("Action cm_move requires targetsectionid or targetcmid"); 63 } 64 65 $this->validate_cms($course, $ids, __FUNCTION__); 66 67 // Check capabilities on every activity context. 68 foreach ($ids as $cmid) { 69 $modcontext = context_module::instance($cmid); 70 require_capability('moodle/course:manageactivities', $modcontext); 71 } 72 73 $modinfo = get_fast_modinfo($course); 74 75 // Target cm has more priority than target section. 76 if (!empty($targetcmid)) { 77 $this->validate_cms($course, [$targetcmid], __FUNCTION__); 78 $targetcm = $modinfo->get_cm($targetcmid); 79 $targetsection = $modinfo->get_section_info_by_id($targetcm->section, MUST_EXIST); 80 } else { 81 $this->validate_sections($course, [$targetsectionid], __FUNCTION__); 82 $targetcm = null; 83 $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); 84 } 85 86 // The origin sections must be updated as well. 87 $originalsections = []; 88 89 $cms = $this->get_cm_info($modinfo, $ids); 90 foreach ($cms as $cm) { 91 $currentsection = $modinfo->get_section_info_by_id($cm->section, MUST_EXIST); 92 moveto_module($cm, $targetsection, $targetcm); 93 $updates->add_cm_put($cm->id); 94 if ($currentsection->id != $targetsection->id) { 95 $originalsections[$currentsection->id] = true; 96 } 97 // If some of the original sections are also target sections, we don't need to update them. 98 if (array_key_exists($targetsection->id, $originalsections)) { 99 unset($originalsections[$targetsection->id]); 100 } 101 } 102 103 // Use section_state to return the full affected section and activities updated state. 104 $this->cm_state($updates, $course, $ids, $targetsectionid, $targetcmid); 105 106 foreach (array_keys($originalsections) as $sectionid) { 107 $updates->add_section_put($sectionid); 108 } 109 } 110 111 /** 112 * Move course sections to another location in the same course. 113 * 114 * @param stateupdates $updates the affected course elements track 115 * @param stdClass $course the course object 116 * @param int[] $ids the list of affected course module ids 117 * @param int $targetsectionid optional target section id 118 * @param int $targetcmid optional target cm id 119 */ 120 public function section_move( 121 stateupdates $updates, 122 stdClass $course, 123 array $ids, 124 ?int $targetsectionid = null, 125 ?int $targetcmid = null 126 ): void { 127 // Validate target elements. 128 if (!$targetsectionid) { 129 throw new moodle_exception("Action cm_move requires targetsectionid"); 130 } 131 132 $this->validate_sections($course, $ids, __FUNCTION__); 133 134 $coursecontext = context_course::instance($course->id); 135 require_capability('moodle/course:movesections', $coursecontext); 136 137 $modinfo = get_fast_modinfo($course); 138 139 // Target section. 140 $this->validate_sections($course, [$targetsectionid], __FUNCTION__); 141 $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); 142 143 $affectedsections = [$targetsection->section => true]; 144 145 $sections = $this->get_section_info($modinfo, $ids); 146 foreach ($sections as $section) { 147 $affectedsections[$section->section] = true; 148 move_section_to($course, $section->section, $targetsection->section); 149 } 150 151 // Use section_state to return the section and activities updated state. 152 $this->section_state($updates, $course, $ids, $targetsectionid); 153 154 // All course sections can be renamed because of the resort. 155 $allsections = $modinfo->get_section_info_all(); 156 foreach ($allsections as $section) { 157 // Ignore the affected sections because they are already in the updates. 158 if (isset($affectedsections[$section->section])) { 159 continue; 160 } 161 $updates->add_section_put($section->id); 162 } 163 // The section order is at a course level. 164 $updates->add_course_put(); 165 } 166 167 /** 168 * Create a course section. 169 * 170 * This method follows the same logic as changenumsections.php. 171 * 172 * @param stateupdates $updates the affected course elements track 173 * @param stdClass $course the course object 174 * @param int[] $ids not used 175 * @param int $targetsectionid optional target section id (if not passed section will be appended) 176 * @param int $targetcmid not used 177 */ 178 public function section_add( 179 stateupdates $updates, 180 stdClass $course, 181 array $ids = [], 182 ?int $targetsectionid = null, 183 ?int $targetcmid = null 184 ): void { 185 186 $coursecontext = context_course::instance($course->id); 187 require_capability('moodle/course:update', $coursecontext); 188 189 // Get course format settings. 190 $format = course_get_format($course->id); 191 $lastsectionnumber = $format->get_last_section_number(); 192 $maxsections = $format->get_max_sections(); 193 194 if ($lastsectionnumber >= $maxsections) { 195 throw new moodle_exception('maxsectionslimit', 'moodle', $maxsections); 196 } 197 198 $modinfo = get_fast_modinfo($course); 199 200 // Get target section. 201 if ($targetsectionid) { 202 $this->validate_sections($course, [$targetsectionid], __FUNCTION__); 203 $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); 204 // Inserting sections at any position except in the very end requires capability to move sections. 205 require_capability('moodle/course:movesections', $coursecontext); 206 $insertposition = $targetsection->section + 1; 207 } else { 208 // Get last section. 209 $insertposition = 0; 210 } 211 212 course_create_section($course, $insertposition); 213 214 // Adding a section affects the full course structure. 215 $this->course_state($updates, $course); 216 } 217 218 /** 219 * Delete course sections. 220 * 221 * This method follows the same logic as editsection.php. 222 * 223 * @param stateupdates $updates the affected course elements track 224 * @param stdClass $course the course object 225 * @param int[] $ids section ids 226 * @param int $targetsectionid not used 227 * @param int $targetcmid not used 228 */ 229 public function section_delete( 230 stateupdates $updates, 231 stdClass $course, 232 array $ids = [], 233 ?int $targetsectionid = null, 234 ?int $targetcmid = null 235 ): void { 236 237 $coursecontext = context_course::instance($course->id); 238 require_capability('moodle/course:update', $coursecontext); 239 require_capability('moodle/course:movesections', $coursecontext); 240 241 $modinfo = get_fast_modinfo($course); 242 243 foreach ($ids as $sectionid) { 244 $section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST); 245 // Send all activity deletions. 246 if (!empty($modinfo->sections[$section->section])) { 247 foreach ($modinfo->sections[$section->section] as $modnumber) { 248 $cm = $modinfo->cms[$modnumber]; 249 $updates->add_cm_remove($cm->id); 250 } 251 } 252 course_delete_section($course, $section, true, true); 253 $updates->add_section_remove($sectionid); 254 } 255 256 // Removing a section affects the full course structure. 257 $this->course_state($updates, $course); 258 } 259 260 /** 261 * Hide course sections. 262 * 263 * @param stateupdates $updates the affected course elements track 264 * @param stdClass $course the course object 265 * @param int[] $ids section ids 266 * @param int $targetsectionid not used 267 * @param int $targetcmid not used 268 */ 269 public function section_hide( 270 stateupdates $updates, 271 stdClass $course, 272 array $ids = [], 273 ?int $targetsectionid = null, 274 ?int $targetcmid = null 275 ): void { 276 $this->set_section_visibility($updates, $course, $ids, 0); 277 } 278 279 /** 280 * Show course sections. 281 * 282 * @param stateupdates $updates the affected course elements track 283 * @param stdClass $course the course object 284 * @param int[] $ids section ids 285 * @param int $targetsectionid not used 286 * @param int $targetcmid not used 287 */ 288 public function section_show( 289 stateupdates $updates, 290 stdClass $course, 291 array $ids = [], 292 ?int $targetsectionid = null, 293 ?int $targetcmid = null 294 ): void { 295 $this->set_section_visibility($updates, $course, $ids, 1); 296 } 297 298 /** 299 * Show course sections. 300 * 301 * @param stateupdates $updates the affected course elements track 302 * @param stdClass $course the course object 303 * @param int[] $ids section ids 304 * @param int $visible the new visible value 305 */ 306 protected function set_section_visibility ( 307 stateupdates $updates, 308 stdClass $course, 309 array $ids, 310 int $visible 311 ) { 312 $this->validate_sections($course, $ids, __FUNCTION__); 313 $coursecontext = context_course::instance($course->id); 314 require_all_capabilities(['moodle/course:update', 'moodle/course:sectionvisibility'], $coursecontext); 315 316 $modinfo = get_fast_modinfo($course); 317 318 foreach ($ids as $sectionid) { 319 $section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST); 320 course_update_section($course, $section, ['visible' => $visible]); 321 } 322 $this->section_state($updates, $course, $ids); 323 } 324 325 /** 326 * Show course cms. 327 * 328 * @param stateupdates $updates the affected course elements track 329 * @param stdClass $course the course object 330 * @param int[] $ids cm ids 331 * @param int $targetsectionid not used 332 * @param int $targetcmid not used 333 */ 334 public function cm_show( 335 stateupdates $updates, 336 stdClass $course, 337 array $ids = [], 338 ?int $targetsectionid = null, 339 ?int $targetcmid = null 340 ): void { 341 $this->set_cm_visibility($updates, $course, $ids, 1, 1); 342 } 343 344 /** 345 * Hide course cms. 346 * 347 * @param stateupdates $updates the affected course elements track 348 * @param stdClass $course the course object 349 * @param int[] $ids cm ids 350 * @param int $targetsectionid not used 351 * @param int $targetcmid not used 352 */ 353 public function cm_hide( 354 stateupdates $updates, 355 stdClass $course, 356 array $ids = [], 357 ?int $targetsectionid = null, 358 ?int $targetcmid = null 359 ): void { 360 $this->set_cm_visibility($updates, $course, $ids, 0, 1); 361 } 362 363 /** 364 * Stealth course cms. 365 * 366 * @param stateupdates $updates the affected course elements track 367 * @param stdClass $course the course object 368 * @param int[] $ids cm ids 369 * @param int $targetsectionid not used 370 * @param int $targetcmid not used 371 */ 372 public function cm_stealth( 373 stateupdates $updates, 374 stdClass $course, 375 array $ids = [], 376 ?int $targetsectionid = null, 377 ?int $targetcmid = null 378 ): void { 379 $this->set_cm_visibility($updates, $course, $ids, 1, 0); 380 } 381 382 /** 383 * Internal method to define the cm visibility. 384 * 385 * @param stateupdates $updates the affected course elements track 386 * @param stdClass $course the course object 387 * @param int[] $ids cm ids 388 * @param int $visible the new visible value 389 * @param int $coursevisible the new course visible value 390 */ 391 protected function set_cm_visibility( 392 stateupdates $updates, 393 stdClass $course, 394 array $ids, 395 int $visible, 396 int $coursevisible 397 ): void { 398 global $CFG; 399 400 $this->validate_cms($course, $ids, __FUNCTION__); 401 402 // Check capabilities on every activity context. 403 foreach ($ids as $cmid) { 404 $modcontext = context_module::instance($cmid); 405 require_all_capabilities(['moodle/course:manageactivities', 'moodle/course:activityvisibility'], $modcontext); 406 } 407 408 $format = course_get_format($course->id); 409 $modinfo = get_fast_modinfo($course); 410 411 $cms = $this->get_cm_info($modinfo, $ids); 412 foreach ($cms as $cm) { 413 // Check stealth availability. 414 if (!$coursevisible) { 415 $section = $cm->get_section_info(); 416 $allowstealth = !empty($CFG->allowstealth) && $format->allow_stealth_module_visibility($cm, $section); 417 $coursevisible = ($allowstealth) ? 0 : 1; 418 } 419 set_coursemodule_visible($cm->id, $visible, $coursevisible, false); 420 course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 421 } 422 course_modinfo::purge_course_modules_cache($course->id, $ids); 423 rebuild_course_cache($course->id, false, true); 424 425 foreach ($cms as $cm) { 426 $updates->add_cm_put($cm->id); 427 } 428 } 429 430 /** 431 * Move course cms to the right. Indent = 1. 432 * 433 * @param stateupdates $updates the affected course elements track 434 * @param stdClass $course the course object 435 * @param int[] $ids cm ids 436 * @param int $targetsectionid not used 437 * @param int $targetcmid not used 438 */ 439 public function cm_moveright( 440 stateupdates $updates, 441 stdClass $course, 442 array $ids = [], 443 ?int $targetsectionid = null, 444 ?int $targetcmid = null 445 ): void { 446 $this->set_cm_indentation($updates, $course, $ids, 1); 447 } 448 449 /** 450 * Move course cms to the left. Indent = 0. 451 * 452 * @param stateupdates $updates the affected course elements track 453 * @param stdClass $course the course object 454 * @param int[] $ids cm ids 455 * @param int $targetsectionid not used 456 * @param int $targetcmid not used 457 */ 458 public function cm_moveleft( 459 stateupdates $updates, 460 stdClass $course, 461 array $ids = [], 462 ?int $targetsectionid = null, 463 ?int $targetcmid = null 464 ): void { 465 $this->set_cm_indentation($updates, $course, $ids, 0); 466 } 467 468 /** 469 * Internal method to define the cm indentation level. 470 * 471 * @param stateupdates $updates the affected course elements track 472 * @param stdClass $course the course object 473 * @param int[] $ids cm ids 474 * @param int $indent new value for indentation 475 */ 476 protected function set_cm_indentation( 477 stateupdates $updates, 478 stdClass $course, 479 array $ids, 480 int $indent 481 ): void { 482 global $DB; 483 484 $this->validate_cms($course, $ids, __FUNCTION__); 485 486 // Check capabilities on every activity context. 487 foreach ($ids as $cmid) { 488 $modcontext = context_module::instance($cmid); 489 require_capability('moodle/course:manageactivities', $modcontext); 490 } 491 $modinfo = get_fast_modinfo($course); 492 $cms = $this->get_cm_info($modinfo, $ids); 493 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($cms), SQL_PARAMS_NAMED); 494 $DB->set_field_select('course_modules', 'indent', $indent, "id $insql", $inparams); 495 rebuild_course_cache($course->id, false, true); 496 foreach ($cms as $cm) { 497 $modcontext = context_module::instance($cm->id); 498 course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 499 $updates->add_cm_put($cm->id); 500 } 501 } 502 503 /** 504 * Extract several cm_info from the course_modinfo. 505 * 506 * @param course_modinfo $modinfo the course modinfo. 507 * @param int[] $ids the course modules $ids 508 * @return cm_info[] the extracted cm_info objects 509 */ 510 protected function get_cm_info (course_modinfo $modinfo, array $ids): array { 511 $cms = []; 512 foreach ($ids as $cmid) { 513 $cms[$cmid] = $modinfo->get_cm($cmid); 514 } 515 return $cms; 516 } 517 518 /** 519 * Extract several section_info from the course_modinfo. 520 * 521 * @param course_modinfo $modinfo the course modinfo. 522 * @param int[] $ids the course modules $ids 523 * @return section_info[] the extracted section_info objects 524 */ 525 protected function get_section_info(course_modinfo $modinfo, array $ids): array { 526 $sections = []; 527 foreach ($ids as $sectionid) { 528 $sections[$sectionid] = $modinfo->get_section_info_by_id($sectionid); 529 } 530 return $sections; 531 } 532 533 /** 534 * Update the course content section collapsed value. 535 * 536 * @param stateupdates $updates the affected course elements track 537 * @param stdClass $course the course object 538 * @param int[] $ids the collapsed section ids 539 * @param int $targetsectionid not used 540 * @param int $targetcmid not used 541 */ 542 public function section_content_collapsed( 543 stateupdates $updates, 544 stdClass $course, 545 array $ids = [], 546 ?int $targetsectionid = null, 547 ?int $targetcmid = null 548 ): void { 549 if (!empty($ids)) { 550 $this->validate_sections($course, $ids, __FUNCTION__); 551 } 552 $format = course_get_format($course->id); 553 $format->set_sections_preference('contentcollapsed', $ids); 554 } 555 556 /** 557 * Update the course index section collapsed value. 558 * 559 * @param stateupdates $updates the affected course elements track 560 * @param stdClass $course the course object 561 * @param int[] $ids the collapsed section ids 562 * @param int $targetsectionid not used 563 * @param int $targetcmid not used 564 */ 565 public function section_index_collapsed( 566 stateupdates $updates, 567 stdClass $course, 568 array $ids = [], 569 ?int $targetsectionid = null, 570 ?int $targetcmid = null 571 ): void { 572 if (!empty($ids)) { 573 $this->validate_sections($course, $ids, __FUNCTION__); 574 } 575 $format = course_get_format($course->id); 576 $format->set_sections_preference('indexcollapsed', $ids); 577 } 578 579 /** 580 * Add the update messages of the updated version of any cm and section related to the cm ids. 581 * 582 * This action is mainly used by legacy actions to partially update the course state when the 583 * result of core_course_edit_module is not enough to generate the correct state data. 584 * 585 * @param stateupdates $updates the affected course elements track 586 * @param stdClass $course the course object 587 * @param int[] $ids the list of affected course module ids 588 * @param int $targetsectionid optional target section id 589 * @param int $targetcmid optional target cm id 590 */ 591 public function cm_state( 592 stateupdates $updates, 593 stdClass $course, 594 array $ids, 595 ?int $targetsectionid = null, 596 ?int $targetcmid = null 597 ): void { 598 599 // Collect all section and cm to return. 600 $cmids = []; 601 foreach ($ids as $cmid) { 602 $cmids[$cmid] = true; 603 } 604 if ($targetcmid) { 605 $cmids[$targetcmid] = true; 606 } 607 608 $sectionids = []; 609 if ($targetsectionid) { 610 $this->validate_sections($course, [$targetsectionid], __FUNCTION__); 611 $sectionids[$targetsectionid] = true; 612 } 613 614 $this->validate_cms($course, array_keys($cmids), __FUNCTION__); 615 616 $modinfo = course_modinfo::instance($course); 617 618 foreach (array_keys($cmids) as $cmid) { 619 620 // Add this action to updates array. 621 $updates->add_cm_put($cmid); 622 623 $cm = $modinfo->get_cm($cmid); 624 $sectionids[$cm->section] = true; 625 } 626 627 foreach (array_keys($sectionids) as $sectionid) { 628 $updates->add_section_put($sectionid); 629 } 630 } 631 632 /** 633 * Add the update messages of the updated version of any cm and section related to the section ids. 634 * 635 * This action is mainly used by legacy actions to partially update the course state when the 636 * result of core_course_edit_module is not enough to generate the correct state data. 637 * 638 * @param stateupdates $updates the affected course elements track 639 * @param stdClass $course the course object 640 * @param int[] $ids the list of affected course section ids 641 * @param int $targetsectionid optional target section id 642 * @param int $targetcmid optional target cm id 643 */ 644 public function section_state( 645 stateupdates $updates, 646 stdClass $course, 647 array $ids, 648 ?int $targetsectionid = null, 649 ?int $targetcmid = null 650 ): void { 651 652 $cmids = []; 653 if ($targetcmid) { 654 $this->validate_cms($course, [$targetcmid], __FUNCTION__); 655 $cmids[$targetcmid] = true; 656 } 657 658 $sectionids = []; 659 foreach ($ids as $sectionid) { 660 $sectionids[$sectionid] = true; 661 } 662 if ($targetsectionid) { 663 $sectionids[$targetsectionid] = true; 664 } 665 666 $this->validate_sections($course, array_keys($sectionids), __FUNCTION__); 667 668 $modinfo = course_modinfo::instance($course); 669 670 foreach (array_keys($sectionids) as $sectionid) { 671 $sectioninfo = $modinfo->get_section_info_by_id($sectionid); 672 $updates->add_section_put($sectionid); 673 // Add cms. 674 if (empty($modinfo->sections[$sectioninfo->section])) { 675 continue; 676 } 677 678 foreach ($modinfo->sections[$sectioninfo->section] as $modnumber) { 679 $mod = $modinfo->cms[$modnumber]; 680 if ($mod->is_visible_on_course_page()) { 681 $cmids[$mod->id] = true; 682 } 683 } 684 } 685 686 foreach (array_keys($cmids) as $cmid) { 687 // Add this action to updates array. 688 $updates->add_cm_put($cmid); 689 } 690 } 691 692 /** 693 * Add all the update messages from the complete course state. 694 * 695 * This action is mainly used by legacy actions to partially update the course state when the 696 * result of core_course_edit_module is not enough to generate the correct state data. 697 * 698 * @param stateupdates $updates the affected course elements track 699 * @param stdClass $course the course object 700 * @param int[] $ids the list of affected course module ids (not used) 701 * @param int $targetsectionid optional target section id (not used) 702 * @param int $targetcmid optional target cm id (not used) 703 */ 704 public function course_state( 705 stateupdates $updates, 706 stdClass $course, 707 array $ids = [], 708 ?int $targetsectionid = null, 709 ?int $targetcmid = null 710 ): void { 711 712 $modinfo = course_modinfo::instance($course); 713 714 $updates->add_course_put(); 715 716 // Add sections updates. 717 $sections = $modinfo->get_section_info_all(); 718 $sectionids = []; 719 foreach ($sections as $sectioninfo) { 720 $sectionids[] = $sectioninfo->id; 721 } 722 if (!empty($sectionids)) { 723 $this->section_state($updates, $course, $sectionids); 724 } 725 } 726 727 /** 728 * Checks related to sections: course format support them, all given sections exist and topic 0 is not included. 729 * 730 * @param stdClass $course The course where given $sectionids belong. 731 * @param array $sectionids List of sections to validate. 732 * @param string|null $info additional information in case of error (default null). 733 * @throws moodle_exception if any id is not valid 734 */ 735 protected function validate_sections(stdClass $course, array $sectionids, ?string $info = null): void { 736 global $DB; 737 738 if (empty($sectionids)) { 739 throw new moodle_exception('emptysectionids', 'core', null, $info); 740 } 741 742 // No section actions are allowed if course format does not support sections. 743 $courseformat = course_get_format($course->id); 744 if (!$courseformat->uses_sections()) { 745 throw new moodle_exception('sectionactionnotsupported', 'core', null, $info); 746 } 747 748 list($insql, $inparams) = $DB->get_in_or_equal($sectionids, SQL_PARAMS_NAMED); 749 750 // Check if all the given sections exist. 751 $couintsections = $DB->count_records_select('course_sections', "id $insql", $inparams); 752 if ($couintsections != count($sectionids)) { 753 throw new moodle_exception('unexistingsectionid', 'core', null, $info); 754 } 755 } 756 757 /** 758 * Checks related to course modules: all given cm exist. 759 * 760 * @param stdClass $course The course where given $cmids belong. 761 * @param array $cmids List of course module ids to validate. 762 * @param string $info additional information in case of error. 763 * @throws moodle_exception if any id is not valid 764 */ 765 protected function validate_cms(stdClass $course, array $cmids, ?string $info = null): void { 766 767 if (empty($cmids)) { 768 throw new moodle_exception('emptycmids', 'core', null, $info); 769 } 770 771 $moduleinfo = get_fast_modinfo($course->id); 772 $intersect = array_intersect($cmids, array_keys($moduleinfo->get_cms())); 773 if (count($cmids) != count($intersect)) { 774 throw new moodle_exception('unexistingcmid', 'core', null, $info); 775 } 776 } 777 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body