From b4f09ac6aedf8b722ef424f62ab8de2a7fbb4c79 Mon Sep 17 00:00:00 2001 From: Khoa Nguyen Dang Date: Thu, 21 Apr 2022 18:49:40 +0700 Subject: [PATCH] StudentQuiz: Throw 'invalidcomment' error when commenting on the question --- attempt.php | 3 ++ classes/utils.php | 23 ++++++++++++- commenthistory.php | 3 ++ lang/en/studentquiz.php | 3 ++ preview.php | 11 ++++--- renderer.php | 25 ++++++++++++++ reportcomment.php | 2 ++ reportrank.php | 10 ++++-- reportstat.php | 10 +++--- styles.css | 3 +- tests/behat/group_separate_groups.feature | 40 ++++++++++++++++++++--- view.php | 3 ++ 12 files changed, 119 insertions(+), 17 deletions(-) diff --git a/attempt.php b/attempt.php index 9ce14245..9359fc96 100644 --- a/attempt.php +++ b/attempt.php @@ -21,6 +21,7 @@ * @copyright 2017 HSR (http://www.hsr.ch) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_studentquiz\utils; require_once(__DIR__ . '/../../config.php'); require_once($CFG->libdir . '/questionlib.php'); @@ -199,6 +200,8 @@ $navinfo->total = $questionscount; $PAGE->navbar->add(get_string('nav_question_no', 'studentquiz', $navinfo)); +utils::require_access_to_a_relevant_group($cm, $context); + echo $OUTPUT->header(); $info = new stdClass(); diff --git a/classes/utils.php b/classes/utils.php index bc0183bb..418a2b58 100644 --- a/classes/utils.php +++ b/classes/utils.php @@ -479,7 +479,8 @@ public static function groups_get_questions_joins($groupid = 0, $groupidcolumn = * @throws coding_exception if empty or invalid context submitted when $groupid = USERSWITHOUTGROUP */ public static function sq_groups_get_members_join($groupid, $useridcolumn, $context = null) { - if (!$groupid) { + // Don't need to join with group members if the user has the capability 'moodle/site:accessallgroups'. + if (!$groupid || has_capability('moodle/site:accessallgroups', $context)) { $joins = ''; $wheres = ''; $params = []; @@ -803,4 +804,24 @@ public static function get_question_names(array $questionids): array { return $DB->get_records_list('question', 'id', $questionids, '', 'id, name'); } + + /** + * Makes security checks for viewing. Will return an error message if the user cannot access the student quiz. + * + * @param object $cm - The course module object. + * @param \context $context The context module. + * @param string $title Page's title. + * @return void + */ + public static function require_access_to_a_relevant_group(object $cm, \context $context, string $title = ''): void { + global $COURSE, $PAGE; + $groupmode = (int)groups_get_activity_groupmode($cm, $COURSE); + $currentgroup = groups_get_activity_group($cm, true); + + if ($groupmode === SEPARATEGROUPS && !$currentgroup && !has_capability('moodle/site:accessallgroups', $context)) { + $renderer = $PAGE->get_renderer('mod_studentquiz'); + $renderer->render_error_message(get_string('error_permission', 'studentquiz'), $title); + exit(); + } + } } diff --git a/commenthistory.php b/commenthistory.php index 2ca0c7eb..b59bfcfa 100644 --- a/commenthistory.php +++ b/commenthistory.php @@ -23,6 +23,7 @@ */ use mod_studentquiz\commentarea\container; +use mod_studentquiz\utils; require_once('../../config.php'); require_once($CFG->dirroot . '/mod/studentquiz/locallib.php'); @@ -76,6 +77,8 @@ $PAGE->set_heading($title); $PAGE->set_url($actionurl); +utils::require_access_to_a_relevant_group($cm, $context); + echo $OUTPUT->header(); echo $renderer->render_comment_history($questionid, $commentid, $cmid); echo $OUTPUT->footer(); diff --git a/lang/en/studentquiz.php b/lang/en/studentquiz.php index 63da4e43..c64fa1b8 100644 --- a/lang/en/studentquiz.php +++ b/lang/en/studentquiz.php @@ -48,6 +48,7 @@ $string['approved_veryshort'] = 'A'; $string['approveselectedscheck'] = 'Are you sure you want to un-/approve the following questions?

{$a}'; $string['average_column_name'] = 'Average'; +$string['back_to_course_button'] = 'Back to course'; $string['before_answering_end_date'] = 'This StudentQuiz closes for answering on {$a}.'; $string['before_answering_start_date'] = 'Open for answering from {$a}.'; $string['before_submission_end_date'] = 'This StudentQuiz closes for question submission on {$a}.'; @@ -141,6 +142,7 @@ $string['error_form_validation'] = '{$a}'; $string['error_sendalert'] = 'There was an error sending your report to {$a}. Report could not be sent.'; +$string['error_permission'] = 'Sorry, but you need to be part of a group to see this page.'; $string['expandcomment'] = 'Expand comment'; $string['expandall'] = 'Expand all comments'; $string['filter'] = 'Filter'; @@ -572,6 +574,7 @@ $string['studentquiz:manage'] = 'Edit and delete questions on StudentQuiz'; $string['studentquiz:organize'] = 'Move questions into categories on StudentQuiz'; $string['studentquiz:pinquestion'] = 'Pin questions in StudentQuiz'; +$string['studentquiz:preview'] = 'Preview questions'; $string['studentquiz:previewothers'] = 'Preview questions of others on StudentQuiz'; $string['studentquiz:submit'] = 'Create questions on StudentQuiz'; $string['studentquiz:systemnotifytaskdeleteorphanedquestions'] = 'Orphaned questions deleted notification'; diff --git a/preview.php b/preview.php index 54db5e94..e9534b78 100644 --- a/preview.php +++ b/preview.php @@ -21,6 +21,7 @@ * @copyright 2017 HSR (http://www.hsr.ch) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_studentquiz\utils; require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/viewlib.php'); @@ -51,6 +52,12 @@ \mod_studentquiz\access\context_override::ensure_permissions_are_right($context); $studentquiz = mod_studentquiz_load_studentquiz($module->id, $context->id); +$output = $PAGE->get_renderer('mod_studentquiz', 'attempt'); +$PAGE->set_pagelayout('popup'); +$actionurl = new moodle_url('/mod/studentquiz/preview.php', array('cmid' => $cmid, 'questionid' => $questionid)); +$PAGE->set_url($actionurl); + +utils::require_access_to_a_relevant_group($module, $context, get_string('studentquiz:preview', 'studentquiz')); // Lookup question. try { @@ -70,7 +77,6 @@ } // Get and validate existing preview, or start a new one. -$actionurl = new moodle_url('/mod/studentquiz/preview.php', array('cmid' => $cmid, 'questionid' => $questionid)); $previewid = optional_param('previewid', 0, PARAM_INT); $highlight = optional_param('highlight', 0, PARAM_INT); @@ -129,11 +135,8 @@ } else { $title = get_string('deletedquestion', 'qtype_missingtype'); } -$output = $PAGE->get_renderer('mod_studentquiz', 'attempt'); -$PAGE->set_pagelayout('popup'); $PAGE->set_title($title); $PAGE->set_heading($title); -$PAGE->set_url($actionurl); $PAGE->requires->js_call_amd('mod_studentquiz/studentquiz', 'initialise'); echo $OUTPUT->header(); diff --git a/renderer.php b/renderer.php index 1b2a4788..819f5d34 100755 --- a/renderer.php +++ b/renderer.php @@ -66,6 +66,31 @@ public function render_table_data(array $celldata, array $rowattributes=array()) return $rows; } + /** + * Render custom error message, so we can write the behat test for it. + * + * @param string $errormessage Error message. + * @param string $title Page's title. + * @return void + */ + public function render_error_message(string $errormessage, string $title) : void { + if ($title) { + $this->page->set_title($title); + } + // We need to remove settings menu for view page because we are using custom error message. + if ($settingmenu = $this->page->settingsnav->find('modulesettings', \navigation_node::TYPE_SETTING)) { + $settingmenu->remove(); + } + echo $this->output->header(); + echo $this->output->notification($errormessage, 'error', false); + $courseurl = new moodle_url('/course/view.php', ['id' => $this->page->course->id]); + + $backtocourse = new single_button($courseurl, get_string('back_to_course_button', 'studentquiz'), + 'get', true); + echo html_writer::div($this->render($backtocourse), 'studentquizerrormessage'); + echo $this->output->footer(); + } + /** * Render one table cell. * diff --git a/reportcomment.php b/reportcomment.php index 50cf3952..263b5d56 100644 --- a/reportcomment.php +++ b/reportcomment.php @@ -76,6 +76,8 @@ $PAGE->navbar->add($pagename); } +utils::require_access_to_a_relevant_group($cm, $context); + // Keep referer url. $action = (new moodle_url($PAGE->url, ['referer' => $referer]))->out(false); diff --git a/reportrank.php b/reportrank.php index 80e9ea4b..5b0175b2 100644 --- a/reportrank.php +++ b/reportrank.php @@ -21,6 +21,7 @@ * @copyright 2017 HSR (http://www.hsr.ch) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_studentquiz\utils; require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/reportlib.php'); @@ -31,14 +32,17 @@ } $report = new mod_studentquiz_report($cmid); -require_login($report->get_course(), false, $report->get_coursemodule()); +$cm = $report->get_coursemodule(); +require_login($report->get_course(), false, $cm); +$context = $report->get_context(); +$output = $PAGE->get_renderer('mod_studentquiz', 'ranking'); $PAGE->set_title($report->get_ranking_title()); $PAGE->set_heading($report->get_heading()); $PAGE->set_context($report->get_context()); $PAGE->set_url($report->get_rank_url()); -$output = $PAGE->get_renderer('mod_studentquiz', 'ranking'); +utils::require_access_to_a_relevant_group($cm, $context); echo $OUTPUT->header(); @@ -47,4 +51,4 @@ echo $OUTPUT->footer(); // Trigger report rank viewed event. -mod_studentquiz_reportrank_viewed($report->get_cm_id(), $report->get_context()); +mod_studentquiz_reportrank_viewed($report->get_cm_id(), $context); diff --git a/reportstat.php b/reportstat.php index f1a6f16b..10903cc5 100644 --- a/reportstat.php +++ b/reportstat.php @@ -21,6 +21,7 @@ * @copyright 2017 HSR (http://www.hsr.ch) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_studentquiz\utils; require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/reportlib.php'); @@ -31,19 +32,20 @@ } $report = new mod_studentquiz_report($cmid); +$cm = $report->get_coursemodule(); -require_login($report->get_course(), false, $report->get_coursemodule()); - +require_login($report->get_course(), false, $cm); $context = context_module::instance($cmid); +$renderer = $PAGE->get_renderer('mod_studentquiz', 'report'); $PAGE->set_title($report->get_statistic_title()); $PAGE->set_heading($report->get_heading()); $PAGE->set_context($report->get_context()); $PAGE->set_url($report->get_stat_url()); -echo $OUTPUT->header(); +utils::require_access_to_a_relevant_group($cm, $context); -$renderer = $PAGE->get_renderer('mod_studentquiz', 'report'); +echo $OUTPUT->header(); echo $renderer->view_stat($report); diff --git a/styles.css b/styles.css index 17c786bd..b13021c0 100644 --- a/styles.css +++ b/styles.css @@ -819,7 +819,8 @@ } .path-mod-studentquiz #categoryquestions .header.qtype, -.path-mod-studentquiz #categoryquestions .header.state { +.path-mod-studentquiz #categoryquestions .header.state, +.path-mod-studentquiz .studentquizerrormessage { text-align: center; } diff --git a/tests/behat/group_separate_groups.feature b/tests/behat/group_separate_groups.feature index aad39246..392ac15d 100644 --- a/tests/behat/group_separate_groups.feature +++ b/tests/behat/group_separate_groups.feature @@ -7,15 +7,19 @@ Feature: Students can create questions and practice in separate groups. | student1 | Student | One | student1@example.com | | student2 | Student | Two | student2@example.com | | student3 | Student | Three | student3@example.com | + | student4 | Student | Four | student4@example.com | + | teacher | Teacher | One | teacher@example.com | And the following "courses" exist: | fullname | shortname | category | | Course 1 | C1 | 0 | And the following "course enrolments" exist: - | user | course | role | - | student1 | C1 | student | - | student2 | C1 | student | - | student3 | C1 | student | + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | student4 | C1 | student | + | teacher | C1 | editingteacher | And the following "groups" exist: | name | course | idnumber | | Group 1 | C1 | G1 | @@ -123,3 +127,31 @@ Feature: Students can create questions and practice in separate groups. And I should see "Separate groups: Group 1" And "Student One" "text" should appear before "Student Two" "text" And I should not see "Student Three" + + @javascript + Scenario: Students without a group will not be able to access the Student Quiz activity in separate groups. + Given I am on the "C1" "Course" page logged in as "student4" + And I follow "StudentQuiz 1" + Then I should see "Sorry, but you need to be part of a group to see this page." + And "Back to course" "button" should exist + + @javascript + Scenario: Teacher without a group but has the capability 'moodle/site:accessallgroups' can write a comment on Student Quiz in separate groups. + Given I am on the "C1" "Course" page logged in as "admin" + And I follow "StudentQuiz 1" + And I click on "Create new question" "button" + And I set the field "item_qtype_truefalse" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + And I set the field "Question name" to "Question of Student 1" + And I set the field "Question text" to "The correct answer is true" + And I press "id_submitbutton" + And I log out + And I am on the "C1" "Course" page logged in as "teacher" + And I follow "StudentQuiz 1" + And I click on "Start Quiz" "button" + And I set the field "True" to "1" + And I press "Check" + And I enter the text "Comment 1" into the "Add public comment" editor + And I press "Add comment" + And I wait until ".studentquiz-comment-item:nth-child(1)" "css_element" exists + Then I should see "Comment 1" in the ".studentquiz-comment-item:nth-child(1) .studentquiz-comment-text" "css_element" diff --git a/view.php b/view.php index 5a069746..ebae1b3e 100755 --- a/view.php +++ b/view.php @@ -24,6 +24,7 @@ * @copyright 2017 HSR (http://www.hsr.ch) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_studentquiz\utils; require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/viewlib.php'); @@ -100,6 +101,8 @@ $PAGE->set_heading($COURSE->fullname); $PAGE->set_cm($cm, $course); +utils::require_access_to_a_relevant_group($cm, $context); + // Process actions. $view->process_actions();