MDL-61133 core_question: add new question tags modal
authorSimey Lameze <simey@moodle.com>
Wed, 31 Jan 2018 05:46:30 +0000 (13:46 +0800)
committerSimey Lameze <simey@moodle.com>
Tue, 6 Feb 2018 02:09:10 +0000 (10:09 +0800)
lang/en/question.php
question/amd/build/edit_tags.min.js [new file with mode: 0644]
question/amd/build/repository.min.js [new file with mode: 0644]
question/amd/src/edit_tags.js [new file with mode: 0644]
question/amd/src/repository.js [new file with mode: 0644]
question/classes/bank/view.php

index b3c377d..140d4a8 100644 (file)
@@ -269,6 +269,7 @@ $string['questionsinuse'] = '(* Questions marked by an asterisk are already in u
 $string['questionsmovedto'] = 'Questions still in use moved to "{$a}" in the parent course category.';
 $string['questionsrescuedfrom'] = 'Questions saved from context {$a}.';
 $string['questionsrescuedfrominfo'] = 'These questions (some of which may be hidden) were saved when context {$a} was deleted because they are still used by some quizzes or other activities.';
+$string['questiontags'] = 'Question tags';
 $string['questiontype'] = 'Question type';
 $string['questionuse'] = 'Use question in this activity';
 $string['questionvariant'] = 'Question variant';
diff --git a/question/amd/build/edit_tags.min.js b/question/amd/build/edit_tags.min.js
new file mode 100644 (file)
index 0000000..859ba13
Binary files /dev/null and b/question/amd/build/edit_tags.min.js differ
diff --git a/question/amd/build/repository.min.js b/question/amd/build/repository.min.js
new file mode 100644 (file)
index 0000000..35de00b
Binary files /dev/null and b/question/amd/build/repository.min.js differ
diff --git a/question/amd/src/edit_tags.js b/question/amd/src/edit_tags.js
new file mode 100644 (file)
index 0000000..592a3c6
--- /dev/null
@@ -0,0 +1,233 @@
+// 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/>.
+
+/**
+ * A javascript module to handle question tags editing.
+ *
+ * @module     core_question/edit_tags
+ * @copyright  2018 Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+            'jquery',
+            'core/fragment',
+            'core/str',
+            'core/modal_events',
+            'core/modal_factory',
+            'core/notification',
+            'core/custom_interaction_events',
+            'core_question/repository',
+        ],
+        function(
+            $,
+            Fragment,
+            Str,
+            ModalEvents,
+            ModalFactory,
+            Notification,
+            CustomEvents,
+            Repository
+        ) {
+
+    var SELECTORS = {
+        TAGS_LINK: '[data-action="edittags"]',
+        SAVE_BUTTON: '[data-action="save"]',
+        LOADING_ICON: '[data-region="overlay-icon-container"]',
+    };
+
+    /**
+     * Enable the save button in the footer.
+     *
+     * @param {object} root The container element.
+     * @method enableSaveButton
+     */
+    var enableSaveButton = function(root) {
+        root.find(SELECTORS.SAVE_BUTTON).prop('disabled', false);
+    };
+
+    /**
+     * Disable the save button in the footer.
+     *
+     * @param {object} root The container element.
+     * @method disableSaveButton
+     */
+    var disableSaveButton = function(root) {
+        root.find(SELECTORS.SAVE_BUTTON).prop('disabled', true);
+    };
+
+    /**
+     * Get the serialised form data.
+     *
+     * @method getFormData
+     * @param {object} modal The modal object.
+     * @return {string} serialised form data
+     */
+    var getFormData = function(modal) {
+        return modal.getBody().find('form').serialize();
+    };
+
+    /**
+     * Set the element state to loading.
+     *
+     * @param {object} root The container element
+     * @method startLoading
+     */
+    var startLoading = function(root) {
+        var loadingIconContainer = root.find(SELECTORS.LOADING_ICON);
+
+        loadingIconContainer.removeClass('hidden');
+    };
+
+    /**
+     * Remove the loading state from the element.
+     *
+     * @param {object} root The container element
+     * @method stopLoading
+     */
+    var stopLoading = function(root) {
+        var loadingIconContainer = root.find(SELECTORS.LOADING_ICON);
+
+        loadingIconContainer.addClass('hidden');
+    };
+
+    /**
+     * Register event listeners for the module.
+     *
+     * @param {object} root The calendar root element
+     */
+    var registerEventListeners = function(root) {
+        var modalPromise = ModalFactory.create(
+            {
+                type: ModalFactory.types.SAVE_CANCEL,
+                large: false
+            },
+            [root, SELECTORS.TAGS_LINK]
+        ).then(function(modal) {
+            // All of this code only executes once, when the modal is
+            // first created. This allows us to add any code that should
+            // only be run once, such as adding event handlers to the modal.
+            Str.get_string('questiontags', 'question')
+                .then(function(string) {
+                    modal.setTitle(string);
+                    return string;
+                })
+                .fail(Notification.exception);
+
+            modal.getRoot().on(ModalEvents.save, function(e) {
+                var form = modal.getBody().find('form');
+                form.submit();
+                e.preventDefault();
+            });
+
+            modal.getRoot().on('submit', 'form', function(e) {
+                save(modal, root).then(function() {
+                    modal.hide();
+                    return;
+                }).fail(Notification.exception);
+
+                // Stop the form from actually submitting and prevent it's
+                // propagation because we have already handled the event.
+                e.preventDefault();
+                e.stopPropagation();
+            });
+
+            return modal;
+        });
+
+        // We need to add an event handler to the tags link because there are
+        // multiple links on the page and without adding a listener we don't know
+        // which one the user clicked on the show the modal.
+        root.on(CustomEvents.events.activate, SELECTORS.TAGS_LINK, function(e) {
+            var currentTarget = $(e.currentTarget);
+
+            var questionId = currentTarget.data('questionid'),
+                canEdit = !!currentTarget.data('canedit'),
+                contextId = currentTarget.data('contextid');
+
+            // This code gets called each time the user clicks the tag link
+            // so we can use it to reload the contents of the tag modal.
+            modalPromise.then(function(modal) {
+                // Display spinner and disable save button.
+                disableSaveButton(root);
+                startLoading(root);
+
+                var args = {
+                    id: questionId
+                };
+
+                var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);
+                modal.setBody(tagsFragment);
+
+                tagsFragment.then(function() {
+                        enableSaveButton(root);
+                        return;
+                    })
+                    .always(function() {
+                        // Always hide the loading spinner when the request
+                        // has completed.
+                        stopLoading(root);
+                        return;
+                    })
+                .fail(Notification.exception);
+
+                // Show or hide the save button depending on whether the user
+                // has the capability to edit the tags.
+                if (canEdit) {
+                    modal.getRoot().find(SELECTORS.SAVE_BUTTON).show();
+                } else {
+                    modal.getRoot().find(SELECTORS.SAVE_BUTTON).hide();
+                }
+
+                return modal;
+            }).fail(Notification.exception);
+
+            e.preventDefault();
+        });
+    };
+
+    /**
+     * Send the form data to the server to save question tags.
+     *
+     * @method save
+     * @param {object} modal The modal object.
+     * @param {object} root The container element.
+     * @return {object} A promise
+     */
+    var save = function(modal, root) {
+        // Display spinner and disable save button.
+        disableSaveButton(root);
+        startLoading(root);
+
+        var formData = getFormData(modal);
+
+        // Send the form data to the server for processing.
+        return Repository.submitTagCreateUpdateForm(formData)
+            .always(function() {
+                // Regardless of success or error we should always stop
+                // the loading icon and re-enable the buttons.
+                stopLoading(root);
+                enableSaveButton(root);
+                return;
+            })
+            .fail(Notification.exception);
+    };
+
+    return {
+        init: function(root) {
+            root = $(root);
+            registerEventListeners(root);
+        }
+    };
+});
diff --git a/question/amd/src/repository.js b/question/amd/src/repository.js
new file mode 100644 (file)
index 0000000..42e7272
--- /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/>.
+
+/**
+ * A javascript module to handle question ajax actions.
+ *
+ * @module     core_question/repository
+ * @class      repository
+ * @package    core_question
+ * @copyright  2017 Simey Lameze <lameze@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/ajax'], function($, Ajax) {
+
+    /**
+     * Submit the form data for the question tags form.
+     *
+     * @method submitTagCreateUpdateForm
+     * @param {string} formdata The URL encoded values from the form
+     * @return {promise}
+     */
+    var submitTagCreateUpdateForm = function(formdata) {
+        var request = {
+            methodname: 'core_question_submit_tags_form',
+            args: {
+                formdata: formdata
+            }
+        };
+
+        return Ajax.call([request])[0];
+    };
+
+    return {
+        submitTagCreateUpdateForm: submitTagCreateUpdateForm
+    };
+});
index edeef00..bc65791 100644 (file)
@@ -480,6 +480,8 @@ class view {
                 $this->baseurl, $cat, $this->cm,
                 null, $page, $perpage, $showhidden, $showquestiontext,
                 $this->contexts->having_cap('moodle/question:add'));
+
+        $PAGE->requires->js_call_amd('core_question/edit_tags', 'init', ['#questionscontainer']);
     }
 
     protected function print_choose_category_message($categoryandcontext) {
@@ -701,7 +703,7 @@ class view {
         echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
         echo \html_writer::input_hidden_params($this->baseurl);
 
-        echo '<div class="categoryquestionscontainer">';
+        echo '<div class="categoryquestionscontainer" id="questionscontainer">';
         $this->start_table();
         $rowcount = 0;
         foreach ($questions as $question) {