MDL-66074 mod_forum: Grader documentation
[moodle.git] / mod / forum / amd / src / local / grades / grader.js
CommitLineData
bae67469
MM
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * This module will tie together all of the different calls the gradable module will make.
18 *
19 * @module mod_forum/local/grades/grader
20 * @package mod_forum
21 * @copyright 2019 Mathew May <mathew.solutions>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24import Templates from 'core/templates';
bae67469 25import Selectors from './local/grader/selectors';
45c0584c 26import getUserPicker from './local/grader/user_picker';
bae67469 27import {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/fullscreen';
f281c616 28import getGradingPanelFunctions from './local/grader/gradingpanel';
77ee8778
AN
29import {add as addToast} from 'core/toast';
30import {get_string as getString} from 'core/str';
ce1c4701 31import {failedUpdate} from 'core_grades/grades/grader/gradingpanel/normalise';
bae67469
MM
32
33const templateNames = {
34 grader: {
35 app: 'mod_forum/local/grades/grader',
ce1c4701
AN
36 gradingPanel: {
37 error: 'mod_forum/local/grades/local/grader/gradingpanel/error',
38 },
bae67469
MM
39 },
40};
41
42const displayUserPicker = (root, html) => {
43 const pickerRegion = root.querySelector(Selectors.regions.pickerRegion);
44 Templates.replaceNodeContents(pickerRegion, html, '');
45};
46
f281c616
AN
47const fetchContentFromRender = (html, js) => {
48 return [html, js];
49};
50
51const getUpdateUserContentFunction = (root, getContentForUser, getGradeForUser) => {
bae67469
MM
52 return async(user) => {
53 const [
f281c616
AN
54 [html, js],
55 userGrade,
bae67469 56 ] = await Promise.all([
f281c616
AN
57 getContentForUser(user.id).then(fetchContentFromRender),
58 getGradeForUser(user.id),
bae67469
MM
59 ]);
60 Templates.replaceNodeContents(root.querySelector(Selectors.regions.moduleReplace), html, js);
f281c616
AN
61
62 const [
63 gradingPanelHtml,
64 gradingPanelJS
65 ] = await Templates.render(userGrade.templatename, userGrade.grade).then(fetchContentFromRender);
66 Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanel), gradingPanelHtml, gradingPanelJS);
bae67469
MM
67 };
68};
69
eaee6477 70const registerEventListeners = (graderLayout, userPicker, saveGradeFunction) => {
bae67469
MM
71 const graderContainer = graderLayout.getContainer();
72 graderContainer.addEventListener('click', (e) => {
73 if (e.target.closest(Selectors.buttons.toggleFullscreen)) {
74 e.stopImmediatePropagation();
75 e.preventDefault();
76 graderLayout.toggleFullscreen();
eaee6477
AN
77
78 return;
79 }
80
81 if (e.target.closest(Selectors.buttons.closeGrader)) {
bae67469
MM
82 e.stopImmediatePropagation();
83 e.preventDefault();
84
85 graderLayout.close();
eaee6477
AN
86
87 return;
88 }
89
90 if (e.target.closest(Selectors.buttons.saveGrade)) {
91 saveGradeFunction(userPicker.currentUser);
bae67469
MM
92 }
93 });
94};
95
77ee8778
AN
96/**
97 * Get the function used to save a user grade.
98 *
ce1c4701 99 * @param {Element} root The container for the grader
77ee8778
AN
100 * @param {Function} setGradeForUser The function that will be called.
101 * @return {Function}
102 */
f281c616 103const getSaveUserGradeFunction = (root, setGradeForUser) => {
77ee8778
AN
104 return async user => {
105 try {
ce1c4701 106 root.querySelector(Selectors.regions.gradingPanelErrors).innerHTML = '';
77ee8778 107 const result = await setGradeForUser(user.id, root.querySelector(Selectors.regions.gradingPanel));
ce1c4701
AN
108 if (result.success) {
109 addToast(await getString('grades:gradesavedfor', 'mod_forum', user));
110 }
111 if (result.failed) {
112 displayGradingError(root, user, result.error);
113 }
77ee8778
AN
114
115 return result;
ce1c4701
AN
116 } catch (err) {
117 displayGradingError(root, user, err);
118
119 return failedUpdate(err);
77ee8778 120 }
f281c616
AN
121 };
122};
123
ce1c4701
AN
124/**
125 * Display a grading error, typically from a failed save.
126 *
127 * @param {Element} root The container for the grader
128 * @param {Object} user The user who was errored
129 * @param {Object} err The details of the error
130 */
131const displayGradingError = async(root, user, err) => {
132 const [
133 {html, js},
134 errorString
135 ] = await Promise.all([
136 Templates.renderForPromise(templateNames.grader.gradingPanel.error, {error: err}),
137 await getString('grades:gradesavefailed', 'mod_forum', {error: err.message, ...user}),
138 ]);
139
140 Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanelErrors), html, js);
141 addToast(errorString);
142};
143
eaee6477
AN
144/**
145 * Launch the grader interface with the specified parameters.
146 *
147 * @param {Function} getListOfUsers A function to get the list of users
148 * @param {Function} getContentForUser A function to get the content for a specific user
149 * @param {Function} getGradeForUser A function get the grade details for a specific user
150 * @param {Function} setGradeForUser A function to set the grade for a specific user
151 */
f281c616 152export const launch = async(getListOfUsers, getContentForUser, getGradeForUser, setGradeForUser, {
aa04b722 153 initialUserId = null, moduleName
bae67469
MM
154} = {}) => {
155
156 const [
157 graderLayout,
158 graderHTML,
159 userList,
160 ] = await Promise.all([
161 createFullScreenWindow({fullscreen: false, showLoader: false}),
d3b890f8 162 Templates.render(templateNames.grader.app, {moduleName: moduleName}),
bae67469
MM
163 getListOfUsers(),
164 ]);
165 const graderContainer = graderLayout.getContainer();
166
45c0584c
AN
167 const saveGradeFunction = getSaveUserGradeFunction(graderContainer, setGradeForUser);
168
bae67469 169 Templates.replaceNodeContents(graderContainer, graderHTML, '');
f281c616
AN
170 const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser, getGradeForUser);
171
45c0584c
AN
172 // Fetch the userpicker for display.
173 const userPicker = await getUserPicker(
f281c616 174 userList,
f281c616 175 updateUserContent,
aa04b722
AN
176 saveGradeFunction,
177 {
178 initialUserId,
179 },
f281c616 180 );
bae67469 181
eaee6477
AN
182 // Register all event listeners.
183 registerEventListeners(graderLayout, userPicker, saveGradeFunction);
184
45c0584c
AN
185 // Display the newly created user picker.
186 displayUserPicker(graderContainer, userPicker.rootNode);
bae67469 187};
f281c616
AN
188
189export {getGradingPanelFunctions};