From 09f9f41c430fcd8e649b3bd1f189d758dec9609e Mon Sep 17 00:00:00 2001 From: Peter Mayer Date: Tue, 2 Apr 2024 01:29:52 +0200 Subject: [PATCH] Add mootimetertool_ranking --- amd/build/handle_button_clicked.min.js | 2 +- amd/build/handle_button_clicked.min.js.map | 2 +- .../handle_clicked_element_use_dataset.min.js | 3 + ...dle_clicked_element_use_dataset.min.js.map | 1 + amd/src/handle_button_clicked.js | 70 +- amd/src/handle_clicked_element_use_dataset.js | 84 ++ classes/external/add_new_page.php | 7 +- classes/external/store_setting.php | 4 +- classes/helper.php | 64 +- classes/toolhelper.php | 8 + db/caches.php | 13 + scss/content.scss | 3 + styles.css | 3 + .../elements/snippet_clickable_icon.mustache | 9 + tests/helper_test.php | 14 +- tools/quiz/classes/quiz.php | 4 +- tools/ranking/amd/build/approve_phrase.min.js | 3 + .../amd/build/approve_phrase.min.js.map | 1 + tools/ranking/amd/build/store_phrase.min.js | 3 + .../ranking/amd/build/store_phrase.min.js.map | 1 + tools/ranking/amd/src/approve_phrase.js | 89 ++ tools/ranking/amd/src/store_phrase.js | 95 ++ tools/ranking/amd/src/thumbvote.js | 134 +++ .../classes/external/approve_phrase.php | 107 +++ .../ranking/classes/external/store_phrase.php | 126 +++ .../classes/external/vote_phrase_thumb.php | 96 ++ .../classes/local/inplace_edit_answer.php | 99 +++ tools/ranking/classes/ranking.php | 819 ++++++++++++++++++ tools/ranking/db/install.xml | 38 + tools/ranking/db/services.php | 53 ++ tools/ranking/db/upgrade.php | 98 +++ tools/ranking/db/upgradelib.php | 93 ++ .../lang/de/mootimetertool_ranking.php | 38 + .../lang/en/mootimetertool_ranking.php | 43 + tools/ranking/pix/ranking-white.svg | 66 ++ tools/ranking/pix/ranking.svg | 65 ++ tools/ranking/styles.css | 130 +++ .../templates/snippet_phase_selector.mustache | 31 + .../templates/snippet_toggle_yes_no.mustache | 12 + .../ranking/templates/view_overview.mustache | 34 + tools/ranking/templates/view_phase_1.mustache | 51 ++ tools/ranking/templates/view_phase_2.mustache | 65 ++ .../templates/view_phase_3_thumbs.mustache | 62 ++ tools/ranking/templates/view_results.mustache | 15 + .../ranking/templates/view_settings.mustache | 51 ++ tools/ranking/version.php | 30 + version.php | 2 +- 47 files changed, 2771 insertions(+), 70 deletions(-) create mode 100644 amd/build/handle_clicked_element_use_dataset.min.js create mode 100644 amd/build/handle_clicked_element_use_dataset.min.js.map create mode 100644 amd/src/handle_clicked_element_use_dataset.js create mode 100644 templates/elements/snippet_clickable_icon.mustache create mode 100644 tools/ranking/amd/build/approve_phrase.min.js create mode 100644 tools/ranking/amd/build/approve_phrase.min.js.map create mode 100644 tools/ranking/amd/build/store_phrase.min.js create mode 100644 tools/ranking/amd/build/store_phrase.min.js.map create mode 100644 tools/ranking/amd/src/approve_phrase.js create mode 100644 tools/ranking/amd/src/store_phrase.js create mode 100644 tools/ranking/amd/src/thumbvote.js create mode 100644 tools/ranking/classes/external/approve_phrase.php create mode 100644 tools/ranking/classes/external/store_phrase.php create mode 100644 tools/ranking/classes/external/vote_phrase_thumb.php create mode 100644 tools/ranking/classes/local/inplace_edit_answer.php create mode 100644 tools/ranking/classes/ranking.php create mode 100644 tools/ranking/db/install.xml create mode 100644 tools/ranking/db/services.php create mode 100755 tools/ranking/db/upgrade.php create mode 100644 tools/ranking/db/upgradelib.php create mode 100644 tools/ranking/lang/de/mootimetertool_ranking.php create mode 100644 tools/ranking/lang/en/mootimetertool_ranking.php create mode 100644 tools/ranking/pix/ranking-white.svg create mode 100644 tools/ranking/pix/ranking.svg create mode 100644 tools/ranking/styles.css create mode 100644 tools/ranking/templates/snippet_phase_selector.mustache create mode 100644 tools/ranking/templates/snippet_toggle_yes_no.mustache create mode 100644 tools/ranking/templates/view_overview.mustache create mode 100644 tools/ranking/templates/view_phase_1.mustache create mode 100644 tools/ranking/templates/view_phase_2.mustache create mode 100644 tools/ranking/templates/view_phase_3_thumbs.mustache create mode 100644 tools/ranking/templates/view_results.mustache create mode 100644 tools/ranking/templates/view_settings.mustache create mode 100644 tools/ranking/version.php diff --git a/amd/build/handle_button_clicked.min.js b/amd/build/handle_button_clicked.min.js index 402dd306..3c1cdae9 100644 --- a/amd/build/handle_button_clicked.min.js +++ b/amd/build/handle_button_clicked.min.js @@ -1,3 +1,3 @@ -define("mod_mootimeter/handle_button_clicked",["exports","core/ajax","core/log","core/modal_save_cancel","core/modal_delete_cancel","core/modal_events","core/str","mod_mootimeter/reload_page"],(function(_exports,_ajax,_log,_modal_save_cancel,_modal_delete_cancel,_modal_events,_str,_reload_page){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_log=_interopRequireDefault(_log),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events);_exports.init=async uniqueID=>{var obj=document.getElementById(uniqueID);if(!document.getElementById(uniqueID))return;obj.addEventListener("click",(function(){modal.show()}));const pageid=document.getElementById("mootimeterstate").dataset.pageid;var confirmationTitleStr,confirmationQuestionStr,modal,dataset=obj.dataset;if(confirmationTitleStr=obj.getAttribute("data-confirmationtitlestr")?dataset.confirmationtitlestr:(0,_str.get_string)("delete","core"),confirmationQuestionStr=obj.getAttribute("data-confirmationquestionstr")?dataset.confirmationquestionstr:(0,_str.get_string)("areyousure"),obj.getAttribute("data-confirmationtype")){switch(dataset.confirmationtype){case"DELETE_CANCEL":modal=await _modal_delete_cancel.default.create({title:confirmationTitleStr,body:confirmationQuestionStr,pageid:pageid});break;case"SAVE_CANCEL":modal=await _modal_save_cancel.default.create({title:confirmationTitleStr,body:confirmationQuestionStr,pageid:pageid})}modal.getRoot().on(_modal_events.default.delete,(function(){var uniqueID=obj.id,ajaxmethode=obj.dataset.ajaxmethode;buttonClickedHandle(pageid,uniqueID,ajaxmethode)})),modal.getRoot().on(_modal_events.default.save,(function(){var uniqueID=obj.id,ajaxmethode=obj.dataset.ajaxmethode;buttonClickedHandle(pageid,uniqueID,ajaxmethode)}))}else window.console.log("No confirmationtype specified! Abort!")};const buttonClickedHandle=async(pageid,uniqueID,ajaxmethode)=>{var dataset=JSON.stringify(document.getElementById(uniqueID).dataset);const response=await((pageid,thisDataset,ajaxmethode)=>(0,_ajax.call)([{methodname:ajaxmethode,args:{pageid:pageid,thisDataset:thisDataset}}])[0])(pageid,dataset,ajaxmethode);if(200!=response.code&&_log.default.error(response.string),1==response.reload){const queryString=window.location.search,urlParams=new URLSearchParams(queryString);(0,_reload_page.execReloadPage)(urlParams.get("pageid"),urlParams.get("id"))}}})); +define("mod_mootimeter/handle_button_clicked",["exports","core/ajax","core/log","core/modal_save_cancel","core/modal_delete_cancel","core/modal_events","core/str","mod_mootimeter/reload_page"],(function(_exports,_ajax,_log,_modal_save_cancel,_modal_delete_cancel,_modal_events,_str,_reload_page){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_log=_interopRequireDefault(_log),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events);_exports.init=async uniqueID=>{var obj=document.getElementById(uniqueID);if(!document.getElementById(uniqueID))return;const pageid=document.getElementById("mootimeterstate").dataset.pageid;var confirmationTitleStr,dataset=obj.dataset;if(obj.getAttribute("data-confirmationtype")){var confirmationQuestionStr,modal;switch(obj.addEventListener("click",(function(){modal.show()})),confirmationTitleStr=obj.getAttribute("data-confirmationtitlestr")?dataset.confirmationtitlestr:(0,_str.get_string)("delete","core"),confirmationQuestionStr=obj.getAttribute("data-confirmationquestionstr")?dataset.confirmationquestionstr:(0,_str.get_string)("areyousure"),dataset.confirmationtype){case"DELETE_CANCEL":modal=await _modal_delete_cancel.default.create({title:confirmationTitleStr,body:confirmationQuestionStr,pageid:pageid});break;case"SAVE_CANCEL":modal=await _modal_save_cancel.default.create({title:confirmationTitleStr,body:confirmationQuestionStr,pageid:pageid})}modal.getRoot().on(_modal_events.default.delete,(function(){var uniqueID=obj.id,ajaxmethode=obj.dataset.ajaxmethode;buttonClickedHandle(pageid,uniqueID,ajaxmethode)})),modal.getRoot().on(_modal_events.default.save,(function(){var uniqueID=obj.id,ajaxmethode=obj.dataset.ajaxmethode;buttonClickedHandle(pageid,uniqueID,ajaxmethode)}))}else obj.addEventListener("click",(function(){const pageid=document.getElementById("mootimeterstate").dataset.pageid;var uniqueID=obj.id,ajaxmethode=obj.dataset.ajaxmethode;buttonClickedHandle(pageid,uniqueID,ajaxmethode)}))};const buttonClickedHandle=async(pageid,uniqueID,ajaxmethode)=>{var dataset=JSON.stringify(document.getElementById(uniqueID).dataset);const response=await((pageid,thisDataset,ajaxmethode)=>(0,_ajax.call)([{methodname:ajaxmethode,args:{pageid:pageid,thisDataset:thisDataset}}])[0])(pageid,dataset,ajaxmethode);if(200!=response.code&&_log.default.error(response.string),1==response.reload){const queryString=window.location.search,urlParams=new URLSearchParams(queryString);(0,_reload_page.execReloadPage)(urlParams.get("pageid"),urlParams.get("id"))}}})); //# sourceMappingURL=handle_button_clicked.min.js.map \ No newline at end of file diff --git a/amd/build/handle_button_clicked.min.js.map b/amd/build/handle_button_clicked.min.js.map index b4df6dff..ba61f8a4 100644 --- a/amd/build/handle_button_clicked.min.js.map +++ b/amd/build/handle_button_clicked.min.js.map @@ -1 +1 @@ -{"version":3,"file":"handle_button_clicked.min.js","sources":["../src/handle_button_clicked.js"],"sourcesContent":["import {call as fetchMany} from 'core/ajax';\nimport Log from 'core/log';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\nimport {get_string as getString} from 'core/str';\nimport {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page';\n\nexport const init = async(uniqueID) => {\n var obj = document.getElementById(uniqueID);\n\n if (!document.getElementById(uniqueID)) {\n return;\n }\n\n obj.addEventListener(\"click\", buttonClicked);\n const pageid = document.getElementById('mootimeterstate').dataset.pageid;\n var dataset = obj.dataset;\n var confirmationTitleStr;\n if (!obj.getAttribute(\"data-confirmationtitlestr\")) {\n confirmationTitleStr = getString('delete', 'core');\n } else {\n confirmationTitleStr = dataset.confirmationtitlestr;\n }\n\n var confirmationQuestionStr;\n if (!obj.getAttribute(\"data-confirmationquestionstr\")) {\n confirmationQuestionStr = getString('areyousure');\n } else {\n confirmationQuestionStr = dataset.confirmationquestionstr;\n }\n\n var modal;\n if (!obj.getAttribute(\"data-confirmationtype\")) {\n window.console.log('No confirmationtype specified! Abort!');\n return;\n } else {\n switch (dataset.confirmationtype) {\n case 'DELETE_CANCEL':\n modal = await ModalDeleteCancel.create({\n title: confirmationTitleStr,\n body: confirmationQuestionStr,\n pageid: pageid,\n });\n break;\n case 'SAVE_CANCEL':\n modal = await ModalSaveCancel.create({\n title: confirmationTitleStr,\n body: confirmationQuestionStr,\n pageid: pageid,\n });\n break;\n }\n }\n\n modal.getRoot().on(ModalEvents.delete, function() {\n var uniqueID = obj.id;\n var ajaxmethode = obj.dataset.ajaxmethode;\n\n buttonClickedHandle(pageid, uniqueID, ajaxmethode);\n });\n\n modal.getRoot().on(ModalEvents.save, function() {\n var uniqueID = obj.id;\n var ajaxmethode = obj.dataset.ajaxmethode;\n\n buttonClickedHandle(pageid, uniqueID, ajaxmethode);\n });\n\n /**\n * Store the value.\n */\n function buttonClicked() {\n modal.show();\n }\n};\n\n/**\n * Call to store input value\n * @param {int} pageid\n * @param {string} thisDataset\n * @param {string} ajaxmethode\n * @returns {mixed}\n */\nconst execButtonClicked = (\n pageid,\n thisDataset,\n ajaxmethode\n) => fetchMany([{\n methodname: ajaxmethode,\n args: {\n pageid,\n thisDataset\n },\n}])[0];\n\n/**\n * Executes the call to store input value.\n * @param {int} pageid\n * @param {string} uniqueID\n * @param {string} ajaxmethode\n */\nconst buttonClickedHandle = async(pageid, uniqueID, ajaxmethode) => {\n var dataset = JSON.stringify(document.getElementById(uniqueID).dataset);\n const response = await execButtonClicked(pageid, dataset, ajaxmethode);\n\n if (response.code != 200) {\n Log.error(response.string);\n }\n\n if (response.reload == true) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n reloadPage(urlParams.get('pageid'), urlParams.get('id'));\n }\n};\n"],"names":["async","obj","document","getElementById","uniqueID","addEventListener","modal","show","pageid","dataset","confirmationTitleStr","confirmationQuestionStr","getAttribute","confirmationtitlestr","confirmationquestionstr","confirmationtype","ModalDeleteCancel","create","title","body","ModalSaveCancel","getRoot","on","ModalEvents","delete","id","ajaxmethode","buttonClickedHandle","save","window","console","log","JSON","stringify","response","thisDataset","methodname","args","execButtonClicked","code","error","string","reload","queryString","location","search","urlParams","URLSearchParams","get"],"mappings":"2qBAQoBA,MAAAA,eACZC,IAAMC,SAASC,eAAeC,cAE7BF,SAASC,eAAeC,iBAI7BH,IAAII,iBAAiB,oBA0DjBC,MAAMC,gBAzDJC,OAASN,SAASC,eAAe,mBAAmBM,QAAQD,WAE9DE,qBAOAC,wBAOAL,MAfAG,QAAUR,IAAIQ,WAKdC,qBAHCT,IAAIW,aAAa,6BAGKH,QAAQI,sBAFR,mBAAU,SAAU,QAS3CF,wBAHCV,IAAIW,aAAa,gCAGQH,QAAQK,yBAFR,mBAAU,cAMnCb,IAAIW,aAAa,iCAIVH,QAAQM,sBACP,gBACDT,YAAcU,6BAAkBC,OAAO,CACnCC,MAAOR,qBACPS,KAAMR,wBACNH,OAAQA,mBAGX,cACDF,YAAcc,2BAAgBH,OAAO,CACjCC,MAAOR,qBACPS,KAAMR,wBACNH,OAAQA,SAMxBF,MAAMe,UAAUC,GAAGC,sBAAYC,QAAQ,eAC/BpB,SAAWH,IAAIwB,GACfC,YAAczB,IAAIQ,QAAQiB,YAE9BC,oBAAoBnB,OAAQJ,SAAUsB,gBAG1CpB,MAAMe,UAAUC,GAAGC,sBAAYK,MAAM,eAC7BxB,SAAWH,IAAIwB,GACfC,YAAczB,IAAIQ,QAAQiB,YAE9BC,oBAAoBnB,OAAQJ,SAAUsB,qBAhCtCG,OAAOC,QAAQC,IAAI,gDAoErBJ,oBAAsB3B,MAAMQ,OAAQJ,SAAUsB,mBAC5CjB,QAAUuB,KAAKC,UAAU/B,SAASC,eAAeC,UAAUK,eACzDyB,cApBgB,EACtB1B,OACA2B,YACAT,eACC,cAAU,CAAC,CACZU,WAAYV,YACZW,KAAM,CACF7B,OAAAA,OACA2B,YAAAA,gBAEJ,GAUuBG,CAAkB9B,OAAQC,QAASiB,gBAErC,KAAjBQ,SAASK,mBACLC,MAAMN,SAASO,QAGA,GAAnBP,SAASQ,OAAgB,OACnBC,YAAcd,OAAOe,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBJ,6CAC3BG,UAAUE,IAAI,UAAWF,UAAUE,IAAI"} \ No newline at end of file +{"version":3,"file":"handle_button_clicked.min.js","sources":["../src/handle_button_clicked.js"],"sourcesContent":["import {call as fetchMany} from 'core/ajax';\nimport Log from 'core/log';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\nimport {get_string as getString} from 'core/str';\nimport {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page';\n\nexport const init = async (uniqueID) => {\n var obj = document.getElementById(uniqueID);\n\n if (!document.getElementById(uniqueID)) {\n return;\n }\n\n const pageid = document.getElementById('mootimeterstate').dataset.pageid;\n var dataset = obj.dataset;\n var confirmationTitleStr;\n\n if (obj.getAttribute(\"data-confirmationtype\")) {\n\n obj.addEventListener(\"click\", showModal);\n\n if (!obj.getAttribute(\"data-confirmationtitlestr\")) {\n confirmationTitleStr = getString('delete', 'core');\n } else {\n confirmationTitleStr = dataset.confirmationtitlestr;\n }\n\n var confirmationQuestionStr;\n if (!obj.getAttribute(\"data-confirmationquestionstr\")) {\n confirmationQuestionStr = getString('areyousure');\n } else {\n confirmationQuestionStr = dataset.confirmationquestionstr;\n }\n\n var modal;\n\n switch (dataset.confirmationtype) {\n case 'DELETE_CANCEL':\n modal = await ModalDeleteCancel.create({\n title: confirmationTitleStr,\n body: confirmationQuestionStr,\n pageid: pageid,\n });\n break;\n case 'SAVE_CANCEL':\n modal = await ModalSaveCancel.create({\n title: confirmationTitleStr,\n body: confirmationQuestionStr,\n pageid: pageid,\n });\n break;\n }\n\n modal.getRoot().on(ModalEvents.delete, function () {\n var uniqueID = obj.id;\n var ajaxmethode = obj.dataset.ajaxmethode;\n\n buttonClickedHandle(pageid, uniqueID, ajaxmethode);\n });\n\n modal.getRoot().on(ModalEvents.save, function () {\n var uniqueID = obj.id;\n var ajaxmethode = obj.dataset.ajaxmethode;\n\n buttonClickedHandle(pageid, uniqueID, ajaxmethode);\n });\n\n } else {\n obj.addEventListener(\"click\", buttonClicked);\n }\n\n /**\n * Handle Button clicked event.\n */\n function buttonClicked() {\n const pageid = document.getElementById('mootimeterstate').dataset.pageid;\n var uniqueID = obj.id;\n var ajaxmethode = obj.dataset.ajaxmethode;\n buttonClickedHandle(pageid, uniqueID, ajaxmethode);\n }\n\n /**\n * Store the value.\n */\n function showModal() {\n modal.show();\n }\n};\n\n/**\n * Call to store input value\n * @param {int} pageid\n * @param {string} thisDataset\n * @param {string} ajaxmethode\n * @returns {mixed}\n */\nconst execButtonClicked = (\n pageid,\n thisDataset,\n ajaxmethode\n) => fetchMany([{\n methodname: ajaxmethode,\n args: {\n pageid,\n thisDataset\n },\n}])[0];\n\n/**\n * Executes the call to store input value.\n * @param {int} pageid\n * @param {string} uniqueID\n * @param {string} ajaxmethode\n */\nconst buttonClickedHandle = async(pageid, uniqueID, ajaxmethode) => {\n var dataset = JSON.stringify(document.getElementById(uniqueID).dataset);\n const response = await execButtonClicked(pageid, dataset, ajaxmethode);\n\n if (response.code != 200) {\n Log.error(response.string);\n }\n\n if (response.reload == true) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n reloadPage(urlParams.get('pageid'), urlParams.get('id'));\n }\n};\n"],"names":["async","obj","document","getElementById","uniqueID","pageid","dataset","confirmationTitleStr","getAttribute","confirmationQuestionStr","modal","addEventListener","show","confirmationtitlestr","confirmationquestionstr","confirmationtype","ModalDeleteCancel","create","title","body","ModalSaveCancel","getRoot","on","ModalEvents","delete","id","ajaxmethode","buttonClickedHandle","save","JSON","stringify","response","thisDataset","methodname","args","execButtonClicked","code","error","string","reload","queryString","window","location","search","urlParams","URLSearchParams","get"],"mappings":"2qBAQoBA,MAAAA,eACZC,IAAMC,SAASC,eAAeC,cAE7BF,SAASC,eAAeC,uBAIvBC,OAASH,SAASC,eAAe,mBAAmBG,QAAQD,WAE9DE,qBADAD,QAAUL,IAAIK,WAGdL,IAAIO,aAAa,yBAA0B,KAUvCC,wBAOAC,aAfJT,IAAIU,iBAAiB,oBAkErBD,MAAME,UA7DFL,qBAHCN,IAAIO,aAAa,6BAGKF,QAAQO,sBAFR,mBAAU,SAAU,QAS3CJ,wBAHCR,IAAIO,aAAa,gCAGQF,QAAQQ,yBAFR,mBAAU,cAOhCR,QAAQS,sBACP,gBACDL,YAAcM,6BAAkBC,OAAO,CACnCC,MAAOX,qBACPY,KAAMV,wBACNJ,OAAQA,mBAGX,cACDK,YAAcU,2BAAgBH,OAAO,CACjCC,MAAOX,qBACPY,KAAMV,wBACNJ,OAAQA,SAKpBK,MAAMW,UAAUC,GAAGC,sBAAYC,QAAQ,eAC/BpB,SAAWH,IAAIwB,GACfC,YAAczB,IAAIK,QAAQoB,YAE9BC,oBAAoBtB,OAAQD,SAAUsB,gBAG1ChB,MAAMW,UAAUC,GAAGC,sBAAYK,MAAM,eAC7BxB,SAAWH,IAAIwB,GACfC,YAAczB,IAAIK,QAAQoB,YAE9BC,oBAAoBtB,OAAQD,SAAUsB,qBAI1CzB,IAAIU,iBAAiB,0BAOfN,OAASH,SAASC,eAAe,mBAAmBG,QAAQD,WAC9DD,SAAWH,IAAIwB,GACfC,YAAczB,IAAIK,QAAQoB,YAC9BC,oBAAoBtB,OAAQD,SAAUsB,uBAoCxCC,oBAAsB3B,MAAMK,OAAQD,SAAUsB,mBAC5CpB,QAAUuB,KAAKC,UAAU5B,SAASC,eAAeC,UAAUE,eACzDyB,cApBgB,EACtB1B,OACA2B,YACAN,eACC,cAAU,CAAC,CACZO,WAAYP,YACZQ,KAAM,CACF7B,OAAAA,OACA2B,YAAAA,gBAEJ,GAUuBG,CAAkB9B,OAAQC,QAASoB,gBAErC,KAAjBK,SAASK,mBACLC,MAAMN,SAASO,QAGA,GAAnBP,SAASQ,OAAgB,OACnBC,YAAcC,OAAOC,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBL,6CAC3BI,UAAUE,IAAI,UAAWF,UAAUE,IAAI"} \ No newline at end of file diff --git a/amd/build/handle_clicked_element_use_dataset.min.js b/amd/build/handle_clicked_element_use_dataset.min.js new file mode 100644 index 00000000..f7e46809 --- /dev/null +++ b/amd/build/handle_clicked_element_use_dataset.min.js @@ -0,0 +1,3 @@ +define("mod_mootimeter/handle_clicked_element_use_dataset",["exports","core/ajax","core/log","mod_mootimeter/reload_page"],(function(_exports,_ajax,_log,_reload_page){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_log=(obj=_log)&&obj.__esModule?obj:{default:obj};_exports.init=uniqueID=>{const obj=document.getElementById(uniqueID);document.getElementById(uniqueID)&&obj.addEventListener("click",(function(){const pageid=document.getElementById("mootimeterstate").dataset.pageid,ajaxmethode=obj.dataset.ajaxmethode,inputname=obj.dataset.name,inputvalue=obj.dataset.value,thisDataset=JSON.stringify(obj.dataset);return execStoreDataValue(ajaxmethode,pageid,inputname,inputvalue,thisDataset)}))};const execStoreDataValue=async(ajaxmethode,pageid,inputname,inputvalue,thisDataset)=>{const response=await((ajaxmethode,pageid,inputname,inputvalue,thisDataset)=>(0,_ajax.call)([{methodname:ajaxmethode,args:{pageid:pageid,inputname:inputname,inputvalue:inputvalue,thisDataset:thisDataset}}])[0])(ajaxmethode,pageid,inputname,inputvalue,thisDataset);200!=response.code&&_log.default.error(response.string),200==response.code&&_log.default.info(response.string);let options=[];if(response.options&&(options=JSON.parse(response.options)),1==response.reload||1==options.reload){const queryString=window.location.search,urlParams=new URLSearchParams(queryString);(0,_reload_page.execReloadPage)(urlParams.get("pageid"),urlParams.get("id"))}}})); + +//# sourceMappingURL=handle_clicked_element_use_dataset.min.js.map \ No newline at end of file diff --git a/amd/build/handle_clicked_element_use_dataset.min.js.map b/amd/build/handle_clicked_element_use_dataset.min.js.map new file mode 100644 index 00000000..9f80455d --- /dev/null +++ b/amd/build/handle_clicked_element_use_dataset.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"handle_clicked_element_use_dataset.min.js","sources":["../src/handle_clicked_element_use_dataset.js"],"sourcesContent":["import {call as fetchMany} from 'core/ajax';\nimport Log from 'core/log';\nimport {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page';\n\nexport const init = (uniqueID) => {\n const obj = document.getElementById(uniqueID);\n\n if (!document.getElementById(uniqueID)) {\n return;\n }\n\n obj.addEventListener(\"click\", mootimeterStoreData);\n\n /**\n * Store the value.\n * @returns {mixed}\n */\n function mootimeterStoreData() {\n const mtmstate = document.getElementById('mootimeterstate').dataset;\n const pageid = mtmstate.pageid;\n const ajaxmethode = obj.dataset.ajaxmethode;\n const inputname = obj.dataset.name;\n const inputvalue = obj.dataset.value;\n const thisDataset = JSON.stringify(obj.dataset);\n return execStoreDataValue(ajaxmethode, pageid, inputname, inputvalue, thisDataset);\n }\n};\n\n/**\n * Call to store input value\n * @param {string} ajaxmethode\n * @param {int} pageid\n * @param {string} inputname\n * @param {string} inputvalue\n * @param {string} thisDataset\n * @returns {mixed}\n */\nconst storeDataValue = (\n ajaxmethode,\n pageid,\n inputname,\n inputvalue,\n thisDataset\n) => fetchMany([{\n methodname: ajaxmethode,\n args: {\n pageid,\n inputname,\n inputvalue,\n thisDataset\n },\n}])[0];\n\n/**\n * Executes the call to store input value.\n * @param {string} ajaxmethode\n * @param {int} pageid\n * @param {string} inputname\n * @param {string} inputvalue\n * @param {string} thisDataset\n */\nconst execStoreDataValue = async (ajaxmethode, pageid, inputname, inputvalue, thisDataset) => {\n\n const response = await storeDataValue(ajaxmethode, pageid, inputname, inputvalue, thisDataset);\n\n if (response.code != 200) {\n Log.error(response.string);\n }\n\n if (response.code == 200) {\n Log.info(response.string);\n }\n\n let options = [];\n if (response.options) {\n options = JSON.parse(response.options);\n }\n\n if (response.reload == true || options.reload == true) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n reloadPage(urlParams.get('pageid'), urlParams.get('id'));\n }\n};\n"],"names":["uniqueID","obj","document","getElementById","addEventListener","pageid","dataset","ajaxmethode","inputname","name","inputvalue","value","thisDataset","JSON","stringify","execStoreDataValue","async","response","methodname","args","storeDataValue","code","error","string","info","options","parse","reload","queryString","window","location","search","urlParams","URLSearchParams","get"],"mappings":"4TAIqBA,iBACXC,IAAMC,SAASC,eAAeH,UAE/BE,SAASC,eAAeH,WAI7BC,IAAIG,iBAAiB,0BAQXC,OADWH,SAASC,eAAe,mBAAmBG,QACpCD,OAClBE,YAAcN,IAAIK,QAAQC,YAC1BC,UAAYP,IAAIK,QAAQG,KACxBC,WAAaT,IAAIK,QAAQK,MACzBC,YAAcC,KAAKC,UAAUb,IAAIK,gBAChCS,mBAAmBR,YAAaF,OAAQG,UAAWE,WAAYE,uBAqCxEG,mBAAqBC,MAAOT,YAAaF,OAAQG,UAAWE,WAAYE,qBAEpEK,cA1Ba,EACnBV,YACAF,OACAG,UACAE,WACAE,eACC,cAAU,CAAC,CACZM,WAAYX,YACZY,KAAM,CACFd,OAAAA,OACAG,UAAAA,UACAE,WAAAA,WACAE,YAAAA,gBAEJ,GAYuBQ,CAAeb,YAAaF,OAAQG,UAAWE,WAAYE,aAE7D,KAAjBK,SAASI,mBACLC,MAAML,SAASM,QAGF,KAAjBN,SAASI,mBACLG,KAAKP,SAASM,YAGlBE,QAAU,MACVR,SAASQ,UACTA,QAAUZ,KAAKa,MAAMT,SAASQ,UAGX,GAAnBR,SAASU,QAAoC,GAAlBF,QAAQE,OAAgB,OAC7CC,YAAcC,OAAOC,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBL,6CAC3BI,UAAUE,IAAI,UAAWF,UAAUE,IAAI"} \ No newline at end of file diff --git a/amd/src/handle_button_clicked.js b/amd/src/handle_button_clicked.js index ca58f2eb..4e1b54dd 100644 --- a/amd/src/handle_button_clicked.js +++ b/amd/src/handle_button_clicked.js @@ -6,35 +6,36 @@ import ModalEvents from 'core/modal_events'; import {get_string as getString} from 'core/str'; import {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page'; -export const init = async(uniqueID) => { +export const init = async (uniqueID) => { var obj = document.getElementById(uniqueID); if (!document.getElementById(uniqueID)) { return; } - obj.addEventListener("click", buttonClicked); const pageid = document.getElementById('mootimeterstate').dataset.pageid; var dataset = obj.dataset; var confirmationTitleStr; - if (!obj.getAttribute("data-confirmationtitlestr")) { - confirmationTitleStr = getString('delete', 'core'); - } else { - confirmationTitleStr = dataset.confirmationtitlestr; - } - var confirmationQuestionStr; - if (!obj.getAttribute("data-confirmationquestionstr")) { - confirmationQuestionStr = getString('areyousure'); - } else { - confirmationQuestionStr = dataset.confirmationquestionstr; - } + if (obj.getAttribute("data-confirmationtype")) { + + obj.addEventListener("click", showModal); + + if (!obj.getAttribute("data-confirmationtitlestr")) { + confirmationTitleStr = getString('delete', 'core'); + } else { + confirmationTitleStr = dataset.confirmationtitlestr; + } + + var confirmationQuestionStr; + if (!obj.getAttribute("data-confirmationquestionstr")) { + confirmationQuestionStr = getString('areyousure'); + } else { + confirmationQuestionStr = dataset.confirmationquestionstr; + } + + var modal; - var modal; - if (!obj.getAttribute("data-confirmationtype")) { - window.console.log('No confirmationtype specified! Abort!'); - return; - } else { switch (dataset.confirmationtype) { case 'DELETE_CANCEL': modal = await ModalDeleteCancel.create({ @@ -51,26 +52,39 @@ export const init = async(uniqueID) => { }); break; } - } - modal.getRoot().on(ModalEvents.delete, function() { - var uniqueID = obj.id; - var ajaxmethode = obj.dataset.ajaxmethode; + modal.getRoot().on(ModalEvents.delete, function () { + var uniqueID = obj.id; + var ajaxmethode = obj.dataset.ajaxmethode; - buttonClickedHandle(pageid, uniqueID, ajaxmethode); - }); + buttonClickedHandle(pageid, uniqueID, ajaxmethode); + }); + + modal.getRoot().on(ModalEvents.save, function () { + var uniqueID = obj.id; + var ajaxmethode = obj.dataset.ajaxmethode; + + buttonClickedHandle(pageid, uniqueID, ajaxmethode); + }); + + } else { + obj.addEventListener("click", buttonClicked); + } - modal.getRoot().on(ModalEvents.save, function() { + /** + * Handle Button clicked event. + */ + function buttonClicked() { + const pageid = document.getElementById('mootimeterstate').dataset.pageid; var uniqueID = obj.id; var ajaxmethode = obj.dataset.ajaxmethode; - buttonClickedHandle(pageid, uniqueID, ajaxmethode); - }); + } /** * Store the value. */ - function buttonClicked() { + function showModal() { modal.show(); } }; diff --git a/amd/src/handle_clicked_element_use_dataset.js b/amd/src/handle_clicked_element_use_dataset.js new file mode 100644 index 00000000..9977c37d --- /dev/null +++ b/amd/src/handle_clicked_element_use_dataset.js @@ -0,0 +1,84 @@ +import {call as fetchMany} from 'core/ajax'; +import Log from 'core/log'; +import {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page'; + +export const init = (uniqueID) => { + const obj = document.getElementById(uniqueID); + + if (!document.getElementById(uniqueID)) { + return; + } + + obj.addEventListener("click", mootimeterStoreData); + + /** + * Store the value. + * @returns {mixed} + */ + function mootimeterStoreData() { + const mtmstate = document.getElementById('mootimeterstate').dataset; + const pageid = mtmstate.pageid; + const ajaxmethode = obj.dataset.ajaxmethode; + const inputname = obj.dataset.name; + const inputvalue = obj.dataset.value; + const thisDataset = JSON.stringify(obj.dataset); + return execStoreDataValue(ajaxmethode, pageid, inputname, inputvalue, thisDataset); + } +}; + +/** + * Call to store input value + * @param {string} ajaxmethode + * @param {int} pageid + * @param {string} inputname + * @param {string} inputvalue + * @param {string} thisDataset + * @returns {mixed} + */ +const storeDataValue = ( + ajaxmethode, + pageid, + inputname, + inputvalue, + thisDataset +) => fetchMany([{ + methodname: ajaxmethode, + args: { + pageid, + inputname, + inputvalue, + thisDataset + }, +}])[0]; + +/** + * Executes the call to store input value. + * @param {string} ajaxmethode + * @param {int} pageid + * @param {string} inputname + * @param {string} inputvalue + * @param {string} thisDataset + */ +const execStoreDataValue = async (ajaxmethode, pageid, inputname, inputvalue, thisDataset) => { + + const response = await storeDataValue(ajaxmethode, pageid, inputname, inputvalue, thisDataset); + + if (response.code != 200) { + Log.error(response.string); + } + + if (response.code == 200) { + Log.info(response.string); + } + + let options = []; + if (response.options) { + options = JSON.parse(response.options); + } + + if (response.reload == true || options.reload == true) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + reloadPage(urlParams.get('pageid'), urlParams.get('id')); + } +}; diff --git a/classes/external/add_new_page.php b/classes/external/add_new_page.php index 1ba291ce..6df4292f 100644 --- a/classes/external/add_new_page.php +++ b/classes/external/add_new_page.php @@ -78,8 +78,11 @@ public static function execute(string $tool, int $instance): array { $record->tool = $tool; $record->instance = $instance; $record->title = ""; - - $pageid = $mtmhelper->store_page($record); + try { + $pageid = $mtmhelper->store_page($record); + } catch (\Exception $e) { + echo $e->getMessage(); + } $cm = \mod_mootimeter\helper::get_cm_by_instance($instance); $return = [ diff --git a/classes/external/store_setting.php b/classes/external/store_setting.php index f21d3353..bfc1d4e8 100644 --- a/classes/external/store_setting.php +++ b/classes/external/store_setting.php @@ -89,9 +89,11 @@ public static function execute(int $pageid, string $inputname, string $inputvalu try { + $dataset = json_decode($datasetjson); + $helper = new \mod_mootimeter\helper(); $helper->set_tool_config($pageid, $inputname, $inputvalue); - $return = ['code' => 200, 'string' => 'ok']; + $return = ['code' => 200, 'string' => 'ok', 'options' => json_encode(['reload' => !empty($dataset->reload)])]; } catch (\Exception $e) { diff --git a/classes/helper.php b/classes/helper.php index a2fea37c..4aa7c9a2 100644 --- a/classes/helper.php +++ b/classes/helper.php @@ -50,6 +50,8 @@ class helper { const ERRORCODE_TO_MANY_ANSWERS = 1001; /** @var int Webservice returning error code - Duplicate Answers */ const ERRORCODE_DUPLICATE_ANSWER = 1002; + /** @var int Webservice returning error code - Unspecified error occured */ + const ERRORCODE_ERROR_OCCURED = 999; /** @var int Page is visible */ const PAGE_VISIBLE = 1; @@ -1037,18 +1039,15 @@ public function toggle_state(object $page, string $statename): int { * * @param string $table * @param object|array $record - * @param bool $updateexisting + * @param array $updateexisting - Updatecondition - empty: not updating existing records. * @param string $answercolumn * @param bool $allowmultipleanswers * @return array - * @throws dml_exception - * @throws coding_exception - * @throws cache_exception */ public function store_answer( string $table, object|array $record, - bool $updateexisting = false, + array $updateexisting = [], string $answercolumn = 'answer', bool $allowmultipleanswers = false ): array { @@ -1076,9 +1075,8 @@ public function store_answer( if ($allowmultipleanswers) { - if ($updateexisting) { - $params = ['pageid' => $recordtemp->pageid, 'usermodified' => $USER->id]; - $DB->delete_records($table, $params); + if (!empty($updateexisting)) { + $DB->delete_records($table, $updateexisting); } foreach ($record as $dataobject) { @@ -1111,15 +1109,13 @@ public function store_answer( // Store the answer to db or update it. if ($updateexisting) { - $params = ['pageid' => $dataobject->pageid, 'usermodified' => $dataobject->usermodified]; - // This is necessary, because if one user changes the setting, there could be more than one answer already stored. // In this cases we want to delete all previous answers and start from scratch. - if ($DB->count_records($table, $params) > 1) { - $DB->delete_records($table, $params); + if ($DB->count_records($table, $updateexisting) > 1) { + $DB->delete_records($table, $updateexisting); } - $origrecord = $DB->get_record($table, $params); + $origrecord = $DB->get_record($table, $updateexisting); } if (!empty($origrecord)) { @@ -1241,11 +1237,18 @@ public function delete_answers_of_user(string $table, int $pageid, int $userid): * @param int $pageid * @param string $answercolumn * @param int $userid + * @param string $cacheidentifier * @return array * @throws dml_exception */ - public function get_user_answers(string $table, int $pageid, string $answercolumn = 'answer', int $userid = 0): array { - $pageanswers = $this->get_answers($table, $pageid, $answercolumn); + public function get_user_answers( + string $table, + int $pageid, + string $answercolumn = 'answer', + int $userid = 0, + string $cacheidentifier = 'answers' + ): array { + $pageanswers = $this->get_answers($table, $pageid, $answercolumn, $cacheidentifier); $useranswers = []; @@ -1263,15 +1266,15 @@ public function get_user_answers(string $table, int $pageid, string $answercolum * * @param string $table * @param int $pageid - * @param string $answercolumn + * @param string $answercolumn NOT uses yet. This is just because of simular arguments with get_answers_grouped. + * @param string $cacheidentifier * @return array * @throws dml_exception */ - public function get_answers(string $table, int $pageid, string $answercolumn = 'answer') { + public function get_answers(string $table, int $pageid, string $answercolumn = 'answer', string $cacheidentifier = 'answers') { global $DB; - - $cache = \cache::make('mod_mootimeter', 'answers'); - $cachekey = 'answers_' . $pageid; + $cache = \cache::make('mod_mootimeter', $cacheidentifier); + $cachekey = $cacheidentifier . '_' . $pageid; $records = json_decode($cache->get($cachekey)); if (empty($records)) { @@ -1288,15 +1291,21 @@ public function get_answers(string $table, int $pageid, string $answercolumn = ' * @param string $table * @param array $params * @param string $answercolumn + * @param string $cacheidentifier * @return array * @throws coding_exception * @throws dml_exception */ - public function get_answers_grouped(string $table, array $params, string $answercolumn = 'answer'): array { + public function get_answers_grouped( + string $table, + array $params, + string $answercolumn = 'answer', + string $cacheidentifier = 'answers' + ): array { global $DB; - $cache = \cache::make('mod_mootimeter', 'answers'); - $cachekey = 'cnt_' . $params['pageid']; + $cache = \cache::make('mod_mootimeter', $cacheidentifier); + $cachekey = 'cnt_' . $cacheidentifier . '_' . $params['pageid']; $records = json_decode($cache->get($cachekey), true); if (empty($records)) { @@ -1350,14 +1359,15 @@ public function get_page_last_update_time(int|object $pageorid, bool $ignoreansw * Clear all caches. * * @param int $pageid + * @param string $cacheidentifier * @return void * @throws coding_exception * @throws cache_exception */ - public function clear_caches(int $pageid): void { - $cache = \cache::make('mod_mootimeter', 'answers'); - $cache->delete('answers_' . $pageid); - $cache->delete('cnt_' . $pageid); + public function clear_caches(int $pageid, string $cacheidentifier = 'answers'): void { + $cache = \cache::make('mod_mootimeter', $cacheidentifier); + $cache->delete($cacheidentifier . '_' . $pageid); + $cache->delete('cnt_' . $cacheidentifier . '_' . $pageid); } /** diff --git a/classes/toolhelper.php b/classes/toolhelper.php index 26175b6c..c2994903 100644 --- a/classes/toolhelper.php +++ b/classes/toolhelper.php @@ -150,4 +150,12 @@ public function is_option_selected(int $optionid, object $config, string $attrib return false; } + /** + * Tool specific cache definitions used in mootimeter core methods + * @return array + */ + public function get_tool_cachedefinition() { + return []; + } + } diff --git a/db/caches.php b/db/caches.php index b864c4b9..4777dcc6 100644 --- a/db/caches.php +++ b/db/caches.php @@ -31,3 +31,16 @@ 'mode' => cache_store::MODE_APPLICATION, ], ]; + +$plugininfotools = new mod_mootimeter\plugininfo\mootimetertool; +$enabledtools = $plugininfotools->get_enabled_plugins(); + +foreach ($enabledtools as $tool => $toolname) { + // Hook to do further actions depending on mtmt tool. + $classname = "\mootimetertool_" . $tool . "\\" . $tool; + $toolhelper = new $classname(); + $tooldefinitions = $toolhelper->get_tool_cachedefinition(); + if (!empty($tooldefinitions)) { + $definitions = array_merge($definitions, $tooldefinitions); + } +} diff --git a/scss/content.scss b/scss/content.scss index 1a8c8f8f..cba4c858 100644 --- a/scss/content.scss +++ b/scss/content.scss @@ -63,6 +63,9 @@ width: 100%; height: 600px; justify-content: center; + overflow-y: scroll; + scrollbar-width: thin; + scrollbar-color: #60616D #f8f9fa; } @media screen and (max-width: 539px) { diff --git a/styles.css b/styles.css index c233abad..52386fbc 100644 --- a/styles.css +++ b/styles.css @@ -529,6 +529,9 @@ width: 100%; height: 600px; justify-content: center; + overflow-y: scroll; + scrollbar-width: thin; + scrollbar-color: #60616D #f8f9fa; } @media screen and (max-width: 539px) { .mootimetercontainer .mootimetercolcontent .mootimeter-colcontent-preview { diff --git a/templates/elements/snippet_clickable_icon.mustache b/templates/elements/snippet_clickable_icon.mustache new file mode 100644 index 00000000..58c543a6 --- /dev/null +++ b/templates/elements/snippet_clickable_icon.mustache @@ -0,0 +1,9 @@ + +{{#js}} + require(['mod_mootimeter/handle_button_clicked'], (module) => module.init("{{id}}_{{uniqid}}")); +{{/js}} diff --git a/tests/helper_test.php b/tests/helper_test.php index 5ca7b80c..4880e6a4 100644 --- a/tests/helper_test.php +++ b/tests/helper_test.php @@ -323,7 +323,7 @@ public function test_store_answer_exception_missing_pageid(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - false, + [], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') ); @@ -357,7 +357,7 @@ public function test_store_answer_exception_not_enroled_to_course(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - true, + ['pageid' => $page->id, 'usermodified' => $this->users['student']->id], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') ); @@ -393,7 +393,7 @@ public function test_store_answer_update_existing_and_multiple_answers(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - true, + ['pageid' => $page->id, 'usermodified' => $this->users['student']->id], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') // False. ); @@ -412,7 +412,7 @@ public function test_store_answer_update_existing_and_multiple_answers(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - true, + ['pageid' => $page->id, 'usermodified' => $this->users['student']->id], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') // False. ); @@ -453,7 +453,7 @@ public function test_store_answer_multiple_answers(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - false, + [], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') // True. ); @@ -473,7 +473,7 @@ public function test_store_answer_multiple_answers(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - false, + [], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') // True. ); @@ -500,7 +500,7 @@ public function test_store_answer_multiple_answers(): void { $helper->store_answer( 'mootimetertool_wordcloud_answers', $records, - true, + ['pageid' => $page->id, 'usermodified' => $this->users['student']->id], self::ANSWER_COLUMN_WORDCLOD, (bool)$helper::get_tool_config($page, 'multipleanswers') // True. ); diff --git a/tools/quiz/classes/quiz.php b/tools/quiz/classes/quiz.php index a4c2b878..0c111de0 100644 --- a/tools/quiz/classes/quiz.php +++ b/tools/quiz/classes/quiz.php @@ -215,7 +215,7 @@ public function get_visualization_settings_charjs(int $visualizationtypeid, $pag * @return void */ public function insert_answer(object $page, mixed $aoids) { - global $DB; + global $DB, $USER; $records = []; @@ -247,7 +247,7 @@ public function insert_answer(object $page, mixed $aoids) { $this->store_answer( $this->get_answer_table(), $records, - true, + ['pageid' => $page->id, 'usermodified' => $USER->id], $this->get_answer_column(), $enablemultipleanswers ); diff --git a/tools/ranking/amd/build/approve_phrase.min.js b/tools/ranking/amd/build/approve_phrase.min.js new file mode 100644 index 00000000..a3377489 --- /dev/null +++ b/tools/ranking/amd/build/approve_phrase.min.js @@ -0,0 +1,3 @@ +define("mootimetertool_ranking/approve_phrase",["exports","core/ajax","mod_mootimeter/reload_page","mod_mootimeter/utils"],(function(_exports,_ajax,_reload_page,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=(yesid,noid,radioname)=>{window.console.log('input[type="radio"][name="'+radioname+'"]');for(let elem of document.querySelectorAll('input[type="radio"][name="'+radioname+'"]'))elem.addEventListener("input",(event=>{approve(event)}));function approve(event){const id=event.target.id,pageid=document.getElementById(id).dataset.pageid,phraseid=document.getElementById(id).dataset.phraseid,togglevalue=document.getElementById(id).dataset.togglevalue;window.console.log([id,pageid,phraseid,togglevalue]),approvePhrase(id,pageid,phraseid,togglevalue)}};const approvePhrase=async(inputid,pageid,phraseid,togglevalue)=>{document.getElementById(inputid).disabled=!0;const response=await((pageid,phraseid,value)=>(0,_ajax.call)([{methodname:"mootimetertool_ranking_approve_phrase",args:{pageid:pageid,phraseid:phraseid,value:value}}])[0])(pageid,phraseid,togglevalue);if((0,_utils.removeInfoBox)("mtmt_phrase_warning"),200!=response.code&&((0,_utils.renderInfoBox)("mtmt_tool-colct-header","mtmt_phrase_warning","warning",response.string),document.getElementById(inputid).disabled=!1,document.getElementById(inputid).focus()),200==response.code){const queryString=window.location.search,urlParams=new URLSearchParams(queryString);await(0,_reload_page.execReloadPage)(urlParams.get("pageid"),urlParams.get("id"))}}})); + +//# sourceMappingURL=approve_phrase.min.js.map \ No newline at end of file diff --git a/tools/ranking/amd/build/approve_phrase.min.js.map b/tools/ranking/amd/build/approve_phrase.min.js.map new file mode 100644 index 00000000..1b6646de --- /dev/null +++ b/tools/ranking/amd/build/approve_phrase.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"approve_phrase.min.js","sources":["../src/approve_phrase.js"],"sourcesContent":["import {call as fetchMany} from 'core/ajax';\nimport {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page';\nimport {renderInfoBox} from 'mod_mootimeter/utils';\nimport {removeInfoBox} from 'mod_mootimeter/utils';\n\nexport const init = (yesid, noid, radioname) => {\n\n // var yesObj = document.getElementById(yesid);\n // var noObj = document.getElementById(noid);\n\n // window.console.log(yesObj);\n // window.console.log(noObj);\n // if (!document.getElementById(yesObj) || !document.getElementById(noObj)) {\n // return;\n // }\n\n window.console.log('input[type=\"radio\"][name=\"' + radioname + '\"]');\n for (let elem of document.querySelectorAll('input[type=\"radio\"][name=\"' + radioname +'\"]')) {\n elem.addEventListener(\"input\", (event) => {\n approve(event);\n });\n }\n\n // yesObj.addEventListener(\"change\", approve);\n // noObj.addEventListener(\"change\", approve);\n\n /**\n * Create new page.\n * @param {object} event\n */\n function approve(event) {\n const id = event.target.id;\n const pageid = document.getElementById(id).dataset.pageid;\n const phraseid = document.getElementById(id).dataset.phraseid;\n const togglevalue = document.getElementById(id).dataset.togglevalue;\n window.console.log([id, pageid, phraseid, togglevalue]);\n approvePhrase(id, pageid, phraseid, togglevalue);\n }\n};\n\n/**\n * Call to approve a phrase\n * @param {int} pageid\n * @param {int} phraseid\n * @param {int} value\n */\nconst execApprovePhrase = (\n pageid,\n phraseid,\n value\n) => fetchMany([{\n methodname: 'mootimetertool_ranking_approve_phrase',\n args: {\n pageid,\n phraseid,\n value\n },\n}])[0];\n\n/**\n * Executes the call to approve a phrase.\n * @param {string} inputid\n * @param {int} pageid\n * @param {int} phraseid\n * @param {int} togglevalue\n */\nconst approvePhrase = async (inputid, pageid, phraseid, togglevalue) => {\n\n // Disable the input field until the page is refreshed.\n document.getElementById(inputid).disabled = true;\n\n const response = await execApprovePhrase(pageid, phraseid, togglevalue);\n\n const infoboxid = \"mtmt_phrase_warning\";\n\n removeInfoBox(infoboxid);\n\n if (response.code != 200) {\n renderInfoBox(\"mtmt_tool-colct-header\", infoboxid, \"warning\", response.string);\n document.getElementById(inputid).disabled = false;\n document.getElementById(inputid).focus();\n }\n\n if (response.code == 200) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n await reloadPage(urlParams.get('pageid'), urlParams.get('id'));\n }\n};\n"],"names":["yesid","noid","radioname","window","console","log","elem","document","querySelectorAll","addEventListener","event","approve","id","target","pageid","getElementById","dataset","phraseid","togglevalue","approvePhrase","async","inputid","disabled","response","value","methodname","args","execApprovePhrase","code","string","focus","queryString","location","search","urlParams","URLSearchParams","get"],"mappings":"oQAKoB,CAACA,MAAOC,KAAMC,aAW9BC,OAAOC,QAAQC,IAAI,6BAA+BH,UAAY,UACzD,IAAII,QAAQC,SAASC,iBAAiB,6BAA+BN,UAAW,MACjFI,KAAKG,iBAAiB,SAAUC,QAC5BC,QAAQD,mBAWPC,QAAQD,aACPE,GAAKF,MAAMG,OAAOD,GAClBE,OAASP,SAASQ,eAAeH,IAAII,QAAQF,OAC7CG,SAAWV,SAASQ,eAAeH,IAAII,QAAQC,SAC/CC,YAAcX,SAASQ,eAAeH,IAAII,QAAQE,YACxDf,OAAOC,QAAQC,IAAI,CAACO,GAAIE,OAAQG,SAAUC,cAC1CC,cAAcP,GAAIE,OAAQG,SAAUC,qBA8BtCC,cAAgBC,MAAOC,QAASP,OAAQG,SAAUC,eAGpDX,SAASQ,eAAeM,SAASC,UAAW,QAEtCC,cAzBgB,EACtBT,OACAG,SACAO,SACC,cAAU,CAAC,CACZC,WAAY,wCACZC,KAAM,CACFZ,OAAAA,OACAG,SAAAA,SACAO,MAAAA,UAEJ,GAcuBG,CAAkBb,OAAQG,SAAUC,yCAEzC,uBAIG,KAAjBK,SAASK,gCACK,yBALA,sBAKqC,UAAWL,SAASM,QACvEtB,SAASQ,eAAeM,SAASC,UAAW,EAC5Cf,SAASQ,eAAeM,SAASS,SAGhB,KAAjBP,SAASK,KAAa,OAChBG,YAAc5B,OAAO6B,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBJ,mBAChC,+BAAWG,UAAUE,IAAI,UAAWF,UAAUE,IAAI"} \ No newline at end of file diff --git a/tools/ranking/amd/build/store_phrase.min.js b/tools/ranking/amd/build/store_phrase.min.js new file mode 100644 index 00000000..ae363d38 --- /dev/null +++ b/tools/ranking/amd/build/store_phrase.min.js @@ -0,0 +1,3 @@ +define("mootimetertool_ranking/store_phrase",["exports","core/ajax","mod_mootimeter/reload_page","mod_mootimeter/utils"],(function(_exports,_ajax,_reload_page,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=(inputid,enterid)=>{const ao=document.getElementById(inputid);ao&&ao.addEventListener("keyup",(function(event){"Enter"!==event.code&&"NumpadEnter"!==event.code||store(inputid)}));const ae=document.getElementById(enterid);function store(inputid){var pageid=document.getElementById(inputid).dataset.pageid,phrase=document.getElementById(inputid).value;storePhrase(pageid,phrase,inputid)}ae&&ae.addEventListener("click",(function(){store(inputid)}))};const storePhrase=async(pageid,phrase,inputid)=>{document.getElementById(inputid).disabled=!0;const response=await((pageid,phrase)=>(0,_ajax.call)([{methodname:"mootimetertool_ranking_store_phrase",args:{pageid:pageid,phrase:phrase}}])[0])(pageid,phrase);if((0,_utils.removeInfoBox)("mtmt_phrase_warning"),1e3!=response.code&&1001!=response.code&&1002!=response.code||((0,_utils.renderInfoBox)("mtmt_tool-colct-header","mtmt_phrase_warning","warning",response.string),document.getElementById(inputid).disabled=!1,document.getElementById(inputid).focus()),200==response.code){const queryString=window.location.search,urlParams=new URLSearchParams(queryString);await(0,_reload_page.execReloadPage)(urlParams.get("pageid"),urlParams.get("id")),await(0,_utils.delay)(100);for(var elements=document.getElementsByClassName("mtmt-wc-phraseinput"),i=0;i {\n\n // Register event to input box.\n const ao = document.getElementById(inputid);\n if (ao) {\n ao.addEventListener(\"keyup\", function (event) {\n if (event.code === 'Enter' || event.code === 'NumpadEnter') {\n store(inputid);\n }\n });\n }\n\n // Register event to submit button.\n const ae = document.getElementById(enterid);\n if (ae) {\n ae.addEventListener(\"click\", function () {\n store(inputid);\n });\n }\n\n /**\n * Create new page.\n * @param {string} inputid\n */\n function store(inputid) {\n var pageid = document.getElementById(inputid).dataset.pageid;\n var phrase = document.getElementById(inputid).value;\n storePhrase(pageid, phrase, inputid);\n }\n};\n\n/**\n * Call to store an phrase\n * @param {int} pageid\n * @param {string} phrase\n * @returns {array}\n */\nconst execStorePhrase = (\n pageid,\n phrase,\n) => fetchMany([{\n methodname: 'mootimetertool_ranking_store_phrase',\n args: {\n pageid,\n phrase\n },\n}])[0];\n\n/**\n * Executes the call to store an phrase.\n * @param {int} pageid\n * @param {string} phrase\n * @param {string} inputid\n */\nconst storePhrase = async (pageid, phrase, inputid) => {\n\n // Disable the input field until the page is refreshed.\n document.getElementById(inputid).disabled = true;\n\n const response = await execStorePhrase(pageid, phrase);\n\n const infoboxid = \"mtmt_phrase_warning\";\n\n removeInfoBox(infoboxid);\n\n if (response.code == 1000 || response.code == 1001 || response.code == 1002) {\n renderInfoBox(\"mtmt_tool-colct-header\", infoboxid, \"warning\", response.string);\n document.getElementById(inputid).disabled = false;\n document.getElementById(inputid).focus();\n }\n\n if (response.code == 200) {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n await reloadPage(urlParams.get('pageid'), urlParams.get('id'));\n\n // Now set the focus to the input field.\n\n // Yes, it is not nice, but the dom needs some time to be updated.\n await delay(100);\n\n var elements = document.getElementsByClassName(\"mtmt-wc-phraseinput\");\n for (var i = 0; i < elements.length; i++) {\n if (elements.item(i).autofocus == true) {\n elements.item(i).focus();\n }\n }\n }\n};\n"],"names":["inputid","enterid","ao","document","getElementById","addEventListener","event","code","store","ae","pageid","dataset","phrase","value","storePhrase","async","disabled","response","methodname","args","execStorePhrase","string","focus","queryString","window","location","search","urlParams","URLSearchParams","get","elements","getElementsByClassName","i","length","item","autofocus"],"mappings":"kQAMoB,CAACA,QAASC,iBAGpBC,GAAKC,SAASC,eAAeJ,SAC/BE,IACAA,GAAGG,iBAAiB,SAAS,SAAUC,OAChB,UAAfA,MAAMC,MAAmC,gBAAfD,MAAMC,MAChCC,MAAMR,kBAMRS,GAAKN,SAASC,eAAeH,kBAW1BO,MAAMR,aACPU,OAASP,SAASC,eAAeJ,SAASW,QAAQD,OAClDE,OAAST,SAASC,eAAeJ,SAASa,MAC9CC,YAAYJ,OAAQE,OAAQZ,SAb5BS,IACAA,GAAGJ,iBAAiB,SAAS,WACzBG,MAAMR,mBAsChBc,YAAcC,MAAOL,OAAQE,OAAQZ,WAGvCG,SAASC,eAAeJ,SAASgB,UAAW,QAEtCC,cAtBc,EACpBP,OACAE,UACC,cAAU,CAAC,CACZM,WAAY,sCACZC,KAAM,CACFT,OAAAA,OACAE,OAAAA,WAEJ,GAauBQ,CAAgBV,OAAQE,oCAE7B,uBAIG,KAAjBK,SAASV,MAAiC,MAAjBU,SAASV,MAAiC,MAAjBU,SAASV,gCAC7C,yBALA,sBAKqC,UAAWU,SAASI,QACvElB,SAASC,eAAeJ,SAASgB,UAAW,EAC5Cb,SAASC,eAAeJ,SAASsB,SAGhB,KAAjBL,SAASV,KAAa,OAChBgB,YAAcC,OAAOC,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBL,mBAChC,+BAAWI,UAAUE,IAAI,UAAWF,UAAUE,IAAI,aAKlD,gBAAM,aAERC,SAAW3B,SAAS4B,uBAAuB,uBACtCC,EAAI,EAAGA,EAAIF,SAASG,OAAQD,IACC,GAA9BF,SAASI,KAAKF,GAAGG,WACjBL,SAASI,KAAKF,GAAGV"} \ No newline at end of file diff --git a/tools/ranking/amd/src/approve_phrase.js b/tools/ranking/amd/src/approve_phrase.js new file mode 100644 index 00000000..7b7cc4b1 --- /dev/null +++ b/tools/ranking/amd/src/approve_phrase.js @@ -0,0 +1,89 @@ +import {call as fetchMany} from 'core/ajax'; +import {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page'; +import {renderInfoBox} from 'mod_mootimeter/utils'; +import {removeInfoBox} from 'mod_mootimeter/utils'; + +export const init = (yesid, noid, radioname) => { + + // var yesObj = document.getElementById(yesid); + // var noObj = document.getElementById(noid); + + // window.console.log(yesObj); + // window.console.log(noObj); + // if (!document.getElementById(yesObj) || !document.getElementById(noObj)) { + // return; + // } + + window.console.log('input[type="radio"][name="' + radioname + '"]'); + for (let elem of document.querySelectorAll('input[type="radio"][name="' + radioname +'"]')) { + elem.addEventListener("input", (event) => { + approve(event); + }); + } + + // yesObj.addEventListener("change", approve); + // noObj.addEventListener("change", approve); + + /** + * Create new page. + * @param {object} event + */ + function approve(event) { + const id = event.target.id; + const pageid = document.getElementById(id).dataset.pageid; + const phraseid = document.getElementById(id).dataset.phraseid; + const togglevalue = document.getElementById(id).dataset.togglevalue; + window.console.log([id, pageid, phraseid, togglevalue]); + approvePhrase(id, pageid, phraseid, togglevalue); + } +}; + +/** + * Call to approve a phrase + * @param {int} pageid + * @param {int} phraseid + * @param {int} value + */ +const execApprovePhrase = ( + pageid, + phraseid, + value +) => fetchMany([{ + methodname: 'mootimetertool_ranking_approve_phrase', + args: { + pageid, + phraseid, + value + }, +}])[0]; + +/** + * Executes the call to approve a phrase. + * @param {string} inputid + * @param {int} pageid + * @param {int} phraseid + * @param {int} togglevalue + */ +const approvePhrase = async (inputid, pageid, phraseid, togglevalue) => { + + // Disable the input field until the page is refreshed. + document.getElementById(inputid).disabled = true; + + const response = await execApprovePhrase(pageid, phraseid, togglevalue); + + const infoboxid = "mtmt_phrase_warning"; + + removeInfoBox(infoboxid); + + if (response.code != 200) { + renderInfoBox("mtmt_tool-colct-header", infoboxid, "warning", response.string); + document.getElementById(inputid).disabled = false; + document.getElementById(inputid).focus(); + } + + if (response.code == 200) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + await reloadPage(urlParams.get('pageid'), urlParams.get('id')); + } +}; diff --git a/tools/ranking/amd/src/store_phrase.js b/tools/ranking/amd/src/store_phrase.js new file mode 100644 index 00000000..8cf02e7b --- /dev/null +++ b/tools/ranking/amd/src/store_phrase.js @@ -0,0 +1,95 @@ +import {call as fetchMany} from 'core/ajax'; +import {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page'; +import {renderInfoBox} from 'mod_mootimeter/utils'; +import {removeInfoBox} from 'mod_mootimeter/utils'; +import {delay} from 'mod_mootimeter/utils'; + +export const init = (inputid, enterid) => { + + // Register event to input box. + const ao = document.getElementById(inputid); + if (ao) { + ao.addEventListener("keyup", function (event) { + if (event.code === 'Enter' || event.code === 'NumpadEnter') { + store(inputid); + } + }); + } + + // Register event to submit button. + const ae = document.getElementById(enterid); + if (ae) { + ae.addEventListener("click", function () { + store(inputid); + }); + } + + /** + * Create new page. + * @param {string} inputid + */ + function store(inputid) { + var pageid = document.getElementById(inputid).dataset.pageid; + var phrase = document.getElementById(inputid).value; + storePhrase(pageid, phrase, inputid); + } +}; + +/** + * Call to store an phrase + * @param {int} pageid + * @param {string} phrase + * @returns {array} + */ +const execStorePhrase = ( + pageid, + phrase, +) => fetchMany([{ + methodname: 'mootimetertool_ranking_store_phrase', + args: { + pageid, + phrase + }, +}])[0]; + +/** + * Executes the call to store an phrase. + * @param {int} pageid + * @param {string} phrase + * @param {string} inputid + */ +const storePhrase = async (pageid, phrase, inputid) => { + + // Disable the input field until the page is refreshed. + document.getElementById(inputid).disabled = true; + + const response = await execStorePhrase(pageid, phrase); + + const infoboxid = "mtmt_phrase_warning"; + + removeInfoBox(infoboxid); + + if (response.code == 1000 || response.code == 1001 || response.code == 1002) { + renderInfoBox("mtmt_tool-colct-header", infoboxid, "warning", response.string); + document.getElementById(inputid).disabled = false; + document.getElementById(inputid).focus(); + } + + if (response.code == 200) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + await reloadPage(urlParams.get('pageid'), urlParams.get('id')); + + // Now set the focus to the input field. + + // Yes, it is not nice, but the dom needs some time to be updated. + await delay(100); + + var elements = document.getElementsByClassName("mtmt-wc-phraseinput"); + for (var i = 0; i < elements.length; i++) { + if (elements.item(i).autofocus == true) { + elements.item(i).focus(); + } + } + } +}; diff --git a/tools/ranking/amd/src/thumbvote.js b/tools/ranking/amd/src/thumbvote.js new file mode 100644 index 00000000..79222bbc --- /dev/null +++ b/tools/ranking/amd/src/thumbvote.js @@ -0,0 +1,134 @@ +import {call as fetchMany} from 'core/ajax'; +import Log from 'core/log'; +import ModalSaveCancel from 'core/modal_save_cancel'; +import ModalDeleteCancel from 'core/modal_delete_cancel'; +import ModalEvents from 'core/modal_events'; +import {get_string as getString} from 'core/str'; +import {execReloadPage as reloadPage} from 'mod_mootimeter/reload_page'; + +export const init = async (uniqueID) => { + var obj = document.getElementById(uniqueID); + + if (!document.getElementById(uniqueID)) { + return; + } + + const pageid = document.getElementById('mootimeterstate').dataset.pageid; + var dataset = obj.dataset; + var confirmationTitleStr; + + window.console.log(obj.getAttribute("data-confirmationtype")); + window.console.log("TEST"); + + if (obj.getAttribute("data-confirmationtype")) { + window.console.log("POST"); + + obj.addEventListener("click", showModal); + + if (!obj.getAttribute("data-confirmationtitlestr")) { + confirmationTitleStr = getString('delete', 'core'); + } else { + confirmationTitleStr = dataset.confirmationtitlestr; + } + + var confirmationQuestionStr; + if (!obj.getAttribute("data-confirmationquestionstr")) { + confirmationQuestionStr = getString('areyousure'); + } else { + confirmationQuestionStr = dataset.confirmationquestionstr; + } + + var modal; + + switch (dataset.confirmationtype) { + case 'DELETE_CANCEL': + modal = await ModalDeleteCancel.create({ + title: confirmationTitleStr, + body: confirmationQuestionStr, + pageid: pageid, + }); + break; + case 'SAVE_CANCEL': + modal = await ModalSaveCancel.create({ + title: confirmationTitleStr, + body: confirmationQuestionStr, + pageid: pageid, + }); + break; + } + + modal.getRoot().on(ModalEvents.delete, function () { + var uniqueID = obj.id; + var ajaxmethode = obj.dataset.ajaxmethode; + + buttonClickedHandle(pageid, uniqueID, ajaxmethode); + }); + + modal.getRoot().on(ModalEvents.save, function () { + var uniqueID = obj.id; + var ajaxmethode = obj.dataset.ajaxmethode; + + buttonClickedHandle(pageid, uniqueID, ajaxmethode); + }); + + } else { + obj.addEventListener("click", buttonClicked); + } + + /** + * Handle Button clicked event. + */ + function buttonClicked() { + const pageid = document.getElementById('mootimeterstate').dataset.pageid; + var uniqueID = obj.id; + var ajaxmethode = obj.dataset.ajaxmethode; + buttonClickedHandle(pageid, uniqueID, ajaxmethode); + } + + /** + * Store the value. + */ + function showModal() { + modal.show(); + } +}; + +/** + * Call to store input value + * @param {int} pageid + * @param {string} thisDataset + * @param {string} ajaxmethode + * @returns {mixed} + */ +const execButtonClicked = ( + pageid, + thisDataset, + ajaxmethode +) => fetchMany([{ + methodname: ajaxmethode, + args: { + pageid, + thisDataset + }, +}])[0]; + +/** + * Executes the call to store input value. + * @param {int} pageid + * @param {string} uniqueID + * @param {string} ajaxmethode + */ +const buttonClickedHandle = async(pageid, uniqueID, ajaxmethode) => { + var dataset = JSON.stringify(document.getElementById(uniqueID).dataset); + const response = await execButtonClicked(pageid, dataset, ajaxmethode); + + if (response.code != 200) { + Log.error(response.string); + } + + if (response.reload == true) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + reloadPage(urlParams.get('pageid'), urlParams.get('id')); + } +}; diff --git a/tools/ranking/classes/external/approve_phrase.php b/tools/ranking/classes/external/approve_phrase.php new file mode 100644 index 00000000..7ce6ef61 --- /dev/null +++ b/tools/ranking/classes/external/approve_phrase.php @@ -0,0 +1,107 @@ +. + +/** + * Web service to approve a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mootimetertool_ranking\external; + +use coding_exception; +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use invalid_parameter_exception; +use dml_exception; +use mod_mootimeter\helper; + +/** + * Web service to approve a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class approve_phrase extends external_api { + /** + * Describes the parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters() { + return new external_function_parameters([ + 'pageid' => new external_value(PARAM_INT, 'The page id to obtain results for.', VALUE_REQUIRED), + 'phraseid' => new external_value(PARAM_INT, 'The phraseid of the phrase to be approved.', VALUE_REQUIRED), + 'value' => new external_value(PARAM_INT, 'The new state value.', VALUE_REQUIRED), + ]); + } + + /** + * Execute the service. + * + * @param int $pageid + * @param int $phraseid + * @param int $value + * @return array + */ + public static function execute(int $pageid, int $phraseid, int $value): array { + global $USER; + + [ + 'pageid' => $pageid, + 'phraseid' => $phraseid, + 'value' => $value, + ] = self::validate_parameters(self::execute_parameters(), [ + 'pageid' => $pageid, + 'phraseid' => $phraseid, + 'value' => $value, + ]); + + $helper = new helper(); + $page = $helper->get_page($pageid); + + $ranking = new \mootimetertool_ranking\ranking(); + + $success = $ranking->set_phrase_approval_state($phraseid, $value); + + if ($success) { + return ['code' => helper::ERRORCODE_OK, 'string' => 'ok']; + } + return ['code' => helper::ERRORCODE_TO_MANY_ANSWERS, 'string' => 'ok']; + } + + /** + * Describes the return structure of the service.. + * + * @return external_single_structure + */ + public static function execute_returns() { + return new external_single_structure( + [ + 'code' => new external_value(PARAM_INT, 'Return code of storage process.'), + 'string' => new external_value(PARAM_TEXT, 'Return string of storage process.'), + ], + 'Status of phrase state storing process' + ); + } +} diff --git a/tools/ranking/classes/external/store_phrase.php b/tools/ranking/classes/external/store_phrase.php new file mode 100644 index 00000000..ca963680 --- /dev/null +++ b/tools/ranking/classes/external/store_phrase.php @@ -0,0 +1,126 @@ +. + +/** + * Web service to store a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mootimetertool_ranking\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use mod_mootimeter\helper; + +/** + * Web service to store a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class store_phrase extends external_api { + /** + * Describes the parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters() { + return new external_function_parameters([ + 'pageid' => new external_value(PARAM_INT, 'The page id to obtain results for.', VALUE_REQUIRED), + 'phrase' => new external_value(PARAM_RAW, 'The phrase the user entered.', VALUE_REQUIRED), + ]); + } + + /** + * Execute the service. + * + * @param int $pageid + * @param string $phrase + * @return array + */ + public static function execute(int $pageid, string $phrase): array { + global $USER; + + [ + 'pageid' => $pageid, + 'phrase' => $phrase, + ] = self::validate_parameters(self::execute_parameters(), [ + 'pageid' => $pageid, + 'phrase' => $phrase, + ]); + + if (empty($phrase) && strlen($phrase) == 0) { + return [ + 'code' => helper::ERRORCODE_EMPTY_ANSWER, + 'string' => get_string('error_empty_phrases', 'mootimetertool_ranking'), + ]; + } + + $helper = new helper(); + $page = $helper->get_page($pageid); + + $maxnumberofphrases = helper::get_tool_config($page->id, "maxinputsperuser"); + $ranking = new \mootimetertool_ranking\ranking(); + $submittedphrases = $ranking->get_user_answers( + 'mootimetertool_ranking_phrases', + $page->id, + $ranking::PHRASE_COLUMN, + $USER->id + ); + + if (count($submittedphrases) >= $maxnumberofphrases && $maxnumberofphrases > 0) { + return [ + 'code' => helper::ERRORCODE_TO_MANY_ANSWERS, + 'string' => get_string('error_to_many_phrases', 'mootimetertool_ranking'), + ]; + } + + $submittedphrases = $ranking->get_phrase_list_array($page->id, $USER->id); + // Use strtolower in order to ignore case sensitive inputs of users. This makes the result more precise. + if (!helper::get_tool_config($page->id, "allowduplicatephrases") && in_array(strtolower($phrase), $submittedphrases)) { + return [ + 'code' => helper::ERRORCODE_DUPLICATE_ANSWER, + 'string' => get_string('error_no_duplicate_phrases', 'mootimetertool_ranking'), + ]; + } + + $ranking->insert_phrase($page, $phrase); + return ['code' => helper::ERRORCODE_OK, 'string' => 'ok']; + } + + /** + * Describes the return structure of the service.. + * + * @return external_single_structure + */ + public static function execute_returns() { + return new external_single_structure( + [ + 'code' => new external_value(PARAM_INT, 'Return code of storage process.'), + 'string' => new external_value(PARAM_TEXT, 'Return string of storage process.'), + ], + 'Status of phrase storing process' + ); + } +} diff --git a/tools/ranking/classes/external/vote_phrase_thumb.php b/tools/ranking/classes/external/vote_phrase_thumb.php new file mode 100644 index 00000000..1c367f6e --- /dev/null +++ b/tools/ranking/classes/external/vote_phrase_thumb.php @@ -0,0 +1,96 @@ +. + +/** + * Web service to store a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mootimetertool_ranking\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use mod_mootimeter\helper; + +/** + * Web service to store a phrase. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class vote_phrase_thumb extends external_api { + /** + * Describes the parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters() { + return new external_function_parameters([ + 'pageid' => new external_value(PARAM_INT, 'The page id to obtain results for.', VALUE_REQUIRED), + 'thisDataset' => new external_value(PARAM_TEXT, 'The dataset of the clicked object.', VALUE_REQUIRED), + ]); + } + + /** + * Execute the service. + * + * @param int $pageid + * @param string $thisdataset + * @return array + */ + public static function execute(int $pageid, string $thisdataset): array { + global $USER; + + [ + 'pageid' => $pageid, + 'thisDataset' => $thisdataset, + ] = self::validate_parameters(self::execute_parameters(), [ + 'pageid' => $pageid, + 'thisDataset' => $thisdataset, + ]); + + $dataset = json_decode($thisdataset); + $ranking = new \mootimetertool_ranking\ranking(); + + $phrase = $ranking->get_phrase($dataset->phraseid); + $ranking->insert_answer($phrase, $dataset->thumbvalue); + return ['code' => helper::ERRORCODE_OK, 'string' => 'ok', 'reload' => true]; + } + + /** + * Describes the return structure of the service.. + * + * @return external_single_structure + */ + public static function execute_returns() { + return new external_single_structure( + [ + 'code' => new external_value(PARAM_INT, 'Return code of storage process.'), + 'string' => new external_value(PARAM_TEXT, 'Return string of storage process.'), + 'reload' => new external_value(PARAM_BOOL, 'Indicator to reload page.', VALUE_DEFAULT, false), + ], + 'Status of phrase storing process' + ); + } +} diff --git a/tools/ranking/classes/local/inplace_edit_answer.php b/tools/ranking/classes/local/inplace_edit_answer.php new file mode 100644 index 00000000..411735f8 --- /dev/null +++ b/tools/ranking/classes/local/inplace_edit_answer.php @@ -0,0 +1,99 @@ +. + +/** + * Helper class to handle inplace edit of phrases. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mootimetertool_ranking\local; + +use coding_exception; +use dml_exception; + +/** + * Helper class to handle inplace edit of phrases. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class inplace_edit_answer extends \core\output\inplace_editable { + + /** + * Constructor. + * + * @param object $page + * @param object $answer + * @return void + * @throws dml_exception + * @throws coding_exception + */ + public function __construct(object $page, object $answer) { + + $ranking = new \mootimetertool_ranking\ranking(); + + $instance = $ranking::get_instance_by_pageid($page->id); + $cm = $ranking::get_cm_by_instance($instance); + + parent::__construct( + 'mootimeter', + 'ranking_editanswer', + $page->id . "_" . $answer->id, + has_capability('mod/mootimeter:moderator', \context_module::instance($cm->id)), + format_string($answer->{$ranking::ANSWER_COLUMN}), + $answer->{$ranking::ANSWER_COLUMN} + ); + } + + /** + * Updates the value in database and returns itself, called from inplace_editable callback + * + * @param mixed $itemid + * @param mixed $newvalue + * @return \self + */ + public static function update(mixed $itemid, mixed $newvalue) { + global $DB; + + $newvalue = clean_param($newvalue, PARAM_TEXT); + + $helper = new \mod_mootimeter\helper(); + + list($pageid, $answerid) = explode("_", $itemid); + + $answertable = $helper->get_tool_answer_table($pageid); + $answercol = $helper->get_tool_answer_column($pageid); + + // First update the existing value. + $answerrecord = $DB->get_record($answertable, ['id' => $answerid]); + $answerrecord->{$answercol} = $newvalue; + $DB->update_record($answertable, $answerrecord); + + // Now clear the answers cache. + $helper->clear_caches($pageid); + + // Finally return itself. + $tmpl = new self($helper->get_page($pageid), $answerrecord); + return $tmpl; + + } +} diff --git a/tools/ranking/classes/ranking.php b/tools/ranking/classes/ranking.php new file mode 100644 index 00000000..1333dcbf --- /dev/null +++ b/tools/ranking/classes/ranking.php @@ -0,0 +1,819 @@ +. + +/** + * Pluginlib + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + **/ + +namespace mootimetertool_ranking; + +use coding_exception; +use dml_exception; +use moodle_exception; +use moodle_url; +use pix_icon; + +/** + * Pluginlib + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + **/ +class ranking extends \mod_mootimeter\toolhelper { + + /** + * @var string Answer coloum + */ + const ANSWER_COLUMN = "votevalue"; + + /** + * @var string Answer coloum + */ + const PHRASE_COLUMN = "phrase"; + + /** + * @var string Cacheidentifier phrases. + */ + const CACHEIDENTIFIER_PHRASES = 'phrases'; + + /** + * @var string Cacheidentifier phrases votes. + */ + const CACHEIDENTIFIER_VOTES = 'phrasevotes'; + + /** + * @var string Phrases table + */ + const PHRASES_TABLE = "mootimetertool_ranking_phrases"; + + /** + * @var string Votes table + */ + const PHRASE_VOTES_TABLE = "mootimetertool_ranking_votes"; + + /** + * @var string Name of table column of the answer table where the user id is stored + */ + const ANSWER_USERID_COLUMN = 'usermodified'; + + /** + * @var int Phrase state - declined. + */ + const PHRASE_STATE_DECLINED = 0; + + /** + * @var int Phrase state - not reviewed. + */ + const PHRASE_STATE_NOT_REVIEWED = 5; + + /** + * @var int Phrase state - accepted. + */ + const PHRASE_STATE_ACCEPTED = 10; + + /** + * @var int Vote value - thumb down. + */ + const VOTE_VALUE_THUMBDOWN = -1; + + /** + * @var int Vote value - thumb up. + */ + const VOTE_VALUE_THUMBUP = 1; + + /** + * Get the tools answer column. + * @return string + */ + public function get_answer_column() { + return self::ANSWER_COLUMN; + } + + /** + * Get the tools answer table. + * @return string + */ + public function get_answer_table() { + return self::PHRASE_VOTES_TABLE; + } + + /** + * Get the tools answer table. + * @return string + */ + public function get_phrases_table() { + return self::PHRASES_TABLE; + } + + /** + * Get the userid column name in the answer table of the tool. + * + * @return ?string the column name where the user id is stored in the answer table, null if no user id is stored + */ + public function get_answer_userid_column(): ?string { + return self::ANSWER_USERID_COLUMN; + } + + /** + * Will be executed after the page is created. + * @param object $page + * @return void + */ + public function hook_after_new_page_created(object $page): void { + global $USER; + + $record = new \stdClass(); + $record->pageid = $page->id; + $record->usermodified = $USER->id; + $record->pageid = $page->id; + $record->phrase = ""; + $record->state = self::PHRASE_STATE_NOT_REVIEWED; + $record->timecreated = time(); + + // Store two answer options as default. + $this->store_phrase($record); + $this->store_phrase($record); + return; + } + + /** + * Store a phrase option. + * + * @param object $record + * @return int + */ + public function store_phrase(object $record): int { + global $DB; + + $instance = \mod_mootimeter\helper::get_instance_by_pageid($record->pageid); + $cm = \mod_mootimeter\helper::get_cm_by_instance($instance); + if (!has_capability('mod/mootimeter:moderator', \context_module::instance($cm->id))) { + return 0; + } + + // Set the default value for timemodified to 0. This is necessary to make usage of GREATEST SQL method possible. + $record->timemodified = 0; + + if (!empty($record->id)) { + $page = $this->get_page($record->pageid); + $origrecord = $DB->get_record($this->get_phrases_table(), ['id' => $record->id], '*', MUST_EXIST); + $origrecord->pageid = $record->pageid; + $origrecord->phrase = $record->optiontext; + $origrecord->timemodified = time(); + + $DB->update_record($this->get_phrases_table(), $origrecord); + return $origrecord->id; + } + + return $DB->insert_record($this->get_phrases_table(), $record, true); + } + + /** + * Get a phrase object. + * + * @param int $phraseid + * @return object|bool + * @throws dml_exception + */ + public function get_phrase(int $phraseid): object|bool { + global $DB; + return $DB->get_record(self::PHRASES_TABLE, ['id' => $phraseid]); + } + + /** + * Get the user vote record of a phrase. + * + * @param int $userid + * @param int $phraseid + * @return object|bool + */ + public function get_user_phrase_vote(int $userid, int $phraseid): object|bool { + global $DB; + return $DB->get_record(self::PHRASE_VOTES_TABLE, ['phraseid' => $phraseid, 'usermodified' => $userid]); + } + + /** + * Delete all DB entries related to a specific page. + * @param object $page + * @return bool + */ + public function delete_page_tool(object $page) { + global $DB; + try { + $DB->delete_records($this->get_answer_table(), ['pageid' => $page->id]); + $DB->delete_records($this->get_phrases_table(), ['pageid' => $page->id]); + } catch (\Exception $e) { + return false; + } + return true; + } + + /** + * Insert a phrase. + * + * @param object $page + * @param string $phrase + * @return void + */ + public function insert_phrase(object $page, string $phrase): void { + global $USER; + + $record = new \stdClass(); + $record->usermodified = $USER->id; + $record->pageid = $page->id; + $record->phrase = $phrase; + $record->state = self::PHRASE_STATE_NOT_REVIEWED; + $record->timecreated = time(); + + $this->store_answer(self::PHRASES_TABLE, $record, [], 'phrase'); + } + + /** + * Get a pure list of unique phrases, without id etc. + * + * @param int $pageid + * @param int $userid + * @return array + * @throws dml_exception + */ + public function get_phrase_list_array(int $pageid, int $userid = 0): array { + global $DB; + + $params = [ + 'pageid' => $pageid, + ]; + + if (!empty($userid)) { + $params['usermodified'] = $userid; + } + + return array_keys($DB->get_records_menu(self::PHRASES_TABLE, $params, "", self::PHRASE_COLUMN)); + } + + /** + * Page type specivic insert_answer + * + * @param object $phrase + * @param string $votevalue + * @return void + */ + public function insert_answer(object $phrase, $votevalue): void { + global $USER; + + $record = new \stdClass(); + $record->pageid = $phrase->pageid; + $record->phraseid = $phrase->id; + $record->votevalue = $votevalue; + $record->timecreated = time(); + + $updatecondition = ['pageid' => $phrase->pageid, 'usermodified' => $USER->id, 'phraseid' => $phrase->id]; + + $this->store_answer(self::PHRASE_VOTES_TABLE, $record, $updatecondition, self::ANSWER_COLUMN); + } + + /** + * Get the lastupdated timestamp. + * + * @param int|object $pageorid + * @param bool $ignoreanswers + * @return int + */ + public function get_last_update_time(int|object $pageorid, bool $ignoreanswers = false): int { + global $DB; + + $page = $pageorid; + if (!is_object($page)) { + $page = $this->get_page($page); + } + + $instance = self::get_instance_by_pageid($page->id); + $cm = self::get_cm_by_instance($instance); + + // We only want to deliver results if the teacher allowed to view it. + if ( + empty($this->get_tool_config($page->id, 'showonteacherpermission')) + && !has_capability('mod/mootimeter:moderator', \context_module::instance($cm->id)) + ) { + return 0; + } + + $mostrecenttimeanswer = 0; + if (!$ignoreanswers) { + // It's important, that the default value is NOT null, but 0 instead. Otherwise GREATEST will return null anyway. + $sql = 'SELECT SUM(GREATEST(timecreated, timemodified)) as time FROM ' + . '{' . $this->get_answer_table() . '} WHERE pageid = :pageid'; + $mostrecenttimeanswer = $DB->get_field_sql($sql, ['pageid' => $page->id]); + } + + // It's important, that the default value is NOT null, but 0 instead. Otherwise GREATEST will return null anyway. + $mostrecenttimephrases = 0; + $sql = 'SELECT SUM(GREATEST(timecreated, timemodified)) as time FROM ' + . '{' . $this->get_phrases_table() . '} WHERE pageid = :pageid'; + $mostrecenttimephrases = $DB->get_field_sql($sql, ['pageid' => $page->id]); + + return $mostrecenttimeanswer + $mostrecenttimephrases; + } + + /** + * Handels inplace_edit. + * + * @param string $itemtype + * @param string $itemid + * @param mixed $newvalue + * @return mixed + */ + public function handle_inplace_edit(string $itemtype, string $itemid, mixed $newvalue) { + if ($itemtype === 'editanswer') { + return \mootimetertool_ranking\local\inplace_edit_answer::update($itemid, $newvalue); + } + } + + /** + * Get all parameters that are necessary for rendering the tools view. + * + * @param object $page + * @return array + */ + public function get_renderer_params(object $page) { + + switch($this->get_tool_config($page, 'phase')) { + case 2: + $params = $this->get_renderer_params_phase_two($page); + break; + case 3: + $params = $this->get_renderer_params_phase_three($page); + break; + default: + $params = $this->get_renderer_params_phase_one($page); + break; + } + + return $params; + } + + /** + * Get the rendering parameters for phase one. + * + * @param object $page + * @return array + */ + public function get_renderer_params_phase_two(object $page) { + global $USER; + + $params = $this->get_indicator_params($page); + $params['pageid'] = $page->id; + + $params['heading_phase_2'] = $this->get_tool_config($page, 'heading_phase_2'); + + // Parameter for initializing Badges. + $params["toolname"] = ['pill' => get_string("pluginname", "mootimetertool_" . $page->tool)]; + $params["phase"] = [ + 'pill' => get_string("phase_two_heading", "mootimetertool_" . $page->tool), + 'additional_class' => 'mtmt-ranking-phase-1', + ]; + + $params['template'] = "mootimetertool_" . $page->tool . "/view_phase_2"; + + $phrases = $this->get_user_answers( + self::PHRASES_TABLE, + $page->id, + self::PHRASE_COLUMN, + $USER->id, + self::CACHEIDENTIFIER_PHRASES + ); + + $params["phrases"] = array_values(array_map(function ($phrase) use ($page) { + + // Define yes toggle. + $datasettoggleyes = [ + 'data-pageid="' . $page->id . '"', + 'data-phraseid="' . $phrase->id . '"', + 'data-togglevalue="' . self::PHRASE_STATE_ACCEPTED . '"', + ]; + + // Define no toggle. + $datasettoggleno = [ + 'data-pageid="' . $page->id . '"', + 'data-phraseid="' . $phrase->id . '"', + 'data-togglevalue="' . self::PHRASE_STATE_DECLINED . '"', + ]; + + // Add delete button to answer. + $dataseticontrash = [ + 'data-ajaxmethode = "mod_mootimeter_delete_single_answer"', + 'data-pageid="' . $page->id . '"', + 'data-answerid="' . $phrase->id . '"', + 'data-confirmationtitlestr="' . get_string('delete_single_answer_dialog_title', 'mod_mootimeter') . '"', + 'data-confirmationquestionstr="' . get_string('delete_single_answer_dialog_question', 'mod_mootimeter') . '"', + 'data-confirmationtype="DELETE_CANCEL"', + ]; + + return [ + 'phrase' => $phrase->phrase, + 'deletebutton' => [ + 'id' => 'mtmt_delete_answer_' . $phrase->id, + 'iconid' => 'mtmt_delete_iconid_' . $phrase->id, + 'dataset' => implode(" ", $dataseticontrash), + ], + 'radioname' => 'mtmt_toggle_' . $phrase->id, + 'yes_id' => 'mtmt_toggle_yes_' . $phrase->id, + 'yes_active' => (isset($phrase->state) && $phrase->state == self::PHRASE_STATE_ACCEPTED) ? 'active' : '', + 'yes_checked' => (isset($phrase->state) && $phrase->state == self::PHRASE_STATE_ACCEPTED) ? 'checked' : '', + 'yes_dataset' => implode(" ", $datasettoggleyes), + 'no_id' => 'mtmt_toggle_no_' . $phrase->id, + 'no_active' => (isset($phrase->state) && $phrase->state == self::PHRASE_STATE_DECLINED) ? 'active' : '', + 'no_checked' => (isset($phrase->state) && $phrase->state == self::PHRASE_STATE_DECLINED) ? 'checked' : '', + 'no_dataset' => implode(" ", $datasettoggleno), + + ]; + }, $phrases)); + + return $params; + } + + /** + * Get the rendering parameters for phase one. + * + * @param object $page + * @return array + */ + public function get_renderer_params_phase_three(object $page) { + global $USER; + + $params = $this->get_indicator_params($page); + + $params['pageid'] = $page->id; + + $params['heading_phase_3'] = $this->get_tool_config($page, 'heading_phase_3'); + + // Parameter for initializing Badges. + $params["toolname"] = ['pill' => get_string("pluginname", "mootimetertool_" . $page->tool)]; + $params["phase"] = [ + 'pill' => get_string("phase_three_heading", "mootimetertool_" . $page->tool), + 'additional_class' => 'mtmt-ranking-phase-1', + ]; + + $params['template'] = "mootimetertool_" . $page->tool . "/view_phase_3_thumbs"; + + $params["phrases"] = array_values(array_map(function ($phrase) use ($page) { + global $USER; + + // Add thumbs up button. + $dataseticonthumbup = [ + 'data-ajaxmethode = "mootimetertool_ranking_vote_phrase_thumb"', + 'data-pageid="' . $page->id . '"', + 'data-thumbvalue="' . self::VOTE_VALUE_THUMBUP. '"', + 'data-phraseid="' . $phrase->id . '"', + ]; + + // Add thumbs up button. + $dataseticonthumbdown = [ + 'data-ajaxmethode = "mootimetertool_ranking_vote_phrase_thumb"', + 'data-pageid="' . $page->id . '"', + 'data-thumbvalue="' . self::VOTE_VALUE_THUMBDOWN . '"', + 'data-phraseid="' . $phrase->id . '"', + ]; + + $uservote = $this->get_user_phrase_vote($USER->id, $phrase->id); + $additionalclassthumbup = ''; + $additionalclassthumbdown = ''; + if (!empty($uservote->votevalue)) { + if ($uservote->votevalue > 0) { + $additionalclassthumbup = 'active'; + } else if ($uservote->votevalue < 0) { + $additionalclassthumbdown = 'active'; + } + } + + return [ + 'phrase' => $phrase->phrase, + + 'thumbup' => [ + 'id' => 'mtmt_ranking_thumbup_' . $phrase->id, + 'faicon' => 'fa-thumbs-up', + 'dataset' => implode(' ', $dataseticonthumbup), + 'additional_class' => 'mtmt_ranking_thumbup ' . $additionalclassthumbup, + ], + 'thumbdown' => [ + 'id' => 'mtmt_ranking_thumbdown_' . $phrase->id, + 'faicon' => 'fa-thumbs-down', + 'dataset' => implode(" ", $dataseticonthumbdown), + 'additional_class' => 'mtmt_ranking_thumbdown ' . $additionalclassthumbdown, + ], + ]; + }, $this->get_user_answers(self::PHRASES_TABLE, $page->id, self::PHRASE_COLUMN, $USER->id, self::CACHEIDENTIFIER_PHRASES))); + + return $params; + } + + + /** + * Get the rendering parameters for phase one. + * + * @param object $page + * @return array + */ + public function get_renderer_params_phase_one(object $page) { + global $USER; + + $params = $this->get_indicator_params($page); + $params['pageid'] = $page->id; + + $params['heading_phase_1'] = $this->get_tool_config($page, 'heading_phase_1'); + + // Parameter for initializing Badges. + $params["toolname"] = ['pill' => get_string("pluginname", "mootimetertool_" . $page->tool)]; + + $params["phase"] = [ + 'pill' => get_string("phase_one_heading", "mootimetertool_" . $page->tool), + 'additional_class' => 'mtmt-ranking-phase-1', + ]; + + $params['template'] = "mootimetertool_" . $page->tool . "/view_phase_1"; + + $params["phrases"] = array_values(array_map(function ($element) use ($page) { + + // Add delete button to answer. + $dataseticontrash = [ + 'data-ajaxmethode = "mod_mootimeter_delete_single_answer"', + 'data-pageid="' . $page->id . '"', + 'data-answerid="' . $element->id . '"', + 'data-confirmationtitlestr="' . get_string('delete_single_answer_dialog_title', 'mod_mootimeter') . '"', + 'data-confirmationquestionstr="' . get_string('delete_single_answer_dialog_question', 'mod_mootimeter') . '"', + 'data-confirmationtype="DELETE_CANCEL"', + ]; + + return [ + 'pill' => $element->phrase, + 'additional_class' => 'mootimeter-pill-inline', + 'deletebutton' => [ + 'id' => 'mtmt_delete_answer_' . $element->id, + 'iconid' => 'mtmt_delete_iconid_' . $element->id, + 'dataset' => implode(" ", $dataseticontrash), + ], + ]; + }, $this->get_user_answers(self::PHRASES_TABLE, $page->id, self::PHRASE_COLUMN, $USER->id))); + + $params['input_answer'] = [ + 'mtm-input-id' => 'mootimeter_type_answer', + 'mtm-input-name' => 'answer', + 'mtm-button-id' => 'mootimeter_enter_answer', + 'dataset' => 'data-pageid="' . $page->id . '"', + 'autofocus' => true, + 'additional_class' => 'mtmt-wc-answerinput', + ]; + + $params['button_answer'] = [ + 'mtm-button-id' => 'mootimeter_enter_answer', + 'mtm-button-text' => 'Senden', + ]; + + return $params; + } + + /** + * Get the indicatior params. + * + * @param object $page + * @return array + */ + public function get_indicator_params(object $page): array { + $datasetphase1 = [ + 'data-ajaxmethode="mod_mootimeter_store_setting"', + 'data-value="1"', + 'data-name="phase"', + 'data-reload="1"', + ]; + $datasetphase2 = [ + 'data-ajaxmethode="mod_mootimeter_store_setting"', + 'data-value="2"', + 'data-name="phase"', + 'data-reload="1"', + ]; + $datasetphase3 = [ + 'data-ajaxmethode="mod_mootimeter_store_setting"', + 'data-value="3"', + 'data-name="phase"', + 'data-reload="1"', + ]; + + $params = []; + + $params['indicator_bar'] = [ + 'phase_1' => [ + 'active' => ($this->get_tool_config($page, 'phase') == 1) ? ' active' : '', + 'id' => 'mtm_indicator_phase_1', + 'value' => 1, + 'dataset' => join(" ", $datasetphase1), + ], + 'phase_2' => [ + 'active' => ($this->get_tool_config($page, 'phase') == 2) ? ' active' : '', + 'id' => 'mtm_indicator_phase_2', + 'value' => 2, + 'dataset' => join(" ", $datasetphase2), + ], + 'phase_3' => [ + 'active' => ($this->get_tool_config($page, 'phase') == 3) ? ' active' : '', + 'id' => 'mtm_indicator_phase_3', + 'value' => 3, + 'dataset' => join(" ", $datasetphase3), + ], + ]; + + return $params; + } + + /** + * Get the params for settings column. + * + * @param object $page + * @param array $params + * @return array + */ + public function get_col_settings_tool_params(object $page, array $params = []) { + global $USER; + + $instance = self::get_instance_by_pageid($page->id); + $cm = self::get_cm_by_instance($instance); + if (!has_capability('mod/mootimeter:moderator', \context_module::instance($cm->id)) || empty($USER->editing)) { + return []; + } + + $params['template'] = 'mootimetertool_ranking/view_settings'; + + $params['heading_phase_1'] = [ + 'title' => get_string('settings_heading_phase', 'mootimetertool_ranking'), + 'mtm-input-id' => 'mtm_input_heading_phase_1', + 'mtm-input-value' => s(self::get_tool_config($page, 'heading_phase_1')), + 'mtm-input-placeholder' => get_string('enter_task', 'mootimetertool_ranking'), + 'mtm-input-name' => "heading_phase_1", + 'pageid' => $page->id, + 'ajaxmethode' => "mod_mootimeter_store_setting", + ]; + $params['heading_phase_2'] = [ + 'title' => get_string('settings_heading_phase', 'mootimetertool_ranking'), + 'mtm-input-id' => 'mtm_input_heading_phase_2', + 'mtm-input-value' => s(self::get_tool_config($page, 'heading_phase_2')), + 'mtm-input-placeholder' => get_string('enter_task', 'mootimetertool_ranking'), + 'mtm-input-name' => "heading_phase_2", + 'pageid' => $page->id, + 'ajaxmethode' => "mod_mootimeter_store_setting", + ]; + $params['heading_phase_3'] = [ + 'title' => get_string('settings_heading_phase', 'mootimetertool_ranking'), + 'mtm-input-id' => 'mtm_input_heading_phase_3', + 'mtm-input-value' => s(self::get_tool_config($page, 'heading_phase_3')), + 'mtm-input-placeholder' => get_string('enter_task', 'mootimetertool_ranking'), + 'mtm-input-name' => "heading_phase_3", + 'pageid' => $page->id, + 'ajaxmethode' => "mod_mootimeter_store_setting", + ]; + + $params['maxvotesperuser'] = [ + 'title' => get_string('votes_max_number', 'mootimetertool_ranking'), + 'additional_class' => 'mootimeter_settings_selector', + 'id' => "maxvotesperuser", + 'name' => "maxvotesperuser", + 'min' => 0, + 'pageid' => $page->id, + 'ajaxmethode' => "mod_mootimeter_store_setting", + 'value' => (empty(self::get_tool_config($page->id, "maxvotesperuser"))) ? '0' : self::get_tool_config( + $page->id, + "maxvotesperuser" + ), + ]; + + $params['maxphrasesperuser'] = [ + 'title' => get_string('phrases_max_number', 'mootimetertool_ranking'), + 'additional_class' => 'mootimeter_settings_selector', + 'id' => "maxphrasesperuser", + 'name' => "maxphrasesperuser", + 'min' => 0, + 'pageid' => $page->id, + 'ajaxmethode' => "mod_mootimeter_store_setting", + 'value' => (empty(self::get_tool_config($page->id, "maxphrasesperuser"))) ? '0' : self::get_tool_config( + $page->id, + "maxphrasesperuser" + ), + ]; + + $params['settings']['anonymousmode'] = [ + 'cb_with_label_id' => 'anonymousmode', + 'pageid' => $page->id, + 'cb_with_label_text' => get_string('anonymousmode', 'mod_mootimeter') + . " " . get_string('anonymousmode_desc', 'mod_mootimeter'), + 'cb_with_label_name' => 'anonymousmode', + 'cb_with_label_additional_class' => 'mootimeter_settings_selector', + 'cb_with_label_ajaxmethod' => "mod_mootimeter_store_setting", + 'cb_with_label_checked' => (\mod_mootimeter\helper::get_tool_config($page, 'anonymousmode') ? "checked" : ""), + ]; + + $answers = $this->get_answers(self::PHRASE_VOTES_TABLE, $page->id, self::PHRASE_COLUMN, self::CACHEIDENTIFIER_VOTES); + // The anonymous mode could not be changed if, there are any answers already given. + if (!empty($answers)) { + $params['settings']['anonymousmode']['cb_with_label_disabled'] = 'disabled'; + unset($params['settings']['anonymousmode']['cb_with_label_ajaxmethod']); + } + + $returnparams['colsettings'] = $params; + return $returnparams; + } + + /** + * Get the answer overview params. + * + * @param object $cm + * @param object $page + * @return array + */ + public function get_tool_answer_overview_params(object $cm, object $page): array { + return []; + } + + + /** + * Get the answer overview. + * + * @param object $cm + * @param object $page + * @return string + */ + public function get_answer_overview(object $cm, object $page): string { + return ""; + global $OUTPUT; + $params = $this->get_answer_overview_params($cm, $page); + return $OUTPUT->render_from_template("mod_mootimeter/answers_overview", $params['pagecontent']); + } + + /** + * Get content menu bar params. + * + * @param object $page + * @param array $params Defaultparams + * @return mixed + * @throws coding_exception + * @throws dml_exception + * @throws moodle_exception + */ + public function get_content_menu_tool_params(object $page, array $params) { + return $params; + } + + /** + * Set the phrase approval state. + * + * @param int $phraseid + * @param int $state + * @return bool + */ + public function set_phrase_approval_state(int $phraseid, int $state): bool { + global $DB; + + $phrase = $DB->get_record(self::PHRASES_TABLE, ['id' => $phraseid]); + + $phrase->state = $state; + + $success = $DB->update_record(self::PHRASES_TABLE, $phrase); + + // Rebuild cache. + $this->clear_caches($phrase->pageid, self::CACHEIDENTIFIER_PHRASES); + $this->get_answers(self::PHRASES_TABLE, $phrase->pageid, self::PHRASE_COLUMN, self::CACHEIDENTIFIER_PHRASES); + + return $success; + } + + /** + * Tool specific cache definitions used in mootimeter core methods + * @return array + */ + public function get_tool_cachedefinition() { + return [ + self::CACHEIDENTIFIER_PHRASES => [ + 'mode' => \cache_store::MODE_APPLICATION, + ], + self::CACHEIDENTIFIER_VOTES => [ + 'mode' => \cache_store::MODE_APPLICATION, + ], + ]; + } +} diff --git a/tools/ranking/db/install.xml b/tools/ranking/db/install.xml new file mode 100644 index 00000000..fe6d7649 --- /dev/null +++ b/tools/ranking/db/install.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+
diff --git a/tools/ranking/db/services.php b/tools/ranking/db/services.php new file mode 100644 index 00000000..e9470d83 --- /dev/null +++ b/tools/ranking/db/services.php @@ -0,0 +1,53 @@ +. + +/** + * External service definitions for mootimetertool_ranking. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$functions = [ + 'mootimetertool_ranking_store_phrase' => [ + 'classname' => 'mootimetertool_ranking\external\store_phrase', + 'methodname' => 'execute', + 'description' => 'Store phrase of mootimetertool_ranking.', + 'type' => 'write', + 'ajax' => true, + 'capabilities' => 'mod/mootimeter:view', + ], + 'mootimetertool_ranking_approve_phrase' => [ + 'classname' => 'mootimetertool_ranking\external\approve_phrase', + 'methodname' => 'execute', + 'description' => 'Approve a phrase of mootimetertool_ranking.', + 'type' => 'write', + 'ajax' => true, + 'capabilities' => 'mod/mootimeter:view', + ], + 'mootimetertool_ranking_vote_phrase_thumb' => [ + 'classname' => 'mootimetertool_ranking\external\vote_phrase_thumb', + 'methodname' => 'execute', + 'description' => 'Vote for a phrase of mootimetertool_ranking.', + 'type' => 'write', + 'ajax' => true, + 'capabilities' => 'mod/mootimeter:view', + ], +]; diff --git a/tools/ranking/db/upgrade.php b/tools/ranking/db/upgrade.php new file mode 100755 index 00000000..6e34929d --- /dev/null +++ b/tools/ranking/db/upgrade.php @@ -0,0 +1,98 @@ +. + +/** + * Plugin upgrade steps are defined here. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + **/ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/upgradelib.php'); + +/** + * Execute mootimetertool_ranking upgrade from the given old version. + * + * @param int $oldversion + * @return bool + */ +function xmldb_mootimetertool_ranking_upgrade($oldversion) { + global $DB; + + $dbman = $DB->get_manager(); + + if ($oldversion < 2024030601) { + + mootimetertool_ranking_create_tables(); + + // Ranking savepoint reached. + upgrade_plugin_savepoint(true, 2024030601, 'mootimetertool', 'ranking'); + } + + if ($oldversion < 2024030602) { + + // Define field pageid to be added to mootimetertool_ranking_phrases. + $table = new xmldb_table('mootimetertool_ranking_phrases'); + $field = new xmldb_field('pageid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'usermodified'); + + // Conditionally launch add field pageid. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Ranking savepoint reached. + upgrade_plugin_savepoint(true, 2024030602, 'mootimetertool', 'ranking'); + } + + if ($oldversion < 2024030606) { + // Ranking savepoint reached. + upgrade_plugin_savepoint(true, 2024030606, 'mootimetertool', 'ranking'); + } + + if ($oldversion < 2024030607) { + + // Define field pageid to be added to mootimetertool_ranking_votes. + $table = new xmldb_table('mootimetertool_ranking_votes'); + $field = new xmldb_field('pageid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'usermodified'); + + // Conditionally launch add field pageid. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Ranking savepoint reached. + upgrade_plugin_savepoint(true, 2024030607, 'mootimetertool', 'ranking'); + } + + if ($oldversion < 2024030609) { + + // Define field timemodified to be added to mootimetertool_ranking_votes. + $table = new xmldb_table('mootimetertool_ranking_votes'); + $field = new xmldb_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'timecreated'); + + // Conditionally launch add field timemodified. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Ranking savepoint reached. + upgrade_plugin_savepoint(true, 2024030609, 'mootimetertool', 'ranking'); + } +} diff --git a/tools/ranking/db/upgradelib.php b/tools/ranking/db/upgradelib.php new file mode 100644 index 00000000..3a7b4505 --- /dev/null +++ b/tools/ranking/db/upgradelib.php @@ -0,0 +1,93 @@ +. + +/** + * Plugin upgrade helper functions are defined here. + * + * @package mootimetertool_ranking + * @category upgrade + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + **/ + +/** + * Helper function used by the upgrade.php file. + */ +function mod_mootimeter_helper_function() { + global $DB; + + // Please note: you can only use raw low level database access here. + // Avoid Moodle API calls in upgrade steps. + // + // For more information please read {@link https://docs.moodle.org/dev/Upgrade_API}. +} + +/** + * Create ranking tabels + * @return void + * @throws coding_exception + * @throws ddl_exception + * @throws ddl_change_structure_exception + */ +function mootimetertool_ranking_create_tables() { + global $DB; + + $dbman = $DB->get_manager(); + + // Define table mootimetertool_ranking_phrases to be created. + $table = new xmldb_table('mootimetertool_ranking_phrases'); + + // Adding fields to table mootimetertool_ranking_phrases. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('pageid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('phrase', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('state', XMLDB_TYPE_INTEGER, '2', null, null, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table mootimetertool_ranking_phrases. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); + + // Conditionally launch create table for mootimetertool_ranking_phrases. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define table mootimetertool_ranking_votes to be created. + $table = new xmldb_table('mootimetertool_ranking_votes'); + + // Adding fields to table mootimetertool_ranking_votes. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('pageid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('phraseid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('votevalue', XMLDB_TYPE_INTEGER, '5', null, null, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + + // Adding keys to table mootimetertool_ranking_votes. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); + + // Conditionally launch create table for mootimetertool_ranking_votes. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + +} diff --git a/tools/ranking/lang/de/mootimetertool_ranking.php b/tools/ranking/lang/de/mootimetertool_ranking.php new file mode 100644 index 00000000..17ee7996 --- /dev/null +++ b/tools/ranking/lang/de/mootimetertool_ranking.php @@ -0,0 +1,38 @@ +. + +/** + * Plugin strings are defined here. + * + * @package mootimetertool_ranking + * @category string + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$string['pluginname'] = 'Ranking'; +$string['tool_description_short'] = "Bewerten von Aussagen"; +$string['error_empty_phrases'] = 'Es gibt noch keine Aussagen.'; +$string['phase_one_heading'] = 'Phase 1: Aussagen sammeln'; +$string['phase_two_heading'] = 'Phase 2: Aussagen freigeben'; +$string['phase_three_heading'] = 'Phase 3: Abstimmung'; +$string['phrase'] = 'Aussage'; +$string['visibility'] = 'Sichtbarkeit'; +$string['phases'] = 'Phasen'; +$string['approved'] = 'Freigegeben für Phase 3'; diff --git a/tools/ranking/lang/en/mootimetertool_ranking.php b/tools/ranking/lang/en/mootimetertool_ranking.php new file mode 100644 index 00000000..e53be771 --- /dev/null +++ b/tools/ranking/lang/en/mootimetertool_ranking.php @@ -0,0 +1,43 @@ +. + +/** + * Plugin strings are defined here. + * + * @package mootimetertool_ranking + * @category string + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$string['pluginname'] = 'Ranking'; +$string['tool_description_short'] = "Vote for phrases"; +$string['error_empty_phrases'] = 'There are no phrases yet.'; +$string['phase_one_heading'] = 'Phase 1: Collect phrases'; +$string['phase_two_heading'] = 'Phase 2: Review phrases'; +$string['phase_three_heading'] = 'Phase 3: Ranking'; +$string['phrase'] = 'Phrase'; +$string['visibility'] = 'Visibility'; +$string['phases'] = 'Phases'; +$string['phase'] = 'Phase {$a}'; +$string['approved'] = 'Approved for phase 3'; +$string['enter_task'] = 'Please enter a heading or a task'; +$string['settings_heading_phase'] = 'Task or heading'; +$string['phrases_max_number'] = 'Maximum number of phrases that were accepted per participant (0: unlimited)'; +$string['votes_max_number'] = 'Maximum number of votes that were accepted per participant (0: unlimited)'; diff --git a/tools/ranking/pix/ranking-white.svg b/tools/ranking/pix/ranking-white.svg new file mode 100644 index 00000000..2a3f1441 --- /dev/null +++ b/tools/ranking/pix/ranking-white.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + diff --git a/tools/ranking/pix/ranking.svg b/tools/ranking/pix/ranking.svg new file mode 100644 index 00000000..53e5d4cb --- /dev/null +++ b/tools/ranking/pix/ranking.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + diff --git a/tools/ranking/styles.css b/tools/ranking/styles.css new file mode 100644 index 00000000..ada7943a --- /dev/null +++ b/tools/ranking/styles.css @@ -0,0 +1,130 @@ +:root { + --timeline-background: #e8e8ea +} + +.mtmt-ranking-flex-row { + flex-direction: row !important; + gap: 0 !important; +} + +.mtmt-ranking-phase { + background-color: var(--white); + color: var(--black); + margin-left: auto; + margin-top: -10px; +} + +.mtmt_ranking_phase_2, +.mtmt_ranking_phase_3 { + height: auto !important; + overflow: visible !important; +} + +.mtmt-ranking-green.active { + background-color: var(--success) !important; +} + +.mtmt-ranking-red.active { + background-color: var(--danger) !important; +} + +.mtmt_ranking_phrase_box { + padding: 20px 10px; + border: 1px solid var(--secondary); + border-radius: 10px; + margin-bottom: 10px; + /* position: relative; */ +} + +.mtmt_ranking_thumbup, +.mtmt_ranking_thumbdown { + color: var(--secondary); + cursor: pointer; +} + +.mtmt_ranking_thumbup.active, +.mtmt_ranking_thumbdown.active { + color: var(--primary); + cursor: pointer; +} + +.mtmt_ranking_thumbup:hover, +.mtmt_ranking_thumbdown:hover { + color: var(--primary); +} + +.mtmt_ranking_thumbup.active:hover, +.mtmt_ranking_thumbdown.active:hover { + color: var(--secondary); +} + +/* +=== Timeline indicators - START +*/ + +.mtmt_ranking_timeline { + width: 120px; + height: 10px; + margin: 20px; + background: var(--timeline-background); + position: relative; +} + +.mtmt_ranking_indicators { + position: relative; +} + +.mtmt_ranking_indicator-1, +.mtmt_ranking_indicator-2, +.mtmt_ranking_indicator-3 { + border: 1px var(--timeline-background); + background: var(--timeline-background); + border-radius: 50%; + width: 30px; + height: 30px; + top: -11px; + position: absolute; + font-weight: 900; + text-align: center; + padding: 1px; + cursor: pointer; +} + +.mtmt_ranking_indicator-1 { + left: -7px; +} + +.mtmt_ranking_indicator-2 { + left: 53px; +} + +.mtmt_ranking_indicator-3 { + left: 113px; +} + +.mtmt_ranking_indicator-1:hover, +.mtmt_ranking_indicator-2:hover, +.mtmt_ranking_indicator-3:hover { + background: var(--primary); + border: 1px solid var(--primary); + color: var(--white); +} + +.mtmt_ranking_indicator-1.active, +.mtmt_ranking_indicator-2.active, +.mtmt_ranking_indicator-3.active { + border: 1px var(--primary); + background: var(--primary); + color: var(--white) +} + +.mtmt_ranking_indicator-1.active:hover, +.mtmt_ranking_indicator-2.active:hover, +.mtmt_ranking_indicator-3.active:hover { + background: var(--timeline-background); + color: var(--black) +} + +/* +=== Timeline indicators - END +*/ diff --git a/tools/ranking/templates/snippet_phase_selector.mustache b/tools/ranking/templates/snippet_phase_selector.mustache new file mode 100644 index 00000000..f12cfa91 --- /dev/null +++ b/tools/ranking/templates/snippet_phase_selector.mustache @@ -0,0 +1,31 @@ +
+
+
+ + {{#phase_1}} +
{{value}}
+ {{#js}} + require(['mod_mootimeter/handle_clicked_element_use_dataset'], (module) => module.init("{{id}}_{{uniqid}}")); + {{/js}} + {{/phase_1}} + + + {{#phase_2}} +
{{value}}
+ {{#js}} + require(['mod_mootimeter/handle_clicked_element_use_dataset'], (module) => module.init("{{id}}_{{uniqid}}")); + {{/js}} + {{/phase_2}} + + + {{#phase_3}} +
{{value}}
+ {{#js}} + require(['mod_mootimeter/handle_clicked_element_use_dataset'], (module) => module.init("{{id}}_{{uniqid}}")); + {{/js}} + {{/phase_3}} + +
+
+
+ diff --git a/tools/ranking/templates/snippet_toggle_yes_no.mustache b/tools/ranking/templates/snippet_toggle_yes_no.mustache new file mode 100644 index 00000000..49bf68a0 --- /dev/null +++ b/tools/ranking/templates/snippet_toggle_yes_no.mustache @@ -0,0 +1,12 @@ +
+ + +
+ +{{#js}} +require(['mootimetertool_ranking/approve_phrase'], (module) => module.init("{{yes_id}}_{{uniqid}}", "{{no_id}}_{{uniqid}}", "{{radioname}}")); +{{/js}} diff --git a/tools/ranking/templates/view_overview.mustache b/tools/ranking/templates/view_overview.mustache new file mode 100644 index 00000000..4181d943 --- /dev/null +++ b/tools/ranking/templates/view_overview.mustache @@ -0,0 +1,34 @@ + + + + + + + + + + + + {{#answers}} + + + + + + + + {{/answers}} + +
#{{#str}} name {{/str}}{{#str}} answer {{/str}}{{#str}} date {{/str}} / {{#str}} time {{/str}}{{#str}} options {{/str}}
{{nbr}}{{userfullname}} + {{#answer}} + {{> core/inplace_editable}} + {{/answer}} + {{datetime}} + {{#options}} + {{> mod_mootimeter/elements/snippet_button_icon_only_rounded_with_js}} + {{/options}} +
+ +{{#js}} +require(['mod_mootimeter/reload_page_on_state_change'], (module) => module.init("mtmt_wordcloud_overview_{{uniqid}}")); +{{/js}} diff --git a/tools/ranking/templates/view_phase_1.mustache b/tools/ranking/templates/view_phase_1.mustache new file mode 100644 index 00000000..ccf38597 --- /dev/null +++ b/tools/ranking/templates/view_phase_1.mustache @@ -0,0 +1,51 @@ +{{#withwrapper}} +
+ {{/withwrapper}} + +
+ + {{{error}}} + +
+ + {{#toolname}} + {{> mod_mootimeter/elements/snippet_pill}} + {{/toolname}} + + {{#indicator_bar}} + {{> mootimetertool_ranking/snippet_phase_selector}} + {{/indicator_bar}} + +
+

{{{heading_phase_1}}}

+
+ +
+ {{#phrases}} + {{> mod_mootimeter/elements/snippet_pill}} + {{/phrases}} +
+ + {{#input_answer}} + {{> mod_mootimeter/elements/snippet_input}} + {{#js}} + require(['mootimetertool_ranking/store_phrase'], (module) => module.init( + "{{mtm-input-id}}_{{uniqid}}", + "{{mtm-button-id}}_{{uniqid}}" + )); + {{/js}} + {{/input_answer}} + + {{#button_answer}} + {{> mod_mootimeter/elements/snippet_button_full}} + {{/button_answer}} + + +
+ {{#withwrapper}} +
+{{/withwrapper}} + +{{#js}} +require(['mod_mootimeter/reload_page_on_state_change'], (module) => module.init("mtmt_ranking_overview_{{uniqid}}")); +{{/js}} diff --git a/tools/ranking/templates/view_phase_2.mustache b/tools/ranking/templates/view_phase_2.mustache new file mode 100644 index 00000000..085dc504 --- /dev/null +++ b/tools/ranking/templates/view_phase_2.mustache @@ -0,0 +1,65 @@ +{{#withwrapper}} +
+ {{/withwrapper}} + +
+ + {{{error}}} + +
+ + {{#toolname}} + {{> mod_mootimeter/elements/snippet_pill}} + {{/toolname}} + + {{#indicator_bar}} + {{> mootimetertool_ranking/snippet_phase_selector}} + {{/indicator_bar}} + +
+

{{{heading_phase_2}}}

+
+ +
+ + + + + + + + {{#phrases}} + + + + + + {{/phrases}} +
{{#str}} phrase, mootimetertool_ranking {{/str}}{{#str}} approved, mootimetertool_ranking {{/str}}
{{phrase}}{{> mootimetertool_ranking/snippet_toggle_yes_no}} + {{#deletebutton}}{{>mod_mootimeter/elements/snippet_delete_cross}}{{/deletebutton}} +
+
+ + {{#input_answer}} + {{> mod_mootimeter/elements/snippet_input}} + {{#js}} + require(['mootimetertool_ranking/store_phrase'], (module) => module.init( + "{{mtm-input-id}}_{{uniqid}}", + "{{mtm-button-id}}_{{uniqid}}" + )); + {{/js}} + {{/input_answer}} + + {{#button_answer}} + {{> mod_mootimeter/elements/snippet_button_full}} + {{/button_answer}} + + +
+ {{#withwrapper}} +
+{{/withwrapper}} + +{{#js}} +require(['mod_mootimeter/reload_page_on_state_change'], (module) => module.init("mtmt_ranking_refresh_{{uniqid}}")); +{{/js}} diff --git a/tools/ranking/templates/view_phase_3_thumbs.mustache b/tools/ranking/templates/view_phase_3_thumbs.mustache new file mode 100644 index 00000000..b62670be --- /dev/null +++ b/tools/ranking/templates/view_phase_3_thumbs.mustache @@ -0,0 +1,62 @@ +{{#withwrapper}} +
+ {{/withwrapper}} + +
+ + {{{error}}} + +
+ + {{#toolname}} + {{> mod_mootimeter/elements/snippet_pill}} + {{/toolname}} + + {{#indicator_bar}} + {{> mootimetertool_ranking/snippet_phase_selector}} + {{/indicator_bar}} + +
+

{{{heading_phase_3}}}

+
+ +
+ + {{#phrases}} +
+
{{phrase}}
+
+ {{#thumbup}} + {{> mod_mootimeter/elements/snippet_clickable_icon}} + {{/thumbup}} + {{#thumbdown}} + {{> mod_mootimeter/elements/snippet_clickable_icon}} + {{/thumbdown}} +
+
+ {{/phrases}} +
+ + {{#input_answer}} + {{> mod_mootimeter/elements/snippet_input}} + {{#js}} + require(['mootimetertool_ranking/store_phrase'], (module) => module.init( + "{{mtm-input-id}}_{{uniqid}}", + "{{mtm-button-id}}_{{uniqid}}" + )); + {{/js}} + {{/input_answer}} + + {{#button_answer}} + {{> mod_mootimeter/elements/snippet_button_full}} + {{/button_answer}} + + +
+ {{#withwrapper}} +
+{{/withwrapper}} + +{{#js}} +require(['mod_mootimeter/reload_page_on_state_change'], (module) => module.init("mtmt_ranking_refresh_{{uniqid}}")); +{{/js}} diff --git a/tools/ranking/templates/view_results.mustache b/tools/ranking/templates/view_results.mustache new file mode 100644 index 00000000..79c89217 --- /dev/null +++ b/tools/ranking/templates/view_results.mustache @@ -0,0 +1,15 @@ + + +
+
+ +{{#js}} + require(['mootimetertool_wordcloud/redraw_wordcloud'], (module) => module.init("wordcloudcanvas_{{uniqid}}")); +{{/js}} diff --git a/tools/ranking/templates/view_settings.mustache b/tools/ranking/templates/view_settings.mustache new file mode 100644 index 00000000..d4c96bac --- /dev/null +++ b/tools/ranking/templates/view_settings.mustache @@ -0,0 +1,51 @@ +
+ + {{> mod_mootimeter/elements/snippet_settings_col_header}} + + {{#settings}} + + {{> mod_mootimeter/elements/snippet_divider}} + +

{{#str}} phase , mootimetertool_ranking, 1 {{/str}}

+

{{#str}} settings_heading_phase, mootimetertool_ranking{{/str}} + {{#heading_phase_1}} + {{> mod_mootimeter/elements/snippet_input}} + {{/heading_phase_1}} +

+ {{#maxphrasesperuser}} + {{> mod_mootimeter/elements/snippet_number_input}} + {{/maxphrasesperuser}} + + {{> mod_mootimeter/elements/snippet_divider}} + +

{{#str}} phase , mootimetertool_ranking, 2 {{/str}}

+

{{#str}} settings_heading_phase, mootimetertool_ranking{{/str}} + + {{#heading_phase_2}} + {{> mod_mootimeter/elements/snippet_input}} + {{/heading_phase_2}} +

+ + {{> mod_mootimeter/elements/snippet_divider}} + +

{{#str}} phase , mootimetertool_ranking, 3 {{/str}}

+

{{#str}} settings_heading_phase, mootimetertool_ranking{{/str}} + {{#heading_phase_3}} + {{> mod_mootimeter/elements/snippet_input}} + {{/heading_phase_3}} +

+ {{#maxvotesperuser}} + {{> mod_mootimeter/elements/snippet_number_input}} + {{/maxvotesperuser}} + + {{> mod_mootimeter/elements/snippet_divider}} + +

{{#str}} settings {{/str}}

+ + {{#anonymousmode}} + {{> mod_mootimeter/elements/snippet_checkbox_with_label}} + {{/anonymousmode}} + + {{/settings}} + +
diff --git a/tools/ranking/version.php b/tools/ranking/version.php new file mode 100644 index 00000000..7a0ce607 --- /dev/null +++ b/tools/ranking/version.php @@ -0,0 +1,30 @@ +. + +/** + * Plugin version and other meta-data are defined here. + * + * @package mootimetertool_ranking + * @copyright 2024, ISB Bayern + * @author Peter Mayer + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2024030609; +$plugin->requires = 2022111800; +$plugin->component = 'mootimetertool_ranking'; diff --git a/version.php b/version.php index 187423b0..e49150e9 100644 --- a/version.php +++ b/version.php @@ -27,7 +27,7 @@ $plugin->component = 'mod_mootimeter'; $plugin->release = '0.2.0'; -$plugin->version = 2024032800; +$plugin->version = 2024032803; $plugin->requires = 2022112800; $plugin->maturity = MATURITY_ALPHA; $plugin->supported = [403, 404]; // A range of branch numbers of supported moodle versions.