MDL-66893 mod_forum: update navigation bar in grader UI
[moodle.git] / mod / forum / amd / src / grades / grader.js
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/>.
16 /**
17  * This module will tie together all of the different calls the gradable module will make.
18  *
19  * @module     mod_forum/grades/grader
20  * @package    mod_forum
21  * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 import * as Selectors from './grader/selectors';
25 import Repository from 'mod_forum/repository';
26 import Templates from 'core/templates';
27 import * as Grader from '../local/grades/grader';
28 import Notification from 'core/notification';
29 import CourseRepository from 'core_course/repository';
30 import {relativeUrl} from 'core/url';
32 const templateNames = {
33     contentRegion: 'mod_forum/grades/grader/discussion/posts',
34 };
36 /**
37  * Curried function with CMID set, this is then used in unified grader as a fetch a users content.
38  *
39  * @param {Number} cmid
40  * @return {Function}
41  */
42 const getContentForUserIdFunction = (cmid) => (userid) => {
43     /**
44      * Given the parent function is called with the second param set execute the partially executed function.
45      *
46      * @param {Number} userid
47      */
48     return Repository.getDiscussionByUserID(userid, cmid)
49         .then(context => {
50             // Rebuild the returned data for the template.
51             context.discussions = context.discussions.map(discussionPostMapper);
53             return Templates.render(templateNames.contentRegion, context);
54         })
55         .catch(Notification.exception);
56 };
58 /**
59  * Curried function with CMID set, this is then used in unified grader as a fetch users call.
60  * The function curried fetches all users in a course for a given CMID.
61  *
62  * @param {Number} cmid
63  * @return {Array} Array of users for a given context.
64  */
65 const getUsersForCmidFunction = (cmid) => async() => {
66     const context = await CourseRepository.getUsersFromCourseModuleID(cmid);
68     return context.users;
69 };
72 const findGradableNode = node => node.closest(Selectors.gradableItem);
74 /**
75  * For a discussion we need to manipulate it's posts to hide certain UI elements.
76  *
77  * @param {Object} discussion
78  * @return {Array} name, id, posts
79  */
80 const discussionPostMapper = (discussion) => {
81     // Map postid => post.
82     const parentMap = new Map();
83     discussion.posts.parentposts.forEach(post => parentMap.set(post.id, post));
84     const userPosts = discussion.posts.userposts.map(post => {
85         post.subject = null;
86         post.readonly = true;
87         post.starter = !post.parentid;
88         post.parent = parentMap.get(post.parentid);
89         post.html.rating = null;
91         return post;
92     });
94     return {
95         id: discussion.id,
96         name: discussion.name,
97         posts: userPosts,
98     };
99 };
101 /**
102  * Launch the Grader.
103  *
104  * @param {HTMLElement} rootNode the root HTML element describing what is to be graded
105  */
106 const launchWholeForumGrading = async(rootNode) => {
107     const data = rootNode.dataset;
108     const gradingPanelFunctions = await Grader.getGradingPanelFunctions(
109         'mod_forum',
110         data.contextid,
111         data.gradingComponent,
112         data.gradingComponentSubtype,
113         data.gradableItemtype
114     );
116     await Grader.launch(
117         getUsersForCmidFunction(data.cmid),
118         getContentForUserIdFunction(data.cmid),
119         gradingPanelFunctions.getter,
120         gradingPanelFunctions.setter,
121         {
122             groupid: data.groupid,
123             initialUserId: data.initialuserid,
124             moduleName: data.name,
125             courseName: data.courseName,
126             courseUrl: relativeUrl('/course/view.php', {id: data.courseId})
127         }
128     );
129 };
131 /**
132  * Register listeners to launch the grading panel.
133  */
134 export const registerLaunchListeners = () => {
135     document.addEventListener('click', async(e) => {
136         if (e.target.matches(Selectors.launch)) {
137             const rootNode = findGradableNode(e.target);
139             if (!rootNode) {
140                 throw Error('Unable to find a gradable item');
141             }
143             if (rootNode.matches(Selectors.gradableItems.wholeForum)) {
144                 // Note: The preventDefault must be before any async function calls because the function becomes async
145                 // at that point and the default action is implemented.
146                 e.preventDefault();
147                 try {
148                     await launchWholeForumGrading(rootNode);
149                 } catch (error) {
150                     Notification.exception(error);
151                 }
152             } else {
153                 throw Error('Unable to find a valid gradable item');
154             }
155         }
156     });
157 };