--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Detects if an element is fullscreen.
+ *
+ * @module core/fullscreen
+ * @class fullscreen
+ * @package core
+ * @copyright 2020 University of Nottingham
+ * @author Neill Magill <neill.magill@nottingham.ac.uk>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Gets the element that is fullscreen or null if no element is fullscreen.
+ *
+ * @returns {HTMLElement}
+ */
+export const getElement = () => {
+ let element = null;
+ if (document.fullscreenElement) {
+ element = document.fullscreenElement;
+ } else if (document.mozFullscreenElement) {
+ // Fallback for older Firefox.
+ element = document.mozFullscreenElement;
+ } else if (document.msFullscreenElement) {
+ // Fallback for Edge and IE.
+ element = document.msFullscreenElement;
+ } else if (document.webkitFullscreenElement) {
+ // Fallback for Chrome, Edge and Safari.
+ element = document.webkitFullscreenElement;
+ }
+
+ return element;
+};
'core/local/aria/focuslock',
'core/pending',
'core/aria',
-], function($, Templates, Notification, KeyCodes, CustomEvents, ModalBackdrop, Event, ModalEvents, FocusLock, Pending, Aria) {
+ 'core/fullscreen'
+], function(
+ $,
+ Templates,
+ Notification,
+ KeyCodes,
+ CustomEvents,
+ ModalBackdrop,
+ Event,
+ ModalEvents,
+ FocusLock,
+ Pending,
+ Aria,
+ Fullscreen
+) {
var SELECTORS = {
CONTAINER: '[data-region="modal-container"]',
};
/**
- * Add the modal to the page, if it hasn't already been added. This includes running any
+ * Attach the modal to the correct part of the page.
+ *
+ * If it hasn't already been added it runs any
* javascript that has been cached until now.
*
* @method attachToDOM
*/
Modal.prototype.attachToDOM = function() {
+ this.getAttachmentPoint().append(this.root);
+
if (this.isAttached) {
return;
}
- $('body').append(this.root);
FocusLock.trapFocus(this.root[0]);
// If we'd cached any JS then we can run it how that the modal is
return this.getRoot().hasClass('fade');
};
+ /**
+ * Gets the jQuery wrapped node that the Modal should be attached to.
+ *
+ * @returns {jQuery}
+ */
+ Modal.prototype.getAttachmentPoint = function() {
+ return $(Fullscreen.getElement() || document.body);
+ };
+
/**
* Display this modal. The modal will be attached to the DOM if it hasn't
* already been.
this.hideFooter();
}
- if (!this.isAttached) {
- this.attachToDOM();
- }
+ this.attachToDOM();
return this.getBackdrop()
.then(function(backdrop) {
Modal.prototype.hide = function() {
this.getBackdrop().done(function(backdrop) {
FocusLock.untrapFocus();
+
if (!this.countOtherVisibleModals()) {
// Hide the backdrop if we're the last open modal.
backdrop.hide();
this.getRoot().removeClass('show').addClass('hide');
}
+ // Ensure the modal is moved onto the body node if it is still attached to the DOM.
+ if ($(document.body).find(this.getRoot()).length) {
+ $(document.body).append(this.getRoot());
+ }
+
this.root.trigger(ModalEvents.hidden, this);
}.bind(this));
};
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define(['jquery', 'core/templates', 'core/notification'],
- function($, Templates, Notification) {
+define(['jquery', 'core/templates', 'core/notification', 'core/fullscreen'],
+ function($, Templates, Notification, Fullscreen) {
var SELECTORS = {
ROOT: '[data-region="modal-backdrop"]',
return this.root;
};
+ /**
+ * Gets the jQuery wrapped node that the Modal should be attached to.
+ *
+ * @returns {jQuery}
+ */
+ ModalBackdrop.prototype.getAttachmentPoint = function() {
+ return $(Fullscreen.getElement() || document.body);
+ };
+
/**
* Add the modal backdrop to the page, if it hasn't already been added.
*
* @method attachToDOM
*/
ModalBackdrop.prototype.attachToDOM = function() {
+ this.getAttachmentPoint().append(this.root);
+
if (this.isAttached) {
return;
}
- $('body').append(this.root);
this.isAttached = true;
};
return;
}
- if (!this.isAttached) {
- this.attachToDOM();
- }
+ this.attachToDOM();
this.root.removeClass('hide').addClass('show');
};
} else {
this.getRoot().removeClass('show').addClass('hide');
}
+
+ // Ensure the modal is moved onto the body node if it is still attached to the DOM.
+ if ($(document.body).find(this.getRoot()).length) {
+ $(document.body).append(this.getRoot());
+ }
};
/**
upgrade_main_savepoint(true, 2021052500.36);
}
+ if ($oldversion < 2021052500.42) {
+ // Get all lessons that are set with a completion criteria of 'requires grade' but with no grade type set.
+ $sql = "SELECT cm.id
+ FROM {course_modules} cm
+ JOIN {lesson} l ON l.id = cm.instance
+ JOIN {modules} m ON m.id = cm.module
+ WHERE m.name = :name AND cm.completiongradeitemnumber IS NOT NULL AND l.grade = :grade";
+
+ do {
+ if ($invalidconfigrations = $DB->get_records_sql($sql, ['name' => 'lesson', 'grade' => 0], 0, 1000)) {
+ list($insql, $inparams) = $DB->get_in_or_equal(array_keys($invalidconfigrations), SQL_PARAMS_NAMED);
+ $DB->set_field_select('course_modules', 'completiongradeitemnumber', null, "id $insql", $inparams);
+ }
+ } while ($invalidconfigrations);
+
+ upgrade_main_savepoint(true, 2021052500.42);
+ }
+
return true;
}
--- /dev/null
+@mod @mod_forum @javascript
+Feature: View discussion while grading in a forum
+ In order to grade efficiently
+ As a teacher
+ I want to be able to see the full discussion the student was taking part in.
+
+ Background:
+ # Student 1 needs to be created first or they will not be the first user on the grading screen.
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student.1@example.com |
+ | student2 | Student | 2 | student.2@example.com |
+ | teacher | Teacher | Tom | 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 |
+ | teacher | C1 | editingteacher |
+ And the following "activity" exists:
+ | activity | forum |
+ | name | Gradable forum |
+ | intro | Standard forum description |
+ | course | C1 |
+ | idnumber | forum1 |
+ | grade_forum | 100 |
+ | scale | 100 |
+ # If there is more than one pots for Student 1 the test will not be able to select the
+ # correct View discussion link, as there is no selector for thier container.
+ And the following forum discussions exist in course "Course 1":
+ | forum | user | name | message |
+ | Gradable forum | student1 | My topic | This is the thing I posted about |
+ And the following forum replies exist in course "Course 1":
+ | forum | user | discussion | message |
+ | Gradable forum | student2 | My topic | I disagree |
+
+ Scenario: Viewing a discussion
+ Given I log in as "teacher"
+ And I am on "Course 1" course homepage
+ And I follow "Gradable forum"
+ And I press "Grade users"
+ When I press "View discussion"
+ Then I should see "I disagree" in the "My topic" "dialogue"
+ And I click on "Cancel" "button" in the "My topic" "dialogue"
+ And I should not see "I disagree"
+
+ Scenario: Viewing a discussion while grading is fullscreen
+ Given I log in as "teacher"
+ And I am on "Course 1" course homepage
+ And I follow "Gradable forum"
+ And I press "Grade users"
+ # Uses the aria-label for the menu in in the grading interface.
+ And I press "Actions for the grader interface"
+ And I press "Toggle full screen"
+ When I press "View discussion"
+ Then I should see "I disagree" in the "My topic" "dialogue"
+ And I click on "Cancel" "button" in the "My topic" "dialogue"
+ And I should not see "I disagree"
.then(tableRoot => {
// Always update the toggle state.
// This ensures that the bulk actions are disabled after changing the page size.
- CheckboxToggleAll.setGroupState(tableRoot, 'participants-table', checkCountButtonClicked);
+ CheckboxToggleAll.setGroupState(root, 'participants-table', checkCountButtonClicked);
return tableRoot;
})
const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10);
const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10);
- CheckboxToggleAll.updateSlavesFromMasterState(tableRoot, 'participants-table');
+ CheckboxToggleAll.updateSlavesFromMasterState(root, 'participants-table');
const pageCountStrings = [
{
And the field "Select 'Student 18x'" matches value "0"
And the field "Select 'Student 19x'" matches value "0"
+ # Pressing the "Select all X users" button should select all including the 21st user (Student 9x).
And I press "Select all 21 users"
And I should see "Student 9x"
And the field "Select 'Teacher 1x'" matches value "1"
And the field "Select 'Student 17x'" matches value "1"
And the field "Select 'Student 18x'" matches value "1"
And the field "Select 'Student 19x'" matches value "1"
+ And the "With selected users..." "select" should be enabled
And I click on "Deselect all" "checkbox"
And the field "Select 'Teacher 1x'" matches value "0"