diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index 2dc7996..a4c7817 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 services: postgres: @@ -73,7 +73,7 @@ jobs: - name: Moodle Code Checker if: ${{ always() }} - run: moodle-plugin-ci codechecker --max-warnings 0 + run: moodle-plugin-ci codechecker - name: Moodle PHPDoc Checker if: ${{ always() }} @@ -93,7 +93,7 @@ jobs: - name: Grunt if: ${{ always() }} - run: moodle-plugin-ci grunt --max-lint-warnings 0 + run: moodle-plugin-ci grunt - name: PHPUnit tests if: ${{ always() }} diff --git a/CHANGELOG.md b/CHANGELOG.md index a017f0c..c647485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] -### Changed -- + +## [1.2.1] - 2023-08-21 + +- Changed submission button for student answer changed from Save to Submit. +- Fixed various codestyle issues. +- Added column in qbank view for displaying question text/media +- Swapped the order of firstname and lastname in csv export of attendance list +- Improvement of guest user feedback upon trying to attend a quiz which doesn't allow guests to attend +- Added new template for "guests_not_allowed" feedback ## [1.2.0] - 2023-03-31 diff --git a/README.md b/README.md index 682d970..f4a4548 100755 --- a/README.md +++ b/README.md @@ -20,9 +20,14 @@ The teacher can, at a later date, go back through the results and, for each ques The repolls are treated as separate questions, and you will get a correct order in the review page. ## Documentation -Documentation is available [here](https://github.com/KQMATH/moodle-mod_jazzquiz/wiki), including [installation instructions](https://github.com/KQMATH/moodle-mod_jazzquiz/wiki/Installation-instructions). + + +Documentation is available as +[github pages](https://kqmath.github.io/) at +[https://kqmath.github.io/docs/jazzquiz/](https://kqmath.github.io/docs/jazzquiz/) ## Feedback: + **Project lead:** Hans Georg Schaathun: **Developer:** Sebastian S. Gundersen: diff --git a/amd/src/core.js b/amd/src/core.js index ee9436a..4e7f989 100755 --- a/amd/src/core.js +++ b/amd/src/core.js @@ -14,14 +14,14 @@ // along with Moodle. If not, see . /** - * @package mod_jazzquiz + * @module mod_jazzquiz * @author Sebastian S. Gundersen * @copyright 2014 University of Wisconsin - Madison * @copyright 2018 NTNU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function ($, mConfig, mString, Y, mEvent) { +define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function($, mConfig, mString, Y, mEvent) { // Contains the needed values for using the ajax script. let session = { @@ -56,10 +56,7 @@ define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function url: url, data: data, dataType: 'json', - success: success, - error: function (xhr, status, error) { - //console.error('XHR Error: ' + error + '. Status: ' + status); - } + success: success }).fail(() => setText(Quiz.info, 'error_with_request')); } @@ -126,6 +123,7 @@ define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function return; } Quiz.show(Question.box.html(data.html)); + // eslint-disable-next-line no-eval eval(data.js); data.css.forEach(cssUrl => { let head = document.getElementsByTagName('head')[0]; @@ -265,7 +263,7 @@ define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function changeQuizState(state, data) { this.isNewState = (this.state !== state); this.state = state; - this.role.onStateChange(state); + this.role.onStateChange(); const event = this.events[state]; this.role[event](data); } @@ -342,7 +340,7 @@ define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function static renderMaximaEquation(input, targetId) { const target = document.getElementById(targetId); if (target === null) { - //console.error('Target element #' + targetId + ' not found.'); + // Log error to console: 'Target element #' + targetId + ' not found.'. return; } if (cache[input] !== undefined) { @@ -359,10 +357,10 @@ define(['jquery', 'core/config', 'core/str', 'core/yui', 'core/event'], function /** * Retrieve a language string that was sent along with the page. - * @param $element + * @param {*} $element * @param {string} key Which string in the language file we want. * @param {string} [from=jazzquiz] Which language file we want the string from. Default is jazzquiz. - * @param [args] This is {$a} in the string for the key. + * @param {array} args This is {$a} in the string for the key. */ function setText($element, key, from, args) { from = (from !== undefined) ? from : 'jazzquiz'; diff --git a/amd/src/edit.js b/amd/src/edit.js index 622cdbc..5c0d631 100755 --- a/amd/src/edit.js +++ b/amd/src/edit.js @@ -13,15 +13,18 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . + +import Sortable from '../../js/sortable.min.js'; + /** - * @package mod_jazzquiz + * @module mod_jazzquiz * @author Sebastian S. Gundersen * @copyright 2015 University of Wisconsin - Madison * @copyright 2018 NTNU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery'], function ($) { +define(['jquery'], function($) { /** * Submit the question order to the server. An empty array will delete all questions. @@ -41,7 +44,7 @@ define(['jquery'], function ($) { */ function getQuestionOrder() { let order = []; - $('.questionlist li').each(function () { + $('.questionlist li').each(function() { order.push($(this).data('question-id')); }); return order; @@ -69,8 +72,12 @@ define(['jquery'], function ($) { return order; } + /** + * Add click-listener to a quiz by module id. + * @param {number} courseModuleId + */ function listenAddToQuiz(courseModuleId) { - $('.jazzquiz-add-selected-questions').on('click', function () { + $('.jazzquiz-add-selected-questions').on('click', function() { const $checkboxes = $('#categoryquestions td input[type=checkbox]:checked'); let questionIds = ''; for (const checkbox of $checkboxes) { @@ -86,26 +93,30 @@ define(['jquery'], function ($) { return { initialize: courseModuleId => { - $('.edit-question-action').on('click', function () { + $('.edit-question-action').on('click', function() { const action = $(this).data('action'); const questionId = $(this).data('question-id'); let order = []; switch (action) { - case 'up': + case 'up': { order = offsetQuestion(questionId, 1); break; - case 'down': + } + case 'down': { order = offsetQuestion(questionId, -1); break; - case 'delete': + } + case 'delete': { order = getQuestionOrder(); const index = order.indexOf(questionId); if (index !== -1) { order.splice(index, 1); } break; - default: + } + default: { return; + } } submitQuestionOrder(order, courseModuleId); }); diff --git a/amd/src/instructor.js b/amd/src/instructor.js index 117044b..6fe6111 100755 --- a/amd/src/instructor.js +++ b/amd/src/instructor.js @@ -14,14 +14,14 @@ // along with Moodle. If not, see . /** - * @package mod_jazzquiz + * @module mod_jazzquiz * @author Sebastian S. Gundersen * @copyright 2014 University of Wisconsin - Madison * @copyright 2018 NTNU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { +define(['jquery', 'mod_jazzquiz/core'], function($, Jazz) { const Quiz = Jazz.Quiz; const Question = Jazz.Question; @@ -140,7 +140,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } $row.addClass('merge-from'); let $table = $row.parent().parent(); - $table.find('tr').each(function () { + $table.find('tr').each(function() { const $cells = $(this).find('td'); if ($cells[1].id !== $barCell.attr('id')) { $(this).addClass('merge-into'); @@ -233,7 +233,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { // Add rows. for (let i = 0; i < responses.length; i++) { - //const percent = (parseInt(responses[i].count) / total) * 100; + // Const percent = (parseInt(responses[i].count) / total) * 100; const percent = (parseInt(responses[i].count) / highestResponseCount) * 100; // Check if row with same response already exists. @@ -262,7 +262,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { const countHtml = '' + responses[i].count + ''; let responseCell = row.insertCell(0); - responseCell.onclick = function () { + responseCell.onclick = function() { $(this).parent().toggleClass('selected-vote-option'); }; @@ -299,7 +299,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } } } - }; + } /** * Sort the responses in the graph by how many had the same response. @@ -440,7 +440,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } /** - * refresh() equivalent for votes. + * Method refresh() equivalent for votes. */ refreshVotes() { // Should we show the results? @@ -687,7 +687,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { this.quiz.question.isRunning = false; } - onSessionClosed(data) { + onSessionClosed() { Quiz.hide(Instructor.side); Quiz.hide(Instructor.correctAnswer); Instructor.enableControls([]); @@ -695,7 +695,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { this.quiz.question.isRunning = false; } - onVoting(data) { + onVoting() { if (!this.responses.showResponses) { this.responses.hide(); } @@ -704,7 +704,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { this.responses.refreshVotes(); } - onStateChange(state) { + onStateChange() { $('#region-main').find('ul.nav.nav-tabs').css('display', 'none'); $('#region-main-settings-menu').css('display', 'none'); $('.region_main_settings_menu_proxy').css('display', 'none'); @@ -776,7 +776,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { 'jazzquiz-question-id': questions[i].jazzquizquestionid }); $questionButton.data('test', 1); - $questionButton.on('click', function () { + $questionButton.on('click', function() { const questionId = $(this).data('question-id'); const time = $(this).data('time'); const jazzQuestionId = $(this).data('jazzquiz-question-id'); @@ -810,6 +810,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { runVoting() { const options = Instructor.getSelectedAnswersForVote(); const data = {questions: encodeURIComponent(JSON.stringify(options))}; + // eslint-disable-next-line no-return-assign Ajax.post('run_voting', data, () => {}); } @@ -871,6 +872,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { Quiz.hide(Question.box); Quiz.hide(Instructor.controls); setText(Quiz.info, 'closing_session'); + // eslint-disable-next-line no-return-assign Ajax.post('close_session', {}, () => window.location = location.href.split('&')[0]); } @@ -947,7 +949,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } static addReportEventHandlers() { - $(document).on('click', '#report_overview_controls button', function () { + $(document).on('click', '#report_overview_controls button', function() { const action = $(this).data('action'); if (action === 'attendance') { $('#report_overview_responded').fadeIn(); @@ -962,7 +964,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } return { - initialize: function (totalQuestions, reportView, slots) { + initialize: function(totalQuestions, reportView, slots) { let quiz = new Quiz(Instructor); quiz.role.totalQuestions = totalQuestions; if (reportView) { @@ -978,6 +980,6 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { quiz.poll(500); } } - } + }; }); diff --git a/amd/src/student.js b/amd/src/student.js index 2846f21..e148218 100755 --- a/amd/src/student.js +++ b/amd/src/student.js @@ -14,14 +14,14 @@ // along with Moodle. If not, see . /** - * @package mod_jazzquiz + * @module mod_jazzquiz * @author Sebastian S. Gundersen * @copyright 2014 University of Wisconsin - Madison * @copyright 2018 NTNU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { +define(['jquery', 'mod_jazzquiz/core'], function($, Jazz) { const Quiz = Jazz.Quiz; const Question = Jazz.Question; @@ -47,11 +47,11 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { }); } - onNotRunning(data) { + onNotRunning() { setText(Quiz.info, 'instructions_for_student'); } - onPreparing(data) { + onPreparing() { setText(Quiz.info, 'wait_for_instructor'); } @@ -65,7 +65,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { } } - onReviewing(data) { + onReviewing() { this.quiz.question.isVoteRunning = false; this.quiz.question.isRunning = false; this.quiz.question.hideTimer(); @@ -73,7 +73,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { setText(Quiz.info, 'wait_for_instructor'); } - onSessionClosed(data) { + onSessionClosed() { window.location = location.href.split('&')[0]; } @@ -93,7 +93,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { this.quiz.question.isVoteRunning = true; } - onStateChange(state) { + onStateChange() { } @@ -104,7 +104,7 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { onTimerTick(timeLeft) { setText(Question.timer, 'question_will_end_in_x_seconds', 'jazzquiz', timeLeft); } - + // eslint-disable-next-line no-unused-vars onQuestionRefreshed(data) { } @@ -118,7 +118,9 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { return; } this.quiz.question.isSaving = true; + // eslint-disable-next-line no-undef if (typeof tinyMCE !== 'undefined') { + // eslint-disable-next-line no-undef tinyMCE.triggerSave(); } const serialized = Question.form.serializeArray(); @@ -165,6 +167,6 @@ define(['jquery', 'mod_jazzquiz/core'], function ($, Jazz) { let quiz = new Quiz(Student); quiz.poll(2000); } - } + }; }); diff --git a/classes/bank/jazzquiz_question_bank_view.php b/classes/bank/jazzquiz_question_bank_view.php index e425430..043130d 100755 --- a/classes/bank/jazzquiz_question_bank_view.php +++ b/classes/bank/jazzquiz_question_bank_view.php @@ -54,8 +54,28 @@ protected function wanted_columns(): array { 'core_question\\local\\bank\\checkbox_column', 'qbank_viewquestiontype\\question_type_column', 'qbank_viewquestionname\\viewquestionname_column_helper', - 'qbank_previewquestion\\preview_action_column' + 'qbank_previewquestion\\preview_action_column', ]; + + // Needs to check qbshowtext parameter manually from baseurl in order + // to determine whether to display the question text row below questions + $queryparams = explode(';', $this->baseurl); + + foreach ($queryparams as $value) { + $value = str_replace('&', '', $value); + $value = str_replace('amp', '', $value); + $param = explode('=', $value); + + if ($param[0] == 'qbshowtext') { + if ((int)$param[1] != 0) { + array_push($columns, 'qbank_viewquestiontext\\question_text_row'); + } + break; + } + } + + + foreach ($columns as $column) { $this->requiredcolumns[$column] = new $column($this); } @@ -100,7 +120,7 @@ public function display($pagevars, $tabname): void { $cat, null, $page, - $perpage, + $perpage, $this->contexts->having_cap('moodle/question:add') ); $this->display_add_selected_questions_button(); @@ -109,7 +129,13 @@ public function display($pagevars, $tabname): void { private function display_add_selected_questions_button() { $straddtoquiz = get_string('add_to_quiz', 'jazzquiz'); - echo ''; + echo + '
+ + + '; } /** diff --git a/classes/exporter.php b/classes/exporter.php index c3d6149..b7a379c 100755 --- a/classes/exporter.php +++ b/classes/exporter.php @@ -153,11 +153,20 @@ public function export_attendance_csv(jazzquiz_session $session) { $name = $session->data->id . '_' . $session->data->name; $attendances = $session->get_attendances(); $this->csv_file("session_{$name}_attendance"); - echo "Student\tResponses\r\n"; + echo "IdNumber\tFirst Name\tLast Name\tResponses\r\n"; foreach ($attendances as $attendance) { - $name = $attendance['name']; + $idnumber = $attendance['idnumber']; + $userfirstlastname = explode(', ', $attendance['name']); + if (count($userfirstlastname) >= 2) { + $lastname = $userfirstlastname[1]; + $firstname = $userfirstlastname[0]; + } else { + $lastname = $userfirstlastname[0]; + // For anonymous, but it doesn't even make sense since if it's anon, no name should display. + $firstname = $userfirstlastname[0]; + } $count = $attendance['count']; - echo "$name\t$count\r\n"; + echo "$idnumber\t$lastname\t$firstname\t$count\r\n"; } } diff --git a/classes/improviser.php b/classes/improviser.php index cab8960..98550c6 100755 --- a/classes/improviser.php +++ b/classes/improviser.php @@ -60,10 +60,10 @@ public function get_all_improvised_question_definitions() { $questions = []; $context = \context_module::instance($this->jazzquiz->cm->id); $parts = explode('/', $context->path); - debugging( "get_all_improvised: ".$parts ) ; + debugging( "get_all_improvised: ".$parts ); foreach ($parts as $part) { // Selecting name first, to prevent duplicate improvise questions. - debugging( "get_all_improvised: ".$part ) ; + debugging( "get_all_improvised: ".$part ); $sql = "SELECT q.name, q.id FROM {question} q @@ -256,7 +256,7 @@ private function insert_multichoice_question_definition(string $name, array $ans } $question->id = $DB->insert_record('question', $question); $qbankentry->id = $DB->insert_record('question_bank_entries', $qbankentry); - $this->insert_question_version( $qbankentry->id, $question->id ) ; + $this->insert_question_version( $qbankentry->id, $question->id ); // Add options. $options = $this->make_multichoice_options($question->id); $DB->insert_record('qtype_multichoice_options', $options); @@ -269,17 +269,17 @@ private function insert_multichoice_question_definition(string $name, array $ans } /* Create a question version database object, linking the given question - * and question bank entry. - * @param int $qbankid The ID of the question banke entry - * @param int $qid The ID of the question + * and question bank entry. + * @param int $qbankid The ID of the question banke entry + * @param int $qid The ID of the question * @return \stdClass */ private function insert_question_version(int $qbankid, int $qid) { global $DB; $qv = new \stdClass(); - $qv->questionbankentryid = $qbankid ; - $qv->version = 1 ; - $qv->questionid = $qid ; + $qv->questionbankentryid = $qbankid; + $qv->version = 1; + $qv->questionid = $qid; $qv->id = $DB->insert_record('question_versions', $qv); return $qv; } @@ -301,7 +301,7 @@ private function insert_shortanswer_question_definition(string $name) { } $question->id = $DB->insert_record('question', $question); $qbankentry->id = $DB->insert_record('question_bank_entries', $qbankentry); - $this->insert_question_version( $qbankentry->id, $question->id ) ; + $this->insert_question_version( $qbankentry->id, $question->id ); // Add options. $options = $this->make_shortanswer_options($question->id); @@ -324,7 +324,7 @@ private function insert_shortmath_question_definition(string $name) { } $question->id = $DB->insert_record('question', $question); $qbankentry->id = $DB->insert_record('question_bank_entries', $qbankentry); - $this->insert_question_version( $qbankentry->id, $question->id ) ; + $this->insert_question_version( $qbankentry->id, $question->id ); // Add options. Important: If shortmath changes options table in the future, this must be changed too. $options = $this->make_shortanswer_options($question->id); $DB->insert_record('qtype_shortmath_options', $options); diff --git a/classes/jazzquiz_session.php b/classes/jazzquiz_session.php index 4b3eb7a..b2bbe27 100755 --- a/classes/jazzquiz_session.php +++ b/classes/jazzquiz_session.php @@ -139,7 +139,7 @@ public function user_name_for_answer($userid) { if (!$user) { return '?'; } - return fullname($user); + return $user->lastname . ', ' . $user->firstname; } public function user_name_for_attendance($userid) { @@ -151,7 +151,19 @@ public function user_name_for_attendance($userid) { if (!$user) { return '?'; } - return fullname($user); + return $user->lastname . ', ' . $user->firstname; + } + + public function user_idnumber_for_attendance($userid) { + global $DB; + if ($this->requires_anonymous_attendance() || is_null($userid)) { + return get_string('anonymous', 'jazzquiz'); + } + $user = $DB->get_record('user', ['id' => $userid]); + if (!$user) { + return '?'; + } + return $user->idnumber; } /** @@ -475,6 +487,7 @@ public function get_attendances() : array { $records = $DB->get_records('jazzquiz_attendance', ['sessionid' => $this->data->id]); foreach ($records as $record) { $attendances[] = [ + 'idnumber' => $this->user_idnumber_for_attendance($record->userid), 'name' => $this->user_name_for_attendance($record->userid), 'count' => $record->numresponses ]; diff --git a/classes/output/renderer.php b/classes/output/renderer.php index e1f2ca9..cc91f67 100755 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -156,6 +156,18 @@ public function quiz_not_running($cmid) { ]); } + + /** + * Shows the "guests not allowed" page when trying to access a + * quiz which does not allow guests in guest mode. + * @throws \moodle_exception + */ + public function guests_not_allowed() { + echo $this->render_from_template('jazzquiz/guests_not_allowed', []); + } + + + /** * Renders the quiz to the page * @param jazzquiz_session $session @@ -373,7 +385,20 @@ public function require_core(jazzquiz_session $session) { */ public function require_quiz(jazzquiz_session $session) { $this->require_core($session); - $this->page->requires->js('/question/qengine.js'); + + // question/qengine.js is deprecated for Moodle versions after 401. + // Checks moodle version in order to determine which question engine + // to use, making the jazzquiz independent of the Moodle version. + global $CFG; + include $CFG->dirroot.'/version.php'; + $branch = $CFG->branch; + + if ( (int) $branch <= 401 ) { + $this->page->requires->js('/question/qengine.js'); + } else { + $this->page->requires->js_call_amd('core_question/question_engine', 'initialize'); + } + if ($session->jazzquiz->is_instructor()) { $count = count($session->jazzquiz->questions); $params = [$count, false, []]; diff --git a/edit.php b/edit.php index 888c3c2..17475cd 100755 --- a/edit.php +++ b/edit.php @@ -57,10 +57,13 @@ function jazzquiz_session_open($jazzquizid) { * @return string * @throws \coding_exception */ -function get_qbank_view(\core_question\local\bank\question_edit_contexts $contexts, jazzquiz $jazzquiz, \moodle_url $url, array $pagevars) { +function get_qbank_view( + \core_question\local\bank\question_edit_contexts $contexts, + jazzquiz $jazzquiz, \moodle_url $url, array $pagevars) { + $qperpage = optional_param('qperpage', 10, PARAM_INT); $qpage = optional_param('qpage', 0, PARAM_INT); - $new_pagevars = [ + $newpagevars = [ 'qpage' => $qpage, 'qperpage' => $qperpage, 'cat' => $pagevars['cat'], @@ -71,7 +74,7 @@ function get_qbank_view(\core_question\local\bank\question_edit_contexts $contex // Capture question bank display in buffer to have the renderer render output. ob_start(); $questionbank = new bank\jazzquiz_question_bank_view($contexts, $url, $jazzquiz->course, $jazzquiz->cm); - $questionbank->display($new_pagevars, 'editq'); + $questionbank->display($newpagevars, 'editq'); return ob_get_clean(); } @@ -84,7 +87,10 @@ function get_qbank_view(\core_question\local\bank\question_edit_contexts $contex * @throws \coding_exception * @throws \moodle_exception */ -function list_questions(\core_question\local\bank\question_edit_contexts $contexts, jazzquiz $jazzquiz, \moodle_url $url, array $pagevars) { +function list_questions( + \core_question\local\bank\question_edit_contexts $contexts, + jazzquiz $jazzquiz, \moodle_url $url, array $pagevars) { + $qbankview = get_qbank_view($contexts, $jazzquiz, $url, $pagevars); $jazzquiz->renderer->list_questions($jazzquiz, $jazzquiz->questions, $qbankview, $url); } @@ -133,7 +139,10 @@ function jazzquiz_edit_edit_question(jazzquiz $jazzquiz) { * @throws \coding_exception * @throws \moodle_exception */ -function jazzquiz_edit_qlist(jazzquiz $jazzquiz, \core_question\local\bank\question_edit_contexts $contexts, \moodle_url $url, array $pagevars) { +function jazzquiz_edit_qlist( + jazzquiz $jazzquiz, \core_question\local\bank\question_edit_contexts $contexts, + \moodle_url $url, array $pagevars) { + $jazzquiz->renderer->header($jazzquiz, 'edit'); list_questions($contexts, $jazzquiz, $url, $pagevars); $jazzquiz->renderer->footer(); diff --git a/lang/en/jazzquiz.php b/lang/en/jazzquiz.php index 591ec15..0c0f145 100755 --- a/lang/en/jazzquiz.php +++ b/lang/en/jazzquiz.php @@ -139,6 +139,9 @@ $string['x_seconds_left'] = '{$a}s left'; $string['error_saving_vote'] = 'There was an error saving your vote.'; $string['you_already_voted'] = 'Sorry, but you have already voted.'; +$string['guest_login'] = 'You are not enrolled on the course, and there is no quiz session open to guests at the moment.'; +$string['no_guests'] = 'It is possible that the teacher has not yet started a quiz session, and that you may try again later. It is also possible that you have to log in as a student and enrol on the course to be allowed to take part.'; +$string['ask_teacher'] = 'If in doubt, ask the teacher.'; // Instructor Controls. $string['startquiz'] = 'Start quiz'; @@ -162,6 +165,8 @@ $string['a_students_answered'] = '{$a} students answered at least one question.'; $string['no_attempts_found'] = 'No attempts found.'; $string['student'] = 'Student'; + +// Problem: $string['student_id'] = 'StudentID'; //not working in report.mustache, not sure why $string['select_session'] = 'Select session to review:'; $string['attendance_list'] = 'Attendance list'; $string['download_report'] = 'Download report'; diff --git a/styles.css b/styles.css index 8bfe2c3..ce2d008 100755 --- a/styles.css +++ b/styles.css @@ -409,11 +409,11 @@ position: absolute; right: 32px; margin-top: 4px; - color: #ff5555; + color: #f55; } #report_overview_controls a:last-child:hover { - color: #cc3333; + color: #c33; } #report_overview_responded table tr { @@ -474,6 +474,6 @@ .start-question-menu .MathJax_Display, .questionlist li .controls .name .MathJax_Display { - text-align: left !important; - margin: 0 !important; + text-align: left ; + margin: 0 ; } diff --git a/templates/continue_session.mustache b/templates/continue_session.mustache index 467de12..ff98e84 100755 --- a/templates/continue_session.mustache +++ b/templates/continue_session.mustache @@ -1,3 +1,12 @@ +{{! + @template continue_session + + Example context (json): + { + "str": "Continue session...", + "path": "https://example.com/" + } +}}

{{# str }} instructor_sessions_going, jazzquiz {{/ str }}

diff --git a/templates/edit_question_list.mustache b/templates/edit_question_list.mustache index 336d1c7..3c6768d 100755 --- a/templates/edit_question_list.mustache +++ b/templates/edit_question_list.mustache @@ -1,3 +1,6 @@ +{{! + @template edit_question_list +}}

diff --git a/templates/guests_not_allowed.mustache b/templates/guests_not_allowed.mustache new file mode 100644 index 0000000..0110232 --- /dev/null +++ b/templates/guests_not_allowed.mustache @@ -0,0 +1,8 @@ +{{! + @template guests_not_allowed +}} +
+

{{# str }} guest_login, jazzquiz {{/ str }}

+

{{# str }} no_guests, jazzquiz {{/ str }}

+

{{# str }} ask_teacher, jazzquiz {{/ str }}

+
diff --git a/templates/join_session.mustache b/templates/join_session.mustache index 486949a..1d747c7 100755 --- a/templates/join_session.mustache +++ b/templates/join_session.mustache @@ -1,3 +1,6 @@ +{{! + @template join_session +}}

{{# str }} join_quiz_instructions, jazzquiz {{/ str }}

diff --git a/templates/no_session.mustache b/templates/no_session.mustache index c99f060..3bb497f 100755 --- a/templates/no_session.mustache +++ b/templates/no_session.mustache @@ -1,3 +1,6 @@ +{{! + @template no_session +}}

{{# str }} quiz_not_running, jazzquiz {{/ str }}

diff --git a/templates/question.mustache b/templates/question.mustache index a35283b..9a16d63 100755 --- a/templates/question.mustache +++ b/templates/question.mustache @@ -1,3 +1,6 @@ +{{! + @template question +}}

{{{ question }}}
@@ -5,7 +8,7 @@ {{^ instructor }}
- +
{{/ instructor }} diff --git a/templates/quiz.mustache b/templates/quiz.mustache index 177d534..2ff9728 100755 --- a/templates/quiz.mustache +++ b/templates/quiz.mustache @@ -1,3 +1,6 @@ +{{! + @template quiz +}}
{{# instructor }}
diff --git a/templates/report.mustache b/templates/report.mustache index 93b97ee..7b59063 100755 --- a/templates/report.mustache +++ b/templates/report.mustache @@ -1,3 +1,6 @@ +{{! + @template report +}}
{{# select_session }}
@@ -46,11 +49,13 @@

{{# str }} attendance_list, jazzquiz {{/str}}

+ {{# students }} + diff --git a/templates/start_session.mustache b/templates/start_session.mustache index 71ad29a..2f03678 100755 --- a/templates/start_session.mustache +++ b/templates/start_session.mustache @@ -1,3 +1,6 @@ +{{! + @template start_session +}}

{{# str }} teacher_start_instructions, jazzquiz {{/ str }}


diff --git a/version.php b/version.php index 62ee98d..0fec825 100755 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023033100; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2023033105; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2022041900; // Moodle 4.0 (or above). $plugin->cron = 0; // Period in seconds for cron to run. $plugin->component = 'mod_jazzquiz'; diff --git a/view.php b/view.php index 55cbeb6..a14822b 100755 --- a/view.php +++ b/view.php @@ -25,6 +25,7 @@ */ namespace mod_jazzquiz; +use Exception; require_once("../../config.php"); require_once($CFG->dirroot . '/mod/jazzquiz/lib.php'); @@ -156,6 +157,7 @@ function jazzquiz_view_default(jazzquiz $jazzquiz) { } } + /** * Entry point for viewing a quiz. */ @@ -168,12 +170,34 @@ function jazzquiz_view() { header('Location: /'); exit; } + $action = optional_param('action', '', PARAM_ALPHANUM); $jazzquiz = new jazzquiz($cmid); $session = $jazzquiz->load_open_session(); - if (!$session || !$session->data->allowguests) { - require_capability('mod/jazzquiz:attempt', $jazzquiz->context); + $iscapable = true; + + /* + * Checks that the user is authorised for he quiz. + * access or not. + * The require_capability() method checks this for students + * and teacher, but it cannot handle the case where guest + * access is allowed. Hence, if guests are allowed, no + * further check is made. + */ + if (!$session || $session->data->allowguests != 1) { + try { + /* + * require_capability() throws an exception if the user does not + * have the required capabilities. Usually this means that the student + * or teacher is not enrolled on the course. + */ + require_capability('mod/jazzquiz:attempt', $jazzquiz->context); + } catch (Exception $e) { + // Indicates that the guest user is not allowed to access this session. + $iscapable = false; + } } + $PAGE->set_pagelayout('incourse'); $PAGE->set_context($jazzquiz->context); $PAGE->set_cm($jazzquiz->cm); @@ -187,14 +211,30 @@ function jazzquiz_view() { $url->param('action', $action); $PAGE->set_url($url); - if ($jazzquiz->is_instructor()) { - $improviser = new improviser($jazzquiz); - $improviser->insert_default_improvised_question_definitions(); - } - if ($action === 'quizstart') { - jazzquiz_view_start_quiz($jazzquiz); + if ($iscapable) { + if ($jazzquiz->is_instructor()) { + $improviser = new improviser($jazzquiz); + $improviser->insert_default_improvised_question_definitions(); + } + if ($action === 'quizstart') { + jazzquiz_view_start_quiz($jazzquiz); + } else { + jazzquiz_view_default($jazzquiz); + } } else { - jazzquiz_view_default($jazzquiz); + /* + * Shows "guests_not_allowed" if capability is false and + * session doesn't allow for guests to attend. + * + * This is triggered when the session does not allow for guests + * to attend, and the user trying to attend is a guest. + */ + + /** @var output\renderer $renderer */ + $renderer = $jazzquiz->renderer; + $renderer->header($jazzquiz, 'view'); + $renderer->guests_not_allowed(); + $renderer->footer(); } }
Student ID {{# str }} student, jazzquiz {{/str}} {{# str }} responses, jazzquiz {{/ str }}
{{ idnumber }} {{ name }} {{# str }} a_responses, jazzquiz, {{ count }} {{/ str }}