Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 18 /** 19 * External course API 20 * 21 * @package core_course 22 * @category external 23 * @copyright 2009 Petr Skodak 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die; 28 29 use core_course\external\course_summary_exporter; 30 use core_external\external_api; 31 use core_external\external_description; 32 use core_external\external_files; 33 use core_external\external_format_value; 34 use core_external\external_function_parameters; 35 use core_external\external_multiple_structure; 36 use core_external\external_single_structure; 37 use core_external\external_value; 38 use core_external\external_warnings; 39 use core_external\util; 40 require_once (__DIR__ . "/lib.php"); 41 42 /** 43 * Course external functions 44 * 45 * @package core_course 46 * @category external 47 * @copyright 2011 Jerome Mouneyrac 48 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 49 * @since Moodle 2.2 50 */ 51 class core_course_external extends external_api { 52 53 /** 54 * Returns description of method parameters 55 * 56 * @return external_function_parameters 57 * @since Moodle 2.9 Options available 58 * @since Moodle 2.2 59 */ 60 public static function get_course_contents_parameters() { 61 return new external_function_parameters( 62 array('courseid' => new external_value(PARAM_INT, 'course id'), 63 'options' => new external_multiple_structure ( 64 new external_single_structure( 65 array( 66 'name' => new external_value(PARAM_ALPHANUM, 67 'The expected keys (value format) are: 68 excludemodules (bool) Do not return modules, return only the sections structure 69 excludecontents (bool) Do not return module contents (i.e: files inside a resource) 70 includestealthmodules (bool) Return stealth modules for students in a special 71 section (with id -1) 72 sectionid (int) Return only this section 73 sectionnumber (int) Return only this section with number (order) 74 cmid (int) Return only this module information (among the whole sections structure) 75 modname (string) Return only modules with this name "label, forum, etc..." 76 modid (int) Return only the module with this id (to be used with modname'), 77 'value' => new external_value(PARAM_RAW, 'the value of the option, 78 this param is personaly validated in the external function.') 79 ) 80 ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array()) 81 ) 82 ); 83 } 84 85 /** 86 * Get course contents 87 * 88 * @param int $courseid course id 89 * @param array $options Options for filtering the results, used since Moodle 2.9 90 * @return array 91 * @since Moodle 2.9 Options available 92 * @since Moodle 2.2 93 */ 94 public static function get_course_contents($courseid, $options = array()) { 95 global $CFG, $DB, $USER, $PAGE; 96 require_once($CFG->dirroot . "/course/lib.php"); 97 require_once($CFG->libdir . '/completionlib.php'); 98 99 //validate parameter 100 $params = self::validate_parameters(self::get_course_contents_parameters(), 101 array('courseid' => $courseid, 'options' => $options)); 102 103 $filters = array(); 104 if (!empty($params['options'])) { 105 106 foreach ($params['options'] as $option) { 107 $name = trim($option['name']); 108 // Avoid duplicated options. 109 if (!isset($filters[$name])) { 110 switch ($name) { 111 case 'excludemodules': 112 case 'excludecontents': 113 case 'includestealthmodules': 114 $value = clean_param($option['value'], PARAM_BOOL); 115 $filters[$name] = $value; 116 break; 117 case 'sectionid': 118 case 'sectionnumber': 119 case 'cmid': 120 case 'modid': 121 $value = clean_param($option['value'], PARAM_INT); 122 if (is_numeric($value)) { 123 $filters[$name] = $value; 124 } else { 125 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 126 } 127 break; 128 case 'modname': 129 $value = clean_param($option['value'], PARAM_PLUGIN); 130 if ($value) { 131 $filters[$name] = $value; 132 } else { 133 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 134 } 135 break; 136 default: 137 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 138 } 139 } 140 } 141 } 142 143 //retrieve the course 144 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); 145 146 // now security checks 147 $context = context_course::instance($course->id, IGNORE_MISSING); 148 try { 149 self::validate_context($context); 150 } catch (Exception $e) { 151 $exceptionparam = new stdClass(); 152 $exceptionparam->message = $e->getMessage(); 153 $exceptionparam->courseid = $course->id; 154 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); 155 } 156 157 $canupdatecourse = has_capability('moodle/course:update', $context); 158 159 //create return value 160 $coursecontents = array(); 161 162 if ($canupdatecourse or $course->visible 163 or has_capability('moodle/course:viewhiddencourses', $context)) { 164 165 //retrieve sections 166 $modinfo = get_fast_modinfo($course); 167 $sections = $modinfo->get_section_info_all(); 168 $courseformat = course_get_format($course); 169 $coursenumsections = $courseformat->get_last_section_number(); 170 $stealthmodules = array(); // Array to keep all the modules available but not visible in a course section/topic. 171 172 $completioninfo = new completion_info($course); 173 174 //for each sections (first displayed to last displayed) 175 $modinfosections = $modinfo->get_sections(); 176 foreach ($sections as $key => $section) { 177 178 // This becomes true when we are filtering and we found the value to filter with. 179 $sectionfound = false; 180 181 // Filter by section id. 182 if (!empty($filters['sectionid'])) { 183 if ($section->id != $filters['sectionid']) { 184 continue; 185 } else { 186 $sectionfound = true; 187 } 188 } 189 190 // Filter by section number. Note that 0 is a valid section number. 191 if (isset($filters['sectionnumber'])) { 192 if ($key != $filters['sectionnumber']) { 193 continue; 194 } else { 195 $sectionfound = true; 196 } 197 } 198 199 // reset $sectioncontents 200 $sectionvalues = array(); 201 $sectionvalues['id'] = $section->id; 202 $sectionvalues['name'] = get_section_name($course, $section); 203 $sectionvalues['visible'] = $section->visible; 204 205 $options = (object) array('noclean' => true); 206 list($sectionvalues['summary'], $sectionvalues['summaryformat']) = 207 \core_external\util::format_text($section->summary, $section->summaryformat, 208 $context, 'course', 'section', $section->id, $options); 209 $sectionvalues['section'] = $section->section; 210 $sectionvalues['hiddenbynumsections'] = $section->section > $coursenumsections ? 1 : 0; 211 $sectionvalues['uservisible'] = $section->uservisible; 212 if (!empty($section->availableinfo)) { 213 $sectionvalues['availabilityinfo'] = \core_availability\info::format_info($section->availableinfo, $course); 214 } 215 216 $sectioncontents = array(); 217 218 // For each module of the section. 219 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) { 220 foreach ($modinfosections[$section->section] as $cmid) { 221 $cm = $modinfo->cms[$cmid]; 222 $cminfo = cm_info::create($cm); 223 $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id); 224 225 // Stop here if the module is not visible to the user on the course main page: 226 // The user can't access the module and the user can't view the module on the course page. 227 if (!$cm->uservisible && !$cm->is_visible_on_course_page()) { 228 continue; 229 } 230 231 // This becomes true when we are filtering and we found the value to filter with. 232 $modfound = false; 233 234 // Filter by cmid. 235 if (!empty($filters['cmid'])) { 236 if ($cmid != $filters['cmid']) { 237 continue; 238 } else { 239 $modfound = true; 240 } 241 } 242 243 // Filter by module name and id. 244 if (!empty($filters['modname'])) { 245 if ($cm->modname != $filters['modname']) { 246 continue; 247 } else if (!empty($filters['modid'])) { 248 if ($cm->instance != $filters['modid']) { 249 continue; 250 } else { 251 // Note that if we are only filtering by modname we don't break the loop. 252 $modfound = true; 253 } 254 } 255 } 256 257 $module = array(); 258 259 $modcontext = context_module::instance($cm->id); 260 261 //common info (for people being able to see the module or availability dates) 262 $module['id'] = $cm->id; 263 $module['name'] = \core_external\util::format_string($cm->name, $modcontext); 264 $module['instance'] = $cm->instance; 265 $module['contextid'] = $modcontext->id; 266 $module['modname'] = (string) $cm->modname; 267 $module['modplural'] = (string) $cm->modplural; 268 $module['modicon'] = $cm->get_icon_url()->out(false); 269 $module['indent'] = $cm->indent; 270 $module['onclick'] = $cm->onclick; 271 $module['afterlink'] = $cm->afterlink; 272 $activitybadgedata = $cm->get_activitybadge(); 273 if (!empty($activitybadgedata)) { 274 $module['activitybadge'] = $activitybadgedata; 275 } 276 $module['customdata'] = json_encode($cm->customdata); 277 $module['completion'] = $cm->completion; 278 $module['downloadcontent'] = $cm->downloadcontent; 279 $module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false); 280 $module['dates'] = $activitydates; 281 $module['groupmode'] = $cm->groupmode; 282 283 // Check module completion. 284 $completion = $completioninfo->is_enabled($cm); 285 if ($completion != COMPLETION_DISABLED) { 286 $exporter = new \core_completion\external\completion_info_exporter($course, $cm, $USER->id); 287 $renderer = $PAGE->get_renderer('core'); 288 $modulecompletiondata = (array)$exporter->export($renderer); 289 $module['completiondata'] = $modulecompletiondata; 290 } 291 292 if (!empty($cm->showdescription) or $module['noviewlink']) { 293 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML. 294 $options = array('noclean' => true); 295 list($module['description'], $descriptionformat) = \core_external\util::format_text($cm->content, 296 FORMAT_HTML, $modcontext, $cm->modname, 'intro', $cm->id, $options); 297 } 298 299 //url of the module 300 $url = $cm->url; 301 if ($url) { //labels don't have url 302 $module['url'] = $url->out(false); 303 } 304 305 $canviewhidden = has_capability('moodle/course:viewhiddenactivities', 306 context_module::instance($cm->id)); 307 //user that can view hidden module should know about the visibility 308 $module['visible'] = $cm->visible; 309 $module['visibleoncoursepage'] = $cm->visibleoncoursepage; 310 $module['uservisible'] = $cm->uservisible; 311 if (!empty($cm->availableinfo)) { 312 $module['availabilityinfo'] = \core_availability\info::format_info($cm->availableinfo, $course); 313 } 314 315 // Availability date (also send to user who can see hidden module). 316 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) { 317 $module['availability'] = $cm->availability; 318 } 319 320 // Return contents only if the user can access to the module. 321 if ($cm->uservisible) { 322 $baseurl = 'webservice/pluginfile.php'; 323 324 // Call $modulename_export_contents (each module callback take care about checking the capabilities). 325 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php'); 326 $getcontentfunction = $cm->modname.'_export_contents'; 327 if (function_exists($getcontentfunction)) { 328 $contents = $getcontentfunction($cm, $baseurl); 329 $module['contentsinfo'] = array( 330 'filescount' => count($contents), 331 'filessize' => 0, 332 'lastmodified' => 0, 333 'mimetypes' => array(), 334 ); 335 foreach ($contents as $content) { 336 // Check repository file (only main file). 337 if (!isset($module['contentsinfo']['repositorytype'])) { 338 $module['contentsinfo']['repositorytype'] = 339 isset($content['repositorytype']) ? $content['repositorytype'] : ''; 340 } 341 if (isset($content['filesize'])) { 342 $module['contentsinfo']['filessize'] += $content['filesize']; 343 } 344 if (isset($content['timemodified']) && 345 ($content['timemodified'] > $module['contentsinfo']['lastmodified'])) { 346 347 $module['contentsinfo']['lastmodified'] = $content['timemodified']; 348 } 349 if (isset($content['mimetype'])) { 350 $module['contentsinfo']['mimetypes'][$content['mimetype']] = $content['mimetype']; 351 } 352 } 353 354 if (empty($filters['excludecontents']) and !empty($contents)) { 355 $module['contents'] = $contents; 356 } else { 357 $module['contents'] = array(); 358 } 359 } 360 } 361 362 // Assign result to $sectioncontents, there is an exception, 363 // stealth activities in non-visible sections for students go to a special section. 364 if (!empty($filters['includestealthmodules']) && !$section->uservisible && $cm->is_stealth()) { 365 $stealthmodules[] = $module; 366 } else { 367 $sectioncontents[] = $module; 368 } 369 370 // If we just did a filtering, break the loop. 371 if ($modfound) { 372 break; 373 } 374 375 } 376 } 377 $sectionvalues['modules'] = $sectioncontents; 378 379 // assign result to $coursecontents 380 $coursecontents[$key] = $sectionvalues; 381 382 // Break the loop if we are filtering. 383 if ($sectionfound) { 384 break; 385 } 386 } 387 388 // Now that we have iterated over all the sections and activities, check the visibility. 389 // We didn't this before to be able to retrieve stealth activities. 390 foreach ($coursecontents as $sectionnumber => $sectioncontents) { 391 $section = $sections[$sectionnumber]; 392 393 if (!$courseformat->is_section_visible($section)) { 394 unset($coursecontents[$sectionnumber]); 395 continue; 396 } 397 398 // Remove section and modules information if the section is not visible for the user. 399 if (!$section->uservisible) { 400 $coursecontents[$sectionnumber]['modules'] = array(); 401 // Remove summary information if the section is completely hidden only, 402 // even if the section is not user visible, the summary is always displayed among the availability information. 403 if (!$section->visible) { 404 $coursecontents[$sectionnumber]['summary'] = ''; 405 } 406 } 407 } 408 409 // Include stealth modules in special section (without any info). 410 if (!empty($stealthmodules)) { 411 $coursecontents[] = array( 412 'id' => -1, 413 'name' => '', 414 'summary' => '', 415 'summaryformat' => FORMAT_MOODLE, 416 'modules' => $stealthmodules 417 ); 418 } 419 420 } 421 return $coursecontents; 422 } 423 424 /** 425 * Returns description of method result value 426 * 427 * @return \core_external\external_description 428 * @since Moodle 2.2 429 */ 430 public static function get_course_contents_returns() { 431 $completiondefinition = \core_completion\external\completion_info_exporter::get_read_structure(VALUE_DEFAULT, []); 432 433 return new external_multiple_structure( 434 new external_single_structure( 435 array( 436 'id' => new external_value(PARAM_INT, 'Section ID'), 437 'name' => new external_value(PARAM_RAW, 'Section name'), 438 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL), 439 'summary' => new external_value(PARAM_RAW, 'Section description'), 440 'summaryformat' => new external_format_value('summary'), 441 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL), 442 'hiddenbynumsections' => new external_value(PARAM_INT, 'Whether is a section hidden in the course format', 443 VALUE_OPTIONAL), 444 'uservisible' => new external_value(PARAM_BOOL, 'Is the section visible for the user?', VALUE_OPTIONAL), 445 'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.', VALUE_OPTIONAL), 446 'modules' => new external_multiple_structure( 447 new external_single_structure( 448 array( 449 'id' => new external_value(PARAM_INT, 'activity id'), 450 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL), 451 'name' => new external_value(PARAM_RAW, 'activity module name'), 452 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL), 453 'contextid' => new external_value(PARAM_INT, 'Activity context id.', VALUE_OPTIONAL), 454 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL), 455 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL), 456 'uservisible' => new external_value(PARAM_BOOL, 'Is the module visible for the user?', 457 VALUE_OPTIONAL), 458 'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.', 459 VALUE_OPTIONAL), 460 'visibleoncoursepage' => new external_value(PARAM_INT, 'is the module visible on course page', 461 VALUE_OPTIONAL), 462 'modicon' => new external_value(PARAM_URL, 'activity icon url'), 463 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'), 464 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'), 465 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL), 466 'indent' => new external_value(PARAM_INT, 'number of identation in the site'), 467 'onclick' => new external_value(PARAM_RAW, 'Onclick action.', VALUE_OPTIONAL), 468 'afterlink' => new external_value(PARAM_RAW, 'After link info to be displayed.', 469 VALUE_OPTIONAL), 470 'activitybadge' => self::get_activitybadge_structure(), 471 'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL), 472 'noviewlink' => new external_value(PARAM_BOOL, 'Whether the module has no view page', 473 VALUE_OPTIONAL), 474 'completion' => new external_value(PARAM_INT, 'Type of completion tracking: 475 0 means none, 1 manual, 2 automatic.', VALUE_OPTIONAL), 476 'completiondata' => $completiondefinition, 477 'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL), 478 'dates' => new external_multiple_structure( 479 new external_single_structure( 480 array( 481 'label' => new external_value(PARAM_TEXT, 'date label'), 482 'timestamp' => new external_value(PARAM_INT, 'date timestamp'), 483 'relativeto' => new external_value(PARAM_INT, 'relative date timestamp', 484 VALUE_OPTIONAL), 485 'dataid' => new external_value(PARAM_NOTAGS, 'cm data id', VALUE_OPTIONAL), 486 ) 487 ), 488 'Course dates', 489 VALUE_DEFAULT, 490 [] 491 ), 492 'groupmode' => new external_value(PARAM_INT, 'Group mode value', VALUE_OPTIONAL), 493 'contents' => new external_multiple_structure( 494 new external_single_structure( 495 array( 496 // content info 497 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'), 498 'filename'=> new external_value(PARAM_FILE, 'filename'), 499 'filepath'=> new external_value(PARAM_PATH, 'filepath'), 500 'filesize'=> new external_value(PARAM_INT, 'filesize'), 501 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL), 502 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL), 503 'timecreated' => new external_value(PARAM_INT, 'Time created'), 504 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 505 'sortorder' => new external_value(PARAM_INT, 'Content sort order'), 506 'mimetype' => new external_value(PARAM_RAW, 'File mime type.', VALUE_OPTIONAL), 507 'isexternalfile' => new external_value(PARAM_BOOL, 'Whether is an external file.', 508 VALUE_OPTIONAL), 509 'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for external files.', 510 VALUE_OPTIONAL), 511 512 // copyright related info 513 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'), 514 'author' => new external_value(PARAM_TEXT, 'Content owner'), 515 'license' => new external_value(PARAM_TEXT, 'Content license'), 516 'tags' => new external_multiple_structure( 517 \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', 518 VALUE_OPTIONAL 519 ), 520 ) 521 ), 'Course contents', VALUE_DEFAULT, array() 522 ), 523 'contentsinfo' => new external_single_structure( 524 array( 525 'filescount' => new external_value(PARAM_INT, 'Total number of files.'), 526 'filessize' => new external_value(PARAM_INT, 'Total files size.'), 527 'lastmodified' => new external_value(PARAM_INT, 'Last time files were modified.'), 528 'mimetypes' => new external_multiple_structure( 529 new external_value(PARAM_RAW, 'File mime type.'), 530 'Files mime types.' 531 ), 532 'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for 533 the main file.', VALUE_OPTIONAL), 534 ), 'Contents summary information.', VALUE_OPTIONAL 535 ), 536 ) 537 ), 'list of module' 538 ) 539 ) 540 ) 541 ); 542 } 543 544 /** 545 * Returns description of activitybadge data. 546 * 547 * @return external_description 548 */ 549 protected static function get_activitybadge_structure(): external_description { 550 return new external_single_structure( 551 [ 552 'badgecontent' => new external_value( 553 PARAM_TEXT, 554 'The content to be displayed in the activity badge', 555 VALUE_OPTIONAL 556 ), 557 'badgestyle' => new external_value( 558 PARAM_TEXT, 559 'The style for the activity badge', 560 VALUE_OPTIONAL 561 ), 562 'badgeurl' => new external_value( 563 PARAM_URL, 564 'An optional URL to redirect the user when the activity badge is clicked', 565 VALUE_OPTIONAL 566 ), 567 'badgeelementid' => new external_value( 568 PARAM_ALPHANUMEXT, 569 'An optional id in case the module wants to add some code for the activity badge', 570 VALUE_OPTIONAL 571 ), 572 'badgeextraattributes' => new external_multiple_structure( 573 new external_single_structure( 574 [ 575 'name' => new external_value( 576 PARAM_TEXT, 577 'The attribute name', 578 VALUE_OPTIONAL 579 ), 580 'value' => new external_value( 581 PARAM_TEXT, 582 'The attribute value', 583 VALUE_OPTIONAL 584 ), 585 ], 586 'Each of the attribute names and values', 587 VALUE_OPTIONAL 588 ), 589 'An optional array of extra HTML attributes to add to the badge element', 590 VALUE_OPTIONAL 591 ), 592 ], 593 'Activity badge to display near the name', 594 VALUE_OPTIONAL 595 ); 596 } 597 598 /** 599 * Returns description of method parameters 600 * 601 * @return external_function_parameters 602 * @since Moodle 2.3 603 */ 604 public static function get_courses_parameters() { 605 return new external_function_parameters( 606 array('options' => new external_single_structure( 607 array('ids' => new external_multiple_structure( 608 new external_value(PARAM_INT, 'Course id') 609 , 'List of course id. If empty return all courses 610 except front page course.', 611 VALUE_OPTIONAL) 612 ), 'options - operator OR is used', VALUE_DEFAULT, array()) 613 ) 614 ); 615 } 616 617 /** 618 * Get courses 619 * 620 * @param array $options It contains an array (list of ids) 621 * @return array 622 * @since Moodle 2.2 623 */ 624 public static function get_courses($options = array()) { 625 global $CFG, $DB; 626 require_once($CFG->dirroot . "/course/lib.php"); 627 628 //validate parameter 629 $params = self::validate_parameters(self::get_courses_parameters(), 630 array('options' => $options)); 631 632 //retrieve courses 633 if (!array_key_exists('ids', $params['options']) 634 or empty($params['options']['ids'])) { 635 $courses = $DB->get_records('course'); 636 } else { 637 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']); 638 } 639 640 //create return value 641 $coursesinfo = array(); 642 foreach ($courses as $course) { 643 644 // now security checks 645 $context = context_course::instance($course->id, IGNORE_MISSING); 646 $courseformatoptions = course_get_format($course)->get_format_options(); 647 try { 648 self::validate_context($context); 649 } catch (Exception $e) { 650 $exceptionparam = new stdClass(); 651 $exceptionparam->message = $e->getMessage(); 652 $exceptionparam->courseid = $course->id; 653 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); 654 } 655 if ($course->id != SITEID) { 656 require_capability('moodle/course:view', $context); 657 } 658 659 $courseinfo = array(); 660 $courseinfo['id'] = $course->id; 661 $courseinfo['fullname'] = \core_external\util::format_string($course->fullname, $context); 662 $courseinfo['shortname'] = \core_external\util::format_string($course->shortname, $context); 663 $courseinfo['displayname'] = \core_external\util::format_string(get_course_display_name_for_list($course), $context); 664 $courseinfo['categoryid'] = $course->category; 665 list($courseinfo['summary'], $courseinfo['summaryformat']) = 666 \core_external\util::format_text($course->summary, $course->summaryformat, $context, 'course', 'summary', 0); 667 $courseinfo['format'] = $course->format; 668 $courseinfo['startdate'] = $course->startdate; 669 $courseinfo['enddate'] = $course->enddate; 670 $courseinfo['showactivitydates'] = $course->showactivitydates; 671 $courseinfo['showcompletionconditions'] = $course->showcompletionconditions; 672 if (array_key_exists('numsections', $courseformatoptions)) { 673 // For backward-compartibility 674 $courseinfo['numsections'] = $courseformatoptions['numsections']; 675 } 676 $courseinfo['pdfexportfont'] = $course->pdfexportfont; 677 678 $handler = core_course\customfield\course_handler::create(); 679 if ($customfields = $handler->export_instance_data($course->id)) { 680 $courseinfo['customfields'] = []; 681 foreach ($customfields as $data) { 682 $courseinfo['customfields'][] = [ 683 'type' => $data->get_type(), 684 'value' => $data->get_value(), 685 'valueraw' => $data->get_data_controller()->get_value(), 686 'name' => $data->get_name(), 687 'shortname' => $data->get_shortname() 688 ]; 689 } 690 } 691 692 //some field should be returned only if the user has update permission 693 $courseadmin = has_capability('moodle/course:update', $context); 694 if ($courseadmin) { 695 $courseinfo['categorysortorder'] = $course->sortorder; 696 $courseinfo['idnumber'] = $course->idnumber; 697 $courseinfo['showgrades'] = $course->showgrades; 698 $courseinfo['showreports'] = $course->showreports; 699 $courseinfo['newsitems'] = $course->newsitems; 700 $courseinfo['visible'] = $course->visible; 701 $courseinfo['maxbytes'] = $course->maxbytes; 702 if (array_key_exists('hiddensections', $courseformatoptions)) { 703 // For backward-compartibility 704 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections']; 705 } 706 // Return numsections for backward-compatibility with clients who expect it. 707 $courseinfo['numsections'] = course_get_format($course)->get_last_section_number(); 708 $courseinfo['groupmode'] = $course->groupmode; 709 $courseinfo['groupmodeforce'] = $course->groupmodeforce; 710 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid; 711 $courseinfo['lang'] = clean_param($course->lang, PARAM_LANG); 712 $courseinfo['timecreated'] = $course->timecreated; 713 $courseinfo['timemodified'] = $course->timemodified; 714 $courseinfo['forcetheme'] = clean_param($course->theme, PARAM_THEME); 715 $courseinfo['enablecompletion'] = $course->enablecompletion; 716 $courseinfo['completionnotify'] = $course->completionnotify; 717 $courseinfo['courseformatoptions'] = array(); 718 foreach ($courseformatoptions as $key => $value) { 719 $courseinfo['courseformatoptions'][] = array( 720 'name' => $key, 721 'value' => $value 722 ); 723 } 724 } 725 726 if ($courseadmin or $course->visible 727 or has_capability('moodle/course:viewhiddencourses', $context)) { 728 $coursesinfo[] = $courseinfo; 729 } 730 } 731 732 return $coursesinfo; 733 } 734 735 /** 736 * Returns description of method result value 737 * 738 * @return \core_external\external_description 739 * @since Moodle 2.2 740 */ 741 public static function get_courses_returns() { 742 return new external_multiple_structure( 743 new external_single_structure( 744 array( 745 'id' => new external_value(PARAM_INT, 'course id'), 746 'shortname' => new external_value(PARAM_RAW, 'course short name'), 747 'categoryid' => new external_value(PARAM_INT, 'category id'), 748 'categorysortorder' => new external_value(PARAM_INT, 749 'sort order into the category', VALUE_OPTIONAL), 750 'fullname' => new external_value(PARAM_RAW, 'full name'), 751 'displayname' => new external_value(PARAM_RAW, 'course display name'), 752 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 753 'summary' => new external_value(PARAM_RAW, 'summary'), 754 'summaryformat' => new external_format_value('summary'), 755 'format' => new external_value(PARAM_PLUGIN, 756 'course format: weeks, topics, social, site,..'), 757 'showgrades' => new external_value(PARAM_INT, 758 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 759 'newsitems' => new external_value(PARAM_INT, 760 'number of recent items appearing on the course page', VALUE_OPTIONAL), 761 'startdate' => new external_value(PARAM_INT, 762 'timestamp when the course start'), 763 'enddate' => new external_value(PARAM_INT, 764 'timestamp when the course end'), 765 'numsections' => new external_value(PARAM_INT, 766 '(deprecated, use courseformatoptions) number of weeks/topics', 767 VALUE_OPTIONAL), 768 'maxbytes' => new external_value(PARAM_INT, 769 'largest size of file that can be uploaded into the course', 770 VALUE_OPTIONAL), 771 'showreports' => new external_value(PARAM_INT, 772 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 773 'visible' => new external_value(PARAM_INT, 774 '1: available to student, 0:not available', VALUE_OPTIONAL), 775 'hiddensections' => new external_value(PARAM_INT, 776 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', 777 VALUE_OPTIONAL), 778 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', 779 VALUE_OPTIONAL), 780 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', 781 VALUE_OPTIONAL), 782 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', 783 VALUE_OPTIONAL), 784 'timecreated' => new external_value(PARAM_INT, 785 'timestamp when the course have been created', VALUE_OPTIONAL), 786 'timemodified' => new external_value(PARAM_INT, 787 'timestamp when the course have been modified', VALUE_OPTIONAL), 788 'enablecompletion' => new external_value(PARAM_INT, 789 'Enabled, control via completion and activity settings. Disbaled, 790 not shown in activity settings.', 791 VALUE_OPTIONAL), 792 'completionnotify' => new external_value(PARAM_INT, 793 '1: yes 0: no', VALUE_OPTIONAL), 794 'lang' => new external_value(PARAM_SAFEDIR, 795 'forced course language', VALUE_OPTIONAL), 796 'forcetheme' => new external_value(PARAM_PLUGIN, 797 'name of the force theme', VALUE_OPTIONAL), 798 'courseformatoptions' => new external_multiple_structure( 799 new external_single_structure( 800 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 801 'value' => new external_value(PARAM_RAW, 'course format option value') 802 )), 'additional options for particular course format', VALUE_OPTIONAL 803 ), 804 'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'), 805 'showcompletionconditions' => new external_value(PARAM_BOOL, 806 'Whether the activity completion conditions are shown or not'), 807 'customfields' => new external_multiple_structure( 808 new external_single_structure( 809 ['name' => new external_value(PARAM_RAW, 'The name of the custom field'), 810 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 811 'type' => new external_value(PARAM_COMPONENT, 812 'The type of the custom field - text, checkbox...'), 813 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 814 'value' => new external_value(PARAM_RAW, 'The value of the custom field')] 815 ), 'Custom fields and associated values', VALUE_OPTIONAL), 816 ), 'course' 817 ) 818 ); 819 } 820 821 /** 822 * Returns description of method parameters 823 * 824 * @return external_function_parameters 825 * @since Moodle 2.2 826 */ 827 public static function create_courses_parameters() { 828 $courseconfig = get_config('moodlecourse'); //needed for many default values 829 return new external_function_parameters( 830 array( 831 'courses' => new external_multiple_structure( 832 new external_single_structure( 833 array( 834 'fullname' => new external_value(PARAM_TEXT, 'full name'), 835 'shortname' => new external_value(PARAM_TEXT, 'course short name'), 836 'categoryid' => new external_value(PARAM_INT, 'category id'), 837 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 838 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), 839 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT), 840 'format' => new external_value(PARAM_PLUGIN, 841 'course format: weeks, topics, social, site,..', 842 VALUE_DEFAULT, $courseconfig->format), 843 'showgrades' => new external_value(PARAM_INT, 844 '1 if grades are shown, otherwise 0', VALUE_DEFAULT, 845 $courseconfig->showgrades), 846 'newsitems' => new external_value(PARAM_INT, 847 'number of recent items appearing on the course page', 848 VALUE_DEFAULT, $courseconfig->newsitems), 849 'startdate' => new external_value(PARAM_INT, 850 'timestamp when the course start', VALUE_OPTIONAL), 851 'enddate' => new external_value(PARAM_INT, 852 'timestamp when the course end', VALUE_OPTIONAL), 853 'numsections' => new external_value(PARAM_INT, 854 '(deprecated, use courseformatoptions) number of weeks/topics', 855 VALUE_OPTIONAL), 856 'maxbytes' => new external_value(PARAM_INT, 857 'largest size of file that can be uploaded into the course', 858 VALUE_DEFAULT, $courseconfig->maxbytes), 859 'showreports' => new external_value(PARAM_INT, 860 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT, 861 $courseconfig->showreports), 862 'visible' => new external_value(PARAM_INT, 863 '1: available to student, 0:not available', VALUE_OPTIONAL), 864 'hiddensections' => new external_value(PARAM_INT, 865 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', 866 VALUE_OPTIONAL), 867 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', 868 VALUE_DEFAULT, $courseconfig->groupmode), 869 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', 870 VALUE_DEFAULT, $courseconfig->groupmodeforce), 871 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', 872 VALUE_DEFAULT, 0), 873 'enablecompletion' => new external_value(PARAM_INT, 874 'Enabled, control via completion and activity settings. Disabled, 875 not shown in activity settings.', 876 VALUE_OPTIONAL), 877 'completionnotify' => new external_value(PARAM_INT, 878 '1: yes 0: no', VALUE_OPTIONAL), 879 'lang' => new external_value(PARAM_SAFEDIR, 880 'forced course language', VALUE_OPTIONAL), 881 'forcetheme' => new external_value(PARAM_PLUGIN, 882 'name of the force theme', VALUE_OPTIONAL), 883 'courseformatoptions' => new external_multiple_structure( 884 new external_single_structure( 885 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 886 'value' => new external_value(PARAM_RAW, 'course format option value') 887 )), 888 'additional options for particular course format', VALUE_OPTIONAL), 889 'customfields' => new external_multiple_structure( 890 new external_single_structure( 891 array( 892 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 893 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), 894 )), 'custom fields for the course', VALUE_OPTIONAL 895 ) 896 )), 'courses to create' 897 ) 898 ) 899 ); 900 } 901 902 /** 903 * Create courses 904 * 905 * @param array $courses 906 * @return array courses (id and shortname only) 907 * @since Moodle 2.2 908 */ 909 public static function create_courses($courses) { 910 global $CFG, $DB; 911 require_once($CFG->dirroot . "/course/lib.php"); 912 require_once($CFG->libdir . '/completionlib.php'); 913 914 $params = self::validate_parameters(self::create_courses_parameters(), 915 array('courses' => $courses)); 916 917 $availablethemes = core_component::get_plugin_list('theme'); 918 $availablelangs = get_string_manager()->get_list_of_translations(); 919 920 $transaction = $DB->start_delegated_transaction(); 921 922 foreach ($params['courses'] as $course) { 923 924 // Ensure the current user is allowed to run this function 925 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING); 926 try { 927 self::validate_context($context); 928 } catch (Exception $e) { 929 $exceptionparam = new stdClass(); 930 $exceptionparam->message = $e->getMessage(); 931 $exceptionparam->catid = $course['categoryid']; 932 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); 933 } 934 require_capability('moodle/course:create', $context); 935 936 // Fullname and short name are required to be non-empty. 937 if (trim($course['fullname']) === '') { 938 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname'); 939 } else if (trim($course['shortname']) === '') { 940 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname'); 941 } 942 943 // Make sure lang is valid 944 if (array_key_exists('lang', $course)) { 945 if (empty($availablelangs[$course['lang']])) { 946 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); 947 } 948 if (!has_capability('moodle/course:setforcedlanguage', $context)) { 949 unset($course['lang']); 950 } 951 } 952 953 // Make sure theme is valid 954 if (array_key_exists('forcetheme', $course)) { 955 if (!empty($CFG->allowcoursethemes)) { 956 if (empty($availablethemes[$course['forcetheme']])) { 957 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); 958 } else { 959 $course['theme'] = $course['forcetheme']; 960 } 961 } 962 } 963 964 //force visibility if ws user doesn't have the permission to set it 965 $category = $DB->get_record('course_categories', array('id' => $course['categoryid'])); 966 if (!has_capability('moodle/course:visibility', $context)) { 967 $course['visible'] = $category->visible; 968 } 969 970 //set default value for completion 971 $courseconfig = get_config('moodlecourse'); 972 if (completion_info::is_enabled_for_site()) { 973 if (!array_key_exists('enablecompletion', $course)) { 974 $course['enablecompletion'] = $courseconfig->enablecompletion; 975 } 976 } else { 977 $course['enablecompletion'] = 0; 978 } 979 980 $course['category'] = $course['categoryid']; 981 982 // Summary format. 983 $course['summaryformat'] = util::validate_format($course['summaryformat']); 984 985 if (!empty($course['courseformatoptions'])) { 986 foreach ($course['courseformatoptions'] as $option) { 987 $course[$option['name']] = $option['value']; 988 } 989 } 990 991 // Custom fields. 992 if (!empty($course['customfields'])) { 993 foreach ($course['customfields'] as $field) { 994 $course['customfield_'.$field['shortname']] = $field['value']; 995 } 996 } 997 998 //Note: create_course() core function check shortname, idnumber, category 999 $course['id'] = create_course((object) $course)->id; 1000 1001 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']); 1002 } 1003 1004 $transaction->allow_commit(); 1005 1006 return $resultcourses; 1007 } 1008 1009 /** 1010 * Returns description of method result value 1011 * 1012 * @return \core_external\external_description 1013 * @since Moodle 2.2 1014 */ 1015 public static function create_courses_returns() { 1016 return new external_multiple_structure( 1017 new external_single_structure( 1018 array( 1019 'id' => new external_value(PARAM_INT, 'course id'), 1020 'shortname' => new external_value(PARAM_RAW, 'short name'), 1021 ) 1022 ) 1023 ); 1024 } 1025 1026 /** 1027 * Update courses 1028 * 1029 * @return external_function_parameters 1030 * @since Moodle 2.5 1031 */ 1032 public static function update_courses_parameters() { 1033 return new external_function_parameters( 1034 array( 1035 'courses' => new external_multiple_structure( 1036 new external_single_structure( 1037 array( 1038 'id' => new external_value(PARAM_INT, 'ID of the course'), 1039 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL), 1040 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL), 1041 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL), 1042 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 1043 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), 1044 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL), 1045 'format' => new external_value(PARAM_PLUGIN, 1046 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL), 1047 'showgrades' => new external_value(PARAM_INT, 1048 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 1049 'newsitems' => new external_value(PARAM_INT, 1050 'number of recent items appearing on the course page', VALUE_OPTIONAL), 1051 'startdate' => new external_value(PARAM_INT, 1052 'timestamp when the course start', VALUE_OPTIONAL), 1053 'enddate' => new external_value(PARAM_INT, 1054 'timestamp when the course end', VALUE_OPTIONAL), 1055 'numsections' => new external_value(PARAM_INT, 1056 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL), 1057 'maxbytes' => new external_value(PARAM_INT, 1058 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL), 1059 'showreports' => new external_value(PARAM_INT, 1060 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 1061 'visible' => new external_value(PARAM_INT, 1062 '1: available to student, 0:not available', VALUE_OPTIONAL), 1063 'hiddensections' => new external_value(PARAM_INT, 1064 '(deprecated, use courseformatoptions) How the hidden sections in the course are 1065 displayed to students', VALUE_OPTIONAL), 1066 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 1067 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 1068 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 1069 'enablecompletion' => new external_value(PARAM_INT, 1070 'Enabled, control via completion and activity settings. Disabled, 1071 not shown in activity settings.', VALUE_OPTIONAL), 1072 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 1073 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL), 1074 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL), 1075 'courseformatoptions' => new external_multiple_structure( 1076 new external_single_structure( 1077 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 1078 'value' => new external_value(PARAM_RAW, 'course format option value') 1079 )), 'additional options for particular course format', VALUE_OPTIONAL), 1080 'customfields' => new external_multiple_structure( 1081 new external_single_structure( 1082 [ 1083 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 1084 'value' => new external_value(PARAM_RAW, 'The value of the custom field') 1085 ] 1086 ), 'Custom fields', VALUE_OPTIONAL), 1087 ) 1088 ), 'courses to update' 1089 ) 1090 ) 1091 ); 1092 } 1093 1094 /** 1095 * Update courses 1096 * 1097 * @param array $courses 1098 * @since Moodle 2.5 1099 */ 1100 public static function update_courses($courses) { 1101 global $CFG, $DB; 1102 require_once($CFG->dirroot . "/course/lib.php"); 1103 $warnings = array(); 1104 1105 $params = self::validate_parameters(self::update_courses_parameters(), 1106 array('courses' => $courses)); 1107 1108 $availablethemes = core_component::get_plugin_list('theme'); 1109 $availablelangs = get_string_manager()->get_list_of_translations(); 1110 1111 foreach ($params['courses'] as $course) { 1112 // Catch any exception while updating course and return as warning to user. 1113 try { 1114 // Ensure the current user is allowed to run this function. 1115 $context = context_course::instance($course['id'], MUST_EXIST); 1116 self::validate_context($context); 1117 1118 $oldcourse = course_get_format($course['id'])->get_course(); 1119 1120 require_capability('moodle/course:update', $context); 1121 1122 // Check if user can change category. 1123 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) { 1124 require_capability('moodle/course:changecategory', $context); 1125 $course['category'] = $course['categoryid']; 1126 } 1127 1128 // Check if the user can change fullname, and the new value is non-empty. 1129 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) { 1130 require_capability('moodle/course:changefullname', $context); 1131 if (trim($course['fullname']) === '') { 1132 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname'); 1133 } 1134 } 1135 1136 // Check if the user can change shortname, and the new value is non-empty. 1137 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) { 1138 require_capability('moodle/course:changeshortname', $context); 1139 if (trim($course['shortname']) === '') { 1140 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname'); 1141 } 1142 } 1143 1144 // Check if the user can change the idnumber. 1145 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) { 1146 require_capability('moodle/course:changeidnumber', $context); 1147 } 1148 1149 // Check if user can change summary. 1150 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) { 1151 require_capability('moodle/course:changesummary', $context); 1152 } 1153 1154 // Summary format. 1155 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) { 1156 require_capability('moodle/course:changesummary', $context); 1157 $course['summaryformat'] = util::validate_format($course['summaryformat']); 1158 } 1159 1160 // Check if user can change visibility. 1161 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) { 1162 require_capability('moodle/course:visibility', $context); 1163 } 1164 1165 // Make sure lang is valid. 1166 if (array_key_exists('lang', $course) && ($oldcourse->lang != $course['lang'])) { 1167 require_capability('moodle/course:setforcedlanguage', $context); 1168 if (empty($availablelangs[$course['lang']])) { 1169 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); 1170 } 1171 } 1172 1173 // Make sure theme is valid. 1174 if (array_key_exists('forcetheme', $course)) { 1175 if (!empty($CFG->allowcoursethemes)) { 1176 if (empty($availablethemes[$course['forcetheme']])) { 1177 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); 1178 } else { 1179 $course['theme'] = $course['forcetheme']; 1180 } 1181 } 1182 } 1183 1184 // Make sure completion is enabled before setting it. 1185 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) { 1186 $course['enabledcompletion'] = 0; 1187 } 1188 1189 // Make sure maxbytes are less then CFG->maxbytes. 1190 if (array_key_exists('maxbytes', $course)) { 1191 // We allow updates back to 0 max bytes, a special value denoting the course uses the site limit. 1192 // Otherwise, either use the size specified, or cap at the max size for the course. 1193 if ($course['maxbytes'] != 0) { 1194 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']); 1195 } 1196 } 1197 1198 if (!empty($course['courseformatoptions'])) { 1199 foreach ($course['courseformatoptions'] as $option) { 1200 if (isset($option['name']) && isset($option['value'])) { 1201 $course[$option['name']] = $option['value']; 1202 } 1203 } 1204 } 1205 1206 // Prepare list of custom fields. 1207 if (isset($course['customfields'])) { 1208 foreach ($course['customfields'] as $field) { 1209 $course['customfield_' . $field['shortname']] = $field['value']; 1210 } 1211 } 1212 1213 // Update course if user has all required capabilities. 1214 update_course((object) $course); 1215 } catch (Exception $e) { 1216 $warning = array(); 1217 $warning['item'] = 'course'; 1218 $warning['itemid'] = $course['id']; 1219 if ($e instanceof moodle_exception) { 1220 $warning['warningcode'] = $e->errorcode; 1221 } else { 1222 $warning['warningcode'] = $e->getCode(); 1223 } 1224 $warning['message'] = $e->getMessage(); 1225 $warnings[] = $warning; 1226 } 1227 } 1228 1229 $result = array(); 1230 $result['warnings'] = $warnings; 1231 return $result; 1232 } 1233 1234 /** 1235 * Returns description of method result value 1236 * 1237 * @return \core_external\external_description 1238 * @since Moodle 2.5 1239 */ 1240 public static function update_courses_returns() { 1241 return new external_single_structure( 1242 array( 1243 'warnings' => new external_warnings() 1244 ) 1245 ); 1246 } 1247 1248 /** 1249 * Returns description of method parameters 1250 * 1251 * @return external_function_parameters 1252 * @since Moodle 2.2 1253 */ 1254 public static function delete_courses_parameters() { 1255 return new external_function_parameters( 1256 array( 1257 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')), 1258 ) 1259 ); 1260 } 1261 1262 /** 1263 * Delete courses 1264 * 1265 * @param array $courseids A list of course ids 1266 * @since Moodle 2.2 1267 */ 1268 public static function delete_courses($courseids) { 1269 global $CFG, $DB; 1270 require_once($CFG->dirroot."/course/lib.php"); 1271 1272 // Parameter validation. 1273 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids)); 1274 1275 $warnings = array(); 1276 1277 foreach ($params['courseids'] as $courseid) { 1278 $course = $DB->get_record('course', array('id' => $courseid)); 1279 1280 if ($course === false) { 1281 $warnings[] = array( 1282 'item' => 'course', 1283 'itemid' => $courseid, 1284 'warningcode' => 'unknowncourseidnumber', 1285 'message' => 'Unknown course ID ' . $courseid 1286 ); 1287 continue; 1288 } 1289 1290 // Check if the context is valid. 1291 $coursecontext = context_course::instance($course->id); 1292 self::validate_context($coursecontext); 1293 1294 // Check if the current user has permission. 1295 if (!can_delete_course($courseid)) { 1296 $warnings[] = array( 1297 'item' => 'course', 1298 'itemid' => $courseid, 1299 'warningcode' => 'cannotdeletecourse', 1300 'message' => 'You do not have the permission to delete this course' . $courseid 1301 ); 1302 continue; 1303 } 1304 1305 if (delete_course($course, false) === false) { 1306 $warnings[] = array( 1307 'item' => 'course', 1308 'itemid' => $courseid, 1309 'warningcode' => 'cannotdeletecategorycourse', 1310 'message' => 'Course ' . $courseid . ' failed to be deleted' 1311 ); 1312 continue; 1313 } 1314 } 1315 1316 fix_course_sortorder(); 1317 1318 return array('warnings' => $warnings); 1319 } 1320 1321 /** 1322 * Returns description of method result value 1323 * 1324 * @return \core_external\external_description 1325 * @since Moodle 2.2 1326 */ 1327 public static function delete_courses_returns() { 1328 return new external_single_structure( 1329 array( 1330 'warnings' => new external_warnings() 1331 ) 1332 ); 1333 } 1334 1335 /** 1336 * Returns description of method parameters 1337 * 1338 * @return external_function_parameters 1339 * @since Moodle 2.3 1340 */ 1341 public static function duplicate_course_parameters() { 1342 return new external_function_parameters( 1343 array( 1344 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'), 1345 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'), 1346 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'), 1347 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'), 1348 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1), 1349 'options' => new external_multiple_structure( 1350 new external_single_structure( 1351 array( 1352 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name: 1353 "activities" (int) Include course activites (default to 1 that is equal to yes), 1354 "blocks" (int) Include course blocks (default to 1 that is equal to yes), 1355 "filters" (int) Include course filters (default to 1 that is equal to yes), 1356 "users" (int) Include users (default to 0 that is equal to no), 1357 "enrolments" (int) Include enrolment methods (default to 1 - restore only with users), 1358 "role_assignments" (int) Include role assignments (default to 0 that is equal to no), 1359 "comments" (int) Include user comments (default to 0 that is equal to no), 1360 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no), 1361 "logs" (int) Include course logs (default to 0 that is equal to no), 1362 "grade_histories" (int) Include histories (default to 0 that is equal to no)' 1363 ), 1364 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' 1365 ) 1366 ) 1367 ), 'Course duplication options', VALUE_DEFAULT, array() 1368 ), 1369 ) 1370 ); 1371 } 1372 1373 /** 1374 * Duplicate a course 1375 * 1376 * @param int $courseid 1377 * @param string $fullname Duplicated course fullname 1378 * @param string $shortname Duplicated course shortname 1379 * @param int $categoryid Duplicated course parent category id 1380 * @param int $visible Duplicated course availability 1381 * @param array $options List of backup options 1382 * @return array New course info 1383 * @since Moodle 2.3 1384 */ 1385 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) { 1386 global $CFG, $USER, $DB; 1387 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1388 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1389 1390 // Parameter validation. 1391 $params = self::validate_parameters( 1392 self::duplicate_course_parameters(), 1393 array( 1394 'courseid' => $courseid, 1395 'fullname' => $fullname, 1396 'shortname' => $shortname, 1397 'categoryid' => $categoryid, 1398 'visible' => $visible, 1399 'options' => $options 1400 ) 1401 ); 1402 1403 // Context validation. 1404 1405 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) { 1406 throw new moodle_exception('invalidcourseid', 'error'); 1407 } 1408 1409 // Category where duplicated course is going to be created. 1410 $categorycontext = context_coursecat::instance($params['categoryid']); 1411 self::validate_context($categorycontext); 1412 1413 // Course to be duplicated. 1414 $coursecontext = context_course::instance($course->id); 1415 self::validate_context($coursecontext); 1416 1417 $backupdefaults = array( 1418 'activities' => 1, 1419 'blocks' => 1, 1420 'filters' => 1, 1421 'users' => 0, 1422 'enrolments' => backup::ENROL_WITHUSERS, 1423 'role_assignments' => 0, 1424 'comments' => 0, 1425 'userscompletion' => 0, 1426 'logs' => 0, 1427 'grade_histories' => 0 1428 ); 1429 1430 $backupsettings = array(); 1431 // Check for backup and restore options. 1432 if (!empty($params['options'])) { 1433 foreach ($params['options'] as $option) { 1434 1435 // Strict check for a correct value (allways 1 or 0, true or false). 1436 $value = clean_param($option['value'], PARAM_INT); 1437 1438 if ($value !== 0 and $value !== 1) { 1439 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1440 } 1441 1442 if (!isset($backupdefaults[$option['name']])) { 1443 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1444 } 1445 1446 $backupsettings[$option['name']] = $value; 1447 } 1448 } 1449 1450 // Capability checking. 1451 1452 // The backup controller check for this currently, this may be redundant. 1453 require_capability('moodle/course:create', $categorycontext); 1454 require_capability('moodle/restore:restorecourse', $categorycontext); 1455 require_capability('moodle/backup:backupcourse', $coursecontext); 1456 1457 if (!empty($backupsettings['users'])) { 1458 require_capability('moodle/backup:userinfo', $coursecontext); 1459 require_capability('moodle/restore:userinfo', $categorycontext); 1460 } 1461 1462 // Check if the shortname is used. 1463 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) { 1464 foreach ($foundcourses as $foundcourse) { 1465 $foundcoursenames[] = $foundcourse->fullname; 1466 } 1467 1468 $foundcoursenamestring = implode(',', $foundcoursenames); 1469 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring); 1470 } 1471 1472 // Backup the course. 1473 1474 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 1475 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id); 1476 1477 foreach ($backupsettings as $name => $value) { 1478 if ($setting = $bc->get_plan()->get_setting($name)) { 1479 $bc->get_plan()->get_setting($name)->set_value($value); 1480 } 1481 } 1482 1483 $backupid = $bc->get_backupid(); 1484 $backupbasepath = $bc->get_plan()->get_basepath(); 1485 1486 $bc->execute_plan(); 1487 $results = $bc->get_results(); 1488 $file = $results['backup_destination']; 1489 1490 $bc->destroy(); 1491 1492 // Restore the backup immediately. 1493 1494 // Check if we need to unzip the file because the backup temp dir does not contains backup files. 1495 if (!file_exists($backupbasepath . "/moodle_backup.xml")) { 1496 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath); 1497 } 1498 1499 // Create new course. 1500 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']); 1501 1502 $rc = new restore_controller($backupid, $newcourseid, 1503 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE); 1504 1505 foreach ($backupsettings as $name => $value) { 1506 $setting = $rc->get_plan()->get_setting($name); 1507 if ($setting->get_status() == backup_setting::NOT_LOCKED) { 1508 $setting->set_value($value); 1509 } 1510 } 1511 1512 if (!$rc->execute_precheck()) { 1513 $precheckresults = $rc->get_precheck_results(); 1514 if (is_array($precheckresults) && !empty($precheckresults['errors'])) { 1515 if (empty($CFG->keeptempdirectoriesonbackup)) { 1516 fulldelete($backupbasepath); 1517 } 1518 1519 $errorinfo = ''; 1520 1521 foreach ($precheckresults['errors'] as $error) { 1522 $errorinfo .= $error; 1523 } 1524 1525 if (array_key_exists('warnings', $precheckresults)) { 1526 foreach ($precheckresults['warnings'] as $warning) { 1527 $errorinfo .= $warning; 1528 } 1529 } 1530 1531 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); 1532 } 1533 } 1534 1535 $rc->execute_plan(); 1536 $rc->destroy(); 1537 1538 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST); 1539 $course->fullname = $params['fullname']; 1540 $course->shortname = $params['shortname']; 1541 $course->visible = $params['visible']; 1542 1543 // Set shortname and fullname back. 1544 $DB->update_record('course', $course); 1545 1546 if (empty($CFG->keeptempdirectoriesonbackup)) { 1547 fulldelete($backupbasepath); 1548 } 1549 1550 // Delete the course backup file created by this WebService. Originally located in the course backups area. 1551 $file->delete(); 1552 1553 return array('id' => $course->id, 'shortname' => $course->shortname); 1554 } 1555 1556 /** 1557 * Returns description of method result value 1558 * 1559 * @return \core_external\external_description 1560 * @since Moodle 2.3 1561 */ 1562 public static function duplicate_course_returns() { 1563 return new external_single_structure( 1564 array( 1565 'id' => new external_value(PARAM_INT, 'course id'), 1566 'shortname' => new external_value(PARAM_RAW, 'short name'), 1567 ) 1568 ); 1569 } 1570 1571 /** 1572 * Returns description of method parameters for import_course 1573 * 1574 * @return external_function_parameters 1575 * @since Moodle 2.4 1576 */ 1577 public static function import_course_parameters() { 1578 return new external_function_parameters( 1579 array( 1580 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'), 1581 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'), 1582 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0), 1583 'options' => new external_multiple_structure( 1584 new external_single_structure( 1585 array( 1586 'name' => new external_value(PARAM_ALPHA, 'The backup option name: 1587 "activities" (int) Include course activites (default to 1 that is equal to yes), 1588 "blocks" (int) Include course blocks (default to 1 that is equal to yes), 1589 "filters" (int) Include course filters (default to 1 that is equal to yes)' 1590 ), 1591 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' 1592 ) 1593 ) 1594 ), 'Course import options', VALUE_DEFAULT, array() 1595 ), 1596 ) 1597 ); 1598 } 1599 1600 /** 1601 * Imports a course 1602 * 1603 * @param int $importfrom The id of the course we are importing from 1604 * @param int $importto The id of the course we are importing to 1605 * @param bool $deletecontent Whether to delete the course we are importing to content 1606 * @param array $options List of backup options 1607 * @return null 1608 * @since Moodle 2.4 1609 */ 1610 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) { 1611 global $CFG, $USER, $DB; 1612 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1613 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1614 1615 // Parameter validation. 1616 $params = self::validate_parameters( 1617 self::import_course_parameters(), 1618 array( 1619 'importfrom' => $importfrom, 1620 'importto' => $importto, 1621 'deletecontent' => $deletecontent, 1622 'options' => $options 1623 ) 1624 ); 1625 1626 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) { 1627 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']); 1628 } 1629 1630 // Context validation. 1631 1632 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) { 1633 throw new moodle_exception('invalidcourseid', 'error'); 1634 } 1635 1636 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) { 1637 throw new moodle_exception('invalidcourseid', 'error'); 1638 } 1639 1640 $importfromcontext = context_course::instance($importfrom->id); 1641 self::validate_context($importfromcontext); 1642 1643 $importtocontext = context_course::instance($importto->id); 1644 self::validate_context($importtocontext); 1645 1646 $backupdefaults = array( 1647 'activities' => 1, 1648 'blocks' => 1, 1649 'filters' => 1 1650 ); 1651 1652 $backupsettings = array(); 1653 1654 // Check for backup and restore options. 1655 if (!empty($params['options'])) { 1656 foreach ($params['options'] as $option) { 1657 1658 // Strict check for a correct value (allways 1 or 0, true or false). 1659 $value = clean_param($option['value'], PARAM_INT); 1660 1661 if ($value !== 0 and $value !== 1) { 1662 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1663 } 1664 1665 if (!isset($backupdefaults[$option['name']])) { 1666 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1667 } 1668 1669 $backupsettings[$option['name']] = $value; 1670 } 1671 } 1672 1673 // Capability checking. 1674 1675 require_capability('moodle/backup:backuptargetimport', $importfromcontext); 1676 require_capability('moodle/restore:restoretargetimport', $importtocontext); 1677 1678 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE, 1679 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); 1680 1681 foreach ($backupsettings as $name => $value) { 1682 $bc->get_plan()->get_setting($name)->set_value($value); 1683 } 1684 1685 $backupid = $bc->get_backupid(); 1686 $backupbasepath = $bc->get_plan()->get_basepath(); 1687 1688 $bc->execute_plan(); 1689 $bc->destroy(); 1690 1691 // Restore the backup immediately. 1692 1693 // Check if we must delete the contents of the destination course. 1694 if ($params['deletecontent']) { 1695 $restoretarget = backup::TARGET_EXISTING_DELETING; 1696 } else { 1697 $restoretarget = backup::TARGET_EXISTING_ADDING; 1698 } 1699 1700 $rc = new restore_controller($backupid, $importto->id, 1701 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget); 1702 1703 foreach ($backupsettings as $name => $value) { 1704 $rc->get_plan()->get_setting($name)->set_value($value); 1705 } 1706 1707 if (!$rc->execute_precheck()) { 1708 $precheckresults = $rc->get_precheck_results(); 1709 if (is_array($precheckresults) && !empty($precheckresults['errors'])) { 1710 if (empty($CFG->keeptempdirectoriesonbackup)) { 1711 fulldelete($backupbasepath); 1712 } 1713 1714 $errorinfo = ''; 1715 1716 foreach ($precheckresults['errors'] as $error) { 1717 $errorinfo .= $error; 1718 } 1719 1720 if (array_key_exists('warnings', $precheckresults)) { 1721 foreach ($precheckresults['warnings'] as $warning) { 1722 $errorinfo .= $warning; 1723 } 1724 } 1725 1726 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); 1727 } 1728 } else { 1729 if ($restoretarget == backup::TARGET_EXISTING_DELETING) { 1730 restore_dbops::delete_course_content($importto->id); 1731 } 1732 } 1733 1734 $rc->execute_plan(); 1735 $rc->destroy(); 1736 1737 if (empty($CFG->keeptempdirectoriesonbackup)) { 1738 fulldelete($backupbasepath); 1739 } 1740 1741 return null; 1742 } 1743 1744 /** 1745 * Returns description of method result value 1746 * 1747 * @return \core_external\external_description 1748 * @since Moodle 2.4 1749 */ 1750 public static function import_course_returns() { 1751 return null; 1752 } 1753 1754 /** 1755 * Returns description of method parameters 1756 * 1757 * @return external_function_parameters 1758 * @since Moodle 2.3 1759 */ 1760 public static function get_categories_parameters() { 1761 return new external_function_parameters( 1762 array( 1763 'criteria' => new external_multiple_structure( 1764 new external_single_structure( 1765 array( 1766 'key' => new external_value(PARAM_ALPHA, 1767 'The category column to search, expected keys (value format) are:'. 1768 '"id" (int) the category id,'. 1769 '"ids" (string) category ids separated by commas,'. 1770 '"name" (string) the category name,'. 1771 '"parent" (int) the parent category id,'. 1772 '"idnumber" (string) category idnumber'. 1773 ' - user must have \'moodle/category:manage\' to search on idnumber,'. 1774 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed, 1775 then the function return all categories that the user can see.'. 1776 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'. 1777 '"theme" (string) only return the categories having this theme'. 1778 ' - user must have \'moodle/category:manage\' to search on theme'), 1779 'value' => new external_value(PARAM_RAW, 'the value to match') 1780 ) 1781 ), 'criteria', VALUE_DEFAULT, array() 1782 ), 1783 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos 1784 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1) 1785 ) 1786 ); 1787 } 1788 1789 /** 1790 * Get categories 1791 * 1792 * @param array $criteria Criteria to match the results 1793 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default) 1794 * @return array list of categories 1795 * @since Moodle 2.3 1796 */ 1797 public static function get_categories($criteria = array(), $addsubcategories = true) { 1798 global $CFG, $DB; 1799 require_once($CFG->dirroot . "/course/lib.php"); 1800 1801 // Validate parameters. 1802 $params = self::validate_parameters(self::get_categories_parameters(), 1803 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories)); 1804 1805 // Retrieve the categories. 1806 $categories = array(); 1807 if (!empty($params['criteria'])) { 1808 1809 $conditions = array(); 1810 $wheres = array(); 1811 foreach ($params['criteria'] as $crit) { 1812 $key = trim($crit['key']); 1813 1814 // Trying to avoid duplicate keys. 1815 if (!isset($conditions[$key])) { 1816 1817 $context = context_system::instance(); 1818 $value = null; 1819 switch ($key) { 1820 case 'id': 1821 $value = clean_param($crit['value'], PARAM_INT); 1822 $conditions[$key] = $value; 1823 $wheres[] = $key . " = :" . $key; 1824 break; 1825 1826 case 'ids': 1827 $value = clean_param($crit['value'], PARAM_SEQUENCE); 1828 $ids = explode(',', $value); 1829 list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); 1830 $conditions = array_merge($conditions, $paramids); 1831 $wheres[] = 'id ' . $sqlids; 1832 break; 1833 1834 case 'idnumber': 1835 if (has_capability('moodle/category:manage', $context)) { 1836 $value = clean_param($crit['value'], PARAM_RAW); 1837 $conditions[$key] = $value; 1838 $wheres[] = $key . " = :" . $key; 1839 } else { 1840 // We must throw an exception. 1841 // Otherwise the dev client would think no idnumber exists. 1842 throw new moodle_exception('criteriaerror', 1843 'webservice', '', null, 1844 'You don\'t have the permissions to search on the "idnumber" field.'); 1845 } 1846 break; 1847 1848 case 'name': 1849 $value = clean_param($crit['value'], PARAM_TEXT); 1850 $conditions[$key] = $value; 1851 $wheres[] = $key . " = :" . $key; 1852 break; 1853 1854 case 'parent': 1855 $value = clean_param($crit['value'], PARAM_INT); 1856 $conditions[$key] = $value; 1857 $wheres[] = $key . " = :" . $key; 1858 break; 1859 1860 case 'visible': 1861 if (has_capability('moodle/category:viewhiddencategories', $context)) { 1862 $value = clean_param($crit['value'], PARAM_INT); 1863 $conditions[$key] = $value; 1864 $wheres[] = $key . " = :" . $key; 1865 } else { 1866 throw new moodle_exception('criteriaerror', 1867 'webservice', '', null, 1868 'You don\'t have the permissions to search on the "visible" field.'); 1869 } 1870 break; 1871 1872 case 'theme': 1873 if (has_capability('moodle/category:manage', $context)) { 1874 $value = clean_param($crit['value'], PARAM_THEME); 1875 $conditions[$key] = $value; 1876 $wheres[] = $key . " = :" . $key; 1877 } else { 1878 throw new moodle_exception('criteriaerror', 1879 'webservice', '', null, 1880 'You don\'t have the permissions to search on the "theme" field.'); 1881 } 1882 break; 1883 1884 default: 1885 throw new moodle_exception('criteriaerror', 1886 'webservice', '', null, 1887 'You can not search on this criteria: ' . $key); 1888 } 1889 } 1890 } 1891 1892 if (!empty($wheres)) { 1893 $wheres = implode(" AND ", $wheres); 1894 1895 $categories = $DB->get_records_select('course_categories', $wheres, $conditions); 1896 1897 // Retrieve its sub subcategories (all levels). 1898 if ($categories and !empty($params['addsubcategories'])) { 1899 $newcategories = array(); 1900 1901 // Check if we required visible/theme checks. 1902 $additionalselect = ''; 1903 $additionalparams = array(); 1904 if (isset($conditions['visible'])) { 1905 $additionalselect .= ' AND visible = :visible'; 1906 $additionalparams['visible'] = $conditions['visible']; 1907 } 1908 if (isset($conditions['theme'])) { 1909 $additionalselect .= ' AND theme= :theme'; 1910 $additionalparams['theme'] = $conditions['theme']; 1911 } 1912 1913 foreach ($categories as $category) { 1914 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect; 1915 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category. 1916 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams); 1917 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys. 1918 } 1919 $categories = $categories + $newcategories; 1920 } 1921 } 1922 1923 } else { 1924 // Retrieve all categories in the database. 1925 $categories = $DB->get_records('course_categories'); 1926 } 1927 1928 // The not returned categories. key => category id, value => reason of exclusion. 1929 $excludedcats = array(); 1930 1931 // The returned categories. 1932 $categoriesinfo = array(); 1933 1934 // We need to sort the categories by path. 1935 // The parent cats need to be checked by the algo first. 1936 usort($categories, "core_course_external::compare_categories_by_path"); 1937 1938 foreach ($categories as $category) { 1939 1940 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return). 1941 $parents = explode('/', $category->path); 1942 unset($parents[0]); // First key is always empty because path start with / => /1/2/4. 1943 foreach ($parents as $parentid) { 1944 // Note: when the parent exclusion was due to the context, 1945 // the sub category could still be returned. 1946 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') { 1947 $excludedcats[$category->id] = 'parent'; 1948 } 1949 } 1950 1951 // Check the user can use the category context. 1952 $context = context_coursecat::instance($category->id); 1953 try { 1954 self::validate_context($context); 1955 } catch (Exception $e) { 1956 $excludedcats[$category->id] = 'context'; 1957 1958 // If it was the requested category then throw an exception. 1959 if (isset($params['categoryid']) && $category->id == $params['categoryid']) { 1960 $exceptionparam = new stdClass(); 1961 $exceptionparam->message = $e->getMessage(); 1962 $exceptionparam->catid = $category->id; 1963 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); 1964 } 1965 } 1966 1967 // Return the category information. 1968 if (!isset($excludedcats[$category->id])) { 1969 1970 // Final check to see if the category is visible to the user. 1971 if (core_course_category::can_view_category($category)) { 1972 1973 $categoryinfo = array(); 1974 $categoryinfo['id'] = $category->id; 1975 $categoryinfo['name'] = \core_external\util::format_string($category->name, $context); 1976 list($categoryinfo['description'], $categoryinfo['descriptionformat']) = 1977 \core_external\util::format_text($category->description, $category->descriptionformat, 1978 $context, 'coursecat', 'description', null); 1979 $categoryinfo['parent'] = $category->parent; 1980 $categoryinfo['sortorder'] = $category->sortorder; 1981 $categoryinfo['coursecount'] = $category->coursecount; 1982 $categoryinfo['depth'] = $category->depth; 1983 $categoryinfo['path'] = $category->path; 1984 1985 // Some fields only returned for admin. 1986 if (has_capability('moodle/category:manage', $context)) { 1987 $categoryinfo['idnumber'] = $category->idnumber; 1988 $categoryinfo['visible'] = $category->visible; 1989 $categoryinfo['visibleold'] = $category->visibleold; 1990 $categoryinfo['timemodified'] = $category->timemodified; 1991 $categoryinfo['theme'] = clean_param($category->theme, PARAM_THEME); 1992 } 1993 1994 $categoriesinfo[] = $categoryinfo; 1995 } else { 1996 $excludedcats[$category->id] = 'visibility'; 1997 } 1998 } 1999 } 2000 2001 // Sorting the resulting array so it looks a bit better for the client developer. 2002 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder"); 2003 2004 return $categoriesinfo; 2005 } 2006 2007 /** 2008 * Sort categories array by path 2009 * private function: only used by get_categories 2010 * 2011 * @param stdClass $category1 2012 * @param stdClass $category2 2013 * @return int result of strcmp 2014 * @since Moodle 2.3 2015 */ 2016 private static function compare_categories_by_path($category1, $category2) { 2017 return strcmp($category1->path, $category2->path); 2018 } 2019 2020 /** 2021 * Sort categories array by sortorder 2022 * private function: only used by get_categories 2023 * 2024 * @param array $category1 2025 * @param array $category2 2026 * @return int result of strcmp 2027 * @since Moodle 2.3 2028 */ 2029 private static function compare_categories_by_sortorder($category1, $category2) { 2030 return strcmp($category1['sortorder'], $category2['sortorder']); 2031 } 2032 2033 /** 2034 * Returns description of method result value 2035 * 2036 * @return \core_external\external_description 2037 * @since Moodle 2.3 2038 */ 2039 public static function get_categories_returns() { 2040 return new external_multiple_structure( 2041 new external_single_structure( 2042 array( 2043 'id' => new external_value(PARAM_INT, 'category id'), 2044 'name' => new external_value(PARAM_RAW, 'category name'), 2045 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 2046 'description' => new external_value(PARAM_RAW, 'category description'), 2047 'descriptionformat' => new external_format_value('description'), 2048 'parent' => new external_value(PARAM_INT, 'parent category id'), 2049 'sortorder' => new external_value(PARAM_INT, 'category sorting order'), 2050 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'), 2051 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 2052 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 2053 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL), 2054 'depth' => new external_value(PARAM_INT, 'category depth'), 2055 'path' => new external_value(PARAM_TEXT, 'category path'), 2056 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL), 2057 ), 'List of categories' 2058 ) 2059 ); 2060 } 2061 2062 /** 2063 * Returns description of method parameters 2064 * 2065 * @return external_function_parameters 2066 * @since Moodle 2.3 2067 */ 2068 public static function create_categories_parameters() { 2069 return new external_function_parameters( 2070 array( 2071 'categories' => new external_multiple_structure( 2072 new external_single_structure( 2073 array( 2074 'name' => new external_value(PARAM_TEXT, 'new category name'), 2075 'parent' => new external_value(PARAM_INT, 2076 'the parent category id inside which the new category will be created 2077 - set to 0 for a root category', 2078 VALUE_DEFAULT, 0), 2079 'idnumber' => new external_value(PARAM_RAW, 2080 'the new category idnumber', VALUE_OPTIONAL), 2081 'description' => new external_value(PARAM_RAW, 2082 'the new category description', VALUE_OPTIONAL), 2083 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 2084 'theme' => new external_value(PARAM_THEME, 2085 'the new category theme. This option must be enabled on moodle', 2086 VALUE_OPTIONAL), 2087 ) 2088 ) 2089 ) 2090 ) 2091 ); 2092 } 2093 2094 /** 2095 * Create categories 2096 * 2097 * @param array $categories - see create_categories_parameters() for the array structure 2098 * @return array - see create_categories_returns() for the array structure 2099 * @since Moodle 2.3 2100 */ 2101 public static function create_categories($categories) { 2102 global $DB; 2103 2104 $params = self::validate_parameters(self::create_categories_parameters(), 2105 array('categories' => $categories)); 2106 2107 $transaction = $DB->start_delegated_transaction(); 2108 2109 $createdcategories = array(); 2110 foreach ($params['categories'] as $category) { 2111 if ($category['parent']) { 2112 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) { 2113 throw new moodle_exception('unknowcategory'); 2114 } 2115 $context = context_coursecat::instance($category['parent']); 2116 } else { 2117 $context = context_system::instance(); 2118 } 2119 self::validate_context($context); 2120 require_capability('moodle/category:manage', $context); 2121 2122 // this will validate format and throw an exception if there are errors 2123 util::validate_format($category['descriptionformat']); 2124 2125 $newcategory = core_course_category::create($category); 2126 $context = context_coursecat::instance($newcategory->id); 2127 2128 $createdcategories[] = array( 2129 'id' => $newcategory->id, 2130 'name' => \core_external\util::format_string($newcategory->name, $context), 2131 ); 2132 } 2133 2134 $transaction->allow_commit(); 2135 2136 return $createdcategories; 2137 } 2138 2139 /** 2140 * Returns description of method parameters 2141 * 2142 * @return external_function_parameters 2143 * @since Moodle 2.3 2144 */ 2145 public static function create_categories_returns() { 2146 return new external_multiple_structure( 2147 new external_single_structure( 2148 array( 2149 'id' => new external_value(PARAM_INT, 'new category id'), 2150 'name' => new external_value(PARAM_RAW, 'new category name'), 2151 ) 2152 ) 2153 ); 2154 } 2155 2156 /** 2157 * Returns description of method parameters 2158 * 2159 * @return external_function_parameters 2160 * @since Moodle 2.3 2161 */ 2162 public static function update_categories_parameters() { 2163 return new external_function_parameters( 2164 array( 2165 'categories' => new external_multiple_structure( 2166 new external_single_structure( 2167 array( 2168 'id' => new external_value(PARAM_INT, 'course id'), 2169 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL), 2170 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 2171 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL), 2172 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL), 2173 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 2174 'theme' => new external_value(PARAM_THEME, 2175 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL), 2176 ) 2177 ) 2178 ) 2179 ) 2180 ); 2181 } 2182 2183 /** 2184 * Update categories 2185 * 2186 * @param array $categories The list of categories to update 2187 * @return null 2188 * @since Moodle 2.3 2189 */ 2190 public static function update_categories($categories) { 2191 global $DB; 2192 2193 // Validate parameters. 2194 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories)); 2195 2196 $transaction = $DB->start_delegated_transaction(); 2197 2198 foreach ($params['categories'] as $cat) { 2199 $category = core_course_category::get($cat['id']); 2200 2201 $categorycontext = context_coursecat::instance($cat['id']); 2202 self::validate_context($categorycontext); 2203 require_capability('moodle/category:manage', $categorycontext); 2204 2205 // If the category parent is being changed, check for capability in the new parent category 2206 if (isset($cat['parent']) && ($cat['parent'] !== $category->parent)) { 2207 if ($cat['parent'] == 0) { 2208 // Creating a top level category requires capability in the system context 2209 $parentcontext = context_system::instance(); 2210 } else { 2211 // Category context 2212 $parentcontext = context_coursecat::instance($cat['parent']); 2213 } 2214 self::validate_context($parentcontext); 2215 require_capability('moodle/category:manage', $parentcontext); 2216 } 2217 2218 // this will throw an exception if descriptionformat is not valid 2219 util::validate_format($cat['descriptionformat']); 2220 2221 $category->update($cat); 2222 } 2223 2224 $transaction->allow_commit(); 2225 } 2226 2227 /** 2228 * Returns description of method result value 2229 * 2230 * @return \core_external\external_description 2231 * @since Moodle 2.3 2232 */ 2233 public static function update_categories_returns() { 2234 return null; 2235 } 2236 2237 /** 2238 * Returns description of method parameters 2239 * 2240 * @return external_function_parameters 2241 * @since Moodle 2.3 2242 */ 2243 public static function delete_categories_parameters() { 2244 return new external_function_parameters( 2245 array( 2246 'categories' => new external_multiple_structure( 2247 new external_single_structure( 2248 array( 2249 'id' => new external_value(PARAM_INT, 'category id to delete'), 2250 'newparent' => new external_value(PARAM_INT, 2251 'the parent category to move the contents to, if specified', VALUE_OPTIONAL), 2252 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this 2253 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0) 2254 ) 2255 ) 2256 ) 2257 ) 2258 ); 2259 } 2260 2261 /** 2262 * Delete categories 2263 * 2264 * @param array $categories A list of category ids 2265 * @return array 2266 * @since Moodle 2.3 2267 */ 2268 public static function delete_categories($categories) { 2269 global $CFG, $DB; 2270 require_once($CFG->dirroot . "/course/lib.php"); 2271 2272 // Validate parameters. 2273 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories)); 2274 2275 $transaction = $DB->start_delegated_transaction(); 2276 2277 foreach ($params['categories'] as $category) { 2278 $deletecat = core_course_category::get($category['id'], MUST_EXIST); 2279 $context = context_coursecat::instance($deletecat->id); 2280 require_capability('moodle/category:manage', $context); 2281 self::validate_context($context); 2282 self::validate_context(get_category_or_system_context($deletecat->parent)); 2283 2284 if ($category['recursive']) { 2285 // If recursive was specified, then we recursively delete the category's contents. 2286 if ($deletecat->can_delete_full()) { 2287 $deletecat->delete_full(false); 2288 } else { 2289 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); 2290 } 2291 } else { 2292 // In this situation, we don't delete the category's contents, we either move it to newparent or parent. 2293 // If the parent is the root, moving is not supported (because a course must always be inside a category). 2294 // We must move to an existing category. 2295 if (!empty($category['newparent'])) { 2296 $newparentcat = core_course_category::get($category['newparent']); 2297 } else { 2298 $newparentcat = core_course_category::get($deletecat->parent); 2299 } 2300 2301 // This operation is not allowed. We must move contents to an existing category. 2302 if (!$newparentcat->id) { 2303 throw new moodle_exception('movecatcontentstoroot'); 2304 } 2305 2306 self::validate_context(context_coursecat::instance($newparentcat->id)); 2307 if ($deletecat->can_move_content_to($newparentcat->id)) { 2308 $deletecat->delete_move($newparentcat->id, false); 2309 } else { 2310 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); 2311 } 2312 } 2313 } 2314 2315 $transaction->allow_commit(); 2316 } 2317 2318 /** 2319 * Returns description of method parameters 2320 * 2321 * @return external_function_parameters 2322 * @since Moodle 2.3 2323 */ 2324 public static function delete_categories_returns() { 2325 return null; 2326 } 2327 2328 /** 2329 * Describes the parameters for delete_modules. 2330 * 2331 * @return external_function_parameters 2332 * @since Moodle 2.5 2333 */ 2334 public static function delete_modules_parameters() { 2335 return new external_function_parameters ( 2336 array( 2337 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID', 2338 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'), 2339 ) 2340 ); 2341 } 2342 2343 /** 2344 * Deletes a list of provided module instances. 2345 * 2346 * @param array $cmids the course module ids 2347 * @since Moodle 2.5 2348 */ 2349 public static function delete_modules($cmids) { 2350 global $CFG, $DB; 2351 2352 // Require course file containing the course delete module function. 2353 require_once($CFG->dirroot . "/course/lib.php"); 2354 2355 // Clean the parameters. 2356 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids)); 2357 2358 // Keep track of the course ids we have performed a capability check on to avoid repeating. 2359 $arrcourseschecked = array(); 2360 2361 foreach ($params['cmids'] as $cmid) { 2362 // Get the course module. 2363 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST); 2364 2365 // Check if we have not yet confirmed they have permission in this course. 2366 if (!in_array($cm->course, $arrcourseschecked)) { 2367 // Ensure the current user has required permission in this course. 2368 $context = context_course::instance($cm->course); 2369 self::validate_context($context); 2370 // Add to the array. 2371 $arrcourseschecked[] = $cm->course; 2372 } 2373 2374 // Ensure they can delete this module. 2375 $modcontext = context_module::instance($cm->id); 2376 require_capability('moodle/course:manageactivities', $modcontext); 2377 2378 // Delete the module. 2379 course_delete_module($cm->id); 2380 } 2381 } 2382 2383 /** 2384 * Describes the delete_modules return value. 2385 * 2386 * @return external_single_structure 2387 * @since Moodle 2.5 2388 */ 2389 public static function delete_modules_returns() { 2390 return null; 2391 } 2392 2393 /** 2394 * Returns description of method parameters 2395 * 2396 * @return external_function_parameters 2397 * @since Moodle 2.9 2398 */ 2399 public static function view_course_parameters() { 2400 return new external_function_parameters( 2401 array( 2402 'courseid' => new external_value(PARAM_INT, 'id of the course'), 2403 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0) 2404 ) 2405 ); 2406 } 2407 2408 /** 2409 * Trigger the course viewed event. 2410 * 2411 * @param int $courseid id of course 2412 * @param int $sectionnumber sectionnumber (0, 1, 2...) 2413 * @return array of warnings and status result 2414 * @since Moodle 2.9 2415 * @throws moodle_exception 2416 */ 2417 public static function view_course($courseid, $sectionnumber = 0) { 2418 global $CFG; 2419 require_once($CFG->dirroot . "/course/lib.php"); 2420 2421 $params = self::validate_parameters(self::view_course_parameters(), 2422 array( 2423 'courseid' => $courseid, 2424 'sectionnumber' => $sectionnumber 2425 )); 2426 2427 $warnings = array(); 2428 2429 $course = get_course($params['courseid']); 2430 $context = context_course::instance($course->id); 2431 self::validate_context($context); 2432 2433 if (!empty($params['sectionnumber'])) { 2434 2435 // Get section details and check it exists. 2436 $modinfo = get_fast_modinfo($course); 2437 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST); 2438 2439 // Check user is allowed to see it. 2440 if (!$coursesection->uservisible) { 2441 require_capability('moodle/course:viewhiddensections', $context); 2442 } 2443 } 2444 2445 course_view($context, $params['sectionnumber']); 2446 2447 $result = array(); 2448 $result['status'] = true; 2449 $result['warnings'] = $warnings; 2450 return $result; 2451 } 2452 2453 /** 2454 * Returns description of method result value 2455 * 2456 * @return \core_external\external_description 2457 * @since Moodle 2.9 2458 */ 2459 public static function view_course_returns() { 2460 return new external_single_structure( 2461 array( 2462 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 2463 'warnings' => new external_warnings() 2464 ) 2465 ); 2466 } 2467 2468 /** 2469 * Returns description of method parameters 2470 * 2471 * @return external_function_parameters 2472 * @since Moodle 3.0 2473 */ 2474 public static function search_courses_parameters() { 2475 return new external_function_parameters( 2476 array( 2477 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name 2478 (search, modulelist (only admins), blocklist (only admins), tagid)'), 2479 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'), 2480 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0), 2481 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), 2482 'requiredcapabilities' => new external_multiple_structure( 2483 new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'), 2484 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array() 2485 ), 2486 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0), 2487 'onlywithcompletion' => new external_value(PARAM_BOOL, 'limit to courses where completion is enabled', 2488 VALUE_DEFAULT, 0), 2489 ) 2490 ); 2491 } 2492 2493 /** 2494 * Return the course information that is public (visible by every one) 2495 * 2496 * @param core_course_list_element $course course in list object 2497 * @param stdClass $coursecontext course context object 2498 * @return array the course information 2499 * @since Moodle 3.2 2500 */ 2501 protected static function get_course_public_information(core_course_list_element $course, $coursecontext) { 2502 global $OUTPUT; 2503 2504 static $categoriescache = array(); 2505 2506 // Category information. 2507 if (!array_key_exists($course->category, $categoriescache)) { 2508 $categoriescache[$course->category] = core_course_category::get($course->category, IGNORE_MISSING); 2509 } 2510 $category = $categoriescache[$course->category]; 2511 2512 // Retrieve course overview used files. 2513 $files = array(); 2514 foreach ($course->get_course_overviewfiles() as $file) { 2515 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(), 2516 $file->get_filearea(), null, $file->get_filepath(), 2517 $file->get_filename())->out(false); 2518 $files[] = array( 2519 'filename' => $file->get_filename(), 2520 'fileurl' => $fileurl, 2521 'filesize' => $file->get_filesize(), 2522 'filepath' => $file->get_filepath(), 2523 'mimetype' => $file->get_mimetype(), 2524 'timemodified' => $file->get_timemodified(), 2525 ); 2526 } 2527 2528 // Retrieve the course contacts, 2529 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services. 2530 $coursecontacts = array(); 2531 foreach ($course->get_course_contacts() as $contact) { 2532 $coursecontacts[] = array( 2533 'id' => $contact['user']->id, 2534 'fullname' => $contact['username'], 2535 'roles' => array_map(function($role){ 2536 return array('id' => $role->id, 'name' => $role->displayname); 2537 }, $contact['roles']), 2538 'role' => array('id' => $contact['role']->id, 'name' => $contact['role']->displayname), 2539 'rolename' => $contact['rolename'] 2540 ); 2541 } 2542 2543 // Allowed enrolment methods (maybe we can self-enrol). 2544 $enroltypes = array(); 2545 $instances = enrol_get_instances($course->id, true); 2546 foreach ($instances as $instance) { 2547 $enroltypes[] = $instance->enrol; 2548 } 2549 2550 // Format summary. 2551 list($summary, $summaryformat) = 2552 \core_external\util::format_text($course->summary, $course->summaryformat, $coursecontext, 'course', 'summary', null); 2553 2554 $categoryname = ''; 2555 if (!empty($category)) { 2556 $categoryname = \core_external\util::format_string($category->name, $category->get_context()); 2557 } 2558 2559 $displayname = get_course_display_name_for_list($course); 2560 $coursereturns = array(); 2561 $coursereturns['id'] = $course->id; 2562 $coursereturns['fullname'] = \core_external\util::format_string($course->fullname, $coursecontext); 2563 $coursereturns['displayname'] = \core_external\util::format_string($displayname, $coursecontext); 2564 $coursereturns['shortname'] = \core_external\util::format_string($course->shortname, $coursecontext); 2565 $coursereturns['categoryid'] = $course->category; 2566 $coursereturns['categoryname'] = $categoryname; 2567 $coursereturns['summary'] = $summary; 2568 $coursereturns['summaryformat'] = $summaryformat; 2569 $coursereturns['summaryfiles'] = util::get_area_files($coursecontext->id, 'course', 'summary', false, false); 2570 $coursereturns['overviewfiles'] = $files; 2571 $coursereturns['contacts'] = $coursecontacts; 2572 $coursereturns['enrollmentmethods'] = $enroltypes; 2573 $coursereturns['sortorder'] = $course->sortorder; 2574 $coursereturns['showactivitydates'] = $course->showactivitydates; 2575 $coursereturns['showcompletionconditions'] = $course->showcompletionconditions; 2576 2577 $handler = core_course\customfield\course_handler::create(); 2578 if ($customfields = $handler->export_instance_data($course->id)) { 2579 $coursereturns['customfields'] = []; 2580 foreach ($customfields as $data) { 2581 $coursereturns['customfields'][] = [ 2582 'type' => $data->get_type(), 2583 'value' => $data->get_value(), 2584 'valueraw' => $data->get_data_controller()->get_value(), 2585 'name' => $data->get_name(), 2586 'shortname' => $data->get_shortname() 2587 ]; 2588 } 2589 } 2590 2591 $courseimage = \core_course\external\course_summary_exporter::get_course_image($course); 2592 if (!$courseimage) { 2593 $courseimage = $OUTPUT->get_generated_url_for_course($coursecontext); 2594 } 2595 $coursereturns['courseimage'] = $courseimage; 2596 2597 return $coursereturns; 2598 } 2599 2600 /** 2601 * Search courses following the specified criteria. 2602 * 2603 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid) 2604 * @param string $criteriavalue Criteria value 2605 * @param int $page Page number (for pagination) 2606 * @param int $perpage Items per page 2607 * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list). 2608 * @param int $limittoenrolled Limit to only enrolled courses 2609 * @param int onlywithcompletion Limit to only courses where completion is enabled 2610 * @return array of course objects and warnings 2611 * @since Moodle 3.0 2612 * @throws moodle_exception 2613 */ 2614 public static function search_courses($criterianame, 2615 $criteriavalue, 2616 $page=0, 2617 $perpage=0, 2618 $requiredcapabilities=array(), 2619 $limittoenrolled=0, 2620 $onlywithcompletion=0) { 2621 global $CFG; 2622 2623 $warnings = array(); 2624 2625 $parameters = array( 2626 'criterianame' => $criterianame, 2627 'criteriavalue' => $criteriavalue, 2628 'page' => $page, 2629 'perpage' => $perpage, 2630 'requiredcapabilities' => $requiredcapabilities, 2631 'limittoenrolled' => $limittoenrolled, 2632 'onlywithcompletion' => $onlywithcompletion 2633 ); 2634 $params = self::validate_parameters(self::search_courses_parameters(), $parameters); 2635 self::validate_context(context_system::instance()); 2636 2637 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid'); 2638 if (!in_array($params['criterianame'], $allowedcriterianames)) { 2639 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' . 2640 'allowed values are: '.implode(',', $allowedcriterianames)); 2641 } 2642 2643 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') { 2644 require_capability('moodle/site:config', context_system::instance()); 2645 } 2646 2647 $paramtype = array( 2648 'search' => PARAM_RAW, 2649 'modulelist' => PARAM_PLUGIN, 2650 'blocklist' => PARAM_INT, 2651 'tagid' => PARAM_INT 2652 ); 2653 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]); 2654 2655 // Prepare the search API options. 2656 $searchcriteria = array(); 2657 $searchcriteria[$params['criterianame']] = $params['criteriavalue']; 2658 if ($params['onlywithcompletion']) { 2659 $searchcriteria['onlywithcompletion'] = true; 2660 } 2661 2662 $options = array(); 2663 if ($params['perpage'] != 0) { 2664 $offset = $params['page'] * $params['perpage']; 2665 $options = array('offset' => $offset, 'limit' => $params['perpage']); 2666 } 2667 2668 // Search the courses. 2669 $courses = core_course_category::search_courses($searchcriteria, $options, $params['requiredcapabilities']); 2670 $totalcount = core_course_category::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']); 2671 2672 if (!empty($limittoenrolled)) { 2673 // Get the courses where the current user has access. 2674 $enrolled = enrol_get_my_courses(array('id', 'cacherev')); 2675 } 2676 2677 $finalcourses = array(); 2678 $categoriescache = array(); 2679 2680 foreach ($courses as $course) { 2681 if (!empty($limittoenrolled)) { 2682 // Filter out not enrolled courses. 2683 if (!isset($enrolled[$course->id])) { 2684 $totalcount--; 2685 continue; 2686 } 2687 } 2688 2689 $coursecontext = context_course::instance($course->id); 2690 2691 $finalcourses[] = self::get_course_public_information($course, $coursecontext); 2692 } 2693 2694 return array( 2695 'total' => $totalcount, 2696 'courses' => $finalcourses, 2697 'warnings' => $warnings 2698 ); 2699 } 2700 2701 /** 2702 * Returns a course structure definition 2703 * 2704 * @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible 2705 * @return external_single_structure the course structure 2706 * @since Moodle 3.2 2707 */ 2708 protected static function get_course_structure($onlypublicdata = true) { 2709 $coursestructure = array( 2710 'id' => new external_value(PARAM_INT, 'course id'), 2711 'fullname' => new external_value(PARAM_RAW, 'course full name'), 2712 'displayname' => new external_value(PARAM_RAW, 'course display name'), 2713 'shortname' => new external_value(PARAM_RAW, 'course short name'), 2714 'courseimage' => new external_value(PARAM_URL, 'Course image', VALUE_OPTIONAL), 2715 'categoryid' => new external_value(PARAM_INT, 'category id'), 2716 'categoryname' => new external_value(PARAM_RAW, 'category name'), 2717 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL), 2718 'summary' => new external_value(PARAM_RAW, 'summary'), 2719 'summaryformat' => new external_format_value('summary'), 2720 'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL), 2721 'overviewfiles' => new external_files('additional overview files attached to this course'), 2722 'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'), 2723 'showcompletionconditions' => new external_value(PARAM_BOOL, 2724 'Whether the activity completion conditions are shown or not'), 2725 'contacts' => new external_multiple_structure( 2726 new external_single_structure( 2727 array( 2728 'id' => new external_value(PARAM_INT, 'contact user id'), 2729 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'), 2730 ) 2731 ), 2732 'contact users' 2733 ), 2734 'enrollmentmethods' => new external_multiple_structure( 2735 new external_value(PARAM_PLUGIN, 'enrollment method'), 2736 'enrollment methods list' 2737 ), 2738 'customfields' => new external_multiple_structure( 2739 new external_single_structure( 2740 array( 2741 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), 2742 'shortname' => new external_value(PARAM_RAW, 2743 'The shortname of the custom field - to be able to build the field class in the code'), 2744 'type' => new external_value(PARAM_ALPHANUMEXT, 2745 'The type of the custom field - text field, checkbox...'), 2746 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 2747 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), 2748 ) 2749 ), 'Custom fields', VALUE_OPTIONAL), 2750 ); 2751 2752 if (!$onlypublicdata) { 2753 $extra = array( 2754 'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL), 2755 'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL), 2756 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 2757 'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL), 2758 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL), 2759 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL), 2760 'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL), 2761 'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 2762 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL), 2763 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 2764 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 2765 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 2766 'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL), 2767 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 2768 'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL), 2769 'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL), 2770 'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL), 2771 'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL), 2772 'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL), 2773 'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL), 2774 'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL), 2775 'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL), 2776 'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL), 2777 'filters' => new external_multiple_structure( 2778 new external_single_structure( 2779 array( 2780 'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name'), 2781 'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit'), 2782 'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit'), 2783 ) 2784 ), 2785 'Course filters', VALUE_OPTIONAL 2786 ), 2787 'courseformatoptions' => new external_multiple_structure( 2788 new external_single_structure( 2789 array( 2790 'name' => new external_value(PARAM_RAW, 'Course format option name.'), 2791 'value' => new external_value(PARAM_RAW, 'Course format option value.'), 2792 ) 2793 ), 2794 'Additional options for particular course format.', VALUE_OPTIONAL 2795 ), 2796 ); 2797 $coursestructure = array_merge($coursestructure, $extra); 2798 } 2799 return new external_single_structure($coursestructure); 2800 } 2801 2802 /** 2803 * Returns description of method result value 2804 * 2805 * @return \core_external\external_description 2806 * @since Moodle 3.0 2807 */ 2808 public static function search_courses_returns() { 2809 return new external_single_structure( 2810 array( 2811 'total' => new external_value(PARAM_INT, 'total course count'), 2812 'courses' => new external_multiple_structure(self::get_course_structure(), 'course'), 2813 'warnings' => new external_warnings() 2814 ) 2815 ); 2816 } 2817 2818 /** 2819 * Returns description of method parameters 2820 * 2821 * @return external_function_parameters 2822 * @since Moodle 3.0 2823 */ 2824 public static function get_course_module_parameters() { 2825 return new external_function_parameters( 2826 array( 2827 'cmid' => new external_value(PARAM_INT, 'The course module id') 2828 ) 2829 ); 2830 } 2831 2832 /** 2833 * Return information about a course module. 2834 * 2835 * @param int $cmid the course module id 2836 * @return array of warnings and the course module 2837 * @since Moodle 3.0 2838 * @throws moodle_exception 2839 */ 2840 public static function get_course_module($cmid) { 2841 global $CFG, $DB; 2842 2843 $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid)); 2844 $warnings = array(); 2845 2846 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST); 2847 $context = context_module::instance($cm->id); 2848 self::validate_context($context); 2849 2850 // If the user has permissions to manage the activity, return all the information. 2851 if (has_capability('moodle/course:manageactivities', $context)) { 2852 require_once($CFG->dirroot . '/course/modlib.php'); 2853 require_once($CFG->libdir . '/gradelib.php'); 2854 2855 $info = $cm; 2856 // Get the extra information: grade, advanced grading and outcomes data. 2857 $course = get_course($cm->course); 2858 list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course); 2859 // Grades. 2860 $gradeinfo = array('grade', 'gradepass', 'gradecat'); 2861 foreach ($gradeinfo as $gfield) { 2862 if (isset($extrainfo->{$gfield})) { 2863 $info->{$gfield} = $extrainfo->{$gfield}; 2864 } 2865 } 2866 if (isset($extrainfo->grade) and $extrainfo->grade < 0) { 2867 $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade))); 2868 } 2869 // Advanced grading. 2870 if (isset($extrainfo->_advancedgradingdata)) { 2871 $info->advancedgrading = array(); 2872 foreach ($extrainfo as $key => $val) { 2873 if (strpos($key, 'advancedgradingmethod_') === 0) { 2874 $info->advancedgrading[] = array( 2875 'area' => str_replace('advancedgradingmethod_', '', $key), 2876 'method' => $val 2877 ); 2878 } 2879 } 2880 } 2881 // Outcomes. 2882 foreach ($extrainfo as $key => $val) { 2883 if (strpos($key, 'outcome_') === 0) { 2884 if (!isset($info->outcomes)) { 2885 $info->outcomes = array(); 2886 } 2887 $id = str_replace('outcome_', '', $key); 2888 $outcome = grade_outcome::fetch(array('id' => $id)); 2889 $scaleitems = $outcome->load_scale(); 2890 $info->outcomes[] = array( 2891 'id' => $id, 2892 'name' => \core_external\util::format_string($outcome->get_name(), $context), 2893 'scale' => $scaleitems->scale 2894 ); 2895 } 2896 } 2897 } else { 2898 // Return information is safe to show to any user. 2899 $info = new stdClass(); 2900 $info->id = $cm->id; 2901 $info->course = $cm->course; 2902 $info->module = $cm->module; 2903 $info->modname = $cm->modname; 2904 $info->instance = $cm->instance; 2905 $info->section = $cm->section; 2906 $info->sectionnum = $cm->sectionnum; 2907 $info->groupmode = $cm->groupmode; 2908 $info->groupingid = $cm->groupingid; 2909 $info->completion = $cm->completion; 2910 $info->downloadcontent = $cm->downloadcontent; 2911 } 2912 // Format name. 2913 $info->name = \core_external\util::format_string($cm->name, $context); 2914 $result = array(); 2915 $result['cm'] = $info; 2916 $result['warnings'] = $warnings; 2917 return $result; 2918 } 2919 2920 /** 2921 * Returns description of method result value 2922 * 2923 * @return \core_external\external_description 2924 * @since Moodle 3.0 2925 */ 2926 public static function get_course_module_returns() { 2927 return new external_single_structure( 2928 array( 2929 'cm' => new external_single_structure( 2930 array( 2931 'id' => new external_value(PARAM_INT, 'The course module id'), 2932 'course' => new external_value(PARAM_INT, 'The course id'), 2933 'module' => new external_value(PARAM_INT, 'The module type id'), 2934 'name' => new external_value(PARAM_RAW, 'The activity name'), 2935 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'), 2936 'instance' => new external_value(PARAM_INT, 'The activity instance id'), 2937 'section' => new external_value(PARAM_INT, 'The module section id'), 2938 'sectionnum' => new external_value(PARAM_INT, 'The module section number'), 2939 'groupmode' => new external_value(PARAM_INT, 'Group mode'), 2940 'groupingid' => new external_value(PARAM_INT, 'Grouping id'), 2941 'completion' => new external_value(PARAM_INT, 'If completion is enabled'), 2942 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL), 2943 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL), 2944 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL), 2945 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL), 2946 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL), 2947 'visibleoncoursepage' => new external_value(PARAM_INT, 'If visible on course page', VALUE_OPTIONAL), 2948 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL), 2949 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL), 2950 'completionpassgrade' => new external_value(PARAM_INT, 'Completion pass grade setting', VALUE_OPTIONAL), 2951 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL), 2952 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL), 2953 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL), 2954 'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL), 2955 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL), 2956 'grade' => new external_value(PARAM_FLOAT, 'Grade (max value or scale id)', VALUE_OPTIONAL), 2957 'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL), 2958 'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL), 2959 'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL), 2960 'advancedgrading' => new external_multiple_structure( 2961 new external_single_structure( 2962 array( 2963 'area' => new external_value(PARAM_AREA, 'Gradable area name'), 2964 'method' => new external_value(PARAM_COMPONENT, 'Grading method'), 2965 ) 2966 ), 2967 'Advanced grading settings', VALUE_OPTIONAL 2968 ), 2969 'outcomes' => new external_multiple_structure( 2970 new external_single_structure( 2971 array( 2972 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'), 2973 'name' => new external_value(PARAM_RAW, 'Outcome full name'), 2974 'scale' => new external_value(PARAM_TEXT, 'Scale items') 2975 ) 2976 ), 2977 'Outcomes information', VALUE_OPTIONAL 2978 ), 2979 ) 2980 ), 2981 'warnings' => new external_warnings() 2982 ) 2983 ); 2984 } 2985 2986 /** 2987 * Returns description of method parameters 2988 * 2989 * @return external_function_parameters 2990 * @since Moodle 3.0 2991 */ 2992 public static function get_course_module_by_instance_parameters() { 2993 return new external_function_parameters( 2994 array( 2995 'module' => new external_value(PARAM_COMPONENT, 'The module name'), 2996 'instance' => new external_value(PARAM_INT, 'The module instance id') 2997 ) 2998 ); 2999 } 3000 3001 /** 3002 * Return information about a course module. 3003 * 3004 * @param string $module the module name 3005 * @param int $instance the activity instance id 3006 * @return array of warnings and the course module 3007 * @since Moodle 3.0 3008 * @throws moodle_exception 3009 */ 3010 public static function get_course_module_by_instance($module, $instance) { 3011 3012 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(), 3013 array( 3014 'module' => $module, 3015 'instance' => $instance, 3016 )); 3017 3018 $warnings = array(); 3019 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST); 3020 3021 return self::get_course_module($cm->id); 3022 } 3023 3024 /** 3025 * Returns description of method result value 3026 * 3027 * @return \core_external\external_description 3028 * @since Moodle 3.0 3029 */ 3030 public static function get_course_module_by_instance_returns() { 3031 return self::get_course_module_returns(); 3032 } 3033 3034 /** 3035 * Returns description of method parameters 3036 * 3037 * @return external_function_parameters 3038 * @since Moodle 3.2 3039 */ 3040 public static function get_user_navigation_options_parameters() { 3041 return new external_function_parameters( 3042 array( 3043 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')), 3044 ) 3045 ); 3046 } 3047 3048 /** 3049 * Return a list of navigation options in a set of courses that are avaialable or not for the current user. 3050 * 3051 * @param array $courseids a list of course ids 3052 * @return array of warnings and the options availability 3053 * @since Moodle 3.2 3054 * @throws moodle_exception 3055 */ 3056 public static function get_user_navigation_options($courseids) { 3057 global $CFG; 3058 require_once($CFG->dirroot . '/course/lib.php'); 3059 3060 // Parameter validation. 3061 $params = self::validate_parameters(self::get_user_navigation_options_parameters(), array('courseids' => $courseids)); 3062 $courseoptions = array(); 3063 3064 list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true); 3065 3066 if (!empty($courses)) { 3067 foreach ($courses as $course) { 3068 // Fix the context for the frontpage. 3069 if ($course->id == SITEID) { 3070 $course->context = context_system::instance(); 3071 } 3072 $navoptions = course_get_user_navigation_options($course->context, $course); 3073 $options = array(); 3074 foreach ($navoptions as $name => $available) { 3075 $options[] = array( 3076 'name' => $name, 3077 'available' => $available, 3078 ); 3079 } 3080 3081 $courseoptions[] = array( 3082 'id' => $course->id, 3083 'options' => $options 3084 ); 3085 } 3086 } 3087 3088 $result = array( 3089 'courses' => $courseoptions, 3090 'warnings' => $warnings 3091 ); 3092 return $result; 3093 } 3094 3095 /** 3096 * Returns description of method result value 3097 * 3098 * @return \core_external\external_description 3099 * @since Moodle 3.2 3100 */ 3101 public static function get_user_navigation_options_returns() { 3102 return new external_single_structure( 3103 array( 3104 'courses' => new external_multiple_structure( 3105 new external_single_structure( 3106 array( 3107 'id' => new external_value(PARAM_INT, 'Course id'), 3108 'options' => new external_multiple_structure( 3109 new external_single_structure( 3110 array( 3111 'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name'), 3112 'available' => new external_value(PARAM_BOOL, 'Whether the option is available or not'), 3113 ) 3114 ) 3115 ) 3116 ) 3117 ), 'List of courses' 3118 ), 3119 'warnings' => new external_warnings() 3120 ) 3121 ); 3122 } 3123 3124 /** 3125 * Returns description of method parameters 3126 * 3127 * @return external_function_parameters 3128 * @since Moodle 3.2 3129 */ 3130 public static function get_user_administration_options_parameters() { 3131 return new external_function_parameters( 3132 array( 3133 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')), 3134 ) 3135 ); 3136 } 3137 3138 /** 3139 * Return a list of administration options in a set of courses that are available or not for the current user. 3140 * 3141 * @param array $courseids a list of course ids 3142 * @return array of warnings and the options availability 3143 * @since Moodle 3.2 3144 * @throws moodle_exception 3145 */ 3146 public static function get_user_administration_options($courseids) { 3147 global $CFG; 3148 require_once($CFG->dirroot . '/course/lib.php'); 3149 3150 // Parameter validation. 3151 $params = self::validate_parameters(self::get_user_administration_options_parameters(), array('courseids' => $courseids)); 3152 $courseoptions = array(); 3153 3154 list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true); 3155 3156 if (!empty($courses)) { 3157 foreach ($courses as $course) { 3158 $adminoptions = course_get_user_administration_options($course, $course->context); 3159 $options = array(); 3160 foreach ($adminoptions as $name => $available) { 3161 $options[] = array( 3162 'name' => $name, 3163 'available' => $available, 3164 ); 3165 } 3166 3167 $courseoptions[] = array( 3168 'id' => $course->id, 3169 'options' => $options 3170 ); 3171 } 3172 } 3173 3174 $result = array( 3175 'courses' => $courseoptions, 3176 'warnings' => $warnings 3177 ); 3178 return $result; 3179 } 3180 3181 /** 3182 * Returns description of method result value 3183 * 3184 * @return \core_external\external_description 3185 * @since Moodle 3.2 3186 */ 3187 public static function get_user_administration_options_returns() { 3188 return self::get_user_navigation_options_returns(); 3189 } 3190 3191 /** 3192 * Returns description of method parameters 3193 * 3194 * @return external_function_parameters 3195 * @since Moodle 3.2 3196 */ 3197 public static function get_courses_by_field_parameters() { 3198 return new external_function_parameters( 3199 array( 3200 'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or: 3201 id: course id 3202 ids: comma separated course ids 3203 shortname: course short name 3204 idnumber: course id number 3205 category: category id the course belongs to 3206 ', VALUE_DEFAULT, ''), 3207 'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '') 3208 ) 3209 ); 3210 } 3211 3212 3213 /** 3214 * Get courses matching a specific field (id/s, shortname, idnumber, category) 3215 * 3216 * @param string $field field name to search, or empty for all courses 3217 * @param string $value value to search 3218 * @return array list of courses and warnings 3219 * @throws invalid_parameter_exception 3220 * @since Moodle 3.2 3221 */ 3222 public static function get_courses_by_field($field = '', $value = '') { 3223 global $DB, $CFG; 3224 require_once($CFG->dirroot . '/course/lib.php'); 3225 require_once($CFG->libdir . '/filterlib.php'); 3226 3227 $params = self::validate_parameters(self::get_courses_by_field_parameters(), 3228 array( 3229 'field' => $field, 3230 'value' => $value, 3231 ) 3232 ); 3233 $warnings = array(); 3234 3235 if (empty($params['field'])) { 3236 $courses = $DB->get_records('course', null, 'id ASC'); 3237 } else { 3238 switch ($params['field']) { 3239 case 'id': 3240 case 'category': 3241 $value = clean_param($params['value'], PARAM_INT); 3242 break; 3243 case 'ids': 3244 $value = clean_param($params['value'], PARAM_SEQUENCE); 3245 break; 3246 case 'shortname': 3247 $value = clean_param($params['value'], PARAM_TEXT); 3248 break; 3249 case 'idnumber': 3250 $value = clean_param($params['value'], PARAM_RAW); 3251 break; 3252 default: 3253 throw new invalid_parameter_exception('Invalid field name'); 3254 } 3255 3256 if ($params['field'] === 'ids') { 3257 // Preload categories to avoid loading one at a time. 3258 $courseids = explode(',', $value); 3259 list ($listsql, $listparams) = $DB->get_in_or_equal($courseids); 3260 $categoryids = $DB->get_fieldset_sql(" 3261 SELECT DISTINCT cc.id 3262 FROM {course} c 3263 JOIN {course_categories} cc ON cc.id = c.category 3264 WHERE c.id $listsql", $listparams); 3265 core_course_category::get_many($categoryids); 3266 3267 // Load and validate all courses. This is called because it loads the courses 3268 // more efficiently. 3269 list ($courses, $warnings) = util::validate_courses($courseids, [], 3270 false, true); 3271 } else { 3272 $courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC'); 3273 } 3274 } 3275 3276 $coursesdata = array(); 3277 foreach ($courses as $course) { 3278 $context = context_course::instance($course->id); 3279 $canupdatecourse = has_capability('moodle/course:update', $context); 3280 $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context); 3281 3282 // Check if the course is visible in the site for the user. 3283 if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) { 3284 continue; 3285 } 3286 // Get the public course information, even if we are not enrolled. 3287 $courseinlist = new core_course_list_element($course); 3288 3289 // Now, check if we have access to the course, unless it was already checked. 3290 try { 3291 if (empty($course->contextvalidated)) { 3292 self::validate_context($context); 3293 } 3294 } catch (Exception $e) { 3295 // User can not access the course, check if they can see the public information about the course and return it. 3296 if (core_course_category::can_view_course_info($course)) { 3297 $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context); 3298 } 3299 continue; 3300 } 3301 $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context); 3302 // Return information for any user that can access the course. 3303 $coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'enddate', 'maxbytes', 'showreports', 'visible', 3304 'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme', 3305 'marker'); 3306 3307 // Course filters. 3308 $coursesdata[$course->id]['filters'] = filter_get_available_in_context($context); 3309 3310 // Information for managers only. 3311 if ($canupdatecourse) { 3312 $managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested', 3313 'cacherev'); 3314 $coursefields = array_merge($coursefields, $managerfields); 3315 } 3316 3317 // Populate fields. 3318 foreach ($coursefields as $field) { 3319 $coursesdata[$course->id][$field] = $course->{$field}; 3320 } 3321 3322 // Clean lang and auth fields for external functions (it may content uninstalled themes or language packs). 3323 if (isset($coursesdata[$course->id]['theme'])) { 3324 $coursesdata[$course->id]['theme'] = clean_param($coursesdata[$course->id]['theme'], PARAM_THEME); 3325 } 3326 if (isset($coursesdata[$course->id]['lang'])) { 3327 $coursesdata[$course->id]['lang'] = clean_param($coursesdata[$course->id]['lang'], PARAM_LANG); 3328 } 3329 3330 $courseformatoptions = course_get_format($course)->get_config_for_external(); 3331 foreach ($courseformatoptions as $key => $value) { 3332 $coursesdata[$course->id]['courseformatoptions'][] = array( 3333 'name' => $key, 3334 'value' => $value 3335 ); 3336 } 3337 } 3338 3339 return array( 3340 'courses' => $coursesdata, 3341 'warnings' => $warnings 3342 ); 3343 } 3344 3345 /** 3346 * Returns description of method result value 3347 * 3348 * @return \core_external\external_description 3349 * @since Moodle 3.2 3350 */ 3351 public static function get_courses_by_field_returns() { 3352 // Course structure, including not only public viewable fields. 3353 return new external_single_structure( 3354 array( 3355 'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'), 3356 'warnings' => new external_warnings() 3357 ) 3358 ); 3359 } 3360 3361 /** 3362 * Returns description of method parameters 3363 * 3364 * @return external_function_parameters 3365 * @since Moodle 3.2 3366 */ 3367 public static function check_updates_parameters() { 3368 return new external_function_parameters( 3369 array( 3370 'courseid' => new external_value(PARAM_INT, 'Course id to check'), 3371 'tocheck' => new external_multiple_structure( 3372 new external_single_structure( 3373 array( 3374 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location. 3375 Only module supported right now.'), 3376 'id' => new external_value(PARAM_INT, 'Context instance id'), 3377 'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'), 3378 ) 3379 ), 3380 'Instances to check' 3381 ), 3382 'filter' => new external_multiple_structure( 3383 new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments, 3384 gradeitems, outcomes'), 3385 'Check only for updates in these areas', VALUE_DEFAULT, array() 3386 ) 3387 ) 3388 ); 3389 } 3390 3391 /** 3392 * Check if there is updates affecting the user for the given course and contexts. 3393 * Right now only modules are supported. 3394 * This WS calls mod_check_updates_since for each module to check if there is any update the user should we aware of. 3395 * 3396 * @param int $courseid the list of modules to check 3397 * @param array $tocheck the list of modules to check 3398 * @param array $filter check only for updates in these areas 3399 * @return array list of updates and warnings 3400 * @throws moodle_exception 3401 * @since Moodle 3.2 3402 */ 3403 public static function check_updates($courseid, $tocheck, $filter = array()) { 3404 global $CFG, $DB; 3405 require_once($CFG->dirroot . "/course/lib.php"); 3406 3407 $params = self::validate_parameters( 3408 self::check_updates_parameters(), 3409 array( 3410 'courseid' => $courseid, 3411 'tocheck' => $tocheck, 3412 'filter' => $filter, 3413 ) 3414 ); 3415 3416 $course = get_course($params['courseid']); 3417 $context = context_course::instance($course->id); 3418 self::validate_context($context); 3419 3420 list($instances, $warnings) = course_check_updates($course, $params['tocheck'], $filter); 3421 3422 $instancesformatted = array(); 3423 foreach ($instances as $instance) { 3424 $updates = array(); 3425 foreach ($instance['updates'] as $name => $data) { 3426 if (empty($data->updated)) { 3427 continue; 3428 } 3429 $updatedata = array( 3430 'name' => $name, 3431 ); 3432 if (!empty($data->timeupdated)) { 3433 $updatedata['timeupdated'] = $data->timeupdated; 3434 } 3435 if (!empty($data->itemids)) { 3436 $updatedata['itemids'] = $data->itemids; 3437 } 3438 $updates[] = $updatedata; 3439 } 3440 if (!empty($updates)) { 3441 $instancesformatted[] = array( 3442 'contextlevel' => $instance['contextlevel'], 3443 'id' => $instance['id'], 3444 'updates' => $updates 3445 ); 3446 } 3447 } 3448 3449 return array( 3450 'instances' => $instancesformatted, 3451 'warnings' => $warnings 3452 ); 3453 } 3454 3455 /** 3456 * Returns description of method result value 3457 * 3458 * @return \core_external\external_description 3459 * @since Moodle 3.2 3460 */ 3461 public static function check_updates_returns() { 3462 return new external_single_structure( 3463 array( 3464 'instances' => new external_multiple_structure( 3465 new external_single_structure( 3466 array( 3467 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level'), 3468 'id' => new external_value(PARAM_INT, 'Instance id'), 3469 'updates' => new external_multiple_structure( 3470 new external_single_structure( 3471 array( 3472 'name' => new external_value(PARAM_ALPHANUMEXT, 'Name of the area updated.'), 3473 'timeupdated' => new external_value(PARAM_INT, 'Last time was updated', VALUE_OPTIONAL), 3474 'itemids' => new external_multiple_structure( 3475 new external_value(PARAM_INT, 'Instance id'), 3476 'The ids of the items updated', 3477 VALUE_OPTIONAL 3478 ) 3479 ) 3480 ) 3481 ) 3482 ) 3483 ) 3484 ), 3485 'warnings' => new external_warnings() 3486 ) 3487 ); 3488 } 3489 3490 /** 3491 * Returns description of method parameters 3492 * 3493 * @return external_function_parameters 3494 * @since Moodle 3.3 3495 */ 3496 public static function get_updates_since_parameters() { 3497 return new external_function_parameters( 3498 array( 3499 'courseid' => new external_value(PARAM_INT, 'Course id to check'), 3500 'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'), 3501 'filter' => new external_multiple_structure( 3502 new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments, 3503 gradeitems, outcomes'), 3504 'Check only for updates in these areas', VALUE_DEFAULT, array() 3505 ) 3506 ) 3507 ); 3508 } 3509 3510 /** 3511 * Check if there are updates affecting the user for the given course since the given time stamp. 3512 * 3513 * This function is a wrapper of self::check_updates for retrieving all the updates since a given time for all the activities. 3514 * 3515 * @param int $courseid the list of modules to check 3516 * @param int $since check updates since this time stamp 3517 * @param array $filter check only for updates in these areas 3518 * @return array list of updates and warnings 3519 * @throws moodle_exception 3520 * @since Moodle 3.3 3521 */ 3522 public static function get_updates_since($courseid, $since, $filter = array()) { 3523 global $CFG, $DB; 3524 3525 $params = self::validate_parameters( 3526 self::get_updates_since_parameters(), 3527 array( 3528 'courseid' => $courseid, 3529 'since' => $since, 3530 'filter' => $filter, 3531 ) 3532 ); 3533 3534 $course = get_course($params['courseid']); 3535 $modinfo = get_fast_modinfo($course); 3536 $tocheck = array(); 3537 3538 // Retrieve all the visible course modules for the current user. 3539 $cms = $modinfo->get_cms(); 3540 foreach ($cms as $cm) { 3541 if (!$cm->uservisible) { 3542 continue; 3543 } 3544 $tocheck[] = array( 3545 'id' => $cm->id, 3546 'contextlevel' => 'module', 3547 'since' => $params['since'], 3548 ); 3549 } 3550 3551 return self::check_updates($course->id, $tocheck, $params['filter']); 3552 } 3553 3554 /** 3555 * Returns description of method result value 3556 * 3557 * @return \core_external\external_description 3558 * @since Moodle 3.3 3559 */ 3560 public static function get_updates_since_returns() { 3561 return self::check_updates_returns(); 3562 } 3563 3564 /** 3565 * Parameters for function edit_module() 3566 * 3567 * @since Moodle 3.3 3568 * @return external_function_parameters 3569 */ 3570 public static function edit_module_parameters() { 3571 return new external_function_parameters( 3572 array( 3573 'action' => new external_value(PARAM_ALPHA, 3574 'action: hide, show, stealth, duplicate, delete, moveleft, moveright, group...', VALUE_REQUIRED), 3575 'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED), 3576 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), 3577 )); 3578 } 3579 3580 /** 3581 * Performs one of the edit module actions and return new html for AJAX 3582 * 3583 * Returns html to replace the current module html with, for example: 3584 * - empty string for "delete" action, 3585 * - two modules html for "duplicate" action 3586 * - updated module html for everything else 3587 * 3588 * Throws exception if operation is not permitted/possible 3589 * 3590 * @since Moodle 3.3 3591 * @param string $action 3592 * @param int $id 3593 * @param null|int $sectionreturn 3594 * @return string 3595 */ 3596 public static function edit_module($action, $id, $sectionreturn = null) { 3597 global $PAGE, $DB; 3598 // Validate and normalize parameters. 3599 $params = self::validate_parameters(self::edit_module_parameters(), 3600 array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn)); 3601 $action = $params['action']; 3602 $id = $params['id']; 3603 $sectionreturn = $params['sectionreturn']; 3604 3605 // Set of permissions an editing user may have. 3606 $contextarray = [ 3607 'moodle/course:update', 3608 'moodle/course:manageactivities', 3609 'moodle/course:activityvisibility', 3610 'moodle/course:sectionvisibility', 3611 'moodle/course:movesections', 3612 'moodle/course:setcurrentsection', 3613 ]; 3614 $PAGE->set_other_editing_capability($contextarray); 3615 3616 list($course, $cm) = get_course_and_cm_from_cmid($id); 3617 $modcontext = context_module::instance($cm->id); 3618 $coursecontext = context_course::instance($course->id); 3619 self::validate_context($modcontext); 3620 $format = course_get_format($course); 3621 if ($sectionreturn) { 3622 $format->set_section_number($sectionreturn); 3623 } 3624 $renderer = $format->get_renderer($PAGE); 3625 3626 switch($action) { 3627 case 'hide': 3628 case 'show': 3629 case 'stealth': 3630 require_capability('moodle/course:activityvisibility', $modcontext); 3631 $visible = ($action === 'hide') ? 0 : 1; 3632 $visibleoncoursepage = ($action === 'stealth') ? 0 : 1; 3633 set_coursemodule_visible($id, $visible, $visibleoncoursepage); 3634 \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 3635 break; 3636 case 'duplicate': 3637 require_capability('moodle/course:manageactivities', $coursecontext); 3638 require_capability('moodle/backup:backuptargetimport', $coursecontext); 3639 require_capability('moodle/restore:restoretargetimport', $coursecontext); 3640 if (!course_allowed_module($course, $cm->modname)) { 3641 throw new moodle_exception('No permission to create that activity'); 3642 } 3643 if ($newcm = duplicate_module($course, $cm)) { 3644 3645 $modinfo = $format->get_modinfo(); 3646 $section = $modinfo->get_section_info($newcm->sectionnum); 3647 $cm = $modinfo->get_cm($id); 3648 3649 // Get both original and new element html. 3650 $result = $renderer->course_section_updated_cm_item($format, $section, $cm); 3651 $result .= $renderer->course_section_updated_cm_item($format, $section, $newcm); 3652 return $result; 3653 } 3654 break; 3655 case 'groupsseparate': 3656 case 'groupsvisible': 3657 case 'groupsnone': 3658 require_capability('moodle/course:manageactivities', $modcontext); 3659 if ($action === 'groupsseparate') { 3660 $newgroupmode = SEPARATEGROUPS; 3661 } else if ($action === 'groupsvisible') { 3662 $newgroupmode = VISIBLEGROUPS; 3663 } else { 3664 $newgroupmode = NOGROUPS; 3665 } 3666 if (set_coursemodule_groupmode($cm->id, $newgroupmode)) { 3667 \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); 3668 } 3669 break; 3670 case 'moveleft': 3671 case 'moveright': 3672 require_capability('moodle/course:manageactivities', $modcontext); 3673 $indent = $cm->indent + (($action === 'moveright') ? 1 : -1); 3674 if ($cm->indent >= 0) { 3675 $DB->update_record('course_modules', array('id' => $cm->id, 'indent' => $indent)); 3676 rebuild_course_cache($cm->course); 3677 } 3678 break; 3679 case 'delete': 3680 require_capability('moodle/course:manageactivities', $modcontext); 3681 course_delete_module($cm->id, true); 3682 return ''; 3683 default: 3684 throw new coding_exception('Unrecognised action'); 3685 } 3686 3687 $modinfo = $format->get_modinfo(); 3688 $section = $modinfo->get_section_info($cm->sectionnum); 3689 $cm = $modinfo->get_cm($id); 3690 return $renderer->course_section_updated_cm_item($format, $section, $cm); 3691 } 3692 3693 /** 3694 * Return structure for edit_module() 3695 * 3696 * @since Moodle 3.3 3697 * @return \core_external\external_description 3698 */ 3699 public static function edit_module_returns() { 3700 return new external_value(PARAM_RAW, 'html to replace the current module with'); 3701 } 3702 3703 /** 3704 * Parameters for function get_module() 3705 * 3706 * @since Moodle 3.3 3707 * @return external_function_parameters 3708 */ 3709 public static function get_module_parameters() { 3710 return new external_function_parameters( 3711 array( 3712 'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED), 3713 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), 3714 )); 3715 } 3716 3717 /** 3718 * Returns html for displaying one activity module on course page 3719 * 3720 * @since Moodle 3.3 3721 * @param int $id 3722 * @param null|int $sectionreturn 3723 * @return string 3724 */ 3725 public static function get_module($id, $sectionreturn = null) { 3726 global $PAGE; 3727 // Validate and normalize parameters. 3728 $params = self::validate_parameters(self::get_module_parameters(), 3729 array('id' => $id, 'sectionreturn' => $sectionreturn)); 3730 $id = $params['id']; 3731 $sectionreturn = $params['sectionreturn']; 3732 3733 // Set of permissions an editing user may have. 3734 $contextarray = [ 3735 'moodle/course:update', 3736 'moodle/course:manageactivities', 3737 'moodle/course:activityvisibility', 3738 'moodle/course:sectionvisibility', 3739 'moodle/course:movesections', 3740 'moodle/course:setcurrentsection', 3741 ]; 3742 $PAGE->set_other_editing_capability($contextarray); 3743 3744 // Validate access to the course (note, this is html for the course view page, we don't validate access to the module). 3745 list($course, $cm) = get_course_and_cm_from_cmid($id); 3746 self::validate_context(context_course::instance($course->id)); 3747 3748 $format = course_get_format($course); 3749 if ($sectionreturn) { 3750 $format->set_section_number($sectionreturn); 3751 } 3752 $renderer = $format->get_renderer($PAGE); 3753 3754 $modinfo = $format->get_modinfo(); 3755 $section = $modinfo->get_section_info($cm->sectionnum); 3756 return $renderer->course_section_updated_cm_item($format, $section, $cm); 3757 } 3758 3759 /** 3760 * Return structure for get_module() 3761 * 3762 * @since Moodle 3.3 3763 * @return \core_external\external_description 3764 */ 3765 public static function get_module_returns() { 3766 return new external_value(PARAM_RAW, 'html to replace the current module with'); 3767 } 3768 3769 /** 3770 * Parameters for function edit_section() 3771 * 3772 * @since Moodle 3.3 3773 * @return external_function_parameters 3774 */ 3775 public static function edit_section_parameters() { 3776 return new external_function_parameters( 3777 array( 3778 'action' => new external_value(PARAM_ALPHA, 'action: hide, show, stealth, setmarker, removemarker', VALUE_REQUIRED), 3779 'id' => new external_value(PARAM_INT, 'course section id', VALUE_REQUIRED), 3780 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), 3781 )); 3782 } 3783 3784 /** 3785 * Performs one of the edit section actions 3786 * 3787 * @since Moodle 3.3 3788 * @param string $action 3789 * @param int $id section id 3790 * @param int $sectionreturn section to return to 3791 * @return string 3792 */ 3793 public static function edit_section($action, $id, $sectionreturn) { 3794 global $DB; 3795 // Validate and normalize parameters. 3796 $params = self::validate_parameters(self::edit_section_parameters(), 3797 array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn)); 3798 $action = $params['action']; 3799 $id = $params['id']; 3800 $sr = $params['sectionreturn']; 3801 3802 $section = $DB->get_record('course_sections', array('id' => $id), '*', MUST_EXIST); 3803 $coursecontext = context_course::instance($section->course); 3804 self::validate_context($coursecontext); 3805 3806 $rv = course_get_format($section->course)->section_action($section, $action, $sectionreturn); 3807 if ($rv) { 3808 return json_encode($rv); 3809 } else { 3810 return null; 3811 } 3812 } 3813 3814 /** 3815 * Return structure for edit_section() 3816 * 3817 * @since Moodle 3.3 3818 * @return \core_external\external_description 3819 */ 3820 public static function edit_section_returns() { 3821 return new external_value(PARAM_RAW, 'Additional data for javascript (JSON-encoded string)'); 3822 } 3823 3824 /** 3825 * Returns description of method parameters 3826 * 3827 * @return external_function_parameters 3828 */ 3829 public static function get_enrolled_courses_by_timeline_classification_parameters() { 3830 return new external_function_parameters( 3831 array( 3832 'classification' => new external_value(PARAM_ALPHA, 'future, inprogress, or past'), 3833 'limit' => new external_value(PARAM_INT, 'Result set limit', VALUE_DEFAULT, 0), 3834 'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0), 3835 'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null), 3836 'customfieldname' => new external_value(PARAM_ALPHANUMEXT, 'Used when classification = customfield', 3837 VALUE_DEFAULT, null), 3838 'customfieldvalue' => new external_value(PARAM_RAW, 'Used when classification = customfield', 3839 VALUE_DEFAULT, null), 3840 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', 3841 VALUE_DEFAULT, null), 3842 ) 3843 ); 3844 } 3845 3846 /** 3847 * Get courses matching the given timeline classification. 3848 * 3849 * NOTE: The offset applies to the unfiltered full set of courses before the classification 3850 * filtering is done. 3851 * E.g. 3852 * If the user is enrolled in 5 courses: 3853 * c1, c2, c3, c4, and c5 3854 * And c4 and c5 are 'future' courses 3855 * 3856 * If a request comes in for future courses with an offset of 1 it will mean that 3857 * c1 is skipped (because the offset applies *before* the classification filtering) 3858 * and c4 and c5 will be return. 3859 * 3860 * @param string $classification past, inprogress, or future 3861 * @param int $limit Result set limit 3862 * @param int $offset Offset the full course set before timeline classification is applied 3863 * @param string $sort SQL sort string for results 3864 * @param string $customfieldname 3865 * @param string $customfieldvalue 3866 * @param string $searchvalue 3867 * @return array list of courses and warnings 3868 * @throws invalid_parameter_exception 3869 */ 3870 public static function get_enrolled_courses_by_timeline_classification( 3871 string $classification, 3872 int $limit = 0, 3873 int $offset = 0, 3874 string $sort = null, 3875 string $customfieldname = null, 3876 string $customfieldvalue = null, 3877 string $searchvalue = null 3878 ) { 3879 global $CFG, $PAGE, $USER; 3880 require_once($CFG->dirroot . '/course/lib.php'); 3881 3882 $params = self::validate_parameters(self::get_enrolled_courses_by_timeline_classification_parameters(), 3883 array( 3884 'classification' => $classification, 3885 'limit' => $limit, 3886 'offset' => $offset, 3887 'sort' => $sort, 3888 'customfieldvalue' => $customfieldvalue, 3889 'searchvalue' => $searchvalue, 3890 ) 3891 ); 3892 3893 $classification = $params['classification']; 3894 $limit = $params['limit']; 3895 $offset = $params['offset']; 3896 $sort = $params['sort']; 3897 $customfieldvalue = $params['customfieldvalue']; 3898 $searchvalue = clean_param($params['searchvalue'], PARAM_TEXT); 3899 3900 switch($classification) { 3901 case COURSE_TIMELINE_ALLINCLUDINGHIDDEN: 3902 break; 3903 case COURSE_TIMELINE_ALL: 3904 break; 3905 case COURSE_TIMELINE_PAST: 3906 break; 3907 case COURSE_TIMELINE_INPROGRESS: 3908 break; 3909 case COURSE_TIMELINE_FUTURE: 3910 break; 3911 case COURSE_FAVOURITES: 3912 break; 3913 case COURSE_TIMELINE_HIDDEN: 3914 break; 3915 case COURSE_TIMELINE_SEARCH: 3916 break; 3917 case COURSE_CUSTOMFIELD: 3918 break; 3919 default: 3920 throw new invalid_parameter_exception('Invalid classification'); 3921 } 3922 3923 self::validate_context(context_user::instance($USER->id)); 3924 3925 $requiredproperties = course_summary_exporter::define_properties(); 3926 $fields = join(',', array_keys($requiredproperties)); 3927 $hiddencourses = get_hidden_courses_on_timeline(); 3928 $courses = []; 3929 3930 // If the timeline requires really all courses, get really all courses. 3931 if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN) { 3932 $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT); 3933 3934 // Otherwise if the timeline requires the hidden courses then restrict the result to only $hiddencourses. 3935 } else if ($classification == COURSE_TIMELINE_HIDDEN) { 3936 $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, 3937 COURSE_DB_QUERY_LIMIT, $hiddencourses); 3938 3939 // Otherwise get the requested courses and exclude the hidden courses. 3940 } else if ($classification == COURSE_TIMELINE_SEARCH) { 3941 // Prepare the search API options. 3942 $searchcriteria['search'] = $searchvalue; 3943 $options = ['idonly' => true]; 3944 $courses = course_get_enrolled_courses_for_logged_in_user_from_search( 3945 0, 3946 $offset, 3947 $sort, 3948 $fields, 3949 COURSE_DB_QUERY_LIMIT, 3950 $searchcriteria, 3951 $options 3952 ); 3953 } else { 3954 $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, 3955 COURSE_DB_QUERY_LIMIT, [], $hiddencourses); 3956 } 3957 3958 $favouritecourseids = []; 3959 $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id)); 3960 $favourites = $ufservice->find_favourites_by_type('core_course', 'courses'); 3961 3962 if ($favourites) { 3963 $favouritecourseids = array_map( 3964 function($favourite) { 3965 return $favourite->itemid; 3966 }, $favourites); 3967 } 3968 3969 if ($classification == COURSE_FAVOURITES) { 3970 list($filteredcourses, $processedcount) = course_filter_courses_by_favourites( 3971 $courses, 3972 $favouritecourseids, 3973 $limit 3974 ); 3975 } else if ($classification == COURSE_CUSTOMFIELD) { 3976 list($filteredcourses, $processedcount) = course_filter_courses_by_customfield( 3977 $courses, 3978 $customfieldname, 3979 $customfieldvalue, 3980 $limit 3981 ); 3982 } else { 3983 list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification( 3984 $courses, 3985 $classification, 3986 $limit 3987 ); 3988 } 3989 3990 $renderer = $PAGE->get_renderer('core'); 3991 $formattedcourses = array_map(function($course) use ($renderer, $favouritecourseids) { 3992 if ($course == null) { 3993 return; 3994 } 3995 context_helper::preload_from_record($course); 3996 $context = context_course::instance($course->id); 3997 $isfavourite = false; 3998 if (in_array($course->id, $favouritecourseids)) { 3999 $isfavourite = true; 4000 } 4001 $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]); 4002 return $exporter->export($renderer); 4003 }, $filteredcourses); 4004 4005 $formattedcourses = array_filter($formattedcourses, function($course) { 4006 if ($course != null) { 4007 return $course; 4008 } 4009 }); 4010 4011 return [ 4012 'courses' => $formattedcourses, 4013 'nextoffset' => $offset + $processedcount 4014 ]; 4015 } 4016 4017 /** 4018 * Returns description of method result value 4019 * 4020 * @return \core_external\external_description 4021 */ 4022 public static function get_enrolled_courses_by_timeline_classification_returns() { 4023 return new external_single_structure( 4024 array( 4025 'courses' => new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Course'), 4026 'nextoffset' => new external_value(PARAM_INT, 'Offset for the next request') 4027 ) 4028 ); 4029 } 4030 4031 /** 4032 * Returns description of method parameters 4033 * 4034 * @return external_function_parameters 4035 */ 4036 public static function set_favourite_courses_parameters() { 4037 return new external_function_parameters( 4038 array( 4039 'courses' => new external_multiple_structure( 4040 new external_single_structure( 4041 array( 4042 'id' => new external_value(PARAM_INT, 'course ID'), 4043 'favourite' => new external_value(PARAM_BOOL, 'favourite status') 4044 ) 4045 ) 4046 ) 4047 ) 4048 ); 4049 } 4050 4051 /** 4052 * Set the course favourite status for an array of courses. 4053 * 4054 * @param array $courses List with course id's and favourite status. 4055 * @return array Array with an array of favourite courses. 4056 */ 4057 public static function set_favourite_courses( 4058 array $courses 4059 ) { 4060 global $USER; 4061 4062 $params = self::validate_parameters(self::set_favourite_courses_parameters(), 4063 array( 4064 'courses' => $courses 4065 ) 4066 ); 4067 4068 $warnings = []; 4069 4070 $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id)); 4071 4072 foreach ($params['courses'] as $course) { 4073 4074 $warning = []; 4075 4076 $favouriteexists = $ufservice->favourite_exists('core_course', 'courses', $course['id'], 4077 \context_course::instance($course['id'])); 4078 4079 if ($course['favourite']) { 4080 if (!$favouriteexists) { 4081 try { 4082 $ufservice->create_favourite('core_course', 'courses', $course['id'], 4083 \context_course::instance($course['id'])); 4084 } catch (Exception $e) { 4085 $warning['courseid'] = $course['id']; 4086 if ($e instanceof moodle_exception) { 4087 $warning['warningcode'] = $e->errorcode; 4088 } else { 4089 $warning['warningcode'] = $e->getCode(); 4090 } 4091 $warning['message'] = $e->getMessage(); 4092 $warnings[] = $warning; 4093 $warnings[] = $warning; 4094 } 4095 } else { 4096 $warning['courseid'] = $course['id']; 4097 $warning['warningcode'] = 'coursealreadyfavourited'; 4098 $warning['message'] = 'Course already favourited'; 4099 $warnings[] = $warning; 4100 } 4101 } else { 4102 if ($favouriteexists) { 4103 try { 4104 $ufservice->delete_favourite('core_course', 'courses', $course['id'], 4105 \context_course::instance($course['id'])); 4106 } catch (Exception $e) { 4107 $warning['courseid'] = $course['id']; 4108 if ($e instanceof moodle_exception) { 4109 $warning['warningcode'] = $e->errorcode; 4110 } else { 4111 $warning['warningcode'] = $e->getCode(); 4112 } 4113 $warning['message'] = $e->getMessage(); 4114 $warnings[] = $warning; 4115 $warnings[] = $warning; 4116 } 4117 } else { 4118 $warning['courseid'] = $course['id']; 4119 $warning['warningcode'] = 'cannotdeletefavourite'; 4120 $warning['message'] = 'Could not delete favourite status for course'; 4121 $warnings[] = $warning; 4122 } 4123 } 4124 } 4125 4126 return [ 4127 'warnings' => $warnings 4128 ]; 4129 } 4130 4131 /** 4132 * Returns description of method result value 4133 * 4134 * @return \core_external\external_description 4135 */ 4136 public static function set_favourite_courses_returns() { 4137 return new external_single_structure( 4138 array( 4139 'warnings' => new external_warnings() 4140 ) 4141 ); 4142 } 4143 4144 /** 4145 * Returns description of method parameters 4146 * 4147 * @return external_function_parameters 4148 * @since Moodle 3.6 4149 */ 4150 public static function get_recent_courses_parameters() { 4151 return new external_function_parameters( 4152 array( 4153 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0), 4154 'limit' => new external_value(PARAM_INT, 'result set limit', VALUE_DEFAULT, 0), 4155 'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0), 4156 'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null) 4157 ) 4158 ); 4159 } 4160 4161 /** 4162 * Get last accessed courses adding additional course information like images. 4163 * 4164 * @param int $userid User id from which the courses will be obtained 4165 * @param int $limit Restrict result set to this amount 4166 * @param int $offset Skip this number of records from the start of the result set 4167 * @param string|null $sort SQL string for sorting 4168 * @return array List of courses 4169 * @throws invalid_parameter_exception 4170 */ 4171 public static function get_recent_courses(int $userid = 0, int $limit = 0, int $offset = 0, string $sort = null) { 4172 global $USER, $PAGE; 4173 4174 if (empty($userid)) { 4175 $userid = $USER->id; 4176 } 4177 4178 $params = self::validate_parameters(self::get_recent_courses_parameters(), 4179 array( 4180 'userid' => $userid, 4181 'limit' => $limit, 4182 'offset' => $offset, 4183 'sort' => $sort 4184 ) 4185 ); 4186 4187 $userid = $params['userid']; 4188 $limit = $params['limit']; 4189 $offset = $params['offset']; 4190 $sort = $params['sort']; 4191 4192 $usercontext = context_user::instance($userid); 4193 4194 self::validate_context($usercontext); 4195 4196 if ($userid != $USER->id and !has_capability('moodle/user:viewdetails', $usercontext)) { 4197 return array(); 4198 } 4199 4200 $courses = course_get_recent_courses($userid, $limit, $offset, $sort); 4201 4202 $renderer = $PAGE->get_renderer('core'); 4203 4204 $recentcourses = array_map(function($course) use ($renderer) { 4205 context_helper::preload_from_record($course); 4206 $context = context_course::instance($course->id); 4207 $isfavourite = !empty($course->component); 4208 $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]); 4209 return $exporter->export($renderer); 4210 }, $courses); 4211 4212 return $recentcourses; 4213 } 4214 4215 /** 4216 * Returns description of method result value 4217 * 4218 * @return \core_external\external_description 4219 * @since Moodle 3.6 4220 */ 4221 public static function get_recent_courses_returns() { 4222 return new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Courses'); 4223 } 4224 4225 /** 4226 * Returns description of method parameters 4227 * 4228 * @return external_function_parameters 4229 */ 4230 public static function get_enrolled_users_by_cmid_parameters() { 4231 return new external_function_parameters([ 4232 'cmid' => new external_value(PARAM_INT, 'id of the course module', VALUE_REQUIRED), 4233 'groupid' => new external_value(PARAM_INT, 'id of the group', VALUE_DEFAULT, 0), 4234 'onlyactive' => new external_value(PARAM_BOOL, 'whether to return only active users or all.', 4235 VALUE_DEFAULT, false), 4236 ]); 4237 } 4238 4239 /** 4240 * Get all users in a course for a given cmid. 4241 * 4242 * @param int $cmid Course Module id from which the users will be obtained 4243 * @param int $groupid Group id from which the users will be obtained 4244 * @param bool $onlyactive Whether to return only the active enrolled users or all enrolled users in the course. 4245 * @return array List of users 4246 * @throws invalid_parameter_exception 4247 */ 4248 public static function get_enrolled_users_by_cmid(int $cmid, int $groupid = 0, bool $onlyactive = false) { 4249 global $PAGE; 4250 $warnings = []; 4251 4252 self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [ 4253 'cmid' => $cmid, 4254 'groupid' => $groupid, 4255 'onlyactive' => $onlyactive, 4256 ]); 4257 4258 list($course, $cm) = get_course_and_cm_from_cmid($cmid); 4259 $coursecontext = context_course::instance($course->id); 4260 self::validate_context($coursecontext); 4261 4262 $enrolledusers = get_enrolled_users($coursecontext, '', $groupid, 'u.*', null, 0, 0, $onlyactive); 4263 4264 $users = array_map(function ($user) use ($PAGE) { 4265 $user->fullname = fullname($user); 4266 $userpicture = new user_picture($user); 4267 $userpicture->size = 1; 4268 $user->profileimage = $userpicture->get_url($PAGE)->out(false); 4269 return $user; 4270 }, $enrolledusers); 4271 sort($users); 4272 4273 return [ 4274 'users' => $users, 4275 'warnings' => $warnings, 4276 ]; 4277 } 4278 4279 /** 4280 * Returns description of method result value 4281 * 4282 * @return \core_external\external_description 4283 */ 4284 public static function get_enrolled_users_by_cmid_returns() { 4285 return new external_single_structure([ 4286 'users' => new external_multiple_structure(self::user_description()), 4287 'warnings' => new external_warnings(), 4288 ]); 4289 } 4290 4291 /** 4292 * Create user return value description. 4293 * 4294 * @return \core_external\external_description 4295 */ 4296 public static function user_description() { 4297 $userfields = array( 4298 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), 4299 'profileimage' => new external_value(PARAM_URL, 'The location of the users larger image', VALUE_OPTIONAL), 4300 'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL), 4301 'firstname' => new external_value( 4302 core_user::get_property_type('firstname'), 4303 'The first name(s) of the user', 4304 VALUE_OPTIONAL), 4305 'lastname' => new external_value( 4306 core_user::get_property_type('lastname'), 4307 'The family name of the user', 4308 VALUE_OPTIONAL), 4309 ); 4310 return new external_single_structure($userfields); 4311 } 4312 4313 /** 4314 * Returns description of method parameters. 4315 * 4316 * @return external_function_parameters 4317 */ 4318 public static function add_content_item_to_user_favourites_parameters() { 4319 return new external_function_parameters([ 4320 'componentname' => new external_value(PARAM_TEXT, 4321 'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED), 4322 'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED) 4323 ]); 4324 } 4325 4326 /** 4327 * Add a content item to a user's favourites. 4328 * 4329 * @param string $componentname the name of the component from which this content item originates. 4330 * @param int $contentitemid the id of the content item. 4331 * @return stdClass the exporter content item. 4332 */ 4333 public static function add_content_item_to_user_favourites(string $componentname, int $contentitemid) { 4334 global $USER; 4335 4336 [ 4337 'componentname' => $componentname, 4338 'contentitemid' => $contentitemid, 4339 ] = self::validate_parameters(self::add_content_item_to_user_favourites_parameters(), 4340 [ 4341 'componentname' => $componentname, 4342 'contentitemid' => $contentitemid, 4343 ] 4344 ); 4345 4346 self::validate_context(context_user::instance($USER->id)); 4347 4348 $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); 4349 4350 return $contentitemservice->add_to_user_favourites($USER, $componentname, $contentitemid); 4351 } 4352 4353 /** 4354 * Returns description of method result value. 4355 * 4356 * @return \core_external\external_description 4357 */ 4358 public static function add_content_item_to_user_favourites_returns() { 4359 return \core_course\local\exporters\course_content_item_exporter::get_read_structure(); 4360 } 4361 4362 /** 4363 * Returns description of method parameters. 4364 * 4365 * @return external_function_parameters 4366 */ 4367 public static function remove_content_item_from_user_favourites_parameters() { 4368 return new external_function_parameters([ 4369 'componentname' => new external_value(PARAM_TEXT, 4370 'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED), 4371 'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 4372 ]); 4373 } 4374 4375 /** 4376 * Remove a content item from a user's favourites. 4377 * 4378 * @param string $componentname the name of the component from which this content item originates. 4379 * @param int $contentitemid the id of the content item. 4380 * @return stdClass the exported content item. 4381 */ 4382 public static function remove_content_item_from_user_favourites(string $componentname, int $contentitemid) { 4383 global $USER; 4384 4385 [ 4386 'componentname' => $componentname, 4387 'contentitemid' => $contentitemid, 4388 ] = self::validate_parameters(self::remove_content_item_from_user_favourites_parameters(), 4389 [ 4390 'componentname' => $componentname, 4391 'contentitemid' => $contentitemid, 4392 ] 4393 ); 4394 4395 self::validate_context(context_user::instance($USER->id)); 4396 4397 $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); 4398 4399 return $contentitemservice->remove_from_user_favourites($USER, $componentname, $contentitemid); 4400 } 4401 4402 /** 4403 * Returns description of method result value. 4404 * 4405 * @return \core_external\external_description 4406 */ 4407 public static function remove_content_item_from_user_favourites_returns() { 4408 return \core_course\local\exporters\course_content_item_exporter::get_read_structure(); 4409 } 4410 4411 /** 4412 * Returns description of method result value 4413 * 4414 * @return \core_external\external_description 4415 */ 4416 public static function get_course_content_items_returns() { 4417 return new external_single_structure([ 4418 'content_items' => new external_multiple_structure( 4419 \core_course\local\exporters\course_content_item_exporter::get_read_structure() 4420 ), 4421 ]); 4422 } 4423 4424 /** 4425 * Returns description of method parameters 4426 * 4427 * @return external_function_parameters 4428 */ 4429 public static function get_course_content_items_parameters() { 4430 return new external_function_parameters([ 4431 'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED), 4432 ]); 4433 } 4434 4435 /** 4436 * Given a course ID fetch all accessible modules for that course 4437 * 4438 * @param int $courseid The course we want to fetch the modules for 4439 * @return array Contains array of modules and their metadata 4440 */ 4441 public static function get_course_content_items(int $courseid) { 4442 global $USER; 4443 4444 [ 4445 'courseid' => $courseid, 4446 ] = self::validate_parameters(self::get_course_content_items_parameters(), [ 4447 'courseid' => $courseid, 4448 ]); 4449 4450 $coursecontext = context_course::instance($courseid); 4451 self::validate_context($coursecontext); 4452 $course = get_course($courseid); 4453 4454 $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); 4455 4456 $contentitems = $contentitemservice->get_content_items_for_user_in_course($USER, $course); 4457 return ['content_items' => $contentitems]; 4458 } 4459 4460 /** 4461 * Returns description of method parameters. 4462 * 4463 * @return external_function_parameters 4464 */ 4465 public static function toggle_activity_recommendation_parameters() { 4466 return new external_function_parameters([ 4467 'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)', VALUE_REQUIRED), 4468 'id' => new external_value(PARAM_INT, 'id of the activity or whatever', VALUE_REQUIRED), 4469 ]); 4470 } 4471 4472 /** 4473 * Update the recommendation for an activity item. 4474 * 4475 * @param string $area identifier for this activity. 4476 * @param int $id Associated id. This is needed in conjunction with the area to find the recommendation. 4477 * @return array some warnings or something. 4478 */ 4479 public static function toggle_activity_recommendation(string $area, int $id): array { 4480 ['area' => $area, 'id' => $id] = self::validate_parameters(self::toggle_activity_recommendation_parameters(), 4481 ['area' => $area, 'id' => $id]); 4482 4483 $context = context_system::instance(); 4484 self::validate_context($context); 4485 4486 require_capability('moodle/course:recommendactivity', $context); 4487 4488 $manager = \core_course\local\factory\content_item_service_factory::get_content_item_service(); 4489 4490 $status = $manager->toggle_recommendation($area, $id); 4491 return ['id' => $id, 'area' => $area, 'status' => $status]; 4492 } 4493 4494 /** 4495 * Returns warnings. 4496 * 4497 * @return \core_external\external_description 4498 */ 4499 public static function toggle_activity_recommendation_returns() { 4500 return new external_single_structure( 4501 [ 4502 'id' => new external_value(PARAM_INT, 'id of the activity or whatever'), 4503 'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)'), 4504 'status' => new external_value(PARAM_BOOL, 'If created or deleted'), 4505 ] 4506 ); 4507 } 4508 4509 /** 4510 * Returns description of method parameters 4511 * 4512 * @return external_function_parameters 4513 */ 4514 public static function get_activity_chooser_footer_parameters() { 4515 return new external_function_parameters([ 4516 'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED), 4517 'sectionid' => new external_value(PARAM_INT, 'ID of the section', VALUE_REQUIRED), 4518 ]); 4519 } 4520 4521 /** 4522 * Given a course ID we need to build up a footre for the chooser. 4523 * 4524 * @param int $courseid The course we want to fetch the modules for 4525 * @param int $sectionid The section we want to fetch the modules for 4526 * @return array 4527 */ 4528 public static function get_activity_chooser_footer(int $courseid, int $sectionid) { 4529 [ 4530 'courseid' => $courseid, 4531 'sectionid' => $sectionid, 4532 ] = self::validate_parameters(self::get_activity_chooser_footer_parameters(), [ 4533 'courseid' => $courseid, 4534 'sectionid' => $sectionid, 4535 ]); 4536 4537 $coursecontext = context_course::instance($courseid); 4538 self::validate_context($coursecontext); 4539 4540 $activeplugin = get_config('core', 'activitychooseractivefooter'); 4541 4542 if ($activeplugin !== COURSE_CHOOSER_FOOTER_NONE) { 4543 $footerdata = component_callback($activeplugin, 'custom_chooser_footer', [$courseid, $sectionid]); 4544 return [ 4545 'footer' => true, 4546 'customfooterjs' => $footerdata->get_footer_js_file(), 4547 'customfootertemplate' => $footerdata->get_footer_template(), 4548 'customcarouseltemplate' => $footerdata->get_carousel_template(), 4549 ]; 4550 } else { 4551 return [ 4552 'footer' => false, 4553 ]; 4554 } 4555 } 4556 4557 /** 4558 * Returns description of method result value 4559 * 4560 * @return \core_external\external_description 4561 */ 4562 public static function get_activity_chooser_footer_returns() { 4563 return new external_single_structure( 4564 [ 4565 'footer' => new external_value(PARAM_BOOL, 'Is a footer being return by this request?', VALUE_REQUIRED), 4566 'customfooterjs' => new external_value(PARAM_RAW, 'The path to the plugin JS file', VALUE_OPTIONAL), 4567 'customfootertemplate' => new external_value(PARAM_RAW, 'The prerendered footer', VALUE_OPTIONAL), 4568 'customcarouseltemplate' => new external_value(PARAM_RAW, 'Either "" or the prerendered carousel page', 4569 VALUE_OPTIONAL), 4570 ] 4571 ); 4572 } 4573 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body