From 749b4164739fd454df8afcc98ddc134dc4e9e0ee Mon Sep 17 00:00:00 2001 From: Laurent David Date: Mon, 13 Dec 2021 22:46:26 +0100 Subject: [PATCH] MDL-73312 mod_bigbluebuttonbn: Fix sort by date for recordings * Fix sorting for dates in recording table * Code review and getString usage in JS --- .../amd/build/recordings.min.js | 2 +- .../amd/build/recordings.min.js.map | 2 +- mod/bigbluebuttonbn/amd/src/recordings.js | 91 +++++++++---------- .../classes/external/get_recordings.php | 1 + .../recordings/recording_data.php | 2 +- mod/bigbluebuttonbn/tests/behat/room.feature | 34 +++++++ mod/bigbluebuttonbn/tests/generator/lib.php | 4 +- 7 files changed, 84 insertions(+), 52 deletions(-) create mode 100644 mod/bigbluebuttonbn/tests/behat/room.feature diff --git a/mod/bigbluebuttonbn/amd/build/recordings.min.js b/mod/bigbluebuttonbn/amd/build/recordings.min.js index a085484ac49..9bd6ff7e313 100644 --- a/mod/bigbluebuttonbn/amd/build/recordings.min.js +++ b/mod/bigbluebuttonbn/amd/build/recordings.min.js @@ -1,2 +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/recordings",["exports","./repository","core/notification","core/str","core/loadingicon","core/modal_factory","core/modal_events","core/pending"],function(a,b,c,d,f,g,h,i){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=l(b);d=l(d);g=j(g);h=j(h);i=j(i);function j(a){return a&&a.__esModule?a:{default:a}}function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();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}function m(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function n(a){for(var b=1,c;ba.length)b=a.length;for(var c=0,d=Array(b);c.*?".concat(c,".*?"),"i");i.set("data",b.filter({asList:!0},function(a){var b=a.get("recording");if(b&&d.test(b)){return!0}var c=a.get("description");return c&&d.test(c)}))},o=function(a){var c=function(a,b){var c=a.closest("[data-".concat(b,"]"));if(c){return c.dataset[b]}return null},e=a.dataset,f={bigbluebuttonbnid:k,recordingid:c(a,"recordingid"),additionaloptions:c(a,"additionaloptions"),action:e.action};if(!f.additionaloptions){f.additionaloptions={}}if("import"===e.action){var i=c(a,"source-instance-id"),j=c(a,"source-course-id");if(!f.additionaloptions){f.additionaloptions={}}f.additionaloptions.sourceid=i?i:0;f.additionaloptions.bbbcourseid=j?j:0}f.additionaloptions=JSON.stringify(f.additionaloptions);if("1"===a.dataset.requireConfirmation){return new Promise(function(a){return g.default.create({title:d.get_string("confirm"),body:p(f),type:g.default.types.SAVE_CANCEL}).then(function(){var b=q(regeneratorRuntime.mark(function b(c){return regeneratorRuntime.wrap(function(b){while(1){switch(b.prev=b.next){case 0:b.t0=c;b.next=3;return d.get_string("ok","moodle");case 3:b.t1=b.sent;b.t0.setSaveButtonText.call(b.t0,b.t1);c.getRoot().on(h.default.save,function(){a(!0)});c.getRoot().on(h.default.hidden,function(){c.destroy();a(!1)});c.show();return b.abrupt("return",c);case 9:case"end":return b.stop();}}},b)}));return function(){return b.apply(this,arguments)}}()).catch(Notification.exception)}).then(function(a){return a?b.updateRecording(f):function(){return null}})}else{return b.updateRecording(f)}},p=function(){var a=q(regeneratorRuntime.mark(function a(b){var c,e,f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=document.querySelector("#playbacks-".concat(b.recordingid));a.next=3;return d.get_string("true"===f.dataset.imported?"view_recording_link":"view_recording","bigbluebuttonbn");case 3:g=a.sent;a.next=6;return d.get_string("view_recording_".concat(b.action,"_confirmation"),"bigbluebuttonbn",g);case 6:h=a.sent;if(!("import"===b.action)){a.next=9;break}return a.abrupt("return",h);case 9:i=null===(c=document.querySelector("a#recording-".concat(b.action,"-").concat(b.recordingid)))||void 0===c?void 0:null===(e=c.dataset)||void 0===e?void 0:e.links;if(!(!i||0===i)){a.next=12;break}return a.abrupt("return",h);case 12:a.next=14;return d.get_string(1===i?"view_recording_".concat(b.action,"_confirmation_warning_p"):"view_recording_".concat(b.action,"_confirmation_warning_s"),"bigbluebuttonbn",i);case 14:j=a.sent;return a.abrupt("return",j+"\n\n"+h);case 16:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),r=function(a){var b=a.target.closest("[data-action=\"play\"]");if(b){a.preventDefault();var e=window.open("","_blank");e.opener=null;e.location.href=b.href;return}var d=a.target.closest("a[data-action]");if(d&&!d.classList.contains("disabled")){a.preventDefault();var g=(0,f.addIconToContainerWithPromise)(i.get("boundingBox").getDOMNode());o(d).then(m).catch(c.exception).then(g.resolve).catch()}},s=function(a){a.preventDefault();var b=a.target.closest("div[role=search]"),c=b.querySelector("input[name=search]");n(c.value)};return{filterByText:n,refreshTableData:m,registerEventListeners:function registerEventListeners(){var a=i.get("boundingBox").getDOMNode();a.addEventListener("click",r);var b=document.querySelector(e);if(b){var c=document.querySelector(e+" button");c.addEventListener("click",s)}}}},F=function(a,b,c){if(!c){return Promise.resolve()}if(!c.status){return Promise.resolve()}var d=c.tabledata,e=new i.default("mod_bigbluebuttonbn/recordings/setupDatatable");return Promise.all([z(d.locale),y()]).then(function(a){var b=r(a,2),c=b[0],d=b[1];c.Intl.add("datatable-paginator",c.config.lang,n({},d));return c}).then(function(a){var b=B(c);a.RecordsPaginatorView=Y.Base.create("my-paginator-view",a.DataTable.Paginator.View,[],{_modelChange:function _modelChange(a){var b=a.changed,c=b&&b.totalItems;if(c){this._updateControlsUI(a.target.get("page"))}}});var e=new a.DataTable({paginatorView:"RecordsPaginatorView",width:"1195px",columns:d.columns,data:b,rowsPerPage:10,paginatorLocation:["header","footer"],autoSync:!0});return e}).then(function(c){c.render(a);var d=E(a,b,c),e=d.registerEventListeners;e();return c}).then(function(a){e.resolve();return a})};a.init=function init(a,b){D(a).then(function(c){return F(a,b,c)}).catch(c.exception)}}); +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/recordings",["exports","./repository","core/notification","core/prefetch","core/str","core/loadingicon","core/modal_factory","core/modal_events","core/pending"],function(a,b,c,d,e,f,g,h,i){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=l(b);g=j(g);h=j(h);i=j(i);function j(a){return a&&a.__esModule?a:{default:a}}function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();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}function m(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function n(a){for(var b=1,c;ba.length)b=a.length;for(var c=0,d=Array(b);c.*?".concat(c,".*?"),"i");i.set("data",b.filter({asList:!0},function(a){var b=a.get("recording");if(b&&d.test(b)){return!0}var c=a.get("description");return c&&d.test(c)}))},o=function(a){var c=function(a,b){var c=a.closest("[data-".concat(b,"]"));if(c){return c.dataset[b]}return null},d=a.dataset,f={bigbluebuttonbnid:k,recordingid:c(a,"recordingid"),additionaloptions:c(a,"additionaloptions"),action:d.action};if(!f.additionaloptions){f.additionaloptions={}}if("import"===d.action){var i=c(a,"source-instance-id"),j=c(a,"source-course-id");if(!f.additionaloptions){f.additionaloptions={}}f.additionaloptions.sourceid=i?i:0;f.additionaloptions.bbbcourseid=j?j:0}f.additionaloptions=JSON.stringify(f.additionaloptions);if("1"===a.dataset.requireConfirmation){return new Promise(function(a){return g.default.create({title:(0,e.get_string)("confirm"),body:p(f),type:g.default.types.SAVE_CANCEL}).then(function(){var b=t(regeneratorRuntime.mark(function b(c){return regeneratorRuntime.wrap(function(b){while(1){switch(b.prev=b.next){case 0:b.t0=c;b.next=3;return(0,e.get_string)("ok","moodle");case 3:b.t1=b.sent;b.t0.setSaveButtonText.call(b.t0,b.t1);c.getRoot().on(h.default.save,function(){a(!0)});c.getRoot().on(h.default.hidden,function(){c.destroy();a(!1)});c.show();return b.abrupt("return",c);case 9:case"end":return b.stop();}}},b)}));return function(){return b.apply(this,arguments)}}()).catch(Notification.exception)}).then(function(a){return a?b.updateRecording(f):function(){return null}})}else{return b.updateRecording(f)}},p=function(){var a=t(regeneratorRuntime.mark(function a(b){var c,d,f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=document.querySelector("#playbacks-".concat(b.recordingid));a.next=3;return(0,e.get_string)("true"===f.dataset.imported?"view_recording_link":"view_recording","bigbluebuttonbn");case 3:g=a.sent;a.next=6;return(0,e.get_string)("view_recording_".concat(b.action,"_confirmation"),"bigbluebuttonbn",g);case 6:h=a.sent;if(!("import"===b.action)){a.next=9;break}return a.abrupt("return",h);case 9:i=null===(c=document.querySelector("a#recording-".concat(b.action,"-").concat(b.recordingid)))||void 0===c?void 0:null===(d=c.dataset)||void 0===d?void 0:d.links;if(!(!i||0===i)){a.next=12;break}return a.abrupt("return",h);case 12:a.next=14;return(0,e.get_string)(1===i?"view_recording_".concat(b.action,"_confirmation_warning_p"):"view_recording_".concat(b.action,"_confirmation_warning_s"),"bigbluebuttonbn",i);case 14:j=a.sent;return a.abrupt("return",j+"\n\n"+h);case 16:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),q=function(a){var b=a.target.closest("[data-action=\"play\"]");if(b){a.preventDefault();var e=window.open("","_blank");e.opener=null;e.location.href=b.href;return}var d=a.target.closest("a[data-action]");if(d&&!d.classList.contains("disabled")){a.preventDefault();var g=(0,f.addIconToContainerWithPromise)(i.get("boundingBox").getDOMNode());o(d).then(m).catch(c.exception).then(g.resolve).catch()}},r=function(a){a.preventDefault();var b=a.target.closest("div[role=search]"),c=b.querySelector("input[name=search]");n(c.value)};return{filterByText:n,refreshTableData:m,registerEventListeners:function registerEventListeners(){var a=i.get("boundingBox").getDOMNode();a.addEventListener("click",q);var b=document.querySelector(d);if(b){var c=document.querySelector(d+" button");c.addEventListener("click",r)}}}},J=function(a,b,c){if(!c){return Promise.resolve()}if(!c.status){return Promise.resolve()}var d=c.tabledata,e=new i.default("mod_bigbluebuttonbn/recordings/setupDatatable");return Promise.all([D(d.locale),C()]).then(function(a){var b=o(a,2),c=b[0],e=b[1];c.Intl.add("datatable-paginator",c.config.lang,n({},e));c.DataTable.BodyView.Formatters.customDate=function dateCustomFormatter(){return function(a){return E(d.locale,a.value)}};return c}).then(function(a){var b=F(c);a.RecordsPaginatorView=Y.Base.create("my-paginator-view",a.DataTable.Paginator.View,[],{_modelChange:function _modelChange(a){var b=a.changed,c=b&&b.totalItems;if(c){this._updateControlsUI(a.target.get("page"))}}});return new a.DataTable({paginatorView:"RecordsPaginatorView",width:"1195px",columns:d.columns,data:b,rowsPerPage:10,paginatorLocation:["header","footer"],autoSync:!0})}).then(function(c){c.render(a);var d=I(a,b,c),e=d.registerEventListeners;e();return c}).then(function(a){e.resolve();return a})};a.init=function init(a,b){H(a).then(function(c){return J(a,b,c)}).catch(c.exception)}}); //# sourceMappingURL=recordings.min.js.map diff --git a/mod/bigbluebuttonbn/amd/build/recordings.min.js.map b/mod/bigbluebuttonbn/amd/build/recordings.min.js.map index 0807e18ba55..2862da4aa7f 100644 --- a/mod/bigbluebuttonbn/amd/build/recordings.min.js.map +++ b/mod/bigbluebuttonbn/amd/build/recordings.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/recordings.js"],"names":["stringList","getStringsForYui","stringMap","map","key","component","then","first","prev","next","last","goToLabel","goToAction","perPage","showAll","catch","getYuiInstance","lang","Promise","resolve","YUI","use","Y","formatDates","locale","dateList","row","date","Date","toLocaleDateString","weekday","year","month","day","getFormattedData","response","recordingData","tabledata","rowData","JSON","parse","data","getTableNode","tableSelector","document","querySelector","fetchRecordingData","tableNode","dataset","importMode","repository","fetchRecordingsToImport","bbbid","bbbSourceInstanceId","bbbSourceCourseId","tools","groupId","fetchRecordings","getDataTableFunctions","tableId","searchFormId","dataTable","updateTableFromResponse","status","get","reset","set","currentFilter","filterByText","refreshTableData","value","dataModel","escapedRegex","replace","rsearch","RegExp","filter","asList","item","name","test","description","requestAction","element","getDataFromAction","dataType","dataElement","closest","elementData","payload","bigbluebuttonbnid","recordingid","additionaloptions","action","bbbsourceid","bbbcourseid","sourceid","stringify","requireConfirmation","ModalFactory","create","title","Str","get_string","body","recordingConfirmationMessage","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","ModalEvents","save","hidden","destroy","show","Notification","exception","proceed","updateRecording","playbackElement","imported","recordingType","confirmation","associatedLinkCount","links","confirmationWarning","processAction","e","popoutLink","target","preventDefault","videoPlayer","window","open","opener","location","href","clickedLink","classList","contains","iconPromise","getDOMNode","displayException","processSearchSubmission","parentNode","searchInput","registerEventListeners","boundingBox","addEventListener","searchForm","searchButton","setupDatatable","pendingPromise","Pending","all","yuiInstance","strings","Intl","add","config","tableData","RecordsPaginatorView","Base","DataTable","Paginator","View","_modelChange","changed","totalItems","_updateControlsUI","paginatorView","width","columns","rowsPerPage","paginatorLocation","autoSync","render","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS for the recordings page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/recordings\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as repository from './repository';\nimport {exception as displayException} from 'core/notification';\nimport {get_strings as getStrings} from 'core/str';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport * as Str from 'core/str';\nimport Pending from 'core/pending';\n\nconst stringList = [\n 'view_recording_yui_first',\n 'view_recording_yui_prev',\n 'view_recording_yui_next',\n 'view_recording_yui_last',\n 'view_recording_yui_page',\n 'view_recording_yui_go',\n 'view_recording_yui_rows',\n 'view_recording_yui_show_all',\n];\n\nconst getStringsForYui = () => {\n const stringMap = stringList.map(key => {\n return {\n key,\n component: 'bigbluebuttonbn',\n };\n });\n\n return getStrings(stringMap)\n .then(([first, prev, next, last, goToLabel, goToAction, perPage, showAll]) => {\n return {\n first,\n prev,\n next,\n last,\n goToLabel,\n goToAction,\n perPage,\n showAll,\n };\n })\n .catch();\n};\n\nconst getYuiInstance = lang => new Promise(resolve => {\n // eslint-disable-next-line\n YUI({\n lang,\n }).use('intl', 'datatable', 'datatable-sort', 'datatable-paginator', 'datatype-number', Y => {\n resolve(Y);\n });\n});\n\n/**\n * Format the supplied date per the specified locale.\n *\n * @param {string} locale\n * @param {array} dateList\n * @returns {array}\n */\nconst formatDates = (locale, dateList) => dateList.map(row => {\n const date = new Date(row.date);\n row.date = date.toLocaleDateString(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n\n return row;\n});\n\n/**\n * Format response data for the table.\n *\n * @param {string} response JSON-encoded table data\n * @returns {array}\n */\nconst getFormattedData = response => {\n const recordingData = response.tabledata;\n const rowData = JSON.parse(recordingData.data);\n\n return formatDates(recordingData.locale, rowData);\n};\n\nconst getTableNode = tableSelector => document.querySelector(tableSelector);\n\nconst fetchRecordingData = tableSelector => {\n const tableNode = getTableNode(tableSelector);\n\n if (tableNode.dataset.importMode) {\n return repository.fetchRecordingsToImport(\n tableNode.dataset.bbbid,\n tableNode.dataset.bbbSourceInstanceId,\n tableNode.dataset.bbbSourceCourseId,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n } else {\n return repository.fetchRecordings(\n tableNode.dataset.bbbid,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n }\n};\n\n/**\n * Fetch the data table functinos for the specified table.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {Object} dataTable\n * @returns {Object}\n * @private\n */\nconst getDataTableFunctions = (tableId, searchFormId, dataTable) => {\n const tableNode = getTableNode(tableId);\n const bbbid = tableNode.dataset.bbbid;\n\n const updateTableFromResponse = response => {\n if (!response || !response.status) {\n // There was no output at all.\n return;\n }\n\n dataTable.get('data').reset(getFormattedData(response));\n dataTable.set(\n 'currentData',\n dataTable.get('data')\n );\n\n const currentFilter = dataTable.get('currentFilter');\n if (currentFilter) {\n filterByText(currentFilter);\n }\n };\n\n const refreshTableData = () => fetchRecordingData(tableId).then(updateTableFromResponse);\n\n const filterByText = value => {\n const dataModel = dataTable.get('currentData');\n dataTable.set('currentFilter', value);\n\n const escapedRegex = value.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n const rsearch = new RegExp(`.*?${escapedRegex}.*?`, 'i');\n\n dataTable.set('data', dataModel.filter({asList: true}, item => {\n const name = item.get('recording');\n if (name && rsearch.test(name)) {\n return true;\n }\n\n const description = item.get('description');\n return description && rsearch.test(description);\n }));\n };\n\n const requestAction = (element) => {\n const getDataFromAction = (element, dataType) => {\n const dataElement = element.closest(`[data-${dataType}]`);\n if (dataElement) {\n return dataElement.dataset[dataType];\n }\n\n return null;\n };\n\n const elementData = element.dataset;\n const payload = {\n bigbluebuttonbnid: bbbid,\n recordingid: getDataFromAction(element, 'recordingid'),\n additionaloptions: getDataFromAction(element, 'additionaloptions'),\n action: elementData.action,\n };\n // Slight change for import, for additional options.\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n if (elementData.action === 'import') {\n const bbbsourceid = getDataFromAction(element, 'source-instance-id');\n const bbbcourseid = getDataFromAction(element, 'source-course-id');\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n payload.additionaloptions.sourceid = bbbsourceid ? bbbsourceid : 0;\n payload.additionaloptions.bbbcourseid = bbbcourseid ? bbbcourseid : 0;\n }\n // Now additional options should be a json string.\n payload.additionaloptions = JSON.stringify(payload.additionaloptions);\n if (element.dataset.requireConfirmation === \"1\") {\n // Create the confirmation dialogue.\n return new Promise((resolve) =>\n ModalFactory.create({\n title: Str.get_string('confirm'),\n body: recordingConfirmationMessage(payload),\n type: ModalFactory.types.SAVE_CANCEL\n }).then(async(modal) => {\n modal.setSaveButtonText(await Str.get_string('ok', 'moodle'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, () => {\n resolve(true);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n resolve(false);\n });\n\n modal.show();\n\n return modal;\n }).catch(Notification.exception)\n ).then((proceed) =>\n proceed ? repository.updateRecording(payload) : () => null\n );\n } else {\n return repository.updateRecording(payload);\n }\n };\n\n const recordingConfirmationMessage = async(data) => {\n\n const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`);\n const recordingType = await Str.get_string(\n playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording',\n 'bigbluebuttonbn'\n );\n\n const confirmation = await Str.get_string(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType);\n\n if (data.action === 'import') {\n return confirmation;\n }\n\n // If it has associated links imported in a different course/activity, show that in confirmation dialog.\n const associatedLinkCount = document.querySelector(`a#recording-${data.action}-${data.recordingid}`)?.dataset?.links;\n if (!associatedLinkCount || associatedLinkCount === 0) {\n return confirmation;\n }\n\n const confirmationWarning = await Str.get_string(\n associatedLinkCount === 1\n ? `view_recording_${data.action}_confirmation_warning_p`\n : `view_recording_${data.action}_confirmation_warning_s`,\n 'bigbluebuttonbn',\n associatedLinkCount\n );\n\n return confirmationWarning + '\\n\\n' + confirmation;\n };\n\n /**\n * Process an action event.\n *\n * @param {Event} e\n */\n const processAction = e => {\n const popoutLink = e.target.closest('[data-action=\"play\"]');\n if (popoutLink) {\n e.preventDefault();\n\n const videoPlayer = window.open('', '_blank');\n videoPlayer.opener = null;\n videoPlayer.location.href = popoutLink.href;\n // TODO send a recording viewed event when this event will be implemented.\n return;\n }\n\n // Fetch any clicked anchor.\n const clickedLink = e.target.closest('a[data-action]');\n if (clickedLink && !clickedLink.classList.contains('disabled')) {\n e.preventDefault();\n\n // Create a spinning icon on the table.\n const iconPromise = addIconToContainerWithPromise(dataTable.get('boundingBox').getDOMNode());\n\n requestAction(clickedLink)\n .then(refreshTableData)\n .catch(displayException)\n .then(iconPromise.resolve)\n .catch();\n }\n };\n\n const processSearchSubmission = e => {\n // Prevent the default action.\n e.preventDefault();\n const parentNode = e.target.closest('div[role=search]');\n const searchInput = parentNode.querySelector('input[name=search]');\n filterByText(searchInput.value);\n };\n\n const registerEventListeners = () => {\n // Add event listeners to the table boundingBox.\n const boundingBox = dataTable.get('boundingBox').getDOMNode();\n boundingBox.addEventListener('click', processAction);\n\n // Setup the search from handlers.\n const searchForm = document.querySelector(searchFormId);\n if (searchForm) {\n const searchButton = document.querySelector(searchFormId + ' button');\n searchButton.addEventListener('click', processSearchSubmission);\n }\n };\n\n return {\n filterByText,\n refreshTableData,\n registerEventListeners,\n };\n};\n\n/**\n * Setup the data table for the specified BBB instance.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {object} response The response from the data request\n * @returns {Promise}\n */\nconst setupDatatable = (tableId, searchFormId, response) => {\n if (!response) {\n return Promise.resolve();\n }\n\n if (!response.status) {\n // Something failed. Continue to show the plain output.\n return Promise.resolve();\n }\n\n const recordingData = response.tabledata;\n\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable');\n return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()])\n .then(([yuiInstance, strings]) => {\n // Add the fetched strings to the YUI Instance.\n yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings});\n\n return yuiInstance;\n })\n .then(yuiInstance => {\n const tableData = getFormattedData(response);\n yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view',yuiInstance.DataTable.Paginator.View, [],{\n _modelChange : function (e) {\n var changed = e.changed,\n totalItems = (changed && changed.totalItems);\n if (totalItems) {\n this._updateControlsUI(e.target.get('page'));\n }\n }\n });\n const dataTable = new yuiInstance.DataTable({\n paginatorView: \"RecordsPaginatorView\",\n width: \"1195px\",\n columns: recordingData.columns,\n data: tableData,\n rowsPerPage: 10,\n paginatorLocation: ['header', 'footer'],\n autoSync: true\n });\n return dataTable;\n })\n .then(dataTable => {\n dataTable.render(tableId);\n const {registerEventListeners} = getDataTableFunctions(\n tableId,\n searchFormId,\n dataTable);\n registerEventListeners();\n return dataTable;\n })\n .then(dataTable => {\n pendingPromise.resolve();\n return dataTable;\n });\n};\n\n/**\n * Initialise recordings code.\n *\n * @method init\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n */\nexport const init = (tableId, searchFormId) => {\n fetchRecordingData(tableId)\n .then(response => setupDatatable(tableId, searchFormId, response))\n .catch(displayException);\n};\n"],"file":"recordings.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/recordings.js"],"names":["stringsWithKeys","first","prev","next","last","page","go","rows","all","Object","entries","map","entry","getStringsForYui","stringMap","keys","key","component","then","stringArray","assign","index","catch","getYuiInstance","lang","Promise","resolve","YUI","use","Y","formatDate","locale","date","realDate","Date","toLocaleDateString","weekday","year","month","day","getFormattedData","response","recordingData","tabledata","JSON","parse","data","getTableNode","tableSelector","document","querySelector","fetchRecordingData","tableNode","dataset","importMode","repository","fetchRecordingsToImport","bbbid","bbbSourceInstanceId","bbbSourceCourseId","tools","groupId","fetchRecordings","getDataTableFunctions","tableId","searchFormId","dataTable","updateTableFromResponse","status","get","reset","set","currentFilter","filterByText","refreshTableData","value","dataModel","escapedRegex","replace","rsearch","RegExp","filter","asList","item","name","test","description","requestAction","element","getDataFromAction","dataType","dataElement","closest","elementData","payload","bigbluebuttonbnid","recordingid","additionaloptions","action","bbbsourceid","bbbcourseid","sourceid","stringify","requireConfirmation","ModalFactory","create","title","body","recordingConfirmationMessage","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","ModalEvents","save","hidden","destroy","show","Notification","exception","proceed","updateRecording","playbackElement","imported","recordingType","confirmation","associatedLinkCount","links","confirmationWarning","processAction","e","popoutLink","target","preventDefault","videoPlayer","window","open","opener","location","href","clickedLink","classList","contains","iconPromise","getDOMNode","displayException","processSearchSubmission","parentNode","searchInput","registerEventListeners","boundingBox","addEventListener","searchForm","searchButton","setupDatatable","pendingPromise","Pending","yuiInstance","strings","Intl","add","config","DataTable","BodyView","Formatters","customDate","dateCustomFormatter","cell","tableData","RecordsPaginatorView","Base","Paginator","View","_modelChange","changed","totalItems","_updateControlsUI","paginatorView","width","columns","rowsPerPage","paginatorLocation","autoSync","render","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS for the recordings page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/recordings\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as repository from './repository';\nimport {exception as displayException} from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\n\nconst stringsWithKeys = {\n first: 'view_recording_yui_first',\n prev: 'view_recording_yui_prev',\n next: 'view_recording_yui_next',\n last: 'view_recording_yui_last',\n page: 'view_recording_yui_page',\n go: 'view_recording_yui_go',\n rows: 'view_recording_yui_rows',\n all: 'view_recording_yui_all',\n};\n// Load global strings.\nprefetchStrings('bigbluebuttonbn', Object.entries(stringsWithKeys).map((entry) => entry[1]));\n\nconst getStringsForYui = () => {\n const stringMap = Object.keys(stringsWithKeys).map(key => {\n return {\n key: stringsWithKeys[key],\n component: 'mod_bigbluebuttonbn',\n };\n });\n\n // Return an object with the matching string keys (we want an object with {: ...}).\n return getStrings(stringMap)\n .then((stringArray) => Object.assign({}, ...Object.keys(stringsWithKeys).map(\n (key, index) => ({[key]: stringArray[index]})))\n ).catch();\n};\n\nconst getYuiInstance = lang => new Promise(resolve => {\n // eslint-disable-next-line\n YUI({\n lang,\n }).use('intl', 'datatable', 'datatable-sort', 'datatable-paginator', 'datatype-number', Y => {\n resolve(Y);\n });\n});\n\n/**\n * Format the supplied date per the specified locale.\n *\n * @param {string} locale\n * @param {number} date\n * @returns {array}\n */\nconst formatDate = (locale, date) => {\n const realDate = new Date(date);\n return realDate.toLocaleDateString(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n};\n\n/**\n * Format response data for the table.\n *\n * @param {string} response JSON-encoded table data\n * @returns {array}\n */\nconst getFormattedData = response => {\n const recordingData = response.tabledata;\n return JSON.parse(recordingData.data);\n};\n\nconst getTableNode = tableSelector => document.querySelector(tableSelector);\n\nconst fetchRecordingData = tableSelector => {\n const tableNode = getTableNode(tableSelector);\n\n if (tableNode.dataset.importMode) {\n return repository.fetchRecordingsToImport(\n tableNode.dataset.bbbid,\n tableNode.dataset.bbbSourceInstanceId,\n tableNode.dataset.bbbSourceCourseId,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n } else {\n return repository.fetchRecordings(\n tableNode.dataset.bbbid,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n }\n};\n\n/**\n * Fetch the data table functinos for the specified table.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {Object} dataTable\n * @returns {Object}\n * @private\n */\nconst getDataTableFunctions = (tableId, searchFormId, dataTable) => {\n const tableNode = getTableNode(tableId);\n const bbbid = tableNode.dataset.bbbid;\n\n const updateTableFromResponse = response => {\n if (!response || !response.status) {\n // There was no output at all.\n return;\n }\n\n dataTable.get('data').reset(getFormattedData(response));\n dataTable.set(\n 'currentData',\n dataTable.get('data')\n );\n\n const currentFilter = dataTable.get('currentFilter');\n if (currentFilter) {\n filterByText(currentFilter);\n }\n };\n\n const refreshTableData = () => fetchRecordingData(tableId).then(updateTableFromResponse);\n\n const filterByText = value => {\n const dataModel = dataTable.get('currentData');\n dataTable.set('currentFilter', value);\n\n const escapedRegex = value.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n const rsearch = new RegExp(`.*?${escapedRegex}.*?`, 'i');\n\n dataTable.set('data', dataModel.filter({asList: true}, item => {\n const name = item.get('recording');\n if (name && rsearch.test(name)) {\n return true;\n }\n\n const description = item.get('description');\n return description && rsearch.test(description);\n }));\n };\n\n const requestAction = (element) => {\n const getDataFromAction = (element, dataType) => {\n const dataElement = element.closest(`[data-${dataType}]`);\n if (dataElement) {\n return dataElement.dataset[dataType];\n }\n\n return null;\n };\n\n const elementData = element.dataset;\n const payload = {\n bigbluebuttonbnid: bbbid,\n recordingid: getDataFromAction(element, 'recordingid'),\n additionaloptions: getDataFromAction(element, 'additionaloptions'),\n action: elementData.action,\n };\n // Slight change for import, for additional options.\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n if (elementData.action === 'import') {\n const bbbsourceid = getDataFromAction(element, 'source-instance-id');\n const bbbcourseid = getDataFromAction(element, 'source-course-id');\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n payload.additionaloptions.sourceid = bbbsourceid ? bbbsourceid : 0;\n payload.additionaloptions.bbbcourseid = bbbcourseid ? bbbcourseid : 0;\n }\n // Now additional options should be a json string.\n payload.additionaloptions = JSON.stringify(payload.additionaloptions);\n if (element.dataset.requireConfirmation === \"1\") {\n // Create the confirmation dialogue.\n return new Promise((resolve) =>\n ModalFactory.create({\n title: getString('confirm'),\n body: recordingConfirmationMessage(payload),\n type: ModalFactory.types.SAVE_CANCEL\n }).then(async(modal) => {\n modal.setSaveButtonText(await getString('ok', 'moodle'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, () => {\n resolve(true);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n resolve(false);\n });\n\n modal.show();\n\n return modal;\n }).catch(Notification.exception)\n ).then((proceed) =>\n proceed ? repository.updateRecording(payload) : () => null\n );\n } else {\n return repository.updateRecording(payload);\n }\n };\n\n const recordingConfirmationMessage = async(data) => {\n\n const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`);\n const recordingType = await getString(\n playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording',\n 'bigbluebuttonbn'\n );\n\n const confirmation = await getString(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType);\n\n if (data.action === 'import') {\n return confirmation;\n }\n\n // If it has associated links imported in a different course/activity, show that in confirmation dialog.\n const associatedLinkCount = document.querySelector(`a#recording-${data.action}-${data.recordingid}`)?.dataset?.links;\n if (!associatedLinkCount || associatedLinkCount === 0) {\n return confirmation;\n }\n\n const confirmationWarning = await getString(\n associatedLinkCount === 1\n ? `view_recording_${data.action}_confirmation_warning_p`\n : `view_recording_${data.action}_confirmation_warning_s`,\n 'bigbluebuttonbn',\n associatedLinkCount\n );\n\n return confirmationWarning + '\\n\\n' + confirmation;\n };\n\n /**\n * Process an action event.\n *\n * @param {Event} e\n */\n const processAction = e => {\n const popoutLink = e.target.closest('[data-action=\"play\"]');\n if (popoutLink) {\n e.preventDefault();\n\n const videoPlayer = window.open('', '_blank');\n videoPlayer.opener = null;\n videoPlayer.location.href = popoutLink.href;\n // TODO send a recording viewed event when this event will be implemented.\n return;\n }\n\n // Fetch any clicked anchor.\n const clickedLink = e.target.closest('a[data-action]');\n if (clickedLink && !clickedLink.classList.contains('disabled')) {\n e.preventDefault();\n\n // Create a spinning icon on the table.\n const iconPromise = addIconToContainerWithPromise(dataTable.get('boundingBox').getDOMNode());\n\n requestAction(clickedLink)\n .then(refreshTableData)\n .catch(displayException)\n .then(iconPromise.resolve)\n .catch();\n }\n };\n\n const processSearchSubmission = e => {\n // Prevent the default action.\n e.preventDefault();\n const parentNode = e.target.closest('div[role=search]');\n const searchInput = parentNode.querySelector('input[name=search]');\n filterByText(searchInput.value);\n };\n\n const registerEventListeners = () => {\n // Add event listeners to the table boundingBox.\n const boundingBox = dataTable.get('boundingBox').getDOMNode();\n boundingBox.addEventListener('click', processAction);\n\n // Setup the search from handlers.\n const searchForm = document.querySelector(searchFormId);\n if (searchForm) {\n const searchButton = document.querySelector(searchFormId + ' button');\n searchButton.addEventListener('click', processSearchSubmission);\n }\n };\n\n return {\n filterByText,\n refreshTableData,\n registerEventListeners,\n };\n};\n\n/**\n * Setup the data table for the specified BBB instance.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {object} response The response from the data request\n * @returns {Promise}\n */\nconst setupDatatable = (tableId, searchFormId, response) => {\n if (!response) {\n return Promise.resolve();\n }\n\n if (!response.status) {\n // Something failed. Continue to show the plain output.\n return Promise.resolve();\n }\n\n const recordingData = response.tabledata;\n\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable');\n return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()])\n .then(([yuiInstance, strings]) => {\n // Here we use a custom formatter for date.\n // See https://clarle.github.io/yui3/yui/docs/api/classes/DataTable.BodyView.Formatters.html\n // Inspired from examples here: https://clarle.github.io/yui3/yui/docs/datatable/\n // Normally formatter have the prototype: (col) => (cell) => , see:\n // https://clarle.github.io/yui3/yui/docs/api/files/datatable_js_formatters.js.html#l100 .\n const dateCustomFormatter = () => (cell) => formatDate(recordingData.locale, cell.value);\n // Add the fetched strings to the YUI Instance.\n yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings});\n yuiInstance.DataTable.BodyView.Formatters.customDate = dateCustomFormatter;\n return yuiInstance;\n })\n .then(yuiInstance => {\n\n const tableData = getFormattedData(response);\n yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view', yuiInstance.DataTable.Paginator.View, [], {\n _modelChange: function(e) {\n var changed = e.changed,\n totalItems = (changed && changed.totalItems);\n if (totalItems) {\n this._updateControlsUI(e.target.get('page'));\n }\n }\n });\n return new yuiInstance.DataTable({\n paginatorView: \"RecordsPaginatorView\",\n width: \"1195px\",\n columns: recordingData.columns,\n data: tableData,\n rowsPerPage: 10,\n paginatorLocation: ['header', 'footer'],\n autoSync: true\n });\n })\n .then(dataTable => {\n dataTable.render(tableId);\n const {registerEventListeners} = getDataTableFunctions(\n tableId,\n searchFormId,\n dataTable);\n registerEventListeners();\n return dataTable;\n })\n .then(dataTable => {\n pendingPromise.resolve();\n return dataTable;\n });\n};\n\n/**\n * Initialise recordings code.\n *\n * @method init\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n */\nexport const init = (tableId, searchFormId) => {\n fetchRecordingData(tableId)\n .then(response => setupDatatable(tableId, searchFormId, response))\n .catch(displayException);\n};\n"],"file":"recordings.min.js"} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/src/recordings.js b/mod/bigbluebuttonbn/amd/src/recordings.js index c3cb769482a..6840e07f031 100644 --- a/mod/bigbluebuttonbn/amd/src/recordings.js +++ b/mod/bigbluebuttonbn/amd/src/recordings.js @@ -23,46 +23,39 @@ import * as repository from './repository'; import {exception as displayException} from 'core/notification'; -import {get_strings as getStrings} from 'core/str'; +import {prefetchStrings} from 'core/prefetch'; +import {get_string as getString, 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 stringsWithKeys = { + first: 'view_recording_yui_first', + prev: 'view_recording_yui_prev', + next: 'view_recording_yui_next', + last: 'view_recording_yui_last', + page: 'view_recording_yui_page', + go: 'view_recording_yui_go', + rows: 'view_recording_yui_rows', + all: 'view_recording_yui_all', +}; +// Load global strings. +prefetchStrings('bigbluebuttonbn', Object.entries(stringsWithKeys).map((entry) => entry[1])); const getStringsForYui = () => { - const stringMap = stringList.map(key => { + const stringMap = Object.keys(stringsWithKeys).map(key => { return { - key, - component: 'bigbluebuttonbn', + key: stringsWithKeys[key], + component: 'mod_bigbluebuttonbn', }; }); + // Return an object with the matching string keys (we want an object with {: ...}). return getStrings(stringMap) - .then(([first, prev, next, last, goToLabel, goToAction, perPage, showAll]) => { - return { - first, - prev, - next, - last, - goToLabel, - goToAction, - perPage, - showAll, - }; - }) - .catch(); + .then((stringArray) => Object.assign({}, ...Object.keys(stringsWithKeys).map( + (key, index) => ({[key]: stringArray[index]}))) + ).catch(); }; const getYuiInstance = lang => new Promise(resolve => { @@ -78,20 +71,18 @@ const getYuiInstance = lang => new Promise(resolve => { * Format the supplied date per the specified locale. * * @param {string} locale - * @param {array} dateList + * @param {number} date * @returns {array} */ -const formatDates = (locale, dateList) => dateList.map(row => { - const date = new Date(row.date); - row.date = date.toLocaleDateString(locale, { +const formatDate = (locale, date) => { + const realDate = new Date(date); + return realDate.toLocaleDateString(locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', }); - - return row; -}); +}; /** * Format response data for the table. @@ -101,9 +92,7 @@ const formatDates = (locale, dateList) => dateList.map(row => { */ const getFormattedData = response => { const recordingData = response.tabledata; - const rowData = JSON.parse(recordingData.data); - - return formatDates(recordingData.locale, rowData); + return JSON.parse(recordingData.data); }; const getTableNode = tableSelector => document.querySelector(tableSelector); @@ -215,11 +204,11 @@ const getDataTableFunctions = (tableId, searchFormId, dataTable) => { // Create the confirmation dialogue. return new Promise((resolve) => ModalFactory.create({ - title: Str.get_string('confirm'), + title: getString('confirm'), body: recordingConfirmationMessage(payload), type: ModalFactory.types.SAVE_CANCEL }).then(async(modal) => { - modal.setSaveButtonText(await Str.get_string('ok', 'moodle')); + modal.setSaveButtonText(await getString('ok', 'moodle')); // Handle save event. modal.getRoot().on(ModalEvents.save, () => { @@ -248,12 +237,12 @@ const getDataTableFunctions = (tableId, searchFormId, dataTable) => { const recordingConfirmationMessage = async(data) => { const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`); - const recordingType = await Str.get_string( + const recordingType = await getString( playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording', 'bigbluebuttonbn' ); - const confirmation = await Str.get_string(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType); + const confirmation = await getString(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType); if (data.action === 'import') { return confirmation; @@ -265,7 +254,7 @@ const getDataTableFunctions = (tableId, searchFormId, dataTable) => { return confirmation; } - const confirmationWarning = await Str.get_string( + const confirmationWarning = await getString( associatedLinkCount === 1 ? `view_recording_${data.action}_confirmation_warning_p` : `view_recording_${data.action}_confirmation_warning_s`, @@ -360,15 +349,22 @@ const setupDatatable = (tableId, searchFormId, response) => { const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable'); return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()]) .then(([yuiInstance, strings]) => { + // Here we use a custom formatter for date. + // See https://clarle.github.io/yui3/yui/docs/api/classes/DataTable.BodyView.Formatters.html + // Inspired from examples here: https://clarle.github.io/yui3/yui/docs/datatable/ + // Normally formatter have the prototype: (col) => (cell) => , see: + // https://clarle.github.io/yui3/yui/docs/api/files/datatable_js_formatters.js.html#l100 . + const dateCustomFormatter = () => (cell) => formatDate(recordingData.locale, cell.value); // Add the fetched strings to the YUI Instance. yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings}); - + yuiInstance.DataTable.BodyView.Formatters.customDate = dateCustomFormatter; return yuiInstance; }) .then(yuiInstance => { + const tableData = getFormattedData(response); - yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view',yuiInstance.DataTable.Paginator.View, [],{ - _modelChange : function (e) { + 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) { @@ -376,7 +372,7 @@ const setupDatatable = (tableId, searchFormId, response) => { } } }); - const dataTable = new yuiInstance.DataTable({ + return new yuiInstance.DataTable({ paginatorView: "RecordsPaginatorView", width: "1195px", columns: recordingData.columns, @@ -385,7 +381,6 @@ const setupDatatable = (tableId, searchFormId, response) => { paginatorLocation: ['header', 'footer'], autoSync: true }); - return dataTable; }) .then(dataTable => { dataTable.render(tableId); diff --git a/mod/bigbluebuttonbn/classes/external/get_recordings.php b/mod/bigbluebuttonbn/classes/external/get_recordings.php index b6b45df1902..d319840f84a 100644 --- a/mod/bigbluebuttonbn/classes/external/get_recordings.php +++ b/mod/bigbluebuttonbn/classes/external/get_recordings.php @@ -150,6 +150,7 @@ class get_recordings extends external_api { '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), + 'formatter' => new external_value(PARAM_ALPHANUMEXT, 'Formatter name', VALUE_OPTIONAL), ])), 'data' => new external_value(PARAM_RAW), // For now it will be json encoded. ], '', VALUE_OPTIONAL), diff --git a/mod/bigbluebuttonbn/classes/local/bigbluebutton/recordings/recording_data.php b/mod/bigbluebuttonbn/classes/local/bigbluebutton/recordings/recording_data.php index a4536fcfb53..6cd077d0c9b 100644 --- a/mod/bigbluebuttonbn/classes/local/bigbluebutton/recordings/recording_data.php +++ b/mod/bigbluebuttonbn/classes/local/bigbluebutton/recordings/recording_data.php @@ -129,7 +129,7 @@ class recording_data { 'sortable' => true, 'width' => '225px', 'type' => 'html', - 'allowHTML' => true, + 'formatter' => 'customDate', ]; $columns[] = [ 'key' => 'duration', diff --git a/mod/bigbluebuttonbn/tests/behat/room.feature b/mod/bigbluebuttonbn/tests/behat/room.feature new file mode 100644 index 00000000000..113bf665724 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/behat/room.feature @@ -0,0 +1,34 @@ +@mod @mod_bigbluebuttonbn @core_form @course +Feature: The recording can be managed through the room page and as a user I can interact with the table + + Background: Make sure that import recording is enabled and course, activities and recording exists + Given a BigBlueButton mock server is configured + And the following "courses" exist: + | fullname | shortname | category | + | Test Course 1 | C1 | 0 | + And the following "users" exist: + | username | firstname | lastname | email | + | user1 | User | 1 | user1@example.com | + And the following "activities" exist: + | activity | name | intro | course | idnumber | type | recordings_imported | + | bigbluebuttonbn | RoomRecordings | Test Room Recording description | C1 | bigbluebuttonbn1 | 0 | 0 | + And the following "mod_bigbluebuttonbn > meeting" exists: + | activity | RoomRecordings | + And the following "mod_bigbluebuttonbn > recordings" exist: + | bigbluebuttonbn | name | description | status | starttime | + | RoomRecordings | Recording 1 | Description 1 | 2 | 1619666194 | + | RoomRecordings | Recording 2 | Description 2 | 2 | 1639668194 | + | RoomRecordings | Recording 3 | Description 3 | 2 | 1629666194 | + | RoomRecordings | Recording 4 | Description 4 | 2 | 1649666194 | + + @javascript + Scenario: Recording should be sortable by date + Given I am on the "RoomRecordings" "bigbluebuttonbn activity" page logged in as admin + Then I click on "th[data-yui3-col-id='date'] .yui3-datatable-sort-indicator" "css_element" + Then "Recording 1" "text" should appear before "Recording 3" "text" + Then "Recording 3" "text" should appear before "Recording 2" "text" + Then "Recording 2" "text" should appear before "Recording 4" "text" + Then I click on "th[data-yui3-col-id='date'] .yui3-datatable-sort-indicator" "css_element" + Then "Recording 1" "text" should appear after "Recording 3" "text" + Then "Recording 3" "text" should appear after "Recording 2" "text" + Then "Recording 2" "text" should appear after "Recording 4" "text" diff --git a/mod/bigbluebuttonbn/tests/generator/lib.php b/mod/bigbluebuttonbn/tests/generator/lib.php index b11cc56425e..ee9c269bcc4 100644 --- a/mod/bigbluebuttonbn/tests/generator/lib.php +++ b/mod/bigbluebuttonbn/tests/generator/lib.php @@ -232,6 +232,7 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { * @throws moodle_exception */ protected function create_mockserver_recording(instance $instance, stdClass $recordingdata, array $data): string { + $now = time(); $mockdata = array_merge((array) $recordingdata, [ 'meetingID' => $instance->get_meeting_id(), 'meta' => [ @@ -246,7 +247,8 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { 'bbb-recording-tags' => $data['tags'] ?? '', ], ]); - + $mockdata['startTime'] = $data['starttime'] ?? $now; + $mockdata['endTime'] = $data['endtime'] ?? $mockdata['startTime'] + HOURSECS; $result = $this->send_mock_request('backoffice/createRecording', [], $mockdata); return (string) $result->recordID;