From 239a29a341c06f2b2d090e96488fba9e8f6ac3d7 Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Thu, 17 Sep 2020 13:26:38 +0000 Subject: [PATCH 01/51] switch sortName to name prop (shows list as names were entered) --- .../imports/ui/components/user-list/service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index 11b31ee10d..a0e43d1d91 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -179,7 +179,7 @@ const userFindSorting = { emojiTime: 1, role: 1, phoneUser: 1, - sortName: 1, + name: 1, userId: 1, }; @@ -516,7 +516,7 @@ export const getUserNamesLink = (docTitle, fnSortedLabel, lnSortedLabel) => { const mimeType = 'text/plain'; const userNamesObj = getUsers() .map((u) => { - const name = u.sortName.split(' '); + const name = u.name.split(' '); return ({ firstName: name[0], middleNames: name.length > 2 ? name.slice(1, name.length - 1) : null, From 6a139aa2249deaeec6533ed8383bbb29063d08e7 Mon Sep 17 00:00:00 2001 From: Joao Siebel Date: Fri, 18 Sep 2020 18:05:31 -0300 Subject: [PATCH 02/51] Fix last name sort function for save user name --- bigbluebutton-html5/imports/ui/components/user-list/service.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index a0e43d1d91..3b68c30387 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -495,6 +495,7 @@ const sortUsersByFirstName = (a, b) => { }; const sortUsersByLastName = (a, b) => { + if (!a.lastName && !b.lastName) return 0; if (a.lastName && !b.lastName) return -1; if (!a.lastName && b.lastName) return 1; From 00656ddf638d0a9e037aa19fb8a0648e88084ecf Mon Sep 17 00:00:00 2001 From: Anton Georgiev Date: Mon, 21 Sep 2020 14:31:32 +0000 Subject: [PATCH 03/51] Removed unused bowser.js --- bigbluebutton-html5/client/main.html | 1 - .../public/compatibility/bowser.js | 601 ------------------ 2 files changed, 602 deletions(-) delete mode 100755 bigbluebutton-html5/public/compatibility/bowser.js diff --git a/bigbluebutton-html5/client/main.html b/bigbluebutton-html5/client/main.html index fe5209cf13..3148595a76 100755 --- a/bigbluebutton-html5/client/main.html +++ b/bigbluebutton-html5/client/main.html @@ -66,7 +66,6 @@ with BigBlueButton; if not, see . }); - diff --git a/bigbluebutton-html5/public/compatibility/bowser.js b/bigbluebutton-html5/public/compatibility/bowser.js deleted file mode 100755 index 5b907f136f..0000000000 --- a/bigbluebutton-html5/public/compatibility/bowser.js +++ /dev/null @@ -1,601 +0,0 @@ -/*! - * Bowser - a browser detector - * https://github.com/ded/bowser - * MIT License | (c) Dustin Diaz 2015 - */ - -!function (root, name, definition) { - if (typeof module != 'undefined' && module.exports) module.exports = definition() - else if (typeof define == 'function' && define.amd) define(name, definition) - else root[name] = definition() -}(this, 'bowser', function () { - /** - * See useragents.js for examples of navigator.userAgent - */ - - var t = true - - function detect(ua) { - - function getFirstMatch(regex) { - var match = ua.match(regex); - return (match && match.length > 1 && match[1]) || ''; - } - - function getSecondMatch(regex) { - var match = ua.match(regex); - return (match && match.length > 1 && match[2]) || ''; - } - - var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() - , likeAndroid = /like android/i.test(ua) - , android = !likeAndroid && /android/i.test(ua) - , nexusMobile = /nexus\s*[0-6]\s*/i.test(ua) - , nexusTablet = !nexusMobile && /nexus\s*[0-9]+/i.test(ua) - , chromeos = /CrOS/.test(ua) - , silk = /silk/i.test(ua) - , sailfish = /sailfish/i.test(ua) - , tizen = /tizen/i.test(ua) - , webos = /(web|hpw)os/i.test(ua) - , windowsphone = /windows phone/i.test(ua) - , samsungBrowser = /SamsungBrowser/i.test(ua) - , windows = !windowsphone && /windows/i.test(ua) - , mac = !iosdevice && !silk && /macintosh/i.test(ua) - , linux = !android && !sailfish && !tizen && !webos && /linux/i.test(ua) - , edgeVersion = getFirstMatch(/edge\/(\d+(\.\d+)?)/i) - , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i) - , tablet = /tablet/i.test(ua) && !/tablet pc/i.test(ua) - , mobile = !tablet && /[^-]mobi/i.test(ua) - , xbox = /xbox/i.test(ua) - , result - - if (/opera/i.test(ua)) { - // an old Opera - result = { - name: 'Opera' - , opera: t - , version: versionIdentifier || getFirstMatch(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i) - } - } else if (/opr\/|opios/i.test(ua)) { - // a new Opera - result = { - name: 'Opera' - , opera: t - , version: getFirstMatch(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i) || versionIdentifier - } - } - else if (/SamsungBrowser/i.test(ua)) { - result = { - name: 'Samsung Internet for Android' - , samsungBrowser: t - , version: versionIdentifier || getFirstMatch(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i) - } - } - else if (/coast/i.test(ua)) { - result = { - name: 'Opera Coast' - , coast: t - , version: versionIdentifier || getFirstMatch(/(?:coast)[\s\/](\d+(\.\d+)?)/i) - } - } - else if (/yabrowser/i.test(ua)) { - result = { - name: 'Yandex Browser' - , yandexbrowser: t - , version: versionIdentifier || getFirstMatch(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i) - } - } - else if (/ucbrowser/i.test(ua)) { - result = { - name: 'UC Browser' - , ucbrowser: t - , version: getFirstMatch(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i) - } - } - else if (/mxios/i.test(ua)) { - result = { - name: 'Maxthon' - , maxthon: t - , version: getFirstMatch(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i) - } - } - else if (/epiphany/i.test(ua)) { - result = { - name: 'Epiphany' - , epiphany: t - , version: getFirstMatch(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i) - } - } - else if (/puffin/i.test(ua)) { - result = { - name: 'Puffin' - , puffin: t - , version: getFirstMatch(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i) - } - } - else if (/sleipnir/i.test(ua)) { - result = { - name: 'Sleipnir' - , sleipnir: t - , version: getFirstMatch(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i) - } - } - else if (/k-meleon/i.test(ua)) { - result = { - name: 'K-Meleon' - , kMeleon: t - , version: getFirstMatch(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i) - } - } - else if (windowsphone) { - result = { - name: 'Windows Phone' - , windowsphone: t - } - if (edgeVersion) { - result.msedge = t - result.version = edgeVersion - } - else { - result.msie = t - result.version = getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i) - } - } - else if (/msie|trident/i.test(ua)) { - result = { - name: 'Internet Explorer' - , msie: t - , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i) - } - } else if (chromeos) { - result = { - name: 'Chrome' - , chromeos: t - , chromeBook: t - , chrome: t - , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) - } - } else if (/chrome.+? edge/i.test(ua)) { - result = { - name: 'Microsoft Edge' - , msedge: t - , version: edgeVersion - } - } - else if (/vivaldi/i.test(ua)) { - result = { - name: 'Vivaldi' - , vivaldi: t - , version: getFirstMatch(/vivaldi\/(\d+(\.\d+)?)/i) || versionIdentifier - } - } - else if (sailfish) { - result = { - name: 'Sailfish' - , sailfish: t - , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i) - } - } - else if (/seamonkey\//i.test(ua)) { - result = { - name: 'SeaMonkey' - , seamonkey: t - , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i) - } - } - else if (/firefox|iceweasel|fxios/i.test(ua)) { - result = { - name: 'Firefox' - , firefox: t - , version: getFirstMatch(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i) - } - if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) { - result.firefoxos = t - } - } - else if (silk) { - result = { - name: 'Amazon Silk' - , silk: t - , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i) - } - } - else if (/phantom/i.test(ua)) { - result = { - name: 'PhantomJS' - , phantom: t - , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i) - } - } - else if (/slimerjs/i.test(ua)) { - result = { - name: 'SlimerJS' - , slimer: t - , version: getFirstMatch(/slimerjs\/(\d+(\.\d+)?)/i) - } - } - else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) { - result = { - name: 'BlackBerry' - , blackberry: t - , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i) - } - } - else if (webos) { - result = { - name: 'WebOS' - , webos: t - , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i) - }; - /touchpad\//i.test(ua) && (result.touchpad = t) - } - else if (/bada/i.test(ua)) { - result = { - name: 'Bada' - , bada: t - , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i) - }; - } - else if (tizen) { - result = { - name: 'Tizen' - , tizen: t - , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier - }; - } - else if (/qupzilla/i.test(ua)) { - result = { - name: 'QupZilla' - , qupzilla: t - , version: getFirstMatch(/(?:qupzilla)[\s\/](\d+(?:\.\d+)+)/i) || versionIdentifier - } - } - else if (/chromium/i.test(ua)) { - result = { - name: 'Chromium' - , chromium: t - , version: getFirstMatch(/(?:chromium)[\s\/](\d+(?:\.\d+)?)/i) || versionIdentifier - } - } - else if (/chrome|crios|crmo/i.test(ua)) { - result = { - name: 'Chrome' - , chrome: t - , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) - } - } - else if (android) { - result = { - name: 'Android' - , version: versionIdentifier - } - } - else if (/safari|applewebkit/i.test(ua)) { - result = { - name: 'Safari' - , safari: t - } - if (versionIdentifier) { - result.version = versionIdentifier - } - } - else if (iosdevice) { - result = { - name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod' - } - // WTF: version is not part of user agent in web apps - if (versionIdentifier) { - result.version = versionIdentifier - } - } - else if(/googlebot/i.test(ua)) { - result = { - name: 'Googlebot' - , googlebot: t - , version: getFirstMatch(/googlebot\/(\d+(\.\d+))/i) || versionIdentifier - } - } - else { - result = { - name: getFirstMatch(/^(.*)\/(.*) /), - version: getSecondMatch(/^(.*)\/(.*) /) - }; - } - - // set webkit or gecko flag for browsers based on these engines - if (!result.msedge && /(apple)?webkit/i.test(ua)) { - if (/(apple)?webkit\/537\.36/i.test(ua)) { - result.name = result.name || "Blink" - result.blink = t - } else { - result.name = result.name || "Webkit" - result.webkit = t - } - if (!result.version && versionIdentifier) { - result.version = versionIdentifier - } - } else if (!result.opera && /gecko\//i.test(ua)) { - result.name = result.name || "Gecko" - result.gecko = t - result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i) - } - - // set OS flags for platforms that have multiple browsers - if (!result.windowsphone && !result.msedge && (android || result.silk)) { - result.android = t - } else if (!result.windowsphone && !result.msedge && iosdevice) { - result[iosdevice] = t - result.ios = t - } else if (mac) { - result.mac = t - } else if (xbox) { - result.xbox = t - } else if (windows) { - result.windows = t - } else if (linux) { - result.linux = t - } - - function getWindowsVersion (s) { - switch (s) { - case 'NT': return 'NT' - case 'XP': return 'XP' - case 'NT 5.0': return '2000' - case 'NT 5.1': return 'XP' - case 'NT 5.2': return '2003' - case 'NT 6.0': return 'Vista' - case 'NT 6.1': return '7' - case 'NT 6.2': return '8' - case 'NT 6.3': return '8.1' - case 'NT 10.0': return '10' - default: return undefined - } - } - - // OS version extraction - var osVersion = ''; - if (result.windows) { - osVersion = getWindowsVersion(getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i)) - } else if (result.windowsphone) { - osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); - } else if (result.mac) { - osVersion = getFirstMatch(/Mac OS X (\d+([_\.\s]\d+)*)/i); - osVersion = osVersion.replace(/[_\s]/g, '.'); - } else if (iosdevice) { - osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i); - osVersion = osVersion.replace(/[_\s]/g, '.'); - } else if (android) { - osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i); - } else if (result.webos) { - osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i); - } else if (result.blackberry) { - osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i); - } else if (result.bada) { - osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i); - } else if (result.tizen) { - osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i); - } - if (osVersion) { - result.osversion = osVersion; - } - - // device type extraction - var osMajorVersion = !result.windows && osVersion.split('.')[0]; - if ( - tablet - || nexusTablet - || iosdevice == 'ipad' - || (android && (osMajorVersion == 3 || (osMajorVersion >= 4 && !mobile))) - || result.silk - ) { - result.tablet = t - } else if ( - mobile - || iosdevice == 'iphone' - || iosdevice == 'ipod' - || android - || nexusMobile - || result.blackberry - || result.webos - || result.bada - ) { - result.mobile = t - } - - // Graded Browser Support - // http://developer.yahoo.com/yui/articles/gbs - if (result.msedge || - (result.msie && result.version >= 10) || - (result.yandexbrowser && result.version >= 15) || - (result.vivaldi && result.version >= 1.0) || - (result.chrome && result.version >= 20) || - (result.samsungBrowser && result.version >= 4) || - (result.firefox && result.version >= 20.0) || - (result.safari && result.version >= 6) || - (result.opera && result.version >= 10.0) || - (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) || - (result.blackberry && result.version >= 10.1) - || (result.chromium && result.version >= 20) - ) { - result.a = t; - } - else if ((result.msie && result.version < 10) || - (result.chrome && result.version < 20) || - (result.firefox && result.version < 20.0) || - (result.safari && result.version < 6) || - (result.opera && result.version < 10.0) || - (result.ios && result.osversion && result.osversion.split(".")[0] < 6) - || (result.chromium && result.version < 20) - ) { - result.c = t - } else result.x = t - - return result - } - - var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent || '' : '') - - bowser.test = function (browserList) { - for (var i = 0; i < browserList.length; ++i) { - var browserItem = browserList[i]; - if (typeof browserItem=== 'string') { - if (browserItem in bowser) { - return true; - } - } - } - return false; - } - - /** - * Get version precisions count - * - * @example - * getVersionPrecision("1.10.3") // 3 - * - * @param {string} version - * @return {number} - */ - function getVersionPrecision(version) { - return version.split(".").length; - } - - /** - * Array::map polyfill - * - * @param {Array} arr - * @param {Function} iterator - * @return {Array} - */ - function map(arr, iterator) { - var result = [], i; - if (Array.prototype.map) { - return Array.prototype.map.call(arr, iterator); - } - for (i = 0; i < arr.length; i++) { - result.push(iterator(arr[i])); - } - return result; - } - - /** - * Calculate browser version weight - * - * @example - * compareVersions(['1.10.2.1', '1.8.2.1.90']) // 1 - * compareVersions(['1.010.2.1', '1.09.2.1.90']); // 1 - * compareVersions(['1.10.2.1', '1.10.2.1']); // 0 - * compareVersions(['1.10.2.1', '1.0800.2']); // -1 - * - * @param {Array} versions versions to compare - * @return {Number} comparison result - */ - function compareVersions(versions) { - // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2 - var precision = Math.max(getVersionPrecision(versions[0]), getVersionPrecision(versions[1])); - var chunks = map(versions, function (version) { - var delta = precision - getVersionPrecision(version); - - // 2) "9" -> "9.0" (for precision = 2) - version = version + new Array(delta + 1).join(".0"); - - // 3) "9.0" -> ["000000000"", "000000009"] - return map(version.split("."), function (chunk) { - return new Array(20 - chunk.length).join("0") + chunk; - }).reverse(); - }); - - // iterate in reverse order by reversed chunks array - while (--precision >= 0) { - // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true) - if (chunks[0][precision] > chunks[1][precision]) { - return 1; - } - else if (chunks[0][precision] === chunks[1][precision]) { - if (precision === 0) { - // all version chunks are same - return 0; - } - } - else { - return -1; - } - } - } - - /** - * Check if browser is unsupported - * - * @example - * bowser.isUnsupportedBrowser({ - * msie: "10", - * firefox: "23", - * chrome: "29", - * safari: "5.1", - * opera: "16", - * phantom: "534" - * }); - * - * @param {Object} minVersions map of minimal version to browser - * @param {Boolean} [strictMode = false] flag to return false if browser wasn't found in map - * @param {String} [ua] user agent string - * @return {Boolean} - */ - function isUnsupportedBrowser(minVersions, strictMode, ua) { - var _bowser = bowser; - - // make strictMode param optional with ua param usage - if (typeof strictMode === 'string') { - ua = strictMode; - strictMode = void(0); - } - - if (strictMode === void(0)) { - strictMode = false; - } - if (ua) { - _bowser = detect(ua); - } - - var version = "" + _bowser.version; - for (var browser in minVersions) { - if (minVersions.hasOwnProperty(browser)) { - if (_bowser[browser]) { - if (typeof minVersions[browser] !== 'string') { - throw new Error('Browser version in the minVersion map should be a string: ' + browser + ': ' + String(minVersions)); - } - - // browser version and min supported version. - return compareVersions([version, minVersions[browser]]) < 0; - } - } - } - - return strictMode; // not found - } - - /** - * Check if browser is supported - * - * @param {Object} minVersions map of minimal version to browser - * @param {Boolean} [strictMode = false] flag to return false if browser wasn't found in map - * @param {String} [ua] user agent string - * @return {Boolean} - */ - function check(minVersions, strictMode, ua) { - return !isUnsupportedBrowser(minVersions, strictMode, ua); - } - - bowser.isUnsupportedBrowser = isUnsupportedBrowser; - bowser.compareVersions = compareVersions; - bowser.check = check; - - /* - * Set our detect method to the main bowser object so we can - * reuse it to test other user agents. - * This is needed to implement future tests. - */ - bowser._detect = detect; - - return bowser -}); \ No newline at end of file From a3cf7cd98e3cc43b5502f9f333f3ccaf7bd250ff Mon Sep 17 00:00:00 2001 From: Joao Siebel Date: Mon, 21 Sep 2020 15:50:54 -0300 Subject: [PATCH 04/51] Prevent validateAuthToken spamming. If an ejected user tries to enter in the meeting using the current url html5 client keep trying to validate that user, but without success causing a validateAuthToken message spam until the connection times out. --- .../api/users/server/methods/validateAuthToken.js | 11 ++++++++++- bigbluebutton-html5/imports/ui/services/auth/index.js | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js index 7303caa2ba..cc1014c218 100644 --- a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js @@ -3,6 +3,7 @@ import RedisPubSub from '/imports/startup/server/redis'; import Logger from '/imports/startup/server/logger'; import pendingAuthenticationsStore from '../store/pendingAuthentications'; import BannedUsers from '../store/bannedUsers'; +import Users from '/imports/api/users'; export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) { const REDIS_CONFIG = Meteor.settings.private.redis; @@ -13,10 +14,18 @@ export default function validateAuthToken(meetingId, requesterUserId, requesterT if (externalId) { if (BannedUsers.has(meetingId, externalId)) { Logger.warn(`A banned user with extId ${externalId} tried to enter in meeting ${meetingId}`); - return; + return { invalid: true, reason: 'User has been banned.' }; } } + // Check if a removed user is trying to access the meeting using the same sessionToken + const isUserEjected = Users.findOne({ meetingId, authToken: requesterToken, ejected: true }); + + if (isUserEjected) { + Logger.warn(`An invalid sessionToken tried to validateAuthToken meetingId=${meetingId} authToken=${requesterToken}`); + return { invalid: true, reason: 'User has been ejected.' }; + } + // Store reference of methodInvocationObject ( to postpone the connection userId definition ) pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this); diff --git a/bigbluebutton-html5/imports/ui/services/auth/index.js b/bigbluebutton-html5/imports/ui/services/auth/index.js index b866da0b3b..02368a6e98 100755 --- a/bigbluebutton-html5/imports/ui/services/auth/index.js +++ b/bigbluebutton-html5/imports/ui/services/auth/index.js @@ -220,11 +220,11 @@ class Auth { const result = await makeCall('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID); - if (!result) { + if (result && result.invalid) { clearTimeout(validationTimeout); reject({ error: 401, - description: 'User has been banned.', + description: result.reason, }); return; } From a7e1623619e61c87672a4204b70077ecd1a6b137 Mon Sep 17 00:00:00 2001 From: Pedro Beschorner Marin Date: Fri, 18 Sep 2020 15:57:52 -0300 Subject: [PATCH 05/51] Option to prevent eject users by permission violation PermissionCheck verifies ejectOnViolation before eject an user --- .../org/bigbluebutton/SystemConfiguration.scala | 1 + .../bigbluebutton/core/apps/PermissionCheck.scala | 15 ++++++++++----- akka-bbb-apps/src/universal/conf/application.conf | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala index 981b75c0fe..caa2b12fa4 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala @@ -40,6 +40,7 @@ trait SystemConfiguration { lazy val maxNumberOfUndos = Try(config.getInt("sharedNotes.maxNumberOfUndos")).getOrElse(30) lazy val applyPermissionCheck = Try(config.getBoolean("apps.checkPermissions")).getOrElse(false) + lazy val ejectOnViolation = Try(config.getBoolean("apps.ejectOnViolation")).getOrElse(false) lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings") lazy val voiceConfRecordCodec = Try(config.getString("voiceConf.recordCodec")).getOrElse("wav") diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PermissionCheck.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PermissionCheck.scala index 760203a56d..e581de9ced 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PermissionCheck.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PermissionCheck.scala @@ -37,7 +37,7 @@ trait RightsManagementTrait extends SystemConfiguration { } } -object PermissionCheck { +object PermissionCheck extends SystemConfiguration { val MOD_LEVEL = 100 val AUTHED_LEVEL = 50 @@ -83,12 +83,17 @@ object PermissionCheck { def ejectUserForFailedPermission(meetingId: String, userId: String, reason: String, outGW: OutMsgRouter, liveMeeting: LiveMeeting): Unit = { - val ejectedBy = SystemUser.ID + if (ejectOnViolation) { + val ejectedBy = SystemUser.ID - UsersApp.ejectUserFromMeeting(outGW, liveMeeting, userId, ejectedBy, reason, EjectReasonCode.PERMISSION_FAILED, ban = false) + UsersApp.ejectUserFromMeeting(outGW, liveMeeting, userId, ejectedBy, reason, EjectReasonCode.PERMISSION_FAILED, ban = false) - // send a system message to force disconnection - Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason, outGW) + // send a system message to force disconnection + Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason, outGW) + } else { + // TODO: get this object a context so it can use the akka logging system + println(s"Skipping violation ejection of ${userId} trying to ${reason} in ${meetingId}") + } } def addOldPresenter(users: Users2x, userId: String): OldPresenter = { diff --git a/akka-bbb-apps/src/universal/conf/application.conf b/akka-bbb-apps/src/universal/conf/application.conf index 4bf02465c1..5d2568bbd1 100755 --- a/akka-bbb-apps/src/universal/conf/application.conf +++ b/akka-bbb-apps/src/universal/conf/application.conf @@ -76,6 +76,7 @@ services { apps { checkPermissions = true + ejectOnViolation = false endMeetingWhenNoMoreAuthedUsers = false endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2 } From a98c4b68b50accebb7bcc589feb0bc4305c1672a Mon Sep 17 00:00:00 2001 From: Pedro Beschorner Marin Date: Fri, 31 Jul 2020 16:09:48 -0300 Subject: [PATCH 06/51] Add secure tag to bbb-web JSESSIONID cookie Revert this to make whatever you want when running bbb-web without https --- bigbluebutton-web/grails-app/conf/application.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bigbluebutton-web/grails-app/conf/application.yml b/bigbluebutton-web/grails-app/conf/application.yml index b9147b63de..2f40d7d252 100644 --- a/bigbluebutton-web/grails-app/conf/application.yml +++ b/bigbluebutton-web/grails-app/conf/application.yml @@ -26,6 +26,11 @@ endpoints: jmx: enabled: true +server: + session: + cookie: + secure: true + --- grails: mime: From fc73e875977cd4c3cc715e2a67352bccc22f2836 Mon Sep 17 00:00:00 2001 From: Fred Dixon Date: Wed, 23 Sep 2020 14:43:47 -0400 Subject: [PATCH 07/51] Bump to 2.2.26 --- bigbluebutton-config/bigbluebutton-release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigbluebutton-config/bigbluebutton-release b/bigbluebutton-config/bigbluebutton-release index 700ffdf811..66650a8806 100644 --- a/bigbluebutton-config/bigbluebutton-release +++ b/bigbluebutton-config/bigbluebutton-release @@ -1 +1 @@ -BIGBLUEBUTTON_RELEASE=2.2.25 +BIGBLUEBUTTON_RELEASE=2.2.26 From 1268753519d9b6ea403bd632216c0199a5b9e20c Mon Sep 17 00:00:00 2001 From: Calvin Walton Date: Wed, 23 Sep 2020 16:16:57 -0400 Subject: [PATCH 08/51] Fix io deadlock in recording scripts process execution utilities The previous implementation of the BigBlueButton.execute method runs the process with separate stdout and stderr streams. It first reads all of the output from stdout, then reads all of the output from stderr. This can cause a deadlock if the process writes a lot of data to stderr. The IO buffer for stderr could fill, blocking progress. But since it hasn't closed stdout, the ruby script is still waiting on a read to stdout. Switch to an execution method (using IO.popen) that allows combining stdout and stderr into a single stream, eliminating the issue. --- .../core/lib/recordandplayback.rb | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb index 77a4cfcf73..59eb0b6346 100755 --- a/record-and-playback/core/lib/recordandplayback.rb +++ b/record-and-playback/core/lib/recordandplayback.rb @@ -37,6 +37,8 @@ require 'find' require 'rubygems' require 'net/http' require 'fnv' +require 'shellwords' +require 'English' module BigBlueButton class MissingDirectoryException < RuntimeError @@ -97,40 +99,28 @@ module BigBlueButton end def self.execute(command, fail_on_error = true) - status = ExecutionStatus.new - status.detailedStatus = Open4::popen4(command) do |pid, stdin, stdout, stderr| - BigBlueButton.logger.info("Executing: #{command}") - - status.output = stdout.readlines - BigBlueButton.logger.info("Output: #{Array(status.output).join} ") unless status.output.empty? - - status.errors = stderr.readlines - unless status.errors.empty? - BigBlueButton.logger.error("Error: stderr: #{Array(status.errors).join}") + BigBlueButton.logger.info("Executing: #{command.respond_to?(:to_ary) ? Shellwords.join(command) : command}") + IO.popen(command, err: %i[child out]) do |io| + io.each_line do |line| + BigBlueButton.logger.info(line.chomp) end end + status = $CHILD_STATUS + BigBlueButton.logger.info("Success?: #{status.success?}") BigBlueButton.logger.info("Process exited? #{status.exited?}") BigBlueButton.logger.info("Exit status: #{status.exitstatus}") - if status.success? == false and fail_on_error - raise "Execution failed" - end + raise 'Execution failed' if status.success? == false && fail_on_error + status end def self.exec_ret(*command) - BigBlueButton.logger.info "Executing: #{command.join(' ')}" - IO.popen([*command, :err => [:child, :out]]) do |io| - io.each_line do |line| - BigBlueButton.logger.info line.chomp - end - end - BigBlueButton.logger.info "Exit status: #{$?.exitstatus}" - return $?.exitstatus + execute(command, false).exitstatus end def self.exec_redirect_ret(outio, *command) - BigBlueButton.logger.info "Executing: #{command.join(' ')}" + BigBlueButton.logger.info "Executing: #{Shellwords.join(command)}" BigBlueButton.logger.info "Sending output to #{outio}" IO.pipe do |r, w| pid = spawn(*command, :out => outio, :err => w) From 1dfa2ff2a830e6814894f37c2768fd27a8f29dfd Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Thu, 24 Sep 2020 14:53:21 +0000 Subject: [PATCH 09/51] remove string concatenation from webcam profile formatted label --- .../imports/ui/components/video-preview/component.jsx | 2 +- bigbluebutton-html5/private/locales/en.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx b/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx index cc4c8f80b6..3d2bebf1f8 100755 --- a/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx @@ -563,7 +563,7 @@ class VideoPreview extends Component { return ( )})} diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index 3324f8c2a4..2b8b6a419f 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -545,9 +545,9 @@ "app.recording.stopDescription": "Are you sure you want to pause the recording? You can resume by selecting the record button again.", "app.videoPreview.cameraLabel": "Camera", "app.videoPreview.profileLabel": "Quality", - "app.videoPreview.quality.low": "Low", - "app.videoPreview.quality.medium": "Medium", - "app.videoPreview.quality.high": "High", + "app.videoPreview.quality.low": "Low quality", + "app.videoPreview.quality.medium": "Medium quality", + "app.videoPreview.quality.high": "High quality", "app.videoPreview.quality.hd": "High definition", "app.videoPreview.cancelLabel": "Cancel", "app.videoPreview.closeLabel": "Close", From 7536419930ef82605c94602bd26740e6969b2510 Mon Sep 17 00:00:00 2001 From: Anton Georgiev Date: Thu, 24 Sep 2020 14:45:52 -0400 Subject: [PATCH 10/51] add log for moderator forcing end meeting --- .../ui/components/end-meeting-confirmation/container.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/container.jsx b/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/container.jsx index 43075e881d..941d90507b 100644 --- a/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/container.jsx @@ -3,6 +3,7 @@ import { withTracker } from 'meteor/react-meteor-data'; import { withModalMounter } from '/imports/ui/components/modal/service'; import { makeCall } from '/imports/ui/services/api'; import EndMeetingComponent from './component'; +import logger from '/imports/startup/client/logger'; const EndMeetingContainer = props => ; @@ -12,6 +13,10 @@ export default withModalMounter(withTracker(({ mountModal }) => ({ }, endMeeting: () => { + logger.warn({ + logCode: 'moderator_forcing_end_meeting', + extraInfo: { logType: 'user_action' }, + }, 'this user clicked on EndMeeting and confirmed, removing everybody from the meeting'); makeCall('endMeeting'); mountModal(null); }, From 2fe0530d6e6d6191f95e490eee8e95edfcddcb84 Mon Sep 17 00:00:00 2001 From: Anton Georgiev Date: Fri, 25 Sep 2020 09:57:53 -0400 Subject: [PATCH 11/51] Pulled HTML5 locales and tested Sept 25, 2020 --- bigbluebutton-html5/private/locales/de.json | 13 +-- bigbluebutton-html5/private/locales/es.json | 3 - .../private/locales/fa_IR.json | 6 +- bigbluebutton-html5/private/locales/fr.json | 6 +- bigbluebutton-html5/private/locales/gl.json | 9 +- .../private/locales/it_IT.json | 88 +++++++++++-------- bigbluebutton-html5/private/locales/ja.json | 6 +- .../private/locales/ja_JP.json | 6 +- .../private/locales/ko_KR.json | 3 - bigbluebutton-html5/private/locales/nl.json | 9 +- bigbluebutton-html5/private/locales/pt.json | 9 +- .../private/locales/zh_CN.json | 6 +- .../private/locales/zh_TW.json | 3 - 13 files changed, 92 insertions(+), 75 deletions(-) diff --git a/bigbluebutton-html5/private/locales/de.json b/bigbluebutton-html5/private/locales/de.json index 4f3853472c..7ab38384cd 100644 --- a/bigbluebutton-html5/private/locales/de.json +++ b/bigbluebutton-html5/private/locales/de.json @@ -322,7 +322,7 @@ "app.settings.main.cancel.label.description": "Verwirft die Änderungen und schließt das Einstellungsmenü", "app.settings.main.save.label": "Speichern", "app.settings.main.save.label.description": "Speichert die Einstellungen und schließt das Einstellungsmenü", - "app.settings.dataSavingTab.label": "Datenvolumeneinsparung", + "app.settings.dataSavingTab.label": "Datensparmodus", "app.settings.dataSavingTab.webcam": "Webcams aktiviert", "app.settings.dataSavingTab.screenShare": "Bildschirmfreigabe aktiviert", "app.settings.dataSavingTab.description": "Um Datentransfervolumen zu sparen, können Sie hier einstellen, was angezeigt wird.", @@ -422,7 +422,7 @@ "app.audioModal.help.macNotAllowed": "Es scheint so, als ob Ihre Mac Systemeinstellungen die Mikrofonfreigabe blockiert. Öffnen Sie Systemeinstellungen > Sicherheit & Privatsphäre > Mikrofon und stellen Sie sicher, dass der von Ihnen verwendete Browser ausgewählt ist.", "app.audioModal.audioDialTitle": "Mit dem Telefon teilnehmen", "app.audioDial.audioDialDescription": "Anrufen", - "app.audioDial.audioDialConfrenceText": "und geben Sie die Konferenz-PIN-Nummer ein:", + "app.audioDial.audioDialConfrenceText": "und geben Sie die Konferenz-PIN ein:", "app.audioModal.autoplayBlockedDesc": "Wir benötigen Ihre Zustimmung für die Audiowiedergabe.", "app.audioModal.playAudio": "Audio wiedergeben", "app.audioModal.playAudio.arialabel" : "Audio wiedergeben", @@ -545,9 +545,9 @@ "app.recording.stopDescription": "Sind Sie sicher, dass Sie die Aufnahme pausieren wollen? Sie können Sie durch erneutes Drücken des Aufnahmeknopfs jederzeit fortsetzen.", "app.videoPreview.cameraLabel": "Kamera", "app.videoPreview.profileLabel": "Qualität", - "app.videoPreview.quality.low": "Niedrig", - "app.videoPreview.quality.medium": "Mittel", - "app.videoPreview.quality.high": "Hoch", + "app.videoPreview.quality.low": "Niedrige Qualität", + "app.videoPreview.quality.medium": "Mittlere Qualität", + "app.videoPreview.quality.high": "Hohe Qualität", "app.videoPreview.quality.hd": "High Definition", "app.videoPreview.cancelLabel": "Abbrechen", "app.videoPreview.closeLabel": "Schließen", @@ -562,6 +562,8 @@ "app.videoPreview.webcamNotFoundLabel": "Keine Webcam gefunden", "app.videoPreview.profileNotFoundLabel": "Keine unterstützten Kameraprofile", "app.video.joinVideo": "Webcam freigeben", + "app.video.connecting": "Webcamfreigabe wird gestartet ...", + "app.video.dataSaving": "Webcamfreigabe ist im Datensparmodus deaktiviert", "app.video.leaveVideo": "Webcamfreigabe beenden", "app.video.iceCandidateError": "Fehler beim Hinzufügen vom ice candidate", "app.video.iceConnectionStateError": "Verbindungsfehler (Fehler 1107)", @@ -587,6 +589,7 @@ "app.video.chromeExtensionErrorLink": "diese Chrome Erweiterung", "app.video.pagination.prevPage": "Vorherige Videos ansehen", "app.video.pagination.nextPage": "Nächste Videos ansehen", + "app.video.clientDisconnected": "Webcam kann aufgrund von Verbindungsproblemen nicht freigegeben werden", "app.fullscreenButton.label": "{0} zum Vollbild machen", "app.deskshare.iceConnectionStateError": "Verbindungsfehler beim Teilen des Bildschirms (Fehler 1108)", "app.sfu.mediaServerConnectionError2000": "Keine Verbindung zum Medienserver (Fehler 2000)", diff --git a/bigbluebutton-html5/private/locales/es.json b/bigbluebutton-html5/private/locales/es.json index ddc333c7f5..d646159418 100644 --- a/bigbluebutton-html5/private/locales/es.json +++ b/bigbluebutton-html5/private/locales/es.json @@ -545,9 +545,6 @@ "app.recording.stopDescription": "¿Está seguro de querer pausar la grabación? Puede continuarla volviendo a presionar el botón de grabación nuevamente.", "app.videoPreview.cameraLabel": "Webcam", "app.videoPreview.profileLabel": "Calidad", - "app.videoPreview.quality.low": "Baja", - "app.videoPreview.quality.medium": "Media", - "app.videoPreview.quality.high": "Alta", "app.videoPreview.quality.hd": "Alta definición", "app.videoPreview.cancelLabel": "Cancelar", "app.videoPreview.closeLabel": "Cerrar", diff --git a/bigbluebutton-html5/private/locales/fa_IR.json b/bigbluebutton-html5/private/locales/fa_IR.json index 624a1be894..b6badb6193 100644 --- a/bigbluebutton-html5/private/locales/fa_IR.json +++ b/bigbluebutton-html5/private/locales/fa_IR.json @@ -110,6 +110,9 @@ "app.userList.userOptions.enableNote": "یادداشت های اشتراکی فعال شد", "app.userList.userOptions.showUserList": "لیست کاربران در حال حاضر برای شرکت کنندگان قابل مشاهده است", "app.userList.userOptions.enableOnlyModeratorWebcam": "شما در حال حاضر میتوانید دوربین خود را به اشتراک بگذارید، همه تصویر شما را خواهند دید", + "app.userList.userOptions.savedNames.title": "فهرست کاربران حاضر در جلسه {0} در {1}", + "app.userList.userOptions.sortedFirstName.heading": "مرتب شده بر اساس نام:", + "app.userList.userOptions.sortedLastName.heading": "مرتب شده بر اساس نام خانوادگی:", "app.media.label": "صدا و تصویر", "app.media.autoplayAlertDesc": "دادن اجازه دسترسی", "app.media.screenshare.start": "اشتراک صفحه نمایش شروع شد", @@ -542,9 +545,6 @@ "app.recording.stopDescription": "آیا از توقف ضبط اطمینان دارید؟شما میتوانید مجددا عملیات ضبط را فعال نمایید", "app.videoPreview.cameraLabel": "دوربین", "app.videoPreview.profileLabel": "کیفیت", - "app.videoPreview.quality.low": "اندک", - "app.videoPreview.quality.medium": "متوسط", - "app.videoPreview.quality.high": "زیاد", "app.videoPreview.quality.hd": "خیلی زیاد", "app.videoPreview.cancelLabel": "لغو", "app.videoPreview.closeLabel": "بستن", diff --git a/bigbluebutton-html5/private/locales/fr.json b/bigbluebutton-html5/private/locales/fr.json index 97f565d9d2..adfafa1d40 100644 --- a/bigbluebutton-html5/private/locales/fr.json +++ b/bigbluebutton-html5/private/locales/fr.json @@ -545,9 +545,6 @@ "app.recording.stopDescription": "Êtes-vous sûr de vouloir mettre l'enregistrement en pause ? Vous pouvez reprendre en utilisant à nouveau le bouton d'enregistrement.", "app.videoPreview.cameraLabel": "Caméra", "app.videoPreview.profileLabel": "Qualité", - "app.videoPreview.quality.low": "Bas", - "app.videoPreview.quality.medium": "Moyen", - "app.videoPreview.quality.high": "Haut", "app.videoPreview.quality.hd": "Haute définition", "app.videoPreview.cancelLabel": "Annuler", "app.videoPreview.closeLabel": "Fermer", @@ -562,6 +559,8 @@ "app.videoPreview.webcamNotFoundLabel": "Webcam introuvable", "app.videoPreview.profileNotFoundLabel": "Profil de caméra non supporté", "app.video.joinVideo": "Partager webcam", + "app.video.connecting": "Le partage de la webcam démarre...", + "app.video.dataSaving": "Le partage de la webcam est désactivé en économies de données", "app.video.leaveVideo": "Arrêtez le partage de la webcam", "app.video.iceCandidateError": "Erreur lors de l'ajout du candidat ICE", "app.video.iceConnectionStateError": "Échec de connexion (erreur ICE 1107)", @@ -587,6 +586,7 @@ "app.video.chromeExtensionErrorLink": "cette extension Chrome", "app.video.pagination.prevPage": "Voir les vidéos précédentes", "app.video.pagination.nextPage": "Voir les vidéos suivantes", + "app.video.clientDisconnected": "La webcam ne peut pas être partagée à cause de problèmes de connexion", "app.fullscreenButton.label": "Rendre {0} plein écran", "app.deskshare.iceConnectionStateError": "Connexion échoué lors du partage d'écran (erreur ICE 1108)", "app.sfu.mediaServerConnectionError2000": "Impossible de se connecter au serveur multimédia (erreur 2000)", diff --git a/bigbluebutton-html5/private/locales/gl.json b/bigbluebutton-html5/private/locales/gl.json index 5f18b3307a..bf31837473 100644 --- a/bigbluebutton-html5/private/locales/gl.json +++ b/bigbluebutton-html5/private/locales/gl.json @@ -545,9 +545,9 @@ "app.recording.stopDescription": "Confirma que quere deter a gravación? Pode continuala premendo de novo o botón de gravación.", "app.videoPreview.cameraLabel": "Cámara web", "app.videoPreview.profileLabel": "Calidade", - "app.videoPreview.quality.low": "Baixa", - "app.videoPreview.quality.medium": "Media", - "app.videoPreview.quality.high": "Alta", + "app.videoPreview.quality.low": "Baixa calidade", + "app.videoPreview.quality.medium": "Calidade media", + "app.videoPreview.quality.high": "Alta calidade", "app.videoPreview.quality.hd": "Alta definición", "app.videoPreview.cancelLabel": "Cancelar", "app.videoPreview.closeLabel": "Pechar", @@ -562,6 +562,8 @@ "app.videoPreview.webcamNotFoundLabel": "Non se atopou a cámara web", "app.videoPreview.profileNotFoundLabel": "Non hai ningún perfil de cámara admitido", "app.video.joinVideo": "Compartir a cámara web", + "app.video.connecting": "Iniciando a compartición da cámara…", + "app.video.dataSaving": "O uso compartido de cámaras web está desactivado no Aforro de datos", "app.video.leaveVideo": "Deixar de compartir a cámara web", "app.video.iceCandidateError": "Produciuse un erro ao engadir un candidato ICE", "app.video.iceConnectionStateError": "Produciuse un fallo de conexión (erro ICE 1107)", @@ -587,6 +589,7 @@ "app.video.chromeExtensionErrorLink": "esta extensión de Chrome", "app.video.pagination.prevPage": "Ver os vídeos anteriores", "app.video.pagination.nextPage": "Ver os seguintes vídeos", + "app.video.clientDisconnected": "Non é posíbel compartir a cámara web por mor de problemas de conexión", "app.fullscreenButton.label": "Poñer {0} a pantalla completa", "app.deskshare.iceConnectionStateError": "Produciuse un fallo de conexión ao compartir a pantalla (erro ICE 1108)", "app.sfu.mediaServerConnectionError2000": "Non foi posíbel conectar co servidor multimedia (erro 2000)", diff --git a/bigbluebutton-html5/private/locales/it_IT.json b/bigbluebutton-html5/private/locales/it_IT.json index 3ad0875862..24d5986c00 100644 --- a/bigbluebutton-html5/private/locales/it_IT.json +++ b/bigbluebutton-html5/private/locales/it_IT.json @@ -76,7 +76,7 @@ "app.userList.menu.removeConfirmation.label": "Rimuovi utente ({0})", "app.userlist.menu.removeConfirmation.desc": "Impedire a questo utente di riconnettersi alla sessione.", "app.userList.menu.muteUserAudio.label": "Silenzia utente", - "app.userList.menu.unmuteUserAudio.label": "Riattiva utente", + "app.userList.menu.unmuteUserAudio.label": "Riattiva microfono utente", "app.userList.userAriaLabel": "{0} {1} {2} Stato {3}", "app.userList.menu.promoteUser.label": "Promuovi a moderatore", "app.userList.menu.demoteUser.label": "Converti in spettatore", @@ -102,7 +102,7 @@ "app.userList.userOptions.disableNote": "Ora le note condivise sono bloccate", "app.userList.userOptions.hideUserList": "La lista utenti è ora nascosta agli utenti", "app.userList.userOptions.webcamsOnlyForModerator": "Solo i moderatori possono vedere il video degli spettatori (impostazioni)", - "app.userList.content.participants.options.clearedStatus": "Ripristinati tutti gli stati", + "app.userList.content.participants.options.clearedStatus": "Ripristinati gli stati di tutti gli utenti", "app.userList.userOptions.enableCam": "La webcam degli utenti è ora abilitata", "app.userList.userOptions.enableMic": "Il microfono degli utenti è ora abilitato", "app.userList.userOptions.enablePrivChat": "Le chat private sono abilitate", @@ -110,6 +110,9 @@ "app.userList.userOptions.enableNote": "Le note condivise sono abilitate", "app.userList.userOptions.showUserList": "La lista utenti è ora visibile agli utenti", "app.userList.userOptions.enableOnlyModeratorWebcam": "Puoi attivare la tua webcam adesso, tutti ti vedranno", + "app.userList.userOptions.savedNames.title": "Lista degli utenti del meeting {0} al {1}", + "app.userList.userOptions.sortedFirstName.heading": "Ordinati per nome: ", + "app.userList.userOptions.sortedLastName.heading": "Ordinati per cognome: ", "app.media.label": "Audio/Video", "app.media.autoplayAlertDesc": "Premetti l'accesso", "app.media.screenshare.start": "Condivisione schermo avviata", @@ -127,9 +130,9 @@ "app.meeting.meetingTimeHasEnded": "Tempo scaduto. Il meeting terminerà a breve", "app.meeting.endedMessage": "Verrai riportato alla pagina iniziale", "app.meeting.alertMeetingEndsUnderMinutesSingular": "Il meeting termino tra un minuto.", - "app.meeting.alertMeetingEndsUnderMinutesPlural": "Il meeting termino tra {0} minuti.", - "app.meeting.alertBreakoutEndsUnderMinutesPlural": "La stanza breakout termina tra {0} minuti.", - "app.meeting.alertBreakoutEndsUnderMinutesSingular": "La stanza breakout termina tra uno minuto.", + "app.meeting.alertMeetingEndsUnderMinutesPlural": "Il meeting termina tra {0} minuti.", + "app.meeting.alertBreakoutEndsUnderMinutesPlural": "La stanza separata termina tra {0} minuti.", + "app.meeting.alertBreakoutEndsUnderMinutesSingular": "La stanza separata termina tra un minuto.", "app.presentation.hide": "Nascondi presentazione", "app.presentation.notificationLabel": "Presentazione corrente", "app.presentation.slideContent": "Contenuto della slide", @@ -141,7 +144,7 @@ "app.presentation.presentationToolbar.selectLabel": "Seleziona slide", "app.presentation.presentationToolbar.prevSlideLabel": "Slide precedente", "app.presentation.presentationToolbar.prevSlideDesc": "Spostati alla slide precedente", - "app.presentation.presentationToolbar.nextSlideLabel": "Prossima slide", + "app.presentation.presentationToolbar.nextSlideLabel": "Slide successiva", "app.presentation.presentationToolbar.nextSlideDesc": "Spostati sulla prossima slide", "app.presentation.presentationToolbar.skipSlideLabel": "Salta slide", "app.presentation.presentationToolbar.skipSlideDesc": "Spostati ad una slide specifica", @@ -173,20 +176,22 @@ "app.presentationUploder.browseImagesLabel": "o sfoglia/acquisisci immagini", "app.presentationUploder.fileToUpload": "Da caricare...", "app.presentationUploder.currentBadge": "Attuale", - "app.presentationUploder.rejectedError": "Il file selezionato è stato rifiutato. controllare il tipo di file.", + "app.presentationUploder.rejectedError": "Il file selezionato è stato rifiutato. Controlla il tipo di file.", "app.presentationUploder.upload.progress": "Caricamento ({0}%)", "app.presentationUploder.upload.413": "Il file è troppo grande. Per favore dividilo in più file.", + "app.presentationUploder.upload.408": "Richiesta per il toked di upload scaduta.", "app.presentationUploder.upload.404": "404: Token di upload non valido", + "app.presentationUploder.upload.401": "Richiesta per il token di upload presentazione fallita.", "app.presentationUploder.conversion.conversionProcessingSlides": "Elaborazione pagina {0} di {1}", "app.presentationUploder.conversion.genericConversionStatus": "Conversione file...", "app.presentationUploder.conversion.generatingThumbnail": "Elaborazione anteprima...", "app.presentationUploder.conversion.generatedSlides": "Slide elaborate...", "app.presentationUploder.conversion.generatingSvg": "Elaborazione immagine SVG...", - "app.presentationUploder.conversion.pageCountExceeded": "Numero massimo di pagine ecceduto. Suddividi il file in documenti multipli.", - "app.presentationUploder.conversion.officeDocConversionInvalid": "Conversione del documento office fallita. È possibile provare a caricare un PDF.", - "app.presentationUploder.conversion.officeDocConversionFailed": "Conversione del documento office fallita. È possibile provare a caricare un PDF.", + "app.presentationUploder.conversion.pageCountExceeded": "Numero massimo di pagine superato. Suddividi il file in documenti multipli.", + "app.presentationUploder.conversion.officeDocConversionInvalid": "Conversione del documento office fallita. In alternativa prova a caricare un PDF.", + "app.presentationUploder.conversion.officeDocConversionFailed": "Conversione del documento office fallita. In alternativa prova a caricare un PDF.", "app.presentationUploder.conversion.pdfHasBigPage": "Non è stato possibile convertire il file PDF, prova ad ottimizzarlo", - "app.presentationUploder.conversion.timeout": "Maledizione, la conversione del file ci sta impiegando troppo", + "app.presentationUploder.conversion.timeout": "Ops, la conversione del file ha impiegato troppo tempo", "app.presentationUploder.conversion.pageCountFailed": "Impossibile determinare il numero di pagine.", "app.presentationUploder.isDownloadableLabel": "Non permettere il download della presentazione", "app.presentationUploder.isNotDownloadableLabel": "Permetti il download della presentazione", @@ -235,7 +240,7 @@ "app.polling.pollingTitle": "Opzioni sondaggio", "app.polling.pollAnswerLabel": "Risposte {0}", "app.polling.pollAnswerDesc": "Seleziona questa opzione per votare {0}", - "app.failedMessage": "Tragedia! ci sono problemi di connessione con il server.", + "app.failedMessage": "Spiacente, ci sono problemi di connessione con il server.", "app.downloadPresentationButton.label": "Scarica la presentazione originale", "app.connectingMessage": "Connessione...", "app.waitingMessage": "Disconnesso. Prossimo tentativo in {0} secondi...", @@ -248,14 +253,14 @@ "app.navBar.settingsDropdown.exitFullscreenLabel": "Esci da schermo intero", "app.navBar.settingsDropdown.fullscreenDesc": "Menu impostazioni a schermo intero", "app.navBar.settingsDropdown.settingsDesc": "Cambia le impostazioni generali", - "app.navBar.settingsDropdown.aboutDesc": "Visualizza informazioni sul software", + "app.navBar.settingsDropdown.aboutDesc": "Visualizza informazioni sul software client", "app.navBar.settingsDropdown.leaveSessionDesc": "Abbandona il meeting", "app.navBar.settingsDropdown.exitFullscreenDesc": "Esci dalla modalità a schermo intero", "app.navBar.settingsDropdown.hotkeysLabel": "Scorciatoie da tastiera", "app.navBar.settingsDropdown.hotkeysDesc": "Lista delle scorciatoie da tastiera disponibili", "app.navBar.settingsDropdown.helpLabel": "Aiuto", "app.navBar.settingsDropdown.helpDesc": "Vai alle video guide (apre una nuova tab)", - "app.navBar.settingsDropdown.endMeetingDesc": "Termina il meeting", + "app.navBar.settingsDropdown.endMeetingDesc": "Termina il meeting corrente", "app.navBar.settingsDropdown.endMeetingLabel": "Fine Meeting", "app.navBar.userListToggleBtnLabel": "Attiva/Disattiva lista utenti", "app.navBar.toggleUserList.ariaLabel": "Attiva/disattiva Utenti e messaggi", @@ -279,7 +284,7 @@ "app.about.dismissDesc": "Chiudi finestra informazioni", "app.actionsBar.changeStatusLabel": "Cambia stato", "app.actionsBar.muteLabel": "Silenzia", - "app.actionsBar.unmuteLabel": "Riattiva", + "app.actionsBar.unmuteLabel": "Riattiva microfono", "app.actionsBar.camOffLabel": "Disattiva webcam", "app.actionsBar.raiseLabel": "Alza la mano", "app.actionsBar.label": "Barra delle azioni", @@ -287,21 +292,21 @@ "app.actionsBar.actionsDropdown.restorePresentationDesc": "Ripristina la presentazione dopo che è stata chiusa", "app.screenshare.screenShareLabel" : "Condivisione schermo", "app.submenu.application.applicationSectionTitle": "Applicazione", - "app.submenu.application.animationsLabel": "Animazione", + "app.submenu.application.animationsLabel": "Animazioni", "app.submenu.application.audioAlertLabel": "Notifiche audio per la Chat", - "app.submenu.application.pushAlertLabel": "Finestra di notifica Chat", + "app.submenu.application.pushAlertLabel": "Notifica popup per la Chat", "app.submenu.application.userJoinAudioAlertLabel": "Notifica audio per ingresso utente", - "app.submenu.application.userJoinPushAlertLabel": "Notifica video per ingresso utente", - "app.submenu.application.fontSizeControlLabel": "Dimensione carattere", - "app.submenu.application.increaseFontBtnLabel": "Aumenta dimensione carattere", - "app.submenu.application.decreaseFontBtnLabel": "Diminuisci dimensione carattere", + "app.submenu.application.userJoinPushAlertLabel": "Notifica popup per ingresso utente", + "app.submenu.application.fontSizeControlLabel": "Dimensione caratteri", + "app.submenu.application.increaseFontBtnLabel": "Aumenta dimensione caratteri", + "app.submenu.application.decreaseFontBtnLabel": "Diminuisci dimensione caratteri", "app.submenu.application.currentSize": "attualmente {0}", "app.submenu.application.languageLabel": "Lingua dell'interfaccia", - "app.submenu.application.languageOptionLabel": "Scegli linguaggio", + "app.submenu.application.languageOptionLabel": "Scegli lingua", "app.submenu.application.noLocaleOptionLabel": "Non ci sono localizzazioni attive", "app.submenu.audio.micSourceLabel": "Sorgente ingresso audio", "app.submenu.audio.speakerSourceLabel": "Sorgente uscita audio", - "app.submenu.audio.streamVolumeLabel": "Volume del tuo streaming audio", + "app.submenu.audio.streamVolumeLabel": "Volume del tuo stream audio", "app.submenu.video.title": "Video", "app.submenu.video.videoSourceLabel": "Visualizza sorgente", "app.submenu.video.videoOptionLabel": "Seleziona visualizzazione sorgente", @@ -340,8 +345,8 @@ "app.actionsBar.actionsDropdown.pollBtnLabel": "Avvia un sondaggio", "app.actionsBar.actionsDropdown.pollBtnDesc": "Attiva/Disattiva pannello sondaggi", "app.actionsBar.actionsDropdown.saveUserNames": "Salva lista nomi utente", - "app.actionsBar.actionsDropdown.createBreakoutRoom": "Crea una stanza breakout", - "app.actionsBar.actionsDropdown.createBreakoutRoomDesc": "crea un breakout per dividere il meeting in corso", + "app.actionsBar.actionsDropdown.createBreakoutRoom": "Crea stanze separate", + "app.actionsBar.actionsDropdown.createBreakoutRoomDesc": "crea stanze separate per dividere il meeting in corso", "app.actionsBar.actionsDropdown.captionsLabel": "Scrivi sottotitoli", "app.actionsBar.actionsDropdown.captionsDesc": "Attiva/Disattiva pannello sottotitoli", "app.actionsBar.actionsDropdown.takePresenter": "Diventa Presentatore", @@ -388,9 +393,9 @@ "app.audioNotificaion.reconnectingAsListenOnly": "Il microfono è stato bloccato per gli spettatori, puoi collegarti solo come ascoltatore", "app.breakoutJoinConfirmation.title": "Entra nella Stanza Separata ", "app.breakoutJoinConfirmation.message": "Vuoi partecipare", - "app.breakoutJoinConfirmation.confirmDesc": "Partecipa con te nella Stanza Separata", + "app.breakoutJoinConfirmation.confirmDesc": "Partecipa con te alla Stanza Separata", "app.breakoutJoinConfirmation.dismissLabel": "Annulla", - "app.breakoutJoinConfirmation.dismissDesc": "Chiudi e non accettare ingressi nella Stanza Separata", + "app.breakoutJoinConfirmation.dismissDesc": "Chiudi e rifiuta di entrare nella Stanza Separata", "app.breakoutJoinConfirmation.freeJoinMessage": "Scegli una Stanza Separata in cui entrare", "app.breakoutTimeRemainingMessage": "Tempo rimasto per la Stanza Separata: {0}", "app.breakoutWillCloseMessage": "Tempo scaduto. La Stanza Separata verrà chiusa a breve", @@ -409,7 +414,7 @@ "app.audioModal.no": "No", "app.audioModal.yes.arialabel" : "Il test audio si sente", "app.audioModal.no.arialabel" : "Il test audio non si sente", - "app.audioModal.echoTestTitle": "Questo è un test audio privato. Pronuncia qualche parola. Riesci a sentire il test audio?", + "app.audioModal.echoTestTitle": "Questo è un test audio privato. Pronuncia qualche parola. \nRiesci a sentire il test audio?", "app.audioModal.settingsTitle": "Cambia le impostazioni audio", "app.audioModal.helpTitle": "C'è stato un problema con i tuoi dispositivi audio/video", "app.audioModal.helpText": "Hai consentito l'accesso al tuo microfono? Una finestra dovrebbe apparire quando provi a partecipare, chiedendo il permesso di accedere ai dispositivi audio/video. Se non è così, prova a cambiare i permessi del microfono nelle impostazioni del browser.", @@ -487,7 +492,7 @@ "app.userList.guest.pendingGuestAlert": "ha effettuato l'accesso alla sessione ed è in attesa di approvazione.", "app.userList.guest.rememberChoice": "Ricorda la scelta", "app.user-info.title": "Cerca directory", - "app.toast.breakoutRoomEnded": "La Stanza Separata è terminata. Torna al meeting.", + "app.toast.breakoutRoomEnded": "La Stanza Separata è terminata. Torna al meeting e riattiva l'audio.", "app.toast.chat.public": "Nuovo messaggio nella chat pubblica", "app.toast.chat.private": "Nuovo messaggio privato", "app.toast.chat.system": "Sistema", @@ -506,16 +511,16 @@ "app.shortcut-help.functionLabel": "Funzione", "app.shortcut-help.closeLabel": "Chiudi", "app.shortcut-help.closeDesc": "Chidui menu scorciatoie da tastiera", - "app.shortcut-help.openOptions": "Apre la finestra delle opzioni", + "app.shortcut-help.openOptions": "Apre il menù Opzioni", "app.shortcut-help.toggleUserList": "Mostra/Nascondi la lista utenti", - "app.shortcut-help.toggleMute": "Attiva/Disattiva il silenziamento utente", + "app.shortcut-help.toggleMute": "Attiva/Disattiva il microfono", "app.shortcut-help.togglePublicChat": "Visualizza/Nascondi chat pubblica (la lista utenti deve essere visibile)", "app.shortcut-help.hidePrivateChat": "Nascondi chat privata", "app.shortcut-help.closePrivateChat": "Chiudi chat privata", - "app.shortcut-help.openActions": "Apre il menu strumenti", + "app.shortcut-help.openActions": "Apri il menu Azioni", "app.shortcut-help.openStatus": "Apre il menu di stato", - "app.shortcut-help.togglePan": "Attiva lo strumento Panoramico (Presentatore)", - "app.shortcut-help.nextSlideDesc": "Slide seguente (Presentatore)", + "app.shortcut-help.togglePan": "Attiva lo strumento Panoramica (Presentatore)", + "app.shortcut-help.nextSlideDesc": "Slide successiva (Presentatore)", "app.shortcut-help.previousSlideDesc": "Slide precedente (Presentatore)", "app.lock-viewers.title": "Blocca spettatori", "app.lock-viewers.description": "Queste opzioni ti danno la possibilità di disattivare specifiche funzioni agli utenti", @@ -539,16 +544,21 @@ "app.recording.stopDescription": "Vuoi interrompere la registrazione? Puoi riavviarla in seguito con il bottone di registrazione", "app.videoPreview.cameraLabel": "Webcam", "app.videoPreview.profileLabel": "Qualità", + "app.videoPreview.quality.hd": "Alta definizione", "app.videoPreview.cancelLabel": "Annulla", "app.videoPreview.closeLabel": "Chiudi", "app.videoPreview.findingWebcamsLabel": "Ricerca webcams", "app.videoPreview.startSharingLabel": "Avvia condivisione", + "app.videoPreview.stopSharingLabel": "Interrompi condivisione", + "app.videoPreview.sharedCameraLabel": "Questa webcam è già condivisa", "app.videoPreview.webcamOptionLabel": "Scegli webcam", "app.videoPreview.webcamPreviewLabel": "Anteprima webcam", "app.videoPreview.webcamSettingsTitle": "Impostazioni webcam", "app.videoPreview.webcamNotFoundLabel": "Webcam non trovata", "app.videoPreview.profileNotFoundLabel": "Webcam non supportata", "app.video.joinVideo": "Mostra webcam", + "app.video.connecting": "Sto avviando la condivisione webcam ...", + "app.video.dataSaving": "La condivisione webcam è disattivata in Risparmio Dati", "app.video.leaveVideo": "Interrompi webcam", "app.video.iceCandidateError": "Errore nell'aggiunta del candidato ICE", "app.video.iceConnectionStateError": "Connessione fallita (errore ICE 1107)", @@ -572,6 +582,9 @@ "app.video.videoMenuDesc": "Apre il menu video", "app.video.chromeExtensionError": "Devi installare", "app.video.chromeExtensionErrorLink": "questa estensione per Chrome", + "app.video.pagination.prevPage": "Vedi i video precedenti", + "app.video.pagination.nextPage": "Vedi i video successivi", + "app.video.clientDisconnected": "La webcam non può essere condivisa a causa di problemi di connessione", "app.fullscreenButton.label": "Metti {0} a schermo intero", "app.deskshare.iceConnectionStateError": "Connessione fallita nella condivisione dello schermo (errore ICE 1108)", "app.sfu.mediaServerConnectionError2000": "Impossibile connettersi al server dei media (errore 2000)", @@ -585,6 +598,7 @@ "app.sfu.noAvailableCodec2203": "Il server non ha trovato un codec appropriato (error 2203)", "app.meeting.endNotification.ok.label": "OK", "app.whiteboard.annotations.poll": "I risultati del sondaggio sono stati pubblicati", + "app.whiteboard.annotations.pollResult": "Risultato Sondaggio", "app.whiteboard.toolbar.tools": "Strumenti", "app.whiteboard.toolbar.tools.hand": "Panoramica", "app.whiteboard.toolbar.tools.pencil": "Matita", @@ -621,8 +635,8 @@ "app.feedback.sendFeedbackDesc": "Invia una recensione e lascia il Meeting", "app.videoDock.webcamFocusLabel": "Metti in evidenza", "app.videoDock.webcamFocusDesc": "Metti in evidenza la webcam selezionata", - "app.videoDock.webcamUnfocusLabel": "Disattiva webcam", - "app.videoDock.webcamUnfocusDesc": "Disattiva la webcam selezionata", + "app.videoDock.webcamUnfocusLabel": "Ripristina webcam", + "app.videoDock.webcamUnfocusDesc": "Ripristina la webcam selezionata", "app.videoDock.autoplayBlockedDesc": "Abbiamo bisogno del tuo permesso per mostrarti le webcam degli utenti.", "app.videoDock.autoplayAllowLabel": "Visualizza webcam", "app.invitation.title": "Invito alla Stanza Separata", @@ -653,7 +667,7 @@ "app.createBreakoutRoom.addParticipantLabel": "+ Aggiungi partecipante", "app.createBreakoutRoom.freeJoin": "Permetti agli utenti di entrare in una Stanza Separata", "app.createBreakoutRoom.leastOneWarnBreakout": "Devi posizionare almeno un utente nella Stanza Separata", - "app.createBreakoutRoom.modalDesc": "Consiglio: Per assegnare un utente ad una stanza trascina il suo nome sulla stanza prescelta", + "app.createBreakoutRoom.modalDesc": "Consiglio: Per assegnare un utente ad una stanza trascina il suo nome su una specifica stanza separata.", "app.createBreakoutRoom.roomTime": "{0} minuti", "app.createBreakoutRoom.numberOfRoomsError": "Il numero di stanze non è valido.", "app.externalVideo.start": "Condividi un nuovo video", diff --git a/bigbluebutton-html5/private/locales/ja.json b/bigbluebutton-html5/private/locales/ja.json index 0d062441ab..68526611a7 100644 --- a/bigbluebutton-html5/private/locales/ja.json +++ b/bigbluebutton-html5/private/locales/ja.json @@ -545,9 +545,6 @@ "app.recording.stopDescription": "録画を止めますか?もう一度ボタンをクリックするとまた再開します", "app.videoPreview.cameraLabel": "カメラ", "app.videoPreview.profileLabel": "品質", - "app.videoPreview.quality.low": "低", - "app.videoPreview.quality.medium": "中", - "app.videoPreview.quality.high": "高", "app.videoPreview.quality.hd": "HD", "app.videoPreview.cancelLabel": "キャンセル", "app.videoPreview.closeLabel": "閉じる", @@ -562,6 +559,8 @@ "app.videoPreview.webcamNotFoundLabel": "ウェブカメラが見つかりませんでした", "app.videoPreview.profileNotFoundLabel": "カメラプロフィールがサポートされていません", "app.video.joinVideo": "ウェブカメラを共有", + "app.video.connecting": "ウェブカメラの共有を始めます...", + "app.video.dataSaving": "通信量を減らすため、ウェブカメラの共有は無効になっています", "app.video.leaveVideo": "ウェブカメラの共有を終了", "app.video.iceCandidateError": "ICE候補の追加に失敗しました", "app.video.iceConnectionStateError": "接続失敗 (ICE error 1107)", @@ -587,6 +586,7 @@ "app.video.chromeExtensionErrorLink": "このChromeの拡張機能", "app.video.pagination.prevPage": "前のビデオを見る", "app.video.pagination.nextPage": "次のビデオを見る", + "app.video.clientDisconnected": "接続に問題が生じたためウェブカメラは共有できません", "app.fullscreenButton.label": "{0}を全画面に切り替える", "app.deskshare.iceConnectionStateError": "画面共有の接続に失敗しました (ICE error 1108)", "app.sfu.mediaServerConnectionError2000": "メディアサーバーに接続できません (error 2000)", diff --git a/bigbluebutton-html5/private/locales/ja_JP.json b/bigbluebutton-html5/private/locales/ja_JP.json index 3844a136c0..b37a586d59 100644 --- a/bigbluebutton-html5/private/locales/ja_JP.json +++ b/bigbluebutton-html5/private/locales/ja_JP.json @@ -545,9 +545,6 @@ "app.recording.stopDescription": "録画を止めますか? 再度ボタンをクリックすると再開します", "app.videoPreview.cameraLabel": "カメラ", "app.videoPreview.profileLabel": "品質", - "app.videoPreview.quality.low": "低", - "app.videoPreview.quality.medium": "中", - "app.videoPreview.quality.high": "高", "app.videoPreview.quality.hd": "HD", "app.videoPreview.cancelLabel": "キャンセル", "app.videoPreview.closeLabel": "閉じる", @@ -562,6 +559,8 @@ "app.videoPreview.webcamNotFoundLabel": "ウェブカメラが見つかりませんでした", "app.videoPreview.profileNotFoundLabel": "カメラプロフィールがサポートされていません", "app.video.joinVideo": "ウェブカメラを共有", + "app.video.connecting": "ウェブカメラの共有を始めます...", + "app.video.dataSaving": "通信量を減らすため、ウェブカメラの共有は無効になっています", "app.video.leaveVideo": "ウェブカメラの共有を終了", "app.video.iceCandidateError": "ICE候補の追加に失敗しました", "app.video.iceConnectionStateError": "接続失敗 (ICE error 1107)", @@ -587,6 +586,7 @@ "app.video.chromeExtensionErrorLink": "Chromeの拡張機能", "app.video.pagination.prevPage": "前のビデオを見る", "app.video.pagination.nextPage": "次のビデオを見る", + "app.video.clientDisconnected": "接続に問題が生じたためウェブカメラは共有できません", "app.fullscreenButton.label": "{0}を全画面に切り替える", "app.deskshare.iceConnectionStateError": "画面共有の接続に失敗しました (ICE error 1108)", "app.sfu.mediaServerConnectionError2000": "メディアサーバーに接続できません (error 2000)", diff --git a/bigbluebutton-html5/private/locales/ko_KR.json b/bigbluebutton-html5/private/locales/ko_KR.json index 7a5ec5d9f3..f71162489a 100644 --- a/bigbluebutton-html5/private/locales/ko_KR.json +++ b/bigbluebutton-html5/private/locales/ko_KR.json @@ -542,9 +542,6 @@ "app.recording.stopDescription": "녹화를 일시중지 하시겠습니까 ? 녹화 재시작 버튼을 누르면 계속 녹화할 수 있습니다 ", "app.videoPreview.cameraLabel": "카메라", "app.videoPreview.profileLabel": "품질", - "app.videoPreview.quality.low": "낮음", - "app.videoPreview.quality.medium": "중간", - "app.videoPreview.quality.high": "높음", "app.videoPreview.quality.hd": "고해상도", "app.videoPreview.cancelLabel": "취소", "app.videoPreview.closeLabel": "닫기", diff --git a/bigbluebutton-html5/private/locales/nl.json b/bigbluebutton-html5/private/locales/nl.json index 13c9889c00..6744d84d49 100644 --- a/bigbluebutton-html5/private/locales/nl.json +++ b/bigbluebutton-html5/private/locales/nl.json @@ -545,9 +545,9 @@ "app.recording.stopDescription": "Weet u zeker dat u de opname wilt pauzeren? U kunt later doorgaan door de opnieuw op de opnameknop te klikken.", "app.videoPreview.cameraLabel": "Camera", "app.videoPreview.profileLabel": "Kwaliteit", - "app.videoPreview.quality.low": "Laag", - "app.videoPreview.quality.medium": "Medium", - "app.videoPreview.quality.high": "Hoog", + "app.videoPreview.quality.low": "Lage kwaliteit", + "app.videoPreview.quality.medium": "Medium kwaliteit", + "app.videoPreview.quality.high": "Hoge kwaliteit", "app.videoPreview.quality.hd": "Hoge kwaliteit", "app.videoPreview.cancelLabel": "Annuleren", "app.videoPreview.closeLabel": "Sluiten", @@ -562,6 +562,8 @@ "app.videoPreview.webcamNotFoundLabel": "Webcam niet gevonden", "app.videoPreview.profileNotFoundLabel": "Geen ondersteund cameraprofiel", "app.video.joinVideo": "Webcam delen", + "app.video.connecting": "Webcam delen begint ...", + "app.video.dataSaving": "Webcam delen is gedeactiveerd in Gegevensbesparing", "app.video.leaveVideo": "Stop met delen webcam", "app.video.iceCandidateError": "Fout bij het toevoegen van ICE-kandidaat", "app.video.iceConnectionStateError": "Verbindingsfout (ICE-fout 1107)", @@ -587,6 +589,7 @@ "app.video.chromeExtensionErrorLink": "deze Chrome-extensie", "app.video.pagination.prevPage": "Zie eerdere video's", "app.video.pagination.nextPage": "Zie volgende video's", + "app.video.clientDisconnected": "Webcam kan niet gedeeld worden wegens verbindingsproblemen", "app.fullscreenButton.label": "Maak {0} volledig scherm", "app.deskshare.iceConnectionStateError": "Verbinding mislukt tijdens delen van scherm (ICE error 1108)", "app.sfu.mediaServerConnectionError2000": "Kan geen verbinding maken met mediaserver (fout 2000)", diff --git a/bigbluebutton-html5/private/locales/pt.json b/bigbluebutton-html5/private/locales/pt.json index 34bfc225bd..05cb1718ba 100644 --- a/bigbluebutton-html5/private/locales/pt.json +++ b/bigbluebutton-html5/private/locales/pt.json @@ -110,6 +110,9 @@ "app.userList.userOptions.enableNote": "Notas partilhadas estão agora ativas", "app.userList.userOptions.showUserList": "Lista de utilizadores é agora exibida a participantes", "app.userList.userOptions.enableOnlyModeratorWebcam": "Pode ativar a sua webcam agora, e todos o poderão ver", + "app.userList.userOptions.savedNames.title": "Lista de utilizadores na sessão {0} em {1}", + "app.userList.userOptions.sortedFirstName.heading": "Ordenado por primeiro nome:", + "app.userList.userOptions.sortedLastName.heading": "Ordenado por último nome:", "app.media.label": "Media", "app.media.autoplayAlertDesc": "Permitir acesso", "app.media.screenshare.start": "A partilha do ecrã iniciou", @@ -542,9 +545,6 @@ "app.recording.stopDescription": "Pretende pausar a gravação? Poderá recomeçar novamente mais tarde clicando no botão de gravação.", "app.videoPreview.cameraLabel": "Câmera", "app.videoPreview.profileLabel": "Qualidade", - "app.videoPreview.quality.low": "Baixa", - "app.videoPreview.quality.medium": "Média", - "app.videoPreview.quality.high": "Alta", "app.videoPreview.quality.hd": "Alta definição", "app.videoPreview.cancelLabel": "Cancelar", "app.videoPreview.closeLabel": "Fechar", @@ -559,6 +559,8 @@ "app.videoPreview.webcamNotFoundLabel": "Webcam não encontrada", "app.videoPreview.profileNotFoundLabel": "Não existe perfil de câmera suportado", "app.video.joinVideo": "Partilhar webcam", + "app.video.connecting": "A partilha da webcam está a iniciar...", + "app.video.dataSaving": "A partilha de webcam está desactivada no menu de Poupança de Dados", "app.video.leaveVideo": "Parar partilha de webcam", "app.video.iceCandidateError": "Error ao adicionar candidato ICE", "app.video.iceConnectionStateError": "Falha na ligação (erro ICE 1107)", @@ -584,6 +586,7 @@ "app.video.chromeExtensionErrorLink": "esta extensão Chrome", "app.video.pagination.prevPage": "Página anterior", "app.video.pagination.nextPage": "Página seguinte", + "app.video.clientDisconnected": "A webcam não pode ser partilhada devido a um problema de ligação", "app.fullscreenButton.label": "Passar {0} a ecrã inteiro", "app.deskshare.iceConnectionStateError": "A ligação falhou ao partilhar o ecrã (erro ICE 1108)", "app.sfu.mediaServerConnectionError2000": "Não foi possível ligar ao media server (erro 2000)", diff --git a/bigbluebutton-html5/private/locales/zh_CN.json b/bigbluebutton-html5/private/locales/zh_CN.json index f78cf152a0..1f62fed8b4 100644 --- a/bigbluebutton-html5/private/locales/zh_CN.json +++ b/bigbluebutton-html5/private/locales/zh_CN.json @@ -545,9 +545,6 @@ "app.recording.stopDescription": "确定要暂停录制吗?再次点“录制”按钮可以继续录制。", "app.videoPreview.cameraLabel": "摄像头", "app.videoPreview.profileLabel": "质量", - "app.videoPreview.quality.low": "低", - "app.videoPreview.quality.medium": "中", - "app.videoPreview.quality.high": "高", "app.videoPreview.quality.hd": "高清晰度", "app.videoPreview.cancelLabel": "取消", "app.videoPreview.closeLabel": "关闭", @@ -562,6 +559,8 @@ "app.videoPreview.webcamNotFoundLabel": "没有找到摄像头", "app.videoPreview.profileNotFoundLabel": "没有支持的摄像头配置文件", "app.video.joinVideo": "分享摄像头", + "app.video.connecting": "摄像头共享正在启动...", + "app.video.dataSaving": "保存数据时禁止摄像头共享", "app.video.leaveVideo": "停止分享摄像头", "app.video.iceCandidateError": "添加ICE候选者时出错", "app.video.iceConnectionStateError": "连接失败 (ICE error 1107)", @@ -587,6 +586,7 @@ "app.video.chromeExtensionErrorLink": "这个Chrome浏览器扩展插件", "app.video.pagination.prevPage": "查看上一个视频", "app.video.pagination.nextPage": "查看下一个视频", + "app.video.clientDisconnected": "由于连接问题,无法共享摄像头", "app.fullscreenButton.label": "全屏显示{0}", "app.deskshare.iceConnectionStateError": "分线屏幕时连接失败 (ICE error 1108)", "app.sfu.mediaServerConnectionError2000": "无法连接到媒体服务器 (error 2000)", diff --git a/bigbluebutton-html5/private/locales/zh_TW.json b/bigbluebutton-html5/private/locales/zh_TW.json index e4b49608da..d73d44479f 100644 --- a/bigbluebutton-html5/private/locales/zh_TW.json +++ b/bigbluebutton-html5/private/locales/zh_TW.json @@ -542,9 +542,6 @@ "app.recording.stopDescription": "您確定要暫停錄製嗎? 您可以再次選擇錄製按鈕繼續。", "app.videoPreview.cameraLabel": "攝影機", "app.videoPreview.profileLabel": "品質", - "app.videoPreview.quality.low": "低", - "app.videoPreview.quality.medium": "中", - "app.videoPreview.quality.high": "高", "app.videoPreview.quality.hd": "高解析度", "app.videoPreview.cancelLabel": "取消", "app.videoPreview.closeLabel": "關閉", From 1866eb71945f86012aea5e57858dad995cfba67e Mon Sep 17 00:00:00 2001 From: Calvin Walton Date: Fri, 25 Sep 2020 12:36:24 -0400 Subject: [PATCH 12/51] Perform captions generation in UTF-16 encoding The indexes returned in recording events from BBB refer to positions within a UTF-16 encoded string. Rather than attempt to untangle this in the server (which might have a performance cost), it's easier to switch the caption processing code to operate in UTF-16 encoding as well to make it work consistently. The PyICU library provides a UnicodeString type which is a UTF-16 string similar to Java and JavaScript, but which supports all the python indexing methods. It's fairly straightforwards to swap it in in place of the types used previously, and works natively as an input to the ICU line break iterator too. Fixes #10531 --- .../core/scripts/utils/gen_webvtt | 205 ++++++++++-------- 1 file changed, 112 insertions(+), 93 deletions(-) diff --git a/record-and-playback/core/scripts/utils/gen_webvtt b/record-and-playback/core/scripts/utils/gen_webvtt index 0221a4ccb5..d9ab086ffa 100755 --- a/record-and-playback/core/scripts/utils/gen_webvtt +++ b/record-and-playback/core/scripts/utils/gen_webvtt @@ -22,7 +22,7 @@ from lxml import etree from collections import deque from fractions import Fraction import io -from icu import Locale, BreakIterator +from icu import Locale, BreakIterator, UnicodeString import unicodedata import html import logging @@ -35,12 +35,14 @@ logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) + def webvtt_timestamp(ms): frac_s = int(ms % 1000) s = int(ms / 1000 % 60) m = int(ms / 1000 / 60 % 60) h = int(ms / 1000 / 60 / 60) - return '{:02}:{:02}:{:02}.{:03}'.format(h, m, s, frac_s) + return "{:02}:{:02}:{:02}.{:03}".format(h, m, s, frac_s) + class CaptionLine: def __init__(self): @@ -48,10 +50,11 @@ class CaptionLine: self.start_time = 0 self.end_time = 0 + class Caption: def __init__(self, locale): self.locale = locale - self.text = list() + self.text = UnicodeString() self.timestamps = list() self._del_timestamps = list() @@ -63,24 +66,30 @@ class Caption: else: del_timestamp = self.timestamps[i] self._del_timestamps[i] = del_timestamp - logger.debug("Removing text %s at %d:%d, del_ts: %d", - repr(''.join(self.text[i:j])), i, j, del_timestamp) + logger.debug( + "Removing text %s at %d:%d, del_ts: %d", + repr(str(self.text[i:j])), + i, + j, + del_timestamp, + ) if len(text) > 0: - logger.debug("Inserting text %s at %d:%d, ts: %d", - repr(''.join(text)), i, j, timestamp) + logger.debug( + "Inserting text %s at %d:%d, ts: %d", repr(str(text)), i, j, timestamp + ) if i < len(self.timestamps) and timestamp > self.timestamps[i]: timestamp = self._del_timestamps[i] if timestamp is None: if i > 0: - timestamp = self.timestamps[i-1] + timestamp = self.timestamps[i - 1] else: timestamp = self.timestamps[i] logger.debug("Out of order timestamps, using ts: %d", timestamp) self._del_timestamps[i:j] = [del_timestamp] * len(text) - if (i < len(self._del_timestamps)): + if i < len(self._del_timestamps): self._del_timestamps[i] = del_timestamp self.text[i:j] = text @@ -94,9 +103,9 @@ class Caption: stop_pos = 0 start_pos = None for event in events: - if event['name'] == 'record_status': - status = event['status'] - timestamp = event['timestamp'] + if event["name"] == "record_status": + status = event["status"] + timestamp = event["timestamp"] if status and not record: record = True @@ -106,13 +115,14 @@ class Caption: # Find the position of the first character after recording # started start_pos = stop_pos - while start_pos < len(self.timestamps) and \ - self.timestamps[start_pos] < start_ts: + while ( + start_pos < len(self.timestamps) + and self.timestamps[start_pos] < start_ts + ): start_pos += 1 - logger.debug("Replacing characters %d:%d", - stop_pos, start_pos) - self.text[stop_pos:start_pos] = ["\n"] + logger.debug("Replacing characters %d:%d", stop_pos, start_pos) + self.text[stop_pos:start_pos] = "\n" self.timestamps[stop_pos:start_pos] = [stop_ts - ts_offset] start_pos = stop_pos + 1 @@ -130,8 +140,10 @@ class Caption: # Find the position of the first character after recording # stopped, and apply ts offsets stop_pos = start_pos - while stop_pos < len(self.timestamps) and \ - self.timestamps[stop_pos] < stop_ts: + while ( + stop_pos < len(self.timestamps) + and self.timestamps[stop_pos] < stop_ts + ): self.timestamps[stop_pos] -= ts_offset stop_pos += 1 @@ -149,17 +161,16 @@ class Caption: # Apply all of the caption events to generate the full text # with per-character timestamps for event in events: - if event['name'] == 'edit_caption_history': - locale = event['locale'] - i = event['start_index'] - j = event['end_index'] - timestamp = event['timestamp'] - text = event['text'] + if event["name"] == "edit_caption_history": + locale = event["locale"] + i = event["start_index"] + j = event["end_index"] + timestamp = event["timestamp"] + text = UnicodeString(event["text"]) caption = captions.get(locale) if caption is None: - logger.info("Started caption stream for locale '%s'", - locale) + logger.info("Started caption stream for locale '%s'", locale) captions[locale] = caption = cls(locale) caption.apply_edit(i, j, timestamp, text) @@ -175,15 +186,12 @@ class Caption: def split_lines(self, max_length=32): lines = list() - str_text = "".join(self.text) - locale = Locale(self.locale) - logger.debug("Using locale %s for word-wrapping", - locale.getDisplayName(locale)) + logger.debug("Using locale %s for word-wrapping", locale.getDisplayName(locale)) break_iter = BreakIterator.createLineInstance(locale) - break_iter.setText(str_text) - + break_iter.setText(self.text) + line = CaptionLine() line_start = 0 prev_break = 0 @@ -194,39 +202,45 @@ class Caption: status = break_iter.getRuleStatus() line_end = next_break - while line_end > line_start and ( \ - self.text[line_end-1].isspace() or \ - unicodedata.category(self.text[line_end-1]) in ['Cc', 'Mn'] - ): + logger.debug("text len: %d, line end: %d", len(self.text), line_end) + while line_end > line_start and ( + self.text[line_end - 1].isspace() + or unicodedata.category(self.text[line_end - 1]) in ["Cc", "Mn"] + ): line_end -= 1 do_break = False text_section = unicodedata.normalize( - 'NFC', "".join(self.text[line_start:line_end])) + "NFC", str(self.text[line_start:line_end]) + ) timestamps_section = self.timestamps[line_start:next_break] start_time = min(timestamps_section) end_time = max(timestamps_section) if len(text_section) > max_length: if prev_break == line_start: # Over-long string. Just chop it into bits - line_end = next_break = prev_break + max_length + next_break = prev_break + max_length + continue else: next_break = prev_break do_break = True else: # Status [100,200) indicates a required (hard) line break - if next_break >= len(self.text) or \ - (status >= 100 and status < 200): + if next_break >= len(self.text) or (status >= 100 and status < 200): line.text = text_section line.start_time = start_time line.end_time = end_time do_break = True if do_break: - logger.debug("text section %d -> %d (%d): %s", - line.start_time, line.end_time, - len(line.text), repr(line.text)) + logger.debug( + "text section %d -> %d (%d): %s", + line.start_time, + line.end_time, + len(line.text), + repr(line.text), + ) lines.append(line) line = CaptionLine() line_start = next_break @@ -242,7 +256,7 @@ class Caption: def write_webvtt(self, f): # Write magic - f.write("WEBVTT\n\n".encode('utf-8')) + f.write("WEBVTT\n\n".encode("utf-8")) lines = self.split_lines() @@ -297,49 +311,48 @@ class Caption: if next_start_time - end_time < 500: end_time = next_start_time - f.write("{} --> {}\n".format( - webvtt_timestamp(start_time), - webvtt_timestamp(end_time) - ).encode('utf-8')) - f.write(html.escape(text, quote=False).encode('utf-8')) - f.write("\n\n".encode('utf-8')) + f.write( + "{} --> {}\n".format( + webvtt_timestamp(start_time), webvtt_timestamp(end_time) + ).encode("utf-8") + ) + f.write(html.escape(text, quote=False).encode("utf-8")) + f.write("\n\n".encode("utf-8")) def caption_desc(self): locale = Locale(self.locale) - return { - "locale": self.locale, - "localeName": locale.getDisplayName(locale) - } + return {"locale": self.locale, "localeName": locale.getDisplayName(locale)} def parse_record_status(event, element): - userId = element.find('userId') - status = element.find('status') + userId = element.find("userId") + status = element.find("status") + + event["name"] = "record_status" + event["user_id"] = userId.text + event["status"] = status.text == "true" - event['name'] = 'record_status' - event['user_id'] = userId.text - event['status'] = (status.text == 'true') def parse_caption_edit(event, element): - locale = element.find('locale') - text = element.find('text') - startIndex = element.find('startIndex') - endIndex = element.find('endIndex') - localeCode = element.find('localeCode') + locale = element.find("locale") + text = element.find("text") + startIndex = element.find("startIndex") + endIndex = element.find("endIndex") + localeCode = element.find("localeCode") - event['name'] = 'edit_caption_history' - event['locale_name'] = locale.text + event["name"] = "edit_caption_history" + event["locale_name"] = locale.text if localeCode is not None: - event['locale'] = localeCode.text + event["locale"] = localeCode.text else: # Fallback for missing 'localeCode' - event['locale'] = "en" + event["locale"] = "en" if text.text is None: - event['text'] = list() + event["text"] = "" else: - event['text'] = list(text.text) - event['start_index'] = int(startIndex.text) - event['end_index'] = int(endIndex.text) + event["text"] = text.text + event["start_index"] = int(startIndex.text) + event["end_index"] = int(endIndex.text) def parse_events(directory="."): @@ -353,22 +366,22 @@ def parse_events(directory="."): event = {} # Convert timestamps to be in seconds from recording start - timestamp = int(element.attrib['timestamp']) + timestamp = int(element.attrib["timestamp"]) if not start_time: start_time = timestamp timestamp = timestamp - start_time # Only need events from these modules - if not element.attrib['module'] in ['CAPTION','PARTICIPANT']: + if not element.attrib["module"] in ["CAPTION", "PARTICIPANT"]: continue - event['name'] = name = element.attrib['eventname'] - event['timestamp'] = timestamp + event["name"] = name = element.attrib["eventname"] + event["timestamp"] = timestamp - if name == 'RecordStatusEvent': + if name == "RecordStatusEvent": parse_record_status(event, element) have_record_events = True - elif name == 'EditCaptionHistoryEvent': + elif name == "EditCaptionHistoryEvent": parse_caption_edit(event, element) else: logger.debug("Unhandled event: %s", name) @@ -381,25 +394,31 @@ def parse_events(directory="."): if not have_record_events: # Add a fake record start event to the events list event = { - 'name': 'record_status', - 'user_id': None, - 'timestamp': 0, - 'status': True - } + "name": "record_status", + "user_id": None, + "timestamp": 0, + "status": True, + } events.appendleft(event) return events + if __name__ == "__main__": parser = argparse.ArgumentParser( - description="Generate WebVTT files from BigBlueButton captions", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("-i", "--input", metavar="PATH", - help="input directory with events.xml file", - default=os.curdir) - parser.add_argument("-o", "--output", metavar="PATH", - help="output directory", - default=os.curdir) + description="Generate WebVTT files from BigBlueButton captions", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-i", + "--input", + metavar="PATH", + help="input directory with events.xml file", + default=os.curdir, + ) + parser.add_argument( + "-o", "--output", metavar="PATH", help="output directory", default=os.curdir + ) args = parser.parse_args() rawdir = args.input @@ -419,6 +438,6 @@ if __name__ == "__main__": filename = os.path.join(outputdir, "captions.json") logger.info("Writing captions index file to %s", filename) - caption_descs = [ caption.caption_desc() for caption in captions.values() ] + caption_descs = [caption.caption_desc() for caption in captions.values()] with open(filename, "w") as f: json.dump(caption_descs, f) From 86c821f4c5671339bca59707928e891e209e3c7d Mon Sep 17 00:00:00 2001 From: Calvin Walton Date: Fri, 25 Sep 2020 16:13:29 -0400 Subject: [PATCH 13/51] Recording: Skip shapes with missing "thickness" attribute Not sure what causes this issue, but there's nothing reasonable that can be done to draw a shape with no thickness. Just skip it. --- .../presentation/scripts/publish/presentation.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index efcf69fada..c828ddb3f7 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -649,11 +649,15 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:type] == 'ellipse' or shape[:type] == 'triangle' or shape[:type] == 'line' shape[:color] = color_to_hex(event.at_xpath('color').text) - thickness = event.at_xpath('thickness').text + thickness = event.at_xpath('thickness') + unless thickness + BigBlueButton.logger.warn("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} is missing thickness") + return + end if $version_atleast_2_0_0 - shape[:thickness_percent] = thickness.to_f + shape[:thickness_percent] = thickness.text.to_f else - shape[:thickness] = thickness.to_i + shape[:thickness] = thickness.text.to_i end end if shape[:type] == 'rectangle' From bc478104aa7ca1806799fb9e48ccc5d3c7b0ce6f Mon Sep 17 00:00:00 2001 From: Anton Georgiev Date: Fri, 25 Sep 2020 16:38:30 -0400 Subject: [PATCH 14/51] Bump up to 2.2.27 --- bigbluebutton-config/bigbluebutton-release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigbluebutton-config/bigbluebutton-release b/bigbluebutton-config/bigbluebutton-release index 66650a8806..5efaf189ea 100644 --- a/bigbluebutton-config/bigbluebutton-release +++ b/bigbluebutton-config/bigbluebutton-release @@ -1 +1 @@ -BIGBLUEBUTTON_RELEASE=2.2.26 +BIGBLUEBUTTON_RELEASE=2.2.27 From 619ffa0ec17d3377fb35f445923ea4f01c9b024d Mon Sep 17 00:00:00 2001 From: Mario Jr Date: Fri, 25 Sep 2020 20:11:44 -0300 Subject: [PATCH 15/51] Port SIP.js to 0.17.1 release This considerably changes the way we process audio signaling and start audio elements in user's browser. We now avoid using AudioContext element for both microphone and listenonly calls, once it is unstable for some iOS devices (cracky audio, user stops hearing audio after a while). Increased default value for listenOnlyCallTimeout: this avoids activating FreeSWITCH's fallback when ICE negotiation takes longer than 15sec (tested on DO). Increased listenonly logs. This fixes #8133 #10388 --- bigbluebutton-html5/client/main.html | 3 +- .../api/audio/client/bridge/kurento.js | 28 +- .../imports/api/audio/client/bridge/sip.js | 601 +- .../audio/audio-modal/component.jsx | 7 + .../ui/services/audio-manager/index.js | 93 +- bigbluebutton-html5/imports/utils/sdpUtils.js | 4 +- .../private/config/settings.yml | 2 +- .../public/compatibility/kurento-extension.js | 5 + .../public/compatibility/sip.js | 32292 ++++++++++------ 9 files changed, 20968 insertions(+), 12067 deletions(-) mode change 100755 => 100644 bigbluebutton-html5/public/compatibility/sip.js diff --git a/bigbluebutton-html5/client/main.html b/bigbluebutton-html5/client/main.html index 3148595a76..477638ae33 100755 --- a/bigbluebutton-html5/client/main.html +++ b/bigbluebutton-html5/client/main.html @@ -72,7 +72,6 @@ with BigBlueButton; if not, see .
-