Merge branch 'MDL-42504' of git://github.com/timhunt/moodle

This commit is contained in:
Dan Poltawski 2013-11-19 10:30:16 +08:00
commit 2a0a33a6ee
8 changed files with 151 additions and 7 deletions

View File

@ -60,3 +60,4 @@ if ($attemptobj->is_finished()) {
$attemptobj->process_auto_save($timenow);
$transaction->allow_commit();
echo 'OK';

View File

@ -185,6 +185,12 @@ $string['confirmserverdelete'] = 'Are you sure you want to remove the server <b>
$string['confirmstartattemptlimit'] = 'Number of attempts allowed: {$a}. You are about to start a new attempt. Do you wish to proceed?';
$string['confirmstartattempttimelimit'] = 'This quiz has a time limit and is limited to {$a} attempt(s). You are about to start a new attempt. Do you wish to proceed?';
$string['confirmstarttimelimit'] = 'The quiz has a time limit. Are you sure that you wish to start?';
$string['connectionok'] = 'Network connection restored. You may continue safely.';
$string['connectionerror'] = 'Network connection lost. (Autosave failed).
Make a note of any responses entered on this page in the last few minutes, then try to re-connect.
Once connection has been re-established, your responses should be saved and this message will disappear.';
$string['containercategorycreated'] = 'This category has been created to store all the original categories moved to site level due to the causes specified below.';
$string['continueattemptquiz'] = 'Continue the last attempt';
$string['continuepreview'] = 'Continue the last preview';

View File

@ -493,6 +493,8 @@ class mod_quiz_renderer extends plugin_renderer_base {
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('form');
$output .= $this->connection_warning();
return $output;
}
@ -1163,6 +1165,18 @@ class mod_quiz_renderer extends plugin_renderer_base {
return $this->heading($title, 3) . html_writer::tag('div', $graph, array('class' => 'graph'));
}
/**
* Output the connection warning messages, which are initially hidden, and
* only revealed by JavaScript if necessary.
*/
public function connection_warning() {
$options = array('filter' => false, 'newlines' => false);
$warning = format_text(get_string('connectionerror', 'quiz'), FORMAT_MARKDOWN, $options);
$ok = format_text(get_string('connectionok', 'quiz'), FORMAT_MARKDOWN, $options);
return html_writer::tag('div', $warning, array('id' => 'connection-error', 'style' => 'display: none;', 'role' => 'alert')) .
html_writer::tag('div', $ok, array('id' => 'connection-ok', 'style' => 'display: none;', 'role' => 'alert'));
}
}
class mod_quiz_links_to_other_attempts implements renderable {

View File

@ -28,6 +28,28 @@ body.jsenabled .questionflagcheckbox {
display: none;
}
#page-mod-quiz-attempt #connection-ok,
#page-mod-quiz-attempt #connection-error {
position: fixed;
top: 0;
width: 80%;
left: 10%;
color: #555;
border-radius: 0 0 10px 10px;
box-shadow: 5px 5px 20px 0 #666666;
padding: 1em 1em 0;
z-index: 10000;
}
#page-mod-quiz-attempt #connection-error {
background-color: #fcc;
}
#page-mod-quiz-attempt #connection-ok {
background-color: #cfb;
width: 60%;
left: 20%;
}
/** Mod quiz attempt **/
.generalbox#passwordbox {
/* Should probably match .generalbox#intro above */

View File

@ -30,13 +30,17 @@ M.mod_quiz.autosave = {
TINYMCE_DETECTION_DELAY: 500,
TINYMCE_DETECTION_REPEATS: 20,
WATCH_HIDDEN_DELAY: 1000,
FAILURES_BEFORE_NOTIFY: 1,
FIRST_SUCCESSFUL_SAVE: -1,
/** Selectors. */
SELECTORS: {
QUIZ_FORM: '#responseform',
VALUE_CHANGE_ELEMENTS: 'input, textarea',
CHANGE_ELEMENTS: 'input, select',
HIDDEN_INPUTS: 'input[type=hidden]'
HIDDEN_INPUTS: 'input[type=hidden]',
CONNECTION_ERROR: '#connection-error',
CONNECTION_OK: '#connection-ok'
},
/** Script that handles the auto-saves. */
@ -57,9 +61,13 @@ M.mod_quiz.autosave = {
/** Y.io transaction for the save ajax request. */
save_transaction: null,
/** @property Failed saves count. */
savefailures: 0,
/** Properly bound key change handler. */
editor_change_handler: null,
/** Record of the value of all the hidden fields, last time they were checked. */
hidden_field_values: {},
/**
@ -202,7 +210,10 @@ M.mod_quiz.autosave = {
this.save_transaction = Y.io(this.AUTOSAVE_HANDLER, {
method: 'POST',
form: {id: this.form},
on: {complete: this.save_done},
on: {
success: this.save_done,
failure: this.save_failed
},
context: this
});
},
@ -215,6 +226,29 @@ M.mod_quiz.autosave = {
Y.log('Dirty after save.');
this.start_save_timer();
}
if (this.savefailures > 0) {
Y.one(this.SELECTORS.CONNECTION_ERROR).hide();
Y.one(this.SELECTORS.CONNECTION_OK).show();
this.savefailures = this.FIRST_SUCCESSFUL_SAVE;
} else if (this.savefailures === this.FIRST_SUCCESSFUL_SAVE) {
Y.one(this.SELECTORS.CONNECTION_OK).hide();
this.savefailures = 0;
}
},
save_failed: function() {
Y.log('Save failed.');
this.save_transaction = null;
// We want to retry soon.
this.start_save_timer();
this.savefailures = Math.max(1, this.savefailures + 1);
if (this.savefailures === this.FAILURES_BEFORE_NOTIFY) {
Y.one(this.SELECTORS.CONNECTION_ERROR).show();
Y.one(this.SELECTORS.CONNECTION_OK).hide();
}
},
is_time_nearly_over: function() {

View File

@ -1 +1 @@
YUI.add("moodle-mod_quiz-autosave",function(e,t){M.mod_quiz=M.mod_quiz||{},M.mod_quiz.autosave={TINYMCE_DETECTION_DELAY:500,TINYMCE_DETECTION_REPEATS:20,WATCH_HIDDEN_DELAY:1e3,SELECTORS:{QUIZ_FORM:"#responseform",VALUE_CHANGE_ELEMENTS:"input, textarea",CHANGE_ELEMENTS:"input, select",HIDDEN_INPUTS:"input[type=hidden]"},AUTOSAVE_HANDLER:M.cfg.wwwroot+"/mod/quiz/autosave.ajax.php",delay:12e4,form:null,dirty:!1,delay_timer:null,save_transaction:null,editor_change_handler:null,hidden_field_values:{},init:function(t){this.form=e.one(this.SELECTORS.QUIZ_FORM);if(!this.form)return;this.delay=t*1e3,this.form.delegate("valuechange",this.value_changed,this.SELECTORS.VALUE_CHANGE_ELEMENTS,this),this.form.delegate("change",this.value_changed,this.SELECTORS.CHANGE_ELEMENTS,this),this.form.on("submit",this.stop_autosaving,this),this.init_tinymce(this.TINYMCE_DETECTION_REPEATS),this.save_hidden_field_values(),this.watch_hidden_fields()},save_hidden_field_values:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name");if(!t)return;this.hidden_field_values[t]=e.get("value")},this)},watch_hidden_fields:function(){this.detect_hidden_field_changes(),e.later(this.WATCH_HIDDEN_DELAY,this,this.watch_hidden_fields)},detect_hidden_field_changes:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name"),n=e.get("value");if(!t)return;if(!(t in this.hidden_field_values)||n!==this.hidden_field_values[t])this.hidden_field_values[t]=n,this.value_changed({target:e})},this)},init_tinymce:function(t){if(typeof tinyMCE=="undefined"){t>0&&e.later(this.TINYMCE_DETECTION_DELAY,this,this.init_tinymce,[t-1]);return}this.editor_change_handler=e.bind(this.editor_changed,this),tinyMCE.onAddEditor.add(e.bind(this.init_tinymce_editor,this))},init_tinymce_editor:function(e,t){t.onChange.add(this.editor_change_handler),t.onRedo.add(this.editor_change_handler),t.onUndo.add(this.editor_change_handler),t.onKeyDown.add(this.editor_change_handler)},value_changed:function(e){if(e.target.get("name")==="thispage"||e.target.get("name")==="scrollpos"||e.target.get("name").match(/_:flagged$/))return;this.start_save_timer_if_necessary()},editor_changed:function(e){this.start_save_timer_if_necessary()},start_save_timer_if_necessary:function(){this.dirty=!0;if(this.delay_timer||this.save_transaction)return;this.start_save_timer()},start_save_timer:function(){this.cancel_delay(),this.delay_timer=e.later(this.delay,this,this.save_changes)},cancel_delay:function(){this.delay_timer&&this.delay_timer!==!0&&this.delay_timer.cancel(),this.delay_timer=null},save_changes:function(){this.cancel_delay(),this.dirty=!1;if(this.is_time_nearly_over()){this.stop_autosaving();return}typeof tinyMCE!="undefined"&&tinyMCE.triggerSave(),this.save_transaction=e.io(this.AUTOSAVE_HANDLER,{method:"POST",form:{id:this.form},on:{complete:this.save_done},context:this})},save_done:function(){this.save_transaction=null,this.dirty&&this.start_save_timer()},is_time_nearly_over:function(){return M.mod_quiz.timer&&M.mod_quiz.timer.endtime&&(new Date).getTime()+2*this.delay>M.mod_quiz.timer.endtime},stop_autosaving:function(){this.cancel_delay(),this.delay_timer=!0,this.save_transaction&&this.save_transaction.abort()}}},"@VERSION@",{requires:["base","node","event","event-valuechange","node-event-delegate","io-form"]});
YUI.add("moodle-mod_quiz-autosave",function(e,t){M.mod_quiz=M.mod_quiz||{},M.mod_quiz.autosave={TINYMCE_DETECTION_DELAY:500,TINYMCE_DETECTION_REPEATS:20,WATCH_HIDDEN_DELAY:1e3,FAILURES_BEFORE_NOTIFY:1,FIRST_SUCCESSFUL_SAVE:-1,SELECTORS:{QUIZ_FORM:"#responseform",VALUE_CHANGE_ELEMENTS:"input, textarea",CHANGE_ELEMENTS:"input, select",HIDDEN_INPUTS:"input[type=hidden]",CONNECTION_ERROR:"#connection-error",CONNECTION_OK:"#connection-ok"},AUTOSAVE_HANDLER:M.cfg.wwwroot+"/mod/quiz/autosave.ajax.php",delay:12e4,form:null,dirty:!1,delay_timer:null,save_transaction:null,savefailures:0,editor_change_handler:null,hidden_field_values:{},init:function(t){this.form=e.one(this.SELECTORS.QUIZ_FORM);if(!this.form)return;this.delay=t*1e3,this.form.delegate("valuechange",this.value_changed,this.SELECTORS.VALUE_CHANGE_ELEMENTS,this),this.form.delegate("change",this.value_changed,this.SELECTORS.CHANGE_ELEMENTS,this),this.form.on("submit",this.stop_autosaving,this),this.init_tinymce(this.TINYMCE_DETECTION_REPEATS),this.save_hidden_field_values(),this.watch_hidden_fields()},save_hidden_field_values:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name");if(!t)return;this.hidden_field_values[t]=e.get("value")},this)},watch_hidden_fields:function(){this.detect_hidden_field_changes(),e.later(this.WATCH_HIDDEN_DELAY,this,this.watch_hidden_fields)},detect_hidden_field_changes:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name"),n=e.get("value");if(!t)return;if(!(t in this.hidden_field_values)||n!==this.hidden_field_values[t])this.hidden_field_values[t]=n,this.value_changed({target:e})},this)},init_tinymce:function(t){if(typeof tinyMCE=="undefined"){t>0&&e.later(this.TINYMCE_DETECTION_DELAY,this,this.init_tinymce,[t-1]);return}this.editor_change_handler=e.bind(this.editor_changed,this),tinyMCE.onAddEditor.add(e.bind(this.init_tinymce_editor,this))},init_tinymce_editor:function(e,t){t.onChange.add(this.editor_change_handler),t.onRedo.add(this.editor_change_handler),t.onUndo.add(this.editor_change_handler),t.onKeyDown.add(this.editor_change_handler)},value_changed:function(e){if(e.target.get("name")==="thispage"||e.target.get("name")==="scrollpos"||e.target.get("name").match(/_:flagged$/))return;this.start_save_timer_if_necessary()},editor_changed:function(e){this.start_save_timer_if_necessary()},start_save_timer_if_necessary:function(){this.dirty=!0;if(this.delay_timer||this.save_transaction)return;this.start_save_timer()},start_save_timer:function(){this.cancel_delay(),this.delay_timer=e.later(this.delay,this,this.save_changes)},cancel_delay:function(){this.delay_timer&&this.delay_timer!==!0&&this.delay_timer.cancel(),this.delay_timer=null},save_changes:function(){this.cancel_delay(),this.dirty=!1;if(this.is_time_nearly_over()){this.stop_autosaving();return}typeof tinyMCE!="undefined"&&tinyMCE.triggerSave(),this.save_transaction=e.io(this.AUTOSAVE_HANDLER,{method:"POST",form:{id:this.form},on:{success:this.save_done,failure:this.save_failed},context:this})},save_done:function(){this.save_transaction=null,this.dirty&&this.start_save_timer(),this.savefailures>0?(e.one(this.SELECTORS.CONNECTION_ERROR).hide(),e.one(this.SELECTORS.CONNECTION_OK).show(),this.savefailures=this.FIRST_SUCCESSFUL_SAVE):this.savefailures===this.FIRST_SUCCESSFUL_SAVE&&(e.one(this.SELECTORS.CONNECTION_OK).hide(),this.savefailures=0)},save_failed:function(){this.save_transaction=null,this.start_save_timer(),this.savefailures=Math.max(1,this.savefailures+1),this.savefailures===this.FAILURES_BEFORE_NOTIFY&&(e.one(this.SELECTORS.CONNECTION_ERROR).show(),e.one(this.SELECTORS.CONNECTION_OK).hide())},is_time_nearly_over:function(){return M.mod_quiz.timer&&M.mod_quiz.timer.endtime&&(new Date).getTime()+2*this.delay>M.mod_quiz.timer.endtime},stop_autosaving:function(){this.cancel_delay(),this.delay_timer=!0,this.save_transaction&&this.save_transaction.abort()}}},"@VERSION@",{requires:["base","node","event","event-valuechange","node-event-delegate","io-form"]});

View File

@ -30,13 +30,17 @@ M.mod_quiz.autosave = {
TINYMCE_DETECTION_DELAY: 500,
TINYMCE_DETECTION_REPEATS: 20,
WATCH_HIDDEN_DELAY: 1000,
FAILURES_BEFORE_NOTIFY: 1,
FIRST_SUCCESSFUL_SAVE: -1,
/** Selectors. */
SELECTORS: {
QUIZ_FORM: '#responseform',
VALUE_CHANGE_ELEMENTS: 'input, textarea',
CHANGE_ELEMENTS: 'input, select',
HIDDEN_INPUTS: 'input[type=hidden]'
HIDDEN_INPUTS: 'input[type=hidden]',
CONNECTION_ERROR: '#connection-error',
CONNECTION_OK: '#connection-ok'
},
/** Script that handles the auto-saves. */
@ -57,9 +61,13 @@ M.mod_quiz.autosave = {
/** Y.io transaction for the save ajax request. */
save_transaction: null,
/** @property Failed saves count. */
savefailures: 0,
/** Properly bound key change handler. */
editor_change_handler: null,
/** Record of the value of all the hidden fields, last time they were checked. */
hidden_field_values: {},
/**
@ -194,7 +202,10 @@ M.mod_quiz.autosave = {
this.save_transaction = Y.io(this.AUTOSAVE_HANDLER, {
method: 'POST',
form: {id: this.form},
on: {complete: this.save_done},
on: {
success: this.save_done,
failure: this.save_failed
},
context: this
});
},
@ -205,6 +216,28 @@ M.mod_quiz.autosave = {
if (this.dirty) {
this.start_save_timer();
}
if (this.savefailures > 0) {
Y.one(this.SELECTORS.CONNECTION_ERROR).hide();
Y.one(this.SELECTORS.CONNECTION_OK).show();
this.savefailures = this.FIRST_SUCCESSFUL_SAVE;
} else if (this.savefailures === this.FIRST_SUCCESSFUL_SAVE) {
Y.one(this.SELECTORS.CONNECTION_OK).hide();
this.savefailures = 0;
}
},
save_failed: function() {
this.save_transaction = null;
// We want to retry soon.
this.start_save_timer();
this.savefailures = Math.max(1, this.savefailures + 1);
if (this.savefailures === this.FAILURES_BEFORE_NOTIFY) {
Y.one(this.SELECTORS.CONNECTION_ERROR).show();
Y.one(this.SELECTORS.CONNECTION_OK).hide();
}
},
is_time_nearly_over: function() {

View File

@ -28,13 +28,17 @@ M.mod_quiz.autosave = {
TINYMCE_DETECTION_DELAY: 500,
TINYMCE_DETECTION_REPEATS: 20,
WATCH_HIDDEN_DELAY: 1000,
FAILURES_BEFORE_NOTIFY: 1,
FIRST_SUCCESSFUL_SAVE: -1,
/** Selectors. */
SELECTORS: {
QUIZ_FORM: '#responseform',
VALUE_CHANGE_ELEMENTS: 'input, textarea',
CHANGE_ELEMENTS: 'input, select',
HIDDEN_INPUTS: 'input[type=hidden]'
HIDDEN_INPUTS: 'input[type=hidden]',
CONNECTION_ERROR: '#connection-error',
CONNECTION_OK: '#connection-ok'
},
/** Script that handles the auto-saves. */
@ -55,9 +59,13 @@ M.mod_quiz.autosave = {
/** Y.io transaction for the save ajax request. */
save_transaction: null,
/** @property Failed saves count. */
savefailures: 0,
/** Properly bound key change handler. */
editor_change_handler: null,
/** Record of the value of all the hidden fields, last time they were checked. */
hidden_field_values: {},
/**
@ -200,7 +208,10 @@ M.mod_quiz.autosave = {
this.save_transaction = Y.io(this.AUTOSAVE_HANDLER, {
method: 'POST',
form: {id: this.form},
on: {complete: this.save_done},
on: {
success: this.save_done,
failure: this.save_failed
},
context: this
});
},
@ -213,6 +224,29 @@ M.mod_quiz.autosave = {
Y.log('Dirty after save.');
this.start_save_timer();
}
if (this.savefailures > 0) {
Y.one(this.SELECTORS.CONNECTION_ERROR).hide();
Y.one(this.SELECTORS.CONNECTION_OK).show();
this.savefailures = this.FIRST_SUCCESSFUL_SAVE;
} else if (this.savefailures === this.FIRST_SUCCESSFUL_SAVE) {
Y.one(this.SELECTORS.CONNECTION_OK).hide();
this.savefailures = 0;
}
},
save_failed: function() {
Y.log('Save failed.');
this.save_transaction = null;
// We want to retry soon.
this.start_save_timer();
this.savefailures = Math.max(1, this.savefailures + 1);
if (this.savefailures === this.FAILURES_BEFORE_NOTIFY) {
Y.one(this.SELECTORS.CONNECTION_ERROR).show();
Y.one(this.SELECTORS.CONNECTION_OK).hide();
}
},
is_time_nearly_over: function() {