diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php
index a3114c0e2e9e2..06dab0bf8b3f4 100644
--- a/mod/quiz/lib.php
+++ b/mod/quiz/lib.php
@@ -2606,3 +2606,21 @@ function mod_quiz_calculate_question_stats(context $context): ?all_calculated_fo
$report = new quiz_statistics_report();
return $report->calculate_questions_stats_for_question_bank($cm->instance, false, false);
}
+
+/**
+ * Return a list of all the user preferences used by mod_quiz.
+ *
+ * @uses core_user::is_current_user
+ *
+ * @return array[]
+ */
+function mod_quiz_user_preferences(): array {
+ $preferences = [];
+ $preferences['quiz_timerhidden'] = [
+ 'type' => PARAM_INT,
+ 'null' => NULL_NOT_ALLOWED,
+ 'default' => '0',
+ 'permissioncallback' => [core_user::class, 'is_current_user'],
+ ];
+ return $preferences;
+}
diff --git a/mod/quiz/module.js b/mod/quiz/module.js
index 0c3b65a411f6b..1921b6e43e127 100644
--- a/mod/quiz/module.js
+++ b/mod/quiz/module.js
@@ -78,6 +78,7 @@ M.mod_quiz.timer = {
M.mod_quiz.timer.endtime = M.pageloadstarttime.getTime() + start*1000;
M.mod_quiz.timer.preview = preview;
M.mod_quiz.timer.update();
+
Y.one('#quiz-timer-wrapper').setStyle('display', 'flex');
require(['core_form/changechecker'], function(FormChangeChecker) {
M.mod_quiz.timer.FormChangeChecker = FormChangeChecker;
@@ -85,31 +86,64 @@ M.mod_quiz.timer = {
Y.one('#toggle-timer').on('click', function() {
M.mod_quiz.timer.toggleVisibility();
});
+
+ // We store the visibility as a user preference. If the value is not '1',
+ // i. e. it is '0' or the item does not exist, the timer must be shown.
+ require(['core_user/repository'], function(UserRepository) {
+ UserRepository.getUserPreference('quiz_timerhidden')
+ .then((response) => {
+ M.mod_quiz.timer.setVisibility(response !== '1', false);
+ return;
+ })
+ // If there is an error, we catch and ignore it, because (i) no matter what we do,
+ // we do not have the stored value, so we will need to take a reasonable default
+ // and (ii) the student who is currently taking the quiz is probably not interested
+ // in the technical details why the fetch failed, even less, because they can hardly
+ // do anything to solve the problem. However, we still log that there was an error
+ // to leave a trace, e. g. for debugging.
+ .catch((error) => {
+ M.mod_quiz.timer.setVisibility(true, false);
+ Y.log(error, 'error', 'moodle-mod_quiz');
+ });
+ });
+ },
+
+ /**
+ * Toggle the timer's visibility.
+ */
+ toggleVisibility: function() {
+ var Y = M.mod_quiz.timer.Y;
+ var timer = Y.one('#quiz-time-left');
+
+ // If the timer is currently hidden, the visibility should be set to true and vice versa.
+ this.setVisibility(timer.getAttribute('hidden') === 'hidden');
},
/**
- * Hide or show the timer.
- * @param {boolean} whether we are ultimately displaying the timer and disabling the button
+ * Set visibility of the timer.
+ * @param visible whether the timer should be visible
+ * @param updatePref whether the new status should be stored as a preference
*/
- toggleVisibility: function(finalShow = false) {
+ setVisibility: function(visible, updatePref = true) {
var Y = M.mod_quiz.timer.Y;
var timer = Y.one('#quiz-time-left');
var button = Y.one('#toggle-timer');
- // When time is running out, we show the timer and disable the button.
- if (finalShow) {
- timer.show();
+ if (visible) {
button.setContent(M.util.get_string('hide', 'moodle'));
- button.setAttribute('disabled', true);
- return;
+ timer.show();
+ } else {
+ button.setContent(M.util.get_string('show', 'moodle'));
+ timer.hide();
}
- timer.toggleView();
- if (timer.getAttribute('hidden') === 'hidden') {
- button.setContent(M.util.get_string('show', 'moodle'));
- } else {
- button.setContent(M.util.get_string('hide', 'moodle'));
+ // Only update the user preference if this has been requested.
+ if (updatePref) {
+ require(['core_user/repository'], function(UserRepository) {
+ UserRepository.setUserPreference('quiz_timerhidden', (visible ? '0' : '1'));
+ });
}
+
},
/**
@@ -157,7 +191,12 @@ M.mod_quiz.timer = {
Y.one('#quiz-timer').removeClass('timeleft' + (secondsleft + 2))
.removeClass('timeleft' + (secondsleft + 1))
.addClass('timeleft' + secondsleft);
- M.mod_quiz.timer.toggleVisibility(true);
+
+ // From now on, the timer should be visible and should not be hideable anymore.
+ // We use the second (optional) parameter in order to leave the user preference
+ // unchanged.
+ M.mod_quiz.timer.setVisibility(true, false);
+ Y.one('#toggle-timer').setAttribute('disabled', true);
}
// Update the time display.
diff --git a/mod/quiz/templates/timer.mustache b/mod/quiz/templates/timer.mustache
index 2e414b48b78ee..57516b4b00c34 100644
--- a/mod/quiz/templates/timer.mustache
+++ b/mod/quiz/templates/timer.mustache
@@ -28,7 +28,7 @@
{{#str}} timeleft, quiz {{/str}}
-