diff --git a/lang/en/question.php b/lang/en/question.php index e3e2468469bf6..34eef1f10c7bf 100644 --- a/lang/en/question.php +++ b/lang/en/question.php @@ -498,6 +498,8 @@ $string['qbanknotfound'] = 'The \'{$a}\' question bank plugin doesn\'t exist or is not recognised.'; $string['noquestionbanks'] = 'No question bank plugin found.'; $string['questionloaderror'] = 'Could not load the question options.'; +$string['version_selection'] = 'Version {$a->version}'; +$string['question_version'] = 'Question version'; // Deprecated since Moodle 4.0. $string['notflagged'] = 'Not flagged'; diff --git a/question/bank/comment/amd/build/comment.min.js b/question/bank/comment/amd/build/comment.min.js index b84b05a4db920..c9dea20ef26b5 100644 --- a/question/bank/comment/amd/build/comment.min.js +++ b/question/bank/comment/amd/build/comment.min.js @@ -6,6 +6,6 @@ define("qbank_comment/comment",["exports","core/fragment","core/str","core/modal * @copyright 2021 Catalyst IT Australia Pty Ltd * @author Safat Shahin * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_modal_events=_interopRequireDefault(_modal_events),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification);_exports.init=questionSelector=>{let target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid"),courseID=target.getAttribute("data-courseid");target.addEventListener("click",(()=>{((questionId,courseID,contextId)=>{let args={questionid:questionId,courseid:courseID},commentFragment=_fragment.default.loadFragment("qbank_comment","question_comment",contextId,args);_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:Str.get_string("commentheader","qbank_comment"),body:commentFragment,large:!0}).then((modal=>{let root=modal.getRoot();return root.on(_modal_events.default.bodyRendered,(function(){document.querySelectorAll("div.comment-area a")[0].style.display="none"})),Str.get_strings([{key:"addcomment",component:"qbank_comment"},{key:"close",component:"qbank_comment"}]).then((strings=>{modal.setButtonText("save",strings[0]),modal.setButtonText("cancel",strings[1])})).fail(_notification.default.exception),root.on(_modal_events.default.cancel,(function(){location.reload(),modal.hide()})),root.on(_modal_events.default.save,(function(e){e.preventDefault();const submitlink=document.querySelectorAll("div.comment-area a")[0],textarea=document.querySelectorAll("div.comment-area textarea")[0];textarea.value!=textarea.getAttribute("aria-label")&&""!=textarea.value&&submitlink.click()})),root.on("click",'button[data-action="hide"]',(()=>{location.reload(),modal.hide()})),modal.show(),modal})).fail(_notification.default.exception)})(questionId,courseID,1)}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_modal_events=_interopRequireDefault(_modal_events),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification);_exports.init=questionSelector=>{let target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid"),courseID=target.getAttribute("data-courseid");target.addEventListener("click",(()=>{((questionId,courseID,contextId)=>{let args={questionid:questionId,courseid:courseID,uniqueidentifier:"question_comment_version_dropdown"};_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:Str.get_string("commentheader","qbank_comment"),body:_fragment.default.loadFragment("qbank_comment","question_comment",contextId,args),large:!0}).then((modal=>{let root=modal.getRoot();return root.on(_modal_events.default.bodyRendered,(function(){document.querySelectorAll("div.comment-area a")[0].style.display="none"})),root.on("change","#question_comment_version_dropdown",(function(e){args.questionid=e.target.value,modal.setBody(_fragment.default.loadFragment("qbank_comment","question_comment",contextId,args))})),Str.get_strings([{key:"addcomment",component:"qbank_comment"},{key:"close",component:"qbank_comment"}]).then((strings=>{modal.setButtonText("save",strings[0]),modal.setButtonText("cancel",strings[1])})).fail(_notification.default.exception),root.on(_modal_events.default.cancel,(function(){location.reload(),modal.hide()})),root.on(_modal_events.default.save,(function(e){e.preventDefault();const submitlink=document.querySelectorAll("div.comment-area a")[0],textarea=document.querySelectorAll("div.comment-area textarea")[0];textarea.value!=textarea.getAttribute("aria-label")&&""!=textarea.value&&submitlink.click()})),root.on("click",'button[data-action="hide"]',(()=>{location.reload(),modal.hide()})),modal.show(),modal})).fail(_notification.default.exception)})(questionId,courseID,1)}))}})); //# sourceMappingURL=comment.min.js.map \ No newline at end of file diff --git a/question/bank/comment/amd/build/comment.min.js.map b/question/bank/comment/amd/build/comment.min.js.map index ed4b8ef22d044..13f179165393b 100644 --- a/question/bank/comment/amd/build/comment.min.js.map +++ b/question/bank/comment/amd/build/comment.min.js.map @@ -1 +1 @@ -{"version":3,"file":"comment.min.js","sources":["../src/comment.js"],"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 * Column selector js.\n *\n * @module qbank_comment/comment\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport * as Str from 'core/str';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {Number} questionId\n * @param {Number} courseID\n * @param {Number} contextId\n */\nconst commentEvent = (questionId, courseID, contextId) => {\n let args = {\n questionid: questionId,\n courseid: courseID\n };\n let commentFragment = Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args);\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: Str.get_string('commentheader', 'qbank_comment'),\n body: commentFragment,\n large: true,\n }).then((modal) => {\n let root = modal.getRoot();\n\n // Don't display the default add comment link in the modal.\n root.on(ModalEvents.bodyRendered, function() {\n const submitlink = document.querySelectorAll(\"div.comment-area a\")[0];\n submitlink.style.display = 'none';\n });\n\n // Get the required strings and updated the modal button text labels.\n Str.get_strings([\n {key: 'addcomment', component: 'qbank_comment'},\n {key: 'close', component: 'qbank_comment'},\n ]).then((strings) => {\n modal.setButtonText('save', strings[0]);\n modal.setButtonText('cancel', strings[1]);\n return;\n }).fail(Notification.exception);\n\n root.on(ModalEvents.cancel, function() {\n location.reload();\n modal.hide();\n });\n\n // Handle adding the comment when the button in the modal is clicked.\n root.on(ModalEvents.save, function(e) {\n e.preventDefault();\n const submitlink = document.querySelectorAll(\"div.comment-area a\")[0];\n const textarea = document.querySelectorAll(\"div.comment-area textarea\")[0];\n\n // Check there is a valid comment to add, and trigger adding if there is.\n if (textarea.value != textarea.getAttribute('aria-label') && textarea.value != '') {\n submitlink.click();\n }\n\n });\n root.on('click', 'button[data-action=\"hide\"]', () => {\n location.reload();\n modal.hide();\n });\n modal.show();\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question comment identifier.\n */\nexport const init = (questionSelector) => {\n let target = document.querySelector(questionSelector);\n let contextId = 1;\n let questionId = target.getAttribute('data-questionid'),\n courseID = target.getAttribute('data-courseid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any comment count row.\n commentEvent(questionId, courseID, contextId);\n });\n};\n"],"names":["questionSelector","target","document","querySelector","questionId","getAttribute","courseID","addEventListener","contextId","args","questionid","courseid","commentFragment","Fragment","loadFragment","create","type","ModalFactory","types","SAVE_CANCEL","title","Str","get_string","body","large","then","modal","root","getRoot","on","ModalEvents","bodyRendered","querySelectorAll","style","display","get_strings","key","component","strings","setButtonText","fail","Notification","exception","cancel","location","reload","hide","save","e","preventDefault","submitlink","textarea","value","click","show","commentEvent"],"mappings":";;;;;;;;g8BAoGqBA,uBACbC,OAASC,SAASC,cAAcH,kBAEhCI,WAAaH,OAAOI,aAAa,mBACjCC,SAAWL,OAAOI,aAAa,iBACnCJ,OAAOM,iBAAiB,SAAS,KAnEhB,EAACH,WAAYE,SAAUE,iBACpCC,KAAO,CACPC,WAAYN,WACZO,SAAUL,UAEVM,gBAAkBC,kBAASC,aAAa,gBAAiB,mBAAoBN,UAAWC,6BAC/EM,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,YACzBC,MAAOC,IAAIC,WAAW,gBAAiB,iBACvCC,KAAMX,gBACNY,OAAO,IACRC,MAAMC,YACDC,KAAOD,MAAME,iBAGjBD,KAAKE,GAAGC,sBAAYC,cAAc,WACX7B,SAAS8B,iBAAiB,sBAAsB,GACxDC,MAAMC,QAAU,UAI/Bb,IAAIc,YAAY,CACZ,CAACC,IAAK,aAAcC,UAAW,iBAC/B,CAACD,IAAK,QAASC,UAAW,mBAC3BZ,MAAMa,UACLZ,MAAMa,cAAc,OAAQD,QAAQ,IACpCZ,MAAMa,cAAc,SAAUD,QAAQ,OAEvCE,KAAKC,sBAAaC,WAErBf,KAAKE,GAAGC,sBAAYa,QAAQ,WACxBC,SAASC,SACTnB,MAAMoB,UAIVnB,KAAKE,GAAGC,sBAAYiB,MAAM,SAASC,GAC/BA,EAAEC,uBACIC,WAAahD,SAAS8B,iBAAiB,sBAAsB,GAC7DmB,SAAWjD,SAAS8B,iBAAiB,6BAA6B,GAGpEmB,SAASC,OAASD,SAAS9C,aAAa,eAAmC,IAAlB8C,SAASC,OAClEF,WAAWG,WAInB1B,KAAKE,GAAG,QAAS,8BAA8B,KAC3Ce,SAASC,SACTnB,MAAMoB,UAEVpB,MAAM4B,OACC5B,SACRc,KAAKC,sBAAaC,YAgBjBa,CAAanD,WAAYE,SALb"} \ No newline at end of file +{"version":3,"file":"comment.min.js","sources":["../src/comment.js"],"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 * Column selector js.\n *\n * @module qbank_comment/comment\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport * as Str from 'core/str';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {Number} questionId\n * @param {Number} courseID\n * @param {Number} contextId\n */\nconst commentEvent = (questionId, courseID, contextId) => {\n let args = {\n questionid: questionId,\n courseid: courseID,\n uniqueidentifier: 'question_comment_version_dropdown'\n };\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: Str.get_string('commentheader', 'qbank_comment'),\n body: Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args),\n large: true,\n }).then((modal) => {\n let root = modal.getRoot();\n\n // Don't display the default add comment link in the modal.\n root.on(ModalEvents.bodyRendered, function() {\n const submitlink = document.querySelectorAll(\"div.comment-area a\")[0];\n submitlink.style.display = 'none';\n });\n\n // Version selection event.\n root.on('change', '#question_comment_version_dropdown', function(e) {\n args.questionid = e.target.value;\n modal.setBody(Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args));\n });\n\n // Get the required strings and updated the modal button text labels.\n Str.get_strings([\n {key: 'addcomment', component: 'qbank_comment'},\n {key: 'close', component: 'qbank_comment'},\n ]).then((strings) => {\n modal.setButtonText('save', strings[0]);\n modal.setButtonText('cancel', strings[1]);\n return;\n }).fail(Notification.exception);\n\n root.on(ModalEvents.cancel, function() {\n location.reload();\n modal.hide();\n });\n\n // Handle adding the comment when the button in the modal is clicked.\n root.on(ModalEvents.save, function(e) {\n e.preventDefault();\n const submitlink = document.querySelectorAll(\"div.comment-area a\")[0];\n const textarea = document.querySelectorAll(\"div.comment-area textarea\")[0];\n\n // Check there is a valid comment to add, and trigger adding if there is.\n if (textarea.value != textarea.getAttribute('aria-label') && textarea.value != '') {\n submitlink.click();\n }\n\n });\n root.on('click', 'button[data-action=\"hide\"]', () => {\n location.reload();\n modal.hide();\n });\n modal.show();\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question comment identifier.\n */\nexport const init = (questionSelector) => {\n let target = document.querySelector(questionSelector);\n let contextId = 1;\n let questionId = target.getAttribute('data-questionid'),\n courseID = target.getAttribute('data-courseid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any comment count row.\n commentEvent(questionId, courseID, contextId);\n });\n};\n"],"names":["questionSelector","target","document","querySelector","questionId","getAttribute","courseID","addEventListener","contextId","args","questionid","courseid","uniqueidentifier","create","type","ModalFactory","types","SAVE_CANCEL","title","Str","get_string","body","Fragment","loadFragment","large","then","modal","root","getRoot","on","ModalEvents","bodyRendered","querySelectorAll","style","display","e","value","setBody","get_strings","key","component","strings","setButtonText","fail","Notification","exception","cancel","location","reload","hide","save","preventDefault","submitlink","textarea","click","show","commentEvent"],"mappings":";;;;;;;;g8BA0GqBA,uBACbC,OAASC,SAASC,cAAcH,kBAEhCI,WAAaH,OAAOI,aAAa,mBACjCC,SAAWL,OAAOI,aAAa,iBACnCJ,OAAOM,iBAAiB,SAAS,KAzEhB,EAACH,WAAYE,SAAUE,iBACpCC,KAAO,CACPC,WAAYN,WACZO,SAAUL,SACVM,iBAAkB,4DAETC,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,YACzBC,MAAOC,IAAIC,WAAW,gBAAiB,iBACvCC,KAAMC,kBAASC,aAAa,gBAAiB,mBAAoBf,UAAWC,MAC5Ee,OAAO,IACRC,MAAMC,YACDC,KAAOD,MAAME,iBAGjBD,KAAKE,GAAGC,sBAAYC,cAAc,WACX7B,SAAS8B,iBAAiB,sBAAsB,GACxDC,MAAMC,QAAU,UAI/BP,KAAKE,GAAG,SAAU,sCAAsC,SAASM,GAC7D1B,KAAKC,WAAayB,EAAElC,OAAOmC,MAC3BV,MAAMW,QAAQf,kBAASC,aAAa,gBAAiB,mBAAoBf,UAAWC,UAIxFU,IAAImB,YAAY,CACZ,CAACC,IAAK,aAAcC,UAAW,iBAC/B,CAACD,IAAK,QAASC,UAAW,mBAC3Bf,MAAMgB,UACLf,MAAMgB,cAAc,OAAQD,QAAQ,IACpCf,MAAMgB,cAAc,SAAUD,QAAQ,OAEvCE,KAAKC,sBAAaC,WAErBlB,KAAKE,GAAGC,sBAAYgB,QAAQ,WACxBC,SAASC,SACTtB,MAAMuB,UAIVtB,KAAKE,GAAGC,sBAAYoB,MAAM,SAASf,GAC/BA,EAAEgB,uBACIC,WAAalD,SAAS8B,iBAAiB,sBAAsB,GAC7DqB,SAAWnD,SAAS8B,iBAAiB,6BAA6B,GAGpEqB,SAASjB,OAASiB,SAAShD,aAAa,eAAmC,IAAlBgD,SAASjB,OAClEgB,WAAWE,WAInB3B,KAAKE,GAAG,QAAS,8BAA8B,KAC3CkB,SAASC,SACTtB,MAAMuB,UAEVvB,MAAM6B,OACC7B,SACRiB,KAAKC,sBAAaC,YAgBjBW,CAAapD,WAAYE,SALb"} \ No newline at end of file diff --git a/question/bank/comment/amd/src/comment.js b/question/bank/comment/amd/src/comment.js index c0c28e5609d79..ce8e26dcbcb90 100644 --- a/question/bank/comment/amd/src/comment.js +++ b/question/bank/comment/amd/src/comment.js @@ -39,13 +39,13 @@ import Notification from 'core/notification'; const commentEvent = (questionId, courseID, contextId) => { let args = { questionid: questionId, - courseid: courseID + courseid: courseID, + uniqueidentifier: 'question_comment_version_dropdown' }; - let commentFragment = Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args); ModalFactory.create({ type: ModalFactory.types.SAVE_CANCEL, title: Str.get_string('commentheader', 'qbank_comment'), - body: commentFragment, + body: Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args), large: true, }).then((modal) => { let root = modal.getRoot(); @@ -56,6 +56,12 @@ const commentEvent = (questionId, courseID, contextId) => { submitlink.style.display = 'none'; }); + // Version selection event. + root.on('change', '#question_comment_version_dropdown', function(e) { + args.questionid = e.target.value; + modal.setBody(Fragment.loadFragment('qbank_comment', 'question_comment', contextId, args)); + }); + // Get the required strings and updated the modal button text labels. Str.get_strings([ {key: 'addcomment', component: 'qbank_comment'}, diff --git a/question/bank/comment/lib.php b/question/bank/comment/lib.php index eacfa04a38088..898ee50827a3f 100644 --- a/question/bank/comment/lib.php +++ b/question/bank/comment/lib.php @@ -109,6 +109,9 @@ function qbank_comment_preview_display($question, $courseid): string { * @return string rendered output */ function qbank_comment_output_fragment_question_comment($args): string { + if (!$args['questionid'] || !$args['uniqueidentifier']) { + return ''; + } global $USER, $PAGE, $CFG, $DB; $displaydata = []; require_once($CFG->dirroot . '/question/engine/bank.php'); @@ -133,5 +136,9 @@ function qbank_comment_output_fragment_question_comment($args): string { $displaydata['commenstdisabled'] = true; } + $selector = \core_question\output\question_version_selection::make_for_question($args['uniqueidentifier'], $args['questionid']); + $qbankrenderer = $PAGE->get_renderer('core_question', 'bank'); + $displaydata['versionselection'] = $selector->export_for_template($qbankrenderer); + return $PAGE->get_renderer('qbank_comment')->render_comment_fragment($displaydata); } diff --git a/question/bank/comment/templates/comment_modal.mustache b/question/bank/comment/templates/comment_modal.mustache index 571078529e6be..c6266024ae772 100644 --- a/question/bank/comment/templates/comment_modal.mustache +++ b/question/bank/comment/templates/comment_modal.mustache @@ -33,7 +33,9 @@ ] } }} - +{{#versionselection}} + {{> core_question/question_version_selection }} +{{/versionselection}}
{{{question}}}
diff --git a/question/bank/comment/tests/behat/question_comment.feature b/question/bank/comment/tests/behat/question_comment.feature index 18e306eff1d94..1aaca0d492b8d 100644 --- a/question/bank/comment/tests/behat/question_comment.feature +++ b/question/bank/comment/tests/behat/question_comment.feature @@ -146,3 +146,25 @@ Feature: A Teacher can comment in a question And I switch to "questionpreview" window And I press "Comments" Then I should not see "Some new comment" + + @javascript + Scenario: Comments modal can change the version using dropdown + Given I log in as "teacher1" + And I am on the "Test quiz" "quiz activity" page + When I navigate to "Question bank" in current page administration + And I set the field "Select a category" to "Test questions" + And I should see "First question" + And I click on "Edit" "link" in the "First question" "table_row" + And I follow "Edit question" + And I set the field "id_name" to "Renamed question v2" + And I set the field "id_questiontext" to "edited question" + And I press "id_submitbutton" + And I should not see "First question" + And I should see "Renamed question v2" + And I click "0" on the row on the comments column + And I should see "Version 2" + Then I should see "edited question" + And I click on "question_version_dropdown" "select" + And I should see "Version 1" + And I click on "Version 1" "option" + And I should see "Answer the first question" diff --git a/question/bank/usage/amd/build/usage.min.js b/question/bank/usage/amd/build/usage.min.js index a32c61b9aec31..11e80743bd27d 100644 --- a/question/bank/usage/amd/build/usage.min.js +++ b/question/bank/usage/amd/build/usage.min.js @@ -1,4 +1,4 @@ -define("qbank_usage/usage",["exports","core/fragment","core/str","core/modal_factory","core/notification"],(function(_exports,_fragment,Str,_modal_factory,_notification){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("qbank_usage/usage",["exports","core/fragment","core/modal_factory","core/notification","core/str"],(function(_exports,_fragment,_modal_factory,_notification,Str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Usage column selector js. * @@ -6,6 +6,6 @@ define("qbank_usage/usage",["exports","core/fragment","core/str","core/modal_fac * @copyright 2021 Catalyst IT Australia Pty Ltd * @author Safat Shahin * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification);const getFragment=(args,contextId)=>_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args);_exports.init=(questionSelector,contextId)=>{let target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid");target.addEventListener("click",(()=>{((questionId,contextId)=>{let args={questionid:questionId};_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:Str.get_string("usageheader","qbank_usage"),body:getFragment(args,contextId),large:!0}).then((modal=>(modal.show(),modal.getRoot().on("click","a[href].page-link",(function(e){e.preventDefault();let attr=e.target.getAttribute("href");"#"!==attr&&(args.querystring=attr,modal.setBody(getFragment(args,contextId)))})),modal))).fail(_notification.default.exception)})(questionId,contextId)}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);_exports.init=(questionSelector,contextId)=>{let target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid");target.addEventListener("click",(()=>{((questionId,contextId)=>{let args={questionid:questionId,uniqueidentifier:"question_usage_version_dropdown"};_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:Str.get_string("usageheader","qbank_usage"),body:_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args),large:!0}).then((modal=>(modal.show(),modal.getRoot().on("click","a[href].page-link",(function(e){e.preventDefault();let attr=e.target.getAttribute("href");"#"!==attr&&(args.querystring=attr,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args)))})),modal.getRoot().on("change","#question_usage_version_dropdown",(function(e){args.questionid=e.target.value,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args))})),modal))).fail(_notification.default.exception)})(questionId,contextId)}))}})); //# sourceMappingURL=usage.min.js.map \ No newline at end of file diff --git a/question/bank/usage/amd/build/usage.min.js.map b/question/bank/usage/amd/build/usage.min.js.map index e67e58f65005d..8e1cc759b3520 100644 --- a/question/bank/usage/amd/build/usage.min.js.map +++ b/question/bank/usage/amd/build/usage.min.js.map @@ -1 +1 @@ -{"version":3,"file":"usage.min.js","sources":["../src/usage.js"],"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 * Usage column selector js.\n *\n * @module qbank_usage/usage\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport * as Str from 'core/str';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\n\n/**\n * Get the fragment.\n *\n * @method getFragment\n * @param {{questioned: int}} args\n * @param {int} contextId\n * @return {string}\n */\nconst getFragment = (args, contextId) => {\n return Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args);\n};\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {int} questionId\n * @param {int} contextId\n */\nconst usageEvent = (questionId, contextId) => {\n let args = {\n questionid: questionId\n };\n ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: Str.get_string('usageheader', 'qbank_usage'),\n body: getFragment(args, contextId),\n large: true,\n }).then((modal) => {\n modal.show();\n modal.getRoot().on('click', 'a[href].page-link', function(e) {\n e.preventDefault();\n let attr = e.target.getAttribute(\"href\");\n if (attr !== '#') {\n args.querystring = attr;\n modal.setBody(getFragment(args, contextId));\n }\n });\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question usage identifier.\n * @param {int} contextId the question context id.\n */\nexport const init = (questionSelector, contextId) => {\n let target = document.querySelector(questionSelector);\n let questionId = target.getAttribute('data-questionid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any usage count row.\n usageEvent(questionId, contextId);\n });\n};\n"],"names":["getFragment","args","contextId","Fragment","loadFragment","questionSelector","target","document","querySelector","questionId","getAttribute","addEventListener","questionid","create","type","ModalFactory","types","CANCEL","title","Str","get_string","body","large","then","modal","show","getRoot","on","e","preventDefault","attr","querystring","setBody","fail","Notification","exception","usageEvent"],"mappings":";;;;;;;;o4BAqCMA,YAAc,CAACC,KAAMC,YAChBC,kBAASC,aAAa,cAAe,iBAAkBF,UAAWD,oBAwCzD,CAACI,iBAAkBH,iBAC/BI,OAASC,SAASC,cAAcH,kBAChCI,WAAaH,OAAOI,aAAa,mBACrCJ,OAAOK,iBAAiB,SAAS,KAjClB,EAACF,WAAYP,iBACxBD,KAAO,CACPW,WAAYH,mCAEHI,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,OACzBC,MAAOC,IAAIC,WAAW,cAAe,eACrCC,KAAMrB,YAAYC,KAAMC,WACxBoB,OAAO,IACRC,MAAMC,QACLA,MAAMC,OACND,MAAME,UAAUC,GAAG,QAAS,qBAAqB,SAASC,GACtDA,EAAEC,qBACEC,KAAOF,EAAEtB,OAAOI,aAAa,QACpB,MAAToB,OACA7B,KAAK8B,YAAcD,KACnBN,MAAMQ,QAAQhC,YAAYC,KAAMC,gBAGjCsB,SACRS,KAAKC,sBAAaC,YAejBC,CAAW3B,WAAYP"} \ No newline at end of file +{"version":3,"file":"usage.min.js","sources":["../src/usage.js"],"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 * Usage column selector js.\n *\n * @module qbank_usage/usage\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\nimport * as Str from 'core/str';\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {int} questionId\n * @param {int} contextId\n */\nconst usageEvent = (questionId, contextId) => {\n let args = {\n questionid: questionId,\n uniqueidentifier: 'question_usage_version_dropdown'\n };\n ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: Str.get_string('usageheader', 'qbank_usage'),\n body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args),\n large: true,\n }).then((modal) => {\n modal.show();\n modal.getRoot().on('click', 'a[href].page-link', function(e) {\n e.preventDefault();\n let attr = e.target.getAttribute(\"href\");\n if (attr !== '#') {\n args.querystring = attr;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n }\n });\n // Version selection event.\n modal.getRoot().on('change', '#question_usage_version_dropdown', function(e) {\n args.questionid = e.target.value;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n });\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question usage identifier.\n * @param {int} contextId the question context id.\n */\nexport const init = (questionSelector, contextId) => {\n let target = document.querySelector(questionSelector);\n let questionId = target.getAttribute('data-questionid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any usage count row.\n usageEvent(questionId, contextId);\n });\n};\n"],"names":["questionSelector","contextId","target","document","querySelector","questionId","getAttribute","addEventListener","args","questionid","uniqueidentifier","create","type","ModalFactory","types","CANCEL","title","Str","get_string","body","Fragment","loadFragment","large","then","modal","show","getRoot","on","e","preventDefault","attr","querystring","setBody","value","fail","Notification","exception","usageEvent"],"mappings":";;;;;;;;44BAwEoB,CAACA,iBAAkBC,iBAC/BC,OAASC,SAASC,cAAcJ,kBAChCK,WAAaH,OAAOI,aAAa,mBACrCJ,OAAOK,iBAAiB,SAAS,KAvClB,EAACF,WAAYJ,iBACxBO,KAAO,CACPC,WAAYJ,WACZK,iBAAkB,0DAETC,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,OACzBC,MAAOC,IAAIC,WAAW,cAAe,eACrCC,KAAMC,kBAASC,aAAa,cAAe,iBAAkBpB,UAAWO,MACxEc,OAAO,IACRC,MAAMC,QACLA,MAAMC,OACND,MAAME,UAAUC,GAAG,QAAS,qBAAqB,SAASC,GACtDA,EAAEC,qBACEC,KAAOF,EAAE1B,OAAOI,aAAa,QACpB,MAATwB,OACAtB,KAAKuB,YAAcD,KACnBN,MAAMQ,QAAQZ,kBAASC,aAAa,cAAe,iBAAkBpB,UAAWO,WAIxFgB,MAAME,UAAUC,GAAG,SAAU,oCAAoC,SAASC,GACtEpB,KAAKC,WAAamB,EAAE1B,OAAO+B,MAC3BT,MAAMQ,QAAQZ,kBAASC,aAAa,cAAe,iBAAkBpB,UAAWO,UAE7EgB,SACRU,KAAKC,sBAAaC,YAejBC,CAAWhC,WAAYJ"} \ No newline at end of file diff --git a/question/bank/usage/amd/src/usage.js b/question/bank/usage/amd/src/usage.js index bdb72d6a977d0..4ec87d1d25291 100644 --- a/question/bank/usage/amd/src/usage.js +++ b/question/bank/usage/amd/src/usage.js @@ -23,21 +23,9 @@ */ import Fragment from 'core/fragment'; -import * as Str from 'core/str'; import ModalFactory from 'core/modal_factory'; import Notification from 'core/notification'; - -/** - * Get the fragment. - * - * @method getFragment - * @param {{questioned: int}} args - * @param {int} contextId - * @return {string} - */ -const getFragment = (args, contextId) => { - return Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args); -}; +import * as Str from 'core/str'; /** * Event listeners for the module. @@ -48,12 +36,13 @@ const getFragment = (args, contextId) => { */ const usageEvent = (questionId, contextId) => { let args = { - questionid: questionId + questionid: questionId, + uniqueidentifier: 'question_usage_version_dropdown' }; ModalFactory.create({ type: ModalFactory.types.CANCEL, title: Str.get_string('usageheader', 'qbank_usage'), - body: getFragment(args, contextId), + body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args), large: true, }).then((modal) => { modal.show(); @@ -62,9 +51,14 @@ const usageEvent = (questionId, contextId) => { let attr = e.target.getAttribute("href"); if (attr !== '#') { args.querystring = attr; - modal.setBody(getFragment(args, contextId)); + modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args)); } }); + // Version selection event. + modal.getRoot().on('change', '#question_usage_version_dropdown', function(e) { + args.questionid = e.target.value; + modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args)); + }); return modal; }).fail(Notification.exception); }; diff --git a/question/bank/usage/lib.php b/question/bank/usage/lib.php index 3bad4b065181b..d91a99663a5b0 100644 --- a/question/bank/usage/lib.php +++ b/question/bank/usage/lib.php @@ -32,6 +32,9 @@ * @return string rendered output */ function qbank_usage_output_fragment_question_usage(array $args): string { + if (!$args['questionid'] || !$args['uniqueidentifier']) { + return ''; + } global $USER, $PAGE, $CFG, $DB; require_once($CFG->dirroot . '/question/engine/bank.php'); $displaydata = []; @@ -59,6 +62,9 @@ function qbank_usage_output_fragment_question_usage(array $args): string { } } $displaydata['tablesql'] = $questionusagetable->export_for_fragment(); + $selector = \core_question\output\question_version_selection::make_for_question($args['uniqueidentifier'], $args['questionid']); + $qbankrenderer = $PAGE->get_renderer('core_question', 'bank'); + $displaydata['versionselection'] = $selector->export_for_template($qbankrenderer); return $PAGE->get_renderer('qbank_usage')->render_usage_fragment($displaydata); } diff --git a/question/bank/usage/templates/usage_modal.mustache b/question/bank/usage/templates/usage_modal.mustache index af11f9858a340..9e533e0d29957 100644 --- a/question/bank/usage/templates/usage_modal.mustache +++ b/question/bank/usage/templates/usage_modal.mustache @@ -26,7 +26,9 @@ ] } }} - +{{#versionselection}} + {{> core_question/question_version_selection }} +{{/versionselection}}
{{{question}}}
diff --git a/question/bank/usage/tests/behat/question_usage_column.feature b/question/bank/usage/tests/behat/question_usage_column.feature index ed0f73b18fe06..7394cb1a061ca 100644 --- a/question/bank/usage/tests/behat/question_usage_column.feature +++ b/question/bank/usage/tests/behat/question_usage_column.feature @@ -39,6 +39,6 @@ Feature: Use the qbank plugin manager page for question usage And I should see "Test questions" And I should see "0" on the usage column When I click "0" on the usage column - Then I should see "Question usage" + Then I should see "Version 1" And I click on "Close" "button" in the ".modal-dialog" "css_element" And I should see "0" on the usage column diff --git a/question/classes/output/question_version_selection.php b/question/classes/output/question_version_selection.php new file mode 100644 index 0000000000000..a7a5558af73e8 --- /dev/null +++ b/question/classes/output/question_version_selection.php @@ -0,0 +1,91 @@ +. + +namespace core_question\output; + +use renderer_base; +use templatable; +use renderable; +use question_bank; + +require_once($CFG->dirroot . '/question/engine/bank.php'); + +/** + * A UI widget to select other versions of a particular question. + * + * It will help plugins to enable version selection in locations like modal, page etc. + * + * @package core_question + * @copyright 2022 Catalyst IT Australia Pty Ltd + * @author Safat Shahin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class question_version_selection implements templatable, renderable { + + /** @var string */ + private $uniqueidentifier; + + /** @var int */ + private $currentselectedquestionid = null; + + /** + * Constructor. + * + * @param string $uniqueidentifier unique identifier for the api usage. + * @param int $currentlyselectedquestionid selected question id in dropdown. + */ + protected function __construct(string $uniqueidentifier, int $currentlyselectedquestionid) { + $this->uniqueidentifier = $uniqueidentifier; + $this->currentselectedquestionid = $currentlyselectedquestionid; + } + + /** + * Set the selected question id for the currently selected question. + * + * @param string $uniqueidentifier unique identifier for the api usage. + * @param int $currentlyselectedquestionid selected question id in dropdown. + * @return self an instance of this UI widget for the given question. + */ + public static function make_for_question(string $uniqueidentifier, int $currentlyselectedquestionid): self { + return new self($uniqueidentifier, $currentlyselectedquestionid); + } + + /** + * Export the data for version selection mustache. + * + * @param renderer_base $output renderer of the output + * @return array + */ + public function export_for_template(renderer_base $output): array { + $displaydata = []; + $versionsoptions = question_bank::get_all_versions_of_question($this->currentselectedquestionid); + foreach ($versionsoptions as $versionsoption) { + $versionsoption->selected = false; + $a = new \stdClass(); + $a->version = $versionsoption->version; + $versionsoption->name = get_string('version_selection', 'core_question', $a); + if ($versionsoption->questionid == $this->currentselectedquestionid) { + $versionsoption->selected = true; + } + $displaydata[] = $versionsoption; + } + + return [ + 'options' => $displaydata, + 'uniqueidentifier' => $this->uniqueidentifier, + ]; + } +} diff --git a/question/engine/bank.php b/question/engine/bank.php index d89a81665f011..866d7d152806c 100644 --- a/question/engine/bank.php +++ b/question/engine/bank.php @@ -288,6 +288,27 @@ public static function make_question($questiondata) { return self::get_qtype($questiondata->qtype, false)->make_question($questiondata, false); } + /** + * Get all the versions of a particular question. + * + * @param int $questionid id of the question + * @return array The array keys are version number, and the values are objects with three int fields + * version (same as array key), versionid and questionid. + */ + public static function get_all_versions_of_question(int $questionid): array { + global $DB; + $sql = "SELECT qv.id AS versionid, qv.version, qv.questionid + FROM {question_versions} qv + WHERE qv.questionbankentryid = (SELECT DISTINCT qbe.id + FROM {question_bank_entries} qbe + JOIN {question_versions} qv ON qbe.id = qv.questionbankentryid + JOIN {question} q ON qv.questionid = q.id + WHERE q.id = ?) + ORDER BY qv.version DESC"; + + return $DB->get_records_sql($sql, [$questionid]); + } + /** * @return question_finder a question finder. */ diff --git a/question/templates/question_version_selection.mustache b/question/templates/question_version_selection.mustache new file mode 100644 index 0000000000000..fb12edb8bace8 --- /dev/null +++ b/question/templates/question_version_selection.mustache @@ -0,0 +1,41 @@ +{{! + 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 . +}} +{{! + @template core_question/question_version_selection + + The version selection for question bank. + * options - Versions of the question + * uniqueidentifier - identifier to target the change from js + + Example context (json): + { + "options": [{ + "name": "Version 1", + "selected": true, + "value": "10" + }, + { + "name": "Version 2", + "selected": false, + "value": "20" + }], + "uniqueidentifier": "question_comment_version_selection" + } +}} + + diff --git a/question/tests/version_test.php b/question/tests/version_test.php index 19108ba0d107b..43c6c2e2ed8b5 100644 --- a/question/tests/version_test.php +++ b/question/tests/version_test.php @@ -254,4 +254,33 @@ public function test_id_number_in_bank_entry() { $this->assertEquals($questionbankentry[$questionid2]->idnumber, 'id3'); $this->assertEquals($questionbankentry[$questionid3]->idnumber, 'id3'); } + + /** + * Test that all the versions are available from the method. + * + * @covers ::get_all_versions_of_question + */ + public function test_get_all_versions_of_question() { + $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); + $question = $this->qgenerator->create_question('shortanswer', null, + [ + 'category' => $qcategory->id, + 'idnumber' => 'id1' + ]); + $questionid1 = $question->id; + + // Create a new version. + $this->qgenerator->update_question($question, null, ['idnumber' => 'id2']); + $questionid2 = $question->id; + // Change the id number and get the question object. + $this->qgenerator->update_question($question, null, ['idnumber' => 'id3']); + $questionid3 = $question->id; + + $questiondefinition = question_bank::get_all_versions_of_question($question->id); + + // Test the versions are available. + $this->assertEquals(array_slice($questiondefinition, 0, 1)[0]->questionid, $questionid3); + $this->assertEquals(array_slice($questiondefinition, 1, 1)[0]->questionid, $questionid2); + $this->assertEquals(array_slice($questiondefinition, 2, 1)[0]->questionid, $questionid1); + } }