Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

MDL-30062 Webservices: added update_course webservice

  • Loading branch information...
commit 791723c34ad66f03036631f4b2e90c97f05c695d 1 parent 1918a24
Rajesh Taneja rajeshtaneja authored
206 course/externallib.php
@@ -627,6 +627,212 @@ public static function create_courses_returns() {
627 627 }
628 628
629 629 /**
  630 + * Update courses
  631 + *
  632 + * @return external_function_parameters
  633 + * @since Moodle 2.5
  634 + */
  635 + public static function update_courses_parameters() {
  636 + return new external_function_parameters(
  637 + array(
  638 + 'courses' => new external_multiple_structure(
  639 + new external_single_structure(
  640 + array(
  641 + 'id' => new external_value(PARAM_INT, 'ID of the course'),
  642 + 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
  643 + 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
  644 + 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
  645 + 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
  646 + 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
  647 + 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
  648 + 'format' => new external_value(PARAM_PLUGIN,
  649 + 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
  650 + 'showgrades' => new external_value(PARAM_INT,
  651 + '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
  652 + 'newsitems' => new external_value(PARAM_INT,
  653 + 'number of recent items appearing on the course page', VALUE_OPTIONAL),
  654 + 'startdate' => new external_value(PARAM_INT,
  655 + 'timestamp when the course start', VALUE_OPTIONAL),
  656 + 'numsections' => new external_value(PARAM_INT,
  657 + '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
  658 + 'maxbytes' => new external_value(PARAM_INT,
  659 + 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
  660 + 'showreports' => new external_value(PARAM_INT,
  661 + 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
  662 + 'visible' => new external_value(PARAM_INT,
  663 + '1: available to student, 0:not available', VALUE_OPTIONAL),
  664 + 'hiddensections' => new external_value(PARAM_INT,
  665 + '(deprecated, use courseformatoptions) How the hidden sections in the course are
  666 + displayed to students', VALUE_OPTIONAL),
  667 + 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
  668 + 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
  669 + 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
  670 + 'enablecompletion' => new external_value(PARAM_INT,
  671 + 'Enabled, control via completion and activity settings. Disabled,
  672 + not shown in activity settings.', VALUE_OPTIONAL),
  673 + 'completionstartonenrol' => new external_value(PARAM_INT,
  674 + '1: begin tracking a student\'s progress in course completion after
  675 + course enrolment. 0: does not', VALUE_OPTIONAL),
  676 + 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
  677 + 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
  678 + 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
  679 + 'courseformatoptions' => new external_multiple_structure(
  680 + new external_single_structure(
  681 + array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
  682 + 'value' => new external_value(PARAM_RAW, 'course format option value')
  683 + )),
  684 + 'additional options for particular course format', VALUE_OPTIONAL),
  685 + )
  686 + ), 'courses to update'
  687 + )
  688 + )
  689 + );
  690 + }
  691 +
  692 + /**
  693 + * Update courses
  694 + *
  695 + * @param array $courses
  696 + * @since Moodle 2.5
  697 + */
  698 + public static function update_courses($courses) {
  699 + global $CFG, $DB;
  700 + require_once($CFG->dirroot . "/course/lib.php");
  701 + $warnings = array();
  702 +
  703 + $params = self::validate_parameters(self::update_courses_parameters(),
  704 + array('courses' => $courses));
  705 +
  706 + $availablethemes = get_plugin_list('theme');
  707 + $availablelangs = get_string_manager()->get_list_of_translations();
  708 +
  709 + foreach ($params['courses'] as $course) {
  710 + // Catch any exception while updating course and return as warning to user.
  711 + try {
  712 + // Ensure the current user is allowed to run this function.
  713 + $context = context_course::instance($course['id'], MUST_EXIST);
  714 + self::validate_context($context);
  715 +
  716 + $oldcourse = course_get_format($course['id'])->get_course();
  717 +
  718 + require_capability('moodle/course:update', $context);
  719 +
  720 + // Check if user can change category.
  721 + if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
  722 + require_capability('moodle/course:changecategory', $context);
  723 + $course['category'] = $course['categoryid'];
  724 + }
  725 +
  726 + // Check if the user can change fullname.
  727 + if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
  728 + require_capability('moodle/course:changefullname', $context);
  729 + }
  730 +
  731 + // Check if the shortname already exist and user have capability.
  732 + if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
  733 + require_capability('moodle/course:changeshortname', $context);
  734 + if ($DB->record_exists('course', array('shortname' => $course['shortname']))) {
  735 + throw new moodle_exception('shortnametaken');
  736 + }
  737 + }
  738 +
  739 + // Check if the id number already exist and user have capability.
  740 + if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
  741 + require_capability('moodle/course:changeidnumber', $context);
  742 + if ($DB->record_exists('course', array('idnumber' => $course['idnumber']))) {
  743 + throw new moodle_exception('idnumbertaken');
  744 + }
  745 + }
  746 +
  747 + // Check if user can change summary.
  748 + if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
  749 + require_capability('moodle/course:changesummary', $context);
  750 + }
  751 +
  752 + // Summary format.
  753 + if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
  754 + require_capability('moodle/course:changesummary', $context);
  755 + $course['summaryformat'] = external_validate_format($course['summaryformat']);
  756 + }
  757 +
  758 + // Check if user can change visibility.
  759 + if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
  760 + require_capability('moodle/course:visibility', $context);
  761 + }
  762 +
  763 + // Make sure lang is valid.
  764 + if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
  765 + throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
  766 + }
  767 +
  768 + // Make sure theme is valid.
  769 + if (array_key_exists('forcetheme', $course)) {
  770 + if (!empty($CFG->allowcoursethemes)) {
  771 + if (empty($availablethemes[$course['forcetheme']])) {
  772 + throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
  773 + } else {
  774 + $course['theme'] = $course['forcetheme'];
  775 + }
  776 + }
  777 + }
  778 +
  779 + // Make sure completion is enabled before setting it.
  780 + if ((array_key_exists('enabledcompletion', $course) ||
  781 + array_key_exists('completionstartonenrol', $course)) &&
  782 + !completion_info::is_enabled_for_site()) {
  783 + $course['enabledcompletion'] = 0;
  784 + $course['completionstartonenrol'] = 0;
  785 + }
  786 +
  787 + // Make sure maxbytes are less then CFG->maxbytes.
  788 + if (array_key_exists('maxbytes', $course)) {
  789 + $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
  790 + }
  791 +
  792 + if (!empty($course['courseformatoptions'])) {
  793 + foreach ($course['courseformatoptions'] as $option) {
  794 + if (isset($option['name']) && isset($option['value'])) {
  795 + $course[$option['name']] = $option['value'];
  796 + }
  797 + }
  798 + }
  799 +
  800 + // Update course if user has all required capabilities.
  801 + update_course((object) $course);
  802 + } catch (Exception $e) {
  803 + $warning = array();
  804 + $warning['item'] = 'course';
  805 + $warning['itemid'] = $course['id'];
  806 + if ($e instanceof moodle_exception) {
  807 + $warning['warningcode'] = $e->errorcode;
  808 + } else {
  809 + $warning['warningcode'] = $e->getCode();
  810 + }
  811 + $warning['message'] = $e->getMessage();
  812 + $warnings[] = $warning;
  813 + }
  814 + }
  815 +
  816 + $result = array();
  817 + $result['warnings'] = $warnings;
  818 + return $result;
  819 + }
  820 +
  821 + /**
  822 + * Returns description of method result value
  823 + *
  824 + * @return external_description
  825 + * @since Moodle 2.5
  826 + */
  827 + public static function update_courses_returns() {
  828 + return new external_single_structure(
  829 + array(
  830 + 'warnings' => new external_warnings()
  831 + )
  832 + );
  833 + }
  834 +
  835 + /**
630 836 * Returns description of method parameters
631 837 *
632 838 * @return external_function_parameters
206 course/tests/externallib_test.php
@@ -637,4 +637,210 @@ public function test_duplicate_course() {
637 637 // Check that the course has been duplicated.
638 638 $this->assertEquals($newcourse['shortname'], $duplicate['shortname']);
639 639 }
  640 +
  641 + /**
  642 + * Test update_courses
  643 + */
  644 + public function test_update_courses() {
  645 + global $DB, $CFG, $USER;
  646 +
  647 + $this->resetAfterTest(true);
  648 +
  649 + // Set the required capabilities by the external function.
  650 + $contextid = context_system::instance()->id;
  651 + $roleid = $this->assignUserCapability('moodle/course:update', $contextid);
  652 + $this->assignUserCapability('moodle/course:changecategory', $contextid, $roleid);
  653 + $this->assignUserCapability('moodle/course:changefullname', $contextid, $roleid);
  654 + $this->assignUserCapability('moodle/course:changeshortname', $contextid, $roleid);
  655 + $this->assignUserCapability('moodle/course:changeidnumber', $contextid, $roleid);
  656 + $this->assignUserCapability('moodle/course:changesummary', $contextid, $roleid);
  657 + $this->assignUserCapability('moodle/course:visibility', $contextid, $roleid);
  658 + $this->assignUserCapability('moodle/course:viewhiddencourses', $contextid, $roleid);
  659 +
  660 + // Create category and course.
  661 + $category1 = self::getDataGenerator()->create_category();
  662 + $category2 = self::getDataGenerator()->create_category();
  663 + $originalcourse1 = self::getDataGenerator()->create_course();
  664 + self::getDataGenerator()->enrol_user($USER->id, $originalcourse1->id, $roleid);
  665 + $originalcourse2 = self::getDataGenerator()->create_course();
  666 + self::getDataGenerator()->enrol_user($USER->id, $originalcourse2->id, $roleid);
  667 +
  668 + // Course values to be updated.
  669 + $course1['id'] = $originalcourse1->id;
  670 + $course1['fullname'] = 'Updated test course 1';
  671 + $course1['shortname'] = 'Udestedtestcourse1';
  672 + $course1['categoryid'] = $category1->id;
  673 + $course2['id'] = $originalcourse2->id;
  674 + $course2['fullname'] = 'Updated test course 2';
  675 + $course2['shortname'] = 'Updestedtestcourse2';
  676 + $course2['categoryid'] = $category2->id;
  677 + $course2['idnumber'] = 'Updatedidnumber2';
  678 + $course2['summary'] = 'Updaated description for course 2';
  679 + $course2['summaryformat'] = FORMAT_HTML;
  680 + $course2['format'] = 'topics';
  681 + $course2['showgrades'] = 1;
  682 + $course2['newsitems'] = 3;
  683 + $course2['startdate'] = 1420092000; // 01/01/2015.
  684 + $course2['numsections'] = 4;
  685 + $course2['maxbytes'] = 100000;
  686 + $course2['showreports'] = 1;
  687 + $course2['visible'] = 0;
  688 + $course2['hiddensections'] = 0;
  689 + $course2['groupmode'] = 0;
  690 + $course2['groupmodeforce'] = 0;
  691 + $course2['defaultgroupingid'] = 0;
  692 + $course2['enablecompletion'] = 1;
  693 + $course2['lang'] = 'en';
  694 + $course2['forcetheme'] = 'base';
  695 + $courses = array($course1, $course2);
  696 +
  697 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  698 +
  699 + // Check that right number of courses were created.
  700 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  701 +
  702 + // Check that the courses were correctly created.
  703 + foreach ($courses as $course) {
  704 + $courseinfo = course_get_format($course['id'])->get_course();
  705 + if ($course['id'] == $course2['id']) {
  706 + $this->assertEquals($course2['fullname'], $courseinfo->fullname);
  707 + $this->assertEquals($course2['shortname'], $courseinfo->shortname);
  708 + $this->assertEquals($course2['categoryid'], $courseinfo->category);
  709 + $this->assertEquals($course2['idnumber'], $courseinfo->idnumber);
  710 + $this->assertEquals($course2['summary'], $courseinfo->summary);
  711 + $this->assertEquals($course2['summaryformat'], $courseinfo->summaryformat);
  712 + $this->assertEquals($course2['format'], $courseinfo->format);
  713 + $this->assertEquals($course2['showgrades'], $courseinfo->showgrades);
  714 + $this->assertEquals($course2['newsitems'], $courseinfo->newsitems);
  715 + $this->assertEquals($course2['startdate'], $courseinfo->startdate);
  716 + $this->assertEquals($course2['numsections'], $courseinfo->numsections);
  717 + $this->assertEquals($course2['maxbytes'], $courseinfo->maxbytes);
  718 + $this->assertEquals($course2['showreports'], $courseinfo->showreports);
  719 + $this->assertEquals($course2['visible'], $courseinfo->visible);
  720 + $this->assertEquals($course2['hiddensections'], $courseinfo->hiddensections);
  721 + $this->assertEquals($course2['groupmode'], $courseinfo->groupmode);
  722 + $this->assertEquals($course2['groupmodeforce'], $courseinfo->groupmodeforce);
  723 + $this->assertEquals($course2['defaultgroupingid'], $courseinfo->defaultgroupingid);
  724 + $this->assertEquals($course2['lang'], $courseinfo->lang);
  725 +
  726 + if (!empty($CFG->allowcoursethemes)) {
  727 + $this->assertEquals($course2['forcetheme'], $courseinfo->theme);
  728 + }
  729 +
  730 + if (completion_info::is_enabled_for_site()) {
  731 + $this->assertEquals($course2['enabledcompletion'], $courseinfo->enablecompletion);
  732 + $this->assertEquals($course2['completionstartonenrol'], $courseinfo->completionstartonenrol);
  733 + }
  734 + } else if ($course['id'] == $course1['id']) {
  735 + $this->assertEquals($course1['fullname'], $courseinfo->fullname);
  736 + $this->assertEquals($course1['shortname'], $courseinfo->shortname);
  737 + $this->assertEquals($course1['categoryid'], $courseinfo->category);
  738 + $this->assertEquals(FORMAT_MOODLE, $courseinfo->summaryformat);
  739 + $this->assertEquals('topics', $courseinfo->format);
  740 + $this->assertEquals(5, $courseinfo->numsections);
  741 + $this->assertEquals(0, $courseinfo->newsitems);
  742 + $this->assertEquals(FORMAT_MOODLE, $courseinfo->summaryformat);
  743 + } else {
  744 + throw moodle_exception('Unexpected shortname');
  745 + }
  746 + }
  747 +
  748 + $courses = array($course1);
  749 + // Try update course without update capability.
  750 + $user = self::getDataGenerator()->create_user();
  751 + $this->setUser($user);
  752 + $this->unassignUserCapability('moodle/course:update', $contextid, $roleid);
  753 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  754 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  755 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  756 +
  757 + // Try update course category without capability.
  758 + $this->assignUserCapability('moodle/course:update', $contextid, $roleid);
  759 + $this->unassignUserCapability('moodle/course:changecategory', $contextid, $roleid);
  760 + $user = self::getDataGenerator()->create_user();
  761 + $this->setUser($user);
  762 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  763 + $course1['categoryid'] = $category2->id;
  764 + $courses = array($course1);
  765 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  766 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  767 +
  768 + // Try update course fullname without capability.
  769 + $this->assignUserCapability('moodle/course:changecategory', $contextid, $roleid);
  770 + $this->unassignUserCapability('moodle/course:changefullname', $contextid, $roleid);
  771 + $user = self::getDataGenerator()->create_user();
  772 + $this->setUser($user);
  773 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  774 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  775 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  776 + $course1['fullname'] = 'Testing fullname without permission';
  777 + $courses = array($course1);
  778 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  779 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  780 +
  781 + // Try update course shortname without capability.
  782 + $this->assignUserCapability('moodle/course:changefullname', $contextid, $roleid);
  783 + $this->unassignUserCapability('moodle/course:changeshortname', $contextid, $roleid);
  784 + $user = self::getDataGenerator()->create_user();
  785 + $this->setUser($user);
  786 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  787 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  788 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  789 + $course1['shortname'] = 'Testing shortname without permission';
  790 + $courses = array($course1);
  791 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  792 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  793 +
  794 + // Try update course idnumber without capability.
  795 + $this->assignUserCapability('moodle/course:changeshortname', $contextid, $roleid);
  796 + $this->unassignUserCapability('moodle/course:changeidnumber', $contextid, $roleid);
  797 + $user = self::getDataGenerator()->create_user();
  798 + $this->setUser($user);
  799 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  800 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  801 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  802 + $course1['idnumber'] = 'NEWIDNUMBER';
  803 + $courses = array($course1);
  804 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  805 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  806 +
  807 + // Try update course summary without capability.
  808 + $this->assignUserCapability('moodle/course:changeidnumber', $contextid, $roleid);
  809 + $this->unassignUserCapability('moodle/course:changesummary', $contextid, $roleid);
  810 + $user = self::getDataGenerator()->create_user();
  811 + $this->setUser($user);
  812 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  813 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  814 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  815 + $course1['summary'] = 'New summary';
  816 + $courses = array($course1);
  817 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  818 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  819 +
  820 + // Try update course with invalid summary format.
  821 + $this->assignUserCapability('moodle/course:changesummary', $contextid, $roleid);
  822 + $user = self::getDataGenerator()->create_user();
  823 + $this->setUser($user);
  824 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  825 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  826 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  827 + $course1['summaryformat'] = 10;
  828 + $courses = array($course1);
  829 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  830 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  831 +
  832 + // Try update course visibility without capability.
  833 + $this->unassignUserCapability('moodle/course:visibility', $contextid, $roleid);
  834 + $user = self::getDataGenerator()->create_user();
  835 + $this->setUser($user);
  836 + self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid);
  837 + $course1['summaryformat'] = FORMAT_MOODLE;
  838 + $courses = array($course1);
  839 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  840 + $this->assertEquals(0, count($updatedcoursewarnings['warnings']));
  841 + $course1['visible'] = 0;
  842 + $courses = array($course1);
  843 + $updatedcoursewarnings = core_course_external::update_courses($courses);
  844 + $this->assertEquals(1, count($updatedcoursewarnings['warnings']));
  845 + }
640 846 }
9 lib/db/services.php
@@ -514,6 +514,15 @@
514 514 'capabilities'=> 'moodle/backup:backupcourse,moodle/restore:restorecourse,moodle/course:create',
515 515 ),
516 516
  517 + 'core_course_update_courses' => array(
  518 + 'classname' => 'core_course_external',
  519 + 'methodname' => 'update_courses',
  520 + 'classpath' => 'course/externallib.php',
  521 + 'description' => 'Update courses',
  522 + 'type' => 'write',
  523 + 'capabilities'=> 'moodle/course:update,moodle/course:changecategory,moodle/course:changefullname,moodle/course:changeshortname,moodle/course:changeidnumber,moodle/course:changesummary,moodle/course:visibility',
  524 + ),
  525 +
517 526 // === course category related functions ===
518 527
519 528 'core_course_get_categories' => array(
2  version.php
@@ -30,7 +30,7 @@
30 30 defined('MOODLE_INTERNAL') || die();
31 31
32 32
33   -$version = 2013013100.00; // YYYYMMDD = weekly release date of this DEV branch
  33 +$version = 2013020500.00; // YYYYMMDD = weekly release date of this DEV branch
34 34 // RR = release increments - 00 in DEV branches
35 35 // .XX = incremental changes
36 36

0 comments on commit 791723c

Please sign in to comment.
Something went wrong with that request. Please try again.