mirror of
https://github.com/moodle/moodle.git
synced 2025-04-06 00:42:44 +02:00
Merge branch 'MDL-81745-main' of https://github.com/roland04/moodle
This commit is contained in:
commit
4d530b5048
5
.upgradenotes/MDL-81745-2024060507174301.yml
Normal file
5
.upgradenotes/MDL-81745-2024060507174301.yml
Normal file
@ -0,0 +1,5 @@
|
||||
issueNumber: MDL-81745
|
||||
notes:
|
||||
mod_feedback:
|
||||
- message: Added new `mod_feedback_questions_reorder` external function
|
||||
type: improved
|
@ -1,59 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Process ajax requests
|
||||
*
|
||||
* @copyright Andreas Grabs
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package mod_feedback
|
||||
*/
|
||||
|
||||
if (!defined('AJAX_SCRIPT')) {
|
||||
define('AJAX_SCRIPT', true);
|
||||
}
|
||||
|
||||
require(__DIR__.'/../../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;
|
2
mod/feedback/amd/build/edit.min.js
vendored
2
mod/feedback/amd/build/edit.min.js
vendored
@ -1,3 +1,3 @@
|
||||
define("mod_feedback/edit",["jquery","core/ajax","core/str","core/notification"],(function($,ajax,str,notification){var manager={deleteItem:function(e){e.preventDefault();var targetUrl=$(e.currentTarget).attr("href");str.get_strings([{key:"confirmation",component:"admin"},{key:"confirmdeleteitem",component:"mod_feedback"},{key:"yes",component:"moodle"},{key:"no",component:"moodle"}]).then((function(s){notification.confirm(s[0],s[1],s[2],s[3],(function(){window.location=targetUrl}))})).catch()},setup:function(){$("body").delegate('[data-action="delete"]',"click",manager.deleteItem)}};return{setup:manager.setup}}));
|
||||
define("mod_feedback/edit",["exports","core/loadingicon","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/toast","mod_feedback/local/repository"],(function(_exports,_loadingicon,_notification,_pending,_prefetch,_sortable_list,_str,_toast,_repository){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list);const Selectors_deleteQuestionButton='[data-action="delete"]',Selectors_sortableListRegion='[data-region="questions-sortable-list"]',Selectors_sortableElement='[data-region="questions-sortable-list"] .feedback_itemlist[id]',Selectors_sortableElementTitle='[data-region="item-title"]',getItemOrder=element=>{const sortableList=element.closest(Selectors_sortableListRegion);let itemOrder=[];return sortableList.querySelectorAll(Selectors_sortableElement).forEach((item=>{var id;itemOrder.push((id=item.id,Number(id.replace(/^.*feedback_item_/i,""))))})),itemOrder.toString()};let initialized=!1,moduleId=null;_exports.init=cmId=>{if(moduleId=cmId,initialized)return;(0,_prefetch.prefetchStrings)("core",["yes","no"]),(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_feedback",["confirmdeleteitem","questionmoved"]),document.addEventListener("click",(async event=>{const deleteButton=event.target.closest(Selectors_deleteQuestionButton);if(deleteButton){event.preventDefault();const confirmationStrings=await(0,_str.getStrings)([{key:"confirmation",component:"admin"},{key:"confirmdeleteitem",component:"mod_feedback"},{key:"yes",component:"core"},{key:"no",component:"core"}]);_notification.default.confirm(...confirmationStrings,(()=>{window.location=deleteButton.getAttribute("href")}))}else;}));new _sortable_list.default(document.querySelector(Selectors_sortableListRegion)).getElementName=element=>{var _element$0$querySelec;return Promise.resolve(null===(_element$0$querySelec=element[0].querySelector(Selectors_sortableElementTitle))||void 0===_element$0$querySelec?void 0:_element$0$querySelec.textContent)},document.addEventListener(_sortable_list.default.EVENTS.elementDrop,(event=>{if(!event.detail.positionChanged)return;const pendingPromise=new _pending.default("mod_feedback/questions:reorder"),itemOrder=getItemOrder(event.detail.element[0]);(0,_loadingicon.addIconToContainerRemoveOnCompletion)(event.detail.element[0],pendingPromise),(0,_repository.reorderQuestions)(moduleId,itemOrder).then((()=>(0,_str.getString)("questionmoved","mod_feedback"))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})),initialized=!0}}));
|
||||
|
||||
//# sourceMappingURL=edit.min.js.map
|
File diff suppressed because one or more lines are too long
10
mod/feedback/amd/build/local/repository.min.js
vendored
Normal file
10
mod/feedback/amd/build/local/repository.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("mod_feedback/local/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
|
||||
/**
|
||||
* Module to handle feedback AJAX requests
|
||||
*
|
||||
* @module mod_feedback/local/repository
|
||||
* @copyright 2024 Mikel Martín <mikel@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reorderQuestions=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.reorderQuestions=(moduleId,itemOrder)=>{const request={methodname:"mod_feedback_questions_reorder",args:{cmid:moduleId,itemorder:itemOrder}};return _ajax.default.call([request])[0]}}));
|
||||
|
||||
//# sourceMappingURL=repository.min.js.map
|
1
mod/feedback/amd/build/local/repository.min.js.map
Normal file
1
mod/feedback/amd/build/local/repository.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"repository.min.js","sources":["../../src/local/repository.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle feedback AJAX requests\n *\n * @module mod_feedback/local/repository\n * @copyright 2024 Mikel Martín <mikel@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Reorder questions for a given feedback course module\n *\n * @param {Number} moduleId\n * @param {String} itemOrder\n * @return {Promise}\n */\nexport const reorderQuestions = (moduleId, itemOrder) => {\n const request = {\n methodname: 'mod_feedback_questions_reorder',\n args: {cmid: moduleId, itemorder: itemOrder}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["moduleId","itemOrder","request","methodname","args","cmid","itemorder","Ajax","call"],"mappings":";;;;;;;4KAgCgC,CAACA,SAAUC,mBACjCC,QAAU,CACZC,WAAY,iCACZC,KAAM,CAACC,KAAML,SAAUM,UAAWL,mBAG/BM,cAAKC,KAAK,CAACN,UAAU"}
|
@ -18,48 +18,118 @@
|
||||
*
|
||||
* @module mod_feedback/edit
|
||||
* @copyright 2016 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax', 'core/str', 'core/notification'],
|
||||
function($, ajax, str, notification) {
|
||||
var manager = {
|
||||
deleteItem: function(e) {
|
||||
e.preventDefault();
|
||||
var targetUrl = $(e.currentTarget).attr('href');
|
||||
|
||||
str.get_strings([
|
||||
{
|
||||
key: 'confirmation',
|
||||
component: 'admin'
|
||||
},
|
||||
{
|
||||
key: 'confirmdeleteitem',
|
||||
component: 'mod_feedback'
|
||||
},
|
||||
{
|
||||
key: 'yes',
|
||||
component: 'moodle'
|
||||
},
|
||||
{
|
||||
key: 'no',
|
||||
component: 'moodle'
|
||||
}
|
||||
])
|
||||
.then(function(s) {
|
||||
notification.confirm(s[0], s[1], s[2], s[3], function() {
|
||||
window.location = targetUrl;
|
||||
});
|
||||
"use strict";
|
||||
|
||||
return;
|
||||
})
|
||||
.catch();
|
||||
},
|
||||
import {addIconToContainerRemoveOnCompletion} from 'core/loadingicon';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import SortableList from 'core/sortable_list';
|
||||
import {getString, getStrings} from 'core/str';
|
||||
import {add as addToast} from 'core/toast';
|
||||
import {reorderQuestions} from 'mod_feedback/local/repository';
|
||||
|
||||
setup: function() {
|
||||
$('body').delegate('[data-action="delete"]', 'click', manager.deleteItem);
|
||||
const Selectors = {
|
||||
deleteQuestionButton: '[data-action="delete"]',
|
||||
sortableListRegion: '[data-region="questions-sortable-list"]',
|
||||
sortableElement: '[data-region="questions-sortable-list"] .feedback_itemlist[id]',
|
||||
sortableElementTitle: '[data-region="item-title"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Feedback question item id from the DOM id of an item.
|
||||
*
|
||||
* @param {String} id The dom id, f.g.: feedback_item_22
|
||||
* @return int
|
||||
*/
|
||||
const getItemId = (id) => {
|
||||
return Number(id.replace(/^.*feedback_item_/i, ''));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the order of the items in the sortable list.
|
||||
*
|
||||
* @param {Element} element The element to get the order from.
|
||||
* @return string
|
||||
*/
|
||||
const getItemOrder = (element) => {
|
||||
const sortableList = element.closest(Selectors.sortableListRegion);
|
||||
let itemOrder = [];
|
||||
sortableList.querySelectorAll(Selectors.sortableElement).forEach((item) => {
|
||||
itemOrder.push(getItemId(item.id));
|
||||
});
|
||||
return itemOrder.toString();
|
||||
};
|
||||
|
||||
let initialized = false;
|
||||
let moduleId = null;
|
||||
|
||||
/**
|
||||
* Initialise editor and all it's modules
|
||||
*
|
||||
* @param {Integer} cmId
|
||||
*/
|
||||
export const init = (cmId) => {
|
||||
|
||||
moduleId = cmId;
|
||||
|
||||
// Ensure we only add our listeners once (can be called multiple times).
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
prefetchStrings('core', [
|
||||
'yes',
|
||||
'no',
|
||||
]);
|
||||
prefetchStrings('admin', [
|
||||
'confirmation',
|
||||
]);
|
||||
prefetchStrings('mod_feedback', [
|
||||
'confirmdeleteitem',
|
||||
'questionmoved',
|
||||
]);
|
||||
|
||||
// Add event listeners.
|
||||
document.addEventListener('click', async event => {
|
||||
|
||||
// Delete question.
|
||||
const deleteButton = event.target.closest(Selectors.deleteQuestionButton);
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
const confirmationStrings = await getStrings([
|
||||
{key: 'confirmation', component: 'admin'},
|
||||
{key: 'confirmdeleteitem', component: 'mod_feedback'},
|
||||
{key: 'yes', component: 'core'},
|
||||
{key: 'no', component: 'core'},
|
||||
]);
|
||||
Notification.confirm(...confirmationStrings, () => {
|
||||
window.location = deleteButton.getAttribute('href');
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
setup: manager.setup
|
||||
};
|
||||
});
|
||||
// Initialize sortable list to handle active conditions moving.
|
||||
const sortableList = new SortableList(document.querySelector(Selectors.sortableListRegion));
|
||||
sortableList.getElementName = element => Promise.resolve(element[0].querySelector(Selectors.sortableElementTitle)?.textContent);
|
||||
|
||||
document.addEventListener(SortableList.EVENTS.elementDrop, event => {
|
||||
if (!event.detail.positionChanged) {
|
||||
return;
|
||||
}
|
||||
const pendingPromise = new Pending('mod_feedback/questions:reorder');
|
||||
const itemOrder = getItemOrder(event.detail.element[0]);
|
||||
addIconToContainerRemoveOnCompletion(event.detail.element[0], pendingPromise);
|
||||
reorderQuestions(moduleId, itemOrder)
|
||||
.then(() => getString('questionmoved', 'mod_feedback'))
|
||||
.then(addToast)
|
||||
.then(() => pendingPromise.resolve())
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
};
|
||||
|
40
mod/feedback/amd/src/local/repository.js
Normal file
40
mod/feedback/amd/src/local/repository.js
Normal file
@ -0,0 +1,40 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to handle feedback AJAX requests
|
||||
*
|
||||
* @module mod_feedback/local/repository
|
||||
* @copyright 2024 Mikel Martín <mikel@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
|
||||
/**
|
||||
* Reorder questions for a given feedback course module
|
||||
*
|
||||
* @param {Number} moduleId
|
||||
* @param {String} itemOrder
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const reorderQuestions = (moduleId, itemOrder) => {
|
||||
const request = {
|
||||
methodname: 'mod_feedback_questions_reorder',
|
||||
args: {cmid: moduleId, itemorder: itemOrder}
|
||||
};
|
||||
|
||||
return Ajax.call([request])[0];
|
||||
};
|
@ -68,8 +68,10 @@ class mod_feedback_complete_form extends moodleform {
|
||||
$this->structure = $structure;
|
||||
$this->gopage = isset($customdata['gopage']) ? $customdata['gopage'] : 0;
|
||||
$isanonymous = $this->structure->is_anonymous() ? ' ianonymous' : '';
|
||||
parent::__construct(null, $customdata, 'POST', '',
|
||||
array('id' => $formid, 'class' => 'feedback_form' . $isanonymous), true);
|
||||
parent::__construct(
|
||||
customdata: $customdata,
|
||||
attributes: ['id' => $formid, 'class' => 'feedback_form' . $isanonymous],
|
||||
);
|
||||
$this->set_display_vertical();
|
||||
}
|
||||
|
||||
@ -167,10 +169,12 @@ class mod_feedback_complete_form extends moodleform {
|
||||
* This will add all items to the form, including pagebreaks as horizontal rules.
|
||||
*/
|
||||
protected function definition_preview() {
|
||||
$this->_form->addElement('html', html_writer::start_div('', ['data-region' => 'questions-sortable-list']));
|
||||
foreach ($this->structure->get_items() as $feedbackitem) {
|
||||
$itemobj = feedback_get_item_class($feedbackitem->typ);
|
||||
$itemobj->complete_form_element($feedbackitem, $this);
|
||||
}
|
||||
$this->_form->addElement('html', html_writer::end_div());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,6 +323,7 @@ class mod_feedback_complete_form extends moodleform {
|
||||
$attributes = $element->getAttributes();
|
||||
$class = !empty($attributes['class']) ? ' ' . $attributes['class'] : '';
|
||||
$attributes['class'] = $this->get_suggested_class($item) . $class;
|
||||
|
||||
$element->setAttributes($attributes);
|
||||
|
||||
// Add required rule.
|
||||
@ -468,13 +473,13 @@ class mod_feedback_complete_form extends moodleform {
|
||||
$menu->add($action);
|
||||
}
|
||||
$editmenu = $OUTPUT->render($menu);
|
||||
$draghandle = $OUTPUT->render_from_template('core/drag_handle',
|
||||
['movetitle' => get_string('move_item', 'mod_feedback')]);
|
||||
|
||||
$name = $element->getLabel();
|
||||
|
||||
$name = html_writer::span('', 'itemdd', array('id' => 'feedback_item_box_' . $item->id)) .
|
||||
html_writer::span($name, 'itemname') .
|
||||
html_writer::span($editmenu, 'itemactions');
|
||||
$element->setLabel(html_writer::span($name, 'itemtitle', ['class' => 'mx-5']));
|
||||
$name = html_writer::div($draghandle, 'itemhandle', ['data-drag-type' => 'move']) .
|
||||
html_writer::div($element->getLabel(), 'itemname', ['data-region' => 'item-title']) .
|
||||
html_writer::div($editmenu, 'itemactions');
|
||||
$element->setLabel(html_writer::div($name, 'itemtitle d-flex'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -580,9 +585,5 @@ class mod_feedback_complete_form extends moodleform {
|
||||
}
|
||||
|
||||
$this->_form->display();
|
||||
|
||||
if ($this->mode == self::MODE_EDIT) {
|
||||
$PAGE->requires->js_call_amd('mod_feedback/edit', 'setup');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
89
mod/feedback/classes/external/questions/reorder.php
vendored
Normal file
89
mod/feedback/classes/external/questions/reorder.php
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_feedback\external\questions;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_value;
|
||||
use core_external\external_function_parameters;
|
||||
use context_module;
|
||||
|
||||
/**
|
||||
* External method for reordering feedback questions.
|
||||
*
|
||||
* @package mod_feedback
|
||||
* @copyright 2024 Mikel Martín <mikel@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class reorder extends external_api {
|
||||
|
||||
/**
|
||||
* Describes the parameters for reorder.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters(
|
||||
[
|
||||
'cmid' => new external_value(PARAM_INT, 'Feedback course module id'),
|
||||
'itemorder' => new external_value(PARAM_SEQUENCE, 'Feedback order by sequence of question item ids'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* External function to reorder feedback questions.
|
||||
*
|
||||
* @param int $cmid
|
||||
* @param string $itemorder
|
||||
* @return bool
|
||||
*/
|
||||
public static function execute(int $cmid, string $itemorder): bool {
|
||||
global $DB;
|
||||
[
|
||||
'cmid' => $cmid,
|
||||
'itemorder' => $itemorder,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'cmid' => $cmid,
|
||||
'itemorder' => $itemorder,
|
||||
]);
|
||||
|
||||
$cm = get_coursemodule_from_id('feedback', $cmid, 0, false, MUST_EXIST);
|
||||
$feedback = $DB->get_record('feedback', ['id' => $cm->instance], '*', MUST_EXIST);
|
||||
$context = context_module::instance($cm->id);
|
||||
|
||||
self::validate_context($context);
|
||||
require_capability('mod/feedback:edititems', $context);
|
||||
|
||||
$itemlist = explode(',', trim($itemorder, ',')) ?: [];
|
||||
if (count($itemlist) > 0) {
|
||||
return feedback_ajax_saveitemorder($itemlist, $feedback);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the data returned from the external function.
|
||||
*
|
||||
* @return external_value
|
||||
*/
|
||||
public static function execute_returns(): external_value {
|
||||
return new external_value(PARAM_BOOL, '', VALUE_REQUIRED);
|
||||
}
|
||||
}
|
@ -141,4 +141,11 @@ $functions = array(
|
||||
'capabilities' => 'mod/feedback:view',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
'mod_feedback_questions_reorder' => [
|
||||
'classname' => 'mod_feedback\external\questions\reorder',
|
||||
'description' => 'Saves the new order of the questions in the feedback.',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
'capabilities' => 'mod/feedback:edititems',
|
||||
],
|
||||
);
|
||||
|
@ -86,20 +86,7 @@ $PAGE->activityheader->set_attrs([
|
||||
'description' => ''
|
||||
]);
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
//Adding the javascript module for the items dragdrop.
|
||||
if (count($feedbackitems) > 1) {
|
||||
$PAGE->requires->strings_for_js([
|
||||
'pluginname',
|
||||
'move_item',
|
||||
'position',
|
||||
], 'feedback');
|
||||
$PAGE->requires->yui_module(
|
||||
'moodle-mod_feedback-dragdrop',
|
||||
'M.mod_feedback.init_dragdrop',
|
||||
[['cmid' => $cm->id]]
|
||||
);
|
||||
}
|
||||
$PAGE->requires->js_call_amd('mod_feedback/edit', 'init', [$cm->id]);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('edit_items', 'mod_feedback'), 3);
|
||||
@ -109,8 +96,6 @@ $renderer = $PAGE->get_renderer('mod_feedback');
|
||||
echo $renderer->main_action_bar($actionbar);
|
||||
$form = new mod_feedback_complete_form(mod_feedback_complete_form::MODE_EDIT,
|
||||
$feedbackstructure, 'feedback_edit_form');
|
||||
echo '<div id="feedback_dragarea">'; // The container for the dragging area.
|
||||
$form->display();
|
||||
echo '</div>';
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
@ -246,6 +246,7 @@ $string['privacy:metadata:value:value'] = 'The chosen answer.';
|
||||
$string['privacy:metadata:valuetmp'] = 'A record of the answer to a question in a submission in progress.';
|
||||
$string['question'] = 'Question';
|
||||
$string['questionandsubmission'] = 'Question and submission settings';
|
||||
$string['questionmoved'] = 'Question moved';
|
||||
$string['questions'] = 'Questions';
|
||||
$string['questionslimited'] = 'Showing only {$a} first questions, view individual answers or download table data to view all.';
|
||||
$string['radio'] = 'Multiple choice - single answer';
|
||||
|
@ -2867,7 +2867,7 @@ function feedback_page_type_list($pagetype, $parentcontext, $currentcontext) {
|
||||
|
||||
/**
|
||||
* Move save the items of the given $feedback in the order of $itemlist.
|
||||
* @param string $itemlist a comma separated list with item ids
|
||||
* @param array $itemlist a list with item ids
|
||||
* @param stdClass $feedback
|
||||
* @return bool true if success
|
||||
*/
|
||||
|
@ -237,7 +237,8 @@ class behat_mod_feedback extends behat_base {
|
||||
public static function get_partial_named_selectors(): array {
|
||||
return [
|
||||
new behat_component_named_selector('Question', [
|
||||
".//*[starts-with(@id, 'fitem_feedback_item_')][.//*[contains(text(), %locator%)]]",
|
||||
".//*[starts-with(@id, 'fitem_feedback_item_') or starts-with(@id, 'fgroup_feedback_item_')]" .
|
||||
"[.//*[contains(text(), %locator%)]]",
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ Feature: Testing multichoice questions in feedback
|
||||
# Change the settings so we don't analyse empty submits
|
||||
And I am on the "Learning experience" "feedback activity" page
|
||||
And I navigate to "Questions" in current page administration
|
||||
And I open the action menu in "//div[contains(@class, 'feedback_itemlist') and contains(.,'multichoice1')]" "xpath_element"
|
||||
And I click on "Edit" "link" in the "this is a multiple choice 1" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And I set the field "Omit empty submits in analysis" to "Yes"
|
||||
And I press "Save changes to question"
|
||||
@ -290,7 +290,7 @@ Feature: Testing multichoice questions in feedback
|
||||
# Change the settings so we don't analyse empty submits
|
||||
And I am on the "Learning experience" "feedback activity" page
|
||||
And I navigate to "Questions" in current page administration
|
||||
And I open the action menu in "//div[contains(@class, 'feedback_itemlist') and contains(.,'multichoice1')]" "xpath_element"
|
||||
And I click on "Edit" "link" in the "this is a multiple choice 1" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And I set the field "Omit empty submits in analysis" to "Yes"
|
||||
And I press "Save changes to question"
|
||||
@ -408,7 +408,7 @@ Feature: Testing multichoice questions in feedback
|
||||
# Change the settings so we don't analyse empty submits
|
||||
And I am on the "Learning experience" "feedback activity" page
|
||||
And I navigate to "Questions" in current page administration
|
||||
And I open the action menu in "//div[contains(@class, 'feedback_itemlist') and contains(.,'multichoice1')]" "xpath_element"
|
||||
And I click on "Edit" "link" in the "this is a multiple choice 1" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And I set the field "Omit empty submits in analysis" to "Yes"
|
||||
And I press "Save changes to question"
|
||||
|
@ -17,23 +17,24 @@ Feature: Managing feedback questions
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | idnumber |
|
||||
| feedback | Learning experience course 1 | C1 | feedback1 |
|
||||
And the following "mod_feedback > question" exists:
|
||||
| activity | feedback1 |
|
||||
| name | Is it me you're looking for? |
|
||||
| label | q1 |
|
||||
|
||||
Scenario: Teacher can create a new feedback question
|
||||
Given I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
When I add a "Short text answer" question to the feedback with:
|
||||
| Question | Is it me you're looking for? |
|
||||
| Label | q1 |
|
||||
Then I should see "(q1) Is it me you're looking for?"
|
||||
| Question | I can see it in your eyes |
|
||||
| Label | q2 |
|
||||
Then I should see "(q2) I can see it in your eyes"
|
||||
|
||||
@javascript
|
||||
Scenario: Teacher can edit feedback questions
|
||||
Given I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
And I add a "Short text answer" question to the feedback with:
|
||||
| Question | Is it me you're looking for? |
|
||||
| Label | q1 |
|
||||
When I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
When I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And I set the field "Question" to "Can you see it in my eyes?"
|
||||
And I press "Save changes to question"
|
||||
@ -44,10 +45,7 @@ Feature: Managing feedback questions
|
||||
Scenario: Teacher can edit and save as new feedback questions
|
||||
Given I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
And I add a "Short text answer" question to the feedback with:
|
||||
| Question | Is it me you're looking for? |
|
||||
| Label | q1 |
|
||||
When I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
When I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And I set the field "Question" to "You can se it in my eyes?"
|
||||
And I press "Save as new question"
|
||||
@ -58,10 +56,7 @@ Feature: Managing feedback questions
|
||||
Scenario: Teacher can delete feedback questions
|
||||
Given I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
And I add a "Short text answer" question to the feedback with:
|
||||
| Question | Is it me you're looking for? |
|
||||
| Label | q1 |
|
||||
When I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
When I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Delete question" in the open action menu
|
||||
And I click on "Yes" "button" in the "Confirmation" "dialogue"
|
||||
Then I should not see "(q1) Is it me you're looking for?"
|
||||
@ -70,18 +65,29 @@ Feature: Managing feedback questions
|
||||
Scenario: Teacher can mark as required feedback questions
|
||||
Given I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
And I add a "Short text answer" question to the feedback with:
|
||||
| Question | Is it me you're looking for? |
|
||||
| Label | q1 |
|
||||
| Required | 0 |
|
||||
When I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
When I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Set as required" in the open action menu
|
||||
And I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
Then the field "Required" matches value "1"
|
||||
And I press "Cancel"
|
||||
And I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Set as not required" in the open action menu
|
||||
And I open the action menu in "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I click on "Edit" "link" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I choose "Edit question" in the open action menu
|
||||
And the field "Required" matches value "0"
|
||||
|
||||
@javascript
|
||||
Scenario: Teacher can move questions
|
||||
Given the following "mod_feedback > questions" exist:
|
||||
| activity | label | name |
|
||||
| feedback1 | q2 | I can see it in your eyes |
|
||||
| feedback1 | q3 | I can see it in your smile |
|
||||
And I am on the "Learning experience course 1" "feedback activity" page logged in as teacher
|
||||
And I click on "Edit questions" "link" in the "region-main" "region"
|
||||
When I click on "Move this question" "button" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
Then I should see "After \"(q2) I can see it in your eyes\"" in the "Move this question" "dialogue"
|
||||
And I should not see "To the top of the list" in the "Move this question" "dialogue"
|
||||
And I click on "After \"(q3) I can see it in your smile\"" "link" in the "Move this question" "dialogue"
|
||||
And I click on "Move this question" "button" in the "Is it me you're looking for?" "mod_feedback > Question"
|
||||
And I click on "To the top of the list" "link" in the "Move this question" "dialogue"
|
||||
|
75
mod/feedback/tests/external/questions/reorder_test.php
vendored
Normal file
75
mod/feedback/tests/external/questions/reorder_test.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_feedback\external\questions;
|
||||
|
||||
use core_external\external_api;
|
||||
|
||||
/**
|
||||
* Unit tests of external class for re-ordering feedback question items
|
||||
*
|
||||
* @package mod_feedback
|
||||
* @covers \mod_feedback\external\questions\reorder
|
||||
* @copyright 2024 Mikel Martín <mikel@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class reorder_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Text execute method
|
||||
*/
|
||||
public function test_execute(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course with a feedback activity and some questions.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
|
||||
$cm = get_coursemodule_from_instance('feedback', $feedback->id);
|
||||
|
||||
$feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
|
||||
$item1 = $feedbackgenerator->create_item_label($feedback);
|
||||
$item2 = $feedbackgenerator->create_item_info($feedback);
|
||||
$item3 = $feedbackgenerator->create_item_numeric($feedback);
|
||||
|
||||
// Check initial items order.
|
||||
$this->assertEquals([$item1->id, $item2->id, $item3->id], $this->get_feedback_item_order($feedback));
|
||||
|
||||
// Call the execute method to invert the items order.
|
||||
$result = reorder::execute($cm->id, "$item3->id,$item2->id,$item1->id");
|
||||
$result = external_api::clean_returnvalue(reorder::execute_returns(), $result);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Check items order is inverted.
|
||||
$this->assertEquals([$item3->id, $item2->id, $item1->id], $this->get_feedback_item_order($feedback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order of the feedback items.
|
||||
*
|
||||
* @param object $feedback The feedback activity.
|
||||
* @return array
|
||||
*/
|
||||
private function get_feedback_item_order($feedback) {
|
||||
global $DB;
|
||||
return $DB->get_fieldset_select(
|
||||
'feedback_item',
|
||||
'id',
|
||||
'feedback = :feedbackid ORDER BY position ASC',
|
||||
['feedbackid' => $feedback->id]
|
||||
);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2024100700; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2024100701; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2024100100; // Requires this Moodle version.
|
||||
$plugin->component = 'mod_feedback'; // Full name of the plugin (used for diagnostics)
|
||||
$plugin->cron = 0;
|
||||
|
265
mod/feedback/yui/dragdrop/dragdrop.js
vendored
265
mod/feedback/yui/dragdrop/dragdrop.js
vendored
@ -1,265 +0,0 @@
|
||||
YUI.add('moodle-mod_feedback-dragdrop', function(Y) {
|
||||
var DRAGDROPNAME = 'mod_feedback_dragdrop';
|
||||
var CSS = {
|
||||
DRAGAREA: '#feedback_dragarea',
|
||||
DRAGITEMCLASS: 'feedback_itemlist',
|
||||
DRAGITEM: '.row.feedback_itemlist',
|
||||
DRAGLIST: '#feedback_dragarea form',
|
||||
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'];
|
||||
|
||||
var handletitle = M.util.get_string('move_item', 'feedback');
|
||||
|
||||
//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) {
|
||||
var item_id = this.get_node_id(v.get('id')); //Get the id of the feedback item.
|
||||
var mydraghandle = this.get_drag_handle(handletitle, CSS.DRAGHANDLE, 'icon');
|
||||
v.append(mydraghandle); // 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);
|
||||
|
||||
//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.hasClass(CSS.DRAGITEMCLASS)) {
|
||||
//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 (!drop.hasClass(CSS.DRAGITEMCLASS)) {
|
||||
if (!drop.contains(drag)) {
|
||||
drop.appendChild(drag);
|
||||
}
|
||||
var childElement;
|
||||
var elementId;
|
||||
var elements = [];
|
||||
drop.all(CSS.DRAGITEM).each(function(v) {
|
||||
childElement = v.one('.felement')?.one('[id^="feedback_item_"]');
|
||||
if (childElement) {
|
||||
elementId = this.get_node_id(childElement.get('id'));
|
||||
if (elements.indexOf(elementId) == -1) {
|
||||
elements.push(elementId);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
var spinner = M.util.add_spinner(Y, dragnode);
|
||||
this.save_item_order(this.cmid, elements.toString(), spinner);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
require(['core/notification', 'core/str', 'core/toast'], function(Notification, Strings, Toast) {
|
||||
Strings.get_string('changessaved', 'core').then(function(saveString) {
|
||||
return Toast.add(saveString);
|
||||
}).catch(Notification.exception);
|
||||
});
|
||||
},
|
||||
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']
|
||||
});
|
@ -1589,12 +1589,6 @@ $popout-header-height: 4rem;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.drag_target_active {
|
||||
opacity: .25;
|
||||
}
|
||||
.drag_item_active {
|
||||
opacity: .5;
|
||||
}
|
||||
.feedback_bar_image {
|
||||
height: 10px;
|
||||
}
|
||||
@ -1611,39 +1605,44 @@ $popout-header-height: 4rem;
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
.feedback_form {
|
||||
.itemactions {
|
||||
display: inline-block;
|
||||
margin: 0 map-get($spacers, 2);
|
||||
}
|
||||
}
|
||||
.feedback-item-label {
|
||||
width: 100%;
|
||||
}
|
||||
// Feedback edit form.
|
||||
#feedback_edit_form {
|
||||
[id*=_feedback_item_].feedback_itemlist {
|
||||
[id*=_feedback_item_].feedback_itemlist,
|
||||
.feedback_itemlist.sortable-list-is-dragged {
|
||||
padding: map-get($spacers, 3);
|
||||
border: $border-width solid $border-color;
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
@include border-radius();
|
||||
.itemhandle {
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
.itemname {
|
||||
margin-right: map-get($spacers, 5);
|
||||
}
|
||||
.action-menu {
|
||||
.itemactions {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
.dropdown-toggle {
|
||||
@include border-radius(.5rem);
|
||||
width: $icon-medium-width;
|
||||
height: $icon-medium-height;
|
||||
}
|
||||
}
|
||||
.dropdown-toggle {
|
||||
border-radius: .5rem;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
&.sortable-list-current-position {
|
||||
background-color: $light;
|
||||
}
|
||||
}
|
||||
.sortable-list-is-dragged {
|
||||
opacity: .75;
|
||||
max-width: $course-content-maxwidth;
|
||||
}
|
||||
.loading-icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: calc(50% - .5rem);
|
||||
}
|
||||
}
|
||||
|
||||
// Templates page.
|
||||
|
@ -35062,12 +35062,6 @@ img.userpicture {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.path-mod-feedback .drag_target_active {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.path-mod-feedback .drag_item_active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.path-mod-feedback .feedback_bar_image {
|
||||
height: 10px;
|
||||
}
|
||||
@ -35080,35 +35074,46 @@ img.userpicture {
|
||||
.path-mod-feedback .templateslist th.header.action {
|
||||
width: 10%;
|
||||
}
|
||||
.path-mod-feedback .feedback_form .itemactions {
|
||||
display: inline-block;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.path-mod-feedback .feedback-item-label {
|
||||
width: 100%;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged {
|
||||
padding: 1rem;
|
||||
border: 1px solid #dee2e6;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemhandle {
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemname,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemname {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .action-menu {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemactions,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemactions {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .dropdown-toggle {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemactions .dropdown-toggle,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemactions .dropdown-toggle {
|
||||
border-radius: 0.5rem;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist.sortable-list-current-position,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged.sortable-list-current-position {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form .sortable-list-is-dragged {
|
||||
opacity: 0.75;
|
||||
max-width: 830px;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form .loading-icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: calc(50% - 0.5rem);
|
||||
}
|
||||
.path-mod-feedback#page-mod-feedback-manage_templates .coursetemplates .no-overflow,
|
||||
.path-mod-feedback#page-mod-feedback-manage_templates .publictemplates .no-overflow {
|
||||
overflow: visible;
|
||||
|
@ -35062,12 +35062,6 @@ img.userpicture {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.path-mod-feedback .drag_target_active {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.path-mod-feedback .drag_item_active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.path-mod-feedback .feedback_bar_image {
|
||||
height: 10px;
|
||||
}
|
||||
@ -35080,35 +35074,46 @@ img.userpicture {
|
||||
.path-mod-feedback .templateslist th.header.action {
|
||||
width: 10%;
|
||||
}
|
||||
.path-mod-feedback .feedback_form .itemactions {
|
||||
display: inline-block;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.path-mod-feedback .feedback-item-label {
|
||||
width: 100%;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged {
|
||||
padding: 1rem;
|
||||
border: 1px solid #dee2e6;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemhandle {
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemname,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemname {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .action-menu {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemactions,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemactions {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .dropdown-toggle {
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist .itemactions .dropdown-toggle,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged .itemactions .dropdown-toggle {
|
||||
border-radius: 0.5rem;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form [id*=_feedback_item_].feedback_itemlist.sortable-list-current-position,
|
||||
.path-mod-feedback #feedback_edit_form .feedback_itemlist.sortable-list-is-dragged.sortable-list-current-position {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form .sortable-list-is-dragged {
|
||||
opacity: 0.75;
|
||||
max-width: 830px;
|
||||
}
|
||||
.path-mod-feedback #feedback_edit_form .loading-icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: calc(50% - 0.5rem);
|
||||
}
|
||||
.path-mod-feedback#page-mod-feedback-manage_templates .coursetemplates .no-overflow,
|
||||
.path-mod-feedback#page-mod-feedback-manage_templates .publictemplates .no-overflow {
|
||||
overflow: visible;
|
||||
|
Loading…
x
Reference in New Issue
Block a user