mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 20:42:22 +02:00
MDL-10405 course: deleting sections
This commit is contained in:
parent
88cd577ef3
commit
ca9cae84ad
@ -29,6 +29,7 @@ require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
$id = required_param('id', PARAM_INT); // course_sections.id
|
||||
$sectionreturn = optional_param('sr', 0, PARAM_INT);
|
||||
$deletesection = optional_param('delete', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_url('/course/editsection.php', array('id'=>$id, 'sr'=> $sectionreturn));
|
||||
|
||||
@ -43,6 +44,41 @@ require_capability('moodle/course:update', $context);
|
||||
// Get section_info object with all availability options.
|
||||
$sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
|
||||
|
||||
// Deleting the section.
|
||||
if ($deletesection) {
|
||||
$cancelurl = course_get_url($course, $sectioninfo, array('sr' => $sectionreturn));
|
||||
if (course_can_delete_section($course, $sectioninfo)) {
|
||||
$confirm = optional_param('confirm', false, PARAM_BOOL) && confirm_sesskey();
|
||||
if ($confirm) {
|
||||
course_delete_section($course, $sectioninfo, true);
|
||||
$courseurl = course_get_url($course, 0, array('sr' => $sectionreturn));
|
||||
redirect($courseurl);
|
||||
} else {
|
||||
if (get_string_manager()->string_exists('deletesection', 'format_' . $course->format)) {
|
||||
$strdelete = get_string('deletesection', 'format_' . $course->format);
|
||||
} else {
|
||||
$strdelete = get_string('deletesection');
|
||||
}
|
||||
$PAGE->navbar->add($strdelete);
|
||||
$PAGE->set_title($strdelete);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->box_start('noticebox');
|
||||
$optionsyes = array('id' => $id, 'confirm' => 1, 'delete' => 1, 'sesskey' => sesskey());
|
||||
$deleteurl = new moodle_url('/course/editsection.php', $optionsyes);
|
||||
$formcontinue = new single_button($deleteurl, get_string('yes'));
|
||||
$formcancel = new single_button($cancelurl, get_string('no'), 'get');
|
||||
echo $OUTPUT->confirm(get_string('confirmdeletesection', '',
|
||||
get_section_name($course, $sectioninfo)), $formcontinue, $formcancel);
|
||||
echo $OUTPUT->box_end();
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
notice(get_string('nopermissions', 'error', get_string('deletesection')), $cancelurl);
|
||||
}
|
||||
}
|
||||
|
||||
$editoroptions = array('context'=>$context ,'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
|
||||
$mform = course_get_format($course->id)->editsection_form($PAGE->url,
|
||||
array('cs' => $sectioninfo, 'editoroptions' => $editoroptions));
|
||||
|
@ -935,6 +935,85 @@ abstract class format_base {
|
||||
*/
|
||||
public function section_get_available_hook(section_info $section, &$available, &$availableinfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this format allows to delete sections
|
||||
*
|
||||
* If format supports deleting sections it is also recommended to define language string
|
||||
* 'deletesection' inside the format.
|
||||
*
|
||||
* Do not call this function directly, instead use {@link course_can_delete_section()}
|
||||
*
|
||||
* @param int|stdClass|section_info $section
|
||||
* @return bool
|
||||
*/
|
||||
public function can_delete_section($section) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a section
|
||||
*
|
||||
* Do not call this function directly, instead call {@link course_delete_section()}
|
||||
*
|
||||
* @param int|stdClass|section_info $section
|
||||
* @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it.
|
||||
* @return bool whether section was deleted
|
||||
*/
|
||||
public function delete_section($section, $forcedeleteifnotempty = false) {
|
||||
global $DB;
|
||||
if (!$this->uses_sections()) {
|
||||
// Not possible to delete section if sections are not used.
|
||||
return false;
|
||||
}
|
||||
if (!is_object($section)) {
|
||||
$section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section),
|
||||
'id,section,sequence');
|
||||
}
|
||||
if (!$section || !$section->section) {
|
||||
// Not possible to delete 0-section.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$forcedeleteifnotempty && !empty($section->sequence)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$course = $this->get_course();
|
||||
|
||||
// Remove the marker if it points to this section.
|
||||
if ($section->section == $course->marker) {
|
||||
course_set_marker($course->id, 0);
|
||||
}
|
||||
|
||||
$lastsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
|
||||
WHERE course = ?', array($course->id));
|
||||
|
||||
// Find out if we need to descrease the 'numsections' property later.
|
||||
$courseformathasnumsections = array_key_exists('numsections',
|
||||
$this->get_format_options());
|
||||
$decreasenumsections = $courseformathasnumsections && ($section->section <= $course->numsections);
|
||||
|
||||
// Move the section to the end.
|
||||
move_section_to($course, $section->section, $lastsection, true);
|
||||
|
||||
// Delete all modules from the section.
|
||||
foreach (preg_split('/,/', $section->sequence, -1, PREG_SPLIT_NO_EMPTY) as $cmid) {
|
||||
course_delete_module($cmid);
|
||||
}
|
||||
|
||||
// Delete section and it's format options.
|
||||
$DB->delete_records('course_format_options', array('sectionid' => $section->id));
|
||||
$DB->delete_records('course_sections', array('id' => $section->id));
|
||||
rebuild_course_cache($course->id, true);
|
||||
|
||||
// Descrease 'numsections' if needed.
|
||||
if ($decreasenumsections) {
|
||||
$this->update_course_format_options(array('numsections' => $course->numsections - 1));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -226,6 +226,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$isstealth = isset($course->numsections) && ($section->section > $course->numsections);
|
||||
|
||||
if ($onsectionpage) {
|
||||
$baseurl = course_get_url($course, $section->section);
|
||||
@ -237,7 +238,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
$controls = array();
|
||||
|
||||
$url = clone($baseurl);
|
||||
if (has_capability('moodle/course:sectionvisibility', $coursecontext)) {
|
||||
if (!$isstealth && has_capability('moodle/course:sectionvisibility', $coursecontext)) {
|
||||
if ($section->visible) { // Show the hide/show eye.
|
||||
$strhidefromothers = get_string('hidefromothers', 'format_'.$course->format);
|
||||
$url->param('hide', $section->section);
|
||||
@ -255,7 +256,21 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
}
|
||||
}
|
||||
|
||||
if (!$onsectionpage && has_capability('moodle/course:movesections', $coursecontext)) {
|
||||
if (course_can_delete_section($course, $section)) {
|
||||
if (get_string_manager()->string_exists('deletesection', 'format_'.$course->format)) {
|
||||
$strdelete = get_string('deletesection', 'format_'.$course->format);
|
||||
} else {
|
||||
$strdelete = get_string('deletesection');
|
||||
}
|
||||
$url = new moodle_url('/course/editsection.php', array('id' => $section->id,
|
||||
'sr' => $onsectionpage ? $section->section : 0, 'delete' => 1));
|
||||
$controls[] = html_writer::link($url,
|
||||
html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/delete'),
|
||||
'class' => 'iconsmall edit', 'alt' => $strdelete)),
|
||||
array('title' => $strdelete));
|
||||
}
|
||||
|
||||
if (!$isstealth && !$onsectionpage && has_capability('moodle/course:movesections', $coursecontext)) {
|
||||
$url = clone($baseurl);
|
||||
if ($section->section > 1) { // Add a arrow to move section up.
|
||||
$url->param('section', $section->section);
|
||||
@ -530,7 +545,10 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
$o = '';
|
||||
$o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix orphaned hidden'));
|
||||
$o.= html_writer::tag('div', '', array('class' => 'left side'));
|
||||
$o.= html_writer::tag('div', '', array('class' => 'right side'));
|
||||
$course = course_get_format($this->page->course)->get_course();
|
||||
$section = course_get_format($this->page->course)->get_section($sectionno);
|
||||
$rightcontent = $this->section_right_content($section, $course, false);
|
||||
$o.= html_writer::tag('div', $rightcontent, array('class' => 'right side'));
|
||||
$o.= html_writer::start_tag('div', array('class' => 'content'));
|
||||
$o.= $this->output->heading(get_string('orphanedactivitiesinsectionno', '', $sectionno), 3, 'sectionname');
|
||||
return $o;
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
$string['currentsection'] = 'This topic';
|
||||
$string['deletesection'] = 'Delete topic';
|
||||
$string['sectionname'] = 'Topic';
|
||||
$string['pluginname'] = 'Topics format';
|
||||
$string['section0name'] = 'General';
|
||||
|
@ -339,4 +339,16 @@ class format_topics extends format_base {
|
||||
}
|
||||
return $this->update_format_options($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this format allows to delete sections
|
||||
*
|
||||
* Do not call this function directly, instead use {@link course_can_delete_section()}
|
||||
*
|
||||
* @param int|stdClass|section_info $section
|
||||
* @return bool
|
||||
*/
|
||||
public function can_delete_section($section) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -97,8 +97,9 @@ class format_topics_renderer extends format_section_renderer_base {
|
||||
}
|
||||
$url->param('sesskey', sesskey());
|
||||
|
||||
$isstealth = $section->section > $course->numsections;
|
||||
$controls = array();
|
||||
if (has_capability('moodle/course:setcurrentsection', $coursecontext)) {
|
||||
if (!$isstealth && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
|
||||
if ($course->marker == $section->section) { // Show the "light globe" on/off.
|
||||
$url->param('marker', 0);
|
||||
$controls[] = html_writer::link($url,
|
||||
|
@ -0,0 +1,85 @@
|
||||
@format @format_topics
|
||||
Feature: Sections can be edited and deleted in topics format
|
||||
In order to rearrange my course contents
|
||||
As a teacher
|
||||
I need to edit and Delete topics
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format | coursedisplay | numsections |
|
||||
| Course 1 | C1 | topics | 0 | 5 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Test assignment name | Test assignment description | C1 | assign1 | 0 |
|
||||
| book | Test book name | Test book description | C1 | book1 | 1 |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 4 |
|
||||
| choice | Test choice name | Test choice description | C1 | choice1 | 5 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
|
||||
Scenario: Edit section summary in topics format
|
||||
When I click on "Edit summary" "link" in the "li#section-2" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Summary | Welcome to section 2 |
|
||||
And I press "Save changes"
|
||||
Then I should see "Welcome to section 2" in the "li#section-2" "css_element"
|
||||
|
||||
Scenario: Edit section default name in topics format
|
||||
When I click on "Edit summary" "link" in the "li#section-2" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Use default section name | 0 |
|
||||
| name | This is the second topic |
|
||||
And I press "Save changes"
|
||||
Then I should see "This is the second topic" in the "li#section-2" "css_element"
|
||||
And I should not see "Topic 2" in the "li#section-2" "css_element"
|
||||
|
||||
Scenario: Deleting the last section in topics format
|
||||
When I click on "Delete topic" "link" in the "li#section-5" "css_element"
|
||||
Then I should see "Are you absolutely sure you want to delete \"Topic 5\"? All activities will be also deleted"
|
||||
And I press "Yes"
|
||||
And I should not see "Topic 5"
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting the middle section in topics format
|
||||
When I click on "Delete topic" "link" in the "li#section-4" "css_element"
|
||||
And I press "Yes"
|
||||
Then I should not see "Topic 5"
|
||||
And I should not see "Test chat name"
|
||||
And I should see "Test choice name" in the "li#section-4" "css_element"
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting the orphaned section in topics format
|
||||
When I follow "Reduce the number of sections"
|
||||
Then I should see "Orphaned activities (section 5)" in the "li#section-5" "css_element"
|
||||
And I click on "Delete topic" "link" in the "li#section-5" "css_element"
|
||||
And I press "Yes"
|
||||
And I should not see "Topic 5"
|
||||
And I should not see "Orphaned activities"
|
||||
And "li#section-5" "css_element" should not exist
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting a section when orphaned section is present in topics format
|
||||
When I follow "Reduce the number of sections"
|
||||
Then I should see "Orphaned activities (section 5)" in the "li#section-5" "css_element"
|
||||
And "li#section-5.orphaned" "css_element" should exist
|
||||
And "li#section-4.orphaned" "css_element" should not exist
|
||||
And I click on "Delete topic" "link" in the "li#section-1" "css_element"
|
||||
And I press "Yes"
|
||||
And I should not see "Test book name"
|
||||
And I should see "Orphaned activities (section 4)" in the "li#section-4" "css_element"
|
||||
And "li#section-5" "css_element" should not exist
|
||||
And "li#section-4.orphaned" "css_element" should exist
|
||||
And "li#section-3.orphaned" "css_element" should not exist
|
@ -2,6 +2,11 @@ This files describes API changes for course formats
|
||||
|
||||
Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
|
||||
|
||||
=== 2.9 ===
|
||||
* Course formats may support deleting sections, see MDL-10405 for more details.
|
||||
format_section_renderer_base::section_edit_controls() is now also called for
|
||||
stealth sections and it also returns "delete" control.
|
||||
|
||||
=== 2.8 ===
|
||||
* The activity chooser now uses M.course.format.get_sectionwrapperclass()
|
||||
to determine the section selector, rather than a hard-coded `li.section`.
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
$string['currentsection'] = 'This week';
|
||||
$string['deletesection'] = 'Delete week';
|
||||
$string['sectionname'] = 'Week';
|
||||
$string['pluginname'] = 'Weekly format';
|
||||
$string['section0name'] = 'General';
|
||||
|
@ -393,4 +393,16 @@ class format_weeks extends format_base {
|
||||
$dates = $this->get_section_dates($section);
|
||||
return (($timenow >= $dates->start) && ($timenow < $dates->end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this format allows to delete sections
|
||||
*
|
||||
* Do not call this function directly, instead use {@link course_can_delete_section()}
|
||||
*
|
||||
* @param int|stdClass|section_info $section
|
||||
* @return bool
|
||||
*/
|
||||
public function can_delete_section($section) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
88
course/format/weeks/tests/behat/edit_delete_sections.feature
Normal file
88
course/format/weeks/tests/behat/edit_delete_sections.feature
Normal file
@ -0,0 +1,88 @@
|
||||
@format @format_weeks
|
||||
Feature: Sections can be edited and deleted in weeks format
|
||||
In order to rearrange my course contents
|
||||
As a teacher
|
||||
I need to edit and Delete weeks
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format | coursedisplay | numsections | startdate |
|
||||
| Course 1 | C1 | weeks | 0 | 5 | 957139200 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Test assignment name | Test assignment description | C1 | assign1 | 0 |
|
||||
| book | Test book name | Test book description | C1 | book1 | 1 |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 4 |
|
||||
| choice | Test choice name | Test choice description | C1 | choice1 | 5 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
|
||||
Scenario: Edit section summary in weeks format
|
||||
When I click on "Edit summary" "link" in the "li#section-2" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Summary | Welcome to section 2 |
|
||||
And I press "Save changes"
|
||||
Then I should see "Welcome to section 2" in the "li#section-2" "css_element"
|
||||
|
||||
Scenario: Edit section default name in weeks format
|
||||
Given I should see "8 May - 14 May" in the "li#section-2" "css_element"
|
||||
When I click on "Edit summary" "link" in the "li#section-2" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Use default section name | 0 |
|
||||
| name | This is the second week |
|
||||
And I press "Save changes"
|
||||
Then I should see "This is the second week" in the "li#section-2" "css_element"
|
||||
And I should not see "8 May - 14 May" in the "li#section-2" "css_element"
|
||||
|
||||
Scenario: Deleting the last section in weeks format
|
||||
Given I should see "29 May - 4 June" in the "li#section-5" "css_element"
|
||||
When I click on "Delete week" "link" in the "li#section-5" "css_element"
|
||||
Then I should see "Are you absolutely sure you want to delete \"29 May - 4 June\"? All activities will be also deleted"
|
||||
And I press "Yes"
|
||||
And I should not see "29 May - 4 June"
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting the middle section in weeks format
|
||||
Given I should see "29 May - 4 June" in the "li#section-5" "css_element"
|
||||
When I click on "Delete week" "link" in the "li#section-4" "css_element"
|
||||
And I press "Yes"
|
||||
Then I should not see "29 May - 4 June"
|
||||
And I should not see "Test chat name"
|
||||
And I should see "Test choice name" in the "li#section-4" "css_element"
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting the orphaned section in weeks format
|
||||
When I follow "Reduce the number of sections"
|
||||
Then I should see "Orphaned activities (section 5)" in the "li#section-5" "css_element"
|
||||
And I click on "Delete week" "link" in the "li#section-5" "css_element"
|
||||
And I press "Yes"
|
||||
And I should not see "29 May - 4 June"
|
||||
And I should not see "Orphaned activities"
|
||||
And "li#section-5" "css_element" should not exist
|
||||
And I navigate to "Edit settings" node in "Course administration"
|
||||
And I expand all fieldsets
|
||||
And the field "Number of sections" matches value "4"
|
||||
|
||||
Scenario: Deleting a section when orphaned section is present in weeks format
|
||||
When I follow "Reduce the number of sections"
|
||||
Then I should see "Orphaned activities (section 5)" in the "li#section-5" "css_element"
|
||||
And "li#section-5.orphaned" "css_element" should exist
|
||||
And "li#section-4.orphaned" "css_element" should not exist
|
||||
And I click on "Delete week" "link" in the "li#section-1" "css_element"
|
||||
And I press "Yes"
|
||||
And I should not see "Test book name"
|
||||
And I should see "Orphaned activities (section 4)" in the "li#section-4" "css_element"
|
||||
And "li#section-5" "css_element" should not exist
|
||||
And "li#section-4.orphaned" "css_element" should exist
|
||||
And "li#section-3.orphaned" "css_element" should not exist
|
@ -1771,9 +1771,10 @@ function delete_mod_from_section($modid, $sectionid) {
|
||||
* @param object $course
|
||||
* @param int $section Section number (not id!!!)
|
||||
* @param int $destination
|
||||
* @param bool $ignorenumsections
|
||||
* @return boolean Result
|
||||
*/
|
||||
function move_section_to($course, $section, $destination) {
|
||||
function move_section_to($course, $section, $destination, $ignorenumsections = false) {
|
||||
/// Moves a whole course section up and down within the course
|
||||
global $USER, $DB;
|
||||
|
||||
@ -1783,7 +1784,7 @@ function move_section_to($course, $section, $destination) {
|
||||
|
||||
// compartibility with course formats using field 'numsections'
|
||||
$courseformatoptions = course_get_format($course)->get_format_options();
|
||||
if ((array_key_exists('numsections', $courseformatoptions) &&
|
||||
if ((!$ignorenumsections && array_key_exists('numsections', $courseformatoptions) &&
|
||||
($destination > $courseformatoptions['numsections'])) || ($destination < 1)) {
|
||||
return false;
|
||||
}
|
||||
@ -1825,6 +1826,57 @@ function move_section_to($course, $section, $destination) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete a course section and may delete all modules inside it.
|
||||
*
|
||||
* No permissions are checked here, use {@link course_can_delete_section()} to
|
||||
* check if section can actually be deleted.
|
||||
*
|
||||
* @param int|stdClass $course
|
||||
* @param int|stdClass|section_info $section
|
||||
* @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it.
|
||||
* @return bool whether section was deleted
|
||||
*/
|
||||
function course_delete_section($course, $section, $forcedeleteifnotempty = true) {
|
||||
return course_get_format($course)->delete_section($section, $forcedeleteifnotempty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user can delete a section (if course format allows it and user has proper permissions).
|
||||
*
|
||||
* @param int|stdClass $course
|
||||
* @param int|stdClass|section_info $section
|
||||
* @return bool
|
||||
*/
|
||||
function course_can_delete_section($course, $section) {
|
||||
if (is_object($section)) {
|
||||
$section = $section->section;
|
||||
}
|
||||
if (!$section) {
|
||||
// Not possible to delete 0-section.
|
||||
return false;
|
||||
}
|
||||
// Course format should allow to delete sections.
|
||||
if (!course_get_format($course)->can_delete_section($section)) {
|
||||
return false;
|
||||
}
|
||||
// Make sure user has capability to update course and move sections.
|
||||
$context = context_course::instance(is_object($course) ? $course->id : $course);
|
||||
if (!has_all_capabilities(array('moodle/course:movesections', 'moodle/course:update'), $context)) {
|
||||
return false;
|
||||
}
|
||||
// Make sure user has capability to delete each activity in this section.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
if (!empty($modinfo->sections[$section])) {
|
||||
foreach ($modinfo->sections[$section] as $cmid) {
|
||||
if (!has_capability('moodle/course:manageactivities', context_module::instance($cmid))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
|
||||
* an original position number and a target position number, rebuilds the array so that the
|
||||
|
@ -859,6 +859,130 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEquals(3, $course->marker);
|
||||
}
|
||||
|
||||
public function test_course_can_delete_section() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$courseweeks = $generator->create_course(
|
||||
array('numsections' => 5, 'format' => 'weeks'),
|
||||
array('createsections' => true));
|
||||
$assign1 = $generator->create_module('assign', array('course' => $courseweeks, 'section' => 1));
|
||||
$assign2 = $generator->create_module('assign', array('course' => $courseweeks, 'section' => 2));
|
||||
|
||||
$coursetopics = $generator->create_course(
|
||||
array('numsections' => 5, 'format' => 'topics'),
|
||||
array('createsections' => true));
|
||||
|
||||
$coursesingleactivity = $generator->create_course(
|
||||
array('format' => 'singleactivity'),
|
||||
array('createsections' => true));
|
||||
|
||||
// Enrol student and teacher.
|
||||
$roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
|
||||
$student = $generator->create_user();
|
||||
$teacher = $generator->create_user();
|
||||
|
||||
$generator->enrol_user($student->id, $courseweeks->id, $roleids['student']);
|
||||
$generator->enrol_user($teacher->id, $courseweeks->id, $roleids['editingteacher']);
|
||||
|
||||
$generator->enrol_user($student->id, $coursetopics->id, $roleids['student']);
|
||||
$generator->enrol_user($teacher->id, $coursetopics->id, $roleids['editingteacher']);
|
||||
|
||||
$generator->enrol_user($student->id, $coursesingleactivity->id, $roleids['student']);
|
||||
$generator->enrol_user($teacher->id, $coursesingleactivity->id, $roleids['editingteacher']);
|
||||
|
||||
// Teacher should be able to delete sections (except for 0) in topics and weeks format.
|
||||
$this->setUser($teacher);
|
||||
|
||||
// For topics and weeks formats will return false for section 0 and true for any other section.
|
||||
$this->assertFalse(course_can_delete_section($courseweeks, 0));
|
||||
$this->assertTrue(course_can_delete_section($courseweeks, 1));
|
||||
|
||||
$this->assertFalse(course_can_delete_section($coursetopics, 0));
|
||||
$this->assertTrue(course_can_delete_section($coursetopics, 1));
|
||||
|
||||
// For singleactivity course format no section can be deleted.
|
||||
$this->assertFalse(course_can_delete_section($coursesingleactivity, 0));
|
||||
$this->assertFalse(course_can_delete_section($coursesingleactivity, 1));
|
||||
|
||||
// Now let's revoke a capability from teacher to manage activity in section 1.
|
||||
$modulecontext = context_module::instance($assign1->cmid);
|
||||
assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleids['editingteacher'],
|
||||
$modulecontext);
|
||||
$modulecontext->mark_dirty();
|
||||
$this->assertFalse(course_can_delete_section($courseweeks, 1));
|
||||
$this->assertTrue(course_can_delete_section($courseweeks, 2));
|
||||
|
||||
// Student does not have permissions to delete sections.
|
||||
$this->setUser($student);
|
||||
$this->assertFalse(course_can_delete_section($courseweeks, 1));
|
||||
$this->assertFalse(course_can_delete_section($coursetopics, 1));
|
||||
$this->assertFalse(course_can_delete_section($coursesingleactivity, 1));
|
||||
}
|
||||
|
||||
public function test_course_delete_section() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(array('numsections' => 6, 'format' => 'topics'),
|
||||
array('createsections' => true));
|
||||
$assign0 = $generator->create_module('assign', array('course' => $course, 'section' => 0));
|
||||
$assign1 = $generator->create_module('assign', array('course' => $course, 'section' => 1));
|
||||
$assign21 = $generator->create_module('assign', array('course' => $course, 'section' => 2));
|
||||
$assign22 = $generator->create_module('assign', array('course' => $course, 'section' => 2));
|
||||
$assign3 = $generator->create_module('assign', array('course' => $course, 'section' => 3));
|
||||
$assign5 = $generator->create_module('assign', array('course' => $course, 'section' => 5));
|
||||
$assign6 = $generator->create_module('assign', array('course' => $course, 'section' => 6));
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
// Attempt to delete non-existing section.
|
||||
$this->assertFalse(course_delete_section($course, 10, false));
|
||||
$this->assertFalse(course_delete_section($course, 9, true));
|
||||
|
||||
// Attempt to delete 0-section.
|
||||
$this->assertFalse(course_delete_section($course, 0, true));
|
||||
$this->assertTrue($DB->record_exists('course_modules', array('id' => $assign0->cmid)));
|
||||
|
||||
// Delete last section.
|
||||
$this->assertTrue(course_delete_section($course, 6, true));
|
||||
$this->assertFalse($DB->record_exists('course_modules', array('id' => $assign6->cmid)));
|
||||
$this->assertEquals(5, course_get_format($course)->get_course()->numsections);
|
||||
|
||||
// Delete empty section.
|
||||
$this->assertTrue(course_delete_section($course, 4, false));
|
||||
$this->assertEquals(4, course_get_format($course)->get_course()->numsections);
|
||||
|
||||
// Delete section in the middle (2).
|
||||
$this->assertFalse(course_delete_section($course, 2, false));
|
||||
$this->assertTrue(course_delete_section($course, 2, true));
|
||||
$this->assertFalse($DB->record_exists('course_modules', array('id' => $assign21->cmid)));
|
||||
$this->assertFalse($DB->record_exists('course_modules', array('id' => $assign22->cmid)));
|
||||
$this->assertEquals(3, course_get_format($course)->get_course()->numsections);
|
||||
$this->assertEquals(array(0 => array($assign0->cmid),
|
||||
1 => array($assign1->cmid),
|
||||
2 => array($assign3->cmid),
|
||||
3 => array($assign5->cmid)), get_fast_modinfo($course)->sections);
|
||||
|
||||
// Make last section orphaned.
|
||||
update_course((object)array('id' => $course->id, 'numsections' => 2));
|
||||
$this->assertEquals(2, course_get_format($course)->get_course()->numsections);
|
||||
|
||||
// Remove orphaned section.
|
||||
$this->assertTrue(course_delete_section($course, 3, true));
|
||||
$this->assertEquals(2, course_get_format($course)->get_course()->numsections);
|
||||
|
||||
// Remove marked section.
|
||||
course_set_marker($course->id, 1);
|
||||
$this->assertTrue(course_get_format($course)->is_section_current(1));
|
||||
$this->assertTrue(course_delete_section($course, 1, true));
|
||||
$this->assertFalse(course_get_format($course)->is_section_current(1));
|
||||
}
|
||||
|
||||
public function test_get_course_display_name_for_list() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest(true);
|
||||
|
@ -270,6 +270,7 @@ $string['complete'] = 'Complete';
|
||||
$string['completereport'] = 'Complete report';
|
||||
$string['configuration'] = 'Configuration';
|
||||
$string['confirm'] = 'Confirm';
|
||||
$string['confirmdeletesection'] = 'Are you absolutely sure you want to delete "{$a}"? All activities will be also deleted';
|
||||
$string['confirmed'] = 'Your registration has been confirmed';
|
||||
$string['confirmednot'] = 'Your registration has not yet been confirmed!';
|
||||
$string['confirmcheckfull'] = 'Are you absolutely sure you want to confirm {$a} ?';
|
||||
@ -480,6 +481,7 @@ $string['deletechecktypename'] = 'Are you sure that you want to delete the {$a->
|
||||
$string['deletecheckfiles'] = 'Are you absolutely sure you want to delete these files?';
|
||||
$string['deletecheckfull'] = 'Are you absolutely sure you want to completely delete {$a} ?';
|
||||
$string['deletecheckwarning'] = 'You are about to delete these files';
|
||||
$string['deletesection'] = 'Delete section';
|
||||
$string['deleteselected'] = 'Delete selected';
|
||||
$string['deleteselectedkey'] = 'Delete selected key';
|
||||
$string['deletingcourse'] = 'Deleting {$a}';
|
||||
|
Loading…
x
Reference in New Issue
Block a user