Compare commits
615 Commits
develop
...
snyk-fix-4
Author | SHA1 | Date | |
---|---|---|---|
|
dffea814d4 | ||
|
6f08344a70 | ||
|
1484f516d0 | ||
|
8be32a7bf3 | ||
|
f66f07d177 | ||
|
7a8abd0127 | ||
|
3dd588d704 | ||
|
ff0b2f33b6 | ||
|
7371aa6c18 | ||
|
36b5a16c15 | ||
|
562a13f527 | ||
|
cf6202c572 | ||
|
e6e1f28036 | ||
|
b4a11facd4 | ||
|
ea7867340f | ||
|
07c0433ba0 | ||
|
a642391caf | ||
|
656cdb44cf | ||
|
352f3ebd58 | ||
|
565a8bfcd4 | ||
|
cf966e396b | ||
|
466e328477 | ||
|
06d1a74cdc | ||
|
2c6de02f1c | ||
|
d4ee755a2d | ||
|
3eb3e345af | ||
|
ca18587289 | ||
|
f85bc7f976 | ||
|
6ec1272a2b | ||
|
894515ddb6 | ||
|
450a36d244 | ||
|
3282f230a9 | ||
|
20d4944ce6 | ||
|
80780ddde1 | ||
|
31ac4e7b52 | ||
|
0ea1d8894f | ||
|
dc11f2977b | ||
|
8287fe04d7 | ||
|
4f193ba67a | ||
|
7274fd90da | ||
|
c441b8531a | ||
|
99d1262ff1 | ||
|
59deebdbcb | ||
|
1e2731ef63 | ||
|
565f8a14c7 | ||
|
6cd9d0513e | ||
|
e28a595b52 | ||
|
027115aa14 | ||
|
e1eabefc56 | ||
|
c35580ece4 | ||
|
3b8e7e1968 | ||
|
06c5a2eece | ||
|
290b03e35f | ||
|
4f9ca0e7a3 | ||
|
d0c28c84ec | ||
|
2c20dc3329 | ||
|
2beb822cbe | ||
|
91b39664f9 | ||
|
8b0175e67b | ||
|
5f26293cb2 | ||
|
d9cf1a07d9 | ||
|
9d3b6f0b6c | ||
|
31336c8290 | ||
|
fff955b211 | ||
|
5387fef101 | ||
|
b248143cae | ||
|
437c684423 | ||
|
6918d4e090 | ||
|
1c57db94bb | ||
|
631d5186e5 | ||
|
d3e2d3965c | ||
|
025942de5b | ||
|
623c90b19f | ||
|
4ccc883437 | ||
|
3648748ce0 | ||
|
baa62ab1ef | ||
|
9377121704 | ||
|
7fbf51054b | ||
|
8b6cdc6edf | ||
|
f390049fea | ||
|
610f9a2650 | ||
|
4c6e0289d4 | ||
|
fa0ad14c35 | ||
|
2317d2f98f | ||
|
358a5bd901 | ||
|
15525a6936 | ||
|
bfae93eb0e | ||
|
864e63ffcc | ||
|
f460d54a4e | ||
|
cf8f7528d0 | ||
|
6dc88facd9 | ||
|
a46e38fbbe | ||
|
da3d8140da | ||
|
f5665f3bc4 | ||
|
0c9e11c0e7 | ||
|
fe3b81628f | ||
|
ce8bb567a0 | ||
|
a86bdc9f1e | ||
|
2ef8b4649f | ||
|
a71527a825 | ||
|
59e2609ffa | ||
|
063b77a0fc | ||
|
a4756314a7 | ||
|
de018453f6 | ||
|
9539ad09c0 | ||
|
65ebaecc9a | ||
|
c662a08d12 | ||
|
91762178b9 | ||
|
fd929a1508 | ||
|
b697667364 | ||
|
aeaf206f33 | ||
|
7b42421f41 | ||
|
6588537bf4 | ||
|
e8a361f11c | ||
|
61b94e149d | ||
|
677177b72b | ||
|
7cab994333 | ||
|
77d6f77452 | ||
|
885ba839c6 | ||
|
ed929295f4 | ||
|
a9cd4718ba | ||
|
9080d2e42a | ||
|
d966ea759a | ||
|
b7d2ceff58 | ||
|
7911978d58 | ||
|
a239384c39 | ||
|
89cf90b2cb | ||
|
8efbde2bc5 | ||
|
859cfc1f85 | ||
|
37d6aa0140 | ||
|
496a42ff90 | ||
|
c0caafd2e6 | ||
|
d42861dff9 | ||
|
443b80904b | ||
|
25147a5719 | ||
|
4bab8cf0ca | ||
|
ae70940d0e | ||
|
fe5b15ee5b | ||
|
9b60e6ae5a | ||
|
81e025b3d9 | ||
|
fdb95bb693 | ||
|
acc8ea4809 | ||
|
8bfe0a9b99 | ||
|
fc33fa599d | ||
|
ee873794e9 | ||
|
109317f3e5 | ||
|
285ad950a4 | ||
|
1f774ce888 | ||
|
a3a0bba93a | ||
|
45352c7721 | ||
|
9e2ea86c3d | ||
|
c697caf00c | ||
|
e28b29bd0c | ||
|
ab5dd32744 | ||
|
6021da1004 | ||
|
d6f22c2cd7 | ||
|
f57a1d07de | ||
|
04e916798b | ||
|
d3e78e7d1e | ||
|
ea6e9461dc | ||
|
62eb2c53cf | ||
|
63e59ede1e | ||
|
301cf342a9 | ||
|
ee8b1021b9 | ||
|
220b0a9040 | ||
|
5bc104fbf8 | ||
|
376037c31e | ||
|
a943717c26 | ||
|
cc53fb58f2 | ||
|
2b6d313c80 | ||
|
aebb693379 | ||
|
122b706035 | ||
|
e757cf15ee | ||
|
7a1081a974 | ||
|
06b7628f61 | ||
|
b2b57aca03 | ||
|
4a1c81fa76 | ||
|
6ea1559ced | ||
|
c6b9364204 | ||
|
b2a08bfaff | ||
|
0f691e8968 | ||
|
4c4fc491d3 | ||
|
b6087ab8cb | ||
|
d749a2af08 | ||
|
5810f8b662 | ||
|
fc6f2581c6 | ||
|
0dc21a34d6 | ||
|
15e857abe5 | ||
|
d30974c771 | ||
|
4d9e20891d | ||
|
eb29634a87 | ||
|
4e7d7dd452 | ||
|
8dad2db2e2 | ||
|
41c58d2466 | ||
|
356f6331c1 | ||
|
64d5afc864 | ||
|
13f66bddda | ||
|
6b4f2de798 | ||
|
e5a9ce705f | ||
|
1fc488bfea | ||
|
6a146c0548 | ||
|
400fff96c9 | ||
|
d4ce0b26b1 | ||
|
013ba50137 | ||
|
b81d624c66 | ||
|
54403fb587 | ||
|
d0307e6f21 | ||
|
850e8af19a | ||
|
d484867c99 | ||
|
403959bc34 | ||
|
8bf56ccad9 | ||
|
5f59453548 | ||
|
2084392ac7 | ||
|
141c553b17 | ||
|
9956af9aa1 | ||
|
f91402bc4a | ||
|
a2e22fffb3 | ||
|
698a736d58 | ||
|
da60b246d0 | ||
|
fd6bd798f8 | ||
|
fa38c7747e | ||
|
7d156e8828 | ||
|
be96230894 | ||
|
c83999dd46 | ||
|
de83c75716 | ||
|
ebe0488aa6 | ||
|
35c2370aca | ||
|
ea21caec53 | ||
|
4cb07ad7e7 | ||
|
0a0e8ef773 | ||
|
503e7420a2 | ||
|
0eae452d25 | ||
|
3c2d89280b | ||
|
1b481a9500 | ||
|
5f1c07f4c0 | ||
|
ffe42d1b10 | ||
|
b4158cbf14 | ||
|
b5fe0ac64c | ||
|
304c3136be | ||
|
e24e358ddd | ||
|
a184c67e44 | ||
|
c6ad69f292 | ||
|
3b3e7d840a | ||
|
f5be95da32 | ||
|
49c33b4eea | ||
|
45e1724954 | ||
|
31537b1910 | ||
|
8e40d91877 | ||
|
183983be7f | ||
|
1e9e461f50 | ||
|
916d601d2b | ||
|
85b5e83bd5 | ||
|
6eaf8cd44d | ||
|
b98340bd27 | ||
|
10cb2f3875 | ||
|
2f3e709324 | ||
|
711487c2c9 | ||
|
e511535adc | ||
|
fda4a5a2ff | ||
|
72032f2397 | ||
|
6cb0e914ab | ||
|
68f66a1fbb | ||
|
501d627392 | ||
|
610f3b165b | ||
|
10a6a840b5 | ||
|
71f958ca47 | ||
|
555a8f6522 | ||
|
66788e9697 | ||
|
4e93a4de72 | ||
|
29ea5c3e07 | ||
|
3e845076cb | ||
|
e4cced4b92 | ||
|
9676afceaf | ||
|
f1bad0257d | ||
|
33a704981e | ||
|
fcbfcb1bbc | ||
|
8374e8b21a | ||
|
8502b50d1b | ||
|
7bf1cdd028 | ||
|
c7006d8d3f | ||
|
efa3ca0245 | ||
|
6f4cb742d8 | ||
|
3359289e1a | ||
|
fdae7b48f4 | ||
|
07db720f3e | ||
|
2c2d2186ea | ||
|
990daeeb07 | ||
|
ae9a601660 | ||
|
daf03638ae | ||
|
2fc3119c14 | ||
|
2466bb289e | ||
|
a0733d2c5b | ||
|
882ce76d75 | ||
|
9ada7458d3 | ||
|
766f5e3af7 | ||
|
f41aec10d4 | ||
|
1b84bb318a | ||
|
ac8acfe507 | ||
|
64c4567d06 | ||
|
2c3939d147 | ||
|
578daa130a | ||
|
c69b79872b | ||
|
e1e00ea24d | ||
|
7b34845c0b | ||
|
fe76d76913 | ||
|
8e067c4eef | ||
|
980056a2e8 | ||
|
d90caf77dc | ||
|
2d66db96a1 | ||
|
b418f115fe | ||
|
4e8a708c95 | ||
|
6e9fbfb216 | ||
|
4b469f03c5 | ||
|
fdbab50205 | ||
|
2fec7076b6 | ||
|
70be65b20a | ||
|
3466f41fbf | ||
|
a6c8012277 | ||
|
1f8ca310f1 | ||
|
9506144857 | ||
|
83f75cc727 | ||
|
b26e32d519 | ||
|
6635b0ec14 | ||
|
361717c69a | ||
|
ca7c2d1589 | ||
|
3390d30fc8 | ||
|
f6b2d276a7 | ||
|
25e619a4f6 | ||
|
abea38e718 | ||
|
6cf3cd47fb | ||
|
4879d3e688 | ||
|
f255f4b69e | ||
|
80d4bdbfef | ||
|
95d013d645 | ||
|
a4c6f98d59 | ||
|
1ed6d0fdd2 | ||
|
78ece5f05c | ||
|
96a583b3e5 | ||
|
fbf5cfa7d2 | ||
|
2f091ffd3d | ||
|
5f0eb9ecb9 | ||
|
51519e9c7a | ||
|
1f97a6f253 | ||
|
95c945f4b3 | ||
|
fa872e2b55 | ||
|
04714848cf | ||
|
08a50f8c01 | ||
|
17c132376a | ||
|
22be736c88 | ||
|
aca3730d9f | ||
|
388858000c | ||
|
a744193536 | ||
|
8d741fa016 | ||
|
0e38681e32 | ||
|
5d6c066acb | ||
|
5f963c4465 | ||
|
34f8cf30ba | ||
|
b22a9d0c8b | ||
|
b4528a0032 | ||
|
d924e23069 | ||
|
aa1af6b83f | ||
|
5cf2b78d22 | ||
|
8d3473cd4d | ||
|
e13fc75f6d | ||
|
4ede1a1218 | ||
|
5398df1a8a | ||
|
22efe4647f | ||
|
1babc38e7e | ||
|
e1dc4b55e4 | ||
|
262cd0b735 | ||
|
55238d0b50 | ||
|
afe30cf678 | ||
|
d54fd57c00 | ||
|
551028ba51 | ||
|
75af5c03e7 | ||
|
792add0a3d | ||
|
e59d6dcea8 | ||
|
8495777d5b | ||
|
7551eaebb0 | ||
|
fd8c927140 | ||
|
59cdb136ad | ||
|
f50e10b5ea | ||
|
35bf9b537c | ||
|
45585215cc | ||
|
bb2ecb7c66 | ||
|
aadb5f688f | ||
|
b8c2f3f774 | ||
|
e64ab86dd7 | ||
|
47c5a04e21 | ||
|
d4cea455c1 | ||
|
e778f5857c | ||
|
fe6993d699 | ||
|
e7840e19c5 | ||
|
86caf70e3b | ||
|
bb126a953f | ||
|
98a62c0fab | ||
|
3da6812407 | ||
|
66b3f00e4b | ||
|
d5f7e7d0e4 | ||
|
dedd455863 | ||
|
999ed90a56 | ||
|
5db198e6a5 | ||
|
54bcd9076e | ||
|
ddb7fb77f5 | ||
|
94534455b8 | ||
|
d0a703e734 | ||
|
14b8603164 | ||
|
59a23ed98d | ||
|
a8d2307b9a | ||
|
333b6727b9 | ||
|
13e27ab154 | ||
|
5f7c747698 | ||
|
a9f68154cf | ||
|
4f8fa07702 | ||
|
62c7f5a35a | ||
|
125d70699b | ||
|
c88c23302e | ||
|
2a38249ddf | ||
|
453bb8fe73 | ||
|
283a029e6c | ||
|
08115a93a6 | ||
|
630c79e5c7 | ||
|
742733f7a8 | ||
|
2ac1e7cb97 | ||
|
f19aac1895 | ||
|
409cf1decb | ||
|
5a9c3a140a | ||
|
e15f50e7a8 | ||
|
a8495c8433 | ||
|
8eb425b4bc | ||
|
d683ab2240 | ||
|
3daf0ba7d3 | ||
|
44d655523f | ||
|
cd21fffaaa | ||
|
9e8acbe007 | ||
|
c541c2fdc2 | ||
|
78f26fd599 | ||
|
ae2fb8ba78 | ||
|
5a24e9a520 | ||
|
8b7cfdb54f | ||
|
d041fe660b | ||
|
2282b6220a | ||
|
70cccbe9b7 | ||
|
41348a1767 | ||
|
374ad5045d | ||
|
b61ceb2883 | ||
|
cfa15e8b60 | ||
|
bde7fe7dba | ||
|
5063cb5dfa | ||
|
15b70514fb | ||
|
74c2a259ba | ||
|
5f45e8b2fc | ||
|
32d24e564c | ||
|
229fb4ad09 | ||
|
af05863ad3 | ||
|
db8452f3f1 | ||
|
6f86883324 | ||
|
2471174b51 | ||
|
852b80850f | ||
|
9dd0bb56e2 | ||
|
6a3323ea3a | ||
|
1724f9fce2 | ||
|
92df679e2c | ||
|
5d671b3b50 | ||
|
31d2bf798e | ||
|
bb1a0b75d7 | ||
|
bba51c38fa | ||
|
d71202c195 | ||
|
f314aaf046 | ||
|
8b4b226603 | ||
|
686903a529 | ||
|
c7149c931b | ||
|
d72e8d98d7 | ||
|
c6ccb19fdc | ||
|
2f12992c20 | ||
|
2f8daf34fa | ||
|
c594be415a | ||
|
1b61900548 | ||
|
72029590e5 | ||
|
fd8f0bddab | ||
|
3de1cfed29 | ||
|
0970160ede | ||
|
301b7cd0f2 | ||
|
37870bb1d2 | ||
|
8db78c7200 | ||
|
80b6251c09 | ||
|
bedef5bf89 | ||
|
fbe79a07d7 | ||
|
b9c619a4a6 | ||
|
49864dc6e6 | ||
|
b8e1656d14 | ||
|
ab64a9e265 | ||
|
96b6d02768 | ||
|
64a0d73812 | ||
|
188894612d | ||
|
221327e4c5 | ||
|
07f6edd515 | ||
|
17427fbd9b | ||
|
cc1caf443f | ||
|
42b7905065 | ||
|
b5e6f528fc | ||
|
a8488ac841 | ||
|
7b9e3e09cd | ||
|
1b65c36743 | ||
|
0688cba0b4 | ||
|
b9ebd45ac3 | ||
|
e5b8b2f7d9 | ||
|
06262e309a | ||
|
34c3918e20 | ||
|
72eb50860c | ||
|
bd4edcf726 | ||
|
f910c6ff0f | ||
|
decfd3335b | ||
|
e9f1d76330 | ||
|
dca4f734ff | ||
|
f71e3f8965 | ||
|
a7e5df126f | ||
|
684d54da66 | ||
|
7021d3fa8d | ||
|
bd967e8193 | ||
|
7a26d761b0 | ||
|
54d56524c9 | ||
|
1571573fee | ||
|
a9973d3cff | ||
|
4a44e37d93 | ||
|
a1dd1371e8 | ||
|
ed44b33b15 | ||
|
281308ba4a | ||
|
371378cca8 | ||
|
39de071ab2 | ||
|
545712db34 | ||
|
ce883f8678 | ||
|
4e1e5e361e | ||
|
730803cc20 | ||
|
d33a1c028a | ||
|
3dc4cb28d9 | ||
|
bc7c3143ba | ||
|
ab370872bf | ||
|
f2dbd891cd | ||
|
65b4c5d5d7 | ||
|
d24cdfde6f | ||
|
eb82bbb6de | ||
|
134fe725de | ||
|
f5175d0b71 | ||
|
33f44e1dca | ||
|
c026ecc727 | ||
|
73b7bded08 | ||
|
423bf8ab28 | ||
|
44bb3a67de | ||
|
a2d7743773 | ||
|
df6da10cea | ||
|
77b29d745a | ||
|
0693103e14 | ||
|
94347949d9 | ||
|
c14b1510c2 | ||
|
76fd4508c9 | ||
|
d6670a4145 | ||
|
520668632f | ||
|
c125cbc720 | ||
|
c89154d2a8 | ||
|
82b9b1f408 | ||
|
f230fea85a | ||
|
b994e45f67 | ||
|
01c3f6fb45 | ||
|
577ed647d9 | ||
|
20b4ffe7dd | ||
|
cb075137b3 | ||
|
b2f25decd6 | ||
|
cb744adf52 | ||
|
5f1bed6d44 | ||
|
a22068be18 | ||
|
d7d228d047 | ||
|
2e980ca5d4 | ||
|
6283c856fb | ||
|
11f5481b5a | ||
|
ac3c7790d4 | ||
|
09687431b7 | ||
|
f046ec20af | ||
|
f327c988b9 | ||
|
693183291d | ||
|
c4e24e93bf | ||
|
ea2b855bf7 | ||
|
31db461114 | ||
|
1dc942243b | ||
|
898d6a6156 | ||
|
f7a77beb61 | ||
|
6129314a33 | ||
|
fd1a27fe21 | ||
|
fb0b98a6af | ||
|
8388d55f80 | ||
|
5cd379910f | ||
|
c390799eab | ||
|
d22c9568b8 | ||
|
791074819d | ||
|
f0e5d8aa9d | ||
|
f63bbdceed | ||
|
756499bbb0 | ||
|
2d23f82e55 | ||
|
d402f519c6 | ||
|
1dac6867d9 | ||
|
f7b16ac973 | ||
|
ea301da3a3 | ||
|
56edf23a1f | ||
|
97c5c79772 | ||
|
ef0b78ce30 | ||
|
fde37f08c9 | ||
|
27efad9fc2 | ||
|
61df5319e5 | ||
|
32d6532308 | ||
|
410ca25d01 | ||
|
f1dbc8a8eb | ||
|
f92d3a1ca5 | ||
|
7bce5cf45a | ||
|
c8668bcfe2 | ||
|
66902996dc |
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -16,8 +16,8 @@ HOW TO WRITE A GOOD PULL REQUEST?
|
||||
-->
|
||||
|
||||
### What does this PR do?
|
||||
<!-- A brief description of each change being made with this pull request. -->
|
||||
|
||||
<!-- A brief description of each change being made with this pull request. -->
|
||||
|
||||
### Closes Issue(s)
|
||||
<!-- List here all the issues closed by this pull request. Use keyword `closes` before each issue number
|
||||
@ -25,18 +25,12 @@ Closes #123456
|
||||
-->
|
||||
Closes #
|
||||
|
||||
|
||||
### Motivation
|
||||
|
||||
<!-- What inspired you to submit this pull request? -->
|
||||
|
||||
|
||||
### How to test
|
||||
<!-- List here everything that is necessary for the reviewer to be able to test it completely (docs link, step-by-step, bug cases)
|
||||
- Is there any specific setup needed, different than the default?
|
||||
- The linked issue contains all necessary content?
|
||||
- Have you found any different case that might be tested when you were fixing/implementing it?
|
||||
-->
|
||||
|
||||
|
||||
### More
|
||||
|
||||
<!-- Anything else we should know when reviewing? -->
|
||||
- [ ] Added/updated documentation
|
||||
|
7
.github/actions/merge-branches/action.yml
vendored
7
.github/actions/merge-branches/action.yml
vendored
@ -1,9 +1,12 @@
|
||||
name: Merge branches
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Checkout ${{ github.event.pull_request.base.ref || 'master' }}
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref || '' }}
|
||||
fetch-depth: 0 # Fetch all history
|
||||
|
36
.github/actions/upload-blob-report/action.yml
vendored
36
.github/actions/upload-blob-report/action.yml
vendored
@ -1,36 +0,0 @@
|
||||
name: Upload blob report
|
||||
description: Merge and upload the blob report to GitHub Actions Artifacts
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
run: npm ci
|
||||
- name: Merge artifacts
|
||||
uses: actions/upload-artifact/merge@v4
|
||||
with:
|
||||
name: all-blob-reports
|
||||
pattern: blob-report-*
|
||||
delete-merged: true
|
||||
- name: Download all blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: all-blob-reports
|
||||
path: bigbluebutton-tests/playwright/all-blob-reports
|
||||
- name: Merge into HTML Report
|
||||
shell: bash
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
run: npx playwright merge-reports --reporter html ./all-blob-reports
|
||||
- name: Upload HTML tests report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-report
|
||||
overwrite: true
|
||||
path: |
|
||||
bigbluebutton-tests/playwright/playwright-report
|
||||
bigbluebutton-tests/playwright/test-results
|
7
.github/config.yml
vendored
7
.github/config.yml
vendored
@ -1,7 +0,0 @@
|
||||
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
|
||||
|
||||
# Comment to be posted to on PRs from first time contributors in your repository
|
||||
newPRWelcomeComment: >
|
||||
Thank you for this contribution!
|
||||
Could you please confirm if you already sent in the signed Contributor License Agreement? See https://docs.bigbluebutton.org/support/faq.html#why-do-i-need-to-sign-a-contributor-license-agreement-to-contribute-source-code
|
||||
Thanks in advance!
|
106
.github/dependabot.yml
vendored
106
.github/dependabot.yml
vendored
@ -1,106 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
# maintaining legacy branch
|
||||
# no configuration for now
|
||||
|
||||
# current branch
|
||||
## excluding bigbluebutton-tests/playwright, bigbluebutton-tests/puppeteer, docs
|
||||
- package-ecosystem: npm
|
||||
directory: "/bbb-export-annotations"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
- package-ecosystem: npm
|
||||
directory: "/bigbluebutton-html5"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
- package-ecosystem: npm
|
||||
directory: "/bbb-learning-dashboard"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
- package-ecosystem: gradle
|
||||
directory: "/bigbluebutton-web"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
- package-ecosystem: bundler
|
||||
directory: "/record-and-playback/core"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
vendor: true
|
||||
- package-ecosystem: maven
|
||||
directory: "/bbb-fsesl-client"
|
||||
target-branch: "v2.7.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0 # zero means only security pull requests and no (optional) version updates
|
||||
|
||||
|
||||
# upcoming release branch
|
||||
## excluding bigbluebutton-tests/playwright, bigbluebutton-tests/puppeteer, docs, bbb-graphql-client-test
|
||||
- package-ecosystem: npm
|
||||
directory: "/bbb-graphql-actions"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: npm
|
||||
directory: "/bbb-export-annotations"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: npm
|
||||
directory: "/bigbluebutton-html5"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: npm
|
||||
directory: "/bbb-learning-dashboard"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: gradle
|
||||
directory: "/bigbluebutton-web"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: bundler
|
||||
directory: "/record-and-playback/core"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: maven
|
||||
directory: "/bbb-fsesl-client"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: gomod
|
||||
directory: "/bbb-graphql-middleware"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
- package-ecosystem: gomod
|
||||
directory: "/bbb-graphql-server"
|
||||
target-branch: "v3.0.x-release"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 20 # both security and versions updates
|
||||
|
||||
# upstream (default) branch
|
||||
# no configuration for now
|
@ -1,123 +0,0 @@
|
||||
name: Automated tests - publish results
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- Automated tests
|
||||
types:
|
||||
- completed
|
||||
- requested
|
||||
env:
|
||||
isCompleted: ${{ github.event.workflow_run.status == 'completed' }}
|
||||
jobs:
|
||||
get-pr-data:
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: github-api-request
|
||||
cancel-in-progress: false
|
||||
if: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||
outputs:
|
||||
pr-number: ${{ steps.pr.outputs.result || steps.set-env.outputs.pr-number }}
|
||||
workflow-id: ${{ github.event.workflow_run.id || steps.set-env.outputs.workflow-id }}
|
||||
steps:
|
||||
- name: Find associated pull request
|
||||
if: ${{ !fromJson(env.isCompleted) }}
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const response = await github.rest.search.issuesAndPullRequests({
|
||||
q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',
|
||||
per_page: 1,
|
||||
})
|
||||
const items = response.data.items
|
||||
if (items.length < 1) {
|
||||
console.error('No PRs found')
|
||||
return
|
||||
}
|
||||
const pullRequestNumber = items[0].number
|
||||
return pullRequestNumber
|
||||
- name: Download PR artifact
|
||||
if: ${{ fromJson(env.isCompleted) }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
try {
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "pr-comment-data"
|
||||
})[0];
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
let fs = require('fs');
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr-comment-data.zip`, Buffer.from(download.data));
|
||||
} catch (error) {
|
||||
console.log('No artifact found');
|
||||
}
|
||||
- name: Unzip artifact
|
||||
if: ${{ fromJson(env.isCompleted) }}
|
||||
run: unzip pr-comment-data.zip
|
||||
- name: Set env variables from artifact
|
||||
if: ${{ fromJson(env.isCompleted) }}
|
||||
id: set-env
|
||||
run: |
|
||||
echo "pr-number=$(cat ./pr_number)" >> $GITHUB_OUTPUT
|
||||
echo "workflow-id=$(cat ./workflow_id)" >> $GITHUB_OUTPUT
|
||||
comment-pr:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
needs: get-pr-data
|
||||
steps:
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ needs.get-pr-data.outputs.pr-number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: Automated tests Summary
|
||||
- name: Remove previous comment
|
||||
if: steps.fc.outputs.comment-id != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: ${{ steps.fc.outputs.comment-id }}
|
||||
})
|
||||
- name: In progress tests comment
|
||||
if: ${{ !fromJson(env.isCompleted) }}
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
issue-number: ${{ needs.get-pr-data.outputs.pr-number }}
|
||||
body: |
|
||||
<h1>Automated tests Summary</h1>
|
||||
<h3><strong>:hourglass_flowing_sand:</strong> Tests are running...</h3>
|
||||
- name: Passing tests comment
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
issue-number: ${{ needs.get-pr-data.outputs.pr-number }}
|
||||
body: |
|
||||
<h1>Automated tests Summary</h1>
|
||||
<h3><strong>:white_check_mark:</strong> All the CI tests have passed!</h3>
|
||||
- name: Failing tests comment
|
||||
if: ${{ github.event.workflow_run.conclusion != 'success' && fromJson(env.isCompleted) }}
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
issue-number: ${{ needs.get-pr-data.outputs.pr-number }}
|
||||
body: |
|
||||
<h1> Automated tests Summary</h1>
|
||||
<h3><strong>:rotating_light:</strong> Test workflow has failed</h3>
|
||||
|
||||
___
|
||||
|
||||
[Click here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ needs.get-pr-data.outputs.workflow-id }}) to check the action test reports
|
188
.github/workflows/automated-tests.yml
vendored
188
.github/workflows/automated-tests.yml
vendored
@ -8,7 +8,6 @@ on:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "**/*.md"
|
||||
- "bigbluebutton-html5/public/locales/*.json"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
@ -33,7 +32,6 @@ jobs:
|
||||
bbb-export-annotations,
|
||||
bbb-learning-dashboard,
|
||||
bbb-playback-record,
|
||||
bbb-graphql-server,
|
||||
bbb-etherpad,
|
||||
bbb-web,
|
||||
bbb-fsesl-akka,
|
||||
@ -53,9 +51,6 @@ jobs:
|
||||
cache-files-list: bbb-learning-dashboard
|
||||
- package: bbb-playback-record
|
||||
build-list: bbb-playback bbb-playback-notes bbb-playback-podcast bbb-playback-presentation bbb-playback-screenshare bbb-playback-video bbb-record-core
|
||||
- package: bbb-graphql-server
|
||||
build-name: bbb-graphql-server
|
||||
build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions
|
||||
- package: bbb-etherpad
|
||||
cache-files-list: bbb-etherpad.placeholder.sh
|
||||
cache-urls-list: https://api.github.com/repos/mconf/ep_pad_ttl/commits https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits https://api.github.com/repos/mconf/ep_redis_publisher/commits https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits
|
||||
@ -64,7 +59,7 @@ jobs:
|
||||
- package: bbb-fsesl-akka
|
||||
cache-files-list: akka-bbb-fsesl bbb-common-message
|
||||
- package: bbb-html5
|
||||
build-list: bbb-html5
|
||||
build-list: bbb-html5-nodejs bbb-html5
|
||||
cache-files-list: bigbluebutton-html5
|
||||
- package: bbb-freeswitch
|
||||
build-list: bbb-freeswitch-core bbb-freeswitch-sounds
|
||||
@ -74,9 +69,9 @@ jobs:
|
||||
build-list: bbb-webrtc-sfu bbb-webrtc-recorder
|
||||
cache-files-list: bbb-webrtc-sfu.placeholder.sh bbb-webrtc-recorder.placeholder.sh
|
||||
- package: others
|
||||
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton bbb-livekit
|
||||
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Merge branches
|
||||
uses: ./.github/actions/merge-branches
|
||||
- name: Set cache-key vars
|
||||
@ -91,7 +86,7 @@ jobs:
|
||||
- name: Handle cache
|
||||
if: matrix.cache-files-list != ''
|
||||
id: cache-action
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: artifacts.tar
|
||||
key: ${{ runner.os }}-${{ matrix.package }}-${{ env.BIGBLUEBUTTON_RELEASE }}-commits-${{ env.CACHE_KEY_FILES }}-urls-${{ env.CACHE_KEY_URLS }}
|
||||
@ -103,87 +98,79 @@ jobs:
|
||||
echo "${{ matrix.build-list || matrix.package }}" | xargs -n 1 ./build/setup.sh
|
||||
tar cvf artifacts.tar artifacts/
|
||||
- name: Archive packages
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts_${{ matrix.package }}.tar
|
||||
path: artifacts.tar
|
||||
install-and-run-tests:
|
||||
needs: build-package
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
env:
|
||||
shard: ${{ matrix.shard }}/8
|
||||
MATRIX_SHARD_UNDERSCORED: ${{ matrix.shard }}_8
|
||||
shard: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Merge branches
|
||||
uses: ./.github/actions/merge-branches
|
||||
- run: ./build/get_external_dependencies.sh
|
||||
- name: Download artifacts_bbb-apps-akka
|
||||
uses: actions/download-artifact@v4
|
||||
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@v4
|
||||
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@v4
|
||||
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@v4
|
||||
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@v4
|
||||
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@v4
|
||||
with:
|
||||
name: artifacts_bbb-graphql-server.tar
|
||||
- run: tar xf artifacts.tar
|
||||
- name: Download artifacts_bbb-etherpad
|
||||
uses: actions/download-artifact@v4
|
||||
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@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts_bbb-freeswitch.tar
|
||||
- run: tar xf artifacts.tar
|
||||
- name: Download artifacts_bbb-webrtc
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts_bbb-webrtc.tar
|
||||
- run: tar xf artifacts.tar
|
||||
- name: Download artifacts_bbb-web
|
||||
uses: actions/download-artifact@v4
|
||||
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@v4
|
||||
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@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts_bbb-html5.tar
|
||||
- run: tar xf artifacts.tar
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts_others.tar
|
||||
- run: tar xf artifacts.tar
|
||||
@ -257,78 +244,15 @@ jobs:
|
||||
apt --purge -y remove apache2-bin
|
||||
'
|
||||
- name: Install BBB
|
||||
env:
|
||||
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
|
||||
ACTIONS_RUNNER_DEBUG: true
|
||||
run: |
|
||||
sudo -i <<'EOF'
|
||||
set -e
|
||||
cd /root/
|
||||
wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release--no-mongo/bbb-install.sh -O bbb-install.sh
|
||||
sed -i "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" bbb-install.sh
|
||||
chmod +x bbb-install.sh
|
||||
|
||||
COMMAND="./bbb-install.sh -v jammy-30-dev -s bbb-ci.test -j -d /certs/"
|
||||
TIMEOUT=1500 # 25 minutes
|
||||
MAX_RETRIES=3
|
||||
RETRY_INTERVAL=60
|
||||
RETRY_COUNT=0
|
||||
SUCCESS=0
|
||||
|
||||
while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do
|
||||
echo "Attempt $((RETRY_COUNT + 1)) of $MAX_RETRIES to install BBB..."
|
||||
|
||||
# Run the command with timeout and handle its exit code
|
||||
# Capture both stdout and stderr
|
||||
COMMAND_EXIT_CODE=0
|
||||
timeout $TIMEOUT $COMMAND || COMMAND_EXIT_CODE=$?
|
||||
|
||||
if [[ $COMMAND_EXIT_CODE -eq 0 ]]; then
|
||||
SUCCESS=1
|
||||
break
|
||||
elif [[ $COMMAND_EXIT_CODE -eq 124 ]]; then
|
||||
echo "Installation timed out after ${TIMEOUT} seconds. Retrying..."
|
||||
else
|
||||
echo "Installation failed with exit code $COMMAND_EXIT_CODE"
|
||||
echo "Retrying installation within $RETRY_INTERVAL seconds..."
|
||||
sleep $RETRY_INTERVAL
|
||||
fi
|
||||
echo "Stop any ongoing processes related to apt-get or dpkg that might be stuck"
|
||||
# Use -q to suppress "no process found" messages
|
||||
killall -q apt-get || true
|
||||
killall -q dpkg || true
|
||||
|
||||
echo "Remove the lock files that may have been left behind"
|
||||
# Group lock file removal for better readability
|
||||
rm -f /var/lib/dpkg/lock-frontend
|
||||
rm -f /var/lib/dpkg/lock
|
||||
rm -f /var/cache/apt/archives/lock
|
||||
|
||||
echo "Reconfigure the package manager"
|
||||
dpkg --configure -a
|
||||
|
||||
echo "Clean up any partially installed packages"
|
||||
apt-get clean
|
||||
apt-get autoremove
|
||||
|
||||
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||
done
|
||||
|
||||
if [[ $SUCCESS -eq 0 ]]; then
|
||||
echo "All attempts to install BBB failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bbb-conf --salt bbbci
|
||||
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
|
||||
sudo yq e -i '.log_level = "TRACE"' /usr/share/bbb-graphql-middleware/config.yml
|
||||
bbb-conf --restart
|
||||
EOF
|
||||
- name: List systemctl services
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
sudo -i <<EOF
|
||||
systemctl --type=service --state=running,exited,failed --all --no-pager --no-legend
|
||||
set -e
|
||||
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.7.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 focal-27-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
|
||||
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
|
||||
bbb-conf --restart
|
||||
EOF
|
||||
- name: Install test dependencies
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
@ -339,18 +263,13 @@ jobs:
|
||||
npx playwright install
|
||||
'
|
||||
- name: Run tests
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 25
|
||||
max_attempts: 2
|
||||
command: |
|
||||
cd ./bigbluebutton-tests/playwright
|
||||
npm run test-chromium-ci -- --shard ${{ env.shard }}
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
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
|
||||
run: npm run test-chromium-ci -- --shard ${{ matrix.shard }}
|
||||
- name: Run Firefox tests
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
if: |
|
||||
@ -365,13 +284,13 @@ jobs:
|
||||
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 -- --shard ${{ env.shard }}
|
||||
npm run test-firefox-ci -- --shard ${{ matrix.shard }}
|
||||
'
|
||||
- if: always()
|
||||
- if: always() && github.event_name == 'pull_request'
|
||||
name: Upload blob report to GitHub Actions Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: blob-report-${{ matrix.shard }}
|
||||
name: all-blob-reports
|
||||
path: bigbluebutton-tests/playwright/blob-report
|
||||
- if: failure()
|
||||
name: Prepare artifacts (configs and logs)
|
||||
@ -399,31 +318,42 @@ jobs:
|
||||
ls -t /root/*.tar.gz | head -1 | xargs -I '{}' cp '{}' /home/runner/work/bigbluebutton/bigbluebutton/bbb-logs.tar.gz
|
||||
EOF
|
||||
- if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bbb-configs-${{ env.MATRIX_SHARD_UNDERSCORED }}
|
||||
name: bbb-configs
|
||||
path: configs
|
||||
- if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bbb-logs-${{ env.MATRIX_SHARD_UNDERSCORED }}
|
||||
name: bbb-logs
|
||||
path: ./bbb-logs.tar.gz
|
||||
upload-report:
|
||||
if: always() && !contains(github.event.head_commit.message, 'Merge pull request')
|
||||
if: always()
|
||||
needs: install-and-run-tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
hasReportData: ${{ needs.install-and-run-tests.result == 'success' || needs.install-and-run-tests.result == 'failure' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Upload blob report
|
||||
if: ${{ env.hasReportData }}
|
||||
uses: ./.github/actions/upload-blob-report
|
||||
- name: Remove unnecessary artifact
|
||||
uses: geekyeggo/delete-artifact@v5
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
run: npm ci
|
||||
- name: Download all blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: all-blob-reports
|
||||
failOnError: false
|
||||
path: bigbluebutton-tests/playwright/all-blob-reports
|
||||
- name: Merge into HTML Report
|
||||
working-directory: ./bigbluebutton-tests/playwright
|
||||
run: npx playwright merge-reports --reporter html ./all-blob-reports
|
||||
- name: Upload HTML tests report
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: tests-report
|
||||
path: |
|
||||
bigbluebutton-tests/playwright/playwright-report
|
||||
bigbluebutton-tests/playwright/test-results
|
||||
- name: Write PR data for auto-comment
|
||||
if: github.event_name == 'pull_request'
|
||||
working-directory: ./
|
||||
@ -433,7 +363,7 @@ jobs:
|
||||
echo ${{ github.run_id }} > ./pr-comment-data/workflow_id
|
||||
- name: Upload PR data for auto-comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pr-comment-data
|
||||
path: pr-comment-data
|
||||
|
13
.github/workflows/check-merge-conflict.yml
vendored
13
.github/workflows/check-merge-conflict.yml
vendored
@ -12,18 +12,15 @@ permissions:
|
||||
jobs:
|
||||
main:
|
||||
permissions:
|
||||
pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs
|
||||
pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: github-api-request
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: Check for dirty pull requests
|
||||
uses: eps1lon/actions-label-merge-conflict@v3
|
||||
uses: eps1lon/actions-label-merge-conflict@releases/2.x
|
||||
with:
|
||||
dirtyLabel: "status: conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: |
|
||||
This pull request has conflicts ☹
|
||||
Please resolve those so we can review the pull request.
|
||||
Thanks.
|
||||
This pull request has conflicts ☹
|
||||
Please resolve those so we can review the pull request.
|
||||
Thanks.
|
||||
|
15
.github/workflows/deploy-docs.yml
vendored
15
.github/workflows/deploy-docs.yml
vendored
@ -7,6 +7,7 @@ on:
|
||||
- 'develop'
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/**'
|
||||
|
||||
# Do not build the docs concurrently
|
||||
concurrency:
|
||||
@ -22,20 +23,20 @@ jobs:
|
||||
working-directory: ./docs
|
||||
steps:
|
||||
# Setup
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: ./docs/package-lock.json
|
||||
node-version: 18
|
||||
cache: yarn
|
||||
cache-dependency-path: ./docs/yarn.lock
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
# Build static docs
|
||||
- name: Build all versions
|
||||
run: ./build.sh
|
||||
- name: Build website
|
||||
run: npm run docusaurus build
|
||||
run: yarn build
|
||||
- name: upload build artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
|
36
.github/workflows/ts-code-compilation.yml
vendored
36
.github/workflows/ts-code-compilation.yml
vendored
@ -1,36 +0,0 @@
|
||||
name: Typescript - compile code
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
- "v2.[5-9].x-release"
|
||||
- "v[3-9].*.x-release"
|
||||
paths:
|
||||
- "bigbluebutton-html5/**.ts"
|
||||
- "bigbluebutton-html5/**.tsx"
|
||||
- "bigbluebutton-html5/package.json"
|
||||
- "bigbluebutton-html5/package-lock.json"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "bigbluebutton-html5/**.ts"
|
||||
- "bigbluebutton-html5/**.tsx"
|
||||
- "bigbluebutton-html5/package.json"
|
||||
- "bigbluebutton-html5/package-lock.json"
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
ts-code-compilation:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Merge branches
|
||||
uses: ./.github/actions/merge-branches
|
||||
- name: run npm ci
|
||||
working-directory: bigbluebutton-html5
|
||||
run: npm ci
|
||||
- name: typescript code compilation
|
||||
working-directory: bigbluebutton-html5
|
||||
run: npx tsc
|
36
.github/workflows/ts-code-validation.yml
vendored
36
.github/workflows/ts-code-validation.yml
vendored
@ -1,36 +0,0 @@
|
||||
name: Typescript - validate code (eslint)
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
- "v2.[5-9].x-release"
|
||||
- "v[3-9].*.x-release"
|
||||
paths:
|
||||
- "bigbluebutton-html5/**.ts"
|
||||
- "bigbluebutton-html5/**.tsx"
|
||||
- "bigbluebutton-html5/package.json"
|
||||
- "bigbluebutton-html5/package-lock.json"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "bigbluebutton-html5/**.ts"
|
||||
- "bigbluebutton-html5/**.tsx"
|
||||
- "bigbluebutton-html5/package.json"
|
||||
- "bigbluebutton-html5/package-lock.json"
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
ts-code-validation:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Merge branches
|
||||
uses: ./.github/actions/merge-branches
|
||||
- name: install npm dependencies
|
||||
working-directory: bigbluebutton-html5
|
||||
run: npm ci
|
||||
- name: typescript code validation with eslint
|
||||
working-directory: bigbluebutton-html5
|
||||
run: npx eslint . --ext .ts,.tsx
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,6 +23,3 @@ cache/*
|
||||
artifacts/*
|
||||
bbb-presentation-video.zip
|
||||
bbb-presentation-video
|
||||
bbb-graphql-actions-adapter-server/
|
||||
bigbluebutton-html5/public/locales/index.json
|
||||
node_modules
|
||||
|
@ -12,7 +12,7 @@ stages:
|
||||
|
||||
# define which docker image to use for builds
|
||||
default:
|
||||
image: bigbluebutton/bbb-build:v3.0.x-release--2024-08-30-014114
|
||||
image: bigbluebutton/bbb-build:bbb27-2023-06-13-java17
|
||||
|
||||
# This stage uses git to find out since when each package has been unmodified.
|
||||
# it then checks an API endpoint on the package server to find out for which of
|
||||
@ -102,16 +102,6 @@ bbb-html5-build:
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-html5
|
||||
|
||||
bbb-graphql-middleware-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-graphql-middleware
|
||||
|
||||
bbb-graphql-server-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-graphql-server
|
||||
|
||||
bbb-learning-dashboard-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
@ -122,6 +112,11 @@ bbb-libreoffice-docker-build:
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-libreoffice-docker
|
||||
|
||||
bbb-lti-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-lti
|
||||
|
||||
bbb-mkclean-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
@ -182,11 +177,6 @@ bbb-webrtc-recorder-build:
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-webrtc-recorder
|
||||
|
||||
bbb-livekit:
|
||||
extends: .build_job
|
||||
script:
|
||||
- build/setup-inside-docker.sh bbb-livekit
|
||||
|
||||
bbb-transcription-controller-build:
|
||||
extends: .build_job
|
||||
script:
|
||||
|
12
README.md
12
README.md
@ -1,20 +1,20 @@
|
||||
BigBlueButton
|
||||
=============
|
||||
BigBlueButton is an open-source virtual classroom designed to help teachers teach and learners learn.
|
||||
BigBlueButton is an open source web conferencing system.
|
||||
|
||||
BigBlueButton supports real-time sharing of audio, video, slides (with whiteboard annotations), chat, and the screen. Instructors can engage remote students with polling, emojis, multi-user whiteboards, shared notes, and breakout rooms. During the session, BigBlueButton generates analytics that are visible to moderators in the Learning Analytics Dashboard.
|
||||
BigBlueButton supports real-time sharing of audio, video, slides (with whiteboard controls), chat, and the screen. Instructors can engage remote students with polling, emojis, multi-user whiteboard, and breakout rooms.
|
||||
|
||||
Presenters can record and playback content for later sharing with others.
|
||||
|
||||
We designed BigBlueButton for online learning, it can be used for many other applications as well. The educational use cases for BigBlueButton are
|
||||
We designed BigBlueButton for online learning, (though it can be used for many [other applications](https://www.c4isrnet.com/it-networks/2015/02/11/disa-to-replace-dco-with-new-collaboration-services-tool/) as well). The educational use cases for BigBlueButton are
|
||||
|
||||
* Online tutoring (one-to-one)
|
||||
* Flipped classrooms (recording content ahead of your session)
|
||||
* Group collaboration (many-to-many)
|
||||
* Online classes (one-to-many)
|
||||
|
||||
The latest version is BigBlueButton 2.7. You can install BigBlueButton 2.7 on Ubuntu 20.04 using [bbb-install.sh](https://github.com/bigbluebutton/bbb-install) within 30 minutes (or your money back 😉).
|
||||
You can install on a Ubuntu 20.04 64-bit server. We provide [bbb-install.sh](https://github.com/bigbluebutton/bbb-install) to let you have a server up and running within 30 minutes (or your money back 😉).
|
||||
|
||||
For full technical documentation of BigBlueButton -- including architecture, features, API, and GreenLight (the default front-end) -- see [https://docs.bigbluebutton.org/](https://docs.bigbluebutton.org/).
|
||||
For full technical documentation BigBlueButton -- including architecture, features, API, and GreenLight (the default front-end) -- see [https://docs.bigbluebutton.org/](https://docs.bigbluebutton.org/).
|
||||
|
||||
BigBlueButton and the BigBlueButton Logo are trademarks of [BigBlueButton Inc](https://bigbluebutton.org).
|
||||
BigBlueButton and the BigBlueButton Logo are trademarks of [BigBlueButton Inc](https://bigbluebutton.org) .
|
||||
|
@ -9,7 +9,7 @@ We actively support BigBlueButton through the community forums and through secur
|
||||
| 2.5.x (or earlier) | :x: |
|
||||
| 2.6.x | :white_check_mark: |
|
||||
| 2.7.x | :white_check_mark: |
|
||||
| 3.0.x | :x: |
|
||||
| 2.8.x | :x: |
|
||||
|
||||
We have released 2.7 to the community and are going to support both 2.6 and 2.7 together for the coming months (while we're actively developing the next release). Also, BigBlueButton 2.5 is now end of life.
|
||||
|
||||
@ -17,7 +17,7 @@ As such, we recommend that all administrators deploy 2.7 going forward. You'll
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you have found a security vulnerability in BigBlueButton please let us know directly by
|
||||
If you believe you have found a security vunerability in BigBlueButton please let us know directly by
|
||||
- using GitHub's "Report a vulnerability" functionality on https://github.com/bigbluebutton/bigbluebutton/security/advisories
|
||||
- or e-mailing security@bigbluebutton.org with as much detail as possible.
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bigbluebutton.build
|
||||
|
||||
import sbt._
|
||||
import Keys._
|
||||
|
||||
object Dependencies {
|
||||
|
||||
@ -12,8 +13,8 @@ object Dependencies {
|
||||
val scalactic = "3.0.8"
|
||||
|
||||
// Libraries
|
||||
val pekkoVersion = "1.0.1"
|
||||
val pekkoHttpVersion = "1.0.0"
|
||||
val akkaVersion = "2.6.17"
|
||||
val akkaHttpVersion = "10.2.7"
|
||||
val gson = "2.8.9"
|
||||
val jackson = "2.13.5"
|
||||
val logback = "1.2.13"
|
||||
@ -27,24 +28,18 @@ object Dependencies {
|
||||
// BigBlueButton
|
||||
val bbbCommons = "0.0.22-SNAPSHOT"
|
||||
|
||||
// Database
|
||||
val slick = "3.4.1"
|
||||
val postgresql = "42.5.0"
|
||||
val slickPg = "0.21.1"
|
||||
|
||||
// Test
|
||||
val scalaTest = "3.2.11"
|
||||
val mockito = "2.23.0"
|
||||
val akkaTestKit = "2.6.0"
|
||||
val jacksonDataFormat = "2.13.5"
|
||||
}
|
||||
|
||||
object Compile {
|
||||
val scalaLibrary = "org.scala-lang" % "scala-library" % Versions.scala
|
||||
val scalaCompiler = "org.scala-lang" % "scala-compiler" % Versions.scala
|
||||
|
||||
val pekkoActor = "org.apache.pekko" %% "pekko-actor" % Versions.pekkoVersion
|
||||
val pekkoSlf4j = "org.apache.pekko" %% "pekko-slf4j" % Versions.pekkoVersion
|
||||
val akkaActor = "com.typesafe.akka" % "akka-actor_2.13" % Versions.akkaVersion
|
||||
val akkaSl4fj = "com.typesafe.akka" % "akka-slf4j_2.13" % Versions.akkaVersion
|
||||
|
||||
val googleGson = "com.google.code.gson" % "gson" % Versions.gson
|
||||
val jacksonModule = "com.fasterxml.jackson.module" %% "jackson-module-scala" % Versions.jackson
|
||||
@ -53,21 +48,13 @@ object Dependencies {
|
||||
val commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
|
||||
val sprayJson = "io.spray" % "spray-json_2.13" % Versions.spray
|
||||
|
||||
val pekkoStream = "org.apache.pekko" %% "pekko-stream" % Versions.pekkoVersion
|
||||
val pekkoHttp = "org.apache.pekko" %% "pekko-http" % Versions.pekkoHttpVersion
|
||||
val pekkoHttpSprayJson = "org.apache.pekko" %% "pekko-http-spray-json" % Versions.pekkoHttpVersion
|
||||
val akkaStream = "com.typesafe.akka" %% "akka-stream" % Versions.akkaVersion
|
||||
val akkaHttp = "com.typesafe.akka" %% "akka-http" % Versions.akkaHttpVersion
|
||||
val akkaHttpSprayJson = "com.typesafe.akka" %% "akka-http-spray-json" % Versions.akkaHttpVersion
|
||||
|
||||
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
|
||||
|
||||
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons
|
||||
|
||||
val slick = "com.typesafe.slick" %% "slick" % Versions.slick
|
||||
val slickHikaricp = "com.typesafe.slick" %% "slick-hikaricp" % Versions.slick
|
||||
val slickPg = "com.github.tminglei" %% "slick-pg" % Versions.slickPg
|
||||
val slickPgSprayJson = "com.github.tminglei" %% "slick-pg_spray-json" % Versions.slickPg
|
||||
|
||||
val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql
|
||||
val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat
|
||||
}
|
||||
|
||||
object Test {
|
||||
@ -88,9 +75,9 @@ object Dependencies {
|
||||
val runtime = Seq(
|
||||
Compile.scalaLibrary,
|
||||
Compile.scalaCompiler,
|
||||
Compile.pekkoActor,
|
||||
Compile.pekkoSlf4j,
|
||||
Compile.pekkoStream,
|
||||
Compile.akkaActor,
|
||||
Compile.akkaSl4fj,
|
||||
Compile.akkaStream,
|
||||
Compile.googleGson,
|
||||
Compile.jacksonModule,
|
||||
Compile.quicklens,
|
||||
@ -98,13 +85,7 @@ object Dependencies {
|
||||
Compile.commonsCodec,
|
||||
Compile.sprayJson,
|
||||
Compile.apacheLang,
|
||||
Compile.pekkoHttp,
|
||||
Compile.pekkoHttpSprayJson,
|
||||
Compile.bbbCommons,
|
||||
Compile.slick,
|
||||
Compile.slickHikaricp,
|
||||
Compile.slickPg,
|
||||
Compile.slickPgSprayJson,
|
||||
Compile.postgresql,
|
||||
Compile.jacksonDataFormat) ++ testing
|
||||
Compile.akkaHttp,
|
||||
Compile.akkaHttpSprayJson,
|
||||
Compile.bbbCommons) ++ testing
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import org.apache.pekko.http.scaladsl.model._
|
||||
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import org.apache.pekko.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.{ ApiResponseFailure, ApiResponseSuccess, UserInfosApiMsg }
|
||||
import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus, UserInfoService }
|
||||
import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus }
|
||||
import spray.json._
|
||||
|
||||
import scala.concurrent._
|
||||
import ExecutionContext.Implicits.global
|
||||
|
||||
@ -65,7 +63,7 @@ trait JsonSupportProtocolMeetingInfoResponse extends SprayJsonSupport with Defau
|
||||
implicit val meetingInfoResponseJsonFormat = jsonFormat1(MeetingInfoResponse)
|
||||
}
|
||||
|
||||
class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService, userInfoService: UserInfoService)
|
||||
class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService)
|
||||
extends JsonSupportProtocolHealthResponse
|
||||
with JsonSupportProtocolMeetingInfoResponse {
|
||||
|
||||
@ -126,28 +124,5 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService, user
|
||||
}
|
||||
complete(entityFuture)
|
||||
}
|
||||
} ~
|
||||
path("userInfo") {
|
||||
(headerValueByName("x-session-token") & headerValueByName("user-agent")) { (sessionToken, userAgent) =>
|
||||
get {
|
||||
val entityFuture = userInfoService.getUserInfo(sessionToken).map {
|
||||
case ApiResponseSuccess(msg, userInfos: UserInfosApiMsg) =>
|
||||
val responseMap = userInfoService.generateResponseMap(userInfos)
|
||||
userInfoService.createHttpResponse(StatusCodes.OK, responseMap)
|
||||
|
||||
case ApiResponseFailure(msg, msgId, arg) =>
|
||||
userInfoService.createHttpResponse(
|
||||
StatusCodes.OK,
|
||||
Map(
|
||||
"response" -> "unauthorized",
|
||||
"message" -> msg,
|
||||
"message_id" -> msgId,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
complete(entityFuture)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import org.apache.pekko.actor.ActorSystem
|
||||
import org.apache.pekko.event.Logging
|
||||
import org.apache.pekko.http.scaladsl.Http
|
||||
import org.apache.pekko.stream.ActorMaterializer
|
||||
import org.bigbluebutton.common2.redis.{MessageSender, RedisConfig, RedisPublisher}
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.Logging
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import org.bigbluebutton.common2.redis.{ MessageSender, RedisConfig, RedisPublisher }
|
||||
import org.bigbluebutton.core._
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
||||
import org.bigbluebutton.core2.AnalyticsActor
|
||||
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
|
||||
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlConnectionsActor, LearningDashboardActor, RedisRecorderActor}
|
||||
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
|
||||
import org.bigbluebutton.endpoint.redis.{ RedisRecorderActor, ExportAnnotationsActor }
|
||||
import org.bigbluebutton.endpoint.redis.LearningDashboardActor
|
||||
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
|
||||
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService, UserInfoService}
|
||||
import org.bigbluebutton.service.{ HealthzService, MeetingInfoActor, MeetingInfoService }
|
||||
|
||||
object Boot extends App with SystemConfiguration {
|
||||
|
||||
@ -50,6 +52,8 @@ object Boot extends App with SystemConfiguration {
|
||||
|
||||
val meetingInfoService = MeetingInfoService(system, meetingInfoActorRef)
|
||||
|
||||
val apiService = new ApiService(healthzService, meetingInfoService)
|
||||
|
||||
val redisRecorderActor = system.actorOf(
|
||||
RedisRecorderActor.props(system, redisConfig, healthzService),
|
||||
"redisRecorderActor"
|
||||
@ -65,12 +69,6 @@ object Boot extends App with SystemConfiguration {
|
||||
"LearningDashboardActor"
|
||||
)
|
||||
|
||||
val graphqlConnectionsActor = system.actorOf(
|
||||
GraphqlConnectionsActor.props(system, eventBus, outGW),
|
||||
"GraphqlConnectionsActor"
|
||||
)
|
||||
|
||||
ClientSettings.loadClientSettingsFromFile()
|
||||
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
|
||||
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
||||
|
||||
@ -87,15 +85,9 @@ object Boot extends App with SystemConfiguration {
|
||||
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
|
||||
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
|
||||
|
||||
eventBus.subscribe(graphqlConnectionsActor, meetingManagerChannel)
|
||||
bbbMsgBus.subscribe(graphqlConnectionsActor, analyticsChannel)
|
||||
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
|
||||
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
||||
|
||||
val userInfoService = UserInfoService(system, bbbActor)
|
||||
val apiService = new ApiService(healthzService, meetingInfoService, userInfoService)
|
||||
|
||||
val redisMessageHandlerActor = system.actorOf(ReceivedJsonMsgHandlerActor.props(bbbMsgBus, incomingJsonMessageBus))
|
||||
incomingJsonMessageBus.subscribe(redisMessageHandlerActor, toAkkaAppsJsonChannel)
|
||||
|
||||
|
@ -1,190 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import org.bigbluebutton.common2.util.YamlUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.io.{ ByteArrayInputStream, File }
|
||||
import scala.io.BufferedSource
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
object ClientSettings extends SystemConfiguration {
|
||||
var clientSettingsFromFile: Map[String, Object] = Map("" -> "")
|
||||
val logger = LoggerFactory.getLogger(this.getClass)
|
||||
|
||||
def loadClientSettingsFromFile() = {
|
||||
val clientSettingsFile = scala.io.Source.fromFile(clientSettingsPath, "UTF-8")
|
||||
|
||||
val clientSettingsFileOverrideToCheck = new File(clientSettingsPathOverride)
|
||||
|
||||
val clientSettingsFileOverride = if (clientSettingsFileOverrideToCheck.exists())
|
||||
scala.io.Source.fromFile(
|
||||
clientSettingsPathOverride,
|
||||
"UTF-8"
|
||||
)
|
||||
else new BufferedSource(new ByteArrayInputStream(Array[Byte]()))
|
||||
|
||||
clientSettingsFromFile =
|
||||
common2.util.YamlUtil.mergeImmutableMaps(
|
||||
common2.util.YamlUtil.toMap[Object](clientSettingsFile.mkString) match {
|
||||
case Success(value) => value
|
||||
case Failure(exception) =>
|
||||
println("Error while fetching client Settings: ", exception)
|
||||
Map[String, Object]()
|
||||
},
|
||||
common2.util.YamlUtil.toMap[Object](clientSettingsFileOverride.mkString) match {
|
||||
case Success(value) => value
|
||||
case Failure(exception) =>
|
||||
println("Error while fetching client override Settings: ", exception)
|
||||
Map[String, Object]()
|
||||
}
|
||||
)
|
||||
|
||||
//Remove `:private` once it's used only by HTML5 client's internal configs
|
||||
clientSettingsFromFile -= "private"
|
||||
}
|
||||
|
||||
def getClientSettingsWithOverride(clientSettingsOverrideJson: String): Map[String, Object] = {
|
||||
if (clientSettingsOverrideJson.nonEmpty) {
|
||||
val scalaMapClientOverride = common2.util.JsonUtil.toMap[Object](clientSettingsOverrideJson)
|
||||
scalaMapClientOverride match {
|
||||
case Success(clientSettingsOverrideAsMap) => YamlUtil.mergeImmutableMaps(clientSettingsFromFile, clientSettingsOverrideAsMap)
|
||||
case Failure(msg) =>
|
||||
logger.debug("No valid JSON override of client configuration in create call: {}", msg)
|
||||
clientSettingsFromFile
|
||||
}
|
||||
} else clientSettingsFromFile
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPathAsIntOrElse(map: Map[String, Any], path: String, alternativeValue: Int): Int = {
|
||||
getConfigPropertyValueByPath(map, path) match {
|
||||
case Some(configValue: Int) => configValue
|
||||
case _ =>
|
||||
logger.debug(s"Config `$path` with type Integer not found in clientSettings.")
|
||||
alternativeValue
|
||||
}
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPathAsStringOrElse(map: Map[String, Any], path: String, alternativeValue: String): String = {
|
||||
getConfigPropertyValueByPath(map, path) match {
|
||||
case Some(configValue: String) => configValue
|
||||
case _ =>
|
||||
logger.debug(s"Config `$path` with type String not found in clientSettings.")
|
||||
alternativeValue
|
||||
}
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPathAsBooleanOrElse(map: Map[String, Any], path: String, alternativeValue: Boolean): Boolean = {
|
||||
getConfigPropertyValueByPath(map, path) match {
|
||||
case Some(configValue: Boolean) => configValue
|
||||
case _ =>
|
||||
logger.debug(s"Config `$path` with type Boolean found in clientSettings.")
|
||||
alternativeValue
|
||||
}
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPathAsListOfStringOrElse(map: Map[String, Any], path: String, alternativeValue: List[String]): List[String] = {
|
||||
getConfigPropertyValueByPath(map, path) match {
|
||||
case Some(configValue: List[String]) => configValue
|
||||
case _ =>
|
||||
logger.debug(s"Config `$path` with type List[String] not found in clientSettings.")
|
||||
alternativeValue
|
||||
}
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPathAsListOfIntOrElse(map: Map[String, Any], path: String, alternativeValue: List[Int]): List[Int] = {
|
||||
getConfigPropertyValueByPath(map, path) match {
|
||||
case Some(configValue: List[Int]) => configValue
|
||||
case _ =>
|
||||
logger.debug(s"Config `$path` with type List[Int] not found in clientSettings.")
|
||||
alternativeValue
|
||||
}
|
||||
}
|
||||
|
||||
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
|
||||
val keys = path.split("\\.")
|
||||
|
||||
def getRecursive(map: Map[String, Any], keys: Seq[String]): Option[Any] = {
|
||||
keys match {
|
||||
case Seq(head, tail @ _*) =>
|
||||
map.get(head) match {
|
||||
case Some(innerMap: Map[String, Any]) => getRecursive(innerMap, tail)
|
||||
case otherValue if tail.isEmpty => otherValue
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
getRecursive(map, keys)
|
||||
}
|
||||
|
||||
def getPluginsFromConfig(config: Map[String, Any]): Map[String, Plugin] = {
|
||||
var pluginsFromConfig: Map[String, Plugin] = Map()
|
||||
|
||||
val pluginsConfig = getConfigPropertyValueByPath(config, "public.plugins")
|
||||
pluginsConfig match {
|
||||
case Some(plugins: List[Map[String, Any]]) =>
|
||||
for {
|
||||
plugin <- plugins
|
||||
} yield {
|
||||
if (plugin.contains("name") && plugin.contains("url")) {
|
||||
|
||||
val pluginName = plugin("name").toString
|
||||
val pluginUrl = plugin("url").toString
|
||||
var pluginDataChannels: Map[String, DataChannel] = Map()
|
||||
if (plugin.contains("dataChannels")) {
|
||||
plugin("dataChannels") match {
|
||||
case dataChannels: List[Map[String, Any]] =>
|
||||
for {
|
||||
dataChannel <- dataChannels
|
||||
} yield {
|
||||
if (dataChannel.contains("name")) {
|
||||
val channelName = dataChannel("name").toString
|
||||
val pushPermission = {
|
||||
if (dataChannel.contains("pushPermission")) {
|
||||
dataChannel("pushPermission") match {
|
||||
case wPerm: List[String] => wPerm
|
||||
case _ => {
|
||||
logger.warn(s"Invalid pushPermission for channel $channelName in plugin $pluginName")
|
||||
List()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn(s"Missing config pushPermission for channel $channelName in plugin $pluginName")
|
||||
List()
|
||||
}
|
||||
}
|
||||
val replaceOrDeletePermission = {
|
||||
if (dataChannel.contains("replaceOrDeletePermission")) {
|
||||
dataChannel("replaceOrDeletePermission") match {
|
||||
case dPerm: List[String] => dPerm
|
||||
case _ => {
|
||||
logger.warn(s"Invalid replaceOrDeletePermission for channel $channelName in plugin $pluginName")
|
||||
List()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List()
|
||||
}
|
||||
}
|
||||
|
||||
pluginDataChannels += (channelName -> DataChannel(channelName, pushPermission, replaceOrDeletePermission))
|
||||
}
|
||||
}
|
||||
case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format")
|
||||
}
|
||||
}
|
||||
|
||||
pluginsFromConfig += (pluginName -> Plugin(pluginName, pluginUrl, pluginDataChannels))
|
||||
}
|
||||
}
|
||||
case _ => logger.warn(s"Invalid plugins config found.")
|
||||
}
|
||||
|
||||
pluginsFromConfig
|
||||
}
|
||||
|
||||
case class DataChannel(name: String, pushPermission: List[String], replaceOrDeletePermission: List[String])
|
||||
case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel])
|
||||
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, Routing }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.{ MeetingStatus2x }
|
||||
@ -23,7 +21,7 @@ object LockSettingsUtil {
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter,
|
||||
vu: VoiceUserState, mute: Boolean
|
||||
)(implicit context: ActorContext): Unit = {
|
||||
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||
VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, mute)
|
||||
}
|
||||
|
||||
@ -31,7 +29,7 @@ object LockSettingsUtil {
|
||||
disableMic: Boolean,
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter
|
||||
)(implicit context: ActorContext): Unit = {
|
||||
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
|
||||
Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user =>
|
||||
if (user.role == Roles.VIEWER_ROLE && !vu.listenOnly && user.locked) {
|
||||
@ -50,7 +48,7 @@ object LockSettingsUtil {
|
||||
def enforceLockSettingsForAllVoiceUsers(
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter
|
||||
)(implicit context: ActorContext): Unit = {
|
||||
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
applyMutingOfUsers(permissions.disableMic, liveMeeting, outGW)
|
||||
}
|
||||
@ -59,7 +57,7 @@ object LockSettingsUtil {
|
||||
voiceUser: VoiceUserState,
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter
|
||||
)(implicit context: ActorContext): Unit = {
|
||||
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (permissions.disableMic) {
|
||||
@ -80,7 +78,7 @@ object LockSettingsUtil {
|
||||
intUserId: String,
|
||||
liveMeeting: LiveMeeting,
|
||||
outGW: OutMsgRouter
|
||||
)(implicit context: ActorContext): Unit = {
|
||||
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||
val voiceUser = VoiceUsers.findWithIntId(liveMeeting.voiceUsers, intUserId)
|
||||
voiceUser.foreach { vu =>
|
||||
// Make sure that listen only user is muted. (ralam dec 6, 2019
|
||||
|
@ -1,9 +1,6 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
import scala.util.Try
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
trait SystemConfiguration {
|
||||
@ -41,16 +38,12 @@ trait SystemConfiguration {
|
||||
|
||||
lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings")
|
||||
lazy val voiceConfRecordCodec = Try(config.getString("voiceConf.recordCodec")).getOrElse("wav")
|
||||
lazy val voiceConfRecordEnableFileSplitter = Try(config.getBoolean("voiceConf.recordEnableFileSplitter")).getOrElse(false)
|
||||
lazy val voiceConfRecordFileSplitterIntervalInMinutes = Try(config.getInt("voiceConf.recordFileSplitterIntervalInMinutes")).getOrElse(15)
|
||||
lazy val checkVoiceRecordingInterval = Try(config.getInt("voiceConf.checkRecordingInterval")).getOrElse(19)
|
||||
lazy val syncVoiceUsersStatusInterval = Try(config.getInt("voiceConf.syncUserStatusInterval")).getOrElse(43)
|
||||
lazy val ejectRogueVoiceUsers = Try(config.getBoolean("voiceConf.ejectRogueVoiceUsers")).getOrElse(true)
|
||||
lazy val dialInApprovalAudioPath = Try(config.getString("voiceConf.dialInApprovalAudioPath")).getOrElse("ivr/ivr-please_hold_while_party_contacted.wav")
|
||||
lazy val toggleListenOnlyAfterMuteTimer = Try(config.getInt("voiceConf.toggleListenOnlyAfterMuteTimer")).getOrElse(4)
|
||||
lazy val transparentListenOnlyThreshold = Try(config.getInt("voiceConf.transparentListenOnlyThreshold")).getOrElse(0)
|
||||
lazy val muteOnStartThreshold = Try(config.getInt("voiceConf.muteOnStartThreshold")).getOrElse(0)
|
||||
lazy val dialInEnforceGuestPolicy = Try(config.getBoolean("voiceConf.dialInEnforceGuestPolicy")).getOrElse(true)
|
||||
|
||||
lazy val recordingChapterBreakLengthInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(0)
|
||||
|
||||
@ -86,13 +79,6 @@ trait SystemConfiguration {
|
||||
|
||||
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
|
||||
|
||||
lazy val clientSettingsPath = Try(config.getString("client.clientSettingsFilePath")).getOrElse(
|
||||
"/usr/share/bigbluebutton/html5-client/private/config/settings.yml"
|
||||
)
|
||||
lazy val clientSettingsPathOverride = Try(config.getString("client.clientSettingsOverrideFilePath")).getOrElse(
|
||||
"/etc/bigbluebutton/bbb-html5.yml"
|
||||
)
|
||||
|
||||
// Grab the "interface" parameter from the http config
|
||||
val httpHost = config.getString("http.interface")
|
||||
// Grab the "port" parameter from the http config
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
import org.apache.pekko.actor._
|
||||
import org.apache.pekko.actor.ActorLogging
|
||||
import org.apache.pekko.actor.SupervisorStrategy.Resume
|
||||
import org.apache.pekko.util.Timeout
|
||||
import akka.actor._
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core.bus._
|
||||
@ -13,9 +13,6 @@ import org.bigbluebutton.SystemConfiguration
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingEndReason
|
||||
import org.bigbluebutton.core.models.Roles
|
||||
import org.bigbluebutton.core.running.RunningMeeting
|
||||
import org.bigbluebutton.core.util.ColorPicker
|
||||
import org.bigbluebutton.core2.RunningMeetings
|
||||
@ -46,13 +43,6 @@ class BigBlueButtonActor(
|
||||
|
||||
private val meetings = new RunningMeetings
|
||||
|
||||
private case class SessionTokenInfo(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
replaced: Boolean = false
|
||||
)
|
||||
private var sessionTokens = new collection.immutable.HashMap[String, SessionTokenInfo] //sessionToken -> SessionTokenInfo
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
val sw: StringWriter = new StringWriter()
|
||||
@ -63,20 +53,8 @@ class BigBlueButtonActor(
|
||||
}
|
||||
}
|
||||
|
||||
object BBBTasksExecutor
|
||||
context.system.scheduler.schedule(
|
||||
1 minute,
|
||||
1 minute,
|
||||
self,
|
||||
BBBTasksExecutor
|
||||
)
|
||||
|
||||
override def preStart() {
|
||||
bbbMsgBus.subscribe(self, meetingManagerChannel)
|
||||
DatabaseConnection.initialize()
|
||||
|
||||
//Terminate all previous meetings, as they will not function following the akka-apps restart
|
||||
MeetingDAO.setAllMeetingsEnded(MeetingEndReason.ENDED_DUE_TO_SERVICE_INTERRUPTION, "system")
|
||||
}
|
||||
|
||||
override def postStop() {
|
||||
@ -85,76 +63,35 @@ class BigBlueButtonActor(
|
||||
|
||||
def receive = {
|
||||
// Internal messages
|
||||
case BBBTasksExecutor => handleMeetingTasksExecutor()
|
||||
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
|
||||
|
||||
//Api messages
|
||||
case msg: GetUserApiMsg => handleGetUserApiMsg(msg, sender)
|
||||
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleGetUserApiMsg(msg: GetUserApiMsg, actorRef: ActorRef): Unit = {
|
||||
log.debug("RECEIVED GetUserApiMsg msg {}", msg)
|
||||
|
||||
sessionTokens.get(msg.sessionToken) match {
|
||||
case Some(sessionTokenInfo) =>
|
||||
if (sessionTokenInfo.replaced) {
|
||||
log.debug("handleGetUserApiMsg ({}): Session token replaced.", msg.sessionToken)
|
||||
actorRef ! ApiResponseFailure("Session token replaced.", "session_token_replaced")
|
||||
} else {
|
||||
RunningMeetings.findWithId(meetings, sessionTokenInfo.meetingId) match {
|
||||
case Some(m) =>
|
||||
log.debug("handleGetUserApiMsg ({}): {}.", msg.sessionToken, m)
|
||||
m.actorRef forward (msg)
|
||||
|
||||
case None =>
|
||||
//The meeting is ended, it will return some data just to confirm the session was valid
|
||||
//The client can request data after the meeting is ended
|
||||
val userInfos = Map(
|
||||
"returncode" -> "SUCCESS",
|
||||
"sessionToken" -> msg.sessionToken,
|
||||
"meetingID" -> sessionTokenInfo.meetingId,
|
||||
"internalUserID" -> sessionTokenInfo.userId,
|
||||
"externMeetingID" -> "",
|
||||
"externUserID" -> "",
|
||||
"currentlyInMeeting" -> false,
|
||||
"authToken" -> "",
|
||||
"role" -> Roles.VIEWER_ROLE,
|
||||
"guest" -> "false",
|
||||
"guestStatus" -> "ALLOWED",
|
||||
"moderator" -> false,
|
||||
"presenter" -> false,
|
||||
"hideViewersCursor" -> false,
|
||||
"hideViewersAnnotation" -> false,
|
||||
"hideUserList" -> false,
|
||||
"webcamsOnlyForModerator" -> false
|
||||
)
|
||||
|
||||
log.debug("handleGetUserApiMsg ({}): Meeting is ended.", msg.sessionToken)
|
||||
actorRef ! ApiResponseSuccess("Meeting is ended.", UserInfosApiMsg(userInfos))
|
||||
}
|
||||
}
|
||||
|
||||
case None =>
|
||||
log.debug("handleGetUserApiMsg ({}): Meeting not found.", msg.sessionToken)
|
||||
actorRef ! ApiResponseFailure("Meeting not found.", "meeting_not_found")
|
||||
}
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
msg.core match {
|
||||
|
||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case m: RegisterUserSessionTokenReqMsg => handleRegisterUserSessionTokenReqMsg(m)
|
||||
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
||||
case _: UserGraphqlConnectionEstablishedSysMsg => //Ignore
|
||||
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
|
||||
case _: CheckGraphqlMiddlewareAlivePongSysMsg => //Ignore
|
||||
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
|
||||
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
||||
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
||||
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||
}
|
||||
}
|
||||
|
||||
def handleValidateConnAuthTokenSysMsg(msg: ValidateConnAuthTokenSysMsg): Unit = {
|
||||
RunningMeetings.findWithId(meetings, msg.body.meetingId) match {
|
||||
case Some(meeting) =>
|
||||
meeting.actorRef forward msg
|
||||
|
||||
case None =>
|
||||
val event = MsgBuilder.buildValidateConnAuthTokenSysRespMsg(msg.body.meetingId, msg.body.userId,
|
||||
false, msg.body.connId, msg.body.app)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,32 +101,6 @@ class BigBlueButtonActor(
|
||||
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
||||
} yield {
|
||||
log.debug("FORWARDING Register user message")
|
||||
|
||||
//Store sessionTokens and associate them with their respective meetingId + userId owners
|
||||
sessionTokens += (msg.body.sessionToken -> SessionTokenInfo(msg.body.meetingId, msg.body.intUserId))
|
||||
|
||||
m.actorRef forward (msg)
|
||||
}
|
||||
}
|
||||
|
||||
def handleRegisterUserSessionTokenReqMsg(msg: RegisterUserSessionTokenReqMsg): Unit = {
|
||||
log.debug("RECEIVED RegisterUserSessionTokenReqMsg msg {}", msg)
|
||||
for {
|
||||
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
||||
} yield {
|
||||
log.debug("FORWARDING Register user session token message")
|
||||
|
||||
//Store sessionTokens and associate them with their respective meetingId + userId owners
|
||||
sessionTokens += (msg.body.sessionToken -> SessionTokenInfo(msg.body.meetingId, msg.body.userId))
|
||||
|
||||
if (msg.body.replaceSessionToken.nonEmpty) {
|
||||
for {
|
||||
sessionTokenInfo <- sessionTokens.get(msg.body.replaceSessionToken)
|
||||
} yield {
|
||||
sessionTokens += (msg.body.replaceSessionToken -> sessionTokenInfo.copy(replaced = true))
|
||||
}
|
||||
}
|
||||
|
||||
m.actorRef forward (msg)
|
||||
}
|
||||
}
|
||||
@ -219,18 +130,32 @@ class BigBlueButtonActor(
|
||||
}
|
||||
}
|
||||
|
||||
private def handleGetRunningMeetingsReqMsg(msg: GetRunningMeetingsReqMsg): Unit = {
|
||||
val liveMeetings = RunningMeetings.meetings(meetings)
|
||||
val meetingIds = liveMeetings.map(m => m.props.meetingProp.intId)
|
||||
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(GetRunningMeetingsRespMsg.NAME, routing)
|
||||
val header = BbbCoreBaseHeader(GetRunningMeetingsRespMsg.NAME)
|
||||
|
||||
val body = GetRunningMeetingsRespMsgBody(meetingIds)
|
||||
val event = GetRunningMeetingsRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
private def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
|
||||
RunningMeetings.meetings(meetings).filter(_.props.systemProps.html5InstanceId == msg.body.html5InstanceId).foreach(m => {
|
||||
m.actorRef ! msg
|
||||
})
|
||||
}
|
||||
|
||||
private def handleCheckAlivePingSysMsg(msg: CheckAlivePingSysMsg): Unit = {
|
||||
val event = MsgBuilder.buildCheckAlivePingSysMsg(msg.body.system, msg.body.bbbWebTimestamp, System.currentTimeMillis())
|
||||
healthzService.sendPubSubStatusMessage(msg.body.akkaAppsTimestamp, System.currentTimeMillis())
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
private def handleMeetingTasksExecutor(): Unit = {
|
||||
// Delays meeting data for 1 hour post-meeting in case users request info after it ends.
|
||||
// This routine ensures proper purging if akka-apps restart and the handleDestroyMeeting scheduler does not run.
|
||||
MeetingDAO.deleteOldMeetings()
|
||||
}
|
||||
|
||||
private def handleDestroyMeeting(msg: DestroyMeetingInternalMsg): Unit = {
|
||||
|
||||
for {
|
||||
@ -260,16 +185,6 @@ class BigBlueButtonActor(
|
||||
context.stop(m.actorRef)
|
||||
}
|
||||
|
||||
//Delay removal of session tokens and Graphql data once users might request some info after the meeting is ended
|
||||
context.system.scheduler.scheduleOnce(Duration.create(60, TimeUnit.MINUTES)) {
|
||||
log.debug("Removing Graphql data and session tokens. meetingID={}", msg.meetingId)
|
||||
|
||||
sessionTokens = sessionTokens.filter(sessionTokenInfo => sessionTokenInfo._2.meetingId != msg.meetingId)
|
||||
|
||||
//In Db, Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
|
||||
MeetingDAO.delete(msg.meetingId)
|
||||
}
|
||||
|
||||
//Remove ColorPicker idx of the meeting
|
||||
ColorPicker.reset(m.props.meetingProp.intId)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.bigbluebutton.core.api
|
||||
|
||||
import org.bigbluebutton.core.apps.users.UserEstablishedGraphqlConnectionInternalMsgHdlr
|
||||
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
|
||||
import spray.json.JsObject
|
||||
case class InMessageHeader(name: String)
|
||||
@ -25,7 +24,16 @@ case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
|
||||
* Audit message sent to meeting to trigger updating clients of meeting time remaining.
|
||||
* @param meetingId
|
||||
*/
|
||||
case class MonitorGuestWaitPresenceInternalMsg(meetingId: String) extends InMessage
|
||||
case class SendTimeRemainingAuditInternalMsg(meetingId: String, timeUpdatedInMinutes: Int) extends InMessage
|
||||
|
||||
/**
|
||||
* Parent message sent to breakout rooms to trigger updating clients of meeting time remaining.
|
||||
* @param meetingId
|
||||
* @param timeLeftInSec
|
||||
*/
|
||||
case class SendBreakoutTimeRemainingInternalMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int) extends InMessage
|
||||
|
||||
case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
|
||||
|
||||
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||
case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage
|
||||
@ -106,29 +114,15 @@ case class EjectUserFromBreakoutInternalMsg(parentId: String, breakoutId: String
|
||||
case class CapturePresentationReqInternalMsg(userId: String, parentMeetingId: String, filename: String, allPages: Boolean = true) extends InMessage
|
||||
|
||||
/**
|
||||
* Sent to the same meeting to force a new presenter to the Pod
|
||||
* @param presenterId
|
||||
* Sent by breakout room to parent meeting to obtain padId
|
||||
* @param breakoutId
|
||||
* @param filename
|
||||
*/
|
||||
case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMessage
|
||||
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
|
||||
|
||||
/**
|
||||
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
|
||||
* @param userId
|
||||
*/
|
||||
case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends InMessage
|
||||
|
||||
/**
|
||||
* Sent by GraphqlActionsActor to inform MeetingActor that user came back from disconnection
|
||||
* @param userId
|
||||
*/
|
||||
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String, clientType: String, isMobile: Boolean) extends InMessage
|
||||
|
||||
/**
|
||||
* API endpoint /userInfo to provide User Session Variables messages
|
||||
*/
|
||||
case class GetUserApiMsg(sessionToken: String)
|
||||
case class UserInfosApiMsg(infos: Map[String, Any])
|
||||
|
||||
trait ApiResponse
|
||||
case class ApiResponseSuccess(msg: String, any: Any = null) extends ApiResponse
|
||||
case class ApiResponseFailure(msg: String, msgId: String, any: Any = null) extends ApiResponse
|
||||
// DeskShare
|
||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
|
||||
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
|
||||
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage
|
||||
|
@ -20,9 +20,10 @@ object BreakoutModel {
|
||||
captureSlidesFilename: String,
|
||||
allPages: Boolean,
|
||||
presId: String,
|
||||
sourcePresentationFilename: String,
|
||||
): BreakoutRoom2x = {
|
||||
new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false,
|
||||
captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename, allPages, presId)
|
||||
captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename, allPages, presId, sourcePresentationFilename)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,32 @@ class CaptionModel {
|
||||
return None
|
||||
}
|
||||
|
||||
def updateTranscriptOwner(name: String, locale: String, ownerId: String): Map[String, TranscriptVO] = {
|
||||
var updatedTranscripts = new HashMap[String, TranscriptVO]
|
||||
|
||||
// clear owner from previous locale
|
||||
if (ownerId.length > 0) {
|
||||
findTranscriptByOwnerId(ownerId).foreach(t => {
|
||||
val oldTranscript = t._2.copy(ownerId = "")
|
||||
|
||||
transcripts += t._1 -> oldTranscript
|
||||
updatedTranscripts += t._1 -> oldTranscript
|
||||
})
|
||||
}
|
||||
// change the owner if it does exist
|
||||
if (transcripts contains name) {
|
||||
val newTranscript = transcripts(name).copy(ownerId = ownerId)
|
||||
|
||||
transcripts += name -> newTranscript
|
||||
updatedTranscripts += name -> newTranscript
|
||||
} else { // create the locale if it doesn't exist
|
||||
val addedTranscript = createTranscript(name, locale, ownerId)
|
||||
updatedTranscripts += name -> addedTranscript
|
||||
}
|
||||
|
||||
updatedTranscripts
|
||||
}
|
||||
|
||||
def getHistory(): Map[String, TranscriptVO] = {
|
||||
transcripts
|
||||
}
|
||||
@ -71,6 +97,20 @@ class CaptionModel {
|
||||
locale
|
||||
}
|
||||
|
||||
def checkCaptionOwnerLogOut(userId: String): Option[(String, TranscriptVO)] = {
|
||||
var rtnTranscript: Option[(String, TranscriptVO)] = None
|
||||
|
||||
if (userId.length > 0) {
|
||||
findTranscriptByOwnerId(userId).foreach(t => {
|
||||
val oldTranscript = t._2.copy(ownerId = "")
|
||||
|
||||
transcripts += t._1 -> oldTranscript
|
||||
rtnTranscript = Some((t._1, oldTranscript))
|
||||
})
|
||||
}
|
||||
rtnTranscript
|
||||
}
|
||||
|
||||
def isUserCaptionOwner(userId: String, name: String): Boolean = {
|
||||
var isOwner: Boolean = false;
|
||||
|
||||
|
@ -5,6 +5,7 @@ import org.bigbluebutton.core2.message.handlers.guests._
|
||||
|
||||
trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
|
||||
with GuestsWaitingApprovedMsgHdlr
|
||||
with GuestWaitingLeftMsgHdlr
|
||||
with UpdatePositionInWaitingQueueReqMsgHdlr
|
||||
with SetGuestPolicyMsgHdlr
|
||||
with SetGuestLobbyMessageMsgHdlr
|
||||
|
@ -88,12 +88,8 @@ object PermissionCheck extends SystemConfiguration {
|
||||
|
||||
UsersApp.ejectUserFromMeeting(outGW, liveMeeting, userId, ejectedBy, reason, EjectReasonCode.PERMISSION_FAILED, ban = false)
|
||||
|
||||
// Force reconnection with graphql to refresh permissions
|
||||
for {
|
||||
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
|
||||
} yield {
|
||||
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, reason, outGW)
|
||||
}
|
||||
// send a system message to force disconnection
|
||||
Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason, outGW)
|
||||
} else {
|
||||
// TODO: get this object a context so it can use the akka logging system
|
||||
println(s"Skipping violation ejection of ${userId} trying to ${reason} in ${meetingId}")
|
||||
|
@ -18,7 +18,7 @@ class PresentationModel {
|
||||
presentations.values.toVector
|
||||
}
|
||||
|
||||
def getCurrentPresentation(): Option[Presentation] = {
|
||||
def getCurrentPresentation(): Option[Presentation] = { // todo remove
|
||||
presentations.values find (p => p.current)
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ object TimerModel {
|
||||
stopwatch: Boolean = true,
|
||||
time: Int = 0,
|
||||
accumulated: Int = 0,
|
||||
track: String = "noTrack",
|
||||
track: String = "",
|
||||
): Unit = {
|
||||
model.stopwatch = stopwatch
|
||||
model.time = time
|
||||
@ -14,16 +14,20 @@ object TimerModel {
|
||||
model.track = track
|
||||
}
|
||||
|
||||
def reset(model: TimerModel) : Unit = {
|
||||
model.accumulated = 0
|
||||
model.startedAt = if (model.running) System.currentTimeMillis() else 0
|
||||
def reset(model: TimerModel, stopwatch: Boolean, time: Int, accumulated: Int, startedAt: Long, track: String) : Unit = {
|
||||
model.stopwatch = stopwatch
|
||||
model.time = time
|
||||
model.accumulated = accumulated
|
||||
model.startedAt = startedAt
|
||||
model.track = track
|
||||
model.endedAt = 0
|
||||
}
|
||||
|
||||
def setIsActive(model: TimerModel, active: Boolean): Unit = {
|
||||
model.isActive = active
|
||||
}
|
||||
|
||||
def getIsActive(model: TimerModel): Boolean = {
|
||||
def getIsACtive(model: TimerModel): Boolean = {
|
||||
model.isActive
|
||||
}
|
||||
|
||||
@ -44,39 +48,10 @@ object TimerModel {
|
||||
}
|
||||
|
||||
def setRunning(model: TimerModel, running: Boolean): Unit = {
|
||||
resetTimerIfFinished(model)
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
// If the timer is running and will stop, update accumulated time
|
||||
if (isRunning(model) && !running) {
|
||||
val accumulated = getAccumulated(model) + Math.abs(now - getStartedAt(model)).toInt
|
||||
setAccumulated(model, accumulated)
|
||||
}
|
||||
|
||||
// If the timer is not running and will start, set the start time
|
||||
if (!isRunning(model) && running) {
|
||||
setStartedAt(model, now)
|
||||
}
|
||||
|
||||
// Update the running status of the model
|
||||
model.running = running
|
||||
}
|
||||
|
||||
def resetTimerIfFinished(model: TimerModel) = {
|
||||
// If the timer is finished, reset the accumulated time and start time if running
|
||||
if (isRunning(model)
|
||||
&& !isStopwatch(model)
|
||||
&& (model.startedAt + (model.time - model.accumulated)) < System.currentTimeMillis()) {
|
||||
model.running = false
|
||||
reset(model)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def isRunning(model: TimerModel): Boolean = {
|
||||
def getRunning(model: TimerModel): Boolean = {
|
||||
model.running
|
||||
}
|
||||
|
||||
@ -84,7 +59,7 @@ object TimerModel {
|
||||
model.stopwatch = stopwatch
|
||||
}
|
||||
|
||||
def isStopwatch(model: TimerModel): Boolean = {
|
||||
def getStopwatch(model: TimerModel): Boolean = {
|
||||
model.stopwatch
|
||||
}
|
||||
|
||||
@ -103,14 +78,23 @@ object TimerModel {
|
||||
def getTime(model: TimerModel): Int = {
|
||||
model.time
|
||||
}
|
||||
|
||||
def setEndedAt(model: TimerModel, timestamp: Long): Unit = {
|
||||
model.endedAt = timestamp
|
||||
}
|
||||
|
||||
def getEndedAt(model: TimerModel): Long = {
|
||||
model.endedAt
|
||||
}
|
||||
}
|
||||
|
||||
class TimerModel {
|
||||
private var startedAt: Long = 0
|
||||
private var endedAt: Long = 0
|
||||
private var accumulated: Int = 0
|
||||
private var running: Boolean = false
|
||||
private var time: Int = 0
|
||||
private var stopwatch: Boolean = true
|
||||
private var track: String = "noTrack"
|
||||
private var track: String = ""
|
||||
private var isActive: Boolean = false
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import scala.collection.immutable.HashMap
|
||||
import org.bigbluebutton.common2.msgs.AnnotationVO
|
||||
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresPageWritersDAO }
|
||||
|
||||
class WhiteboardModel extends SystemConfiguration {
|
||||
private var _whiteboards = new HashMap[String, Whiteboard]()
|
||||
@ -46,80 +45,33 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
k -> newValue
|
||||
}).toMap
|
||||
|
||||
def addAnnotations(wbId: String, meetingId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||
val wb = getWhiteboard(wbId)
|
||||
|
||||
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||
var annotationsAdded = Array[AnnotationVO]()
|
||||
var annotationsDiffAdded = Array[AnnotationVO]()
|
||||
val wb = getWhiteboard(wbId)
|
||||
var newAnnotationsMap = wb.annotationsMap
|
||||
|
||||
for (annotation <- annotations) {
|
||||
val oldAnnotation = wb.annotationsMap.get(annotation.id)
|
||||
if (oldAnnotation.isDefined) {
|
||||
if (!oldAnnotation.isEmpty) {
|
||||
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
|
||||
if (hasPermission) {
|
||||
val mergedAnnotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo)
|
||||
|
||||
// Apply cleaning if it's an arrow annotation
|
||||
val finalAnnotationInfo = if (oldAnnotation.get.annotationInfo.get("type").contains("arrow")) {
|
||||
cleanArrowAnnotationProps(mergedAnnotationInfo)
|
||||
} else {
|
||||
mergedAnnotationInfo
|
||||
}
|
||||
|
||||
val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo)
|
||||
val newAnnotation = oldAnnotation.get.copy(annotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo))
|
||||
newAnnotationsMap += (annotation.id -> newAnnotation)
|
||||
annotationsAdded :+= newAnnotation
|
||||
annotationsDiffAdded :+= annotation
|
||||
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||
annotationsAdded :+= annotation
|
||||
println(s"Updated annotation onpage [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||
} else {
|
||||
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
|
||||
}
|
||||
} else if (annotation.annotationInfo.contains("type")) {
|
||||
newAnnotationsMap += (annotation.id -> annotation)
|
||||
annotationsAdded :+= annotation
|
||||
annotationsDiffAdded :+= annotation
|
||||
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||
} else {
|
||||
println(s"New annotation [${annotation.id}] with no type, ignoring...")
|
||||
println(s"New annotation [${annotation.id}] with no type, ignoring (probably received a remove message before and now the shape is incomplete, ignoring...")
|
||||
}
|
||||
}
|
||||
|
||||
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded)
|
||||
|
||||
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||
saveWhiteboard(newWb)
|
||||
annotationsDiffAdded
|
||||
}
|
||||
|
||||
private def overwriteLineShapeHandles(oldProps: Map[String, Any], newProps: Map[String, Any]): Map[String, Any] = {
|
||||
val newHandles = newProps.get("handles")
|
||||
val updatedProps = oldProps ++ newProps.filter {
|
||||
case ("handles", _) => false // Remove the old handles
|
||||
case _ => true
|
||||
}
|
||||
updatedProps ++ newHandles.map("handles" -> _)
|
||||
}
|
||||
|
||||
private def cleanArrowAnnotationProps(annotationInfo: Map[String, _]): Map[String, _] = {
|
||||
annotationInfo.get("props") match {
|
||||
case Some(props: Map[String, _]) =>
|
||||
val cleanedProps = props.map {
|
||||
case ("end", endProps: Map[String, _]) => "end" -> cleanEndOrStartProps(endProps)
|
||||
case ("start", startProps: Map[String, _]) => "start" -> cleanEndOrStartProps(startProps)
|
||||
case other => other
|
||||
}
|
||||
annotationInfo + ("props" -> cleanedProps)
|
||||
case _ => annotationInfo
|
||||
}
|
||||
}
|
||||
|
||||
private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = {
|
||||
props.get("type") match {
|
||||
case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type
|
||||
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact", "isPrecise") // Remove unwanted properties for 'point' type
|
||||
case _ => props
|
||||
}
|
||||
annotationsAdded
|
||||
}
|
||||
|
||||
def getHistory(wbId: String): Array[AnnotationVO] = {
|
||||
@ -127,42 +79,33 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
wb.annotationsMap.values.toArray
|
||||
}
|
||||
|
||||
def deleteAnnotations(wbId: String, meetingId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||
val wb = getWhiteboard(wbId)
|
||||
|
||||
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||
var annotationsIdsRemoved = Array[String]()
|
||||
val wb = getWhiteboard(wbId)
|
||||
var newAnnotationsMap = wb.annotationsMap
|
||||
|
||||
for (annotationId <- annotationsIds) {
|
||||
val annotation = wb.annotationsMap.get(annotationId)
|
||||
|
||||
if (annotation.isDefined) {
|
||||
if (!annotation.isEmpty) {
|
||||
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
|
||||
if (hasPermission) {
|
||||
newAnnotationsMap -= annotationId
|
||||
println(s"Removed annotation $annotationId on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||
println("Removing annotation on page [" + wb.id + "]. After numAnnotations=[" + newAnnotationsMap.size + "].")
|
||||
annotationsIdsRemoved :+= annotationId
|
||||
} else {
|
||||
println(s"User $userId doesn't have permission to remove annotation $annotationId, ignoring...")
|
||||
println("User doesn't have permission to remove this annotation, ignoring...")
|
||||
}
|
||||
} else {
|
||||
println(s"Annotation $annotationId not found while trying to delete it.")
|
||||
}
|
||||
}
|
||||
|
||||
// Update whiteboard and save
|
||||
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||
saveWhiteboard(updatedWb)
|
||||
|
||||
PresAnnotationDAO.delete(meetingId, userId, annotationsIdsRemoved)
|
||||
|
||||
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||
saveWhiteboard(newWb)
|
||||
annotationsIdsRemoved
|
||||
}
|
||||
|
||||
def modifyWhiteboardAccess(meetingId: String, wbId: String, multiUser: Array[String]) {
|
||||
def modifyWhiteboardAccess(wbId: String, multiUser: Array[String]) {
|
||||
val wb = getWhiteboard(wbId)
|
||||
val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
|
||||
PresPageWritersDAO.updateMultiuser(meetingId, newWb)
|
||||
saveWhiteboard(newWb)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.bigbluebutton.core.apps.audiocaptions
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import akka.actor.ActorContext
|
||||
|
||||
class AudioCaptionsApp2x(implicit val context: ActorContext)
|
||||
extends UpdateTranscriptPubMsgHdlr
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.audiocaptions
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.UserTranscriptionErrorDAO
|
||||
import org.bigbluebutton.core.models.AudioCaptions
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -24,8 +23,5 @@ trait TranscriptionProviderErrorMsgHdlr {
|
||||
}
|
||||
|
||||
broadcastEvent(msg.header.userId, msg.body.errorCode, msg.body.errorMessage)
|
||||
|
||||
UserTranscriptionErrorDAO.insert(msg.header.userId, msg.header.meetingId, msg.body.errorCode, msg.body.errorMessage)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
package org.bigbluebutton.core.apps.audiocaptions
|
||||
|
||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsStringOrElse
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.CaptionDAO
|
||||
import org.bigbluebutton.core.models.{AudioCaptions, UserState, Pads, Users2x}
|
||||
import org.bigbluebutton.core.models.AudioCaptions
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
trait UpdateTranscriptPubMsgHdlr {
|
||||
this: AudioCaptionsApp2x =>
|
||||
@ -26,17 +22,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def sendPadUpdateCmdMsg(groupId: String, defaultPad: String, text: String, transcript: Boolean): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadUpdateCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadUpdateCmdMsg.NAME, liveMeeting.props.meetingProp.intId)
|
||||
val body = PadUpdateCmdMsgBody(groupId, defaultPad, text)
|
||||
val event = PadUpdateCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
// Adapt to the current captions' recording process
|
||||
def editTranscript(
|
||||
userId: String,
|
||||
@ -78,13 +63,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
||||
|
||||
val transcript = AudioCaptions.parseTranscript(msg.body.transcript)
|
||||
|
||||
|
||||
for {
|
||||
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
} yield {
|
||||
CaptionDAO.insertOrUpdateCaption(msg.body.transcriptId, meetingId, msg.header.userId, transcript, u.speechLocale)
|
||||
}
|
||||
|
||||
broadcastEvent(
|
||||
msg.header.userId,
|
||||
msg.body.transcriptId,
|
||||
@ -92,31 +70,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
||||
msg.body.locale,
|
||||
msg.body.result,
|
||||
)
|
||||
|
||||
if(msg.body.result) {
|
||||
val userName = Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId).get match {
|
||||
case u: UserState => u.name
|
||||
case _ => "???"
|
||||
}
|
||||
|
||||
val now = LocalDateTime.now()
|
||||
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||
val formattedTime = now.format(formatter)
|
||||
|
||||
val userSpoke = s"\n $userName ($formattedTime): $transcript"
|
||||
|
||||
val defaultPad = getConfigPropertyValueByPathAsStringOrElse(
|
||||
liveMeeting.clientSettings,
|
||||
"public.captions.defaultPad",
|
||||
alternativeValue = ""
|
||||
)
|
||||
|
||||
Pads.getGroup(liveMeeting.pads, defaultPad) match {
|
||||
case Some(group) => sendPadUpdateCmdMsg(group.groupId, defaultPad, userSpoke, transcript = true)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
|
||||
with SendMessageToAllBreakoutRoomsMsgHdlr
|
||||
with SendMessageToBreakoutRoomInternalMsgHdlr
|
||||
with RequestBreakoutJoinURLReqMsgHdlr
|
||||
with SetBreakoutRoomInviteDismissedReqMsgHdlr
|
||||
with SendBreakoutUsersUpdateMsgHdlr
|
||||
with TransferUserToMeetingRequestHdlr
|
||||
with EndBreakoutRoomInternalMsgHdlr
|
||||
@ -63,23 +62,13 @@ object BreakoutRoomsUtil extends SystemConfiguration {
|
||||
checksum(apiCall.concat(baseString).concat(sharedSecret))
|
||||
}
|
||||
|
||||
def joinParams(
|
||||
username: String,
|
||||
userId: String,
|
||||
isBreakout: Boolean,
|
||||
breakoutMeetingId: String,
|
||||
avatarURL: String,
|
||||
role: String,
|
||||
password: String
|
||||
): (collection.immutable.Map[String, String], collection.immutable.Map[String, String]) = {
|
||||
val moderator = role == "MODERATOR"
|
||||
def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutMeetingId: String,
|
||||
password: String): (collection.immutable.Map[String, String], collection.immutable.Map[String, String]) = {
|
||||
val params = collection.immutable.HashMap(
|
||||
"fullName" -> urlEncode(username),
|
||||
"userID" -> urlEncode(userId),
|
||||
"isBreakout" -> urlEncode(isBreakout.toString()),
|
||||
"meetingID" -> urlEncode(breakoutMeetingId),
|
||||
"avatarURL" -> urlEncode(avatarURL),
|
||||
"userdata-bbb_parent_room_moderator" -> urlEncode(moderator.toString()),
|
||||
"password" -> urlEncode(password),
|
||||
"redirect" -> urlEncode("true")
|
||||
)
|
||||
|
@ -41,15 +41,8 @@ object BreakoutHdlrHelpers extends SystemConfiguration {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
||||
apiCall = "join"
|
||||
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(
|
||||
user.name,
|
||||
userId + "-" + roomSequence,
|
||||
true,
|
||||
externalMeetingId,
|
||||
user.avatar,
|
||||
user.role,
|
||||
liveMeeting.props.password.moderatorPass
|
||||
)
|
||||
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
|
||||
externalMeetingId, liveMeeting.props.password.moderatorPass)
|
||||
// We generate a first url with redirect -> true
|
||||
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
|
||||
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
|
||||
@ -125,7 +118,7 @@ object BreakoutHdlrHelpers extends SystemConfiguration {
|
||||
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
liveMeeting.props.breakoutProps.parentId,
|
||||
BreakoutRoomUsersUpdateInternalMsg(liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId,
|
||||
new BreakoutRoomUsersUpdateInternalMsg(liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId,
|
||||
breakoutUsers, breakoutVoiceUsers)
|
||||
))
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.breakout
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.BreakoutRoomCreatedInternalMsg
|
||||
import org.bigbluebutton.core.apps.BreakoutModel
|
||||
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
||||
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
|
||||
@ -22,12 +21,9 @@ trait BreakoutRoomCreatedMsgHdlr {
|
||||
} yield {
|
||||
val updatedRoom = sendBreakoutRoomStarted(startedRoom)
|
||||
var updatedModel = breakoutModel.update(updatedRoom)
|
||||
// BreakoutRoomDAO.updateRoomStarted(room.id)
|
||||
|
||||
// We postpone sending invitation until all breakout rooms have been created
|
||||
if (updatedModel.hasAllStarted()) {
|
||||
updatedModel = updatedModel.copy(startedOn = Some(System.currentTimeMillis()))
|
||||
BreakoutRoomDAO.updateRoomsStarted(room.parentId)
|
||||
updatedModel = sendBreakoutRoomsList(updatedModel)
|
||||
}
|
||||
updatedModel
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.BreakoutRoomEndedInternalMsg
|
||||
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
@ -37,9 +36,7 @@ trait BreakoutRoomEndedInternalMsgHdlr {
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
|
||||
BreakoutRoomDAO.updateRoomsEnded(liveMeeting.props.meetingProp.intId)
|
||||
state.update(None)
|
||||
} else {
|
||||
state.update(Some(model))
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.BreakoutRoomUsersUpdateInternalMsg
|
||||
import org.bigbluebutton.core.db.{ BreakoutRoomUserDAO, UserBreakoutRoomDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
|
||||
@ -13,11 +13,24 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
|
||||
|
||||
def handleBreakoutRoomUsersUpdateInternalMsg(msg: BreakoutRoomUsersUpdateInternalMsg, state: MeetingState2x): MeetingState2x = {
|
||||
|
||||
def broadcastEvent(room: BreakoutRoom2x): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(UpdateBreakoutUsersEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UpdateBreakoutUsersEvtMsg.NAME, props.meetingProp.intId, "not-used")
|
||||
|
||||
val users = room.users.map(u => BreakoutUserVO(u.id, u.name))
|
||||
val body = UpdateBreakoutUsersEvtMsgBody(props.meetingProp.intId, msg.breakoutId, users)
|
||||
val event = UpdateBreakoutUsersEvtMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
val breakoutModel = for {
|
||||
model <- state.breakout
|
||||
room <- model.find(msg.breakoutId)
|
||||
} yield {
|
||||
val updatedRoom = room.copy(users = msg.users, voiceUsers = msg.voiceUsers)
|
||||
val msgEvent = broadcastEvent(updatedRoom)
|
||||
outGW.send(msgEvent)
|
||||
|
||||
//Update user lastActivityTime in parent room (to avoid be ejected while is in Breakout room)
|
||||
for {
|
||||
@ -35,12 +48,6 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
|
||||
}
|
||||
}
|
||||
|
||||
val usersInRoom = for {
|
||||
breakoutRoomUser <- updatedRoom.users
|
||||
u <- RegisteredUsers.findWithBreakoutRoomId(breakoutRoomUser.id, liveMeeting.registeredUsers)
|
||||
} yield u.id
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(props.meetingProp.intId, usersInRoom, updatedRoom)
|
||||
BreakoutRoomUserDAO.updateUserJoined(props.meetingProp.intId, usersInRoom, updatedRoom)
|
||||
model.update(updatedRoom)
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,13 @@ package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.getRedirectUrls
|
||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.{ getRedirectUrls }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, NotificationDAO}
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.EjectReasonCode
|
||||
import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter}
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core.models.{ EjectReasonCode }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
|
||||
trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
@ -29,6 +28,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
for {
|
||||
breakoutModel <- state.breakout
|
||||
} yield {
|
||||
|
||||
//Eject user from room From
|
||||
for {
|
||||
roomFrom <- breakoutModel.rooms.get(msg.body.fromBreakoutId)
|
||||
@ -38,9 +38,6 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
})
|
||||
}
|
||||
|
||||
val isSameRoom = msg.body.fromBreakoutId == msg.body.toBreakoutId
|
||||
val removePreviousRoomFromDb = !breakoutModel.rooms.exists(r => r._2.freeJoin) && !isSameRoom
|
||||
|
||||
//Get join URL for room To
|
||||
val redirectToHtml5JoinURL = (
|
||||
for {
|
||||
@ -49,6 +46,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
} yield redirectToHtml5JoinURL
|
||||
).getOrElse("")
|
||||
|
||||
|
||||
BreakoutHdlrHelpers.sendChangeUserBreakoutMsg(
|
||||
outGW,
|
||||
meetingId,
|
||||
@ -58,15 +56,6 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
redirectToHtml5JoinURL,
|
||||
)
|
||||
|
||||
//Update database
|
||||
BreakoutRoomUserDAO.updateRoomChanged(
|
||||
meetingId,
|
||||
msg.body.userId,
|
||||
msg.body.fromBreakoutId,
|
||||
msg.body.toBreakoutId,
|
||||
redirectToHtml5JoinURL,
|
||||
removePreviousRoomFromDb)
|
||||
|
||||
//Send notification to moved User
|
||||
for {
|
||||
roomFrom <- breakoutModel.rooms.get(msg.body.fromBreakoutId)
|
||||
@ -82,7 +71,6 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
Vector(roomTo.shortName)
|
||||
)
|
||||
outGW.send(notifyUserEvent)
|
||||
NotificationDAO.insert(notifyUserEvent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse}
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{BreakoutModel, PermissionCheck, RightsManagementTrait}
|
||||
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
||||
import org.bigbluebutton.core.domain.{BreakoutRoom2x, MeetingState2x}
|
||||
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
||||
import org.bigbluebutton.core.models.PresentationInPod
|
||||
import org.bigbluebutton.core.running.{LiveMeeting, OutMsgRouter}
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
|
||||
trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
@ -17,10 +15,6 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
def handleCreateBreakoutRoomsCmdMsg(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
|
||||
|
||||
|
||||
val minOfRooms = 2
|
||||
val maxOfRooms = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.app.breakouts.breakoutRoomLimit", 16)
|
||||
|
||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("breakoutRooms")) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "Breakout rooms is disabled for this meeting."
|
||||
@ -32,15 +26,6 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
|
||||
reason, outGW, liveMeeting)
|
||||
state
|
||||
} else if(msg.body.rooms.length > maxOfRooms || msg.body.rooms.length < minOfRooms) {
|
||||
log.warning(
|
||||
"Attempt to create breakout rooms with invalid number of rooms (rooms: {}, max: {}, min: {}) in meeting {}",
|
||||
msg.body.rooms.size,
|
||||
maxOfRooms,
|
||||
minOfRooms,
|
||||
liveMeeting.props.meetingProp.intId
|
||||
)
|
||||
state
|
||||
} else {
|
||||
state.breakout match {
|
||||
case Some(breakout) =>
|
||||
@ -69,9 +54,9 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
|
||||
|
||||
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName,
|
||||
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
|
||||
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename,
|
||||
room.allPages, roomPresId)
|
||||
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
|
||||
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename,
|
||||
room.allPages, roomPresId, room.sourcePresentationFilename)
|
||||
|
||||
rooms = rooms + (breakout.id -> breakout)
|
||||
}
|
||||
@ -88,18 +73,18 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
breakout.freeJoin,
|
||||
liveMeeting.props.voiceProp.dialNumber,
|
||||
breakout.voiceConf,
|
||||
msg.body.durationInMinutes,
|
||||
msg.body.durationInMinutes * 60,
|
||||
liveMeeting.props.password.moderatorPass,
|
||||
liveMeeting.props.password.viewerPass,
|
||||
breakout.presId,
|
||||
roomSlides,
|
||||
breakout.sourcePresentationFilename,
|
||||
msg.body.record,
|
||||
liveMeeting.props.breakoutProps.privateChatEnabled,
|
||||
breakout.captureNotes,
|
||||
breakout.captureSlides,
|
||||
breakout.captureNotesFilename,
|
||||
breakout.captureSlidesFilename,
|
||||
pluginProp = liveMeeting.props.pluginProp,
|
||||
)
|
||||
|
||||
val event = buildCreateBreakoutRoomSysCmdMsg(liveMeeting.props.meetingProp.intId, roomDetail)
|
||||
@ -107,7 +92,6 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
}
|
||||
|
||||
val breakoutModel = new BreakoutModel(None, msg.body.durationInMinutes * 60, rooms, msg.body.sendInviteToModerators)
|
||||
BreakoutRoomDAO.insert(breakoutModel, liveMeeting)
|
||||
state.update(Some(breakoutModel))
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,7 @@ package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
||||
import org.bigbluebutton.core.apps.users.UsersApp
|
||||
import org.bigbluebutton.core.db.{ BreakoutRoomUserDAO, UserDAO }
|
||||
import org.bigbluebutton.core.models.RegisteredUsers
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.Sender
|
||||
|
||||
@ -28,12 +27,8 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
|
||||
msg.reasonCode,
|
||||
msg.ban
|
||||
)
|
||||
|
||||
//TODO inform reason
|
||||
UserDAO.softDelete(registeredUser.meetingId, registeredUser.id)
|
||||
|
||||
// Force reconnection with graphql to refresh permissions
|
||||
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, msg.reasonCode, outGW)
|
||||
// send a system message to force disconnection
|
||||
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
|
||||
|
||||
//send users update to parent meeting
|
||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
||||
|
@ -6,8 +6,7 @@ import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingState2x }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO, UserBreakoutRoomDAO }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
|
||||
trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
@ -21,20 +20,25 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
state
|
||||
} else {
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)
|
||||
for {
|
||||
model <- state.breakout
|
||||
} yield {
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(meetingId, room.id, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)))
|
||||
}
|
||||
|
||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||
meetingId,
|
||||
"info",
|
||||
"rooms",
|
||||
"app.toast.breakoutRoomEnded",
|
||||
"Message when the breakout room is ended",
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||
meetingId,
|
||||
"info",
|
||||
"rooms",
|
||||
"app.toast.breakoutRoomEnded",
|
||||
"Message when the breakout room is ended",
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
|
||||
}
|
||||
|
||||
BreakoutRoomDAO.updateRoomsEnded(meetingId)
|
||||
state.update(None)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, ExportJob, MessageTypes, PresentationConversionUpdateEvtMsg, PresentationConversionUpdateEvtMsgBody, PresentationConversionUpdateSysPubMsg, PresentationPageForExport, PresentationUploadTokenSysPubMsg, PresentationUploadTokenSysPubMsgBody, Routing, StoreExportJobInRedisSysMsg, StoreExportJobInRedisSysMsgBody, StoredAnnotations }
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
||||
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
|
||||
import org.bigbluebutton.core.api.{ CaptureSharedNotesReqInternalMsg, CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
||||
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus }
|
||||
import org.bigbluebutton.core.db.{ PresPresentationDAO }
|
||||
import org.bigbluebutton.core.models.{ Pads, PresentationInPod, PresentationPage, PresentationPod }
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||
|
||||
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
@ -23,80 +19,12 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
if (liveMeeting.props.breakoutProps.captureNotes) {
|
||||
handleCaptureNotes(msg)
|
||||
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
|
||||
val captureNotesEvent = BigBlueButtonEvent(msg.parentId, CaptureSharedNotesReqInternalMsg(msg.breakoutId, filename))
|
||||
eventBus.publish(captureNotesEvent)
|
||||
}
|
||||
|
||||
log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
|
||||
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system")
|
||||
}
|
||||
|
||||
def handleCaptureNotes(msg: EndBreakoutRoomInternalMsg) {
|
||||
for {
|
||||
group <- Pads.getGroup(liveMeeting.pads, "notes")
|
||||
} yield {
|
||||
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
|
||||
val userId: String = "system"
|
||||
val jobId: String = s"${msg.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||
val presentationId = PresentationPodsApp.generatePresentationId(filename)
|
||||
|
||||
var pres = new PresentationInPod(presentationId, default = false, current = false, name = filename,
|
||||
pages = Map.empty, downloadable = false, downloadFileExtension = "", removable = true, filenameConverted = filename,
|
||||
uploadCompleted = false, numPages = 0, errorMsgKey = "", errorDetails = Map.empty)
|
||||
|
||||
if (group.rev > 0) {
|
||||
//Request upload of the sharedNotes of breakoutRoom
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
outGW.send(buildPresentationUploadTokenSysPubMsg(msg.parentId, userId, presentationUploadToken, filename, presentationId))
|
||||
|
||||
val exportJob = ExportJob(jobId, "PadCaptureJob", filename, filename, group.padId, "", allPages = true, List(), msg.parentId, presentationUploadToken)
|
||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||
outGW.send(job)
|
||||
} else {
|
||||
pres = pres.copy(errorMsgKey = "204")
|
||||
|
||||
val event = buildPresentationConversionUpdateEvtMsg(msg.parentId, presentationId, filename, jobId)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
PresPresentationDAO.updateConversionStarted(msg.parentId, pres)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def buildStoreExportJobInRedisSysMsg(exportJob: ExportJob, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(StoreExportJobInRedisSysMsg.NAME, routing)
|
||||
val body = StoreExportJobInRedisSysMsgBody(exportJob)
|
||||
val header = BbbCoreHeaderWithMeetingId(StoreExportJobInRedisSysMsg.NAME, liveMeeting.props.meetingProp.intId)
|
||||
val event = StoreExportJobInRedisSysMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildPresentationUploadTokenSysPubMsg(parentMeetingId: String, userId: String, presentationUploadToken: String, filename: String, presId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
|
||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
|
||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildPresentationConversionUpdateEvtMsg(meetingId: String, presentationId: String, presName: String, temporaryPresentationId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "system")
|
||||
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationConversionUpdateEvtMsg.NAME, meetingId, "system")
|
||||
|
||||
val body = PresentationConversionUpdateEvtMsgBody(
|
||||
"DEFAULT_PRESENTATION_POD",
|
||||
"204",
|
||||
"not-used",
|
||||
presentationId,
|
||||
presName,
|
||||
temporaryPresentationId
|
||||
)
|
||||
val event = PresentationConversionUpdateEvtMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,7 @@ import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.BreakoutRoomUserDAO
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.models.{ Users2x, Roles }
|
||||
|
||||
trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
@ -24,9 +23,6 @@ trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
|
||||
requesterUser <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
} yield {
|
||||
if (requesterUser.role == Roles.MODERATOR_ROLE || room.freeJoin) {
|
||||
|
||||
BreakoutRoomUserDAO.insertBreakoutRoom(requesterUser.intId, room, liveMeeting)
|
||||
|
||||
BreakoutHdlrHelpers.sendJoinURL(
|
||||
liveMeeting,
|
||||
outGW,
|
||||
|
@ -0,0 +1,15 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.SendBreakoutTimeRemainingInternalMsg
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait SendBreakoutTimeRemainingInternalMsgHdlr {
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSendBreakoutTimeRemainingInternalMsg(msg: SendBreakoutTimeRemainingInternalMsg): Unit = {
|
||||
val event = MsgBuilder.buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, msg.timeLeftInSec.toInt, msg.timeUpdatedInMinutes)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.SendMessageToBreakoutRoomInternalMsg
|
||||
import org.bigbluebutton.core.api.{ SendMessageToBreakoutRoomInternalMsg }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.db.NotificationDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.RegisteredUsers
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
|
||||
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
@ -33,7 +32,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
|
||||
outGW.send(event)
|
||||
|
||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
msg.header.userId,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"info",
|
||||
@ -42,8 +41,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
"Message for chat sent successfully",
|
||||
Vector(s"${breakoutModel.rooms.size}")
|
||||
)
|
||||
outGW.send(notifyUserEvent)
|
||||
NotificationDAO.insert(notifyUserEvent)
|
||||
outGW.send(notifyModeratorEvent)
|
||||
|
||||
log.debug("Sending message '{}' to all breakout rooms in meeting {}", msg.body.msg, props.meetingProp.intId)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMessageType, GroupChatMsgFromUser }
|
||||
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMsgFromUser }
|
||||
import org.bigbluebutton.core.api.SendMessageToBreakoutRoomInternalMsg
|
||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
@ -18,9 +18,9 @@ trait SendMessageToBreakoutRoomInternalMsgHdlr {
|
||||
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
||||
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
||||
} yield {
|
||||
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), msg.msg, replyToMessageId = "")
|
||||
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser, emphasizedText = true)
|
||||
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
|
||||
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), true, msg.msg)
|
||||
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser)
|
||||
val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm)
|
||||
|
||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
|
@ -1,22 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.BreakoutRoomUserDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
|
||||
trait SetBreakoutRoomInviteDismissedReqMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSetBreakoutRoomInviteDismissedReqMsg(msg: SetBreakoutRoomInviteDismissedReqMsg) = {
|
||||
for {
|
||||
requesterUser <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
} yield {
|
||||
BreakoutRoomUserDAO.updateInviteDismissedAt(requesterUser.meetingId, requesterUser.intId)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.UserDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.VoiceUsers
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
@ -18,13 +17,13 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
val reason = "No permission to transfer user to voice breakout."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else {
|
||||
processTransferUserToMeetingRequest(msg)
|
||||
processRequest(msg)
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
def processTransferUserToMeetingRequest(msg: TransferUserToMeetingRequestMsg) {
|
||||
def processRequest(msg: TransferUserToMeetingRequestMsg) {
|
||||
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
|
||||
// want to transfer from parent meeting to breakout
|
||||
for {
|
||||
@ -33,7 +32,6 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
from <- getVoiceConf(msg.body.fromMeetingId, model)
|
||||
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
||||
} yield {
|
||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||
outGW.send(event)
|
||||
}
|
||||
@ -55,7 +53,6 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
room <- model.find(msg.body.fromMeetingId)
|
||||
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
|
||||
} yield {
|
||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.UpdateBreakoutRoomTimeInternalMsg
|
||||
import org.bigbluebutton.core.api.{ UpdateBreakoutRoomTimeInternalMsg, SendTimeRemainingAuditInternalMsg }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO, NotificationDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||
@ -63,10 +62,9 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
||||
Vector(s"${msg.body.timeInMinutes}")
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
}
|
||||
|
||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
msg.header.userId,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"info",
|
||||
@ -75,12 +73,9 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
||||
"Sent to the moderator that requested breakout duration change",
|
||||
Vector(s"${msg.body.timeInMinutes}")
|
||||
)
|
||||
outGW.send(notifyUserEvent)
|
||||
NotificationDAO.insert(notifyUserEvent)
|
||||
outGW.send(notifyModeratorEvent)
|
||||
|
||||
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
|
||||
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)
|
||||
MeetingDAO.updateMeetingDurationByParentMeeting(props.meetingProp.intId, newDurationInSeconds)
|
||||
breakoutModel.setTime(newDurationInSeconds)
|
||||
}
|
||||
}
|
||||
@ -88,11 +83,12 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
||||
val event = buildUpdateBreakoutRoomsTimeEvtMsg(msg.body.timeInMinutes)
|
||||
outGW.send(event)
|
||||
|
||||
//Force Update time remaining in the clients
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId, msg.body.timeInMinutes)))
|
||||
|
||||
updatedModel match {
|
||||
case Some(model) => {
|
||||
state.update(Some(model))
|
||||
}
|
||||
case None => state
|
||||
case Some(model) => state.update(Some(model))
|
||||
case None => state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
package org.bigbluebutton.core.apps.caption
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.{ CaptionDAO, CaptionLocaleDAO, CaptionTypes }
|
||||
|
||||
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
|
||||
val log = Logging(context.system, getClass)
|
||||
@ -15,10 +14,18 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
|
||||
liveMeeting.captionModel.getHistory()
|
||||
}
|
||||
|
||||
def updateCaptionOwner(liveMeeting: LiveMeeting, name: String, locale: String, userId: String): Map[String, TranscriptVO] = {
|
||||
liveMeeting.captionModel.updateTranscriptOwner(name, locale, userId)
|
||||
}
|
||||
|
||||
def editCaptionHistory(liveMeeting: LiveMeeting, userId: String, startIndex: Integer, endIndex: Integer, name: String, text: String): Boolean = {
|
||||
liveMeeting.captionModel.editHistory(userId, startIndex, endIndex, name, text)
|
||||
}
|
||||
|
||||
def checkCaptionOwnerLogOut(liveMeeting: LiveMeeting, userId: String): Option[(String, TranscriptVO)] = {
|
||||
liveMeeting.captionModel.checkCaptionOwnerLogOut(userId)
|
||||
}
|
||||
|
||||
def isUserCaptionOwner(liveMeeting: LiveMeeting, userId: String, name: String): Boolean = {
|
||||
liveMeeting.captionModel.isUserCaptionOwner(userId, name)
|
||||
}
|
||||
@ -51,23 +58,7 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
|
||||
}
|
||||
}
|
||||
}
|
||||
def handle(msg: CaptionSubmitTranscriptPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
def broadcastSuccessEvent(transcriptId: String, transcript: String, locale: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(CaptionSubmitTranscriptEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(CaptionSubmitTranscriptEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = CaptionSubmitTranscriptEvtMsgBody(transcriptId, transcript, locale, msg.body.captionType)
|
||||
val event = CaptionSubmitTranscriptEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
CaptionDAO.insertOrUpdateCaption(msg.body.transcriptId, meetingId, msg.header.userId,
|
||||
msg.body.transcript, msg.body.locale, msg.body.captionType)
|
||||
|
||||
broadcastSuccessEvent(msg.body.transcriptId, msg.body.transcript, msg.body.locale)
|
||||
}
|
||||
def handle(msg: SendCaptionHistoryReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
def broadcastEvent(msg: SendCaptionHistoryReqMsg, history: Map[String, TranscriptVO]): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
@ -83,25 +74,45 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
|
||||
broadcastEvent(msg, getCaptionHistory(liveMeeting))
|
||||
}
|
||||
|
||||
def handle(msg: AddCaptionLocalePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
def broadcastAddCaptionLocaleEvent(locale: String, userId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
|
||||
val envelope = BbbCoreEnvelope(AddCaptionLocaleEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(AddCaptionLocaleEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
|
||||
def handle(msg: UpdateCaptionOwnerPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
def broadcastUpdateCaptionOwnerEvent(name: String, locale: String, newOwnerId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||
val envelope = BbbCoreEnvelope(UpdateCaptionOwnerEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UpdateCaptionOwnerEvtMsg.NAME, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||
|
||||
val body = AddCaptionLocaleEvtMsgBody(locale)
|
||||
val event = AddCaptionLocaleEvtMsg(header, body)
|
||||
val body = UpdateCaptionOwnerEvtMsgBody(name, locale, newOwnerId)
|
||||
val event = UpdateCaptionOwnerEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
CaptionLocaleDAO.insertOrUpdateCaptionLocale(liveMeeting.props.meetingProp.intId, locale, CaptionTypes.TYPED, userId)
|
||||
}
|
||||
|
||||
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "No permission to add caption locale."
|
||||
val reason = "No permission to change caption owners."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
} else {
|
||||
broadcastAddCaptionLocaleEvent(msg.body.locale, msg.header.userId)
|
||||
updateCaptionOwner(liveMeeting, msg.body.name, msg.body.locale, msg.body.ownerId).foreach(f => {
|
||||
broadcastUpdateCaptionOwnerEvent(f._1, f._2.locale, f._2.ownerId)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def handleUserLeavingMsg(userId: String, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
def broadcastUpdateCaptionOwnerEvent(name: String, locale: String, newOwnerId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||
val envelope = BbbCoreEnvelope(UpdateCaptionOwnerEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UpdateCaptionOwnerEvtMsg.NAME, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||
|
||||
val body = UpdateCaptionOwnerEvtMsgBody(name, locale, newOwnerId)
|
||||
val event = UpdateCaptionOwnerEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
for {
|
||||
transcriptInfo <- checkCaptionOwnerLogOut(liveMeeting, userId)
|
||||
} yield {
|
||||
broadcastUpdateCaptionOwnerEvent(transcriptInfo._1, transcriptInfo._2.locale, transcriptInfo._2.ownerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.bigbluebutton.core.apps.chat
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import akka.actor.ActorContext
|
||||
|
||||
class ChatApp2x(implicit val context: ActorContext)
|
||||
extends GetChatHistoryReqMsgHdlr
|
||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.chat
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ChatMessageDAO
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
|
||||
@ -32,8 +31,6 @@ trait ClearPublicChatHistoryPubMsgHdlr extends LogHelper with RightsManagementTr
|
||||
val newState = for {
|
||||
gc <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
ChatMessageDAO.deleteAllFromChat(liveMeeting.props.meetingProp.intId, msg.body.chatId)
|
||||
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, msg.body.chatId, "", GroupChatMessageType.PUBLIC_CHAT_HIST_CLEARED, Map(), "")
|
||||
broadcastEvent(msg)
|
||||
val newGc = gc.clearMessages()
|
||||
val gcs = state.groupChats.update(newGc)
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.chat
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ChatUserDAO
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
||||
|
||||
trait UserTypingPubMsgHdlr extends LogHelper {
|
||||
@ -17,7 +16,6 @@ trait UserTypingPubMsgHdlr extends LogHelper {
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
ChatUserDAO.updateUserTyping(liveMeeting.props.meetingProp.intId, msg.body.chatId, msg.header.userId)
|
||||
broadcastEvent(msg)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.externalvideo
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
|
||||
class ExternalVideoApp2x(implicit val context: ActorContext)
|
||||
extends StartExternalVideoPubMsgHdlr
|
||||
|
@ -4,8 +4,7 @@ import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x.requestBroadcastStop
|
||||
import org.bigbluebutton.core.db.ExternalVideoDAO
|
||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x.{ requestBroadcastStop }
|
||||
|
||||
trait StartExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||
this: ExternalVideoApp2x =>
|
||||
@ -40,7 +39,6 @@ trait StartExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||
requestBroadcastStop(bus.outGW, liveMeeting)
|
||||
|
||||
ExternalVideoModel.setURL(liveMeeting.externalVideoModel, msg.body.externalVideoUrl)
|
||||
ExternalVideoDAO.insert(liveMeeting.props.meetingProp.intId, msg.body.externalVideoUrl)
|
||||
broadcastEvent(msg)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.externalvideo
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ExternalVideoDAO
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
@ -20,8 +19,6 @@ trait StopExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||
} else {
|
||||
ExternalVideoModel.clear(liveMeeting.externalVideoModel)
|
||||
|
||||
ExternalVideoDAO.updateStoppedSharing(liveMeeting.props.meetingProp.intId)
|
||||
|
||||
//broadcastEvent
|
||||
val msgEvent = MsgBuilder.buildStopExternalVideoEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
bus.outGW.send(msgEvent)
|
||||
|
@ -3,8 +3,7 @@ package org.bigbluebutton.core.apps.externalvideo
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ExternalVideoDAO
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
|
||||
trait UpdateExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
@ -25,7 +24,6 @@ trait UpdateExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||
val reason = "You need to be the presenter to update external video"
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
} else {
|
||||
ExternalVideoDAO.update(liveMeeting.props.meetingProp.intId, msg.body.status, msg.body.rate, msg.body.time, msg.body.state)
|
||||
broadcastEvent(msg)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ import org.bigbluebutton.core.models.GroupChat
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.db.ChatDAO
|
||||
import org.bigbluebutton.core.db.ChatUserDAO
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.models.Roles
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
@ -51,33 +49,27 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
state
|
||||
} else {
|
||||
GroupChatApp.getGroupChatOfUsers(msg.header.userId, msg.body.users, state) match {
|
||||
case Some(groupChat) =>
|
||||
ChatUserDAO.updateChatVisible(msg.header.meetingId, groupChat.id, msg.header.userId, visible = true)
|
||||
state
|
||||
case None =>
|
||||
val newState = for {
|
||||
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||
} yield {
|
||||
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m, emphasizedText = false))
|
||||
val users = {
|
||||
if (msg.body.access == GroupChatAccess.PRIVATE) {
|
||||
val cu = msg.body.users.toSet + msg.header.userId
|
||||
cu.flatMap(u => GroupChatApp.findGroupChatUser(u, liveMeeting.users2x)).toVector
|
||||
} else {
|
||||
Vector.empty
|
||||
}
|
||||
}
|
||||
|
||||
val gc = GroupChatApp.createGroupChat(msg.body.access, createdBy, users, msgs)
|
||||
sendMessages(msg, gc, liveMeeting, bus)
|
||||
|
||||
val groupChats = state.groupChats.add(gc)
|
||||
ChatDAO.insert(liveMeeting.props.meetingProp.intId, gc)
|
||||
state.update(groupChats)
|
||||
val newState = for {
|
||||
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||
} yield {
|
||||
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m))
|
||||
val users = {
|
||||
if (msg.body.access == GroupChatAccess.PRIVATE) {
|
||||
val cu = msg.body.users.toSet + msg.header.userId
|
||||
cu.flatMap(u => GroupChatApp.findGroupChatUser(u, liveMeeting.users2x)).toVector
|
||||
} else {
|
||||
Vector.empty
|
||||
}
|
||||
newState.getOrElse(state)
|
||||
}
|
||||
|
||||
val gc = GroupChatApp.createGroupChat(msg.body.access, createdBy, users, msgs)
|
||||
sendMessages(msg, gc, liveMeeting, bus)
|
||||
|
||||
val groupChats = state.groupChats.add(gc)
|
||||
state.update(groupChats)
|
||||
}
|
||||
|
||||
newState.getOrElse(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ChatMessageReactionDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait DeleteGroupChatMessageReactionReqMsgHdlr extends HandlerHelpers {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handle(msg: DeleteGroupChatMessageReactionReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
val chatMessageReactionsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chatMessageReactions")
|
||||
var chatLocked: Boolean = false
|
||||
var chatLockedForUser: Boolean = false
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
||||
chatLockedForUser = true
|
||||
}
|
||||
|
||||
val userIsModerator = user.role != Roles.MODERATOR_ROLE
|
||||
|
||||
if (!userIsModerator && user.locked) {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||
val modMembers = groupChat.users.filter(cu => Users2x.findWithIntId(liveMeeting.users2x, cu.id) match {
|
||||
case Some(user) => user.role == Roles.MODERATOR_ROLE
|
||||
case None => false
|
||||
})
|
||||
// don't lock private chats that involve a moderator
|
||||
if (modMembers.isEmpty) {
|
||||
chatLocked = permissions.disablePrivChat
|
||||
}
|
||||
} else {
|
||||
chatLocked = permissions.disablePubChat
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatDisabled && !chatMessageReactionsDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
||||
for {
|
||||
gcMessage <- groupChat.msgs.find(gcm => gcm.id == msg.body.messageId)
|
||||
} yield {
|
||||
val chatIsPrivate = groupChat.access == GroupChatAccess.PRIVATE
|
||||
val userIsAParticipant = groupChat.users.exists(u => u.id == user.intId)
|
||||
|
||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||
val event = buildGroupChatMessageReactionDeletedEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.chatId, gcMessage.id, msg.body.reactionEmoji, msg.body.reactionEmojiId)
|
||||
bus.outGW.send(event)
|
||||
ChatMessageReactionDAO.delete(liveMeeting.props.meetingProp.intId, gcMessage.id, msg.header.userId, msg.body.reactionEmoji, msg.body.reactionEmojiId)
|
||||
} else {
|
||||
val reason = "User isn't a participant of the chat"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait DeleteGroupChatMessageReqMsgHdlr extends HandlerHelpers {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handle(msg: DeleteGroupChatMessageReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
val deleteChatMessageDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("deleteChatMessage")
|
||||
var chatLocked: Boolean = false
|
||||
var chatLockedForUser: Boolean = false
|
||||
|
||||
var newState = state
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
||||
chatLockedForUser = true
|
||||
}
|
||||
|
||||
val userIsModerator = user.role == Roles.MODERATOR_ROLE
|
||||
|
||||
if (!userIsModerator && user.locked) {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||
val modMembers = groupChat.users.filter(cu => Users2x.findWithIntId(liveMeeting.users2x, cu.id) match {
|
||||
case Some(user) => user.role == Roles.MODERATOR_ROLE
|
||||
case None => false
|
||||
})
|
||||
// don't lock private chats that involve a moderator
|
||||
if (modMembers.isEmpty) {
|
||||
chatLocked = permissions.disablePrivChat
|
||||
}
|
||||
} else {
|
||||
chatLocked = permissions.disablePubChat
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatDisabled && !deleteChatMessageDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
||||
for {
|
||||
gcMessage <- groupChat.msgs.find(gcm => gcm.id == msg.body.messageId)
|
||||
} yield {
|
||||
val chatIsPrivate = groupChat.access == GroupChatAccess.PRIVATE
|
||||
val userIsAParticipant = groupChat.users.exists(u => u.id == user.intId)
|
||||
val userIsOwner = gcMessage.sender.id == user.intId
|
||||
|
||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||
if (userIsOwner || userIsModerator) {
|
||||
val updatedGroupChat = GroupChatApp.deleteGroupChatMessage(liveMeeting.props.meetingProp.intId, groupChat, state.groupChats, gcMessage, user.intId)
|
||||
|
||||
val event = buildGroupChatMessageDeletedEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.chatId, gcMessage.id)
|
||||
bus.outGW.send(event)
|
||||
newState = state.update(updatedGroupChat)
|
||||
} else {
|
||||
val reason = "User doesn't have permission to delete chat message"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
} else {
|
||||
val reason = "User isn't a participant of the chat"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newState
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait EditGroupChatMessageReqMsgHdlr extends HandlerHelpers {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handle(msg: EditGroupChatMessageReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
val editChatMessageDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("editChatMessage")
|
||||
var chatLocked: Boolean = false
|
||||
var chatLockedForUser: Boolean = false
|
||||
|
||||
var newState = state
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
||||
chatLockedForUser = true
|
||||
}
|
||||
|
||||
val userIsModerator = user.role == Roles.MODERATOR_ROLE
|
||||
|
||||
if (!userIsModerator && user.locked) {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||
val modMembers = groupChat.users.filter(cu => Users2x.findWithIntId(liveMeeting.users2x, cu.id) match {
|
||||
case Some(user) => user.role == Roles.MODERATOR_ROLE
|
||||
case None => false
|
||||
})
|
||||
// don't lock private chats that involve a moderator
|
||||
if (modMembers.isEmpty) {
|
||||
chatLocked = permissions.disablePrivChat
|
||||
}
|
||||
} else {
|
||||
chatLocked = permissions.disablePubChat
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatDisabled && !editChatMessageDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
||||
for {
|
||||
gcMessage <- groupChat.msgs.find(gcm => gcm.id == msg.body.messageId)
|
||||
} yield {
|
||||
val chatIsPrivate = groupChat.access == GroupChatAccess.PRIVATE
|
||||
val userIsAParticipant = groupChat.users.exists(u => u.id == user.intId)
|
||||
val userIsOwner = gcMessage.sender.id == user.intId
|
||||
|
||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||
if (userIsOwner) {
|
||||
val editedGCMessage = gcMessage.copy(message = msg.body.message)
|
||||
val updatedGroupChat = GroupChatApp.updateGroupChatMessage(liveMeeting.props.meetingProp.intId, groupChat, state.groupChats, editedGCMessage)
|
||||
|
||||
val event = buildGroupChatMessageEditedEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.chatId, editedGCMessage)
|
||||
bus.outGW.send(event)
|
||||
newState = state.update(updatedGroupChat)
|
||||
} else {
|
||||
val reason = "User doesn't have permission to edit chat message"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
} else {
|
||||
val reason = "User isn't a participant of the chat"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newState
|
||||
}
|
||||
}
|
@ -1,16 +1,11 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMessageType, GroupChatMsgFromUser, GroupChatMsgToUser, GroupChatUser }
|
||||
import org.bigbluebutton.core.db.ChatMessageDAO
|
||||
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMsgFromUser, GroupChatMsgToUser, GroupChatUser }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
object GroupChatApp {
|
||||
def getGroupChatOfUsers(userId: String, participantIds: Vector[String], state: MeetingState2x): Option[GroupChat] = {
|
||||
state.groupChats.findAllPrivateChatsForUser(userId)
|
||||
.find(groupChat => participantIds.forall(groupChat.users.map(u => u.id).contains))
|
||||
}
|
||||
|
||||
val MAIN_PUBLIC_CHAT = "MAIN-PUBLIC-GROUP-CHAT"
|
||||
|
||||
@ -20,10 +15,10 @@ object GroupChatApp {
|
||||
GroupChatFactory.create(gcId, access, createBy, users, msgs)
|
||||
}
|
||||
|
||||
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser, emphasizedText: Boolean): GroupChatMessage = {
|
||||
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser): GroupChatMessage = {
|
||||
val now = System.currentTimeMillis()
|
||||
val id = GroupChatFactory.genId()
|
||||
GroupChatMessage(id, now, msg.correlationId, now, now, sender, emphasizedText, msg.message, msg.replyToMessageId, msg.metadata)
|
||||
GroupChatMessage(id, now, msg.correlationId, now, now, sender, msg.chatEmphasizedText, msg.message)
|
||||
}
|
||||
|
||||
def toMessageToUser(msg: GroupChatMessage): GroupChatMsgToUser = {
|
||||
@ -31,32 +26,12 @@ object GroupChatApp {
|
||||
sender = msg.sender, chatEmphasizedText = msg.chatEmphasizedText, message = msg.message)
|
||||
}
|
||||
|
||||
def addGroupChatMessage(meetingId: String, chat: GroupChat, chats: GroupChats,
|
||||
msg: GroupChatMessage, messageType: String = GroupChatMessageType.DEFAULT): GroupChats = {
|
||||
if (msg.sender.id == SystemUser.ID) {
|
||||
ChatMessageDAO.insertSystemMsg(meetingId, chat.id, msg.message, messageType, Map(), msg.sender.name)
|
||||
} else {
|
||||
ChatMessageDAO.insert(meetingId, chat.id, msg, messageType)
|
||||
}
|
||||
|
||||
def addGroupChatMessage(chat: GroupChat, chats: GroupChats,
|
||||
msg: GroupChatMessage): GroupChats = {
|
||||
val c = chat.add(msg)
|
||||
chats.update(c)
|
||||
}
|
||||
|
||||
def updateGroupChatMessage(meetingId: String, chat: GroupChat, chats: GroupChats, msg: GroupChatMessage): GroupChats = {
|
||||
ChatMessageDAO.update(meetingId, chat.id, msg.id, msg.message)
|
||||
|
||||
val c = chat.update(msg)
|
||||
chats.update(c)
|
||||
}
|
||||
|
||||
def deleteGroupChatMessage(meetingId: String, chat: GroupChat, chats: GroupChats, msg: GroupChatMessage, deletedBy: String): GroupChats = {
|
||||
ChatMessageDAO.softDelete(meetingId, chat.id, msg.id, deletedBy)
|
||||
|
||||
val c = chat.delete(msg.id)
|
||||
chats.update(c)
|
||||
}
|
||||
|
||||
def findGroupChatUser(userId: String, users: Users2x): Option[GroupChatUser] = {
|
||||
Users2x.findWithIntId(users, userId) match {
|
||||
case Some(u) => Some(GroupChatUser(u.intId, u.name, u.role))
|
||||
@ -94,9 +69,9 @@ object GroupChatApp {
|
||||
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
|
||||
chat <- state.groupChats.find(chatId)
|
||||
} yield {
|
||||
val emphasizedText = sender.role == Roles.MODERATOR_ROLE
|
||||
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg, emphasizedText)
|
||||
val gcs1 = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm1)
|
||||
|
||||
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg)
|
||||
val gcs1 = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm1)
|
||||
state.update(gcs1)
|
||||
}
|
||||
|
||||
@ -107,9 +82,9 @@ object GroupChatApp {
|
||||
}
|
||||
|
||||
val sender = GroupChatUser(SystemUser.ID)
|
||||
val h1 = GroupChatMsgFromUser(correlationId = "cor1", sender = sender, message = "Hello Foo!", replyToMessageId = "")
|
||||
val h2 = GroupChatMsgFromUser(correlationId = "cor2", sender = sender, message = "Hello Bar!", replyToMessageId = "")
|
||||
val h3 = GroupChatMsgFromUser(correlationId = "cor3", sender = sender, message = "Hello Baz!", replyToMessageId = "")
|
||||
val h1 = GroupChatMsgFromUser(correlationId = "cor1", sender = sender, message = "Hello Foo!")
|
||||
val h2 = GroupChatMsgFromUser(correlationId = "cor2", sender = sender, message = "Hello Bar!")
|
||||
val h3 = GroupChatMsgFromUser(correlationId = "cor3", sender = sender, message = "Hello Baz!")
|
||||
val state1 = addH(state, SystemUser.ID, liveMeeting, h1)
|
||||
val state2 = addH(state1, SystemUser.ID, liveMeeting, h2)
|
||||
val state3 = addH(state2, SystemUser.ID, liveMeeting, h3)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
|
||||
class GroupChatHdlrs(implicit val context: ActorContext)
|
||||
extends CreateGroupChatReqMsgHdlr
|
||||
@ -9,13 +9,7 @@ class GroupChatHdlrs(implicit val context: ActorContext)
|
||||
with GetGroupChatMsgsReqMsgHdlr
|
||||
with GetGroupChatsReqMsgHdlr
|
||||
with SendGroupChatMessageMsgHdlr
|
||||
with EditGroupChatMessageReqMsgHdlr
|
||||
with DeleteGroupChatMessageReqMsgHdlr
|
||||
with SendGroupChatMessageReactionReqMsgHdlr
|
||||
with DeleteGroupChatMessageReactionReqMsgHdlr
|
||||
with SendGroupChatMessageFromApiSysPubMsgHdlr
|
||||
with SetGroupChatVisibleReqMsgHdlr
|
||||
with SetGroupChatLastSeenReqMsgHdlr {
|
||||
with SyncGetGroupChatsInfoMsgHdlr {
|
||||
|
||||
val log = Logging(context.system, getClass)
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.SystemUser
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait SendGroupChatMessageFromApiSysPubMsgHdlr extends HandlerHelpers {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handle(msg: SendGroupChatMessageFromApiSysPubMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
|
||||
log.debug("RECEIVED SendGroupChatMessageFromApiSysPubMsg {}", msg)
|
||||
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
if (!chatDisabled) {
|
||||
val newState = for {
|
||||
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
||||
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
||||
} yield {
|
||||
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.body.userName), msg.body.message, replyToMessageId = "")
|
||||
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.body.userName), groupChatMsgFromUser, emphasizedText = true)
|
||||
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.API)
|
||||
|
||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
msg.body.userName, GroupChatApp.MAIN_PUBLIC_CHAT, gcm
|
||||
)
|
||||
|
||||
bus.outGW.send(event)
|
||||
|
||||
state.update(gcs)
|
||||
}
|
||||
|
||||
newState match {
|
||||
case Some(ns) => ns
|
||||
case None => state
|
||||
}
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsBooleanOrElse
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
@ -16,27 +15,13 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
||||
def handle(msg: SendGroupChatMessageMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
|
||||
def determineMessageType(metadata: Map[String, Any]): String = {
|
||||
if (metadata.contains("pluginName")) {
|
||||
GroupChatMessageType.PLUGIN
|
||||
} else {
|
||||
GroupChatMessageType.DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
val replyChatMessageDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("replyChatMessage")
|
||||
var chatLocked: Boolean = false
|
||||
var chatLockedForUser: Boolean = false
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
||||
chatLockedForUser = true
|
||||
}
|
||||
|
||||
if (user.role != Roles.MODERATOR_ROLE && user.locked) {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||
@ -45,7 +30,7 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
||||
case None => false
|
||||
})
|
||||
// don't lock private chats that involve a moderator
|
||||
if (modMembers.isEmpty) {
|
||||
if (modMembers.length == 0) {
|
||||
chatLocked = permissions.disablePrivChat
|
||||
}
|
||||
} else {
|
||||
@ -54,50 +39,32 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
||||
if (!chatDisabled && !(applyPermissionCheck && chatLocked)) {
|
||||
val newState = for {
|
||||
sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||
chat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
val chatIsPrivate = chat.access == GroupChatAccess.PRIVATE;
|
||||
val userIsAParticipant = chat.users.exists(u => u.id == sender.id);
|
||||
val userIsAParticipant = chat.users.filter(u => u.id == sender.id).length > 0;
|
||||
|
||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||
val moderatorChatEmphasizedEnabled = getConfigPropertyValueByPathAsBooleanOrElse(
|
||||
liveMeeting.clientSettings,
|
||||
"public.chat.moderatorChatEmphasized",
|
||||
alternativeValue = true
|
||||
)
|
||||
|
||||
val emphasizedText = moderatorChatEmphasizedEnabled &&
|
||||
!chatIsPrivate &&
|
||||
sender.role == Roles.MODERATOR_ROLE
|
||||
|
||||
val messageType = determineMessageType(msg.body.msg.metadata)
|
||||
val groupChatMsgReceived = {
|
||||
if (replyChatMessageDisabled && msg.body.msg.replyToMessageId != "") {
|
||||
msg.body.msg.copy(replyToMessageId = "")
|
||||
} else {
|
||||
msg.body.msg
|
||||
}
|
||||
}
|
||||
|
||||
val gcMessage = GroupChatApp.toGroupChatMessage(sender, groupChatMsgReceived, emphasizedText)
|
||||
val updatedGroupChat = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcMessage, messageType)
|
||||
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg)
|
||||
val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm)
|
||||
|
||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
msg.header.userId, msg.body.chatId, gcMessage
|
||||
msg.header.userId, msg.body.chatId, gcm
|
||||
)
|
||||
|
||||
bus.outGW.send(event)
|
||||
|
||||
state.update(updatedGroupChat)
|
||||
state.update(gcs)
|
||||
} else {
|
||||
val reason = "User isn't a participant of the chat"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
state
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
newState match {
|
||||
|
@ -1,66 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.PermissionCheck
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ChatMessageReactionDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait SendGroupChatMessageReactionReqMsgHdlr extends HandlerHelpers {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handle(msg: SendGroupChatMessageReactionReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||
val chatMessageReactionsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chatMessageReactions")
|
||||
var chatLocked: Boolean = false
|
||||
var chatLockedForUser: Boolean = false
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||
} yield {
|
||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
||||
chatLockedForUser = true
|
||||
}
|
||||
|
||||
val userIsModerator = user.role == Roles.MODERATOR_ROLE
|
||||
|
||||
if (!userIsModerator && user.locked) {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||
val modMembers = groupChat.users.filter(cu => Users2x.findWithIntId(liveMeeting.users2x, cu.id) match {
|
||||
case Some(user) => user.role == Roles.MODERATOR_ROLE
|
||||
case None => false
|
||||
})
|
||||
// don't lock private chats that involve a moderator
|
||||
if (modMembers.isEmpty) {
|
||||
chatLocked = permissions.disablePrivChat
|
||||
}
|
||||
} else {
|
||||
chatLocked = permissions.disablePubChat
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatDisabled && !chatMessageReactionsDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
||||
for {
|
||||
gcMessage <- groupChat.msgs.find(gcm => gcm.id == msg.body.messageId)
|
||||
} yield {
|
||||
val chatIsPrivate = groupChat.access == GroupChatAccess.PRIVATE
|
||||
val userIsAParticipant = groupChat.users.exists(u => u.id == user.intId)
|
||||
|
||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||
val event = buildGroupChatMessageReactionSentEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.chatId, gcMessage.id, msg.body.reactionEmoji, msg.body.reactionEmojiId)
|
||||
bus.outGW.send(event)
|
||||
ChatMessageReactionDAO.insert(liveMeeting.props.meetingProp.intId, gcMessage.id, msg.header.userId, msg.body.reactionEmoji, msg.body.reactionEmojiId)
|
||||
} else {
|
||||
val reason = "User isn't a participant of the chat"
|
||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.db.ChatUserDAO
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import java.sql.Timestamp
|
||||
import java.time.Instant
|
||||
|
||||
trait SetGroupChatLastSeenReqMsgHdlr {
|
||||
def handle(msg: SetGroupChatLastSeenReqMsg, liveMeeting: LiveMeeting): Unit = {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
} yield {
|
||||
val lastSeenAtInstant = Instant.parse(msg.body.lastSeenAt)
|
||||
val lastSeenAtTimestamp = Timestamp.from(lastSeenAtInstant)
|
||||
|
||||
ChatUserDAO.updateChatLastSeen(liveMeeting.props.meetingProp.intId, msg.body.chatId, user.intId, lastSeenAtTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.ChatUserDAO
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
||||
|
||||
trait SetGroupChatVisibleReqMsgHdlr {
|
||||
def handle(msg: SetGroupChatVisibleReqMsg, liveMeeting: LiveMeeting): Unit = {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
} yield {
|
||||
ChatUserDAO.updateChatVisible(liveMeeting.props.meetingProp.intId, msg.body.chatId, user.intId, msg.body.visible)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package org.bigbluebutton.core.apps.groupchats
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait SyncGetGroupChatsInfoMsgHdlr {
|
||||
this: GroupChatHdlrs =>
|
||||
|
||||
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
|
||||
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
|
||||
val envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
|
||||
val body = SyncGetGroupChatsRespMsgBody(allChats)
|
||||
val event = SyncGetGroupChatsRespMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
|
||||
val envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
|
||||
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs)
|
||||
val event = SyncGetGroupChatMsgsRespMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
// fetching all the group chats in the meeting
|
||||
val chats = GroupChatApp.getAllGroupChatsInMeeting(state)
|
||||
|
||||
// mapping group chats, while fetching and publishing messages for each group chat
|
||||
val allChats = chats map (pc => {
|
||||
|
||||
val msgs = pc.msgs.toVector map (m => GroupChatMsgToUser(m.id, m.createdOn, m.correlationId,
|
||||
m.sender, m.chatEmphasizedText, m.message))
|
||||
val respMsg = buildSyncGetGroupChatMsgsRespMsg(msgs, pc.id)
|
||||
bus.outGW.send(respMsg)
|
||||
|
||||
GroupChatInfo(pc.id, pc.access, pc.createdBy, pc.users)
|
||||
})
|
||||
|
||||
// publishing a message with the group chat info
|
||||
val respMsg = buildSyncGetGroupChatsRespMsg(allChats)
|
||||
bus.outGW.send(respMsg)
|
||||
|
||||
state
|
||||
}
|
||||
}
|
@ -5,8 +5,7 @@ import org.bigbluebutton.core.models.{ Layouts, LayoutsType }
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.{ LayoutDAO, NotificationDAO }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
|
||||
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
||||
this: LayoutApp2x =>
|
||||
@ -36,7 +35,6 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
||||
Layouts.setPresentationVideoRate(liveMeeting.layouts, msg.body.presentationVideoRate)
|
||||
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
||||
|
||||
LayoutDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, liveMeeting.layouts)
|
||||
sendBroadcastLayoutEvtMsg(msg.header.userId)
|
||||
}
|
||||
}
|
||||
@ -73,7 +71,6 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.bigbluebutton.core.apps.layout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.models.Layouts
|
||||
import org.bigbluebutton.core.models.{ Layouts }
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
||||
import org.bigbluebutton.core.db.LayoutDAO
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
|
||||
trait BroadcastPushLayoutMsgHdlr extends RightsManagementTrait {
|
||||
this: LayoutApp2x =>
|
||||
@ -21,7 +20,6 @@ trait BroadcastPushLayoutMsgHdlr extends RightsManagementTrait {
|
||||
Layouts.setPushLayout(liveMeeting.layouts, msg.body.pushLayout)
|
||||
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
||||
|
||||
LayoutDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, liveMeeting.layouts)
|
||||
sendBroadcastPushLayoutEvtMsg(msg.header.userId)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package org.bigbluebutton.core.apps.meeting
|
||||
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
|
||||
trait SyncGetMeetingInfoRespMsgHdlr {
|
||||
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSyncGetMeetingInfoRespMsg(props: DefaultProps): Unit = {
|
||||
val routing = Routing.addMsgToHtml5InstanceIdRouting(props.meetingProp.intId, props.systemProps.html5InstanceId.toString)
|
||||
val envelope = BbbCoreEnvelope(SyncGetMeetingInfoRespMsg.NAME, routing)
|
||||
val header = BbbCoreBaseHeader(SyncGetMeetingInfoRespMsg.NAME)
|
||||
|
||||
val body = SyncGetMeetingInfoRespMsgBody(props)
|
||||
val event = SyncGetMeetingInfoRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.bigbluebutton.core.apps.meeting
|
||||
|
||||
import org.bigbluebutton.common2.msgs.ValidateConnAuthTokenSysMsg
|
||||
import org.bigbluebutton.core.models.RegisteredUsers
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait ValidateConnAuthTokenSysMsgHdlr {
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleValidateConnAuthTokenSysMsg(msg: ValidateConnAuthTokenSysMsg): Unit = {
|
||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(
|
||||
msg.body.authToken,
|
||||
msg.body.userId,
|
||||
liveMeeting.registeredUsers
|
||||
)
|
||||
|
||||
regUser match {
|
||||
case Some(u) =>
|
||||
val event = MsgBuilder.buildValidateConnAuthTokenSysRespMsg(msg.body.meetingId, msg.body.userId,
|
||||
true, msg.body.connId, msg.body.app)
|
||||
outGW.send(event)
|
||||
case None =>
|
||||
val event = MsgBuilder.buildValidateConnAuthTokenSysRespMsg(msg.body.meetingId, msg.body.userId,
|
||||
false, msg.body.connId, msg.body.app)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.{ SharedNotesRevDAO }
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -10,6 +9,7 @@ trait PadContentSysMsgHdlr {
|
||||
this: PadsApp2x =>
|
||||
|
||||
def handle(msg: PadContentSysMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastEvent(externalId: String, padId: String, rev: String, start: Int, end: Int, text: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadContentEvtMsg.NAME, routing)
|
||||
@ -22,18 +22,8 @@ trait PadContentSysMsgHdlr {
|
||||
}
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
SharedNotesRevDAO.update(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
group.externalId,
|
||||
msg.body.rev.toInt,
|
||||
msg.body.start,
|
||||
msg.body.end,
|
||||
msg.body.text
|
||||
)
|
||||
broadcastEvent(group.externalId, msg.body.padId, msg.body.rev, msg.body.start, msg.body.end, msg.body.text)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, msg.body.padId, msg.body.rev, msg.body.start, msg.body.end, msg.body.text)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait PadCreateGroupReqMsgHdlr {
|
||||
this: PadsApp2x =>
|
||||
|
||||
def handle(msg: PadCreateGroupReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastEvent(externalId: String, model: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, liveMeeting.props.meetingProp.intId)
|
||||
val body = PadCreateGroupCmdMsgBody(externalId, model)
|
||||
val event = PadCreateGroupCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
val padEnabled = msg.body.model match {
|
||||
case "notes" => !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes")
|
||||
case "captions" => !liveMeeting.props.meetingProp.disabledFeatures.contains("captions")
|
||||
case _ => false
|
||||
}
|
||||
|
||||
if (padEnabled && !Pads.hasGroup(liveMeeting.pads, msg.body.externalId)) {
|
||||
Pads.addGroup(liveMeeting.pads, msg.body.externalId, msg.body.model, msg.body.name, msg.header.userId)
|
||||
broadcastEvent(msg.body.externalId, msg.body.model)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.SharedNotesDAO
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -23,12 +22,8 @@ trait PadCreatedEvtMsgHdlr {
|
||||
}
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
Pads.setPadId(liveMeeting.pads, group.externalId, msg.body.padId)
|
||||
SharedNotesDAO.insert(liveMeeting.props.meetingProp.intId, group, msg.body.padId, msg.body.name)
|
||||
broadcastEvent(group.externalId, group.userId, msg.body.padId, msg.body.name)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, group.userId, msg.body.padId, msg.body.name)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,22 @@ trait PadGroupCreatedEvtMsgHdlr {
|
||||
this: PadsApp2x =>
|
||||
|
||||
def handle(msg: PadGroupCreatedEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastEvent(externalId: String, model: String, name: String, userId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
|
||||
val envelope = BbbCoreEnvelope(PadGroupCreatedRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PadGroupCreatedRespMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
|
||||
val body = PadGroupCreatedRespMsgBody(externalId, model, name)
|
||||
val event = PadGroupCreatedRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||
case Some(group) => {
|
||||
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId)
|
||||
|
||||
//Group was created, now request to create the pad
|
||||
PadslHdlrHelpers.broadcastPadCreateCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.groupId, msg.body.externalId)
|
||||
broadcastEvent(msg.body.externalId, group.model, group.name, group.userId)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.CaptionDAO
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -41,7 +40,6 @@ trait PadPatchSysMsgHdlr {
|
||||
broadcastEditCaptionHistoryEvent(msg.body.userId, msg.body.start, msg.body.end, group.name, locale, msg.body.text)
|
||||
val tail = liveMeeting.captionModel.getTextTail(group.name)
|
||||
broadcastPadTailEvent(group.externalId, tail)
|
||||
CaptionDAO.insertOrUpdatePadCaption(liveMeeting.props.meetingProp.intId, locale, msg.body.userId, tail)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.SharedNotesDAO
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -30,11 +29,8 @@ trait PadPinnedReqMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
} else {
|
||||
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||
case Some(group) => {
|
||||
SharedNotesDAO.updatePinned(liveMeeting.props.meetingProp.intId, msg.body.externalId, msg.body.pinned)
|
||||
broadcastEvent(group.externalId, msg.body.pinned)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, msg.body.pinned)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.SharedNotesSessionDAO
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -23,11 +22,8 @@ trait PadSessionCreatedEvtMsgHdlr {
|
||||
}
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
SharedNotesSessionDAO.insert(liveMeeting.props.meetingProp.intId, group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.SharedNotesSessionDAO
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -23,11 +22,8 @@ trait PadSessionDeletedSysMsgHdlr {
|
||||
}
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
SharedNotesSessionDAO.delete(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.sessionId)
|
||||
broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.{ SharedNotesRevDAO }
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
@ -23,12 +22,8 @@ trait PadUpdatedSysMsgHdlr {
|
||||
}
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
Pads.setRev(liveMeeting.pads, group.externalId, msg.body.rev)
|
||||
SharedNotesRevDAO.insert(liveMeeting.props.meetingProp.intId, group.externalId, msg.body.rev, msg.body.userId, msg.body.changeset)
|
||||
broadcastEvent(group.externalId, msg.body.padId, msg.body.userId, msg.body.rev, msg.body.changeset)
|
||||
}
|
||||
case _ =>
|
||||
case Some(group) => broadcastEvent(group.externalId, msg.body.padId, msg.body.userId, msg.body.rev, msg.body.changeset)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import akka.actor.ActorContext
|
||||
|
||||
class PadsApp2x(implicit val context: ActorContext)
|
||||
extends PadGroupCreatedEvtMsgHdlr
|
||||
extends PadCreateGroupReqMsgHdlr
|
||||
with PadGroupCreatedEvtMsgHdlr
|
||||
with PadCreateReqMsgHdlr
|
||||
with PadCreatedEvtMsgHdlr
|
||||
with PadCreateSessionReqMsgHdlr
|
||||
|
@ -1,30 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, PadCreateCmdMsg, PadCreateCmdMsgBody, PadCreateGroupCmdMsg, PadCreateGroupCmdMsgBody }
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
|
||||
object PadslHdlrHelpers {
|
||||
|
||||
def broadcastPadCreateGroupCmdMsg(outGW: OutMsgRouter, meetingId: String, externalId: String, model: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, meetingId)
|
||||
val body = PadCreateGroupCmdMsgBody(externalId, model)
|
||||
val event = PadCreateGroupCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastPadCreateCmdMsg(outGW: OutMsgRouter, meetingId: String, groupId: String, name: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateCmdMsg.NAME, meetingId)
|
||||
val body = PadCreateCmdMsgBody(groupId, name)
|
||||
val event = PadCreateCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsg
|
||||
import org.bigbluebutton.core.apps.plugin.PluginHdlrHelpers.{ checkPermission, dataChannelCheckingLogic, defaultCreatorCheck }
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait PluginDataChannelDeleteEntryMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelDeleteEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
dataChannelCheckingLogic(liveMeeting, msg.header.userId, msg.body.pluginName, msg.body.channelName, (user, dc, meetingId) => {
|
||||
val hasPermission = checkPermission(user, dc.replaceOrDeletePermission, defaultCreatorCheck(
|
||||
meetingId, msg.body, msg.header.userId
|
||||
))
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelEntryDAO.delete(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.body.entryId
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelPushEntryMsg
|
||||
import org.bigbluebutton.core.apps.plugin.PluginHdlrHelpers.{ checkPermission, dataChannelCheckingLogic, defaultCreatorCheck }
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait PluginDataChannelPushEntryMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelPushEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
dataChannelCheckingLogic(liveMeeting, msg.header.userId, msg.body.pluginName, msg.body.channelName, (user, dc, meetingId) => {
|
||||
val hasPermission = checkPermission(user, dc.pushPermission)
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelEntryDAO.insert(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.header.userId,
|
||||
msg.body.payloadJson,
|
||||
msg.body.toRoles,
|
||||
msg.body.toUserIds
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelReplaceEntryMsg
|
||||
import org.bigbluebutton.core.apps.plugin.PluginHdlrHelpers.{checkPermission, dataChannelCheckingLogic, defaultCreatorCheck}
|
||||
import org.bigbluebutton.core.db.{JsonUtils, PluginDataChannelEntryDAO}
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{HandlerHelpers, LiveMeeting}
|
||||
|
||||
trait PluginDataChannelReplaceEntryMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelReplaceEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
|
||||
dataChannelCheckingLogic(liveMeeting, msg.header.userId, msg.body.pluginName, msg.body.channelName, (user, dc, meetingId) => {
|
||||
val hasPermission = checkPermission(user, dc.replaceOrDeletePermission, defaultCreatorCheck(
|
||||
meetingId, msg.body, msg.header.userId))
|
||||
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelEntryDAO.replace(
|
||||
msg.header.meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.body.entryId,
|
||||
JsonUtils.mapToJson(msg.body.payloadJson),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg
|
||||
import org.bigbluebutton.core.apps.plugin.PluginHdlrHelpers.{ checkPermission, dataChannelCheckingLogic }
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelResetMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
dataChannelCheckingLogic(liveMeeting, msg.header.userId, msg.body.pluginName, msg.body.channelName, (user, dc, meetingId) => {
|
||||
val hasPermission = checkPermission(user, dc.replaceOrDeletePermission)
|
||||
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelEntryDAO.reset(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelReplaceOrDeleteBaseBody
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.models.{ DataChannel, PluginModel, Roles, UserState, Users2x }
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
object PluginHdlrHelpers {
|
||||
def checkPermission(user: UserState, permissionType: List[String], creatorCheck: => Boolean = false): List[Boolean] = {
|
||||
permissionType.map(_.toLowerCase).map {
|
||||
case "all" => true
|
||||
case "moderator" => user.role == Roles.MODERATOR_ROLE
|
||||
case "presenter" => user.presenter
|
||||
case "creator" => creatorCheck
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
def defaultCreatorCheck[T <: PluginDataChannelReplaceOrDeleteBaseBody](meetingId: String, msgBody: T, userId: String): Boolean = {
|
||||
val creatorUserId = PluginDataChannelEntryDAO.getEntryCreator(
|
||||
meetingId,
|
||||
msgBody.pluginName,
|
||||
msgBody.channelName,
|
||||
msgBody.subChannelName,
|
||||
msgBody.entryId
|
||||
)
|
||||
creatorUserId == userId
|
||||
}
|
||||
|
||||
def dataChannelCheckingLogic(liveMeeting: LiveMeeting, userId: String,
|
||||
pluginName: String, channelName: String,
|
||||
caseSomeDataChannelAndPlugin: (UserState, DataChannel, String) => Unit): Option[Unit] = {
|
||||
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
for {
|
||||
_ <- if (!pluginsDisabled) Some(()) else None
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
||||
} yield {
|
||||
PluginModel.getPluginByName(liveMeeting.plugins, pluginName) match {
|
||||
case Some(p) =>
|
||||
p.manifest.content.dataChannels.getOrElse(List()).find(dc => dc.name == channelName) match {
|
||||
case Some(dc) =>
|
||||
caseSomeDataChannelAndPlugin(user, dc, meetingId)
|
||||
case None => println(s"Data channel '${channelName}' not found in plugin '${pluginName}'.")
|
||||
}
|
||||
case None => println(s"Plugin '${pluginName}' not found.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsgBody
|
||||
|
||||
class PluginHdlrs(implicit val context: ActorContext)
|
||||
extends PluginDataChannelPushEntryMsgHdlr
|
||||
with PluginDataChannelReplaceEntryMsgHdlr
|
||||
with PluginDataChannelDeleteEntryMsgHdlr
|
||||
with PluginDataChannelResetMsgHdlr {
|
||||
|
||||
val log = Logging(context.system, getClass)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.polls
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
|
||||
class PollApp2x(implicit val context: ActorContext)
|
||||
extends GetCurrentPollReqMsgHdlr
|
||||
|
@ -1,56 +0,0 @@
|
||||
package org.bigbluebutton.core.apps.polls
|
||||
|
||||
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, PollUpdatedEvtMsg, PollUpdatedEvtMsgBody, Routing, UserRespondedToPollRecordMsg, UserRespondedToPollRecordMsgBody, UserRespondedToPollRespMsg, UserRespondedToPollRespMsgBody, UserRespondedToTypedPollRespMsg, UserRespondedToTypedPollRespMsgBody }
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
|
||||
object PollHdlrHelpers {
|
||||
|
||||
def broadcastPollUpdatedEvent(outGW: OutMsgRouter, meetingId: String, userId: String, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, meetingId, userId)
|
||||
|
||||
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
||||
val event = PollUpdatedEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToTypedPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||
pollId: String, answer: String, sendToId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, meetingId, sendToId)
|
||||
|
||||
val body = UserRespondedToTypedPollRespMsgBody(pollId, userId, answer)
|
||||
val event = UserRespondedToTypedPollRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToPollRecordMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||
pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, meetingId, userId)
|
||||
|
||||
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
|
||||
val event = UserRespondedToPollRecordMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||
pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, meetingId, sendToId)
|
||||
|
||||
val body = UserRespondedToPollRespMsgBody(pollId, userId, answerIds)
|
||||
val event = UserRespondedToPollRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@ import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.models.Polls
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
|
||||
trait RespondToPollReqMsgHdlr {
|
||||
@ -12,12 +12,45 @@ trait RespondToPollReqMsgHdlr {
|
||||
|
||||
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
if (!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
|
||||
def broadcastPollUpdatedEvent(msg: RespondToPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
||||
val event = PollUpdatedEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
|
||||
val event = UserRespondedToPollRecordMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
|
||||
|
||||
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerIds)
|
||||
val event = UserRespondedToPollRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
if (Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
|
||||
for {
|
||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||
msg.body.questionId, msg.body.answerIds, liveMeeting)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
||||
for {
|
||||
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
||||
} yield {
|
||||
@ -25,14 +58,14 @@ trait RespondToPollReqMsgHdlr {
|
||||
answerId <- msg.body.answerIds
|
||||
} yield {
|
||||
val answerText = poll.questions(0).answers.get(answerId).key
|
||||
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
|
||||
broadcastUserRespondedToPollRecordMsg(msg, pollId, answerId, answerText, poll.isSecret)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, msg.body.answerIds, presenter.intId)
|
||||
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerIds, presenter.intId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1,11 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.polls
|
||||
|
||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsIntOrElse
|
||||
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.models.Polls
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
|
||||
trait RespondToTypedPollReqMsgHdlr {
|
||||
@ -13,60 +12,43 @@ trait RespondToTypedPollReqMsgHdlr {
|
||||
|
||||
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastPollUpdatedEvent(msg: RespondToTypedPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
||||
val event = PollUpdatedEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastUserRespondedToTypedPollRespMsg(msg: RespondToTypedPollReqMsg, pollId: String, answer: String, sendToId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
|
||||
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
|
||||
|
||||
val body = UserRespondedToTypedPollRespMsgBody(pollId, msg.header.userId, answer)
|
||||
val event = UserRespondedToTypedPollRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
if (Polls.isResponsePollType(msg.body.pollId, liveMeeting.polls) &&
|
||||
!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) &&
|
||||
!Polls.hasUserAlreadyAddedTypedAnswer(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
|
||||
Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false &&
|
||||
Polls.checkUserAddedQuestion(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
|
||||
for {
|
||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||
msg.body.questionId, msg.body.answer, liveMeeting)
|
||||
} yield {
|
||||
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
||||
|
||||
//Truncate answer case it is longer than `maxTypedAnswerLength`
|
||||
val maxTypedAnswerLength = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.poll.maxTypedAnswerLength", 45)
|
||||
val answer = msg.body.answer.substring(0, Math.min(msg.body.answer.length, maxTypedAnswerLength))
|
||||
|
||||
val answerExists = Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls)
|
||||
|
||||
//Create answer if it doesn't exist
|
||||
answerExists match {
|
||||
case None => {
|
||||
for {
|
||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||
msg.body.questionId, answer, liveMeeting)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||
|
||||
for {
|
||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastUserRespondedToTypedPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answer, presenter.intId)
|
||||
}
|
||||
}
|
||||
for {
|
||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||
} yield {
|
||||
broadcastUserRespondedToTypedPollRespMsg(msg, pollId, msg.body.answer, presenter.intId)
|
||||
}
|
||||
case _ => //Do nothing, answer with same text exists already
|
||||
}
|
||||
|
||||
//Submit the answer
|
||||
Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls) match {
|
||||
case Some(answerId) => {
|
||||
for {
|
||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||
msg.body.questionId, Seq(answerId), liveMeeting)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||
for {
|
||||
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
||||
} yield {
|
||||
val answerText = poll.questions(0).answers.get(answerId).key
|
||||
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
|
||||
}
|
||||
|
||||
for {
|
||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||
} yield {
|
||||
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, Seq(answerId), presenter.intId)
|
||||
}
|
||||
}
|
||||
}
|
||||
case None => log.error("Error while trying to answer the poll {} in meeting {}: Answer not found or something went wrong while trying to create the answer.", msg.body.pollId, msg.header.meetingId)
|
||||
}
|
||||
|
||||
} else {
|
||||
log.info("Ignoring typed answer from user {} once user already added an answer to this poll {} in meeting {}", msg.header.userId, msg.body.pollId, msg.header.meetingId)
|
||||
}
|
||||
|
@ -2,15 +2,12 @@ package org.bigbluebutton.core.apps.polls
|
||||
|
||||
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.Polls
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
||||
import org.bigbluebutton.core.db.{ChatMessageDAO, JsonUtils, NotificationDAO}
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import spray.json.DefaultJsonProtocol.jsonFormat2
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||
|
||||
trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
||||
this: PollApp2x =>
|
||||
@ -37,7 +34,6 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
||||
Vector()
|
||||
)
|
||||
bus.outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
|
||||
// SendWhiteboardAnnotationPubMsg
|
||||
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
@ -58,27 +54,6 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
||||
for {
|
||||
(result, annotationProp) <- Polls.handleShowPollResultReqMsg(state, msg.header.userId, msg.body.pollId, liveMeeting)
|
||||
} yield {
|
||||
//it will be used to render the chat message (will be stored as json in chat-msg metadata)
|
||||
val resultAsSimpleMap = Map(
|
||||
"id" -> result.id,
|
||||
"questionType" -> result.questionType,
|
||||
"questionText" -> result.questionText.getOrElse(""),
|
||||
"answers" -> {
|
||||
for {
|
||||
answer <- result.answers
|
||||
} yield {
|
||||
Map(
|
||||
"id" -> answer.id,
|
||||
"key" -> answer.key,
|
||||
"numVotes" -> answer.numVotes
|
||||
)
|
||||
}
|
||||
},
|
||||
"numRespondents" -> result.numRespondents,
|
||||
"numResponders" -> result.numResponders,
|
||||
)
|
||||
|
||||
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.POLL, resultAsSimpleMap, "")
|
||||
broadcastEvent(msg, result, annotationProp)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.presentation
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.core.apps.Presentation
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.presentationpod
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg }
|
||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, CaptureSharedNotesReqInternalMsg }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||
@ -45,20 +44,19 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
||||
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
|
||||
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
|
||||
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType, "")
|
||||
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType)
|
||||
|
||||
NewPresFileAvailableMsg(header, body)
|
||||
}
|
||||
|
||||
def buildBroadcastNewPresFileAvailable(newPresFileAvailableMsg: NewPresFileAvailableMsg, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(NewPresFileAvailableEvtMsg.NAME, routing)
|
||||
val envelope = BbbCoreEnvelope(PresentationPageConvertedEventMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(NewPresFileAvailableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
|
||||
val body = NewPresFileAvailableEvtMsgBody(
|
||||
annotatedFileURI = newPresFileAvailableMsg.body.annotatedFileURI,
|
||||
originalFileURI = newPresFileAvailableMsg.body.originalFileURI,
|
||||
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI,
|
||||
presId = newPresFileAvailableMsg.body.presId,
|
||||
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI, presId = newPresFileAvailableMsg.body.presId,
|
||||
fileStateType = newPresFileAvailableMsg.body.fileStateType
|
||||
)
|
||||
val event = NewPresFileAvailableEvtMsg(header, body)
|
||||
@ -85,11 +83,11 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildPresentationUploadTokenSysPubMsg(parentMeetingId: String, userId: String, presentationUploadToken: String, filename: String, presId: String): BbbCommonEnvCoreMsg = {
|
||||
def buildPresentationUploadTokenSysPubMsg(parentId: String, userId: String, presentationUploadToken: String, filename: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
|
||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
|
||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentId, userId)
|
||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentId)
|
||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
@ -103,11 +101,13 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
val whiteboardId = s"${presId}/${pageNumber.toString}"
|
||||
val presentationPage: PresentationPage = currentPres.get.pages(whiteboardId)
|
||||
val width: Double = presentationPage.width
|
||||
val height: Double = presentationPage.height
|
||||
val xOffset: Double = presentationPage.xOffset
|
||||
val yOffset: Double = presentationPage.yOffset
|
||||
val widthRatio: Double = presentationPage.widthRatio
|
||||
val heightRatio: Double = presentationPage.heightRatio
|
||||
val whiteboardHistory: Array[AnnotationVO] = liveMeeting.wbModel.getHistory(whiteboardId)
|
||||
|
||||
val page = new PresentationPageForExport(pageNumber, width, height, whiteboardHistory)
|
||||
val page = new PresentationPageForExport(pageNumber, xOffset, yOffset, widthRatio, heightRatio, whiteboardHistory)
|
||||
getPresentationPagesForExport(pages, pageCount, presId, currentPres, liveMeeting, storeAnnotationPages :+ page)
|
||||
} else {
|
||||
getPresentationPagesForExport(pages, pageCount, presId, currentPres, liveMeeting, storeAnnotationPages)
|
||||
@ -157,7 +157,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
val pages: List[Int] = m.body.pages // Desired presentation pages for export
|
||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
|
||||
|
||||
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, currentPres.get.name, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
|
||||
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
|
||||
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||
|
||||
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
|
||||
@ -203,22 +203,15 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
val filename = m.filename
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
val presentationId = PresentationPodsApp.generatePresentationId(m.filename)
|
||||
|
||||
// Informs bbb-web about the token so that when we use it to upload the presentation, it is able to look it up in the list of tokens
|
||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(parentMeetingId, userId, presentationUploadToken, filename, presentationId))
|
||||
|
||||
var pres = new PresentationInPod(presentationId, default = false, current = false, name = filename,
|
||||
pages = Map.empty, downloadable = false, downloadFileExtension = "", removable = true, filenameConverted = filename,
|
||||
uploadCompleted = false, numPages = 0, errorMsgKey = "", errorDetails = Map.empty)
|
||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(parentMeetingId, userId, presentationUploadToken, filename))
|
||||
|
||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("importPresentationWithAnnotationsFromBreakoutRooms")) {
|
||||
log.error(s"Capturing breakout rooms slides disabled in meeting ${meetingId}.")
|
||||
} else if (currentPres.isEmpty) {
|
||||
log.error(s"No presentation set in meeting ${meetingId}")
|
||||
pres = pres.copy(errorMsgKey = "204")
|
||||
bus.outGW.send(buildBroadcastPresentationConversionUpdateEvtMsg(parentMeetingId, "204", jobId, filename, presentationUploadToken))
|
||||
PresPresentationDAO.updateConversionStarted(parentMeetingId, pres)
|
||||
} else {
|
||||
val allPages: Boolean = m.allPages
|
||||
val pageCount = currentPres.get.pages.size
|
||||
@ -229,7 +222,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
|
||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
|
||||
|
||||
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
|
||||
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
|
||||
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||
|
||||
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
|
||||
@ -243,13 +236,9 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
val annotations = new StoredAnnotations(jobId, presId, storeAnnotationPages)
|
||||
bus.outGW.send(buildStoreAnnotationsInRedisSysMsg(annotations, liveMeeting))
|
||||
} else {
|
||||
pres = pres.copy(errorMsgKey = "204")
|
||||
|
||||
// Notify that no content is available to capture
|
||||
bus.outGW.send(buildBroadcastPresentationConversionUpdateEvtMsg(parentMeetingId, "204", jobId, filename, presentationUploadToken))
|
||||
}
|
||||
|
||||
PresPresentationDAO.updateConversionStarted(parentMeetingId, pres)
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,25 +247,36 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
"Received NewPresFileAvailableMsg meetingId={} presId={}",
|
||||
liveMeeting.props.meetingProp.intId, m.body.presId
|
||||
)
|
||||
|
||||
if (m.body.fileStateType == "Annotated") {
|
||||
val presentationDownloadInfo = Map(
|
||||
"fileURI" -> m.body.annotatedFileURI,
|
||||
"filename" -> m.body.fileName
|
||||
)
|
||||
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "")
|
||||
} else if (m.body.fileStateType == "Converted") {
|
||||
PresPresentationDAO.updateDownloadUri(m.body.presId, m.body.convertedFileURI)
|
||||
} else if (m.body.fileStateType == "Original") {
|
||||
PresPresentationDAO.updateDownloadUri(m.body.presId, m.body.originalFileURI)
|
||||
}
|
||||
|
||||
PresPresentationDAO.updateExportToChatStatus(m.body.presId, "EXPORTED")
|
||||
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting))
|
||||
}
|
||||
|
||||
def handle(m: CaptureSharedNotesReqInternalMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
val parentMeetingId = liveMeeting.props.meetingProp.intId
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, parentMeetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(PresentationPageConversionStartedEventMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(CaptureSharedNotesReqEvtMsg.NAME, parentMeetingId, "not-used")
|
||||
val body = CaptureSharedNotesReqEvtMsgBody(m.breakoutId, m.filename)
|
||||
val event = CaptureSharedNotesReqEvtMsg(header, body)
|
||||
|
||||
bus.outGW.send(BbbCommonEnvCoreMsg(envelope, event))
|
||||
}
|
||||
|
||||
def handle(m: PresAnnStatusMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
PresPresentationDAO.updateExportToChat(m.body.presId, m.body.status, m.body.pageNumber, m.body.error)
|
||||
bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting))
|
||||
}
|
||||
|
||||
def handle(m: PadCapturePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
val userId: String = "system"
|
||||
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||
val filename = m.body.filename
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
|
||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename))
|
||||
|
||||
val exportJob = new ExportJob(jobId, JobTypes.CAPTURE_NOTES, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||
|
||||
bus.outGW.send(job)
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package org.bigbluebutton.core.apps.presentationpod
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.PresPresentationDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.PresentationInPod
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
||||
@ -32,30 +30,7 @@ trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
val errorDetails = scala.collection.immutable.Map(
|
||||
"bigPageNumber" -> msg.body.bigPageNumber.toString,
|
||||
"bigPageSize" -> msg.body.bigPageSize.toString
|
||||
)
|
||||
|
||||
val newState = for {
|
||||
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
||||
pres <- pod.getPresentation(msg.body.presentationId)
|
||||
} yield {
|
||||
val presWithError = PresentationInPod(pres.id, pres.name, pres.default, pres.current, pres.pages, pres.downloadable,
|
||||
pres.downloadFileExtension, pres.removable, pres.filenameConverted, pres.uploadCompleted, pres.numPages,
|
||||
msg.body.messageKey, errorDetails)
|
||||
var pods = state.presentationPodManager.addPod(pod)
|
||||
pods = pods.addPresentationToPod(pod.id, presWithError)
|
||||
|
||||
state.update(pods)
|
||||
}
|
||||
|
||||
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
||||
broadcastEvent(msg)
|
||||
|
||||
newState match {
|
||||
case Some(ns) => ns
|
||||
case None => state
|
||||
}
|
||||
state
|
||||
}
|
||||
}
|
||||
|
@ -2,19 +2,17 @@ package org.bigbluebutton.core.apps.presentationpod
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.PresPresentationDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.PresentationInPod
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||
|
||||
this: PresentationPodHdlrs =>
|
||||
|
||||
def handle(
|
||||
msg: PresentationConversionCompletedSysPubMsg,
|
||||
state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting,
|
||||
bus: MessageBus
|
||||
msg: PresentationConversionCompletedSysPubMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus
|
||||
): MeetingState2x = {
|
||||
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
@ -25,7 +23,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||
pres <- pod.getPresentation(msg.body.presentation.id)
|
||||
} yield {
|
||||
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres, temporaryPresentationId,
|
||||
msg.body.presentation.defaultPresentation, msg.body.presentation.filenameConverted)
|
||||
msg.body.presentation.isInitialPresentation, msg.body.presentation.filenameConverted)
|
||||
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(
|
||||
bus,
|
||||
meetingId,
|
||||
@ -48,13 +46,11 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||
originalDownloadableExtension
|
||||
)
|
||||
|
||||
val presWithConvertedName = PresentationInPod(pres.id, pres.name, default = msg.body.presentation.defaultPresentation,
|
||||
pres.current, pres.pages, pres.downloadable, pres.downloadFileExtension, pres.removable, msg.body.presentation.filenameConverted,
|
||||
uploadCompleted = true, numPages = pres.numPages, errorDetails = Map.empty)
|
||||
val presWithConvertedName = PresentationInPod(pres.id, pres.name, pres.current, pres.pages,
|
||||
pres.downloadable, pres.removable, msg.body.presentation.filenameConverted)
|
||||
var pods = state.presentationPodManager.addPod(pod)
|
||||
pods = pods.addPresentationToPod(pod.id, presWithConvertedName)
|
||||
|
||||
PresPresentationDAO.updatePages(presWithConvertedName)
|
||||
state.update(pods)
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user