Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add callbacks to add or keep section number #117

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 53 additions & 19 deletions classes/form/section_select_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class section_select_form extends moodleform {
/** @var int array of sections which are allowed to be chosen */
protected $allowedsections = [];

/**
* Form definition.
Expand Down Expand Up @@ -100,47 +102,79 @@ public function definition() {
$targetsections = array_slice($targetsections, 0, $targetsectionnum + 1);

// Check for permissions.
$canaddsection = has_capability('moodle/course:update', \context_course::instance($targetcourseid));
$canaddsection = massactionutils::can_add_section($targetcourseid, $targetformat->get_format());

// Find maximum section that may need to be created.
$massactionrequest = $this->_customdata['request'];
$data = \block_massaction\massactionutils::extract_modules_from_json($massactionrequest);
$data = massactionutils::extract_modules_from_json($massactionrequest);
$modules = $data->modulerecords;
$srcmaxsectionnum = max(array_map(function($mod) use ($sourcecoursemodinfo) {
return $sourcecoursemodinfo->get_cm($mod->id)->sectionnum;
}, $modules));

$radioarray = [];

// Attributes for radio buttons.
$attributes = ['class' => 'mt-2'];

// If user can add sections in target course or don't need to be able to.
if ($canaddsection || $srcmaxsectionnum <= $targetsectionnum) {
// We add the default value: Restore each course module to the section number it has in the source course.
$radioarray[] = $mform->createElement('radio', 'targetsectionnum', '',
get_string('keepsectionnum', 'block_massaction'), -1, ['class' => 'mt-2']);
}
$cankeeporiginalsectionnum = ($canaddsection || $srcmaxsectionnum <= $targetsectionnum)
&& massactionutils::can_keep_original_section_number($targetcourseid, $targetformat->get_format());
$this->set_radio_option($cankeeporiginalsectionnum, -1, $attributes);
$radioarray[] = $mform->createElement('radio', 'targetsectionnum', '',
get_string('keepsectionnum', 'block_massaction'), -1, $attributes);

$sectionsrestricted = massactionutils::get_restricted_sections($targetcourseid, $targetformat->get_format());
// Now add the sections of the target course.
foreach ($targetsections as $sectionnum => $sectionname) {
$attributes = ['class' => 'mt-2'];
if (in_array($sectionnum, $sectionsrestricted)) {
$attributes['disabled'] = 'disabled';
}
$this->set_radio_option(!in_array($sectionnum, $sectionsrestricted), $sectionnum, $attributes);
$radioarray[] = $mform->createElement('radio', 'targetsectionnum',
'', $sectionname, $sectionnum, $attributes);
}

if ($canaddsection) {
if (($targetsectionnum + 1) <= $targetformat->get_max_sections()) {
// New section option.
$radioarray[] = $mform->createElement('radio', 'targetsectionnum', '',
get_string('newsection', 'block_massaction'), $targetsectionnum + 1, ['class' => 'mt-2']);
}
}
// Whether user can add new section.
$canaddnewsection = $canaddsection && ($targetsectionnum + 1) <= $targetformat->get_max_sections();
$this->set_radio_option($canaddnewsection, $targetsectionnum + 1, $attributes);
$radioarray[] = $mform->createElement('radio', 'targetsectionnum', '',
get_string('newsection', 'block_massaction'), $targetsectionnum + 1, $attributes);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FOUND 1 ERROR AFFECTING 1 LINE
140 | ERROR | [x] Functions must not contain multiple empty lines in a row; found 2 empty lines

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the redundant empty line

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @TomoTsuyuki for your review

I have updated the patch

$mform->addGroup($radioarray, 'sections', get_string('choosesectiontoduplicateto', 'block_massaction'),
'<br/>', false);
$mform->setDefault('targetsectionnum', -1);
$mform->setDefault('targetsectionnum', reset($this->allowedsections));

$this->add_action_buttons(true, get_string('confirmsectionselect', 'block_massaction'));
}

/**
* Determine if we should disable the option.
*
* @param bool $allowcondition the condition to allow the option
* @param int $optionnumber the option number
* @param array $attributes the attributes
* @return void
*/
private function set_radio_option(bool $allowcondition, int $optionnumber, array &$attributes): void {
if (!$allowcondition) {
$attributes['disabled'] = 'disabled';
} else {
// Allow user to add new section.
unset($attributes['disabled']);
$this->allowedsections[] = "$optionnumber";
}
}

/**
* Validate the form.
*
* @param array $data the form data
* @param array $files the form files
* @return array the errors
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
if (!isset($data['targetsectionnum']) || !in_array($data['targetsectionnum'], $this->allowedsections)) {
$errors['sections'] = get_string('invalidsectionnum', 'block_massaction');
}
return $errors;
}
}
65 changes: 58 additions & 7 deletions classes/massactionutils.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,26 @@ public static function duplicate_cm_to_course(object $course, object $cm): int {
return $newcmid;
}

/**
* Run callback function from a course format plugin.
*
* @param int $courseid the target course id
* @param string $format the course format
* @param string $callbackname the name of the callback function
* @return mixed|null none determined return type
*/
private static function run_course_format_callback(int $courseid, string $format, string $callbackname) {
// Find course format which has the callback function.
$callbacks = get_plugins_with_function($callbackname);
if (!empty($callbacks['format'][$format])) {
// Run the callback function, and return result.
return $callbacks['format'][$format]($courseid);
}

// Return null if there is no callback function.
return null;
}

/**
* Get array of restricted sections from course format callback.
* Example return values from pluginname_massaction_restricted_sections: [1, 3, 5]
Expand All @@ -173,12 +193,43 @@ public static function duplicate_cm_to_course(object $course, object $cm): int {
* @param string $format
* @return array
*/
public static function get_restricted_sections($courseid, $format): array {
$sectionsrestricted = [];
$callbacks = get_plugins_with_function('massaction_restricted_sections');
if (!empty($callbacks['format'][$format])) {
$sectionsrestricted = $callbacks['format'][$format]($courseid);
}
return $sectionsrestricted;
public static function get_restricted_sections(int $courseid, string $format): array {
$sectionsrestricted = self::run_course_format_callback($courseid, $format,
'massaction_restricted_sections');

// Return empty array if there is no callback function.
return $sectionsrestricted ?? [];
}

/**
* Check if the target course format allow to add new sections.
*
* @param int $courseid the target course id
* @param string $format the course format
* @return bool default is true
*/
public static function can_add_section(int $courseid, string $format): bool {
// Check if the course format allows to add new sections.
$canaddsection = self::run_course_format_callback($courseid, $format,
'massaction_can_add_section');

// The course format take precedence over the capability.
return $canaddsection ?? has_capability('moodle/course:update', \context_course::instance($courseid));
}

/**
* Check if the target course format allow to keep original section number.
*
* @param int $courseid the target course id
* @param string $format the course format
* @return bool default is true
*/
public static function can_keep_original_section_number(int $courseid, string $format): bool {
// Check if the course format allows to keep original section number.
$cankeepsectionnumbers = self::run_course_format_callback($courseid, $format,
'massaction_can_keep_original_section_number');

// Return true if there is no callback function.
return $cankeepsectionnumbers ?? true;
}
}
1 change: 1 addition & 0 deletions lang/en/block_massaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
$string['invalidmoduleid'] = 'Invalid module ID: {$a}';
$string['invalidcoursemodule'] = 'Invalid course module';
$string['invalidcourseid'] = 'Invalid course ID';
$string['invalidsectionnum'] = 'Invalid section number';
$string['jsonerror'] = 'Error coding: Invalid JSON format';
$string['limittoenrolled'] = 'Limit target course list to courses in which the user is enrolled';
$string['limittoenrolled_description'] = 'If enabled the course selection of the feature "Duplicate to another course" will be limited to courses in which the user is enrolled. Enabling this is recommended for instances with many courses, because not limiting the courses is likely to result in performance issues and timeouts. Disabling this option is at one own\'s risk.';
Expand Down
116 changes: 116 additions & 0 deletions tests/massactionutils_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?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/>.

/**
* block_massaction phpunit test class.
*
* @package block_massaction
* @copyright 2021 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace block_massaction;

use advanced_testcase;

/**
* block_massaction phpunit test class.
*
* @package block_massaction
* @author Nathan Nguyen <nathannguyen@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class massactionutils_test extends advanced_testcase {

/**
* Private test set up method.
*
* @return array course id and format.
*/
private function get_course_and_format(): array {
$this->resetAfterTest();

// Create a course.
$course = $this->getDataGenerator()->create_course();

// Get course format.
$coursemodinfo = get_fast_modinfo($course);
$format = course_get_format($coursemodinfo->get_course())->get_format();

return [$course->id, $format];
}

/**
* Check default values of the get_restricted_sections method.
* @covers \block_massaction\massactionutils::get_restricted_sections
*
*/
public function test_get_restricted_sections() {
$this->resetAfterTest();

// Target course id and format.
[$courseid, $format] = $this->get_course_and_format();

// Get restricted sections.
$restrictedsections = massactionutils::get_restricted_sections($courseid, $format);

// Check if the restricted sections are empty.
$this->assertIsArray($restrictedsections);
$this->assertEmpty($restrictedsections);
}

/**
* Check default values of the can_add_section method.
* @covers \block_massaction\massactionutils::can_add_section
*
*/
public function test_can_add_section() {
$this->resetAfterTest();

// Create a user.
$user = $this->getDataGenerator()->create_user();
// Set the user as the current user.
$this->setUser($user);

// Target course id and format.
[$courseid, $format] = $this->get_course_and_format();

// Check if a section can be added.
$this->assertFalse(massactionutils::can_add_section($courseid, $format));

// Enrol the user as a editing teacher.
$this->getDataGenerator()->enrol_user($user->id, $courseid, 'editingteacher');

// Check if a section can be added.
$this->assertTrue(massactionutils::can_add_section($courseid, $format));
}

/**
* Check default values of the can_keep_original_section_number method.
* @covers \block_massaction\massactionutils::can_keep_original_section_number
*
*/
public function test_can_keep_original_section_number() {
$this->resetAfterTest();

// Target course id and format.
[$courseid, $format] = $this->get_course_and_format();

// Check if the original section number can be kept.
$this->assertTrue(massactionutils::can_keep_original_section_number($courseid, $format));
}
}