Merge remote-tracking branch 'upstream/v2.5.x-release' into html5-show-ejectedReason

This commit is contained in:
Gustavo Trott 2022-09-21 15:58:01 -03:00
commit 89ea5d07d3
54 changed files with 571 additions and 294 deletions

View File

@ -112,7 +112,7 @@ jobs:
run: |
sudo sh -c '
cd /root/ && wget -q https://ubuntu.bigbluebutton.org/bbb-install-2.5.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v focal-25-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-25-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
@ -132,7 +132,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

@ -24,7 +24,7 @@ trait PresentationPageCountErrorPubMsgHdlr {
liveMeeting.props.meetingProp.intId, msg.header.userId
)
val body = PresentationPageCountErrorEvtMsgBody(msg.body.podId, msg.body.messageKey, msg.body.code, msg.body.presentationId, msg.body.numberOfPages, msg.body.maxNumberPages, msg.body.presName)
val body = PresentationPageCountErrorEvtMsgBody(msg.body.podId, msg.body.messageKey, msg.body.code, msg.body.presentationId, msg.body.numberOfPages, msg.body.maxNumberPages, msg.body.presName, msg.body.temporaryPresentationId)
val event = PresentationPageCountErrorEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)

View File

@ -19,7 +19,7 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
val envelope = BbbCoreEnvelope(PresentationUploadTokenPassRespMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationUploadTokenPassRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = PresentationUploadTokenPassRespMsgBody(msg.body.podId, token, msg.body.filename, msg.body.tmpPresId)
val body = PresentationUploadTokenPassRespMsgBody(msg.body.podId, token, msg.body.filename, msg.body.temporaryPresentationId)
val event = PresentationUploadTokenPassRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)

View File

@ -13,7 +13,7 @@ case class RemovePresentationPodPubMsgBody(podId: String)
object PresentationUploadTokenReqMsg { val NAME = "PresentationUploadTokenReqMsg" }
case class PresentationUploadTokenReqMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenReqMsgBody) extends StandardMsg
case class PresentationUploadTokenReqMsgBody(podId: String, filename: String, tmpPresId: String)
case class PresentationUploadTokenReqMsgBody(podId: String, filename: String, temporaryPresentationId: String)
object GetAllPresentationPodsReqMsg { val NAME = "GetAllPresentationPodsReqMsg" }
case class GetAllPresentationPodsReqMsg(header: BbbClientMsgHeader, body: GetAllPresentationPodsReqMsgBody) extends StandardMsg
@ -65,7 +65,7 @@ case class PresentationPageCountErrorSysPubMsg(
body: PresentationPageCountErrorSysPubMsgBody
) extends StandardMsg
case class PresentationPageCountErrorSysPubMsgBody(podId: String, messageKey: String, code: String, presentationId: String,
numberOfPages: Int, maxNumberPages: Int, presName: String)
numberOfPages: Int, maxNumberPages: Int, presName: String, temporaryPresentationId: String)
object PdfConversionInvalidErrorSysPubMsg { val NAME = "PdfConversionInvalidErrorSysPubMsg" }
case class PdfConversionInvalidErrorSysPubMsg(
@ -182,7 +182,7 @@ case class PdfConversionInvalidErrorEvtMsgBody(podId: String, messageKey: String
object PresentationUploadTokenPassRespMsg { val NAME = "PresentationUploadTokenPassRespMsg" }
case class PresentationUploadTokenPassRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenPassRespMsgBody) extends StandardMsg
case class PresentationUploadTokenPassRespMsgBody(podId: String, authzToken: String, filename: String, tmpPresId: String)
case class PresentationUploadTokenPassRespMsgBody(podId: String, authzToken: String, filename: String, temporaryPresentationId: String)
object PresentationUploadTokenFailRespMsg { val NAME = "PresentationUploadTokenFailRespMsg" }
case class PresentationUploadTokenFailRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenFailRespMsgBody) extends StandardMsg
@ -194,7 +194,7 @@ case class PresentationConversionUpdateEvtMsgBody(podId: String, messageKey: Str
object PresentationPageCountErrorEvtMsg { val NAME = "PresentationPageCountErrorEvtMsg" }
case class PresentationPageCountErrorEvtMsg(header: BbbClientMsgHeader, body: PresentationPageCountErrorEvtMsgBody) extends BbbCoreMsg
case class PresentationPageCountErrorEvtMsgBody(podId: String, messageKey: String, code: String, presentationId: String, numberOfPages: Int, maxNumberPages: Int, presName: String)
case class PresentationPageCountErrorEvtMsgBody(podId: String, messageKey: String, code: String, presentationId: String, numberOfPages: Int, maxNumberPages: Int, presName: String, temporaryPresentationId: String)
object PresentationPageGeneratedEvtMsg { val NAME = "PresentationPageGeneratedEvtMsg" }
case class PresentationPageGeneratedEvtMsg(header: BbbClientMsgHeader, body: PresentationPageGeneratedEvtMsgBody) extends BbbCoreMsg

View File

@ -927,6 +927,13 @@ public class MeetingService implements MessageListener {
User user = new User(message.userId, message.externalUserId,
message.name, message.role, message.avatarURL, message.guest, message.guestStatus,
message.clientType);
if(m.getMaxUsers() > 0 && m.getUsers().size() >= m.getMaxUsers()) {
m.removeEnteredUser(user.getInternalUserId());
gw.ejectDuplicateUser(message.meetingId, user.getInternalUserId(), user.getFullname(), user.getExternalUserId());
return;
}
m.userJoined(user);
m.setGuestStatusWithId(user.getInternalUserId(), message.guestStatus);
UserSession userSession = getUserSessionWithUserId(user.getInternalUserId());

View File

@ -24,7 +24,7 @@ public class GuestPolicyValidator implements ConstraintValidator<GuestPolicyCons
MeetingService meetingService = ServiceUtils.getMeetingService();
UserSession userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
if(userSession == null || userSession.guestStatus.equals(GuestPolicy.DENY)) {
if(userSession == null || !userSession.guestStatus.equals(GuestPolicy.ALLOW)) {
return false;
}

View File

@ -39,9 +39,8 @@ public class MaxParticipantsValidator implements ConstraintValidator<MaxParticip
boolean rejoin = meeting.getUserById(userSession.internalUserId) != null;
boolean reenter = meeting.getEnteredUserById(userSession.internalUserId) != null;
int joinedUsers = meeting.getUsers().size();
int enteredUsers = meeting.getEnteredUsers().size();
boolean reachedMax = (joinedUsers + enteredUsers) >= maxParticipants;
boolean reachedMax = joinedUsers >= maxParticipants;
if(enabled && !rejoin && !reenter && reachedMax) {
return false;
}

View File

@ -206,7 +206,8 @@ public class PresentationFileProcessor {
DocPageCountFailed progress = new DocPageCountFailed(pres.getPodId(), pres.getMeetingId(),
pres.getId(), pres.getId(),
pres.getName(), "notUsedYet", "notUsedYet",
pres.isDownloadable(), pres.isRemovable(), ConversionMessageConstants.PAGE_COUNT_FAILED_KEY);
pres.isDownloadable(), pres.isRemovable(), ConversionMessageConstants.PAGE_COUNT_FAILED_KEY,
pres.getTemporaryPresentationId());
notifier.sendDocConversionProgress(progress);
@ -232,7 +233,7 @@ public class PresentationFileProcessor {
pres.getId(), pres.getId(),
pres.getName(), "notUsedYet", "notUsedYet",
pres.isDownloadable(), pres.isRemovable(), ConversionMessageConstants.PAGE_COUNT_EXCEEDED_KEY,
e.getPageCount(), e.getMaxNumberOfPages());
e.getPageCount(), e.getMaxNumberOfPages(), pres.getTemporaryPresentationId());
notifier.sendDocConversionProgress(progress);
}

View File

@ -13,11 +13,12 @@ public class DocPageCountExceeded implements IDocConversionMsg {
public final String key;
public final Integer numPages;
public final Integer maxNumPages;
public final String temporaryPresentationId;
public DocPageCountExceeded(String podId, String meetingId, String presId, String presInstance,
String filename, String uploaderId, String authzToken,
Boolean downloadable, Boolean removable, String key,
Integer numPages, Integer maxNumPages) {
Integer numPages, Integer maxNumPages, String temporaryPresentationId) {
this.podId = podId;
this.meetingId = meetingId;
this.presId = presId;
@ -30,5 +31,6 @@ public class DocPageCountExceeded implements IDocConversionMsg {
this.key = key;
this.numPages = numPages;
this.maxNumPages = maxNumPages;
this.temporaryPresentationId = temporaryPresentationId;
}
}

View File

@ -11,10 +11,11 @@ public class DocPageCountFailed implements IDocConversionMsg {
public final Boolean downloadable;
public final Boolean removable;
public final String key;
public final String temporaryPresentationId;
public DocPageCountFailed(String podId, String meetingId, String presId, String presInstance,
String filename, String uploaderId, String authzToken,
Boolean downloadable, Boolean removable, String key) {
Boolean downloadable, Boolean removable, String key, String temporaryPresentationId) {
this.podId = podId;
this.meetingId = meetingId;
this.presId = presId;
@ -25,5 +26,6 @@ public class DocPageCountFailed implements IDocConversionMsg {
this.downloadable = downloadable;
this.removable = removable;
this.key = key;
this.temporaryPresentationId = temporaryPresentationId;
}
}

View File

@ -193,7 +193,7 @@ object MsgBuilder {
val header = BbbClientMsgHeader(PresentationPageCountErrorSysPubMsg.NAME, msg.meetingId, msg.authzToken)
val body = PresentationPageCountErrorSysPubMsgBody(podId = msg.podId, messageKey = msg.key,
code = msg.key, msg.presId, 0, 0, msg.filename)
code = msg.key, msg.presId, 0, 0, msg.filename, msg.temporaryPresentationId)
val req = PresentationPageCountErrorSysPubMsg(header, body)
BbbCommonEnvCoreMsg(envelope, req)
}
@ -204,7 +204,7 @@ object MsgBuilder {
val header = BbbClientMsgHeader(PresentationPageCountErrorSysPubMsg.NAME, msg.meetingId, msg.authzToken)
val body = PresentationPageCountErrorSysPubMsgBody(podId = msg.podId, messageKey = msg.key,
code = msg.key, msg.presId, msg.numPages.intValue(), msg.maxNumPages.intValue(), msg.filename)
code = msg.key, msg.presId, msg.numPages.intValue(), msg.maxNumPages.intValue(), msg.filename, msg.temporaryPresentationId)
val req = PresentationPageCountErrorSysPubMsg(header, body)
BbbCommonEnvCoreMsg(envelope, req)
}

View File

@ -1,11 +1,9 @@
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
RUN apt update && apt -y install locales-all fontconfig libxt6 libxrender1
RUN apt update && apt -y install -t \
bullseye-backports \
libreoffice \
RUN apt update && apt -y install libreoffice \
&& rm -f \
/usr/share/java/ant-apache-log4j-1.10.9.jar \
/usr/share/java/log4j-1.2-1.2.17.jar /usr/share/java/log4j-1.2.jar \

View File

@ -1 +1 @@
BIGBLUEBUTTON_RELEASE=2.5.5
BIGBLUEBUTTON_RELEASE=2.5.6

View File

@ -30,15 +30,19 @@ fi
BBB_WEB_ETC_CONFIG=/etc/bigbluebutton/bbb-web.properties
# We'll create a newline file to ensure bigbluebutton.properties ends with a newline
tmpfile=$(mktemp /tmp/carriage-return.XXXXXX)
echo "\n" > $tmpfile
PROTOCOL=http
if [ -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties ]; then
SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | grep ^bigbluebutton.web.serverURL | tail -n 1 | grep -q https; then
SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $tmpfile $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $tmpfile $BBB_WEB_ETC_CONFIG | grep -v '#' | grep ^bigbluebutton.web.serverURL | tail -n 1 | grep -q https; then
PROTOCOL=https
fi
fi
HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $tmpfile $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
HTML5_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml
BBB_WEB_CONFIG=$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties

View File

@ -418,15 +418,15 @@ start_bigbluebutton () {
if systemctl list-units --full -all | grep -q $TOMCAT_USER.service; then
TOMCAT_SERVICE=$TOMCAT_USER
fi
systemctl start $TOMCAT_SERVICE || {
echo
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI or BBB-Demo."
echo "# Run the command:"
echo "# sudo journalctl -u $TOMCAT_SERVICE"
echo "# To better understand the ERROR"
}
[ -z "$TOMCAT_SERVICE" ] || systemctl start $TOMCAT_SERVICE || {
echo
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI or BBB-Demo."
echo "# Run the command:"
echo "# sudo journalctl -u $TOMCAT_SERVICE"
echo "# To better understand the ERROR"
}
fi
systemctl start nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
@ -1154,16 +1154,19 @@ check_state() {
if bbb-conf --status | grep -q inactive; then
if bbb-conf --status | grep -q tomcat9; then
echo "# Warning: $TOMCAT_SERVICE is not started correctly"
echo "#"
echo "# $(bbb-conf --status | grep inactive | grep $TOMCAT_SERVICE)"
echo "#"
if systemctl list-units --full -all | grep -q $TOMCAT_USER.service; then
TOMCAT_SERVICE=$TOMCAT_USER
if bbb-conf --status | grep -q inactive | grep -q $TOMCAT_SERVICE; then
echo "# Warning: $TOMCAT_SERVICE is not started correctly"
echo "#"
fi
fi
if bbb-conf --status | grep inactive | grep -vq tomcat9; then
if bbb-conf --status | grep inactive; then
echo "# Error: Detected some processes have not started correctly"
echo "#"
echo "# $(bbb-conf --status | grep inactive | grep -v $TOMCAT_SERVICE)"
echo "# $(bbb-conf --status | grep inactive)"
echo "#"
fi
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

@ -19,6 +19,22 @@ function breakouts() {
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
Logger.debug('Publishing Breakouts', { meetingId, userId });
const fields = {
fields: {
[`url_${userId}`]: 1,
breakoutId: 1,
externalId: 1,
freeJoin: 1,
isDefaultName: 1,
joinedUsers: 1,
name: 1,
parentMeetingId: 1,
sequence: 1,
shortName: 1,
timeRemaining: 1,
},
};
if (!!User && User.role === ROLE_MODERATOR) {
const presenterSelector = {
$or: [
@ -39,7 +55,7 @@ function breakouts() {
return condition;
};
publicationSafeGuard(comparisonFunc, this);
return Breakouts.find(presenterSelector);
return Breakouts.find(presenterSelector, fields);
}
const selector = {
@ -58,22 +74,6 @@ function breakouts() {
],
};
const fields = {
fields: {
[`url_${userId}`]: 1,
breakoutId: 1,
externalId: 1,
freeJoin: 1,
isDefaultName: 1,
joinedUsers: 1,
name: 1,
parentMeetingId: 1,
sequence: 1,
shortName: 1,
timeRemaining: 1,
},
};
return Breakouts.find(selector, fields);
}

View File

@ -7,20 +7,20 @@ export default function handlePresentationUploadTokenPass({ body, header }, meet
check(body, Object);
const { userId } = header;
const { podId, authzToken, filename, tmpPresId } = body;
const { podId, authzToken, filename, temporaryPresentationId } = body;
check(userId, String);
check(podId, String);
check(authzToken, String);
check(filename, String);
check(tmpPresId, String)
check(temporaryPresentationId, String)
const selector = {
meetingId,
podId,
userId,
filename,
tmpPresId,
temporaryPresentationId,
};
const modifier = {
@ -29,7 +29,7 @@ export default function handlePresentationUploadTokenPass({ body, header }, meet
userId,
filename,
authzToken,
tmpPresId,
temporaryPresentationId,
failed: false,
used: false,
};

View File

@ -3,7 +3,7 @@ import { check } from 'meteor/check';
import { extractCredentials } from '/imports/api/common/server/helpers';
import Logger from '/imports/startup/server/logger';
export default function requestPresentationUploadToken(podId, filename, tmpPresId) {
export default function requestPresentationUploadToken(podId, filename, temporaryPresentationId) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'PresentationUploadTokenReqMsg';
@ -15,12 +15,12 @@ export default function requestPresentationUploadToken(podId, filename, tmpPresI
check(requesterUserId, String);
check(podId, String);
check(filename, String);
check(tmpPresId, String);
check(temporaryPresentationId, String);
const payload = {
podId,
filename,
tmpPresId
temporaryPresentationId
};
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);

View File

@ -4,7 +4,7 @@ import PresentationUploadToken from '/imports/api/presentation-upload-token';
import Logger from '/imports/startup/server/logger';
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
function presentationUploadToken(podId, filename, tmpPresId) {
function presentationUploadToken(podId, filename, temporaryPresentationId) {
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
@ -16,14 +16,14 @@ function presentationUploadToken(podId, filename, tmpPresId) {
check(podId, String);
check(filename, String);
check(tmpPresId, String);
check(temporaryPresentationId, String);
const selector = {
meetingId,
podId,
userId,
filename,
tmpPresId,
temporaryPresentationId,
};
Logger.debug('Publishing PresentationUploadToken', { meetingId, userId });

View File

@ -25,6 +25,7 @@ export default function handlePresentationConversionUpdate({ body }, meetingId)
const {
presentationId, podId, messageKey: status, presName: presentationName,
temporaryPresentationId
} = body;
check(meetingId, String);
@ -73,9 +74,17 @@ export default function handlePresentationConversionUpdate({ body }, meetingId)
id: presentationId ?? body.presentationToken,
};
const modifier = {
$set: Object.assign({ meetingId, podId }, statusModifier),
};
let modifier
if (temporaryPresentationId){
modifier = {
$set: Object.assign({ meetingId, podId, temporaryPresentationId, }, statusModifier),
};
} else {
modifier = {
$set: Object.assign({ meetingId, podId }, statusModifier),
};
}
try {
const { insertedId } = Presentations.upsert(selector, modifier);

View File

@ -17,6 +17,7 @@ const propTypes = {
const DEFAULT_LANGUAGE = Meteor.settings.public.app.defaultSettings.application.fallbackLocale;
const CLIENT_VERSION = Meteor.settings.public.app.html5ClientBuild;
const FALLBACK_ON_EMPTY_STRING = Meteor.settings.public.app.fallbackOnEmptyLocaleString;
const RTL_LANGUAGES = ['ar', 'dv', 'fa', 'he'];
const LARGE_FONT_LANGUAGES = ['te', 'km'];
@ -163,7 +164,7 @@ class IntlStartup extends Component {
{normalizedLocale
&& (
<IntlProvider locale={normalizedLocale} messages={messages}>
<IntlProvider fallbackOnEmptyString={FALLBACK_ON_EMPTY_STRING} locale={normalizedLocale} messages={messages}>
{children}
</IntlProvider>
)

View File

@ -34,6 +34,26 @@ process.on('uncaughtException', (err) => {
process.exit(1);
});
const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`
const serverHealth = () => {
const memoryData = process.memoryUsage();
const memoryUsage = {
rss: formatMemoryUsage(memoryData.rss),
heapTotal: formatMemoryUsage(memoryData.heapTotal),
heapUsed: formatMemoryUsage(memoryData.heapUsed),
external: formatMemoryUsage(memoryData.external),
}
const cpuData = process.cpuUsage();
const cpuUsage = {
system: formatMemoryUsage(cpuData.system),
user: formatMemoryUsage(cpuData.user),
}
Logger.info('Server health ', {memoryUsage, cpuUsage});
};
Meteor.startup(() => {
const APP_CONFIG = Meteor.settings.public.app;
const CDN_URL = APP_CONFIG.cdn;
@ -41,6 +61,16 @@ Meteor.startup(() => {
Logger.warn(`Started bbb-html5 process with instanceId=${instanceId}`);
const LOG_CONFIG = Meteor.settings.private.serverLog;
const { healthChecker } = LOG_CONFIG;
const { enable: enableHealthCheck, intervalMs: healthCheckInterval } = healthChecker;
if (enableHealthCheck) {
Meteor.setInterval(() => {
serverHealth();
}, healthCheckInterval);
}
const { customHeartbeat } = APP_CONFIG;
if (customHeartbeat) {

View File

@ -3,7 +3,17 @@ import { createLogger, format, transports } from 'winston';
import WinstonPromTransport from './prom-metrics/winstonPromTransport';
const LOG_CONFIG = Meteor?.settings?.private?.serverLog || {};
const { level } = LOG_CONFIG;
const { level, includeServerInfo } = LOG_CONFIG;
const serverInfoFormat = format.printf(({ level, message, timestamp, ...metadata }) => {
const instanceId = parseInt(process.env.INSTANCE_ID, 10) || 1;
const role = process.env.BBB_HTML5_ROLE;
const server = includeServerInfo && !Meteor?.isDevelopment ? `${role}-${instanceId} ` : "";
let msg = `${timestamp} ${server}[${level}] : ${message}`;
if (metadata) msg += JSON.stringify(metadata)
return msg
});
const Logger = createLogger({
level,
@ -11,6 +21,8 @@ const Logger = createLogger({
format.colorize({ level: true }),
format.splat(),
format.simple(),
format.timestamp(),
serverInfoFormat,
),
transports: [
// console logging

View File

@ -203,12 +203,9 @@ const AssignBtns = styled(Button)`
color: ${colorPrimary};
font-size: ${fontSizeSmall};
white-space: nowrap;
margin: 0 auto 0 0;
margin: 3px auto;
align-self: flex-end;
[dir="rtl"] & {
margin: 0 0 0 auto;
}
width: 100%;
`;
const CheckBoxesContainer = styled(FlexRow)`

View File

@ -172,7 +172,7 @@ class LockViewersComponent extends Component {
<Styled.Bold>{intl.formatMessage(intlMessages.featuresLable)}</Styled.Bold>
<Styled.Bold>{intl.formatMessage(intlMessages.lockStatusLabel)}</Styled.Bold>
</Styled.SubHeader>
<Styled.Row>
<Styled.Row data-test="lockShareWebcamItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -197,7 +197,7 @@ class LockViewersComponent extends Component {
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
<Styled.Row>
<Styled.Row data-test="lockSeeOtherViewersWebcamItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -222,7 +222,7 @@ class LockViewersComponent extends Component {
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
<Styled.Row>
<Styled.Row data-test="lockShareMicrophoneItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -250,7 +250,7 @@ class LockViewersComponent extends Component {
{isChatEnabled() ? (
<Fragment>
<Styled.Row>
<Styled.Row data-test="lockPublicChatItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -275,7 +275,7 @@ class LockViewersComponent extends Component {
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
<Styled.Row>
<Styled.Row data-test="lockPrivateChatItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -305,7 +305,7 @@ class LockViewersComponent extends Component {
}
{NotesService.isEnabled()
? (
<Styled.Row>
<Styled.Row data-test="lockEditSharedNotesItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -333,7 +333,7 @@ class LockViewersComponent extends Component {
)
: null
}
<Styled.Row>
<Styled.Row data-test="lockUserListItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
@ -359,7 +359,7 @@ class LockViewersComponent extends Component {
</Styled.Col>
</Styled.Row>
<Styled.Row>
<Styled.Row data-test="hideViewersCursorItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>

View File

@ -205,7 +205,7 @@ class Presentation extends PureComponent {
this.currentPresentationToastId = toast(this.renderCurrentPresentationToast(), {
onClose: () => { this.currentPresentationToastId = null; },
autoClose: shouldCloseToast,
className: 'actionToast',
className: 'actionToast currentPresentationToast',
});
}
}

View File

@ -275,32 +275,45 @@ class PresentationUploader extends Component {
...propPresentations,
...presentations,
});
const presStateMapped = presState.map((presentation) => {
propPresentations.forEach((propPres) => {
if (propPres.id === presentation.id) {
const prevPropPres = prevPropPresentations.find((pres) => pres.id === propPres.id);
if (propPres.isCurrent !== prevPropPres?.isCurrent) {
presentation.isCurrent = propPres.isCurrent;
shouldUpdateState = true;
}
}
});
return presentation;
}).filter((presentation) => {
const presStateFiltered = presState.filter((presentation) => {
const currentPropPres = propPresentations.find((pres) => pres.id === presentation.id);
const prevPropPres = prevPropPresentations.find((pres) => pres.id === presentation.id);
const hasConversionError = presentation?.conversion?.error;
const finishedConversion = presentation?.conversion?.done || currentPropPres?.conversion?.done;
const hasTemporaryId = presentation.id.startsWith(presentation.filename);
if (hasConversionError || (!finishedConversion && hasTemporaryId)) return true;
if (!currentPropPres) return false;
if(presentation?.conversion?.done !== finishedConversion) {
shouldUpdateState = true;
}
if (currentPropPres.isCurrent !== prevPropPres?.isCurrent) {
presentation.isCurrent = currentPropPres.isCurrent;
}
presentation.conversion = currentPropPres.conversion;
presentation.isDownloadable = currentPropPres.isDownloadable;
presentation.isRemovable = currentPropPres.isRemovable;
return true;
}).filter(presentation => {
const duplicated = presentations.find(
(pres) => pres.filename === presentation.filename
&& pres.id !== presentation.id
);
if (duplicated
&& duplicated.id.startsWith(presentation.filename)
&& !presentation.id.startsWith(presentation.filename)
&& presentation?.conversion?.done === duplicated?.conversion?.done) {
return false;
}
return true;
});
if (shouldUpdateState) {
this.setState({
presentations: presStateMapped,
presentations: _.uniqBy(presStateFiltered, 'id')
});
}
@ -729,7 +742,7 @@ class PresentationUploader extends Component {
</tr>
</thead>
<tbody>
{presentationsSorted.map((item) => this.renderPresentationItem(item))}
{_.uniqBy(presentationsSorted, 'id').map((item) => this.renderPresentationItem(item))}
</tbody>
</Styled.Table>
</Styled.FileList>
@ -747,7 +760,7 @@ class PresentationUploader extends Component {
let converted = 0;
let presentationsSorted = presentations
.filter((p) => (p.upload.progress || p.conversion.status) && p.file)
.filter((p) => (p.upload.progress || p.upload.error || p.conversion.status) && p.file)
.sort((a, b) => a.uploadTimestamp - b.uploadTimestamp)
.sort((a, b) => a.conversion.done - b.conversion.done);
@ -992,7 +1005,7 @@ class PresentationUploader extends Component {
}
renderPresentationItemStatus(item) {
const { intl } = this.props;
const { intl, fileSizeMax } = this.props;
if (!item.upload.done && item.upload.progress === 0) {
return intl.formatMessage(intlMessages.fileToUpload);
}
@ -1006,13 +1019,13 @@ class PresentationUploader extends Component {
const constraint = {};
if (item.upload.done && item.upload.error) {
if (item.conversion.status === 'FILE_TOO_LARGE') {
constraint['0'] = ((item.conversion.maxFileSize) / 1000 / 1000).toFixed(2);
}
if (item.upload.progress < 100) {
const errorMessage = intlMessages.badConnectionError;
return intl.formatMessage(errorMessage);
if (item.conversion.status === 'FILE_TOO_LARGE' || item.upload.status === 413) {
constraint['0'] = (fileSizeMax / 1000 / 1000).toFixed(2);
} else {
if (item.upload.progress < 100) {
const errorMessage = intlMessages.badConnectionError;
return intl.formatMessage(errorMessage);
}
}
const errorMessage = intlMessages[item.upload.status] || intlMessages.genericError;

View File

@ -68,7 +68,7 @@ const dispatchTogglePresentationDownloadable = (presentation, newState) => {
const observePresentationConversion = (
meetingId,
tmpPresId,
temporaryPresentationId,
onConversion,
) => new Promise((resolve) => {
const conversionTimeout = setTimeout(() => {
@ -89,7 +89,7 @@ const observePresentationConversion = (
query.observe({
added: (doc) => {
if (doc.temporaryPresentationId !== tmpPresId) return;
if (doc.temporaryPresentationId !== temporaryPresentationId) return;
if (doc.conversion.status === 'FILE_TOO_LARGE' || doc.conversion.status === 'UNSUPPORTED_DOCUMENT') {
onConversion(doc.conversion);
@ -98,7 +98,7 @@ const observePresentationConversion = (
}
},
changed: (newDoc) => {
if (newDoc.temporaryPresentationId !== tmpPresId) return;
if (newDoc.temporaryPresentationId !== temporaryPresentationId) return;
onConversion(newDoc.conversion);
@ -117,12 +117,12 @@ const observePresentationConversion = (
});
const requestPresentationUploadToken = (
tmpPresId,
temporaryPresentationId,
podId,
meetingId,
filename,
) => new Promise((resolve, reject) => {
makeCall('requestPresentationUploadToken', podId, filename, tmpPresId);
makeCall('requestPresentationUploadToken', podId, filename, temporaryPresentationId);
let computation = null;
const timeout = setTimeout(() => {
@ -132,13 +132,13 @@ const requestPresentationUploadToken = (
Tracker.autorun((c) => {
computation = c;
const sub = Meteor.subscribe('presentation-upload-token', podId, filename, tmpPresId);
const sub = Meteor.subscribe('presentation-upload-token', podId, filename, temporaryPresentationId);
if (!sub.ready()) return;
const PresentationToken = PresentationUploadToken.findOne({
podId,
meetingId,
tmpPresId,
temporaryPresentationId,
used: false,
});
@ -165,13 +165,13 @@ const uploadAndConvertPresentation = (
onProgress,
onConversion,
) => {
const tmpPresId = _.uniqueId(Random.id(20))
const temporaryPresentationId = _.uniqueId(Random.id(20))
const data = new FormData();
data.append('fileUpload', file);
data.append('conference', meetingId);
data.append('room', meetingId);
data.append('temporaryPresentationId', tmpPresId);
data.append('temporaryPresentationId', temporaryPresentationId);
// TODO: Currently the uploader is not related to a POD so the id is fixed to the default
data.append('pod_id', podId);
@ -183,12 +183,12 @@ const uploadAndConvertPresentation = (
body: data,
};
return requestPresentationUploadToken(tmpPresId, podId, meetingId, file.name)
return requestPresentationUploadToken(temporaryPresentationId, podId, meetingId, file.name)
.then((token) => {
makeCall('setUsedToken', token);
return futch(endpoint.replace('upload', `${token}/upload`), opts, onProgress);
})
.then(() => observePresentationConversion(meetingId, tmpPresId, onConversion))
.then(() => observePresentationConversion(meetingId, temporaryPresentationId, onConversion))
// Trap the error so we can have parallel upload
.catch((error) => {
logger.debug({
@ -197,7 +197,7 @@ const uploadAndConvertPresentation = (
error,
},
}, 'Generic presentation upload exception catcher');
observePresentationConversion(meetingId, tmpPresId, onConversion);
observePresentationConversion(meetingId, temporaryPresentationId, onConversion);
onUpload({ error: true, done: true, status: error.code });
return Promise.resolve();
});

View File

@ -314,12 +314,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) {
const { sharedDevices } = this.props;
const { webcamDeviceId } = this.state;
const shared = sharedDevices.includes(webcamDeviceId);
if (type !== EFFECT_TYPES.NONE_TYPE) {
return this.startVirtualBackground(this.currentVideoStream, type, name);
return this.startVirtualBackground(this.currentVideoStream, type, name).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);
}
}
@ -366,12 +387,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

@ -27,7 +27,7 @@ const clearPreview = (annotation) => {
const clearFakeAnnotations = () => {
UnsentAnnotations.remove({});
Annotations.remove({ id: /-fake/g });
Annotations.remove({ id: /-fake/g, annotationType: { $ne: 'text' } });
}
function handleAddedLiveSyncPreviewAnnotation({

View File

@ -240,16 +240,22 @@ class VirtualBackgroundService {
changeBackgroundImage(parameters = null) {
const virtualBackgroundImagePath = getVirtualBgImagePath();
let imagesrc = virtualBackgroundImagePath + '';
let name = '';
let type = 'blur';
let isVirtualBackground = false;
if (parameters != null && Object.keys(parameters).length > 0) {
imagesrc = parameters.name;
name = parameters.name;
type = parameters.type;
this._options.virtualBackground.isVirtualBackground = parameters.isVirtualBackground;
isVirtualBackground = parameters.isVirtualBackground;
}
this._options.virtualBackground.virtualSource = virtualBackgroundImagePath + name;
this._options.virtualBackground.backgroundType = type;
this._options.virtualBackground.isVirtualBackground = isVirtualBackground;
if (this._options.virtualBackground.backgroundType === 'image') {
this._virtualImage = document.createElement('img');
this._virtualImage.crossOrigin = 'anonymous';
this._virtualImage.src = virtualBackgroundImagePath + name;
}
this._virtualImage = document.createElement('img');
this._virtualImage.crossOrigin = 'anonymous';
this._virtualImage.src = virtualBackgroundImagePath + imagesrc;
}
/**

View File

@ -423,43 +423,140 @@
}
}
},
"@formatjs/intl-displaynames": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-1.2.10.tgz",
"integrity": "sha512-GROA2RP6+7Ouu0WnHFF78O5XIU7pBfI19WM1qm93l6MFWibUk67nCfVCK3VAYJkLy8L8ZxjkYT11VIAfvSz8wg==",
"@formatjs/ecma402-abstract": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.12.0.tgz",
"integrity": "sha512-0/wm9b7brUD40kx7KSE0S532T8EfH06Zc41rGlinoNyYXnuusR6ull2x63iFJgVXgwahm42hAW7dcYdZ+llZzA==",
"requires": {
"@formatjs/intl-utils": "^2.3.0"
"@formatjs/intl-localematcher": "0.2.31",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/fast-memoize": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.6.tgz",
"integrity": "sha512-9CWZ3+wCkClKHX+i5j+NyoBVqGf0pIskTo6Xl6ihGokYM2yqSSS68JIgeo+99UIHc+7vi9L3/SDSz/dWI9SNlA==",
"requires": {
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/icu-messageformat-parser": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.7.tgz",
"integrity": "sha512-KM4ikG5MloXMulqn39Js3ypuVzpPKq/DDplvl01PE2qD9rAzFO8YtaUCC9vr9j3sRXwdHPeTe8r3J/8IJgvYEQ==",
"requires": {
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/icu-skeleton-parser": "1.3.13",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/icu-skeleton-parser": {
"version": "1.3.13",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.13.tgz",
"integrity": "sha512-qb1kxnA4ep76rV+d9JICvZBThBpK5X+nh1dLmmIReX72QyglicsaOmKEcdcbp7/giCWfhVs6CXPVA2JJ5/ZvAw==",
"requires": {
"@formatjs/ecma402-abstract": "1.12.0",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/intl": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.4.0.tgz",
"integrity": "sha512-6b3ex1nz9ZET+Jx/ApYhX62Q/SvZvNK81qG1XAFUKWylJUIFd/bm8hG6CMgh7QVzAeFPa3LIf1y0BkNAQ9VYEw==",
"requires": {
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/fast-memoize": "1.2.6",
"@formatjs/icu-messageformat-parser": "2.1.7",
"@formatjs/intl-displaynames": "6.1.2",
"@formatjs/intl-listformat": "7.1.2",
"intl-messageformat": "10.1.4",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/intl-displaynames": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.1.2.tgz",
"integrity": "sha512-JbZANoYwNXNy+6NlicVe1/tpiyp+NKJD0WTHgp14aQbMaAg66VVPNAruFmhfhkIABF4jGvc4tdKYAANmWD8W6w==",
"requires": {
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/intl-localematcher": "0.2.31",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/intl-listformat": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-1.4.8.tgz",
"integrity": "sha512-WNMQlEg0e50VZrGIkgD5n7+DAMGt3boKi1GJALfhFMymslJb5i+5WzWxyj/3a929Z6MAFsmzRIJjKuv+BxKAOQ==",
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.1.2.tgz",
"integrity": "sha512-WfWkJ8k41jZIhXgBtC2T1SpTSKYig99g9MVqrVRco4kduv/6GUWq1eMjk84qZfbU4rwdwc8qct+/gB6DTS17+w==",
"requires": {
"@formatjs/intl-utils": "^2.3.0"
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/intl-localematcher": "0.2.31",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/intl-relativetimeformat": {
"version": "4.5.16",
"resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-4.5.16.tgz",
"integrity": "sha512-IQ0haY97oHAH5OYUdykNiepdyEWj3SAT+Fp9ZpR85ov2JNiFx+12WWlxlVS8ehdyncC2ZMt/SwFIy2huK2+6/A==",
"@formatjs/intl-localematcher": {
"version": "0.2.31",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz",
"integrity": "sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA==",
"requires": {
"@formatjs/intl-utils": "^2.3.0"
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"@formatjs/intl-unified-numberformat": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz",
"integrity": "sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==",
"requires": {
"@formatjs/intl-utils": "^2.3.0"
}
},
"@formatjs/intl-utils": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz",
"integrity": "sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ=="
},
"@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@ -640,11 +737,6 @@
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/invariant": {
"version": "2.2.34",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz",
"integrity": "sha512-lYUtmJ9BqUN688fGY1U1HZoWT1/Jrmgigx2loq4ZcJpICECm/Om3V314BxdzypO0u5PORKGMM6x0OXaljV1YFg=="
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -2926,26 +3018,22 @@
"side-channel": "^1.0.4"
}
},
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"version": "10.1.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.1.4.tgz",
"integrity": "sha512-tXCmWCXhbeHOF28aIf5b9ce3kwdwGyIiiSXVZsyDwksMiGn5Tp0MrMvyeuHuz4uN1UL+NfGOztHmE+6aLFp1wQ==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/fast-memoize": "1.2.6",
"@formatjs/icu-messageformat-parser": "2.1.7",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"invariant": {
@ -5210,22 +5298,27 @@
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"react-intl": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-3.12.1.tgz",
"integrity": "sha512-cgumW29mwROIqyp8NXStYsoIm27+8FqnxykiLSawWjOxGIBeLuN/+p2srei5SRIumcJefOkOIHP+NDck05RgHg==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.1.0.tgz",
"integrity": "sha512-aNy7wX/ZfKgpTv2x27E1sio50fnTrSWD96yfQdVfUfvZrIed6rVPccVxfAtLnIK/M9L1TGrckne3kwFj3Ipikg==",
"requires": {
"@formatjs/intl-displaynames": "^1.2.0",
"@formatjs/intl-listformat": "^1.4.1",
"@formatjs/intl-relativetimeformat": "^4.5.9",
"@formatjs/intl-unified-numberformat": "^3.2.0",
"@formatjs/intl-utils": "^2.2.0",
"@formatjs/ecma402-abstract": "1.12.0",
"@formatjs/icu-messageformat-parser": "2.1.7",
"@formatjs/intl": "2.4.0",
"@formatjs/intl-displaynames": "6.1.2",
"@formatjs/intl-listformat": "7.1.2",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/invariant": "^2.2.31",
"@types/react": "16 || 17 || 18",
"hoist-non-react-statics": "^3.3.2",
"intl-format-cache": "^4.2.21",
"intl-messageformat": "^7.8.4",
"intl-messageformat-parser": "^3.6.4",
"shallow-equal": "^1.2.1"
"intl-messageformat": "10.1.4",
"tslib": "2.4.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"react-is": {
@ -5573,11 +5666,6 @@
"crypt": ">= 0.0.1"
}
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",

View File

@ -62,7 +62,7 @@
"react-dom": "^16.14.0",
"react-draggable": "^4.4.5",
"react-dropzone": "^7.0.1",
"react-intl": "^3.12.1",
"react-intl": "^6.1.0",
"react-loading-skeleton": "^3.0.3",
"react-modal": "~3.6.1",
"react-player": "^2.10.0",

View File

@ -203,6 +203,10 @@ public:
- critical
- danger
- warning
# Whether the fallback mechanism should be used
# when the locale string is empty. If false, the empty
# string will be returned.
fallbackOnEmptyLocaleString: true
externalVideoPlayer:
enabled: true
kurento:
@ -794,6 +798,10 @@ private:
serverLog:
level: info
streamerLog: false
includeServerInfo: true
healthChecker:
enable: true
intervalMs: 30000
minBrowserVersions:
- browser: chrome
version: 72

View File

@ -661,7 +661,7 @@
"app.notification.recordingStop": "این جلسه ضبط نمی‌شود",
"app.notification.recordingPaused": "این جلسه دیگر ضبط نمی‌شود",
"app.notification.recordingAriaLabel": "زمان ضبط‌شده",
"app.notification.userJoinPushAlert": "{0} به جلسه پیوستند",
"app.notification.userJoinPushAlert": "{0} به جلسه پیوست",
"app.notification.userLeavePushAlert": "{0} جلسه را ترک کرد",
"app.submenu.notification.raiseHandLabel": "بالابردن دست",
"app.shortcut-help.title": "میانبرهای صفحه کلید",

View File

@ -643,6 +643,7 @@
"app.userList.guest.privateMessageLabel": "Message",
"app.userList.guest.acceptLabel": "Accepter",
"app.userList.guest.denyLabel": "Refuser",
"app.userList.guest.feedbackMessage": "Opération en cours :",
"app.user-info.title": "Recherche dans l'annuaire",
"app.toast.breakoutRoomEnded": "La réunion privée s'est terminée. Veuillez rejoindre l'audio.",
"app.toast.chat.public": "Nouveau message de discussion publique",

View File

@ -643,6 +643,7 @@
"app.userList.guest.privateMessageLabel": "Üzenet",
"app.userList.guest.acceptLabel": "Engedélyezés",
"app.userList.guest.denyLabel": "Tiltás",
"app.userList.guest.feedbackMessage": "Alkalmazott művelet:",
"app.user-info.title": "Címtárkeresés",
"app.toast.breakoutRoomEnded": "A csoportterem bezárult. Csatlakozz újra a megbeszéléshez",
"app.toast.chat.public": "Új nyilvános üzenet",
@ -1018,6 +1019,11 @@
"app.learningDashboard.userDetails.response": "Válasz",
"app.learningDashboard.userDetails.mostCommonAnswer": "Leggyakoribb válasz",
"app.learningDashboard.userDetails.anonymousAnswer": "Névtelen szavazás",
"app.learningDashboard.userDetails.talkTime": "Beszélgetés ideje",
"app.learningDashboard.userDetails.messages": "Üzenetek",
"app.learningDashboard.userDetails.emojis": "Hangulatjelek",
"app.learningDashboard.userDetails.raiseHands": "Kézfeltartások",
"app.learningDashboard.userDetails.pollVotes": "Szavazatok",
"app.learningDashboard.usersTable.title": "Áttekintés",
"app.learningDashboard.usersTable.colOnline": "Kapcsolódás ideje",
"app.learningDashboard.usersTable.colTalk": "Beszélgetés ideje",

View File

@ -64,7 +64,7 @@
"app.userList.sharingWebcam": "Webcam",
"app.userList.menuTitleContext": "Opsi yang tersedia",
"app.userList.chatListItem.unreadSingular": "Satu pesan baru",
"app.userList.chatListItem.unreadPlural": "[0] pesan baru",
"app.userList.chatListItem.unreadPlural": "{0} pesan baru",
"app.userList.menu.chat.label": "Mulai obrolan privat",
"app.userList.menu.clearStatus.label": "Bersihkan status",
"app.userList.menu.removeUser.label": "Hapus pengguna",

View File

@ -73,7 +73,7 @@
"app.userList.you": "당신",
"app.userList.locked": "잠김",
"app.userList.byModerator": "(주관자)에 의해 ",
"app.userList.label": "사용자 리스트",
"app.userList.label": "사용자 목록",
"app.userList.toggleCompactView.label": "간단한 보기로 전환",
"app.userList.moderator": "주관자",
"app.userList.mobile": "모바일",
@ -345,7 +345,7 @@
"app.navBar.settingsDropdown.helpDesc": "비디오 도움말로 사용자 연결 (새로운탭 열어서)",
"app.navBar.settingsDropdown.endMeetingDesc": "현재 미팅 끝내기",
"app.navBar.settingsDropdown.endMeetingLabel": "미팅 끝",
"app.navBar.userListToggleBtnLabel": "사용자 리스트 전환",
"app.navBar.userListToggleBtnLabel": "사용자 목록보기 전환",
"app.navBar.toggleUserList.ariaLabel": "사용자와 메시지 전환",
"app.navBar.toggleUserList.newMessages": "새로운 메시지 알림과 함께",
"app.navBar.toggleUserList.newMsgAria": "{0}으로 부터 신규 메시지",
@ -608,7 +608,7 @@
"app.error.fallback.presentation.title": "에러 발생",
"app.error.fallback.presentation.description": "기록 되었습니다. 페이지를 새로고침 해 보세요 ",
"app.error.fallback.presentation.reloadButton": "새로고침",
"app.guest.waiting": "합류 허가를 기다림",
"app.guest.waiting": "승인 대기 중",
"app.guest.errorSeeConsole": "오류: 자세한 내용은 콘솔에서 확인",
"app.guest.noModeratorResponse": "주관자가 응답하지 않습니다.",
"app.guest.noSessionToken": "세션 토큰을 수신하지 못했습니다.",
@ -619,16 +619,16 @@
"app.guest.meetingEnded": "미팅이 종료되었습니다.",
"app.guest.guestWait": "주관자가 회의참여를 승인할 때까지 기다려주십시오.",
"app.guest.guestDeny": "미팅 참가가 거부된 게스트",
"app.guest.seatWait": "승인을 대기 중인 게스트",
"app.guest.seatWait": "승인을 기다리는 게스트",
"app.guest.allow": "승인된 게스트",
"app.guest.firstPositionInWaitingQueue": "귀하는 대기열의 첫번째에 있습니다.",
"app.guest.positionInWaitingQueue": "대기열 내 귀하의 위치:",
"app.guest.guestInvalid": "게스트 사용자가 유효하지 않습니다",
"app.guest.meetingForciblyEnded": "강제 종료된 회의에는 참여할 수 없습니다",
"app.userList.guest.waitingUsers": "사용자 기다림",
"app.userList.guest.waitingUsers": "대기중인 사용자",
"app.userList.guest.waitingUsersTitle": "사용자 관리",
"app.userList.guest.optionTitle": "보류중인 사용자 검토",
"app.userList.guest.allowAllAuthenticated": "인증된 자를 모두 허용",
"app.userList.guest.optionTitle": "대기중인 사용자 보기",
"app.userList.guest.allowAllAuthenticated": "인증되었으면 허용",
"app.userList.guest.allowAllGuests": "모든 게스트 허용",
"app.userList.guest.allowEveryone": "모두 허용",
"app.userList.guest.denyEveryone": "모두 거절",

View File

@ -6,10 +6,10 @@
"app.chat.disconnected": "Вы отсоединены, сообщения не могут быть отправлены",
"app.chat.locked": "Чат заблокирован, сообщения не могут быть отправлены",
"app.chat.inputLabel": "Ввод сообщений для чата {0}",
"app.chat.inputPlaceholder": "Сообщение {0}",
"app.chat.inputPlaceholder": "Сообщение в {0}",
"app.chat.titlePublic": "Общий чат",
"app.chat.titlePrivate": "Приватный чат с {0}",
"app.chat.partnerDisconnected": "{0} покинул конференцию",
"app.chat.partnerDisconnected": "{0} покинул(а) конференцию",
"app.chat.closeChatLabel": "Закрыть {0}",
"app.chat.hideChatLabel": "Скрыть {0}",
"app.chat.moreMessages": "Больше сообщений ниже",
@ -43,7 +43,9 @@
"app.captions.menu.backgroundColor": "Цвет фона",
"app.captions.menu.previewLabel": "Предварительный просмотр",
"app.captions.menu.cancelLabel": "Отмена",
"app.captions.hide": "Скрыть скрытые субтитры",
"app.captions.hide": "Скрыть субтитры",
"app.captions.ownership": "Стать владельцем",
"app.captions.ownershipTooltip": "Вы будете назначены владельцем {0} субтитров",
"app.captions.dictationStart": "Начать диктовку",
"app.captions.dictationStop": "Остановить диктовку",
"app.captions.dictationOnDesc": "Включить распознавание речи",
@ -171,6 +173,7 @@
"app.presentation.options.fullscreen": "Полный экран",
"app.presentation.options.exitFullscreen": "Выход из режима полного экрана",
"app.presentation.options.minimize": "Свернуть",
"app.presentation.options.snapshot": "Снимок текущего слайда",
"app.presentation.options.downloading": "Загрузка...",
"app.presentation.options.downloaded": "Текущая презентация была скачана",
"app.presentation.options.downloadFailed": "Невозможно скачать текущую презентацию",
@ -339,7 +342,7 @@
"app.navBar.settingsDropdown.hotkeysLabel": "Клавиши быстрого доступа",
"app.navBar.settingsDropdown.hotkeysDesc": "Список доступных клавиш быстрого доступа",
"app.navBar.settingsDropdown.helpLabel": "Справка",
"app.navBar.settingsDropdown.helpDesc": "Ссылает пользователей на видеоуроки ( в новой вкладке)",
"app.navBar.settingsDropdown.helpDesc": "Ссылает пользователей на видеоуроки (в новой вкладке)",
"app.navBar.settingsDropdown.endMeetingDesc": "Прервать текущую конференцию",
"app.navBar.settingsDropdown.endMeetingLabel": "Закончить конференцию",
"app.navBar.userListToggleBtnLabel": "Включить/Выключить список пользователей",
@ -390,7 +393,7 @@
"app.submenu.application.paginationEnabledLabel": "Разделение видео",
"app.submenu.application.layoutOptionLabel": "Тип компоновки",
"app.submenu.notification.SectionTitle": "Уведомления",
"app.submenu.notification.Desc": "Определить как и о чём вы будете оповещаться.",
"app.submenu.notification.Desc": "Определить, как и о чём вы будете оповещаться.",
"app.submenu.notification.audioAlertLabel": "Звуковые оповещения",
"app.submenu.notification.pushAlertLabel": "Всплывающие оповещения",
"app.submenu.notification.messagesLabel": "Сообщение чата",
@ -417,6 +420,7 @@
"app.settings.main.save.label.description": "Сохраняет изменения и закрывает меню настроек",
"app.settings.dataSavingTab.label": "Сохранение данных",
"app.settings.dataSavingTab.webcam": "Отображать веб-камеры",
"app.settings.dataSavingTab.screenShare": "Отображать демонстрацию рабочего стола",
"app.settings.dataSavingTab.description": "Чтобы сохранить устойчивую скорость соединения, выберите, что будет отображаться",
"app.settings.save-notification.label": "Настройки сохранены",
"app.statusNotifier.lowerHands": "Опустить руки",
@ -578,7 +582,7 @@
"app.modal.close": "Закрыть",
"app.modal.close.description": "Сбрасывает изменения и закрывает окно",
"app.modal.confirm": "Готово",
"app.modal.newTab": "( откроет новую вкладку )",
"app.modal.newTab": "(откроет новую вкладку)",
"app.modal.confirm.description": "Сохранить изменения и закрыть окно",
"app.modal.randomUser.noViewers.description": "Недостаточно участников для случайного выбора",
"app.modal.randomUser.selected.description": "Вы были выбраны случайным образом",
@ -639,6 +643,7 @@
"app.userList.guest.privateMessageLabel": "Сообщение",
"app.userList.guest.acceptLabel": "Принять",
"app.userList.guest.denyLabel": "Отклонить",
"app.userList.guest.feedbackMessage": "Выполненное действие: ",
"app.user-info.title": "Поиск в каталоге",
"app.toast.breakoutRoomEnded": "Групповая работа закончилась. Пожалуйста, снова присоединитесь к аудио конференции.",
"app.toast.chat.public": "Новое сообщение в публичном чате",
@ -955,8 +960,8 @@
"playback.button.fullscreen.aria": "Полноэкранный контент",
"playback.button.restore.aria": "Восстановить контент",
"playback.button.search.aria": "Поиск",
"playback.button.section.aria": "Боковая секция",
"playback.button.swap.aria": "Изменить контент",
"playback.button.section.aria": "Боковая панель",
"playback.button.swap.aria": "Поменять местами контент",
"playback.button.theme.aria": "Изменить тему",
"playback.error.wrapper.aria": "Область ошибок",
"playback.loader.wrapper.aria": "Область загрузки",
@ -964,7 +969,9 @@
"playback.player.about.modal.shortcuts.title": "Ярлыки",
"playback.player.about.modal.shortcuts.alt": "Alt",
"playback.player.about.modal.shortcuts.shift": "Shift",
"playback.player.about.modal.shortcuts.fullscreen": "Включить/выключить полноэкранный режим",
"playback.player.about.modal.shortcuts.play": "Старт/Пауза",
"playback.player.about.modal.shortcuts.section": "Включить/выключить боковую панель",
"playback.player.about.modal.shortcuts.seek.backward": "Перемотка назад",
"playback.player.about.modal.shortcuts.seek.forward": "Перемотка вперед",
"playback.player.about.modal.shortcuts.skip.next": "Следующий слайд",
@ -1012,6 +1019,11 @@
"app.learningDashboard.userDetails.response": "Ответ",
"app.learningDashboard.userDetails.mostCommonAnswer": "Самый распространенный ответ",
"app.learningDashboard.userDetails.anonymousAnswer": "Анонимное голосование",
"app.learningDashboard.userDetails.talkTime": "Время разговора",
"app.learningDashboard.userDetails.messages": "Сообщений",
"app.learningDashboard.userDetails.emojis": "Смайликов",
"app.learningDashboard.userDetails.raiseHands": "Поднятых рук",
"app.learningDashboard.userDetails.pollVotes": "Варианты ответов",
"app.learningDashboard.usersTable.title": "Обзор",
"app.learningDashboard.usersTable.colOnline": "Время в сети",
"app.learningDashboard.usersTable.colTalk": "Время разговора",
@ -1039,11 +1051,17 @@
"app.learningDashboard.statusTimelineTable.thumbnail": "Миниатюра презентации",
"app.learningDashboard.errors.invalidToken": "Неверный токен сеанса",
"app.learningDashboard.errors.dataUnavailable": "Данные больше не доступны",
"mobileApp.portals.list.empty.addFirstPortal.label": "Создайте ваш первый портал с помощью кнопки выше, ",
"mobileApp.portals.list.empty.orUseOurDemoServer.label": "или используйте наш демо-сервер.",
"mobileApp.portals.list.add.button.label": "Создать портал",
"mobileApp.portals.fields.name.label": "Имя портала",
"mobileApp.portals.fields.name.placeholder": "Демонстрация BigBlueButton",
"mobileApp.portals.fields.url.label": "URL сервера",
"mobileApp.portals.addPortalPopup.confirm.button.label": "Сохранить",
"mobileApp.portals.drawerNavigation.button.label": "Порталы",
"mobileApp.portals.addPortalPopup.validation.emptyFields": "Поля, обязательные для заполнения",
"mobileApp.portals.addPortalPopup.validation.portalNameAlreadyExists": "Имя уже используется",
"mobileApp.portals.addPortalPopup.validation.urlInvalid": "Ошибка при загрузке страницы. Проверьте URL и Интернет соединение."
"mobileApp.portals.addPortalPopup.validation.urlInvalid": "Ошибка при загрузке страницы. Проверьте URL и сетевое подключение."
}

View File

@ -298,7 +298,7 @@
"app.poll.liveResult.usersTitle": "Kullanıcılar",
"app.poll.liveResult.responsesTitle": "Yanıt",
"app.poll.liveResult.secretLabel": "Bu isimsiz bir ankettir. Bireysel cevaplar gösterilmez.",
"app.poll.removePollOpt": "Kaldırılan Anket seçenekleri [0]",
"app.poll.removePollOpt": "Kaldırılan Anket seçenekleri {0}",
"app.poll.emptyPollOpt": "Boş",
"app.polling.pollingTitle": "Anket seçenekleri",
"app.polling.pollQuestionTitle": "Anket Sorusu",

View File

@ -47,6 +47,8 @@
"app.captions.ownership": "接管",
"app.textInput.sendLabel": "发送",
"app.title.defaultViewLabel": "默认演讲视图",
"app.notes.title": "共享笔记",
"app.notes.label": "笔记",
"app.user.activityCheck": "用户活动检查",
"app.user.activityCheck.label": "确认用户是否仍然会议中({0}",
"app.user.activityCheck.check": "检查",
@ -132,6 +134,7 @@
"app.screenshare.viewerLoadingLabel": "正在加载演讲者屏幕",
"app.screenshare.presenterSharingLabel": "你正在分享自己的屏幕",
"app.screenshare.screenshareFinalError": "代码 {0}。 无法共享屏幕。",
"app.screenshare.screenshareRetryError": "允许其他成员桌面共享",
"app.meeting.ended": "此会议已结束",
"app.meeting.meetingTimeRemaining": "会议时间剩余:{0} ",
"app.meeting.meetingTimeHasEnded": "结束时间到。会议即将结束 ",
@ -222,6 +225,11 @@
"app.poll.customPlaceholder": "增加选项",
"app.poll.noPresentationSelected": "没有选择演示文件!请选择。",
"app.poll.clickHereToSelect": "点击选择",
"app.poll.question.label" : "输入问题",
"app.poll.optionalQuestion.label" : "输入问题(可选)",
"app.poll.userResponse.label" : "简答",
"app.poll.responseTypes.label" : "问题类型",
"app.poll.addItem.label" : "增加选项",
"app.poll.secretPoll.label" : "匿名投票",
"app.poll.questionErr": "必须输入投票问题",
"app.poll.optionErr": "输入一个投票选项",
@ -231,6 +239,8 @@
"app.poll.tf": "真/假",
"app.poll.y": "是",
"app.poll.n": "否",
"app.poll.abstention": "放弃",
"app.poll.yna": "是 / 否 / 放弃",
"app.poll.a2": "A / B",
"app.poll.a3": "A / B / C",
"app.poll.a4": "A / B / C / D",
@ -239,6 +249,7 @@
"app.poll.answer.false": "错误",
"app.poll.answer.yes": "是",
"app.poll.answer.no": "否",
"app.poll.answer.abstention": "放弃",
"app.poll.answer.a": "A",
"app.poll.answer.b": "B",
"app.poll.answer.c": "C",
@ -259,6 +270,7 @@
"app.navBar.settingsDropdown.fullscreenLabel": "全屏显示",
"app.navBar.settingsDropdown.settingsLabel": "设置",
"app.navBar.settingsDropdown.aboutLabel": "关于",
"app.navBar.settingsDropdown.leaveSessionLabel": "离开会议",
"app.navBar.settingsDropdown.exitFullscreenLabel": "退出全屏",
"app.navBar.settingsDropdown.fullscreenDesc": "将设置菜单全屏显示",
"app.navBar.settingsDropdown.settingsDesc": "更改通用设置",
@ -280,6 +292,7 @@
"app.navBar.emptyAudioBrdige": "没有活动麦克风。分享麦克风可将音频添加到录像中。",
"app.leaveConfirmation.confirmLabel": "退出",
"app.leaveConfirmation.confirmDesc": "您将从会议退出",
"app.endMeeting.title": "结束{0}",
"app.endMeeting.noUserDescription": "您确定要结束此会议吗?",
"app.endMeeting.yesLabel": "是",
"app.endMeeting.noLabel": "否",
@ -297,9 +310,11 @@
"app.actionsBar.raiseLabel": "举手",
"app.actionsBar.label": "操作栏",
"app.actionsBar.actionsDropdown.restorePresentationLabel": "恢复演示文件",
"app.actionsBar.actionsDropdown.minimizePresentationLabel": "最小化演示文件",
"app.screenshare.screenShareLabel" : "分享屏幕",
"app.submenu.application.applicationSectionTitle": "应用",
"app.submenu.application.animationsLabel": "动画",
"app.submenu.application.audioFilterLabel": "麦克风音频过滤",
"app.submenu.application.fontSizeControlLabel": "字号",
"app.submenu.application.increaseFontBtnLabel": "增大界面字号",
"app.submenu.application.decreaseFontBtnLabel": "减小界面字号",
@ -307,12 +322,14 @@
"app.submenu.application.languageLabel": "界面语言",
"app.submenu.application.languageOptionLabel": "选择语言",
"app.submenu.application.noLocaleOptionLabel": "没有可用的语言",
"app.submenu.application.layoutOptionLabel": "界面布局",
"app.submenu.notification.SectionTitle": "通知",
"app.submenu.notification.Desc": "定义通知方式和内容。",
"app.submenu.notification.audioAlertLabel": "音频提醒",
"app.submenu.notification.pushAlertLabel": "弹窗提醒",
"app.submenu.notification.messagesLabel": "聊天消息",
"app.submenu.notification.userJoinLabel": "用户加入",
"app.submenu.notification.userLeaveLabel": "用户离开",
"app.submenu.audio.micSourceLabel": "麦克风源",
"app.submenu.audio.speakerSourceLabel": "扬声器源",
"app.submenu.audio.streamVolumeLabel": "您音频流的音量",
@ -337,6 +354,8 @@
"app.settings.save-notification.label": "设置已保存",
"app.statusNotifier.lowerHands": "没举手",
"app.statusNotifier.raisedHandsTitle": "已举手",
"app.statusNotifier.raisedHandDesc": "{0}人举手",
"app.statusNotifier.raisedHandDescOneUser": "{0}人举手",
"app.statusNotifier.and": "和",
"app.switch.onLabel": "开启",
"app.switch.offLabel": "关闭",
@ -344,6 +363,7 @@
"app.talkingIndicator.isTalking" : "{0} 正在说话...",
"app.talkingIndicator.wasTalking" : "{0} 沉默了",
"app.actionsBar.actionsDropdown.actionsLabel": "操作",
"app.actionsBar.actionsDropdown.presentationLabel": "管理演示文件",
"app.actionsBar.actionsDropdown.initPollLabel": "发起投票",
"app.actionsBar.actionsDropdown.desktopShareLabel": "分享您的桌面",
"app.actionsBar.actionsDropdown.stopDesktopShareLabel": "停止分享您的桌面",
@ -360,9 +380,11 @@
"app.actionsBar.actionsDropdown.captionsDesc": "切换字幕面板",
"app.actionsBar.actionsDropdown.takePresenter": "我当演示者",
"app.actionsBar.actionsDropdown.takePresenterDesc": "设定自己为新的演示者",
"app.actionsBar.actionsDropdown.selectRandUserLabel": "随机选择用户",
"app.actionsBar.emojiMenu.statusTriggerLabel": "设置状态",
"app.actionsBar.emojiMenu.awayLabel": "离开",
"app.actionsBar.emojiMenu.awayDesc": "更改您的状态为离开",
"app.actionsBar.emojiMenu.raiseHandLabel": "举手",
"app.actionsBar.emojiMenu.raiseHandDesc": "举手提问",
"app.actionsBar.emojiMenu.neutralLabel": "未决定",
"app.actionsBar.emojiMenu.neutralDesc": "更改您的状态为未决定",
@ -436,6 +458,7 @@
"app.audioModal.playAudio.arialabel" : "播放音频",
"app.audioDial.tipIndicator": "提醒",
"app.audioDial.tipMessage": "按手机上的“0”键可静音/取消静音。",
"app.audioModal.connecting": "正在建立音频连接",
"app.audioManager.joinedAudio": "您已加入会议的音频交流",
"app.audioManager.joinedEcho": "您已开始回音测试",
"app.audioManager.leftAudio": "您已退出会议音频交流",
@ -550,6 +573,10 @@
"app.connection-status.title": "连接状态",
"app.connection-status.description": "查看用户的连接状态",
"app.connection-status.more": "更多",
"app.connection-status.no": "否",
"app.connection-status.yes": "是",
"app.learning-dashboard.label": "学习分析面板",
"app.learning-dashboard.clickHereToOpen": "打开学习分析面板",
"app.recording.startTitle": "开始录制",
"app.recording.stopTitle": "暂停录制",
"app.recording.resumeTitle": "恢复录制",
@ -714,6 +741,10 @@
"app.layout.style.smart": "智能布局",
"app.layout.style.presentationFocus": "聚焦到演讲",
"app.layout.style.videoFocus": "聚焦到视频",
"app.layout.style.customPush": "自定义(发布到所有成员)",
"app.layout.style.smartPush": "智能布局(发布到所有成员)",
"app.layout.style.presentationFocusPush": "聚集到演示(发布到所有成员)",
"app.layout.style.videoFocusPush": "聚集到视频(发布到所有成员)",
"playback.button.about.aria": "关于",
"playback.button.clear.aria": "清楚搜索记录",
"playback.button.close.aria": "关闭弹窗",
@ -732,9 +763,11 @@
"playback.player.screenshare.wrapper.aria": "屏幕分享区",
"playback.player.search.modal.title": "搜索",
"playback.player.thumbnails.wrapper.aria": "缩略图区域",
"app.learningDashboard.dashboardTitle": "学习分析面板",
"app.learningDashboard.indicators.usersOnline": "活跃用户",
"app.learningDashboard.indicators.usersTotal": "参会人员总数",
"app.learningDashboard.indicators.polls": "投票",
"app.learningDashboard.userDetails.raiseHands": "举手",
"app.learningDashboard.usersTable.title": "概览",
"app.learningDashboard.usersTable.colOnline": "在线时长",
"app.learningDashboard.usersTable.colTalk": "发言时长",

View File

@ -98,6 +98,7 @@ exports.etherpadEditable = 'body[id="innerdocbody"]';
// Notifications
exports.smallToastMsg = 'div[data-test="toastSmallMsg"]';
exports.currentPresentationToast = 'div[data-test="toastSmallMsg"] > div';
exports.notificationsTab = 'span[id="notificationTab"]';
exports.chatPopupAlertsBtn = 'input[data-test="chatPopupAlertsBtn"]';
exports.hasUnreadMessages = 'button[data-test="hasUnreadMessages"]';

View File

@ -35,7 +35,7 @@ class Page {
const joinUrl = helpers.getJoinURL(this.meetingId, this.initParameters, isModerator, customParameter);
const response = await this.page.goto(joinUrl);
await expect(response.ok()).toBeTruthy();
const hasErrorLabel = await this.page.evaluate(checkElement, [e.errorMessageLabel]);
const hasErrorLabel = await this.checkElement(e.errorMessageLabel);
await expect(hasErrorLabel, 'Getting error when joining. Check if the BBB_URL and BBB_SECRET are set correctly').toBeFalsy();
this.settings = await generateSettingsData(this.page);
const { autoJoinAudioModal } = this.settings;
@ -87,9 +87,9 @@ class Page {
await this.waitForSelector(e.videoPreview, videoPreviewTimeout);
await this.waitAndClick(e.startSharingWebcam);
}
await this.waitForSelector(e.webcamConnecting);
await this.waitForSelector(e.webcamContainer, VIDEO_LOADING_WAIT_TIME);
await this.waitForSelector(e.leaveVideo, VIDEO_LOADING_WAIT_TIME);
await this.wasRemoved(e.webcamConnecting);
}
getLocator(selector) {

View File

@ -2,6 +2,15 @@ const { expect } = require("@playwright/test");
// Common
function checkElement([element, index = 0]) {
/* Because this function is passed to a page.evaluate, it can only
* take a single argument; that's why we pass it an array. It's so
* easy to pass it a string by mistake that we check to make sure
* the second argument is an integer and not a character from a
* destructured string.
*/
if (typeof index != "number") {
throw Error("Assert failed: index not a number");
}
return document.querySelectorAll(element)[index] !== undefined;
}

View File

@ -55,7 +55,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

@ -2,7 +2,6 @@ const { expect } = require('@playwright/test');
const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const e = require('../core/elements');
const { sleep } = require('../core/helpers');
const { checkElement } = require('../core/util');
async function enableChatPopup(test) {
await test.waitAndClick(e.notificationsTab);
@ -55,9 +54,9 @@ async function waitAndClearNotification(testPage) {
}
async function waitAndClearDefaultPresentationNotification(testPage) {
const hasPresentationUploaded = await testPage.page.evaluate(checkElement, e.whiteboard);
if (!hasPresentationUploaded) {
await testPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await testPage.waitForSelector(e.whiteboard,ELEMENT_WAIT_LONGER_TIME);
const hasCurrentPresentationToast = await testPage.checkElement(e.currentPresentationToast);
if (hasCurrentPresentationToast) {
await waitAndClearNotification(testPage);
}
}

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

@ -2,7 +2,9 @@ const { test, devices } = require('@playwright/test');
const { ScreenShare } = require('./screenshare');
test.describe.parallel('Screenshare', () => {
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

@ -418,4 +418,4 @@ endWhenNoModeratorDelayInMinutes=1
#disabledFeatures=
# Allow endpoint with current BigBlueButton version
allowRevealOfBBBVersion=false
allowRevealOfBBBVersion=false

View File

@ -31,6 +31,7 @@ import org.bigbluebutton.api.domain.GuestPolicy
import org.bigbluebutton.api.domain.Meeting
import org.bigbluebutton.api.domain.UserSession
import org.bigbluebutton.api.service.ValidationService
import org.bigbluebutton.api.service.ServiceUtils
import org.bigbluebutton.api.util.ParamsUtil
import org.bigbluebutton.api.util.ResponseBuilder
import org.bigbluebutton.presentation.PresentationUrlDownloadService
@ -229,16 +230,9 @@ class ApiController {
String fullName = ParamsUtil.stripControlChars(params.fullName)
String externalMeetingId = params.meetingID
String attPW = params.password
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
// the createTime mismatch with meeting's createTime, complain
// In the future, the createTime param will be required
@ -511,13 +505,7 @@ class ApiController {
return
}
String externalMeetingId = params.meetingID
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
boolean isRunning = meeting != null && meeting.isRunning();
response.addHeader("Cache-Control", "no-cache")
@ -548,10 +536,7 @@ class ApiController {
return
}
String externalMeetingId = params.meetingID
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId)
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId)
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingid", meeting.getInternalId());
@ -595,10 +580,7 @@ class ApiController {
return
}
String externalMeetingId = params.meetingID
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
withFormat {
xml {
@ -721,9 +703,7 @@ class ApiController {
}
// Translate the external meeting id into an internal meeting id.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(params.meetingID);
Meeting meeting = meetingService.getMeeting(internalMeetingId);
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
String pollXML = params.pollXML
@ -905,6 +885,7 @@ class ApiController {
reject = true
respMessage = "The maximum number of participants allowed for this meeting has been reached."
} else {
log.info("User ${us.internalUserId} has entered")
meeting.userEntered(us.internalUserId)
}
}
@ -1167,10 +1148,7 @@ class ApiController {
return
}
String externalMeetingId = params.meetingID
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId)
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId)
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);
if (meeting != null){
uploadDocuments(meeting, true);
@ -1327,7 +1305,11 @@ class ApiController {
requestBody = StringUtils.isEmpty(requestBody) ? null : requestBody;
Boolean isDefaultPresentationCurrent = false;
def listOfPresentation = []
def presentationListHasCurrent = false
// This part of the code is responsible for organize the presentations in a certain order
// It selects the one that has the current=true, and put it in the 0th place.
// Afterwards, the 0th presentation is going to be uploaded first, which spares processing time
if (requestBody == null) {
if (isFromInsertAPI){
log.warn("Insert Document API called without a payload - ignoring")
@ -1363,6 +1345,7 @@ class ApiController {
}
}
}
presentationListHasCurrent = hasCurrent;
}
listOfPresentation.eachWithIndex { document, index ->
@ -1382,10 +1365,14 @@ class ApiController {
}
// The array has already been processed to let the first be the current. (This way it is
// ensured that only one document is current)
if (index == 0) {
if (index == 0 && isFromInsertAPI) {
if (presentationListHasCurrent) {
isCurrent = true
}
} else if (index == 0 && !isFromInsertAPI){
isCurrent = true
}
isCurrent = isCurrent && !isFromInsertAPI
// Verifying whether the document is a base64 encoded or a url to download.
if (!StringUtils.isEmpty(document.@url.toString())) {
def fileName;
@ -1659,7 +1646,10 @@ class ApiController {
// Users that are entering the meeting
int enteredUsers = meeting.getEnteredUsers().size()
Boolean reachedMax = (joinedUsers + enteredUsers) >= maxParticipants;
log.info("Joined users - ${joinedUsers}")
log.info("Entered users - ${enteredUsers}")
Boolean reachedMax = joinedUsers >= maxParticipants;
if (enabled && !rejoin && !reenter && reachedMax) {
return true;
}

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))/g" private/config/settings.yml
fi
mkdir -p staging/usr/share/bigbluebutton/nginx
@ -98,10 +99,13 @@ fi
cp node-v14.19.1-linux-x64.tar.gz 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
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

@ -552,7 +552,7 @@ module BigBlueButton
ffmpeg_filter << ",trim=end=#{ms_to_s(duration)}"
ffmpeg_cmd = [*FFMPEG]
ffmpeg_cmd = [*FFMPEG, '-copyts']
ffmpeg_inputs.each do |input|
ffmpeg_cmd += ['-ss', ms_to_s(input[:seek]), '-i', input[:filename]]
end