Commit 2ab697f6 authored by Sara Arjona's avatar Sara Arjona
Browse files

Merge branch 'MDL-69561-master' of https://github.com/lameze/moodle

parents 3da8ea46 89e59a60
......@@ -275,7 +275,7 @@ class backup_module_structure_step extends backup_structure_step {
'visibleold', 'groupmode', 'groupingid',
'completion', 'completiongradeitemnumber', 'completionpassgrade',
'completionview', 'completionexpected',
'availability', 'showdescription'));
'availability', 'showdescription', 'downloadcontent'));
$tags = new backup_nested_element('tags');
$tag = new backup_nested_element('tag', array('id'), array('name', 'rawname'));
......
......@@ -275,6 +275,7 @@ class core_course_external extends external_api {
$module['afterlink'] = $cm->afterlink;
$module['customdata'] = json_encode($cm->customdata);
$module['completion'] = $cm->completion;
$module['downloadcontent'] = $cm->downloadcontent;
$module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false);
$module['dates'] = $activitydates;
......@@ -477,6 +478,7 @@ class core_course_external extends external_api {
'completion' => new external_value(PARAM_INT, 'Type of completion tracking:
0 means none, 1 manual, 2 automatic.', VALUE_OPTIONAL),
'completiondata' => $completiondefinition,
'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL),
'dates' => new external_multiple_structure(
new external_single_structure(
array(
......@@ -2828,6 +2830,7 @@ class core_course_external extends external_api {
$info->groupmode = $cm->groupmode;
$info->groupingid = $cm->groupingid;
$info->completion = $cm->completion;
$info->downloadcontent = $cm->downloadcontent;
}
// Format name.
$info->name = external_format_string($cm->name, $context->id);
......@@ -2871,6 +2874,7 @@ class core_course_external extends external_api {
'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL),
'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
'grade' => new external_value(PARAM_FLOAT, 'Grade (max value or scale id)', VALUE_OPTIONAL),
'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL),
......
......@@ -467,7 +467,7 @@ function get_array_of_activities($courseid) {
$mod[$seq]->showdescription = $rawmods[$seq]->showdescription;
$mod[$seq]->availability = $rawmods[$seq]->availability;
$mod[$seq]->deletioninprogress = $rawmods[$seq]->deletioninprogress;
$mod[$seq]->downloadcontent = $rawmods[$seq]->downloadcontent;
$modname = $mod[$seq]->mod;
$functionname = $modname."_get_coursemodule_info";
......@@ -852,6 +852,23 @@ function set_coursemodule_idnumber($id, $idnumber) {
return ($cm->idnumber != $idnumber);
}
/**
* Set downloadcontent value to course module.
*
* @param int $id The id of the module.
* @param bool $downloadcontent Whether the module can be downloaded when download course content is enabled.
* @return bool True if downloadcontent has been updated, false otherwise.
*/
function set_downloadcontent(int $id, bool $downloadcontent): bool {
global $DB;
$cm = $DB->get_record('course_modules', ['id' => $id], 'id, course, downloadcontent', MUST_EXIST);
if ($cm->downloadcontent != $downloadcontent) {
$DB->set_field('course_modules', 'downloadcontent', $downloadcontent, ['id' => $cm->id]);
rebuild_course_cache($cm->course, true);
}
return ($cm->downloadcontent != $downloadcontent);
}
/**
* Set the visibility of a module and inherent properties.
*
......
......@@ -67,6 +67,9 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
if (isset($moduleinfo->cmidnumber)) {
$newcm->idnumber = $moduleinfo->cmidnumber;
}
if (isset($moduleinfo->downloadcontent)) {
$newcm->downloadcontent = $moduleinfo->downloadcontent;
}
$newcm->groupmode = $moduleinfo->groupmode;
$newcm->groupingid = $moduleinfo->groupingid;
$completion = new completion_info($course);
......@@ -460,6 +463,10 @@ function set_moduleinfo_defaults($moduleinfo) {
$moduleinfo->visibleoncoursepage = 1;
}
if (!isset($moduleinfo->downloadcontent)) {
$moduleinfo->downloadcontent = DOWNLOAD_COURSE_CONTENT_ENABLED;
}
return $moduleinfo;
}
......@@ -662,6 +669,10 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber);
}
if (isset($moduleinfo->downloadcontent)) {
set_downloadcontent($moduleinfo->coursemodule, $moduleinfo->downloadcontent);
}
// Update module tags.
if (core_tag_tag::is_enabled('core', 'course_modules') && isset($moduleinfo->tags)) {
core_tag_tag::set_item_tags('core', 'course_modules', $moduleinfo->coursemodule, $modcontext, $moduleinfo->tags);
......@@ -731,6 +742,7 @@ function get_moduleinfo_data($cm, $course) {
$data->completionpassgrade = $cm->completionpassgrade;
$data->completiongradeitemnumber = $cm->completiongradeitemnumber;
$data->showdescription = $cm->showdescription;
$data->downloadcontent = $cm->downloadcontent;
$data->tags = core_tag_tag::get_item_tags_array('core', 'course_modules', $cm->id);
if (!empty($CFG->enableavailability)) {
$data->availabilityconditionsjson = $cm->availability;
......@@ -830,6 +842,7 @@ function prepare_new_moduleinfo_data($course, $modulename, $section) {
$data->id = '';
$data->instance = '';
$data->coursemodule = '';
$data->downloadcontent = DOWNLOAD_COURSE_CONTENT_ENABLED;
// Apply completion defaults.
$defaults = \core_completion\manager::get_default_completion($course, $module);
......
......@@ -27,6 +27,7 @@ require_once($CFG->libdir.'/completionlib.php');
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->libdir.'/plagiarismlib.php');
use core\content\export\exporters\component_exporter;
use core_grades\component_gradeitems;
/**
......@@ -637,6 +638,22 @@ abstract class moodleform_mod extends moodleform {
$mform->addHelpButton('groupmode', 'groupmode', 'group');
}
if ($CFG->downloadcoursecontentallowed) {
$choices = [
DOWNLOAD_COURSE_CONTENT_DISABLED => get_string('no'),
DOWNLOAD_COURSE_CONTENT_ENABLED => get_string('yes'),
];
$mform->addElement('select', 'downloadcontent', get_string('downloadcontent', 'course'), $choices);
$downloadcontentdefault = $this->_cm->downloadcontent ?? DOWNLOAD_COURSE_CONTENT_ENABLED;
$mform->addHelpButton('downloadcontent', 'downloadcontent', 'course');
if (has_capability('moodle/course:configuredownloadcontent', $this->get_context())) {
$mform->setDefault('downloadcontent', $downloadcontentdefault);
} else {
$mform->hardFreeze('downloadcontent');
$mform->setConstant('downloadcontent', $downloadcontentdefault);
}
}
if ($this->_features->groupings) {
// Groupings selector - used to select grouping for groups in activity.
$options = array();
......
@core @core_course
Feature: Activities content download can be controlled
In order to allow or restrict access to download activity content
As a teacher
I can disable the content download of an activity
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| manager1 | Manager | 1 | manager1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| manager1 | C1 | manager |
And the following "activities" exist:
| activity | name | intro | introformat | course |
| page | Page1 | PageDesc1 | 1 | C1 |
And the following "activities" exist:
| activity | name | intro | introformat | course | downloadcontent |
| folder | Folder1 | FolderDesc1 | 1 | C1 | 0 |
And I log in as "admin"
And the following config values are set as admin:
| downloadcoursecontentallowed | 1 |
And I log out
Scenario: "Include in course downloads (if that feature is enabled)" field default is set to "Yes" if nothing has been set
Given I am on the Page1 "Page Activity editing" page logged in as admin
Then the field "Include in course downloads (if that feature is enabled)" matches value "Yes"
Scenario: "Include in course downloads (if that feature is enabled)" field is not visible if course content is disabled on site level
Given I log in as "admin"
And the following config values are set as admin:
| downloadcoursecontentallowed | 0 |
And I am on the Page1 "Page Activity editing" page
Then "Include in course downloads (if that feature is enabled)" "select" should not exist
Scenario: "Include in course downloads (if that feature is enabled)" field is visible even if course content is disabled on course level
Given I log in as "admin"
And I am on "Course 1" course homepage
And I navigate to "Settings" in current page administration
When I set the field "Enable download course content" to "No"
And I press "Save and display"
And I am on the Page1 "Page Activity editing" page
Then "Include in course downloads (if that feature is enabled)" "select" should exist
Scenario: "Include in course downloads (if that feature is enabled)" field should be visible but not editable for users without configuredownloadcontent capability
Given I log in as "manager1"
And I am on the Folder1 "Folder Activity editing" page
And "Include in course downloads (if that feature is enabled)" "field" should exist
And I log out
And I log in as "admin"
When I set the following system permissions of "Manager" role:
| capability | permission |
| moodle/course:configuredownloadcontent | Prohibit |
And I log out
And I log in as "manager1"
And I am on the Folder1 "Folder Activity editing" page
Then I should see "Include in course downloads (if that feature is enabled)"
And I should see "No"
And "Include in course downloads (if that feature is enabled)" "select" should not exist
......@@ -7233,4 +7233,30 @@ class core_course_courselib_testcase extends advanced_testcase {
$this->assertEquals(1, average_number_of_participants(true));
}
/**
* Test the set_downloadcontent() function.
*/
public function test_set_downloadcontent() {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$page = $generator->create_module('page', ['course' => $course]);
// Test the module 'downloadcontent' field is set to enabled.
set_downloadcontent($page->cmid, DOWNLOAD_COURSE_CONTENT_ENABLED);
$modinfo = get_fast_modinfo($course)->get_cm($page->cmid);
$this->assertEquals(DOWNLOAD_COURSE_CONTENT_ENABLED, $modinfo->downloadcontent);
// Now let's test the 'downloadcontent' value is updated to disabled.
set_downloadcontent($page->cmid, DOWNLOAD_COURSE_CONTENT_DISABLED);
$modinfo = get_fast_modinfo($course)->get_cm($page->cmid);
$this->assertEquals(DOWNLOAD_COURSE_CONTENT_DISABLED, $modinfo->downloadcontent);
// Nothing to update, the download course content value is the same, it should return false.
$this->assertFalse(set_downloadcontent($page->cmid, DOWNLOAD_COURSE_CONTENT_DISABLED));
// The download course content value has changed, it should return true in this case.
$this->assertTrue(set_downloadcontent($page->cmid, DOWNLOAD_COURSE_CONTENT_ENABLED));
}
}
......@@ -1366,6 +1366,28 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertEquals($pagecm->instance, $sections[0]['modules'][0]["instance"]);
}
/**
* Test get_course_contents returns downloadcontent value.
*/
public function test_get_course_contents_downloadcontent() {
$this->resetAfterTest();
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, [
['name' => 'modname', 'value' => 'page'],
['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(1, $sections[0]['modules']);
$this->assertEquals('page', $sections[0]['modules'][0]['modname']);
$this->assertEquals($pagecm->downloadcontent, $sections[0]['modules'][0]['downloadcontent']);
$this->assertEquals(DOWNLOAD_COURSE_CONTENT_ENABLED, $sections[0]['modules'][0]['downloadcontent']);
}
/**
* Test get course contents completion manual
*/
......@@ -2373,7 +2395,7 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(0, $result['warnings']);
// Test we retrieve all the fields.
$this->assertCount(29, $result['cm']);
$this->assertCount(30, $result['cm']);
$this->assertEquals($record['name'], $result['cm']['name']);
$this->assertEquals($options['idnumber'], $result['cm']['idnumber']);
$this->assertEquals(100, $result['cm']['grade']);
......@@ -2381,6 +2403,7 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertEquals('submissions', $result['cm']['advancedgrading'][0]['area']);
$this->assertEmpty($result['cm']['advancedgrading'][0]['method']);
$this->assertEquals($outcomescale, $result['cm']['outcomes'][0]['scale']);
$this->assertEquals(DOWNLOAD_COURSE_CONTENT_ENABLED, $result['cm']['downloadcontent']);
$student = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
......@@ -2405,7 +2428,7 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(0, $result['warnings']);
// Test we retrieve only the few files we can see.
$this->assertCount(11, $result['cm']);
$this->assertCount(12, $result['cm']);
$this->assertEquals($assign->cmid, $result['cm']['id']);
$this->assertEquals($course->id, $result['cm']['course']);
$this->assertEquals('assign', $result['cm']['modname']);
......@@ -2441,10 +2464,11 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(0, $result['warnings']);
// Test we retrieve all the fields.
$this->assertCount(27, $result['cm']);
$this->assertCount(28, $result['cm']);
$this->assertEquals($record['name'], $result['cm']['name']);
$this->assertEquals($record['grade'], $result['cm']['grade']);
$this->assertEquals($options['idnumber'], $result['cm']['idnumber']);
$this->assertEquals(DOWNLOAD_COURSE_CONTENT_ENABLED, $result['cm']['downloadcontent']);
$student = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
......@@ -2469,7 +2493,7 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(0, $result['warnings']);
// Test we retrieve only the few files we can see.
$this->assertCount(11, $result['cm']);
$this->assertCount(12, $result['cm']);
$this->assertEquals($quiz->cmid, $result['cm']['id']);
$this->assertEquals($course->id, $result['cm']['course']);
$this->assertEquals('quiz', $result['cm']['modname']);
......
......@@ -63,6 +63,8 @@ class core_course_modlib_testcase extends advanced_testcase {
$expecteddata->coursemodule = '';
$expecteddata->advancedgradingmethod_submissions = ''; // Not grading methods enabled by default.
$expecteddata->completion = 0;
$expecteddata->downloadcontent = DOWNLOAD_COURSE_CONTENT_ENABLED;
// Unset untestable.
unset($data->introeditor);
unset($data->_advancedgradingdata);
......@@ -115,6 +117,7 @@ class core_course_modlib_testcase extends advanced_testcase {
$expecteddata->completionpassgrade = $assigncm->completionpassgrade;
$expecteddata->completiongradeitemnumber = null;
$expecteddata->showdescription = $assigncm->showdescription;
$expecteddata->downloadcontent = $assigncm->downloadcontent;
$expecteddata->tags = core_tag_tag::get_item_tags_array('core', 'course_modules', $assigncm->id);
$expecteddata->availabilityconditionsjson = null;
$expecteddata->advancedgradingmethod_submissions = null;
......
......@@ -79,6 +79,8 @@ $string['customfieldsettings'] = 'Common course custom fields settings';
$string['downloadcourseconfirmation'] = 'You are about to download a zip file of course content (excluding items which cannot be downloaded and any files larger than {$a}).';
$string['downloadcoursecontent'] = 'Download course content';
$string['downloadcoursecontent_help'] = 'This setting determines whether course content may be downloaded by users with the download course content capability (by default users with the role of student or teacher).';
$string['downloadcontent'] = 'Include in course downloads (if that feature is enabled)';
$string['downloadcontent_help'] = 'This setting determines whether this activity can be downloaded when download course content is enabled for this course';
$string['enabledownloadcoursecontent'] = 'Enable download course content';
$string['errorendbeforestart'] = 'The end date ({$a}) is before the course start date.';
$string['favourite'] = 'Starred course';
......
......@@ -71,6 +71,13 @@ class content {
}
} else if ($currentcontext->contextlevel == CONTEXT_MODULE) {
$cm = get_fast_modinfo($currentcontext->get_course_context()->instanceid)->cms[$currentcontext->instanceid];
// Do not export course content if disabled at activity level.
if (isset($cm->downloadcontent) && $cm->downloadcontent == DOWNLOAD_COURSE_CONTENT_DISABLED) {
return false;
}
// Modules can only be exported if exporting is allowed in their course context.
$canexport = self::can_export_context($currentcontext->get_course_context(), $user);
}
......
......@@ -308,6 +308,7 @@
<FIELD NAME="showdescription" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Some module types support a 'description' which shows within the module pages. This option controls whether it also displays on the course main page. 0 = does not display (default), 1 = displays"/>
<FIELD NAME="availability" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Availability restrictions for viewing this activity, in JSON format. Null if no restrictions."/>
<FIELD NAME="deletioninprogress" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="downloadcontent" TYPE="int" LENGTH="1" NOTNULL="false" SEQUENCE="false" COMMENT="Whether the ability to download course module content is enabled for this activity" DEFAULT="1"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
......
......@@ -3116,5 +3116,19 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2021110100.00);
}
if ($oldversion < 2021110800.02) {
// Define a field 'downloadcontent' in the 'course_modules' table.
$table = new xmldb_table('course_modules');
$field = new xmldb_field('downloadcontent', XMLDB_TYPE_INTEGER, '1', null, null, null, 1, 'deletioninprogress');
// Conditionally launch add field 'downloadcontent'.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2021110800.02);
}
return true;
}
......@@ -843,6 +843,7 @@ class course_modinfo {
* @property-read string $afterlink Extra HTML code to display after link - calculated on request
* @property-read string $afterediticons Extra HTML code to display after editing icons (e.g. more icons) - calculated on request
* @property-read bool $deletioninprogress True if this course module is scheduled for deletion, false otherwise.
* @property-read bool $downloadcontent True if content download is enabled for this course module, false otherwise.
*/
class cm_info implements IteratorAggregate {
/**
......@@ -1156,6 +1157,11 @@ class cm_info implements IteratorAggregate {
*/
private $deletioninprogress;
/**
* @var int enable/disable download content for this course module
*/
private $downloadcontent;
/**
* List of class read-only properties and their getter methods.
* Used by magic functions __get(), __isset(), __empty()
......@@ -1209,7 +1215,8 @@ class cm_info implements IteratorAggregate {
'visible' => false,
'visibleoncoursepage' => false,
'visibleold' => false,
'deletioninprogress' => false
'deletioninprogress' => false,
'downloadcontent' => false
);
/**
......@@ -1656,7 +1663,8 @@ class cm_info implements IteratorAggregate {
static $cmfields = array('id', 'course', 'module', 'instance', 'section', 'idnumber', 'added',
'score', 'indent', 'visible', 'visibleoncoursepage', 'visibleold', 'groupmode', 'groupingid',
'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected', 'completionpassgrade',
'showdescription', 'availability', 'deletioninprogress');
'showdescription', 'availability', 'deletioninprogress', 'downloadcontent');
foreach ($cmfields as $key) {
$cmrecord->$key = $this->$key;
}
......@@ -1869,6 +1877,7 @@ class cm_info implements IteratorAggregate {
$this->score = isset($mod->score) ? $mod->score : 0;
$this->visibleold = isset($mod->visibleold) ? $mod->visibleold : 0;
$this->deletioninprogress = isset($mod->deletioninprogress) ? $mod->deletioninprogress : 0;
$this->downloadcontent = $mod->downloadcontent ?? null;
// Note: it saves effort and database space to always include the
// availability and completion fields, even if availability or completion
......
......@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2021110800.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2021110800.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev+ (Build: 20211106)'; // Human-friendly version name
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment