From 1db23e818c3feb927a79c59e3485222ddc9e546e Mon Sep 17 00:00:00 2001 From: Safat Shahin Date: Mon, 7 Jun 2021 19:17:06 +1000 Subject: [PATCH] MDL-71641 qbank_tagquestion: Add Tag question plugin to core This implementation will introduce a qbank plugin "tagquestion" which will implement the tag question actions in the question bank view by replacing the core class. Having this plugin will give users the flexibility of enabling or disabling this action. --- lib/classes/plugin_manager.php | 1 + question/amd/build/edit_tags.min.js | 2 +- question/amd/build/edit_tags.min.js.map | 2 +- question/amd/build/repository.min.js | 2 +- question/amd/build/repository.min.js.map | 2 +- question/amd/build/selectors.min.js | 2 +- question/amd/build/selectors.min.js.map | 2 +- question/amd/src/edit_tags.js | 4 + question/amd/src/repository.js | 4 + question/amd/src/selectors.js | 3 + .../tagquestion/amd/build/edit_tags.min.js | 2 + .../amd/build/edit_tags.min.js.map | 1 + .../tagquestion/amd/build/repository.min.js | 2 + .../amd/build/repository.min.js.map | 1 + .../tagquestion/amd/build/selectors.min.js | 2 + .../amd/build/selectors.min.js.map | 1 + .../bank/tagquestion/amd/src/edit_tags.js | 275 ++++++++++++ .../bank/tagquestion/amd/src/repository.js | 52 +++ .../bank/tagquestion/amd/src/selectors.js | 34 ++ .../classes/external/submit_tags.php | 135 ++++++ .../tagquestion/classes/form/tags_form.php | 88 ++++ .../tagquestion/classes/plugin_feature.php | 36 ++ .../tagquestion/classes/privacy/provider.php | 32 ++ .../classes/tags_action_column.php | 98 ++++ question/bank/tagquestion/db/services.php | 35 ++ .../tagquestion/lang/en/qbank_tagquestion.php | 27 ++ question/bank/tagquestion/lib.php | 92 ++++ .../tests/behat/tag_question_action.feature | 49 ++ .../tests/external/submit_tags_test.php | 420 ++++++++++++++++++ question/bank/tagquestion/version.php | 31 ++ question/classes/bank/tags_action_column.php | 11 +- question/classes/external.php | 20 + question/lib.php | 5 + question/tests/externallib_test.php | 370 --------------- question/type/edit_question_form.php | 4 +- question/type/tags_form.php | 5 + question/upgrade.txt | 2 + 37 files changed, 1472 insertions(+), 382 deletions(-) create mode 100644 question/bank/tagquestion/amd/build/edit_tags.min.js create mode 100644 question/bank/tagquestion/amd/build/edit_tags.min.js.map create mode 100644 question/bank/tagquestion/amd/build/repository.min.js create mode 100644 question/bank/tagquestion/amd/build/repository.min.js.map create mode 100644 question/bank/tagquestion/amd/build/selectors.min.js create mode 100644 question/bank/tagquestion/amd/build/selectors.min.js.map create mode 100644 question/bank/tagquestion/amd/src/edit_tags.js create mode 100644 question/bank/tagquestion/amd/src/repository.js create mode 100644 question/bank/tagquestion/amd/src/selectors.js create mode 100644 question/bank/tagquestion/classes/external/submit_tags.php create mode 100644 question/bank/tagquestion/classes/form/tags_form.php create mode 100644 question/bank/tagquestion/classes/plugin_feature.php create mode 100644 question/bank/tagquestion/classes/privacy/provider.php create mode 100644 question/bank/tagquestion/classes/tags_action_column.php create mode 100644 question/bank/tagquestion/db/services.php create mode 100644 question/bank/tagquestion/lang/en/qbank_tagquestion.php create mode 100644 question/bank/tagquestion/lib.php create mode 100644 question/bank/tagquestion/tests/behat/tag_question_action.feature create mode 100644 question/bank/tagquestion/tests/external/submit_tags_test.php create mode 100644 question/bank/tagquestion/version.php diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index b8da2e822d1..b2f328df7a4 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1943,6 +1943,7 @@ class core_plugin_manager { 'editquestion', 'exportquestions', 'importquestions', + 'tagquestion', 'viewcreator', 'viewquestionname', 'viewquestiontext', diff --git a/question/amd/build/edit_tags.min.js b/question/amd/build/edit_tags.min.js index 785d7adb073..68e4d566f4e 100644 --- a/question/amd/build/edit_tags.min.js +++ b/question/amd/build/edit_tags.min.js @@ -1,2 +1,2 @@ -define ("core_question/edit_tags",["jquery","core/fragment","core/str","core/modal_events","core/modal_factory","core/notification","core/custom_interaction_events","core_question/repository","core_question/selectors"],function(a,b,c,d,e,f,g,h,i){var j=function(a){a.find(i.actions.save).prop("disabled",!1)},k=function(a){a.find(i.actions.save).prop("disabled",!0)},l=function(a){return a.getBody().find("form").serialize()},m=function(a){var b=a.find(i.containers.loadingIcon);b.removeClass("hidden")},n=function(a){var b=a.find(i.containers.loadingIcon);b.addClass("hidden")},o=function(a,b){a.getBody().attr("data-contextid",b)},p=function(a){return a.getBody().data("contextid")},q=function(a,b){a.getBody().attr("data-questionid",b)},r=function(a){return a.getBody().data("questionid")},s=function(h){var l=e.create({type:e.types.SAVE_CANCEL,large:!1},[h,i.actions.edittags]).then(function(a){c.get_string("questiontags","question").then(function(b){a.setTitle(b);return b}).fail(f.exception);a.getRoot().on(d.save,function(b){var c=a.getBody().find("form");c.submit();b.preventDefault()});a.getRoot().on("submit","form",function(b){t(a,h).then(function(){a.hide();location.reload()}).fail(f.exception);b.preventDefault();b.stopPropagation()});return a});h.on(g.events.activate,i.actions.edittags,function(c){var d=a(c.currentTarget),e=d.data("questionid"),g=!!d.data("cantag"),p=d.data("contextid");l.then(function(a){k(h);m(h);var c=b.loadFragment("question","tags_form",p,{id:e});a.setBody(c);c.then(function(){j(h)}).always(function(){n(h)}).fail(f.exception);if(g){a.getRoot().find(i.actions.save).show()}else{a.getRoot().find(i.actions.save).hide()}q(a,e);o(a,p);return a}).fail(f.exception);c.preventDefault()})},t=function(a,b){k(b);m(b);var c=l(a),d=r(a),e=p(a);return h.submitTagCreateUpdateForm(d,e,c).always(function(){n(b);j(b)}).fail(f.exception)};return{init:function init(b){b=a(b);s(b)}}}); +define ("core_question/edit_tags",["jquery","core/fragment","core/str","core/modal_events","core/modal_factory","core/notification","core/custom_interaction_events","core_question/repository","core_question/selectors"],function(a,b,c,d,e,f,g,h,i){var j=function(a){a.find(i.actions.save).prop("disabled",!1)},k=function(a){a.find(i.actions.save).prop("disabled",!0)},l=function(a){return a.getBody().find("form").serialize()},m=function(a){var b=a.find(i.containers.loadingIcon);b.removeClass("hidden")},n=function(a){var b=a.find(i.containers.loadingIcon);b.addClass("hidden")},o=function(a,b){a.getBody().attr("data-contextid",b)},p=function(a){return a.getBody().data("contextid")},q=function(a,b){a.getBody().attr("data-questionid",b)},r=function(a){return a.getBody().data("questionid")},s=function(h){var l=e.create({type:e.types.SAVE_CANCEL,large:!1},[h,i.actions.edittags]).then(function(a){c.get_string("questiontags","question").then(function(b){a.setTitle(b);return b}).fail(f.exception);a.getRoot().on(d.save,function(b){var c=a.getBody().find("form");c.submit();b.preventDefault()});a.getRoot().on("submit","form",function(b){t(a,h).then(function(){a.hide();location.reload()}).fail(f.exception);b.preventDefault();b.stopPropagation()});return a});h.on(g.events.activate,i.actions.edittags,function(c){var d=a(c.currentTarget),e=d.data("questionid"),g=!!d.data("cantag"),p=d.data("contextid");l.then(function(a){k(h);m(h);var c=b.loadFragment("question","tags_form",p,{id:e});a.setBody(c);c.then(function(){j(h)}).always(function(){n(h)}).fail(f.exception);if(g){a.getRoot().find(i.actions.save).show()}else{a.getRoot().find(i.actions.save).hide()}q(a,e);o(a,p);return a}).fail(f.exception);c.preventDefault()})},t=function(a,b){k(b);m(b);var c=l(a),d=r(a),e=p(a);return h.submitTagCreateUpdateForm(d,e,c).always(function(){n(b);j(b)}).fail(f.exception)};return{init:function init(b){window.console.warn("warn: The core_question/repository has been deprecated.Please use qbank_tagquestion/repository instead.");b=a(b);s(b)}}}); //# sourceMappingURL=edit_tags.min.js.map diff --git a/question/amd/build/edit_tags.min.js.map b/question/amd/build/edit_tags.min.js.map index 0952d5dad3e..219ac72622b 100644 --- a/question/amd/build/edit_tags.min.js.map +++ b/question/amd/build/edit_tags.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/edit_tags.js"],"names":["define","$","Fragment","Str","ModalEvents","ModalFactory","Notification","CustomEvents","Repository","QuestionSelectors","enableSaveButton","root","find","actions","save","prop","disableSaveButton","getFormData","modal","getBody","serialize","startLoading","loadingIconContainer","containers","loadingIcon","removeClass","stopLoading","addClass","setContextId","contextId","attr","getContextId","data","setQuestionId","questionId","getQuestionId","registerEventListeners","modalPromise","create","type","types","SAVE_CANCEL","large","edittags","then","get_string","string","setTitle","fail","exception","getRoot","on","e","form","submit","preventDefault","hide","location","reload","stopPropagation","events","activate","currentTarget","canTag","tagsFragment","loadFragment","id","setBody","always","show","formData","submitTagCreateUpdateForm","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question tags editing.\n *\n * @module core_question/edit_tags\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/fragment',\n 'core/str',\n 'core/modal_events',\n 'core/modal_factory',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core_question/repository',\n 'core_question/selectors',\n ],\n function(\n $,\n Fragment,\n Str,\n ModalEvents,\n ModalFactory,\n Notification,\n CustomEvents,\n Repository,\n QuestionSelectors\n ) {\n\n /**\n * Enable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method enableSaveButton\n */\n var enableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', false);\n };\n\n /**\n * Disable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method disableSaveButton\n */\n var disableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', true);\n };\n\n /**\n * Get the serialised form data.\n *\n * @method getFormData\n * @param {object} modal The modal object.\n * @return {string} serialised form data\n */\n var getFormData = function(modal) {\n return modal.getBody().find('form').serialize();\n };\n\n /**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\n var startLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n };\n\n /**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\n var stopLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n };\n\n /**\n * Set the context Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} contextId The context id.\n */\n var setContextId = function(modal, contextId) {\n modal.getBody().attr('data-contextid', contextId);\n };\n\n /**\n * Get the context Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The context id.\n */\n var getContextId = function(modal) {\n return modal.getBody().data('contextid');\n };\n\n /**\n * Set the question Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} questionId The question Id.\n */\n var setQuestionId = function(modal, questionId) {\n modal.getBody().attr('data-questionid', questionId);\n };\n\n /**\n * Get the question Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The question Id.\n */\n var getQuestionId = function(modal) {\n return modal.getBody().data('questionid');\n };\n\n /**\n * Register event listeners for the module.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n var modalPromise = ModalFactory.create(\n {\n type: ModalFactory.types.SAVE_CANCEL,\n large: false\n },\n [root, QuestionSelectors.actions.edittags]\n ).then(function(modal) {\n // All of this code only executes once, when the modal is\n // first created. This allows us to add any code that should\n // only be run once, such as adding event handlers to the modal.\n Str.get_string('questiontags', 'question')\n .then(function(string) {\n modal.setTitle(string);\n return string;\n })\n .fail(Notification.exception);\n\n modal.getRoot().on(ModalEvents.save, function(e) {\n var form = modal.getBody().find('form');\n form.submit();\n e.preventDefault();\n });\n\n modal.getRoot().on('submit', 'form', function(e) {\n save(modal, root).then(function() {\n modal.hide();\n location.reload();\n return;\n }).fail(Notification.exception);\n\n // Stop the form from actually submitting and prevent it's\n // propagation because we have already handled the event.\n e.preventDefault();\n e.stopPropagation();\n });\n\n return modal;\n });\n\n // We need to add an event handler to the tags link because there are\n // multiple links on the page and without adding a listener we don't know\n // which one the user clicked on the show the modal.\n root.on(CustomEvents.events.activate, QuestionSelectors.actions.edittags, function(e) {\n var currentTarget = $(e.currentTarget);\n\n var questionId = currentTarget.data('questionid'),\n canTag = !!currentTarget.data('cantag'),\n contextId = currentTarget.data('contextid');\n\n // This code gets called each time the user clicks the tag link\n // so we can use it to reload the contents of the tag modal.\n modalPromise.then(function(modal) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var args = {\n id: questionId\n };\n\n var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);\n modal.setBody(tagsFragment);\n\n tagsFragment.then(function() {\n enableSaveButton(root);\n return;\n })\n .always(function() {\n // Always hide the loading spinner when the request\n // has completed.\n stopLoading(root);\n return;\n })\n .fail(Notification.exception);\n\n // Show or hide the save button depending on whether the user\n // has the capability to edit the tags.\n if (canTag) {\n modal.getRoot().find(QuestionSelectors.actions.save).show();\n } else {\n modal.getRoot().find(QuestionSelectors.actions.save).hide();\n }\n\n setQuestionId(modal, questionId);\n setContextId(modal, contextId);\n\n return modal;\n }).fail(Notification.exception);\n\n e.preventDefault();\n });\n };\n\n /**\n * Send the form data to the server to save question tags.\n *\n * @method save\n * @param {object} modal The modal object.\n * @param {object} root The container element.\n * @return {object} A promise\n */\n var save = function(modal, root) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var formData = getFormData(modal);\n var questionId = getQuestionId(modal);\n var contextId = getContextId(modal);\n\n // Send the form data to the server for processing.\n return Repository.submitTagCreateUpdateForm(questionId, contextId, formData)\n .always(function() {\n // Regardless of success or error we should always stop\n // the loading icon and re-enable the buttons.\n stopLoading(root);\n enableSaveButton(root);\n return;\n })\n .fail(Notification.exception);\n };\n\n return {\n init: function(root) {\n root = $(root);\n registerEventListeners(root);\n }\n };\n});\n"],"file":"edit_tags.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/edit_tags.js"],"names":["define","$","Fragment","Str","ModalEvents","ModalFactory","Notification","CustomEvents","Repository","QuestionSelectors","enableSaveButton","root","find","actions","save","prop","disableSaveButton","getFormData","modal","getBody","serialize","startLoading","loadingIconContainer","containers","loadingIcon","removeClass","stopLoading","addClass","setContextId","contextId","attr","getContextId","data","setQuestionId","questionId","getQuestionId","registerEventListeners","modalPromise","create","type","types","SAVE_CANCEL","large","edittags","then","get_string","string","setTitle","fail","exception","getRoot","on","e","form","submit","preventDefault","hide","location","reload","stopPropagation","events","activate","currentTarget","canTag","tagsFragment","loadFragment","id","setBody","always","show","formData","submitTagCreateUpdateForm","init","window","console","warn"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question tags editing.\n *\n * @deprecated since Moodle 4.0\n * @todo Final deprecation on Moodle 4.4 MDL-72438\n * @module core_question/edit_tags\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/fragment',\n 'core/str',\n 'core/modal_events',\n 'core/modal_factory',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core_question/repository',\n 'core_question/selectors',\n ],\n function(\n $,\n Fragment,\n Str,\n ModalEvents,\n ModalFactory,\n Notification,\n CustomEvents,\n Repository,\n QuestionSelectors\n ) {\n\n /**\n * Enable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method enableSaveButton\n */\n var enableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', false);\n };\n\n /**\n * Disable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method disableSaveButton\n */\n var disableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', true);\n };\n\n /**\n * Get the serialised form data.\n *\n * @method getFormData\n * @param {object} modal The modal object.\n * @return {string} serialised form data\n */\n var getFormData = function(modal) {\n return modal.getBody().find('form').serialize();\n };\n\n /**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\n var startLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n };\n\n /**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\n var stopLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n };\n\n /**\n * Set the context Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} contextId The context id.\n */\n var setContextId = function(modal, contextId) {\n modal.getBody().attr('data-contextid', contextId);\n };\n\n /**\n * Get the context Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The context id.\n */\n var getContextId = function(modal) {\n return modal.getBody().data('contextid');\n };\n\n /**\n * Set the question Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} questionId The question Id.\n */\n var setQuestionId = function(modal, questionId) {\n modal.getBody().attr('data-questionid', questionId);\n };\n\n /**\n * Get the question Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The question Id.\n */\n var getQuestionId = function(modal) {\n return modal.getBody().data('questionid');\n };\n\n /**\n * Register event listeners for the module.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n var modalPromise = ModalFactory.create(\n {\n type: ModalFactory.types.SAVE_CANCEL,\n large: false\n },\n [root, QuestionSelectors.actions.edittags]\n ).then(function(modal) {\n // All of this code only executes once, when the modal is\n // first created. This allows us to add any code that should\n // only be run once, such as adding event handlers to the modal.\n Str.get_string('questiontags', 'question')\n .then(function(string) {\n modal.setTitle(string);\n return string;\n })\n .fail(Notification.exception);\n\n modal.getRoot().on(ModalEvents.save, function(e) {\n var form = modal.getBody().find('form');\n form.submit();\n e.preventDefault();\n });\n\n modal.getRoot().on('submit', 'form', function(e) {\n save(modal, root).then(function() {\n modal.hide();\n location.reload();\n return;\n }).fail(Notification.exception);\n\n // Stop the form from actually submitting and prevent it's\n // propagation because we have already handled the event.\n e.preventDefault();\n e.stopPropagation();\n });\n\n return modal;\n });\n\n // We need to add an event handler to the tags link because there are\n // multiple links on the page and without adding a listener we don't know\n // which one the user clicked on the show the modal.\n root.on(CustomEvents.events.activate, QuestionSelectors.actions.edittags, function(e) {\n var currentTarget = $(e.currentTarget);\n\n var questionId = currentTarget.data('questionid'),\n canTag = !!currentTarget.data('cantag'),\n contextId = currentTarget.data('contextid');\n\n // This code gets called each time the user clicks the tag link\n // so we can use it to reload the contents of the tag modal.\n modalPromise.then(function(modal) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var args = {\n id: questionId\n };\n\n var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);\n modal.setBody(tagsFragment);\n\n tagsFragment.then(function() {\n enableSaveButton(root);\n return;\n })\n .always(function() {\n // Always hide the loading spinner when the request\n // has completed.\n stopLoading(root);\n return;\n })\n .fail(Notification.exception);\n\n // Show or hide the save button depending on whether the user\n // has the capability to edit the tags.\n if (canTag) {\n modal.getRoot().find(QuestionSelectors.actions.save).show();\n } else {\n modal.getRoot().find(QuestionSelectors.actions.save).hide();\n }\n\n setQuestionId(modal, questionId);\n setContextId(modal, contextId);\n\n return modal;\n }).fail(Notification.exception);\n\n e.preventDefault();\n });\n };\n\n /**\n * Send the form data to the server to save question tags.\n *\n * @method save\n * @param {object} modal The modal object.\n * @param {object} root The container element.\n * @return {object} A promise\n */\n var save = function(modal, root) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var formData = getFormData(modal);\n var questionId = getQuestionId(modal);\n var contextId = getContextId(modal);\n\n // Send the form data to the server for processing.\n return Repository.submitTagCreateUpdateForm(questionId, contextId, formData)\n .always(function() {\n // Regardless of success or error we should always stop\n // the loading icon and re-enable the buttons.\n stopLoading(root);\n enableSaveButton(root);\n return;\n })\n .fail(Notification.exception);\n };\n\n return {\n init: function(root) {\n window.console.warn('warn: The core_question/repository has been deprecated.' +\n 'Please use qbank_tagquestion/repository instead.');\n root = $(root);\n registerEventListeners(root);\n }\n };\n});\n"],"file":"edit_tags.min.js"} \ No newline at end of file diff --git a/question/amd/build/repository.min.js b/question/amd/build/repository.min.js index 0d9f41eafd7..b0773a68d96 100644 --- a/question/amd/build/repository.min.js +++ b/question/amd/build/repository.min.js @@ -1,2 +1,2 @@ -define ("core_question/repository",["jquery","core/ajax"],function(a,b){return{submitTagCreateUpdateForm:function submitTagCreateUpdateForm(a,c,d){return b.call([{methodname:"core_question_submit_tags_form",args:{questionid:a,contextid:c,formdata:d}}])[0]}}}); +define ("core_question/repository",["jquery","core/ajax"],function(a,b){return{submitTagCreateUpdateForm:function submitTagCreateUpdateForm(a,c,d){window.console.warn("warn: The core_question/repository has been deprecated.Please use qbank_tagquestion/repository instead.");return b.call([{methodname:"core_question_submit_tags_form",args:{questionid:a,contextid:c,formdata:d}}])[0]}}}); //# sourceMappingURL=repository.min.js.map diff --git a/question/amd/build/repository.min.js.map b/question/amd/build/repository.min.js.map index 6ab5340c9da..b858f96ede5 100644 --- a/question/amd/build/repository.min.js.map +++ b/question/amd/build/repository.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/repository.js"],"names":["define","$","Ajax","submitTagCreateUpdateForm","questionId","contextId","formdata","call","methodname","args","questionid","contextid"],"mappings":"AAsBAA,OAAM,4BAAC,CAAC,QAAD,CAAW,WAAX,CAAD,CAA0B,SAASC,CAAT,CAAYC,CAAZ,CAAkB,CAsB9C,MAAO,CACHC,yBAAyB,CAdG,QAA5BA,CAAAA,yBAA4B,CAASC,CAAT,CAAqBC,CAArB,CAAgCC,CAAhC,CAA0C,CAUtE,MAAOJ,CAAAA,CAAI,CAACK,IAAL,CAAU,CATH,CACVC,UAAU,CAAE,gCADF,CAEVC,IAAI,CAAE,CACFC,UAAU,CAAEN,CADV,CAEFO,SAAS,CAAEN,CAFT,CAGFC,QAAQ,CAAEA,CAHR,CAFI,CASG,CAAV,EAAqB,CAArB,CACV,CAEM,CAGV,CAzBK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question ajax actions.\n *\n * @module core_question/repository\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n /**\n * Submit the form data for the question tags form.\n *\n * @method submitTagCreateUpdateForm\n * @param {string} formdata The URL encoded values from the form\n * @return {promise}\n */\n var submitTagCreateUpdateForm = function(questionId, contextId, formdata) {\n var request = {\n methodname: 'core_question_submit_tags_form',\n args: {\n questionid: questionId,\n contextid: contextId,\n formdata: formdata\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n return {\n submitTagCreateUpdateForm: submitTagCreateUpdateForm\n };\n});\n"],"file":"repository.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/repository.js"],"names":["define","$","Ajax","submitTagCreateUpdateForm","questionId","contextId","formdata","window","console","warn","call","methodname","args","questionid","contextid"],"mappings":"AAwBAA,OAAM,4BAAC,CAAC,QAAD,CAAW,WAAX,CAAD,CAA0B,SAASC,CAAT,CAAYC,CAAZ,CAAkB,CAwB9C,MAAO,CACHC,yBAAyB,CAhBG,QAA5BA,CAAAA,yBAA4B,CAASC,CAAT,CAAqBC,CAArB,CAAgCC,CAAhC,CAA0C,CACtEC,MAAM,CAACC,OAAP,CAAeC,IAAf,4GAWA,MAAOP,CAAAA,CAAI,CAACQ,IAAL,CAAU,CATH,CACVC,UAAU,CAAE,gCADF,CAEVC,IAAI,CAAE,CACFC,UAAU,CAAET,CADV,CAEFU,SAAS,CAAET,CAFT,CAGFC,QAAQ,CAAEA,CAHR,CAFI,CASG,CAAV,EAAqB,CAArB,CACV,CAEM,CAGV,CA3BK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question ajax actions.\n *\n * @deprecated since Moodle 4.0\n * @todo Final deprecation on Moodle 4.4 MDL-72438\n * @module core_question/repository\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n /**\n * Submit the form data for the question tags form.\n *\n * @method submitTagCreateUpdateForm\n * @param {string} formdata The URL encoded values from the form\n * @return {promise}\n */\n var submitTagCreateUpdateForm = function(questionId, contextId, formdata) {\n window.console.warn('warn: The core_question/repository has been deprecated.' +\n 'Please use qbank_tagquestion/repository instead.');\n var request = {\n methodname: 'core_question_submit_tags_form',\n args: {\n questionid: questionId,\n contextid: contextId,\n formdata: formdata\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n return {\n submitTagCreateUpdateForm: submitTagCreateUpdateForm\n };\n});\n"],"file":"repository.min.js"} \ No newline at end of file diff --git a/question/amd/build/selectors.min.js b/question/amd/build/selectors.min.js index 51fef64a1ab..31f9a9cf3b7 100644 --- a/question/amd/build/selectors.min.js +++ b/question/amd/build/selectors.min.js @@ -1,2 +1,2 @@ -define ("core_question/selectors",[],function(){return{actions:{save:"[data-action=\"save\"]",edittags:"[data-action=\"edittags\"]"},containers:{loadingIcon:"[data-region=\"overlay-icon-container\"]"}}}); +define ("core_question/selectors",[],function(){window.console.warn("warn: The core_question/selectors has been deprecated. Please use qbank_tagquestion/selectors instead.");return{actions:{save:"[data-action=\"save\"]",edittags:"[data-action=\"edittags\"]"},containers:{loadingIcon:"[data-region=\"overlay-icon-container\"]"}}}); //# sourceMappingURL=selectors.min.js.map diff --git a/question/amd/build/selectors.min.js.map b/question/amd/build/selectors.min.js.map index 8d3ec286f72..03a39ffd946 100644 --- a/question/amd/build/selectors.min.js.map +++ b/question/amd/build/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/selectors.js"],"names":["define","actions","save","edittags","containers","loadingIcon"],"mappings":"AAsBAA,OAAM,2BAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,OAAO,CAAE,CACLC,IAAI,CAAE,wBADD,CAELC,QAAQ,CAAE,4BAFL,CADN,CAKHC,UAAU,CAAE,CACRC,WAAW,CAAE,0CADL,CALT,CASV,CAVK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * The purpose of this module is to centralize selectors related to question.\n *\n * @module core_question/question_selectors\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n actions: {\n save: '[data-action=\"save\"]',\n edittags: '[data-action=\"edittags\"]',\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n };\n});\n"],"file":"selectors.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/selectors.js"],"names":["define","window","console","warn","actions","save","edittags","containers","loadingIcon"],"mappings":"AAwBAA,OAAM,2BAAC,EAAD,CAAK,UAAW,CAClBC,MAAM,CAACC,OAAP,CAAeC,IAAf,CAAoB,wGAApB,EACA,MAAO,CACHC,OAAO,CAAE,CACLC,IAAI,CAAE,wBADD,CAELC,QAAQ,CAAE,4BAFL,CADN,CAKHC,UAAU,CAAE,CACRC,WAAW,CAAE,0CADL,CALT,CASV,CAXK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * The purpose of this module is to centralize selectors related to question.\n *\n * @deprecated since Moodle 4.0\n * @todo Final deprecation on Moodle 4.4 MDL-72438\n * @module core_question/question_selectors\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n window.console.warn('warn: The core_question/selectors has been deprecated. Please use qbank_tagquestion/selectors instead.');\n return {\n actions: {\n save: '[data-action=\"save\"]',\n edittags: '[data-action=\"edittags\"]',\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n };\n});\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/question/amd/src/edit_tags.js b/question/amd/src/edit_tags.js index 8c3f68483ec..e952b8e6cf4 100644 --- a/question/amd/src/edit_tags.js +++ b/question/amd/src/edit_tags.js @@ -16,6 +16,8 @@ /** * 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 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -268,6 +270,8 @@ define([ 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); } diff --git a/question/amd/src/repository.js b/question/amd/src/repository.js index bb054c78981..40fafb528b8 100644 --- a/question/amd/src/repository.js +++ b/question/amd/src/repository.js @@ -16,6 +16,8 @@ /** * A javascript module to handle question ajax actions. * + * @deprecated since Moodle 4.0 + * @todo Final deprecation on Moodle 4.4 MDL-72438 * @module core_question/repository * @copyright 2017 Simey Lameze * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -30,6 +32,8 @@ define(['jquery', 'core/ajax'], function($, Ajax) { * @return {promise} */ var submitTagCreateUpdateForm = function(questionId, contextId, formdata) { + window.console.warn('warn: The core_question/repository has been deprecated.' + + 'Please use qbank_tagquestion/repository instead.'); var request = { methodname: 'core_question_submit_tags_form', args: { diff --git a/question/amd/src/selectors.js b/question/amd/src/selectors.js index 1019cf36212..6a3fb874894 100644 --- a/question/amd/src/selectors.js +++ b/question/amd/src/selectors.js @@ -16,11 +16,14 @@ /** * The purpose of this module is to centralize selectors related to question. * + * @deprecated since Moodle 4.0 + * @todo Final deprecation on Moodle 4.4 MDL-72438 * @module core_question/question_selectors * @copyright 2018 Simey Lameze * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define([], function() { + window.console.warn('warn: The core_question/selectors has been deprecated. Please use qbank_tagquestion/selectors instead.'); return { actions: { save: '[data-action="save"]', diff --git a/question/bank/tagquestion/amd/build/edit_tags.min.js b/question/bank/tagquestion/amd/build/edit_tags.min.js new file mode 100644 index 00000000000..7aad4512bc9 --- /dev/null +++ b/question/bank/tagquestion/amd/build/edit_tags.min.js @@ -0,0 +1,2 @@ +define ("qbank_tagquestion/edit_tags",["jquery","core/fragment","core/str","core/modal_events","core/modal_factory","core/notification","core/custom_interaction_events","qbank_tagquestion/repository","qbank_tagquestion/selectors"],function(a,b,c,d,e,f,g,h,i){var j=function(a){a.find(i.actions.save).prop("disabled",!1)},k=function(a){a.find(i.actions.save).prop("disabled",!0)},l=function(a){return a.getBody().find("form").serialize()},m=function(a){var b=a.find(i.containers.loadingIcon);b.removeClass("hidden")},n=function(a){var b=a.find(i.containers.loadingIcon);b.addClass("hidden")},o=function(a,b){a.getBody().attr("data-contextid",b)},p=function(a){return a.getBody().data("contextid")},q=function(a,b){a.getBody().attr("data-questionid",b)},r=function(a){return a.getBody().data("questionid")},s=function(h){var l=e.create({type:e.types.SAVE_CANCEL,large:!1},[h,i.actions.edittags]).then(function(a){c.get_string("questiontags","question").then(function(b){a.setTitle(b);return b}).fail(f.exception);a.getRoot().on(d.save,function(b){var c=a.getBody().find("form");c.submit();b.preventDefault()});a.getRoot().on("submit","form",function(b){t(a,h).then(function(){a.hide();location.reload()}).fail(f.exception);b.preventDefault();b.stopPropagation()});return a});h.on(g.events.activate,i.actions.edittags,function(c){var d=a(c.currentTarget),e=d.data("questionid"),g=!!d.data("cantag"),p=d.data("contextid");l.then(function(a){k(h);m(h);var c=b.loadFragment("qbank_tagquestion","tags_form",p,{id:e});a.setBody(c);c.then(function(){j(h)}).always(function(){n(h)}).fail(f.exception);if(g){a.getRoot().find(i.actions.save).show()}else{a.getRoot().find(i.actions.save).hide()}q(a,e);o(a,p);return a}).fail(f.exception);c.preventDefault()})},t=function(a,b){k(b);m(b);var c=l(a),d=r(a),e=p(a);return h.submitTagCreateUpdateForm(d,e,c).always(function(){n(b);j(b)}).fail(f.exception)};return{init:function init(b){b=a(b);s(b)}}}); +//# sourceMappingURL=edit_tags.min.js.map diff --git a/question/bank/tagquestion/amd/build/edit_tags.min.js.map b/question/bank/tagquestion/amd/build/edit_tags.min.js.map new file mode 100644 index 00000000000..af31e293403 --- /dev/null +++ b/question/bank/tagquestion/amd/build/edit_tags.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/edit_tags.js"],"names":["define","$","Fragment","Str","ModalEvents","ModalFactory","Notification","CustomEvents","Repository","QuestionSelectors","enableSaveButton","root","find","actions","save","prop","disableSaveButton","getFormData","modal","getBody","serialize","startLoading","loadingIconContainer","containers","loadingIcon","removeClass","stopLoading","addClass","setContextId","contextId","attr","getContextId","data","setQuestionId","questionId","getQuestionId","registerEventListeners","modalPromise","create","type","types","SAVE_CANCEL","large","edittags","then","get_string","string","setTitle","fail","exception","getRoot","on","e","form","submit","preventDefault","hide","location","reload","stopPropagation","events","activate","currentTarget","canTag","tagsFragment","loadFragment","id","setBody","always","show","formData","submitTagCreateUpdateForm","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question tags editing.\n *\n * @module qbank_tagquestion/edit_tags\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/fragment',\n 'core/str',\n 'core/modal_events',\n 'core/modal_factory',\n 'core/notification',\n 'core/custom_interaction_events',\n 'qbank_tagquestion/repository',\n 'qbank_tagquestion/selectors',\n ],\n function(\n $,\n Fragment,\n Str,\n ModalEvents,\n ModalFactory,\n Notification,\n CustomEvents,\n Repository,\n QuestionSelectors\n ) {\n\n /**\n * Enable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method enableSaveButton\n */\n var enableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', false);\n };\n\n /**\n * Disable the save button in the footer.\n *\n * @param {object} root The container element.\n * @method disableSaveButton\n */\n var disableSaveButton = function(root) {\n root.find(QuestionSelectors.actions.save).prop('disabled', true);\n };\n\n /**\n * Get the serialised form data.\n *\n * @method getFormData\n * @param {object} modal The modal object.\n * @return {string} serialised form data\n */\n var getFormData = function(modal) {\n return modal.getBody().find('form').serialize();\n };\n\n /**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\n var startLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n };\n\n /**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\n var stopLoading = function(root) {\n var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n };\n\n /**\n * Set the context Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} contextId The context id.\n */\n var setContextId = function(modal, contextId) {\n modal.getBody().attr('data-contextid', contextId);\n };\n\n /**\n * Get the context Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The context id.\n */\n var getContextId = function(modal) {\n return modal.getBody().data('contextid');\n };\n\n /**\n * Set the question Id data attribute on the modal.\n *\n * @param {Promise} modal The modal promise.\n * @param {int} questionId The question Id.\n */\n var setQuestionId = function(modal, questionId) {\n modal.getBody().attr('data-questionid', questionId);\n };\n\n /**\n * Get the question Id data attribute value from the modal body.\n *\n * @param {Promise} modal The modal promise.\n * @return {int} The question Id.\n */\n var getQuestionId = function(modal) {\n return modal.getBody().data('questionid');\n };\n\n /**\n * Register event listeners for the module.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n var modalPromise = ModalFactory.create(\n {\n type: ModalFactory.types.SAVE_CANCEL,\n large: false\n },\n [root, QuestionSelectors.actions.edittags]\n ).then(function(modal) {\n // All of this code only executes once, when the modal is\n // first created. This allows us to add any code that should\n // only be run once, such as adding event handlers to the modal.\n Str.get_string('questiontags', 'question')\n .then(function(string) {\n modal.setTitle(string);\n return string;\n })\n .fail(Notification.exception);\n\n modal.getRoot().on(ModalEvents.save, function(e) {\n var form = modal.getBody().find('form');\n form.submit();\n e.preventDefault();\n });\n\n modal.getRoot().on('submit', 'form', function(e) {\n save(modal, root).then(function() {\n modal.hide();\n location.reload();\n return;\n }).fail(Notification.exception);\n\n // Stop the form from actually submitting and prevent it's\n // propagation because we have already handled the event.\n e.preventDefault();\n e.stopPropagation();\n });\n\n return modal;\n });\n\n // We need to add an event handler to the tags link because there are\n // multiple links on the page and without adding a listener we don't know\n // which one the user clicked on the show the modal.\n root.on(CustomEvents.events.activate, QuestionSelectors.actions.edittags, function(e) {\n var currentTarget = $(e.currentTarget);\n\n var questionId = currentTarget.data('questionid'),\n canTag = !!currentTarget.data('cantag'),\n contextId = currentTarget.data('contextid');\n\n // This code gets called each time the user clicks the tag link\n // so we can use it to reload the contents of the tag modal.\n modalPromise.then(function(modal) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var args = {\n id: questionId\n };\n\n var tagsFragment = Fragment.loadFragment('qbank_tagquestion', 'tags_form', contextId, args);\n modal.setBody(tagsFragment);\n\n tagsFragment.then(function() {\n enableSaveButton(root);\n return;\n })\n .always(function() {\n // Always hide the loading spinner when the request\n // has completed.\n stopLoading(root);\n return;\n })\n .fail(Notification.exception);\n\n // Show or hide the save button depending on whether the user\n // has the capability to edit the tags.\n if (canTag) {\n modal.getRoot().find(QuestionSelectors.actions.save).show();\n } else {\n modal.getRoot().find(QuestionSelectors.actions.save).hide();\n }\n\n setQuestionId(modal, questionId);\n setContextId(modal, contextId);\n\n return modal;\n }).fail(Notification.exception);\n\n e.preventDefault();\n });\n };\n\n /**\n * Send the form data to the server to save question tags.\n *\n * @method save\n * @param {object} modal The modal object.\n * @param {object} root The container element.\n * @return {object} A promise\n */\n var save = function(modal, root) {\n // Display spinner and disable save button.\n disableSaveButton(root);\n startLoading(root);\n\n var formData = getFormData(modal);\n var questionId = getQuestionId(modal);\n var contextId = getContextId(modal);\n\n // Send the form data to the server for processing.\n return Repository.submitTagCreateUpdateForm(questionId, contextId, formData)\n .always(function() {\n // Regardless of success or error we should always stop\n // the loading icon and re-enable the buttons.\n stopLoading(root);\n enableSaveButton(root);\n return;\n })\n .fail(Notification.exception);\n };\n\n return {\n init: function(root) {\n root = $(root);\n registerEventListeners(root);\n }\n };\n});\n"],"file":"edit_tags.min.js"} \ No newline at end of file diff --git a/question/bank/tagquestion/amd/build/repository.min.js b/question/bank/tagquestion/amd/build/repository.min.js new file mode 100644 index 00000000000..8f944664b71 --- /dev/null +++ b/question/bank/tagquestion/amd/build/repository.min.js @@ -0,0 +1,2 @@ +define ("qbank_tagquestion/repository",["jquery","core/ajax"],function(a,b){return{submitTagCreateUpdateForm:function submitTagCreateUpdateForm(a,c,d){return b.call([{methodname:"qbank_tagquestion_submit_tags_form",args:{questionid:a,contextid:c,formdata:d}}])[0]}}}); +//# sourceMappingURL=repository.min.js.map diff --git a/question/bank/tagquestion/amd/build/repository.min.js.map b/question/bank/tagquestion/amd/build/repository.min.js.map new file mode 100644 index 00000000000..26cff3e3f03 --- /dev/null +++ b/question/bank/tagquestion/amd/build/repository.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/repository.js"],"names":["define","$","Ajax","submitTagCreateUpdateForm","questionId","contextId","formdata","call","methodname","args","questionid","contextid"],"mappings":"AAwBAA,OAAM,gCAAC,CAAC,QAAD,CAAW,WAAX,CAAD,CAA0B,SAASC,CAAT,CAAYC,CAAZ,CAAkB,CAwB9C,MAAO,CACHC,yBAAyB,CAdG,QAA5BA,CAAAA,yBAA4B,CAASC,CAAT,CAAqBC,CAArB,CAAgCC,CAAhC,CAA0C,CAUtE,MAAOJ,CAAAA,CAAI,CAACK,IAAL,CAAU,CATH,CACVC,UAAU,CAAE,oCADF,CAEVC,IAAI,CAAE,CACFC,UAAU,CAAEN,CADV,CAEFO,SAAS,CAAEN,CAFT,CAGFC,QAAQ,CAAEA,CAHR,CAFI,CASG,CAAV,EAAqB,CAArB,CACV,CAEM,CAGV,CA3BK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle question ajax actions.\n *\n * @module qbank_tagquestion/repository\n * @class repository\n * @package qbank_tagquestion\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n /**\n * Submit the form data for the question tags form.\n *\n * @method submitTagCreateUpdateForm\n * @param {int} questionId\n * @param {int} contextId\n * @param {string} formdata The URL encoded values from the form\n * @return {promise}\n */\n var submitTagCreateUpdateForm = function(questionId, contextId, formdata) {\n var request = {\n methodname: 'qbank_tagquestion_submit_tags_form',\n args: {\n questionid: questionId,\n contextid: contextId,\n formdata: formdata\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n return {\n submitTagCreateUpdateForm: submitTagCreateUpdateForm\n };\n});\n"],"file":"repository.min.js"} \ No newline at end of file diff --git a/question/bank/tagquestion/amd/build/selectors.min.js b/question/bank/tagquestion/amd/build/selectors.min.js new file mode 100644 index 00000000000..9eaae0436c6 --- /dev/null +++ b/question/bank/tagquestion/amd/build/selectors.min.js @@ -0,0 +1,2 @@ +define ("qbank_tagquestion/selectors",[],function(){return{actions:{save:"[data-action=\"save\"]",edittags:"[data-action=\"edittags\"]"},containers:{loadingIcon:"[data-region=\"overlay-icon-container\"]"}}}); +//# sourceMappingURL=selectors.min.js.map diff --git a/question/bank/tagquestion/amd/build/selectors.min.js.map b/question/bank/tagquestion/amd/build/selectors.min.js.map new file mode 100644 index 00000000000..b67431b5d53 --- /dev/null +++ b/question/bank/tagquestion/amd/build/selectors.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/selectors.js"],"names":["define","actions","save","edittags","containers","loadingIcon"],"mappings":"AAuBAA,OAAM,+BAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,OAAO,CAAE,CACLC,IAAI,CAAE,wBADD,CAELC,QAAQ,CAAE,4BAFL,CADN,CAKHC,UAAU,CAAE,CACRC,WAAW,CAAE,0CADL,CALT,CASV,CAVK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * The purpose of this module is to centralize selectors related to question.\n *\n * @module qbank_tagquestion/question_selectors\n * @package qbank_tagquestion\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n actions: {\n save: '[data-action=\"save\"]',\n edittags: '[data-action=\"edittags\"]',\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n };\n});\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/question/bank/tagquestion/amd/src/edit_tags.js b/question/bank/tagquestion/amd/src/edit_tags.js new file mode 100644 index 00000000000..50bdc1dcd35 --- /dev/null +++ b/question/bank/tagquestion/amd/src/edit_tags.js @@ -0,0 +1,275 @@ +// 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 . + +/** + * A javascript module to handle question tags editing. + * + * @module qbank_tagquestion/edit_tags + * @copyright 2018 Simey Lameze + * @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', + 'qbank_tagquestion/repository', + 'qbank_tagquestion/selectors', + ], + function( + $, + Fragment, + Str, + ModalEvents, + ModalFactory, + 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 = ModalFactory.create( + { + type: ModalFactory.types.SAVE_CANCEL, + large: false + }, + [root, QuestionSelectors.actions.edittags] + ).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(); + location.reload(); + 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, 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('qbank_tagquestion', '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 (canTag) { + modal.getRoot().find(QuestionSelectors.actions.save).show(); + } else { + modal.getRoot().find(QuestionSelectors.actions.save).hide(); + } + + setQuestionId(modal, questionId); + setContextId(modal, contextId); + + 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); + 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; + }) + .fail(Notification.exception); + }; + + return { + init: function(root) { + root = $(root); + registerEventListeners(root); + } + }; +}); diff --git a/question/bank/tagquestion/amd/src/repository.js b/question/bank/tagquestion/amd/src/repository.js new file mode 100644 index 00000000000..9141813353e --- /dev/null +++ b/question/bank/tagquestion/amd/src/repository.js @@ -0,0 +1,52 @@ +// 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 . + +/** + * A javascript module to handle question ajax actions. + * + * @module qbank_tagquestion/repository + * @class repository + * @package qbank_tagquestion + * @copyright 2017 Simey Lameze + * @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 {int} questionId + * @param {int} contextId + * @param {string} formdata The URL encoded values from the form + * @return {promise} + */ + var submitTagCreateUpdateForm = function(questionId, contextId, formdata) { + var request = { + methodname: 'qbank_tagquestion_submit_tags_form', + args: { + questionid: questionId, + contextid: contextId, + formdata: formdata + } + }; + + return Ajax.call([request])[0]; + }; + + return { + submitTagCreateUpdateForm: submitTagCreateUpdateForm + }; +}); diff --git a/question/bank/tagquestion/amd/src/selectors.js b/question/bank/tagquestion/amd/src/selectors.js new file mode 100644 index 00000000000..a3de37f0b21 --- /dev/null +++ b/question/bank/tagquestion/amd/src/selectors.js @@ -0,0 +1,34 @@ +// 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 . + +/** + * The purpose of this module is to centralize selectors related to question. + * + * @module qbank_tagquestion/question_selectors + * @package qbank_tagquestion + * @copyright 2018 Simey Lameze + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define([], function() { + return { + actions: { + save: '[data-action="save"]', + edittags: '[data-action="edittags"]', + }, + containers: { + loadingIcon: '[data-region="overlay-icon-container"]', + }, + }; +}); diff --git a/question/bank/tagquestion/classes/external/submit_tags.php b/question/bank/tagquestion/classes/external/submit_tags.php new file mode 100644 index 00000000000..9d6b36fb3ab --- /dev/null +++ b/question/bank/tagquestion/classes/external/submit_tags.php @@ -0,0 +1,135 @@ +. + +namespace qbank_tagquestion\external; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/externallib.php'); +require_once($CFG->dirroot . '/question/engine/lib.php'); +require_once($CFG->dirroot . '/question/engine/datalib.php'); +require_once($CFG->libdir . '/questionlib.php'); + +use core_tag_tag; +use external_api; +use external_function_parameters; +use external_single_structure; +use external_value; +use qbank_tagquestion\form\tags_form; + +/** + * External qbank_tagquestion API. + * + * @package qbank_tagquestion + * @copyright 2016 Pau Ferrer + * @author 2021 Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class submit_tags extends external_api { + /** + * Returns description of method parameters. + * + * @return external_function_parameters. + */ + public static function execute_parameters() { + return new external_function_parameters([ + 'questionid' => new external_value(PARAM_INT, 'The question id'), + 'contextid' => new external_value(PARAM_INT, 'The editing context id'), + 'formdata' => new external_value(PARAM_RAW, 'The data from the tag form'), + ]); + } + + /** + * Handles the tags form submission. + * + * @param int $questionid The question id. + * @param int $contextid The editing context id. + * @param string $formdata The question tag form data in a URI encoded param string + * @return array The created or modified question tag + */ + public static function execute($questionid, $contextid, $formdata) { + global $DB, $CFG; + + $data = []; + $result = ['status' => false]; + + // Parameter validation. + $params = self::validate_parameters(self::execute_parameters(), [ + 'questionid' => $questionid, + 'contextid' => $contextid, + 'formdata' => $formdata + ]); + + $editingcontext = \context::instance_by_id($params['contextid']); + self::validate_context($editingcontext); + parse_str($params['formdata'], $data); + + if (!$question = $DB->get_record_sql(' + SELECT q.*, qc.contextid + FROM {question} q + JOIN {question_categories} qc ON qc.id = q.category + WHERE q.id = ?', [$params['questionid']])) { + throw new \moodle_exception('questiondoesnotexist', 'question'); + } + + $cantag = question_has_capability_on($question, 'tag'); + $questioncontext = \context::instance_by_id($question->contextid); + $contexts = new \question_edit_contexts($editingcontext); + + $formoptions = [ + 'editingcontext' => $editingcontext, + 'questioncontext' => $questioncontext, + 'contexts' => $contexts->all() + ]; + + $mform = new tags_form(null, $formoptions, 'post', '', null, $cantag, $data); + + if ($validateddata = $mform->get_data()) { + if ($cantag) { + if (isset($validateddata->tags)) { + // Due to a mform bug, if there's no tags set on the tag element, it submits the name as the value. + // The only way to discover is checking if the tag element is an array. + $tags = is_array($validateddata->tags) ? $validateddata->tags : []; + + core_tag_tag::set_item_tags('core_question', 'question', $validateddata->id, + $questioncontext, $tags); + + $result['status'] = true; + } + + if (isset($validateddata->coursetags)) { + $coursetags = is_array($validateddata->coursetags) ? $validateddata->coursetags : []; + core_tag_tag::set_item_tags('core_question', 'question', $validateddata->id, + $editingcontext->get_course_context(false), $coursetags); + + $result['status'] = true; + } + } + } + + return $result; + } + + /** + * Returns description of method result value. + */ + public static function execute_returns() { + return new external_single_structure([ + 'status' => new external_value(PARAM_BOOL, 'status: true if success') + ]); + } + +} diff --git a/question/bank/tagquestion/classes/form/tags_form.php b/question/bank/tagquestion/classes/form/tags_form.php new file mode 100644 index 00000000000..7269b51935d --- /dev/null +++ b/question/bank/tagquestion/classes/form/tags_form.php @@ -0,0 +1,88 @@ +. + +namespace qbank_tagquestion\form; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/lib/formslib.php'); +require_once($CFG->dirroot . '/lib/questionlib.php'); + +/** + * The mform class for manage question tags. + * + * @package qbank_tagquestion + * @copyright 2018 Simey Lameze + * @author 2021 Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tags_form extends \moodleform { + + public function definition() { + $mform = $this->_form; + $customdata = $this->_customdata; + + $mform->disable_form_change_checker(); + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + + $mform->addElement('hidden', 'categoryid'); + $mform->setType('categoryid', PARAM_INT); + + $mform->addElement('hidden', 'contextid'); + $mform->setType('contextid', PARAM_INT); + + $mform->addElement('static', 'questionname', get_string('questionname', 'question')); + $mform->addElement('static', 'questioncategory', get_string('categorycurrent', 'question')); + $mform->addElement('static', 'context', ''); + + if (\core_tag_tag::is_enabled('core_question', 'question')) { + $tags = \core_tag_tag::get_tags_by_area_in_contexts('core_question', 'question', $customdata['contexts']); + $tagstrings = []; + foreach ($tags as $tag) { + $tagstrings[$tag->name] = $tag->name; + } + + $options = [ + 'tags' => true, + 'multiple' => true, + 'noselectionstring' => get_string('anytags', 'quiz'), + ]; + $mform->addElement('autocomplete', 'tags', get_string('tags'), $tagstrings, $options); + + // Is the question category in a course context? + $qcontext = $customdata['questioncontext']; + $qcoursecontext = $qcontext->get_course_context(false); + $iscourseoractivityquestion = !empty($qcoursecontext); + // Is the current context we're editing in a course context? + $editingcontext = $customdata['editingcontext']; + $editingcoursecontext = $editingcontext->get_course_context(false); + $iseditingcontextcourseoractivity = !empty($editingcoursecontext); + + if ($iseditingcontextcourseoractivity && !$iscourseoractivityquestion) { + // If the question is being edited in a course or activity context + // and the question isn't a course or activity level question then + // allow course tags to be added to the course. + $coursetagheader = get_string('questionformtagheader', 'core_question', + $editingcoursecontext->get_context_name(true)); + $mform->addElement('autocomplete', 'coursetags', $coursetagheader, $tagstrings, $options); + + } + } + } + +} diff --git a/question/bank/tagquestion/classes/plugin_feature.php b/question/bank/tagquestion/classes/plugin_feature.php new file mode 100644 index 00000000000..792dc780217 --- /dev/null +++ b/question/bank/tagquestion/classes/plugin_feature.php @@ -0,0 +1,36 @@ +. + +namespace qbank_tagquestion; + +use core_question\local\bank\plugin_features_base; + +/** + * Class columns is the entrypoint for the columns. + * + * @package qbank_tagquestion + * @copyright 2021 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class plugin_feature extends plugin_features_base{ + + public function get_question_columns($qbank): array { + return [ + new tags_action_column($qbank), + ]; + } +} diff --git a/question/bank/tagquestion/classes/privacy/provider.php b/question/bank/tagquestion/classes/privacy/provider.php new file mode 100644 index 00000000000..edb86c5d895 --- /dev/null +++ b/question/bank/tagquestion/classes/privacy/provider.php @@ -0,0 +1,32 @@ +. + +namespace qbank_tagquestion\privacy; + +/** + * Privacy Subsystem for qbank_tagquestion implementing null_provider. + * + * @package qbank_tagquestion + * @copyright 2021 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements \core_privacy\local\metadata\null_provider { + + public static function get_reason(): string { + return 'privacy:metadata'; + } +} diff --git a/question/bank/tagquestion/classes/tags_action_column.php b/question/bank/tagquestion/classes/tags_action_column.php new file mode 100644 index 00000000000..4a4edb05a4c --- /dev/null +++ b/question/bank/tagquestion/classes/tags_action_column.php @@ -0,0 +1,98 @@ +. + +namespace qbank_tagquestion; + +use core_question\local\bank\action_column_base; +use core_question\local\bank\menuable_action; + +/** + * Action to add and remove tags to questions. + * + * @package qbank_tagquestion + * @copyright 2018 Simey Lameze + * @author 2021 Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tags_action_column extends action_column_base implements menuable_action { + + /** + * @var string store this lang string for performance. + */ + protected $managetags; + + /** + * @var bool tags enabled or not from config. + */ + protected $tagsenabled = true; + + public function init(): void { + parent::init(); + $this->check_tags_status(); + if ($this->tagsenabled) { + global $PAGE; + $PAGE->requires->js_call_amd('qbank_tagquestion/edit_tags', 'init', ['#questionscontainer']); + } + $this->managetags = get_string('managetags', 'tag'); + } + + protected function check_tags_status(): void { + global $CFG; + if (!$CFG->usetags) { + $this->tagsenabled = false; + } + } + + public function get_name(): string { + return 'tagsaction'; + } + + protected function display_content($question, $rowclasses): void { + global $OUTPUT; + + if (\core_tag_tag::is_enabled('core_question', 'question') && + question_has_capability_on($question, 'view') && $this->tagsenabled) { + + [$url, $attributes] = $this->get_link_url_and_attributes($question); + echo \html_writer::link($url, $OUTPUT->pix_icon('t/tags', + $this->managetags), $attributes); + } + } + + protected function get_link_url_and_attributes($question): array { + $url = new \moodle_url($this->qbank->returnurl); + + $attributes = [ + 'data-action' => 'edittags', + 'data-cantag' => question_has_capability_on($question, 'tag'), + 'data-contextid' => $this->qbank->get_most_specific_context()->id, + 'data-questionid' => $question->id + ]; + + return [$url, $attributes]; + } + + public function get_action_menu_link(\stdClass $question): ?\action_menu_link { + if (!\core_tag_tag::is_enabled('core_question', 'question') || + !question_has_capability_on($question, 'view') || !$this->tagsenabled) { + return null; + } + + [$url, $attributes] = $this->get_link_url_and_attributes($question); + return new \action_menu_link_secondary($url, new \pix_icon('t/tags', ''), + $this->managetags, $attributes); + } +} diff --git a/question/bank/tagquestion/db/services.php b/question/bank/tagquestion/db/services.php new file mode 100644 index 00000000000..61372ccb47f --- /dev/null +++ b/question/bank/tagquestion/db/services.php @@ -0,0 +1,35 @@ +. + +/** + * External services definition for qbank_tagquestion. + * + * @package qbank_tagquestion + * @copyright 2021 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$functions = [ + 'qbank_tagquestion_submit_tags_form' => [ + 'classname' => 'qbank_tagquestion\external\submit_tags', + 'description' => 'Update the question tags.', + 'type' => 'write', + 'ajax' => true, + ], +]; diff --git a/question/bank/tagquestion/lang/en/qbank_tagquestion.php b/question/bank/tagquestion/lang/en/qbank_tagquestion.php new file mode 100644 index 00000000000..7042f5c8d99 --- /dev/null +++ b/question/bank/tagquestion/lang/en/qbank_tagquestion.php @@ -0,0 +1,27 @@ +. + +/** + * Strings for component qbank_tagquestion, language 'en'. + * + * @package qbank_tagquestion + * @copyright 2021 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['pluginname'] = 'Tag question'; +$string['privacy:metadata'] = 'Tag question plugin does not store any user data.'; diff --git a/question/bank/tagquestion/lib.php b/question/bank/tagquestion/lib.php new file mode 100644 index 00000000000..936490010e4 --- /dev/null +++ b/question/bank/tagquestion/lib.php @@ -0,0 +1,92 @@ +. + +/** + * Question related functions. + * + * This file was created just because Fragment API expects callbacks to be defined on lib.php. + * + * @package qbank_tagquestion + * @copyright 2018 Simey Lameze + * @author 2021 Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use qbank_tagquestion\form\tags_form; + +/** + * Question tags fragment callback. + * + * @param array $args Arguments to the form. + * @return null|string The rendered form. + */ +function qbank_tagquestion_output_fragment_tags_form($args) { + + if (!empty($args['id'])) { + global $CFG, $DB; + require_once($CFG->libdir . '/questionlib.php'); + $id = clean_param($args['id'], PARAM_INT); + $editingcontext = $args['context']; + + // Load the question and some related information. + $question = $DB->get_record('question', ['id' => $id]); + + if ($coursecontext = $editingcontext->get_course_context(false)) { + $course = $DB->get_record('course', ['id' => $coursecontext->instanceid]); + $filtercourses = [$course]; + } else { + $filtercourses = null; + } + + $category = $DB->get_record('question_categories', ['id' => $question->category]); + $questioncontext = \context::instance_by_id($category->contextid); + $contexts = new \question_edit_contexts($editingcontext); + + // Load the question tags and filter the course tags by the current course. + if (core_tag_tag::is_enabled('core_question', 'question')) { + $tagobjectsbyquestion = core_tag_tag::get_items_tags('core_question', 'question', [$question->id]); + if (!empty($tagobjectsbyquestion[$question->id])) { + $tagobjects = $tagobjectsbyquestion[$question->id]; + $sortedtagobjects = question_sort_tags($tagobjects, + context::instance_by_id($category->contextid), $filtercourses); + } + } + $formoptions = [ + 'editingcontext' => $editingcontext, + 'questioncontext' => $questioncontext, + 'contexts' => $contexts->all() + ]; + $data = [ + 'id' => $question->id, + 'questioncategory' => $category->name, + 'questionname' => $question->name, + 'categoryid' => $category->id, + 'contextid' => $category->contextid, + 'context' => $questioncontext->get_context_name(), + 'tags' => $sortedtagobjects->tags ?? [], + 'coursetags' => $sortedtagobjects->coursetags ?? [], + ]; + + $cantag = question_has_capability_on($question, 'tag'); + $mform = new tags_form(null, $formoptions, 'post', '', null, $cantag, $data); + $mform->set_data($data); + + return $mform->render(); + } + +} diff --git a/question/bank/tagquestion/tests/behat/tag_question_action.feature b/question/bank/tagquestion/tests/behat/tag_question_action.feature new file mode 100644 index 00000000000..693017c251a --- /dev/null +++ b/question/bank/tagquestion/tests/behat/tag_question_action.feature @@ -0,0 +1,49 @@ +@qbank @qbank_tagquestion +Feature: Use the qbank plugin manager page for tagquestion + In order to check the plugin behaviour with enable and disable + + Background: + Given the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "activities" exist: + | activity | name | course | idnumber | + | quiz | Test quiz | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Course | C1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | First question | Answer the first question | + + Scenario: Enable/disable tagquestion column from the base view + Given I log in as "admin" + When I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration + And I should see "Tag question" + And I click on "Disable" "link" in the "Tag question" "table_row" + And I am on the "Test quiz" "quiz activity" page + And I navigate to "Question bank > Questions" in current page administration + And I click on "#action-menu-toggle-2" "css_element" in the "First question" "table_row" + Then I should not see "Manage tags" + And I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration + And I click on "Enable" "link" in the "Tag question" "table_row" + And I am on the "Test quiz" "quiz activity" page + And I navigate to "Question bank > Questions" in current page administration + And I click on "#action-menu-toggle-2" "css_element" in the "First question" "table_row" + And I should see "Manage tags" + + Scenario: Enable/disable tagquestion section from question edit form + Given I log in as "admin" + When I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration + And I should see "Tag question" + And I click on "Disable" "link" in the "Tag question" "table_row" + And I am on the "Test quiz" "quiz activity" page + And I navigate to "Question bank > Questions" in current page administration + And I choose "Edit question" action for "First question" in the question bank + Then I should not see "Tags" + And I navigate to "Plugins > Question bank plugins > Manage question bank plugins" in site administration + And I click on "Enable" "link" in the "Tag question" "table_row" + And I am on the "Test quiz" "quiz activity" page + And I navigate to "Question bank > Questions" in current page administration + And I choose "Edit question" action for "First question" in the question bank + And I should see "Tags" diff --git a/question/bank/tagquestion/tests/external/submit_tags_test.php b/question/bank/tagquestion/tests/external/submit_tags_test.php new file mode 100644 index 00000000000..f4d5602abe0 --- /dev/null +++ b/question/bank/tagquestion/tests/external/submit_tags_test.php @@ -0,0 +1,420 @@ +. + +namespace qbank_tagquestion\external; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +/** + * Question external functions tests. + * + * @package qbank_tagquestion + * @copyright 2016 Pau Ferrer + * @author 2021 Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class submit_tags_test extends \externallib_advanced_testcase { + + /** + * Set up for every test + */ + public function setUp(): void { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup test data. + $this->course = $this->getDataGenerator()->create_course(); + + // Create users. + $this->student = self::getDataGenerator()->create_user(); + + // Users enrolments. + $this->studentrole = $DB->get_record('role', ['shortname' => 'student']); + $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); + } + + /** + * submit_tags_form should throw an exception when the question id doesn't match + * a question. + */ + public function test_submit_tags_form_incorrect_question_id() { + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); + $questioncontext = \context::instance_by_id($qcat->contextid); + $editingcontext = $questioncontext; + $question = $questions[0]; + // Generate an id for a question that doesn't exist. + $missingquestionid = $questions[1]->id * 2; + $question->id = $missingquestionid; + $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); + + // We should receive an exception if the question doesn't exist. + $this->expectException('moodle_exception'); + submit_tags::execute($missingquestionid, $editingcontext->id, $formdata); + } + + /** + * submit_tags_form should throw an exception when the context id doesn't match + * a context. + */ + public function test_submit_tags_form_incorrect_context_id() { + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); + $questioncontext = \context::instance_by_id($qcat->contextid); + $editingcontext = $questioncontext; + $question = $questions[0]; + // Generate an id for a context that doesn't exist. + $missingcontextid = $editingcontext->id * 200; + $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); + + // We should receive an exception if the question doesn't exist. + $this->expectException('moodle_exception'); + submit_tags::execute($question->id, $missingcontextid, $formdata); + } + + /** + * submit_tags_form should return false when tags are disabled. + */ + public function test_submit_tags_form_tags_disabled() { + global $CFG; + + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); + $questioncontext = \context::instance_by_id($qcat->contextid); + $editingcontext = $questioncontext; + $question = $questions[0]; + $user = $this->create_user_can_tag($course); + $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); + + $this->setUser($user); + $CFG->usetags = false; + $result = submit_tags::execute($question->id, $editingcontext->id, $formdata); + $CFG->usetags = true; + + $this->assertFalse($result['status']); + } + + /** + * submit_tags_form should return false if the user does not have any capability + * to tag the question. + */ + public function test_submit_tags_form_no_tag_permissions() { + global $DB; + + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); + $questiongenerator = $generator->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); + $questioncontext = \context::instance_by_id($qcat->contextid); + $editingcontext = $questioncontext; + $question = $questions[0]; + $formdata = $this->generate_encoded_submit_tags_form_string( + $question, + $qcat, + $questioncontext, + ['foo'], + ['bar'] + ); + + // Prohibit all of the tag capabilities. + assign_capability('moodle/question:tagmine', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); + assign_capability('moodle/question:tagall', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); + + $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); + $user->ignoresesskey = true; + $this->setUser($user); + + $result = submit_tags::execute($question->id, $editingcontext->id, $formdata); + + $this->assertFalse($result['status']); + } + + /** + * submit_tags_form should return false if the user only has the capability to + * tag their own questions and the question is not theirs. + */ + public function test_submit_tags_form_tagmine_permission_non_owner_question() { + global $DB; + + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); + $questiongenerator = $generator->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); + $questioncontext = \context::instance_by_id($qcat->contextid); + $editingcontext = $questioncontext; + $question = $questions[0]; + $formdata = $this->generate_encoded_submit_tags_form_string( + $question, + $qcat, + $questioncontext, + ['foo'], + ['bar'] + ); + + // Make sure the question isn't created by the user. + $question->createdby = $user->id + 1; + + // Prohibit all of the tag capabilities. + assign_capability('moodle/question:tagmine', CAP_ALLOW, $teacherrole->id, $questioncontext->id); + assign_capability('moodle/question:tagall', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); + + $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); + $user->ignoresesskey = true; + $this->setUser($user); + + $result = submit_tags::execute($question->id, $editingcontext->id, $formdata); + + $this->assertFalse($result['status']); + } + + /** + * Data provided for the submit_tags_form test to check that course tags are + * only created in the correct editing and question context combinations. + * + * @return array Test cases + */ + public function get_submit_tags_form_testcases() { + return [ + 'course - course' => [ + 'editingcontext' => 'course', + 'questioncontext' => 'course', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'course - course - empty tags' => [ + 'editingcontext' => 'course', + 'questioncontext' => 'course', + 'questiontags' => [], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'course - course category' => [ + 'editingcontext' => 'course', + 'questioncontext' => 'category', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => true + ], + 'course - system' => [ + 'editingcontext' => 'course', + 'questioncontext' => 'system', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => true + ], + 'course category - course' => [ + 'editingcontext' => 'category', + 'questioncontext' => 'course', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'course category - course category' => [ + 'editingcontext' => 'category', + 'questioncontext' => 'category', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'course category - system' => [ + 'editingcontext' => 'category', + 'questioncontext' => 'system', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'system - course' => [ + 'editingcontext' => 'system', + 'questioncontext' => 'course', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'system - course category' => [ + 'editingcontext' => 'system', + 'questioncontext' => 'category', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + 'system - system' => [ + 'editingcontext' => 'system', + 'questioncontext' => 'system', + 'questiontags' => ['foo'], + 'coursetags' => ['bar'], + 'expectcoursetags' => false + ], + ]; + } + + /** + * Tests that submit_tags_form only creates course tags when the correct combination + * of editing context and question context is provided. + * + * Course tags can only be set on a course category or system context question that + * is being editing in a course context. + * + * @dataProvider get_submit_tags_form_testcases() + * @param string $editingcontext The type of the context the question is being edited in + * @param string $questioncontext The type of the context the question belongs to + * @param string[] $questiontags The tag names to set as question tags + * @param string[] $coursetags The tag names to set as course tags + * @param bool $expectcoursetags If the given course tags should have been set or not + */ + public function test_submit_tags_form_context_combinations( + $editingcontext, + $questioncontext, + $questiontags, + $coursetags, + $expectcoursetags + ) { + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions($questioncontext); + $coursecontext = \context_course::instance($course->id); + $questioncontext = \context::instance_by_id($qcat->contextid); + + switch($editingcontext) { + case 'system': + $editingcontext = \context_system::instance(); + break; + + case 'category': + $editingcontext = \context_coursecat::instance($category->id); + break; + + default: + $editingcontext = \context_course::instance($course->id); + } + + $user = $this->create_user_can_tag($course); + $question = $questions[0]; + $formdata = $this->generate_encoded_submit_tags_form_string( + $question, + $qcat, + $questioncontext, + $questiontags, // Question tags. + $coursetags // Course tags. + ); + + $this->setUser($user); + + $result = submit_tags::execute($question->id, $editingcontext->id, $formdata); + + $this->assertTrue($result['status']); + + $tagobjects = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); + $coursetagobjects = []; + $questiontagobjects = []; + + if ($expectcoursetags) { + // If the use case is expecting course tags to be created then split + // the tags into course tags and question tags and ensure we have + // the correct number of course tags. + + while ($tagobject = array_shift($tagobjects)) { + if ($tagobject->taginstancecontextid == $questioncontext->id) { + $questiontagobjects[] = $tagobject; + } else if ($tagobject->taginstancecontextid == $coursecontext->id) { + $coursetagobjects[] = $tagobject; + } + } + + $this->assertCount(count($coursetags), $coursetagobjects); + } else { + $questiontagobjects = $tagobjects; + } + + // Ensure the expected number of question tags was created. + $this->assertCount(count($questiontags), $questiontagobjects); + + foreach ($questiontagobjects as $tagobject) { + // If we have any question tags then make sure they are in the list + // of expected tags and have the correct context. + $this->assertContains($tagobject->name, $questiontags); + $this->assertEquals($questioncontext->id, $tagobject->taginstancecontextid); + } + + foreach ($coursetagobjects as $tagobject) { + // If we have any course tags then make sure they are in the list + // of expected course tags and have the correct context. + $this->assertContains($tagobject->name, $coursetags); + $this->assertEquals($coursecontext->id, $tagobject->taginstancecontextid); + } + } + + /** + * Build the encoded form data expected by the submit_tags_form external function. + * + * @param \stdClass $question The question record + * @param \stdClass $questioncategory The question category record + * @param \context $questioncontext Context for the question category + * @param array $tags A list of tag names for the question + * @param array $coursetags A list of course tag names for the question + * @return string HTML encoded string of the data + */ + protected function generate_encoded_submit_tags_form_string($question, $questioncategory, + $questioncontext, $tags = [], $coursetags = []) { + + $data = [ + 'id' => $question->id, + 'categoryid' => $questioncategory->id, + 'contextid' => $questioncontext->id, + 'questionname' => $question->name, + 'questioncategory' => $questioncategory->name, + 'context' => $questioncontext->get_context_name(false), + 'tags' => $tags, + 'coursetags' => $coursetags + ]; + $data = \qbank_tagquestion\form\tags_form::mock_generate_submit_keys($data); + + return http_build_query($data, '', '&'); + } + + /** + * Create a user, enrol them in the course, and give them the capability to + * tag all questions in the system context. + * + * @param \stdClass $course The course record to enrol in + * @return \stdClass The user record + */ + protected function create_user_can_tag($course) { + global $DB; + + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $roleid = $generator->create_role(); + $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); + $systemcontext = \context_system::instance(); + + $generator->role_assign($roleid, $user->id, $systemcontext->id); + $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); + + // Give the user global ability to tag questions. + assign_capability('moodle/question:tagall', CAP_ALLOW, $roleid, $systemcontext, true); + // Allow the user to submit form data. + $user->ignoresesskey = true; + + return $user; + } + +} diff --git a/question/bank/tagquestion/version.php b/question/bank/tagquestion/version.php new file mode 100644 index 00000000000..2709228d670 --- /dev/null +++ b/question/bank/tagquestion/version.php @@ -0,0 +1,31 @@ +. + +/** + * Version information for qbank_tagquestion. + * + * @package qbank_tagquestion + * @copyright 2021 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->component = 'qbank_tagquestion'; +$plugin->version = 2021090300; +$plugin->requires = 2021052500; +$plugin->maturity = MATURITY_STABLE; diff --git a/question/classes/bank/tags_action_column.php b/question/classes/bank/tags_action_column.php index 222ac580b36..991e2315f54 100644 --- a/question/classes/bank/tags_action_column.php +++ b/question/classes/bank/tags_action_column.php @@ -40,11 +40,12 @@ class tags_action_column extends action_column_base implements menuable_action { public function init() { parent::init(); - global $CFG; - if ($CFG->usetags) { - global $PAGE; - $PAGE->requires->js_call_amd('core_question/edit_tags', 'init', ['#questionscontainer']); - } + // Removed for conflicting js calls. + // ...global $CFG;. + // ...if ($CFG->usetags) {. + // ...global $PAGE;. + // ...$PAGE->requires->js_call_amd('core_question/edit_tags', 'init', ['#questionscontainer']);. + // ...}. $this->managetags = get_string('managetags', 'tag'); } diff --git a/question/classes/external.php b/question/classes/external.php index 9bda45d7c54..3a5661d1231 100644 --- a/question/classes/external.php +++ b/question/classes/external.php @@ -121,6 +121,9 @@ class core_question_external extends external_api { * Returns description of method parameters. * * @return external_function_parameters. + * @deprecated since Moodle 4.0 + * @see \qbank_tagquestion\external\qbank_tagquestion_external + * @todo Final deprecation on Moodle 4.4 MDL-72438 */ public static function submit_tags_form_parameters() { return new external_function_parameters([ @@ -137,6 +140,9 @@ class core_question_external extends external_api { * @param int $contextid The editing context id. * @param string $formdata The question tag form data in a URI encoded param string * @return array The created or modified question tag + * @deprecated since Moodle 4.0 + * @see \qbank_tagquestion\external\qbank_tagquestion_external + * @todo Final deprecation on Moodle 4.4 MDL-72438 */ public static function submit_tags_form($questionid, $contextid, $formdata) { global $DB, $CFG; @@ -206,6 +212,10 @@ class core_question_external extends external_api { /** * Returns description of method result value. + * + * @deprecated since Moodle 4.0 + * @see \qbank_tagquestion\external\qbank_tagquestion_external + * @todo Final deprecation on Moodle 4.4 MDL-72438 */ public static function submit_tags_form_returns() { return new external_single_structure([ @@ -213,6 +223,16 @@ class core_question_external extends external_api { ]); } + /** + * Marking the method as deprecated. + * + * @return bool + * @todo Final deprecation on Moodle 4.4 MDL-72438 + */ + public static function submit_tags_form_is_deprecated() { + return true; + } + /** * Returns description of method parameters. * diff --git a/question/lib.php b/question/lib.php index 56fda425f95..a326e1cf087 100644 --- a/question/lib.php +++ b/question/lib.php @@ -33,8 +33,13 @@ defined('MOODLE_INTERNAL') || die(); * * @param array $args Arguments to the form. * @return null|string The rendered form. + * @deprecated since Moodle 4.0 + * @see /question/bank/qbank_tagquestion/lib.php + * @todo Final deprecation on Moodle 4.4 MDL-72438 */ function core_question_output_fragment_tags_form($args) { + debugging('Function core_question_output_fragment_tags_form() is deprecated, + please use core_question_output_fragment_tags_form() from qbank_tagquestion instead.', DEBUG_DEVELOPER); if (!empty($args['id'])) { global $CFG, $DB; diff --git a/question/tests/externallib_test.php b/question/tests/externallib_test.php index 06783ce26de..19a54df21be 100644 --- a/question/tests/externallib_test.php +++ b/question/tests/externallib_test.php @@ -103,376 +103,6 @@ class core_question_external_testcase extends externallib_advanced_testcase { } } - /** - * submit_tags_form should throw an exception when the question id doesn't match - * a question. - */ - public function test_submit_tags_form_incorrect_question_id() { - $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); - $questioncontext = context::instance_by_id($qcat->contextid); - $editingcontext = $questioncontext; - $question = $questions[0]; - // Generate an id for a question that doesn't exist. - $missingquestionid = $questions[1]->id * 2; - $question->id = $missingquestionid; - $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); - - // We should receive an exception if the question doesn't exist. - $this->expectException('moodle_exception'); - core_question_external::submit_tags_form($missingquestionid, $editingcontext->id, $formdata); - } - - /** - * submit_tags_form should throw an exception when the context id doesn't match - * a context. - */ - public function test_submit_tags_form_incorrect_context_id() { - $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); - $questioncontext = context::instance_by_id($qcat->contextid); - $editingcontext = $questioncontext; - $question = $questions[0]; - // Generate an id for a context that doesn't exist. - $missingcontextid = $editingcontext->id * 200; - $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); - - // We should receive an exception if the question doesn't exist. - $this->expectException('moodle_exception'); - core_question_external::submit_tags_form($question->id, $missingcontextid, $formdata); - } - - /** - * submit_tags_form should return false when tags are disabled. - */ - public function test_submit_tags_form_tags_disabled() { - global $CFG; - - $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); - $questioncontext = context::instance_by_id($qcat->contextid); - $editingcontext = $questioncontext; - $question = $questions[0]; - $user = $this->create_user_can_tag($course); - $formdata = $this->generate_encoded_submit_tags_form_string($question, $qcat, $questioncontext, [], []); - - $this->setUser($user); - $CFG->usetags = false; - $result = core_question_external::submit_tags_form($question->id, $editingcontext->id, $formdata); - $CFG->usetags = true; - - $this->assertFalse($result['status']); - } - - /** - * submit_tags_form should return false if the user does not have any capability - * to tag the question. - */ - public function test_submit_tags_form_no_tag_permissions() { - global $DB; - - $generator = $this->getDataGenerator(); - $user = $generator->create_user(); - $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); - $questiongenerator = $generator->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); - $questioncontext = context::instance_by_id($qcat->contextid); - $editingcontext = $questioncontext; - $question = $questions[0]; - $formdata = $this->generate_encoded_submit_tags_form_string( - $question, - $qcat, - $questioncontext, - ['foo'], - ['bar'] - ); - - // Prohibit all of the tag capabilities. - assign_capability('moodle/question:tagmine', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); - assign_capability('moodle/question:tagall', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); - - $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); - $user->ignoresesskey = true; - $this->setUser($user); - - $result = core_question_external::submit_tags_form($question->id, $editingcontext->id, $formdata); - - $this->assertFalse($result['status']); - } - - /** - * submit_tags_form should return false if the user only has the capability to - * tag their own questions and the question is not theirs. - */ - public function test_submit_tags_form_tagmine_permission_non_owner_question() { - global $DB; - - $generator = $this->getDataGenerator(); - $user = $generator->create_user(); - $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); - $questiongenerator = $generator->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions(); - $questioncontext = context::instance_by_id($qcat->contextid); - $editingcontext = $questioncontext; - $question = $questions[0]; - $formdata = $this->generate_encoded_submit_tags_form_string( - $question, - $qcat, - $questioncontext, - ['foo'], - ['bar'] - ); - - // Make sure the question isn't created by the user. - $question->createdby = $user->id + 1; - - // Prohibit all of the tag capabilities. - assign_capability('moodle/question:tagmine', CAP_ALLOW, $teacherrole->id, $questioncontext->id); - assign_capability('moodle/question:tagall', CAP_PROHIBIT, $teacherrole->id, $questioncontext->id); - - $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); - $user->ignoresesskey = true; - $this->setUser($user); - - $result = core_question_external::submit_tags_form($question->id, $editingcontext->id, $formdata); - - $this->assertFalse($result['status']); - } - - /** - * Data provided for the submit_tags_form test to check that course tags are - * only created in the correct editing and question context combinations. - * - * @return array Test cases - */ - public function get_submit_tags_form_testcases() { - return [ - 'course - course' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course - course - empty tags' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'course', - 'questiontags' => [], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course - course category' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => true - ], - 'course - system' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => true - ], - 'course category - course' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course category - course category' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course category - system' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - course' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - course category' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - system' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - ]; - } - - /** - * Tests that submit_tags_form only creates course tags when the correct combination - * of editing context and question context is provided. - * - * Course tags can only be set on a course category or system context question that - * is being editing in a course context. - * - * @dataProvider get_submit_tags_form_testcases() - * @param string $editingcontext The type of the context the question is being edited in - * @param string $questioncontext The type of the context the question belongs to - * @param string[] $questiontags The tag names to set as question tags - * @param string[] $coursetags The tag names to set as course tags - * @param bool $expectcoursetags If the given course tags should have been set or not - */ - public function test_submit_tags_form_context_combinations( - $editingcontext, - $questioncontext, - $questiontags, - $coursetags, - $expectcoursetags - ) { - $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions($questioncontext); - $coursecontext = context_course::instance($course->id); - $questioncontext = context::instance_by_id($qcat->contextid); - - switch($editingcontext) { - case 'system': - $editingcontext = context_system::instance(); - break; - - case 'category': - $editingcontext = context_coursecat::instance($category->id); - break; - - default: - $editingcontext = context_course::instance($course->id); - } - - $user = $this->create_user_can_tag($course); - $question = $questions[0]; - $formdata = $this->generate_encoded_submit_tags_form_string( - $question, - $qcat, - $questioncontext, - $questiontags, // Question tags. - $coursetags // Course tags. - ); - - $this->setUser($user); - - $result = core_question_external::submit_tags_form($question->id, $editingcontext->id, $formdata); - - $this->assertTrue($result['status']); - - $tagobjects = core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $coursetagobjects = []; - $questiontagobjects = []; - - if ($expectcoursetags) { - // If the use case is expecting course tags to be created then split - // the tags into course tags and question tags and ensure we have - // the correct number of course tags. - - while ($tagobject = array_shift($tagobjects)) { - if ($tagobject->taginstancecontextid == $questioncontext->id) { - $questiontagobjects[] = $tagobject; - } else if ($tagobject->taginstancecontextid == $coursecontext->id) { - $coursetagobjects[] = $tagobject; - } - } - - $this->assertCount(count($coursetags), $coursetagobjects); - } else { - $questiontagobjects = $tagobjects; - } - - // Ensure the expected number of question tags was created. - $this->assertCount(count($questiontags), $questiontagobjects); - - foreach ($questiontagobjects as $tagobject) { - // If we have any question tags then make sure they are in the list - // of expected tags and have the correct context. - $this->assertContains($tagobject->name, $questiontags); - $this->assertEquals($questioncontext->id, $tagobject->taginstancecontextid); - } - - foreach ($coursetagobjects as $tagobject) { - // If we have any course tags then make sure they are in the list - // of expected course tags and have the correct context. - $this->assertContains($tagobject->name, $coursetags); - $this->assertEquals($coursecontext->id, $tagobject->taginstancecontextid); - } - } - - /** - * Build the encoded form data expected by the submit_tags_form external function. - * - * @param stdClass $question The question record - * @param stdClass $questioncategory The question category record - * @param context $questioncontext Context for the question category - * @param array $tags A list of tag names for the question - * @param array $coursetags A list of course tag names for the question - * @return string HTML encoded string of the data - */ - protected function generate_encoded_submit_tags_form_string($question, $questioncategory, - $questioncontext, $tags = [], $coursetags = []) { - global $CFG; - - require_once($CFG->dirroot . '/question/type/tags_form.php'); - - $data = [ - 'id' => $question->id, - 'categoryid' => $questioncategory->id, - 'contextid' => $questioncontext->id, - 'questionname' => $question->name, - 'questioncategory' => $questioncategory->name, - 'context' => $questioncontext->get_context_name(false), - 'tags' => $tags, - 'coursetags' => $coursetags - ]; - $data = core_question\form\tags::mock_generate_submit_keys($data); - - return http_build_query($data, '', '&'); - } - - /** - * Create a user, enrol them in the course, and give them the capability to - * tag all questions in the system context. - * - * @param stdClass $course The course record to enrol in - * @return stdClass The user record - */ - protected function create_user_can_tag($course) { - global $DB; - - $generator = $this->getDataGenerator(); - $user = $generator->create_user(); - $roleid = $generator->create_role(); - $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); - $systemcontext = context_system::instance(); - - $generator->role_assign($roleid, $user->id, $systemcontext->id); - $generator->enrol_user($user->id, $course->id, $teacherrole->id, 'manual'); - - // Give the user global ability to tag questions. - assign_capability('moodle/question:tagall', CAP_ALLOW, $roleid, $systemcontext, true); - // Allow the user to submit form data. - $user->ignoresesskey = true; - - return $user; - } - /** * Data provider for the get_random_question_summaries test. */ diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 384e689128b..b408b2e46a7 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -216,7 +216,9 @@ abstract class question_edit_form extends question_wizard_form { // Any questiontype specific fields. $this->definition_inner($mform); - if (core_tag_tag::is_enabled('core_question', 'question')) { + if (core_tag_tag::is_enabled('core_question', 'question') + && class_exists('qbank_tagquestion\\tags_action_column') + && \core\plugininfo\qbank::is_plugin_enabled('qbank_tagquestion')) { $this->add_tag_fields($mform); } diff --git a/question/type/tags_form.php b/question/type/tags_form.php index cca587f614a..d3c22c8630c 100644 --- a/question/type/tags_form.php +++ b/question/type/tags_form.php @@ -33,6 +33,9 @@ require_once($CFG->dirroot . '/lib/questionlib.php'); * * @copyright 2018 Simey Lameze * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @deprecated since Moodle 4.0 + * @see \qbank_tagquestion\form\tags_form + * @todo MDL-71679 class renaming */ class tags extends \moodleform { @@ -40,6 +43,8 @@ class tags extends \moodleform { * The form definition */ public function definition() { + debugging('Class column_base in core_question\form\tags is deprecated, + please use qbank_tagquestion\form\tags_form instead.', DEBUG_DEVELOPER); $mform = $this->_form; $customdata = $this->_customdata; diff --git a/question/upgrade.txt b/question/upgrade.txt index f2b7fedbf61..6d76a66d63d 100644 --- a/question/upgrade.txt +++ b/question/upgrade.txt @@ -6,6 +6,8 @@ This files describes API changes for code that uses the question API. are divided in two different parts, base classes and feature classes. All the base classes are moved classes/local/bank and all the feature classes will be moved to the plugin for that feature. +2) submit_tags_form and associated external services for question tag, tags_form in question/type, + core_question_output_fragment_tags_form method in lib is deprecated and moved to the tagquestion plugin. === 3.9 ==