[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 18 /** 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 require_once("$CFG->libdir/externallib.php"); 30 31 /** 32 * Course external functions 33 * 34 * @package core_course 35 * @category external 36 * @copyright 2011 Jerome Mouneyrac 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 * @since Moodle 2.2 39 */ 40 class core_course_external extends external_api { 41 42 /** 43 * Returns description of method parameters 44 * 45 * @return external_function_parameters 46 * @since Moodle 2.9 Options available 47 * @since Moodle 2.2 48 */ 49 public static function get_course_contents_parameters() { 50 return new external_function_parameters( 51 array('courseid' => new external_value(PARAM_INT, 'course id'), 52 'options' => new external_multiple_structure ( 53 new external_single_structure( 54 array( 55 'name' => new external_value(PARAM_ALPHANUM, 56 'The expected keys (value format) are: 57 excludemodules (bool) Do not return modules, return only the sections structure 58 excludecontents (bool) Do not return module contents (i.e: files inside a resource) 59 sectionid (int) Return only this section 60 sectionnumber (int) Return only this section with number (order) 61 cmid (int) Return only this module information (among the whole sections structure) 62 modname (string) Return only modules with this name "label, forum, etc..." 63 modid (int) Return only the module with this id (to be used with modname'), 64 'value' => new external_value(PARAM_RAW, 'the value of the option, 65 this param is personaly validated in the external function.') 66 ) 67 ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array()) 68 ) 69 ); 70 } 71 72 /** 73 * Get course contents 74 * 75 * @param int $courseid course id 76 * @param array $options Options for filtering the results, used since Moodle 2.9 77 * @return array 78 * @since Moodle 2.9 Options available 79 * @since Moodle 2.2 80 */ 81 public static function get_course_contents($courseid, $options = array()) { 82 global $CFG, $DB; 83 require_once($CFG->dirroot . "/course/lib.php"); 84 85 //validate parameter 86 $params = self::validate_parameters(self::get_course_contents_parameters(), 87 array('courseid' => $courseid, 'options' => $options)); 88 89 $filters = array(); 90 if (!empty($params['options'])) { 91 92 foreach ($params['options'] as $option) { 93 $name = trim($option['name']); 94 // Avoid duplicated options. 95 if (!isset($filters[$name])) { 96 switch ($name) { 97 case 'excludemodules': 98 case 'excludecontents': 99 $value = clean_param($option['value'], PARAM_BOOL); 100 $filters[$name] = $value; 101 break; 102 case 'sectionid': 103 case 'sectionnumber': 104 case 'cmid': 105 case 'modid': 106 $value = clean_param($option['value'], PARAM_INT); 107 if (is_numeric($value)) { 108 $filters[$name] = $value; 109 } else { 110 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 111 } 112 break; 113 case 'modname': 114 $value = clean_param($option['value'], PARAM_PLUGIN); 115 if ($value) { 116 $filters[$name] = $value; 117 } else { 118 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 119 } 120 break; 121 default: 122 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); 123 } 124 } 125 } 126 } 127 128 //retrieve the course 129 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); 130 131 if ($course->id != SITEID) { 132 // Check course format exist. 133 if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) { 134 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, 135 get_string('courseformatnotfound', 'error', $course->format)); 136 } else { 137 require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php'); 138 } 139 } 140 141 // now security checks 142 $context = context_course::instance($course->id, IGNORE_MISSING); 143 try { 144 self::validate_context($context); 145 } catch (Exception $e) { 146 $exceptionparam = new stdClass(); 147 $exceptionparam->message = $e->getMessage(); 148 $exceptionparam->courseid = $course->id; 149 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); 150 } 151 152 $canupdatecourse = has_capability('moodle/course:update', $context); 153 154 //create return value 155 $coursecontents = array(); 156 157 if ($canupdatecourse or $course->visible 158 or has_capability('moodle/course:viewhiddencourses', $context)) { 159 160 //retrieve sections 161 $modinfo = get_fast_modinfo($course); 162 $sections = $modinfo->get_section_info_all(); 163 164 //for each sections (first displayed to last displayed) 165 $modinfosections = $modinfo->get_sections(); 166 foreach ($sections as $key => $section) { 167 168 if (!$section->uservisible) { 169 continue; 170 } 171 172 // This becomes true when we are filtering and we found the value to filter with. 173 $sectionfound = false; 174 175 // Filter by section id. 176 if (!empty($filters['sectionid'])) { 177 if ($section->id != $filters['sectionid']) { 178 continue; 179 } else { 180 $sectionfound = true; 181 } 182 } 183 184 // Filter by section number. Note that 0 is a valid section number. 185 if (isset($filters['sectionnumber'])) { 186 if ($key != $filters['sectionnumber']) { 187 continue; 188 } else { 189 $sectionfound = true; 190 } 191 } 192 193 // reset $sectioncontents 194 $sectionvalues = array(); 195 $sectionvalues['id'] = $section->id; 196 $sectionvalues['name'] = get_section_name($course, $section); 197 $sectionvalues['visible'] = $section->visible; 198 199 $options = (object) array('noclean' => true); 200 list($sectionvalues['summary'], $sectionvalues['summaryformat']) = 201 external_format_text($section->summary, $section->summaryformat, 202 $context->id, 'course', 'section', $section->id, $options); 203 $sectionvalues['section'] = $section->section; 204 $sectioncontents = array(); 205 206 //for each module of the section 207 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) { 208 foreach ($modinfosections[$section->section] as $cmid) { 209 $cm = $modinfo->cms[$cmid]; 210 211 // stop here if the module is not visible to the user 212 if (!$cm->uservisible) { 213 continue; 214 } 215 216 // This becomes true when we are filtering and we found the value to filter with. 217 $modfound = false; 218 219 // Filter by cmid. 220 if (!empty($filters['cmid'])) { 221 if ($cmid != $filters['cmid']) { 222 continue; 223 } else { 224 $modfound = true; 225 } 226 } 227 228 // Filter by module name and id. 229 if (!empty($filters['modname'])) { 230 if ($cm->modname != $filters['modname']) { 231 continue; 232 } else if (!empty($filters['modid'])) { 233 if ($cm->instance != $filters['modid']) { 234 continue; 235 } else { 236 // Note that if we are only filtering by modname we don't break the loop. 237 $modfound = true; 238 } 239 } 240 } 241 242 $module = array(); 243 244 $modcontext = context_module::instance($cm->id); 245 246 //common info (for people being able to see the module or availability dates) 247 $module['id'] = $cm->id; 248 $module['name'] = external_format_string($cm->name, $modcontext->id); 249 $module['instance'] = $cm->instance; 250 $module['modname'] = $cm->modname; 251 $module['modplural'] = $cm->modplural; 252 $module['modicon'] = $cm->get_icon_url()->out(false); 253 $module['indent'] = $cm->indent; 254 255 if (!empty($cm->showdescription) or $cm->modname == 'label') { 256 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML. 257 list($module['description'], $descriptionformat) = external_format_text($cm->content, 258 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id); 259 } 260 261 //url of the module 262 $url = $cm->url; 263 if ($url) { //labels don't have url 264 $module['url'] = $url->out(false); 265 } 266 267 $canviewhidden = has_capability('moodle/course:viewhiddenactivities', 268 context_module::instance($cm->id)); 269 //user that can view hidden module should know about the visibility 270 $module['visible'] = $cm->visible; 271 272 // Availability date (also send to user who can see hidden module). 273 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) { 274 $module['availability'] = $cm->availability; 275 } 276 277 $baseurl = 'webservice/pluginfile.php'; 278 279 //call $modulename_export_contents 280 //(each module callback take care about checking the capabilities) 281 282 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php'); 283 $getcontentfunction = $cm->modname.'_export_contents'; 284 if (function_exists($getcontentfunction)) { 285 if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) { 286 $module['contents'] = $contents; 287 } else { 288 $module['contents'] = array(); 289 } 290 } 291 292 //assign result to $sectioncontents 293 $sectioncontents[] = $module; 294 295 // If we just did a filtering, break the loop. 296 if ($modfound) { 297 break; 298 } 299 300 } 301 } 302 $sectionvalues['modules'] = $sectioncontents; 303 304 // assign result to $coursecontents 305 $coursecontents[] = $sectionvalues; 306 307 // Break the loop if we are filtering. 308 if ($sectionfound) { 309 break; 310 } 311 } 312 } 313 return $coursecontents; 314 } 315 316 /** 317 * Returns description of method result value 318 * 319 * @return external_description 320 * @since Moodle 2.2 321 */ 322 public static function get_course_contents_returns() { 323 return new external_multiple_structure( 324 new external_single_structure( 325 array( 326 'id' => new external_value(PARAM_INT, 'Section ID'), 327 'name' => new external_value(PARAM_TEXT, 'Section name'), 328 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL), 329 'summary' => new external_value(PARAM_RAW, 'Section description'), 330 'summaryformat' => new external_format_value('summary'), 331 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL), 332 'modules' => new external_multiple_structure( 333 new external_single_structure( 334 array( 335 'id' => new external_value(PARAM_INT, 'activity id'), 336 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL), 337 'name' => new external_value(PARAM_RAW, 'activity module name'), 338 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL), 339 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL), 340 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL), 341 'modicon' => new external_value(PARAM_URL, 'activity icon url'), 342 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'), 343 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'), 344 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL), 345 'indent' => new external_value(PARAM_INT, 'number of identation in the site'), 346 'contents' => new external_multiple_structure( 347 new external_single_structure( 348 array( 349 // content info 350 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'), 351 'filename'=> new external_value(PARAM_FILE, 'filename'), 352 'filepath'=> new external_value(PARAM_PATH, 'filepath'), 353 'filesize'=> new external_value(PARAM_INT, 'filesize'), 354 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL), 355 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL), 356 'timecreated' => new external_value(PARAM_INT, 'Time created'), 357 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 358 'sortorder' => new external_value(PARAM_INT, 'Content sort order'), 359 360 // copyright related info 361 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'), 362 'author' => new external_value(PARAM_TEXT, 'Content owner'), 363 'license' => new external_value(PARAM_TEXT, 'Content license'), 364 ) 365 ), VALUE_DEFAULT, array() 366 ) 367 ) 368 ), 'list of module' 369 ) 370 ) 371 ) 372 ); 373 } 374 375 /** 376 * Returns description of method parameters 377 * 378 * @return external_function_parameters 379 * @since Moodle 2.3 380 */ 381 public static function get_courses_parameters() { 382 return new external_function_parameters( 383 array('options' => new external_single_structure( 384 array('ids' => new external_multiple_structure( 385 new external_value(PARAM_INT, 'Course id') 386 , 'List of course id. If empty return all courses 387 except front page course.', 388 VALUE_OPTIONAL) 389 ), 'options - operator OR is used', VALUE_DEFAULT, array()) 390 ) 391 ); 392 } 393 394 /** 395 * Get courses 396 * 397 * @param array $options It contains an array (list of ids) 398 * @return array 399 * @since Moodle 2.2 400 */ 401 public static function get_courses($options = array()) { 402 global $CFG, $DB; 403 require_once($CFG->dirroot . "/course/lib.php"); 404 405 //validate parameter 406 $params = self::validate_parameters(self::get_courses_parameters(), 407 array('options' => $options)); 408 409 //retrieve courses 410 if (!array_key_exists('ids', $params['options']) 411 or empty($params['options']['ids'])) { 412 $courses = $DB->get_records('course'); 413 } else { 414 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']); 415 } 416 417 //create return value 418 $coursesinfo = array(); 419 foreach ($courses as $course) { 420 421 // now security checks 422 $context = context_course::instance($course->id, IGNORE_MISSING); 423 $courseformatoptions = course_get_format($course)->get_format_options(); 424 try { 425 self::validate_context($context); 426 } catch (Exception $e) { 427 $exceptionparam = new stdClass(); 428 $exceptionparam->message = $e->getMessage(); 429 $exceptionparam->courseid = $course->id; 430 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); 431 } 432 require_capability('moodle/course:view', $context); 433 434 $courseinfo = array(); 435 $courseinfo['id'] = $course->id; 436 $courseinfo['fullname'] = external_format_string($course->fullname, $context->id); 437 $courseinfo['shortname'] = external_format_string($course->shortname, $context->id); 438 $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id); 439 $courseinfo['categoryid'] = $course->category; 440 list($courseinfo['summary'], $courseinfo['summaryformat']) = 441 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0); 442 $courseinfo['format'] = $course->format; 443 $courseinfo['startdate'] = $course->startdate; 444 if (array_key_exists('numsections', $courseformatoptions)) { 445 // For backward-compartibility 446 $courseinfo['numsections'] = $courseformatoptions['numsections']; 447 } 448 449 //some field should be returned only if the user has update permission 450 $courseadmin = has_capability('moodle/course:update', $context); 451 if ($courseadmin) { 452 $courseinfo['categorysortorder'] = $course->sortorder; 453 $courseinfo['idnumber'] = $course->idnumber; 454 $courseinfo['showgrades'] = $course->showgrades; 455 $courseinfo['showreports'] = $course->showreports; 456 $courseinfo['newsitems'] = $course->newsitems; 457 $courseinfo['visible'] = $course->visible; 458 $courseinfo['maxbytes'] = $course->maxbytes; 459 if (array_key_exists('hiddensections', $courseformatoptions)) { 460 // For backward-compartibility 461 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections']; 462 } 463 $courseinfo['groupmode'] = $course->groupmode; 464 $courseinfo['groupmodeforce'] = $course->groupmodeforce; 465 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid; 466 $courseinfo['lang'] = $course->lang; 467 $courseinfo['timecreated'] = $course->timecreated; 468 $courseinfo['timemodified'] = $course->timemodified; 469 $courseinfo['forcetheme'] = $course->theme; 470 $courseinfo['enablecompletion'] = $course->enablecompletion; 471 $courseinfo['completionnotify'] = $course->completionnotify; 472 $courseinfo['courseformatoptions'] = array(); 473 foreach ($courseformatoptions as $key => $value) { 474 $courseinfo['courseformatoptions'][] = array( 475 'name' => $key, 476 'value' => $value 477 ); 478 } 479 } 480 481 if ($courseadmin or $course->visible 482 or has_capability('moodle/course:viewhiddencourses', $context)) { 483 $coursesinfo[] = $courseinfo; 484 } 485 } 486 487 return $coursesinfo; 488 } 489 490 /** 491 * Returns description of method result value 492 * 493 * @return external_description 494 * @since Moodle 2.2 495 */ 496 public static function get_courses_returns() { 497 return new external_multiple_structure( 498 new external_single_structure( 499 array( 500 'id' => new external_value(PARAM_INT, 'course id'), 501 'shortname' => new external_value(PARAM_TEXT, 'course short name'), 502 'categoryid' => new external_value(PARAM_INT, 'category id'), 503 'categorysortorder' => new external_value(PARAM_INT, 504 'sort order into the category', VALUE_OPTIONAL), 505 'fullname' => new external_value(PARAM_TEXT, 'full name'), 506 'displayname' => new external_value(PARAM_TEXT, 'course display name'), 507 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 508 'summary' => new external_value(PARAM_RAW, 'summary'), 509 'summaryformat' => new external_format_value('summary'), 510 'format' => new external_value(PARAM_PLUGIN, 511 'course format: weeks, topics, social, site,..'), 512 'showgrades' => new external_value(PARAM_INT, 513 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 514 'newsitems' => new external_value(PARAM_INT, 515 'number of recent items appearing on the course page', VALUE_OPTIONAL), 516 'startdate' => new external_value(PARAM_INT, 517 'timestamp when the course start'), 518 'numsections' => new external_value(PARAM_INT, 519 '(deprecated, use courseformatoptions) number of weeks/topics', 520 VALUE_OPTIONAL), 521 'maxbytes' => new external_value(PARAM_INT, 522 'largest size of file that can be uploaded into the course', 523 VALUE_OPTIONAL), 524 'showreports' => new external_value(PARAM_INT, 525 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 526 'visible' => new external_value(PARAM_INT, 527 '1: available to student, 0:not available', VALUE_OPTIONAL), 528 'hiddensections' => new external_value(PARAM_INT, 529 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', 530 VALUE_OPTIONAL), 531 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', 532 VALUE_OPTIONAL), 533 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', 534 VALUE_OPTIONAL), 535 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', 536 VALUE_OPTIONAL), 537 'timecreated' => new external_value(PARAM_INT, 538 'timestamp when the course have been created', VALUE_OPTIONAL), 539 'timemodified' => new external_value(PARAM_INT, 540 'timestamp when the course have been modified', VALUE_OPTIONAL), 541 'enablecompletion' => new external_value(PARAM_INT, 542 'Enabled, control via completion and activity settings. Disbaled, 543 not shown in activity settings.', 544 VALUE_OPTIONAL), 545 'completionnotify' => new external_value(PARAM_INT, 546 '1: yes 0: no', VALUE_OPTIONAL), 547 'lang' => new external_value(PARAM_SAFEDIR, 548 'forced course language', VALUE_OPTIONAL), 549 'forcetheme' => new external_value(PARAM_PLUGIN, 550 'name of the force theme', VALUE_OPTIONAL), 551 'courseformatoptions' => new external_multiple_structure( 552 new external_single_structure( 553 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 554 'value' => new external_value(PARAM_RAW, 'course format option value') 555 )), 556 'additional options for particular course format', VALUE_OPTIONAL 557 ), 558 ), 'course' 559 ) 560 ); 561 } 562 563 /** 564 * Returns description of method parameters 565 * 566 * @return external_function_parameters 567 * @since Moodle 2.2 568 */ 569 public static function create_courses_parameters() { 570 $courseconfig = get_config('moodlecourse'); //needed for many default values 571 return new external_function_parameters( 572 array( 573 'courses' => new external_multiple_structure( 574 new external_single_structure( 575 array( 576 'fullname' => new external_value(PARAM_TEXT, 'full name'), 577 'shortname' => new external_value(PARAM_TEXT, 'course short name'), 578 'categoryid' => new external_value(PARAM_INT, 'category id'), 579 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 580 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), 581 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT), 582 'format' => new external_value(PARAM_PLUGIN, 583 'course format: weeks, topics, social, site,..', 584 VALUE_DEFAULT, $courseconfig->format), 585 'showgrades' => new external_value(PARAM_INT, 586 '1 if grades are shown, otherwise 0', VALUE_DEFAULT, 587 $courseconfig->showgrades), 588 'newsitems' => new external_value(PARAM_INT, 589 'number of recent items appearing on the course page', 590 VALUE_DEFAULT, $courseconfig->newsitems), 591 'startdate' => new external_value(PARAM_INT, 592 'timestamp when the course start', VALUE_OPTIONAL), 593 'numsections' => new external_value(PARAM_INT, 594 '(deprecated, use courseformatoptions) number of weeks/topics', 595 VALUE_OPTIONAL), 596 'maxbytes' => new external_value(PARAM_INT, 597 'largest size of file that can be uploaded into the course', 598 VALUE_DEFAULT, $courseconfig->maxbytes), 599 'showreports' => new external_value(PARAM_INT, 600 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT, 601 $courseconfig->showreports), 602 'visible' => new external_value(PARAM_INT, 603 '1: available to student, 0:not available', VALUE_OPTIONAL), 604 'hiddensections' => new external_value(PARAM_INT, 605 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', 606 VALUE_OPTIONAL), 607 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', 608 VALUE_DEFAULT, $courseconfig->groupmode), 609 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', 610 VALUE_DEFAULT, $courseconfig->groupmodeforce), 611 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', 612 VALUE_DEFAULT, 0), 613 'enablecompletion' => new external_value(PARAM_INT, 614 'Enabled, control via completion and activity settings. Disabled, 615 not shown in activity settings.', 616 VALUE_OPTIONAL), 617 'completionnotify' => new external_value(PARAM_INT, 618 '1: yes 0: no', VALUE_OPTIONAL), 619 'lang' => new external_value(PARAM_SAFEDIR, 620 'forced course language', VALUE_OPTIONAL), 621 'forcetheme' => new external_value(PARAM_PLUGIN, 622 'name of the force theme', VALUE_OPTIONAL), 623 'courseformatoptions' => new external_multiple_structure( 624 new external_single_structure( 625 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 626 'value' => new external_value(PARAM_RAW, 'course format option value') 627 )), 628 'additional options for particular course format', VALUE_OPTIONAL), 629 ) 630 ), 'courses to create' 631 ) 632 ) 633 ); 634 } 635 636 /** 637 * Create courses 638 * 639 * @param array $courses 640 * @return array courses (id and shortname only) 641 * @since Moodle 2.2 642 */ 643 public static function create_courses($courses) { 644 global $CFG, $DB; 645 require_once($CFG->dirroot . "/course/lib.php"); 646 require_once($CFG->libdir . '/completionlib.php'); 647 648 $params = self::validate_parameters(self::create_courses_parameters(), 649 array('courses' => $courses)); 650 651 $availablethemes = core_component::get_plugin_list('theme'); 652 $availablelangs = get_string_manager()->get_list_of_translations(); 653 654 $transaction = $DB->start_delegated_transaction(); 655 656 foreach ($params['courses'] as $course) { 657 658 // Ensure the current user is allowed to run this function 659 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING); 660 try { 661 self::validate_context($context); 662 } catch (Exception $e) { 663 $exceptionparam = new stdClass(); 664 $exceptionparam->message = $e->getMessage(); 665 $exceptionparam->catid = $course['categoryid']; 666 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); 667 } 668 require_capability('moodle/course:create', $context); 669 670 // Make sure lang is valid 671 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) { 672 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); 673 } 674 675 // Make sure theme is valid 676 if (array_key_exists('forcetheme', $course)) { 677 if (!empty($CFG->allowcoursethemes)) { 678 if (empty($availablethemes[$course['forcetheme']])) { 679 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); 680 } else { 681 $course['theme'] = $course['forcetheme']; 682 } 683 } 684 } 685 686 //force visibility if ws user doesn't have the permission to set it 687 $category = $DB->get_record('course_categories', array('id' => $course['categoryid'])); 688 if (!has_capability('moodle/course:visibility', $context)) { 689 $course['visible'] = $category->visible; 690 } 691 692 //set default value for completion 693 $courseconfig = get_config('moodlecourse'); 694 if (completion_info::is_enabled_for_site()) { 695 if (!array_key_exists('enablecompletion', $course)) { 696 $course['enablecompletion'] = $courseconfig->enablecompletion; 697 } 698 } else { 699 $course['enablecompletion'] = 0; 700 } 701 702 $course['category'] = $course['categoryid']; 703 704 // Summary format. 705 $course['summaryformat'] = external_validate_format($course['summaryformat']); 706 707 if (!empty($course['courseformatoptions'])) { 708 foreach ($course['courseformatoptions'] as $option) { 709 $course[$option['name']] = $option['value']; 710 } 711 } 712 713 //Note: create_course() core function check shortname, idnumber, category 714 $course['id'] = create_course((object) $course)->id; 715 716 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']); 717 } 718 719 $transaction->allow_commit(); 720 721 return $resultcourses; 722 } 723 724 /** 725 * Returns description of method result value 726 * 727 * @return external_description 728 * @since Moodle 2.2 729 */ 730 public static function create_courses_returns() { 731 return new external_multiple_structure( 732 new external_single_structure( 733 array( 734 'id' => new external_value(PARAM_INT, 'course id'), 735 'shortname' => new external_value(PARAM_TEXT, 'short name'), 736 ) 737 ) 738 ); 739 } 740 741 /** 742 * Update courses 743 * 744 * @return external_function_parameters 745 * @since Moodle 2.5 746 */ 747 public static function update_courses_parameters() { 748 return new external_function_parameters( 749 array( 750 'courses' => new external_multiple_structure( 751 new external_single_structure( 752 array( 753 'id' => new external_value(PARAM_INT, 'ID of the course'), 754 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL), 755 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL), 756 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL), 757 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 758 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), 759 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL), 760 'format' => new external_value(PARAM_PLUGIN, 761 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL), 762 'showgrades' => new external_value(PARAM_INT, 763 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 764 'newsitems' => new external_value(PARAM_INT, 765 'number of recent items appearing on the course page', VALUE_OPTIONAL), 766 'startdate' => new external_value(PARAM_INT, 767 'timestamp when the course start', VALUE_OPTIONAL), 768 'numsections' => new external_value(PARAM_INT, 769 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL), 770 'maxbytes' => new external_value(PARAM_INT, 771 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL), 772 'showreports' => new external_value(PARAM_INT, 773 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 774 'visible' => new external_value(PARAM_INT, 775 '1: available to student, 0:not available', VALUE_OPTIONAL), 776 'hiddensections' => new external_value(PARAM_INT, 777 '(deprecated, use courseformatoptions) How the hidden sections in the course are 778 displayed to students', VALUE_OPTIONAL), 779 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 780 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 781 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 782 'enablecompletion' => new external_value(PARAM_INT, 783 'Enabled, control via completion and activity settings. Disabled, 784 not shown in activity settings.', VALUE_OPTIONAL), 785 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 786 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL), 787 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL), 788 'courseformatoptions' => new external_multiple_structure( 789 new external_single_structure( 790 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 791 'value' => new external_value(PARAM_RAW, 'course format option value') 792 )), 793 'additional options for particular course format', VALUE_OPTIONAL), 794 ) 795 ), 'courses to update' 796 ) 797 ) 798 ); 799 } 800 801 /** 802 * Update courses 803 * 804 * @param array $courses 805 * @since Moodle 2.5 806 */ 807 public static function update_courses($courses) { 808 global $CFG, $DB; 809 require_once($CFG->dirroot . "/course/lib.php"); 810 $warnings = array(); 811 812 $params = self::validate_parameters(self::update_courses_parameters(), 813 array('courses' => $courses)); 814 815 $availablethemes = core_component::get_plugin_list('theme'); 816 $availablelangs = get_string_manager()->get_list_of_translations(); 817 818 foreach ($params['courses'] as $course) { 819 // Catch any exception while updating course and return as warning to user. 820 try { 821 // Ensure the current user is allowed to run this function. 822 $context = context_course::instance($course['id'], MUST_EXIST); 823 self::validate_context($context); 824 825 $oldcourse = course_get_format($course['id'])->get_course(); 826 827 require_capability('moodle/course:update', $context); 828 829 // Check if user can change category. 830 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) { 831 require_capability('moodle/course:changecategory', $context); 832 $course['category'] = $course['categoryid']; 833 } 834 835 // Check if the user can change fullname. 836 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) { 837 require_capability('moodle/course:changefullname', $context); 838 } 839 840 // Check if the user can change shortname. 841 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) { 842 require_capability('moodle/course:changeshortname', $context); 843 } 844 845 // Check if the user can change the idnumber. 846 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) { 847 require_capability('moodle/course:changeidnumber', $context); 848 } 849 850 // Check if user can change summary. 851 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) { 852 require_capability('moodle/course:changesummary', $context); 853 } 854 855 // Summary format. 856 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) { 857 require_capability('moodle/course:changesummary', $context); 858 $course['summaryformat'] = external_validate_format($course['summaryformat']); 859 } 860 861 // Check if user can change visibility. 862 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) { 863 require_capability('moodle/course:visibility', $context); 864 } 865 866 // Make sure lang is valid. 867 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) { 868 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); 869 } 870 871 // Make sure theme is valid. 872 if (array_key_exists('forcetheme', $course)) { 873 if (!empty($CFG->allowcoursethemes)) { 874 if (empty($availablethemes[$course['forcetheme']])) { 875 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); 876 } else { 877 $course['theme'] = $course['forcetheme']; 878 } 879 } 880 } 881 882 // Make sure completion is enabled before setting it. 883 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) { 884 $course['enabledcompletion'] = 0; 885 } 886 887 // Make sure maxbytes are less then CFG->maxbytes. 888 if (array_key_exists('maxbytes', $course)) { 889 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']); 890 } 891 892 if (!empty($course['courseformatoptions'])) { 893 foreach ($course['courseformatoptions'] as $option) { 894 if (isset($option['name']) && isset($option['value'])) { 895 $course[$option['name']] = $option['value']; 896 } 897 } 898 } 899 900 // Update course if user has all required capabilities. 901 update_course((object) $course); 902 } catch (Exception $e) { 903 $warning = array(); 904 $warning['item'] = 'course'; 905 $warning['itemid'] = $course['id']; 906 if ($e instanceof moodle_exception) { 907 $warning['warningcode'] = $e->errorcode; 908 } else { 909 $warning['warningcode'] = $e->getCode(); 910 } 911 $warning['message'] = $e->getMessage(); 912 $warnings[] = $warning; 913 } 914 } 915 916 $result = array(); 917 $result['warnings'] = $warnings; 918 return $result; 919 } 920 921 /** 922 * Returns description of method result value 923 * 924 * @return external_description 925 * @since Moodle 2.5 926 */ 927 public static function update_courses_returns() { 928 return new external_single_structure( 929 array( 930 'warnings' => new external_warnings() 931 ) 932 ); 933 } 934 935 /** 936 * Returns description of method parameters 937 * 938 * @return external_function_parameters 939 * @since Moodle 2.2 940 */ 941 public static function delete_courses_parameters() { 942 return new external_function_parameters( 943 array( 944 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')), 945 ) 946 ); 947 } 948 949 /** 950 * Delete courses 951 * 952 * @param array $courseids A list of course ids 953 * @since Moodle 2.2 954 */ 955 public static function delete_courses($courseids) { 956 global $CFG, $DB; 957 require_once($CFG->dirroot."/course/lib.php"); 958 959 // Parameter validation. 960 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids)); 961 962 $warnings = array(); 963 964 foreach ($params['courseids'] as $courseid) { 965 $course = $DB->get_record('course', array('id' => $courseid)); 966 967 if ($course === false) { 968 $warnings[] = array( 969 'item' => 'course', 970 'itemid' => $courseid, 971 'warningcode' => 'unknowncourseidnumber', 972 'message' => 'Unknown course ID ' . $courseid 973 ); 974 continue; 975 } 976 977 // Check if the context is valid. 978 $coursecontext = context_course::instance($course->id); 979 self::validate_context($coursecontext); 980 981 // Check if the current user has permission. 982 if (!can_delete_course($courseid)) { 983 $warnings[] = array( 984 'item' => 'course', 985 'itemid' => $courseid, 986 'warningcode' => 'cannotdeletecourse', 987 'message' => 'You do not have the permission to delete this course' . $courseid 988 ); 989 continue; 990 } 991 992 if (delete_course($course, false) === false) { 993 $warnings[] = array( 994 'item' => 'course', 995 'itemid' => $courseid, 996 'warningcode' => 'cannotdeletecategorycourse', 997 'message' => 'Course ' . $courseid . ' failed to be deleted' 998 ); 999 continue; 1000 } 1001 } 1002 1003 fix_course_sortorder(); 1004 1005 return array('warnings' => $warnings); 1006 } 1007 1008 /** 1009 * Returns description of method result value 1010 * 1011 * @return external_description 1012 * @since Moodle 2.2 1013 */ 1014 public static function delete_courses_returns() { 1015 return new external_single_structure( 1016 array( 1017 'warnings' => new external_warnings() 1018 ) 1019 ); 1020 } 1021 1022 /** 1023 * Returns description of method parameters 1024 * 1025 * @return external_function_parameters 1026 * @since Moodle 2.3 1027 */ 1028 public static function duplicate_course_parameters() { 1029 return new external_function_parameters( 1030 array( 1031 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'), 1032 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'), 1033 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'), 1034 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'), 1035 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1), 1036 'options' => new external_multiple_structure( 1037 new external_single_structure( 1038 array( 1039 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name: 1040 "activities" (int) Include course activites (default to 1 that is equal to yes), 1041 "blocks" (int) Include course blocks (default to 1 that is equal to yes), 1042 "filters" (int) Include course filters (default to 1 that is equal to yes), 1043 "users" (int) Include users (default to 0 that is equal to no), 1044 "role_assignments" (int) Include role assignments (default to 0 that is equal to no), 1045 "comments" (int) Include user comments (default to 0 that is equal to no), 1046 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no), 1047 "logs" (int) Include course logs (default to 0 that is equal to no), 1048 "grade_histories" (int) Include histories (default to 0 that is equal to no)' 1049 ), 1050 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' 1051 ) 1052 ) 1053 ), VALUE_DEFAULT, array() 1054 ), 1055 ) 1056 ); 1057 } 1058 1059 /** 1060 * Duplicate a course 1061 * 1062 * @param int $courseid 1063 * @param string $fullname Duplicated course fullname 1064 * @param string $shortname Duplicated course shortname 1065 * @param int $categoryid Duplicated course parent category id 1066 * @param int $visible Duplicated course availability 1067 * @param array $options List of backup options 1068 * @return array New course info 1069 * @since Moodle 2.3 1070 */ 1071 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) { 1072 global $CFG, $USER, $DB; 1073 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1074 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1075 1076 // Parameter validation. 1077 $params = self::validate_parameters( 1078 self::duplicate_course_parameters(), 1079 array( 1080 'courseid' => $courseid, 1081 'fullname' => $fullname, 1082 'shortname' => $shortname, 1083 'categoryid' => $categoryid, 1084 'visible' => $visible, 1085 'options' => $options 1086 ) 1087 ); 1088 1089 // Context validation. 1090 1091 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) { 1092 throw new moodle_exception('invalidcourseid', 'error'); 1093 } 1094 1095 // Category where duplicated course is going to be created. 1096 $categorycontext = context_coursecat::instance($params['categoryid']); 1097 self::validate_context($categorycontext); 1098 1099 // Course to be duplicated. 1100 $coursecontext = context_course::instance($course->id); 1101 self::validate_context($coursecontext); 1102 1103 $backupdefaults = array( 1104 'activities' => 1, 1105 'blocks' => 1, 1106 'filters' => 1, 1107 'users' => 0, 1108 'role_assignments' => 0, 1109 'comments' => 0, 1110 'userscompletion' => 0, 1111 'logs' => 0, 1112 'grade_histories' => 0 1113 ); 1114 1115 $backupsettings = array(); 1116 // Check for backup and restore options. 1117 if (!empty($params['options'])) { 1118 foreach ($params['options'] as $option) { 1119 1120 // Strict check for a correct value (allways 1 or 0, true or false). 1121 $value = clean_param($option['value'], PARAM_INT); 1122 1123 if ($value !== 0 and $value !== 1) { 1124 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1125 } 1126 1127 if (!isset($backupdefaults[$option['name']])) { 1128 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1129 } 1130 1131 $backupsettings[$option['name']] = $value; 1132 } 1133 } 1134 1135 // Capability checking. 1136 1137 // The backup controller check for this currently, this may be redundant. 1138 require_capability('moodle/course:create', $categorycontext); 1139 require_capability('moodle/restore:restorecourse', $categorycontext); 1140 require_capability('moodle/backup:backupcourse', $coursecontext); 1141 1142 if (!empty($backupsettings['users'])) { 1143 require_capability('moodle/backup:userinfo', $coursecontext); 1144 require_capability('moodle/restore:userinfo', $categorycontext); 1145 } 1146 1147 // Check if the shortname is used. 1148 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) { 1149 foreach ($foundcourses as $foundcourse) { 1150 $foundcoursenames[] = $foundcourse->fullname; 1151 } 1152 1153 $foundcoursenamestring = implode(',', $foundcoursenames); 1154 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring); 1155 } 1156 1157 // Backup the course. 1158 1159 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 1160 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id); 1161 1162 foreach ($backupsettings as $name => $value) { 1163 $bc->get_plan()->get_setting($name)->set_value($value); 1164 } 1165 1166 $backupid = $bc->get_backupid(); 1167 $backupbasepath = $bc->get_plan()->get_basepath(); 1168 1169 $bc->execute_plan(); 1170 $results = $bc->get_results(); 1171 $file = $results['backup_destination']; 1172 1173 $bc->destroy(); 1174 1175 // Restore the backup immediately. 1176 1177 // Check if we need to unzip the file because the backup temp dir does not contains backup files. 1178 if (!file_exists($backupbasepath . "/moodle_backup.xml")) { 1179 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath); 1180 } 1181 1182 // Create new course. 1183 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']); 1184 1185 $rc = new restore_controller($backupid, $newcourseid, 1186 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE); 1187 1188 foreach ($backupsettings as $name => $value) { 1189 $setting = $rc->get_plan()->get_setting($name); 1190 if ($setting->get_status() == backup_setting::NOT_LOCKED) { 1191 $setting->set_value($value); 1192 } 1193 } 1194 1195 if (!$rc->execute_precheck()) { 1196 $precheckresults = $rc->get_precheck_results(); 1197 if (is_array($precheckresults) && !empty($precheckresults['errors'])) { 1198 if (empty($CFG->keeptempdirectoriesonbackup)) { 1199 fulldelete($backupbasepath); 1200 } 1201 1202 $errorinfo = ''; 1203 1204 foreach ($precheckresults['errors'] as $error) { 1205 $errorinfo .= $error; 1206 } 1207 1208 if (array_key_exists('warnings', $precheckresults)) { 1209 foreach ($precheckresults['warnings'] as $warning) { 1210 $errorinfo .= $warning; 1211 } 1212 } 1213 1214 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); 1215 } 1216 } 1217 1218 $rc->execute_plan(); 1219 $rc->destroy(); 1220 1221 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST); 1222 $course->fullname = $params['fullname']; 1223 $course->shortname = $params['shortname']; 1224 $course->visible = $params['visible']; 1225 1226 // Set shortname and fullname back. 1227 $DB->update_record('course', $course); 1228 1229 if (empty($CFG->keeptempdirectoriesonbackup)) { 1230 fulldelete($backupbasepath); 1231 } 1232 1233 // Delete the course backup file created by this WebService. Originally located in the course backups area. 1234 $file->delete(); 1235 1236 return array('id' => $course->id, 'shortname' => $course->shortname); 1237 } 1238 1239 /** 1240 * Returns description of method result value 1241 * 1242 * @return external_description 1243 * @since Moodle 2.3 1244 */ 1245 public static function duplicate_course_returns() { 1246 return new external_single_structure( 1247 array( 1248 'id' => new external_value(PARAM_INT, 'course id'), 1249 'shortname' => new external_value(PARAM_TEXT, 'short name'), 1250 ) 1251 ); 1252 } 1253 1254 /** 1255 * Returns description of method parameters for import_course 1256 * 1257 * @return external_function_parameters 1258 * @since Moodle 2.4 1259 */ 1260 public static function import_course_parameters() { 1261 return new external_function_parameters( 1262 array( 1263 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'), 1264 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'), 1265 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0), 1266 'options' => new external_multiple_structure( 1267 new external_single_structure( 1268 array( 1269 'name' => new external_value(PARAM_ALPHA, 'The backup option name: 1270 "activities" (int) Include course activites (default to 1 that is equal to yes), 1271 "blocks" (int) Include course blocks (default to 1 that is equal to yes), 1272 "filters" (int) Include course filters (default to 1 that is equal to yes)' 1273 ), 1274 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' 1275 ) 1276 ) 1277 ), VALUE_DEFAULT, array() 1278 ), 1279 ) 1280 ); 1281 } 1282 1283 /** 1284 * Imports a course 1285 * 1286 * @param int $importfrom The id of the course we are importing from 1287 * @param int $importto The id of the course we are importing to 1288 * @param bool $deletecontent Whether to delete the course we are importing to content 1289 * @param array $options List of backup options 1290 * @return null 1291 * @since Moodle 2.4 1292 */ 1293 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) { 1294 global $CFG, $USER, $DB; 1295 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1296 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1297 1298 // Parameter validation. 1299 $params = self::validate_parameters( 1300 self::import_course_parameters(), 1301 array( 1302 'importfrom' => $importfrom, 1303 'importto' => $importto, 1304 'deletecontent' => $deletecontent, 1305 'options' => $options 1306 ) 1307 ); 1308 1309 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) { 1310 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']); 1311 } 1312 1313 // Context validation. 1314 1315 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) { 1316 throw new moodle_exception('invalidcourseid', 'error'); 1317 } 1318 1319 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) { 1320 throw new moodle_exception('invalidcourseid', 'error'); 1321 } 1322 1323 $importfromcontext = context_course::instance($importfrom->id); 1324 self::validate_context($importfromcontext); 1325 1326 $importtocontext = context_course::instance($importto->id); 1327 self::validate_context($importtocontext); 1328 1329 $backupdefaults = array( 1330 'activities' => 1, 1331 'blocks' => 1, 1332 'filters' => 1 1333 ); 1334 1335 $backupsettings = array(); 1336 1337 // Check for backup and restore options. 1338 if (!empty($params['options'])) { 1339 foreach ($params['options'] as $option) { 1340 1341 // Strict check for a correct value (allways 1 or 0, true or false). 1342 $value = clean_param($option['value'], PARAM_INT); 1343 1344 if ($value !== 0 and $value !== 1) { 1345 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1346 } 1347 1348 if (!isset($backupdefaults[$option['name']])) { 1349 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); 1350 } 1351 1352 $backupsettings[$option['name']] = $value; 1353 } 1354 } 1355 1356 // Capability checking. 1357 1358 require_capability('moodle/backup:backuptargetimport', $importfromcontext); 1359 require_capability('moodle/restore:restoretargetimport', $importtocontext); 1360 1361 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE, 1362 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); 1363 1364 foreach ($backupsettings as $name => $value) { 1365 $bc->get_plan()->get_setting($name)->set_value($value); 1366 } 1367 1368 $backupid = $bc->get_backupid(); 1369 $backupbasepath = $bc->get_plan()->get_basepath(); 1370 1371 $bc->execute_plan(); 1372 $bc->destroy(); 1373 1374 // Restore the backup immediately. 1375 1376 // Check if we must delete the contents of the destination course. 1377 if ($params['deletecontent']) { 1378 $restoretarget = backup::TARGET_EXISTING_DELETING; 1379 } else { 1380 $restoretarget = backup::TARGET_EXISTING_ADDING; 1381 } 1382 1383 $rc = new restore_controller($backupid, $importto->id, 1384 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget); 1385 1386 foreach ($backupsettings as $name => $value) { 1387 $rc->get_plan()->get_setting($name)->set_value($value); 1388 } 1389 1390 if (!$rc->execute_precheck()) { 1391 $precheckresults = $rc->get_precheck_results(); 1392 if (is_array($precheckresults) && !empty($precheckresults['errors'])) { 1393 if (empty($CFG->keeptempdirectoriesonbackup)) { 1394 fulldelete($backupbasepath); 1395 } 1396 1397 $errorinfo = ''; 1398 1399 foreach ($precheckresults['errors'] as $error) { 1400 $errorinfo .= $error; 1401 } 1402 1403 if (array_key_exists('warnings', $precheckresults)) { 1404 foreach ($precheckresults['warnings'] as $warning) { 1405 $errorinfo .= $warning; 1406 } 1407 } 1408 1409 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); 1410 } 1411 } else { 1412 if ($restoretarget == backup::TARGET_EXISTING_DELETING) { 1413 restore_dbops::delete_course_content($importto->id); 1414 } 1415 } 1416 1417 $rc->execute_plan(); 1418 $rc->destroy(); 1419 1420 if (empty($CFG->keeptempdirectoriesonbackup)) { 1421 fulldelete($backupbasepath); 1422 } 1423 1424 return null; 1425 } 1426 1427 /** 1428 * Returns description of method result value 1429 * 1430 * @return external_description 1431 * @since Moodle 2.4 1432 */ 1433 public static function import_course_returns() { 1434 return null; 1435 } 1436 1437 /** 1438 * Returns description of method parameters 1439 * 1440 * @return external_function_parameters 1441 * @since Moodle 2.3 1442 */ 1443 public static function get_categories_parameters() { 1444 return new external_function_parameters( 1445 array( 1446 'criteria' => new external_multiple_structure( 1447 new external_single_structure( 1448 array( 1449 'key' => new external_value(PARAM_ALPHA, 1450 'The category column to search, expected keys (value format) are:'. 1451 '"id" (int) the category id,'. 1452 '"ids" (string) category ids separated by commas,'. 1453 '"name" (string) the category name,'. 1454 '"parent" (int) the parent category id,'. 1455 '"idnumber" (string) category idnumber'. 1456 ' - user must have \'moodle/category:manage\' to search on idnumber,'. 1457 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed, 1458 then the function return all categories that the user can see.'. 1459 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'. 1460 '"theme" (string) only return the categories having this theme'. 1461 ' - user must have \'moodle/category:manage\' to search on theme'), 1462 'value' => new external_value(PARAM_RAW, 'the value to match') 1463 ) 1464 ), 'criteria', VALUE_DEFAULT, array() 1465 ), 1466 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos 1467 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1) 1468 ) 1469 ); 1470 } 1471 1472 /** 1473 * Get categories 1474 * 1475 * @param array $criteria Criteria to match the results 1476 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default) 1477 * @return array list of categories 1478 * @since Moodle 2.3 1479 */ 1480 public static function get_categories($criteria = array(), $addsubcategories = true) { 1481 global $CFG, $DB; 1482 require_once($CFG->dirroot . "/course/lib.php"); 1483 1484 // Validate parameters. 1485 $params = self::validate_parameters(self::get_categories_parameters(), 1486 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories)); 1487 1488 // Retrieve the categories. 1489 $categories = array(); 1490 if (!empty($params['criteria'])) { 1491 1492 $conditions = array(); 1493 $wheres = array(); 1494 foreach ($params['criteria'] as $crit) { 1495 $key = trim($crit['key']); 1496 1497 // Trying to avoid duplicate keys. 1498 if (!isset($conditions[$key])) { 1499 1500 $context = context_system::instance(); 1501 $value = null; 1502 switch ($key) { 1503 case 'id': 1504 $value = clean_param($crit['value'], PARAM_INT); 1505 $conditions[$key] = $value; 1506 $wheres[] = $key . " = :" . $key; 1507 break; 1508 1509 case 'ids': 1510 $value = clean_param($crit['value'], PARAM_SEQUENCE); 1511 $ids = explode(',', $value); 1512 list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); 1513 $conditions = array_merge($conditions, $paramids); 1514 $wheres[] = 'id ' . $sqlids; 1515 break; 1516 1517 case 'idnumber': 1518 if (has_capability('moodle/category:manage', $context)) { 1519 $value = clean_param($crit['value'], PARAM_RAW); 1520 $conditions[$key] = $value; 1521 $wheres[] = $key . " = :" . $key; 1522 } else { 1523 // We must throw an exception. 1524 // Otherwise the dev client would think no idnumber exists. 1525 throw new moodle_exception('criteriaerror', 1526 'webservice', '', null, 1527 'You don\'t have the permissions to search on the "idnumber" field.'); 1528 } 1529 break; 1530 1531 case 'name': 1532 $value = clean_param($crit['value'], PARAM_TEXT); 1533 $conditions[$key] = $value; 1534 $wheres[] = $key . " = :" . $key; 1535 break; 1536 1537 case 'parent': 1538 $value = clean_param($crit['value'], PARAM_INT); 1539 $conditions[$key] = $value; 1540 $wheres[] = $key . " = :" . $key; 1541 break; 1542 1543 case 'visible': 1544 if (has_capability('moodle/category:manage', $context) 1545 or has_capability('moodle/category:viewhiddencategories', 1546 context_system::instance())) { 1547 $value = clean_param($crit['value'], PARAM_INT); 1548 $conditions[$key] = $value; 1549 $wheres[] = $key . " = :" . $key; 1550 } else { 1551 throw new moodle_exception('criteriaerror', 1552 'webservice', '', null, 1553 'You don\'t have the permissions to search on the "visible" field.'); 1554 } 1555 break; 1556 1557 case 'theme': 1558 if (has_capability('moodle/category:manage', $context)) { 1559 $value = clean_param($crit['value'], PARAM_THEME); 1560 $conditions[$key] = $value; 1561 $wheres[] = $key . " = :" . $key; 1562 } else { 1563 throw new moodle_exception('criteriaerror', 1564 'webservice', '', null, 1565 'You don\'t have the permissions to search on the "theme" field.'); 1566 } 1567 break; 1568 1569 default: 1570 throw new moodle_exception('criteriaerror', 1571 'webservice', '', null, 1572 'You can not search on this criteria: ' . $key); 1573 } 1574 } 1575 } 1576 1577 if (!empty($wheres)) { 1578 $wheres = implode(" AND ", $wheres); 1579 1580 $categories = $DB->get_records_select('course_categories', $wheres, $conditions); 1581 1582 // Retrieve its sub subcategories (all levels). 1583 if ($categories and !empty($params['addsubcategories'])) { 1584 $newcategories = array(); 1585 1586 // Check if we required visible/theme checks. 1587 $additionalselect = ''; 1588 $additionalparams = array(); 1589 if (isset($conditions['visible'])) { 1590 $additionalselect .= ' AND visible = :visible'; 1591 $additionalparams['visible'] = $conditions['visible']; 1592 } 1593 if (isset($conditions['theme'])) { 1594 $additionalselect .= ' AND theme= :theme'; 1595 $additionalparams['theme'] = $conditions['theme']; 1596 } 1597 1598 foreach ($categories as $category) { 1599 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect; 1600 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category. 1601 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams); 1602 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys. 1603 } 1604 $categories = $categories + $newcategories; 1605 } 1606 } 1607 1608 } else { 1609 // Retrieve all categories in the database. 1610 $categories = $DB->get_records('course_categories'); 1611 } 1612 1613 // The not returned categories. key => category id, value => reason of exclusion. 1614 $excludedcats = array(); 1615 1616 // The returned categories. 1617 $categoriesinfo = array(); 1618 1619 // We need to sort the categories by path. 1620 // The parent cats need to be checked by the algo first. 1621 usort($categories, "core_course_external::compare_categories_by_path"); 1622 1623 foreach ($categories as $category) { 1624 1625 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return). 1626 $parents = explode('/', $category->path); 1627 unset($parents[0]); // First key is always empty because path start with / => /1/2/4. 1628 foreach ($parents as $parentid) { 1629 // Note: when the parent exclusion was due to the context, 1630 // the sub category could still be returned. 1631 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') { 1632 $excludedcats[$category->id] = 'parent'; 1633 } 1634 } 1635 1636 // Check category depth is <= maxdepth (do not check for user who can manage categories). 1637 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth) 1638 and !has_capability('moodle/category:manage', $context)) { 1639 $excludedcats[$category->id] = 'depth'; 1640 } 1641 1642 // Check the user can use the category context. 1643 $context = context_coursecat::instance($category->id); 1644 try { 1645 self::validate_context($context); 1646 } catch (Exception $e) { 1647 $excludedcats[$category->id] = 'context'; 1648 1649 // If it was the requested category then throw an exception. 1650 if (isset($params['categoryid']) && $category->id == $params['categoryid']) { 1651 $exceptionparam = new stdClass(); 1652 $exceptionparam->message = $e->getMessage(); 1653 $exceptionparam->catid = $category->id; 1654 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); 1655 } 1656 } 1657 1658 // Return the category information. 1659 if (!isset($excludedcats[$category->id])) { 1660 1661 // Final check to see if the category is visible to the user. 1662 if ($category->visible 1663 or has_capability('moodle/category:viewhiddencategories', context_system::instance()) 1664 or has_capability('moodle/category:manage', $context)) { 1665 1666 $categoryinfo = array(); 1667 $categoryinfo['id'] = $category->id; 1668 $categoryinfo['name'] = $category->name; 1669 list($categoryinfo['description'], $categoryinfo['descriptionformat']) = 1670 external_format_text($category->description, $category->descriptionformat, 1671 $context->id, 'coursecat', 'description', null); 1672 $categoryinfo['parent'] = $category->parent; 1673 $categoryinfo['sortorder'] = $category->sortorder; 1674 $categoryinfo['coursecount'] = $category->coursecount; 1675 $categoryinfo['depth'] = $category->depth; 1676 $categoryinfo['path'] = $category->path; 1677 1678 // Some fields only returned for admin. 1679 if (has_capability('moodle/category:manage', $context)) { 1680 $categoryinfo['idnumber'] = $category->idnumber; 1681 $categoryinfo['visible'] = $category->visible; 1682 $categoryinfo['visibleold'] = $category->visibleold; 1683 $categoryinfo['timemodified'] = $category->timemodified; 1684 $categoryinfo['theme'] = $category->theme; 1685 } 1686 1687 $categoriesinfo[] = $categoryinfo; 1688 } else { 1689 $excludedcats[$category->id] = 'visibility'; 1690 } 1691 } 1692 } 1693 1694 // Sorting the resulting array so it looks a bit better for the client developer. 1695 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder"); 1696 1697 return $categoriesinfo; 1698 } 1699 1700 /** 1701 * Sort categories array by path 1702 * private function: only used by get_categories 1703 * 1704 * @param array $category1 1705 * @param array $category2 1706 * @return int result of strcmp 1707 * @since Moodle 2.3 1708 */ 1709 private static function compare_categories_by_path($category1, $category2) { 1710 return strcmp($category1->path, $category2->path); 1711 } 1712 1713 /** 1714 * Sort categories array by sortorder 1715 * private function: only used by get_categories 1716 * 1717 * @param array $category1 1718 * @param array $category2 1719 * @return int result of strcmp 1720 * @since Moodle 2.3 1721 */ 1722 private static function compare_categories_by_sortorder($category1, $category2) { 1723 return strcmp($category1['sortorder'], $category2['sortorder']); 1724 } 1725 1726 /** 1727 * Returns description of method result value 1728 * 1729 * @return external_description 1730 * @since Moodle 2.3 1731 */ 1732 public static function get_categories_returns() { 1733 return new external_multiple_structure( 1734 new external_single_structure( 1735 array( 1736 'id' => new external_value(PARAM_INT, 'category id'), 1737 'name' => new external_value(PARAM_TEXT, 'category name'), 1738 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 1739 'description' => new external_value(PARAM_RAW, 'category description'), 1740 'descriptionformat' => new external_format_value('description'), 1741 'parent' => new external_value(PARAM_INT, 'parent category id'), 1742 'sortorder' => new external_value(PARAM_INT, 'category sorting order'), 1743 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'), 1744 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 1745 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 1746 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL), 1747 'depth' => new external_value(PARAM_INT, 'category depth'), 1748 'path' => new external_value(PARAM_TEXT, 'category path'), 1749 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL), 1750 ), 'List of categories' 1751 ) 1752 ); 1753 } 1754 1755 /** 1756 * Returns description of method parameters 1757 * 1758 * @return external_function_parameters 1759 * @since Moodle 2.3 1760 */ 1761 public static function create_categories_parameters() { 1762 return new external_function_parameters( 1763 array( 1764 'categories' => new external_multiple_structure( 1765 new external_single_structure( 1766 array( 1767 'name' => new external_value(PARAM_TEXT, 'new category name'), 1768 'parent' => new external_value(PARAM_INT, 1769 'the parent category id inside which the new category will be created 1770 - set to 0 for a root category', 1771 VALUE_DEFAULT, 0), 1772 'idnumber' => new external_value(PARAM_RAW, 1773 'the new category idnumber', VALUE_OPTIONAL), 1774 'description' => new external_value(PARAM_RAW, 1775 'the new category description', VALUE_OPTIONAL), 1776 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 1777 'theme' => new external_value(PARAM_THEME, 1778 'the new category theme. This option must be enabled on moodle', 1779 VALUE_OPTIONAL), 1780 ) 1781 ) 1782 ) 1783 ) 1784 ); 1785 } 1786 1787 /** 1788 * Create categories 1789 * 1790 * @param array $categories - see create_categories_parameters() for the array structure 1791 * @return array - see create_categories_returns() for the array structure 1792 * @since Moodle 2.3 1793 */ 1794 public static function create_categories($categories) { 1795 global $CFG, $DB; 1796 require_once($CFG->libdir . "/coursecatlib.php"); 1797 1798 $params = self::validate_parameters(self::create_categories_parameters(), 1799 array('categories' => $categories)); 1800 1801 $transaction = $DB->start_delegated_transaction(); 1802 1803 $createdcategories = array(); 1804 foreach ($params['categories'] as $category) { 1805 if ($category['parent']) { 1806 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) { 1807 throw new moodle_exception('unknowcategory'); 1808 } 1809 $context = context_coursecat::instance($category['parent']); 1810 } else { 1811 $context = context_system::instance(); 1812 } 1813 self::validate_context($context); 1814 require_capability('moodle/category:manage', $context); 1815 1816 // this will validate format and throw an exception if there are errors 1817 external_validate_format($category['descriptionformat']); 1818 1819 $newcategory = coursecat::create($category); 1820 1821 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name); 1822 } 1823 1824 $transaction->allow_commit(); 1825 1826 return $createdcategories; 1827 } 1828 1829 /** 1830 * Returns description of method parameters 1831 * 1832 * @return external_function_parameters 1833 * @since Moodle 2.3 1834 */ 1835 public static function create_categories_returns() { 1836 return new external_multiple_structure( 1837 new external_single_structure( 1838 array( 1839 'id' => new external_value(PARAM_INT, 'new category id'), 1840 'name' => new external_value(PARAM_TEXT, 'new category name'), 1841 ) 1842 ) 1843 ); 1844 } 1845 1846 /** 1847 * Returns description of method parameters 1848 * 1849 * @return external_function_parameters 1850 * @since Moodle 2.3 1851 */ 1852 public static function update_categories_parameters() { 1853 return new external_function_parameters( 1854 array( 1855 'categories' => new external_multiple_structure( 1856 new external_single_structure( 1857 array( 1858 'id' => new external_value(PARAM_INT, 'course id'), 1859 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL), 1860 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 1861 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL), 1862 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL), 1863 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 1864 'theme' => new external_value(PARAM_THEME, 1865 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL), 1866 ) 1867 ) 1868 ) 1869 ) 1870 ); 1871 } 1872 1873 /** 1874 * Update categories 1875 * 1876 * @param array $categories The list of categories to update 1877 * @return null 1878 * @since Moodle 2.3 1879 */ 1880 public static function update_categories($categories) { 1881 global $CFG, $DB; 1882 require_once($CFG->libdir . "/coursecatlib.php"); 1883 1884 // Validate parameters. 1885 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories)); 1886 1887 $transaction = $DB->start_delegated_transaction(); 1888 1889 foreach ($params['categories'] as $cat) { 1890 $category = coursecat::get($cat['id']); 1891 1892 $categorycontext = context_coursecat::instance($cat['id']); 1893 self::validate_context($categorycontext); 1894 require_capability('moodle/category:manage', $categorycontext); 1895 1896 // this will throw an exception if descriptionformat is not valid 1897 external_validate_format($cat['descriptionformat']); 1898 1899 $category->update($cat); 1900 } 1901 1902 $transaction->allow_commit(); 1903 } 1904 1905 /** 1906 * Returns description of method result value 1907 * 1908 * @return external_description 1909 * @since Moodle 2.3 1910 */ 1911 public static function update_categories_returns() { 1912 return null; 1913 } 1914 1915 /** 1916 * Returns description of method parameters 1917 * 1918 * @return external_function_parameters 1919 * @since Moodle 2.3 1920 */ 1921 public static function delete_categories_parameters() { 1922 return new external_function_parameters( 1923 array( 1924 'categories' => new external_multiple_structure( 1925 new external_single_structure( 1926 array( 1927 'id' => new external_value(PARAM_INT, 'category id to delete'), 1928 'newparent' => new external_value(PARAM_INT, 1929 'the parent category to move the contents to, if specified', VALUE_OPTIONAL), 1930 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this 1931 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0) 1932 ) 1933 ) 1934 ) 1935 ) 1936 ); 1937 } 1938 1939 /** 1940 * Delete categories 1941 * 1942 * @param array $categories A list of category ids 1943 * @return array 1944 * @since Moodle 2.3 1945 */ 1946 public static function delete_categories($categories) { 1947 global $CFG, $DB; 1948 require_once($CFG->dirroot . "/course/lib.php"); 1949 require_once($CFG->libdir . "/coursecatlib.php"); 1950 1951 // Validate parameters. 1952 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories)); 1953 1954 $transaction = $DB->start_delegated_transaction(); 1955 1956 foreach ($params['categories'] as $category) { 1957 $deletecat = coursecat::get($category['id'], MUST_EXIST); 1958 $context = context_coursecat::instance($deletecat->id); 1959 require_capability('moodle/category:manage', $context); 1960 self::validate_context($context); 1961 self::validate_context(get_category_or_system_context($deletecat->parent)); 1962 1963 if ($category['recursive']) { 1964 // If recursive was specified, then we recursively delete the category's contents. 1965 if ($deletecat->can_delete_full()) { 1966 $deletecat->delete_full(false); 1967 } else { 1968 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); 1969 } 1970 } else { 1971 // In this situation, we don't delete the category's contents, we either move it to newparent or parent. 1972 // If the parent is the root, moving is not supported (because a course must always be inside a category). 1973 // We must move to an existing category. 1974 if (!empty($category['newparent'])) { 1975 $newparentcat = coursecat::get($category['newparent']); 1976 } else { 1977 $newparentcat = coursecat::get($deletecat->parent); 1978 } 1979 1980 // This operation is not allowed. We must move contents to an existing category. 1981 if (!$newparentcat->id) { 1982 throw new moodle_exception('movecatcontentstoroot'); 1983 } 1984 1985 self::validate_context(context_coursecat::instance($newparentcat->id)); 1986 if ($deletecat->can_move_content_to($newparentcat->id)) { 1987 $deletecat->delete_move($newparentcat->id, false); 1988 } else { 1989 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); 1990 } 1991 } 1992 } 1993 1994 $transaction->allow_commit(); 1995 } 1996 1997 /** 1998 * Returns description of method parameters 1999 * 2000 * @return external_function_parameters 2001 * @since Moodle 2.3 2002 */ 2003 public static function delete_categories_returns() { 2004 return null; 2005 } 2006 2007 /** 2008 * Describes the parameters for delete_modules. 2009 * 2010 * @return external_external_function_parameters 2011 * @since Moodle 2.5 2012 */ 2013 public static function delete_modules_parameters() { 2014 return new external_function_parameters ( 2015 array( 2016 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID', 2017 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'), 2018 ) 2019 ); 2020 } 2021 2022 /** 2023 * Deletes a list of provided module instances. 2024 * 2025 * @param array $cmids the course module ids 2026 * @since Moodle 2.5 2027 */ 2028 public static function delete_modules($cmids) { 2029 global $CFG, $DB; 2030 2031 // Require course file containing the course delete module function. 2032 require_once($CFG->dirroot . "/course/lib.php"); 2033 2034 // Clean the parameters. 2035 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids)); 2036 2037 // Keep track of the course ids we have performed a capability check on to avoid repeating. 2038 $arrcourseschecked = array(); 2039 2040 foreach ($params['cmids'] as $cmid) { 2041 // Get the course module. 2042 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST); 2043 2044 // Check if we have not yet confirmed they have permission in this course. 2045 if (!in_array($cm->course, $arrcourseschecked)) { 2046 // Ensure the current user has required permission in this course. 2047 $context = context_course::instance($cm->course); 2048 self::validate_context($context); 2049 // Add to the array. 2050 $arrcourseschecked[] = $cm->course; 2051 } 2052 2053 // Ensure they can delete this module. 2054 $modcontext = context_module::instance($cm->id); 2055 require_capability('moodle/course:manageactivities', $modcontext); 2056 2057 // Delete the module. 2058 course_delete_module($cm->id); 2059 } 2060 } 2061 2062 /** 2063 * Describes the delete_modules return value. 2064 * 2065 * @return external_single_structure 2066 * @since Moodle 2.5 2067 */ 2068 public static function delete_modules_returns() { 2069 return null; 2070 } 2071 2072 /** 2073 * Returns description of method parameters 2074 * 2075 * @return external_function_parameters 2076 * @since Moodle 2.9 2077 */ 2078 public static function view_course_parameters() { 2079 return new external_function_parameters( 2080 array( 2081 'courseid' => new external_value(PARAM_INT, 'id of the course'), 2082 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0) 2083 ) 2084 ); 2085 } 2086 2087 /** 2088 * Trigger the course viewed event. 2089 * 2090 * @param int $courseid id of course 2091 * @param int $sectionnumber sectionnumber (0, 1, 2...) 2092 * @return array of warnings and status result 2093 * @since Moodle 2.9 2094 * @throws moodle_exception 2095 */ 2096 public static function view_course($courseid, $sectionnumber = 0) { 2097 global $CFG; 2098 require_once($CFG->dirroot . "/course/lib.php"); 2099 2100 $params = self::validate_parameters(self::view_course_parameters(), 2101 array( 2102 'courseid' => $courseid, 2103 'sectionnumber' => $sectionnumber 2104 )); 2105 2106 $warnings = array(); 2107 2108 $course = get_course($params['courseid']); 2109 $context = context_course::instance($course->id); 2110 self::validate_context($context); 2111 2112 if (!empty($params['sectionnumber'])) { 2113 2114 // Get section details and check it exists. 2115 $modinfo = get_fast_modinfo($course); 2116 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST); 2117 2118 // Check user is allowed to see it. 2119 if (!$coursesection->uservisible) { 2120 require_capability('moodle/course:viewhiddensections', $context); 2121 } 2122 } 2123 2124 course_view($context, $params['sectionnumber']); 2125 2126 $result = array(); 2127 $result['status'] = true; 2128 $result['warnings'] = $warnings; 2129 return $result; 2130 } 2131 2132 /** 2133 * Returns description of method result value 2134 * 2135 * @return external_description 2136 * @since Moodle 2.9 2137 */ 2138 public static function view_course_returns() { 2139 return new external_single_structure( 2140 array( 2141 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 2142 'warnings' => new external_warnings() 2143 ) 2144 ); 2145 } 2146 2147 /** 2148 * Returns description of method parameters 2149 * 2150 * @return external_function_parameters 2151 * @since Moodle 3.0 2152 */ 2153 public static function search_courses_parameters() { 2154 return new external_function_parameters( 2155 array( 2156 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name 2157 (search, modulelist (only admins), blocklist (only admins), tagid)'), 2158 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'), 2159 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0), 2160 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), 2161 'requiredcapabilities' => new external_multiple_structure( 2162 new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'), 2163 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array() 2164 ), 2165 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0), 2166 ) 2167 ); 2168 } 2169 2170 /** 2171 * Search courses following the specified criteria. 2172 * 2173 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid) 2174 * @param string $criteriavalue Criteria value 2175 * @param int $page Page number (for pagination) 2176 * @param int $perpage Items per page 2177 * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list). 2178 * @param int $limittoenrolled Limit to only enrolled courses 2179 * @return array of course objects and warnings 2180 * @since Moodle 3.0 2181 * @throws moodle_exception 2182 */ 2183 public static function search_courses($criterianame, 2184 $criteriavalue, 2185 $page=0, 2186 $perpage=0, 2187 $requiredcapabilities=array(), 2188 $limittoenrolled=0) { 2189 global $CFG; 2190 require_once($CFG->libdir . '/coursecatlib.php'); 2191 2192 $warnings = array(); 2193 2194 $parameters = array( 2195 'criterianame' => $criterianame, 2196 'criteriavalue' => $criteriavalue, 2197 'page' => $page, 2198 'perpage' => $perpage, 2199 'requiredcapabilities' => $requiredcapabilities 2200 ); 2201 $params = self::validate_parameters(self::search_courses_parameters(), $parameters); 2202 self::validate_context(context_system::instance()); 2203 2204 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid'); 2205 if (!in_array($params['criterianame'], $allowedcriterianames)) { 2206 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' . 2207 'allowed values are: '.implode(',', $allowedcriterianames)); 2208 } 2209 2210 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') { 2211 require_capability('moodle/site:config', context_system::instance()); 2212 } 2213 2214 $paramtype = array( 2215 'search' => PARAM_RAW, 2216 'modulelist' => PARAM_PLUGIN, 2217 'blocklist' => PARAM_INT, 2218 'tagid' => PARAM_INT 2219 ); 2220 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]); 2221 2222 // Prepare the search API options. 2223 $searchcriteria = array(); 2224 $searchcriteria[$params['criterianame']] = $params['criteriavalue']; 2225 2226 $options = array(); 2227 if ($params['perpage'] != 0) { 2228 $offset = $params['page'] * $params['perpage']; 2229 $options = array('offset' => $offset, 'limit' => $params['perpage']); 2230 } 2231 2232 // Search the courses. 2233 $courses = coursecat::search_courses($searchcriteria, $options, $params['requiredcapabilities']); 2234 $totalcount = coursecat::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']); 2235 2236 if (!empty($limittoenrolled)) { 2237 // Get the courses where the current user has access. 2238 $enrolled = enrol_get_my_courses(array('id', 'cacherev')); 2239 } 2240 2241 $finalcourses = array(); 2242 $categoriescache = array(); 2243 2244 foreach ($courses as $course) { 2245 if (!empty($limittoenrolled)) { 2246 // Filter out not enrolled courses. 2247 if (!isset($enrolled[$course->id])) { 2248 $totalcount--; 2249 continue; 2250 } 2251 } 2252 2253 $coursecontext = context_course::instance($course->id); 2254 2255 // Category information. 2256 if (!isset($categoriescache[$course->category])) { 2257 $categoriescache[$course->category] = coursecat::get($course->category); 2258 } 2259 $category = $categoriescache[$course->category]; 2260 2261 // Retrieve course overfiew used files. 2262 $files = array(); 2263 foreach ($course->get_course_overviewfiles() as $file) { 2264 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(), 2265 $file->get_filearea(), null, $file->get_filepath(), 2266 $file->get_filename())->out(false); 2267 $files[] = array( 2268 'filename' => $file->get_filename(), 2269 'fileurl' => $fileurl, 2270 'filesize' => $file->get_filesize(), 2271 'filepath' => $file->get_filepath(), 2272 'mimetype' => $file->get_mimetype(), 2273 'timemodified' => $file->get_timemodified(), 2274 ); 2275 } 2276 2277 // Retrieve the course contacts, 2278 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services. 2279 $coursecontacts = array(); 2280 foreach ($course->get_course_contacts() as $contact) { 2281 $coursecontacts[] = array( 2282 'id' => $contact['user']->id, 2283 'fullname' => $contact['username'] 2284 ); 2285 } 2286 2287 // Allowed enrolment methods (maybe we can self-enrol). 2288 $enroltypes = array(); 2289 $instances = enrol_get_instances($course->id, true); 2290 foreach ($instances as $instance) { 2291 $enroltypes[] = $instance->enrol; 2292 } 2293 2294 // Format summary. 2295 list($summary, $summaryformat) = 2296 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null); 2297 2298 $displayname = get_course_display_name_for_list($course); 2299 $coursereturns = array(); 2300 $coursereturns['id'] = $course->id; 2301 $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id); 2302 $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id); 2303 $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id); 2304 $coursereturns['categoryid'] = $course->category; 2305 $coursereturns['categoryname'] = $category->name; 2306 $coursereturns['summary'] = $summary; 2307 $coursereturns['summaryformat'] = $summaryformat; 2308 $coursereturns['overviewfiles'] = $files; 2309 $coursereturns['contacts'] = $coursecontacts; 2310 $coursereturns['enrollmentmethods'] = $enroltypes; 2311 $finalcourses[] = $coursereturns; 2312 } 2313 2314 return array( 2315 'total' => $totalcount, 2316 'courses' => $finalcourses, 2317 'warnings' => $warnings 2318 ); 2319 } 2320 2321 /** 2322 * Returns description of method result value 2323 * 2324 * @return external_description 2325 * @since Moodle 3.0 2326 */ 2327 public static function search_courses_returns() { 2328 2329 return new external_single_structure( 2330 array( 2331 'total' => new external_value(PARAM_INT, 'total course count'), 2332 'courses' => new external_multiple_structure( 2333 new external_single_structure( 2334 array( 2335 'id' => new external_value(PARAM_INT, 'course id'), 2336 'fullname' => new external_value(PARAM_TEXT, 'course full name'), 2337 'displayname' => new external_value(PARAM_TEXT, 'course display name'), 2338 'shortname' => new external_value(PARAM_TEXT, 'course short name'), 2339 'categoryid' => new external_value(PARAM_INT, 'category id'), 2340 'categoryname' => new external_value(PARAM_TEXT, 'category name'), 2341 'summary' => new external_value(PARAM_RAW, 'summary'), 2342 'summaryformat' => new external_format_value('summary'), 2343 'overviewfiles' => new external_files('additional overview files attached to this course'), 2344 'contacts' => new external_multiple_structure( 2345 new external_single_structure( 2346 array( 2347 'id' => new external_value(PARAM_INT, 'contact user id'), 2348 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'), 2349 ) 2350 ), 2351 'contact users' 2352 ), 2353 'enrollmentmethods' => new external_multiple_structure( 2354 new external_value(PARAM_PLUGIN, 'enrollment method'), 2355 'enrollment methods list' 2356 ), 2357 ) 2358 ), 'course' 2359 ), 2360 'warnings' => new external_warnings() 2361 ) 2362 ); 2363 } 2364 2365 /** 2366 * Returns description of method parameters 2367 * 2368 * @return external_function_parameters 2369 * @since Moodle 3.0 2370 */ 2371 public static function get_course_module_parameters() { 2372 return new external_function_parameters( 2373 array( 2374 'cmid' => new external_value(PARAM_INT, 'The course module id') 2375 ) 2376 ); 2377 } 2378 2379 /** 2380 * Return information about a course module. 2381 * 2382 * @param int $cmid the course module id 2383 * @return array of warnings and the course module 2384 * @since Moodle 3.0 2385 * @throws moodle_exception 2386 */ 2387 public static function get_course_module($cmid) { 2388 2389 $params = self::validate_parameters(self::get_course_module_parameters(), 2390 array( 2391 'cmid' => $cmid, 2392 )); 2393 2394 $warnings = array(); 2395 2396 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST); 2397 $context = context_module::instance($cm->id); 2398 self::validate_context($context); 2399 2400 // If the user has permissions to manage the activity, return all the information. 2401 if (has_capability('moodle/course:manageactivities', $context)) { 2402 $info = $cm; 2403 } else { 2404 // Return information is safe to show to any user. 2405 $info = new stdClass(); 2406 $info->id = $cm->id; 2407 $info->course = $cm->course; 2408 $info->module = $cm->module; 2409 $info->modname = $cm->modname; 2410 $info->instance = $cm->instance; 2411 $info->section = $cm->section; 2412 $info->sectionnum = $cm->sectionnum; 2413 $info->groupmode = $cm->groupmode; 2414 $info->groupingid = $cm->groupingid; 2415 $info->completion = $cm->completion; 2416 } 2417 // Format name. 2418 $info->name = external_format_string($cm->name, $context->id); 2419 2420 $result = array(); 2421 $result['cm'] = $info; 2422 $result['warnings'] = $warnings; 2423 return $result; 2424 } 2425 2426 /** 2427 * Returns description of method result value 2428 * 2429 * @return external_description 2430 * @since Moodle 3.0 2431 */ 2432 public static function get_course_module_returns() { 2433 return new external_single_structure( 2434 array( 2435 'cm' => new external_single_structure( 2436 array( 2437 'id' => new external_value(PARAM_INT, 'The course module id'), 2438 'course' => new external_value(PARAM_INT, 'The course id'), 2439 'module' => new external_value(PARAM_INT, 'The module type id'), 2440 'name' => new external_value(PARAM_RAW, 'The activity name'), 2441 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'), 2442 'instance' => new external_value(PARAM_INT, 'The activity instance id'), 2443 'section' => new external_value(PARAM_INT, 'The module section id'), 2444 'sectionnum' => new external_value(PARAM_INT, 'The module section number'), 2445 'groupmode' => new external_value(PARAM_INT, 'Group mode'), 2446 'groupingid' => new external_value(PARAM_INT, 'Grouping id'), 2447 'completion' => new external_value(PARAM_INT, 'If completion is enabled'), 2448 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL), 2449 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL), 2450 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL), 2451 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL), 2452 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL), 2453 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL), 2454 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL), 2455 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL), 2456 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL), 2457 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL), 2458 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL), 2459 ) 2460 ), 2461 'warnings' => new external_warnings() 2462 ) 2463 ); 2464 } 2465 2466 /** 2467 * Returns description of method parameters 2468 * 2469 * @return external_function_parameters 2470 * @since Moodle 3.0 2471 */ 2472 public static function get_course_module_by_instance_parameters() { 2473 return new external_function_parameters( 2474 array( 2475 'module' => new external_value(PARAM_COMPONENT, 'The module name'), 2476 'instance' => new external_value(PARAM_INT, 'The module instance id') 2477 ) 2478 ); 2479 } 2480 2481 /** 2482 * Return information about a course module. 2483 * 2484 * @param string $module the module name 2485 * @param int $instance the activity instance id 2486 * @return array of warnings and the course module 2487 * @since Moodle 3.0 2488 * @throws moodle_exception 2489 */ 2490 public static function get_course_module_by_instance($module, $instance) { 2491 2492 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(), 2493 array( 2494 'module' => $module, 2495 'instance' => $instance, 2496 )); 2497 2498 $warnings = array(); 2499 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST); 2500 2501 return self::get_course_module($cm->id); 2502 } 2503 2504 /** 2505 * Returns description of method result value 2506 * 2507 * @return external_description 2508 * @since Moodle 3.0 2509 */ 2510 public static function get_course_module_by_instance_returns() { 2511 return self::get_course_module_returns(); 2512 } 2513 2514 /** 2515 * Returns description of method parameters 2516 * 2517 * @return external_function_parameters 2518 * @since Moodle 3.2 2519 */ 2520 public static function get_activities_overview_parameters() { 2521 return new external_function_parameters( 2522 array( 2523 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')), 2524 ) 2525 ); 2526 } 2527 2528 /** 2529 * Return activities overview for the given courses. 2530 * 2531 * @param array $courseids a list of course ids 2532 * @return array of warnings and the activities overview 2533 * @since Moodle 3.2 2534 * @throws moodle_exception 2535 */ 2536 public static function get_activities_overview($courseids) { 2537 global $USER; 2538 2539 // Parameter validation. 2540 $params = self::validate_parameters(self::get_activities_overview_parameters(), array('courseids' => $courseids)); 2541 $courseoverviews = array(); 2542 2543 list($courses, $warnings) = external_util::validate_courses($params['courseids']); 2544 2545 if (!empty($courses)) { 2546 // Add lastaccess to each course (required by print_overview function). 2547 // We need the complete user data, the ws server does not load a complete one. 2548 $user = get_complete_user_data('id', $USER->id); 2549 foreach ($courses as $course) { 2550 if (isset($user->lastcourseaccess[$course->id])) { 2551 $course->lastaccess = $user->lastcourseaccess[$course->id]; 2552 } else { 2553 $course->lastaccess = 0; 2554 } 2555 } 2556 2557 $overviews = array(); 2558 if ($modules = get_plugin_list_with_function('mod', 'print_overview')) { 2559 foreach ($modules as $fname) { 2560 $fname($courses, $overviews); 2561 } 2562 } 2563 2564 // Format output. 2565 foreach ($overviews as $courseid => $modules) { 2566 $courseoverviews[$courseid]['id'] = $courseid; 2567 $courseoverviews[$courseid]['overviews'] = array(); 2568 2569 foreach ($modules as $modname => $overviewtext) { 2570 $courseoverviews[$courseid]['overviews'][] = array( 2571 'module' => $modname, 2572 'overviewtext' => $overviewtext // This text doesn't need formatting. 2573 ); 2574 } 2575 } 2576 } 2577 2578 $result = array( 2579 'courses' => $courseoverviews, 2580 'warnings' => $warnings 2581 ); 2582 return $result; 2583 } 2584 2585 /** 2586 * Returns description of method result value 2587 * 2588 * @return external_description 2589 * @since Moodle 3.2 2590 */ 2591 public static function get_activities_overview_returns() { 2592 return new external_single_structure( 2593 array( 2594 'courses' => new external_multiple_structure( 2595 new external_single_structure( 2596 array( 2597 'id' => new external_value(PARAM_INT, 'Course id'), 2598 'overviews' => new external_multiple_structure( 2599 new external_single_structure( 2600 array( 2601 'module' => new external_value(PARAM_PLUGIN, 'Module name'), 2602 'overviewtext' => new external_value(PARAM_RAW, 'Overview text'), 2603 ) 2604 ) 2605 ) 2606 ) 2607 ), 'List of courses' 2608 ), 2609 'warnings' => new external_warnings() 2610 ) 2611 ); 2612 } 2613 2614 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |