diff --git a/mod/feedback/ajax.php b/mod/feedback/ajax.php new file mode 100644 index 00000000000..a562690d194 --- /dev/null +++ b/mod/feedback/ajax.php @@ -0,0 +1,59 @@ +. + +/** + * Process ajax requests + * + * @copyright Andreas Grabs + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License + * @package feedback + */ + +if (!defined('AJAX_SCRIPT')) { + define('AJAX_SCRIPT', true); +} + +require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); +require_once('lib.php'); + +$id = required_param('id', PARAM_INT); +$action = optional_param('action', '', PARAM_ALPHA); +$sesskey = optional_param('sesskey', false, PARAM_TEXT); +$itemorder = optional_param('itemorder', false, PARAM_SEQUENCE); + +$cm = get_coursemodule_from_id('feedback', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); +$feedback = $DB->get_record('feedback', array('id'=>$cm->instance), '*', MUST_EXIST); + +require_sesskey(); + +$context = context_module::instance($cm->id); +require_login($course, true, $cm); +require_capability('mod/feedback:edititems', $context); + +$return = false; + +switch ($action) { + case 'saveitemorder': + $itemlist = explode(',', trim($itemorder, ',')); + if (count($itemlist) > 0) { + $return = feedback_ajax_saveitemorder($itemlist, $feedback); + } + break; +} + +echo json_encode($return); +die; diff --git a/mod/feedback/edit.php b/mod/feedback/edit.php index b928c11e084..6a96292f26b 100644 --- a/mod/feedback/edit.php +++ b/mod/feedback/edit.php @@ -22,8 +22,8 @@ * @package feedback */ -require_once("../../config.php"); -require_once("lib.php"); +require_once('../../config.php'); +require_once('lib.php'); require_once('edit_form.php'); feedback_init_feedback_session(); @@ -49,11 +49,11 @@ if (! $cm = get_coursemodule_from_id('feedback', $id)) { print_error('invalidcoursemodule'); } -if (! $course = $DB->get_record("course", array("id"=>$cm->course))) { +if (! $course = $DB->get_record('course', array('id'=>$cm->course))) { print_error('coursemisconf'); } -if (! $feedback = $DB->get_record("feedback", array("id"=>$cm->instance))) { +if (! $feedback = $DB->get_record('feedback', array('id'=>$cm->instance))) { print_error('invalidcoursemodule'); } @@ -63,7 +63,7 @@ require_login($course, true, $cm); require_capability('mod/feedback:edititems', $context); -//move up/down items +//Move up/down items if ($moveupitem) { $item = $DB->get_record('feedback_item', array('id'=>$moveupitem)); feedback_moveup_item($item); @@ -73,7 +73,7 @@ if ($movedownitem) { feedback_movedown_item($item); } -//moving of items +//Moving of items if ($movehere && isset($SESSION->feedback->moving->movingitem)) { $item = $DB->get_record('feedback_item', array('id'=>$SESSION->feedback->moving->movingitem)); feedback_move_item($item, intval($movehere)); @@ -94,14 +94,14 @@ if ($switchitemrequired) { exit; } -//the create_template-form +//The create_template-form $create_template_form = new feedback_edit_create_template_form(); $create_template_form->set_feedbackdata(array('context'=>$context, 'course'=>$course)); $create_template_form->set_form_elements(); $create_template_form->set_data(array('id'=>$id, 'do_show'=>'templates')); $create_template_formdata = $create_template_form->get_data(); if (isset($create_template_formdata->savetemplate) && $create_template_formdata->savetemplate == 1) { - //check the capabilities to create templates + //Check the capabilities to create templates. if (!has_capability('mod/feedback:createprivatetemplate', $context) AND !has_capability('mod/feedback:createpublictemplate', $context)) { print_error('cannotsavetempl', 'feedback'); @@ -109,7 +109,7 @@ if (isset($create_template_formdata->savetemplate) && $create_template_formdata- if (trim($create_template_formdata->templatename) == '') { $savereturn = 'notsaved_name'; } else { - //if the feedback is located on the frontpage then templates can be public + //If the feedback is located on the frontpage then templates can be public. if (has_capability('mod/feedback:createpublictemplate', get_system_context())) { $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0; } else { @@ -125,7 +125,7 @@ if (isset($create_template_formdata->savetemplate) && $create_template_formdata- } } -//get the feedbackitems +//Get the feedbackitems $lastposition = 0; $feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id), 'position'); if (is_array($feedbackitems)) { @@ -140,36 +140,48 @@ if (is_array($feedbackitems)) { $lastposition++; -//the add_item-form +//The add_item-form $add_item_form = new feedback_edit_add_question_form('edit_item.php'); $add_item_form->set_data(array('cmid'=>$id, 'position'=>$lastposition)); -//the use_template-form +//The use_template-form $use_template_form = new feedback_edit_use_template_form('use_templ.php'); $use_template_form->set_feedbackdata(array('course' => $course)); $use_template_form->set_form_elements(); $use_template_form->set_data(array('id'=>$id)); -/// Print the page header -$strfeedbacks = get_string("modulenameplural", "feedback"); -$strfeedback = get_string("modulename", "feedback"); +//Print the page header. +$strfeedbacks = get_string('modulenameplural', 'feedback'); +$strfeedback = get_string('modulename', 'feedback'); $PAGE->set_url('/mod/feedback/edit.php', array('id'=>$cm->id, 'do_show'=>$do_show)); $PAGE->set_heading(format_string($course->fullname)); $PAGE->set_title(format_string($feedback->name)); + +//Adding the javascript module for the items dragdrop. +if ($do_show == 'edit' and $CFG->enableajax) { + $PAGE->requires->strings_for_js(array( + 'pluginname', + 'move_item', + 'position', + ), 'feedback'); + $PAGE->requires->yui_module('moodle-mod_feedback-dragdrop', 'M.mod_feedback.init_dragdrop', + array(array('cmid' => $cm->id))); +} + echo $OUTPUT->header(); /// print the tabs require('tabs.php'); -/// Print the main part of the page +/// Print the main part of the page. /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// $savereturn=isset($savereturn)?$savereturn:''; -//print the messages +//Print the messages. if ($savereturn == 'notsaved_name') { echo '

'. get_string('name_required', 'feedback'). @@ -189,7 +201,7 @@ if ($savereturn == 'failed') { } /////////////////////////////////////////////////////////////////////////// -///print the template-section +///Print the template-section. /////////////////////////////////////////////////////////////////////////// if ($do_show == 'templates') { echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide'); @@ -218,7 +230,7 @@ if ($do_show == 'templates') { echo $OUTPUT->box_end(); } /////////////////////////////////////////////////////////////////////////// -///print the Item-Edit-section +///Print the Item-Edit-section. /////////////////////////////////////////////////////////////////////////// if ($do_show == 'edit') { @@ -239,7 +251,7 @@ if ($do_show == 'edit') { echo $OUTPUT->heading($anker); } - //check, if there exists required-elements + //Check, if there exists required-elements. $params = array('feedback' => $feedback->id, 'required' => 1); $countreq = $DB->count_records('feedback_item', $params); if ($countreq > 0) { @@ -248,12 +260,12 @@ if ($do_show == 'edit') { echo ''; } - //use list instead a table + //Use list instead a table echo $OUTPUT->box_start('feedback_items'); if (isset($SESSION->feedback->moving) AND $SESSION->feedback->moving->shouldmoving == 1) { $moveposition = 1; $movehereurl = new moodle_url($url, array('movehere'=>$moveposition)); - //only shown if shouldmoving = 1 + //Only shown if shouldmoving = 1 echo $OUTPUT->box_start('feedback_item_box_'.$align.' clipboard'); $buttonlink = $movehereurl->out(); $strbutton = get_string('move_here', 'feedback'); @@ -263,35 +275,41 @@ if ($do_show == 'edit') { '; echo $OUTPUT->box_end(); } - //print the inserted items + //Print the inserted items $itempos = 0; + echo '

'; //The container for the dragging area + echo ''; + echo '
'; } else { echo $OUTPUT->box(get_string('no_items_available_yet', 'feedback'), 'generalbox boxaligncenter'); diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 598e9e03140..bc896a460eb 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -3122,3 +3122,24 @@ function feedback_page_type_list($pagetype, $parentcontext, $currentcontext) { $module_pagetype = array('mod-feedback-*'=>get_string('page-mod-feedback-x', 'feedback')); return $module_pagetype; } + +/** + * Move save the items of the given $feedback in the order of $itemlist. + * @param string $itemlist a comma separated list with item ids + * @param stdClass $feedback + * @return bool true if success + */ +function feedback_ajax_saveitemorder($itemlist, $feedback) { + global $DB; + + $result = true; + $position = 0; + foreach ($itemlist as $itemid) { + $position++; + $result = $result && $DB->set_field('feedback_item', + 'position', + $position, + array('id'=>$itemid, 'feedback'=>$feedback->id)); + } + return $result; +} diff --git a/mod/feedback/styles.css b/mod/feedback/styles.css index c59a73c49e2..68cb7dd14eb 100644 --- a/mod/feedback/styles.css +++ b/mod/feedback/styles.css @@ -82,3 +82,17 @@ hr.feedback_pagebreak { background-color:#aaaaaa; border:0px; } + +.drag_target_active { + opacity: .25; +} + +.drag_item_active { + opacity: .5; +} + +ul#feedback_draglist { + list-style: none; + padding: 0; + margin: 0; +} diff --git a/mod/feedback/yui/dragdrop/dragdrop.js b/mod/feedback/yui/dragdrop/dragdrop.js new file mode 100644 index 00000000000..32deb763bc5 --- /dev/null +++ b/mod/feedback/yui/dragdrop/dragdrop.js @@ -0,0 +1,282 @@ +YUI.add('moodle-mod_feedback-dragdrop', function(Y) { + var DRAGDROPNAME = 'mod_feedback_dragdrop'; + var CSS = { + OLDMOVE : 'span.feedback_item_command_move', + OLDMOVEUP : 'span.feedback_item_command_moveup', + OLDMOVEDOWN : 'span.feedback_item_command_movedown', + DRAGAREA : '#feedback_dragarea', + DRAGITEM : 'li.feedback_itemlist', + DRAGLIST : '#feedback_dragarea ul#feedback_draglist', + POSITIONLABEL : '.feedback_item_commands.position', + ITEMBOX : '#feedback_item_box_', + DRAGHANDLE : 'itemhandle' + }; + + var DRAGDROP = function() { + DRAGDROP.superclass.constructor.apply(this, arguments); + }; + + Y.extend(DRAGDROP, M.core.dragdrop, { + + initializer : function(params) { + //Static Vars + this.cmid = params.cmid; + this.goingUp = false, lastY = 0; + + var groups = ['feedbackitem']; + + handletitle = M.util.get_string('move_item', 'feedback'); + this.mydraghandle = this.get_drag_handle(handletitle, CSS.DRAGHANDLE, 'icon'); + + //Get the list of li's in the lists and add the drag handle. + basenode = Y.Node.one(CSS.DRAGLIST); + listitems = basenode.all(CSS.DRAGITEM).each(function(v) { + item_id = this.get_node_id(v.get('id')); //Get the id of the feedback item. + item_box = Y.Node.one(CSS.ITEMBOX + item_id); //Get the current item box so we can add the drag handle. + v.insert(this.mydraghandle.cloneNode(true), item_box); //Insert the new handle into the item box. + }, this); + + //We use a delegate to make all items draggable + var del = new Y.DD.Delegate({ + container: CSS.DRAGLIST, + nodes: CSS.DRAGITEM, + target: { + padding: '0 0 0 20' + }, + handles: ['.' + CSS.DRAGHANDLE], + dragConfig: {groups: groups} + }); + + //Add plugins to the delegate + del.dd.plug(Y.Plugin.DDProxy, { + // Don't move the node at the end of the drag + moveOnEnd: false, + cloneNode: true + }); + del.dd.plug(Y.Plugin.DDConstrained, { + // Keep it inside the .course-content + constrain: CSS.DRAGAREA + }); + del.dd.plug(Y.Plugin.DDWinScroll); + + //Listen for all drop:over events + del.on('drop:over', this.drop_over_handler, this); + //Listen for all drag:drag events + del.on('drag:drag', this.drag_drag_handler, this); + //Listen for all drag:start events + del.on('drag:start', this.drag_start_handler, this); + //Listen for a drag:end events + del.on('drag:end', this.drag_end_handler, this); + //Listen for all drag:drophit events + del.on('drag:drophit', this.drag_drophit_handler, this); + //Listen for all drag:dropmiss events + del.on('drag:dropmiss', this.drag_dropmiss_handler, this); + + // Remove all legacy move icons. + Y.all(CSS.OLDMOVEUP).remove(); + Y.all(CSS.OLDMOVEDOWN).remove(); + Y.all(CSS.OLDMOVE).remove(); + + //Create targets for drop. + var droparea = Y.Node.one(CSS.DRAGLIST); + var tar = new Y.DD.Drop({ + groups: groups, + node: droparea + }); + + }, + + /** + * Handles the drop:over event. + * + * @param e the event + * @return void + */ + drop_over_handler : function(e) { + //Get a reference to our drag and drop nodes + var drag = e.drag.get('node'), + drop = e.drop.get('node'); + + //Are we dropping on an li node? + if (drop.get('tagName').toLowerCase() === 'li') { + //Are we not going up? + if (!this.goingUp) { + drop = drop.get('nextSibling'); + } + //Add the node to this list + e.drop.get('node').get('parentNode').insertBefore(drag, drop); + //Resize this nodes shim, so we can drop on it later. + e.drop.sizeShim(); + } + }, + + /** + * Handles the drag:drag event. + * + * @param e the event + * @return void + */ + drag_drag_handler : function(e) { + //Get the last y point + var y = e.target.lastXY[1]; + //Is it greater than the lastY var? + if (y < this.lastY) { + //We are going up + this.goingUp = true; + } else { + //We are going down. + this.goingUp = false; + } + //Cache for next check + this.lastY = y; + }, + + /** + * Handles the drag:start event. + * + * @param e the event + * @return void + */ + drag_start_handler : function(e) { + //Get our drag object + var drag = e.target; + + //Set some styles here + drag.get('node').addClass('drag_target_active'); + drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML')); + drag.get('dragNode').addClass('drag_item_active'); + drag.get('dragNode').setStyles({ + borderColor: drag.get('node').getStyle('borderColor'), + backgroundColor: drag.get('node').getStyle('backgroundColor') + }); + }, + + /** + * Handles the drag:end event. + * + * @param e the event + * @return void + */ + drag_end_handler : function(e) { + var drag = e.target; + //Put our styles back + drag.get('node').removeClass('drag_target_active'); + }, + + /** + * Handles the drag:drophit event. + * + * @param e the event + * @return void + */ + drag_drophit_handler : function(e) { + var drop = e.drop.get('node'), + drag = e.drag.get('node'); + dragnode = Y.one(drag); + //If we are not on an li, we must have been dropped on a ul. + if (drop.get('tagName').toLowerCase() !== 'li') { + if (!drop.contains(drag)) { + drop.appendChild(drag); + } + myElements = ''; + counter = 1; + drop.get('children').each(function(v) { + poslabeltext = '(' + M.util.get_string('position', 'feedback') + ':' + counter + ')'; + poslabel = v.one(CSS.POSITIONLABEL); + poslabel.setHTML(poslabeltext); + myElements = myElements + ',' + this.get_node_id(v.get('id')); + counter++; + }, this); + var spinner = M.util.add_spinner(Y, dragnode); + this.save_item_order(this.cmid, myElements, spinner); + } + }, + + /** + * Handles the drag:dropmiss event. + * + * @param e the event + * @return void + */ + drag_dropmiss_handler : function(e) { + var msg = { + name : 'dropmiss', + message : 'dropmiss' + }; + return new M.core.exception(msg); + }, + + /** + * Save the new item order. + * + * @param cmid the coursemodule id + * @param itemorder A comma separated list with item ids + * @param spinner The spinner icon shown while saving + * @return void + */ + save_item_order : function(cmid, itemorder, spinner) { + + Y.io(M.cfg.wwwroot + '/mod/feedback/ajax.php', { + //The needed paramaters + data: {action: 'saveitemorder', + id: cmid, + itemorder: itemorder, + sesskey: M.cfg.sesskey + }, + + timeout: 5000, //5 seconds for timeout I think it is enough. + + //Define the events. + on: { + start : function(transactionid) { + spinner.show(); + }, + success : function(transactionid, xhr) { + var response = xhr.responseText; + var ergebnis = Y.JSON.parse(response); + window.setTimeout(function(e) { + spinner.hide(); + }, 250); + }, + failure : function(transactionid, xhr) { + var msg = { + name : xhr.status+' '+xhr.statusText, + message : xhr.responseText + }; + return new M.core.exception(msg); + //~ this.ajax_failure(xhr); + spinner.hide(); + } + }, + context:this + }); + }, + + /** + * Returns the numeric id from the dom id of an item. + * + * @param id The dom id, f.g.: feedback_item_22 + * @return int + */ + get_node_id : function(id) { + return Number(id.replace(/feedback_item_/i, '')); + } + + }, { + NAME : DRAGDROPNAME, + ATTRS : { + cmid : { + value : 0 + } + } + + }); + + M.mod_feedback = M.mod_feedback || {}; + M.mod_feedback.init_dragdrop = function(params) { + return new DRAGDROP(params); + } + +}, '@VERSION@', { + requires:['io', 'json-parse', 'dd-constrain', 'dd-proxy', 'dd-drop', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification'] +});