MDL-20636 Preserve scroll position when doing action on the quiz edit page.

This commit is contained in:
Tim Hunt
2011-02-11 17:36:02 +00:00
parent f9b0500f7f
commit fd214b596d
4 changed files with 150 additions and 90 deletions
+8 -14
View File
@@ -25,10 +25,16 @@
// Initialise everything on the quiz edit/order and paging page.
var quiz_edit = {};
function quiz_edit_init() {
function quiz_edit_init(Y) {
M.core_scroll_manager.scroll_to_saved_pos(Y);
Y.on('submit', function(e) {
M.core_scroll_manager.save_scroll_pos(Y, 'id_existingcategory');
}, '#mform1');
Y.on('submit', function(e) {
M.core_scroll_manager.save_scroll_pos(Y, e.target.get('firstChild'));
}, '.quizsavegradesform');
// Add random question dialogue --------------------------------------------
var randomquestiondialog = YAHOO.util.Dom.get('randomquestiondialog');
if (randomquestiondialog) {
YAHOO.util.Dom.get(document.body).appendChild(randomquestiondialog);
@@ -148,15 +154,3 @@ function quiz_settings_init() {
}, 50);
});
}
// Depending on which page this is, do the appropriate initialisation.
function quiz_edit_generic_init() {
switch (document.body.id) {
case 'page-mod-quiz-edit':
quiz_edit_init();
break;
case 'page-mod-quiz-mod':
quiz_settings_init();
}
}
YAHOO.util.Event.onDOMReady(quiz_edit_generic_init);
+19 -12
View File
@@ -115,6 +115,7 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
//this page otherwise they would go in question_edit_setup
$quiz_reordertool = optional_param('reordertool', -1, PARAM_BOOL);
$quiz_qbanktool = optional_param('qbanktool', -1, PARAM_BOOL);
$scrollpos = optional_param('scrollpos', '', PARAM_INT);
list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) =
question_edit_setup('editq', '/mod/quiz/edit.php', true);
@@ -183,18 +184,22 @@ foreach ($params as $key => $value) {
}
}
$afteractionurl = new moodle_url($thispageurl);
if ($scrollpos) {
$afteractionurl->param('scrollpos', $scrollpos);
}
if (($up = optional_param('up', false, PARAM_INT)) && confirm_sesskey()) {
$quiz->questions = quiz_move_question_up($quiz->questions, $up);
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (($down = optional_param('down', false, PARAM_INT)) && confirm_sesskey()) {
$quiz->questions = quiz_move_question_down($quiz->questions, $down);
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) {
@@ -203,7 +208,7 @@ if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) {
$quiz->questions = quiz_repaginate($quiz->questions, $questionsperpage );
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (($addquestion = optional_param('addquestion', 0, PARAM_INT)) && confirm_sesskey()) {
@@ -213,7 +218,7 @@ if (($addquestion = optional_param('addquestion', 0, PARAM_INT)) && confirm_sess
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
$thispageurl->param('lastchanged', $addquestion);
redirect($thispageurl);
redirect($afteractionurl);
}
if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
@@ -227,7 +232,7 @@ if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
}
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if ((optional_param('addrandom', false, PARAM_BOOL)) && confirm_sesskey()) {
@@ -240,7 +245,7 @@ if ((optional_param('addrandom', false, PARAM_BOOL)) && confirm_sesskey()) {
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (optional_param('addnewpagesafterselected', null, PARAM_CLEAN) && !empty($selectedquestionids) && confirm_sesskey()) {
@@ -249,7 +254,7 @@ if (optional_param('addnewpagesafterselected', null, PARAM_CLEAN) && !empty($sel
}
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
$addpage = optional_param('addpage', false, PARAM_INT);
@@ -257,7 +262,7 @@ if ($addpage !== false && confirm_sesskey()) {
$quiz->questions = quiz_add_page_break_at($quiz->questions, $addpage);
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
$deleteemptypage = optional_param('deleteemptypage', false, PARAM_INT);
@@ -265,7 +270,7 @@ if (($deleteemptypage !== false) && confirm_sesskey()) {
$quiz->questions = quiz_delete_empty_page($quiz->questions, $deleteemptypage);
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
$remove = optional_param('remove', false, PARAM_INT);
@@ -273,7 +278,7 @@ if (($remove = optional_param('remove', false, PARAM_INT)) && confirm_sesskey())
quiz_remove_question($quiz, $remove);
quiz_update_sumgrades($quiz);
quiz_delete_previews($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (optional_param('quizdeleteselected', false, PARAM_BOOL) && !empty($selectedquestionids) && confirm_sesskey()) {
@@ -282,7 +287,7 @@ if (optional_param('quizdeleteselected', false, PARAM_BOOL) && !empty($selectedq
}
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
redirect($thispageurl);
redirect($afteractionurl);
}
if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
@@ -375,7 +380,7 @@ if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
quiz_update_all_final_grades($quiz);
quiz_update_grades($quiz, 0, true);
}
redirect($thispageurl);
redirect($afteractionurl);
}
$questionbank->process_actions($thispageurl, $cm);
@@ -403,7 +408,9 @@ for ($pageiter = 1; $pageiter <= $numberoflisteners; $pageiter++) {
$quizeditconfig->dialoglisteners[] = 'addrandomdialoglaunch_' . $pageiter;
}
$PAGE->requires->data_for_js('quiz_edit_config', $quizeditconfig);
$PAGE->requires->js('/question/qengine.js');
$PAGE->requires->js('/mod/quiz/edit.js');
$PAGE->requires->js_init_call('quiz_edit_init');
// Print the tabs to switch mode.
if ($quiz_reordertool) {
+31 -31
View File
@@ -182,11 +182,12 @@ function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number, $inc
}
// More random questions are needed, create them.
$form->questiontext = array('text' => $includesubcategories, 'format' => 0);
$form->defaultmark = 1;
$form->hidden = 1;
for ($i = 0; $i < $number; $i += 1) {
$form = new stdClass();
$form->questiontext = array('text' => $includesubcategories, 'format' => 0);
$form->category = $category->id . ',' . $category->contextid;
$form->defaultmark = 1;
$form->hidden = 1;
$form->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question = new stdClass;
$question->qtype = 'random';
@@ -256,7 +257,8 @@ function quiz_save_new_layout($quiz) {
* @param integer $quizid The id of the quiz to update / add the instances for.
*/
function quiz_update_question_instance($grade, $questionid, $quiz) {
$instance = $DB->get_record('quiz_question_instances', array('quiz' => $quizid,
global $DB;
$instance = $DB->get_record('quiz_question_instances', array('quiz' => $quiz->id,
'question' => $questionid));
$slot = quiz_get_slot_for_question($quiz, $questionid);
@@ -467,7 +469,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
$questions[$qnum] = $fakequestion;
$quiz->grades[$qnum] = 0;
} else if ($qnum and question_bank::qtype_exists($questions[$qnum]->qtype)) {
} else if ($qnum && !question_bank::qtype_exists($questions[$qnum]->qtype)) {
$questions[$qnum]->qtype = 'missingtype';
}
@@ -489,10 +491,10 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
echo '</div>';
if ($allowdelete) {
echo '<div class="quizpagedelete">';
echo '<a title="' . get_string('removeemptypage', 'quiz') . '" href="' .
$pageurl->out(true, array('deleteemptypage' => $count - 1, 'sesskey'=>sesskey())) .
'"><img src="' . $OUTPUT->pix_url('t/delete') . '" ' .
'class="iconsmall" alt="' . $strremove . '" /></a>';
echo $OUTPUT->action_icon($pageurl->out(true, array('deleteemptypage' => $count - 1, 'sesskey'=>sesskey())),
new pix_icon('t/delete', $strremove),
new component_action('click', 'M.core_scroll_manager.save_scroll_action'),
array('title' => $strremove));
echo '</div>';
}
}
@@ -552,28 +554,28 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
if ($count >= $lastindex - 1) {
$upbuttonclass = 'upwithoutdown';
}
echo "<a title=\"$strmoveup\" href=\"" .
$pageurl->out(true, array('up' => $question->id, 'sesskey'=>sesskey())) . "\"><img
src=\"" . $OUTPUT->pix_url('t/up') . "\" class=\"iconsmall
$upbuttonclass\" alt=\"$strmoveup\" /></a>";
echo $OUTPUT->action_icon($pageurl->out(true, array('up' => $question->id, 'sesskey'=>sesskey())),
new pix_icon('t/up', $strmoveup),
new component_action('click', 'M.core_scroll_manager.save_scroll_action'),
array('title' => $strmoveup));
}
}
if ($count < $lastindex - 1) {
if (!$hasattempts) {
echo "<a title=\"$strmovedown\" href=\"" .
$pageurl->out(true, array('down' => $question->id, 'sesskey'=>sesskey())) . "\"><img
src=\"" . $OUTPUT->pix_url('t/down') . "\" class=\"iconsmall\"" .
" alt=\"$strmovedown\" /></a>";
echo $OUTPUT->action_icon($pageurl->out(true, array('down' => $question->id, 'sesskey'=>sesskey())),
new pix_icon('t/down', $strmovedown),
new component_action('click', 'M.core_scroll_manager.save_scroll_action'),
array('title' => $strmovedown));
}
}
if ($allowdelete && (empty($question->id) || question_has_capability_on($question, 'use', $question->category))) {
// remove from quiz, not question delete.
if (!$hasattempts) {
echo "<a title=\"$strremove\" href=\"" .
$pageurl->out(true, array('remove' => $question->id, 'sesskey'=>sesskey())) . "\">
<img src=\"" . $OUTPUT->pix_url('t/delete') . "\" " .
"class=\"iconsmall\" alt=\"$strremove\" /></a>";
echo $OUTPUT->action_icon($pageurl->out(true, array('remove' => $question->id, 'sesskey'=>sesskey())),
new pix_icon('t/delete', $strremove),
new component_action('click', 'M.core_scroll_manager.save_scroll_action'),
array('title' => $strremove));
}
}
?>
@@ -581,7 +583,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
if ($question->qtype != 'description' && !$reordertool) {
?>
<div class="points">
<form method="post" action="edit.php"><div>
<form method="post" action="edit.php" class="quizsavegradesform"><div>
<fieldset class="invisiblefieldset" style="display: block;">
<label for="<?php echo "inputq$question->id" ?>"><?php echo $strgrade; ?></label>:<br />
<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
@@ -660,7 +662,9 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
if (!$reordertool && !$quiz->shufflequestions) {
echo $OUTPUT->container_start('addpage');
$url = new moodle_url($pageurl->out_omit_querystring(), array('cmid' => $quiz->cmid, 'courseid' => $quiz->course, 'addpage' => $count, 'sesskey' => sesskey()));
echo $OUTPUT->single_button($url, get_string('addpagehere', 'quiz'), 'get', array('disabled'=>$hasattempts));
echo $OUTPUT->single_button($url, get_string('addpagehere', 'quiz'), 'post',
array('disabled' => $hasattempts,
'actions' => array(new component_action('click', 'M.core_scroll_manager.save_scroll_action'))));
echo $OUTPUT->container_end();
}
$pageopen = false;
@@ -786,7 +790,7 @@ function quiz_print_randomquestion(&$question, &$pageurl, &$quiz, $quiz_qbanktoo
echo '<span class="questionpreview">' . quiz_question_preview_button($quiz, $question, true) . '</span>';
echo '</div>';
$questionids = question_bank::get_qtype('random')->get_usable_questions_from_category(
$questionids = question_bank::get_qtype('random')->get_available_questions_from_category(
$category->id, $question->questiontext == '1', '0');
$questioncount = count($questionids);
@@ -808,11 +812,7 @@ function quiz_print_randomquestion(&$question, &$pageurl, &$quiz, $quiz_qbanktoo
// Category has questions
// Get a sample from the database,
$toshow = array_slice($questionids, 0, NUM_QS_TO_SHOW_IN_RANDOM);
$questionidstoshow = array();
foreach ($toshow as $a) {
$questionidstoshow[] = $a->id;
}
$questionidstoshow = array_slice($questionids, 0, NUM_QS_TO_SHOW_IN_RANDOM);
$questionstoshow = $DB->get_records_list('question', 'id', $questionidstoshow,
'', 'id,qtype,name,questiontext,questiontextformat');
@@ -874,7 +874,7 @@ function quiz_print_randomquestion_reordertool(&$question, &$pageurl, &$quiz) {
echo $OUTPUT->notification('Random question category not found!');
return;
}
$questioncount = count(question_bank::get_qtype('random')->get_usable_questions_from_category(
$questioncount = count(question_bank::get_qtype('random')->get_available_questions_from_category(
$category->id, $question->questiontext == '1', '0'));
$reordercheckboxlabel = '<label for="s' . $question->id . '">';
@@ -1145,7 +1145,7 @@ class quiz_question_bank_view extends question_bank_view {
function quiz_print_grading_form($quiz, $pageurl, $tabindex) {
global $USER, $OUTPUT;
$strsave = get_string('save', 'quiz');
echo "<form method=\"post\" action=\"edit.php\"><div>";
echo '<form method="post" action="edit.php" class="quizsavegradesform"><div>';
echo '<fieldset class="invisiblefieldset" style="display: block;">';
echo "<input type=\"hidden\" name=\"sesskey\" value=\"" . sesskey() . "\" />";
echo html_writer::input_hidden_params($pageurl);
+92 -33
View File
@@ -1,3 +1,93 @@
/**
* Scroll manager is a class that help with saving the scroll positing when you
* click on an action icon, and then when the page is reloaded after processing
* the action, it scrolls you to exactly where you were. This is much nicer for
* the user.
*
* To use this in your code, you need to ensure that:
* 1. The button that triggers the action has to have a click event handler that
* calls M.core_scroll_manager.save_scroll_pos
* 2. The script that process the action has to grab the scrollpos parameter
* using $scrollpos = optional_param('scrollpos', 0, PARAM_INT);
* 3. After doing the processing, it must add ->param('scrollpos', $scrollpos)
* to the URL that it redirects to.
* 4. Finally, on the page that is reloaded (which should be the same as the one
* the user started on) you need to call M.core_scroll_manager.scroll_to_saved_pos
* on page load.
*/
M.core_scroll_manager = M.core_scroll_manager || {};
/**
* In the form that contains the element, set the value of the form field with
* name scrollpos to the current scroll position. If there is no element with
* that name, it creates a hidden form field wiht that name within the form.
* @param element the element in the form. Should be something that can be
* passed to Y.one.
*/
M.core_scroll_manager.save_scroll_pos = function(Y, element) {
if (typeof(element) == 'string') {
// Have to use getElementById here because element id can contain :.
element = Y.one(document.getElementById(element));
}
var form = element.ancestor('form');
if (!form) {
return;
}
var scrollpos = form.one('input[name=scrollpos]');
if (!scrollpos) {
scrollpos = form.appendChild(form.create('<input type="hidden" name="scrollpos" />'));
}
scrollpos.set('value', form.get('docScrollY'));
}
/**
* Event handler that can be used on a link. Assumes that the link already
* contains at least one URL parameter.
*/
M.core_scroll_manager.save_scroll_action = function(e) {
var link = e.target.ancestor('a[href]');
if (!link) {
M.core_scroll_manager.save_scroll_pos({}, e.target);
return;
}
link.set('href', link.get('href') + '&scrollpos=' + link.get('docScrollY'));
}
/**
* If there is a parameter like scrollpos=123 in the URL, scroll to that saved position.
*/
M.core_scroll_manager.scroll_to_saved_pos = function(Y) {
var matches = window.location.href.match(/^.*[?&]scrollpos=(\d*)(?:&|$|#).*$/, '$1');
if (matches) {
// onDOMReady is the effective one here. I am leaving the immediate call to
// window.scrollTo in case it reduces flicker.
window.scrollTo(0, matches[1]);
Y.on('domready', function() { window.scrollTo(0, matches[1]); });
// And the following horror is necessary to make it work in IE 8.
// Note that the class ie8 on body is only there in Moodle 2.0 and OU Moodle.
if (Y.one('body').hasClass('ie')) {
M.core_scroll_manager.force_ie_to_scroll(matches[1])
}
}
}
/**
* Beat IE into submission.
* @param targetpos the target scroll position.
*/
M.core_scroll_manager.force_ie_to_scroll = function(targetpos) {
var hackcount = 25;
function do_scroll() {
window.scrollTo(0, targetpos);
hackcount -= 1;
if (hackcount > 0) {
setTimeout(do_scroll, 10);
}
}
Y.on('load', do_scroll, window);
}
M.core_question_engine = M.core_question_engine || {};
/**
@@ -14,10 +104,7 @@ M.core_question_engine.questionformalreadysubmitted = false;
M.core_question_engine.init_submit_button = function(Y, button, slot) {
var buttonel = document.getElementById(button);
Y.on('click', function(e) {
var scrollpos = document.getElementById('scrollpos');
if (scrollpos) {
scrollpos.value = YAHOO.util.Dom.getDocumentScrollTop();
}
M.core_scroll_manager.save_scroll_pos(Y, button);
buttonel.form.action = buttonel.form.action + '#q' + slot;
}, buttonel);
}
@@ -49,19 +136,7 @@ M.core_question_engine.init_form = function(Y, form) {
Y.one(form).all('.questionflagsavebutton').remove();
var matches = window.location.href.match(/^.*[?&]scrollpos=(\d*)(?:&|$|#).*$/, '$1');
if (matches) {
// onDOMReady is the effective one here. I am leaving the immediate call to
// window.scrollTo in case it reduces flicker.
window.scrollTo(0, matches[1]);
Y.on('domready', function() { window.scrollTo(0, matches[1]); });
// And the following horror is necessary to make it work in IE 8.
// Note that the class ie8 on body is only there in Moodle 2.0 and OU Moodle.
if (YAHOO.util.Dom.hasClass(document.body, 'ie')) {
question_force_ie_to_scroll(matches[1])
}
}
M.core_scroll_manager.scroll_to_saved_pos(Y);
}
/**
@@ -80,19 +155,3 @@ M.core_question_engine.prevent_repeat_submission = function(e, Y) {
}, 0);
M.core_question_engine.questionformalreadysubmitted = true;
}
/**
* Beat IE into submission.
* @param targetpos the target scroll position.
*/
M.core_question_engine.force_ie_to_scroll = function(targetpos) {
var hackcount = 25;
function do_scroll() {
window.scrollTo(0, targetpos);
hackcount -= 1;
if (hackcount > 0) {
setTimeout(do_scroll, 10);
}
}
Y.on('load', do_scroll, window);
}