From 3440ec1234f4192e85a34724187bde6a52892100 Mon Sep 17 00:00:00 2001 From: nicolasconnault Date: Fri, 8 Aug 2008 05:25:54 +0000 Subject: [PATCH] MDL-12198 --- course/lib.php | 118 +++++++++++++++++++++++++++- course/rest.php | 20 ++--- course/simpletest/testcourselib.php | 116 +++++++++++++++++++++++++++ lib/ajax/section_classes.js | 77 +++++++++--------- 4 files changed, 282 insertions(+), 49 deletions(-) create mode 100755 course/simpletest/testcourselib.php diff --git a/course/lib.php b/course/lib.php index 292ee9ad03..915e6be3cd 100644 --- a/course/lib.php +++ b/course/lib.php @@ -2396,6 +2396,13 @@ function delete_mod_from_section($mod, $section) { return false; } +/** + * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX! + * + * @param object $course + * @param int $section + * @param int $move (-1 or 1) + */ function move_section($course, $section, $move) { /// Moves a whole course section up and down within the course global $USER, $DB; @@ -2444,6 +2451,115 @@ function move_section($course, $section, $move) { return true; } +/** + * Moves a section within a course, from a position to another. + * Be very careful: $section and $destination refer to section number, + * not id!. + * + * @param object $course + * @param int $section Section number (not id!!!) + * @param int $destination + * @return boolean Result + */ +function move_section_to($course, $section, $destination) { +/// Moves a whole course section up and down within the course + global $USER, $DB; + + if (!$destination) { + return true; + } + + if ($destination > $course->numsections or $destination < 1) { + return false; + } + + // Get all sections for this course and re-order them (2 of them should now share the same section number) + if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id), + 'section ASC, id ASC', 'id, section')) { + return false; + } + + $sections = reorder_sections($sections, $section, $destination); + + // Update all sections + foreach ($sections as $id => $position) { + $DB->set_field('course_sections', 'section', $position, array('id' => $id)); + } + + // if the focus is on the section that is being moved, then move the focus along + if (isset($USER->display[$course->id]) and ($USER->display[$course->id] == $section)) { + course_set_display($course->id, $destination); + } + 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 + * move is made without any duplication of section positions. + * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to + * insert a section before the first one, you must give 0 as the target (section 0 can never be moved). + * + * @param array $sections + * @param int $origin_position + * @param int $target_position + * @return array + */ +function reorder_sections($sections, $origin_position, $target_position) { + if (!is_array($sections)) { + return false; + } + + // We can't move section position 0 + if ($origin_position < 1) { + echo "We can't move section position 0"; + return false; + } + + // Locate origin section in sections array + if (!$origin_key = array_search($origin_position, $sections)) { + echo "searched position not in sections array"; + return false; // searched position not in sections array + } + + // Extract origin section + $origin_section = $sections[$origin_key]; + unset($sections[$origin_key]); + + // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!) + $found = false; + $append_array = array(); + foreach ($sections as $id => $position) { + if ($found) { + $append_array[$id] = $position; + unset($sections[$id]); + } + if ($position == $target_position) { + $found = true; + } + } + + // Append moved section + $sections[$origin_key] = $origin_section; + + // Append rest of array (if applicable) + if (!empty($append_array)) { + foreach ($append_array as $id => $position) { + $sections[$id] = $position; + } + } + + // Renumber positions + $position = 0; + foreach ($sections as $id => $p) { + $sections[$id] = $position; + $position++; + } + + return $sections; + +} + /** * Move the module object $mod to the specified $section * If $beforemod exists then that is the module @@ -2992,7 +3108,7 @@ function move_category ($category, $newparentcat) { context_moved($context, $newparent); - // now make it last in new category + // now make it last in new category $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('id'=>$category->id)); // and fix the sortorders diff --git a/course/rest.php b/course/rest.php index 6eb4164b10..c5325e5a05 100644 --- a/course/rest.php +++ b/course/rest.php @@ -54,7 +54,7 @@ switch($_SERVER['REQUEST_METHOD']) { case 'block': switch ($field) { - case 'visible': + case 'visible': blocks_execute_action($PAGE, $pageblocks, 'toggle', $blockinstance); break; @@ -68,19 +68,19 @@ switch($_SERVER['REQUEST_METHOD']) { break; case 'section': - + if (!$DB->record_exists('course_sections', array('course'=>$course->id, 'section'=>$id))) { error_log('AJAX commands.php: Bad Section ID '.$id); die; } - + switch ($field) { case 'visible': set_section_visible($course->id, $id, $value); break; case 'move': - move_section($course, $id, $value); + move_section_to($course, $id, $value); break; } rebuild_course_cache($course->id); @@ -117,7 +117,7 @@ switch($_SERVER['REQUEST_METHOD']) { error_log('AJAX commands.php: Bad section ID '.$sectionid); die; } - + if ($beforeid > 0){ $beforemod = $DB->get_record('course_modules', array('id'=>$beforeid)); } else { @@ -133,8 +133,8 @@ switch($_SERVER['REQUEST_METHOD']) { } rebuild_course_cache($course->id); break; - - case 'course': + + case 'course': switch($field) { case 'marker': $newcourse = new object; @@ -154,8 +154,8 @@ switch($_SERVER['REQUEST_METHOD']) { switch ($class) { case 'block': blocks_execute_action($PAGE, $pageblocks, 'delete', $blockinstance); - break; - + break; + case 'resource': if (!$cm = $DB->get_record('course_modules', array('id'=>$id, 'course'=>$course->id))) { error_log('AJAX rest.php: Bad course module ID '.$id); @@ -197,4 +197,4 @@ switch($_SERVER['REQUEST_METHOD']) { break; } -?> \ No newline at end of file +?> diff --git a/course/simpletest/testcourselib.php b/course/simpletest/testcourselib.php new file mode 100755 index 0000000000..a62b6d3674 --- /dev/null +++ b/course/simpletest/testcourselib.php @@ -0,0 +1,116 @@ +dirroot . '/course/lib.php'); +global $DB; +Mock::generate(get_class($DB), 'mockDB'); + +class courselib_test extends UnitTestCase { + var $realDB; + + function setUp() { + global $DB; + $this->realDB = clone($DB); + $DB = new mockDB(); + } + + function tearDown() { + global $DB; + $DB = $this->realDB; + } + + function testMoveSection() { + global $DB; + $course = new stdClass(); + $course->numsections = 10; + $course->id = 1; + $sections = array(20 => 0, 21 => 1, 22 => 2, 23 => 3, 24 => 4, 25 => 5); + + $DB->setReturnValueAt(0, 'get_records_menu', $sections); + $DB->expectAt(0, 'set_field', array('course_sections', 'section', 0, array('id' => 20))); + $DB->expectAt(1, 'set_field', array('course_sections', 'section', 1, array('id' => 21))); + $DB->expectAt(2, 'set_field', array('course_sections', 'section', 2, array('id' => 23))); + $DB->expectAt(3, 'set_field', array('course_sections', 'section', 3, array('id' => 24))); + $DB->expectAt(4, 'set_field', array('course_sections', 'section', 4, array('id' => 22))); + $DB->expectAt(5, 'set_field', array('course_sections', 'section', 5, array('id' => 25))); + move_section_to($course, 2, 4); + + $DB->setReturnValueAt(1, 'get_records_menu', $sections); + $DB->expectAt(6, 'set_field', array('course_sections', 'section', 0, array('id' => 20))); + $DB->expectAt(7, 'set_field', array('course_sections', 'section', 1, array('id' => 24))); + $DB->expectAt(8, 'set_field', array('course_sections', 'section', 2, array('id' => 21))); + $DB->expectAt(9, 'set_field', array('course_sections', 'section', 3, array('id' => 22))); + $DB->expectAt(10, 'set_field', array('course_sections', 'section', 4, array('id' => 23))); + $DB->expectAt(11, 'set_field', array('course_sections', 'section', 5, array('id' => 25))); + move_section_to($course, 4, 0); + } + + function testReorderSections() { + $sections = array(20 => 0, 21 => 1, 22 => 2, 23 => 3, 24 => 4, 25 => 5); + $this->assertFalse(reorder_sections(1,3,4)); + + $newsections = reorder_sections($sections, 2, 4); + $newsections_flipped = array_flip($newsections); + + $this->assertEqual(20, reset($newsections_flipped)); + $this->assertEqual(21, next($newsections_flipped)); + $this->assertEqual(23, next($newsections_flipped)); + $this->assertEqual(24, next($newsections_flipped)); + $this->assertEqual(22, next($newsections_flipped)); + $this->assertEqual(25, next($newsections_flipped)); + + $newsections = reorder_sections($sections, 4, 0); + $newsections_flipped = array_flip($newsections); + + $this->assertEqual(20, reset($newsections_flipped)); + $this->assertEqual(24, next($newsections_flipped)); + $this->assertEqual(21, next($newsections_flipped)); + $this->assertEqual(22, next($newsections_flipped)); + $this->assertEqual(23, next($newsections_flipped)); + $this->assertEqual(25, next($newsections_flipped)); + + $newsections = reorder_sections($sections, 1, 5); + $newsections_flipped = array_flip($newsections); + + $this->assertEqual(20, reset($newsections_flipped)); + $this->assertEqual(22, next($newsections_flipped)); + $this->assertEqual(23, next($newsections_flipped)); + $this->assertEqual(24, next($newsections_flipped)); + $this->assertEqual(25, next($newsections_flipped)); + $this->assertEqual(21, next($newsections_flipped)); + } +} diff --git a/lib/ajax/section_classes.js b/lib/ajax/section_classes.js index dede0df45f..44d2185ed4 100755 --- a/lib/ajax/section_classes.js +++ b/lib/ajax/section_classes.js @@ -78,7 +78,7 @@ section_class.prototype.init_section = function(id, group, config, isDraggable) } -section_class.prototype.init_buttons = function() { +section_class.prototype.init_buttons = function() { var commandContainer = YAHOO.util.Dom.getElementsByClassName('right',null,this.getEl())[0]; //clear all but show only button @@ -91,7 +91,7 @@ section_class.prototype.init_buttons = function() { if (!this.isWeekFormat) { var highlightbutton = main.mk_button('div', '/i/marker.gif', main.getString('marker', this.sectionId)); YAHOO.util.Event.addListener(highlightbutton, 'click', this.mk_marker, this, true); - commandContainer.appendChild(highlightbutton); + commandContainer.appendChild(highlightbutton); this.highlightButton = highlightbutton; } var viewbutton = main.mk_button('div', '/i/hide.gif', main.getString('hidesection', this.sectionId), @@ -128,7 +128,7 @@ section_class.prototype.process_section = function() { this.numberDisplay = document.createElement('div'); this.numberDisplay.innerHTML = this.getEl().childNodes[0].innerHTML; this.getEl().childNodes[0].innerHTML = ''; - this.getEl().childNodes[0].appendChild(this.numberDisplay); + this.getEl().childNodes[0].appendChild(this.numberDisplay); this.sectionId = this.id.replace(/section-/i, ''); // Okay, we will have to change this if we // ever change the id attributes format @@ -192,7 +192,7 @@ section_class.prototype.endDrag = function() { //add back to resources group this.addToGroup('resources'); -} +} section_class.prototype.move_to_section = function(target) { @@ -212,17 +212,18 @@ section_class.prototype.move_to_section = function(target) { var loopCondition = 'i