Merge branch 'MDL-49932_master' of git://github.com/markn86/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Thu, 30 Apr 2015 02:25:27 +0000 (10:25 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Thu, 30 Apr 2015 02:25:27 +0000 (10:25 +0800)
19 files changed:
badges/lib.php
course/user.php
grade/report/user/lib.php
lib/badgeslib.php
lib/gdlib.php
lib/myprofilelib.php
lib/navigationlib.php
lib/pagelib.php
lib/tests/moodlelib_test.php
lib/upgrade.txt
mod/assign/locallib.php
mod/assign/tests/behat/steps_blind_marking.feature [new file with mode: 0644]
mod/quiz/classes/structure.php
mod/quiz/tests/behat/editing_require_previous.feature
report/log/user.php
report/outline/user.php
report/stats/lib.php
report/stats/user.php
report/usersessions/user.php

index 308c143..6e1be22 100644 (file)
@@ -42,8 +42,8 @@ function core_badges_myprofile_navigation(\core_user\output\myprofile\tree $tree
         return true;
     }
 
-    // Add category.
-    $category = new core_user\output\myprofile\category('badges', get_string('badges', 'badges'), null);
+    // Add category. This node should appear after 'contact' so that administration block appears towards the end. Refer MDL-49928.
+    $category = new core_user\output\myprofile\category('badges', get_string('badges', 'badges'), 'contact');
     $tree->add_category($category);
 
     // Determine context.
index 553267d..a39a622 100644 (file)
@@ -146,14 +146,19 @@ switch ($mode) {
             $url = new moodle_url('/course/user.php', array('id' => $id, 'user' => $user->id, 'mode' => $mode));
             $reportnode = $activenode->add(get_string('pluginname', 'gradereport_user'), $url);
         } else {
+            if ($course->id == SITEID) {
+                $activenode = $PAGE->navigation->find('user' . $user->id, null);
+            } else {
+                $currentcoursenode = $PAGE->navigation->find('currentcourse', null);
+                $activenode = $currentcoursenode->find_active_node();
+            }
             // Check to see if the active node is a user name.
-            $currentcoursenode = $PAGE->navigation->find('currentcourse', null);
-            $activenode = $currentcoursenode->find_active_node();
-            if (strpos($activenode->key, 'user') === false) { // No user name found.
+            if (!preg_match('/^user\d{0,}$/', $activenode->key)) { // No user name found.
                 $userurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id));
                 // Add the user name.
                 $PAGE->navbar->add(fullname($user), $userurl, navigation_node::TYPE_SETTING);
             }
+            $PAGE->navbar->add(get_string('report'));
             $gradeurl = new moodle_url('/course/user.php', array('id' => $id, 'user' => $user->id, 'mode' => $mode));
             // Add the 'grades' node to the navbar.
             $navbar = $PAGE->navbar->add(get_string('grades', 'grades'), $gradeurl, navigation_node::TYPE_SETTING);
index c351800..49cb1a9 100644 (file)
@@ -1171,4 +1171,47 @@ function grade_report_user_profilereport($course, $user, $viewasuser = false) {
     }
 }
 
-
+/**
+ * Add nodes to myprofile page.
+ *
+ * @param \core_user\output\myprofile\tree $tree Tree object
+ * @param stdClass $user user object
+ * @param bool $iscurrentuser
+ * @param stdClass $course Course object
+ */
+function gradereport_user_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) {
+    global $CFG, $USER;
+    if (empty($course)) {
+        // We want to display these reports under the site context.
+        $course = get_fast_modinfo(SITEID)->get_course();
+    }
+    $usercontext = context_user::instance($user->id);
+    $anyreport = has_capability('moodle/user:viewuseractivitiesreport', $usercontext);
+
+    // Start capability checks.
+    if ($anyreport || ($course->showreports && $user->id == $USER->id)) {
+        // Add grade hardcoded grade report if necessary.
+        $gradeaccess = false;
+        $coursecontext = context_course::instance($course->id);
+        if (has_capability('moodle/grade:viewall', $coursecontext)) {
+            // Can view all course grades.
+            $gradeaccess = true;
+        } else if ($course->showgrades) {
+            if ($iscurrentuser && has_capability('moodle/grade:view', $coursecontext)) {
+                // Can view own grades.
+                $gradeaccess = true;
+            } else if (has_capability('moodle/grade:viewall', $usercontext)) {
+                // Can view grades of this user - parent most probably.
+                $gradeaccess = true;
+            } else if ($anyreport) {
+                // Can view grades of this user - parent most probably.
+                $gradeaccess = true;
+            }
+        }
+        if ($gradeaccess) {
+            $url = new moodle_url('/course/user.php', array('mode' => 'grade', 'id' => $course->id, 'user' => $user->id));
+            $node = new core_user\output\myprofile\node('reports', 'grade', get_string('grade'), null, $url);
+            $tree->add_node($node);
+        }
+    }
+}
index 622d7f0..c8af3f8 100644 (file)
@@ -966,7 +966,7 @@ function badges_process_badge_image(badge $badge, $iconfile) {
     require_once($CFG->libdir. '/gdlib.php');
 
     if (!empty($CFG->gdversion)) {
-        process_new_icon($badge->get_context(), 'badges', 'badgeimage', $badge->id, $iconfile);
+        process_new_icon($badge->get_context(), 'badges', 'badgeimage', $badge->id, $iconfile, true);
         @unlink($iconfile);
 
         // Clean up file draft area after badge image has been saved.
index f9ac929..948d783 100644 (file)
@@ -88,16 +88,20 @@ function imagecopybicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $d
 }
 
 /**
- * Stores optimised icon images in icon file area
+ * Stores optimised icon images in icon file area.
+ *
+ * Since 2.9 this function will generate an icon in the same format as the original file when possible.
+ * To counter that behaviour, you can use the argument $preferpng to generate a PNG icon.
  *
  * @param context $context
  * @param string $component
  * @param string filearea
  * @param int $itemid
  * @param string $originalfile
+ * @param boolean $preferpng When true, it will try to generate a PNG file regardless of the original file.
  * @return mixed new unique revision number or false if not saved
  */
-function process_new_icon($context, $component, $filearea, $itemid, $originalfile) {
+function process_new_icon($context, $component, $filearea, $itemid, $originalfile, $preferpng = false) {
     global $CFG;
 
     if (!is_file($originalfile)) {
@@ -139,7 +143,7 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
                 return false;
             }
             // If the user uploads a jpeg them we should process as a jpeg if possible.
-            if (function_exists('imagejpeg')) {
+            if (!$preferpng && function_exists('imagejpeg')) {
                 $imagefnc = 'imagejpeg';
                 $imageext = '.jpg';
                 $filters = null; // Not used.
index 4ca840b..124a093 100644 (file)
@@ -43,7 +43,8 @@ function core_myprofile_navigation(core_user\output\myprofile\tree $tree, $user,
     $courseid = !empty($course) ? $course->id : SITEID;
 
     $contactcategory = new core_user\output\myprofile\category('contact', get_string('userdetails'));
-    $coursedetailscategory = new core_user\output\myprofile\category('coursedetails', get_string('coursedetails'), 'contact');
+    // No after property specified intentionally. It is a hack to make administration block appear towards the end. Refer MDL-49928.
+    $coursedetailscategory = new core_user\output\myprofile\category('coursedetails', get_string('coursedetails'));
     $miscategory = new core_user\output\myprofile\category('miscellaneous', get_string('miscellaneous'), 'coursedetails');
     $reportcategory = new core_user\output\myprofile\category('reports', get_string('reports'), 'miscellaneous');
     $admincategory = new core_user\output\myprofile\category('administration', get_string('administration'), 'reports');
index f0163cd..54bc395 100644 (file)
@@ -4220,7 +4220,7 @@ class settings_navigation extends navigation_node {
 
             // Add the user profile to the dashboard.
             $profilenode = $dashboard->add(get_string('myprofile'), new moodle_url('/user/profile.php',
-                    array('id' => $user->id)), null, 'myprofile');
+                    array('id' => $user->id)), self::TYPE_SETTING, null, 'myprofile');
 
             if (!empty($CFG->navadduserpostslinks)) {
                 // Add nodes for forum posts and discussions if the user can view either or both
index 8f66ba5..0c0e99b 100644 (file)
@@ -1927,4 +1927,40 @@ class moodle_page {
         }
         return $region;
     }
+
+    /**
+     * Add a report node and a specific report to the navigation.
+     *
+     * @param int $userid The user ID that we are looking to add this report node to.
+     * @param array $nodeinfo Name and url of the final node that we are creating.
+     */
+    public function add_report_nodes($userid, $nodeinfo) {
+        global $USER;
+        // Try to find the specific user node.
+        $newusernode = $this->navigation->find('user' . $userid, null);
+        $reportnode = null;
+        $navigationnodeerror =
+                'Could not find the navigation node requested. Please check that the node you are looking for exists.';
+        if ($userid != $USER->id) {
+            // Check that we have a valid node.
+            if (empty($newusernode)) {
+                // Throw an error if we ever reach here.
+                throw new coding_exception($navigationnodeerror);
+            }
+            // Add 'Reports' to the user node.
+            $reportnode = $newusernode->add(get_string('reports'));
+        } else {
+            // We are looking at our own profile.
+            $myprofilenode = $this->settingsnav->find('myprofile', null);
+            // Check that we do end up with a valid node.
+            if (empty($myprofilenode)) {
+                // Throw an error if we ever reach here.
+                throw new coding_exception($navigationnodeerror);
+            }
+            // Add 'Reports' to our node.
+            $reportnode = $myprofilenode->add(get_string('reports'));
+        }
+        // Finally add the report to the navigation tree.
+        $reportnode->add($nodeinfo['name'], $nodeinfo['url'], navigation_node::TYPE_COURSE);
+    }
 }
index ce944fa..54b08df 100644 (file)
@@ -1364,70 +1364,70 @@ class core_moodlelib_testcase extends advanced_testcase {
         // DST switching in Prague.
         // From 2AM to 3AM in 1989.
         $date = new DateTime('1989-03-26T01:59:00+01:00');
-        $this->assertSame('Sunday, 26 March 1989, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 26 March 1989, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('1989-03-26T02:01:00+01:00');
-        $this->assertSame('Sunday, 26 March 1989, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 26 March 1989, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         // From 3AM to 2AM in 1989 - not the same as the west Europe.
         $date = new DateTime('1989-09-24T01:59:00+01:00');
-        $this->assertSame('Sunday, 24 September 1989, 2:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 24 September 1989, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('1989-09-24T02:01:00+01:00');
-        $this->assertSame('Sunday, 24 September 1989, 2:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 24 September 1989, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         // From 2AM to 3AM in 2014.
         $date = new DateTime('2014-03-30T01:59:00+01:00');
-        $this->assertSame('Sunday, 30 March 2014, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 30 March 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('2014-03-30T02:01:00+01:00');
-        $this->assertSame('Sunday, 30 March 2014, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 30 March 2014, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         // From 3AM to 2AM in 2014.
         $date = new DateTime('2014-10-26T01:59:00+01:00');
-        $this->assertSame('Sunday, 26 October 2014, 2:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 26 October 2014, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('2014-10-26T02:01:00+01:00');
-        $this->assertSame('Sunday, 26 October 2014, 2:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 26 October 2014, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         // From 2AM to 3AM in 2020.
         $date = new DateTime('2020-03-29T01:59:00+01:00');
-        $this->assertSame('Sunday, 29 March 2020, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 29 March 2020, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('2020-03-29T02:01:00+01:00');
-        $this->assertSame('Sunday, 29 March 2020, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 29 March 2020, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         // From 3AM to 2AM in 2020.
         $date = new DateTime('2020-10-25T01:59:00+01:00');
-        $this->assertSame('Sunday, 25 October 2020, 2:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 25 October 2020, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
         $date = new DateTime('2020-10-25T02:01:00+01:00');
-        $this->assertSame('Sunday, 25 October 2020, 2:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Europe/Prague'));
+        $this->assertSame('Sunday, 25 October 2020, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague'));
 
         // DST switching in NZ.
         // From 3AM to 2AM in 2015.
         $date = new DateTime('2015-04-05T02:59:00+13:00');
-        $this->assertSame('Sunday, 5 April 2015, 2:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Pacific/Auckland'));
+        $this->assertSame('Sunday, 5 April 2015, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland'));
         $date = new DateTime('2015-04-05T03:01:00+13:00');
-        $this->assertSame('Sunday, 5 April 2015, 2:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Pacific/Auckland'));
+        $this->assertSame('Sunday, 5 April 2015, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland'));
         // From 2AM to 3AM in 2009.
         $date = new DateTime('2015-09-27T01:59:00+12:00');
-        $this->assertSame('Sunday, 27 September 2015, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Pacific/Auckland'));
+        $this->assertSame('Sunday, 27 September 2015, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland'));
         $date = new DateTime('2015-09-27T02:01:00+12:00');
-        $this->assertSame('Sunday, 27 September 2015, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Pacific/Auckland'));
+        $this->assertSame('Sunday, 27 September 2015, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland'));
 
         // DST switching in Perth.
         // From 3AM to 2AM in 2009.
         $date = new DateTime('2008-03-30T01:59:00+08:00');
-        $this->assertSame('Sunday, 30 March 2008, 2:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Australia/Perth'));
+        $this->assertSame('Sunday, 30 March 2008, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth'));
         $date = new DateTime('2008-03-30T02:01:00+08:00');
-        $this->assertSame('Sunday, 30 March 2008, 2:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Australia/Perth'));
+        $this->assertSame('Sunday, 30 March 2008, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth'));
         // From 2AM to 3AM in 2009.
         $date = new DateTime('2008-10-26T01:59:00+08:00');
-        $this->assertSame('Sunday, 26 October 2008, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Australia/Perth'));
+        $this->assertSame('Sunday, 26 October 2008, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth'));
         $date = new DateTime('2008-10-26T02:01:00+08:00');
-        $this->assertSame('Sunday, 26 October 2008, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'Australia/Perth'));
+        $this->assertSame('Sunday, 26 October 2008, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth'));
 
         // DST switching in US.
         // From 2AM to 3AM in 2014.
         $date = new DateTime('2014-03-09T01:59:00-05:00');
-        $this->assertSame('Sunday, 9 March 2014, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'America/New_York'));
+        $this->assertSame('Sunday, 9 March 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York'));
         $date = new DateTime('2014-03-09T02:01:00-05:00');
-        $this->assertSame('Sunday, 9 March 2014, 3:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'America/New_York'));
+        $this->assertSame('Sunday, 9 March 2014, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York'));
         // From 3AM to 2AM in 2014.
         $date = new DateTime('2014-11-02T01:59:00-04:00');
-        $this->assertSame('Sunday, 2 November 2014, 1:59', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'America/New_York'));
+        $this->assertSame('Sunday, 2 November 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York'));
         $date = new DateTime('2014-11-02T02:01:00-04:00');
-        $this->assertSame('Sunday, 2 November 2014, 1:01', userdate($date->getTimestamp(), '%A, %d %B %Y,%k:%M', 'America/New_York'));
+        $this->assertSame('Sunday, 2 November 2014, 01:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York'));
     }
 
     public function test_make_timestamp() {
index bc4d1e5..6622003 100644 (file)
@@ -39,6 +39,10 @@ information provided here is intended especially for developers.
 * The outdated lib/google/Google_Client.php and related files have been completely removed. To use
   the new client, read lib/google/readme_moodle.txt, please.
 * profile_display_badges() has been deprecated. See MDL-48935 for details.
+* Added a new method add_report_nodes() to pagelib.php. If you are looking to add links to the user profile page under the heading "Reports"
+  then please use this function to ensure that the breadcrumb and navigation block are created properly for all user profile pages.
+* process_new_icon() now does not always return a PNG file. When possible, it will try to keep the format of the original file.
+  Set the new argument $preferpng to true to force PNG. See MDL-46763 and MDL-50041 for details.
 
 === 2.8 ===
 
index d38fa6f..79d509f 100644 (file)
@@ -5503,6 +5503,7 @@ class assign {
 
         $adminconfig = $this->get_admin_config();
         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
+        $gradebookplugin = str_replace('assignfeedback_', '', $gradebookplugin);
         $grades = $DB->get_records('assign_grades', array('assignment'=>$this->get_instance()->id));
 
         $plugin = $this->get_feedback_plugin_by_type($gradebookplugin);
diff --git a/mod/assign/tests/behat/steps_blind_marking.feature b/mod/assign/tests/behat/steps_blind_marking.feature
new file mode 100644 (file)
index 0000000..3b4d826
--- /dev/null
@@ -0,0 +1,99 @@
+@mod @mod_assign
+Feature: Assignments correctly add feedback to the grade report when workflow and blind marking are enabled.
+  In order to give students feedback when blind marking
+  As a teacher
+  I should be able to reveal student identities at any time and have my feedback show
+  to the student in the gradebook when the grades are in a released state.
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | category | groupmode |
+      | Course 1 | C1 | 0 | 1 |
+    And the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@example.com |
+      | student1 | Student | 1 | student1@example.com |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+    # Add the assignment.
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Assignment" to section "1" and I fill the form with:
+      | Assignment name | Test assignment name |
+      | Description | Test assignment description |
+      | Online text | 1 |
+      | File submissions | 0 |
+      | Use marking workflow | Yes |
+      | Blind marking | Yes |
+    And I log out
+    # Add a submission.
+    And I log in as "student1"
+    And I follow "Course 1"
+    When I follow "Test assignment name"
+    Then I should not see "Feedback"
+    And I should see "Not marked" in the "Grading status" "table_row"
+    And I press "Add submission"
+    And I set the following fields to these values:
+      | Online text | I'm the student's first submission |
+    And I press "Save changes"
+    And I log out
+    # Mark the submission.
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Test assignment name"
+    And I follow "View/grade all submissions"
+    And I should see "Not marked" in the "Participant 1" "table_row"
+    And I click on "Grade Participant 1" "link" in the "Participant 1" "table_row"
+    And I set the field "Grade out of 100" to "50"
+    And I set the field "Marking workflow state" to "In review"
+    And I set the field "Feedback comments" to "Great job! Lol, not really."
+    And I press "Save changes"
+    And I press "Continue"
+    And I should see "In review" in the "Participant 1" "table_row"
+
+  @javascript
+  Scenario: Student identities are revealed after releasing the grades.
+    When I click on "Grade Participant 1" "link" in the "Participant 1" "table_row"
+    And I set the field "Marking workflow state" to "Ready for release"
+    And I press "Save changes"
+    And I press "Continue"
+    And I should see "Ready for release" in the "Participant 1" "table_row"
+    And I click on "Grade Participant 1" "link" in the "Participant 1" "table_row"
+    And I set the field "Marking workflow state" to "Released"
+    And I press "Save changes"
+    And I press "Continue"
+    And I should see "Released" in the "Participant 1" "table_row"
+    And I set the field "Grading action" to "Reveal student identities"
+    And I press "Continue"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I set the field "Grade report" to "User report"
+    Then I should see "50"
+    And I should see "Great job! Lol, not really."
+
+  @javascript
+  Scenario: Student identities are revealed before releasing the grades.
+    When I click on "Grade Participant 1" "link" in the "Participant 1" "table_row"
+    And I set the field "Marking workflow state" to "Ready for release"
+    And I press "Save changes"
+    And I press "Continue"
+    And I should see "Ready for release" in the "Participant 1" "table_row"
+    And I set the field "Grading action" to "Reveal student identities"
+    And I press "Continue"
+    And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
+    And I set the field "Marking workflow state" to "Released"
+    And I press "Save changes"
+    And I press "Continue"
+    And I should see "Released" in the "Student 1" "table_row"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I set the field "Grade report" to "User report"
+    Then I should see "50"
+    And I should see "Great job! Lol, not really."
index 477ffa2..a02fabe 100644 (file)
@@ -182,7 +182,8 @@ class structure {
         }
 
         if ($this->get_question_type_for_slot($slotnumber) == 'random') {
-            return true;
+            return \question_engine::can_questions_finish_during_the_attempt(
+                    $this->quizobj->get_quiz()->preferredbehaviour);
         }
 
         if (isset($this->slotsinorder[$slotnumber]->canfinish)) {
index 8a8f353..6e17842 100644 (file)
@@ -54,6 +54,24 @@ Feature: Edit quizzes where some questions require the previous one to have been
     And I follow "Edit quiz"
     Then "This question cannot be attempted until the previous question has been completed." "link" should be visible
 
+  @javascript
+  Scenario: A question can depend on a random question
+    Given the following "activities" exist:
+      | activity   | name   | intro              | course | idnumber | preferredbehaviour |
+      | quiz       | Quiz 1 | Quiz 1 description | C1     | quiz1    | immediatefeedback  |
+    And the following "questions" exist:
+      | questioncategory | qtype       | name                    | questiontext   |
+      | Test questions   | truefalse   | TF1                     | First question |
+      | Test questions   | random      | Random (Test questions) | 0              |
+    And quiz "Quiz 1" contains the following questions:
+      | question                | page | requireprevious |
+      | Random (Test questions) | 1    | 0               |
+      | TF1                     | 1    | 1               |
+    And I follow "Course 1"
+    And I follow "Quiz 1"
+    And I follow "Edit quiz"
+    Then "This question cannot be attempted until the previous question has been completed." "link" should be visible
+
   @javascript
   Scenario: The second question can be set to depend on the first
     Given the following "activities" exist:
@@ -104,16 +122,19 @@ Feature: Edit quizzes where some questions require the previous one to have been
       | activity   | name   | intro              | course | idnumber | preferredbehaviour |
       | quiz       | Quiz 1 | Quiz 1 description | C1     | quiz1    | deferredfeedback   |
     And the following "questions" exist:
-      | questioncategory | qtype       | name | questiontext    |
-      | Test questions   | truefalse   | TF1  | First question  |
-      | Test questions   | truefalse   | TF2  | Second question |
+      | questioncategory | qtype       | name                    | questiontext    |
+      | Test questions   | truefalse   | TF1                     | First question  |
+      | Test questions   | truefalse   | TF2                     | Second question |
+      | Test questions   | random      | Random (Test questions) | 0               |
     And quiz "Quiz 1" contains the following questions:
-      | question | page | requireprevious |
-      | TF1      | 1    | 0               |
-      | TF2      | 1    | 1               |
+      | question                | page | requireprevious |
+      | Random (Test questions) | 1    | 0               |
+      | TF1                     | 1    | 1               |
+      | TF2                     | 1    | 1               |
     And I follow "Course 1"
     And I follow "Quiz 1"
     And I follow "Edit quiz"
+    Then "be attempted" "link" in the "TF1" "list_item" should not be visible
     Then "be attempted" "link" in the "TF2" "list_item" should not be visible
 
   @javascript
index 6c00278..cde26be 100644 (file)
@@ -77,14 +77,15 @@ $PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for
 $PAGE->set_title("$course->shortname: $stractivityreport");
 
 // Create the appropriate breadcrumb.
-$newusernode = $PAGE->navigation->find('user' . $user->id, null);
-$reportnode = $newusernode->add(get_string('reports'));
-$url = new moodle_url('/report/log/user.php', array('id' => $user->id, 'course' => $course->id, 'mode' => $mode));
+$navigationnode = array(
+        'url' => new moodle_url('/report/log/user.php', array('id' => $user->id, 'course' => $course->id, 'mode' => $mode))
+    );
 if ($mode === 'today') {
-    $reportnode->add(get_string('todaylogs'), $url, navigation_node::TYPE_SETTING);
+    $navigationnode['name'] = get_string('todaylogs');
 } else {
-    $reportnode->add(get_string('alllogs'), $url, navigation_node::TYPE_SETTING);
+    $navigationnode['name'] = get_string('alllogs');
 }
+$PAGE->add_report_nodes($user->id, $navigationnode);
 
 if ($courseid == SITEID) {
     $PAGE->set_heading(fullname($user));
index 3602e98..8ef583c 100644 (file)
@@ -68,14 +68,15 @@ $PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for
 $PAGE->set_title("$course->shortname: $stractivityreport");
 
 // Create the appropriate breadcrumb.
-$newusernode = $PAGE->navigation->find('user' . $user->id, null);
-$reportnode = $newusernode->add(get_string('reports'));
-$url = new moodle_url('/report/outline/user.php', array('id' => $user->id, 'course' => $course->id, 'mode' => $mode));
+$navigationnode = array(
+        'url' => new moodle_url('/report/outline/user.php', array('id' => $user->id, 'course' => $course->id, 'mode' => $mode))
+    );
 if ($mode === 'complete') {
-    $reportnode->add(get_string('completereport'), $url, navigation_node::TYPE_COURSE);
+    $navigationnode['name'] = get_string('completereport');
 } else {
-    $reportnode->add(get_string('outlinereport'), $url, navigation_node::TYPE_COURSE);
+    $navigationnode['name'] = get_string('outlinereport');
 }
+$PAGE->add_report_nodes($user->id, $navigationnode);
 
 if ($courseid == SITEID) {
     $PAGE->set_heading(fullname($user));
index ac9fc4d..f0a069b 100644 (file)
@@ -127,3 +127,28 @@ function report_stats_supports_logstore($instance) {
     }
     return false;
 }
+
+/**
+ * Add nodes to myprofile page.
+ *
+ * @param \core_user\output\myprofile\tree $tree Tree object
+ * @param stdClass $user user object
+ * @param bool $iscurrentuser
+ * @param stdClass $course Course object
+ * @return bool
+ */
+function report_stats_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) {
+    global $CFG;
+    if (empty($CFG->enablestats)) {
+        return false;
+    }
+    if (empty($course)) {
+        // We want to display these reports under the site context.
+        $course = get_fast_modinfo(SITEID)->get_course();
+    }
+    if (report_stats_can_access_user_report($user, $course)) {
+        $url = new moodle_url('/report/stats/user.php', array('id' => $user->id, 'course' => $course->id));
+        $node = new core_user\output\myprofile\node('reports', 'stats', get_string('stats'), null, $url);
+        $tree->add_node($node);
+    }
+}
index 42360c3..48e151d 100644 (file)
@@ -35,6 +35,13 @@ $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
 $coursecontext   = context_course::instance($course->id);
 $personalcontext = context_user::instance($user->id);
 
+$pageheading = $course->fullname;
+$userfullname = fullname($user);
+if ($courseid == SITEID) {
+    $PAGE->set_context($personalcontext);
+    $pageheading = $userfullname;
+}
+
 if ($USER->id != $user->id and has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)
         and !is_enrolled($coursecontext, $USER) and is_enrolled($coursecontext, $user)) {
     //TODO: do not require parents to be enrolled in courses - this is a hack!
@@ -55,9 +62,24 @@ $PAGE->set_pagelayout('report');
 $PAGE->set_url('/report/stats/user.php', array('id'=>$user->id, 'course'=>$course->id));
 $PAGE->navigation->extend_for_user($user);
 $PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed.
+// Breadcrumb stuff.
+$navigationnode = array(
+        'name' => get_string('stats'),
+        'url' => new moodle_url('/report/stats/user.php', array('id' => $user->id, 'course' => $course->id))
+    );
+$PAGE->add_report_nodes($user->id, $navigationnode);
+
 $PAGE->set_title("$course->shortname: $stractivityreport");
-$PAGE->set_heading($course->fullname);
+$PAGE->set_heading($pageheading);
 echo $OUTPUT->header();
+if ($courseid != SITEID) {
+    echo $OUTPUT->context_header(
+            array(
+            'heading' => $userfullname,
+            'user' => $user,
+            'usercontext' => $personalcontext
+        ), 2);
+}
 
 // Trigger a user report viewed event.
 $event = \report_stats\event\user_report_viewed::create(array('context' => $coursecontext, 'relateduserid' => $user->id));
index 7c91197..52e2d85 100644 (file)
@@ -55,6 +55,12 @@ if ($delete and confirm_sesskey()) {
     redirect($PAGE->url);
 }
 
+// Create the breadcrumb.
+$PAGE->add_report_nodes($USER->id, array(
+        'name' => get_string('navigationlink', 'report_usersessions'),
+        'url' => new moodle_url('/report/usersessions/user.php')
+    ));
+
 echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('mysessions', 'report_usersessions'));