]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-12198
authornicolasconnault <nicolasconnault>
Fri, 8 Aug 2008 05:25:54 +0000 (05:25 +0000)
committernicolasconnault <nicolasconnault>
Fri, 8 Aug 2008 05:25:54 +0000 (05:25 +0000)
course/lib.php
course/rest.php
course/simpletest/testcourselib.php [new file with mode: 0755]
lib/ajax/section_classes.js

index 292ee9ad03a40369b5a00069a8b74e910f318bff..915e6be3cd0d1b8cac740743e6962ba857305ca0 100644 (file)
@@ -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
index 6eb4164b10af8c298021d7c00e2c660168633caa..c5325e5a05d8c95e72c35de2e61fca789cd24e60 100644 (file)
@@ -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 (executable)
index 0000000..a62b6d3
--- /dev/null
@@ -0,0 +1,116 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.org                                            //
+//                                                                       //
+// Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com     //
+//                                                                       //
+// This program 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 2 of the License, or     //
+// (at your option) any later version.                                   //
+//                                                                       //
+// This program 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:                          //
+//                                                                       //
+//          http://www.gnu.org/copyleft/gpl.html                         //
+//                                                                       //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * Unit tests for Course lib.
+ *
+ * @author nicolasconnault@gmail.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package moodlecore
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
+}
+
+require_once($CFG->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));
+    }
+}
index dede0df45f102041c66dd529b820f6201bed4cbe..44d2185ed488aeac8556f151ca24186c18f5fe3d 100755 (executable)
@@ -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<sectionCount';
         var loopStart = 1;
         var loopInc = 'i++';
-        var loopmodifier = 'i - 1'; 
+        var loopmodifier = 'i - 1';
+        var targetOffset = 0;
     } else {
         var loopCondition = 'i > 0';
-        var loopStart = sectionCount - 1;  
-        var loopInc = 'i--'; 
-        var loopmodifier = 'i + 1';       
+        var loopStart = sectionCount - 1;
+        var loopInc = 'i--';
+        var loopmodifier = 'i + 1';
+        var targetOffset = 1;
     }
 
     //move on backend
-    main.connect('POST','class=section&field=move',null,'id='+this.sectionId+'&value='
-            +(target.sectionId - this.sectionId));
+    main.connect('POST','class=section&field=move',null,'id='+this.sectionId+'&value=' + (target.sectionId - targetOffset));
 
     //move on front end
     for (var i=loopStart; eval(loopCondition); eval(loopInc)) {
@@ -253,7 +254,7 @@ section_class.prototype.move_to_section = function(target) {
 }
 
 
-section_class.prototype.swap_with_section = function(sectionIn) {    
+section_class.prototype.swap_with_section = function(sectionIn) {
     var tmpStore = null;
 
     thisIndex = main.get_section_index(this);
@@ -297,7 +298,7 @@ section_class.prototype.toggle_hide = function(e,target,superficial) {
 
         if (!superficial) {
             main.connect('POST', 'class=section&field=visible', null, 'value=1&id='+this.sectionId);
-            for (var x=0; x<this.resources.length; x++) {                                
+            for (var x=0; x<this.resources.length; x++) {
                 this.resources[x].toggle_hide(null, null, true, this.resources[x].hiddenStored);
                 this.resources[x].hiddenStored = null;
             }
@@ -314,7 +315,7 @@ section_class.prototype.toggle_hide = function(e,target,superficial) {
         if (!superficial) {
             main.connect('POST', 'class=section&field=visible', null, 'value=0&id='+this.sectionId);
             for (var x=0; x<this.resources.length; x++) {
-                this.resources[x].hiddenStored = this.resources[x].hidden;                                
+                this.resources[x].hiddenStored = this.resources[x].hidden;
                 this.resources[x].toggle_hide(null, null, true, true);
             }
         }
@@ -323,14 +324,14 @@ section_class.prototype.toggle_hide = function(e,target,superficial) {
 
 
 section_class.prototype.toggle_highlight = function() {
-    if (this.highlighted) {  
+    if (this.highlighted) {
         YAHOO.util.Dom.removeClass(this.getEl(), 'current');
         this.highlighted = false;
     } else {
         YAHOO.util.Dom.addClass(this.getEl(), 'current');
         this.highlighted = true;
     }
-} 
+}
 
 
 section_class.prototype.mk_marker = function() {
@@ -346,16 +347,16 @@ section_class.prototype.mk_marker = function() {
 }
 
 
-section_class.prototype.changeId = function(newId) {                       
-    this.sectionId = newId;       
+section_class.prototype.changeId = function(newId) {
+    this.sectionId = newId;
     this.numberDisplay.firstChild.data = newId;
 
-    //main.connectQueue_add('POST','class=section&field=all',null,'id='+newId+"&summary="+main.mk_safe_for_transport(this.summary)+"&sequence="+this.write_sequence_list(true)+'&visible='+(this.hidden?0:1))           
+    //main.connectQueue_add('POST','class=section&field=all',null,'id='+newId+"&summary="+main.mk_safe_for_transport(this.summary)+"&sequence="+this.write_sequence_list(true)+'&visible='+(this.hidden?0:1))
 
     if (main.marker == this) {
         main.update_marker(this);
     }
-}     
+}
 
 
 section_class.prototype.get_resource_index = function(el) {
@@ -387,7 +388,7 @@ section_class.prototype.remove_resource = function(el) {
         var found = false;
         for (var i=0; i<resourceCount; i++) {
             if (found) {
-                this.resources[i - 1] = this.resources[i];         
+                this.resources[i - 1] = this.resources[i];
                 if (i == resourceCount - 1) {
                     this.resources = this.resources.slice(0, -1);
                     resourceCount--;
@@ -432,8 +433,8 @@ section_class.prototype.insert_resource = function(el, targetel) {
 
     //if inserting into a hidden resource hide
     if (this.hidden) {
-        el.hiddenStored = el.hidden; 
-        el.toggle_hide(null, null, true, true);           
+        el.hiddenStored = el.hidden;
+        el.toggle_hide(null, null, true, true);
     } else {
         if (el.hiddenStored != null) {
             el.toggle_hide(null, null, true, el.hiddenStored);
@@ -450,18 +451,18 @@ section_class.prototype.insert_resource = function(el, targetel) {
                 this.resources[i] = nextStore;
                 nextStore = tempStore;
 
-                if (nextStore != null)     
-                    nextStore.update_index(i+1); 
+                if (nextStore != null)
+                    nextStore.update_index(i+1);
 
             } else if (this.resources[i] == targetel) {
                 found = true;
                 nextStore = this.resources[i];
-                this.resources[i] = el;               
+                this.resources[i] = el;
                 resourcecount++;
 
                 this.resources[i].update_index(i, this.ident);
-                nextStore.update_index(i + 1); 
-            }                              
+                nextStore.update_index(i + 1);
+            }
         }
     }
     //update on frontend
@@ -471,9 +472,9 @@ section_class.prototype.insert_resource = function(el, targetel) {
     } else {
         this.resources_ul.appendChild(el.getEl());
         //this.resources_ul.appendChild(document.createTextNode(' '));
-    }    
-    el.parentObj = this;       
-}    
+    }
+    el.parentObj = this;
+}
 
 
 section_class.prototype.write_sequence_list = function(toReturn) {
@@ -507,9 +508,9 @@ resource_class.prototype.debug = false;
 
 
 resource_class.prototype.init_resource = function(id, group, config, parentObj) {
-    if (!id) { 
+    if (!id) {
         YAHOO.log("Init resource, NO ID FOUND!", 'error');
-        return; 
+        return;
     }
 
     // Some constants.
@@ -517,9 +518,9 @@ resource_class.prototype.init_resource = function(id, group, config, parentObj)
     this.SEPARATEGROUPS = 1;
     this.VISIBLEGROUPS = 2;
 
-    this.is = 'resource';  
+    this.is = 'resource';
     this.init(id, group, config);
-    this.createFrame();     
+    this.createFrame();
     this.isTarget = true;
 
     this.id = this.getEl().id.replace(/module-/i, '');
@@ -589,7 +590,7 @@ resource_class.prototype.init_buttons = function() {
     var moveLeft = false;
     var moveRight = false;
     var updateButton = null;
-       
+
        // for RTL support
        var isrtl = (document.getElementsByTagName("html")[0].dir=="rtl");
 
@@ -675,7 +676,7 @@ resource_class.prototype.init_buttons = function() {
         commandContainer.appendChild(button);
         this.groupButton = button;
     }
-}    
+}
 
 
 resource_class.prototype.indent_left = function() {
@@ -751,14 +752,14 @@ resource_class.prototype.toggle_hide = function(target, e, superficial, force) {
         if (this.debug) {
             YAHOO.log("Resource "+this.getEl().id+" forced to "+force);
         }
-        this.hidden = !force;           
+        this.hidden = !force;
     }
     if (this.hidden) {
         YAHOO.util.Dom.removeClass(this.linkContainer, 'dimmed');
         this.viewButton.childNodes[0].src = this.viewButton.childNodes[0].src.replace(/show.gif/i, 'hide.gif');
         this.viewButton.childNodes[0].alt = this.viewButton.childNodes[0].alt.replace(strshow, strhide);
         this.viewButton.title = this.viewButton.title.replace(strshow, strhide);
-        this.hidden = false;  
+        this.hidden = false;
 
         if (!superficial) {
             main.connect('POST', 'class=resource&field=visible', null, 'value=1&id='+this.id);