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;
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
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
case 'block':
switch ($field) {
- case 'visible':
+ case 'visible':
blocks_execute_action($PAGE, $pageblocks, 'toggle', $blockinstance);
break;
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);
error_log('AJAX commands.php: Bad section ID '.$sectionid);
die;
}
-
+
if ($beforeid > 0){
$beforemod = $DB->get_record('course_modules', array('id'=>$beforeid));
} else {
}
rebuild_course_cache($course->id);
break;
-
- case 'course':
+
+ case 'course':
switch($field) {
case 'marker':
$newcourse = new object;
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);
break;
}
-?>
\ No newline at end of file
+?>
--- /dev/null
+<?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));
+ }
+}
}
-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
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),
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
//add back to resources group
this.addToGroup('resources');
-}
+}
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)) {
}
-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);
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;
}
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);
}
}
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() {
}
-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) {
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--;
//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);
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
} 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) {
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.
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, '');
var moveLeft = false;
var moveRight = false;
var updateButton = null;
-
+
// for RTL support
var isrtl = (document.getElementsByTagName("html")[0].dir=="rtl");
commandContainer.appendChild(button);
this.groupButton = button;
}
-}
+}
resource_class.prototype.indent_left = function() {
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);