From 3be5954500c8d2b75a55f9f7fe6c7e0bee088b97 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Fri, 21 Oct 2022 19:05:09 +0100 Subject: [PATCH] MDL-76059 forms: graceful early exit for invalid button elements. Ensure login form tries to capture guest button only if it exists. --- lib/form/amd/build/submit.min.js | 2 +- lib/form/amd/build/submit.min.js.map | 2 +- lib/form/amd/src/submit.js | 7 ++++++- lib/templates/loginform.mustache | 4 +++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/form/amd/build/submit.min.js b/lib/form/amd/build/submit.min.js index 65e2e4e6055..4904778b8ff 100644 --- a/lib/form/amd/build/submit.min.js +++ b/lib/form/amd/build/submit.min.js @@ -8,6 +8,6 @@ define("core_form/submit",["exports","core_form/events"],(function(_exports,_eve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.8 */ -let cookieListener=0;const cookieListeningButtons=[];let currentUploadCount=0;const uploadListeningButtons=[];let uploadListenersRegistered=!1;const getCookieName=()=>"moodledownload_"+M.cfg.sesskey,clearDownloadCookie=()=>{document.cookie=encodeURIComponent(getCookieName())+"=deleted; expires="+new Date(0).toUTCString()},checkUploadCount=()=>{currentUploadCount?uploadListeningButtons.forEach((button=>{button.disabled=!0})):uploadListeningButtons.forEach((button=>{button.disabled=!1}))};_exports.init=elementId=>{const button=document.getElementById(elementId);button.disabled||uploadListeningButtons.push(button),uploadListenersRegistered||(document.addEventListener(_events.eventTypes.uploadStarted,(e=>{window.console.log(e.target),currentUploadCount++,checkUploadCount()})),document.addEventListener(_events.eventTypes.uploadCompleted,(e=>{window.console.log(e.target),currentUploadCount--,checkUploadCount()})),uploadListenersRegistered=!0),"off"!==button.form.dataset.doubleSubmitProtection&&button.form.addEventListener("submit",(function(event){const disableAction=function(){event.defaultPrevented||button.disabled||(button.disabled=!0,clearDownloadCookie(),(button=>{cookieListeningButtons.push(button),cookieListener||(cookieListener=setInterval((()=>{2==document.cookie.split(getCookieName()+"=").length&&(clearDownloadCookie(),clearInterval(cookieListener),cookieListener=0,cookieListeningButtons.forEach((button=>{button.disabled=!1})))}),500))})(button))};window.addEventListener("beforeunload",disableAction),setTimeout((function(){window.removeEventListener("beforeunload",disableAction)}),0)}),!1)}})); +let cookieListener=0;const cookieListeningButtons=[];let currentUploadCount=0;const uploadListeningButtons=[];let uploadListenersRegistered=!1;const getCookieName=()=>"moodledownload_"+M.cfg.sesskey,clearDownloadCookie=()=>{document.cookie=encodeURIComponent(getCookieName())+"=deleted; expires="+new Date(0).toUTCString()},checkUploadCount=()=>{currentUploadCount?uploadListeningButtons.forEach((button=>{button.disabled=!0})):uploadListeningButtons.forEach((button=>{button.disabled=!1}))};_exports.init=elementId=>{const button=document.getElementById(elementId);null!==button&&(button.disabled||uploadListeningButtons.push(button),uploadListenersRegistered||(document.addEventListener(_events.eventTypes.uploadStarted,(e=>{window.console.log(e.target),currentUploadCount++,checkUploadCount()})),document.addEventListener(_events.eventTypes.uploadCompleted,(e=>{window.console.log(e.target),currentUploadCount--,checkUploadCount()})),uploadListenersRegistered=!0),"off"!==button.form.dataset.doubleSubmitProtection&&button.form.addEventListener("submit",(function(event){const disableAction=function(){event.defaultPrevented||button.disabled||(button.disabled=!0,clearDownloadCookie(),(button=>{cookieListeningButtons.push(button),cookieListener||(cookieListener=setInterval((()=>{2==document.cookie.split(getCookieName()+"=").length&&(clearDownloadCookie(),clearInterval(cookieListener),cookieListener=0,cookieListeningButtons.forEach((button=>{button.disabled=!1})))}),500))})(button))};window.addEventListener("beforeunload",disableAction),setTimeout((function(){window.removeEventListener("beforeunload",disableAction)}),0)}),!1))}})); //# sourceMappingURL=submit.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/submit.min.js.map b/lib/form/amd/build/submit.min.js.map index ba5654212e7..cfd082d404b 100644 --- a/lib/form/amd/build/submit.min.js.map +++ b/lib/form/amd/build/submit.min.js.map @@ -1 +1 @@ -{"version":3,"file":"submit.min.js","sources":["../src/submit.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Submit button JavaScript. All submit buttons will be automatically disabled once the form is\n * submitted, unless that submission results in an error/cancelling the submit.\n *\n * @module core_form/submit\n * @copyright 2019 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.8\n */\n\nimport {eventTypes} from 'core_form/events';\n\n/** @property {number} ID for setInterval used when polling for download cookie */\nlet cookieListener = 0;\n\n/** @property {Array} Array of buttons that need re-enabling if we get a download cookie */\nconst cookieListeningButtons = [];\n\n/** @property {number} Number of files uploading. */\nlet currentUploadCount = 0;\n\n/** @property {Array} Array of buttons that need re-enabling if we get a upload process. */\nconst uploadListeningButtons = [];\n\n/** @property {Boolean} Is upload listeners registered? */\nlet uploadListenersRegistered = false;\n\n/**\n * Listens in case a download cookie is provided.\n *\n * This function is used to detect file downloads. If there is a file download then we get a\n * beforeunload event, but the page is never unloaded and when the file download completes we\n * should re-enable the buttons. We detect this by watching for a specific cookie.\n *\n * PHP function \\core_form\\util::form_download_complete() can be used to send this cookie.\n *\n * @param {HTMLElement} button Button to re-enable\n */\nconst listenForDownloadCookie = (button) => {\n cookieListeningButtons.push(button);\n if (!cookieListener) {\n cookieListener = setInterval(() => {\n // Look for cookie.\n const parts = document.cookie.split(getCookieName() + '=');\n if (parts.length == 2) {\n // We found the cookie, so the file is ready. Expire the cookie and cancel polling.\n clearDownloadCookie();\n clearInterval(cookieListener);\n cookieListener = 0;\n\n // Re-enable all the buttons.\n cookieListeningButtons.forEach((button) => {\n button.disabled = false;\n });\n }\n }, 500);\n }\n};\n\n/**\n * Gets a unique name for the download cookie.\n *\n * @returns {string} Cookie name\n */\nconst getCookieName = () => {\n return 'moodledownload_' + M.cfg.sesskey;\n};\n\n/**\n * Clears the download cookie if there is one.\n */\nconst clearDownloadCookie = () => {\n document.cookie = encodeURIComponent(getCookieName()) + '=deleted; expires=' + new Date(0).toUTCString();\n};\n\n/**\n * Enable submit buttons when all files are uploaded.\n */\nconst checkUploadCount = () => {\n if (currentUploadCount) {\n uploadListeningButtons.forEach(button => {\n button.disabled = true;\n });\n } else {\n uploadListeningButtons.forEach(button => {\n button.disabled = false;\n });\n }\n};\n\n/**\n * Initialises submit buttons.\n *\n * @param {String} elementId Form element\n * @listens event:uploadStarted\n * @listens event:uploadCompleted\n */\nexport const init = (elementId) => {\n const button = document.getElementById(elementId);\n // If buttons are disabled by default, we do not enable them when file upload completed event is fired.\n if (!button.disabled) {\n uploadListeningButtons.push(button);\n }\n\n if (!uploadListenersRegistered) {\n // Add event listener for file upload start.\n document.addEventListener(eventTypes.uploadStarted, e => {\n window.console.log(e.target); // This will be the element/section where the file is uploaded to.\n currentUploadCount++;\n checkUploadCount();\n });\n\n // Add event listener for file upload complete.\n document.addEventListener(eventTypes.uploadCompleted, e => {\n window.console.log(e.target); // This will be the element/section where the file is uploaded to.\n currentUploadCount--;\n checkUploadCount();\n });\n uploadListenersRegistered = true;\n }\n\n // If the form has double submit protection disabled, do nothing.\n if (button.form.dataset.doubleSubmitProtection === 'off') {\n return;\n }\n button.form.addEventListener('submit', function(event) {\n // Only disable it if the browser is really going to another page as a result of the\n // submit.\n const disableAction = function() {\n // If the submit was cancelled, or the button is already disabled, don't do anything.\n if (event.defaultPrevented || button.disabled) {\n return;\n }\n\n button.disabled = true;\n clearDownloadCookie();\n listenForDownloadCookie(button);\n };\n window.addEventListener('beforeunload', disableAction);\n // If there is no beforeunload event as a result of this form submit, then the form\n // submit must have been cancelled, so don't disable the button if the page is\n // unloaded later.\n setTimeout(function() {\n window.removeEventListener('beforeunload', disableAction);\n }, 0);\n }, false);\n};\n"],"names":["cookieListener","cookieListeningButtons","currentUploadCount","uploadListeningButtons","uploadListenersRegistered","getCookieName","M","cfg","sesskey","clearDownloadCookie","document","cookie","encodeURIComponent","Date","toUTCString","checkUploadCount","forEach","button","disabled","elementId","getElementById","push","addEventListener","eventTypes","uploadStarted","e","window","console","log","target","uploadCompleted","form","dataset","doubleSubmitProtection","event","disableAction","defaultPrevented","setInterval","split","length","clearInterval","listenForDownloadCookie","setTimeout","removeEventListener"],"mappings":";;;;;;;;;;IA4BIA,eAAiB,QAGfC,uBAAyB,OAG3BC,mBAAqB,QAGnBC,uBAAyB,OAG3BC,2BAA4B,QAuC1BC,cAAgB,IACX,kBAAoBC,EAAEC,IAAIC,QAM/BC,oBAAsB,KACxBC,SAASC,OAASC,mBAAmBP,iBAAmB,qBAAuB,IAAIQ,KAAK,GAAGC,eAMzFC,iBAAmB,KACjBb,mBACAC,uBAAuBa,SAAQC,SAC3BA,OAAOC,UAAW,KAGtBf,uBAAuBa,SAAQC,SAC3BA,OAAOC,UAAW,oBAYTC,kBACXF,OAASP,SAASU,eAAeD,WAElCF,OAAOC,UACRf,uBAAuBkB,KAAKJ,QAG3Bb,4BAEDM,SAASY,iBAAiBC,mBAAWC,eAAeC,IAChDC,OAAOC,QAAQC,IAAIH,EAAEI,QACrB3B,qBACAa,sBAIJL,SAASY,iBAAiBC,mBAAWO,iBAAiBL,IAClDC,OAAOC,QAAQC,IAAIH,EAAEI,QACrB3B,qBACAa,sBAEJX,2BAA4B,GAImB,QAA/Ca,OAAOc,KAAKC,QAAQC,wBAGxBhB,OAAOc,KAAKT,iBAAiB,UAAU,SAASY,aAGtCC,cAAgB,WAEdD,MAAME,kBAAoBnB,OAAOC,WAIrCD,OAAOC,UAAW,EAClBT,sBAjGqBQ,CAAAA,SAC7BhB,uBAAuBoB,KAAKJ,QACvBjB,iBACDA,eAAiBqC,aAAY,KAGL,GADN3B,SAASC,OAAO2B,MAAMjC,gBAAkB,KAC5CkC,SAEN9B,sBACA+B,cAAcxC,gBACdA,eAAiB,EAGjBC,uBAAuBe,SAASC,SAC5BA,OAAOC,UAAW,QAG3B,OAiFCuB,CAAwBxB,UAE5BS,OAAOJ,iBAAiB,eAAgBa,eAIxCO,YAAW,WACPhB,OAAOiB,oBAAoB,eAAgBR,iBAC5C,MACJ"} \ No newline at end of file +{"version":3,"file":"submit.min.js","sources":["../src/submit.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Submit button JavaScript. All submit buttons will be automatically disabled once the form is\n * submitted, unless that submission results in an error/cancelling the submit.\n *\n * @module core_form/submit\n * @copyright 2019 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.8\n */\n\nimport {eventTypes} from 'core_form/events';\n\n/** @property {number} ID for setInterval used when polling for download cookie */\nlet cookieListener = 0;\n\n/** @property {Array} Array of buttons that need re-enabling if we get a download cookie */\nconst cookieListeningButtons = [];\n\n/** @property {number} Number of files uploading. */\nlet currentUploadCount = 0;\n\n/** @property {Array} Array of buttons that need re-enabling if we get a upload process. */\nconst uploadListeningButtons = [];\n\n/** @property {Boolean} Is upload listeners registered? */\nlet uploadListenersRegistered = false;\n\n/**\n * Listens in case a download cookie is provided.\n *\n * This function is used to detect file downloads. If there is a file download then we get a\n * beforeunload event, but the page is never unloaded and when the file download completes we\n * should re-enable the buttons. We detect this by watching for a specific cookie.\n *\n * PHP function \\core_form\\util::form_download_complete() can be used to send this cookie.\n *\n * @param {HTMLElement} button Button to re-enable\n */\nconst listenForDownloadCookie = (button) => {\n cookieListeningButtons.push(button);\n if (!cookieListener) {\n cookieListener = setInterval(() => {\n // Look for cookie.\n const parts = document.cookie.split(getCookieName() + '=');\n if (parts.length == 2) {\n // We found the cookie, so the file is ready. Expire the cookie and cancel polling.\n clearDownloadCookie();\n clearInterval(cookieListener);\n cookieListener = 0;\n\n // Re-enable all the buttons.\n cookieListeningButtons.forEach((button) => {\n button.disabled = false;\n });\n }\n }, 500);\n }\n};\n\n/**\n * Gets a unique name for the download cookie.\n *\n * @returns {string} Cookie name\n */\nconst getCookieName = () => {\n return 'moodledownload_' + M.cfg.sesskey;\n};\n\n/**\n * Clears the download cookie if there is one.\n */\nconst clearDownloadCookie = () => {\n document.cookie = encodeURIComponent(getCookieName()) + '=deleted; expires=' + new Date(0).toUTCString();\n};\n\n/**\n * Enable submit buttons when all files are uploaded.\n */\nconst checkUploadCount = () => {\n if (currentUploadCount) {\n uploadListeningButtons.forEach(button => {\n button.disabled = true;\n });\n } else {\n uploadListeningButtons.forEach(button => {\n button.disabled = false;\n });\n }\n};\n\n/**\n * Initialises submit buttons.\n *\n * @param {String} elementId Form button element\n * @listens event:uploadStarted\n * @listens event:uploadCompleted\n */\nexport const init = (elementId) => {\n const button = document.getElementById(elementId);\n if (button === null) {\n // Exit early if invalid element id passed.\n return;\n }\n\n // If buttons are disabled by default, we do not enable them when file upload completed event is fired.\n if (!button.disabled) {\n uploadListeningButtons.push(button);\n }\n\n if (!uploadListenersRegistered) {\n // Add event listener for file upload start.\n document.addEventListener(eventTypes.uploadStarted, e => {\n window.console.log(e.target); // This will be the element/section where the file is uploaded to.\n currentUploadCount++;\n checkUploadCount();\n });\n\n // Add event listener for file upload complete.\n document.addEventListener(eventTypes.uploadCompleted, e => {\n window.console.log(e.target); // This will be the element/section where the file is uploaded to.\n currentUploadCount--;\n checkUploadCount();\n });\n uploadListenersRegistered = true;\n }\n\n // If the form has double submit protection disabled, do nothing.\n if (button.form.dataset.doubleSubmitProtection === 'off') {\n return;\n }\n button.form.addEventListener('submit', function(event) {\n // Only disable it if the browser is really going to another page as a result of the\n // submit.\n const disableAction = function() {\n // If the submit was cancelled, or the button is already disabled, don't do anything.\n if (event.defaultPrevented || button.disabled) {\n return;\n }\n\n button.disabled = true;\n clearDownloadCookie();\n listenForDownloadCookie(button);\n };\n window.addEventListener('beforeunload', disableAction);\n // If there is no beforeunload event as a result of this form submit, then the form\n // submit must have been cancelled, so don't disable the button if the page is\n // unloaded later.\n setTimeout(function() {\n window.removeEventListener('beforeunload', disableAction);\n }, 0);\n }, false);\n};\n"],"names":["cookieListener","cookieListeningButtons","currentUploadCount","uploadListeningButtons","uploadListenersRegistered","getCookieName","M","cfg","sesskey","clearDownloadCookie","document","cookie","encodeURIComponent","Date","toUTCString","checkUploadCount","forEach","button","disabled","elementId","getElementById","push","addEventListener","eventTypes","uploadStarted","e","window","console","log","target","uploadCompleted","form","dataset","doubleSubmitProtection","event","disableAction","defaultPrevented","setInterval","split","length","clearInterval","listenForDownloadCookie","setTimeout","removeEventListener"],"mappings":";;;;;;;;;;IA4BIA,eAAiB,QAGfC,uBAAyB,OAG3BC,mBAAqB,QAGnBC,uBAAyB,OAG3BC,2BAA4B,QAuC1BC,cAAgB,IACX,kBAAoBC,EAAEC,IAAIC,QAM/BC,oBAAsB,KACxBC,SAASC,OAASC,mBAAmBP,iBAAmB,qBAAuB,IAAIQ,KAAK,GAAGC,eAMzFC,iBAAmB,KACjBb,mBACAC,uBAAuBa,SAAQC,SAC3BA,OAAOC,UAAW,KAGtBf,uBAAuBa,SAAQC,SAC3BA,OAAOC,UAAW,oBAYTC,kBACXF,OAASP,SAASU,eAAeD,WACxB,OAAXF,SAMCA,OAAOC,UACRf,uBAAuBkB,KAAKJ,QAG3Bb,4BAEDM,SAASY,iBAAiBC,mBAAWC,eAAeC,IAChDC,OAAOC,QAAQC,IAAIH,EAAEI,QACrB3B,qBACAa,sBAIJL,SAASY,iBAAiBC,mBAAWO,iBAAiBL,IAClDC,OAAOC,QAAQC,IAAIH,EAAEI,QACrB3B,qBACAa,sBAEJX,2BAA4B,GAImB,QAA/Ca,OAAOc,KAAKC,QAAQC,wBAGxBhB,OAAOc,KAAKT,iBAAiB,UAAU,SAASY,aAGtCC,cAAgB,WAEdD,MAAME,kBAAoBnB,OAAOC,WAIrCD,OAAOC,UAAW,EAClBT,sBAtGqBQ,CAAAA,SAC7BhB,uBAAuBoB,KAAKJ,QACvBjB,iBACDA,eAAiBqC,aAAY,KAGL,GADN3B,SAASC,OAAO2B,MAAMjC,gBAAkB,KAC5CkC,SAEN9B,sBACA+B,cAAcxC,gBACdA,eAAiB,EAGjBC,uBAAuBe,SAASC,SAC5BA,OAAOC,UAAW,QAG3B,OAsFCuB,CAAwBxB,UAE5BS,OAAOJ,iBAAiB,eAAgBa,eAIxCO,YAAW,WACPhB,OAAOiB,oBAAoB,eAAgBR,iBAC5C,MACJ"} \ No newline at end of file diff --git a/lib/form/amd/src/submit.js b/lib/form/amd/src/submit.js index 64608043866..ff08df1a5b6 100644 --- a/lib/form/amd/src/submit.js +++ b/lib/form/amd/src/submit.js @@ -106,12 +106,17 @@ const checkUploadCount = () => { /** * Initialises submit buttons. * - * @param {String} elementId Form element + * @param {String} elementId Form button element * @listens event:uploadStarted * @listens event:uploadCompleted */ export const init = (elementId) => { const button = document.getElementById(elementId); + if (button === null) { + // Exit early if invalid element id passed. + return; + } + // If buttons are disabled by default, we do not enable them when file upload completed event is fired. if (!button.disabled) { uploadListeningButtons.push(button); diff --git a/lib/templates/loginform.mustache b/lib/templates/loginform.mustache index 7b8caddcb52..129c4087d10 100644 --- a/lib/templates/loginform.mustache +++ b/lib/templates/loginform.mustache @@ -215,6 +215,8 @@ {{/error}} require(['core_form/submit'], function(Submit) { Submit.init("loginbtn"); - Submit.init("loginguestbtn"); + {{#canloginasguest}} + Submit.init("loginguestbtn"); + {{/canloginasguest}} }); {{/js}}