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