Merge branch 'MDL-52832-master' of https://github.com/lucaboesch/moodle
authorDavid Monllao <davidm@moodle.com>
Tue, 6 Feb 2018 11:18:27 +0000 (12:18 +0100)
committerDavid Monllao <davidm@moodle.com>
Tue, 6 Feb 2018 11:18:27 +0000 (12:18 +0100)
mod/quiz/index.php
mod/quiz/locallib.php
mod/quiz/tests/locallib_test.php

index 93f2287..3882882 100644 (file)
@@ -117,6 +117,8 @@ $table->align = $align;
 
 // Populate the table with the list of instances.
 $currentsection = '';
+// Get all closing dates.
+$timeclosedates = quiz_get_user_timeclose($course->id);
 foreach ($quizzes as $quiz) {
     $cm = get_coursemodule_from_instance('quiz', $quiz->id);
     $context = context_module::instance($cm->id);
@@ -146,7 +148,11 @@ foreach ($quizzes as $quiz) {
 
     // Close date.
     if ($quiz->timeclose) {
-        $data[] = userdate($quiz->timeclose);
+        if (($timeclosedates[$quiz->id]->usertimeclose == 0) AND ($timeclosedates[$quiz->id]->usertimelimit == 0)) {
+            $data[] = get_string('noclose', 'quiz');
+        } else {
+            $data[] = userdate($timeclosedates[$quiz->id]->usertimeclose);
+        }
     } else if ($showclosingheader) {
         $data[] = '';
     }
index 36bca77..9aa8627 100644 (file)
@@ -1090,6 +1090,8 @@ function quiz_update_open_attempts(array $conditions) {
 
 /**
  * Returns SQL to compute timeclose and timelimit for every attempt, taking into account user and group overrides.
+ * The query used herein is very similar to the one in function quiz_get_user_timeclose, so, in case you
+ * would change either one of them, make sure to apply your changes to both.
  *
  * @param string $redundantwhereclauses extra where clauses to add to the subquery
  *      for performance. These can use the table alias iquiza for the quiz attempts table.
@@ -1205,6 +1207,53 @@ function quiz_get_user_image_options() {
     );
 }
 
+/**
+ * Return an user's timeclose for all quizzes in a course, hereby taking into account group and user overrides.
+ * The query used herein is very similar to the one in function quiz_get_attempt_usertime_sql, so, in case you
+ * would change either one of them, make sure to apply your changes to both.
+ *
+ * @param int $courseid the course id.
+ * @return object An object with quizids and unixdates of the most lenient close overrides, if any.
+ */
+function quiz_get_user_timeclose($courseid) {
+    global $DB, $USER;
+
+    // For teacher and manager/admins return timeclose.
+    if (has_capability('moodle/course:update', context_course::instance($courseid))) {
+        $sql = "SELECT quiz.id, quiz.timeclose AS usertimeclose, COALESCE(quiz.timelimit, 0) AS usertimelimit
+                  FROM {quiz} quiz
+                 WHERE quiz.course = :courseid
+              GROUP BY quiz.id";
+
+        $results = $DB->get_records_sql($sql, array('courseid' => $courseid));
+        return $results;
+    }
+
+    // The multiple qgo JOINS are necessary because we want timeclose/timelimit = 0 (unlimited) to supercede
+    // any other group override.
+
+    $sql = "SELECT quiz.id,
+  COALESCE(MAX(quo.timeclose), MAX(qgo1.timeclose), MAX(qgo2.timeclose), quiz.timeclose, 0) AS usertimeclose,
+  COALESCE(MAX(quo.timelimit), MAX(qgo3.timelimit), MAX(qgo4.timelimit), quiz.timelimit, 0) AS usertimelimit
+       FROM {quiz} quiz
+  LEFT JOIN {quiz_overrides} quo ON quo.quiz = quiz.id
+  LEFT JOIN {groups_members} gm ON gm.userid = quo.userid
+  LEFT JOIN {quiz_overrides} qgo1 ON qgo1.timeclose = 0 AND qgo1.quiz = quiz.id
+  LEFT JOIN {quiz_overrides} qgo2 ON qgo2.timeclose > 0 AND qgo2.quiz = quiz.id
+  LEFT JOIN {quiz_overrides} qgo3 ON qgo3.timelimit = 0 AND qgo3.quiz = quiz.id
+  LEFT JOIN {quiz_overrides} qgo4 ON qgo4.timelimit > 0 AND qgo4.quiz = quiz.id
+                                  AND qgo1.groupid = gm.groupid
+                                  AND qgo2.groupid = gm.groupid
+                                  AND qgo3.groupid = gm.groupid
+                                  AND qgo4.groupid = gm.groupid
+      WHERE quiz.course = :courseid
+            AND ((quo.userid = :userid) OR ((gm.userid IS NULL) AND (quo.userid IS NULL)))
+   GROUP BY quiz.id";
+
+    $results = $DB->get_records_sql($sql, array('courseid' => $courseid, 'userid' => $USER->id));
+    return $results;
+}
+
 /**
  * Get the choices to offer for the 'Questions per page' option.
  * @return array int => string.
index 081c757..7da393e 100644 (file)
@@ -296,4 +296,128 @@ class mod_quiz_locallib_testcase extends advanced_testcase {
 
         $this->assertTrue(quiz_is_overriden_calendar_event($event));
     }
+
+    /**
+     * Test test_quiz_get_user_timeclose().
+     */
+    public function test_quiz_get_user_timeclose() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        $basetimestamp = time(); // The timestamp we will base the enddates on.
+
+        // Create generator, course and quizzes.
+        $student1 = $this->getDataGenerator()->create_user();
+        $student2 = $this->getDataGenerator()->create_user();
+        $teacher = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
+
+        // Both quizzes close in two hours.
+        $quiz1 = $quizgenerator->create_instance(array('course' => $course->id, 'timeclose' => $basetimestamp + 7200));
+        $quiz2 = $quizgenerator->create_instance(array('course' => $course->id, 'timeclose' => $basetimestamp + 7200));
+        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+
+        $student1id = $student1->id;
+        $student2id = $student2->id;
+        $teacherid = $teacher->id;
+
+        // Users enrolments.
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
+        $this->getDataGenerator()->enrol_user($student1id, $course->id, $studentrole->id, 'manual');
+        $this->getDataGenerator()->enrol_user($student2id, $course->id, $studentrole->id, 'manual');
+        $this->getDataGenerator()->enrol_user($teacherid, $course->id, $teacherrole->id, 'manual');
+
+        // Create groups.
+        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        $group1id = $group1->id;
+        $group2id = $group2->id;
+        $this->getDataGenerator()->create_group_member(array('userid' => $student1id, 'groupid' => $group1id));
+        $this->getDataGenerator()->create_group_member(array('userid' => $student2id, 'groupid' => $group2id));
+
+        // Group 1 gets an group override for quiz 1 to close in three hours.
+        $record1 = (object) [
+            'quiz' => $quiz1->id,
+            'groupid' => $group1id,
+            'timeclose' => $basetimestamp + 10800 // In three hours.
+        ];
+        $DB->insert_record('quiz_overrides', $record1);
+
+        // Let's test quiz 1 closes in three hours for user student 1 since member of group 1.
+        // Quiz 2 closes in two hours.
+        $this->setUser($student1id);
+        $params = new stdClass();
+
+        $comparearray = array();
+        $object = new stdClass();
+        $object->id = $quiz1->id;
+        $object->usertimeclose = $basetimestamp + 10800; // The overriden timeclose for quiz 1.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz1->id] = $object;
+
+        $object = new stdClass();
+        $object->id = $quiz2->id;
+        $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz2->id] = $object;
+
+        $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id));
+
+        // User 2 gets an user override for quiz 1 to close in four hours.
+        $record2 = (object) [
+            'quiz' => $quiz1->id,
+            'userid' => $student2id,
+            'timeclose' => $basetimestamp + 14400 // In four hours.
+        ];
+        $DB->insert_record('quiz_overrides', $record2);
+
+        // Let's test quiz 1 closes in four hours for user student 2 since personally overriden.
+        // Quiz 2 closes in two hours.
+        $this->setUser($student2id);
+
+        $comparearray = array();
+        $object = new stdClass();
+        $object->id = $quiz1->id;
+        $object->usertimeclose = $basetimestamp + 14400; // The overriden timeclose for quiz 1.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz1->id] = $object;
+
+        $object = new stdClass();
+        $object->id = $quiz2->id;
+        $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz2->id] = $object;
+
+        $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id));
+
+        // Let's test a teacher sees the original times.
+        // Quiz 1 and quiz 2 close in two hours.
+        $this->setUser($teacherid);
+
+        $comparearray = array();
+        $object = new stdClass();
+        $object->id = $quiz1->id;
+        $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 1.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz1->id] = $object;
+
+        $object = new stdClass();
+        $object->id = $quiz2->id;
+        $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2.
+        $object->usertimelimit = 0;
+
+        $comparearray[$quiz2->id] = $object;
+
+        $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id));
+    }
 }