Merge pull request #18453 from gustavotrott/merge-27-into-develop-4ago2023

Merge 2.7 into Develop
This commit is contained in:
Gustavo Trott 2023-08-04 17:46:50 -03:00 committed by GitHub
commit dc75850f1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 632 additions and 176 deletions

View File

@ -16,26 +16,99 @@ on:
permissions: permissions:
contents: read contents: read
jobs: jobs:
build-install-and-test: build-bbb-apps-akka:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_AKKA_APPS_KEY=$(git log -1 --format=%H -- akka-bbb-apps)" >> $GITHUB_ENV
- run: echo "CACHE_COMMON_MSG_KEY=$(git log -1 --format=%H -- bbb-common-message)" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-apps-akka-${{ env.CACHE_AKKA_APPS_KEY }}-${{ env.CACHE_COMMON_MSG_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-apps-akka
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-apps-akka.tar
path: |
artifacts.tar
build-bbb-config:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-config
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-config.tar
path: artifacts.tar
build-bbb-export-annotations:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-export-annotations
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-export-annotations.tar
path: artifacts.tar
build-bbb-learning-dashboard:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_LEARNING_DASHBOARD_KEY=$(git log -1 --format=%H -- bbb-learning-dashboard)" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-learning-dashboard-${{ env.CACHE_LEARNING_DASHBOARD_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-learning-dashboard
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-learning-dashboard.tar
path: artifacts.tar
build-bbb-playback-record:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: ./build/get_external_dependencies.sh - run: ./build/get_external_dependencies.sh
- run: ./build/setup.sh bbb-apps-akka - run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: ./build/setup.sh bbb-config - run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- run: ./build/setup.sh bbb-etherpad
- run: ./build/setup.sh bbb-export-annotations
- run: ./build/setup.sh bbb-freeswitch-core
- run: ./build/setup.sh bbb-freeswitch-sounds
- run: ./build/setup.sh bbb-fsesl-akka
- run: ./build/setup.sh bbb-graphql-middleware
- run: ./build/setup.sh bbb-graphql-server
- run: ./build/setup.sh bbb-html5-nodejs
- run: ./build/setup.sh bbb-html5
- run: ./build/setup.sh bbb-learning-dashboard
- run: ./build/setup.sh bbb-libreoffice-docker
- run: ./build/setup.sh bbb-mkclean
- run: ./build/setup.sh bbb-pads
- run: ./build/setup.sh bbb-playback - run: ./build/setup.sh bbb-playback
- run: ./build/setup.sh bbb-playback-notes - run: ./build/setup.sh bbb-playback-notes
- run: ./build/setup.sh bbb-playback-podcast - run: ./build/setup.sh bbb-playback-podcast
@ -43,7 +116,191 @@ jobs:
- run: ./build/setup.sh bbb-playback-screenshare - run: ./build/setup.sh bbb-playback-screenshare
- run: ./build/setup.sh bbb-playback-video - run: ./build/setup.sh bbb-playback-video
- run: ./build/setup.sh bbb-record-core - run: ./build/setup.sh bbb-record-core
- run: ./build/setup.sh bbb-web - run: tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-playback-record.tar
path: |
artifacts.tar
build-bbb-graphql-server:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: ./build/get_external_dependencies.sh
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- run: ./build/setup.sh bbb-graphql-middleware
- run: ./build/setup.sh bbb-graphql-server
- run: tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-graphql-server.tar
path: |
artifacts.tar
build-bbb-etherpad:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_ETHERPAD_VERSION_KEY=$(git log -1 --format=%H -- bbb-etherpad.placeholder.sh)" >> $GITHUB_ENV
- run: echo "CACHE_ETHERPAD_BUILD_KEY=$(git log -1 --format=%H -- build/packages-template/bbb-etherpad)" >> $GITHUB_ENV
- run: echo "CACHE_URL1_KEY=$(curl -s https://api.github.com/repos/mconf/ep_pad_ttl/commits | md5sum | awk '{ print $1 }')" >> $GITHUB_ENV
- run: echo "CACHE_URL2_KEY=$(curl -s https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits | md5sum | awk '{ print $1 }')" >> $GITHUB_ENV
- run: echo "CACHE_URL3_KEY=$(curl -s https://api.github.com/repos/mconf/ep_redis_publisher/commits | md5sum | awk '{ print $1 }')" >> $GITHUB_ENV
- run: echo "CACHE_URL4_KEY=$(curl -s https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits | md5sum | awk '{ print $1 }')" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-etherpad-${{ env.CACHE_ETHERPAD_VERSION_KEY }}-${{ env.CACHE_ETHERPAD_BUILD_KEY }}-${{ env.CACHE_URL1_KEY }}-${{ env.CACHE_URL2_KEY }}-${{ env.CACHE_URL3_KEY }}-${{ env.CACHE_URL4_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-etherpad
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-etherpad.tar
path: |
artifacts.tar
build-bbb-bbb-web:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_BBB_WEB_KEY=$(git log -1 --format=%H -- bigbluebutton-web)" >> $GITHUB_ENV
- run: echo "CACHE_COMMON_MSG_KEY=$(git log -1 --format=%H -- bbb-common-message)" >> $GITHUB_ENV
- run: echo "CACHE_COMMON_WEB_KEY=$(git log -1 --format=%H -- bbb-common-web)" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-web-${{ env.CACHE_BBB_WEB_KEY }}-${{ env.CACHE_COMMON_MSG_KEY }}-${{ env.CACHE_COMMON_WEB_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-web
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-web.tar
path: |
artifacts.tar
build-bbb-fsesl-akka:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_AKKA_FSESL_KEY=$(git log -1 --format=%H -- akka-bbb-fsesl)" >> $GITHUB_ENV
- run: echo "CACHE_COMMON_MSG_KEY=$(git log -1 --format=%H -- bbb-common-message)" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-fsesl-akka-${{ env.CACHE_AKKA_FSESL_KEY }}-${{ env.CACHE_COMMON_MSG_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-fsesl-akka
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-fsesl-akka.tar
path: |
artifacts.tar
build-bbb-html5:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_KEY=$(git log -1 --format=%H -- bigbluebutton-html5)" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-html5-${{ env.CACHE_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-html5-nodejs
./build/setup.sh bbb-html5
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-html5.tar
path: |
artifacts.tar
build-bbb-freeswitch:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history
- run: echo "CACHE_FREESWITCH_KEY=$(git log -1 --format=%H -- build/packages-template/bbb-freeswitch-core)" >> $GITHUB_ENV
- run: echo "CACHE_FREESWITCH_SOUNDS_KEY=$(git log -1 --format=%H -- build/packages-template/bbb-freeswitch-sounds)" >> $GITHUB_ENV
- run: echo "CACHE_SOUNDS_KEY=$(curl -Is http://bigbluebutton.org/downloads/sounds.tar.gz | grep "Last-Modified" | md5sum | awk '{ print $1 }')" >> $GITHUB_ENV
- run: echo "CACHE_BBB_RELEASE_KEY=$(git log -1 --format=%H -- bigbluebutton-config/bigbluebutton-release)" >> $GITHUB_ENV
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- name: Handle cache
id: cache-action
uses: actions/cache@v3
with:
path: artifacts.tar
key: ${{ runner.os }}-bbb-freeswitch-${{ env.CACHE_FREESWITCH_KEY }}-${{ env.CACHE_FREESWITCH_SOUNDS_KEY }}-${{ env.CACHE_SOUNDS_KEY }}-${{ env.CACHE_BBB_RELEASE_KEY }}
- if: ${{ steps.cache-action.outputs.cache-hit != 'true' }}
name: Generate artifacts
run: |
./build/get_external_dependencies.sh
./build/setup.sh bbb-freeswitch-core
./build/setup.sh bbb-freeswitch-sounds
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: artifacts_bbb-freeswitch.tar
path: |
artifacts.tar
build-others:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: ./build/get_external_dependencies.sh
- run: echo "FORCE_GIT_REV=0" >> $GITHUB_ENV #used by setup.sh
- run: echo "FORCE_COMMIT_DATE=0" >> $GITHUB_ENV #used by setup.sh
- run: ./build/setup.sh bbb-mkclean
- run: ./build/setup.sh bbb-pads
- run: ./build/setup.sh bbb-libreoffice-docker
- run: ./build/setup.sh bbb-webrtc-sfu - run: ./build/setup.sh bbb-webrtc-sfu
- run: ./build/setup.sh bbb-webrtc-recorder - run: ./build/setup.sh bbb-webrtc-recorder
- run: ./build/setup.sh bbb-transcription-controller - run: ./build/setup.sh bbb-transcription-controller
@ -57,37 +314,114 @@ jobs:
artifacts.tar artifacts.tar
# - name: Fake package build # - name: Fake package build
# run: | # run: |
# sudo sh -c ' # sudo -i <<EOF
# set -e
# echo "Faking a package build (to speed up installation test)" # echo "Faking a package build (to speed up installation test)"
# cd / # cd /
# wget -q "http://ci.bbb.imdt.dev/artifacts.tar" # wget -nv "http://ci.bbb.imdt.dev/artifacts.tar"
# tar xf artifacts.tar # tar xf artifacts.tar
# ' # mv artifacts /home/runner/work/bigbluebutton/bigbluebutton/artifacts/
# EOF
install-and-run-tests:
needs: [build-bbb-apps-akka, build-bbb-config, build-bbb-export-annotations, build-bbb-learning-dashboard, build-bbb-playback-record, build-bbb-graphql-server, build-bbb-etherpad, build-bbb-bbb-web, build-bbb-fsesl-akka, build-bbb-html5, build-bbb-freeswitch, build-others]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: ./build/get_external_dependencies.sh
- name: Download artifacts_bbb-apps-akka
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-apps-akka.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-config
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-config.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-export-annotations
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-export-annotations.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-learning-dashboard
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-learning-dashboard.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-playback-record
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-playback-record.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-graphql-server
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-graphql-server.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-etherpad
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-etherpad.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-freeswitch
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-freeswitch.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-web
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-web.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-fsesl-akka
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-fsesl-akka.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-html5
uses: actions/download-artifact@v3
with:
name: artifacts_bbb-html5.tar
- run: tar xf artifacts.tar
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: artifacts.tar
- run: tar xf artifacts.tar
- name: Extracting files .tar
run: |
set -e
pwd
echo "----ls artifacts/----"
ls artifacts/
echo "Done"
- name: Generate CA - name: Generate CA
run: | run: |
sudo sh -c ' sudo -i <<EOF
set -e
mkdir /root/bbb-ci-ssl/ mkdir /root/bbb-ci-ssl/
cd /root/bbb-ci-ssl/ cd /root/bbb-ci-ssl/
openssl rand -base64 48 > /root/bbb-ci-ssl/bbb-dev-ca.pass ; openssl rand -base64 48 > /root/bbb-ci-ssl/bbb-dev-ca.pass ;
chmod 600 /root/bbb-ci-ssl/bbb-dev-ca.pass ; chmod 600 /root/bbb-ci-ssl/bbb-dev-ca.pass ;
openssl genrsa -des3 -out bbb-dev-ca.key -passout file:/root/bbb-ci-ssl/bbb-dev-ca.pass 2048 ; openssl genrsa -des3 -out bbb-dev-ca.key -passout file:/root/bbb-ci-ssl/bbb-dev-ca.pass 2048 ;
openssl req -x509 -new -nodes -key bbb-dev-ca.key -sha256 -days 1460 -passin file:/root/bbb-ci-ssl/bbb-dev-ca.pass -out bbb-dev-ca.crt -subj "/C=CA/ST=BBB/L=BBB/O=BBB/OU=BBB/CN=BBB-DEV" ; openssl req -x509 -new -nodes -key bbb-dev-ca.key -sha256 -days 1460 -passin file:/root/bbb-ci-ssl/bbb-dev-ca.pass -out bbb-dev-ca.crt -subj "/C=CA/ST=BBB/L=BBB/O=BBB/OU=BBB/CN=BBB-DEV" ;
' EOF
- name: Trust CA - name: Trust CA
run: | run: |
sudo sh -c ' sudo -i <<EOF
set -e
sudo mkdir /usr/local/share/ca-certificates/bbb-dev/ sudo mkdir /usr/local/share/ca-certificates/bbb-dev/
sudo cp /root/bbb-ci-ssl/bbb-dev-ca.crt /usr/local/share/ca-certificates/bbb-dev/ sudo cp /root/bbb-ci-ssl/bbb-dev-ca.crt /usr/local/share/ca-certificates/bbb-dev/
sudo chmod 644 /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt sudo chmod 644 /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
sudo update-ca-certificates sudo update-ca-certificates
' EOF
- name: Generate certificate - name: Generate certificate
run: | run: |
sudo sh -c ' sudo sh -c '
cd /root/bbb-ci-ssl/ cd /root/bbb-ci-ssl/
echo "$(hostname -I | cut -d" " -f1) bbb-ci.test" >> /etc/hosts echo "$(hostname -I | cut -d" " -f1) bbb-ci.test" >> /etc/hosts
openssl genrsa -out bbb-ci.test.key 2048 openssl genrsa -out bbb-ci.test.key 2048
rm bbb-ci.test.csr bbb-ci.test.crt bbb-ci.test.key rm -f bbb-ci.test.csr bbb-ci.test.crt bbb-ci.test.key
cat > bbb-ci.test.ext << EOF cat > bbb-ci.test.ext << EOF
authorityKeyIdentifier=keyid,issuer authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE basicConstraints=CA:FALSE
@ -107,32 +441,33 @@ jobs:
cat /root/bbb-ci-ssl/bbb-ci.test.key > /local/certs/privkey.pem cat /root/bbb-ci-ssl/bbb-ci.test.key > /local/certs/privkey.pem
' '
- name: Setup local repository - name: Setup local repository
shell: bash
run: | run: |
sudo sh -c ' sudo -i <<EOF
set -e
apt install -yq dpkg-dev apt install -yq dpkg-dev
cd /root && wget -q http://ci.bbb.imdt.dev/cache-3rd-part-packages.tar cd /root && wget -nv http://ci.bbb.imdt.dev/cache-3rd-part-packages.tar
cp -r /home/runner/work/bigbluebutton/bigbluebutton/artifacts/ /artifacts/ cp -r /home/runner/work/bigbluebutton/bigbluebutton/artifacts/ /artifacts/
cd /artifacts && tar xf /root/cache-3rd-part-packages.tar cd /artifacts && tar xf /root/cache-3rd-part-packages.tar
cd /artifacts && dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz cd /artifacts && dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz
echo "deb [trusted=yes] file:/artifacts/ ./" >> /etc/apt/sources.list echo "deb [trusted=yes] file:/artifacts/ ./" >> /etc/apt/sources.list
' EOF
- name: Prepare for install - name: Prepare for install
run: | run: |
sudo sh -c ' sudo sh -c '
apt --purge -y remove apache2-bin apt --purge -y remove apache2-bin
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y
' '
- name: Install BBB - name: Install BBB
run: | run: |
sudo sh -c ' sudo -i <<EOF
set -e
cd /root/ && wget -q https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.8.x-release/bbb-install.sh -O bbb-install.sh cd /root/ && wget -q https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.8.x-release/bbb-install.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-28-develop -s bbb-ci.test -j -d /certs/ cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-28-develop -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci 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 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
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart bbb-conf --restart
' EOF
- name: Install test dependencies - name: Install test dependencies
working-directory: ./bigbluebutton-tests/playwright working-directory: ./bigbluebutton-tests/playwright
run: | run: |
@ -174,7 +509,7 @@ jobs:
- if: failure() - if: failure()
name: Prepare artifacts (configs and logs) name: Prepare artifacts (configs and logs)
run: | run: |
sudo sh -c ' sudo -i <<EOF
mkdir configs mkdir configs
cp /etc/haproxy/haproxy.cfg configs/haproxy.cfg cp /etc/haproxy/haproxy.cfg configs/haproxy.cfg
touch /etc/bigbluebutton/turn-stun-servers.xml touch /etc/bigbluebutton/turn-stun-servers.xml
@ -191,10 +526,11 @@ jobs:
cp /etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml configs/bbb-webrtc-recorder-default.yml cp /etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml configs/bbb-webrtc-recorder-default.yml
cp /usr/share/bigbluebutton/nginx/sip.nginx configs/nginx_sip.nginx cp /usr/share/bigbluebutton/nginx/sip.nginx configs/nginx_sip.nginx
cp /etc/hosts /configs/hosts cp /etc/hosts /configs/hosts
chmod a+r -R configs mv configs /home/runner/work/bigbluebutton/bigbluebutton/configs
chmod a+r -R /home/runner/work/bigbluebutton/bigbluebutton/configs
bbb-conf --zip bbb-conf --zip
cp $(ls -t /root/*.tar.gz | head -1) ./bbb-logs.tar.gz ls -t /root/*.tar.gz | head -1 | xargs -I '{}' cp '{}' /home/runner/work/bigbluebutton/bigbluebutton/bbb-logs.tar.gz
' EOF
- if: failure() - if: failure()
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:

View File

@ -24,9 +24,10 @@ const TRACE_LOGS = Meteor.settings.public.kurento.traceLogs;
const GATHERING_TIMEOUT = Meteor.settings.public.kurento.gatheringTimeout; const GATHERING_TIMEOUT = Meteor.settings.public.kurento.gatheringTimeout;
const MEDIA = Meteor.settings.public.media; const MEDIA = Meteor.settings.public.media;
const DEFAULT_FULLAUDIO_MEDIA_SERVER = MEDIA.audio.fullAudioMediaServer; const DEFAULT_FULLAUDIO_MEDIA_SERVER = MEDIA.audio.fullAudioMediaServer;
const RETRY_THROUGH_RELAY = MEDIA.audio.retryThroughRelay || false;
const LISTEN_ONLY_OFFERING = MEDIA.listenOnlyOffering; const LISTEN_ONLY_OFFERING = MEDIA.listenOnlyOffering;
const MEDIA_TAG = MEDIA.mediaTag.replace(/#/g, ''); const MEDIA_TAG = MEDIA.mediaTag.replace(/#/g, '');
const RECONNECT_TIMEOUT_MS = MEDIA.listenOnlyCallTimeout || 15000; const CONNECTION_TIMEOUT_MS = MEDIA.listenOnlyCallTimeout || 15000;
const { audio: NETWORK_PRIORITY } = MEDIA.networkPriorities || {}; const { audio: NETWORK_PRIORITY } = MEDIA.networkPriorities || {};
const SENDRECV_ROLE = 'sendrecv'; const SENDRECV_ROLE = 'sendrecv';
const RECV_ROLE = 'recv'; const RECV_ROLE = 'recv';
@ -44,6 +45,9 @@ const errorCodeMap = {
1307: 1007, 1307: 1007,
}; };
// Error codes that are prone to a retry according to RETRY_THROUGH_RELAY
const RETRYABLE_ERRORS = [1007, 1010];
const mapErrorCode = (error) => { const mapErrorCode = (error) => {
const { errorCode } = error; const { errorCode } = error;
const mappedErrorCode = errorCodeMap[errorCode]; const mappedErrorCode = errorCodeMap[errorCode];
@ -76,8 +80,9 @@ export default class SFUAudioBridge extends BaseAudioBridge {
this.broker = null; this.broker = null;
this.reconnecting = false; this.reconnecting = false;
this.iceServers = []; this.iceServers = [];
this.inEchoTest = false;
this.bridgeName = BRIDGE_NAME; this.bridgeName = BRIDGE_NAME;
this.handleTermination = this.handleTermination.bind(this);
} }
get inputStream() { get inputStream() {
@ -111,73 +116,104 @@ export default class SFUAudioBridge extends BaseAudioBridge {
return doGUM(constraints, true); return doGUM(constraints, true);
} }
handleTermination() { setConnectionTimeout() {
return this.callback({ status: this.baseCallStates.ended, bridge: this.bridgeName }); if (this.connectionTimeout) this.clearConnectionTimeout();
this.connectionTimeout = setTimeout(() => {
const error = new Error(`ICE negotiation timeout after ${CONNECTION_TIMEOUT_MS / 1000}s`);
error.errorCode = 1010;
// Duplicating key-vals because I can'decide settle on an error pattern - prlanzarin again
error.errorCause = error.message;
error.errorMessage = error.message;
this.handleBrokerFailure(error);
}, CONNECTION_TIMEOUT_MS);
} }
clearReconnectionTimeout() { clearConnectionTimeout() {
this.reconnecting = false; if (this.connectionTimeout) {
if (this.reconnectionTimeout) { clearTimeout(this.connectionTimeout);
clearTimeout(this.reconnectionTimeout); this.connectionTimeout = null;
this.reconnectionTimeout = null;
} }
} }
reconnect() { dispatchAutoplayHandlingEvent(mediaElement) {
this.broker.stop(); const tagFailedEvent = new CustomEvent('audioPlayFailed', {
this.callback({ status: this.baseCallStates.reconnecting, bridge: this.bridgeName }); detail: { mediaElement },
this.reconnecting = true;
// Set up a reconnectionTimeout in case the server is unresponsive
// for some reason. If it gets triggered, end the session and stop
// trying to reconnect
this.reconnectionTimeout = setTimeout(() => {
this.callback({
status: this.baseCallStates.failed,
error: 1010,
bridgeError: 'Reconnection timeout',
bridge: this.bridgeName,
});
this.broker.stop();
this.clearReconnectionTimeout();
}, RECONNECT_TIMEOUT_MS);
this.joinAudio({ isListenOnly: this.isListenOnly }, this.callback).then(
() => this.clearReconnectionTimeout(),
).catch((error) => {
// Error handling is a no-op because it will be "handled" in handleBrokerFailure
logger.debug({
logCode: 'sfuaudio_reconnect_failed',
extraInfo: {
errorMessage: error.errorMessage,
reconnecting: this.reconnecting,
bridge: this.bridgeName,
role: this.role,
},
}, 'SFU audio reconnect failed');
}); });
window.dispatchEvent(tagFailedEvent);
this.callback({ status: this.baseCallStates.autoplayBlocked, bridge: this.bridgeName });
}
reconnect(options = {}) {
// If broker has already started, fire the reconnecting callback so the user
// knows what's going on
if (this.broker.started) {
this.callback({ status: this.baseCallStates.reconnecting, bridge: this.bridgeName });
} else {
// Otherwise: override termination handler so the ended callback doesn't get
// triggered - this is a retry attempt and the user shouldn't be notified
// yet.
this.broker.onended = () => {};
}
this.broker.stop();
this.reconnecting = true;
this._startBroker({ isListenOnly: this.isListenOnly, ...options })
.catch((error) => {
// Error handling is a no-op because it will be "handled" in handleBrokerFailure
logger.debug({
logCode: 'sfuaudio_reconnect_failed',
extraInfo: {
errorMessage: error.errorMessage,
reconnecting: this.reconnecting,
bridge: this.bridgeName,
role: this.role,
},
}, 'SFU audio reconnect failed');
});
} }
handleBrokerFailure(error) { handleBrokerFailure(error) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.clearConnectionTimeout();
mapErrorCode(error); mapErrorCode(error);
const { errorMessage, errorCause, errorCode } = error; const { errorMessage, errorCause, errorCode } = error;
if (this.broker.started && !this.reconnecting) { if (!this.reconnecting) {
logger.error({ if (this.broker.started) {
logCode: 'sfuaudio_error_try_to_reconnect', logger.error({
extraInfo: { logCode: 'sfuaudio_error_try_to_reconnect',
errorMessage, extraInfo: {
errorCode, errorMessage,
errorCause, errorCode,
bridge: this.bridgeName, errorCause,
role: this.role, bridge: this.bridgeName,
}, role: this.role,
}, 'SFU audio failed, try to reconnect'); },
this.reconnect(); }, 'SFU audio failed, try to reconnect');
return resolve(); this.reconnect();
return resolve();
}
if (RETRYABLE_ERRORS.includes(errorCode) && RETRY_THROUGH_RELAY) {
logger.error({
logCode: 'sfuaudio_error_retry_through_relay',
extraInfo: {
errorMessage,
errorCode,
errorCause,
bridge: this.bridgeName,
role: this.role,
},
}, 'SFU audio failed to connect, retry through relay');
this.reconnect({ forceRelay: true });
return resolve();
}
} }
// Already tried reconnecting once OR the user handn't succesfully // Already tried reconnecting once OR the user handn't succesfully
// connected firsthand. Just finish the session and reject with error // connected firsthand and retrying isn't an option. Finish the session
// and reject with the error
logger.error({ logger.error({
logCode: 'sfuaudio_error', logCode: 'sfuaudio_error',
extraInfo: { extraInfo: {
@ -189,7 +225,7 @@ export default class SFUAudioBridge extends BaseAudioBridge {
role: this.role, role: this.role,
}, },
}, 'SFU audio failed'); }, 'SFU audio failed');
this.clearReconnectionTimeout(); this.clearConnectionTimeout();
this.broker.stop(); this.broker.stop();
this.callback({ this.callback({
status: this.baseCallStates.failed, status: this.baseCallStates.failed,
@ -201,23 +237,23 @@ export default class SFUAudioBridge extends BaseAudioBridge {
}); });
} }
dispatchAutoplayHandlingEvent(mediaElement) { handleTermination() {
const tagFailedEvent = new CustomEvent('audioPlayFailed', { this.clearConnectionTimeout();
detail: { mediaElement }, return this.callback({ status: this.baseCallStates.ended, bridge: this.bridgeName });
});
window.dispatchEvent(tagFailedEvent);
this.callback({ status: this.baseCallStates.autoplayBlocked, bridge: this.bridgeName });
} }
handleStart() { handleStart() {
const stream = this.broker.webRtcPeer.getRemoteStream(); const stream = this.broker.webRtcPeer.getRemoteStream();
const mediaElement = document.getElementById(MEDIA_TAG); const mediaElement = document.getElementById(MEDIA_TAG);
return loadAndPlayMediaStream(stream, mediaElement, false).then(() => this return loadAndPlayMediaStream(stream, mediaElement, false).then(() => {
.callback({ this.callback({
status: this.baseCallStates.started, status: this.baseCallStates.started,
bridge: this.bridgeName, bridge: this.bridgeName,
})).catch((error) => { });
this.clearConnectionTimeout();
this.reconnecting = false;
}).catch((error) => {
// NotAllowedError equals autoplay issues, fire autoplay handling event. // NotAllowedError equals autoplay issues, fire autoplay handling event.
// This will be handled in audio-manager. // This will be handled in audio-manager.
if (error.name === 'NotAllowedError') { if (error.name === 'NotAllowedError') {
@ -247,9 +283,32 @@ export default class SFUAudioBridge extends BaseAudioBridge {
} }
async _startBroker(options) { async _startBroker(options) {
try {
this.iceServers = await fetchWebRTCMappedStunTurnServers(this.sessionToken);
} catch (error) {
logger.error({ logCode: 'sfuaudio_stun-turn_fetch_failed' },
'SFU audio bridge failed to fetch STUN/TURN info, using default servers');
this.iceServers = getMappedFallbackStun();
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const {
isListenOnly,
extension,
inputStream,
forceRelay: _forceRelay = false,
} = options;
const handleInitError = (_error) => {
mapErrorCode(_error);
if (RETRYABLE_ERRORS.includes(_error?.errorCode)
|| !RETRY_THROUGH_RELAY
|| this.reconnecting) {
reject(_error);
}
};
try { try {
const { isListenOnly, extension, inputStream } = options;
this.inEchoTest = !!extension; this.inEchoTest = !!extension;
this.isListenOnly = isListenOnly; this.isListenOnly = isListenOnly;
@ -259,7 +318,7 @@ export default class SFUAudioBridge extends BaseAudioBridge {
iceServers: this.iceServers, iceServers: this.iceServers,
mediaServer: getMediaServerAdapter(isListenOnly), mediaServer: getMediaServerAdapter(isListenOnly),
constraints: getAudioConstraints({ deviceId: this.inputDeviceId }), constraints: getAudioConstraints({ deviceId: this.inputDeviceId }),
forceRelay: shouldForceRelay(), forceRelay: _forceRelay || shouldForceRelay(),
stream: (inputStream && inputStream.active) ? inputStream : undefined, stream: (inputStream && inputStream.active) ? inputStream : undefined,
offering: isListenOnly ? LISTEN_ONLY_OFFERING : true, offering: isListenOnly ? LISTEN_ONLY_OFFERING : true,
signalCandidates: SIGNAL_CANDIDATES, signalCandidates: SIGNAL_CANDIDATES,
@ -283,25 +342,19 @@ export default class SFUAudioBridge extends BaseAudioBridge {
this.handleStart().then(resolve).catch(reject); this.handleStart().then(resolve).catch(reject);
}; };
this.broker.joinAudio().catch(reject); // Set up a connectionTimeout in case the server or network are botching
// negotiation or conn checks.
this.setConnectionTimeout();
this.broker.joinAudio().catch(handleInitError);
} catch (error) { } catch (error) {
logger.warn({ logCode: 'sfuaudio_bridge_broker_init_fail' }, handleInitError(error);
'Problem when initializing SFU broker for fullaudio bridge');
reject(error);
} }
}); });
} }
async joinAudio(options, callback) { async joinAudio(options, callback) {
this.callback = callback; this.callback = callback;
this.reconnecting = false;
try {
this.iceServers = await fetchWebRTCMappedStunTurnServers(this.sessionToken);
} catch (error) {
logger.error({ logCode: 'sfuaudio_stun-turn_fetch_failed' },
'SFU audio bridge failed to fetch STUN/TURN info, using default servers');
this.iceServers = getMappedFallbackStun();
}
return this._startBroker(options); return this._startBroker(options);
} }
@ -390,7 +443,8 @@ export default class SFUAudioBridge extends BaseAudioBridge {
exitAudio() { exitAudio() {
const mediaElement = document.getElementById(MEDIA_TAG); const mediaElement = document.getElementById(MEDIA_TAG);
this.clearReconnectionTimeout(); this.clearConnectionTimeout();
this.reconnecting = false;
if (this.broker) { if (this.broker) {
this.broker.stop(); this.broker.stop();

View File

@ -142,6 +142,7 @@ const IsTalkingWrapper = styled.div`
flex-direction: row; flex-direction: row;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
margin-top: 0.5rem;
`; `;
const Speaking = styled.div` const Speaking = styled.div`

View File

@ -20,7 +20,8 @@ import { isPresentationEnabled } from '/imports/ui/services/features';
const { isMobile } = deviceInfo; const { isMobile } = deviceInfo;
const propTypes = { const propTypes = {
allowDownloadable: PropTypes.bool.isRequired, allowDownloadOriginal: PropTypes.bool.isRequired,
allowDownloadWithAnnotations: PropTypes.bool.isRequired,
intl: PropTypes.shape({ intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired, formatMessage: PropTypes.func.isRequired,
}).isRequired, }).isRequired,
@ -804,7 +805,7 @@ class PresentationUploader extends Component {
renderPresentationList() { renderPresentationList() {
const { presentations } = this.state; const { presentations } = this.state;
const { intl, allowDownloadable } = this.props; const { intl } = this.props;
let presentationsSorted = presentations; let presentationsSorted = presentations;
@ -846,9 +847,7 @@ class PresentationUploader extends Component {
</tr> </tr>
<Styled.Head> <Styled.Head>
<th colSpan={4}>{intl.formatMessage(intlMessages.currentLabel)}</th> <th colSpan={4}>{intl.formatMessage(intlMessages.currentLabel)}</th>
{ <th>{intl.formatMessage(intlMessages.actionsLabel)}</th>
allowDownloadable ? <th>{intl.formatMessage(intlMessages.actionsLabel)}</th> : null
}
</Styled.Head> </Styled.Head>
</thead> </thead>
<tbody> <tbody>
@ -978,10 +977,10 @@ class PresentationUploader extends Component {
renderDownloadableWithAnnotationsHint() { renderDownloadableWithAnnotationsHint() {
const { const {
intl, intl,
allowDownloadable, allowDownloadWithAnnotations,
} = this.props; } = this.props;
return allowDownloadable ? ( return allowDownloadWithAnnotations ? (
<Styled.ExportHint> <Styled.ExportHint>
{intl.formatMessage(intlMessages.exportHint)} {intl.formatMessage(intlMessages.exportHint)}
</Styled.ExportHint> </Styled.ExportHint>
@ -994,7 +993,8 @@ class PresentationUploader extends Component {
const { const {
intl, intl,
selectedToBeNextCurrent, selectedToBeNextCurrent,
allowDownloadable, allowDownloadOriginal,
allowDownloadWithAnnotations,
renderPresentationItemStatus, renderPresentationItemStatus,
hasAnnotations, hasAnnotations,
} = this.props; } = this.props;
@ -1068,8 +1068,8 @@ class PresentationUploader extends Component {
</Styled.TableItemStatus> </Styled.TableItemStatus>
{ {
hasError ? null : ( hasError ? null : (
<Styled.TableItemActions notDownloadable={!allowDownloadable}> <Styled.TableItemActions notDownloadable={!allowDownloadOriginal}>
{allowDownloadable ? ( {allowDownloadOriginal || allowDownloadWithAnnotations ? (
<PresentationDownloadDropdown <PresentationDownloadDropdown
hasAnnotations={hasAnyAnnotation} hasAnnotations={hasAnyAnnotation}
disabled={shouldDisableExportButton} disabled={shouldDisableExportButton}
@ -1077,6 +1077,8 @@ class PresentationUploader extends Component {
aria-label={formattedDownloadAriaLabel} aria-label={formattedDownloadAriaLabel}
color="primary" color="primary"
isDownloadable={isDownloadable} isDownloadable={isDownloadable}
allowDownloadOriginal={allowDownloadOriginal}
allowDownloadWithAnnotations={allowDownloadWithAnnotations}
handleToggleDownloadable={this.handleToggleDownloadable} handleToggleDownloadable={this.handleToggleDownloadable}
item={item} item={item}
closeModal={() => Session.set('showUploadPresentationView', false)} closeModal={() => Session.set('showUploadPresentationView', false)}

View File

@ -8,7 +8,11 @@ import PresUploaderToast from '/imports/ui/components/presentation/presentation-
import PresentationUploader from './component'; import PresentationUploader from './component';
import { UsersContext } from '/imports/ui/components/components-data/users-context/context'; import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
import Auth from '/imports/ui/services/auth'; import Auth from '/imports/ui/services/auth';
import { isDownloadPresentationWithAnnotationsEnabled, isPresentationEnabled } from '/imports/ui/services/features'; import {
isDownloadPresentationWithAnnotationsEnabled,
isDownloadOriginalPresentationEnabled,
isPresentationEnabled,
} from '/imports/ui/services/features';
import { hasAnnotations } from '/imports/ui/components/whiteboard/service'; import { hasAnnotations } from '/imports/ui/components/whiteboard/service';
const PRESENTATION_CONFIG = Meteor.settings.public.presentation; const PRESENTATION_CONFIG = Meteor.settings.public.presentation;
@ -44,7 +48,8 @@ export default withTracker(() => {
fileSizeMax: PRESENTATION_CONFIG.mirroredFromBBBCore.uploadSizeMax, fileSizeMax: PRESENTATION_CONFIG.mirroredFromBBBCore.uploadSizeMax,
filePagesMax: PRESENTATION_CONFIG.mirroredFromBBBCore.uploadPagesMax, filePagesMax: PRESENTATION_CONFIG.mirroredFromBBBCore.uploadPagesMax,
fileValidMimeTypes: PRESENTATION_CONFIG.uploadValidMimeTypes, fileValidMimeTypes: PRESENTATION_CONFIG.uploadValidMimeTypes,
allowDownloadable: isDownloadPresentationWithAnnotationsEnabled(), allowDownloadOriginal: isDownloadOriginalPresentationEnabled(),
allowDownloadWithAnnotations: isDownloadPresentationWithAnnotationsEnabled(),
handleSave: Service.handleSavePresentation, handleSave: Service.handleSavePresentation,
handleDismissToast: PresUploaderToast.handleDismissToast, handleDismissToast: PresUploaderToast.handleDismissToast,
renderToastList: Service.renderToastList, renderToastList: Service.renderToastList,

View File

@ -74,6 +74,8 @@ class PresentationDownloadDropdown extends PureComponent {
handleDownloadingOfPresentation, handleDownloadingOfPresentation,
handleToggleDownloadable, handleToggleDownloadable,
isDownloadable, isDownloadable,
allowDownloadOriginal,
allowDownloadWithAnnotations,
item, item,
closeModal, closeModal,
} = this.props; } = this.props;
@ -88,31 +90,35 @@ class PresentationDownloadDropdown extends PureComponent {
closeModal(); closeModal();
}; };
if (!isDownloadable) { if (allowDownloadOriginal) {
if (!isDownloadable) {
this.menuItems.push({
key: this.actionsKey[0],
dataTest: 'enableOriginalPresentationDownload',
label: intl.formatMessage(intlMessages.enableOriginalPresentationDownload),
onClick: () => toggleDownloadOriginalPresentation(true),
});
} else {
this.menuItems.push({
key: this.actionsKey[0],
dataTest: 'disableOriginalPresentationDownload',
label: intl.formatMessage(intlMessages.disableOriginalPresentationDownload),
onClick: () => toggleDownloadOriginalPresentation(false),
});
}
}
if (allowDownloadWithAnnotations) {
this.menuItems.push({ this.menuItems.push({
key: this.actionsKey[0], key: this.actionsKey[1],
dataTest: 'enableOriginalPresentationDownload', id: 'sendCurrentStateDocument',
label: intl.formatMessage(intlMessages.enableOriginalPresentationDownload), dataTest: 'sendCurrentStateDocument',
onClick: () => toggleDownloadOriginalPresentation(true), label: intl.formatMessage(intlMessages.sendCurrentStateDocument),
}); onClick: () => {
} else { closeModal();
this.menuItems.push({ handleDownloadingOfPresentation('Annotated');
key: this.actionsKey[0], },
dataTest: 'disableOriginalPresentationDownload',
label: intl.formatMessage(intlMessages.disableOriginalPresentationDownload),
onClick: () => toggleDownloadOriginalPresentation(false),
}); });
} }
this.menuItems.push({
key: this.actionsKey[1],
id: 'sendCurrentStateDocument',
dataTest: 'sendCurrentStateDocument',
label: intl.formatMessage(intlMessages.sendCurrentStateDocument),
onClick: () => {
closeModal();
handleDownloadingOfPresentation('Annotated');
},
});
return this.menuItems; return this.menuItems;
} }

View File

@ -22,6 +22,7 @@ const TimerWrapper = styled.div`
`; `;
const Timer = styled.div` const Timer = styled.div`
margin-top: 0.5rem;
display: flex; display: flex;
max-height: ${timerPaddingXL}); max-height: ${timerPaddingXL});
`; `;

View File

@ -80,7 +80,7 @@ const UserAvatar = ({
}} }}
> >
<Styled.Talking talking={talking && !muted && avatar.length === 0} animations={animations} /> <Styled.Talking talking={talking && !muted} animations={animations} />
{avatar.length !== 0 && !emoji {avatar.length !== 0 && !emoji
? ( ? (

View File

@ -34,6 +34,11 @@ const Image = styled.div`
height: 100%; height: 100%;
width: 100%; width: 100%;
justify-content: center; justify-content: center;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
`; `;
const Img = styled.img` const Img = styled.img`

View File

@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Styled from './styles'; import Styled from './styles';
import { notify } from '/imports/ui/services/notification';
const ASK_MODERATOR = 'ASK_MODERATOR'; const ASK_MODERATOR = 'ASK_MODERATOR';
const ALWAYS_ACCEPT = 'ALWAYS_ACCEPT'; const ALWAYS_ACCEPT = 'ALWAYS_ACCEPT';
@ -36,6 +37,10 @@ const intlMessages = defineMessages({
id: 'app.guest-policy.button.alwaysDeny', id: 'app.guest-policy.button.alwaysDeny',
description: 'Always deny button label', description: 'Always deny button label',
}, },
feedbackMessage: {
id: 'app.guest-policy.feedbackMessage',
description: 'Feedback message for guest policy change',
},
}); });
const propTypes = { const propTypes = {
@ -47,18 +52,31 @@ const propTypes = {
}; };
class GuestPolicyComponent extends PureComponent { class GuestPolicyComponent extends PureComponent {
constructor(props) {
super(props);
this.handleChangePolicy = this.handleChangePolicy.bind(this);
}
componentWillUnmount() { componentWillUnmount() {
const { setIsOpen } = this.props; const { setIsOpen } = this.props;
setIsOpen(false); setIsOpen(false);
} }
handleChangePolicy(policyRule, messageId) {
const { intl, changeGuestPolicy } = this.props;
changeGuestPolicy(policyRule);
notify(intl.formatMessage(intlMessages.feedbackMessage) + intl.formatMessage(messageId), 'success');
}
render() { render() {
const { const {
setIsOpen, setIsOpen,
intl, intl,
guestPolicy, guestPolicy,
changeGuestPolicy,
isOpen, isOpen,
onRequestClose, onRequestClose,
priority, priority,
@ -91,7 +109,7 @@ class GuestPolicyComponent extends PureComponent {
aria-pressed={guestPolicy === ASK_MODERATOR} aria-pressed={guestPolicy === ASK_MODERATOR}
data-test="askModerator" data-test="askModerator"
onClick={() => { onClick={() => {
changeGuestPolicy(ASK_MODERATOR); this.handleChangePolicy(ASK_MODERATOR, intlMessages.askModerator);
setIsOpen(false); setIsOpen(false);
}} }}
/> />
@ -103,7 +121,7 @@ class GuestPolicyComponent extends PureComponent {
aria-pressed={guestPolicy === ALWAYS_ACCEPT} aria-pressed={guestPolicy === ALWAYS_ACCEPT}
data-test="alwaysAccept" data-test="alwaysAccept"
onClick={() => { onClick={() => {
changeGuestPolicy(ALWAYS_ACCEPT); this.handleChangePolicy(ALWAYS_ACCEPT, intlMessages.alwaysAccept);
setIsOpen(false); setIsOpen(false);
}} }}
/> />
@ -115,7 +133,7 @@ class GuestPolicyComponent extends PureComponent {
aria-pressed={guestPolicy === ALWAYS_DENY} aria-pressed={guestPolicy === ALWAYS_DENY}
data-test="alwaysDeny" data-test="alwaysDeny"
onClick={() => { onClick={() => {
changeGuestPolicy(ALWAYS_DENY); this.handleChangePolicy(ALWAYS_DENY, intlMessages.alwaysDeny);
setIsOpen(false); setIsOpen(false);
}} }}
/> />

View File

@ -61,7 +61,11 @@ export function isCustomVirtualBackgroundsEnabled() {
} }
export function isDownloadPresentationWithAnnotationsEnabled() { export function isDownloadPresentationWithAnnotationsEnabled() {
return getDisabledFeatures().indexOf('downloadPresentationWithAnnotations') === -1 && Meteor.settings.public.presentation.allowDownloadable; return getDisabledFeatures().indexOf('downloadPresentationWithAnnotations') === -1 && Meteor.settings.public.presentation.allowDownloadWithAnnotations;
}
export function isDownloadOriginalPresentationEnabled() {
return getDisabledFeatures().indexOf('downloadOriginalPresentation') === -1 && Meteor.settings.public.presentation.allowDownloadOriginal;
} }
export function isSnapshotOfCurrentSlideEnabled() { export function isSnapshotOfCurrentSlideEnabled() {

View File

@ -59,13 +59,13 @@ public:
# the default logoutUrl matches window.location.origin i.e. bigbluebutton.org for demo.bigbluebutton.org # the default logoutUrl matches window.location.origin i.e. bigbluebutton.org for demo.bigbluebutton.org
# in some cases we want only custom logoutUrl to be used when provided on meeting create. Default value: true # in some cases we want only custom logoutUrl to be used when provided on meeting create. Default value: true
askForConfirmationOnLeave: true askForConfirmationOnLeave: true
wakeLock: wakeLock:
enabled: false enabled: false
allowDefaultLogoutUrl: true allowDefaultLogoutUrl: true
allowUserLookup: false allowUserLookup: false
dynamicGuestPolicy: true dynamicGuestPolicy: true
enableGuestLobbyMessage: true enableGuestLobbyMessage: true
guestPolicyExtraAllowOptions: true guestPolicyExtraAllowOptions: false
alwaysShowWaitingRoomUI: true alwaysShowWaitingRoomUI: true
enableLimitOfViewersInWebcam: false enableLimitOfViewersInWebcam: false
enableMultipleCameras: true enableMultipleCameras: true
@ -169,7 +169,7 @@ public:
enableCopyNetworkStatsButton: true enableCopyNetworkStatsButton: true
# where should client settings be stored? if you run a single BBB server or # where should client settings be stored? if you run a single BBB server or
# a cluster with a reverse proxy in front of it, you may set this to 'local' # a cluster with a reverse proxy in front of it, you may set this to 'local'
# See https://docs.bigbluebutton.org/administration/cluster-proxy # See See https://docs.bigbluebutton.org/administration/cluster-proxy
# allowed values: # allowed values:
# 'session' -> settings are stored in browser sessionStorage # 'session' -> settings are stored in browser sessionStorage
# 'local' -> settings are stored in browser localStorage # 'local' -> settings are stored in browser localStorage
@ -563,7 +563,7 @@ public:
type_private: PRIVATE_ACCESS type_private: PRIVATE_ACCESS
system_userid: SYSTEM_MESSAGE system_userid: SYSTEM_MESSAGE
system_username: SYSTEM_MESSAGE system_username: SYSTEM_MESSAGE
public_id: MAIN-PUBLIC-GROUP-CHAT public_id: public
public_group_id: MAIN-PUBLIC-GROUP-CHAT public_group_id: MAIN-PUBLIC-GROUP-CHAT
public_userid: public_chat_userid public_userid: public_chat_userid
public_username: public_chat_username public_username: public_chat_username
@ -639,6 +639,9 @@ public:
path: 'bridge/sip' path: 'bridge/sip'
- name: fullaudio - name: fullaudio
path: 'bridge/sfu-audio-bridge' path: 'bridge/sfu-audio-bridge'
# Forces a retry with iceTransportPolicy = 'relay' if the first attempt
# fails with a few selected errors codes (eg 1007, 1010)
retryThroughRelay: false
stunTurnServersFetchAddress: '/bigbluebutton/api/stuns' stunTurnServersFetchAddress: '/bigbluebutton/api/stuns'
cacheStunTurnServers: true cacheStunTurnServers: true
fallbackStunServer: '' fallbackStunServer: ''
@ -653,7 +656,7 @@ public:
callHangupTimeout: 2000 callHangupTimeout: 2000
callHangupMaximumRetries: 10 callHangupMaximumRetries: 10
echoTestNumber: 'echo' echoTestNumber: 'echo'
listenOnlyCallTimeout: 25000 listenOnlyCallTimeout: 15000
# Experimental. True is the canonical behavior. Flip to false to reverse # Experimental. True is the canonical behavior. Flip to false to reverse
# the negotiation flow for LO subscribers. # the negotiation flow for LO subscribers.
listenOnlyOffering: false listenOnlyOffering: false
@ -742,7 +745,8 @@ public:
- critical - critical
help: STATS_HELP_URL help: STATS_HELP_URL
presentation: presentation:
allowDownloadable: true allowDownloadOriginal: true
allowDownloadWithAnnotations: true
allowSnapshotOfCurrentSlide: true allowSnapshotOfCurrentSlide: true
panZoomThrottle: 32 panZoomThrottle: 32
restoreOnUpdate: true restoreOnUpdate: true

View File

@ -288,7 +288,7 @@
"app.presentationUploader.sent": "Sent", "app.presentationUploader.sent": "Sent",
"app.presentationUploader.exportingTimeout": "The export is taking too long...", "app.presentationUploader.exportingTimeout": "The export is taking too long...",
"app.presentationUploader.export": "Send to chat", "app.presentationUploader.export": "Send to chat",
"app.presentationUploader.exportCurrentStatePresentation": "Send out a download link for the presentation in the current state of it", "app.presentationUploader.exportCurrentStatePresentation": "Send out a download link for the presentation in its current state",
"app.presentationUploader.enableOriginalPresentationDownload": "Enable download of the original presentation", "app.presentationUploader.enableOriginalPresentationDownload": "Enable download of the original presentation",
"app.presentationUploader.disableOriginalPresentationDownload": "Disable download of the original presentation", "app.presentationUploader.disableOriginalPresentationDownload": "Disable download of the original presentation",
"app.presentationUploader.dropdownExportOptions": "Export options", "app.presentationUploader.dropdownExportOptions": "Export options",
@ -969,6 +969,7 @@
"app.guest-policy.button.alwaysAccept": "Always accept", "app.guest-policy.button.alwaysAccept": "Always accept",
"app.guest-policy.button.alwaysDeny": "Always deny", "app.guest-policy.button.alwaysDeny": "Always deny",
"app.guest-policy.policyBtnDesc": "Sets meeting guest policy", "app.guest-policy.policyBtnDesc": "Sets meeting guest policy",
"app.guest-policy.feedbackMessage": "The guest policy is now: ",
"app.connection-status.ariaTitle": "Connection status modal", "app.connection-status.ariaTitle": "Connection status modal",
"app.connection-status.title": "Connection status", "app.connection-status.title": "Connection status",
"app.connection-status.description": "View users' connection status", "app.connection-status.description": "View users' connection status",

View File

@ -28,7 +28,8 @@ async function generateSettingsData(page) {
pollEnabled: settingsData.poll.enabled, pollEnabled: settingsData.poll.enabled,
pollChatMessage: settingsData.poll.chatMessage, pollChatMessage: settingsData.poll.chatMessage,
// Presentation // Presentation
presentationDownloadable: settingsData.presentation.allowDownloadable, originalPresentationDownloadable: settingsData.presentation.allowDownloadOriginal,
presentationWithAnnotationsDownloadable: settingsData.presentation.allowDownloadWithAnnotations,
externalVideoPlayer: settingsData.externalVideoPlayer.enabled, externalVideoPlayer: settingsData.externalVideoPlayer.enabled,
presentationHidden: settingsData.layout.hidePresentation, presentationHidden: settingsData.layout.hidePresentation,
// Screensharing // Screensharing

View File

@ -5,8 +5,8 @@
"test:filter": "npx playwright test -g", "test:filter": "npx playwright test -g",
"test:headed": "npx playwright test --headed", "test:headed": "npx playwright test --headed",
"test:debug": "npx playwright test --debug -g", "test:debug": "npx playwright test --debug -g",
"test-chromium-ci": "export CI='true' && npx playwright test --project=chromium --grep @ci", "test-chromium-ci": "export CI='true' && npx playwright test --project=chromium --grep @ci --grep-invert @flaky",
"test-firefox-ci": "export CI='true' && npx playwright test --project=firefox --grep @ci", "test-firefox-ci": "export CI='true' && npx playwright test --project=firefox --grep @ci --grep-invert @flaky",
"rewrite-snapshots": "read -p 'CAUTION: You will delete ALL testing folders containing snapshots and run the tests to rewrite these files.\nProceed? (y/n) ' confirm && test $confirm = 'y' && sh core/scripts/rewrite-snapshots.sh" "rewrite-snapshots": "read -p 'CAUTION: You will delete ALL testing folders containing snapshots and run the tests to rewrite these files.\nProceed? (y/n) ' confirm && test $confirm = 'y' && sh core/scripts/rewrite-snapshots.sh"
}, },
"dependencies": { "dependencies": {
@ -19,4 +19,4 @@
"sha1": "^1.1.1", "sha1": "^1.1.1",
"xml2js": "^0.6.0" "xml2js": "^0.6.0"
} }
} }

View File

@ -4,6 +4,7 @@ const { DisabledFeatures } = require('./disabledFeatures');
const c = require('./constants'); const c = require('./constants');
const { encodeCustomParams, getAllShortcutParams, hexToRgb } = require('./util'); const { encodeCustomParams, getAllShortcutParams, hexToRgb } = require('./util');
const { CreateParameters } = require('./createParameters'); const { CreateParameters } = require('./createParameters');
const { linkIssue } = require('../core/helpers');
test.describe.parallel('Create Parameters', () => { test.describe.parallel('Create Parameters', () => {
test('Record Meeting', async ({ browser, context, page }) => { test('Record Meeting', async ({ browser, context, page }) => {
@ -257,7 +258,7 @@ test.describe.parallel('Create Parameters', () => {
await disabledFeatures.downloadPresentationWithAnnotationsExclude(); await disabledFeatures.downloadPresentationWithAnnotationsExclude();
}); });
}); });
test.describe.serial(() => { test.describe.serial(() => {
test('Import Presentation With Annotations From Breakout Rooms', async ({ browser, context, page }) => { test('Import Presentation With Annotations From Breakout Rooms', async ({ browser, context, page }) => {
const disabledFeatures = new DisabledFeatures(browser, context); const disabledFeatures = new DisabledFeatures(browser, context);

View File

@ -122,8 +122,8 @@ class Presentation extends MultiUsers {
} }
async enableAndDisablePresentationDownload(testInfo) { async enableAndDisablePresentationDownload(testInfo) {
const { presentationDownloadable } = getSettings(); const { originalPresentationDownloadable } = getSettings();
test.fail(!presentationDownloadable, 'Presentation download is disable'); test.fail(!originalPresentationDownloadable, 'Presentation download is disable');
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME); await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
// enable original presentation download // enable original presentation download
@ -152,8 +152,8 @@ class Presentation extends MultiUsers {
} }
async sendPresentationToDownload(testInfo) { async sendPresentationToDownload(testInfo) {
const { presentationDownloadable } = getSettings(); const { presentationWithAnnotationsDownloadable } = getSettings();
test.fail(!presentationDownloadable, 'Presentation download is disable'); test.fail(!presentationWithAnnotationsDownloadable, 'Presentation download is disable');
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME); await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await this.modPage.waitAndClick(e.actions); await this.modPage.waitAndClick(e.actions);

View File

@ -89,7 +89,7 @@ test.describe.parallel('Presentation', () => {
await presentation.initPages(page); await presentation.initPages(page);
await presentation.enableAndDisablePresentationDownload(testInfo); await presentation.enableAndDisablePresentationDownload(testInfo);
}); });
test('Send presentation in the current state (with annotations) to chat for downloading @ci', async ({ browser, context, page }, testInfo) => { test('Send presentation in the current state (with annotations) to chat for downloading @ci', async ({ browser, context, page }, testInfo) => {
const presentation = new Presentation(browser, context); const presentation = new Presentation(browser, context);
await presentation.initPages(page); await presentation.initPages(page);

View File

@ -2,9 +2,9 @@ const { test } = require('@playwright/test');
const { MultiUsers } = require('../user/multiusers'); const { MultiUsers } = require('../user/multiusers');
const { Webcam } = require('./webcam'); const { Webcam } = require('./webcam');
test.describe.parallel('Webcam @ci', () => { test.describe.parallel('Webcam', () => {
// https://docs.bigbluebutton.org/2.6/release-tests.html#joining-webcam-automated // https://docs.bigbluebutton.org/2.6/release-tests.html#joining-webcam-automated
test('Shares webcam', async ({ browser, page }) => { test('Shares webcam @ci', async ({ browser, page }) => {
const webcam = new Webcam(browser, page); const webcam = new Webcam(browser, page);
await webcam.init(true, true); await webcam.init(true, true);
await webcam.share(); await webcam.share();
@ -49,7 +49,9 @@ test.describe.parallel('Webcam @ci', () => {
await webcam.applyBackground(); await webcam.applyBackground();
}); });
test('Managing new background', async ({ browser, page }) => { // following test is throwing failures due to mis-comparison screenshot
// as the emulated video is not static, we may add a mask in the middle part - where it moves the most
test('Managing new background @flaky', async ({ browser, page }) => {
const webcam = new Webcam(browser, page); const webcam = new Webcam(browser, page);
await webcam.init(true, true); await webcam.init(true, true);
await webcam.managingNewBackground(); await webcam.managingNewBackground();

View File

@ -432,7 +432,8 @@ endWhenNoModeratorDelayInMinutes=1
# Available options: # Available options:
# chat, sharedNotes, polls, screenshare, externalVideos, presentation, downloadPresentationWithAnnotations # chat, sharedNotes, polls, screenshare, externalVideos, presentation, downloadPresentationWithAnnotations
# learningDashboard, layouts, captions, liveTranscription, virtualBackgrounds, customVirtualBackgrounds # learningDashboard, layouts, captions, liveTranscription, virtualBackgrounds, customVirtualBackgrounds
# breakoutRooms, importSharedNotesFromBreakoutRooms, importPresentationWithAnnotationsFromBreakoutRooms # breakoutRooms, importSharedNotesFromBreakoutRooms, importPresentationWithAnnotationsFromBreakoutRooms,
# downloadOriginalPresentation
disabledFeatures= disabledFeatures=
# Notify users that recording is on # Notify users that recording is on

View File

@ -24,6 +24,15 @@ else
fi fi
COMMIT_DATE="$(git log -n1 --pretty='format:%cd' --date=format:'%Y%m%dT%H%M%S')" COMMIT_DATE="$(git log -n1 --pretty='format:%cd' --date=format:'%Y%m%dT%H%M%S')"
# FORCE_GIT_REV and FORCE_COMMIT_DATE are useful for Github Actions be able to cache previous packages
# It sets FORCE_GIT_REV=0 and FORCE_COMMIT_DATE=0 in order to keep the same package version always
if [ ! -z "$FORCE_GIT_REV" ]; then
GIT_REV=$FORCE_GIT_REV
fi
if [ ! -z "$FORCE_COMMIT_DATE" ]; then
COMMIT_DATE=$FORCE_COMMIT_DATE
fi
# Arrange to write the docker container ID to a temp file, then run # Arrange to write the docker container ID to a temp file, then run
# the container detached and immediately attach it (without stdin) so # the container detached and immediately attach it (without stdin) so
# we can catch CTRL-C in this script and kill the container if so. # we can catch CTRL-C in this script and kill the container if so.

View File

@ -118,6 +118,7 @@ For full details on what is new in BigBlueButton 2.7, see the release notes.
Recent releases: Recent releases:
- [2.7.0-beta.3](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-beta.3)
- [2.7.0-beta.2](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-beta.2) - [2.7.0-beta.2](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-beta.2)
- [2.7.0-beta.1](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-beta.1) - [2.7.0-beta.1](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-beta.1)
- [2.7.0-alpha.3](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-alpha.3) - [2.7.0-alpha.3](https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.7.0-alpha.3)
@ -130,6 +131,10 @@ Recent releases:
If you are using bbb-install to configure your servers, be aware that starting with BigBlueButton 2.6's version of bbb-install by default we install a local TURN server. For more information: https://github.com/bigbluebutton/bbb-install/pull/579 and https://docs.bigbluebutton.org/administration/turn-server If you are using bbb-install to configure your servers, be aware that starting with BigBlueButton 2.6's version of bbb-install by default we install a local TURN server. For more information: https://github.com/bigbluebutton/bbb-install/pull/579 and https://docs.bigbluebutton.org/administration/turn-server
#### Changing the default setting `guestPolicyExtraAllowOptions`
Starting with BigBlueButton 2.7.0-beta.3 we are hiding by default a couple extra options in the guest approve panel. 'Allow all authenticated users' and 'Allow all guests' options will be hidden unless you override the option `app.public.guestPolicyExtraAllowOptions` in `bbb-html5` config file `settings.yml`. These extra options were not relevant to the vast majority of the use cases and when hidden, the interface becomes much simpler.
### Development ### Development
For information on developing in BigBlueButton, see [setting up a development environment for 2.7](/development/guide). For information on developing in BigBlueButton, see [setting up a development environment for 2.7](/development/guide).