MDL-66722 mod_forum: Pull grader into UI
authorAndrew Nicols <andrew@nicols.co.uk>
Thu, 26 Sep 2019 04:25:27 +0000 (12:25 +0800)
committerMathew May <mathewm@hotmail.co.nz>
Wed, 30 Oct 2019 02:23:40 +0000 (10:23 +0800)
Part of MDL-66074

15 files changed:
mod/forum/amd/build/grades/grader.min.js
mod/forum/amd/build/grades/grader.min.js.map
mod/forum/amd/build/local/grades/grader.min.js
mod/forum/amd/build/local/grades/grader.min.js.map
mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js [new file with mode: 0644]
mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js.map [new file with mode: 0644]
mod/forum/amd/build/local/grades/local/grader/selectors.min.js
mod/forum/amd/build/local/grades/local/grader/selectors.min.js.map
mod/forum/amd/build/local/grades/local/grader/user_picker.min.js
mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map
mod/forum/amd/src/grades/grader.js
mod/forum/amd/src/local/grades/grader.js
mod/forum/amd/src/local/grades/local/grader/gradingpanel.js [new file with mode: 0644]
mod/forum/amd/src/local/grades/local/grader/selectors.js
mod/forum/amd/src/local/grades/local/grader/user_picker.js

index d1ba5d5..1c6d8ee 100644 (file)
Binary files a/mod/forum/amd/build/grades/grader.min.js and b/mod/forum/amd/build/grades/grader.min.js differ
index 1f3914b..e643030 100644 (file)
Binary files a/mod/forum/amd/build/grades/grader.min.js.map and b/mod/forum/amd/build/grades/grader.min.js.map differ
index d140999..9a7b35d 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/grader.min.js and b/mod/forum/amd/build/local/grades/grader.min.js differ
index 738c4c0..8e26ad9 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/grader.min.js.map and b/mod/forum/amd/build/local/grades/grader.min.js.map differ
diff --git a/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js b/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js
new file mode 100644 (file)
index 0000000..f3cd712
Binary files /dev/null and b/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js differ
diff --git a/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js.map b/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js.map
new file mode 100644 (file)
index 0000000..a0b9112
Binary files /dev/null and b/mod/forum/amd/build/local/grades/local/grader/gradingpanel.min.js.map differ
index e0cdf7d..2c1440d 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/local/grader/selectors.min.js and b/mod/forum/amd/build/local/grades/local/grader/selectors.min.js differ
index c5f7688..d3dfc8f 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/local/grader/selectors.min.js.map and b/mod/forum/amd/build/local/grades/local/grader/selectors.min.js.map differ
index 4b3772b..93e3993 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js and b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js differ
index 73d7ab5..ecf5061 100644 (file)
Binary files a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map and b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map differ
index 9a2636e..55af0b1 100644 (file)
@@ -94,8 +94,11 @@ const discussionPostMapper = discussion => {
     };
 };
 
+/**
+ * Register listeners to launch the grading panel.
+ */
 export const registerLaunchListeners = () => {
-    document.addEventListener('click', (e) => {
+    document.addEventListener('click', async(e) => {
         if (e.target.matches(Selectors.launch)) {
             const rootNode = findGradableNode(e.target);
 
@@ -104,14 +107,30 @@ export const registerLaunchListeners = () => {
             }
 
             if (rootNode.matches(Selectors.gradableItems.wholeForum)) {
-                const wholeForumFunctions = getWholeForumFunctions(rootNode.dataset.cmid);
-
-                Grader.launch(wholeForumFunctions.getUsers, wholeForumFunctions.getContentForUserId, {
-                    groupid: rootNode.dataset.groupid,
-                    initialUserId: rootNode.dataset.initialuserid,
-                });
-
+                // Note: The preventDefault must be before any async function calls because the function becomes async
+                // at that point and the default action is implemented.
                 e.preventDefault();
+
+                const data = rootNode.dataset;
+                const wholeForumFunctions = getWholeForumFunctions(data.cmid);
+                const gradingPanelFunctions = await Grader.getGradingPanelFunctions(
+                    'mod_forum',
+                    data.contextid,
+                    data.gradingComponent,
+                    data.gradingComponentSubtype,
+                    data.gradableItemtype
+                );
+
+                Grader.launch(
+                    wholeForumFunctions.getUsers,
+                    wholeForumFunctions.getContentForUserId,
+                    gradingPanelFunctions.getter,
+                    gradingPanelFunctions.setter,
+                    {
+                        groupid: data.groupid,
+                        initialUserId: data.initialuserid,
+                    }
+                );
             } else {
                 throw Error('Unable to find a valid gradable item');
             }
index 5f2c958..bbe702f 100644 (file)
@@ -26,6 +26,7 @@ import Templates from 'core/templates';
 import Selectors from './local/grader/selectors';
 import * as UserPicker from './local/grader/user_picker';
 import {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/fullscreen';
+import getGradingPanelFunctions from './local/grader/gradingpanel';
 
 const templateNames = {
     grader: {
@@ -38,16 +39,26 @@ const displayUserPicker = (root, html) => {
     Templates.replaceNodeContents(pickerRegion, html, '');
 };
 
-const getUpdateUserContentFunction = (root, getContentForUser) => {
+const fetchContentFromRender = (html, js) => {
+    return [html, js];
+};
+
+const getUpdateUserContentFunction = (root, getContentForUser, getGradeForUser) => {
     return async(user) => {
         const [
-            {html, js},
+            [html, js],
+            userGrade,
         ] = await Promise.all([
-            getContentForUser(user.id).then((html, js) => {
-                return {html, js};
-            }),
+            getContentForUser(user.id).then(fetchContentFromRender),
+            getGradeForUser(user.id),
         ]);
         Templates.replaceNodeContents(root.querySelector(Selectors.regions.moduleReplace), html, js);
+
+        const [
+            gradingPanelHtml,
+            gradingPanelJS
+        ] = await Templates.render(userGrade.templatename, userGrade.grade).then(fetchContentFromRender);
+        Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanel), gradingPanelHtml, gradingPanelJS);
     };
 };
 
@@ -67,8 +78,14 @@ const registerEventListeners = (graderLayout) => {
     });
 };
 
+const getSaveUserGradeFunction = (root, setGradeForUser) => {
+    return user => {
+        return setGradeForUser(user.id, root.querySelector(Selectors.regions.gradingPanel));
+    };
+};
+
 // Make this explicit rather than object
-export const launch = async(getListOfUsers, getContentForUser, {
+export const launch = async(getListOfUsers, getContentForUser, getGradeForUser, setGradeForUser, {
     initialUserId = 0,
 } = {}) => {
 
@@ -85,8 +102,16 @@ export const launch = async(getListOfUsers, getContentForUser, {
 
     Templates.replaceNodeContents(graderContainer, graderHTML, '');
     registerEventListeners(graderLayout);
-    const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser);
+    const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser, getGradeForUser);
+
+    const pickerHTML = await UserPicker.buildPicker(
+        userList,
+        initialUserId,
+        updateUserContent,
+        getSaveUserGradeFunction(graderContainer, setGradeForUser)
+    );
 
-    const pickerHTML = await UserPicker.buildPicker(userList, initialUserId, updateUserContent);
     displayUserPicker(graderContainer, pickerHTML);
 };
+
+export {getGradingPanelFunctions};
diff --git a/mod/forum/amd/src/local/grades/local/grader/gradingpanel.js b/mod/forum/amd/src/local/grades/local/grader/gradingpanel.js
new file mode 100644 (file)
index 0000000..5d765f8
--- /dev/null
@@ -0,0 +1,48 @@
+// 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/>.
+
+/**
+ * Grading panel functions.
+ *
+ * @module     mod_forum/local/grades/local/grader/gradingpnael
+ * @package    mod_forum
+ * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Get the grade panel setter and getter for the current component.
+ *
+ * @param {String} component The component being graded
+ * @param {Number} context The contextid of the thing being graded
+ * @param {String} gradingComponent The thing providing the grading type
+ * @param {String} gradingSubtype The subtype fo the grading component
+ * @param {String} itemName The name of the thing being graded
+ * @return {Object}
+ */
+export default async(component, context, gradingComponent, gradingSubtype, itemName) => {
+    let gradingMethodHandler = `${gradingComponent}/grades/grader/gradingpanel`;
+    if (gradingSubtype) {
+        gradingMethodHandler += `/${gradingSubtype}`;
+    }
+
+    const GradingMethod = await import(gradingMethodHandler);
+
+    return {
+        getter: userId => GradingMethod.fetchCurrentGrade(component, context, itemName, userId),
+        setter: (userId, formData) => GradingMethod.storeCurrentGrade(component, context, itemName, userId, formData),
+    };
+};
+
index 4b407a8..12fa562 100644 (file)
@@ -34,6 +34,7 @@ export default {
     regions: {
         moduleReplace: getDataSelector('region', 'module_content'),
         pickerRegion: getDataSelector('region', 'user_picker'),
+        gradingPanel: getDataSelector('region', 'grade'),
     },
 };
 
index 40b8246..51f3818 100644 (file)
@@ -44,17 +44,18 @@ const showUser = async(root, users, currentUserIndex, showUserCallback) => {
     Templates.replaceNodeContents(userRegion, html, '');
 };
 
-const bindEvents = (root, users, currentUserIndex, showUserCallback) => {
+const bindEvents = (root, users, currentUserIndex, showUserCallback, saveUserCallback) => {
     root.addEventListener('click', (e) => {
         const button = e.target.closest(Selectors.actions.changeUser);
         if (button) {
+            saveUserCallback(users[currentUserIndex]);
             currentUserIndex += parseInt(button.dataset.direction);
             showUser(root, users, currentUserIndex, showUserCallback);
         }
     });
 };
 
-export const buildPicker = async(users, currentUserID, showUserCallback) => {
+export const buildPicker = async(users, currentUserID, showUserCallback, saveUserCallback) => {
     let root = document.createElement('div');
 
     const [html] = await Promise.all([renderNavigator()]);
@@ -66,7 +67,7 @@ export const buildPicker = async(users, currentUserID, showUserCallback) => {
 
     await showUser(root, users, currentUserIndex, showUserCallback);
 
-    bindEvents(root, users, currentUserIndex, showUserCallback);
+    bindEvents(root, users, currentUserIndex, showUserCallback, saveUserCallback);
 
     return root;
 };