Merge branch 'MDL-70658-final' of github.com:vmdef/moodle
This commit is contained in:
@@ -1929,7 +1929,7 @@ class core_plugin_manager {
|
||||
),
|
||||
|
||||
'mod' => array(
|
||||
'assign', 'assignment', 'book', 'chat', 'choice', 'data', 'feedback', 'folder',
|
||||
'assign', 'assignment', 'bigbluebuttonbn', 'book', 'chat', 'choice', 'data', 'feedback', 'folder',
|
||||
'forum', 'glossary', 'h5pactivity', 'imscp', 'label', 'lesson', 'lti', 'page',
|
||||
'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
|
||||
),
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
define ("mod_bigbluebuttonbn/actions",["./repository","core/notification","./events","core/str"],function(a,b,c,d){"use strict";var f=function(a,c,d){return new Promise(function(e){(0,b.saveCancel)(a,c,d,e)})},g=function(){document.addEventListener("click",function(g){var e=g.target.closest(".bbb-btn-action[data-action=\"end\"]");if(!e){return}g.preventDefault();var h=e.dataset.bbbId,i=e.dataset.groupId?e.dataset.groupId:0;f((0,d.get_string)("end_session_confirm_title","mod_bigbluebuttonbn"),(0,d.get_string)("end_session_confirm","mod_bigbluebuttonbn"),(0,d.get_string)("yes","moodle")).then(function(){return(0,a.endMeeting)(h,i)}).then(function(){(0,c.notifySessionEnded)(h,i)}).catch(b.exception)})},h=!1;if(!h){g();h=!0}});
|
||||
//# sourceMappingURL=actions.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../src/actions.js"],"names":["confirmedPromise","title","question","saveLabel","Promise","resolve","registerEventListeners","document","addEventListener","e","actionButton","target","closest","preventDefault","bbbId","dataset","groupId","then","catch","displayException","listening"],"mappings":"mIA8BMA,CAAAA,CAAgB,CAAG,SAACC,CAAD,CAAQC,CAAR,CAAkBC,CAAlB,QAAgC,IAAIC,CAAAA,OAAJ,CAAY,SAAAC,CAAO,CAAI,CAC5E,iBAAWJ,CAAX,CAAkBC,CAAlB,CAA4BC,CAA5B,CAAuCE,CAAvC,CACH,CAFwD,CAAhC,C,CAInBC,CAAsB,CAAG,UAAM,CACjCC,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAC,CAAC,CAAI,CACpC,GAAMC,CAAAA,CAAY,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiB,sCAAjB,CAArB,CACA,GAAI,CAACF,CAAL,CAAmB,CACf,MACH,CAEDD,CAAC,CAACI,cAAF,GANoC,GAQ9BC,CAAAA,CAAK,CAAGJ,CAAY,CAACK,OAAb,CAAqBD,KARC,CAS9BE,CAAO,CAAGN,CAAY,CAACK,OAAb,CAAqBC,OAArB,CAA+BN,CAAY,CAACK,OAAb,CAAqBC,OAApD,CAA8D,CAT1C,CAWpChB,CAAgB,CACZ,iBAAU,2BAAV,CAAuC,qBAAvC,CADY,CAEZ,iBAAU,qBAAV,CAAiC,qBAAjC,CAFY,CAGZ,iBAAU,KAAV,CAAiB,QAAjB,CAHY,CAAhB,CAKCiB,IALD,CAKM,iBAAM,iBAAkBH,CAAlB,CAAyBE,CAAzB,CAAN,CALN,EAMCC,IAND,CAMM,UAAM,CACR,yBAAmBH,CAAnB,CAA0BE,CAA1B,CAGH,CAVD,EAWCE,KAXD,CAWOC,WAXP,CAYH,CAvBD,CAwBH,C,CAEGC,CAAS,G,CACb,GAAI,CAACA,CAAL,CAAgB,CACZd,CAAsB,GACtBc,CAAS,GACZ,C","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 <http://www.gnu.org/licenses/>.\n/**\n * JS actions.\n *\n * @module mod_bigbluebuttonbn/actions\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {endMeeting as requestEndMeeting} from './repository';\nimport {\n exception as displayException,\n saveCancel,\n} from 'core/notification';\nimport {notifySessionEnded} from './events';\nimport {get_string as getString} from 'core/str';\n\nconst confirmedPromise = (title, question, saveLabel) => new Promise(resolve => {\n saveCancel(title, question, saveLabel, resolve);\n});\n\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const actionButton = e.target.closest('.bbb-btn-action[data-action=\"end\"]');\n if (!actionButton) {\n return;\n }\n\n e.preventDefault();\n\n const bbbId = actionButton.dataset.bbbId;\n const groupId = actionButton.dataset.groupId ? actionButton.dataset.groupId : 0;\n\n confirmedPromise(\n getString('end_session_confirm_title', 'mod_bigbluebuttonbn'),\n getString('end_session_confirm', 'mod_bigbluebuttonbn'),\n getString('yes', 'moodle')\n )\n .then(() => requestEndMeeting(bbbId, groupId))\n .then(() => {\n notifySessionEnded(bbbId, groupId);\n\n return;\n })\n .catch(displayException);\n });\n};\n\nlet listening = false;\nif (!listening) {\n registerEventListeners();\n listening = true;\n}\n"],"file":"actions.min.js"}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
define ("mod_bigbluebuttonbn/events",["exports","core/event_dispatcher"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.notifyCurrentSessionEnded=a.notifySessionEnded=a.eventTypes=void 0;var c={sessionEnded:"mod_bigbluebuttonbn/sessionEnded",currentSessionEnded:"mod_bigbluebuttonbn/currentSessionEnded"};a.eventTypes=c;a.notifySessionEnded=function notifySessionEnded(a,d){return(0,b.dispatchEvent)(c.sessionEnded,{bbbId:a,groupId:d})};a.notifyCurrentSessionEnded=function notifyCurrentSessionEnded(a){return(0,b.dispatchEvent)(c.currentSessionEnded,{},a)}});
|
||||
//# sourceMappingURL=events.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../src/events.js"],"names":["eventTypes","sessionEnded","currentSessionEnded","notifySessionEnded","bbbId","groupId","notifyCurrentSessionEnded","container"],"mappings":"0NAyBO,GAAMA,CAAAA,CAAU,CAAG,CAUtBC,YAAY,CAAE,kCAVQ,CAmBtBC,mBAAmB,CAAE,yCAnBC,CAAnB,C,oCA8B2B,QAArBC,CAAAA,kBAAqB,CAACC,CAAD,CAAQC,CAAR,QAAoB,oBAAcL,CAAU,CAACC,YAAzB,CAAuC,CACzFG,KAAK,CAALA,CADyF,CAEzFC,OAAO,CAAPA,CAFyF,CAAvC,CAApB,C,6BAYO,QAA5BC,CAAAA,yBAA4B,CAAAC,CAAS,QAAI,oBAClDP,CAAU,CAACE,mBADuC,CAElD,EAFkD,CAGlDK,CAHkD,CAAJ,C","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Events for the mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/events\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nexport const eventTypes = {\n /**\n * Fired when a session has been ended.\n *\n * @event mod_bigbluebuttonbn/sessionEnded\n * @type CustomEvent\n * @property {object} detail\n * @property {number} detail.bbbId\n * @property {number} detail.groupId\n */\n sessionEnded: 'mod_bigbluebuttonbn/sessionEnded',\n\n /**\n * Fired when the current session has been ended.\n *\n * @event mod_bigbluebuttonbn/currentSessionEnded\n * @type CustomEvent\n * @property {object} detail\n */\n currentSessionEnded: 'mod_bigbluebuttonbn/currentSessionEnded',\n};\n\n/**\n * Trigger the sessionEnded event.\n *\n * @param {number} bbbId\n * @param {number} groupId\n * @returns {CustomEvent}\n * @fires event:mod_bigbluebuttonbn/sessionEnded\n */\nexport const notifySessionEnded = (bbbId, groupId) => dispatchEvent(eventTypes.sessionEnded, {\n bbbId,\n groupId,\n});\n\n/**\n * Trigger the currentSessionEnded event.\n *\n * @param {Element} container\n * @returns {CustomEvent}\n * @fires event:mod_bigbluebuttonbn/currentSessionEnded\n */\nexport const notifyCurrentSessionEnded = container => dispatchEvent(\n eventTypes.currentSessionEnded,\n {},\n container\n);\n"],"file":"events.min.js"}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
define ("mod_bigbluebuttonbn/index",["exports","./actions","./events"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;a.init=function init(){document.addEventListener(c.eventTypes.sessionEnded,function(){window.location.reload()})}});
|
||||
//# sourceMappingURL=index.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../src/index.js"],"names":["init","document","addEventListener","eventTypes","sessionEnded","window","location","reload"],"mappings":"0KA0BoB,QAAPA,CAAAA,IAAO,EAAM,CACtBC,QAAQ,CAACC,gBAAT,CAA0BC,aAAWC,YAArC,CAAmD,UAAM,CACrDC,MAAM,CAACC,QAAP,CAAgBC,MAAhB,EACH,CAFD,CAGH,C","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Index page for the mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/index\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport './actions';\nimport {eventTypes} from './events';\n\nexport const init = () => {\n document.addEventListener(eventTypes.sessionEnded, () => {\n window.location.reload();\n });\n};\n"],"file":"index.min.js"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
define ("mod_bigbluebuttonbn/repository",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.getMeetingInfo=a.completionValidate=a.endMeeting=a.updateRecording=a.fetchRecordingsToImport=a.fetchRecordings=void 0;a.fetchRecordings=function fetchRecordings(a,c,d){var e={bigbluebuttonbnid:a,tools:c};if(d){e.groupid=d}return(0,b.call)([{methodname:"mod_bigbluebuttonbn_get_recordings",args:e}])[0]};a.fetchRecordingsToImport=function fetchRecordingsToImport(a,c,d,e,f){var g={destinationinstanceid:a,sourcebigbluebuttonbnid:c,sourcecourseid:d,tools:e};if(f){g.groupid=f}return(0,b.call)([{methodname:"mod_bigbluebuttonbn_get_recordings_to_import",args:g}])[0]};a.updateRecording=function updateRecording(a){return(0,b.call)([{methodname:"mod_bigbluebuttonbn_update_recording",args:a}])[0]};a.endMeeting=function endMeeting(a,c){return(0,b.call)([{methodname:"mod_bigbluebuttonbn_end_meeting",args:{bigbluebuttonbnid:a,groupid:c}}])[0]};a.completionValidate=function completionValidate(a){return(0,b.call)([{methodname:"mod_bigbluebuttonbn_completion_validate",args:a}])[0]};a.getMeetingInfo=function getMeetingInfo(a,c){var d=2<arguments.length&&arguments[2]!==void 0?arguments[2]:!1;return(0,b.call)([{methodname:"mod_bigbluebuttonbn_meeting_info",args:{bigbluebuttonbnid:a,groupid:c,updatecache:d}}])[0]}});
|
||||
//# sourceMappingURL=repository.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+2
@@ -0,0 +1,2 @@
|
||||
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("mod_bigbluebuttonbn/rooms",["exports","./actions","./repository","./roomupdater","core/notification","./events"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.setupWindowAutoClose=a.init=void 0;c=h(c);d=h(d);function g(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;g=function(){return a};return a}function h(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=g();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}a.init=function init(a){var b=document.querySelector("a[href*=completion_validate]");if(b){b.addEventListener("click",function(){c.completionValidate(a).catch(e.exception)})}document.addEventListener("click",function(a){var b=a.target.closest("[data-action=\"join\"]");if(b){d.start();window.open(b.href,"bigbluebutton_conference");a.preventDefault()}});document.addEventListener(f.eventTypes.sessionEnded,function(){d.stop();d.updateRoom();(0,e.fetchNotifications)()});window.addEventListener(f.eventTypes.currentSessionEnded,function(){d.stop();d.updateRoom();(0,e.fetchNotifications)()})};var i=function(){window.opener.setTimeout(function(){d.updateRoom(!0)},5e3);window.removeEventListener("onbeforeunload",i)};a.setupWindowAutoClose=function setupWindowAutoClose(){(0,f.notifyCurrentSessionEnded)(window.opener);window.addEventListener("onbeforeunload",i);window.close()}});
|
||||
//# sourceMappingURL=rooms.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../src/rooms.js"],"names":["init","bigbluebuttonbnid","completionElement","document","querySelector","addEventListener","repository","completionValidate","catch","displayException","e","joinButton","target","closest","roomUpdater","start","window","open","href","preventDefault","eventTypes","sessionEnded","stop","updateRoom","currentSessionEnded","autoclose","opener","setTimeout","removeEventListener","setupWindowAutoClose","close"],"mappings":"shBAwBA,OACA,O,6iBAQoB,QAAPA,CAAAA,IAAO,CAACC,CAAD,CAAuB,CACvC,GAAMC,CAAAA,CAAiB,CAAGC,QAAQ,CAACC,aAAT,CAAuB,8BAAvB,CAA1B,CACA,GAAIF,CAAJ,CAAuB,CACnBA,CAAiB,CAACG,gBAAlB,CAAmC,OAAnC,CAA4C,UAAM,CAC9CC,CAAU,CAACC,kBAAX,CAA8BN,CAA9B,EAAiDO,KAAjD,CAAuDC,WAAvD,CACH,CAFD,CAGH,CAEDN,QAAQ,CAACE,gBAAT,CAA0B,OAA1B,CAAmC,SAAAK,CAAC,CAAI,CACpC,GAAMC,CAAAA,CAAU,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiB,wBAAjB,CAAnB,CACA,GAAIF,CAAJ,CAAgB,CACZG,CAAW,CAACC,KAAZ,GACAC,MAAM,CAACC,IAAP,CAAYN,CAAU,CAACO,IAAvB,CAA6B,0BAA7B,EAEAR,CAAC,CAACS,cAAF,EACH,CACJ,CARD,EAUAhB,QAAQ,CAACE,gBAAT,CAA0Be,aAAWC,YAArC,CAAmD,UAAM,CACrDP,CAAW,CAACQ,IAAZ,GACAR,CAAW,CAACS,UAAZ,GACA,0BACH,CAJD,EAMAP,MAAM,CAACX,gBAAP,CAAwBe,aAAWI,mBAAnC,CAAwD,UAAM,CAC1DV,CAAW,CAACQ,IAAZ,GACAR,CAAW,CAACS,UAAZ,GACA,0BACH,CAJD,CAKH,C,IAKKE,CAAAA,CAAS,CAAG,UAAM,CACpBT,MAAM,CAACU,MAAP,CAAcC,UAAd,CAAyB,UAAM,CAC3Bb,CAAW,CAACS,UAAZ,IACH,CAFD,CAEG,GAFH,EAGAP,MAAM,CAACY,mBAAP,CAA2B,gBAA3B,CAA6CH,CAA7C,CACH,C,wBAKmC,QAAvBI,CAAAA,oBAAuB,EAAM,CACtC,gCAA0Bb,MAAM,CAACU,MAAjC,EACAV,MAAM,CAACX,gBAAP,CAAwB,gBAAxB,CAA0CoB,CAA1C,EAEAT,MAAM,CAACc,KAAP,EACH,C","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 <http://www.gnu.org/licenses/>.\n\n/**\n * JS actions for the rooms page for mod_bigbluebuttonbn.\n *\n * @module mod_bigbluebuttonbn/rooms\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport './actions';\nimport * as repository from './repository';\nimport * as roomUpdater from './roomupdater';\nimport {\n exception as displayException,\n fetchNotifications,\n} from 'core/notification';\n\nimport {eventTypes, notifyCurrentSessionEnded} from './events';\n\nexport const init = (bigbluebuttonbnid) => {\n const completionElement = document.querySelector('a[href*=completion_validate]');\n if (completionElement) {\n completionElement.addEventListener(\"click\", () => {\n repository.completionValidate(bigbluebuttonbnid).catch(displayException);\n });\n }\n\n document.addEventListener('click', e => {\n const joinButton = e.target.closest('[data-action=\"join\"]');\n if (joinButton) {\n roomUpdater.start();\n window.open(joinButton.href, 'bigbluebutton_conference');\n\n e.preventDefault();\n }\n });\n\n document.addEventListener(eventTypes.sessionEnded, () => {\n roomUpdater.stop();\n roomUpdater.updateRoom();\n fetchNotifications();\n });\n\n window.addEventListener(eventTypes.currentSessionEnded, () => {\n roomUpdater.stop();\n roomUpdater.updateRoom();\n fetchNotifications();\n });\n};\n\n/**\n * Handle autoclosing of the window.\n */\nconst autoclose = () => {\n window.opener.setTimeout(() => {\n roomUpdater.updateRoom(true);\n }, 5000);\n window.removeEventListener('onbeforeunload', autoclose);\n};\n\n/**\n * Auto close child windows when clicking the End meeting button.\n */\nexport const setupWindowAutoClose = () => {\n notifyCurrentSessionEnded(window.opener);\n window.addEventListener('onbeforeunload', autoclose);\n\n window.close(); // This does not work as scripts can only close windows that are opened by themselves.\n};\n"],"file":"rooms.min.js"}
|
||||
@@ -0,0 +1,2 @@
|
||||
define ("mod_bigbluebuttonbn/roomupdater",["exports","core/templates","core/notification","./repository"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.updateRoom=a.stop=a.start=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var e=0,f=1,g=null,h=!1,i=function(){e=0;f=1};a.start=function start(){h=!0;g=setTimeout(function(){return k()},1000)};var j=function(){h=!1;if(g){clearInterval(g);g=null}i()};a.stop=j;var k=function(){if(!h){return}if(0==e%f){l(!0).then(function(){if(f>=10){f=1}else{f++}}).catch().then(function(){g=setTimeout(function(){return k()},1000)}).catch()}},l=function(){var a=0<arguments.length&&arguments[0]!==void 0?arguments[0]:!1,e=document.getElementById("bbb-room-view"),f=e.dataset.bbbId,g=e.dataset.groupId;return(0,d.getMeetingInfo)(f,g,a).then(function(a){a.haspresentations=!1;if(a.presentations&&a.presentations.length){a.haspresentations=!0}return b.default.renderForPromise("mod_bigbluebuttonbn/room_view",a)}).then(function(a){var c=a.html,d=a.js;return b.default.replaceNodeContents(e,c,d)}).catch(c.exception)};a.updateRoom=l});
|
||||
//# sourceMappingURL=roomupdater.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,66 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* JS actions.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/actions
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {endMeeting as requestEndMeeting} from './repository';
|
||||
import {
|
||||
exception as displayException,
|
||||
saveCancel,
|
||||
} from 'core/notification';
|
||||
import {notifySessionEnded} from './events';
|
||||
import {get_string as getString} from 'core/str';
|
||||
|
||||
const confirmedPromise = (title, question, saveLabel) => new Promise(resolve => {
|
||||
saveCancel(title, question, saveLabel, resolve);
|
||||
});
|
||||
|
||||
const registerEventListeners = () => {
|
||||
document.addEventListener('click', e => {
|
||||
const actionButton = e.target.closest('.bbb-btn-action[data-action="end"]');
|
||||
if (!actionButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const bbbId = actionButton.dataset.bbbId;
|
||||
const groupId = actionButton.dataset.groupId ? actionButton.dataset.groupId : 0;
|
||||
|
||||
confirmedPromise(
|
||||
getString('end_session_confirm_title', 'mod_bigbluebuttonbn'),
|
||||
getString('end_session_confirm', 'mod_bigbluebuttonbn'),
|
||||
getString('yes', 'moodle')
|
||||
)
|
||||
.then(() => requestEndMeeting(bbbId, groupId))
|
||||
.then(() => {
|
||||
notifySessionEnded(bbbId, groupId);
|
||||
|
||||
return;
|
||||
})
|
||||
.catch(displayException);
|
||||
});
|
||||
};
|
||||
|
||||
let listening = false;
|
||||
if (!listening) {
|
||||
registerEventListeners();
|
||||
listening = true;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Events for the mod_bigbluebuttonbn plugin.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/events
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {dispatchEvent} from 'core/event_dispatcher';
|
||||
|
||||
export const eventTypes = {
|
||||
/**
|
||||
* Fired when a session has been ended.
|
||||
*
|
||||
* @event mod_bigbluebuttonbn/sessionEnded
|
||||
* @type CustomEvent
|
||||
* @property {object} detail
|
||||
* @property {number} detail.bbbId
|
||||
* @property {number} detail.groupId
|
||||
*/
|
||||
sessionEnded: 'mod_bigbluebuttonbn/sessionEnded',
|
||||
|
||||
/**
|
||||
* Fired when the current session has been ended.
|
||||
*
|
||||
* @event mod_bigbluebuttonbn/currentSessionEnded
|
||||
* @type CustomEvent
|
||||
* @property {object} detail
|
||||
*/
|
||||
currentSessionEnded: 'mod_bigbluebuttonbn/currentSessionEnded',
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger the sessionEnded event.
|
||||
*
|
||||
* @param {number} bbbId
|
||||
* @param {number} groupId
|
||||
* @returns {CustomEvent}
|
||||
* @fires event:mod_bigbluebuttonbn/sessionEnded
|
||||
*/
|
||||
export const notifySessionEnded = (bbbId, groupId) => dispatchEvent(eventTypes.sessionEnded, {
|
||||
bbbId,
|
||||
groupId,
|
||||
});
|
||||
|
||||
/**
|
||||
* Trigger the currentSessionEnded event.
|
||||
*
|
||||
* @param {Element} container
|
||||
* @returns {CustomEvent}
|
||||
* @fires event:mod_bigbluebuttonbn/currentSessionEnded
|
||||
*/
|
||||
export const notifyCurrentSessionEnded = container => dispatchEvent(
|
||||
eventTypes.currentSessionEnded,
|
||||
{},
|
||||
container
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Index page for the mod_bigbluebuttonbn plugin.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/index
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import './actions';
|
||||
import {eventTypes} from './events';
|
||||
|
||||
export const init = () => {
|
||||
document.addEventListener(eventTypes.sessionEnded, () => {
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,367 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* JS for the mod_form page on mod_bigbluebuttonbn plugin.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/mod_form
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {get_string as getString} from 'core/str';
|
||||
import Notification from 'core/notification';
|
||||
import Templates from "core/templates";
|
||||
|
||||
/**
|
||||
* Get all selectors in one place.
|
||||
*
|
||||
*/
|
||||
const ELEMENT_SELECTOR = {
|
||||
instanceTypeSelection: () => document.querySelector('select#id_type'),
|
||||
instanceTypeProfiles: () => document.querySelector('[data-profile-types]'),
|
||||
participantData: () => document.querySelector('[data-participant-data]'),
|
||||
participantList: () => document.getElementsByName('participants')[0],
|
||||
participantTable: () => document.getElementById('participant_list_table'),
|
||||
participantSelectionType: () => document.getElementsByName('bigbluebuttonbn_participant_selection_type')[0],
|
||||
participantSelection: () => document.getElementsByName('bigbluebuttonbn_participant_selection')[0],
|
||||
participantAddButton: () => document.getElementsByName('bigbluebuttonbn_participant_selection_add')[0],
|
||||
};
|
||||
/**
|
||||
* Initialise the moodle form code.
|
||||
*
|
||||
* This will help hide or show items depending on the selection of the instance type.
|
||||
*
|
||||
* @method init
|
||||
* @param {object} info
|
||||
*/
|
||||
export const init = (info) => {
|
||||
const selectedType = ELEMENT_SELECTOR.instanceTypeSelection();
|
||||
const instanceTypeProfiles = JSON.parse(ELEMENT_SELECTOR.instanceTypeProfiles().dataset.profileTypes);
|
||||
|
||||
let profileType = info.instanceTypeDefault;
|
||||
if (selectedType !== null && selectedType.selectedIndex !== -1) {
|
||||
profileType = selectedType.options[selectedType.selectedIndex].value;
|
||||
}
|
||||
|
||||
const isFeatureEnabled = (profileType, feature) => {
|
||||
const features = instanceTypeProfiles[profileType].features;
|
||||
return (features.indexOf(feature) !== -1);
|
||||
};
|
||||
applyInstanceTypeProfile(profileType, isFeatureEnabled);
|
||||
|
||||
// Change form visible fields depending on the selection.
|
||||
selectedType.addEventListener('change', (e) => {
|
||||
applyInstanceTypeProfile(e.target.value, isFeatureEnabled);
|
||||
});
|
||||
|
||||
ELEMENT_SELECTOR.participantSelectionType().addEventListener('change', (e) => {
|
||||
const currentTypeSelect = e.target;
|
||||
updateSelectionFromType(currentTypeSelect);
|
||||
});
|
||||
|
||||
ELEMENT_SELECTOR.participantAddButton().addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
participantAddFromCurrentSelection();
|
||||
});
|
||||
|
||||
participantListInit();
|
||||
};
|
||||
|
||||
/**
|
||||
* Show or hide form element depending on the selected profile
|
||||
*
|
||||
* @param {string} profileType
|
||||
* @param {function} isFeatureEnabled
|
||||
*/
|
||||
const applyInstanceTypeProfile = (profileType, isFeatureEnabled) => {
|
||||
let showAll = isFeatureEnabled(profileType, 'all');
|
||||
const showFieldset = (id, show) => {
|
||||
// Show room settings validation.
|
||||
const node = document.querySelector('#' + id);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (show) {
|
||||
node.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
node.style.display = 'none';
|
||||
};
|
||||
const showInput = (id, show) => {
|
||||
// Show room settings validation.
|
||||
const node = document.querySelector('#' + id);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
var ancestor = node.closest('div').closest('div');
|
||||
if (show) {
|
||||
ancestor.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
ancestor.style.display = 'none';
|
||||
};
|
||||
const showFormGroup = (id, show) => {
|
||||
// Show room settings validation.
|
||||
const node = document.querySelector('#fgroup_id_' + id);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (show) {
|
||||
node.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
node.classList.add('hidden');
|
||||
};
|
||||
// Show room settings validation.
|
||||
showFieldset('id_room', showAll ||
|
||||
isFeatureEnabled(profileType, 'showroom'));
|
||||
showInput('id_record', showAll ||
|
||||
isFeatureEnabled(profileType, 'showroom'));
|
||||
// Show recordings settings validation.
|
||||
showFieldset('id_recordings', showAll ||
|
||||
isFeatureEnabled(profileType, 'showrecordings'));
|
||||
// Show recordings imported settings validation.
|
||||
showInput('id_recordings_imported', showAll ||
|
||||
isFeatureEnabled(profileType, 'showrecordings'));
|
||||
// Preuploadpresentation feature validation.
|
||||
showFieldset('id_preuploadpresentation', showAll ||
|
||||
isFeatureEnabled(profileType, 'preuploadpresentation'));
|
||||
// Participants feature validation.
|
||||
showFieldset('id_permissions', showAll ||
|
||||
isFeatureEnabled(profileType, 'permissions'));
|
||||
// Schedule feature validation.
|
||||
showFieldset('id_schedule', showAll ||
|
||||
isFeatureEnabled(profileType, 'schedule'));
|
||||
// Common module settings validation.
|
||||
showFieldset('id_modstandardelshdr', showAll ||
|
||||
isFeatureEnabled(profileType, 'modstandardelshdr'));
|
||||
// Restrict access validation.
|
||||
showFieldset('id_availabilityconditionsheader', showAll ||
|
||||
isFeatureEnabled(profileType, 'availabilityconditionsheader'));
|
||||
// Tags validation.
|
||||
showFieldset('id_tagshdr', showAll || isFeatureEnabled(profileType, 'tagshdr'));
|
||||
// Competencies validation.
|
||||
showFieldset('id_competenciessection', showAll ||
|
||||
isFeatureEnabled(profileType, 'competenciessection'));
|
||||
// Completion validation.
|
||||
showFormGroup('completionattendancegroup', showAll ||
|
||||
isFeatureEnabled(profileType, 'completionattendance'));
|
||||
// Completion validation.
|
||||
showFormGroup('completionengagementgroup', showAll ||
|
||||
isFeatureEnabled(profileType, 'completionengagement'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Init the participant list
|
||||
*/
|
||||
const participantListInit = () => {
|
||||
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||
const participantList = getParticipantList();
|
||||
participantList.forEach(participant => {
|
||||
const selectionTypeValue = participant.selectiontype;
|
||||
const selectionValue = participant.selectionid;
|
||||
const selectionRole = participant.role;
|
||||
if (participant.selectiontype === 'all' ||
|
||||
typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined') {
|
||||
// Add it to the form, but don't add the delete button if it is the first item.
|
||||
participantAddToForm(selectionTypeValue, selectionValue, selectionRole, true).then();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add rows to the participant list dependingon the current selection.
|
||||
*
|
||||
* @param {string} selectionTypeValue
|
||||
* @param {string} selectionValue
|
||||
* @param {string} selectedRole
|
||||
* @param {boolean} canRemove
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {
|
||||
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||
const sviewer = await getString('mod_form_field_participant_bbb_role_viewer', 'mod_bigbluebuttonbn');
|
||||
const smoderator = await getString('mod_form_field_participant_bbb_role_moderator', 'mod_bigbluebuttonbn');
|
||||
let roles = {
|
||||
viewer: {'id': 'viewer', label: sviewer},
|
||||
moderator: {'id': 'moderator', label: smoderator}
|
||||
};
|
||||
roles[selectedRole].isselected = true;
|
||||
try {
|
||||
const listTable = document.querySelector('#participant_list_table tbody');
|
||||
const templateContext = {
|
||||
'selectiontypevalue': selectionTypeValue,
|
||||
'selectionvalue': selectionValue,
|
||||
'participanttype': participantData[selectionTypeValue].name,
|
||||
'participantvalue':
|
||||
(selectionTypeValue !== 'all') ?
|
||||
participantData[selectionTypeValue].children[selectionValue].name : null,
|
||||
'roles': Object.values(roles),
|
||||
'canRemove': canRemove
|
||||
};
|
||||
const {html, js} = await Templates.renderForPromise('mod_bigbluebuttonbn/participant_form_add', templateContext);
|
||||
const newNode = Templates.appendNodeContents(listTable, html, js)[0];
|
||||
newNode.querySelector('.participant-select').addEventListener('change', () => {
|
||||
participantListRoleUpdate(selectionTypeValue, selectionValue);
|
||||
});
|
||||
// Now add the callbacks: participantListRoleUpdate() and participantRemove().
|
||||
const removeNode = newNode.querySelector('.remove-button');
|
||||
if (removeNode) {
|
||||
removeNode
|
||||
.addEventListener('click', () => {
|
||||
participantRemove(selectionTypeValue, selectionValue);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
Notification.exception(e);
|
||||
}
|
||||
};
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update the related form element with the list value.
|
||||
*
|
||||
* @param {object} list
|
||||
*/
|
||||
const participantListUpdate = (list) => {
|
||||
const participantList = ELEMENT_SELECTOR.participantList();
|
||||
participantList.value = JSON.stringify(list);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {any}
|
||||
*/
|
||||
const getParticipantList = () => {
|
||||
const participantListValue = ELEMENT_SELECTOR.participantList().value;
|
||||
if (participantListValue) {
|
||||
return JSON.parse(participantListValue);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove participant both in the table/form and in the form element.
|
||||
*
|
||||
* @param {string} selectionTypeValue
|
||||
* @param {string} selectionValue
|
||||
*/
|
||||
const participantRemove = (selectionTypeValue, selectionValue) => {
|
||||
const pList = getParticipantList();
|
||||
const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;
|
||||
const participantListTable = ELEMENT_SELECTOR.participantTable();
|
||||
const selectionid = (selectionValue === '' ? null : selectionValue);
|
||||
for (let i = 0; i < pList.length; i++) {
|
||||
if (pList[i].selectiontype === selectionTypeValue &&
|
||||
pList[i].selectionid === selectionid) {
|
||||
pList.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// Remove from the form.
|
||||
for (let i = 0; i < participantListTable.rows.length; i++) {
|
||||
if (participantListTable.rows[i].id === id) {
|
||||
participantListTable.deleteRow(i);
|
||||
}
|
||||
}
|
||||
// Update value in the form.
|
||||
participantListUpdate(pList);
|
||||
};
|
||||
|
||||
/**
|
||||
* Role update
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {string} id
|
||||
*/
|
||||
const participantListRoleUpdate = (type, id) => {
|
||||
// Update in memory.
|
||||
const participantListRoleSelection = document.querySelector(`#participant_list_tr_${type}-${id} .participant-select`);
|
||||
const pList = getParticipantList();
|
||||
|
||||
for (var i = 0; i < pList.length; i++) {
|
||||
if (pList[i].selectiontype === type &&
|
||||
pList[i].selectionid === (id === '' ? null : parseInt(id))) {
|
||||
pList[i].role = participantListRoleSelection.value;
|
||||
}
|
||||
}
|
||||
// Update in the form.
|
||||
participantListUpdate(pList);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add participant from the currently selected options
|
||||
*/
|
||||
const participantAddFromCurrentSelection = () => {
|
||||
let selectionType = ELEMENT_SELECTOR.participantSelectionType();
|
||||
let selection = ELEMENT_SELECTOR.participantSelection();
|
||||
const pList = getParticipantList();
|
||||
// Lookup to see if it has been added already.
|
||||
for (var i = 0; i < pList.length; i++) {
|
||||
if (pList[i].selectiontype === selectionType.value &&
|
||||
pList[i].selectionid === selection.value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
pList.push({
|
||||
"selectiontype": selectionType.value,
|
||||
"selectionid": selection.value,
|
||||
"role": "viewer"
|
||||
});
|
||||
// Add it to the form.
|
||||
participantAddToForm(selectionType.value, selection.value, 'viewer', true).then();
|
||||
// Update in the form.
|
||||
participantListUpdate(pList);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update selectable options when changing types
|
||||
*
|
||||
* @param {HTMLNode} currentTypeSelect
|
||||
*/
|
||||
const updateSelectionFromType = (currentTypeSelect) => {
|
||||
const createNewOption = (selectItem, label, value) => {
|
||||
const option = document.createElement('option');
|
||||
option.text = label;
|
||||
option.value = value;
|
||||
|
||||
selectItem.add(option);
|
||||
};
|
||||
|
||||
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||
// Clear all selection items.
|
||||
const participantSelect = ELEMENT_SELECTOR.participantSelection();
|
||||
while (participantSelect.firstChild) {
|
||||
participantSelect.removeChild(participantSelect.firstChild);
|
||||
}
|
||||
// Add options depending on the selection.
|
||||
if (currentTypeSelect.selectedIndex !== -1) {
|
||||
const options = Object.values(participantData[currentTypeSelect.value].children);
|
||||
options.forEach(option => {
|
||||
createNewOption(participantSelect, option.name, option.id);
|
||||
});
|
||||
|
||||
if (currentTypeSelect.value === 'all') {
|
||||
createNewOption(participantSelect, '---------------', 'all');
|
||||
currentTypeSelect.disabled = true;
|
||||
} else {
|
||||
currentTypeSelect.disabled = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,416 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* JS for the recordings page on mod_bigbluebuttonbn plugin.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/recordings
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import * as repository from './repository';
|
||||
import {exception as displayException} from 'core/notification';
|
||||
import {get_strings as getStrings} from 'core/str';
|
||||
import {addIconToContainerWithPromise} from 'core/loadingicon';
|
||||
import ModalFactory from 'core/modal_factory';
|
||||
import ModalEvents from 'core/modal_events';
|
||||
import * as Str from 'core/str';
|
||||
import Pending from 'core/pending';
|
||||
|
||||
const stringList = [
|
||||
'view_recording_yui_first',
|
||||
'view_recording_yui_prev',
|
||||
'view_recording_yui_next',
|
||||
'view_recording_yui_last',
|
||||
'view_recording_yui_page',
|
||||
'view_recording_yui_go',
|
||||
'view_recording_yui_rows',
|
||||
'view_recording_yui_show_all',
|
||||
];
|
||||
|
||||
const getStringsForYui = () => {
|
||||
const stringMap = stringList.map(key => {
|
||||
return {
|
||||
key,
|
||||
component: 'bigbluebuttonbn',
|
||||
};
|
||||
});
|
||||
|
||||
return getStrings(stringMap)
|
||||
.then(([first, prev, next, last, goToLabel, goToAction, perPage, showAll]) => {
|
||||
return {
|
||||
first,
|
||||
prev,
|
||||
next,
|
||||
last,
|
||||
goToLabel,
|
||||
goToAction,
|
||||
perPage,
|
||||
showAll,
|
||||
};
|
||||
})
|
||||
.catch();
|
||||
};
|
||||
|
||||
const getYuiInstance = lang => new Promise(resolve => {
|
||||
// eslint-disable-next-line
|
||||
YUI({
|
||||
lang,
|
||||
}).use('intl', 'datatable', 'datatable-sort', 'datatable-paginator', 'datatype-number', Y => {
|
||||
resolve(Y);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Format the supplied date per the specified locale.
|
||||
*
|
||||
* @param {string} locale
|
||||
* @param {array} dateList
|
||||
* @returns {array}
|
||||
*/
|
||||
const formatDates = (locale, dateList) => dateList.map(row => {
|
||||
const date = new Date(row.date);
|
||||
row.date = date.toLocaleDateString(locale, {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
return row;
|
||||
});
|
||||
|
||||
/**
|
||||
* Format response data for the table.
|
||||
*
|
||||
* @param {string} response JSON-encoded table data
|
||||
* @returns {array}
|
||||
*/
|
||||
const getFormattedData = response => {
|
||||
const recordingData = response.tabledata;
|
||||
const rowData = JSON.parse(recordingData.data);
|
||||
|
||||
return formatDates(recordingData.locale, rowData);
|
||||
};
|
||||
|
||||
const getTableNode = tableSelector => document.querySelector(tableSelector);
|
||||
|
||||
const fetchRecordingData = tableSelector => {
|
||||
const tableNode = getTableNode(tableSelector);
|
||||
|
||||
if (tableNode.dataset.importMode) {
|
||||
return repository.fetchRecordingsToImport(
|
||||
tableNode.dataset.bbbid,
|
||||
tableNode.dataset.bbbSourceInstanceId,
|
||||
tableNode.dataset.bbbSourceCourseId,
|
||||
tableNode.dataset.tools,
|
||||
tableNode.dataset.groupId
|
||||
);
|
||||
} else {
|
||||
return repository.fetchRecordings(
|
||||
tableNode.dataset.bbbid,
|
||||
tableNode.dataset.tools,
|
||||
tableNode.dataset.groupId
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the data table functinos for the specified table.
|
||||
*
|
||||
* @param {String} tableId in which we will display the table
|
||||
* @param {String} searchFormId The Id of the relate.
|
||||
* @param {Object} dataTable
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
const getDataTableFunctions = (tableId, searchFormId, dataTable) => {
|
||||
const tableNode = getTableNode(tableId);
|
||||
const bbbid = tableNode.dataset.bbbid;
|
||||
|
||||
const updateTableFromResponse = response => {
|
||||
if (!response || !response.status) {
|
||||
// There was no output at all.
|
||||
return;
|
||||
}
|
||||
|
||||
dataTable.get('data').reset(getFormattedData(response));
|
||||
dataTable.set(
|
||||
'currentData',
|
||||
dataTable.get('data')
|
||||
);
|
||||
|
||||
const currentFilter = dataTable.get('currentFilter');
|
||||
if (currentFilter) {
|
||||
filterByText(currentFilter);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshTableData = () => fetchRecordingData(tableId).then(updateTableFromResponse);
|
||||
|
||||
const filterByText = value => {
|
||||
const dataModel = dataTable.get('currentData');
|
||||
dataTable.set('currentFilter', value);
|
||||
|
||||
const escapedRegex = value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
const rsearch = new RegExp(`<span>.*?${escapedRegex}.*?</span>`, 'i');
|
||||
|
||||
dataTable.set('data', dataModel.filter({asList: true}, item => {
|
||||
const name = item.get('recording');
|
||||
if (name && rsearch.test(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const description = item.get('description');
|
||||
return description && rsearch.test(description);
|
||||
}));
|
||||
};
|
||||
|
||||
const requestAction = (element) => {
|
||||
const getDataFromAction = (element, dataType) => {
|
||||
const dataElement = element.closest(`[data-${dataType}]`);
|
||||
if (dataElement) {
|
||||
return dataElement.dataset[dataType];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const elementData = element.dataset;
|
||||
const payload = {
|
||||
bigbluebuttonbnid: bbbid,
|
||||
recordingid: getDataFromAction(element, 'recordingid'),
|
||||
additionaloptions: getDataFromAction(element, 'additionaloptions'),
|
||||
action: elementData.action,
|
||||
};
|
||||
// Slight change for import, for additional options.
|
||||
if (!payload.additionaloptions) {
|
||||
payload.additionaloptions = {};
|
||||
}
|
||||
if (elementData.action === 'import') {
|
||||
const bbbsourceid = getDataFromAction(element, 'source-instance-id');
|
||||
const bbbcourseid = getDataFromAction(element, 'source-course-id');
|
||||
if (!payload.additionaloptions) {
|
||||
payload.additionaloptions = {};
|
||||
}
|
||||
payload.additionaloptions.sourceid = bbbsourceid ? bbbsourceid : 0;
|
||||
payload.additionaloptions.bbbcourseid = bbbcourseid ? bbbcourseid : 0;
|
||||
}
|
||||
// Now additional options should be a json string.
|
||||
payload.additionaloptions = JSON.stringify(payload.additionaloptions);
|
||||
if (element.dataset.requireConfirmation === "1") {
|
||||
// Create the confirmation dialogue.
|
||||
return new Promise((resolve) =>
|
||||
ModalFactory.create({
|
||||
title: Str.get_string('confirm'),
|
||||
body: recordingConfirmationMessage(payload),
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(async(modal) => {
|
||||
modal.setSaveButtonText(await Str.get_string('ok', 'moodle'));
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, () => {
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, () => {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
modal.show();
|
||||
|
||||
return modal;
|
||||
}).catch(Notification.exception)
|
||||
).then((proceed) =>
|
||||
proceed ? repository.updateRecording(payload) : () => null
|
||||
);
|
||||
} else {
|
||||
return repository.updateRecording(payload);
|
||||
}
|
||||
};
|
||||
|
||||
const recordingConfirmationMessage = async(data) => {
|
||||
|
||||
const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`);
|
||||
const recordingType = await Str.get_string(
|
||||
playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording',
|
||||
'bigbluebuttonbn'
|
||||
);
|
||||
|
||||
const confirmation = await Str.get_string(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType);
|
||||
|
||||
if (data.action === 'import') {
|
||||
return confirmation;
|
||||
}
|
||||
|
||||
// If it has associated links imported in a different course/activity, show that in confirmation dialog.
|
||||
const associatedLinkCount = document.querySelector(`a#recording-${data.action}-${data.recordingid}`)?.dataset?.links;
|
||||
if (!associatedLinkCount || associatedLinkCount === 0) {
|
||||
return confirmation;
|
||||
}
|
||||
|
||||
const confirmationWarning = await Str.get_string(
|
||||
associatedLinkCount === 1
|
||||
? `view_recording_${data.action}_confirmation_warning_p`
|
||||
: `view_recording_${data.action}_confirmation_warning_s`,
|
||||
'bigbluebuttonbn',
|
||||
associatedLinkCount
|
||||
);
|
||||
|
||||
return confirmationWarning + '\n\n' + confirmation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process an action event.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
const processAction = e => {
|
||||
const popoutLink = e.target.closest('[data-action="play"]');
|
||||
if (popoutLink) {
|
||||
e.preventDefault();
|
||||
|
||||
const videoPlayer = window.open('', '_blank');
|
||||
videoPlayer.opener = null;
|
||||
videoPlayer.location.href = popoutLink.href;
|
||||
// TODO send a recording viewed event when this event will be implemented.
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch any clicked anchor.
|
||||
const clickedLink = e.target.closest('a[data-action]');
|
||||
if (clickedLink && !clickedLink.classList.contains('disabled')) {
|
||||
e.preventDefault();
|
||||
|
||||
// Create a spinning icon on the table.
|
||||
const iconPromise = addIconToContainerWithPromise(dataTable.get('boundingBox').getDOMNode());
|
||||
|
||||
requestAction(clickedLink)
|
||||
.then(refreshTableData)
|
||||
.catch(displayException)
|
||||
.then(iconPromise.resolve)
|
||||
.catch();
|
||||
}
|
||||
};
|
||||
|
||||
const processSearchSubmission = e => {
|
||||
// Prevent the default action.
|
||||
e.preventDefault();
|
||||
const parentNode = e.target.closest('div[role=search]');
|
||||
const searchInput = parentNode.querySelector('input[name=search]');
|
||||
filterByText(searchInput.value);
|
||||
};
|
||||
|
||||
const registerEventListeners = () => {
|
||||
// Add event listeners to the table boundingBox.
|
||||
const boundingBox = dataTable.get('boundingBox').getDOMNode();
|
||||
boundingBox.addEventListener('click', processAction);
|
||||
|
||||
// Setup the search from handlers.
|
||||
const searchForm = document.querySelector(searchFormId);
|
||||
if (searchForm) {
|
||||
const searchButton = document.querySelector(searchFormId + ' button');
|
||||
searchButton.addEventListener('click', processSearchSubmission);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
filterByText,
|
||||
refreshTableData,
|
||||
registerEventListeners,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup the data table for the specified BBB instance.
|
||||
*
|
||||
* @param {String} tableId in which we will display the table
|
||||
* @param {String} searchFormId The Id of the relate.
|
||||
* @param {object} response The response from the data request
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const setupDatatable = (tableId, searchFormId, response) => {
|
||||
if (!response) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!response.status) {
|
||||
// Something failed. Continue to show the plain output.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const recordingData = response.tabledata;
|
||||
|
||||
const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable');
|
||||
return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()])
|
||||
.then(([yuiInstance, strings]) => {
|
||||
// Add the fetched strings to the YUI Instance.
|
||||
yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings});
|
||||
|
||||
return yuiInstance;
|
||||
})
|
||||
.then(yuiInstance => {
|
||||
const tableData = getFormattedData(response);
|
||||
yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view',yuiInstance.DataTable.Paginator.View, [],{
|
||||
_modelChange : function (e) {
|
||||
var changed = e.changed,
|
||||
totalItems = (changed && changed.totalItems);
|
||||
if (totalItems) {
|
||||
this._updateControlsUI(e.target.get('page'));
|
||||
}
|
||||
}
|
||||
});
|
||||
const dataTable = new yuiInstance.DataTable({
|
||||
paginatorView: "RecordsPaginatorView",
|
||||
width: "1195px",
|
||||
columns: recordingData.columns,
|
||||
data: tableData,
|
||||
rowsPerPage: 10,
|
||||
paginatorLocation: ['header', 'footer'],
|
||||
autoSync: true
|
||||
});
|
||||
return dataTable;
|
||||
})
|
||||
.then(dataTable => {
|
||||
dataTable.render(tableId);
|
||||
const {registerEventListeners} = getDataTableFunctions(
|
||||
tableId,
|
||||
searchFormId,
|
||||
dataTable);
|
||||
registerEventListeners();
|
||||
return dataTable;
|
||||
})
|
||||
.then(dataTable => {
|
||||
pendingPromise.resolve();
|
||||
return dataTable;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise recordings code.
|
||||
*
|
||||
* @method init
|
||||
* @param {String} tableId in which we will display the table
|
||||
* @param {String} searchFormId The Id of the relate.
|
||||
*/
|
||||
export const init = (tableId, searchFormId) => {
|
||||
fetchRecordingData(tableId)
|
||||
.then(response => setupDatatable(tableId, searchFormId, response))
|
||||
.catch(displayException);
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Repository to perform WS calls for mod_bigbluebuttonbn.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/repository
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {call as fetchMany} from 'core/ajax';
|
||||
|
||||
/**
|
||||
* Fetch the list of recordings from the server.
|
||||
*
|
||||
* @param {Number} bigbluebuttonbnid The instance ID
|
||||
* @param {String} tools the set of tools to display
|
||||
* @param {number} groupid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const fetchRecordings = (bigbluebuttonbnid, tools, groupid) => {
|
||||
const args = {
|
||||
bigbluebuttonbnid,
|
||||
tools,
|
||||
};
|
||||
|
||||
if (groupid) {
|
||||
args.groupid = groupid;
|
||||
}
|
||||
|
||||
return fetchMany([{methodname: 'mod_bigbluebuttonbn_get_recordings', args}])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the list of recordings from the server that can be imported.
|
||||
*
|
||||
* @param {Number} destinationinstanceid The destination instance ID
|
||||
* @param {Number} sourcebigbluebuttonbnid The original instance ID
|
||||
* @param {Number} sourcecourseid The destination instance ID
|
||||
* @param {String} tools the set of tools to display
|
||||
* @param {number} groupid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const fetchRecordingsToImport = (
|
||||
destinationinstanceid,
|
||||
sourcebigbluebuttonbnid,
|
||||
sourcecourseid,
|
||||
tools,
|
||||
groupid
|
||||
) => {
|
||||
const args = {
|
||||
destinationinstanceid,
|
||||
sourcebigbluebuttonbnid,
|
||||
sourcecourseid,
|
||||
tools,
|
||||
};
|
||||
|
||||
if (groupid) {
|
||||
args.groupid = groupid;
|
||||
}
|
||||
|
||||
return fetchMany([{methodname: 'mod_bigbluebuttonbn_get_recordings_to_import', args}])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform an update on a single recording.
|
||||
*
|
||||
* @param {object} args The instance ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const updateRecording = args => fetchMany([
|
||||
{
|
||||
methodname: 'mod_bigbluebuttonbn_update_recording',
|
||||
args,
|
||||
}
|
||||
])[0];
|
||||
|
||||
/**
|
||||
* End the Meeting
|
||||
*
|
||||
* @param {number} bigbluebuttonbnid
|
||||
* @param {number} groupid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const endMeeting = (bigbluebuttonbnid, groupid) => fetchMany([
|
||||
{
|
||||
methodname: 'mod_bigbluebuttonbn_end_meeting',
|
||||
args: {
|
||||
bigbluebuttonbnid,
|
||||
groupid
|
||||
},
|
||||
}
|
||||
])[0];
|
||||
|
||||
/**
|
||||
* Validate completion.
|
||||
*
|
||||
* @param {object} args
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const completionValidate = args => fetchMany([
|
||||
{
|
||||
methodname: 'mod_bigbluebuttonbn_completion_validate',
|
||||
args,
|
||||
}
|
||||
])[0];
|
||||
|
||||
|
||||
/**
|
||||
* Fetch meeting info for the specified meeting.
|
||||
*
|
||||
* @param {number} bigbluebuttonbnid
|
||||
* @param {number} groupid
|
||||
* @param {boolean} [updatecache=false]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const getMeetingInfo = (bigbluebuttonbnid, groupid, updatecache = false) => fetchMany([
|
||||
{
|
||||
methodname: 'mod_bigbluebuttonbn_meeting_info',
|
||||
args: {
|
||||
bigbluebuttonbnid,
|
||||
groupid,
|
||||
updatecache,
|
||||
},
|
||||
}
|
||||
])[0];
|
||||
@@ -0,0 +1,83 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* JS actions for the rooms page for mod_bigbluebuttonbn.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/rooms
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import './actions';
|
||||
import * as repository from './repository';
|
||||
import * as roomUpdater from './roomupdater';
|
||||
import {
|
||||
exception as displayException,
|
||||
fetchNotifications,
|
||||
} from 'core/notification';
|
||||
|
||||
import {eventTypes, notifyCurrentSessionEnded} from './events';
|
||||
|
||||
export const init = (bigbluebuttonbnid) => {
|
||||
const completionElement = document.querySelector('a[href*=completion_validate]');
|
||||
if (completionElement) {
|
||||
completionElement.addEventListener("click", () => {
|
||||
repository.completionValidate(bigbluebuttonbnid).catch(displayException);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('click', e => {
|
||||
const joinButton = e.target.closest('[data-action="join"]');
|
||||
if (joinButton) {
|
||||
roomUpdater.start();
|
||||
window.open(joinButton.href, 'bigbluebutton_conference');
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener(eventTypes.sessionEnded, () => {
|
||||
roomUpdater.stop();
|
||||
roomUpdater.updateRoom();
|
||||
fetchNotifications();
|
||||
});
|
||||
|
||||
window.addEventListener(eventTypes.currentSessionEnded, () => {
|
||||
roomUpdater.stop();
|
||||
roomUpdater.updateRoom();
|
||||
fetchNotifications();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle autoclosing of the window.
|
||||
*/
|
||||
const autoclose = () => {
|
||||
window.opener.setTimeout(() => {
|
||||
roomUpdater.updateRoom(true);
|
||||
}, 5000);
|
||||
window.removeEventListener('onbeforeunload', autoclose);
|
||||
};
|
||||
|
||||
/**
|
||||
* Auto close child windows when clicking the End meeting button.
|
||||
*/
|
||||
export const setupWindowAutoClose = () => {
|
||||
notifyCurrentSessionEnded(window.opener);
|
||||
window.addEventListener('onbeforeunload', autoclose);
|
||||
|
||||
window.close(); // This does not work as scripts can only close windows that are opened by themselves.
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* JS room updater.
|
||||
*
|
||||
* @module mod_bigbluebuttonbn/roomupdater
|
||||
* @copyright 2021 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Templates from "core/templates";
|
||||
import {exception as displayException} from 'core/notification';
|
||||
import {getMeetingInfo} from './repository';
|
||||
|
||||
const timeout = 1000;
|
||||
const maxFactor = 10;
|
||||
|
||||
let updateCount = 0;
|
||||
let updateFactor = 1;
|
||||
let timerReference = null;
|
||||
let timerRunning = false;
|
||||
|
||||
const resetValues = () => {
|
||||
updateCount = 0;
|
||||
updateFactor = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start the information poller.
|
||||
*/
|
||||
export const start = () => {
|
||||
timerRunning = true;
|
||||
timerReference = setTimeout(() => poll(), timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the room updater.
|
||||
*/
|
||||
export const stop = () => {
|
||||
timerRunning = false;
|
||||
if (timerReference) {
|
||||
clearInterval(timerReference);
|
||||
timerReference = null;
|
||||
}
|
||||
|
||||
resetValues();
|
||||
};
|
||||
|
||||
const poll = () => {
|
||||
if (!timerRunning) {
|
||||
// The poller has been stopped.
|
||||
return;
|
||||
}
|
||||
if ((updateCount % updateFactor) === 0) {
|
||||
updateRoom(true)
|
||||
.then(() => {
|
||||
if (updateFactor >= maxFactor) {
|
||||
updateFactor = 1;
|
||||
} else {
|
||||
updateFactor++;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
})
|
||||
.catch()
|
||||
.then(() => {
|
||||
timerReference = setTimeout(() => poll(), timeout);
|
||||
return;
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the room information.
|
||||
*
|
||||
* @param {boolean} [updatecache=false]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const updateRoom = (updatecache = false) => {
|
||||
const bbbRoomViewElement = document.getElementById('bbb-room-view');
|
||||
const bbbId = bbbRoomViewElement.dataset.bbbId;
|
||||
const groupId = bbbRoomViewElement.dataset.groupId;
|
||||
return getMeetingInfo(bbbId, groupId, updatecache)
|
||||
.then(data => {
|
||||
// Just make sure we have the right information for the template.
|
||||
data.haspresentations = false;
|
||||
if (data.presentations && data.presentations.length) {
|
||||
data.haspresentations = true;
|
||||
}
|
||||
return Templates.renderForPromise('mod_bigbluebuttonbn/room_view', data);
|
||||
})
|
||||
.then(({html, js}) => Templates.replaceNodeContents(bbbRoomViewElement, html, js))
|
||||
.catch(displayException);
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for backup BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Fred Dixon (ffdixon [at] blindsidenetworks [dt] com)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/mod/bigbluebuttonbn/backup/moodle2/backup_bigbluebuttonbn_stepslib.php');
|
||||
|
||||
/**
|
||||
* Backup task that provides all the settings and steps to perform one complete backup of the activity.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class backup_bigbluebuttonbn_activity_task extends backup_activity_task
|
||||
{
|
||||
/**
|
||||
* Define (add) particular settings this activity can have.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function define_my_settings() {
|
||||
// No particular settings for this activity.
|
||||
}
|
||||
|
||||
/**
|
||||
* Define (add) particular steps this activity can have.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function define_my_steps() {
|
||||
// Choice only has one structure step.
|
||||
$this->add_step(new backup_bigbluebuttonbn_activity_structure_step('bigbluebuttonbn_structure', 'bigbluebuttonbn.xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Code the transformations to perform in the activity in order to get transportable (encoded) links.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encode_content_links($content) {
|
||||
global $CFG;
|
||||
|
||||
$base = preg_quote($CFG->wwwroot.'/mod/bigbluebuttonbn', '#');
|
||||
|
||||
// Link to the list of bigbluebuttonbns.
|
||||
$pattern = '#('.$base."\/index.php\?id\=)([0-9]+)#";
|
||||
$content = preg_replace($pattern, '$@BIGBLUEBUTTONBNINDEX*$2@$', $content);
|
||||
|
||||
// Link to bigbluebuttonbn view by moduleid.
|
||||
$pattern = '#('.$base."\/view.php\?id\=)([0-9]+)#";
|
||||
$content = preg_replace($pattern, '$@BIGBLUEBUTTONBNVIEWBYID*$2@$', $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for the structure used for backup BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Fred Dixon (ffdixon [at] blindsidenetworks [dt] com)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Define all the backup steps that will be used by the backup_bigbluebuttonbn_activity_task.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class backup_bigbluebuttonbn_activity_structure_step extends backup_activity_structure_step {
|
||||
/**
|
||||
* Define the complete bigbluebuttonbn structure for backup, with file and id annotations.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function define_structure() {
|
||||
|
||||
// To know if we are including userinfo.
|
||||
$userinfo = $this->get_setting_value('userinfo');
|
||||
|
||||
// Define each element separated.
|
||||
$bigbluebuttonbn = new backup_nested_element('bigbluebuttonbn', ['id'], [
|
||||
'type', 'course', 'name', 'intro', 'introformat', 'meetingid',
|
||||
'moderatorpass', 'viewerpass', 'wait', 'record', 'recordallfromstart',
|
||||
'recordhidebutton', 'welcome', 'voicebridge', 'openingtime', 'closingtime', 'timecreated',
|
||||
'timemodified', 'presentation', 'participants', 'userlimit',
|
||||
'recordings_html', 'recordings_deleted', 'recordings_imported', 'recordings_preview',
|
||||
'clienttype', 'muteonstart', 'completionattendance',
|
||||
'completionengagementchats', 'completionengagementtalks', 'completionengagementraisehand',
|
||||
'completionengagementpollvotes', 'completionengagementemojis']);
|
||||
|
||||
$logs = new backup_nested_element('logs');
|
||||
|
||||
$log = new backup_nested_element('log', ['id'], [
|
||||
'courseid', 'bigbluebuttonbnid', 'userid', 'timecreated', 'meetingid', 'log', 'meta']);
|
||||
|
||||
$recordings = new backup_nested_element('recordings');
|
||||
|
||||
$recording = new backup_nested_element('recording', ['id'], [
|
||||
'courseid', 'bigbluebuttonbnid', 'groupid', 'recordingid', 'headlesss', 'imported', 'status', 'importeddata',
|
||||
'timecreated']);
|
||||
|
||||
// Build the tree.
|
||||
$bigbluebuttonbn->add_child($logs);
|
||||
$logs->add_child($log);
|
||||
$bigbluebuttonbn->add_child($recordings);
|
||||
$recordings->add_child($recording);
|
||||
|
||||
// Define sources.
|
||||
$bigbluebuttonbn->set_source_table('bigbluebuttonbn', ['id' => backup::VAR_ACTIVITYID]);
|
||||
|
||||
// This source definition only happen if we are including user info.
|
||||
if ($userinfo) {
|
||||
$log->set_source_table('bigbluebuttonbn_logs', ['bigbluebuttonbnid' => backup::VAR_PARENTID]);
|
||||
$recording->set_source_table('bigbluebuttonbn_recordings', ['bigbluebuttonbnid' => backup::VAR_PARENTID]);
|
||||
}
|
||||
|
||||
// Define id annotations.
|
||||
$log->annotate_ids('user', 'userid');
|
||||
|
||||
// Define file annotations.
|
||||
$bigbluebuttonbn->annotate_files('mod_bigbluebuttonbn', 'intro', null);
|
||||
|
||||
// Return the root element (bigbluebuttonbn), wrapped into standard activity structure.
|
||||
return $this->prepare_activity_structure($bigbluebuttonbn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for restore BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Fred Dixon (ffdixon [at] blindsidenetworks [dt] com)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/mod/bigbluebuttonbn/backup/moodle2/restore_bigbluebuttonbn_stepslib.php');
|
||||
|
||||
/**
|
||||
* Restore task that provides all the settings and steps to perform one complete restore of the activity.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class restore_bigbluebuttonbn_activity_task extends restore_activity_task
|
||||
{
|
||||
/**
|
||||
* Define (add) particular settings this activity can have.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function define_my_settings() {
|
||||
// No particular settings for this activity.
|
||||
}
|
||||
|
||||
/**
|
||||
* Define (add) particular steps this activity can have.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function define_my_steps() {
|
||||
// BigBlueButtonBN only has one structure step.
|
||||
$this->add_step(new restore_bigbluebuttonbn_activity_structure_step('bigbluebuttonbn_structure', 'bigbluebuttonbn.xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the contents in the activity that must be processed by the link decoder.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function define_decode_contents() {
|
||||
$contents = [];
|
||||
$contents[] = new restore_decode_content('bigbluebuttonbn', ['intro'], 'bigbluebuttonbn');
|
||||
$contents[] = new restore_decode_content('bigbluebuttonbn_logs', ['log'], 'bigbluebuttonbn_logs');
|
||||
$contents[] = new restore_decode_content('bigbluebuttonbn_recordings', ['importeddata'], 'bigbluebuttonbn_recordings');
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the decoding rules for links belonging to the activity to be executed by the link decoder.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function define_decode_rules() {
|
||||
$rules = [];
|
||||
$rules[] = new restore_decode_rule('BIGBLUEBUTTONBNVIEWBYID', '/mod/bigbluebuttonbn/view.php?id=$1', 'course_module');
|
||||
$rules[] = new restore_decode_rule('BIGBLUEBUTTONBNINDEX', '/mod/bigbluebuttonbn/index.php?id=$1', 'course');
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the restoring rules for logs belonging to the activity to be executed by the link decoder.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function define_restore_log_rules() {
|
||||
$rules = [];
|
||||
$rules[] = new restore_log_rule('bigbluebuttonbn', 'add', 'view.php?id={course_module}', '{bigbluebuttonbn}');
|
||||
$rules[] = new restore_log_rule('bigbluebuttonbn', 'update', 'view.php?id={course_module}', '{bigbluebuttonbn}');
|
||||
$rules[] = new restore_log_rule('bigbluebuttonbn', 'view', 'view.php?id={course_module}', '{bigbluebuttonbn}');
|
||||
$rules[] = new restore_log_rule('bigbluebuttonbn', 'report', 'report.php?id={course_module}', '{bigbluebuttonbn}');
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the restoring rules for course associated to the activity to be executed by the link decoder.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function define_restore_log_rules_for_course() {
|
||||
$rules = [];
|
||||
$rules[] = new restore_log_rule('bigbluebuttonbn', 'view all', 'index.php?id={course}', null);
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for the structure used for restore BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Fred Dixon (ffdixon [at] blindsidenetworks [dt] com)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Define all the restore steps that will be used by the restore_url_activity_task.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class restore_bigbluebuttonbn_activity_structure_step extends restore_activity_structure_step {
|
||||
/**
|
||||
* Structure step to restore one bigbluebuttonbn activity.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function define_structure() {
|
||||
$paths = [];
|
||||
$paths[] = new restore_path_element('bigbluebuttonbn', '/activity/bigbluebuttonbn');
|
||||
$paths[] = new restore_path_element('bigbluebuttonbn_logs', '/activity/bigbluebuttonbn/logs/log');
|
||||
$paths[] = new restore_path_element('bigbluebuttonbn_recordings', '/activity/bigbluebuttonbn/recordings/recording');
|
||||
// Return the paths wrapped into standard activity structure.
|
||||
return $this->prepare_activity_structure($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a bigbluebuttonbn restore.
|
||||
*
|
||||
* @param array $data The data in object form
|
||||
* @return void
|
||||
*/
|
||||
protected function process_bigbluebuttonbn(array $data) {
|
||||
global $DB;
|
||||
$data = (object) $data;
|
||||
$data->course = $this->get_courseid();
|
||||
$data->timemodified = $this->apply_date_offset($data->timemodified);
|
||||
// Insert the bigbluebuttonbn record.
|
||||
$newitemid = $DB->insert_record('bigbluebuttonbn', $data);
|
||||
// Immediately after inserting "activity" record, call this.
|
||||
$this->apply_activity_instance($newitemid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a bigbluebuttonbn_logs restore (additional table).
|
||||
*
|
||||
* @param array $data The data in object form
|
||||
* @return void
|
||||
*/
|
||||
protected function process_bigbluebuttonbn_logs(array $data) {
|
||||
global $DB;
|
||||
$data = (object) $data;
|
||||
// Apply modifications.
|
||||
$data->courseid = $this->get_mappingid('course', $data->courseid);
|
||||
$data->bigbluebuttonbnid = $this->get_new_parentid('bigbluebuttonbn');
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
// Insert the bigbluebuttonbn_logs record.
|
||||
$newitemid = $DB->insert_record('bigbluebuttonbn_logs', $data);
|
||||
// Immediately after inserting associated record, call this.
|
||||
$this->set_mapping('bigbluebuttonbn_logs', $data->id, $newitemid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a bigbluebuttonbn_recordings restore (additional table).
|
||||
*
|
||||
* @param array $data The data in object form
|
||||
* @return void
|
||||
*/
|
||||
protected function process_bigbluebuttonbn_recordings(array $data) {
|
||||
global $DB;
|
||||
$data = (object) $data;
|
||||
// Apply modifications.
|
||||
$data->courseid = $this->get_mappingid('course', $data->courseid);
|
||||
$data->bigbluebuttonbnid = $this->get_new_parentid('bigbluebuttonbn');
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
// Insert the bigbluebuttonbn_recordings record.
|
||||
$newitemid = $DB->insert_record('bigbluebuttonbn_recordings', $data);
|
||||
// Immediately after inserting associated record, call this.
|
||||
$this->set_mapping('bigbluebuttonbn_recordings', $data->id, $newitemid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to be executed after the restore is completed
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function after_execute() {
|
||||
// Add bigbluebuttonbn related files, no need to match by itemname (just internally handled context).
|
||||
$this->add_related_files('mod_bigbluebuttonbn', 'intro', null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Intermediator for handling requests from the BigBlueButton server.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
* @author Darko Miletic (darko.miletic [at] gmail [dt] com)
|
||||
*/
|
||||
|
||||
// We should not have any require login or MOODLE_INTERNAL Check in this file.
|
||||
// phpcs:disable moodle.Files.MoodleInternal.MoodleInternalGlobalState,moodle.Files.RequireLogin.Missing
|
||||
require(__DIR__ . '/../../config.php');
|
||||
|
||||
use mod_bigbluebuttonbn\broker;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
|
||||
global $PAGE, $USER, $CFG, $SESSION, $DB;
|
||||
|
||||
$params = $_REQUEST;
|
||||
|
||||
$broker = new broker();
|
||||
$error = $broker->validate_parameters($params);
|
||||
if (!empty($error)) {
|
||||
header('HTTP/1.0 400 Bad Request. ' . $error);
|
||||
return;
|
||||
}
|
||||
|
||||
$action = $params['action'];
|
||||
|
||||
$instance = instance::get_from_instanceid($params['bigbluebuttonbn']);
|
||||
if (empty($instance)) {
|
||||
header('HTTP/1.0 410 Gone. The activity may have been deleted');
|
||||
return;
|
||||
}
|
||||
|
||||
$PAGE->set_context($instance->get_context());
|
||||
|
||||
try {
|
||||
switch (strtolower($action)) {
|
||||
case 'recording_ready':
|
||||
broker::recording_ready($instance, $params);
|
||||
return;
|
||||
case 'meeting_events':
|
||||
// When meeting_events callback is implemented by BigBlueButton, Moodle receives a POST request
|
||||
// which is processed in the function using super globals.
|
||||
meeting::meeting_events($instance);
|
||||
return;
|
||||
}
|
||||
header("HTTP/1.0 400 Bad request. The action '{$action}' does not exist");
|
||||
} catch (Exception $e) {
|
||||
header('HTTP/1.0 500 Internal Server Error. ' . $e->getMessage());
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* View for BigBlueButton interaction.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
use core\notification;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\logger;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
require_once(dirname(dirname(dirname(__FILE__))) . '/config.php');
|
||||
|
||||
global $SESSION, $PAGE, $CFG, $DB, $USER, $OUTPUT;
|
||||
|
||||
$action = required_param('action', PARAM_TEXT);
|
||||
$id = optional_param('id', 0, PARAM_INT);
|
||||
$bn = optional_param('bn', 0, PARAM_INT);
|
||||
$rid = optional_param('rid', '', PARAM_TEXT);
|
||||
$rtype = optional_param('rtype', 'presentation', PARAM_TEXT);
|
||||
$errors = optional_param('errors', '', PARAM_TEXT);
|
||||
$timeline = optional_param('timeline', 0, PARAM_INT);
|
||||
$index = optional_param('index', 0, PARAM_INT);
|
||||
$group = optional_param('group', -1, PARAM_INT);
|
||||
|
||||
// Get the bbb instance from either the cmid (id), or the instanceid (bn).
|
||||
if ($id) {
|
||||
$instance = instance::get_from_cmid($id);
|
||||
} else {
|
||||
if ($bn) {
|
||||
$instance = instance::get_from_instanceid($bn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$instance) {
|
||||
throw new moodle_exception('view_error_url_missing_parameters', plugin::COMPONENT);
|
||||
}
|
||||
|
||||
$cm = $instance->get_cm();
|
||||
$course = $instance->get_course();
|
||||
$bigbluebuttonbn = $instance->get_instance_data();
|
||||
$context = $instance->get_context();
|
||||
|
||||
require_login($course, true, $cm);
|
||||
|
||||
// Note : this uses the group optional_param as a value to decide which groupid.
|
||||
$groupid = groups_get_activity_group($cm, true) ?: null;
|
||||
if ($groupid) {
|
||||
$instance->set_group_id($groupid);
|
||||
}
|
||||
|
||||
// Print the page header.
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_url('/mod/bigbluebuttonbn/bbb_view.php', ['id' => $cm->id, 'bigbluebuttonbn' => $bigbluebuttonbn->id]);
|
||||
$PAGE->set_title(format_string($bigbluebuttonbn->name));
|
||||
$PAGE->set_cacheable(false);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->blocks->show_only_fake_blocks();
|
||||
|
||||
switch (strtolower($action)) {
|
||||
case 'logout':
|
||||
if (isset($errors) && $errors != '') {
|
||||
$errors = (array) json_decode(urldecode($errors));
|
||||
$msgerrors = '';
|
||||
foreach ($errors as $error) {
|
||||
$msgerrors .= html_writer::tag('p', $error->{'message'}, ['class' => 'alert alert-danger']) . "\n";
|
||||
}
|
||||
throw new moodle_exception('view_error_bigbluebutton', 'bigbluebuttonbn',
|
||||
$CFG->wwwroot . '/mod/bigbluebuttonbn/view.php?id=' . $id, $msgerrors, $errors);
|
||||
}
|
||||
|
||||
if (empty($bigbluebuttonbn)) {
|
||||
echo get_string('view_message_tab_close', 'bigbluebuttonbn');
|
||||
die();
|
||||
}
|
||||
// Moodle event logger: Create an event for meeting left.
|
||||
logger::log_meeting_left_event($instance);
|
||||
|
||||
// Update the cache.
|
||||
$meeting = new meeting($instance);
|
||||
$meeting->update_cache();
|
||||
|
||||
// Check the origin page.
|
||||
$select = "userid = ? AND log = ?";
|
||||
$params = [
|
||||
'userid' => $USER->id,
|
||||
'log' => logger::EVENT_JOIN,
|
||||
];
|
||||
$accesses = $DB->get_records_select('bigbluebuttonbn_logs', $select, $params, 'id ASC', 'id, meta', 1);
|
||||
$lastaccess = end($accesses);
|
||||
if (!empty($lastaccess->meta)) {
|
||||
$lastaccess = json_decode($lastaccess->meta);
|
||||
// If the user acceded from Timeline it should be redirected to the Dashboard.
|
||||
if (isset($lastaccess->origin) && $lastaccess->origin == logger::ORIGIN_TIMELINE) {
|
||||
redirect($CFG->wwwroot . '/my/');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'join':
|
||||
if (empty($bigbluebuttonbn)) {
|
||||
throw new moodle_exception('view_error_unable_join', 'bigbluebuttonbn');
|
||||
break;
|
||||
}
|
||||
// Check the origin page.
|
||||
$origin = logger::ORIGIN_BASE;
|
||||
if ($timeline) {
|
||||
$origin = logger::ORIGIN_TIMELINE;
|
||||
} else if ($index) {
|
||||
$origin = logger::ORIGIN_INDEX;
|
||||
}
|
||||
|
||||
try {
|
||||
$url = meeting::join_meeting($instance, $origin);
|
||||
redirect($url);
|
||||
} catch (server_not_available_exception $e) {
|
||||
bigbluebutton_proxy::handle_server_not_available($instance);
|
||||
}
|
||||
// We should never reach this point.
|
||||
break;
|
||||
|
||||
case 'play':
|
||||
$recording = recording::get_record(['id' => $rid]);
|
||||
if ($href = $recording->get_remote_playback_url($rtype)) {
|
||||
logger::log_recording_played_event($instance, $rid);
|
||||
redirect(urldecode($href));
|
||||
} else {
|
||||
notification::add(get_string('recordingurlnotfound', 'mod_bigbluebuttonbn'), notification::ERROR);
|
||||
redirect($instance->get_view_url());
|
||||
}
|
||||
// We should never reach this point.
|
||||
break;
|
||||
}
|
||||
// When we reach this point, we can close the tab or window where BBB was opened.
|
||||
echo $OUTPUT->header();
|
||||
// Behat does not like when we close the Windows as it is expecting to locate
|
||||
// on click part of the pages (bug with selenium raising an exception). So this is a workaround.
|
||||
if (!defined('BEHAT_SITE_RUNNING')) {
|
||||
$PAGE->requires->js_call_amd('mod_bigbluebuttonbn/rooms', 'setupWindowAutoClose');
|
||||
}
|
||||
echo $OUTPUT->footer();
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Activity base class.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\analytics\indicator;
|
||||
use core_analytics\local\indicator\community_of_inquiry_activity;
|
||||
|
||||
/**
|
||||
* Activity base class.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class activity_base extends community_of_inquiry_activity {
|
||||
|
||||
/**
|
||||
* No need to fetch grades for resources.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function feedback_check_grades() {
|
||||
// BigBlueButtonBN's feedback is not contained in grades.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cognitive depth indicator - BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\analytics\indicator;
|
||||
|
||||
use cm_info;
|
||||
use lang_string;
|
||||
|
||||
/**
|
||||
* Cognitive depth indicator - bigbluebuttonbn.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cognitive_depth extends activity_base {
|
||||
|
||||
/**
|
||||
* Returns the name.
|
||||
*
|
||||
* If there is a corresponding '_help' string this will be shown as well.
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_name(): lang_string {
|
||||
return new lang_string('indicator:cognitivedepth', 'mod_bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indicator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_indicator_type() {
|
||||
return self::INDICATOR_COGNITIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cognitive depth level.
|
||||
*
|
||||
* @param cm_info $cm
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_cognitive_depth_level(cm_info $cm) {
|
||||
return self::COGNITIVE_LEVEL_4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Social breadth indicator - BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\analytics\indicator;
|
||||
|
||||
use cm_info;
|
||||
use lang_string;
|
||||
|
||||
/**
|
||||
* Social breadth indicator - BigBlueButtonBN.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class social_breadth extends activity_base {
|
||||
|
||||
/**
|
||||
* Returns the name.
|
||||
*
|
||||
* If there is a corresponding '_help' string this will be shown as well.
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_name(): lang_string {
|
||||
return new lang_string('indicator:socialbreadth', 'mod_bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indicator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_indicator_type() {
|
||||
return self::INDICATOR_SOCIAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the social breadth level.
|
||||
*
|
||||
* @param cm_info $cm
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_social_breadth_level(cm_info $cm) {
|
||||
return self::SOCIAL_LEVEL_1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
use Exception;
|
||||
use Firebase\JWT\JWT;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
|
||||
/**
|
||||
* The broker routines
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class broker {
|
||||
|
||||
/** @var array List of required params */
|
||||
protected $requiredparams = [
|
||||
'recording_ready' => [
|
||||
'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.',
|
||||
'signed_parameters' => 'A JWT encoded string must be included as [signed_parameters].'
|
||||
],
|
||||
'meeting_events' => [
|
||||
'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Validate the supplied list of parameters, providing feedback about any missing or incorrect values.
|
||||
*
|
||||
* @param array $params
|
||||
* @return null|string
|
||||
*/
|
||||
public function validate_parameters(array $params): ?string {
|
||||
if (!isset($params['action']) || empty($params['action']) ) {
|
||||
return 'Parameter ['.$params['action'].'] was not included';
|
||||
}
|
||||
|
||||
$action = strtolower($params['action']);
|
||||
if (!array_key_exists($action, $this->requiredparams)) {
|
||||
return "Action {$params['action']} can not be performed.";
|
||||
}
|
||||
return $this->validate_parameters_message($params, $this->requiredparams[$action]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the specified parameter is valid.
|
||||
*
|
||||
* @param array $params
|
||||
* @param array $requiredparams
|
||||
* @return null|string
|
||||
*/
|
||||
protected static function validate_parameters_message(array $params, array $requiredparams): ?string {
|
||||
foreach ($requiredparams as $param => $message) {
|
||||
if (!array_key_exists($param, $params) || $params[$param] == '') {
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is valid.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for responding when recording ready is performed.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param array $params
|
||||
*/
|
||||
public static function recording_ready(instance $instance, array $params): void {
|
||||
// Decodes the received JWT string.
|
||||
try {
|
||||
$decodedparameters = JWT::decode(
|
||||
$params['signed_parameters'],
|
||||
config::get('shared_secret'),
|
||||
['HS256']
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$error = 'Caught exception: ' . $e->getMessage();
|
||||
header('HTTP/1.0 400 Bad Request. ' . $error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validations.
|
||||
if (!isset($decodedparameters->record_id)) {
|
||||
header('HTTP/1.0 400 Bad request. Missing record_id parameter');
|
||||
return;
|
||||
}
|
||||
|
||||
$recording = recording::get_record(['recordingid' => $decodedparameters->record_id]);
|
||||
if (!isset($recording)) {
|
||||
header('HTTP/1.0 400 Bad request. Invalid record_id');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sends the messages.
|
||||
try {
|
||||
// We make sure messages are sent only once.
|
||||
if ($recording->get('status') != recording::RECORDING_STATUS_NOTIFIED) {
|
||||
$task = new \mod_bigbluebuttonbn\task\send_recording_ready_notification();
|
||||
$task->set_instance_id($instance->get_instance_id());
|
||||
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
|
||||
$recording->set('status', recording::RECORDING_STATUS_NOTIFIED);
|
||||
$recording->update();
|
||||
}
|
||||
header('HTTP/1.0 202 Accepted');
|
||||
} catch (Exception $e) {
|
||||
$error = 'Caught exception: ' . $e->getMessage();
|
||||
header('HTTP/1.0 503 Service Unavailable. ' . $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\completion;
|
||||
|
||||
use core_completion\activity_custom_completion;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\logger;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class custom_completion
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David ([email protected])
|
||||
*/
|
||||
class custom_completion extends activity_custom_completion {
|
||||
|
||||
/**
|
||||
* Get current state
|
||||
*
|
||||
* @param string $rule
|
||||
* @return int
|
||||
*/
|
||||
public function get_state(string $rule): int {
|
||||
// Get instance details.
|
||||
$instance = instance::get_from_cmid($this->cm->id);
|
||||
|
||||
if (empty($instance)) {
|
||||
throw new moodle_exception("Can't find bigbluebuttonbn instance {$this->cm->instance}");
|
||||
}
|
||||
|
||||
// Default return value.
|
||||
$value = COMPLETION_INCOMPLETE;
|
||||
$filters = $rule != "completionview" ? [logger::EVENT_SUMMARY] : [logger::EVENT_JOIN, logger::EVENT_PLAYED];
|
||||
$logs = logger::get_user_completion_logs($instance, $this->userid, $filters);
|
||||
|
||||
if (method_exists($this, "get_{$rule}_value")) {
|
||||
$valuecount = $this->count_actions($logs, self::class . "::get_{$rule}_value");
|
||||
if ($valuecount) {
|
||||
if (!is_null($instance->get_instance_var($rule))) {
|
||||
if ($instance->get_instance_var($rule) <= $valuecount) {
|
||||
$value = COMPLETION_COMPLETE;
|
||||
}
|
||||
} else {
|
||||
// If there is at least a hit, we consider it as complete.
|
||||
$value = $valuecount ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute current state from logs.
|
||||
*
|
||||
* @param array $logs
|
||||
* @param callable $logvaluegetter
|
||||
* @return int the number of hits on this particular rule
|
||||
*/
|
||||
protected function count_actions(array $logs, callable $logvaluegetter): int {
|
||||
if (empty($logs)) {
|
||||
// As completion by engagement with $rulename hand was required, the activity hasn't been completed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
$valuecount = 0;
|
||||
foreach ($logs as $log) {
|
||||
$valuecount += $logvaluegetter($log);
|
||||
}
|
||||
|
||||
return $valuecount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the list of custom completion rules that this module defines.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_defined_custom_rules(): array {
|
||||
return [
|
||||
'completionattendance',
|
||||
'completionengagementchats',
|
||||
'completionengagementtalks',
|
||||
'completionengagementraisehand',
|
||||
'completionengagementpollvotes',
|
||||
'completionengagementemojis',
|
||||
'completionview' // Completion view is now a customrule as it depends on the logs and not
|
||||
// the view action itself.
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of the descriptions of custom completion rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_custom_rule_descriptions(): array {
|
||||
$completionengagementchats = $this->cm->customdata['customcompletionrules']['completionengagementchats'] ?? 1;
|
||||
$completionengagementtalks = $this->cm->customdata['customcompletionrules']['completionengagementtalks'] ?? 1;
|
||||
$completionengagementraisehand = $this->cm->customdata['customcompletionrules']['completionengagementraisehand'] ?? 1;
|
||||
$completionengagementpollvotes = $this->cm->customdata['customcompletionrules']['completionengagementpollvotes'] ?? 1;
|
||||
$completionengagementemojis = $this->cm->customdata['customcompletionrules']['completionengagementemojis'] ?? 1;
|
||||
$completionattendance = $this->cm->customdata['customcompletionrules']['completionattendance'] ?? 1;
|
||||
return [
|
||||
'completionengagementchats' => get_string('completionengagementchats_desc', 'mod_bigbluebuttonbn',
|
||||
$completionengagementchats),
|
||||
'completionengagementtalks' => get_string('completionengagementtalks_desc', 'mod_bigbluebuttonbn',
|
||||
$completionengagementtalks),
|
||||
'completionengagementraisehand' => get_string('completionengagementraisehand_desc', 'mod_bigbluebuttonbn',
|
||||
$completionengagementraisehand),
|
||||
'completionengagementpollvotes' => get_string('completionengagementpollvotes_desc', 'mod_bigbluebuttonbn',
|
||||
$completionengagementpollvotes),
|
||||
'completionengagementemojis' => get_string('completionengagementemojis_desc', 'mod_bigbluebuttonbn',
|
||||
$completionengagementemojis),
|
||||
'completionattendance' => get_string('completionattendance_desc', 'mod_bigbluebuttonbn',
|
||||
$completionattendance),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all completion rules, in the order they should be displayed to users.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sort_order(): array {
|
||||
return [
|
||||
'completionview',
|
||||
'completionengagementchats',
|
||||
'completionengagementtalks',
|
||||
'completionengagementraisehand',
|
||||
'completionengagementpollvotes',
|
||||
'completionengagementemojis',
|
||||
'completionattendance',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current state in a friendly version
|
||||
*
|
||||
* @param string $rule
|
||||
* @return string
|
||||
*/
|
||||
public function get_printable_state(string $rule): string {
|
||||
// Get instance details.
|
||||
$instance = instance::get_from_cmid($this->cm->id);
|
||||
|
||||
if (empty($instance)) {
|
||||
throw new moodle_exception("Can't find bigbluebuttonbn instance {$this->cm->instance}");
|
||||
}
|
||||
|
||||
$summary = "";
|
||||
$filters = $rule != "completionview" ? [logger::EVENT_SUMMARY] : [logger::EVENT_JOIN, logger::EVENT_PLAYED];
|
||||
$logs = logger::get_user_completion_logs($instance, $this->userid, $filters);
|
||||
|
||||
if (method_exists($this, "get_{$rule}_value")) {
|
||||
$summary = get_string(
|
||||
$rule . '_event_desc',
|
||||
'mod_bigbluebuttonbn',
|
||||
$this->count_actions($logs, self::class . "::get_{$rule}_value")
|
||||
);
|
||||
}
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current state in a friendly version
|
||||
*
|
||||
* @param string $rule
|
||||
* @return string
|
||||
*/
|
||||
public function get_last_log_timestamp(string $rule): string {
|
||||
// Get instance details.
|
||||
$instance = instance::get_from_cmid($this->cm->id);
|
||||
|
||||
if (empty($instance)) {
|
||||
throw new moodle_exception("Can't find bigbluebuttonbn instance {$this->cm->instance}");
|
||||
}
|
||||
$filters = $rule != "completionview" ? [logger::EVENT_SUMMARY] : [logger::EVENT_JOIN, logger::EVENT_PLAYED];
|
||||
return logger::get_user_completion_logs_max_timestamp($instance, $this->userid, $filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of custom completion rules that are being used by this activity module instance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_available_custom_rules(): array {
|
||||
$availablerules = parent::get_available_custom_rules();
|
||||
$availablerules[] = 'completionview'; // Completion view is now a customrule.
|
||||
return $availablerules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completion view value
|
||||
*
|
||||
* This will override the usual completion value (see COMPLETION_CUSTOM_MODULE_FLOW)
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionview_value(stdClass $log): int {
|
||||
return $log->log == logger::EVENT_PLAYED || $log->log == logger::EVENT_JOIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attendance summary value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionattendance_value(stdClass $log): int {
|
||||
$summary = json_decode($log->meta);
|
||||
if ($summary && !empty($summary->data->duration)) {
|
||||
return COMPLETION_COMPLETE;
|
||||
}
|
||||
return COMPLETION_INCOMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagementchats_value(stdClass $log): int {
|
||||
return self::get_completionengagement_value($log, 'chats');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagementtalks_value(stdClass $log): int {
|
||||
return self::get_completionengagement_value($log, 'talks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagementraisehand_value(stdClass $log): int {
|
||||
return self::get_completionengagement_value($log, 'raisehand');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagementpollvotes_value(stdClass $log): int {
|
||||
return self::get_completionengagement_value($log, 'pollvotes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagementemojis_value(stdClass $log): int {
|
||||
return self::get_completionengagement_value($log, 'emojis');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general completion engagement value
|
||||
*
|
||||
* @param stdClass $log
|
||||
* @param string $type
|
||||
* @return int
|
||||
*/
|
||||
protected static function get_completionengagement_value(stdClass $log, string $type): int {
|
||||
$summary = json_decode($log->meta);
|
||||
if ($summary && !empty($summary->data->engagement->$type)) {
|
||||
return COMPLETION_COMPLETE;
|
||||
}
|
||||
return COMPLETION_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn activity management viewed event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010-2017 Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v2 or later
|
||||
*/
|
||||
class activity_management_viewed extends base {
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_PARTICIPATING) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' viewed the bigbluebuttonbn activity management page for ".
|
||||
"the course module id '##contextinstanceid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return 'BigBlueButtonBN activity management viewed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn activity viewed event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity_viewed extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_PARTICIPATING) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' viewed the bigbluebuttonbn activity " .
|
||||
"with id '##objectid' for the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_activity_viewed', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn abstract base event class. Most mod_bigbluebuttonbn events can extend this class.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class base extends \core\event\base {
|
||||
|
||||
/** @var $bigbluebuttonbn */
|
||||
protected $bigbluebuttonbn;
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Object Id Mapping.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $objectidmapping = ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
|
||||
/**
|
||||
* Legacy log data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $legacylogdata;
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_PARTICIPATING) {
|
||||
$this->data['crud'] = $crud;
|
||||
$this->data['edulevel'] = $edulevel;
|
||||
$this->data['objecttable'] = 'bigbluebuttonbn';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
$vars = [
|
||||
'userid' => $this->userid,
|
||||
'courseid' => $this->courseid,
|
||||
'objectid' => $this->objectid,
|
||||
'contextinstanceid' => $this->contextinstanceid,
|
||||
'other' => $this->other
|
||||
];
|
||||
$string = $this->description;
|
||||
foreach ($vars as $key => $value) {
|
||||
$string = str_replace("##" . $key, $value, $string);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relevant URL.
|
||||
*
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
return new \moodle_url('/mod/bigbluebuttonbn/view.php', ['id' => $this->contextinstanceid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the legacy event log data.
|
||||
*
|
||||
* @param string $action The current action
|
||||
* @param string $info A detailed description of the change. But no more than 255 characters.
|
||||
* @param string $url The url to the assign module instance.
|
||||
*/
|
||||
public function set_legacy_logdata($action = '', $info = '', $url = '') {
|
||||
$fullurl = 'view.php?id=' . $this->contextinstanceid;
|
||||
if ($url != '') {
|
||||
$fullurl .= '&' . $url;
|
||||
}
|
||||
|
||||
$this->legacylogdata = [$this->courseid, 'bigbluebuttonbn', $action, $fullurl, $info, $this->contextinstanceid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return legacy data for add_to_log().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_legacy_logdata() {
|
||||
if (isset($this->legacylogdata)) {
|
||||
return $this->legacylogdata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function validate_data() {
|
||||
parent::validate_data();
|
||||
|
||||
if ($this->contextlevel != CONTEXT_MODULE) {
|
||||
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn class for event name definition.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class events {
|
||||
|
||||
/**
|
||||
* Event name matcher.
|
||||
* @var $events
|
||||
*/
|
||||
public static $events = [
|
||||
'create' => 'activity_created',
|
||||
'view' => 'activity_viewed',
|
||||
'update' => 'activity_updated',
|
||||
'delete' => 'activity_deleted',
|
||||
'meeting_create' => 'meeting_created',
|
||||
'meeting_end' => 'meeting_ended',
|
||||
'meeting_join' => 'meeting_joined',
|
||||
'meeting_left' => 'meeting_left',
|
||||
'recording_delete' => 'recording_deleted',
|
||||
'recording_import' => 'recording_imported',
|
||||
'recording_protect' => 'recording_protected',
|
||||
'recording_publish' => 'recording_published',
|
||||
'recording_unprotect' => 'recording_unprotected',
|
||||
'recording_unpublish' => 'recording_unpublished',
|
||||
'recording_edit' => 'recording_edited',
|
||||
'recording_play' => 'recording_viewed',
|
||||
'live_session' => 'live_session'
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn live_session (Experimental: for being triggered when external events are received).
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class live_session_event extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' triggered action ##other in a ".
|
||||
"bigbluebutton meeting for the bigbluebuttonbn activity with id ".
|
||||
"'##objectid' for the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_live_session', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn meeting created event, triggered when the meeting is created before join.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting_created extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' created a bigbluebutton meeting for ".
|
||||
"the bigbluebuttonbn activity with id '##objectid' for the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_meeting_created', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn meeting ended event, triggered when the meeting is ended by the user.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting_ended extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "A bigbluebutton meeting for the bigbluebuttonbn activity with id ".
|
||||
"'##objectid' for the course id '##courseid' has been forcibly ".
|
||||
"ended by the user with id '##userid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_meeting_ended', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn meeting joined event, triggered when the user joins the session.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting_joined extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_PARTICIPATING) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has joined a bigbluebutton meeting for ".
|
||||
"the bigbluebuttonbn activity with id '##objectid' for the course id ".
|
||||
"'##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_meeting_joined', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn meeting left event, triggered when the user lefts the meeting using the logout button.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting_left extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_PARTICIPATING) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has left a bigbluebutton meeting for ".
|
||||
"the bigbluebuttonbn activity with id '##objectid' for the course id ".
|
||||
"'##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_meeting_left', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording deleted event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_deleted extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has deleted a recording with id ".
|
||||
"'##other' from the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_deleted', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording edited event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_edited extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has edited a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_edited', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording imported event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_imported extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has imported a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_imported', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording protected event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_protected extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has protected a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_protected', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording published event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_published extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has published a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_published', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording unprotected event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_unprotected extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has unprotected a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_unprotected', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording unpublished event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_unpublished extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has unpublished a recording with id ".
|
||||
"'##other' in the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_unpublished', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\event;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn recording viewed event.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_viewed extends base
|
||||
{
|
||||
/**
|
||||
* Init method.
|
||||
* @param string $crud
|
||||
* @param int $edulevel
|
||||
*/
|
||||
protected function init($crud = 'r', $edulevel = self::LEVEL_OTHER) {
|
||||
parent::init($crud, $edulevel);
|
||||
$this->description = "The user with id '##userid' has viewed a recording with id ".
|
||||
"'##other' from the course id '##courseid'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('event_recording_viewed', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return objectid mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return ['db' => 'bigbluebuttonbn', 'restore' => 'bigbluebuttonbn'];
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use restricted_context_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to check whether a user can join a meeting.
|
||||
*
|
||||
* This is mainly used by the mobile application.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class can_join extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'cmid' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED),
|
||||
'groupid' => new external_value(PARAM_INT, 'bigbluebuttonbn group id', VALUE_DEFAULT, 0),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a recording
|
||||
*
|
||||
* @param int $cmid the bigbluebuttonbn course module id
|
||||
* @param null|int $groupid
|
||||
* @return array (empty array for now)
|
||||
* @throws \restricted_context_exception
|
||||
*/
|
||||
public static function execute(
|
||||
int $cmid,
|
||||
?int $groupid = 0
|
||||
): array {
|
||||
// Validate the cmid ID.
|
||||
[
|
||||
'cmid' => $cmid,
|
||||
'groupid' => $groupid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'cmid' => $cmid,
|
||||
'groupid' => $groupid,
|
||||
]);
|
||||
|
||||
$result = [
|
||||
'can_join' => false,
|
||||
'cmid' => $cmid,
|
||||
];
|
||||
|
||||
$instance = instance::get_from_cmid($cmid);
|
||||
if (empty($instance)) {
|
||||
return $result;
|
||||
}
|
||||
// Validate the groupid.
|
||||
if (!groups_group_visible($groupid, $instance->get_course(), $instance->get_cm())) {
|
||||
throw new restricted_context_exception();
|
||||
}
|
||||
$instance->set_group_id($groupid);
|
||||
|
||||
self::validate_context($instance->get_context());
|
||||
|
||||
$meeting = new meeting($instance);
|
||||
|
||||
$result['can_join'] = $meeting->can_join();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.3
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'can_join' => new external_value(PARAM_BOOL, 'Can join session'),
|
||||
'cmid' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to validate completion.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class completion_validate extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_INT, 'bigbluebuttonbn instance id'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark activity as complete
|
||||
*
|
||||
* @param int $bigbluebuttonbnid the bigbluebuttonbn instance id
|
||||
* @return array (empty array for now)
|
||||
*/
|
||||
public static function execute(
|
||||
int $bigbluebuttonbnid
|
||||
): array {
|
||||
// Validate the bigbluebuttonbnid ID.
|
||||
[
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
]);
|
||||
$result = ['warnings' => []];
|
||||
// Fetch the session, features, and profile.
|
||||
$instance = instance::get_from_instanceid($bigbluebuttonbnid);
|
||||
if ($instance) {
|
||||
$context = $instance->get_context();
|
||||
|
||||
// Validate that the user has access to this activity.
|
||||
self::validate_context($context);
|
||||
|
||||
// Get list with all the users enrolled in the course.
|
||||
[$sort, $sqlparams] = users_order_by_sql('u');
|
||||
if (has_capability('moodle/course:update', $context)) {
|
||||
$users = get_enrolled_users($context, 'mod/bigbluebuttonbn:view', 0, 'u.*', $sort);
|
||||
foreach ($users as $user) {
|
||||
// Enqueue a task for processing the completion.
|
||||
bigbluebutton_proxy::enqueue_completion_event($instance->get_instance_data(), $user->id);
|
||||
}
|
||||
} else {
|
||||
$result['warnings'][] = [
|
||||
'item' => 'mod_bigbluebuttonbn',
|
||||
'itemid' => $instance->get_instance_id(),
|
||||
'warningcode' => 'nopermissions',
|
||||
'message' => get_string('nopermissions', 'error', 'completion_validate')
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$result['warnings'][] = [
|
||||
'item' => 'mod_bigbluebuttonbn',
|
||||
'itemid' => $bigbluebuttonbnid,
|
||||
'warningcode' => 'indexerrorbbtn',
|
||||
'message' => get_string('index_error_bbtn', 'mod_bigbluebuttonbn', $bigbluebuttonbnid)
|
||||
];
|
||||
}
|
||||
// We might want to return a status here or some warnings.
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'warnings' => new \external_warnings()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use core\notification;
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton;
|
||||
use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception;
|
||||
use mod_bigbluebuttonbn\logger;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use restricted_context_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to end a meeting.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class end_meeting extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_INT, 'bigbluebuttonbn instance id'),
|
||||
'groupid' => new external_value(PARAM_INT, 'bigbluebuttonbn group id', VALUE_DEFAULT, 0),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a recording
|
||||
*
|
||||
* @param int $bigbluebuttonbnid the bigbluebuttonbn instance id
|
||||
* @param int $groupid the groupid (either 0 or the groupid)
|
||||
* @return array (empty array for now)
|
||||
* @throws \restricted_context_exception
|
||||
*/
|
||||
public static function execute(
|
||||
int $bigbluebuttonbnid,
|
||||
int $groupid
|
||||
): array {
|
||||
// Validate the bigbluebuttonbnid ID.
|
||||
[
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'groupid' => $groupid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'groupid' => $groupid,
|
||||
]);
|
||||
|
||||
// Fetch the session, features, and profile.
|
||||
$instance = instance::get_from_instanceid($bigbluebuttonbnid);
|
||||
if (empty($instance)) {
|
||||
throw new \moodle_exception('Unknown Instance');
|
||||
}
|
||||
if (!groups_group_visible($groupid, $instance->get_course(), $instance->get_cm())) {
|
||||
throw new restricted_context_exception();
|
||||
}
|
||||
$instance->set_group_id($groupid);
|
||||
$context = $instance->get_context();
|
||||
|
||||
// Validate that the user has access to this activity and to manage recordings.
|
||||
self::validate_context($context);
|
||||
|
||||
if (!$instance->user_can_end_meeting()) {
|
||||
throw new restricted_context_exception();
|
||||
}
|
||||
// Execute the end command.
|
||||
$meeting = new meeting($instance);
|
||||
try {
|
||||
$meeting->end_meeting();
|
||||
} catch (bigbluebutton_exception $e) {
|
||||
return [
|
||||
'warnings' => [
|
||||
[
|
||||
'item' => $instance->get_meeting_name(),
|
||||
'itemid' => $instance->get_instance_id(),
|
||||
'warningcode' => 'notFound',
|
||||
'message' => $e->getMessage()
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
logger::log_meeting_ended_event($instance);
|
||||
// Update the cache.
|
||||
$meeting->update_cache();
|
||||
notification::add(get_string('end_session_notification', 'mod_bigbluebuttonbn'), notification::INFO);
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'warnings' => new \external_warnings()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use context_module;
|
||||
use external_api;
|
||||
use external_files;
|
||||
use external_format_value;
|
||||
use external_function_parameters;
|
||||
use external_multiple_structure;
|
||||
use external_single_structure;
|
||||
use external_util;
|
||||
use external_value;
|
||||
use external_warnings;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to get activity per course
|
||||
*
|
||||
* This is mainly used by the mobile application.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class get_bigbluebuttonbns_by_courses extends external_api {
|
||||
/**
|
||||
* Describes the parameters for get_bigbluebuttonbns_by_courses.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.11
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters([
|
||||
'courseids' => new external_multiple_structure(
|
||||
new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, []
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of bigbluebuttonbns in a provided list of courses.
|
||||
* If no list is provided all bigbluebuttonbns that the user can view will be returned.
|
||||
*
|
||||
* @param array $courseids course ids
|
||||
* @return array of warnings and bigbluebuttonbns
|
||||
* @since Moodle 3.11
|
||||
*/
|
||||
public static function execute($courseids = []) {
|
||||
global $USER;
|
||||
$warnings = [];
|
||||
$returnedbigbluebuttonbns = [];
|
||||
|
||||
['courseids' => $courseids] = self::validate_parameters(self::execute_parameters(), ['courseids' => $courseids]);
|
||||
$mycourses = [];
|
||||
if (empty($courseids)) {
|
||||
$mycourses = enrol_get_my_courses();
|
||||
$courseids = array_keys($mycourses);
|
||||
}
|
||||
|
||||
// Ensure there are courseids to loop through.
|
||||
if (!empty($courseids)) {
|
||||
[$courses, $warnings] = external_util::validate_courses($courseids, $mycourses);
|
||||
|
||||
// Get the bigbluebuttonbns in this course, this function checks users visibility permissions.
|
||||
// We can avoid then additional validate_context calls.
|
||||
$bigbluebuttonbns = get_all_instances_in_courses("bigbluebuttonbn", $courses, $USER->id);
|
||||
foreach ($bigbluebuttonbns as $bigbluebuttonbn) {
|
||||
$context = context_module::instance($bigbluebuttonbn->coursemodule);
|
||||
// Entry to return.
|
||||
$bigbluebuttonbn->name = external_format_string($bigbluebuttonbn->name, $context->id);
|
||||
|
||||
[$bigbluebuttonbn->intro, $bigbluebuttonbn->introformat] = external_format_text($bigbluebuttonbn->intro,
|
||||
$bigbluebuttonbn->introformat, $context->id, 'mod_bigbluebuttonbn', 'intro', null);
|
||||
$bigbluebuttonbn->introfiles = external_util::get_area_files($context->id,
|
||||
'mod_bigbluebuttonbn', 'intro', false, false);
|
||||
|
||||
$returnedbigbluebuttonbns[] = $bigbluebuttonbn;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [
|
||||
'bigbluebuttonbns' => $returnedbigbluebuttonbns,
|
||||
'warnings' => $warnings
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the get_bigbluebuttonbns_by_courses return value.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.11
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return new external_single_structure([
|
||||
'bigbluebuttonbns' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'id' => new external_value(PARAM_INT, 'Module id'),
|
||||
'coursemodule' => new external_value(PARAM_INT, 'Course module id'),
|
||||
'course' => new external_value(PARAM_INT, 'Course id'),
|
||||
'name' => new external_value(PARAM_RAW, 'Name'),
|
||||
'intro' => new external_value(PARAM_RAW, 'Description'),
|
||||
'meetingid' => new external_value(PARAM_RAW, 'Meeting id'),
|
||||
'introformat' => new external_format_value('intro', 'Summary format'),
|
||||
'introfiles' => new external_files('Files in the introduction text'),
|
||||
'timemodified' => new external_value(PARAM_INT, 'Last time the instance was modified'),
|
||||
'section' => new external_value(PARAM_INT, 'Course section id'),
|
||||
'visible' => new external_value(PARAM_INT, 'Module visibility'),
|
||||
'groupmode' => new external_value(PARAM_INT, 'Group mode'),
|
||||
'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
|
||||
]
|
||||
)
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\exceptions\meeting_join_exception;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use restricted_context_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to create the meeting (if needed), check user limit, and return the join URL when we can join.
|
||||
*
|
||||
* This is mainly used by the mobile application.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class get_join_url extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'cmid' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED),
|
||||
'groupid' => new external_value(PARAM_INT, 'bigbluebuttonbn group id', VALUE_DEFAULT, 0),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a recording
|
||||
*
|
||||
* @param int $cmid the bigbluebuttonbn course module id
|
||||
* @param null|int $groupid
|
||||
* @return array (empty array for now)
|
||||
*/
|
||||
public static function execute(
|
||||
int $cmid,
|
||||
?int $groupid = 0
|
||||
): array {
|
||||
// Validate the cmid ID.
|
||||
[
|
||||
'cmid' => $cmid,
|
||||
'groupid' => $groupid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'cmid' => $cmid,
|
||||
'groupid' => $groupid,
|
||||
]);
|
||||
$result = ['warnings' => []];
|
||||
|
||||
$instance = instance::get_from_cmid($cmid);
|
||||
if (empty($instance)) {
|
||||
throw new \moodle_exception('nosuchinstance', 'mod_bigbluebuttonbn', null,
|
||||
['entity' => get_string('module', 'course'), 'id' => $cmid]);
|
||||
}
|
||||
// Validate the groupid.
|
||||
if (!groups_group_visible($groupid, $instance->get_course(), $instance->get_cm())) {
|
||||
throw new restricted_context_exception();
|
||||
}
|
||||
$instance->set_group_id($groupid);
|
||||
|
||||
self::validate_context($instance->get_context());
|
||||
|
||||
try {
|
||||
$result['join_url'] = meeting::join_meeting($instance);
|
||||
} catch (meeting_join_exception $e) {
|
||||
$result['warnings'][] = [
|
||||
'item' => 'mod_bigbluebuttonbn',
|
||||
'itemid' => $instance->get_instance_id(),
|
||||
'warningcode' => $e->errorcode,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.3
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'join_url' => new external_value(PARAM_RAW, 'Can join session', VALUE_OPTIONAL),
|
||||
'warnings' => new \external_warnings()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_multiple_structure;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use external_warnings;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_data;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use restricted_context_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to fetch a list of recordings from the BBB service.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class get_recordings extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_INT, 'bigbluebuttonbn instance id'),
|
||||
'tools' => new external_value(PARAM_RAW, 'a set of enabled tools', VALUE_DEFAULT,
|
||||
'protect,unprotect,publish,unpublish,delete'),
|
||||
'groupid' => new external_value(PARAM_INT, 'Group ID', VALUE_DEFAULT, null),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of recordings
|
||||
*
|
||||
* @param int $bigbluebuttonbnid the bigbluebuttonbn instance id to which the recordings are referred.
|
||||
* @param string|null $tools
|
||||
* @param int|null $groupid
|
||||
* @return array of warnings and status result
|
||||
* @throws \webservice_access_exception
|
||||
*/
|
||||
public static function execute(
|
||||
int $bigbluebuttonbnid = 0,
|
||||
?string $tools = 'protect,unprotect,publish,unpublish,delete',
|
||||
?int $groupid = null
|
||||
): array {
|
||||
global $USER;
|
||||
|
||||
$returnval = [
|
||||
'status' => false,
|
||||
'warnings' => [],
|
||||
];
|
||||
|
||||
// Validate the bigbluebuttonbnid ID.
|
||||
[
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'tools' => $tools,
|
||||
'groupid' => $groupid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'tools' => $tools,
|
||||
'groupid' => $groupid,
|
||||
]);
|
||||
|
||||
$tools = explode(',', $tools ?? 'protect,unprotect,publish,unpublish,delete');
|
||||
|
||||
// Fetch the session, features, and profile.
|
||||
$instance = instance::get_from_instanceid($bigbluebuttonbnid);
|
||||
if (!$instance) {
|
||||
$returnval['warnings'][] = [
|
||||
'item' => $bigbluebuttonbnid,
|
||||
'warningcode' => 'nosuchinstance',
|
||||
'message' => get_string('nosuchinstance', 'mod_bigbluebuttonbn',
|
||||
(object) ['id' => $bigbluebuttonbnid, 'entity' => 'bigbluebuttonbn'])
|
||||
];
|
||||
} else {
|
||||
$typeprofiles = bigbluebutton_proxy::get_instance_type_profiles();
|
||||
$profilefeature = $typeprofiles[$instance->get_type()]['features'];
|
||||
$showrecordings = in_array('all', $profilefeature) || in_array('showrecordings', $profilefeature);
|
||||
if ($showrecordings) {
|
||||
$context = $instance->get_context();
|
||||
// Validate that the user has access to this activity.
|
||||
self::validate_context($context);
|
||||
if (!$instance->user_has_group_access($USER, $groupid)) {
|
||||
new restricted_context_exception();
|
||||
}
|
||||
if ($groupid) {
|
||||
$instance->set_group_id($groupid);
|
||||
}
|
||||
$recordings = $instance->get_recordings([], $instance->get_instance_var('recordings_deleted'));
|
||||
$tabledata = recording_data::get_recording_table($recordings, $tools, $instance);
|
||||
|
||||
$returnval['tabledata'] = $tabledata;
|
||||
$returnval['status'] = true;
|
||||
} else {
|
||||
$returnval['warnings'][] = [
|
||||
'item' => $bigbluebuttonbnid,
|
||||
'warningcode' => 'instanceprofilewithoutrecordings',
|
||||
'message' => get_string('instanceprofilewithoutrecordings', 'mod_bigbluebuttonbn')
|
||||
];
|
||||
}
|
||||
}
|
||||
return $returnval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'status' => new external_value(PARAM_BOOL, 'Whether the fetch was successful'),
|
||||
'tabledata' => new external_single_structure([
|
||||
'activity' => new external_value(PARAM_ALPHANUMEXT),
|
||||
'ping_interval' => new external_value(PARAM_INT),
|
||||
'locale' => new external_value(PARAM_TEXT),
|
||||
'profile_features' => new external_multiple_structure(new external_value(PARAM_TEXT)),
|
||||
'columns' => new external_multiple_structure(new external_single_structure([
|
||||
'key' => new external_value(PARAM_ALPHA),
|
||||
'label' => new external_value(PARAM_TEXT),
|
||||
'width' => new external_value(PARAM_ALPHANUMEXT),
|
||||
// See https://datatables.net/reference/option/columns.type .
|
||||
'type' => new external_value(PARAM_ALPHANUMEXT, 'Column type', VALUE_OPTIONAL),
|
||||
'sortable' => new external_value(PARAM_BOOL, 'Whether this column is sortable', VALUE_OPTIONAL, false),
|
||||
'allowHTML' => new external_value(PARAM_BOOL, 'Whether this column contains HTML', VALUE_OPTIONAL, false),
|
||||
])),
|
||||
'data' => new external_value(PARAM_RAW), // For now it will be json encoded.
|
||||
], '', VALUE_OPTIONAL),
|
||||
'warnings' => new external_warnings()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_multiple_structure;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use external_warnings;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_data;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to fetch a list of recordings from the BBB service.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class get_recordings_to_import extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'destinationinstanceid' => new external_value(
|
||||
PARAM_INT,
|
||||
'Id of the other BBB we target for importing recordings into.
|
||||
The idea here is to remove already imported recordings.',
|
||||
VALUE_REQUIRED
|
||||
),
|
||||
'sourcebigbluebuttonbnid' => new external_value(PARAM_INT,
|
||||
'bigbluebuttonbn instance id',
|
||||
VALUE_DEFAULT,
|
||||
0),
|
||||
'sourcecourseid' => new external_value(PARAM_INT,
|
||||
'source courseid to filter by',
|
||||
VALUE_DEFAULT,
|
||||
0),
|
||||
'tools' => new external_value(PARAM_RAW, 'a set of enabled tools', VALUE_DEFAULT,
|
||||
'protect,unprotect,publish,unpublish,delete'),
|
||||
'groupid' => new external_value(PARAM_INT, 'Group ID', VALUE_DEFAULT, null),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of recordings
|
||||
*
|
||||
* @param int $destinationinstanceid the bigbluebuttonbn instance id where recordings have been already imported.
|
||||
* @param int $sourcebigbluebuttonbnid the bigbluebuttonbn instance id to which the recordings are referred.
|
||||
* @param int $sourcecourseid the source courseid to filter by
|
||||
* @param string|null $tools
|
||||
* @param int|null $groupid
|
||||
* @return array of warnings and status result
|
||||
* @throws \webservice_access_exception
|
||||
*/
|
||||
public static function execute(
|
||||
int $destinationinstanceid,
|
||||
?int $sourcebigbluebuttonbnid = 0,
|
||||
?int $sourcecourseid = 0,
|
||||
?string $tools = 'protect,unprotect,publish,unpublish,delete',
|
||||
?int $groupid = null
|
||||
): array {
|
||||
global $USER, $DB;
|
||||
|
||||
$returnval = [
|
||||
'status' => false,
|
||||
'warnings' => [],
|
||||
];
|
||||
|
||||
// Validate the sourcebigbluebuttonbnid ID.
|
||||
[
|
||||
'destinationinstanceid' => $destinationinstanceid,
|
||||
'sourcebigbluebuttonbnid' => $sourcebigbluebuttonbnid,
|
||||
'sourcecourseid' => $sourcecourseid,
|
||||
'tools' => $tools,
|
||||
'groupid' => $groupid
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'destinationinstanceid' => $destinationinstanceid,
|
||||
'sourcebigbluebuttonbnid' => $sourcebigbluebuttonbnid,
|
||||
'sourcecourseid' => $sourcecourseid,
|
||||
'tools' => $tools,
|
||||
'groupid' => $groupid
|
||||
]);
|
||||
|
||||
$tools = explode(',', $tools ?? 'protect,unprotect,publish,unpublish,delete');
|
||||
|
||||
// Fetch the session, features, and profile.
|
||||
$sourceinstance = null;
|
||||
$sourcecourse = null;
|
||||
if ($sourcecourseid) {
|
||||
$sourcecourse = $DB->get_record('course', ['id' => $sourcecourseid], '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
if (!empty($sourcebigbluebuttonbnid)) {
|
||||
$sourceinstance = instance::get_from_instanceid($sourcebigbluebuttonbnid);
|
||||
if (!$sourceinstance) {
|
||||
throw new \invalid_parameter_exception('Source Bigbluebutton Id is invalid');
|
||||
}
|
||||
$sourcecourse = $sourceinstance->get_course();
|
||||
// Validate that the user has access to this activity.
|
||||
self::validate_context($sourceinstance->get_context());
|
||||
}
|
||||
$destinstance = instance::get_from_instanceid($destinationinstanceid);
|
||||
// Validate that the user has access to this activity.
|
||||
self::validate_context($destinstance->get_context());
|
||||
if (!$destinstance->user_has_group_access($USER, $groupid)) {
|
||||
throw new \invalid_parameter_exception('Invalid group for this user ' . $groupid);
|
||||
}
|
||||
if ($groupid) {
|
||||
$destinstance->set_group_id($groupid);
|
||||
}
|
||||
// Exclude itself from the list if in import mode.
|
||||
$excludedids = [$destinstance->get_instance_id()];
|
||||
if ($sourceinstance) {
|
||||
$recordings = $sourceinstance->get_recordings($excludedids);
|
||||
} else {
|
||||
// There is a course id or a 0, so we fetch all recording including deleted recordings from this course.
|
||||
$recordings = recording::get_recordings_for_course(
|
||||
$sourcecourseid,
|
||||
$excludedids,
|
||||
true,
|
||||
false,
|
||||
($sourcecourseid == 0 || $sourcebigbluebuttonbnid == 0),
|
||||
($sourcecourseid == 0 || $sourcebigbluebuttonbnid == 0)
|
||||
);
|
||||
}
|
||||
|
||||
if ($destinationinstanceid) {
|
||||
// Remove recording already imported in this specific activity.
|
||||
$destinationinstance = instance::get_from_instanceid($destinationinstanceid);
|
||||
$importedrecordings = recording::get_recordings_for_instance(
|
||||
$destinationinstance,
|
||||
true,
|
||||
true
|
||||
);
|
||||
// Unset from $recordings if recording is already imported.
|
||||
// Recording $recordings are indexed by $id (moodle table column id).
|
||||
foreach ($recordings as $index => $recording) {
|
||||
foreach ($importedrecordings as $irecord) {
|
||||
if ($irecord->get('recordingid') == $recording->get('recordingid')) {
|
||||
unset($recordings[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$tabledata = recording_data::get_recording_table($recordings, $tools, $sourceinstance, $sourcecourseid);
|
||||
$returnval['tabledata'] = $tabledata;
|
||||
$returnval['status'] = true;
|
||||
|
||||
return $returnval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'status' => new external_value(PARAM_BOOL, 'Whether the fetch was successful'),
|
||||
'tabledata' => new external_single_structure([
|
||||
'activity' => new external_value(PARAM_ALPHANUMEXT),
|
||||
'ping_interval' => new external_value(PARAM_INT),
|
||||
'locale' => new external_value(PARAM_TEXT),
|
||||
'profile_features' => new external_multiple_structure(new external_value(PARAM_TEXT)),
|
||||
'columns' => new external_multiple_structure(new external_single_structure([
|
||||
'key' => new external_value(PARAM_ALPHA),
|
||||
'label' => new external_value(PARAM_TEXT),
|
||||
'width' => new external_value(PARAM_ALPHANUMEXT),
|
||||
// See https://datatables.net/reference/option/columns.type .
|
||||
'type' => new external_value(PARAM_ALPHANUMEXT, 'Column type', VALUE_OPTIONAL),
|
||||
'sortable' => new external_value(PARAM_BOOL, 'Whether this column is sortable', VALUE_OPTIONAL, false),
|
||||
'allowHTML' => new external_value(PARAM_BOOL, 'Whether this column contains HTML', VALUE_OPTIONAL, false),
|
||||
])),
|
||||
'data' => new external_value(PARAM_RAW), // For now it will be json encoded.
|
||||
], '', VALUE_OPTIONAL),
|
||||
'warnings' => new external_warnings()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use restricted_context_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to fetch meeting information.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting_info extends external_api {
|
||||
|
||||
/**
|
||||
* Returns description of method parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_INT, 'bigbluebuttonbn instance id'),
|
||||
'groupid' => new external_value(PARAM_INT, 'bigbluebuttonbn group id', VALUE_DEFAULT, 0),
|
||||
'updatecache' => new external_value(PARAM_BOOL, 'update cache ?', VALUE_DEFAULT, false),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch meeting information.
|
||||
*
|
||||
* @param int $bigbluebuttonbnid the bigbluebuttonbn instance id
|
||||
* @param int $groupid
|
||||
* @param bool $updatecache
|
||||
* @return array
|
||||
*/
|
||||
public static function execute(
|
||||
int $bigbluebuttonbnid,
|
||||
int $groupid,
|
||||
bool $updatecache = false
|
||||
): array {
|
||||
// Validate the bigbluebuttonbnid ID.
|
||||
[
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'groupid' => $groupid,
|
||||
'updatecache' => $updatecache,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'groupid' => $groupid,
|
||||
'updatecache' => $updatecache,
|
||||
]);
|
||||
|
||||
// Fetch the session, features, and profile.
|
||||
$instance = instance::get_from_instanceid($bigbluebuttonbnid);
|
||||
$instance->set_group_id($groupid);
|
||||
if (!groups_group_visible($groupid, $instance->get_course(), $instance->get_cm())) {
|
||||
throw new restricted_context_exception();
|
||||
}
|
||||
$context = $instance->get_context();
|
||||
|
||||
// Validate that the user has access to this activity and to manage recordings.
|
||||
self::validate_context($context);
|
||||
|
||||
// Check if the BBB server is working.
|
||||
$serverversion = bigbluebutton_proxy::get_server_version();
|
||||
if ($serverversion === null) {
|
||||
throw new \moodle_exception('general_error_no_answer',
|
||||
bigbluebutton_proxy::get_server_not_available_message($instance));
|
||||
}
|
||||
return (array) meeting::get_meeting_info_for_instance($instance, $updatecache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([
|
||||
'cmid' => new external_value(PARAM_INT, 'CM id'),
|
||||
'userlimit' => new external_value(PARAM_INT, 'User limit'),
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_RAW, 'bigbluebuttonbn instance id'),
|
||||
'meetingid' => new external_value(PARAM_RAW, 'Meeting id'),
|
||||
'openingtime' => new external_value(PARAM_INT, 'Opening time', VALUE_OPTIONAL),
|
||||
'closingtime' => new external_value(PARAM_INT, 'Closing time', VALUE_OPTIONAL),
|
||||
'statusrunning' => new external_value(PARAM_BOOL, 'Status running', VALUE_OPTIONAL),
|
||||
'statusclosed' => new external_value(PARAM_BOOL, 'Status closed', VALUE_OPTIONAL),
|
||||
'statusopen' => new external_value(PARAM_BOOL, 'Status open', VALUE_OPTIONAL),
|
||||
'statusmessage' => new external_value(PARAM_TEXT, 'Status message', VALUE_OPTIONAL),
|
||||
'startedat' => new external_value(PARAM_INT, 'Started at', VALUE_OPTIONAL),
|
||||
'moderatorcount' => new external_value(PARAM_INT, 'Moderator count', VALUE_OPTIONAL),
|
||||
'participantcount' => new external_value(PARAM_INT, 'Participant count', VALUE_OPTIONAL),
|
||||
'moderatorplural' => new external_value(PARAM_BOOL, 'Several moderators ?', VALUE_OPTIONAL),
|
||||
'participantplural' => new external_value(PARAM_BOOL, 'Several participants ?', VALUE_OPTIONAL),
|
||||
'canjoin' => new external_value(PARAM_BOOL, 'Can join'),
|
||||
'ismoderator' => new external_value(PARAM_BOOL, 'Is moderator'),
|
||||
'presentations' => new \external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'url' => new external_value(PARAM_URL, 'presentation URL'),
|
||||
'iconname' => new external_value(PARAM_RAW, 'icon name'),
|
||||
'icondesc' => new external_value(PARAM_TEXT, 'icon text'),
|
||||
'name' => new external_value(PARAM_TEXT, 'presentation name'),
|
||||
])
|
||||
),
|
||||
'joinurl' => new external_value(PARAM_URL, 'Join URL'),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use coding_exception;
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_action;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_helper;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to update the details of one recording.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class update_recording extends external_api {
|
||||
/**
|
||||
* Updates a recording
|
||||
*
|
||||
* @param int $bigbluebuttonbnid the bigbluebuttonbn instance id, either the same as the one set in the recording or a new
|
||||
* instance to import the recording into.
|
||||
* @param int $recordingid
|
||||
* @param string $action
|
||||
* @param string|null $additionaloptions
|
||||
* @return array (empty array for now)
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function execute(
|
||||
int $bigbluebuttonbnid,
|
||||
int $recordingid,
|
||||
string $action,
|
||||
string $additionaloptions = null
|
||||
): array {
|
||||
// Validate the bigbluebuttonbnid ID.
|
||||
[
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'recordingid' => $recordingid,
|
||||
'action' => $action,
|
||||
'additionaloptions' => $additionaloptions,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbnid,
|
||||
'recordingid' => $recordingid,
|
||||
'action' => $action,
|
||||
'additionaloptions' => $additionaloptions,
|
||||
]);
|
||||
|
||||
switch ($action) {
|
||||
case 'delete':
|
||||
case 'edit':
|
||||
case 'protect':
|
||||
case 'publish':
|
||||
case 'unprotect':
|
||||
case 'unpublish':
|
||||
case 'import':
|
||||
break;
|
||||
default:
|
||||
throw new coding_exception("Unknown action '{$action}'");
|
||||
}
|
||||
$instance = instance::get_from_instanceid($bigbluebuttonbnid);
|
||||
$recordingcontext = $instance->get_context();
|
||||
self::validate_context($recordingcontext);
|
||||
require_capability('mod/bigbluebuttonbn:managerecordings', $recordingcontext);
|
||||
require_capability("mod/bigbluebuttonbn:{$action}recordings", $recordingcontext);
|
||||
|
||||
// Fetch the session, features, and profile.
|
||||
$recording = new recording($recordingid);
|
||||
// Check both the recording instance context and the bbb context.
|
||||
$relatedinstance = instance::get_from_instanceid($recording->get('bigbluebuttonbnid'));
|
||||
if ($relatedinstance) {
|
||||
$recordingcontext = $relatedinstance->get_context();
|
||||
// Validate that the user has access to this activity and to manage recordings.
|
||||
self::validate_context($recordingcontext);
|
||||
require_capability('mod/bigbluebuttonbn:managerecordings', $recordingcontext);
|
||||
require_capability("mod/bigbluebuttonbn:{$action}recordings", $recordingcontext);
|
||||
}
|
||||
$additionaloptionsobject = $additionaloptions ? json_decode($additionaloptions) : null;
|
||||
// Specific action such as import, delete, publish, unpublish, edit,....
|
||||
if (method_exists(recording_action::class, "$action")) {
|
||||
forward_static_call(
|
||||
['\mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_action', "$action"],
|
||||
$recording,
|
||||
$instance,
|
||||
$additionaloptionsobject
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(
|
||||
PARAM_INT,
|
||||
'bigbluebuttonbn instance id, this might be a different one from the one set in recordingid in case of importing'
|
||||
),
|
||||
'recordingid' => new external_value(PARAM_INT, 'The moodle internal recording ID'),
|
||||
'action' => new external_value(PARAM_ALPHANUMEXT, 'The action to perform'),
|
||||
'additionaloptions' => new external_value(PARAM_RAW, 'Additional options', VALUE_REQUIRED, null),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\external;
|
||||
|
||||
use context_module;
|
||||
use external_api;
|
||||
use external_description;
|
||||
use external_function_parameters;
|
||||
use external_single_structure;
|
||||
use external_value;
|
||||
use external_warnings;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External service to trigger the course module viewed event and update the module completion status
|
||||
*
|
||||
* This is mainly used by the mobile application.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @category external
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class view_bigbluebuttonbn extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters([
|
||||
'bigbluebuttonbnid' => new external_value(PARAM_INT, 'bigbluebuttonbn instance id'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the course module viewed event and update the module completion status.
|
||||
*
|
||||
* @param int $instanceid the bigbluebuttonbn instance id
|
||||
* @return array of warnings and status result
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute($instanceid) {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . "/mod/bigbluebuttonbn/lib.php");
|
||||
|
||||
['bigbluebuttonbnid' => $instanceid] = self::validate_parameters(
|
||||
self::execute_parameters(),
|
||||
['bigbluebuttonbnid' => $instanceid]
|
||||
);
|
||||
|
||||
$instance = instance::get_from_instanceid($instanceid);
|
||||
|
||||
if (empty($instance)) {
|
||||
return [
|
||||
'status' => false,
|
||||
'warnings' => [
|
||||
[
|
||||
'item' => 'mod_bigbluebuttonbn',
|
||||
'itemid' => 0,
|
||||
'warningcode' => 'nosuchinstance',
|
||||
'message' => get_string('nosuchinstance', 'mod_bigbluebuttonbn',
|
||||
(object) ['id' => $instanceid, 'entity' => 'bigbluebuttonbn'])
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
$context = context_module::instance($instance->get_cm_id());
|
||||
self::validate_context($context);
|
||||
|
||||
require_capability('mod/bigbluebuttonbn:view', $context);
|
||||
// Call the bigbluebuttonbn/lib API.
|
||||
bigbluebuttonbn_view($instance->get_instance_data(), $instance->get_course(), $instance->get_cm(), $context);
|
||||
return [
|
||||
'status' => true,
|
||||
'warnings' => []
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return new external_single_structure([
|
||||
'status' => new external_value(PARAM_BOOL, 'status: true if success'),
|
||||
'warnings' => new external_warnings()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\bigbluebutton\recordings;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
/**
|
||||
* Collection of helper methods for handling recordings actions in Moodle.
|
||||
*
|
||||
* Utility class for meeting actions
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording_action {
|
||||
/**
|
||||
* Import recording
|
||||
*
|
||||
* @param recording $recording
|
||||
* @param instance $targetinstance
|
||||
*/
|
||||
public static function import(recording $recording, instance $targetinstance) {
|
||||
$recording->create_imported_recording($targetinstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing delete on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function delete(recording $recording) {
|
||||
// As the recordingid was not identified as imported recording link, execute delete on a real recording.
|
||||
// Step 1, delete imported links associated to the recording.
|
||||
$recordingstodelete = recording::get_records(['recordingid' => $recording->get('recordingid'),
|
||||
'imported' => true]);
|
||||
foreach ($recordingstodelete as $rec) {
|
||||
$rec->delete();
|
||||
}
|
||||
$recording->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing edit on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function edit(recording $recording) {
|
||||
$recording->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing unprotect on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function unprotect(recording $recording) {
|
||||
if ($recording->get('imported')) {
|
||||
/* Since the recording link is the one fetched from the BBB server, imported recordings can not be
|
||||
* unprotected. There is no need to do anything else.
|
||||
*/
|
||||
throw new \moodle_exception('cannotperformaction', 'mod_bigblubuebuttobn', '', 'unprotect');
|
||||
}
|
||||
$recording->set('protected', false);
|
||||
$recording->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing protect on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function protect(recording $recording) {
|
||||
if ($recording->get('imported')) {
|
||||
/* Since the recording link is the one fetched from the BBB server, imported recordings can not be
|
||||
* protected. There is no need to do anything else.
|
||||
*/
|
||||
throw new \moodle_exception('cannotperformaction', 'mod_bigblubuebuttobn', '', 'protect');
|
||||
}
|
||||
$recording->set('protected', true);
|
||||
$recording->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing unpublish on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function unpublish(recording $recording) {
|
||||
if ($recording->get('imported')) {
|
||||
/* Since the recording link is the one fetched from the BBB server, imported recordings can not be
|
||||
* unpublished. There is no need to do anything else.
|
||||
*/
|
||||
throw new \moodle_exception('cannotperformaction', 'mod_bigblubuebuttobn', '', 'unpublish');
|
||||
}
|
||||
$recording->set('published', false);
|
||||
$recording->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for performing publish on recordings.
|
||||
*
|
||||
* @param recording $recording
|
||||
*/
|
||||
public static function publish(recording $recording) {
|
||||
if ($recording->get('imported')) {
|
||||
/* Since the recording link is the one fetched from the BBB server, imported recordings can not be
|
||||
* unpublished. There is no need to do anything else.
|
||||
*/
|
||||
throw new \moodle_exception('cannotperformaction', 'mod_bigblubuebuttobn', '', 'publish');
|
||||
}
|
||||
$recording->set('published', true);
|
||||
$recording->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\bigbluebutton\recordings;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\local\helpers\roles;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\output\recording_description_editable;
|
||||
use mod_bigbluebuttonbn\output\recording_name_editable;
|
||||
use mod_bigbluebuttonbn\output\recording_row_actionbar;
|
||||
use mod_bigbluebuttonbn\output\recording_row_playback;
|
||||
use mod_bigbluebuttonbn\output\recording_row_preview;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* The recordings_data.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class recording_data {
|
||||
|
||||
/**
|
||||
* Get the full recording table
|
||||
*
|
||||
* @param array $recordings
|
||||
* @param array $tools
|
||||
* @param instance|null $instance
|
||||
* @param int $courseid
|
||||
* @return array
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public static function get_recording_table(array $recordings, array $tools, instance $instance = null,
|
||||
int $courseid = 0): array {
|
||||
$typeprofiles = bigbluebutton_proxy::get_instance_type_profiles();
|
||||
$typeprofile = empty($instance) ? $typeprofiles[0] : $typeprofiles[$instance->get_type()];
|
||||
$lang = get_string('locale', 'core_langconfig');
|
||||
$locale = substr($lang, 0, strpos($lang, '.'));
|
||||
$tabledata = [
|
||||
'activity' => empty($instance) ? '' : bigbluebutton_proxy::view_get_activity_status($instance),
|
||||
'ping_interval' => (int) config::get('waitformoderator_ping_interval') * 1000,
|
||||
'locale' => substr($locale, 0, strpos($locale, '_')),
|
||||
'profile_features' => $typeprofile['features'],
|
||||
'columns' => [],
|
||||
'data' => '',
|
||||
];
|
||||
$hascapabilityincourse = empty($instance) && roles::has_capability_in_course($courseid,
|
||||
'mod/bigbluebuttonbn:managerecordings');
|
||||
|
||||
$data = [];
|
||||
|
||||
// Build table content.
|
||||
foreach ($recordings as $recording) {
|
||||
// Protected recordings is not a standard feature, remove actions when protected flag is not present.
|
||||
$rowtools = $tools;
|
||||
if (in_array('protect', $rowtools) && $recording->get('protected') === null) {
|
||||
$rowtools = array_diff($rowtools, ['protect', 'unprotect']);
|
||||
}
|
||||
$rowdata = self::row($instance, $recording, $rowtools);
|
||||
if (!empty($rowdata)) {
|
||||
$data[] = $rowdata;
|
||||
}
|
||||
}
|
||||
|
||||
$columns = [
|
||||
[
|
||||
'key' => 'playback',
|
||||
'label' => get_string('view_recording_playback', 'bigbluebuttonbn'),
|
||||
'width' => '125px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'recording',
|
||||
'label' => get_string('view_recording_name', 'bigbluebuttonbn'),
|
||||
'width' => '125px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'description',
|
||||
'label' => get_string('view_recording_description', 'bigbluebuttonbn'),
|
||||
'sortable' => true,
|
||||
'width' => '250px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
],
|
||||
];
|
||||
|
||||
// Initialize table headers.
|
||||
$ispreviewenabled = !empty($instance) && self::preview_enabled($instance);
|
||||
$ispreviewenabled = $ispreviewenabled || $hascapabilityincourse;
|
||||
if ($ispreviewenabled) {
|
||||
$columns[] = [
|
||||
'key' => 'preview',
|
||||
'label' => get_string('view_recording_preview', 'bigbluebuttonbn'),
|
||||
'width' => '250px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$columns[] = [
|
||||
'key' => 'date',
|
||||
'label' => get_string('view_recording_date', 'bigbluebuttonbn'),
|
||||
'sortable' => true,
|
||||
'width' => '225px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
];
|
||||
$columns[] = [
|
||||
'key' => 'duration',
|
||||
'label' => get_string('view_recording_duration', 'bigbluebuttonbn'),
|
||||
'width' => '50px',
|
||||
'allowHTML' => false,
|
||||
'sortable' => true,
|
||||
];
|
||||
// Either instance is empty and we must show the toolbar (with restricted content) or we check
|
||||
// specific rights related to the instance.
|
||||
$canmanagerecordings = !empty($instance) && $instance->can_manage_recordings();
|
||||
$canmanagerecordings = $canmanagerecordings || $hascapabilityincourse;
|
||||
if ($canmanagerecordings) {
|
||||
$columns[] = [
|
||||
'key' => 'actionbar',
|
||||
'label' => get_string('view_recording_actionbar', 'bigbluebuttonbn'),
|
||||
'width' => '120px',
|
||||
'type' => 'html',
|
||||
'allowHTML' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$tabledata['columns'] = $columns;
|
||||
$tabledata['data'] = json_encode($data);
|
||||
|
||||
return $tabledata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function builds a row for the data used by the recording table.
|
||||
*
|
||||
* TODO: replace this with templates whenever possible so we just
|
||||
* return the data via the API.
|
||||
*
|
||||
* @param instance|null $instance $instance
|
||||
* @param recording $rec a recording row
|
||||
* @param array|null $tools
|
||||
* @param int|null $courseid
|
||||
* @return stdClass|null
|
||||
*/
|
||||
public static function row(?instance $instance, recording $rec, ?array $tools = null, ?int $courseid = 0): ?stdClass {
|
||||
global $PAGE;
|
||||
|
||||
$hascapabilityincourse = empty($instance) && roles::has_capability_in_course($courseid,
|
||||
'mod/bigbluebuttonbn:managerecordings');
|
||||
$renderer = $PAGE->get_renderer('mod_bigbluebuttonbn');
|
||||
foreach ($tools as $key => $tool) {
|
||||
if ((!empty($instance) && !$instance->can_perform_on_recordings($tool))
|
||||
|| (empty($instance) && !$hascapabilityincourse)) {
|
||||
unset($tools[$key]);
|
||||
}
|
||||
}
|
||||
if (!self::include_recording_table_row($instance, $rec)) {
|
||||
return null;
|
||||
}
|
||||
$rowdata = new stdClass();
|
||||
|
||||
// Set recording_playback.
|
||||
$recordingplayback = new recording_row_playback($rec, $instance);
|
||||
$rowdata->playback = $renderer->render($recordingplayback);
|
||||
|
||||
if (empty($instance)) {
|
||||
// Set activity name.
|
||||
$rowdata->recording = $rec->get('name');
|
||||
|
||||
// Set activity description.
|
||||
$rowdata->description = $rec->get('description');
|
||||
} else {
|
||||
// Set activity name.
|
||||
$recordingname = new recording_name_editable($rec, $instance);
|
||||
$rowdata->recording = $renderer->render_inplace_editable($recordingname);
|
||||
// Set activity description.
|
||||
$recordingdescription = new recording_description_editable($rec, $instance);
|
||||
$rowdata->description = $renderer->render_inplace_editable($recordingdescription);
|
||||
}
|
||||
|
||||
if ((!empty($instance) && self::preview_enabled($instance)) || $hascapabilityincourse) {
|
||||
// Set recording_preview.
|
||||
$rowdata->preview = '';
|
||||
if ($rec->get('playbacks')) {
|
||||
$rowpreview = new recording_row_preview($rec);
|
||||
$rowdata->preview = $renderer->render($rowpreview);
|
||||
}
|
||||
}
|
||||
// Set date.
|
||||
$starttime = $rec->get('starttime');
|
||||
$rowdata->date = !is_null($starttime) ? floatval($starttime) : 0;
|
||||
// Set duration.
|
||||
$rowdata->duration = self::row_duration($rec);
|
||||
// Set actionbar, if user is allowed to manage recordings.
|
||||
if ((!empty($instance) && $instance->can_manage_recordings()) || $hascapabilityincourse) {
|
||||
$actionbar = new recording_row_actionbar($rec, $tools);
|
||||
$rowdata->actionbar = $renderer->render($actionbar);
|
||||
}
|
||||
return $rowdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function evaluates if recording preview should be included.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return bool
|
||||
*/
|
||||
public static function preview_enabled(instance $instance): bool {
|
||||
return $instance->get_instance_var('recordings_preview') == '1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function converts recording duration used in row for the data used by the recording table.
|
||||
*
|
||||
* @param recording $recording
|
||||
* @return int
|
||||
*/
|
||||
protected static function row_duration(recording $recording): int {
|
||||
$playbacks = $recording->get('playbacks');
|
||||
if (empty($playbacks)) {
|
||||
return 0;
|
||||
}
|
||||
foreach ($playbacks as $playback) {
|
||||
// Ignore restricted playbacks.
|
||||
if (array_key_exists('restricted', $playback) && strtolower($playback['restricted']) == 'true') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take the length form the fist playback with an actual value.
|
||||
if (!empty($playback['length'])) {
|
||||
return intval($playback['length']);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to handle yet unknown recording types
|
||||
*
|
||||
* @param string $playbacktype : for now presentation, video, statistics, capture, notes, podcast
|
||||
* @return string the matching language string or a capitalised version of the provided string
|
||||
*/
|
||||
public static function type_text(string $playbacktype): string {
|
||||
// Check first if string exists, and if it does not, just default to the capitalised version of the string.
|
||||
$text = ucwords($playbacktype);
|
||||
$typestringid = 'view_recording_format_' . $playbacktype;
|
||||
if (get_string_manager()->string_exists($typestringid, 'bigbluebuttonbn')) {
|
||||
$text = get_string($typestringid, 'bigbluebuttonbn');
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function evaluates if recording row should be included in the table.
|
||||
*
|
||||
* @param instance|null $instance
|
||||
* @param recording $rec a bigbluebuttonbn_recordings row
|
||||
* @return bool
|
||||
*/
|
||||
protected static function include_recording_table_row(?instance $instance, recording $rec): bool {
|
||||
if (empty($instance)) {
|
||||
return roles::has_capability_in_course($rec->get('courseid'), 'mod/bigbluebuttonbn:managerecordings');
|
||||
}
|
||||
// Exclude unpublished recordings, only if user has no rights to manage them.
|
||||
if (!$rec->get('published') && !$instance->can_manage_recordings()) {
|
||||
return false;
|
||||
}
|
||||
// Imported recordings are always shown as long as they are published.
|
||||
if ($rec->get('imported')) {
|
||||
return true;
|
||||
}
|
||||
// Administrators and moderators are always allowed.
|
||||
if ($instance->is_admin() || $instance->is_moderator()) {
|
||||
return true;
|
||||
}
|
||||
// When groups are enabled, exclude those to which the user doesn't have access to.
|
||||
if ($instance->uses_groups()) {
|
||||
return intval($rec->get('groupid')) === intval($instance->get_group_id());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
/**
|
||||
* Handles the global configuration based on config.php.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class config {
|
||||
|
||||
/** @var string Default bigbluebutton server url */
|
||||
public const DEFAULT_SERVER_URL = 'http://test-install.blindsidenetworks.com/bigbluebutton/';
|
||||
|
||||
/** @var string Default bigbluebutton server shared secret */
|
||||
public const DEFAULT_SHARED_SECRET = '8cd8ef52e8e101574e400365b55e11a6';
|
||||
|
||||
/**
|
||||
* Returns moodle version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_moodle_version_major() {
|
||||
global $CFG;
|
||||
$versionarray = explode('.', $CFG->version);
|
||||
return $versionarray[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configuration default values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function defaultvalues() {
|
||||
return [
|
||||
'server_url' => self::DEFAULT_SERVER_URL,
|
||||
'shared_secret' => self::DEFAULT_SHARED_SECRET,
|
||||
'voicebridge_editable' => false,
|
||||
'importrecordings_enabled' => false,
|
||||
'importrecordings_from_deleted_enabled' => false,
|
||||
'waitformoderator_default' => false,
|
||||
'waitformoderator_editable' => true,
|
||||
'waitformoderator_ping_interval' => '10',
|
||||
'waitformoderator_cache_ttl' => '60',
|
||||
'userlimit_default' => '0',
|
||||
'userlimit_editable' => false,
|
||||
'preuploadpresentation_enabled' => false,
|
||||
'sendnotifications_enabled' => false,
|
||||
'recordingready_enabled' => false,
|
||||
'recordingstatus_enabled' => false,
|
||||
'meetingevents_enabled' => false,
|
||||
'participant_moderator_default' => '0',
|
||||
'scheduled_pre_opening' => '10',
|
||||
'recordings_enabled' => true,
|
||||
'recordings_html_default' => false,
|
||||
'recordings_html_editable' => false,
|
||||
'recordings_deleted_default' => false,
|
||||
'recordings_deleted_editable' => false,
|
||||
'recordings_imported_default' => false,
|
||||
'recordings_imported_editable' => false,
|
||||
'recordings_preview_default' => true,
|
||||
'recordings_preview_editable' => false,
|
||||
'recordings_validate_url' => true,
|
||||
'recording_default' => true,
|
||||
'recording_editable' => true,
|
||||
'recording_refresh_period' => recording::RECORDING_REFRESH_DEFAULT_PERIOD,
|
||||
'recording_icons_enabled' => true,
|
||||
'recording_all_from_start_default' => false,
|
||||
'recording_all_from_start_editable' => false,
|
||||
'recording_hide_button_default' => false,
|
||||
'recording_hide_button_editable' => false,
|
||||
'general_warning_message' => '',
|
||||
'general_warning_roles' => 'editingteacher,teacher',
|
||||
'general_warning_box_type' => 'info',
|
||||
'general_warning_button_text' => '',
|
||||
'general_warning_button_href' => '',
|
||||
'general_warning_button_class' => '',
|
||||
'muteonstart_default' => false,
|
||||
'muteonstart_editable' => false,
|
||||
'disablecam_default' => false,
|
||||
'disablecam_editable' => true,
|
||||
'disablemic_default' => false,
|
||||
'disablemic_editable' => true,
|
||||
'disableprivatechat_default' => false,
|
||||
'disableprivatechat_editable' => true,
|
||||
'disablepublicchat_default' => false,
|
||||
'disablepublicchat_editable' => true,
|
||||
'disablenote_default' => false,
|
||||
'disablenote_editable' => true,
|
||||
'hideuserlist_default' => false,
|
||||
'hideuserlist_editable' => true,
|
||||
'lockedlayout_default' => false,
|
||||
'lockedlayout_editable' => true,
|
||||
'lockonjoin_default' => false,
|
||||
'lockonjoin_editable' => true,
|
||||
'lockonjoinconfigurable_default' => false,
|
||||
'lockonjoinconfigurable_editable' => true,
|
||||
'welcome_default' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default value for an specific setting.
|
||||
*
|
||||
* @param string $setting
|
||||
* @return string
|
||||
*/
|
||||
protected static function defaultvalue($setting) {
|
||||
$defaultvalues = self::defaultvalues();
|
||||
if (!array_key_exists($setting, $defaultvalues)) {
|
||||
return null;
|
||||
}
|
||||
return $defaultvalues[$setting];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value for an specific setting.
|
||||
*
|
||||
* @param string $setting
|
||||
* @return string
|
||||
*/
|
||||
public static function get($setting) {
|
||||
global $CFG;
|
||||
if (isset($CFG->bigbluebuttonbn[$setting])) {
|
||||
return (string)$CFG->bigbluebuttonbn[$setting];
|
||||
}
|
||||
if (isset($CFG->{'bigbluebuttonbn_'.$setting})) {
|
||||
return (string)$CFG->{'bigbluebuttonbn_'.$setting};
|
||||
}
|
||||
return self::defaultvalue($setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if recording settings are enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function recordings_enabled() {
|
||||
return (boolean)self::get('recordings_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if imported recording settings are enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function importrecordings_enabled() {
|
||||
return (boolean)self::get('importrecordings_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps current settings in an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_options() {
|
||||
return [
|
||||
'version_major' => self::get_moodle_version_major(),
|
||||
'voicebridge_editable' => self::get('voicebridge_editable'),
|
||||
'importrecordings_enabled' => self::get('importrecordings_enabled'),
|
||||
'importrecordings_from_deleted_enabled' => self::get('importrecordings_from_deleted_enabled'),
|
||||
'waitformoderator_default' => self::get('waitformoderator_default'),
|
||||
'waitformoderator_editable' => self::get('waitformoderator_editable'),
|
||||
'userlimit_default' => self::get('userlimit_default'),
|
||||
'userlimit_editable' => self::get('userlimit_editable'),
|
||||
'preuploadpresentation_enabled' => self::get('preuploadpresentation_enabled'),
|
||||
'sendnotifications_enabled' => self::get('sendnotifications_enabled'),
|
||||
'recordings_enabled' => self::get('recordings_enabled'),
|
||||
'meetingevents_enabled' => self::get('meetingevents_enabled'),
|
||||
'recordings_html_default' => self::get('recordings_html_default'),
|
||||
'recordings_html_editable' => self::get('recordings_html_editable'),
|
||||
'recordings_deleted_default' => self::get('recordings_deleted_default'),
|
||||
'recordings_deleted_editable' => self::get('recordings_deleted_editable'),
|
||||
'recordings_imported_default' => self::get('recordings_imported_default'),
|
||||
'recordings_imported_editable' => self::get('recordings_imported_editable'),
|
||||
'recordings_preview_default' => self::get('recordings_preview_default'),
|
||||
'recordings_preview_editable' => self::get('recordings_preview_editable'),
|
||||
'recordings_validate_url' => self::get('recordings_validate_url'),
|
||||
'recording_default' => self::get('recording_default'),
|
||||
'recording_editable' => self::get('recording_editable'),
|
||||
'recording_refresh_period' => self::get('recording_refresh_period'),
|
||||
'recording_icons_enabled' => self::get('recording_icons_enabled'),
|
||||
'recording_all_from_start_default' => self::get('recording_all_from_start_default'),
|
||||
'recording_all_from_start_editable' => self::get('recording_all_from_start_editable'),
|
||||
'recording_hide_button_default' => self::get('recording_hide_button_default'),
|
||||
'recording_hide_button_editable' => self::get('recording_hide_button_editable'),
|
||||
'general_warning_message' => self::get('general_warning_message'),
|
||||
'general_warning_box_type' => self::get('general_warning_box_type'),
|
||||
'general_warning_button_text' => self::get('general_warning_button_text'),
|
||||
'general_warning_button_href' => self::get('general_warning_button_href'),
|
||||
'general_warning_button_class' => self::get('general_warning_button_class'),
|
||||
'muteonstart_editable' => self::get('muteonstart_editable'),
|
||||
'muteonstart_default' => self::get('muteonstart_default'),
|
||||
'disablecam_editable' => self::get('disablecam_editable'),
|
||||
'disablecam_default' => self::get('disablecam_default'),
|
||||
'disablemic_editable' => self::get('disablemic_editable'),
|
||||
'disablemic_default' => self::get('disablemic_default'),
|
||||
'disableprivatechat_editable' => self::get('disableprivatechat_editable'),
|
||||
'disableprivatechat_default' => self::get('disableprivatechat_default'),
|
||||
'disablepublicchat_editable' => self::get('disablepublicchat_editable'),
|
||||
'disablepublicchat_default' => self::get('disablepublicchat_default'),
|
||||
'disablenote_editable' => self::get('disablenote_editable'),
|
||||
'disablenote_default' => self::get('disablenote_default'),
|
||||
'hideuserlist_editable' => self::get('hideuserlist_editable'),
|
||||
'hideuserlist_default' => self::get('hideuserlist_default'),
|
||||
'lockedlayout_editable' => self::get('lockedlayout_editable'),
|
||||
'lockedlayout_default' => self::get('lockedlayout_default'),
|
||||
'lockonjoin_editable' => self::get('lockonjoin_editable'),
|
||||
'lockonjoin_default' => self::get('lockonjoin_default'),
|
||||
'lockonjoinconfigurable_editable' => self::get('lockonjoinconfigurable_editable'),
|
||||
'lockonjoinconfigurable_default' => self::get('lockonjoinconfigurable_default'),
|
||||
'welcome_default' => self::get('welcome_default'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns an array with enabled features for an specific profile type.
|
||||
*
|
||||
* @param array $typeprofiles
|
||||
* @param string $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_enabled_features($typeprofiles, $type = null) {
|
||||
$enabledfeatures = [];
|
||||
$features = $typeprofiles[instance::TYPE_ALL]['features'];
|
||||
if (!is_null($type) && key_exists($type, $typeprofiles)) {
|
||||
$features = $typeprofiles[$type]['features'];
|
||||
}
|
||||
$enabledfeatures['showroom'] = (in_array('all', $features) || in_array('showroom', $features));
|
||||
// Evaluates if recordings are enabled for the Moodle site.
|
||||
$enabledfeatures['showrecordings'] = false;
|
||||
if (self::recordings_enabled()) {
|
||||
$enabledfeatures['showrecordings'] = (in_array('all', $features) || in_array('showrecordings', $features));
|
||||
}
|
||||
$enabledfeatures['importrecordings'] = false;
|
||||
if (self::importrecordings_enabled()) {
|
||||
$enabledfeatures['importrecordings'] = (in_array('all', $features) || in_array('importrecordings', $features));
|
||||
}
|
||||
return $enabledfeatures;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\exceptions;
|
||||
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
|
||||
/**
|
||||
* Class bigbluebutton_exception generic exception
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
class bigbluebutton_exception extends \moodle_exception {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode The name of the string from error.php to print
|
||||
* @param mixed $additionalinfo Extra words and phrases that might be required in the error string
|
||||
*/
|
||||
public function __construct($errorcode, $additionalinfo = null) {
|
||||
parent::__construct($errorcode, plugin::COMPONENT, '', $additionalinfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\exceptions;
|
||||
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn cannot join meeting exception.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
class meeting_join_exception extends \moodle_exception {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode The name of the string from error.php to print
|
||||
*/
|
||||
public function __construct($errorcode) {
|
||||
parent::__construct($errorcode, plugin::COMPONENT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\exceptions;
|
||||
|
||||
/**
|
||||
* Class server_not_available_exception
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
class server_not_available_exception extends \moodle_exception {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode The name of the string from error.php to print
|
||||
* @param string $module name of module
|
||||
* @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to
|
||||
* the site index page.
|
||||
* @param mixed $a Extra words and phrases that might be required in the error string
|
||||
* @param string $debuginfo optional debugging information
|
||||
*/
|
||||
public function __construct($errorcode, $module = '', $link = '', $a = null, $debuginfo = null) {
|
||||
global $CFG;
|
||||
$hasdebugdeveloper = (
|
||||
isset($CFG->debugdisplay) &&
|
||||
isset($CFG->debug) &&
|
||||
$CFG->debugdisplay &&
|
||||
$CFG->debug === DEBUG_DEVELOPER
|
||||
);
|
||||
if ($hasdebugdeveloper && is_null($debuginfo)) {
|
||||
$debuginfo = $this->getTraceAsString();
|
||||
}
|
||||
parent::__construct($errorcode, $module, $link, $a, $debuginfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The mod_bigbluebuttonbn files helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\helpers;
|
||||
|
||||
use cache;
|
||||
use cache_store;
|
||||
use context;
|
||||
use context_module;
|
||||
use context_system;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Utility class for all files routines helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class files {
|
||||
|
||||
/**
|
||||
* Helper for validating pluginfile.
|
||||
*
|
||||
* @param stdClass $context context object
|
||||
* @param string $filearea file area
|
||||
*
|
||||
* @return bool|null false if file not valid
|
||||
*/
|
||||
public static function pluginfile_valid(stdClass $context, string $filearea): ?bool {
|
||||
|
||||
// Can be in context module or in context_system (if is the presentation by default).
|
||||
if (!in_array($context->contextlevel, [CONTEXT_MODULE, CONTEXT_SYSTEM])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!array_key_exists($filearea, self::get_file_areas())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting pluginfile.
|
||||
*
|
||||
* @param stdClass $course course object
|
||||
* @param \cm_info $cm course module object
|
||||
* @param context $context context object
|
||||
* @param string $filearea file area
|
||||
* @param array $args extra arguments
|
||||
*
|
||||
* @return \stored_file|bool
|
||||
*/
|
||||
public static function pluginfile_file(stdClass $course, \cm_info $cm, context $context, string $filearea, array $args) {
|
||||
$filename = self::get_plugin_filename($course, $cm, $context, $args);
|
||||
if (!$filename) {
|
||||
return false;
|
||||
}
|
||||
$fullpath = "/$context->id/mod_bigbluebuttonbn/$filearea/0/" . $filename;
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file_by_hash(sha1($fullpath));
|
||||
if (!$file || $file->is_directory()) {
|
||||
return false;
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full path to the file attached as a preuploaded presentation
|
||||
* or if there is none, set the presentation field will be set to blank.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonformdata BigBlueButtonBN form data
|
||||
* Note that $bigbluebuttonformdata->presentation is the id of the filearea whereas the bbb instance table
|
||||
* stores the file name/path
|
||||
* @return string
|
||||
*/
|
||||
public static function save_media_file(stdClass &$bigbluebuttonformdata): string {
|
||||
if (!isset($bigbluebuttonformdata->presentation) || $bigbluebuttonformdata->presentation == '') {
|
||||
return '';
|
||||
}
|
||||
$context = context_module::instance($bigbluebuttonformdata->coursemodule);
|
||||
// Set the filestorage object.
|
||||
$fs = get_file_storage();
|
||||
// Save the file if it exists that is currently in the draft area.
|
||||
file_save_draft_area_files($bigbluebuttonformdata->presentation, $context->id, 'mod_bigbluebuttonbn', 'presentation', 0);
|
||||
// Get the file if it exists.
|
||||
$files = $fs->get_area_files(
|
||||
$context->id,
|
||||
'mod_bigbluebuttonbn',
|
||||
'presentation',
|
||||
0,
|
||||
'itemid, filepath, filename',
|
||||
false
|
||||
);
|
||||
// Check that there is a file to process.
|
||||
$filesrc = '';
|
||||
if (count($files) == 1) {
|
||||
// Get the first (and only) file.
|
||||
$file = reset($files);
|
||||
$filesrc = '/' . $file->get_filename();
|
||||
}
|
||||
return $filesrc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper return array containing the file descriptor for a preuploaded presentation.
|
||||
*
|
||||
* @param context $context
|
||||
* @param string $presentation matching presentation file name
|
||||
* @param int $id bigbluebutton instance id
|
||||
* @param bool $withnonce add nonce to the url
|
||||
* @return array|null the representation of the presentation as an associative array
|
||||
*/
|
||||
public static function get_presentation(context $context, string $presentation, $id = null, $withnonce = false): ?array {
|
||||
global $CFG;
|
||||
$fs = get_file_storage();
|
||||
$files = [];
|
||||
if (empty($presentation)) {
|
||||
if ($CFG->bigbluebuttonbn_preuploadpresentation_enabled) {
|
||||
// Item has not presentation but presentation is enabled..
|
||||
// Check if exist some file by default in general mod setting ("presentationdefault").
|
||||
$files = $fs->get_area_files(
|
||||
context_system::instance()->id,
|
||||
'mod_bigbluebuttonbn',
|
||||
'presentationdefault',
|
||||
0,
|
||||
"filename",
|
||||
false
|
||||
);
|
||||
$id = null; // This is the general presentation/default so we will generate
|
||||
// an id as if the activity was null.
|
||||
}
|
||||
} else {
|
||||
$files = $fs->get_area_files(
|
||||
$context->id,
|
||||
'mod_bigbluebuttonbn',
|
||||
'presentation',
|
||||
false,
|
||||
'itemid, filepath, filename',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (count($files) == 0) {
|
||||
return null; // No presentation.
|
||||
}
|
||||
|
||||
$pnoncevalue = 0;
|
||||
if ($withnonce) {
|
||||
$nonceid = 0;
|
||||
if (!is_null($id)) {
|
||||
$instance = instance::get_from_instanceid($id);
|
||||
$nonceid = $instance->get_instance_id();
|
||||
}
|
||||
$pnoncevalue = self::generate_nonce($nonceid);
|
||||
}
|
||||
|
||||
$file = null;
|
||||
foreach ($files as $f) {
|
||||
if (basename($f->get_filename()) == basename($presentation)) {
|
||||
$file = $f;
|
||||
}
|
||||
}
|
||||
if (!$file && !empty($files)) {
|
||||
$file = reset($files);
|
||||
}
|
||||
if (empty($file)) {
|
||||
return null; // File was not found.
|
||||
}
|
||||
|
||||
// Note: $pnoncevalue is an int.
|
||||
$url = moodle_url::make_pluginfile_url(
|
||||
$file->get_contextid(),
|
||||
$file->get_component(),
|
||||
$file->get_filearea(),
|
||||
$withnonce ? $pnoncevalue : null, // Hack: item id as a nonce.
|
||||
$file->get_filepath(),
|
||||
$file->get_filename()
|
||||
);
|
||||
return [
|
||||
'icondesc' => get_mimetype_description($file),
|
||||
'iconname' => file_file_icon($file, 24),
|
||||
'name' => $file->get_filename(),
|
||||
'url' => $url->out(false),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting pluginfile name.
|
||||
*
|
||||
* @param stdClass $course course object
|
||||
* @param \cm_info $cm course module object
|
||||
* @param context $context context object
|
||||
* @param array $args extra arguments
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function get_plugin_filename(stdClass $course, \cm_info $cm, context $context, array $args): ?string {
|
||||
global $DB;
|
||||
if ($context->contextlevel != CONTEXT_SYSTEM) {
|
||||
// Plugin has a file to use as default in general setting.
|
||||
// The difference with the standard bigbluebuttonbn_pluginfile_filename() are.
|
||||
// - Context is system, so we don't need to check the cmid in this case.
|
||||
// - The area is "presentationdefault_cache".
|
||||
if (!$DB->get_record('bigbluebuttonbn', ['id' => $cm->instance])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Plugin has a file to use as default in general setting.
|
||||
// The difference with the standard bigbluebuttonbn_pluginfile_filename() are.
|
||||
// - Context is system, so we don't need to check the cmid in this case.
|
||||
// - The area is "presentationdefault_cache".
|
||||
if (count($args) > 1) {
|
||||
$id = 0;
|
||||
if ($cm) {
|
||||
$instance = instance::get_from_cmid($cm->id);
|
||||
$id = $instance->get_instance_id();
|
||||
}
|
||||
$actualnonce = self::get_nonce($id);
|
||||
return ($args['0'] == $actualnonce) ? $args['1'] : null;
|
||||
|
||||
}
|
||||
require_course_login($course, true, $cm, true, true);
|
||||
if (!has_capability('mod/bigbluebuttonbn:join', $context)) {
|
||||
return null;
|
||||
}
|
||||
return implode('/', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper generates a salt used for the preuploaded presentation callback url.
|
||||
*
|
||||
* @param int $id
|
||||
* @return int
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected static function get_nonce(int $id): int {
|
||||
$cache = static::get_nonce_cache();
|
||||
$pnoncekey = sha1($id);
|
||||
$existingnoncedata = $cache->get($pnoncekey);
|
||||
if ($existingnoncedata) {
|
||||
if ($existingnoncedata->counter > 0) {
|
||||
$existingnoncedata->counter--;
|
||||
$cache->set($pnoncekey, $existingnoncedata);
|
||||
return $existingnoncedata->nonce;
|
||||
}
|
||||
}
|
||||
// The item id was adapted for granting public access to the presentation once in order to allow BigBlueButton to gather
|
||||
// the file once.
|
||||
return static::generate_nonce($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a nonce and store it in the cache
|
||||
*
|
||||
* @param int $id
|
||||
* @return int
|
||||
*/
|
||||
protected static function generate_nonce($id): int {
|
||||
$cache = static::get_nonce_cache();
|
||||
$pnoncekey = sha1($id);
|
||||
// The item id was adapted for granting public access to the presentation once in order to allow BigBlueButton to gather
|
||||
// the file once.
|
||||
$pnoncevalue = ((int) microtime()) + mt_rand();
|
||||
$cache->set($pnoncekey, (object) ['nonce' => $pnoncevalue, 'counter' => 2]);
|
||||
return $pnoncevalue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache for nonce
|
||||
*
|
||||
* @return \cache_application|\cache_session|cache_store
|
||||
*/
|
||||
private static function get_nonce_cache() {
|
||||
return cache::make_from_params(
|
||||
cache_store::MODE_APPLICATION,
|
||||
'mod_bigbluebuttonbn',
|
||||
'presentation_cache'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of file areas.
|
||||
*
|
||||
* @return array a list of available file areas
|
||||
*
|
||||
*/
|
||||
protected static function get_file_areas(): array {
|
||||
$areas = [];
|
||||
$areas['presentation'] = get_string('mod_form_block_presentation', 'bigbluebuttonbn');
|
||||
$areas['presentationdefault'] = get_string('mod_form_block_presentation_default', 'bigbluebuttonbn');
|
||||
return $areas;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\helpers;
|
||||
|
||||
use calendar_event;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\logger;
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
use mod_bigbluebuttonbn\task\send_instance_update_notification;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Utility class for all instance (module) routines helper.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
class mod_helper {
|
||||
|
||||
/**
|
||||
* Runs any processes that must run before a bigbluebuttonbn insert/update.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
public static function process_pre_save(stdClass $bigbluebuttonbn) {
|
||||
self::process_pre_save_instance($bigbluebuttonbn);
|
||||
self::process_pre_save_checkboxes($bigbluebuttonbn);
|
||||
self::process_pre_save_common($bigbluebuttonbn);
|
||||
$bigbluebuttonbn->participants = htmlspecialchars_decode($bigbluebuttonbn->participants);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs process for defining the instance (insert/update).
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_pre_save_instance(stdClass $bigbluebuttonbn): void {
|
||||
$bigbluebuttonbn->timemodified = time();
|
||||
if ((integer) $bigbluebuttonbn->instance == 0) {
|
||||
$bigbluebuttonbn->meetingid = 0;
|
||||
$bigbluebuttonbn->timecreated = time();
|
||||
$bigbluebuttonbn->timemodified = 0;
|
||||
// As it is a new activity, assign passwords.
|
||||
$bigbluebuttonbn->moderatorpass = plugin::random_password(12);
|
||||
$bigbluebuttonbn->viewerpass = plugin::random_password(12, $bigbluebuttonbn->moderatorpass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs process for assigning default value to checkboxes.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_pre_save_checkboxes($bigbluebuttonbn) {
|
||||
if (!isset($bigbluebuttonbn->wait)) {
|
||||
$bigbluebuttonbn->wait = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->record)) {
|
||||
$bigbluebuttonbn->record = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordallfromstart)) {
|
||||
$bigbluebuttonbn->recordallfromstart = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordhidebutton)) {
|
||||
$bigbluebuttonbn->recordhidebutton = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordings_html)) {
|
||||
$bigbluebuttonbn->recordings_html = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordings_deleted)) {
|
||||
$bigbluebuttonbn->recordings_deleted = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordings_imported)) {
|
||||
$bigbluebuttonbn->recordings_imported = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordings_preview)) {
|
||||
$bigbluebuttonbn->recordings_preview = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->muteonstart)) {
|
||||
$bigbluebuttonbn->muteonstart = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->disablecam)) {
|
||||
$bigbluebuttonbn->disablecam = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->disablemic)) {
|
||||
$bigbluebuttonbn->disablemic = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->disableprivatechat)) {
|
||||
$bigbluebuttonbn->disableprivatechat = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->disablepublicchat)) {
|
||||
$bigbluebuttonbn->disablepublicchat = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->disablenote)) {
|
||||
$bigbluebuttonbn->disablenote = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->hideuserlist)) {
|
||||
$bigbluebuttonbn->hideuserlist = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->lockedlayout)) {
|
||||
$bigbluebuttonbn->lockedlayout = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->lockonjoin)) {
|
||||
$bigbluebuttonbn->lockonjoin = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->lockonjoinconfigurable)) {
|
||||
$bigbluebuttonbn->lockonjoinconfigurable = 0;
|
||||
}
|
||||
if (!isset($bigbluebuttonbn->recordings_validate_url)) {
|
||||
$bigbluebuttonbn->recordings_validate_url = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs process for wipping common settings when 'recordings only'.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_pre_save_common(stdClass $bigbluebuttonbn): void {
|
||||
// Make sure common settings are removed when 'recordings only'.
|
||||
if ($bigbluebuttonbn->type == instance::TYPE_RECORDING_ONLY) {
|
||||
$bigbluebuttonbn->groupmode = 0;
|
||||
$bigbluebuttonbn->groupingid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs any processes that must be run after a bigbluebuttonbn insert/update.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
public static function process_post_save(stdClass $bigbluebuttonbn): void {
|
||||
if (isset($bigbluebuttonbn->notification) && $bigbluebuttonbn->notification) {
|
||||
self::process_post_save_notification($bigbluebuttonbn);
|
||||
}
|
||||
self::process_post_save_event($bigbluebuttonbn);
|
||||
self::process_post_save_completion($bigbluebuttonbn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a message on insert/update which is sent to all users enrolled.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_post_save_notification(stdClass $bigbluebuttonbn): void {
|
||||
$task = new send_instance_update_notification();
|
||||
$task->set_instance_id($bigbluebuttonbn->id);
|
||||
|
||||
if (isset($bigbluebuttonbn->add) && !empty($bigbluebuttonbn->add)) {
|
||||
$task->set_update_type(send_instance_update_notification::TYPE_CREATED);
|
||||
} else {
|
||||
$task->set_update_type(send_instance_update_notification::TYPE_UPDATED);
|
||||
}
|
||||
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an event after a bigbluebuttonbn insert/update.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_post_save_event(stdClass $bigbluebuttonbn): void {
|
||||
global $CFG, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/calendar/lib.php');
|
||||
$eventid = $DB->get_field('event', 'id', [
|
||||
'modulename' => 'bigbluebuttonbn',
|
||||
'instance' => $bigbluebuttonbn->id,
|
||||
'eventtype' => logger::EVENT_MEETING_START
|
||||
]);
|
||||
|
||||
// Delete the event from calendar when/if openingtime is NOT set.
|
||||
if (!isset($bigbluebuttonbn->openingtime) || !$bigbluebuttonbn->openingtime) {
|
||||
if ($eventid) {
|
||||
$calendarevent = calendar_event::load($eventid);
|
||||
$calendarevent->delete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add event to the calendar as openingtime is set.
|
||||
$event = (object) [
|
||||
'eventtype' => logger::EVENT_MEETING_START,
|
||||
'type' => CALENDAR_EVENT_TYPE_ACTION,
|
||||
'name' => get_string('calendarstarts', 'bigbluebuttonbn', $bigbluebuttonbn->name),
|
||||
'description' => format_module_intro('bigbluebuttonbn', $bigbluebuttonbn, $bigbluebuttonbn->coursemodule, false),
|
||||
'format' => FORMAT_HTML,
|
||||
'courseid' => $bigbluebuttonbn->course,
|
||||
'groupid' => 0,
|
||||
'userid' => 0,
|
||||
'modulename' => 'bigbluebuttonbn',
|
||||
'instance' => $bigbluebuttonbn->id,
|
||||
'timestart' => $bigbluebuttonbn->openingtime,
|
||||
'timeduration' => 0,
|
||||
'timesort' => $bigbluebuttonbn->openingtime,
|
||||
'visible' => instance_is_visible('bigbluebuttonbn', $bigbluebuttonbn),
|
||||
'priority' => null,
|
||||
];
|
||||
|
||||
// Update the event in calendar when/if eventid was found.
|
||||
if ($eventid) {
|
||||
$event->id = $eventid;
|
||||
$calendarevent = calendar_event::load($eventid);
|
||||
$calendarevent->update($event);
|
||||
return;
|
||||
}
|
||||
calendar_event::create($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an event after a bigbluebuttonbn activity is completed.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
|
||||
**/
|
||||
protected static function process_post_save_completion(stdClass $bigbluebuttonbn): void {
|
||||
if (empty($bigbluebuttonbn->completionexpected)) {
|
||||
return;
|
||||
}
|
||||
\core_completion\api::update_completion_date_event(
|
||||
$bigbluebuttonbn->coursemodule,
|
||||
'bigbluebuttonbn',
|
||||
$bigbluebuttonbn->id,
|
||||
$bigbluebuttonbn->completionexpected
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* The mod_bigbluebuttonbn resetting instance helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\helpers;
|
||||
|
||||
use context_module;
|
||||
use core_tag_tag;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
/**
|
||||
* Utility class for resetting instance routines helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class reset {
|
||||
|
||||
/**
|
||||
* Used by the reset_course_userdata for deleting recordings
|
||||
*
|
||||
* This will delete recordings in the database and not in the remote BBB server.
|
||||
*
|
||||
* @param int $courseid
|
||||
*/
|
||||
public static function reset_recordings(int $courseid): void {
|
||||
// Criteria for search : courseid or bigbluebuttonbn=null or subset=false or includedeleted=true.
|
||||
$recordings = recording::get_recordings_for_course(
|
||||
$courseid,
|
||||
[], // Exclude itself.
|
||||
false,
|
||||
true
|
||||
);
|
||||
if ($recordings) {
|
||||
// Remove all the recordings.
|
||||
foreach ($recordings as $recording) {
|
||||
$recording->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the reset_course_userdata for deleting tags linked to bigbluebuttonbn instances in the course.
|
||||
*
|
||||
* @param int $courseid
|
||||
*/
|
||||
public static function reset_tags(int $courseid): void {
|
||||
global $DB;
|
||||
// Remove all the tags linked to the room/activities in this course.
|
||||
if ($bigbluebuttonbns = $DB->get_records('bigbluebuttonbn', ['course' => $courseid])) {
|
||||
foreach ($bigbluebuttonbns as $bigbluebuttonbn) {
|
||||
if (!$cm = get_coursemodule_from_instance('bigbluebuttonbn', $bigbluebuttonbn->id, $courseid)) {
|
||||
continue;
|
||||
}
|
||||
$context = context_module::instance($cm->id);
|
||||
core_tag_tag::delete_instances('mod_bigbluebuttonbn', null, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the reset_course_userdata for deleting events linked to bigbluebuttonbn instances in the course.
|
||||
*
|
||||
* @param string $courseid
|
||||
* @return bool status array
|
||||
*/
|
||||
public static function reset_events($courseid) {
|
||||
global $DB;
|
||||
// Remove all the events.
|
||||
return $DB->delete_records('event', ['modulename' => 'bigbluebuttonbn', 'courseid' => $courseid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns status used on every defined reset action.
|
||||
*
|
||||
* @param string $item
|
||||
* @return array status array
|
||||
*/
|
||||
public static function reset_getstatus(string $item): array {
|
||||
return ['component' => get_string('modulenameplural', 'bigbluebuttonbn'),
|
||||
'item' => get_string("removed{$item}", 'bigbluebuttonbn'),
|
||||
'error' => false];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define items to be reset by course/reset.php
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function reset_course_items(): array {
|
||||
$items = ["events" => 0, "tags" => 0, "logs" => 0];
|
||||
// Include recordings only if enabled.
|
||||
if ((boolean) config::recordings_enabled()) {
|
||||
$items["recordings"] = 0;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* The mod_bigbluebuttonbn roles helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\helpers;
|
||||
|
||||
use cache;
|
||||
use cache_store;
|
||||
use context;
|
||||
use context_course;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Utility class for all roles routines helper
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class roles {
|
||||
|
||||
/** @var int The bigbluebutton viewer role */
|
||||
public const ROLE_VIEWER = 'viewer';
|
||||
|
||||
/** @var string The bigbluebutton moderator role */
|
||||
public const ROLE_MODERATOR = 'moderator';
|
||||
|
||||
/**
|
||||
* Returns user roles in a context.
|
||||
*
|
||||
* @param context $context
|
||||
* @param int $userid
|
||||
*
|
||||
* @return array $userroles
|
||||
*/
|
||||
public static function get_user_roles(context $context, int $userid) {
|
||||
global $DB;
|
||||
$userroles = get_user_roles($context, $userid);
|
||||
if ($userroles) {
|
||||
$where = '';
|
||||
foreach ($userroles as $userrole) {
|
||||
$where .= (empty($where) ? ' WHERE' : ' OR') . ' id=' . $userrole->roleid;
|
||||
}
|
||||
$userroles = $DB->get_records_sql('SELECT * FROM {role}' . $where);
|
||||
}
|
||||
return $userroles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns guest role wrapped in an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_guest_role() {
|
||||
$guestrole = get_guest_role();
|
||||
return [$guestrole->id => $guestrole];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the users in a context wrapped for html select element.
|
||||
*
|
||||
* @param context_course $context
|
||||
* @param null $bbactivity
|
||||
* @return array $users
|
||||
*/
|
||||
public static function get_users_array(context_course $context, $bbactivity = null) {
|
||||
// CONTRIB-7972, check the group of current user and course group mode.
|
||||
$groups = null;
|
||||
$users = (array) get_enrolled_users($context, '', 0, 'u.*', null, 0, 0, true);
|
||||
$course = get_course($context->instanceid);
|
||||
$groupmode = groups_get_course_groupmode($course);
|
||||
if ($bbactivity) {
|
||||
list($bbcourse, $cm) = get_course_and_cm_from_instance($bbactivity->id, 'bigbluebuttonbn');
|
||||
$groupmode = groups_get_activity_groupmode($cm);
|
||||
|
||||
}
|
||||
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
|
||||
global $USER;
|
||||
$groups = groups_get_all_groups($course->id, $USER->id);
|
||||
$users = [];
|
||||
foreach ($groups as $g) {
|
||||
$users += (array) get_enrolled_users($context, '', $g->id, 'u.*', null, 0, 0, true);
|
||||
}
|
||||
}
|
||||
return array_map(
|
||||
function($u) {
|
||||
return ['id' => $u->id, 'name' => fullname($u)];
|
||||
},
|
||||
$users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can do some administration in this course, likely manage recordings
|
||||
*
|
||||
* @param int $courseid
|
||||
* @param string $capability
|
||||
*/
|
||||
public static function has_capability_in_course(int $courseid, string $capability) {
|
||||
global $DB;
|
||||
if (empty($courseid) || $DB->record_exists('course', ['id' => $courseid])) {
|
||||
return has_capability('moodle/site:config', \context_system::instance());
|
||||
}
|
||||
|
||||
$coursecontext = context_course::instance($courseid);
|
||||
return has_capability($capability, $coursecontext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the roles in a context.
|
||||
*
|
||||
* @param context|null $context $context
|
||||
* @param bool|null $onlyviewableroles
|
||||
*
|
||||
* @return array $roles
|
||||
*/
|
||||
public static function get_roles(?context $context = null, ?bool $onlyviewableroles = true) {
|
||||
global $CFG;
|
||||
|
||||
if ($onlyviewableroles == true && $CFG->branch >= 35) {
|
||||
$roles = (array) get_viewable_roles($context);
|
||||
foreach ($roles as $key => $value) {
|
||||
$roles[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
$roles = (array) role_get_names($context);
|
||||
foreach ($roles as $key => $value) {
|
||||
$roles[$key] = $value->localname;
|
||||
}
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the roles in a context wrapped for html select element.
|
||||
*
|
||||
* @param context|null $context $context
|
||||
* @param bool $onlyviewableroles
|
||||
*
|
||||
* @return array $users
|
||||
*/
|
||||
protected static function get_roles_select(context $context = null, bool $onlyviewableroles = true) {
|
||||
global $CFG;
|
||||
|
||||
if ($onlyviewableroles == true && $CFG->branch >= 35) {
|
||||
$roles = (array) get_viewable_roles($context);
|
||||
foreach ($roles as $key => $value) {
|
||||
$roles[$key] = ['id' => $key, 'name' => $value];
|
||||
}
|
||||
} else {
|
||||
$roles = (array) role_get_names($context);
|
||||
foreach ($roles as $key => $value) {
|
||||
$roles[$key] = ['id' => $value->id, 'name' => $value->localname];
|
||||
}
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns role that corresponds to an id.
|
||||
*
|
||||
* @param string|integer $id
|
||||
*
|
||||
* @return stdClass $role
|
||||
*/
|
||||
protected static function get_role($id): stdClass {
|
||||
$roles = (array) role_get_names();
|
||||
if (is_numeric($id) && isset($roles[$id])) {
|
||||
return (object) $roles[$id];
|
||||
}
|
||||
foreach ($roles as $role) {
|
||||
if ($role->shortname == $id) {
|
||||
return $role;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array to populate a list of participants used in mod_form.js.
|
||||
*
|
||||
* @param context $context
|
||||
* @param null|stdClass $bbactivity
|
||||
* @return array $data
|
||||
*/
|
||||
public static function get_participant_data(context $context, ?stdClass $bbactivity = null) {
|
||||
$data = [
|
||||
'all' => [
|
||||
'name' => get_string('mod_form_field_participant_list_type_all', 'bigbluebuttonbn'),
|
||||
'children' => []
|
||||
],
|
||||
];
|
||||
$data['role'] = [
|
||||
'name' => get_string('mod_form_field_participant_list_type_role', 'bigbluebuttonbn'),
|
||||
'children' => self::get_roles_select($context, true)
|
||||
];
|
||||
$data['user'] = [
|
||||
'name' => get_string('mod_form_field_participant_list_type_user', 'bigbluebuttonbn'),
|
||||
'children' => self::get_users_array($context, $bbactivity),
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array to populate a list of participants used in mod_form.php.
|
||||
*
|
||||
* @param stdClass|null $bigbluebuttonbn
|
||||
* @param context $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_participant_list(?stdClass $bigbluebuttonbn, context $context): array {
|
||||
global $USER;
|
||||
if ($bigbluebuttonbn == null) {
|
||||
return self::get_participant_rules_encoded(
|
||||
self::get_participant_list_default($context, $USER->id)
|
||||
);
|
||||
}
|
||||
if (empty($bigbluebuttonbn->participants)) {
|
||||
$bigbluebuttonbn->participants = "[]";
|
||||
}
|
||||
$rules = json_decode($bigbluebuttonbn->participants, true);
|
||||
if (empty($rules)) {
|
||||
$rules = self::get_participant_list_default($context,
|
||||
bigbluebutton_proxy::get_instance_ownerid($bigbluebuttonbn));
|
||||
}
|
||||
return self::get_participant_rules_encoded($rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array to populate a list of participants used in mod_form.php with default values.
|
||||
*
|
||||
* @param context $context
|
||||
* @param int $ownerid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_participant_list_default(context $context, ?int $ownerid = null) {
|
||||
$participantlist = [];
|
||||
$participantlist[] = [
|
||||
'selectiontype' => 'all',
|
||||
'selectionid' => 'all',
|
||||
'role' => self::ROLE_VIEWER,
|
||||
];
|
||||
$defaultrules = explode(',', \mod_bigbluebuttonbn\local\config::get('participant_moderator_default'));
|
||||
foreach ($defaultrules as $defaultrule) {
|
||||
if ($defaultrule == '0') {
|
||||
if (!empty($ownerid) && is_enrolled($context, $ownerid)) {
|
||||
$participantlist[] = [
|
||||
'selectiontype' => 'user',
|
||||
'selectionid' => (string) $ownerid,
|
||||
'role' => self::ROLE_MODERATOR];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$participantlist[] = [
|
||||
'selectiontype' => 'role',
|
||||
'selectionid' => $defaultrule,
|
||||
'role' => self::ROLE_MODERATOR];
|
||||
}
|
||||
return $participantlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array to populate a list of participants used in mod_form.php with bigbluebuttonbn values.
|
||||
*
|
||||
* @param array $rules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_participant_rules_encoded(array $rules): array {
|
||||
foreach ($rules as $key => $rule) {
|
||||
if ($rule['selectiontype'] !== 'role' || is_numeric($rule['selectionid'])) {
|
||||
continue;
|
||||
}
|
||||
$role = self::get_role($rule['selectionid']);
|
||||
if ($role == null) {
|
||||
unset($rules[$key]);
|
||||
continue;
|
||||
}
|
||||
$rule['selectionid'] = $role->id;
|
||||
$rules[$key] = $rule;
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array to populate a list of participant_selection used in mod_form.php.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_participant_selection_data(): array {
|
||||
return [
|
||||
'type_options' => [
|
||||
'all' => get_string('mod_form_field_participant_list_type_all', 'bigbluebuttonbn'),
|
||||
'role' => get_string('mod_form_field_participant_list_type_role', 'bigbluebuttonbn'),
|
||||
'user' => get_string('mod_form_field_participant_list_type_user', 'bigbluebuttonbn'),
|
||||
],
|
||||
'type_selected' => 'all',
|
||||
'options' => ['all' => '---------------'],
|
||||
'selected' => 'all',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate if a user in a context is moderator based on roles and participation rules.
|
||||
*
|
||||
* @param context $context
|
||||
* @param array $participantlist
|
||||
* @param int $userid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_moderator(context $context, array $participantlist, ?int $userid = null): bool {
|
||||
global $USER;
|
||||
// If an admin, then also a moderator.
|
||||
if (has_capability('moodle/site:config', $context)) {
|
||||
return true;
|
||||
}
|
||||
if (!is_array($participantlist)) {
|
||||
return false;
|
||||
}
|
||||
if (empty($userid)) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
$userroles = self::get_guest_role();
|
||||
if (!isguestuser()) {
|
||||
$userroles = self::get_user_roles($context, $userid);
|
||||
}
|
||||
return self::is_moderator_validator($participantlist, $userid, $userroles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates participant list rules to evaluate if a user is moderator.
|
||||
*
|
||||
* @param array $participantlist
|
||||
* @param int $userid
|
||||
* @param array $userroles
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_moderator_validator(array $participantlist, int $userid, array $userroles): bool {
|
||||
// Iterate participant rules.
|
||||
foreach ($participantlist as $participant) {
|
||||
if (self::is_moderator_validate_rule($participant, $userid, $userroles)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate if a user is moderator based on roles and a particular participation rule.
|
||||
*
|
||||
* @param array $participant
|
||||
* @param int $userid
|
||||
* @param array $userroles
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_moderator_validate_rule(array $participant, int $userid, array $userroles): bool {
|
||||
if ($participant['role'] == self::ROLE_VIEWER) {
|
||||
return false;
|
||||
}
|
||||
// Validation for the 'all' rule.
|
||||
if ($participant['selectiontype'] == 'all') {
|
||||
return true;
|
||||
}
|
||||
// Validation for a 'user' rule.
|
||||
if ($participant['selectiontype'] == 'user') {
|
||||
if ($participant['selectionid'] == $userid) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Validation for a 'role' rule.
|
||||
$role = self::get_role($participant['selectionid']);
|
||||
if ($role != null && array_key_exists($role->id, $userroles)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the meeting info cached object when a participant has joined.
|
||||
*
|
||||
* @param string $meetingid
|
||||
* @param bool $ismoderator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function participant_joined(string $meetingid, bool $ismoderator): void {
|
||||
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'mod_bigbluebuttonbn', 'meetings_cache');
|
||||
$result = $cache->get($meetingid);
|
||||
$meetinginfo = json_decode($result['meeting_info']);
|
||||
$meetinginfo->participantCount += 1;
|
||||
if ($ismoderator) {
|
||||
$meetinginfo->moderatorCount += 1;
|
||||
}
|
||||
$cache->set($meetingid, ['creation_time' => $result['creation_time'],
|
||||
'meeting_info' => json_encode($meetinginfo)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns a list of courses a user has access to, wrapped in an array that can be used
|
||||
* by a html select.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return array
|
||||
*/
|
||||
public static function import_get_courses_for_select(instance $instance): array {
|
||||
if ($instance->is_admin()) {
|
||||
$courses = get_courses('all', 'c.fullname ASC');
|
||||
// It includes the name of the site as a course (category 0), so remove the first one.
|
||||
unset($courses['1']);
|
||||
} else {
|
||||
$courses = enrol_get_users_courses($instance->get_user_id(), false, 'id,shortname,fullname');
|
||||
}
|
||||
$courses = array_filter($courses, function($course) {
|
||||
$modules = get_fast_modinfo($course->id);
|
||||
return !empty($modules->instances['bigbluebuttonbn']);
|
||||
});
|
||||
$coursesforselect = [];
|
||||
foreach ($courses as $course) {
|
||||
$coursesforselect[$course->id] = $course->fullname . " (" . $course->shortname . ")";
|
||||
}
|
||||
return $coursesforselect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\proxy;
|
||||
|
||||
use cache;
|
||||
use completion_info;
|
||||
use Exception;
|
||||
use mod_bigbluebuttonbn\completion\custom_completion;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* The bigbluebutton proxy class.
|
||||
*
|
||||
* This class acts as a proxy between Moodle and the BigBlueButton API server,
|
||||
* and handles all requests relating to the server and meetings.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class bigbluebutton_proxy extends proxy_base {
|
||||
|
||||
/**
|
||||
* Builds and returns a url for joining a bigbluebutton meeting.
|
||||
*
|
||||
* @param string $meetingid
|
||||
* @param string $username
|
||||
* @param string $pw
|
||||
* @param string $logouturl
|
||||
* @param string|null $configtoken
|
||||
* @param string|null $userid
|
||||
* @param string|null $createtime
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_join_url(
|
||||
string $meetingid,
|
||||
string $username,
|
||||
string $pw,
|
||||
string $logouturl,
|
||||
string $configtoken = null,
|
||||
string $userid = null,
|
||||
string $createtime = null
|
||||
): ?string {
|
||||
$data = [
|
||||
'meetingID' => $meetingid,
|
||||
'fullName' => $username,
|
||||
'password' => $pw,
|
||||
'logoutURL' => $logouturl,
|
||||
];
|
||||
|
||||
if (!is_null($configtoken)) {
|
||||
$data['configToken'] = $configtoken;
|
||||
}
|
||||
|
||||
if (!is_null($userid)) {
|
||||
$data['userID'] = $userid;
|
||||
}
|
||||
|
||||
if (!is_null($createtime)) {
|
||||
$data['createTime'] = $createtime;
|
||||
}
|
||||
|
||||
return self::action_url('join', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform api request on BBB.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public static function get_server_version(): ?string {
|
||||
$cache = cache::make('mod_bigbluebuttonbn', 'serverinfo');
|
||||
$serverversion = $cache->get('serverversion');
|
||||
|
||||
if (!$serverversion) {
|
||||
$xml = self::fetch_endpoint_xml('');
|
||||
if (!$xml || $xml->returncode != 'SUCCESS') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($xml->version)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$serverversion = (string) $xml->version;
|
||||
$cache->set('serverversion', $serverversion);
|
||||
}
|
||||
|
||||
return (double) $serverversion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting the owner userid of a bigbluebuttonbn instance.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn BigBlueButtonBN instance
|
||||
* @return int ownerid (a valid user id or null if not registered/found)
|
||||
*/
|
||||
public static function get_instance_ownerid(stdClass $bigbluebuttonbn): int {
|
||||
global $DB;
|
||||
|
||||
$filters = [
|
||||
'bigbluebuttonbnid' => $bigbluebuttonbn->id,
|
||||
'log' => 'Add',
|
||||
];
|
||||
|
||||
return (int) $DB->get_field('bigbluebuttonbn_logs', 'userid', $filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper evaluates if a voicebridge number is unique.
|
||||
*
|
||||
* @param int $instance
|
||||
* @param int $voicebridge
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_voicebridge_number_unique(int $instance, int $voicebridge): bool {
|
||||
global $DB;
|
||||
if ($voicebridge == 0) {
|
||||
return true;
|
||||
}
|
||||
$select = 'voicebridge = ' . $voicebridge;
|
||||
if ($instance != 0) {
|
||||
$select .= ' AND id <>' . $instance;
|
||||
}
|
||||
if (!$DB->get_records_select('bigbluebuttonbn', $select)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function validates a remote resource.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_remote_resource_valid(string $url): bool {
|
||||
$urlhost = parse_url($url, PHP_URL_HOST);
|
||||
$serverurlhost = parse_url(\mod_bigbluebuttonbn\local\config::get('server_url'), PHP_URL_HOST);
|
||||
|
||||
if ($urlhost == $serverurlhost) {
|
||||
// Skip validation when the recording URL host is the same as the configured BBB server.
|
||||
return true;
|
||||
}
|
||||
|
||||
$cache = cache::make('mod_bigbluebuttonbn', 'validatedurls');
|
||||
|
||||
if ($cachevalue = $cache->get($urlhost)) {
|
||||
// Skip validation when the recording URL was already validated.
|
||||
return $cachevalue == 1;
|
||||
}
|
||||
|
||||
$curl = new curl();
|
||||
$curl->head($url);
|
||||
|
||||
$isvalid = false;
|
||||
if ($info = $curl->get_info()) {
|
||||
if ($info['http_code'] == 200) {
|
||||
$isvalid = true;
|
||||
} else {
|
||||
debugging(
|
||||
"Resources hosted by {$urlhost} are unreachable. Server responded with {$info['http_code']}",
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
$isvalid = false;
|
||||
}
|
||||
|
||||
// Note: When a cache key is not found, it returns false.
|
||||
// We need to distinguish between a result not found, and an invalid result.
|
||||
$cache->set($urlhost, $isvalid ? 1 : 0);
|
||||
}
|
||||
|
||||
return $isvalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function enqueues one user for being validated as for completion.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn
|
||||
* @param int $userid
|
||||
* @return void
|
||||
*/
|
||||
public static function enqueue_completion_event(stdClass $bigbluebuttonbn, int $userid): void {
|
||||
try {
|
||||
// Create the instance of completion_update_state task.
|
||||
$task = new \mod_bigbluebuttonbn\task\completion_update_state();
|
||||
// Add custom data.
|
||||
$data = [
|
||||
'bigbluebuttonbn' => $bigbluebuttonbn,
|
||||
'userid' => $userid,
|
||||
];
|
||||
$task->set_custom_data($data);
|
||||
// CONTRIB-7457: Task should be executed by a user, maybe Teacher as Student won't have rights for overriding.
|
||||
// $ task -> set_userid ( $ user -> id );.
|
||||
// Enqueue it.
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
} catch (Exception $e) {
|
||||
mtrace("Error while enqueuing completion_update_state task. " . (string) $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function enqueues completion trigger.
|
||||
*
|
||||
* @param stdClass $bigbluebuttonbn
|
||||
* @param int $userid
|
||||
* @return void
|
||||
*/
|
||||
public static function update_completion_state(stdClass $bigbluebuttonbn, int $userid) {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
list($course, $cm) = get_course_and_cm_from_instance($bigbluebuttonbn, 'bigbluebuttonbn');
|
||||
$completion = new completion_info($course);
|
||||
if (!$completion->is_enabled($cm)) {
|
||||
mtrace("Completion not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
$bbbcompletion = new custom_completion($cm, $userid);
|
||||
if ($bbbcompletion->get_overall_completion_state()) {
|
||||
mtrace("Completion succeeded for user $userid");
|
||||
$completion->update_state($cm, COMPLETION_COMPLETE, $userid, true);
|
||||
} else {
|
||||
mtrace("Completion did not succeed for user $userid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns an array with the profiles (with features per profile) for the different types
|
||||
* of bigbluebuttonbn instances.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_instance_type_profiles(): array {
|
||||
$instanceprofiles = [
|
||||
instance::TYPE_ALL => [
|
||||
'id' => instance::TYPE_ALL,
|
||||
'name' => get_string('instance_type_default', 'bigbluebuttonbn'),
|
||||
'features' => ['all']
|
||||
],
|
||||
instance::TYPE_ROOM_ONLY => [
|
||||
'id' => instance::TYPE_ROOM_ONLY,
|
||||
'name' => get_string('instance_type_room_only', 'bigbluebuttonbn'),
|
||||
'features' => ['showroom', 'welcomemessage', 'voicebridge', 'waitformoderator', 'userlimit',
|
||||
'recording', 'sendnotifications', 'preuploadpresentation', 'permissions', 'schedule', 'groups',
|
||||
'modstandardelshdr', 'availabilityconditionsheader', 'tagshdr', 'competenciessection',
|
||||
'completionattendance', 'completionengagement', 'availabilityconditionsheader']
|
||||
],
|
||||
instance::TYPE_RECORDING_ONLY => [
|
||||
'id' => instance::TYPE_RECORDING_ONLY,
|
||||
'name' => get_string('instance_type_recording_only', 'bigbluebuttonbn'),
|
||||
'features' => ['showrecordings', 'importrecordings', 'availabilityconditionsheader']
|
||||
],
|
||||
];
|
||||
return $instanceprofiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns an array with the profiles (with features per profile) for the different types
|
||||
* of bigbluebuttonbn instances that the user is allowed to create.
|
||||
*
|
||||
* @param bool $room
|
||||
* @param bool $recording
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_instance_type_profiles_create_allowed(bool $room, bool $recording): array {
|
||||
$profiles = self::get_instance_type_profiles();
|
||||
if (!$room) {
|
||||
unset($profiles[instance::TYPE_ROOM_ONLY]);
|
||||
unset($profiles[instance::TYPE_ALL]);
|
||||
}
|
||||
if (!$recording) {
|
||||
unset($profiles[instance::TYPE_RECORDING_ONLY]);
|
||||
unset($profiles[instance::TYPE_ALL]);
|
||||
}
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns an array with the profiles (with features per profile) for the different types
|
||||
* of bigbluebuttonbn instances.
|
||||
*
|
||||
* @param array $profiles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_instance_profiles_array(array $profiles = []): array {
|
||||
$profilesarray = [];
|
||||
foreach ($profiles as $key => $profile) {
|
||||
$profilesarray[$profile['id']] = $profile['name'];
|
||||
}
|
||||
return $profilesarray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the status of an activity [open|not_started|ended].
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return string
|
||||
*/
|
||||
public static function view_get_activity_status(instance $instance): string {
|
||||
$now = time();
|
||||
if (!empty($instance->get_instance_var('openingtime')) && $now < $instance->get_instance_var('openingtime')) {
|
||||
// The activity has not been opened.
|
||||
return 'not_started';
|
||||
}
|
||||
if (!empty($instance->get_instance_var('closingtime')) && $now > $instance->get_instance_var('closingtime')) {
|
||||
// The activity has been closed.
|
||||
return 'ended';
|
||||
}
|
||||
// The activity is open.
|
||||
return 'open';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the remote server was contactable.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function require_working_server(instance $instance): void {
|
||||
try {
|
||||
self::get_server_version();
|
||||
} catch (server_not_available_exception $e) {
|
||||
self::handle_server_not_available($instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the server not being available.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function handle_server_not_available(instance $instance): void {
|
||||
\core\notification::add(
|
||||
self::get_server_not_available_message($instance),
|
||||
\core\notification::ERROR
|
||||
);
|
||||
redirect(self::get_server_not_available_url($instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message when server not available
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return string
|
||||
*/
|
||||
public static function get_server_not_available_message(instance $instance): string {
|
||||
if ($instance->is_admin()) {
|
||||
return get_string('view_error_unable_join', 'mod_bigbluebuttonbn');
|
||||
} else if ($instance->is_moderator()) {
|
||||
return get_string('view_error_unable_join_teacher', 'mod_bigbluebuttonbn');
|
||||
} else {
|
||||
return get_string('view_error_unable_join_student', 'mod_bigbluebuttonbn');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL to the page displaying that the server is not available
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return string
|
||||
*/
|
||||
public static function get_server_not_available_url(instance $instance): string {
|
||||
if ($instance->is_admin()) {
|
||||
return new moodle_url('/admin/settings.php', ['section' => 'modsettingbigbluebuttonbn']);
|
||||
} else if ($instance->is_moderator()) {
|
||||
return new moodle_url('/course/view.php', ['id' => $instance->get_course_id()]);
|
||||
} else {
|
||||
return new moodle_url('/course/view.php', ['id' => $instance->get_course_id()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Meeting
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $metadata
|
||||
* @param string|null $presentationname
|
||||
* @param string|null $presentationurl
|
||||
* @return array
|
||||
* @throws bigbluebutton_exception
|
||||
* @throws server_not_available_exception
|
||||
*/
|
||||
public static function create_meeting(
|
||||
array $data,
|
||||
array $metadata,
|
||||
?string $presentationname = null,
|
||||
?string $presentationurl = null
|
||||
): array {
|
||||
$createmeetingurl = self::action_url('create', $data, $metadata);
|
||||
|
||||
$curl = new curl();
|
||||
if (!is_null($presentationname) && !is_null($presentationurl)) {
|
||||
$payload = "<?xml version='1.0' encoding='UTF-8'?><modules><module name='presentation'><document url='" .
|
||||
$presentationurl . "' /></module></modules>";
|
||||
|
||||
$xml = $curl->post($createmeetingurl, $payload);
|
||||
} else {
|
||||
$xml = $curl->get($createmeetingurl);
|
||||
}
|
||||
|
||||
self::assert_returned_xml($xml);
|
||||
|
||||
if (empty($xml->meetingID)) {
|
||||
throw new bigbluebutton_exception('general_error_cannot_create_meeting');
|
||||
}
|
||||
|
||||
if ($xml->hasBeenForciblyEnded === 'true') {
|
||||
throw new bigbluebutton_exception('index_error_forciblyended');
|
||||
}
|
||||
|
||||
return [
|
||||
'meetingID' => (string) $xml->meetingID,
|
||||
'internalMeetingID' => (string) $xml->internalMeetingID,
|
||||
'attendeePW' => (string) $xml->attendeePW,
|
||||
'moderatorPW' => (string) $xml->moderatorPW
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meeting info for a given meeting id
|
||||
*
|
||||
* @param string $meetingid
|
||||
* @return array
|
||||
*/
|
||||
public static function get_meeting_info(string $meetingid): array {
|
||||
$xmlinfo = self::fetch_endpoint_xml('getMeetingInfo', ['meetingID' => $meetingid]);
|
||||
self::assert_returned_xml($xmlinfo, ['meetingid' => $meetingid]);
|
||||
return (array) $xmlinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform end meeting on BBB.
|
||||
*
|
||||
* @param string $meetingid
|
||||
* @param string $modpw
|
||||
*/
|
||||
public static function end_meeting(string $meetingid, string $modpw): void {
|
||||
$xml = self::fetch_endpoint_xml('end', ['meetingID' => $meetingid, 'password' => $modpw]);
|
||||
self::assert_returned_xml($xml, ['meetingid' => $meetingid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper evaluates if the bigbluebutton server used belongs to blindsidenetworks domain.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_bn_server() {
|
||||
if (config::get('bn_server')) {
|
||||
return true;
|
||||
}
|
||||
$parsedurl = parse_url(config::get('server_url'));
|
||||
if (!isset($parsedurl['host'])) {
|
||||
return false;
|
||||
}
|
||||
$h = $parsedurl['host'];
|
||||
$hends = explode('.', $h);
|
||||
$hendslength = count($hends);
|
||||
return ($hends[$hendslength - 1] == 'com' && $hends[$hendslength - 2] == 'blindsidenetworks');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A curl wrapper for bbb.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\proxy;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
global $CFG;
|
||||
require_once("{$CFG->libdir}/filelib.php");
|
||||
|
||||
/**
|
||||
* A curl wrapper for bbb.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class curl extends \curl {
|
||||
/** @var string */
|
||||
protected $contenttype;
|
||||
|
||||
/**
|
||||
* Constructor for the class.
|
||||
*/
|
||||
public function __construct() {
|
||||
$settings = [];
|
||||
if (debugging()) {
|
||||
$settings = ['ignoresecurity' => true];
|
||||
}
|
||||
parent::__construct($settings);
|
||||
|
||||
$this->setopt(['SSL_VERIFYPEER' => true]);
|
||||
$this->set_content_type('text/xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the content type.
|
||||
*/
|
||||
public function get_content_type(): string {
|
||||
return $this->contenttype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired current content type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function set_content_type(string $type): self {
|
||||
$this->contenttype = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP POST method
|
||||
*
|
||||
* @param string $url
|
||||
* @param array|string $params
|
||||
* @param array $options
|
||||
* @return bool
|
||||
*/
|
||||
public function post($url, $params = '', $options = []) {
|
||||
if (!is_string($params)) {
|
||||
debugging('Only string paramaters are supported', DEBUG_DEVELOPER);
|
||||
$params = '';
|
||||
}
|
||||
|
||||
$options = [
|
||||
'CURLOPT_HTTPHEADER' => [
|
||||
'Content-Type: ' . $this->get_content_type(),
|
||||
'Content-Length: ' . strlen($params),
|
||||
'Content-Language: en-US',
|
||||
],
|
||||
];
|
||||
|
||||
return !empty($this->handle_response(parent::post($url, $params, $options)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the specified URL via a HEAD request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
*/
|
||||
public function head($url, $options = []) {
|
||||
$options['followlocation'] = true;
|
||||
$options['timeout'] = 1;
|
||||
|
||||
parent::head($url, $options);
|
||||
|
||||
return $this->get_info();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the specified URL via a GET request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $params
|
||||
* @param array $options
|
||||
*/
|
||||
public function get($url, $params = [], $options = []) {
|
||||
return $this->handle_response(parent::get($url, $params, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the response.
|
||||
*
|
||||
* @param mixed $response
|
||||
* @return null|SimpleXMLElement Null on error
|
||||
*/
|
||||
protected function handle_response($response): ?SimpleXMLElement {
|
||||
if (!$response) {
|
||||
debugging('No response returned for call', DEBUG_DEVELOPER);
|
||||
return null;
|
||||
}
|
||||
|
||||
$previous = libxml_use_internal_errors(true);
|
||||
try {
|
||||
$xml = simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS);
|
||||
} catch (Exception $e) {
|
||||
libxml_use_internal_errors($previous);
|
||||
debugging('Caught exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($xml instanceof SimpleXMLElement) {
|
||||
return $xml;
|
||||
}
|
||||
|
||||
debugging('Issue retrieving information from the server: ' . $response, DEBUG_DEVELOPER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\proxy;
|
||||
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception;
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
use moodle_url;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* The abstract proxy base class.
|
||||
*
|
||||
* This class provides common and shared functions used when interacting with
|
||||
* the BigBlueButton API server.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
abstract class proxy_base {
|
||||
|
||||
/**
|
||||
* Sometimes the server sends back some error and errorKeys that
|
||||
* can be converted to Moodle error messages
|
||||
*/
|
||||
const MEETING_ERROR = [
|
||||
'checksumError' => 'index_error_checksum',
|
||||
'notFound' => 'general_error_not_found',
|
||||
'maxConcurrent' => 'view_error_max_concurrent',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the right URL for the action specified.
|
||||
*
|
||||
* @param string $action
|
||||
* @param array $data
|
||||
* @param array $metadata
|
||||
* @return string
|
||||
*/
|
||||
protected static function action_url(string $action = '', array $data = [], array $metadata = []): string {
|
||||
$baseurl = self::sanitized_url() . $action . '?';
|
||||
$metadata = array_combine(array_map(function($k) {
|
||||
return 'meta_' . $k;
|
||||
}, array_keys($metadata)), $metadata);
|
||||
|
||||
$params = http_build_query($data + $metadata, '', '&');
|
||||
return $baseurl . $params . '&checksum=' . sha1($action . $params . self::sanitized_secret());
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the url used doesn't is in the format required.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function sanitized_url() {
|
||||
$serverurl = trim(config::get('server_url'));
|
||||
if (PHPUNIT_TEST) {
|
||||
$serverurl = (new moodle_url(TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER))->out(false);
|
||||
}
|
||||
if (substr($serverurl, -1) == '/') {
|
||||
$serverurl = rtrim($serverurl, '/');
|
||||
}
|
||||
if (substr($serverurl, -4) == '/api') {
|
||||
$serverurl = rtrim($serverurl, '/api');
|
||||
}
|
||||
return $serverurl . '/api/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the shared_secret used doesn't have trailing white characters.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function sanitized_secret(): string {
|
||||
return trim(config::get('shared_secret'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if there is a problem in the returned XML value
|
||||
*
|
||||
* @param SimpleXMLElement|bool $xml
|
||||
* @param array|null $additionaldetails
|
||||
* @throws bigbluebutton_exception
|
||||
* @throws server_not_available_exception
|
||||
*/
|
||||
protected static function assert_returned_xml($xml, ?array $additionaldetails = null): void {
|
||||
if (empty($xml)) {
|
||||
throw new server_not_available_exception(
|
||||
'general_error_no_answer',
|
||||
plugin::COMPONENT,
|
||||
(new moodle_url('/admin/settings.php?section=modsettingbigbluebuttonbn'))->out()
|
||||
);
|
||||
}
|
||||
if (is_bool($xml) && $xml) {
|
||||
// Nothing to do here, this might be a post returning that everything went well.
|
||||
return;
|
||||
}
|
||||
if ((string) $xml->returncode == 'FAILED') {
|
||||
$messagekey = (string) $xml->messageKey ?? '';
|
||||
if (!$additionaldetails) {
|
||||
$additionaldetails = [];
|
||||
}
|
||||
$additionaldetails['xmlmessage'] = (string) $xml->message ?? '';
|
||||
if (empty($messagekey) || empty(self::MEETING_ERROR[$messagekey])) {
|
||||
$messagekey = 'general_error_unable_connect';
|
||||
}
|
||||
|
||||
throw new bigbluebutton_exception($messagekey, json_encode($additionaldetails));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the XML from an endpoint and test for success.
|
||||
*
|
||||
* If the result could not be loaded, or the returncode was not 'SUCCESS', a null value is returned.
|
||||
*
|
||||
* @param string $action
|
||||
* @param array $data
|
||||
* @param array $metadata
|
||||
* @return null|bool|SimpleXMLElement
|
||||
*/
|
||||
protected static function fetch_endpoint_xml(
|
||||
string $action,
|
||||
array $data = [],
|
||||
array $metadata = []
|
||||
) {
|
||||
if (PHPUNIT_TEST && !defined('TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER')) {
|
||||
return true; // In case we still use fetch and mock server is not defined, this prevents
|
||||
// an error. This can happen if a function from lib.php is called in test from other modules
|
||||
// for example.
|
||||
}
|
||||
$curl = new curl();
|
||||
return $curl->get(self::action_url($action, $data, $metadata));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\local\proxy;
|
||||
|
||||
use cache;
|
||||
use cache_helper;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* The recording proxy.
|
||||
*
|
||||
* This class acts as a proxy between Moodle and the BigBlueButton API server,
|
||||
* and deals with all requests relating to recordings.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class recording_proxy extends proxy_base {
|
||||
|
||||
/**
|
||||
* Invalidate the MUC cache for the specified recording.
|
||||
*
|
||||
* @param string $recordid
|
||||
*/
|
||||
protected static function invalidate_cache_for_recording(string $recordid): void {
|
||||
cache_helper::invalidate_by_event('mod_bigbluebuttonbn/recordingchanged', [$recordid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform deleteRecordings on BBB.
|
||||
*
|
||||
* @param string $recordid a recording id
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete_recording(string $recordid): bool {
|
||||
$result = self::fetch_endpoint_xml('deleteRecordings', ['recordID' => $recordid]);
|
||||
if (!$result || $result->returncode != 'SUCCESS') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform publishRecordings on BBB.
|
||||
*
|
||||
* @param string $recordid
|
||||
* @param string $publish
|
||||
* @return bool
|
||||
*/
|
||||
public static function publish_recording(string $recordid, string $publish = 'true'): bool {
|
||||
$result = self::fetch_endpoint_xml('publishRecordings', [
|
||||
'recordID' => $recordid,
|
||||
'publish' => $publish,
|
||||
]);
|
||||
|
||||
self::invalidate_cache_for_recording($recordid);
|
||||
|
||||
if (!$result || $result->returncode != 'SUCCESS') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform publishRecordings on BBB.
|
||||
*
|
||||
* @param string $recordid
|
||||
* @param string $protected
|
||||
* @return bool
|
||||
*/
|
||||
public static function protect_recording(string $recordid, string $protected = 'true'): bool {
|
||||
$result = self::fetch_endpoint_xml('updateRecordings', [
|
||||
'recordID' => $recordid,
|
||||
'protect' => $protected,
|
||||
]);
|
||||
|
||||
self::invalidate_cache_for_recording($recordid);
|
||||
|
||||
if (!$result || $result->returncode != 'SUCCESS') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform updateRecordings on BBB.
|
||||
*
|
||||
* @param string $recordid a single record identifier
|
||||
* @param array $params ['key'=>param_key, 'value']
|
||||
*/
|
||||
public static function update_recording(string $recordid, array $params): bool {
|
||||
$result = self::fetch_endpoint_xml('updateRecordings', array_merge([
|
||||
'recordID' => $recordid
|
||||
], $params));
|
||||
|
||||
self::invalidate_cache_for_recording($recordid);
|
||||
|
||||
return $result ? $result->returncode == 'SUCCESS' : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to fetch a single recording from a BigBlueButton server.
|
||||
*
|
||||
* @param string $recordingid
|
||||
* @return null|array
|
||||
*/
|
||||
public static function fetch_recording(string $recordingid): ?array {
|
||||
$data = self::fetch_recordings([$recordingid]);
|
||||
|
||||
if (array_key_exists($recordingid, $data)) {
|
||||
return $data[$recordingid];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current recording is a protected recording and purge the cache if necessary.
|
||||
*
|
||||
* @param string $recordingid
|
||||
*/
|
||||
public static function purge_protected_recording(string $recordingid): void {
|
||||
$cache = cache::make('mod_bigbluebuttonbn', 'recordings');
|
||||
|
||||
$recording = $cache->get($recordingid);
|
||||
if (empty($recording)) {
|
||||
// This value was not cached to begin with.
|
||||
return;
|
||||
}
|
||||
|
||||
$currentfetchcache = cache::make('mod_bigbluebuttonbn', 'currentfetch');
|
||||
if ($currentfetchcache->has($recordingid)) {
|
||||
// This item was fetched in the current request.
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('protected', $recording) && $recording['protected'] === 'true') {
|
||||
// This item is protected. Purge it from the cache.
|
||||
$cache->delete($recordingid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to fetch recordings from a BigBlueButton server.
|
||||
*
|
||||
* @param array $keyids list of meetingids or recordingids
|
||||
* @param string $key the param name used for the BBB request (<recordID>|meetingID)
|
||||
* @return array (associative) with recordings indexed by recordID, each recording is a non sequential array
|
||||
*/
|
||||
public static function fetch_recordings(array $keyids = [], string $key = 'recordID'): array {
|
||||
$recordings = [];
|
||||
|
||||
// If $ids is empty return array() to prevent a getRecordings with meetingID and recordID set to ''.
|
||||
if (empty($keyids)) {
|
||||
return $recordings;
|
||||
}
|
||||
|
||||
$cache = cache::make('mod_bigbluebuttonbn', 'recordings');
|
||||
$currentfetchcache = cache::make('mod_bigbluebuttonbn', 'currentfetch');
|
||||
$recordings = array_filter($cache->get_many($keyids));
|
||||
$missingkeys = array_diff(array_values($keyids), array_keys($recordings));
|
||||
|
||||
$pagesize = 25;
|
||||
while ($ids = array_splice($missingkeys, 0, $pagesize)) {
|
||||
$fetchrecordings = self::fetch_recordings_page($ids, $key);
|
||||
$cache->set_many($fetchrecordings);
|
||||
$currentfetchcache->set_many(array_flip(array_keys($fetchrecordings)));
|
||||
|
||||
$recordings += $fetchrecordings;
|
||||
}
|
||||
|
||||
// Sort recordings.
|
||||
return self::sort_recordings($recordings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to fetch a page of recordings from the remote server.
|
||||
*
|
||||
* @param array $ids
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
private static function fetch_recordings_page(array $ids, $key = 'recordID'): array {
|
||||
// The getRecordings call is executed using a method GET (supported by all versions of BBB).
|
||||
$xml = self::fetch_endpoint_xml('getRecordings', [$key => implode(',', $ids)]);
|
||||
|
||||
if (!$xml) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($xml->returncode != 'SUCCESS') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isset($xml->recordings)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$recordings = [];
|
||||
// If there were meetings already created.
|
||||
foreach ($xml->recordings->recording as $recordingxml) {
|
||||
$recording = self::parse_recording($recordingxml);
|
||||
$recordings[$recording['recordID']] = $recording;
|
||||
|
||||
// Check if there is childs.
|
||||
if (isset($recordingxml->breakoutRooms->breakoutRoom)) {
|
||||
foreach ($recordingxml->breakoutRooms->breakoutRoom as $breakoutroom) {
|
||||
$xml = self::fetch_endpoint_xml('getRecordings', ['recordID' => implode(',', (array) $breakoutroom)]);
|
||||
if (!$xml || $xml->returncode != 'SUCCESS' || empty($xml->recordings)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there were meetings already created.
|
||||
foreach ($xml->recordings->recording as $subrecordingxml) {
|
||||
$recording = self::parse_recording($subrecordingxml);
|
||||
$recordings[$recording['recordID']] = $recording;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recordings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to sort an array of recordings. It compares the startTime in two recording objects.
|
||||
*
|
||||
* @param array $recordings
|
||||
* @return array
|
||||
*/
|
||||
public static function sort_recordings(array $recordings): array {
|
||||
global $CFG;
|
||||
|
||||
$resultless = !empty($CFG->bigbluebuttonbn_recordings_sortorder) ? -1 : 1;
|
||||
$resultmore = !empty($CFG->bigbluebuttonbn_recordings_sortorder) ? 1 : -1;
|
||||
|
||||
uasort($recordings, function($a, $b) use ($resultless, $resultmore) {
|
||||
if ($a['startTime'] < $b['startTime']) {
|
||||
return $resultless;
|
||||
}
|
||||
if ($a['startTime'] == $b['startTime']) {
|
||||
return 0;
|
||||
}
|
||||
return $resultmore;
|
||||
});
|
||||
|
||||
return $recordings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to parse an xml recording object and produce an array in the format used by the plugin.
|
||||
*
|
||||
* @param SimpleXMLElement $recording
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_recording(SimpleXMLElement $recording): array {
|
||||
// Add formats.
|
||||
$playbackarray = [];
|
||||
foreach ($recording->playback->format as $format) {
|
||||
$playbackarray[(string) $format->type] = [
|
||||
'type' => (string) $format->type,
|
||||
'url' => trim((string) $format->url), 'length' => (string) $format->length
|
||||
];
|
||||
// Add preview per format when existing.
|
||||
if ($format->preview) {
|
||||
$playbackarray[(string) $format->type]['preview'] =
|
||||
self::parse_preview_images($format->preview);
|
||||
}
|
||||
}
|
||||
// Add the metadata to the recordings array.
|
||||
$metadataarray =
|
||||
self::parse_recording_meta(get_object_vars($recording->metadata));
|
||||
$recordingarray = [
|
||||
'recordID' => (string) $recording->recordID,
|
||||
'meetingID' => (string) $recording->meetingID, 'meetingName' => (string) $recording->name,
|
||||
'published' => (string) $recording->published, 'startTime' => (string) $recording->startTime,
|
||||
'endTime' => (string) $recording->endTime, 'playbacks' => $playbackarray
|
||||
];
|
||||
if (isset($recording->protected)) {
|
||||
$recordingarray['protected'] = (string) $recording->protected;
|
||||
}
|
||||
return $recordingarray + $metadataarray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert an xml recording metadata object to an array in the format used by the plugin.
|
||||
*
|
||||
* @param array $metadata
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_recording_meta(array $metadata): array {
|
||||
$metadataarray = [];
|
||||
foreach ($metadata as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
$value = '';
|
||||
}
|
||||
$metadataarray['meta_' . $key] = $value;
|
||||
}
|
||||
return $metadataarray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert an xml recording preview images to an array in the format used by the plugin.
|
||||
*
|
||||
* @param SimpleXMLElement $preview
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_preview_images(SimpleXMLElement $preview): array {
|
||||
$imagesarray = [];
|
||||
foreach ($preview->images->image as $image) {
|
||||
$imagearray = ['url' => trim((string) $image)];
|
||||
foreach ($image->attributes() as $attkey => $attvalue) {
|
||||
$imagearray[$attkey] = (string) $attvalue;
|
||||
}
|
||||
array_push($imagesarray, $imagearray);
|
||||
}
|
||||
return $imagesarray;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
use mod_bigbluebuttonbn\event\events;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Utility class for all logs routines helper.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent [at] call-learning [dt] fr)
|
||||
*/
|
||||
class logger {
|
||||
|
||||
/** @var string The bigbluebuttonbn Add event */
|
||||
public const EVENT_ADD = 'Add';
|
||||
|
||||
/** @var string The bigbluebuttonbn Edit event */
|
||||
public const EVENT_EDIT = 'Edit';
|
||||
|
||||
/** @var string The bigbluebuttonbn Create event */
|
||||
public const EVENT_CREATE = 'Create';
|
||||
|
||||
/** @var string The bigbluebuttonbn Join event */
|
||||
public const EVENT_JOIN = 'Join';
|
||||
|
||||
/** @var string The bigbluebuttonbn Playback event */
|
||||
public const EVENT_PLAYED = 'Played';
|
||||
|
||||
/** @var string The bigbluebuttonbn Logout event */
|
||||
public const EVENT_LOGOUT = 'Logout';
|
||||
|
||||
/** @var string The bigbluebuttonbn Import event */
|
||||
public const EVENT_IMPORT = 'Import';
|
||||
|
||||
/** @var string The bigbluebuttonbn Delete event */
|
||||
public const EVENT_DELETE = 'Delete';
|
||||
|
||||
/** @var string The bigbluebuttonbn Callback event */
|
||||
public const EVENT_CALLBACK = 'Callback';
|
||||
|
||||
/** @var string The bigbluebuttonbn Summary event */
|
||||
public const EVENT_SUMMARY = 'Summary';
|
||||
|
||||
/** @var string The bigbluebuttonbn meeting_start event */
|
||||
public const EVENT_MEETING_START = 'meeting_start';
|
||||
|
||||
/** @var int The user accessed the session from activity page */
|
||||
public const ORIGIN_BASE = 0;
|
||||
|
||||
/** @var int The user accessed the session from Timeline */
|
||||
public const ORIGIN_TIMELINE = 1;
|
||||
|
||||
/** @var int The user accessed the session from Index */
|
||||
public const ORIGIN_INDEX = 2;
|
||||
|
||||
/**
|
||||
* Get the user event logs related to completion, for the specified user in the named instance.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int|null $userid
|
||||
* @param array|null $filters
|
||||
* @param int|null $timestart
|
||||
* @return array
|
||||
* @throws \coding_exception
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public static function get_user_completion_logs(
|
||||
instance $instance,
|
||||
?int $userid,
|
||||
?array $filters,
|
||||
?int $timestart = 0
|
||||
): array {
|
||||
global $DB;
|
||||
$filters = $filters ?? [self::EVENT_JOIN, self::EVENT_PLAYED, self::EVENT_SUMMARY];
|
||||
[$wheresql, $params] = static::get_user_completion_sql_params($instance, $userid, $filters, $timestart);
|
||||
return $DB->get_records_select('bigbluebuttonbn_logs', $wheresql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user event logs related to completion, for the specified user in the named instance.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int|null $userid
|
||||
* @param array|null $filters
|
||||
* @param int|null $timestart
|
||||
* @return array
|
||||
* @throws \coding_exception
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public static function get_user_completion_logs_with_userfields(
|
||||
instance $instance,
|
||||
?int $userid,
|
||||
?array $filters,
|
||||
?int $timestart = 0
|
||||
): array {
|
||||
global $DB;
|
||||
$filters = $filters ?? [self::EVENT_JOIN, self::EVENT_PLAYED, self::EVENT_SUMMARY];
|
||||
[$wheresql, $params] = static::get_user_completion_sql_params($instance, $userid, $filters, $timestart, 'l');
|
||||
$userfieldsapi = \core_user\fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects;
|
||||
$logtable = new \core\dml\table('bigbluebuttonbn_logs', 'l', '');
|
||||
$logtableselect = $logtable->get_field_select();
|
||||
$logtablefrom = $logtable->get_from_sql();
|
||||
$usertable = new \core\dml\table('user', 'u', '');
|
||||
$usertablefrom = $usertable->get_from_sql();
|
||||
$sql = <<<EOF
|
||||
SELECT {$logtableselect}, {$userfields}
|
||||
FROM {$logtablefrom}
|
||||
INNER JOIN {$usertablefrom} ON u.id = l.userid
|
||||
WHERE $wheresql
|
||||
EOF;
|
||||
return $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest timestamp for any event logs related to completion, for the specified user in the named instance.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int|null $userid
|
||||
* @param array|null $filters
|
||||
* @param int|null $timestart
|
||||
* @return int
|
||||
* @throws \coding_exception
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public static function get_user_completion_logs_max_timestamp(
|
||||
instance $instance,
|
||||
?int $userid,
|
||||
?array $filters,
|
||||
?int $timestart = 0
|
||||
): int {
|
||||
global $DB;
|
||||
|
||||
[$wheresql, $params] = static::get_user_completion_sql_params($instance, $userid, $filters, $timestart);
|
||||
$select = "SELECT MAX(timecreated) ";
|
||||
$lastlogtime = $DB->get_field_sql($select . ' FROM {bigbluebuttonbn_logs} WHERE ' . $wheresql, $params);
|
||||
return $lastlogtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the right SQL query for completion
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int|null $userid
|
||||
* @param array|null $filters
|
||||
* @param int|null $timestart
|
||||
* @param string|null $logtablealias
|
||||
* @return array
|
||||
* @throws \coding_exception
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
protected static function get_user_completion_sql_params(instance $instance, ?int $userid, ?array $filters, ?int $timestart,
|
||||
?string $logtablealias = null) {
|
||||
global $DB;
|
||||
$filters = $filters ?? [self::EVENT_JOIN, self::EVENT_PLAYED, self::EVENT_SUMMARY];
|
||||
[$insql, $params] = $DB->get_in_or_equal($filters, SQL_PARAMS_NAMED);
|
||||
$wheres = [];
|
||||
$wheres['bigbluebuttonbnid'] = '= :instanceid';
|
||||
if ($timestart) {
|
||||
$wheres['timecreated'] = ' > :timestart';
|
||||
$params['timestart'] = $timestart;
|
||||
}
|
||||
if ($userid) {
|
||||
$wheres['userid'] = ' = :userid';
|
||||
$params['userid'] = $userid;
|
||||
}
|
||||
$params['instanceid'] = $instance->get_instance_id();
|
||||
$wheres['log'] = " $insql";
|
||||
$wheresqls = [];
|
||||
foreach ($wheres as $key => $val) {
|
||||
$prefix = !empty($logtablealias) ? "$logtablealias." : "";
|
||||
$wheresqls[] = "$prefix$key $val";
|
||||
}
|
||||
return [join(' AND ', $wheresqls), $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Log that an instance was created.
|
||||
*
|
||||
* Note: This event cannot take the instance class as it is typically called before the cm has been configured.
|
||||
*
|
||||
* @param stdClass $instancedata
|
||||
*/
|
||||
public static function log_instance_created(stdClass $instancedata): void {
|
||||
self::raw_log(
|
||||
self::EVENT_ADD,
|
||||
$instancedata->id,
|
||||
$instancedata->course,
|
||||
$instancedata->meetingid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log that an instance was updated.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function log_instance_updated(instance $instance): void {
|
||||
self::log($instance, self::EVENT_EDIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an instance deleted event.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function log_instance_deleted(instance $instance): void {
|
||||
global $DB;
|
||||
|
||||
$wheresql = 'bigbluebuttonbnid = :instanceid AND log = :logtype AND ' . $DB->sql_compare_text('meta') . ' = :meta';
|
||||
$logs = $DB->get_records_select('bigbluebuttonbn_logs', $wheresql, [
|
||||
'instanceid' => $instance->get_instance_id(),
|
||||
'logtype' => self::EVENT_CREATE,
|
||||
'meta' => "{\"record\":true}"
|
||||
]);
|
||||
|
||||
$meta = "{\"has_recordings\":" . empty($logs) ? "true" : "false" . "}";
|
||||
self::log($instance, self::EVENT_DELETE, [], $meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an event callback.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param array $overrides
|
||||
* @param array $meta
|
||||
* @return int The new count of callback events
|
||||
*/
|
||||
public static function log_event_callback(instance $instance, array $overrides, array $meta): int {
|
||||
self::log(
|
||||
$instance,
|
||||
self::EVENT_CALLBACK,
|
||||
$overrides,
|
||||
json_encode($meta)
|
||||
);
|
||||
|
||||
return self::count_callback_events($meta['recordid'], 'meeting_events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an event summary event.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param array $overrides
|
||||
* @param array $meta
|
||||
*/
|
||||
public static function log_event_summary(instance $instance, array $overrides = [], array $meta = []): void {
|
||||
self::log(
|
||||
$instance,
|
||||
self::EVENT_SUMMARY,
|
||||
$overrides,
|
||||
json_encode($meta)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log that an instance was viewed.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function log_instance_viewed(instance $instance): void {
|
||||
self::log_moodle_event($instance, events::$events['view']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the events for when a meeting was ended.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function log_meeting_ended_event(instance $instance): void {
|
||||
// Moodle event logger: Create an event for meeting ended.
|
||||
self::log_moodle_event($instance, events::$events['meeting_end']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the relevant events for when a meeting was joined.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int $origin
|
||||
*/
|
||||
public static function log_meeting_joined_event(instance $instance, int $origin): void {
|
||||
// Moodle event logger: Create an event for meeting joined.
|
||||
self::log_moodle_event($instance, events::$events['meeting_join']);
|
||||
|
||||
// Internal logger: Instert a record with the meeting created.
|
||||
self::log(
|
||||
$instance,
|
||||
self::EVENT_JOIN,
|
||||
['meetingid' => $instance->get_meeting_id()],
|
||||
json_encode((object) ['origin' => $origin])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the relevant events for when a user left a meeting.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public static function log_meeting_left_event(instance $instance): void {
|
||||
// Moodle event logger: Create an event for meeting left.
|
||||
self::log_moodle_event($instance, events::$events['meeting_left']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the relevant events for when a recording has been played.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int $rid RecordID
|
||||
*/
|
||||
public static function log_recording_played_event(instance $instance, int $rid): void {
|
||||
// Moodle event logger: Create an event for recording played.
|
||||
self::log_moodle_event($instance, events::$events['recording_play'], ['other' => $rid]);
|
||||
|
||||
// Internal logger: Insert a record with the playback played.
|
||||
self::log(
|
||||
$instance,
|
||||
self::EVENT_PLAYED,
|
||||
[
|
||||
'meetingid' => $instance->get_meeting_id(),
|
||||
],
|
||||
json_encode(['recordingid' => $rid])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a bigbluebuttonbn event from an instance.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param string $event
|
||||
* @param array $overrides
|
||||
* @param string|null $meta
|
||||
* @return bool
|
||||
*/
|
||||
protected static function log(instance $instance, string $event, array $overrides = [], ?string $meta = null): bool {
|
||||
return self::raw_log(
|
||||
$event,
|
||||
$instance->get_instance_id(),
|
||||
$instance->get_course_id(),
|
||||
$instance->get_meeting_id(),
|
||||
$overrides,
|
||||
$meta
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a bigbluebuttonbn event from raw data.
|
||||
*
|
||||
* @param string $event
|
||||
* @param int $instanceid
|
||||
* @param int $courseid
|
||||
* @param string $meetingid
|
||||
* @param array $overrides
|
||||
* @param string|null $meta
|
||||
* @return bool
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
protected static function raw_log(
|
||||
string $event,
|
||||
int $instanceid,
|
||||
int $courseid,
|
||||
string $meetingid,
|
||||
array $overrides = [],
|
||||
?string $meta = null
|
||||
): bool {
|
||||
global $DB, $USER;
|
||||
|
||||
$log = (object) array_merge([
|
||||
// Default values.
|
||||
'courseid' => $courseid,
|
||||
'bigbluebuttonbnid' => $instanceid,
|
||||
'userid' => $USER->id,
|
||||
'meetingid' => $meetingid,
|
||||
'timecreated' => time(),
|
||||
'log' => $event,
|
||||
'meta' => $meta,
|
||||
], $overrides);
|
||||
|
||||
return !!$DB->insert_record('bigbluebuttonbn_logs', $log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper register a bigbluebuttonbn event.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param string $type
|
||||
* @param array $options [timecreated, userid, other]
|
||||
*/
|
||||
protected static function log_moodle_event(instance $instance, string $type, array $options = []): void {
|
||||
if (!in_array($type, \mod_bigbluebuttonbn\event\events::$events)) {
|
||||
// No log will be created.
|
||||
return;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'context' => $instance->get_context(),
|
||||
'objectid' => $instance->get_instance_id(),
|
||||
];
|
||||
|
||||
if (array_key_exists('timecreated', $options)) {
|
||||
$params['timecreated'] = $options['timecreated'];
|
||||
}
|
||||
|
||||
if (array_key_exists('userid', $options)) {
|
||||
$params['userid'] = $options['userid'];
|
||||
}
|
||||
|
||||
if (array_key_exists('other', $options)) {
|
||||
$params['other'] = $options['other'];
|
||||
}
|
||||
|
||||
$event = call_user_func_array("\\mod_bigbluebuttonbn\\event\\{$type}::create", [$params]);
|
||||
$event->add_record_snapshot('course_modules', $instance->get_cm());
|
||||
$event->add_record_snapshot('course', $instance->get_course());
|
||||
$event->add_record_snapshot('bigbluebuttonbn', $instance->get_instance_data());
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to count the number of callback logs matching the supplied specifications.
|
||||
*
|
||||
* @param string $recordid
|
||||
* @param string $callbacktype
|
||||
* @return int
|
||||
*/
|
||||
protected static function count_callback_events(string $recordid, string $callbacktype = 'recording_ready'): int {
|
||||
global $DB;
|
||||
$sql = 'SELECT count(DISTINCT id) FROM {bigbluebuttonbn_logs} WHERE log = ? AND meta LIKE ? AND meta LIKE ?';
|
||||
// Callback type added on version 2.4, validate recording_ready first or assume it on records with no callback.
|
||||
if ($callbacktype == 'recording_ready') {
|
||||
$sql .= ' AND (meta LIKE ? OR meta NOT LIKE ? )';
|
||||
$count =
|
||||
$DB->count_records_sql($sql, [
|
||||
self::EVENT_CALLBACK, '%recordid%',
|
||||
"%$recordid%",
|
||||
$callbacktype, 'callback'
|
||||
]);
|
||||
return $count;
|
||||
}
|
||||
$sql .= ' AND meta LIKE ?;';
|
||||
$count = $DB->count_records_sql($sql,
|
||||
[self::EVENT_CALLBACK, '%recordid%', "%$recordid%", "%$callbacktype%"]);
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
use cache;
|
||||
use cache_store;
|
||||
use context_course;
|
||||
use core_tag_tag;
|
||||
use Exception;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\meeting_join_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception;
|
||||
use mod_bigbluebuttonbn\local\helpers\roles;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class to describe a BBB Meeting.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class meeting {
|
||||
|
||||
/** @var instance The bbb instance */
|
||||
protected $instance;
|
||||
|
||||
/** @var stdClass Info about the meeting */
|
||||
protected $meetinginfo = null;
|
||||
|
||||
/**
|
||||
* Constructor for the meeting object.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public function __construct(instance $instance) {
|
||||
$this->instance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to join a meeting.
|
||||
*
|
||||
*
|
||||
* It will create the meeting if not already created.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int $origin
|
||||
* @return string
|
||||
* @throws meeting_join_exception this is sent if we cannot join (meeting full, user needs to wait...)
|
||||
* @throws server_not_available_exception
|
||||
*/
|
||||
public static function join_meeting(instance $instance, $origin = logger::ORIGIN_BASE): string {
|
||||
// See if the session is in progress.
|
||||
$meeting = new meeting($instance);
|
||||
// As the meeting doesn't exist, try to create it.
|
||||
if (empty($meeting->get_meeting_info(true)->createtime)) {
|
||||
$meeting->create_meeting();
|
||||
}
|
||||
return $meeting->join($origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currently stored meeting info
|
||||
*
|
||||
* @return mixed|stdClass
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function get_meeting_info() {
|
||||
if (!$this->meetinginfo) {
|
||||
$this->meetinginfo = $this->do_get_meeting_info();
|
||||
}
|
||||
return $this->meetinginfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return meeting information for the specified instance.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param bool $updatecache Whether to update the cache when fetching the information
|
||||
* @return stdClass
|
||||
*/
|
||||
public static function get_meeting_info_for_instance(instance $instance, bool $updatecache = false): stdClass {
|
||||
$meeting = new self($instance);
|
||||
return $meeting->do_get_meeting_info($updatecache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returns a sha1 encoded string that is unique and will be used as a seed for meetingid.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_unique_meetingid_seed() {
|
||||
global $DB;
|
||||
do {
|
||||
$encodedseed = sha1(plugin::random_password(12));
|
||||
$meetingid = (string) $DB->get_field('bigbluebuttonbn', 'meetingid', ['meetingid' => $encodedseed]);
|
||||
} while ($meetingid == $encodedseed);
|
||||
return $encodedseed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is meeting running ?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_running() {
|
||||
return $this->get_meeting_info()->statusrunning ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force update the meeting in cache.
|
||||
*/
|
||||
public function update_cache() {
|
||||
$this->meetinginfo = $this->do_get_meeting_info(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meeting attendees
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_attendees() {
|
||||
return $this->get_meeting_info()->attendees ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the meeting be joined ?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_join() {
|
||||
return $this->get_meeting_info()->canjoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of participants
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_participant_count() {
|
||||
return $this->get_meeting_info()->participantcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bigbluebutton meeting, send the message to BBB and returns the response in an array.
|
||||
*
|
||||
* @return array
|
||||
* @throws bigbluebutton_exception
|
||||
* @throws server_not_available_exception
|
||||
*/
|
||||
public function create_meeting() {
|
||||
$data = $this->create_meeting_data();
|
||||
$metadata = $this->create_meeting_metadata();
|
||||
$presentation = $this->instance->get_presentation_for_bigbluebutton_upload(); // The URL must contain nonce.
|
||||
$presentationname = $presentation['name'] ?? null;
|
||||
$presentationurl = $presentation['url'] ?? null;
|
||||
$response = bigbluebutton_proxy::create_meeting($data, $metadata, $presentationname, $presentationurl);
|
||||
// New recording management: Insert a recordingID that corresponds to the meeting created.
|
||||
if ($this->instance->is_recorded()) {
|
||||
$recording = new recording(0, (object) [
|
||||
'courseid' => $this->instance->get_course_id(),
|
||||
'bigbluebuttonbnid' => $this->instance->get_instance_id(),
|
||||
'recordingid' => $response['internalMeetingID'],
|
||||
'groupid' => $this->instance->get_group_id()]
|
||||
);
|
||||
$recording->create();
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an end meeting message to BBB server
|
||||
*/
|
||||
public function end_meeting() {
|
||||
bigbluebutton_proxy::end_meeting($this->instance->get_meeting_id(), $this->instance->get_moderator_password());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meeting join URL
|
||||
*
|
||||
* @return string
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function get_join_url() {
|
||||
return bigbluebutton_proxy::get_join_url(
|
||||
$this->instance->get_meeting_id(),
|
||||
$this->instance->get_user_fullname(),
|
||||
$this->instance->get_current_user_password(),
|
||||
$this->instance->get_logout_url()->out(false),
|
||||
null,
|
||||
$this->instance->get_user_id(),
|
||||
$this->get_meeting_info()->createtime
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return meeting information for this meeting.
|
||||
*
|
||||
* @param bool $updatecache Whether to update the cache when fetching the information
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function do_get_meeting_info(bool $updatecache = false): stdClass {
|
||||
$instance = $this->instance;
|
||||
$meetinginfo = (object) [
|
||||
'instanceid' => $instance->get_instance_id(),
|
||||
'bigbluebuttonbnid' => $instance->get_instance_id(),
|
||||
'groupid' => $instance->get_group_id(),
|
||||
'meetingid' => $instance->get_meeting_id(),
|
||||
'cmid' => $instance->get_cm_id(),
|
||||
'ismoderator' => $instance->is_moderator(),
|
||||
'joinurl' => $instance->get_join_url()->out(),
|
||||
'userlimit' => $instance->get_user_limit(),
|
||||
'presentations' => [],
|
||||
];
|
||||
if ($instance->get_instance_var('openingtime')) {
|
||||
$meetinginfo->openingtime = intval($instance->get_instance_var('openingtime'));
|
||||
}
|
||||
if ($instance->get_instance_var('closingtime')) {
|
||||
$meetinginfo->closingtime = intval($instance->get_instance_var('closingtime'));
|
||||
}
|
||||
$activitystatus = bigbluebutton_proxy::view_get_activity_status($instance);
|
||||
// This might raise an exception if info cannot be retrieved.
|
||||
// But this might be totally fine as the meeting is maybe not yet created on BBB side.
|
||||
$participantcount = 0;
|
||||
try {
|
||||
$info = self::retrieve_cached_meeting_info($this->instance->get_meeting_id(), $updatecache);
|
||||
$meetinginfo->statusrunning = $info['running'] === 'true';
|
||||
$meetinginfo->createtime = $info['createTime'] ?? null;
|
||||
$participantcount = isset($info['participantCount']) ? $info['participantCount'] : 0;
|
||||
} catch (bigbluebutton_exception $e) {
|
||||
// The meeting is not created on BBB side, so we have to setup a couple of values here.
|
||||
$meetinginfo->statusrunning = false;
|
||||
$meetinginfo->createtime = null;
|
||||
}
|
||||
$meetinginfo->statusclosed = $activitystatus === 'ended';
|
||||
$meetinginfo->statusopen = !$meetinginfo->statusrunning && $activitystatus === 'open';
|
||||
$meetinginfo->participantcount = $participantcount;
|
||||
|
||||
$canjoin = !$instance->user_must_wait_to_join() || $meetinginfo->statusrunning;
|
||||
// Limit has not been reached or user does not count toward limit.
|
||||
$canjoin = $canjoin && (
|
||||
!$instance->has_user_limit_been_reached($participantcount)
|
||||
|| !$instance->does_current_user_count_towards_user_limit()
|
||||
);
|
||||
$canjoin = $canjoin && ($instance->is_currently_open() || $instance->user_can_force_join());
|
||||
// Double check that the user has the capabilities to join.
|
||||
$canjoin = $canjoin && $instance->can_join();
|
||||
$meetinginfo->canjoin = $canjoin;
|
||||
|
||||
// If user is administrator, moderator or if is viewer and no waiting is required, join allowed.
|
||||
if ($meetinginfo->statusrunning) {
|
||||
$meetinginfo->statusmessage = get_string('view_message_conference_in_progress', 'bigbluebuttonbn');
|
||||
$meetinginfo->startedat = floor(intval($info['startTime']) / 1000); // Milliseconds.
|
||||
$meetinginfo->moderatorcount = $info['moderatorCount'];
|
||||
$meetinginfo->moderatorplural = $info['moderatorCount'] > 1;
|
||||
$meetinginfo->participantcount = $participantcount - $meetinginfo->moderatorcount;
|
||||
$meetinginfo->participantplural = $meetinginfo->participantcount > 1;
|
||||
} else {
|
||||
if ($instance->user_must_wait_to_join() && !$instance->user_can_force_join()) {
|
||||
$meetinginfo->statusmessage = get_string('view_message_conference_wait_for_moderator', 'bigbluebuttonbn');
|
||||
} else {
|
||||
$meetinginfo->statusmessage = get_string('view_message_conference_room_ready', 'bigbluebuttonbn');
|
||||
}
|
||||
}
|
||||
|
||||
$presentation = $instance->get_presentation(); // This is for internal use.
|
||||
if (!empty($presentation)) {
|
||||
$meetinginfo->presentations[] = $presentation;
|
||||
}
|
||||
$meetinginfo->attendees = [];
|
||||
if (!empty($info['attendees'])) {
|
||||
// Make sure attendees is an array of object, not a simpleXML object.
|
||||
foreach ($info['attendees'] as $attendee) {
|
||||
$meetinginfo->attendees[] = (array) $attendee;
|
||||
}
|
||||
}
|
||||
return $meetinginfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a meeting info object cached or fetched from the live session.
|
||||
*
|
||||
* @param string $meetingid
|
||||
* @param bool $updatecache
|
||||
*
|
||||
* @return array
|
||||
* @throws \coding_exception
|
||||
* @throws bigbluebutton_exception
|
||||
*/
|
||||
protected static function retrieve_cached_meeting_info($meetingid, $updatecache = false) {
|
||||
$cachettl = (int) config::get('waitformoderator_cache_ttl');
|
||||
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'mod_bigbluebuttonbn', 'meetings_cache');
|
||||
$result = $cache->get($meetingid);
|
||||
$now = time();
|
||||
if (!$updatecache && !empty($result) && $now < ($result['creation_time'] + $cachettl)) {
|
||||
// Use the value in the cache.
|
||||
return (array) json_decode($result['meeting_info']);
|
||||
}
|
||||
$cache->delete($meetingid); // Make sure we purges the cache before checking info.
|
||||
// Pings again and refreshes the cache.
|
||||
$meetinginfo = bigbluebutton_proxy::get_meeting_info($meetingid);
|
||||
|
||||
$cache->set($meetingid, ['creation_time' => time(), 'meeting_info' => json_encode($meetinginfo)]);
|
||||
return $meetinginfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to prepare data used for create meeting.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function create_meeting_data() {
|
||||
$data = ['meetingID' => $this->instance->get_meeting_id(),
|
||||
'name' => \mod_bigbluebuttonbn\plugin::html2text($this->instance->get_meeting_name(), 64),
|
||||
'attendeePW' => $this->instance->get_viewer_password(),
|
||||
'moderatorPW' => $this->instance->get_moderator_password(),
|
||||
'logoutURL' => $this->instance->get_logout_url()->out(false),
|
||||
];
|
||||
$data['record'] = $this->instance->should_record() ? 'true' : 'false';
|
||||
// Check if auto_start_record is enable.
|
||||
if ($data['record'] == 'true' && $this->instance->should_record_from_start()) {
|
||||
$data['autoStartRecording'] = 'true';
|
||||
// Check if hide_record_button is enable.
|
||||
if (!$this->instance->should_show_recording_button()) {
|
||||
$data['allowStartStopRecording'] = 'false';
|
||||
}
|
||||
}
|
||||
$data['welcome'] = trim($this->instance->get_welcome_message());
|
||||
$voicebridge = intval($this->instance->get_voice_bridge());
|
||||
if ($voicebridge > 0 && $voicebridge < 79999) {
|
||||
$data['voiceBridge'] = $voicebridge;
|
||||
}
|
||||
$maxparticipants = intval($this->instance->get_user_limit());
|
||||
if ($maxparticipants > 0) {
|
||||
$data['maxParticipants'] = $maxparticipants;
|
||||
}
|
||||
if ($this->instance->get_mute_on_start()) {
|
||||
$data['muteOnStart'] = 'true';
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for preparing metadata used while creating the meeting.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function create_meeting_metadata() {
|
||||
global $USER;
|
||||
// Create standard metadata.
|
||||
$origindata = $this->instance->get_origin_data();
|
||||
$metadata = [
|
||||
'bbb-origin' => $origindata->origin,
|
||||
'bbb-origin-version' => $origindata->originVersion,
|
||||
'bbb-origin-server-name' => $origindata->originServerName,
|
||||
'bbb-origin-server-common-name' => $origindata->originServerCommonName,
|
||||
'bbb-origin-tag' => $origindata->originTag,
|
||||
'bbb-context' => $this->instance->get_course()->fullname,
|
||||
'bbb-context-id' => $this->instance->get_course_id(),
|
||||
'bbb-context-name' => trim(html_to_text($this->instance->get_course()->fullname, 0)),
|
||||
'bbb-context-label' => trim(html_to_text($this->instance->get_course()->shortname, 0)),
|
||||
'bbb-recording-name' => plugin::html2text($this->instance->get_meeting_name(), 64),
|
||||
'bbb-recording-description' => plugin::html2text($this->instance->get_meeting_description(),
|
||||
64),
|
||||
'bbb-recording-tags' =>
|
||||
implode(',', core_tag_tag::get_item_tags_array('core',
|
||||
'course_modules', $this->instance->get_cm_id())), // Same as $id.
|
||||
'bbb-meeting-size-hint' =>
|
||||
count_enrolled_users(context_course::instance($this->instance->get_course_id()),
|
||||
'', $this->instance->get_group_id()),
|
||||
];
|
||||
// Special metadata for recording processing.
|
||||
if ((boolean) config::get('recordingstatus_enabled')) {
|
||||
$metadata["bn-recording-status"] = json_encode(
|
||||
[
|
||||
'email' => ['"' . fullname($USER) . '" <' . $USER->email . '>'],
|
||||
'context' => $this->instance->get_view_url(),
|
||||
]
|
||||
);
|
||||
}
|
||||
if ((boolean) config::get('recordingready_enabled')) {
|
||||
$metadata['bn-recording-ready-url'] = $this->instance->get_record_ready_url()->out(false);
|
||||
}
|
||||
if ((boolean) config::get('meetingevents_enabled')) {
|
||||
$metadata['analytics-callback-url'] = $this->instance->get_meeting_event_notification_url()->out(false);
|
||||
}
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for responding when storing live meeting events is requested.
|
||||
*
|
||||
* The callback with a POST request includes:
|
||||
* - Authentication: Bearer <A JWT token containing {"exp":<TIMESTAMP>} encoded with HS512>
|
||||
* - Content Type: application/json
|
||||
* - Body: <A JSON Object>
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return void
|
||||
*/
|
||||
public static function meeting_events(instance $instance) {
|
||||
$bigbluebuttonbn = $instance->get_instance_data();
|
||||
// Decodes the received JWT string.
|
||||
try {
|
||||
// Get the HTTP headers (getallheaders is a PHP function that may only work with Apache).
|
||||
$headers = getallheaders();
|
||||
|
||||
// Pull the Bearer from the headers.
|
||||
if (!array_key_exists('Authorization', $headers)) {
|
||||
$msg = 'Authorization failed';
|
||||
header('HTTP/1.0 400 Bad Request. ' . $msg);
|
||||
return;
|
||||
}
|
||||
$authorization = explode(" ", $headers['Authorization']);
|
||||
|
||||
// Verify the authenticity of the request.
|
||||
$token = \Firebase\JWT\JWT::decode(
|
||||
$authorization[1],
|
||||
config::get('shared_secret'),
|
||||
['HS512']
|
||||
);
|
||||
|
||||
// Get JSON string from the body.
|
||||
$jsonstr = file_get_contents('php://input');
|
||||
|
||||
// Convert JSON string to a JSON object.
|
||||
$jsonobj = json_decode($jsonstr);
|
||||
} catch (Exception $e) {
|
||||
$msg = 'Caught exception: ' . $e->getMessage();
|
||||
header('HTTP/1.0 400 Bad Request. ' . $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate that the bigbluebuttonbn activity corresponds to the meeting_id received.
|
||||
$meetingidelements = explode('[', $jsonobj->{'meeting_id'});
|
||||
$meetingidelements = explode('-', $meetingidelements[0]);
|
||||
if (!isset($bigbluebuttonbn) || $bigbluebuttonbn->meetingid != $meetingidelements[0]) {
|
||||
$msg = 'The activity may have been deleted';
|
||||
header('HTTP/1.0 410 Gone. ' . $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// We make sure events are processed only once.
|
||||
$overrides = ['meetingid' => $jsonobj->{'meeting_id'}];
|
||||
$meta['recordid'] = $jsonobj->{'internal_meeting_id'};
|
||||
$meta['callback'] = 'meeting_events';
|
||||
|
||||
$eventcount = logger::log_event_callback($instance, $overrides, $meta);
|
||||
if ($eventcount === 1) {
|
||||
// Process the events.
|
||||
self::process_meeting_events($instance, $jsonobj);
|
||||
header('HTTP/1.0 200 Accepted. Enqueued.');
|
||||
} else {
|
||||
header('HTTP/1.0 202 Accepted. Already processed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function enqueues list of meeting events to be stored and processed as for completion.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param stdClass $jsonobj
|
||||
*/
|
||||
protected static function process_meeting_events(instance $instance, stdClass $jsonobj) {
|
||||
$meetingid = $jsonobj->{'meeting_id'};
|
||||
$recordid = $jsonobj->{'internal_meeting_id'};
|
||||
$attendees = $jsonobj->{'data'}->{'attendees'};
|
||||
foreach ($attendees as $attendee) {
|
||||
$userid = $attendee->{'ext_user_id'};
|
||||
$overrides['meetingid'] = $meetingid;
|
||||
$overrides['userid'] = $userid;
|
||||
$meta['recordid'] = $recordid;
|
||||
$meta['data'] = $attendee;
|
||||
|
||||
// Stores the log.
|
||||
logger::log_event_summary($instance, $overrides, $meta);
|
||||
|
||||
// Enqueue a task for processing the completion.
|
||||
bigbluebutton_proxy::enqueue_completion_event($instance->get_instance_data(), $userid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a meeting.
|
||||
*
|
||||
* @param int $origin The spec
|
||||
* @return string The URL to redirect to
|
||||
* @throws meeting_join_exception
|
||||
*/
|
||||
public function join(int $origin): string {
|
||||
$this->do_get_meeting_info(true);
|
||||
if ($this->is_running()) {
|
||||
if ($this->instance->has_user_limit_been_reached($this->get_participant_count())
|
||||
&& $this->instance->does_current_user_count_towards_user_limit()) {
|
||||
throw new meeting_join_exception('userlimitreached');
|
||||
}
|
||||
}
|
||||
// If user is not administrator nor moderator (user is student) and waiting is required.
|
||||
if ($this->instance->user_must_wait_to_join()) {
|
||||
throw new meeting_join_exception('waitformoderator');
|
||||
}
|
||||
|
||||
// Moodle event logger: Create an event for meeting joined.
|
||||
logger::log_meeting_joined_event($this->instance, $origin);
|
||||
|
||||
// Before executing the redirect, increment the number of participants.
|
||||
roles::participant_joined($this->instance->get_meeting_id(), $this->instance->is_moderator());
|
||||
return $this->get_join_url();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\local\helpers\roles;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderable for the import page.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Darko Miletic (darko.miletic [at] gmail [dt] com)
|
||||
*/
|
||||
class import_view implements renderable, templatable {
|
||||
/**
|
||||
* @var instance $destinationinstance
|
||||
*/
|
||||
protected $destinationinstance;
|
||||
|
||||
/**
|
||||
* @var int|null $sourceinstanceid the source instance id or null if it is not yet set.
|
||||
*/
|
||||
protected $sourceinstanceid;
|
||||
|
||||
/**
|
||||
* @var int|null $sourcecourseid the source instance id or null if it is not yet set.
|
||||
*/
|
||||
protected $sourcecourseid;
|
||||
|
||||
/**
|
||||
* import_view constructor.
|
||||
*
|
||||
* @param instance $destinationinstance
|
||||
* @param int $sourcecourseid
|
||||
* @param int $sourceinstanceid
|
||||
*/
|
||||
public function __construct(instance $destinationinstance, int $sourcecourseid, int $sourceinstanceid) {
|
||||
$this->destinationinstance = $destinationinstance;
|
||||
$this->sourcecourseid = $sourcecourseid >= 0 ? $sourcecourseid : null;
|
||||
$this->sourceinstanceid = $sourceinstanceid >= 0 ? $sourceinstanceid : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer to template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$courses = roles::import_get_courses_for_select($this->destinationinstance);
|
||||
if (config::get('importrecordings_from_deleted_enabled')) {
|
||||
$courses[0] = get_string('recordings_from_deleted_activities', 'mod_bigbluebuttonbn');
|
||||
ksort($courses);
|
||||
}
|
||||
$context = (object) [
|
||||
'bbbid' => $this->destinationinstance->get_instance_id(),
|
||||
'has_recordings' => true,
|
||||
'bbbsourceid' => 0
|
||||
];
|
||||
|
||||
if (!empty($this->sourceinstanceid)) {
|
||||
$context->sourceid = $this->sourceinstanceid;
|
||||
$context->search = [
|
||||
'value' => ''
|
||||
];
|
||||
$sourceinstance = instance::get_from_instanceid($this->sourceinstanceid);
|
||||
if ($sourceinstance->is_type_room_only()) {
|
||||
$context->has_recordings = false;
|
||||
}
|
||||
$context->bbbsourceid = $sourceinstance->get_instance_id();
|
||||
}
|
||||
|
||||
// Now the selects.
|
||||
if (!empty($this->sourcecourseid)) {
|
||||
$selectrecords = [];
|
||||
|
||||
$cms = get_fast_modinfo($this->sourcecourseid)->instances['bigbluebuttonbn'];
|
||||
foreach ($cms as $cm) {
|
||||
if ($cm->id == $this->destinationinstance->get_cm_id()) {
|
||||
// Skip the target instance.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cm->deletioninprogress) {
|
||||
// Check if the BBB is not currently scheduled for deletion.
|
||||
continue;
|
||||
}
|
||||
|
||||
$selectrecords[$cm->instance] = $cm->name;
|
||||
}
|
||||
if (config::get('importrecordings_from_deleted_enabled')) {
|
||||
$selectrecords[0] =
|
||||
get_string('recordings_from_deleted_activities', 'mod_bigbluebuttonbn');
|
||||
}
|
||||
$actionurl = $this->destinationinstance->get_import_url();
|
||||
$actionurl->param('sourcecourseid', $this->sourcecourseid);
|
||||
|
||||
$select = new \single_select(
|
||||
$actionurl,
|
||||
'sourcebn',
|
||||
$selectrecords,
|
||||
$this->sourceinstanceid ?? ""
|
||||
);
|
||||
$context->bbb_select = $select->export_for_template($output);
|
||||
}
|
||||
$context->sourcecourseid = $this->sourcecourseid ?? 0;
|
||||
|
||||
// Course selector.
|
||||
$context->course_select = (new \single_select(
|
||||
$this->destinationinstance->get_import_url(),
|
||||
'sourcecourseid',
|
||||
$courses,
|
||||
$this->sourcecourseid ?? ""
|
||||
))->export_for_template($output);
|
||||
|
||||
if (!is_null($this->sourcecourseid)) {
|
||||
$context->has_selected_course = true;
|
||||
}
|
||||
|
||||
// Back button.
|
||||
$context->back_button = (new \single_button(
|
||||
$this->destinationinstance->get_view_url(),
|
||||
get_string('view_recording_button_return', 'mod_bigbluebuttonbn')
|
||||
))->export_for_template($output);
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use html_table;
|
||||
use html_writer;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use mod_bigbluebuttonbn\plugin;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Renderer for the Index page.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class index implements renderable {
|
||||
|
||||
/** @var stdClass */
|
||||
protected $course;
|
||||
|
||||
/** @var stdClass[] */
|
||||
protected $instances;
|
||||
|
||||
/**
|
||||
* Constructor for the index renderable.
|
||||
*
|
||||
* @param stdClass $course
|
||||
* @param instance[] List of bbbbn instances
|
||||
*/
|
||||
public function __construct(stdClass $course, array $instances) {
|
||||
$this->course = $course;
|
||||
$this->instances = $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table for the index page.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return html_table
|
||||
*/
|
||||
public function get_table(renderer_base $output): html_table {
|
||||
// Print the list of instances.
|
||||
$table = new html_table();
|
||||
$table->head = [
|
||||
get_string('week'),
|
||||
get_string('index_heading_name', plugin::COMPONENT),
|
||||
get_string('index_heading_group', plugin::COMPONENT),
|
||||
get_string('index_heading_users', plugin::COMPONENT),
|
||||
get_string('index_heading_viewer', plugin::COMPONENT),
|
||||
get_string('index_heading_moderator', plugin::COMPONENT),
|
||||
get_string('index_heading_recording', plugin::COMPONENT),
|
||||
get_string('index_heading_actions', plugin::COMPONENT),
|
||||
];
|
||||
$table->align = ['center', 'left', 'center', 'center', 'center', 'center', 'center'];
|
||||
|
||||
foreach ($this->instances as $instance) {
|
||||
$this->add_instance_to_table($output, $table, $instance);
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add details of the bigbluebuttonbn instance to the table.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @param html_table $table
|
||||
* @param instance $instance
|
||||
*/
|
||||
protected function add_instance_to_table(renderer_base $output, html_table $table, instance $instance): void {
|
||||
$cm = $instance->get_cm();
|
||||
if (!$cm->uservisible) {
|
||||
return;
|
||||
}
|
||||
if (groups_get_activity_groupmode($cm) == 0) {
|
||||
$table->data[] = $this->add_room_row_to_table($output, $instance);
|
||||
} else {
|
||||
// Add 'All participants' room information.
|
||||
$table->data[] = $this->add_room_row_to_table($output, $instance, 0);
|
||||
|
||||
// Add data for the groups belonging to the bbb instance, if any.
|
||||
$groups = groups_get_activity_allowed_groups($cm);
|
||||
foreach ($groups as $group) {
|
||||
$table->data[] = $this->add_room_row_to_table($output, $instance, $group->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the general view.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @param instance $instance
|
||||
* @param int|null $group
|
||||
* @return array
|
||||
*/
|
||||
protected function add_room_row_to_table(renderer_base $output, instance $instance, ?int $group = null): array {
|
||||
if ($group) {
|
||||
$instance = instance::get_group_instance_from_instance($instance, $group);
|
||||
}
|
||||
$meeting = new meeting($instance);
|
||||
|
||||
$viewurl = $instance->get_view_url();
|
||||
if ($groupid = $instance->get_group_id()) {
|
||||
$viewurl->param('group', $groupid);
|
||||
}
|
||||
|
||||
$joinurl = html_writer::link($viewurl, format_string($instance->get_meeting_name()));
|
||||
|
||||
// The meeting info was returned.
|
||||
if ($meeting->is_running()) {
|
||||
return [
|
||||
$instance->get_cm()->sectionnum,
|
||||
$joinurl,
|
||||
$instance->get_group_name(),
|
||||
$this->get_room_usercount($meeting),
|
||||
$this->get_room_attendee_list($meeting, 'VIEWER'),
|
||||
$this->get_room_attendee_list($meeting, 'MODERATOR'),
|
||||
$this->get_room_record_info($output, $instance),
|
||||
$this->get_room_actions($output, $instance, $meeting),
|
||||
];
|
||||
}
|
||||
|
||||
return [$instance->get_cm()->sectionnum, $joinurl, $instance->get_group_name(), '', '', '', '', ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of users in the meeting.
|
||||
*
|
||||
* @param meeting $meeting
|
||||
* @return int
|
||||
*/
|
||||
protected function get_room_usercount(meeting $meeting): int {
|
||||
return count($meeting->get_attendees());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attendee list.
|
||||
*
|
||||
* @param meeting $meeting
|
||||
* @param string $role
|
||||
* @return string
|
||||
*/
|
||||
protected function get_room_attendee_list(meeting $meeting, string $role): string {
|
||||
$attendees = [];
|
||||
|
||||
foreach ($meeting->get_attendees() as $attendee) {
|
||||
if ((string) $attendee->role == $role) {
|
||||
$attendees[] = $attendee->fullName;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $attendees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indication of recording enabled.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @param instance $instance
|
||||
* @return string
|
||||
*/
|
||||
protected function get_room_record_info(renderer_base $output, instance $instance): string {
|
||||
if ($instance->is_recorded()) {
|
||||
// If it has been set when meeting created, set the variable on/off.
|
||||
return get_string('index_enabled', 'bigbluebuttonbn');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns room actions.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @param instance $instance
|
||||
* @param meeting $meeting
|
||||
* @return string
|
||||
*/
|
||||
protected function get_room_actions(renderer_base $output, instance $instance, meeting $meeting): string {
|
||||
if ($instance->is_moderator()) {
|
||||
return $output->render_from_template('mod_bigbluebuttonbn/end_session_button', (object) [
|
||||
'bigbluebuttonbnid' => $instance->get_instance_id(),
|
||||
'groupid' => $instance->get_group_id(),
|
||||
'statusrunning' => $meeting->is_running(),
|
||||
]);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\helpers\roles;
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderable for the instance notification updated message
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Darko Miletic (darko.miletic [at] gmail [dt] com)
|
||||
*/
|
||||
class instance_updated_message implements renderable, templatable {
|
||||
|
||||
/** @var int The activity was created */
|
||||
const TYPE_CREATED = 0;
|
||||
|
||||
/** @var int The activity was updated */
|
||||
const TYPE_UPDATED = 1;
|
||||
|
||||
/**
|
||||
* @var instance $instance
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* Instance updated constructor
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param int $type
|
||||
*/
|
||||
public function __construct(instance $instance, int $type) {
|
||||
$this->instance = $instance;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer to template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
return (object) [
|
||||
'is_update' => $this->type === self::TYPE_UPDATED,
|
||||
'is_create' => $this->type === self::TYPE_CREATED,
|
||||
'name' => $this->instance->get_meeting_name(),
|
||||
'link' => $this->instance->get_view_url()->out(),
|
||||
'description' => $this->instance->get_meeting_description(),
|
||||
'openingtime' => $this->instance->get_instance_var('openingtime'),
|
||||
'closingtime' => $this->instance->get_instance_var('closingtime'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Mobile output class for bigbluebuttonbn
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\meeting_join_exception;
|
||||
use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\logger;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/lib/grouplib.php');
|
||||
|
||||
/**
|
||||
* Mobile output class for bigbluebuttonbn
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2018 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class mobile {
|
||||
|
||||
/**
|
||||
* Returns the bigbluebuttonbn course view for the mobile app.
|
||||
*
|
||||
* @param mixed $args
|
||||
* @return array HTML, javascript and other data.
|
||||
*/
|
||||
public static function mobile_course_view($args): array {
|
||||
global $OUTPUT;
|
||||
|
||||
$args = (object) $args;
|
||||
$versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
|
||||
|
||||
$instance = instance::get_from_cmid($args->cmid);
|
||||
if (!$instance) {
|
||||
return self::mobile_print_error(get_string('view_error_url_missing_parameters', 'bigbluebuttonbn'));
|
||||
}
|
||||
|
||||
$cm = $instance->get_cm();
|
||||
$course = $instance->get_course();
|
||||
|
||||
// Check activity status.
|
||||
if ($instance->before_start_time()) {
|
||||
$message = get_string('view_message_conference_not_started', 'bigbluebuttonbn');
|
||||
|
||||
$notstarted = [
|
||||
'starts_at' => '',
|
||||
'ends_at' => '',
|
||||
];
|
||||
if (!empty($instance->get_instance_var('openingtime'))) {
|
||||
$notstarted['starts_at'] = sprintf(
|
||||
'%s: %s',
|
||||
get_string('mod_form_field_openingtime', 'bigbluebuttonbn'),
|
||||
userdate($instance->get_instance_var('openingtime'))
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($instance->get_instance_var('closingtime'))) {
|
||||
$notstarted['ends_at'] = sprintf(
|
||||
'%s: %s',
|
||||
get_string('mod_form_field_closingtime', 'bigbluebuttonbn'),
|
||||
userdate($instance->get_instance_var('closingtime'))
|
||||
);
|
||||
}
|
||||
|
||||
return self::mobile_print_notification($instance, $message, $notstarted);
|
||||
}
|
||||
|
||||
if ($instance->has_ended()) {
|
||||
$message = get_string('view_message_conference_has_ended', 'bigbluebuttonbn');
|
||||
return self::mobile_print_notification($instance, $message);
|
||||
}
|
||||
|
||||
// Check if the BBB server is working.
|
||||
$serverversion = bigbluebutton_proxy::get_server_version();
|
||||
if ($serverversion === null) {
|
||||
return self::mobile_print_error(bigbluebutton_proxy::get_server_not_available_message($instance));
|
||||
}
|
||||
|
||||
// Mark viewed by user (if required).
|
||||
$completion = new \completion_info($course);
|
||||
$completion->set_module_viewed($cm);
|
||||
|
||||
// Validate if the user is in a role allowed to join.
|
||||
if (!$instance->can_join()) {
|
||||
return self::mobile_print_error(get_string('view_nojoin', 'bigbluebuttonbn'));
|
||||
}
|
||||
|
||||
// Note: This logic should match bbb_view.php.
|
||||
|
||||
// Logic of bbb_view for join to session.
|
||||
if ($instance->user_must_wait_to_join()) {
|
||||
// If user is not administrator nor moderator (user is student) and waiting is required.
|
||||
return self::mobile_print_notification(
|
||||
$instance,
|
||||
get_string('view_message_conference_wait_for_moderator', 'bigbluebuttonbn')
|
||||
);
|
||||
}
|
||||
|
||||
// See if the BBB session is already in progress.
|
||||
$urltojoin = '';
|
||||
try {
|
||||
$urltojoin = meeting::join_meeting($instance);
|
||||
} catch (meeting_join_exception $e) {
|
||||
return self::mobile_print_notification($instance, $e->getMessage());
|
||||
} catch (server_not_available_exception $e) {
|
||||
return self::mobile_print_error(bigbluebutton_proxy::get_server_not_available_message($instance));
|
||||
}
|
||||
|
||||
// Check groups access and show message.
|
||||
$msjgroup = [];
|
||||
$groupmode = groups_get_activity_groupmode($instance->get_cm());
|
||||
if ($groupmode != NOGROUPS) {
|
||||
$msjgroup['message'] = get_string('view_mobile_message_groups_not_supported', 'bigbluebuttonbn');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'bigbluebuttonbn' => $instance->get_instance_data(),
|
||||
'msjgroup' => $msjgroup,
|
||||
'urltojoin' => $urltojoin,
|
||||
'cmid' => $cm->id,
|
||||
'courseid' => $course->id,
|
||||
];
|
||||
|
||||
// We want to show a notification when user excedded 45 seconds without click button.
|
||||
$jstimecreatedmeeting = 'setTimeout(function(){
|
||||
document.getElementById("bigbluebuttonbn-mobile-notifications").style.display = "block";
|
||||
document.getElementById("bigbluebuttonbn-mobile-join").disabled = true;
|
||||
document.getElementById("bigbluebuttonbn-mobile-meetingready").style.display = "none";
|
||||
}, 45000);';
|
||||
|
||||
return [
|
||||
'templates' => [
|
||||
[
|
||||
'id' => 'main',
|
||||
'html' => $OUTPUT->render_from_template("mod_bigbluebuttonbn/mobile_view_page_$versionname", $data),
|
||||
],
|
||||
],
|
||||
'javascript' => $jstimecreatedmeeting,
|
||||
'otherdata' => '',
|
||||
'files' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view for errors.
|
||||
*
|
||||
* @param string $error Error to display.
|
||||
* @return array HTML, javascript and otherdata
|
||||
*/
|
||||
protected static function mobile_print_error($error): array {
|
||||
global $OUTPUT;
|
||||
|
||||
return [
|
||||
'templates' => [
|
||||
[
|
||||
'id' => 'main',
|
||||
'html' => $OUTPUT->render_from_template('mod_bigbluebuttonbn/mobile_view_error', [
|
||||
'error' => $error,
|
||||
]),
|
||||
],
|
||||
],
|
||||
'javascript' => '',
|
||||
'otherdata' => '',
|
||||
'files' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view for messages.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param string $message Message to display.
|
||||
* @param array $notstarted Extra messages for not started session.
|
||||
* @return array HTML, javascript and otherdata
|
||||
*/
|
||||
protected static function mobile_print_notification(instance $instance, $message, $notstarted = []): array {
|
||||
global $OUTPUT, $CFG;
|
||||
|
||||
$data = [
|
||||
'bigbluebuttonbn' => $instance->get_instance_data(),
|
||||
'cmid' => $instance->get_cm_id(),
|
||||
'message' => $message,
|
||||
'not_started' => $notstarted,
|
||||
];
|
||||
|
||||
return [
|
||||
'templates' => [
|
||||
[
|
||||
'id' => 'main',
|
||||
'html' => $OUTPUT->render_from_template('mod_bigbluebuttonbn/mobile_view_notification', $data),
|
||||
],
|
||||
],
|
||||
'javascript' => file_get_contents($CFG->dirroot . '/mod/bigbluebuttonbn/mobileapp/mobile.notification.js'),
|
||||
'otherdata' => '',
|
||||
'files' => ''
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
/**
|
||||
* Renderer for recording name in place editable.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recording_description_editable extends recording_editable {
|
||||
|
||||
/**
|
||||
* Specific constructor with the right label/hint for this editable
|
||||
*
|
||||
* @param recording $rec
|
||||
* @param instance $instance
|
||||
*/
|
||||
public function __construct(recording $rec, instance $instance) {
|
||||
parent::__construct($rec, $instance,
|
||||
get_string('view_recording_description_editlabel', 'mod_bigbluebuttonbn'),
|
||||
get_string('view_recording_description_edithint', 'mod_bigbluebuttonbn'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display
|
||||
*
|
||||
* @param recording $recording a recording
|
||||
* @return string
|
||||
*/
|
||||
public function get_recording_value(recording $recording): string {
|
||||
$metadescription = $recording->get('description');
|
||||
return \html_writer::span($metadescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of editable
|
||||
*/
|
||||
protected static function get_type() {
|
||||
return 'description';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use lang_string;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton;
|
||||
use moodle_exception;
|
||||
use core\output\inplace_editable;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\local\proxy\recording_proxy;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Renderer for recording in place editable.
|
||||
*
|
||||
* Generic class
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
abstract class recording_editable extends \core\output\inplace_editable {
|
||||
|
||||
/** @var instance The bbb instance */
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param recording $rec
|
||||
* @param instance $instance
|
||||
* @param string $edithint
|
||||
* @param string $editlabel
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function __construct(recording $rec, instance $instance, string $edithint, string $editlabel) {
|
||||
$this->instance = $instance;
|
||||
|
||||
$editable = $this->check_capability();
|
||||
$displayvalue = format_string(
|
||||
$this->get_recording_value($rec),
|
||||
true,
|
||||
[
|
||||
'context' => $instance->get_context(),
|
||||
]
|
||||
);
|
||||
|
||||
// Hack here: the ID is the recordID and the meeting ID.
|
||||
parent::__construct(
|
||||
'mod_bigbluebuttonbn',
|
||||
static::get_type(),
|
||||
$rec->get('id'),
|
||||
$editable,
|
||||
$displayvalue,
|
||||
$displayvalue,
|
||||
$edithint,
|
||||
$editlabel
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user can access and or modify this item.
|
||||
*
|
||||
* @return bool
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
protected function check_capability() {
|
||||
global $USER;
|
||||
|
||||
if (!can_access_course($this->instance->get_course(), $USER)) {
|
||||
throw new moodle_exception('noaccess', 'mod_bigbluebuttonbn');
|
||||
}
|
||||
|
||||
return $this->instance->can_manage_recordings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of editable
|
||||
*/
|
||||
protected static function get_type() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real recording value
|
||||
*
|
||||
* @param recording $rec
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get_recording_value(recording $rec): string;
|
||||
|
||||
/**
|
||||
* Update the recording with the new value
|
||||
*
|
||||
* @param int $itemid
|
||||
* @param mixed $value
|
||||
* @return recording_editable
|
||||
*/
|
||||
public static function update($itemid, $value) {
|
||||
$recording = recording::get_record(['id' => $itemid]);
|
||||
$instance = instance::get_from_instanceid($recording->get('bigbluebuttonbnid'));
|
||||
|
||||
require_login($instance->get_course());
|
||||
|
||||
$recording->set(static::get_type(), $value);
|
||||
$recording->update();
|
||||
|
||||
return new static($recording, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function evaluates if a row for the data used by the recording table is editable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function row_editable() {
|
||||
// Since the request to BBB are cached, it is safe to use the wrapper to check the server version.
|
||||
return $this->instance->can_manage_recordings()
|
||||
&& (bigbluebutton_proxy::get_server_version() >= 1.0 || $this->instance->is_blindside_network_server());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
|
||||
/**
|
||||
* Renderer for recording name in place editable.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recording_name_editable extends recording_editable {
|
||||
/**
|
||||
* Specific constructor with the right label/hint for this editable
|
||||
*
|
||||
* @param recording $rec
|
||||
* @param instance $instance
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function __construct(recording $rec, instance $instance) {
|
||||
parent::__construct($rec, $instance,
|
||||
get_string('view_recording_name_editlabel', 'mod_bigbluebuttonbn'),
|
||||
get_string('view_recording_name_edithint', 'mod_bigbluebuttonbn'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display
|
||||
*
|
||||
* @param recording $recording
|
||||
* @return string
|
||||
*/
|
||||
public function get_recording_value(recording $recording): string {
|
||||
$metaname = $recording->get('name');
|
||||
return \html_writer::span($metaname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of editable
|
||||
*/
|
||||
protected static function get_type() {
|
||||
return 'name';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_data;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
use pix_icon;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderer for recording row actionbar column
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recording_row_actionbar implements renderable, templatable {
|
||||
/**
|
||||
* @var $recording
|
||||
*/
|
||||
protected $recording;
|
||||
|
||||
/**
|
||||
* @var $tools
|
||||
*/
|
||||
protected $tools;
|
||||
/**
|
||||
* @var array TOOLS_DEFINITION a list of definition for the the specific tools
|
||||
*/
|
||||
const TOOL_ACTION_DEFINITIONS = [
|
||||
'protect' => [
|
||||
'action' => 'unprotect',
|
||||
'icon' => 'lock',
|
||||
'hidewhen' => '!protected',
|
||||
'requireconfirmation' => true,
|
||||
'disablewhen' => 'imported'
|
||||
],
|
||||
'unprotect' => [
|
||||
'action' => 'protect',
|
||||
'icon' => 'unlock',
|
||||
'hidewhen' => 'protected',
|
||||
'requireconfirmation' => true,
|
||||
'disablewhen' => 'imported'
|
||||
],
|
||||
'publish' => [
|
||||
'action' => 'publish',
|
||||
'icon' => 'show',
|
||||
'hidewhen' => 'published',
|
||||
'requireconfirmation' => true,
|
||||
'disablewhen' => 'imported'
|
||||
],
|
||||
'unpublish' => [
|
||||
'action' => 'unpublish',
|
||||
'icon' => 'hide',
|
||||
'hidewhen' => '!published',
|
||||
'requireconfirmation' => true,
|
||||
'disablewhen' => 'imported'
|
||||
],
|
||||
'delete' => [
|
||||
'action' => 'delete',
|
||||
'icon' => 'trash',
|
||||
'requireconfirmation' => true
|
||||
],
|
||||
'import' => [
|
||||
'action' => 'import',
|
||||
'icon' => 'import',
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* recording_row_actionbar constructor.
|
||||
*
|
||||
* @param recording $recording
|
||||
* @param array $tools
|
||||
*/
|
||||
public function __construct(recording $recording, array $tools) {
|
||||
$this->recording = $recording;
|
||||
$this->tools = $tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export for template
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$context = new stdClass();
|
||||
$context->id = 'recording-actionbar-' . $this->recording->get('id');
|
||||
$context->recordingid = $this->recording->get('id');
|
||||
$context->tools = [];
|
||||
foreach ($this->tools as $tool) {
|
||||
if (!empty(self::TOOL_ACTION_DEFINITIONS[$tool])) {
|
||||
$buttonpayload = self::TOOL_ACTION_DEFINITIONS[$tool];
|
||||
$conditionalhiding = $buttonpayload['hidewhen'] ?? null;
|
||||
$disabledwhen = $buttonpayload['disablewhen'] ?? null;
|
||||
$this->actionbar_update_diplay($buttonpayload, $disabledwhen, $this->recording, 'disabled');
|
||||
$this->actionbar_update_diplay($buttonpayload, $conditionalhiding, $this->recording);
|
||||
|
||||
if (!empty($buttonpayload)) {
|
||||
// With text for $manageaction.
|
||||
$iconortext = '';
|
||||
$linkattributes = [
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
];
|
||||
|
||||
$target = $buttonpayload['action'];
|
||||
if (isset($buttonpayload['target'])) {
|
||||
$target .= '-' . $buttonpayload['target'];
|
||||
}
|
||||
$id = 'recording-' . $target . '-' . $this->recording->get('recordingid');
|
||||
if ((boolean) config::get('recording_icons_enabled')) {
|
||||
// With icon for $manageaction.
|
||||
$iconattributes = [
|
||||
'id' => $id,
|
||||
'class' => 'iconsmall',
|
||||
];
|
||||
$linkattributes = [
|
||||
'id' => $id,
|
||||
'data-action' => $buttonpayload['action'],
|
||||
'data-require-confirmation' => !empty($buttonpayload['requireconfirmation']),
|
||||
'class' => 'action-icon'
|
||||
];
|
||||
if ($this->recording->get('imported')) {
|
||||
$linkattributes['data-links'] = recording::count_records(
|
||||
[
|
||||
'recordingid' => $this->recording->get('recordingid'),
|
||||
'imported' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
if (isset($buttonpayload['disabled'])) {
|
||||
$iconattributes['class'] .= ' fa-' . $buttonpayload['disabled'];
|
||||
$linkattributes['class'] .= ' disabled';
|
||||
}
|
||||
$icon = new pix_icon(
|
||||
'i/' . $buttonpayload['icon'],
|
||||
get_string('view_recording_list_actionbar_' . $buttonpayload['action'], 'bigbluebuttonbn'),
|
||||
'moodle',
|
||||
$iconattributes
|
||||
);
|
||||
$iconortext = $output->render($icon);
|
||||
} else {
|
||||
$iconortext = get_string($buttonpayload['action']);
|
||||
$linkattributes['title'] = get_string($buttonpayload['action']);
|
||||
}
|
||||
$actionlink = new \action_link(new \moodle_url('#'), $iconortext, null, $linkattributes);
|
||||
$context->tools [] = $actionlink->export_for_template($output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the settings for this action and disable or hide the tool from the toolbar
|
||||
*
|
||||
* @param array $buttonpayload
|
||||
* @param string $condition
|
||||
* @param recording $rec
|
||||
* @param string $value
|
||||
*/
|
||||
private function actionbar_update_diplay(&$buttonpayload, $condition, $rec, $value = 'invisible') {
|
||||
if ($condition) {
|
||||
$negates = $condition[0] === '!';
|
||||
$conditionalvariable = ltrim($condition, '!');
|
||||
if ($rec->get($conditionalvariable) xor $negates) {
|
||||
$buttonpayload['disabled'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_data;
|
||||
use mod_bigbluebuttonbn\local\helpers\roles;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderer for recording row playback column
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recording_row_playback implements renderable, templatable {
|
||||
|
||||
/**
|
||||
* @var $instance
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* @var $recording
|
||||
*/
|
||||
protected $recording;
|
||||
|
||||
/**
|
||||
* recording_row_playback constructor.
|
||||
*
|
||||
* @param recording $rec
|
||||
* @param instance $instance
|
||||
*/
|
||||
public function __construct(recording $rec, ?instance $instance) {
|
||||
$this->instance = $instance ?? null;
|
||||
$this->recording = $rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export for template
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$ispublished = $this->recording->get('published');
|
||||
$recordingid = $this->recording->get('id');
|
||||
$context = (object) [
|
||||
'dataimported' => $this->recording->get('imported'),
|
||||
'id' => 'playbacks-' . $this->recording->get('id'),
|
||||
'recordingid' => $recordingid,
|
||||
'additionaloptions' => '',
|
||||
'playbacks' => [],
|
||||
];
|
||||
|
||||
$playbacks = $this->recording->get('playbacks');
|
||||
if ($ispublished && $playbacks) {
|
||||
foreach ($playbacks as $playback) {
|
||||
if ($this->should_be_included($playback)) {
|
||||
$linkattributes = [
|
||||
'id' => "recording-play-{$playback['type']}-{$recordingid}",
|
||||
'class' => 'btn btn-sm btn-default',
|
||||
'data-action' => 'play',
|
||||
'data-target' => $playback['type'],
|
||||
];
|
||||
$actionlink = new \action_link(
|
||||
$playback['url'],
|
||||
recording_data::type_text($playback['type']),
|
||||
null,
|
||||
$linkattributes
|
||||
);
|
||||
$context->playbacks[] = $actionlink->export_for_template($output);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
/**
|
||||
* Helper function renders the link used for recording type in row for the data used by the recording table.
|
||||
*
|
||||
* @param array $playback
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_be_included(array $playback): bool {
|
||||
// All types that are not restricted are included.
|
||||
if (array_key_exists('restricted', $playback) && strtolower($playback['restricted']) == 'false') {
|
||||
return true;
|
||||
}
|
||||
// All types that are not statistics are included.
|
||||
if ($playback['type'] != 'statistics') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Exclude imported recordings.
|
||||
if ($this->recording->get('imported')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exclude non moderators.
|
||||
if ($this->instance) {
|
||||
if (!$this->instance->is_admin() && !$this->instance->is_moderator()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return roles::has_capability_in_course($this->recording->get('courseid'), 'mod/bigbluebuttonbn:managerecordings');
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\bigbluebutton\recordings\recording_data;
|
||||
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
|
||||
use mod_bigbluebuttonbn\recording;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderer for recording_row_preview column
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recording_row_preview implements renderable, templatable {
|
||||
/**
|
||||
* @var $recording
|
||||
*/
|
||||
protected $recording;
|
||||
|
||||
/**
|
||||
* recording_row_playback constructor.
|
||||
*
|
||||
* @param recording $rec
|
||||
*/
|
||||
public function __construct(recording $rec) {
|
||||
$this->recording = $rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export for template
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
global $CFG;
|
||||
|
||||
$context = (object) [
|
||||
'id' => 'preview-' . $this->recording->get('id'),
|
||||
'hidden' => !$this->recording->get('published'),
|
||||
'recordingpreviews' => [],
|
||||
];
|
||||
|
||||
$playbacks = $this->recording->get('playbacks');
|
||||
foreach ($playbacks as $playback) {
|
||||
$thumbnails = [];
|
||||
|
||||
if (isset($playback['preview'])) {
|
||||
foreach ($playback['preview'] as $image) {
|
||||
$validated = true;
|
||||
$url = trim($image['url']);
|
||||
if ($CFG->bigbluebuttonbn_recordings_validate_url) {
|
||||
$validated = bigbluebutton_proxy::is_remote_resource_valid($url);
|
||||
}
|
||||
if ($validated) {
|
||||
$thumbnails[] = $url . '?' . time();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($thumbnails)) {
|
||||
$context->recordingpreviews[] = (object) [
|
||||
'thumbnails' => $thumbnails,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Renderer for recording section.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Laurent David (laurent.david [at] call-learning [dt] fr)
|
||||
*/
|
||||
class recordings_session implements renderable, templatable {
|
||||
|
||||
/**
|
||||
* @var instance
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* recording_section constructor.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public function __construct(instance $instance) {
|
||||
$this->instance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export for template
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$isrecordedtype = $this->instance->is_type_room_and_recordings() || $this->instance->is_type_recordings_only();
|
||||
|
||||
$context = (object) [
|
||||
'bbbid' => $this->instance->get_instance_id(),
|
||||
'groupid' => $this->instance->get_group_id(),
|
||||
'has_recordings' => $this->instance->is_recorded() && $isrecordedtype,
|
||||
'searchbutton' => [
|
||||
'value' => '',
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->instance->can_import_recordings()) {
|
||||
$button = new \single_button(
|
||||
$this->instance->get_import_url(),
|
||||
get_string('view_recording_button_import', 'mod_bigbluebuttonbn')
|
||||
);
|
||||
$context->import_button = $button->export_for_template($output);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use core\notification;
|
||||
use core\output\inplace_editable;
|
||||
use html_table;
|
||||
use html_writer;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use plugin_renderer_base;
|
||||
|
||||
/**
|
||||
* Renderer for the mod_bigbluebuttonbn plugin.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Darko Miletic (darko.miletic [at] gmail [dt] com)
|
||||
*/
|
||||
class renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Render the index table.
|
||||
*
|
||||
* @param index $index
|
||||
* @return string
|
||||
*/
|
||||
protected function render_index(index $index): string {
|
||||
$this->page->requires->js_call_amd('mod_bigbluebuttonbn/index', 'init');
|
||||
|
||||
return html_writer::table($index->get_table($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the groups selector.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @return string
|
||||
*/
|
||||
public function render_groups_selector(instance $instance): string {
|
||||
$groupmode = groups_get_activity_groupmode($instance->get_cm());
|
||||
if ($groupmode === NOGROUPS) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Separate or visible group mode.
|
||||
$groups = groups_get_activity_allowed_groups($instance->get_cm());
|
||||
if (empty($groups)) {
|
||||
// No groups in this course.
|
||||
notification::add(get_string('view_groups_nogroups_warning', 'bigbluebuttonbn'), notification::INFO);
|
||||
return '';
|
||||
}
|
||||
|
||||
// Assign group default values.
|
||||
if (count($groups) == 0) {
|
||||
// Only the All participants group exists.
|
||||
notification::add(get_string('view_groups_notenrolled_warning', 'bigbluebuttonbn'), notification::INFO);
|
||||
return '';
|
||||
}
|
||||
|
||||
if (count($groups) > 1) {
|
||||
notification::add(get_string('view_groups_selection_warning', 'bigbluebuttonbn'), notification::INFO);
|
||||
}
|
||||
|
||||
$groupsmenu = groups_print_activity_menu(
|
||||
$instance->get_cm(),
|
||||
$instance->get_view_url(),
|
||||
true
|
||||
);
|
||||
|
||||
return $groupsmenu . '<br><br>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the view page.
|
||||
*
|
||||
* @param view_page $page
|
||||
* @return string
|
||||
*/
|
||||
public function render_view_page(view_page $page): string {
|
||||
return $this->render_from_template(
|
||||
'mod_bigbluebuttonbn/view_page',
|
||||
$page->export_for_template($this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render inplace editable
|
||||
*
|
||||
* @param inplace_editable $e
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_inplace_editable(inplace_editable $e) {
|
||||
return $this->render_from_template('core/inplace_editable', $e->export_for_template($this));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\output;
|
||||
|
||||
use core\output\notification;
|
||||
use mod_bigbluebuttonbn\instance;
|
||||
use mod_bigbluebuttonbn\local\config;
|
||||
use mod_bigbluebuttonbn\meeting;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* View Page template renderable.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class view_page implements renderable, templatable {
|
||||
|
||||
/** @var instance The instance being rendered */
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* Constructor for the View Page.
|
||||
*
|
||||
* @param instance $instance
|
||||
*/
|
||||
public function __construct(instance $instance) {
|
||||
$this->instance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the content required to render the template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$templatedata = (object) [
|
||||
'instanceid' => $this->instance->get_instance_id(),
|
||||
'groupselector' => $output->render_groups_selector($this->instance),
|
||||
'meetingname' => $this->instance->get_meeting_name(),
|
||||
'description' => $this->instance->get_meeting_description(true),
|
||||
'joinurl' => $this->instance->get_join_url(),
|
||||
];
|
||||
|
||||
if ($this->show_default_server_warning()) {
|
||||
$templatedata->serverwarning = (new notification(
|
||||
get_string('view_warning_default_server', 'mod_bigbluebuttonbn'),
|
||||
notification::NOTIFY_WARNING,
|
||||
false
|
||||
))->export_for_template($output);
|
||||
}
|
||||
|
||||
$viewwarningmessage = config::get('general_warning_message');
|
||||
if ($this->show_view_warning() && !empty($viewwarningmessage)) {
|
||||
$templatedata->sitenotification = (object) [
|
||||
'message' => $viewwarningmessage,
|
||||
'type' => config::get('general_warning_box_type'),
|
||||
'icon' => [
|
||||
'pix' => 'i/bullhorn',
|
||||
'component' => 'core',
|
||||
],
|
||||
];
|
||||
|
||||
if ($url = config::get('general_warning_button_href')) {
|
||||
$templatedata->sitenotification->actions = [[
|
||||
'url' => $url,
|
||||
'title' => config::get('general_warning_button_text'),
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->instance->is_feature_enabled('showroom')) {
|
||||
$roomdata = meeting::get_meeting_info_for_instance($this->instance);
|
||||
$roomdata->haspresentations = false;
|
||||
if (!empty($roomdata->presentations)) {
|
||||
$roomdata->haspresentations = true;
|
||||
}
|
||||
$templatedata->room = $roomdata;
|
||||
}
|
||||
|
||||
if ($this->instance->is_feature_enabled('showrecordings') && $this->instance->is_recorded()) {
|
||||
$recordings = new recordings_session($this->instance);
|
||||
$templatedata->recordings = $recordings->export_for_template($output);
|
||||
} else if ($this->instance->is_type_recordings_only()) {
|
||||
$templatedata->recordingwarning = (new notification(
|
||||
get_string('view_message_recordings_disabled', 'mod_bigbluebuttonbn'),
|
||||
notification::NOTIFY_WARNING,
|
||||
false
|
||||
))->export_for_template($output);
|
||||
}
|
||||
|
||||
return $templatedata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the default server warning.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function show_default_server_warning(): bool {
|
||||
if (!$this->instance->is_admin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config::DEFAULT_SERVER_URL != config::get('server_url')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the view warning.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function show_view_warning(): bool {
|
||||
if ($this->instance->is_admin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$generalwarningroles = explode(',', config::get('general_warning_roles'));
|
||||
$userroles = \mod_bigbluebuttonbn\local\helpers\roles::get_user_roles(
|
||||
$this->instance->get_context(),
|
||||
$this->instance->get_user_id()
|
||||
);
|
||||
|
||||
foreach ($userroles as $userrole) {
|
||||
if (in_array($userrole->shortname, $generalwarningroles)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
/**
|
||||
* Class plugin.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2019 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Darko Miletic (darko.miletic [at] gmail [dt] com)
|
||||
*/
|
||||
abstract class plugin {
|
||||
|
||||
/**
|
||||
* Component name.
|
||||
*/
|
||||
const COMPONENT = 'mod_bigbluebuttonbn';
|
||||
|
||||
/**
|
||||
* Helper function to convert an html string to plain text.
|
||||
*
|
||||
* @param string $html
|
||||
* @param int $len
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function html2text($html, $len = 0) {
|
||||
$text = strip_tags($html);
|
||||
$text = str_replace(' ', ' ', $text);
|
||||
$textlen = strlen($text);
|
||||
$text = mb_substr($text, 0, $len);
|
||||
if ($textlen > $len) {
|
||||
$text .= '...';
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper generates a random password.
|
||||
*
|
||||
* @param int $length
|
||||
* @param string $unique
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function random_password($length = 8, $unique = "") {
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
do {
|
||||
$password = substr(str_shuffle($chars), 0, $length);
|
||||
} while ($unique == $password);
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\privacy;
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\approved_userlist;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\helper;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\userlist;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
/**
|
||||
* Privacy class for requesting user data.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2018 - present, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
|
||||
*/
|
||||
class provider implements
|
||||
// This plugin has data.
|
||||
\core_privacy\local\metadata\provider,
|
||||
|
||||
// This plugin currently implements the original plugin\provider interface.
|
||||
\core_privacy\local\request\plugin\provider,
|
||||
|
||||
// This plugin is capable of determining which users have data within it.
|
||||
\core_privacy\local\request\core_userlist_provider {
|
||||
|
||||
/**
|
||||
* Returns metadata.
|
||||
*
|
||||
* @param collection $collection The initialised collection to add items to.
|
||||
* @return collection A listing of user data stored through this system.
|
||||
*/
|
||||
public static function get_metadata(collection $collection): collection {
|
||||
|
||||
// The table bigbluebuttonbn stores only the room properties.
|
||||
// However, there is a chance that some personal information is stored as metadata.
|
||||
// This would be done in the column 'participants' where rules can be set to define BBB roles.
|
||||
// It is fair to say that only the userid is stored, which is useless if user is removed.
|
||||
// But if this is a concern a refactoring on the way the rules are stored will be required.
|
||||
$collection->add_database_table('bigbluebuttonbn', [
|
||||
'participants' => 'privacy:metadata:bigbluebuttonbn:participants',
|
||||
], 'privacy:metadata:bigbluebuttonbn');
|
||||
|
||||
// The table bigbluebuttonbn_logs stores events triggered by users when using the plugin.
|
||||
// Some personal information along with the resource accessed is stored.
|
||||
$collection->add_database_table('bigbluebuttonbn_logs', [
|
||||
'userid' => 'privacy:metadata:bigbluebuttonbn_logs:userid',
|
||||
'timecreated' => 'privacy:metadata:bigbluebuttonbn_logs:timecreated',
|
||||
'meetingid' => 'privacy:metadata:bigbluebuttonbn_logs:meetingid',
|
||||
'log' => 'privacy:metadata:bigbluebuttonbn_logs:log',
|
||||
'meta' => 'privacy:metadata:bigbluebuttonbn_logs:meta',
|
||||
], 'privacy:metadata:bigbluebuttonbn_logs');
|
||||
|
||||
$collection->add_database_table('bigbluebuttonbn_recordings', [
|
||||
'userid' => 'privacy:metadata:bigbluebuttonbn_logs:userid',
|
||||
], 'privacy:metadata:bigbluebuttonbn_recordings');
|
||||
|
||||
// Personal information has to be passed to BigBlueButton.
|
||||
// This includes the user ID and fullname.
|
||||
$collection->add_external_location_link('bigbluebutton', [
|
||||
'userid' => 'privacy:metadata:bigbluebutton:userid',
|
||||
'fullname' => 'privacy:metadata:bigbluebutton:fullname',
|
||||
], 'privacy:metadata:bigbluebutton');
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param int $userid The user to search.
|
||||
* @return contextlist $contextlist The list of contexts used in this plugin.
|
||||
*/
|
||||
public static function get_contexts_for_userid(int $userid): contextlist {
|
||||
// If user was already deleted, do nothing.
|
||||
if (!\core_user::get_user($userid)) {
|
||||
return new contextlist();
|
||||
}
|
||||
// Fetch all bigbluebuttonbn logs.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
INNER JOIN {course_modules} cm
|
||||
ON cm.id = c.instanceid
|
||||
AND c.contextlevel = :contextlevel
|
||||
INNER JOIN {modules} m
|
||||
ON m.id = cm.module
|
||||
AND m.name = :modname
|
||||
INNER JOIN {bigbluebuttonbn} bigbluebuttonbn
|
||||
ON bigbluebuttonbn.id = cm.instance
|
||||
INNER JOIN {bigbluebuttonbn_logs} bigbluebuttonbnlogs
|
||||
ON bigbluebuttonbnlogs.bigbluebuttonbnid = bigbluebuttonbn.id
|
||||
WHERE bigbluebuttonbnlogs.userid = :userid";
|
||||
|
||||
$params = [
|
||||
'modname' => 'bigbluebuttonbn',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'userid' => $userid,
|
||||
];
|
||||
$contextlist = new contextlist();
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
return $contextlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
|
||||
*
|
||||
* @param approved_contextlist $contextlist a list of contexts approved for export.
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
|
||||
// Filter out any contexts that are not related to modules.
|
||||
$cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
|
||||
if ($context->contextlevel == CONTEXT_MODULE) {
|
||||
$carry[] = $context->instanceid;
|
||||
}
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
if (empty($cmids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $contextlist->get_user();
|
||||
|
||||
// Get all the bigbluebuttonbn activities associated with the above course modules.
|
||||
$instanceidstocmids = self::get_instance_ids_to_cmids_from_cmids($cmids);
|
||||
$instanceids = array_keys($instanceidstocmids);
|
||||
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED);
|
||||
$params = array_merge($inparams, ['userid' => $user->id]);
|
||||
$recordset = $DB->get_recordset_select(
|
||||
'bigbluebuttonbn_logs',
|
||||
"bigbluebuttonbnid $insql AND userid = :userid",
|
||||
$params,
|
||||
'timecreated, id'
|
||||
);
|
||||
self::recordset_loop_and_export($recordset, 'bigbluebuttonbnid', [],
|
||||
function($carry, $record) use ($user, $instanceidstocmids) {
|
||||
$carry[] = [
|
||||
'timecreated' => transform::datetime($record->timecreated),
|
||||
'meetingid' => $record->meetingid,
|
||||
'log' => $record->log,
|
||||
'meta' => $record->meta,
|
||||
];
|
||||
return $carry;
|
||||
},
|
||||
function($instanceid, $data) use ($user, $instanceidstocmids) {
|
||||
$context = \context_module::instance($instanceidstocmids[$instanceid]);
|
||||
$contextdata = helper::get_context_data($context, $user);
|
||||
$finaldata = (object) array_merge((array) $contextdata, ['logs' => $data]);
|
||||
helper::export_context_files($context, $user);
|
||||
writer::with_context($context)->export_data([], $finaldata);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all data for all users in the specified context.
|
||||
*
|
||||
* @param \context $context the context to delete in.
|
||||
*/
|
||||
public static function delete_data_for_all_users_in_context(\context $context) {
|
||||
global $DB;
|
||||
|
||||
if (!$context instanceof \context_module) {
|
||||
return;
|
||||
}
|
||||
|
||||
$instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST);
|
||||
$DB->delete_records('bigbluebuttonbn_logs', ['bigbluebuttonbnid' => $instanceid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all user data for the specified user, in the specified contexts.
|
||||
*
|
||||
* @param approved_contextlist $contextlist a list of contexts approved for deletion.
|
||||
*/
|
||||
public static function delete_data_for_user(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
$count = $contextlist->count();
|
||||
if (empty($count)) {
|
||||
return;
|
||||
}
|
||||
$userid = $contextlist->get_user()->id;
|
||||
foreach ($contextlist->get_contexts() as $context) {
|
||||
if (!$context instanceof \context_module) {
|
||||
return;
|
||||
}
|
||||
$instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST);
|
||||
$DB->delete_records('bigbluebuttonbn_logs', ['bigbluebuttonbnid' => $instanceid, 'userid' => $userid]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a dict of bigbluebuttonbn IDs mapped to their course module ID.
|
||||
*
|
||||
* @param array $cmids The course module IDs.
|
||||
* @return array In the form of [$bigbluebuttonbnid => $cmid].
|
||||
*/
|
||||
protected static function get_instance_ids_to_cmids_from_cmids(array $cmids) {
|
||||
global $DB;
|
||||
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED);
|
||||
$sql = "SELECT bigbluebuttonbn.id, cm.id AS cmid
|
||||
FROM {bigbluebuttonbn} bigbluebuttonbn
|
||||
JOIN {modules} m
|
||||
ON m.name = :bigbluebuttonbn
|
||||
JOIN {course_modules} cm
|
||||
ON cm.instance = bigbluebuttonbn.id
|
||||
AND cm.module = m.id
|
||||
WHERE cm.id $insql";
|
||||
$params = array_merge($inparams, ['bigbluebuttonbn' => 'bigbluebuttonbn']);
|
||||
|
||||
return $DB->get_records_sql_menu($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop and export from a recordset.
|
||||
*
|
||||
* @param \moodle_recordset $recordset The recordset.
|
||||
* @param string $splitkey The record key to determine when to export.
|
||||
* @param mixed $initial The initial data to reduce from.
|
||||
* @param callable $reducer The function to return the dataset, receives current dataset, and the current record.
|
||||
* @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.
|
||||
* @return void
|
||||
*/
|
||||
protected static function recordset_loop_and_export(
|
||||
\moodle_recordset $recordset,
|
||||
$splitkey,
|
||||
$initial,
|
||||
callable $reducer,
|
||||
callable $export
|
||||
) {
|
||||
$data = $initial;
|
||||
$lastid = null;
|
||||
|
||||
foreach ($recordset as $record) {
|
||||
if ($lastid && $record->{$splitkey} != $lastid) {
|
||||
$export($lastid, $data);
|
||||
$data = $initial;
|
||||
}
|
||||
$data = $reducer($data, $record);
|
||||
$lastid = $record->{$splitkey};
|
||||
}
|
||||
$recordset->close();
|
||||
|
||||
if (!empty($lastid)) {
|
||||
$export($lastid, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users who have data within a context.
|
||||
*
|
||||
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
|
||||
*/
|
||||
public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
|
||||
$context = $userlist->get_context();
|
||||
|
||||
if (!$context instanceof \context_module) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'instanceid' => $context->instanceid,
|
||||
'modulename' => 'bigbluebuttonbn',
|
||||
];
|
||||
|
||||
$sql = "SELECT bnl.userid
|
||||
FROM {course_modules} cm
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
|
||||
JOIN {bigbluebuttonbn} bn ON bn.id = cm.instance
|
||||
JOIN {bigbluebuttonbn_logs} bnl ON bnl.bigbluebuttonbnid = bn.id
|
||||
WHERE cm.id = :instanceid";
|
||||
|
||||
$userlist->add_from_sql('userid', $sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple users within a single context.
|
||||
*
|
||||
* @param approved_userlist $userlist The approved context and user information to delete information for.
|
||||
*/
|
||||
public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
|
||||
global $DB;
|
||||
|
||||
$context = $userlist->get_context();
|
||||
$cm = $DB->get_record('course_modules', ['id' => $context->instanceid]);
|
||||
|
||||
list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
|
||||
$params = array_merge(['bigbluebuttonbnid' => $cm->instance], $userinparams);
|
||||
$sql = "bigbluebuttonbnid = :bigbluebuttonbnid AND userid {$userinsql}";
|
||||
|
||||
$DB->delete_records_select('bigbluebuttonbn_logs', $sql, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,788 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
use cache;
|
||||
use context;
|
||||
use context_course;
|
||||
use context_module;
|
||||
use core\persistent;
|
||||
use mod_bigbluebuttonbn\local\proxy\recording_proxy;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* The recording entity.
|
||||
*
|
||||
* This is utility class that defines a single recording, and provides methods for their local handling locally, and
|
||||
* communication with the bigbluebutton server.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2021 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class recording extends persistent {
|
||||
/** The table name. */
|
||||
const TABLE = 'bigbluebuttonbn_recordings';
|
||||
|
||||
/** @var int Defines that the activity used to create the recording no longer exists */
|
||||
public const RECORDING_HEADLESS = 1;
|
||||
|
||||
/** @var int Defines that the recording is not the original but an imported one */
|
||||
public const RECORDING_IMPORTED = 1;
|
||||
|
||||
/** @var int Defines that the list should include imported recordings */
|
||||
public const INCLUDE_IMPORTED_RECORDINGS = true;
|
||||
|
||||
/** @var int A meeting set to be recorded still awaits for a recording update */
|
||||
public const RECORDING_STATUS_AWAITING = 0;
|
||||
|
||||
/** @var int A meeting set to be recorded was not recorded and dismissed by BBB */
|
||||
public const RECORDING_STATUS_DISMISSED = 1;
|
||||
|
||||
/** @var int A meeting set to be recorded has a recording processed */
|
||||
public const RECORDING_STATUS_PROCESSED = 2;
|
||||
|
||||
/** @var int A meeting set to be recorded received notification callback from BBB */
|
||||
public const RECORDING_STATUS_NOTIFIED = 3;
|
||||
|
||||
/** @var int A meeting set to be recorded was processed and set back to an awaiting state */
|
||||
public const RECORDING_STATUS_RESET = 4;
|
||||
|
||||
/** @var bool Whether metadata been changed so the remote information needs to be updated ? */
|
||||
protected $metadatachanged = false;
|
||||
|
||||
/** @var int A refresh period for recordings, defaults to 300s (5mins) */
|
||||
public const RECORDING_REFRESH_DEFAULT_PERIOD = 300;
|
||||
|
||||
/** @var array A cached copy of the metadata */
|
||||
protected $metadata = null;
|
||||
|
||||
/** @var instance A cached copy of the instance */
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* Create an instance of this class.
|
||||
*
|
||||
* @param int $id If set, this is the id of an existing record, used to load the data.
|
||||
* @param stdClass|null $record If set will be passed to from_record
|
||||
* @param null|array $metadata
|
||||
*/
|
||||
public function __construct($id = 0, stdClass $record = null, ?array $metadata = null) {
|
||||
if ($record) {
|
||||
$record->headless = $record->headless ?? false;
|
||||
$record->imported = $record->imported ?? false;
|
||||
$record->groupid = $record->groupid ?? 0;
|
||||
$record->status = $record->status ?? self::RECORDING_STATUS_AWAITING;
|
||||
}
|
||||
parent::__construct($id, $record);
|
||||
|
||||
if ($metadata) {
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to retrieve recordings from the BigBlueButton.
|
||||
*
|
||||
* @param instance $instance
|
||||
* @param bool $includeimported
|
||||
* @param bool $onlyimported
|
||||
*
|
||||
* @return recording[] containing the recordings indexed by recordID, each recording is also a
|
||||
* non sequential associative array itself that corresponds to the actual recording in BBB
|
||||
*/
|
||||
public static function get_recordings_for_instance(
|
||||
instance $instance,
|
||||
bool $includeimported = false,
|
||||
bool $onlyimported = false
|
||||
): array {
|
||||
global $DB;
|
||||
|
||||
[$selects, $params] = self::get_basic_select_from_parameters(false, $includeimported, $onlyimported);
|
||||
$selects[] = "bigbluebuttonbnid = :bbbid";
|
||||
$params['bbbid'] = $instance->get_instance_id();
|
||||
$groupmode = groups_get_activity_groupmode($instance->get_cm());
|
||||
$context = $instance->get_context();
|
||||
if ($groupmode) {
|
||||
[$groupselects, $groupparams] = self::get_select_for_group(
|
||||
$groupmode,
|
||||
$context,
|
||||
$instance->get_course_id(),
|
||||
$instance->get_group_id(),
|
||||
$instance->get_cm()->groupingid
|
||||
);
|
||||
if ($groupselects) {
|
||||
$selects[] = $groupselects;
|
||||
$params = array_merge_recursive($params, $groupparams);
|
||||
}
|
||||
}
|
||||
|
||||
$recordings = self::fetch_records($selects, $params);
|
||||
foreach ($recordings as $recording) {
|
||||
$recording->instance = $instance;
|
||||
}
|
||||
|
||||
return $recordings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to retrieve recordings from a given course.
|
||||
*
|
||||
* @param int $courseid id for a course record or null
|
||||
* @param array $excludedinstanceid exclude recordings from instance ids
|
||||
* @param bool $includeimported
|
||||
* @param bool $onlyimported
|
||||
* @param bool $includedeleted
|
||||
* @param bool $onlydeleted
|
||||
*
|
||||
* @return recording[] containing the recordings indexed by recordID, each recording is also a
|
||||
* non sequential associative array itself that corresponds to the actual recording in BBB
|
||||
*/
|
||||
public static function get_recordings_for_course(
|
||||
int $courseid,
|
||||
array $excludedinstanceid = [],
|
||||
bool $includeimported = false,
|
||||
bool $onlyimported = false,
|
||||
bool $includedeleted = false,
|
||||
bool $onlydeleted = false
|
||||
): array {
|
||||
global $DB;
|
||||
|
||||
[$selects, $params] = self::get_basic_select_from_parameters(
|
||||
$includedeleted, $includeimported, $onlyimported, $onlydeleted
|
||||
);
|
||||
if ($courseid) {
|
||||
$selects[] = "courseid = :courseid";
|
||||
$params['courseid'] = $courseid;
|
||||
$course = $DB->get_record('course', ['id' => $courseid]);
|
||||
$groupmode = groups_get_course_groupmode($course);
|
||||
$context = context_course::instance($courseid);
|
||||
} else {
|
||||
$context = \context_system::instance();
|
||||
$groupmode = NOGROUPS;
|
||||
}
|
||||
|
||||
if ($groupmode) {
|
||||
[$groupselects, $groupparams] = self::get_select_for_group($groupmode, $context, $course->id);
|
||||
if ($groupselects) {
|
||||
$selects[] = $groupselects;
|
||||
$params = array_merge($params, $groupparams);
|
||||
}
|
||||
}
|
||||
|
||||
if ($excludedinstanceid) {
|
||||
[$sqlexcluded, $paramexcluded] = $DB->get_in_or_equal($excludedinstanceid, SQL_PARAMS_NAMED, 'param', false);
|
||||
$selects[] = "bigbluebuttonbnid {$sqlexcluded}";
|
||||
$params = array_merge($params, $paramexcluded);
|
||||
}
|
||||
|
||||
return self::fetch_records($selects, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get select for given group mode and context
|
||||
*
|
||||
* @param int $groupmode
|
||||
* @param context $context
|
||||
* @param int $courseid
|
||||
* @param int $groupid
|
||||
* @param int $groupingid
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_select_for_group($groupmode, $context, $courseid, $groupid = 0, $groupingid = 0): array {
|
||||
global $DB, $USER;
|
||||
|
||||
$selects = [];
|
||||
$params = [];
|
||||
if ($groupmode) {
|
||||
$accessallgroups = has_capability('moodle/site:accessallgroups', $context) || $groupmode == VISIBLEGROUPS;
|
||||
if ($accessallgroups) {
|
||||
if ($context instanceof context_module) {
|
||||
$allowedgroups = groups_get_all_groups($courseid, 0, $groupingid);
|
||||
} else {
|
||||
$allowedgroups = groups_get_all_groups($courseid);
|
||||
}
|
||||
} else {
|
||||
if ($context instanceof context_module) {
|
||||
$allowedgroups = groups_get_all_groups($courseid, $USER->id, $groupingid);
|
||||
} else {
|
||||
$allowedgroups = groups_get_all_groups($courseid, $USER->id);
|
||||
}
|
||||
}
|
||||
$allowedgroupsid = array_map(function($g) {
|
||||
return $g->id;
|
||||
}, $allowedgroups);
|
||||
if ($groupid || empty($allowedgroups)) {
|
||||
$selects[] = "groupid = :groupid";
|
||||
$params['groupid'] = ($groupid && in_array($groupid, $allowedgroupsid)) ?
|
||||
$groupid : 0;
|
||||
} else {
|
||||
if ($accessallgroups) {
|
||||
$allowedgroupsid[] = 0;
|
||||
}
|
||||
list($groupselects, $groupparams) = $DB->get_in_or_equal($allowedgroupsid, SQL_PARAMS_NAMED);
|
||||
$selects[] = 'groupid ' . $groupselects;
|
||||
$params = array_merge_recursive($params, $groupparams);
|
||||
}
|
||||
}
|
||||
return [
|
||||
implode(" AND ", $selects),
|
||||
$params,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get basic sql select from given parameters
|
||||
*
|
||||
* @param bool $includedeleted
|
||||
* @param bool $includeimported
|
||||
* @param bool $onlyimported
|
||||
* @param bool $onlydeleted
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_basic_select_from_parameters(
|
||||
bool $includedeleted = false,
|
||||
bool $includeimported = false,
|
||||
bool $onlyimported = false,
|
||||
bool $onlydeleted = false
|
||||
): array {
|
||||
$selects = [];
|
||||
$params = [];
|
||||
|
||||
// Start with the filters.
|
||||
if ($onlydeleted) {
|
||||
// Only headless recordings when only deleted is set.
|
||||
$selects[] = "headless = :headless";
|
||||
$params['headless'] = self::RECORDING_HEADLESS;
|
||||
} else if (!$includedeleted) {
|
||||
// Exclude headless recordings unless includedeleted.
|
||||
$selects[] = "headless != :headless";
|
||||
$params['headless'] = self::RECORDING_HEADLESS;
|
||||
}
|
||||
|
||||
if (!$includeimported) {
|
||||
// Exclude imported recordings unless includedeleted.
|
||||
$selects[] = "imported != :imported";
|
||||
$params['imported'] = self::RECORDING_IMPORTED;
|
||||
} else if ($onlyimported) {
|
||||
// Exclude non-imported recordings.
|
||||
$selects[] = "imported = :imported";
|
||||
$params['imported'] = self::RECORDING_IMPORTED;
|
||||
}
|
||||
|
||||
// Now get only recordings that have been validated by recording ready callback.
|
||||
$selects[] = "status IN (:status_processed, :status_notified)";
|
||||
$params['status_processed'] = self::RECORDING_STATUS_PROCESSED;
|
||||
$params['status_notified'] = self::RECORDING_STATUS_NOTIFIED;
|
||||
return [$selects, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'courseid' => [
|
||||
'type' => PARAM_INT,
|
||||
],
|
||||
'bigbluebuttonbnid' => [
|
||||
'type' => PARAM_INT,
|
||||
],
|
||||
'groupid' => [
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
],
|
||||
'recordingid' => [
|
||||
'type' => PARAM_RAW,
|
||||
],
|
||||
'headless' => [
|
||||
'type' => PARAM_BOOL,
|
||||
],
|
||||
'imported' => [
|
||||
'type' => PARAM_BOOL,
|
||||
],
|
||||
'status' => [
|
||||
'type' => PARAM_INT,
|
||||
],
|
||||
'importeddata' => [
|
||||
'type' => PARAM_RAW,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => ''
|
||||
],
|
||||
'name' => [
|
||||
'type' => PARAM_TEXT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
'description' => [
|
||||
'type' => PARAM_TEXT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => 0
|
||||
],
|
||||
'protected' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
'starttime' => [
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
'endtime' => [
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
'published' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
'playbacks' => [
|
||||
'type' => PARAM_RAW,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instance that this recording relates to.
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public function get_instance(): instance {
|
||||
if ($this->instance === null) {
|
||||
$this->instance = instance::get_from_instanceid($this->get('bigbluebuttonbnid'));
|
||||
}
|
||||
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Before doing the database update, let's check if we need to update metadata
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function before_update() {
|
||||
// We update if the remote metadata has been changed locally.
|
||||
if ($this->metadatachanged && !$this->get('imported')) {
|
||||
$metadata = $this->fetch_metadata();
|
||||
if ($metadata) {
|
||||
recording_proxy::update_recording(
|
||||
$this->get('recordingid'),
|
||||
$metadata
|
||||
);
|
||||
}
|
||||
$this->metadatachanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new imported recording from current recording
|
||||
*
|
||||
* @param instance $targetinstance
|
||||
* @return recording
|
||||
* @throws \coding_exception
|
||||
* @throws \core\invalid_persistent_exception
|
||||
*/
|
||||
public function create_imported_recording(instance $targetinstance) {
|
||||
$recordingrec = $this->to_record();
|
||||
$remotedata = $this->fetch_metadata();
|
||||
unset($recordingrec->id);
|
||||
$recordingrec->bigbluebuttonbnid = $targetinstance->get_instance_id();
|
||||
$recordingrec->courseid = $targetinstance->get_course_id();
|
||||
$recordingrec->groupid = 0; // The recording is available to everyone.
|
||||
$recordingrec->importeddata = json_encode($remotedata);
|
||||
$recordingrec->imported = true;
|
||||
$recordingrec->headless = false;
|
||||
$importedrecording = new self(0, $recordingrec);
|
||||
$importedrecording->create();
|
||||
return $importedrecording;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the recording in the BBB button
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function before_delete() {
|
||||
$recordid = $this->get('recordingid');
|
||||
if ($recordid && !$this->get('imported')) {
|
||||
recording_proxy::delete_recording($recordid);
|
||||
// Delete in cache if needed.
|
||||
$cachedrecordings = cache::make('mod_bigbluebuttonbn', 'recordings');
|
||||
$cachedrecordings->delete($recordid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
protected function set_name($value) {
|
||||
$this->metadata_set('name', trim($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Description
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
protected function set_description($value) {
|
||||
$this->metadata_set('description', trim($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recording is protected
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
protected function set_protected($value) {
|
||||
$realvalue = $value ? "true" : "false";
|
||||
$this->metadata_set('protected', $realvalue);
|
||||
recording_proxy::protect_recording($this->get('recordingid'), $realvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recording starttime
|
||||
*
|
||||
* @param int $value
|
||||
*/
|
||||
protected function set_starttime($value) {
|
||||
$this->metadata_set('starttime', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recording endtime
|
||||
*
|
||||
* @param int $value
|
||||
*/
|
||||
protected function set_endtime($value) {
|
||||
$this->metadata_set('endtime', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recording is published
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
protected function set_published($value) {
|
||||
$realvalue = $value ? "true" : "false";
|
||||
$this->metadata_set('published', $realvalue);
|
||||
// Now set this flag onto the remote bbb server.
|
||||
recording_proxy::publish_recording($this->get('recordingid'), $realvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* POSSIBLE_REMOTE_META_SOURCE match a field type and its metadataname (historical and current).
|
||||
*/
|
||||
const POSSIBLE_REMOTE_META_SOURCE = [
|
||||
'description' => ['meta_bbb-recording-description', 'meta_contextactivitydescription'],
|
||||
'name' => ['meta_bbb-recording-name', 'meta_contextactivity', 'meetingName'],
|
||||
'playbacks' => ['playbacks'],
|
||||
'starttime' => ['startTime'],
|
||||
'endtime' => ['endTime'],
|
||||
'published' => ['published'],
|
||||
'protected' => ['protected'],
|
||||
'tags' => ['meta_bbb-recording-tags']
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the real metadata name for the possible source.
|
||||
*
|
||||
* @param string $sourcetype the name of the source we look for (name, description...)
|
||||
* @param array $metadata current metadata
|
||||
*/
|
||||
protected function get_possible_meta_name_for_source($sourcetype, $metadata): string {
|
||||
$possiblesource = self::POSSIBLE_REMOTE_META_SOURCE[$sourcetype];
|
||||
$possiblesourcename = $possiblesource[0];
|
||||
foreach ($possiblesource as $possiblesname) {
|
||||
if (isset($meta[$possiblesname])) {
|
||||
$possiblesourcename = $possiblesname;
|
||||
}
|
||||
}
|
||||
return $possiblesourcename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string (metadata) to json object
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function remote_meta_convert() {
|
||||
$remotemeta = $this->raw_get('importeddata');
|
||||
return json_decode($remotemeta, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description is stored in the metadata, so we sometimes needs to do some conversion.
|
||||
*/
|
||||
protected function get_description() {
|
||||
return trim($this->metadata_get('description'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Name is stored in the metadata
|
||||
*/
|
||||
protected function get_name() {
|
||||
return trim($this->metadata_get('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of playbacks for this recording.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_playbacks() {
|
||||
if ($playbacks = $this->metadata_get('playbacks')) {
|
||||
return array_map(function(array $playback): array {
|
||||
$clone = array_merge([], $playback);
|
||||
$clone['url'] = new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
|
||||
'action' => 'play',
|
||||
'bn' => $this->raw_get('bigbluebuttonbnid'),
|
||||
'rid' => $this->get('id'),
|
||||
'rtype' => $clone['type'],
|
||||
]);
|
||||
|
||||
return $clone;
|
||||
}, $playbacks);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the playback URL for the specified type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_remote_playback_url(string $type): ?string {
|
||||
$this->refresh_metadata_if_required();
|
||||
|
||||
$playbacks = $this->metadata_get('playbacks');
|
||||
foreach ($playbacks as $playback) {
|
||||
if ($playback['type'] == $type) {
|
||||
return $playback['url'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is protected. Return null if protected is not implemented.
|
||||
*
|
||||
* @return bool|null
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function get_protected() {
|
||||
$protectedtext = $this->metadata_get('protected');
|
||||
return is_null($protectedtext) ? null : $protectedtext === "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Start time
|
||||
*
|
||||
* @return mixed|null
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function get_starttime() {
|
||||
return $this->metadata_get('starttime');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start time
|
||||
*
|
||||
* @return mixed|null
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function get_endtime() {
|
||||
return $this->metadata_get('endtime');
|
||||
}
|
||||
|
||||
/**
|
||||
* Is published
|
||||
*
|
||||
* @return bool
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function get_published() {
|
||||
$publishedtext = $this->metadata_get('published');
|
||||
return $publishedtext === "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locally stored metadata from this instance
|
||||
*
|
||||
* @param string $fieldname
|
||||
* @param mixed $value
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function metadata_set($fieldname, $value) {
|
||||
// Can we can change the metadata on the imported record ?
|
||||
if ($this->get('imported')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->metadatachanged = true;
|
||||
|
||||
$metadata = $this->fetch_metadata();
|
||||
$possiblesourcename = $this->get_possible_meta_name_for_source($fieldname, $metadata);
|
||||
$metadata[$possiblesourcename] = $value;
|
||||
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information stored in the recording metadata such as description, name and other info
|
||||
*
|
||||
* @param string $fieldname
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function metadata_get($fieldname) {
|
||||
$metadata = $this->fetch_metadata();
|
||||
|
||||
$possiblesourcename = $this->get_possible_meta_name_for_source($fieldname, $metadata);
|
||||
return $metadata[$possiblesourcename] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string Default sort for recordings when fetching from the database.
|
||||
*/
|
||||
const DEFAULT_RECORDING_SORT = 'timecreated ASC';
|
||||
|
||||
/**
|
||||
* Fetch all records which match the specified parameters, including all metadata that relates to them.
|
||||
*
|
||||
* @param array $selects
|
||||
* @param array $params
|
||||
* @return recording[]
|
||||
*/
|
||||
protected static function fetch_records(array $selects, array $params): array {
|
||||
global $DB;
|
||||
|
||||
// Fetch the local data. Arbitrary sort by id, so we get the same result on different db engines.
|
||||
$recordings = $DB->get_records_select(static::TABLE, implode(" AND ", $selects), $params,
|
||||
self::DEFAULT_RECORDING_SORT);
|
||||
|
||||
// Grab the recording IDs.
|
||||
$recordingids = array_filter(array_map(function($recording) {
|
||||
return $recording->recordingid;
|
||||
}, $recordings));
|
||||
|
||||
// Fetch all metadata for these recordings.
|
||||
$metadatas = recording_proxy::fetch_recordings($recordingids);
|
||||
|
||||
// Return the instances.
|
||||
return array_map(function($recording) use ($metadatas) {
|
||||
$metadata = null;
|
||||
if (array_key_exists($recording->recordingid, $metadatas)) {
|
||||
$metadata = $metadatas[$recording->recordingid];
|
||||
}
|
||||
|
||||
return new self(0, $recording, $metadata);
|
||||
}, $recordings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch metadata
|
||||
*
|
||||
* If metadata has changed locally or if it an imported recording, nothing will be done.
|
||||
*
|
||||
* @param bool $force
|
||||
* @return array
|
||||
*/
|
||||
protected function fetch_metadata(bool $force = false): ?array {
|
||||
if ($this->metadata !== null && !$force) {
|
||||
// Metadata is already up-to-date.
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
if ($this->get('imported')) {
|
||||
$this->metadata = json_decode($this->get('importeddata'), true);
|
||||
} else {
|
||||
$this->metadata = recording_proxy::fetch_recording($this->get('recordingid'));
|
||||
}
|
||||
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh metadata if required.
|
||||
*
|
||||
* If this is a protected recording which whose data was not fetched in the current request, then the metadata will
|
||||
* be purged and refetched. This ensures that the url is safe for use with a protected recording.
|
||||
*/
|
||||
protected function refresh_metadata_if_required() {
|
||||
recording_proxy::purge_protected_recording($this->get('recordingid'));
|
||||
$this->fetch_metadata(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronise pending recordings from the server.
|
||||
*
|
||||
* This function should be called by the check_pending_recordings scheduled task.
|
||||
*/
|
||||
public static function sync_pending_recordings_from_server(): void {
|
||||
global $DB;
|
||||
|
||||
$timelimitdays = 30;
|
||||
|
||||
// Fetch the local data.
|
||||
mtrace("=> Looking for any recording awaiting processing from the past {$timelimitdays} days.");
|
||||
$select = 'status = :status_awaiting AND timecreated > :withindays OR status = :status_reset';
|
||||
$recordings = $DB->get_records_select(static::TABLE, $select, [
|
||||
'status_awaiting' => self::RECORDING_STATUS_AWAITING,
|
||||
'withindays' => time() - ($timelimitdays * DAYSECS),
|
||||
'status_reset' => self::RECORDING_STATUS_RESET,
|
||||
], self::DEFAULT_RECORDING_SORT);
|
||||
// Sort by DEFAULT_RECORDING_SORT we get the same result on different db engines.
|
||||
|
||||
$recordingcount = count($recordings);
|
||||
mtrace("=> Found {$recordingcount} recordings to query");
|
||||
|
||||
// Grab the recording IDs.
|
||||
$recordingids = array_map(function($recording) {
|
||||
return $recording->recordingid;
|
||||
}, $recordings);
|
||||
|
||||
// Fetch all metadata for these recordings.
|
||||
mtrace("=> Fetching recording metadata from server");
|
||||
$metadatas = recording_proxy::fetch_recordings($recordingids);
|
||||
|
||||
$foundcount = 0;
|
||||
foreach ($metadatas as $recordingid => $metadata) {
|
||||
mtrace("==> Found updated metadata for {$recordingid}. Updating local cache.");
|
||||
$id = array_search($recordingid, $recordingids);
|
||||
|
||||
$recording = new self(0, $recordings[$id], $metadata);
|
||||
$recording->raw_set('status', self::RECORDING_STATUS_PROCESSED);
|
||||
$recording->update();
|
||||
$foundcount++;
|
||||
}
|
||||
|
||||
mtrace("=> Finished processing recordings. Updated status for {$foundcount} / {$recordingcount} recordings.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\search;
|
||||
|
||||
/**
|
||||
* Search area for mod_bigbluebuttonbn activities.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity extends \core_search\base_activity {
|
||||
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn\search;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Search area for mod_bigbluebuttonbn tags.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tags extends \core_search\base_activity {
|
||||
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritting get_document_recordset()
|
||||
* In this search implementation, we need to re-index all instances (and not only the last modified) because we
|
||||
* are working with core tags and these can be removed from "manage tags" without change the timemodified in
|
||||
* BBB instances.
|
||||
* @param int $modifiedfrom
|
||||
* @param \context|null $context
|
||||
* @return \moodle_recordset|null
|
||||
*/
|
||||
public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
|
||||
global $DB;
|
||||
[$contextjoin, $contextparams] = $this->get_context_restriction_sql($context, $this->get_module_name(), 'modtable');
|
||||
if ($contextjoin === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $DB->get_recordset_sql(
|
||||
'SELECT modtable.* FROM {' . $this->get_module_name() . '} modtable ' . $contextjoin,
|
||||
array_merge($contextparams)
|
||||
);
|
||||
|
||||
return($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding method to index tags of module as string separated by comma.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @param array $options
|
||||
* @return \core_search\document|bool
|
||||
*/
|
||||
public function get_document($record, $options = []) {
|
||||
|
||||
try {
|
||||
$cm = $this->get_cm($this->get_module_name(), $record->id, $record->course);
|
||||
$context = \context_module::instance($cm->id);
|
||||
|
||||
$tags = \core_tag_tag::get_tags_by_area_in_contexts("core", "course_modules", [$context]);
|
||||
$tagsstring = "";
|
||||
if (!empty($tags)) {
|
||||
$res = [];
|
||||
foreach ($tags as $t) {
|
||||
$res[] = $t->name;
|
||||
}
|
||||
$tagsstring = implode(", ", $res);
|
||||
}
|
||||
|
||||
} catch (\dml_missing_record_exception $ex) {
|
||||
// Notify it as we run here as admin, we should see everything.
|
||||
debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document, not all required data is available: ' .
|
||||
$ex->getMessage(), DEBUG_DEVELOPER);
|
||||
return false;
|
||||
} catch (\dml_exception $ex) {
|
||||
// Notify it as we run here as admin, we should see everything.
|
||||
debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare array with data from DB.
|
||||
$doc = \core_search\document_factory::instance($record->id, $this->componentname, $this->areaname);
|
||||
$doc->set('title', content_to_text($record->name, false));
|
||||
$doc->set('content', $tagsstring);
|
||||
$doc->set('contextid', $context->id);
|
||||
$doc->set('courseid', $record->course);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
$doc->set('modified', $record->{static::MODIFIED_FIELD_NAME});
|
||||
|
||||
// Check if this document should be considered new.
|
||||
if (isset($options['lastindexedtime'])) {
|
||||
$createdfield = static::CREATED_FIELD_NAME;
|
||||
if (!empty($createdfield) && ($options['lastindexedtime'] < $record->{$createdfield})) {
|
||||
// If the document was created after the last index time, it must be new.
|
||||
$doc->set_is_new(true);
|
||||
}
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_bigbluebuttonbn;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
/**
|
||||
* Helper class for validating settings used HTML for settings.php.
|
||||
*
|
||||
* @package mod_bigbluebuttonbn
|
||||
* @copyright 2010 onwards, Blindside Networks Inc
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_validator {
|
||||
|
||||
/**
|
||||
* Validate if general section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_general_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['server_url']) ||
|
||||
!isset($CFG->bigbluebuttonbn['shared_secret']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if record meeting section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_record_meeting_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['recording_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_icons_enabled']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_all_from_start_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_all_from_start_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_hide_button_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recording_hide_button_editable']) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if import recording section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_import_recordings_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['importrecordings_enabled']) ||
|
||||
!isset($CFG->bigbluebuttonbn['importrecordings_from_deleted_enabled']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if show recording section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_show_recordings_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['recordings_html_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_html_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_deleted_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_deleted_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_imported_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_imported_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_preview_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_preview_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['recordings_validate_url'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if wait moderator section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_wait_moderator_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['waitformoderator_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['waitformoderator_editable']) ||
|
||||
!isset($CFG->bigbluebuttonbn['waitformoderator_ping_interval']) ||
|
||||
!isset($CFG->bigbluebuttonbn['waitformoderator_cache_ttl']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if static voice bridge section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_static_voice_bridge_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['voicebridge_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if preupload presentation section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_preupload_presentation_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['preuploadpresentation_enabled']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if user limit section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_user_limit_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['userlimit_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['userlimit_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if moderator default section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_moderator_default_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['participant_moderator_default']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if send notification section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_send_notifications_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['sendnotifications_enabled']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if settings extended section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_settings_extended_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['recordingready_enabled']) ||
|
||||
!isset($CFG->bigbluebuttonbn['meetingevents_enabled']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if muteonstart section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_muteonstart_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['muteonstart_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['muteonstart_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if disablecam section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_disablecam_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['disablecam_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['disablecam_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if disablemic section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_disablemic_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['disablemic_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['disablemic_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if disableprivatechat section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_disableprivatechat_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['disableprivatechat_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['disableprivatechat_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if disablepublicchat section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_disablepublicchat_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['disablepublicchat_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['disablepublicchat_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if disablenote section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_disablenote_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['disablenote_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['disablenote_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if hideuserlist section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_hideuserlist_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['hideuserlist_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['hideuserlist_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if lockedlayout section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_lockedlayout_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['lockedlayout_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['lockedlayout_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if lockonjoin section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_lockonjoin_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['lockonjoin_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['lockonjoin_editable']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if lockonjoinconfigurable section will be shown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function section_lockonjoinconfigurable_shown() {
|
||||
global $CFG;
|
||||
return (!isset($CFG->bigbluebuttonbn['lockonjoinconfigurable_default']) ||
|
||||
!isset($CFG->bigbluebuttonbn['lockonjoinconfigurable_editable']));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user