question/amd/src/edit_tags.js

// 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.
 *
 * @deprecated since Moodle 4.0
 * @todo Final deprecation on Moodle 4.4 MDL-72438
 * @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_save_cancel',
            'core/notification',
            'core/custom_interaction_events',
            'core_question/repository',
            'core_question/selectors',
        ],
        function(
            $,
            Fragment,
            Str,
            ModalEvents,
            ModalSaveCancel,
            Notification,
            CustomEvents,
            Repository,
            QuestionSelectors
        ) {

    /**
     * Enable the save button in the footer.
     *
     * @param {object} root The container element.
     * @method enableSaveButton
     */
    var enableSaveButton = function(root) {
        root.find(QuestionSelectors.actions.save).prop('disabled', false);
    };

    /**
     * Disable the save button in the footer.
     *
     * @param {object} root The container element.
     * @method disableSaveButton
     */
    var disableSaveButton = function(root) {
        root.find(QuestionSelectors.actions.save).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(QuestionSelectors.containers.loadingIcon);

        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(QuestionSelectors.containers.loadingIcon);

        loadingIconContainer.addClass('hidden');
    };

    /**
     * Set the context Id data attribute on the modal.
     *
     * @param {Promise} modal The modal promise.
     * @param {int} contextId The context id.
     */
    var setContextId = function(modal, contextId) {
        modal.getBody().attr('data-contextid', contextId);
    };

    /**
     * Get the context Id data attribute value from the modal body.
     *
     * @param {Promise} modal The modal promise.
     * @return {int} The context id.
     */
    var getContextId = function(modal) {
        return modal.getBody().data('contextid');
    };

    /**
     * Set the question Id data attribute on the modal.
     *
     * @param {Promise} modal The modal promise.
     * @param {int} questionId The question Id.
     */
    var setQuestionId = function(modal, questionId) {
        modal.getBody().attr('data-questionid', questionId);
    };

    /**
     * Get the question Id data attribute value from the modal body.
     *
     * @param {Promise} modal The modal promise.
     * @return {int} The question Id.
     */
    var getQuestionId = function(modal) {
        return modal.getBody().data('questionid');
    };

    /**
     * Register event listeners for the module.
     *
     * @param {object} root The calendar root element
     */
    var registerEventListeners = function(root) {

        var modalPromise = ModalSaveCancel.create({
            large: false,
        }).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;
                })
                .catch(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();
                    location.reload();
                    return;
                }).catch(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;
        });

        root.on('click', QuestionSelectors.actions.edittags, function(e) {
            e.preventDefault();
            // eslint-disable-next-line promise/catch-or-return
            modalPromise.then((modal) => modal.show());
        });

        // 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, QuestionSelectors.actions.edittags, function(e) {
            var currentTarget = $(e.currentTarget);

            var questionId = currentTarget.data('questionid'),
                canTag = !!currentTarget.data('cantag'),
                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;
                })
                .catch(Notification.exception);

                // Show or hide the save button depending on whether the user
                // has the capability to edit the tags.
                if (canTag) {
                    modal.getRoot().find(QuestionSelectors.actions.save).show();
                } else {
                    modal.getRoot().find(QuestionSelectors.actions.save).hide();
                }

                setQuestionId(modal, questionId);
                setContextId(modal, contextId);

                return modal;
            }).catch(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);
        var questionId = getQuestionId(modal);
        var contextId = getContextId(modal);

        // Send the form data to the server for processing.
        return Repository.submitTagCreateUpdateForm(questionId, contextId, 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;
            })
            .catch(Notification.exception);
    };

    return {
        init: function(root) {
            window.console.warn('warn: The core_question/repository has been deprecated.' +
                'Please use qbank_tagquestion/repository instead.');
            root = $(root);
            registerEventListeners(root);
        }
    };
});