MDL-69559 course: Add capability and access checks for course download
authorMichael Hawkins <michaelh@moodle.com>
Thu, 3 Sep 2020 05:53:41 +0000 (13:53 +0800)
committerMichael Hawkins <michaelh@moodle.com>
Mon, 26 Oct 2020 02:19:33 +0000 (10:19 +0800)
lang/en/role.php
lib/classes/content.php
lib/db/access.php
lib/tests/content_test.php [new file with mode: 0644]

index 625c0c0..f625163 100644 (file)
@@ -172,6 +172,7 @@ $string['course:changelockedcustomfields'] = 'Change locked custom fields';
 $string['course:changeshortname'] = 'Change course short name';
 $string['course:changesummary'] = 'Change course summary';
 $string['course:configurecustomfields'] = 'Configure custom fields';
+$string['course:downloadcoursecontent'] = 'Download course content';
 $string['course:enrolconfig'] = 'Configure enrol instances in courses';
 $string['course:enrolreview'] = 'Review course enrolments';
 $string['course:setforcedlanguage'] = 'Force course language';
index 1699213..d9ad2c9 100644 (file)
@@ -52,7 +52,30 @@ class content {
      * @return  bool
      */
     public static function can_export_context(context $currentcontext, stdClass $user): bool {
-        return true;
+        global $CFG;
+
+        $canexport = false;
+
+        if ($currentcontext->contextlevel == CONTEXT_COURSE) {
+            if ($CFG->downloadcoursecontentallowed &&
+                    has_capability('moodle/course:downloadcoursecontent', $currentcontext, $user)) {
+
+                $courseinfo = get_fast_modinfo($currentcontext->instanceid)->get_course();
+
+                // If enabled/disabled explicitly set on course, use that as the course setting, otherwise use site default.
+                if (isset($courseinfo->downloadcontent) && $courseinfo->downloadcontent != DOWNLOAD_COURSE_CONTENT_SITE_DEFAULT) {
+                    $canexport = $courseinfo->downloadcontent;
+                } else {
+                    $canexport = get_config('moodlecourse')->downloadcontentsitedefault;
+                }
+
+            }
+        } else if ($currentcontext->contextlevel == CONTEXT_MODULE) {
+            // Modules can only be exported if exporting is allowed in their course context.
+            $canexport = self::can_export_context($currentcontext->get_course_context(), $user);
+        }
+
+        return $canexport;
     }
 
     /**
index 5078f4d..403746a 100644 (file)
@@ -2576,4 +2576,16 @@ $capabilities = array(
             'editingteacher' => CAP_ALLOW,
         ]
     ],
+
+    // Allow users to download course content.
+    'moodle/course:downloadcoursecontent' => [
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'student' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        )
+    ],
 );
diff --git a/lib/tests/content_test.php b/lib/tests/content_test.php
new file mode 100644 (file)
index 0000000..52c8f56
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for core\content class.
+ *
+ * @package     core
+ * @category    test
+ * @copyright   2020 Michael Hawkins <michaelh@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core;
+
+/**
+ * Unit tests for core\content class.
+ *
+ * @package     core
+ * @category    test
+ * @copyright   2020 Michael Hawkins <michaelh@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class content_test extends \advanced_testcase {
+
+    /**
+     * A test to confirm only valid cases allow exporting of course content.
+     */
+    public function test_can_export_context_course() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $course1context = \context_course::instance($course1->id);
+        $course2context = \context_course::instance($course2->id);
+
+        // Enrol user as student in course1 only.
+        $user = $this->getDataGenerator()->create_and_enrol($course1, 'student');
+
+        // Confirm by default enrolled user does not have permission to export in course1.
+        $this->assertFalse(content::can_export_context($course1context, $user));
+
+        // Make course download available on site, but not enabled in course1 or by default.
+        set_config('downloadcoursecontentallowed', true);
+
+        // Confirm user still does not have permission to export (disabled in courses by default).
+        $this->assertFalse(content::can_export_context($course1context, $user));
+
+        // Enable export in courses by default.
+        set_config('downloadcontentsitedefault', DOWNLOAD_COURSE_CONTENT_ENABLED, 'moodlecourse');
+
+        // Confirm user now has permission to export in course1 only.
+        $this->assertTrue(content::can_export_context($course1context, $user));
+
+        // Disable course downloads in course1.
+        $course1->downloadcontent = DOWNLOAD_COURSE_CONTENT_DISABLED;
+        $DB->update_record('course', $course1);
+        rebuild_course_cache($course1->id);
+
+        // Confirm user does not have permission to export in course1.
+        $this->assertFalse(content::can_export_context($course1context, $user));
+
+        // Enable course downloads in course1.
+        $course1->downloadcontent = DOWNLOAD_COURSE_CONTENT_ENABLED;
+        $DB->update_record('course', $course1);
+        rebuild_course_cache($course1->id);
+
+        // Confirm user has permission to export in course1.
+        $this->assertTrue(content::can_export_context($course1context, $user));
+
+        // Confirm user does not have permission to export in course they are not enrolled in (course2).
+        $this->assertFalse(content::can_export_context($course2context, $user));
+
+        // Disable export in courses by default.
+        set_config('downloadcontentsitedefault', DOWNLOAD_COURSE_CONTENT_DISABLED, 'moodlecourse');
+
+        // Confirm user still has permission to export in course1 (still enabled at the course level).
+        $this->assertTrue(content::can_export_context($course1context, $user));
+
+        // Disable the course downloads feature.
+        set_config('downloadcoursecontentallowed', false);
+
+        // Confirm user no longer has permission to export in course1.
+        $this->assertFalse(content::can_export_context($course1context, $user));
+    }
+
+    /**
+     * A test to confirm unsupported contexts will return false when checking whether content can be exported.
+     */
+    public function test_can_export_context_unsupported_context() {
+        $this->resetAfterTest();
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $systemcontext = \context_system::instance();
+
+        // Enrol user as student in course1 only.
+        $user = $this->getDataGenerator()->create_and_enrol($course1, 'student');
+
+        // Make course download available on site (course context).
+        set_config('downloadcoursecontentallowed', true);
+
+        // Confirm system context does not gain permission to export content.
+        $this->assertFalse(content::can_export_context($systemcontext, $user));
+    }
+}