diff --git a/amd/build/maintenance.min.js b/amd/build/maintenance.min.js
index 1b94516..75f393a 100644
--- a/amd/build/maintenance.min.js
+++ b/amd/build/maintenance.min.js
@@ -5,6 +5,6 @@ define("tool_opencast/maintenance",["exports","core/ajax","core/notification","c
* @module tool_opencast/maintenance
* @copyright 2024 Farbod Zamani Boroujeni (zamani@elan-ev.de)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Ajax=_interopRequireWildcard(Ajax),Notification=_interopRequireWildcard(Notification),Str=_interopRequireWildcard(Str),Toast=_interopRequireWildcard(Toast);_exports.init=()=>{Str.get_strings([{key:"maintenancemode_modal_sync_confirmation_title",component:"tool_opencast"},{key:"maintenancemode_modal_sync_confirmation_text",component:"tool_opencast"},{key:"maintenancemode_modal_sync_confirmation_btn",component:"tool_opencast"},{key:"maintenancemode_modal_sync_error_title",component:"tool_opencast"},{key:"maintenancemode_modal_sync_error_noinstance_message",component:"tool_opencast"},{key:"maintenancemode_modal_sync_failed",component:"tool_opencast"},{key:"maintenancemode_modal_sync_succeeded",component:"tool_opencast"}]).then((function(jsstrings){document.querySelectorAll(".form-setting .opencast_config_dt_selector").forEach((dtblock=>{var _dtblock$dataset;if(null!=dtblock&&null!==(_dtblock$dataset=dtblock.dataset)&&void 0!==_dtblock$dataset&&_dtblock$dataset.isoptional){var _enablingelement$chec;const enablingelement=document.getElementById("".concat(dtblock.dataset.settingid,"_enabled")),initialvalue=null!==(_enablingelement$chec=null==enablingelement?void 0:enablingelement.checked)&&void 0!==_enablingelement$chec&&_enablingelement$chec,selects=dtblock.querySelectorAll(".opencast-config-dt-select");selects.forEach((select=>{select.disabled=!initialvalue})),enablingelement.addEventListener("change",(event=>{selects.forEach((select=>{select.disabled=!event.target.checked}))}))}}));document.querySelectorAll(".maintenance-sync-btn").forEach((btn=>{btn.addEventListener("click",(e=>{var _e$target,_e$target$dataset;e.preventDefault();const ocinstanceid=null===(_e$target=e.target)||void 0===_e$target||null===(_e$target$dataset=_e$target.dataset)||void 0===_e$target$dataset?void 0:_e$target$dataset.ocinstanceid;ocinstanceid?Notification.confirm(jsstrings[0],jsstrings[1],jsstrings[2],null,(()=>performSync(ocinstanceid,jsstrings))):Notification.alert(jsstrings[3],jsstrings[4])})),btn.removeAttribute("disabled"),btn.removeAttribute("title"),btn.classList.remove("disabled"),btn.classList.remove("btn-warning"),btn.classList.add("btn-primary")}))})).catch(Notification.exception)};const performSync=(ocinstanceid,jsstrings)=>{ocinstanceid&&Ajax.call([{methodname:"tool_opencast_maintenance_sync",args:{ocinstanceid:ocinstanceid}}])[0].then((data=>{null!=data&&data.status?(Toast.add(jsstrings[6],{type:"success"}),reloadWithDelay()):Toast.add(jsstrings[5],{type:"danger"})})).catch((error=>Notification.exception(error)))},reloadWithDelay=function(){let delay=arguments.length>0&&void 0!==arguments[0]?arguments[0]:3e3;setTimeout((()=>{window.location.reload()}),delay)}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notification=_exports.init=void 0,Ajax=_interopRequireWildcard(Ajax),Notification=_interopRequireWildcard(Notification),Str=_interopRequireWildcard(Str),Toast=_interopRequireWildcard(Toast);_exports.init=()=>{Str.get_strings([{key:"maintenancemode_modal_sync_confirmation_title",component:"tool_opencast"},{key:"maintenancemode_modal_sync_confirmation_text",component:"tool_opencast"},{key:"maintenancemode_modal_sync_confirmation_btn",component:"tool_opencast"},{key:"maintenancemode_modal_sync_error_title",component:"tool_opencast"},{key:"maintenancemode_modal_sync_error_noinstance_message",component:"tool_opencast"},{key:"maintenancemode_modal_sync_failed",component:"tool_opencast"},{key:"maintenancemode_modal_sync_succeeded",component:"tool_opencast"}]).then((function(jsstrings){document.querySelectorAll(".form-setting .opencast_config_dt_selector").forEach((dtblock=>{var _dtblock$dataset;if(null!=dtblock&&null!==(_dtblock$dataset=dtblock.dataset)&&void 0!==_dtblock$dataset&&_dtblock$dataset.isoptional){var _enablingelement$chec;const enablingelement=document.getElementById("".concat(dtblock.dataset.settingid,"_enabled")),initialvalue=null!==(_enablingelement$chec=null==enablingelement?void 0:enablingelement.checked)&&void 0!==_enablingelement$chec&&_enablingelement$chec,selects=dtblock.querySelectorAll(".opencast-config-dt-select");selects.forEach((select=>{select.disabled=!initialvalue})),enablingelement.addEventListener("change",(event=>{selects.forEach((select=>{select.disabled=!event.target.checked}))}))}}));document.querySelectorAll(".maintenance-sync-btn").forEach((btn=>{btn.addEventListener("click",(e=>{var _e$target,_e$target$dataset;e.preventDefault();const ocinstanceid=null===(_e$target=e.target)||void 0===_e$target||null===(_e$target$dataset=_e$target.dataset)||void 0===_e$target$dataset?void 0:_e$target$dataset.ocinstanceid;ocinstanceid?Notification.confirm(jsstrings[0],jsstrings[1],jsstrings[2],null,(()=>performSync(ocinstanceid,jsstrings))):Notification.alert(jsstrings[3],jsstrings[4])})),btn.removeAttribute("disabled"),btn.removeAttribute("title"),btn.classList.remove("disabled"),btn.classList.remove("btn-warning"),btn.classList.add("btn-primary")}))})).catch(Notification.exception)};const performSync=(ocinstanceid,jsstrings)=>{ocinstanceid&&Ajax.call([{methodname:"tool_opencast_maintenance_sync",args:{ocinstanceid:ocinstanceid}}])[0].then((data=>{null!=data&&data.status?(Toast.add(jsstrings[6],{type:"success"}),reloadWithDelay()):Toast.add(jsstrings[5],{type:"danger"})})).catch((error=>Notification.exception(error)))},reloadWithDelay=function(){let delay=arguments.length>0&&void 0!==arguments[0]?arguments[0]:3e3;setTimeout((()=>{window.location.reload()}),delay)};_exports.notification=(message,level,notify)=>{var _window;null!==(_window=window)&&void 0!==_window&&_window.ocMaintenanceNotified||!notify||(Notification.addNotification({message:message,type:level}),window.ocMaintenanceNotified=!0)}}));
//# sourceMappingURL=maintenance.min.js.map
\ No newline at end of file
diff --git a/amd/build/maintenance.min.js.map b/amd/build/maintenance.min.js.map
index f99972c..4aa4f2a 100644
--- a/amd/build/maintenance.min.js.map
+++ b/amd/build/maintenance.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"maintenance.min.js","sources":["../src/maintenance.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 * Javascript to handle maintenance mode in tool opencast.\n *\n * @module tool_opencast/maintenance\n * @copyright 2024 Farbod Zamani Boroujeni (zamani@elan-ev.de)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Ajax from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Str from 'core/str';\nimport * as Toast from 'core/toast';\n\n/**\n * Initializes the tool maintenance js module.\n */\nexport const init = () => {\n // Load strings\n var strings = [\n {key: 'maintenancemode_modal_sync_confirmation_title', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_confirmation_text', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_confirmation_btn', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_error_title', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_error_noinstance_message', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_failed', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_succeeded', component: 'tool_opencast'},\n ];\n Str.get_strings(strings).then(function(jsstrings) {\n // Required functionality for admin_setting_configdatetimeselector.\n const datetimeselectors = document.querySelectorAll('.form-setting .opencast_config_dt_selector');\n datetimeselectors.forEach((dtblock) => {\n if (dtblock?.dataset?.isoptional) {\n const enablingelement = document.getElementById(`${dtblock.dataset.settingid}_enabled`);\n const initialvalue = enablingelement?.checked ?? false;\n const selects = dtblock.querySelectorAll(`.opencast-config-dt-select`);\n selects.forEach((select) => {\n select.disabled = !initialvalue;\n });\n enablingelement.addEventListener('change', (event) => {\n selects.forEach((select) => {\n select.disabled = !event.target.checked;\n });\n });\n }\n });\n\n // Sync Button.\n const syncbtns = document.querySelectorAll('.maintenance-sync-btn');\n syncbtns.forEach((btn) => {\n btn.addEventListener('click', (e) => {\n e.preventDefault();\n const ocinstanceid = e.target?.dataset?.ocinstanceid;\n if (!ocinstanceid) {\n Notification.alert(jsstrings[3], jsstrings[4]);\n return;\n }\n\n Notification.confirm(\n jsstrings[0], jsstrings[1], jsstrings[2], null,\n () => performSync(ocinstanceid, jsstrings)\n );\n });\n // Make the button accessible to use after the listener is added.\n btn.removeAttribute('disabled');\n btn.removeAttribute('title');\n btn.classList.remove('disabled');\n btn.classList.remove('btn-warning');\n btn.classList.add('btn-primary');\n });\n\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Perform sync request via Ajax call.\n * @param {int} ocinstanceid\n * @param {array} jsstrings\n */\nconst performSync = (ocinstanceid, jsstrings) => {\n if (!ocinstanceid) {\n return;\n }\n Ajax.call([{\n methodname: 'tool_opencast_maintenance_sync',\n args: {ocinstanceid: ocinstanceid},\n }])[0]\n .then((data) => {\n if (!data?.status) {\n Toast.add(jsstrings[5], {type: 'danger'});\n return;\n }\n Toast.add(jsstrings[6], {type: 'success'});\n reloadWithDelay();\n return;\n })\n .catch((error) => Notification.exception(error));\n};\n\n/**\n * Reloads the current page with a delay.\n * @param {int} delay default 3000 ms\n */\nconst reloadWithDelay = (delay = 3000) => {\n setTimeout(() => {\n window.location.reload();\n }, delay);\n};\n"],"names":["Str","get_strings","key","component","then","jsstrings","document","querySelectorAll","forEach","dtblock","dataset","_dtblock$dataset","isoptional","enablingelement","getElementById","settingid","initialvalue","checked","selects","select","disabled","addEventListener","event","target","btn","e","preventDefault","ocinstanceid","_e$target","_e$target$dataset","Notification","confirm","performSync","alert","removeAttribute","classList","remove","add","catch","exception","Ajax","call","methodname","args","data","status","Toast","type","reloadWithDelay","error","delay","setTimeout","window","location","reload"],"mappings":";;;;;;;4PA+BoB,KAWhBA,IAAIC,YATU,CACV,CAACC,IAAK,gDAAiDC,UAAW,iBAClE,CAACD,IAAK,+CAAgDC,UAAW,iBACjE,CAACD,IAAK,8CAA+CC,UAAW,iBAChE,CAACD,IAAK,yCAA0CC,UAAW,iBAC3D,CAACD,IAAK,sDAAuDC,UAAW,iBACxE,CAACD,IAAK,oCAAqCC,UAAW,iBACtD,CAACD,IAAK,uCAAwCC,UAAW,mBAEpCC,MAAK,SAASC,WAETC,SAASC,iBAAiB,8CAClCC,SAASC,kCACnBA,MAAAA,kCAAAA,QAASC,qCAATC,iBAAkBC,WAAY,iCACxBC,gBAAkBP,SAASQ,yBAAkBL,QAAQC,QAAQK,uBAC7DC,2CAAeH,MAAAA,uBAAAA,gBAAiBI,gEAChCC,QAAUT,QAAQF,+CACxBW,QAAQV,SAASW,SACbA,OAAOC,UAAYJ,gBAEvBH,gBAAgBQ,iBAAiB,UAAWC,QACxCJ,QAAQV,SAASW,SACbA,OAAOC,UAAYE,MAAMC,OAAON,kBAO/BX,SAASC,iBAAiB,yBAClCC,SAASgB,MACdA,IAAIH,iBAAiB,SAAUI,oCAC3BA,EAAEC,uBACIC,+BAAeF,EAAEF,uDAAFK,UAAUlB,4CAAVmB,kBAAmBF,aACnCA,aAKLG,aAAaC,QACT1B,UAAU,GAAIA,UAAU,GAAIA,UAAU,GAAI,MAC1C,IAAM2B,YAAYL,aAActB,aANhCyB,aAAaG,MAAM5B,UAAU,GAAIA,UAAU,OAUnDmB,IAAIU,gBAAgB,YACpBV,IAAIU,gBAAgB,SACpBV,IAAIW,UAAUC,OAAO,YACrBZ,IAAIW,UAAUC,OAAO,eACrBZ,IAAIW,UAAUE,IAAI,qBAIvBC,MAAMR,aAAaS,kBAQpBP,YAAc,CAACL,aAActB,aAC1BsB,cAGLa,KAAKC,KAAK,CAAC,CACPC,WAAY,iCACZC,KAAM,CAAChB,aAAcA,iBACrB,GACHvB,MAAMwC,OACEA,MAAAA,MAAAA,KAAMC,QAIXC,MAAMT,IAAIhC,UAAU,GAAI,CAAC0C,KAAM,YAC/BC,mBAJIF,MAAMT,IAAIhC,UAAU,GAAI,CAAC0C,KAAM,cAOtCT,OAAOW,OAAUnB,aAAaS,UAAUU,UAOvCD,gBAAkB,eAACE,6DAAQ,IAC7BC,YAAW,KACPC,OAAOC,SAASC,WACjBJ"}
\ No newline at end of file
+{"version":3,"file":"maintenance.min.js","sources":["../src/maintenance.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 * Javascript to handle maintenance mode in tool opencast.\n *\n * @module tool_opencast/maintenance\n * @copyright 2024 Farbod Zamani Boroujeni (zamani@elan-ev.de)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Ajax from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Str from 'core/str';\nimport * as Toast from 'core/toast';\n\n/**\n * Initializes the tool maintenance js module.\n */\nexport const init = () => {\n // Load strings\n var strings = [\n {key: 'maintenancemode_modal_sync_confirmation_title', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_confirmation_text', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_confirmation_btn', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_error_title', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_error_noinstance_message', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_failed', component: 'tool_opencast'},\n {key: 'maintenancemode_modal_sync_succeeded', component: 'tool_opencast'},\n ];\n Str.get_strings(strings).then(function(jsstrings) {\n // Required functionality for admin_setting_configdatetimeselector.\n const datetimeselectors = document.querySelectorAll('.form-setting .opencast_config_dt_selector');\n datetimeselectors.forEach((dtblock) => {\n if (dtblock?.dataset?.isoptional) {\n const enablingelement = document.getElementById(`${dtblock.dataset.settingid}_enabled`);\n const initialvalue = enablingelement?.checked ?? false;\n const selects = dtblock.querySelectorAll(`.opencast-config-dt-select`);\n selects.forEach((select) => {\n select.disabled = !initialvalue;\n });\n enablingelement.addEventListener('change', (event) => {\n selects.forEach((select) => {\n select.disabled = !event.target.checked;\n });\n });\n }\n });\n\n // Sync Button.\n const syncbtns = document.querySelectorAll('.maintenance-sync-btn');\n syncbtns.forEach((btn) => {\n btn.addEventListener('click', (e) => {\n e.preventDefault();\n const ocinstanceid = e.target?.dataset?.ocinstanceid;\n if (!ocinstanceid) {\n Notification.alert(jsstrings[3], jsstrings[4]);\n return;\n }\n\n Notification.confirm(\n jsstrings[0], jsstrings[1], jsstrings[2], null,\n () => performSync(ocinstanceid, jsstrings)\n );\n });\n // Make the button accessible to use after the listener is added.\n btn.removeAttribute('disabled');\n btn.removeAttribute('title');\n btn.classList.remove('disabled');\n btn.classList.remove('btn-warning');\n btn.classList.add('btn-primary');\n });\n\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Perform sync request via Ajax call.\n * @param {int} ocinstanceid\n * @param {array} jsstrings\n */\nconst performSync = (ocinstanceid, jsstrings) => {\n if (!ocinstanceid) {\n return;\n }\n Ajax.call([{\n methodname: 'tool_opencast_maintenance_sync',\n args: {ocinstanceid: ocinstanceid},\n }])[0]\n .then((data) => {\n if (!data?.status) {\n Toast.add(jsstrings[5], {type: 'danger'});\n return;\n }\n Toast.add(jsstrings[6], {type: 'success'});\n reloadWithDelay();\n return;\n })\n .catch((error) => Notification.exception(error));\n};\n\n/**\n * Reloads the current page with a delay.\n * @param {int} delay default 3000 ms\n */\nconst reloadWithDelay = (delay = 3000) => {\n setTimeout(() => {\n window.location.reload();\n }, delay);\n};\n\n/**\n * Opencast Tool maintenance notification handler.\n *\n * It is used to make sure that there is only one maintenance notification printed at a time.\n *\n * @param {string} message\n * @param {string} level\n * @param {bool} notify\n */\nexport const notification = (message, level, notify) => {\n if (!window?.ocMaintenanceNotified && notify) {\n Notification.addNotification({\n message: message,\n type: level\n });\n window.ocMaintenanceNotified = true;\n }\n};\n"],"names":["Str","get_strings","key","component","then","jsstrings","document","querySelectorAll","forEach","dtblock","dataset","_dtblock$dataset","isoptional","enablingelement","getElementById","settingid","initialvalue","checked","selects","select","disabled","addEventListener","event","target","btn","e","preventDefault","ocinstanceid","_e$target","_e$target$dataset","Notification","confirm","performSync","alert","removeAttribute","classList","remove","add","catch","exception","Ajax","call","methodname","args","data","status","Toast","type","reloadWithDelay","error","delay","setTimeout","window","location","reload","message","level","notify","_window","ocMaintenanceNotified","addNotification"],"mappings":";;;;;;;kRA+BoB,KAWhBA,IAAIC,YATU,CACV,CAACC,IAAK,gDAAiDC,UAAW,iBAClE,CAACD,IAAK,+CAAgDC,UAAW,iBACjE,CAACD,IAAK,8CAA+CC,UAAW,iBAChE,CAACD,IAAK,yCAA0CC,UAAW,iBAC3D,CAACD,IAAK,sDAAuDC,UAAW,iBACxE,CAACD,IAAK,oCAAqCC,UAAW,iBACtD,CAACD,IAAK,uCAAwCC,UAAW,mBAEpCC,MAAK,SAASC,WAETC,SAASC,iBAAiB,8CAClCC,SAASC,kCACnBA,MAAAA,kCAAAA,QAASC,qCAATC,iBAAkBC,WAAY,iCACxBC,gBAAkBP,SAASQ,yBAAkBL,QAAQC,QAAQK,uBAC7DC,2CAAeH,MAAAA,uBAAAA,gBAAiBI,gEAChCC,QAAUT,QAAQF,+CACxBW,QAAQV,SAASW,SACbA,OAAOC,UAAYJ,gBAEvBH,gBAAgBQ,iBAAiB,UAAWC,QACxCJ,QAAQV,SAASW,SACbA,OAAOC,UAAYE,MAAMC,OAAON,kBAO/BX,SAASC,iBAAiB,yBAClCC,SAASgB,MACdA,IAAIH,iBAAiB,SAAUI,oCAC3BA,EAAEC,uBACIC,+BAAeF,EAAEF,uDAAFK,UAAUlB,4CAAVmB,kBAAmBF,aACnCA,aAKLG,aAAaC,QACT1B,UAAU,GAAIA,UAAU,GAAIA,UAAU,GAAI,MAC1C,IAAM2B,YAAYL,aAActB,aANhCyB,aAAaG,MAAM5B,UAAU,GAAIA,UAAU,OAUnDmB,IAAIU,gBAAgB,YACpBV,IAAIU,gBAAgB,SACpBV,IAAIW,UAAUC,OAAO,YACrBZ,IAAIW,UAAUC,OAAO,eACrBZ,IAAIW,UAAUE,IAAI,qBAIvBC,MAAMR,aAAaS,kBAQpBP,YAAc,CAACL,aAActB,aAC1BsB,cAGLa,KAAKC,KAAK,CAAC,CACPC,WAAY,iCACZC,KAAM,CAAChB,aAAcA,iBACrB,GACHvB,MAAMwC,OACEA,MAAAA,MAAAA,KAAMC,QAIXC,MAAMT,IAAIhC,UAAU,GAAI,CAAC0C,KAAM,YAC/BC,mBAJIF,MAAMT,IAAIhC,UAAU,GAAI,CAAC0C,KAAM,cAOtCT,OAAOW,OAAUnB,aAAaS,UAAUU,UAOvCD,gBAAkB,eAACE,6DAAQ,IAC7BC,YAAW,KACPC,OAAOC,SAASC,WACjBJ,8BAYqB,CAACK,QAASC,MAAOC,sCACpCL,2BAAAM,QAAQC,wBAAyBF,SAClC3B,aAAa8B,gBAAgB,CACzBL,QAASA,QACTR,KAAMS,QAEVJ,OAAOO,uBAAwB"}
\ No newline at end of file
diff --git a/amd/src/maintenance.js b/amd/src/maintenance.js
index 1accf3f..ab74a14 100644
--- a/amd/src/maintenance.js
+++ b/amd/src/maintenance.js
@@ -121,3 +121,22 @@ const reloadWithDelay = (delay = 3000) => {
window.location.reload();
}, delay);
};
+
+/**
+ * Opencast Tool maintenance notification handler.
+ *
+ * It is used to make sure that there is only one maintenance notification printed at a time.
+ *
+ * @param {string} message
+ * @param {string} level
+ * @param {bool} notify
+ */
+export const notification = (message, level, notify) => {
+ if (!window?.ocMaintenanceNotified && notify) {
+ Notification.addNotification({
+ message: message,
+ type: level
+ });
+ window.ocMaintenanceNotified = true;
+ }
+};
diff --git a/classes/local/api.php b/classes/local/api.php
index d3b3ee1..762df10 100644
--- a/classes/local/api.php
+++ b/classes/local/api.php
@@ -417,8 +417,7 @@ public function oc_get($resource, $runwithroles = []) {
// Check for maintenance first.
if (!empty($this->maintenance) && !$this->maintenance->can_access(__FUNCTION__)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$url = $this->baseurl . $resource;
@@ -522,8 +521,7 @@ public function oc_post($resource, $params = [], $runwithroles = []) {
// Check for maintenance first.
if (!empty($this->maintenance) && !$this->maintenance->can_access(__FUNCTION__)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$url = $this->baseurl . $resource;
@@ -573,8 +571,7 @@ public function oc_put($resource, $params = [], $runwithroles = []) {
// Check for maintenance first.
if (!empty($this->maintenance) && !$this->maintenance->can_access(__FUNCTION__)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$url = $this->baseurl . $resource;
@@ -614,8 +611,7 @@ public function oc_delete($resource, $params = [], $runwithroles = []) {
// Check for maintenance first.
if (!empty($this->maintenance) && !$this->maintenance->can_access(__FUNCTION__)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$url = $this->baseurl . $resource;
diff --git a/classes/local/maintenance_class.php b/classes/local/maintenance_class.php
index 2ba0099..e89ef0c 100644
--- a/classes/local/maintenance_class.php
+++ b/classes/local/maintenance_class.php
@@ -371,16 +371,16 @@ public function decide_access_bounce() {
// If admin and it is not admin cron page,
// we let it pass to avoid interrupting any installation, configuration or upgrade.
if (is_siteadmin() && strpos($frompath, $blacklist['admin_cron']) === false) {
- return;
+ return ['code' => 404];
}
$fromblacklisted = $this->is_path_blacklisted($frompath, $blacklist);
$targetblacklisted = $this->is_path_blacklisted($tagetpath, $blacklist);
// Exception: Calls going up to course from blacklist, or nothing to do with blacklist, we do nothing!
- if ((in_array($tagetpath, $whitelist) && $fromblacklisted) ||
- (in_array($frompath, $whitelist) && !$targetblacklisted)) {
- return;
+ if ((!$fromblacklisted && !$targetblacklisted) || // Outside reaching or loading opencast.
+ (in_array($tagetpath, $whitelist) && $fromblacklisted)) { // Going back from plugin to course or somewhere else
+ return ['code' => 404];
}
// Is ajax or popup, we throw error to make sure the user gets the correct form of notification.
@@ -431,20 +431,20 @@ private function is_path_blacklisted(string $path, array $blacklist) {
return !empty($filterred);
}
+
/**
- * Handles the display of maintenace notification message.
- *
- * It prepares the message and the notification level, then makes sure that the nofictiation message gets printed only once,
- * or it is removed from standard Moodle notification queue when the maintenance period is over.
+ * Displays a notification message based on the maintenance mode settings.
*
- * Using \core\notification::add to add a notification and $SESSION->notifications to remove and check the existance.
+ * This function retrieves the formatted maintenance message, the notification level,
+ * and checks if the maintenance mode is activated. It then uses the Moodle Page API
+ * to load the 'tool_opencast/maintenance' JavaScript module and passes the necessary
+ * parameters to display the notification.
*
- * @uses \core\notification
- * @uses $SESSION->notifications
+ * @global moodle_page $PAGE The global Moodle page object.
* @return void
*/
public function handle_notification_message_display() {
- global $SESSION;
+ global $PAGE;
// Get the formatted message.
$message = $this->get_formatted_message();
@@ -456,30 +456,12 @@ public function handle_notification_message_display() {
// Get the level.
$level = $this->get_notiflevel();
- // Get the session notifications.
- $notifications = isset($SESSION->notifications) ? $SESSION->notifications : [];
-
- // Filter out existing maintenance notifications.
- $existingnotifications = array_filter($notifications, function ($notification) use ($message, $level) {
- return trim($notification->message) === trim($message) && $notification->type === $level;
- });
-
- // Check if the notification is already printed.
- $isprinted = !empty($existingnotifications);
-
- // When it is activated, we print notification only once!
- if (!$isprinted && $this->is_activated()) {
- \core\notification::add($message, $level);
- } else if ($isprinted && !$this->is_activated()) {
- // This is a rare case, where the maintenance is deactivated, but message are still going to be printed.
- // We simple take them out of the session!
- $targetedindecies = array_keys($existingnotifications);
- rsort($targetedindecies); // Descending sort, to maintain the integrity of the session array while unset.
- foreach ($targetedindecies as $index) {
- if (isset($SESSION->notifications[$index])) {
- unset($SESSION->notifications[$index]);
- }
- }
+ // We notify only when it is activated!
+ $notify = $this->is_activated();
+
+ // Make sure that the $PAGE is ready for notifications js module load.
+ if ($PAGE) {
+ $PAGE->requires->js_call_amd('tool_opencast/maintenance', 'notification', [$message, $level, $notify]);
}
}
diff --git a/classes/proxy/decorated_opencastapi_rest_client.php b/classes/proxy/decorated_opencastapi_rest_client.php
index 0308f93..dd4966c 100644
--- a/classes/proxy/decorated_opencastapi_rest_client.php
+++ b/classes/proxy/decorated_opencastapi_rest_client.php
@@ -60,8 +60,7 @@ public function __construct(array $config, ?maintenance_class $maintenance = nul
*/
public function __call(string $method, array $args) {
if (!empty($this->maintenance) && !$this->maintenance->can_access($method)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$returedresult = call_user_func_array([$this->restclient, $method], $args);
if ($returedresult === $this->restclient) {
diff --git a/classes/proxy/decorated_opencastapi_service.php b/classes/proxy/decorated_opencastapi_service.php
index 3e88b25..ac74044 100644
--- a/classes/proxy/decorated_opencastapi_service.php
+++ b/classes/proxy/decorated_opencastapi_service.php
@@ -63,8 +63,7 @@ public function __construct(?OcRest $apiservice = null, ?maintenance_class $main
public function __call(string $method, array $args) {
// Maintenance feature checker.
if (!empty($this->maintenance) && !$this->maintenance->can_access($method)) {
- $this->maintenance->decide_access_bounce();
- return;
+ return $this->maintenance->decide_access_bounce();
}
$response = call_user_func_array([$this->apiservice, $method], $args);