Merge branch 'MDL-49242-master' of git://github.com/jleyva/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 10 Mar 2015 23:11:56 +0000 (00:11 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 10 Mar 2015 23:11:56 +0000 (00:11 +0100)
course/externallib.php
course/tests/externallib_test.php

index 0399d4f..0bd03d5 100644 (file)
@@ -43,6 +43,7 @@ class core_course_external extends external_api {
      * Returns description of method parameters
      *
      * @return external_function_parameters
+     * @since Moodle 2.9 Options available
      * @since Moodle 2.2
      */
     public static function get_course_contents_parameters() {
@@ -50,10 +51,20 @@ class core_course_external extends external_api {
                 array('courseid' => new external_value(PARAM_INT, 'course id'),
                       'options' => new external_multiple_structure (
                               new external_single_structure(
-                                    array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
-                                          'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
+                                array(
+                                    'name' => new external_value(PARAM_ALPHANUM,
+                                                'The expected keys (value format) are:
+                                                excludemodules (bool) Do not return modules, return only the sections structure
+                                                excludecontents (bool) Do not return module contents (i.e: files inside a resource)
+                                                sectionid (int) Return only this section
+                                                sectionnumber (int) Return only this section with number (order)
+                                                cmid (int) Return only this module information (among the whole sections structure)
+                                                modname (string) Return only modules with this name "label, forum, etc..."
+                                                modid (int) Return only the module with this id (to be used with modname'),
+                                    'value' => new external_value(PARAM_RAW, 'the value of the option,
+                                                                    this param is personaly validated in the external function.')
                               )
-                      ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
+                      ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
                 )
         );
     }
@@ -62,8 +73,9 @@ class core_course_external extends external_api {
      * Get course contents
      *
      * @param int $courseid course id
-     * @param array $options These options are not used yet, might be used in later version
+     * @param array $options Options for filtering the results, used since Moodle 2.9
      * @return array
+     * @since Moodle 2.9 Options available
      * @since Moodle 2.2
      */
     public static function get_course_contents($courseid, $options = array()) {
@@ -74,6 +86,45 @@ class core_course_external extends external_api {
         $params = self::validate_parameters(self::get_course_contents_parameters(),
                         array('courseid' => $courseid, 'options' => $options));
 
+        $filters = array();
+        if (!empty($params['options'])) {
+
+            foreach ($params['options'] as $option) {
+                $name = trim($option['name']);
+                // Avoid duplicated options.
+                if (!isset($filters[$name])) {
+                    switch ($name) {
+                        case 'excludemodules':
+                        case 'excludecontents':
+                            $value = clean_param($option['value'], PARAM_BOOL);
+                            $filters[$name] = $value;
+                            break;
+                        case 'sectionid':
+                        case 'sectionnumber':
+                        case 'cmid':
+                        case 'modid':
+                            $value = clean_param($option['value'], PARAM_INT);
+                            if (is_numeric($value)) {
+                                $filters[$name] = $value;
+                            } else {
+                                throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
+                            }
+                            break;
+                        case 'modname':
+                            $value = clean_param($option['value'], PARAM_PLUGIN);
+                            if ($value) {
+                                $filters[$name] = $value;
+                            } else {
+                                throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
+                            }
+                            break;
+                        default:
+                            throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
+                    }
+                }
+            }
+        }
+
         //retrieve the course
         $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
 
@@ -118,6 +169,27 @@ class core_course_external extends external_api {
                     continue;
                 }
 
+                // This becomes true when we are filtering and we found the value to filter with.
+                $sectionfound = false;
+
+                // Filter by section id.
+                if (!empty($filters['sectionid'])) {
+                    if ($section->id != $filters['sectionid']) {
+                        continue;
+                    } else {
+                        $sectionfound = true;
+                    }
+                }
+
+                // Filter by section number. Note that 0 is a valid section number.
+                if (isset($filters['sectionnumber'])) {
+                    if ($key != $filters['sectionnumber']) {
+                        continue;
+                    } else {
+                        $sectionfound = true;
+                    }
+                }
+
                 // reset $sectioncontents
                 $sectionvalues = array();
                 $sectionvalues['id'] = $section->id;
@@ -129,7 +201,7 @@ class core_course_external extends external_api {
                 $sectioncontents = array();
 
                 //for each module of the section
-                if (!empty($modinfosections[$section->section])) {
+                if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
                     foreach ($modinfosections[$section->section] as $cmid) {
                         $cm = $modinfo->cms[$cmid];
 
@@ -138,6 +210,32 @@ class core_course_external extends external_api {
                             continue;
                         }
 
+                        // This becomes true when we are filtering and we found the value to filter with.
+                        $modfound = false;
+
+                        // Filter by cmid.
+                        if (!empty($filters['cmid'])) {
+                            if ($cmid != $filters['cmid']) {
+                                continue;
+                            } else {
+                                $modfound = true;
+                            }
+                        }
+
+                        // Filter by module name and id.
+                        if (!empty($filters['modname'])) {
+                            if ($cm->modname != $filters['modname']) {
+                                continue;
+                            } else if (!empty($filters['modid'])) {
+                                if ($cm->instance != $filters['modid']) {
+                                    continue;
+                                } else {
+                                    // Note that if we are only filtering by modname we don't break the loop.
+                                    $modfound = true;
+                                }
+                            }
+                        }
+
                         $module = array();
 
                         //common info (for people being able to see the module or availability dates)
@@ -177,23 +275,36 @@ class core_course_external extends external_api {
 
                         //call $modulename_export_contents
                         //(each module callback take care about checking the capabilities)
+
                         require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
                         $getcontentfunction = $cm->modname.'_export_contents';
                         if (function_exists($getcontentfunction)) {
-                            if ($contents = $getcontentfunction($cm, $baseurl)) {
+                            if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
                                 $module['contents'] = $contents;
+                            } else {
+                                $module['contents'] = array();
                             }
                         }
 
                         //assign result to $sectioncontents
                         $sectioncontents[] = $module;
 
+                        // If we just did a filtering, break the loop.
+                        if ($modfound) {
+                            break;
+                        }
+
                     }
                 }
                 $sectionvalues['modules'] = $sectioncontents;
 
                 // assign result to $coursecontents
                 $coursecontents[] = $sectionvalues;
+
+                // Break the loop if we are filtering.
+                if ($sectionfound) {
+                    break;
+                }
             }
         }
         return $coursecontents;
index 7adad2c..d496a38 100644 (file)
@@ -597,20 +597,19 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
     }
 
     /**
-     * Test get_course_contents
+     * Create a course with contents
+     * @return array A list with the course object and course modules objects
      */
-    public function test_get_course_contents() {
-        $this->resetAfterTest(true);
-
+    private function prepare_get_course_contents_test() {
         $course  = self::getDataGenerator()->create_course();
         $forumdescription = 'This is the forum description';
         $forum = $this->getDataGenerator()->create_module('forum',
-            array('course'=>$course->id, 'intro' => $forumdescription),
+            array('course' => $course->id, 'intro' => $forumdescription),
             array('showdescription' => true));
         $forumcm = get_coursemodule_from_id('forum', $forum->cmid);
-        $data = $this->getDataGenerator()->create_module('data', array('assessed'=>1, 'scale'=>100, 'course'=>$course->id));
+        $data = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
         $datacm = get_coursemodule_from_instance('page', $data->id);
-        $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
         $pagecm = get_coursemodule_from_instance('page', $page->id);
         $labeldescription = 'This is a very long label to test if more than 50 characters are returned.
                 So bla bla bla bla <b>bold bold bold</b> bla bla bla bla.';
@@ -618,23 +617,36 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
             'intro' => $labeldescription));
         $labelcm = get_coursemodule_from_instance('label', $label->id);
         $url = $this->getDataGenerator()->create_module('url', array('course' => $course->id,
-            'name' => 'URL: % & $ ../'));
+            'name' => 'URL: % & $ ../', 'section' => 2));
+        $urlcm = get_coursemodule_from_instance('url', $url->id);
 
         // Set the required capabilities by the external function.
         $context = context_course::instance($course->id);
         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
         $this->assignUserCapability('moodle/course:update', $context->id, $roleid);
 
-        $sections = core_course_external::get_course_contents($course->id, array());
+        return array($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm);
+    }
+
+    /**
+     * Test get_course_contents
+     */
+    public function test_get_course_contents() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
 
+        $sections = core_course_external::get_course_contents($course->id, array());
         // We need to execute the return values cleaning process to simulate the web service server.
         $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
 
         // Check that forum and label descriptions are correctly returned.
-        $firstsection = array_pop($sections);
+        $firstsection = array_shift($sections);
+        $lastsection = array_pop($sections);
+
         $modinfo = get_fast_modinfo($course);
         $testexecuted = 0;
-        foreach($firstsection['modules'] as $module) {
+        foreach ($firstsection['modules'] as $module) {
             if ($module['id'] == $forumcm->id and $module['modname'] == 'forum') {
                 $cm = $modinfo->cms[$forumcm->id];
                 $formattedtext = format_text($cm->content, FORMAT_HTML,
@@ -653,8 +665,165 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         }
         $this->assertEquals(2, $testexecuted);
 
-        // Check that the only return section has the 5 created modules
-        $this->assertEquals(5, count($firstsection['modules']));
+        // Check that the only return section has the 5 created modules.
+        $this->assertCount(4, $firstsection['modules']);
+        $this->assertCount(1, $lastsection['modules']);
+
+        try {
+            $sections = core_course_external::get_course_contents($course->id,
+                                                                    array(array("name" => "invalid", "value" => 1)));
+            $this->fail('Exception expected due to invalid option.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('errorinvalidparam', $e->errorcode);
+        }
+    }
+
+
+    /**
+     * Test get_course_contents excluding modules
+     */
+    public function test_get_course_contents_excluding_modules() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(array("name" => "excludemodules", "value" => 1)));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $firstsection = array_shift($sections);
+        $lastsection = array_pop($sections);
+
+        $this->assertEmpty($firstsection['modules']);
+        $this->assertEmpty($lastsection['modules']);
+    }
+
+    /**
+     * Test get_course_contents excluding contents
+     */
+    public function test_get_course_contents_excluding_contents() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(array("name" => "excludecontents", "value" => 1)));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        foreach ($sections as $section) {
+            foreach ($section['modules'] as $module) {
+                // Only resources return contents.
+                if (isset($module['contents'])) {
+                    $this->assertEmpty($module['contents']);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test get_course_contents filtering by section number
+     */
+    public function test_get_course_contents_section_number() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(array("name" => "sectionnumber", "value" => 0)));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $this->assertCount(1, $sections);
+        $this->assertCount(4, $sections[0]['modules']);
+    }
+
+    /**
+     * Test get_course_contents filtering by cmid
+     */
+    public function test_get_course_contents_cmid() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(array("name" => "cmid", "value" => $forumcm->id)));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $this->assertCount(2, $sections);
+        $this->assertCount(1, $sections[0]['modules']);
+        $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]);
+    }
+
+
+    /**
+     * Test get_course_contents filtering by cmid and section
+     */
+    public function test_get_course_contents_section_cmid() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(
+                                                                        array("name" => "cmid", "value" => $forumcm->id),
+                                                                        array("name" => "sectionnumber", "value" => 0)
+                                                                        ));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $this->assertCount(1, $sections);
+        $this->assertCount(1, $sections[0]['modules']);
+        $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]);
+    }
+
+    /**
+     * Test get_course_contents filtering by modname
+     */
+    public function test_get_course_contents_modname() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(array("name" => "modname", "value" => "forum")));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $this->assertCount(2, $sections);
+        $this->assertCount(1, $sections[0]['modules']);
+        $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]);
+    }
+
+    /**
+     * Test get_course_contents filtering by modname
+     */
+    public function test_get_course_contents_modid() {
+        $this->resetAfterTest(true);
+
+        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
+
+        // Test exclude modules.
+        $sections = core_course_external::get_course_contents($course->id, array(
+                                                                            array("name" => "modname", "value" => "page"),
+                                                                            array("name" => "modid", "value" => $pagecm->instance),
+                                                                            ));
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
+
+        $this->assertCount(2, $sections);
+        $this->assertCount(1, $sections[0]['modules']);
+        $this->assertEquals("page", $sections[0]['modules'][0]["modname"]);
+        $this->assertEquals($pagecm->instance, $sections[0]['modules'][0]["instance"]);
     }
 
     /**