Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/grades/grader
20
 * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
import * as Selectors from './grader/selectors';
24
import Repository from 'mod_forum/repository';
25
import Templates from 'core/templates';
26
import * as Grader from '../local/grades/grader';
27
import Notification from 'core/notification';
28
import CourseRepository from 'core_course/repository';
29
import {relativeUrl} from 'core/url';
30
 
31
const templateNames = {
32
    contentRegion: 'mod_forum/grades/grader/discussion/posts',
33
};
34
 
35
/**
36
 * Curried function with CMID set, this is then used in unified grader as a fetch a users content.
37
 *
38
 * @param {Number} cmid
39
 * @param {Bool} experimentalDisplayMode
40
 * @return {Function}
41
 */
42
const getContentForUserIdFunction = (cmid, experimentalDisplayMode) => (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);
52
            context.experimentaldisplaymode = experimentalDisplayMode ? true : false;
53
 
54
            return Templates.render(templateNames.contentRegion, context);
55
        })
56
        .catch(Notification.exception);
57
};
58
 
59
/**
60
 * Curried function with CMID set, this is then used in unified grader as a fetch users call.
61
 * The function curried fetches all users in a course for a given CMID.
62
 *
63
 * @param {Number} courseID
64
 * @param {Number} groupID
65
 * @param {Boolean} onlyActive Whether to fetch only the active enrolled users or all enrolled users in the course.
66
 * @return {Array} Array of users for a given context.
67
 */
68
const getGradableUsersForCourseidFunction = (courseID, groupID, onlyActive) => async() => {
69
    const context = await CourseRepository.getGradableUsersFromCourseID(courseID, groupID, onlyActive);
70
 
71
    return context.users;
72
};
73
 
74
 
75
const findGradableNode = node => node.closest(Selectors.gradableItem);
76
 
77
/**
78
 * For a discussion we need to manipulate it's posts to hide certain UI elements.
79
 *
80
 * @param {Object} discussion
81
 * @return {Array} name, id, posts
82
 */
83
const discussionPostMapper = (discussion) => {
84
    // Map postid => post.
85
    const parentMap = new Map();
86
    discussion.posts.parentposts.forEach(post => parentMap.set(post.id, post));
87
    const userPosts = discussion.posts.userposts.map(post => {
88
        post.readonly = true;
89
        post.hasreplies = false;
90
        post.replies = [];
91
 
92
        const parent = post.parentid ? parentMap.get(post.parentid) : null;
93
        if (parent) {
94
            parent.hasreplies = false;
95
            parent.replies = [];
96
            parent.readonly = true;
97
            post.parentauthorname = parent.author.fullname;
98
        }
99
 
100
        return {
101
            parent,
102
            post
103
        };
104
    });
105
 
106
    return {
107
        ...discussion,
108
        posts: userPosts,
109
    };
110
};
111
 
112
/**
113
 * Launch the Grader.
114
 *
115
 * @param {HTMLElement} rootNode the root HTML element describing what is to be graded
116
 * @param {object} param
117
 * @param {bool} [param.focusOnClose=null]
118
 */
119
const launchWholeForumGrading = async(rootNode, {
120
    focusOnClose = null,
121
} = {}) => {
122
    const data = rootNode.dataset;
123
    const gradingPanelFunctions = await Grader.getGradingPanelFunctions(
124
        'mod_forum',
125
        data.contextid,
126
        data.gradingComponent,
127
        data.gradingComponentSubtype,
128
        data.gradableItemtype
129
    );
130
 
131
    const groupID = data.group ? data.group : 0;
132
    const onlyActive = data.gradeOnlyActiveUsers;
133
 
134
    await Grader.launch(
135
        getGradableUsersForCourseidFunction(data.courseId, groupID, onlyActive),
136
        getContentForUserIdFunction(data.cmid, data.experimentalDisplayMode == "1"),
137
        gradingPanelFunctions.getter,
138
        gradingPanelFunctions.setter,
139
        {
140
            groupid: data.groupid,
141
            initialUserId: data.initialuserid,
142
            moduleName: data.name,
143
            courseName: data.courseName,
144
            courseUrl: relativeUrl('/course/view.php', {id: data.courseId}),
145
            sendStudentNotifications: data.sendStudentNotifications,
146
            focusOnClose,
147
        }
148
    );
149
};
150
 
151
/**
152
 * Launch the Grader.
153
 *
154
 * @param {HTMLElement} rootNode the root HTML element describing what is to be graded
155
 * @param {object} param
156
 * @param {bool} [param.focusOnClose=null]
157
 */
158
const launchViewGrading = async(rootNode, {
159
    focusOnClose = null,
160
} = {}) => {
161
    const data = rootNode.dataset;
162
    const gradingPanelFunctions = await Grader.getGradingPanelFunctions(
163
        'mod_forum',
164
        data.contextid,
165
        data.gradingComponent,
166
        data.gradingComponentSubtype,
167
        data.gradableItemtype
168
    );
169
 
170
    await Grader.view(
171
        gradingPanelFunctions.getter,
172
        data.userid,
173
        data.name,
174
        {
175
            focusOnClose,
176
        }
177
    );
178
};
179
 
180
/**
181
 * Register listeners to launch the grading panel.
182
 */
183
export const registerLaunchListeners = () => {
184
    document.addEventListener('click', async(e) => {
185
        if (e.target.matches(Selectors.launch)) {
186
            const rootNode = findGradableNode(e.target);
187
 
188
            if (!rootNode) {
189
                throw Error('Unable to find a gradable item');
190
            }
191
 
192
            if (rootNode.matches(Selectors.gradableItems.wholeForum)) {
193
                // Note: The preventDefault must be before any async function calls because the function becomes async
194
                // at that point and the default action is implemented.
195
                e.preventDefault();
196
                try {
197
                    await launchWholeForumGrading(rootNode, {
198
                        focusOnClose: e.target,
199
                    });
200
                } catch (error) {
201
                    Notification.exception(error);
202
                }
203
            } else {
204
                throw Error('Unable to find a valid gradable item');
205
            }
206
        }
207
        if (e.target.matches(Selectors.viewGrade)) {
208
            e.preventDefault();
209
            const rootNode = findGradableNode(e.target);
210
 
211
            if (!rootNode) {
212
                throw Error('Unable to find a gradable item');
213
            }
214
 
215
            if (rootNode.matches(Selectors.gradableItems.wholeForum)) {
216
                // Note: The preventDefault must be before any async function calls because the function becomes async
217
                // at that point and the default action is implemented.
218
                e.preventDefault();
219
                try {
220
                    await launchViewGrading(rootNode, {
221
                        focusOnClose: e.target,
222
                    });
223
                } catch (error) {
224
                    Notification.exception(error);
225
                }
226
            } else {
227
                throw Error('Unable to find a valid gradable item');
228
            }
229
        }
230
    });
231
};