Merge branch 'wip-MDL-57769-master' of https://github.com/marinaglancy/moodle

This commit is contained in:
Eloy Lafuente (stronk7) 2017-04-05 01:31:08 +02:00
commit aa2b5b214d
43 changed files with 1365 additions and 388 deletions

View file

@ -265,6 +265,28 @@ abstract class format_base {
return $this->course;
}
/**
* Method used in the rendered and during backup instead of legacy 'numsections'
*
* Default renderer will treat sections with sectionnumber greater that the value returned by this
* method as "orphaned" and not display them on the course page unless in editing mode.
* Backup will store this value as 'numsections'.
*
* This method ensures that 3rd party course format plugins that still use 'numsections' continue to
* work but at the same time we no longer expect formats to have 'numsections' property.
*
* @return int
*/
public function get_last_section_number() {
$course = $this->get_course();
if (isset($course->numsections)) {
return $course->numsections;
}
$modinfo = get_fast_modinfo($course);
$sections = $modinfo->get_section_info_all();
return (int)max(array_keys($sections));
}
/**
* Returns true if the course has a front page.
*

View file

@ -303,7 +303,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
$sectionreturn = $onsectionpage ? $section->section : null;
$coursecontext = context_course::instance($course->id);
$isstealth = isset($course->numsections) && ($section->section > $course->numsections);
$numsections = course_get_format($course)->get_last_section_number();
$isstealth = $section->section > $numsections;
$baseurl = course_get_url($course, $sectionreturn);
$baseurl->param('sesskey', sesskey());
@ -369,7 +370,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
}
$url = clone($baseurl);
if ($section->section < $course->numsections) { // Add a arrow to move section down.
if ($section->section < $numsections) { // Add a arrow to move section down.
$url->param('section', $section->section);
$url->param('move', 1);
$strmovedown = get_string('movedown');
@ -643,7 +644,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
}
$forward = $sectionno + 1;
while ($forward <= $course->numsections and empty($links['next'])) {
$numsections = course_get_format($course)->get_last_section_number();
while ($forward <= $numsections and empty($links['next'])) {
if ($canviewhidden || $sections[$forward]->uservisible) {
$params = array();
if (!$sections[$forward]->visible) {
@ -731,7 +733,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
$sectionmenu[course_get_url($course)->out(false)] = get_string('maincoursepage');
$modinfo = get_fast_modinfo($course);
$section = 1;
while ($section <= $course->numsections) {
$numsections = course_get_format($course)->get_last_section_number();
while ($section <= $numsections) {
$thissection = $modinfo->get_section_info($section);
$showsection = $thissection->uservisible or !$course->hiddensections;
if (($showsection) && ($section != $displaysection) && ($url = course_get_url($course, $section))) {
@ -869,6 +872,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
// Now the list of sections..
echo $this->start_section_list();
$numsections = course_get_format($course)->get_last_section_number();
foreach ($modinfo->get_section_info_all() as $section => $thissection) {
if ($section == 0) {
@ -881,7 +885,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
}
continue;
}
if ($section > $course->numsections) {
if ($section > $numsections) {
// activities inside this section are 'orphaned', this section will be printed as 'stealth' below
continue;
}
@ -917,7 +921,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) {
// Print stealth sections if present.
foreach ($modinfo->get_section_info_all() as $section => $thissection) {
if ($section <= $course->numsections or empty($modinfo->sections[$section])) {
if ($section <= $numsections or empty($modinfo->sections[$section])) {
// this is not stealth section or it is empty
continue;
}
@ -928,6 +932,34 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
echo $this->end_section_list();
echo $this->change_number_sections($course, 0);
} else {
echo $this->end_section_list();
}
}
/**
* Returns controls in the bottom of the page to increase/decrease number of sections
*
* @param stdClass $course
* @param int|null $sectionreturn
* @return string
*/
protected function change_number_sections($course, $sectionreturn = null) {
$coursecontext = context_course::instance($course->id);
if (!has_capability('moodle/course:update', $coursecontext)) {
return '';
}
$options = course_get_format($course)->get_format_options();
$supportsnumsections = array_key_exists('numsections', $options);
if ($supportsnumsections) {
// Current course format has 'numsections' option, which is very confusing and we suggest course format
// developers to get rid of it (see MDL-57769 on how to do it).
// Display "Increase section" / "Decrease section" links.
echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
// Increase number of sections.
@ -951,10 +983,28 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
}
echo html_writer::end_tag('div');
} else {
echo $this->end_section_list();
}
} else if (course_get_format($course)->uses_sections()) {
// Current course format does not have 'numsections' option but it has multiple sections suppport.
// Display the "Add section" link that will insert a section in the end.
// Note to course format developers: inserting sections in the other positions should check both
// capabilities 'moodle/course:update' and 'moodle/course:movesections'.
echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
if (get_string_manager()->string_exists('addsection', 'format_'.$course->format)) {
$straddsection = get_string('addsection', 'format_'.$course->format);
} else {
$straddsection = get_string('addsection');
}
$url = new moodle_url('/course/changenumsections.php',
['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()]);
if ($sectionreturn !== null) {
$url->param('sectionreturn', $sectionreturn);
}
$icon = $this->output->pix_icon('t/add', $straddsection);
echo html_writer::link($url, $icon . $straddsection, array('class' => 'add-section'));
echo html_writer::end_tag('div');
}
}
/**

View file

@ -0,0 +1,121 @@
<?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/>.
/**
* Specialised restore for format_topics
*
* @package format_topics
* @category backup
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Specialised restore for format_topics
*
* Processes 'numsections' from the old backup files and hides sections that used to be "orphaned"
*
* @package format_topics
* @category backup
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_format_topics_plugin extends restore_format_plugin {
/** @var int */
protected $originalnumsections = 0;
/**
* Checks if backup file was made on Moodle before 3.3 and we should respect the 'numsections'
* and potential "orphaned" sections in the end of the course.
*
* @return bool
*/
protected function need_restore_numsections() {
$backupinfo = $this->step->get_task()->get_info();
$backuprelease = $backupinfo->backup_release;
return version_compare($backuprelease, '3.3', 'lt');
}
/**
* Creates a dummy path element in order to be able to execute code after restore
*
* @return restore_path_element[]
*/
public function define_course_plugin_structure() {
global $DB;
// Since this method is executed before the restore we can do some pre-checks here.
// In case of merging backup into existing course find the current number of sections.
$target = $this->step->get_task()->get_target();
if (($target == backup::TARGET_CURRENT_ADDING || $target == backup::TARGET_EXISTING_ADDING) &&
$this->need_restore_numsections()) {
$maxsection = $DB->get_field_sql(
'SELECT max(section) FROM {course_sections} WHERE course = ?',
[$this->step->get_task()->get_courseid()]);
$this->originalnumsections = (int)$maxsection;
}
// Dummy path element is needed in order for after_restore_course() to be called.
return [new restore_path_element('dummy_course', $this->get_pathfor('/dummycourse'))];
}
/**
* Dummy process method
*/
public function process_dummy_course() {
}
/**
* Executed after course restore is complete
*
* This method is only executed if course configuration was overridden
*/
public function after_restore_course() {
global $DB;
if (!$this->need_restore_numsections()) {
// Backup file was made in Moodle 3.3 or later, we don't need to process 'numsecitons'.
return;
}
$data = $this->connectionpoint->get_data();
$backupinfo = $this->step->get_task()->get_info();
if ($backupinfo->original_course_format !== 'topics' || !isset($data['tags']['numsections'])) {
// Backup from another course format or backup file does not even have 'numsections'.
return;
}
$numsections = (int)$data['tags']['numsections'];
foreach ($backupinfo->sections as $key => $section) {
// For each section from the backup file check if it was restored and if was "orphaned" in the original
// course and mark it as hidden. This will leave all activities in it visible and available just as it was
// in the original course.
// Exception is when we restore with merging and the course already had a section with this section number,
// in this case we don't modify the visibility.
if ($this->step->get_task()->get_setting_value($key . '_included')) {
$sectionnum = (int)$section->title;
if ($sectionnum > $numsections && $sectionnum > $this->originalnumsections) {
$DB->execute("UPDATE {course_sections} SET visible = 0 WHERE course = ? AND section = ?",
[$this->step->get_task()->get_courseid(), $sectionnum]);
}
}
}
}
}

View file

@ -0,0 +1,47 @@
<?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/>.
/**
* Upgrade scripts for course format "Topics"
*
* @package format_topics
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Upgrade script for format_topics
*
* @param int $oldversion the version we are upgrading from
* @return bool result
*/
function xmldb_format_topics_upgrade($oldversion) {
global $CFG, $DB;
require_once($CFG->dirroot . '/course/format/topics/db/upgradelib.php');
if ($oldversion < 2017020200) {
// Remove 'numsections' option and hide or delete orphaned sections.
format_topics_upgrade_remove_numsections();
upgrade_plugin_savepoint(true, 2017020200, 'format', 'topics');
}
return true;
}

View file

@ -0,0 +1,117 @@
<?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/>.
/**
* Upgrade scripts for course format "Topics"
*
* @package format_topics
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This method finds all courses in 'topics' format that have actual number of sections
* bigger than their 'numsections' course format option.
* For each such course we call {@link format_topics_upgrade_hide_extra_sections()} and
* either delete or hide "orphaned" sections.
*/
function format_topics_upgrade_remove_numsections() {
global $DB;
$sql1 = "SELECT c.id, max(cs.section) AS sectionsactual
FROM {course} c
JOIN {course_sections} cs ON cs.course = c.id
WHERE c.format = :format1
GROUP BY c.id";
$sql2 = "SELECT c.id, n.value AS numsections
FROM {course} c
JOIN {course_format_options} n ON n.courseid = c.id AND n.format = :format1 AND n.name = :numsections AND n.sectionid = 0
WHERE c.format = :format2";
$params = ['format1' => 'topics', 'format2' => 'topics', 'numsections' => 'numsections'];
$actual = $DB->get_records_sql_menu($sql1, $params);
$numsections = $DB->get_records_sql_menu($sql2, $params);
$needfixing = [];
$defaultnumsections = get_config('moodlecourse', 'numsections');
foreach ($actual as $courseid => $sectionsactual) {
if (array_key_exists($courseid, $numsections)) {
$n = (int)$numsections[$courseid];
} else {
$n = $defaultnumsections;
}
if ($sectionsactual > $n) {
$needfixing[$courseid] = $n;
}
}
unset($actual);
unset($numsections);
foreach ($needfixing as $courseid => $numsections) {
format_topics_upgrade_hide_extra_sections($courseid, $numsections);
}
$DB->delete_records('course_format_options', ['format' => 'topics', 'sectionid' => 0, 'name' => 'numsections']);
}
/**
* Find all sections in the course with sectionnum bigger than numsections.
* Either delete these sections or hide them
*
* We will only delete a section if it is completely empty and all sections below
* it are also empty
*
* @param int $courseid
* @param int $numsections
*/
function format_topics_upgrade_hide_extra_sections($courseid, $numsections) {
global $DB;
$sections = $DB->get_records_sql('SELECT id, name, summary, sequence, visible
FROM {course_sections}
WHERE course = ? AND section > ?
ORDER BY section DESC', [$courseid, $numsections]);
$candelete = true;
$tohide = [];
$todelete = [];
foreach ($sections as $section) {
if ($candelete && (!empty($section->summary) || !empty($section->sequence) || !empty($section->name))) {
$candelete = false;
}
if ($candelete) {
$todelete[] = $section->id;
} else if ($section->visible) {
$tohide[] = $section->id;
}
}
if ($todelete) {
// Delete empty sections in the end.
// This is an upgrade script - no events or cache resets are needed.
// We also know that these sections do not have any modules so it is safe to just delete records in the table.
$DB->delete_records_list('course_sections', 'id', $todelete);
}
if ($tohide) {
// Hide other orphaned sections.
// This is different from what set_section_visible() does but we want to preserve actual
// module visibility in this case.
list($sql, $params) = $DB->get_in_or_equal($tohide);
$DB->execute("UPDATE {course_sections} SET visible = 0 WHERE id " . $sql, $params);
}
}

View file

@ -38,15 +38,16 @@ if ($topic = optional_param('topic', 0, PARAM_INT)) {
// End backwards-compatible aliasing..
$context = context_course::instance($course->id);
// Retrieve course format option fields and add them to the $course object.
$course = course_get_format($course)->get_course();
if (($marker >=0) && has_capability('moodle/course:setcurrentsection', $context) && confirm_sesskey()) {
$course->marker = $marker;
course_set_marker($course->id, $marker);
}
// make sure all sections are created
$course = course_get_format($course)->get_course();
course_create_sections_if_missing($course, range(0, $course->numsections));
// Make sure section 0 is created.
course_create_sections_if_missing($course, 0);
$renderer = $PAGE->get_renderer('format_topics');

View file

@ -23,6 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['addsection'] = 'Add topic';
$string['currentsection'] = 'This topic';
$string['editsection'] = 'Edit topic';
$string['editsectionname'] = 'Edit topic name';

View file

@ -214,7 +214,6 @@ class format_topics extends format_base {
*
* Topics format uses the following options:
* - coursedisplay
* - numsections
* - hiddensections
*
* @param bool $foreditform
@ -225,10 +224,6 @@ class format_topics extends format_base {
if ($courseformatoptions === false) {
$courseconfig = get_config('moodlecourse');
$courseformatoptions = array(
'numsections' => array(
'default' => $courseconfig->numsections,
'type' => PARAM_INT,
),
'hiddensections' => array(
'default' => $courseconfig->hiddensections,
'type' => PARAM_INT,
@ -240,21 +235,7 @@ class format_topics extends format_base {
);
}
if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
$courseconfig = get_config('moodlecourse');
$max = $courseconfig->maxsections;
if (!isset($max) || !is_numeric($max)) {
$max = 52;
}
$sectionmenu = array();
for ($i = 0; $i <= $max; $i++) {
$sectionmenu[$i] = "$i";
}
$courseformatoptionsedit = array(
'numsections' => array(
'label' => new lang_string('numberweeks'),
'element_type' => 'select',
'element_attributes' => array($sectionmenu),
),
'hiddensections' => array(
'label' => new lang_string('hiddensections'),
'help' => 'hiddensections',
@ -295,24 +276,24 @@ class format_topics extends format_base {
* @return array array of references to the added form elements.
*/
public function create_edit_form_elements(&$mform, $forsection = false) {
global $COURSE;
$elements = parent::create_edit_form_elements($mform, $forsection);
// Increase the number of sections combo box values if the user has increased the number of sections
// using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been
// reduced below the number of sections already set for the course on the site administration course
// defaults page. This is so that the number of sections is not reduced leaving unintended orphaned
// activities / resources.
if (!$forsection) {
$maxsections = get_config('moodlecourse', 'maxsections');
$numsections = $mform->getElementValue('numsections');
$numsections = $numsections[0];
if ($numsections > $maxsections) {
$element = $mform->getElement('numsections');
for ($i = $maxsections+1; $i <= $numsections; $i++) {
$element->addOption("$i", $i);
}
if (!$forsection && (empty($COURSE->id) || $COURSE->id == SITEID)) {
// Add "numsections" element to the create course form - it will force new course to be prepopulated
// with empty sections.
// The "Number of sections" option is no longer available when editing course, instead teachers should
// delete and add sections when needed.
$courseconfig = get_config('moodlecourse');
$max = (int)$courseconfig->maxsections;
$element = $mform->addElement('select', 'numsections', get_string('numberweeks'), range(0, $max ?: 52));
$mform->setType('numsections', PARAM_INT);
if (is_null($mform->getElementValue('numsections'))) {
$mform->setDefault('numsections', $courseconfig->numsections);
}
array_unshift($elements, $element);
}
return $elements;
}
@ -320,9 +301,7 @@ class format_topics extends format_base {
* Updates format options for a course
*
* In case if course format was changed to 'topics', we try to copy options
* 'coursedisplay', 'numsections' and 'hiddensections' from the previous format.
* If previous course format did not have 'numsections' option, we populate it with the
* current number of sections
* 'coursedisplay' and 'hiddensections' from the previous format.
*
* @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
* @param stdClass $oldcourse if this function is called from {@link update_course()}
@ -330,7 +309,6 @@ class format_topics extends format_base {
* @return bool whether there were any changes to the options values
*/
public function update_course_format_options($data, $oldcourse = null) {
global $DB;
$data = (array)$data;
if ($oldcourse !== null) {
$oldcourse = (array)$oldcourse;
@ -339,33 +317,11 @@ class format_topics extends format_base {
if (!array_key_exists($key, $data)) {
if (array_key_exists($key, $oldcourse)) {
$data[$key] = $oldcourse[$key];
} else if ($key === 'numsections') {
// If previous format does not have the field 'numsections'
// and $data['numsections'] is not set,
// we fill it with the maximum section number from the DB
$maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
WHERE course = ?', array($this->courseid));
if ($maxsection) {
// If there are no sections, or just default 0-section, 'numsections' will be set to default
$data['numsections'] = $maxsection;
}
}
}
}
}
$changed = $this->update_format_options($data);
if ($changed && array_key_exists('numsections', $data)) {
// If the numsections was decreased, try to completely delete the orphaned sections (unless they are not empty).
$numsections = (int)$data['numsections'];
$maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
WHERE course = ?', array($this->courseid));
for ($sectionnum = $maxsection; $sectionnum > $numsections; $sectionnum--) {
if (!$this->delete_section($sectionnum, false)) {
break;
}
}
}
return $changed;
return $this->update_format_options($data);
}
/**
@ -420,8 +376,8 @@ class format_topics extends format_base {
* @return bool
*/
public function allow_stealth_module_visibility($cm, $section) {
// Allow the third visibility state inside visible sections or in section 0, not allow in orphaned sections.
return !$section->section || ($section->visible && $section->section <= $this->get_course()->numsections);
// Allow the third visibility state inside visible sections or in section 0.
return !$section->section || $section->visible;
}
public function section_action($section, $action, $sr) {

View file

@ -119,9 +119,8 @@ class format_topics_renderer extends format_section_renderer_base {
}
$url->param('sesskey', sesskey());
$isstealth = $section->section > $course->numsections;
$controls = array();
if (!$isstealth && $section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
if ($section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
if ($course->marker == $section->section) { // Show the "light globe" on/off.
$url->param('marker', 0);
$markedthistopic = get_string('markedthistopic');

View file

@ -70,9 +70,7 @@ Feature: Sections can be edited and deleted in topics format
Then I should see "Are you absolutely sure you want to completely delete \"Topic 5\" and all the activities it contains?"
And I press "Delete"
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"
And I should see "Topic 4"
Scenario: Deleting the middle section in topics format
When I delete section "4"
@ -80,31 +78,4 @@ Feature: Sections can be edited and deleted in topics format
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 delete section "5"
And I press "Delete"
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 delete section "1"
And I press "Delete"
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
And I should see "Topic 4"

View file

@ -36,32 +36,6 @@ require_once($CFG->dirroot . '/course/lib.php');
*/
class format_topics_testcase extends advanced_testcase {
public function test_update_course_numsections() {
global $DB;
$this->resetAfterTest(true);
$generator = $this->getDataGenerator();
$course = $generator->create_course(array('numsections' => 10, 'format' => 'topics'),
array('createsections' => true));
$generator->create_module('assign', array('course' => $course, 'section' => 7));
$this->setAdminUser();
$this->assertEquals(11, $DB->count_records('course_sections', array('course' => $course->id)));
// Change the numsections to 8, last two sections did not have any activities, they should be deleted.
update_course((object)array('id' => $course->id, 'numsections' => 8));
$this->assertEquals(9, $DB->count_records('course_sections', array('course' => $course->id)));
$this->assertEquals(9, count(get_fast_modinfo($course)->get_section_info_all()));
// Change the numsections to 5, section 8 should be deleted but section 7 should remain as it has activities.
update_course((object)array('id' => $course->id, 'numsections' => 6));
$this->assertEquals(8, $DB->count_records('course_sections', array('course' => $course->id)));
$this->assertEquals(8, count(get_fast_modinfo($course)->get_section_info_all()));
$this->assertEquals(6, course_get_format($course)->get_course()->numsections);
}
/**
* Tests for format_topics::get_section_name method with default section names.
*/

View file

@ -0,0 +1,128 @@
<?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/>.
/**
* format_topics unit tests for upgradelib
*
* @package format_topics
* @copyright 2015 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/format/topics/db/upgradelib.php');
/**
* format_topics unit tests for upgradelib
*
* @package format_topics
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_topics_upgrade_testcase extends advanced_testcase {
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_no_actions() {
global $DB;
$this->resetAfterTest(true);
$params = array('format' => 'topics', 'numsections' => 5, 'startdate' => 1445644800);
$course = $this->getDataGenerator()->create_course($params);
// This test is executed after 'numsections' option was already removed, add it manually.
$DB->insert_record('course_format_options', ['courseid' => $course->id, 'format' => 'topics',
'sectionid' => 0, 'name' => 'numsections', 'value' => '5']);
// There are 6 sections in the course (0-section and sections 1, ... 5).
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
format_topics_upgrade_remove_numsections();
// There are still 6 sections in the course.
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
}
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_delete_empty() {
global $DB;
$this->resetAfterTest(true);
// Set default number of sections to 10.
set_config('numsections', 10, 'moodlecourse');
$params1 = array('format' => 'topics', 'numsections' => 5, 'startdate' => 1445644800);
$course1 = $this->getDataGenerator()->create_course($params1);
$params2 = array('format' => 'topics', 'numsections' => 20, 'startdate' => 1445644800);
$course2 = $this->getDataGenerator()->create_course($params2);
// This test is executed after 'numsections' option was already removed, add it manually and
// set it to be 2 less than actual number of sections.
$DB->insert_record('course_format_options', ['courseid' => $course1->id, 'format' => 'topics',
'sectionid' => 0, 'name' => 'numsections', 'value' => '3']);
// There are 6 sections in the first course (0-section and sections 1, ... 5).
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course1->id]));
// There are 21 sections in the second course.
$this->assertEquals(21, $DB->count_records('course_sections', ['course' => $course2->id]));
format_topics_upgrade_remove_numsections();
// Two sections were deleted in the first course.
$this->assertEquals(4, $DB->count_records('course_sections', ['course' => $course1->id]));
// The second course was reset to 11 sections (default plus 0-section).
$this->assertEquals(11, $DB->count_records('course_sections', ['course' => $course2->id]));
}
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_hide_non_empty() {
global $DB;
$this->resetAfterTest(true);
$params = array('format' => 'topics', 'numsections' => 5, 'startdate' => 1445644800);
$course = $this->getDataGenerator()->create_course($params);
// Add a module to the second last section.
$cm = $this->getDataGenerator()->create_module('forum', ['course' => $course->id, 'section' => 4]);
// This test is executed after 'numsections' option was already removed, add it manually and
// set it to be 2 less than actual number of sections.
$DB->insert_record('course_format_options', ['courseid' => $course->id, 'format' => 'topics',
'sectionid' => 0, 'name' => 'numsections', 'value' => '3']);
// There are 6 sections.
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
format_topics_upgrade_remove_numsections();
// One section was deleted and one hidden.
$this->assertEquals(5, $DB->count_records('course_sections', ['course' => $course->id]));
$this->assertEquals(0, $DB->get_field('course_sections', 'visible', ['course' => $course->id, 'section' => 4]));
// The module is still visible.
$this->assertEquals(1, $DB->get_field('course_modules', 'visible', ['id' => $cm->cmid]));
}
}

View file

@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2017020200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2016112900; // Requires this Moodle version.
$plugin->component = 'format_topics'; // Full name of the plugin (used for diagnostics).

View file

@ -10,6 +10,11 @@ Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
must check $cm->is_visible_on_course_page() when displaying activities list on the course page instead of $cm->uservisible.
For all other plugins except course formats the same property $cm->uservisible indicates if the activity contents
is actually available to student.
* Option "Number of sections" (numsections) was removed from topics and weeks formats, instead the actual number of records
in the course_sections table is treated as a number of sections (excluding section 0 that should always be present).
* Method create_course() will populate the new course with empty sections if $data->numsections is provided even if
"numsections" is not an option defined by the course format.
* course/changenumsections.php can now be used to insert sections at any positions
=== 3.2 ===
* Callback delete_course is deprecated and should be replaced with observer for event \core\event\course_content_deleted

View file

@ -0,0 +1,121 @@
<?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/>.
/**
* Specialised restore for format_weeks
*
* @package format_weeks
* @category backup
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Specialised restore for format_weeks
*
* Processes 'numsections' from the old backup files and hides sections that used to be "orphaned"
*
* @package format_weeks
* @category backup
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_format_weeks_plugin extends restore_format_plugin {
/** @var int */
protected $originalnumsections = 0;
/**
* Checks if backup file was made on Moodle before 3.3 and we should respect the 'numsections'
* and potential "orphaned" sections in the end of the course.
*
* @return bool
*/
protected function need_restore_numsections() {
$backupinfo = $this->step->get_task()->get_info();
$backuprelease = $backupinfo->backup_release;
return version_compare($backuprelease, '3.3', 'lt');
}
/**
* Creates a dummy path element in order to be able to execute code after restore
*
* @return restore_path_element[]
*/
public function define_course_plugin_structure() {
global $DB;
// Since this method is executed before the restore we can do some pre-checks here.
// In case of merging backup into existing course find the current number of sections.
$target = $this->step->get_task()->get_target();
if (($target == backup::TARGET_CURRENT_ADDING || $target == backup::TARGET_EXISTING_ADDING) &&
$this->need_restore_numsections()) {
$maxsection = $DB->get_field_sql(
'SELECT max(section) FROM {course_sections} WHERE course = ?',
[$this->step->get_task()->get_courseid()]);
$this->originalnumsections = (int)$maxsection;
}
// Dummy path element is needed in order for after_restore_course() to be called.
return [new restore_path_element('dummy_course', $this->get_pathfor('/dummycourse'))];
}
/**
* Dummy process method
*/
public function process_dummy_course() {
}
/**
* Executed after course restore is complete
*
* This method is only executed if course configuration was overridden
*/
public function after_restore_course() {
global $DB;
if (!$this->need_restore_numsections()) {
// Backup file was made in Moodle 3.3 or later, we don't need to process 'numsecitons'.
return;
}
$data = $this->connectionpoint->get_data();
$backupinfo = $this->step->get_task()->get_info();
if ($backupinfo->original_course_format !== 'weeks' || !isset($data['tags']['numsections'])) {
// Backup from another course format or backup file does not even have 'numsections'.
return;
}
$numsections = (int)$data['tags']['numsections'];
foreach ($backupinfo->sections as $key => $section) {
// For each section from the backup file check if it was restored and if was "orphaned" in the original
// course and mark it as hidden. This will leave all activities in it visible and available just as it was
// in the original course.
// Exception is when we restore with merging and the course already had a section with this section number,
// in this case we don't modify the visibility.
if ($this->step->get_task()->get_setting_value($key . '_included')) {
$sectionnum = (int)$section->title;
if ($sectionnum > $numsections && $sectionnum > $this->originalnumsections) {
$DB->execute("UPDATE {course_sections} SET visible = 0 WHERE course = ? AND section = ?",
[$this->step->get_task()->get_courseid(), $sectionnum]);
}
}
}
}
}

View file

@ -0,0 +1,47 @@
<?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/>.
/**
* Upgrade scripts for course format "Weeks"
*
* @package format_weeks
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Upgrade script for format_weeks
*
* @param int $oldversion the version we are upgrading from
* @return bool result
*/
function xmldb_format_weeks_upgrade($oldversion) {
global $CFG, $DB;
require_once($CFG->dirroot . '/course/format/weeks/db/upgradelib.php');
if ($oldversion < 2017020200) {
// Remove 'numsections' option and hide or delete orphaned sections.
format_weeks_upgrade_remove_numsections();
upgrade_plugin_savepoint(true, 2017020200, 'format', 'weeks');
}
return true;
}

View file

@ -0,0 +1,117 @@
<?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/>.
/**
* Upgrade scripts for course format "Weeks"
*
* @package format_weeks
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This method finds all courses in 'weeks' format that have actual number of sections
* bigger than their 'numsections' course format option.
* For each such course we call {@link format_weeks_upgrade_hide_extra_sections()} and
* either delete or hide "orphaned" sections.
*/
function format_weeks_upgrade_remove_numsections() {
global $DB;
$sql1 = "SELECT c.id, max(cs.section) AS sectionsactual
FROM {course} c
JOIN {course_sections} cs ON cs.course = c.id
WHERE c.format = :format1
GROUP BY c.id";
$sql2 = "SELECT c.id, n.value AS numsections
FROM {course} c
JOIN {course_format_options} n ON n.courseid = c.id AND n.format = :format1 AND n.name = :numsections AND n.sectionid = 0
WHERE c.format = :format2";
$params = ['format1' => 'weeks', 'format2' => 'weeks', 'numsections' => 'numsections'];
$actual = $DB->get_records_sql_menu($sql1, $params);
$numsections = $DB->get_records_sql_menu($sql2, $params);
$needfixing = [];
$defaultnumsections = get_config('moodlecourse', 'numsections');
foreach ($actual as $courseid => $sectionsactual) {
if (array_key_exists($courseid, $numsections)) {
$n = (int)$numsections[$courseid];
} else {
$n = $defaultnumsections;
}
if ($sectionsactual > $n) {
$needfixing[$courseid] = $n;
}
}
unset($actual);
unset($numsections);
foreach ($needfixing as $courseid => $numsections) {
format_weeks_upgrade_hide_extra_sections($courseid, $numsections);
}
$DB->delete_records('course_format_options', ['format' => 'weeks', 'sectionid' => 0, 'name' => 'numsections']);
}
/**
* Find all sections in the course with sectionnum bigger than numsections.
* Either delete these sections or hide them
*
* We will only delete a section if it is completely empty and all sections below
* it are also empty
*
* @param int $courseid
* @param int $numsections
*/
function format_weeks_upgrade_hide_extra_sections($courseid, $numsections) {
global $DB;
$sections = $DB->get_records_sql('SELECT id, name, summary, sequence, visible
FROM {course_sections}
WHERE course = ? AND section > ?
ORDER BY section DESC', [$courseid, $numsections]);
$candelete = true;
$tohide = [];
$todelete = [];
foreach ($sections as $section) {
if ($candelete && (!empty($section->summary) || !empty($section->sequence) || !empty($section->name))) {
$candelete = false;
}
if ($candelete) {
$todelete[] = $section->id;
} else if ($section->visible) {
$tohide[] = $section->id;
}
}
if ($todelete) {
// Delete empty sections in the end.
// This is an upgrade script - no events or cache resets are needed.
// We also know that these sections do not have any modules so it is safe to just delete records in the table.
$DB->delete_records_list('course_sections', 'id', $todelete);
}
if ($tohide) {
// Hide other orphaned sections.
// This is different from what set_section_visible() does but we want to preserve actual
// module visibility in this case.
list($sql, $params) = $DB->get_in_or_equal($tohide);
$DB->execute("UPDATE {course_sections} SET visible = 0 WHERE id " . $sql, $params);
}
}

View file

@ -37,9 +37,9 @@ if ($week = optional_param('week', 0, PARAM_INT)) {
}
// End backwards-compatible aliasing..
// make sure all sections are created
// Make sure section 0 is created.
$course = course_get_format($course)->get_course();
course_create_sections_if_missing($course, range(0, $course->numsections));
course_create_sections_if_missing($course, 0);
$renderer = $PAGE->get_renderer('format_weeks');

View file

@ -23,6 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['addsection'] = 'Add week';
$string['currentsection'] = 'This week';
$string['editsection'] = 'Edit week';
$string['editsectionname'] = 'Edit week name';

View file

@ -219,7 +219,6 @@ class format_weeks extends format_base {
*
* Weeks format uses the following options:
* - coursedisplay
* - numsections
* - hiddensections
*
* @param bool $foreditform
@ -230,10 +229,6 @@ class format_weeks extends format_base {
if ($courseformatoptions === false) {
$courseconfig = get_config('moodlecourse');
$courseformatoptions = array(
'numsections' => array(
'default' => $courseconfig->numsections,
'type' => PARAM_INT,
),
'hiddensections' => array(
'default' => $courseconfig->hiddensections,
'type' => PARAM_INT,
@ -245,21 +240,7 @@ class format_weeks extends format_base {
);
}
if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
$courseconfig = get_config('moodlecourse');
$sectionmenu = array();
$max = $courseconfig->maxsections;
if (!isset($max) || !is_numeric($max)) {
$max = 52;
}
for ($i = 0; $i <= $max; $i++) {
$sectionmenu[$i] = "$i";
}
$courseformatoptionsedit = array(
'numsections' => array(
'label' => new lang_string('numberweeks'),
'element_type' => 'select',
'element_attributes' => array($sectionmenu),
),
'hiddensections' => array(
'label' => new lang_string('hiddensections'),
'help' => 'hiddensections',
@ -300,24 +281,24 @@ class format_weeks extends format_base {
* @return array array of references to the added form elements.
*/
public function create_edit_form_elements(&$mform, $forsection = false) {
global $COURSE;
$elements = parent::create_edit_form_elements($mform, $forsection);
// Increase the number of sections combo box values if the user has increased the number of sections
// using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been
// reduced below the number of sections already set for the course on the site administration course
// defaults page. This is so that the number of sections is not reduced leaving unintended orphaned
// activities / resources.
if (!$forsection) {
$maxsections = get_config('moodlecourse', 'maxsections');
$numsections = $mform->getElementValue('numsections');
$numsections = $numsections[0];
if ($numsections > $maxsections) {
$element = $mform->getElement('numsections');
for ($i = $maxsections+1; $i <= $numsections; $i++) {
$element->addOption("$i", $i);
}
if (!$forsection && (empty($COURSE->id) || $COURSE->id == SITEID)) {
// Add "numsections" element to the create course form - it will force new course to be prepopulated
// with empty sections.
// The "Number of sections" option is no longer available when editing course, instead teachers should
// delete and add sections when needed.
$courseconfig = get_config('moodlecourse');
$max = (int)$courseconfig->maxsections;
$element = $mform->addElement('select', 'numsections', get_string('numberweeks'), range(0, $max ?: 52));
$mform->setType('numsections', PARAM_INT);
if (is_null($mform->getElementValue('numsections'))) {
$mform->setDefault('numsections', $courseconfig->numsections);
}
array_unshift($elements, $element);
}
return $elements;
}
@ -344,33 +325,11 @@ class format_weeks extends format_base {
if (!array_key_exists($key, $data)) {
if (array_key_exists($key, $oldcourse)) {
$data[$key] = $oldcourse[$key];
} else if ($key === 'numsections') {
// If previous format does not have the field 'numsections'
// and $data['numsections'] is not set,
// we fill it with the maximum section number from the DB
$maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
WHERE course = ?', array($this->courseid));
if ($maxsection) {
// If there are no sections, or just default 0-section, 'numsections' will be set to default
$data['numsections'] = $maxsection;
}
}
}
}
}
$changed = $this->update_format_options($data);
if ($changed && array_key_exists('numsections', $data)) {
// If the numsections was decreased, try to completely delete the orphaned sections (unless they are not empty).
$numsections = (int)$data['numsections'];
$maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
WHERE course = ?', array($this->courseid));
for ($sectionnum = $maxsection; $sectionnum > $numsections; $sectionnum--) {
if (!$this->delete_section($sectionnum, false)) {
break;
}
}
}
return $changed;
return $this->update_format_options($data);
}
/**
@ -479,6 +438,9 @@ class format_weeks extends format_base {
if ($mform->elementExists($fieldnames['numsections'])) {
$numsections = $mform->getElementValue($fieldnames['numsections']);
$numsections = $mform->getElement($fieldnames['numsections'])->exportValue($numsections);
} else if ($this->get_courseid()) {
// For existing courses get the number of sections.
$numsections = $this->get_last_section_number();
} else {
// Fallback to the default value for new courses.
$numsections = get_config('moodlecourse', $fieldnames['numsections']);
@ -507,8 +469,8 @@ class format_weeks extends format_base {
* @return bool
*/
public function allow_stealth_module_visibility($cm, $section) {
// Allow the third visibility state inside visible sections or in section 0, not allow in orphaned sections.
return !$section->section || ($section->visible && $section->section <= $this->get_course()->numsections);
// Allow the third visibility state inside visible sections or in section 0.
return !$section->section || $section->visible;
}
public function section_action($section, $action, $sr) {

View file

@ -76,9 +76,7 @@ Feature: Sections can be edited and deleted in weeks format
Then I should see "Are you absolutely sure you want to completely delete \"29 May - 4 June\" and all the activities it contains?"
And I press "Delete"
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"
And I should see "22 May - 28 May"
Scenario: Deleting the middle section in weeks format
Given I should see "29 May - 4 June" in the "li#section-5" "css_element"
@ -87,31 +85,4 @@ Feature: Sections can be edited and deleted in weeks format
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 delete section "5"
And I press "Delete"
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 delete section "1"
And I press "Delete"
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
And I should see "22 May - 28 May"

View file

@ -36,32 +36,6 @@ require_once($CFG->dirroot . '/course/lib.php');
*/
class format_weeks_testcase extends advanced_testcase {
public function test_update_course_numsections() {
global $DB;
$this->resetAfterTest(true);
$generator = $this->getDataGenerator();
$course = $generator->create_course(array('numsections' => 10, 'format' => 'weeks'),
array('createsections' => true));
$generator->create_module('assign', array('course' => $course, 'section' => 7));
$this->setAdminUser();
$this->assertEquals(11, $DB->count_records('course_sections', array('course' => $course->id)));
// Change the numsections to 8, last two sections did not have any activities, they should be deleted.
update_course((object)array('id' => $course->id, 'numsections' => 8));
$this->assertEquals(9, $DB->count_records('course_sections', array('course' => $course->id)));
$this->assertEquals(9, count(get_fast_modinfo($course)->get_section_info_all()));
// Change the numsections to 5, section 8 should be deleted but section 7 should remain as it has activities.
update_course((object)array('id' => $course->id, 'numsections' => 6));
$this->assertEquals(8, $DB->count_records('course_sections', array('course' => $course->id)));
$this->assertEquals(8, count(get_fast_modinfo($course)->get_section_info_all()));
$this->assertEquals(6, course_get_format($course)->get_course()->numsections);
}
/**
* Tests for format_weeks::get_section_name method with default section names.
*/
@ -224,7 +198,7 @@ class format_weeks_testcase extends advanced_testcase {
* @return void
*/
public function test_default_course_enddate() {
global $CFG, $DB;
global $CFG, $DB, $PAGE;
$this->resetAfterTest(true);
@ -247,6 +221,7 @@ class format_weeks_testcase extends advanced_testcase {
'returnurl' => new moodle_url('/'),
];
$PAGE->set_course($course);
$courseform = new testable_course_edit_form(null, $args);
$courseform->definition_after_data();

View file

@ -0,0 +1,128 @@
<?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/>.
/**
* format_weeks unit tests for upgradelib
*
* @package format_weeks
* @copyright 2015 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/format/weeks/db/upgradelib.php');
/**
* format_weeks unit tests for upgradelib
*
* @package format_weeks
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_weeks_upgrade_testcase extends advanced_testcase {
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_no_actions() {
global $DB;
$this->resetAfterTest(true);
$params = array('format' => 'weeks', 'numsections' => 5, 'startdate' => 1445644800);
$course = $this->getDataGenerator()->create_course($params);
// This test is executed after 'numsections' option was already removed, add it manually.
$DB->insert_record('course_format_options', ['courseid' => $course->id, 'format' => 'weeks',
'sectionid' => 0, 'name' => 'numsections', 'value' => '5']);
// There are 6 sections in the course (0-section and sections 1, ... 5).
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
format_weeks_upgrade_remove_numsections();
// There are still 6 sections in the course.
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
}
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_delete_empty() {
global $DB;
$this->resetAfterTest(true);
// Set default number of sections to 10.
set_config('numsections', 10, 'moodlecourse');
$params1 = array('format' => 'weeks', 'numsections' => 5, 'startdate' => 1445644800);
$course1 = $this->getDataGenerator()->create_course($params1);
$params2 = array('format' => 'weeks', 'numsections' => 20, 'startdate' => 1445644800);
$course2 = $this->getDataGenerator()->create_course($params2);
// This test is executed after 'numsections' option was already removed, add it manually and
// set it to be 2 less than actual number of sections.
$DB->insert_record('course_format_options', ['courseid' => $course1->id, 'format' => 'weeks',
'sectionid' => 0, 'name' => 'numsections', 'value' => '3']);
// There are 6 sections in the first course (0-section and sections 1, ... 5).
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course1->id]));
// There are 21 sections in the second course.
$this->assertEquals(21, $DB->count_records('course_sections', ['course' => $course2->id]));
format_weeks_upgrade_remove_numsections();
// Two sections were deleted in the first course.
$this->assertEquals(4, $DB->count_records('course_sections', ['course' => $course1->id]));
// The second course was reset to 11 sections (default plus 0-section).
$this->assertEquals(11, $DB->count_records('course_sections', ['course' => $course2->id]));
}
/**
* Test upgrade step to remove orphaned sections.
*/
public function test_numsections_hide_non_empty() {
global $DB;
$this->resetAfterTest(true);
$params = array('format' => 'weeks', 'numsections' => 5, 'startdate' => 1445644800);
$course = $this->getDataGenerator()->create_course($params);
// Add a module to the second last section.
$cm = $this->getDataGenerator()->create_module('forum', ['course' => $course->id, 'section' => 4]);
// This test is executed after 'numsections' option was already removed, add it manually and
// set it to be 2 less than actual number of sections.
$DB->insert_record('course_format_options', ['courseid' => $course->id, 'format' => 'weeks',
'sectionid' => 0, 'name' => 'numsections', 'value' => '3']);
// There are 6 sections.
$this->assertEquals(6, $DB->count_records('course_sections', ['course' => $course->id]));
format_weeks_upgrade_remove_numsections();
// One section was deleted and one hidden.
$this->assertEquals(5, $DB->count_records('course_sections', ['course' => $course->id]));
$this->assertEquals(0, $DB->get_field('course_sections', 'visible', ['course' => $course->id, 'section' => 4]));
// The module is still visible.
$this->assertEquals(1, $DB->get_field('course_modules', 'visible', ['id' => $cm->cmid]));
}
}

View file

@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2017020200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2016112900; // Requires this Moodle version.
$plugin->component = 'format_weeks'; // Full name of the plugin (used for diagnostics).