Merge 2.6.x-release into camera-brightness

This commit is contained in:
Joao Victor 2022-08-26 11:53:23 -03:00
commit 24c227cc75
40 changed files with 348 additions and 80 deletions

View File

@ -114,7 +114,7 @@ jobs:
run: |
sudo sh -c '
cd /root/ && wget -q https://ubuntu.bigbluebutton.org/bbb-install-2.6.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v focal-26-dev -s bbb-ci.test -d /certs/
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v focal-26-dev -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci
echo "NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt" >> /usr/share/meteor/bundle/bbb-html5-with-roles.conf
bbb-conf --restart
@ -134,7 +134,22 @@ jobs:
ACTIONS_RUNNER_DEBUG: true
BBB_URL: https://bbb-ci.test/bigbluebutton/api
BBB_SECRET: bbbci
run: npm run test-ci
run: npm run test-chromium-ci
- name: Run Firefox tests
working-directory: ./bigbluebutton-tests/playwright
if: ${{ contains(github.event.pull_request.labels.*.name, 'test Firefox')
|| contains(github.event.pull_request.labels.*.name, 'Test Firefox') }}
env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
ACTIONS_RUNNER_DEBUG: true
BBB_URL: https://bbb-ci.test/bigbluebutton/api
BBB_SECRET: bbbci
# patch playwright's firefox so that it uses the system's root certificate authority
run: |
sh -c '
find $HOME/.cache/ms-playwright -name libnssckbi.so -exec rm {} \; -exec ln -s /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-trust.so {} \;
npm run test-firefox-ci
'
- if: always()
uses: actions/upload-artifact@v3
with:

View File

@ -156,6 +156,15 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
case UpdateExternalVideoEvtMsg.NAME =>
msgSender.send("from-akka-apps-frontend-redis-channel", json)
case NotifyAllInMeetingEvtMsg.NAME =>
msgSender.send("from-akka-apps-frontend-redis-channel", json)
case NotifyUserInMeetingEvtMsg.NAME =>
msgSender.send("from-akka-apps-frontend-redis-channel", json)
case NotifyRoleInMeetingEvtMsg.NAME =>
msgSender.send("from-akka-apps-frontend-redis-channel", json)
case _ =>
msgSender.send(fromAkkaAppsRedisChannel, json)
}

View File

@ -1,4 +1,4 @@
FROM openjdk:11-jre-bullseye
FROM openjdk:17-slim-bullseye
ENV DEBIAN_FRONTEND noninteractive
RUN echo "deb http://deb.debian.org/debian bullseye-backports main" >> /etc/apt/sources.list

View File

@ -1 +1 @@
git clone --branch v2.9.0-beta.1 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
git clone --branch v2.9.0 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu

View File

@ -1 +1 @@
BIGBLUEBUTTON_RELEASE=2.6.0-alpha.2
BIGBLUEBUTTON_RELEASE=2.6.0-alpha.3

View File

@ -454,6 +454,18 @@ display_bigbluebutton_status () {
units="$units bbb-export-annotations"
fi
if [ -f /usr/lib/systemd/system/bbb-rap-caption-inbox.service ]; then
units="$units bbb-rap-caption-inbox"
fi
if [ -f /lib/systemd/system/bbb-rap-resque-worker.service ]; then
units="$units bbb-rap-resque-worker"
fi
if [ -f /lib/systemd/system/bbb-rap-starter.service ]; then
units="$units bbb-rap-starter"
fi
if systemctl list-units --full -all | grep -q $TOMCAT_USER.service; then
TOMCAT_SERVICE=$TOMCAT_USER
fi

View File

@ -4,7 +4,7 @@
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'),
url('fonts/SourceSansPro/SourceSansPro-Light.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Light.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -13,7 +13,7 @@
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'),
url('fonts/SourceSansPro/SourceSansPro-Light.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Light.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -22,7 +22,7 @@
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'),
url('fonts/SourceSansPro/SourceSansPro-Light.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Light.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -31,7 +31,7 @@
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'),
url('fonts/SourceSansPro/SourceSansPro-Regular.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Regular.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -40,7 +40,7 @@
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'),
url('fonts/SourceSansPro/SourceSansPro-Regular.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Regular.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -49,7 +49,7 @@
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'),
url('fonts/SourceSansPro/SourceSansPro-Regular.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Regular.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -58,7 +58,7 @@
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'),
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -67,7 +67,7 @@
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'),
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -76,7 +76,7 @@
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'),
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Semibold.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -85,7 +85,7 @@
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
url('fonts/SourceSansPro/SourceSansPro-Bold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Bold.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -94,7 +94,7 @@
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
url('fonts/SourceSansPro/SourceSansPro-Bold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Bold.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -103,7 +103,7 @@
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
url('fonts/SourceSansPro/SourceSansPro-Bold.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Bold.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -112,7 +112,7 @@
font-style: italic;
font-weight: 300;
src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'),
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -121,7 +121,7 @@
font-style: italic;
font-weight: 300;
src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'),
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -130,7 +130,7 @@
font-style: italic;
font-weight: 300;
src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'),
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-LightItalic.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -139,7 +139,7 @@
font-style: italic;
font-weight: 400;
src: local('Source Sans Pro Italic'), local('SourceSansPro-It'),
url('fonts/SourceSansPro/SourceSansPro-Italic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Italic.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -148,7 +148,7 @@
font-style: italic;
font-weight: 400;
src: local('Source Sans Pro Italic'), local('SourceSansPro-It'),
url('fonts/SourceSansPro/SourceSansPro-Italic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Italic.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -157,7 +157,7 @@
font-style: italic;
font-weight: 400;
src: local('Source Sans Pro Italic'), local('SourceSansPro-It'),
url('fonts/SourceSansPro/SourceSansPro-Italic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-Italic.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -166,7 +166,7 @@
font-style: italic;
font-weight: 600;
src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'),
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -175,7 +175,7 @@
font-style: italic;
font-weight: 600;
src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'),
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -184,7 +184,7 @@
font-style: italic;
font-weight: 600;
src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'),
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-SemiboldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* vietnamese */
@ -193,7 +193,7 @@
font-style: italic;
font-weight: 700;
src: local('Source Sans Pro Bold Italic'), local('SourceSansPro-BoldIt'),
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@ -202,7 +202,7 @@
font-style: italic;
font-weight: 700;
src: local('Source Sans Pro Bold Italic'), local('SourceSansPro-BoldIt'),
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@ -211,6 +211,6 @@
font-style: italic;
font-weight: 700;
src: local('Source Sans Pro Bold Italic'), local('SourceSansPro-BoldIt'),
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff') format('woff');
url('fonts/SourceSansPro/SourceSansPro-BoldItalic.woff?v=VERSION') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

View File

@ -1,6 +1,7 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import VideoStreams from '/imports/api/video-streams';
import flat from 'flat';
export default function updateVideoStream(meetingId, videoStream) {
check(meetingId, String);
@ -23,7 +24,7 @@ export default function updateVideoStream(meetingId, videoStream) {
const modifier = {
$set: Object.assign(
...videoStream,
flat(videoStream),
),
};

View File

@ -55,6 +55,7 @@ const AboutComponent = ({ intl, settings }) => {
return (
<Modal
data-test="aboutModalTitleLabel"
title={intl.formatMessage(intlMessages.title)}
dismiss={{
label: intl.formatMessage(intlMessages.dismissLabel),

View File

@ -48,6 +48,7 @@ import Notifications from '../notifications/container';
import GlobalStyles from '/imports/ui/stylesheets/styled-components/globalStyles';
import MediaService from '/imports/ui/components/media/service';
import ActionsBarContainer from '../actions-bar/container';
import { updateSettings } from '/imports/ui/components/settings/service';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const APP_CONFIG = Meteor.settings.public.app;
@ -313,7 +314,13 @@ class App extends Component {
this.renderDarkMode();
if (meetingLayout !== prevProps.meetingLayout) {
const meetingLayoutDidChange = meetingLayout !== prevProps.meetingLayout;
const pushLayoutMeetingDidChange = pushLayoutMeeting !== prevProps.pushLayoutMeeting;
const shouldSwitchLayout = isPresenter
? meetingLayoutDidChange
: (meetingLayoutDidChange || pushLayoutMeetingDidChange) && pushLayoutMeeting;
if (shouldSwitchLayout) {
let contextLayout = meetingLayout;
if (isMobile()) {
@ -325,13 +332,21 @@ class App extends Component {
value: contextLayout,
});
Settings.application.selectedLayout = contextLayout;
Settings.save();
updateSettings({
application: {
...Settings.application,
selectedLayout: contextLayout,
},
});
}
if (pushLayoutMeeting !== prevProps.pushLayoutMeeting) {
Settings.application.pushLayout = pushLayoutMeeting;
Settings.save();
if (pushLayoutMeetingDidChange) {
updateSettings({
application: {
...Settings.application,
pushLayout: pushLayoutMeeting,
},
});
}
if (meetingLayout === "custom" && !isPresenter) {

View File

@ -224,6 +224,7 @@ class SettingsDropdown extends PureComponent {
{
key: 'list-item-about',
icon: 'about',
dataTest: 'aboutModal',
label: intl.formatMessage(intlMessages.aboutLabel),
// description: intl.formatMessage(intlMessages.aboutDesc),
onClick: () => mountModal(<AboutContainer />),

View File

@ -78,6 +78,7 @@ class Presentation extends PureComponent {
isFullscreen: false,
tldrawAPI: null,
isZoomed: false,
hadPresentation: false,
isPanning: false,
};

View File

@ -171,6 +171,7 @@ const PresentationMenu = (props) => {
{
key: 'list-item-screenshot',
label: intl.formatMessage(intlMessages.snapshotLabel),
dataTest: "presentationSnapshot",
onClick: async () => {
setState({
loading: true,
@ -189,7 +190,7 @@ const PresentationMenu = (props) => {
try {
const { copySvg, getShapes, currentPageId } = tldrawAPI;
const svgString = copySvg(getShapes(currentPageId).map((shape) => shape.id));
const svgString = await copySvg(getShapes(currentPageId).map((shape) => shape.id));
const container = document.createElement('div');
container.innerHTML = svgString;
const svgElem = container.firstChild;

View File

@ -330,6 +330,15 @@ class PresentationUploader extends Component {
}
});
return presentation;
}).filter((presentation) => {
const currentPropPres = propPresentations.find((pres) => pres.id === presentation.id);
if (!currentPropPres) return false;
presentation.conversion = currentPropPres.conversion;
presentation.isRemovable = currentPropPres.isRemovable;
return true;
});
if (shouldUpdateState) {

View File

@ -394,12 +394,33 @@ class VideoPreview extends Component {
);
}
updateVirtualBackgroundInfo = () => {
const { webcamDeviceId } = this.state;
// Update this session's virtual camera effect information if it's enabled
setSessionVirtualBackgroundInfo(
this.currentVideoStream.virtualBgType,
this.currentVideoStream.virtualBgName,
webcamDeviceId,
);
};
// Resolves into true if the background switch is successful, false otherwise
handleVirtualBgSelected(type, name, customParams) {
const { sharedDevices } = this.props;
const { webcamDeviceId } = this.state;
const shared = sharedDevices.includes(webcamDeviceId);
if (type !== EFFECT_TYPES.NONE_TYPE || ENABLE_CAMERA_BRIGHTNESS) {
return this.startVirtualBackground(this.currentVideoStream, type, name, customParams);
return this.startVirtualBackground(this.currentVideoStream, type, name, customParams).then((switched) => {
// If it's not shared we don't have to update here because
// it will be updated in the handleStartSharing method.
if (switched && shared) this.updateVirtualBackgroundInfo();
return switched;
});
} else {
this.stopVirtualBackground(this.currentVideoStream);
if (shared) this.updateVirtualBackgroundInfo();
return Promise.resolve(true);
}
}
@ -446,12 +467,7 @@ class VideoPreview extends Component {
this.currentVideoStream.stop();
}
// Update this session's virtual camera effect information if it's enabled
setSessionVirtualBackgroundInfo(
this.currentVideoStream.virtualBgType,
this.currentVideoStream.virtualBgName,
webcamDeviceId,
);
this.updateVirtualBackgroundInfo();
this.cleanupStreamAndVideo();
startSharing(webcamDeviceId);
if (resolve) resolve();

View File

@ -24,6 +24,11 @@ const findRemoved = (A, B) => {
});
};
const SMALL_HEIGHT = 435;
const SMALLEST_HEIGHT = 363;
const TOOLBAR_SMALL = 28;
const TOOLBAR_LARGE = 38;
export default function Whiteboard(props) {
const {
isPresenter,
@ -211,11 +216,30 @@ export default function Whiteboard(props) {
React.useEffect(() => {
if (hasWBAccess || isPresenter) {
const tdTools = document.getElementById("TD-Tools");
if (tdTools) {
// removes tldraw native help menu button
tdTools.parentElement?.nextSibling?.remove();
tldrawAPI?.setSetting('dockPosition', isRTL ? 'left' : 'right');
const tdToolsDots = document.getElementById("TD-Tools-Dots");
const tdDelete = document.getElementById("TD-Delete");
const tdPrimaryTools = document.getElementById("TD-PrimaryTools");
const tdTools = document.getElementById("TD-Tools");
if (tdToolsDots && tdDelete && tdPrimaryTools) {
const size = props.height < SMALL_HEIGHT ? TOOLBAR_SMALL : TOOLBAR_LARGE;
tdToolsDots.style.height = `${size}px`;
tdToolsDots.style.width = `${size}px`;
const delButton = tdDelete.getElementsByTagName('button')[0];
delButton.style.height = `${size}px`;
delButton.style.width = `${size}px`;
const primaryBtns = tdPrimaryTools?.getElementsByTagName('button');
for (let item of primaryBtns) {
item.style.height = `${size}px`;
item.style.width = `${size}px`;
}
}
if (props.height < SMALLEST_HEIGHT && tdTools) {
tldrawAPI?.setSetting('dockPosition', 'bottom');
tdTools.parentElement.style.bottom = `${TOOLBAR_SMALL}px`;
}
// removes tldraw native help menu button
tdTools?.parentElement?.nextSibling?.remove();
// removes image tool from the tldraw toolbar
document.getElementById("TD-PrimaryTools-Image").style.display = 'none';
}
@ -233,7 +257,6 @@ export default function Whiteboard(props) {
const onMount = (app) => {
app.setSetting('language', document.getElementsByTagName('html')[0]?.lang || 'en');
app.setSetting('dockPosition', isRTL ? 'left' : 'right');
setTLDrawAPI(app);
props.setTldrawAPI(app);
// disable for non presenter that doesn't have multi user access
@ -250,7 +273,7 @@ export default function Whiteboard(props) {
document: {
pageStates: {
[app.getPage()?.id]: {
hoveredId: id,
hoveredId: id || [],
},
},
},

View File

@ -10,10 +10,12 @@ import { layoutSelect } from '../layout/context';
const WhiteboardContainer = (props) => {
const usingUsersContext = useContext(UsersContext);
const isRTL = layoutSelect((i) => i.isRTL);
const width = layoutSelect((i) => i?.output?.presentation?.width);
const height = layoutSelect((i) => i?.output?.presentation?.height);
const { users } = usingUsersContext;
const currentUser = users[Auth.meetingID][Auth.userID];
const isPresenter = currentUser.presenter;
return <Whiteboard {...{isPresenter, currentUser, isRTL}} {...props} meetingId={Auth.meetingID} />
return <Whiteboard {...{isPresenter, currentUser, isRTL, width, height}} {...props} meetingId={Auth.meetingID} />
};
export default withTracker(({ whiteboardId, curPageId, intl, zoomChanger }) => {

View File

@ -218,8 +218,13 @@ const getMultiUserSize = (whiteboardId) => {
const multiUserSize = Users.find(
{
meetingId: Auth.meetingID,
userId: { $in: multiUser },
presenter: false,
$or: [
{
userId: { $in: multiUser },
presenter: false,
},
{ presenter: true },
],
},
{ fields: { userId: 1 } }
).fetch();

View File

@ -32,9 +32,9 @@
"@browser-bunyan/server-stream": "^1.8.0",
"@jitsi/sdp-interop": "0.1.14",
"@material-ui/core": "^4.12.4",
"@mconf/bbb-diff": "^1.2.0",
"@tldraw/core": "1.15.0",
"@tldraw/tldraw": "1.20.0",
"@mconf/bbb-diff": "^1.2.0",
"autoprefixer": "^10.4.4",
"axios": "^0.21.3",
"babel-runtime": "~6.26.0",

View File

@ -3,6 +3,7 @@ const CI = process.env.CI === 'true';
// GLOBAL TESTS VARS
exports.ELEMENT_WAIT_TIME = CI ? 10000 : 5000;
exports.ELEMENT_WAIT_LONGER_TIME = CI ? 20000 : 10000;
exports.ELEMENT_WAIT_EXTRA_LONG_TIME = 15000;
exports.LOOP_INTERVAL = 1200;
exports.USER_LIST_VLIST_BOTS_LISTENING = 50;

View File

@ -114,6 +114,8 @@ exports.hasUnreadMessages = 'button[data-test="hasUnreadMessages"]';
exports.userJoinPushAlerts = 'input[data-test="userJoinPopupAlerts"]';
exports.toastContainer = 'div[data-test="toastContainer"]';
exports.presentationStatusInfo = 'span[data-test="presentationStatusInfo"]';
exports.noButton = 'button[aria-label="No"]';
exports.yesButton = 'button[aria-label="Yes"]';
// Toasts
exports.savedSettingsToast = 'Settings have been saved';
exports.publicChatToast = 'New Public Chat message';
@ -126,6 +128,7 @@ exports.joiningMessageToast = 'You have joined the audio conference';
exports.attendeeJoinedToast = 'Attendee joined the session';
exports.raisingHandToast = 'You have raised your hand';
exports.loweringHandToast = 'Your hand has been lowered';
exports.noActiveMicrophoneToast = 'No active microphone. Share your microphone to add audio to this recording.';
// Icons
const baseBbbIcon = 'i.icon-bbb-';
exports.unmuteIcon = `${baseBbbIcon}unmute`;
@ -304,3 +307,7 @@ exports.pencil = 'button[data-test="pencilTool"]';
exports.showMoreSharedNotesButton = 'span[class="show-more-icon-btn"]'
exports.exportSharedNotesButton = 'button[aria-label="Import/Export from/to different file formats"]';
exports.exportPlainButton = 'span[id="exportplain"]';
// About modal
exports.showAboutModalButton = 'li[data-test="aboutModal"]';
exports.aboutModal = 'div[data-test="aboutModalTitleLabel"]';

View File

@ -182,7 +182,7 @@ class Page {
}
async hasText(selector, text, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(selector);
const locator = this.getLocator(selector).first();
await expect(locator).toContainText(text, { timeout });
}

View File

@ -5,6 +5,7 @@ const c = require('./constants');
const { VIDEO_LOADING_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const util = require('./util');
const { getSettings } = require('../core/settings');
const { waitAndClearDefaultPresentationNotification } = require('../notifications/util');
class CustomParameters extends MultiUsers {
constructor(browser, context) {
@ -92,6 +93,7 @@ class CustomParameters extends MultiUsers {
}
async skipCheck() {
await waitAndClearDefaultPresentationNotification(this.modPage);
await this.modPage.waitAndClick(e.microphoneButton);
await this.modPage.waitForSelector(e.establishingAudioLabel);
await this.modPage.wasRemoved(e.establishingAudioLabel, ELEMENT_WAIT_LONGER_TIME);

View File

@ -2,6 +2,8 @@ const { test } = require('@playwright/test');
const { Notifications } = require('./notifications');
const { ChatNotifications } = require('./chatNotifications');
const { PresenterNotifications } = require('./presenterNotifications');
const { RecordingNotifications } = require('./recordingNotifications');
const c = require('../customparameters/constants');
test.describe.parallel('Notifications', () => {
test('Save settings notification @ci', async ({ browser, context, page }) => {
@ -42,6 +44,29 @@ test.describe.parallel('Notifications', () => {
});
});
test.describe.parallel('Recording', () => {
test('Notification appearing when user is not in audio', async ({ browser, page }) => {
const recordingNotifications = new RecordingNotifications(browser, page);
await recordingNotifications.init(true, true, { customParameter: c.recordMeeting });
await recordingNotifications.notificationNoAudio();
});
test('Notification appearing when user is in listen only', async ({ browser, page }) => {
const recordingNotifications = new RecordingNotifications(browser, page);
await recordingNotifications.init(true, true, { customParameter: c.recordMeeting });
await recordingNotifications.notificationListenOnly();
});
test('No notification appearing when user is in audio', async ({ browser, page }) => {
const recordingNotifications = new RecordingNotifications(browser, page);
await recordingNotifications.init(true, true, { customParameter: c.recordMeeting });
await recordingNotifications.noNotificationInAudio();
});
test('Modal appearing when user wants to start recording', async ({ browser, page }) => {
const recordingNotifications = new RecordingNotifications(browser, page);
await recordingNotifications.init(true, true, { customParameter: c.recordMeeting });
await recordingNotifications.modalStartRecording();
});
});
test.describe.parallel('Presenter @ci', () => {
test('Poll results notification', async ({ browser, context, page }) => {
test.fixme(true, 'Different behaviors in the development and production environment');
@ -56,7 +81,9 @@ test.describe.parallel('Notifications', () => {
await presenterNotifications.fileUploaderNotification();
});
test('Screenshare notification', async ({ browser, context, page }) => {
test('Screenshare notification', async ({ browser, browserName, context, page }) => {
test.skip(browserName === 'firefox' && process.env.DISPLAY === undefined,
"Screenshare tests not able in Firefox browser without desktop");
const presenterNotifications = new PresenterNotifications(browser, context);
await presenterNotifications.initModPage(page);
await presenterNotifications.screenshareToast();

View File

@ -0,0 +1,52 @@
const { expect } = require('@playwright/test');
const Page = require('../core/page');
const util = require('./util');
const { waitAndClearNotification, waitAndClearDefaultPresentationNotification } = require('../notifications/util');
const e = require('../core/elements');
const { connectMicrophone } = require('../audio/util');
const { sleep } = require('../core/helpers');
const { ELEMENT_WAIT_EXTRA_LONG_TIME } = require('../core/constants');
class RecordingNotifications extends Page {
constructor(browser, page) {
super(browser, page);
}
async notificationNoAudio() {
// when you don't join audio at all, there's notification about no active mic
await waitAndClearDefaultPresentationNotification(this);
await this.waitAndClick(e.recordingIndicator, ELEMENT_WAIT_EXTRA_LONG_TIME);
await this.waitForSelector(e.smallToastMsg);
await util.checkNotificationText(this, e.noActiveMicrophoneToast);
}
async notificationListenOnly() {
// when you join listen only, there's notification about no active mic
await waitAndClearDefaultPresentationNotification(this);
await this.waitAndClick(e.joinAudio, ELEMENT_WAIT_EXTRA_LONG_TIME);
await this.waitAndClick(e.listenOnlyButton, ELEMENT_WAIT_EXTRA_LONG_TIME);
await waitAndClearNotification(this);
await this.waitAndClick(e.recordingIndicator);
await this.waitForSelector(e.smallToastMsg);
await util.checkNotificationText(this, e.noActiveMicrophoneToast);
}
async noNotificationInAudio() {
// when you join audio with mic, there's no notification about no active mic
await waitAndClearDefaultPresentationNotification(this);
await this.waitAndClick(e.joinAudio, ELEMENT_WAIT_EXTRA_LONG_TIME);
await this.waitForSelector(e.audioModal, ELEMENT_WAIT_EXTRA_LONG_TIME);
await connectMicrophone(this);
await waitAndClearNotification(this);
await this.waitAndClick(e.recordingIndicator);
await this.wasRemoved(e.smallToastMsg, ELEMENT_WAIT_EXTRA_LONG_TIME);
}
async modalStartRecording() {
await this.waitAndClick(e.recordingIndicator, ELEMENT_WAIT_EXTRA_LONG_TIME);
await this.waitForSelector(e.noButton);
await this.waitForSelector(e.yesButton);
}
}
exports.RecordingNotifications = RecordingNotifications;

View File

@ -1,13 +1,19 @@
const Page = require('../core/page');
const { openSettings, getLocaleValues } = require('./util');
const { openAboutModal, openSettings, getLocaleValues } = require('./util');
const e = require('../core/elements');
class Language extends Page {
class Options extends Page {
constructor(browser, page) {
super(browser, page);
}
async test() {
async openedAboutModal() {
await openAboutModal(this);
await this.hasElement(e.closeModal);
}
async localesTest() {
const selectedKeysBySelector = {
[e.messageTitle]: 'app.userList.messagesTitle',
[e.notesTitle]: 'app.userList.notesTitle',
@ -42,4 +48,4 @@ class Language extends Page {
}
}
exports.Language = Language;
exports.Options = Options;

View File

@ -0,0 +1,20 @@
const { test } = require('@playwright/test');
const { Options } = require('./options');
test.describe.parallel('Options', () => {
test('Open about modal', async ({ browser, page }) => {
const about = new Options(browser, page);
await about.init(true, true);
await about.openedAboutModal();
});
});
test.describe.parallel('Settings', () => {
// https://docs.bigbluebutton.org/2.6/release-tests.html#application-settings
test(`Locales`, async ({ browser, page }) => {
test.slow();
const language = new Options(browser, page);
await language.init(true, true);
await language.localesTest();
});
});

View File

@ -29,5 +29,11 @@ async function getLocaleValues(elements, locale) {
return currentValues;
}
async function openAboutModal(test) {
await test.waitAndClick(e.optionsButton);
await test.waitAndClick(e.showAboutModalButton);
}
exports.openAboutModal = openAboutModal;
exports.openSettings = openSettings;
exports.getLocaleValues = getLocaleValues;

View File

@ -4,7 +4,8 @@
"test:filter": "npx playwright test -g",
"test:headed": "npx playwright test --headed",
"test:debug": "npx playwright test --debug -g",
"test-ci": "export CI='true' && npx playwright test --project=chromium --grep @ci"
"test-chromium-ci": "export CI='true' && npx playwright test --project=chromium --grep @ci",
"test-firefox-ci": "export CI='true' && npx playwright test --project=firefox --grep @ci"
},
"dependencies": {
"@playwright/test": "^1.19.2",
@ -13,4 +14,4 @@
"dotenv": "^16.0.0",
"sha1": "^1.1.1"
}
}
}

View File

@ -30,7 +30,8 @@ class Polling extends MultiUsers {
await waitAndClearDefaultPresentationNotification(this.modPage);
await utilPresentation.uploadSinglePresentation(this.modPage, e.questionSlideFileName);
await this.modPage.waitAndClick(e.quickPoll);
// The slide needs to be uploaded and converted, so wait a bit longer for this step
await this.modPage.waitAndClick(e.quickPoll, ELEMENT_WAIT_LONGER_TIME);
await this.modPage.waitForSelector(e.pollMenuButton);
await this.userPage.hasElement(e.pollingContainer);

View File

@ -3,7 +3,9 @@ const { ScreenShare } = require('./screenshare');
test.describe.parallel('Screenshare', () => {
// https://docs.bigbluebutton.org/2.6/release-tests.html#sharing-screen-in-full-screen-mode-automated
test('Share screen @ci', async ({ browser, page }) => {
test('Share screen @ci', async ({ browser, browserName, page }) => {
test.skip(browserName === 'firefox' && process.env.DISPLAY === undefined,
"Screenshare tests not able in Firefox browser without desktop");
const screenshare = new ScreenShare(browser, page);
await screenshare.init(true, true);
await screenshare.startSharing();

View File

@ -3,7 +3,7 @@ const { VIDEO_LOADING_WAIT_TIME } = require('../core/constants');
async function startScreenshare(test) {
await test.waitAndClick(e.startScreenSharing);
await test.waitForSelector(e.screenshareConnecting);
await test.waitForSelector(e.screenshareConnecting, VIDEO_LOADING_WAIT_TIME);
await test.hasElement(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
}

View File

@ -1,12 +0,0 @@
const { test } = require('@playwright/test');
const { Language } = require('./language');
test.describe.parallel('Settings', () => {
// https://docs.bigbluebutton.org/2.6/release-tests.html#application-settings
test(`Locales`, async ({ browser, page }) => {
test.slow();
const language = new Language(browser, page);
await language.init(true, true);
await language.test();
});
});

View File

@ -140,6 +140,12 @@ else
sed -i 's/events {/worker_rlimit_nofile 10000;\n\nevents {/g' /etc/nginx/nginx.conf
fi
# symlink default bbb nginx config from package if it does not exist
if [ ! -e /etc/bigbluebutton/nginx/include_default.nginx ] ; then
mkdir -p /etc/bigbluebutton/nginx
ln -s /usr/share/bigbluebutton/include_default.nginx /etc/bigbluebutton/nginx/include_default.nginx
fi
# set full BBB version in settings.yml so it can be displayed in the client
BBB_RELEASE_FILE=/etc/bigbluebutton/bigbluebutton-release
BBB_HTML5_SETTINGS_FILE=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml

View File

@ -0,0 +1,17 @@
#!/bin/bash -e
case "$1" in
remove|failed-upgrade|abort-upgrade|abort-install|disappear)
;;
purge)
# remove file deployed by after-install script if it is still a symlink
if [ "x$(readlink -f /etc/bigbluebutton/nginx/include_default.nginx)" = x/usr/share/bigbluebutton/include_default.nginx ] ; then
rm /etc/bigbluebutton/nginx/include_default.nginx
fi
;;
upgrade)
;;
*)
echo "postinst called with unknown argument \`\$1'" >&2
;;
esac

View File

@ -51,6 +51,10 @@ cp cron.daily/* staging/etc/cron.daily
mkdir -p staging/etc/cron.hourly
cp cron.hourly/bbb-resync-freeswitch staging/etc/cron.hourly
mkdir -p staging/usr/share/bigbluebutton/nginx
cp include_default.nginx staging/usr/share/bigbluebutton/
cp bigbluebutton.target staging/lib/systemd/system/
. ./opts-$DISTRO.sh
@ -60,6 +64,7 @@ cp bigbluebutton.target staging/lib/systemd/system/
fpm -s dir -C ./staging -n $PACKAGE \
--version $VERSION --epoch $EPOCH \
--after-install after-install.sh \
--after-remove after-remove.sh \
--before-install before-install.sh \
--description "BigBlueButton configuration utilities" \
$DIRECTORIES \

View File

@ -0,0 +1,9 @@
# The purpose of this file is to be included in an nginx site configuration.
# you may copy this file to /etc/bigbluebutton/nginx/include_default.nginx and
# adapt it to your needs
#
# If you are running a cluster setup, you need to change bbb-html5.nginx
# copy it to /etc/bigbluebutting/nginx/bbb-html5.nginx and include all the
# components in /usr/share/bigbluebutton/nginx/ that you did not change.
include /usr/share/bigbluebutton/nginx/*.nginx;

View File

@ -13,6 +13,5 @@ server {
}
# Include specific rules for record and playback
include /usr/share/bigbluebutton/nginx/*.nginx;
include /etc/bigbluebutton/nginx/*.nginx; # an overriding set of files, possibly present
}

View File

@ -7,6 +7,7 @@ PACKAGE=$(echo $TARGET | cut -d'_' -f1)
VERSION=$(echo $TARGET | cut -d'_' -f2)
DISTRO=$(echo $TARGET | cut -d'_' -f3)
TAG=$(echo $TARGET | cut -d'_' -f4)
BUILD=$1
#
# Clean up directories
@ -17,7 +18,7 @@ rm -rf staging
# New format
if [ -f private/config/settings.yml ]; then
sed -i "s/HTML5_CLIENT_VERSION/$(($1))/" private/config/settings.yml
sed -i "s/HTML5_CLIENT_VERSION/$(($BUILD))/" private/config/settings.yml
fi
mkdir -p staging/usr/share/bigbluebutton/nginx
@ -97,10 +98,13 @@ cp bbb-html5-frontend@.service staging/usr/lib/systemd/system
mkdir -p staging/usr/share
# replace v=VERSION with build number in head and css files
if [ -f staging/usr/share/meteor/bundle/programs/web.browser/head.html ]; then
sed -i "s/VERSION/$(($BUILD))/" staging/usr/share/meteor/bundle/programs/web.browser/head.html
sed -i "s/VERSION/$(($BUILD))/g" staging/usr/share/meteor/bundle/programs/web.browser/head.html
fi
find staging/usr/share/meteor/bundle/programs/web.browser -name '*.css' -exec sed -i "s/VERSION/$(($BUILD))/g" '{}' \;
# Compress CSS, Javascript and tensorflow WASM binaries used for virtual backgrounds. Keep the
# uncompressed versions as well so it works with mismatched nginx location blocks
find staging/usr/share/meteor/bundle/programs/web.browser -name '*.js' -exec gzip -k -f -9 '{}' \;

View File

@ -83,6 +83,10 @@ case "$1" in
else
echo "Error: FreeSWITCH not installed"
fi
systemctl enable bbb-rap-resque-worker.service
systemctl enable bbb-rap-starter.service
systemctl enable bbb-rap-caption-inbox.service
;;
*)