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?
|
### 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)
|
### Closes Issue(s)
|
||||||
<!-- List here all the issues closed by this pull request. Use keyword `closes` before each issue number
|
<!-- List here all the issues closed by this pull request. Use keyword `closes` before each issue number
|
||||||
@ -25,18 +25,12 @@ Closes #123456
|
|||||||
-->
|
-->
|
||||||
Closes #
|
Closes #
|
||||||
|
|
||||||
|
|
||||||
### Motivation
|
### Motivation
|
||||||
|
|
||||||
<!-- What inspired you to submit this pull request? -->
|
<!-- 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
|
### More
|
||||||
|
|
||||||
<!-- Anything else we should know when reviewing? -->
|
<!-- Anything else we should know when reviewing? -->
|
||||||
- [ ] Added/updated documentation
|
- [ ] 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
|
name: Merge branches
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout ${{ github.event.pull_request.base.ref || 'master' }}
|
- name: Checkout ${{ github.event.pull_request.base.ref || 'master' }}
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.base.ref || '' }}
|
ref: ${{ github.event.pull_request.base.ref || '' }}
|
||||||
fetch-depth: 0 # Fetch all history
|
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:
|
paths-ignore:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
- "bigbluebutton-html5/public/locales/*.json"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
@ -33,7 +32,6 @@ jobs:
|
|||||||
bbb-export-annotations,
|
bbb-export-annotations,
|
||||||
bbb-learning-dashboard,
|
bbb-learning-dashboard,
|
||||||
bbb-playback-record,
|
bbb-playback-record,
|
||||||
bbb-graphql-server,
|
|
||||||
bbb-etherpad,
|
bbb-etherpad,
|
||||||
bbb-web,
|
bbb-web,
|
||||||
bbb-fsesl-akka,
|
bbb-fsesl-akka,
|
||||||
@ -53,9 +51,6 @@ jobs:
|
|||||||
cache-files-list: bbb-learning-dashboard
|
cache-files-list: bbb-learning-dashboard
|
||||||
- package: bbb-playback-record
|
- 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
|
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
|
- package: bbb-etherpad
|
||||||
cache-files-list: bbb-etherpad.placeholder.sh
|
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
|
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
|
- package: bbb-fsesl-akka
|
||||||
cache-files-list: akka-bbb-fsesl bbb-common-message
|
cache-files-list: akka-bbb-fsesl bbb-common-message
|
||||||
- package: bbb-html5
|
- package: bbb-html5
|
||||||
build-list: bbb-html5
|
build-list: bbb-html5-nodejs bbb-html5
|
||||||
cache-files-list: bigbluebutton-html5
|
cache-files-list: bigbluebutton-html5
|
||||||
- package: bbb-freeswitch
|
- package: bbb-freeswitch
|
||||||
build-list: bbb-freeswitch-core bbb-freeswitch-sounds
|
build-list: bbb-freeswitch-core bbb-freeswitch-sounds
|
||||||
@ -74,9 +69,9 @@ jobs:
|
|||||||
build-list: bbb-webrtc-sfu bbb-webrtc-recorder
|
build-list: bbb-webrtc-sfu bbb-webrtc-recorder
|
||||||
cache-files-list: bbb-webrtc-sfu.placeholder.sh bbb-webrtc-recorder.placeholder.sh
|
cache-files-list: bbb-webrtc-sfu.placeholder.sh bbb-webrtc-recorder.placeholder.sh
|
||||||
- package: others
|
- 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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Merge branches
|
- name: Merge branches
|
||||||
uses: ./.github/actions/merge-branches
|
uses: ./.github/actions/merge-branches
|
||||||
- name: Set cache-key vars
|
- name: Set cache-key vars
|
||||||
@ -91,7 +86,7 @@ jobs:
|
|||||||
- name: Handle cache
|
- name: Handle cache
|
||||||
if: matrix.cache-files-list != ''
|
if: matrix.cache-files-list != ''
|
||||||
id: cache-action
|
id: cache-action
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: artifacts.tar
|
path: artifacts.tar
|
||||||
key: ${{ runner.os }}-${{ matrix.package }}-${{ env.BIGBLUEBUTTON_RELEASE }}-commits-${{ env.CACHE_KEY_FILES }}-urls-${{ env.CACHE_KEY_URLS }}
|
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
|
echo "${{ matrix.build-list || matrix.package }}" | xargs -n 1 ./build/setup.sh
|
||||||
tar cvf artifacts.tar artifacts/
|
tar cvf artifacts.tar artifacts/
|
||||||
- name: Archive packages
|
- name: Archive packages
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_${{ matrix.package }}.tar
|
name: artifacts_${{ matrix.package }}.tar
|
||||||
path: artifacts.tar
|
path: artifacts.tar
|
||||||
install-and-run-tests:
|
install-and-run-tests:
|
||||||
needs: build-package
|
needs: build-package
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3, 4, 5, 6, 7, 8]
|
shard: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
||||||
env:
|
|
||||||
shard: ${{ matrix.shard }}/8
|
|
||||||
MATRIX_SHARD_UNDERSCORED: ${{ matrix.shard }}_8
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Merge branches
|
- name: Merge branches
|
||||||
uses: ./.github/actions/merge-branches
|
uses: ./.github/actions/merge-branches
|
||||||
- run: ./build/get_external_dependencies.sh
|
- run: ./build/get_external_dependencies.sh
|
||||||
- name: Download artifacts_bbb-apps-akka
|
- name: Download artifacts_bbb-apps-akka
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-apps-akka.tar
|
name: artifacts_bbb-apps-akka.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-config
|
- name: Download artifacts_bbb-config
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-config.tar
|
name: artifacts_bbb-config.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-export-annotations
|
- name: Download artifacts_bbb-export-annotations
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-export-annotations.tar
|
name: artifacts_bbb-export-annotations.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-learning-dashboard
|
- name: Download artifacts_bbb-learning-dashboard
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-learning-dashboard.tar
|
name: artifacts_bbb-learning-dashboard.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-playback-record
|
- name: Download artifacts_bbb-playback-record
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-playback-record.tar
|
name: artifacts_bbb-playback-record.tar
|
||||||
- run: tar xf artifacts.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
|
- name: Download artifacts_bbb-etherpad
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-etherpad.tar
|
name: artifacts_bbb-etherpad.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-freeswitch
|
- name: Download artifacts_bbb-freeswitch
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-freeswitch.tar
|
name: artifacts_bbb-freeswitch.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-webrtc
|
- name: Download artifacts_bbb-webrtc
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-webrtc.tar
|
name: artifacts_bbb-webrtc.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-web
|
- name: Download artifacts_bbb-web
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-web.tar
|
name: artifacts_bbb-web.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-fsesl-akka
|
- name: Download artifacts_bbb-fsesl-akka
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-fsesl-akka.tar
|
name: artifacts_bbb-fsesl-akka.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts_bbb-html5
|
- name: Download artifacts_bbb-html5
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_bbb-html5.tar
|
name: artifacts_bbb-html5.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifacts_others.tar
|
name: artifacts_others.tar
|
||||||
- run: tar xf artifacts.tar
|
- run: tar xf artifacts.tar
|
||||||
@ -257,78 +244,15 @@ jobs:
|
|||||||
apt --purge -y remove apache2-bin
|
apt --purge -y remove apache2-bin
|
||||||
'
|
'
|
||||||
- name: Install BBB
|
- 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: |
|
run: |
|
||||||
sudo -i <<EOF
|
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
|
EOF
|
||||||
- name: Install test dependencies
|
- name: Install test dependencies
|
||||||
working-directory: ./bigbluebutton-tests/playwright
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
@ -339,18 +263,13 @@ jobs:
|
|||||||
npx playwright install
|
npx playwright install
|
||||||
'
|
'
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: nick-fields/retry@v3
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
with:
|
|
||||||
timeout_minutes: 25
|
|
||||||
max_attempts: 2
|
|
||||||
command: |
|
|
||||||
cd ./bigbluebutton-tests/playwright
|
|
||||||
npm run test-chromium-ci -- --shard ${{ env.shard }}
|
|
||||||
env:
|
env:
|
||||||
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
|
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
|
||||||
ACTIONS_RUNNER_DEBUG: true
|
ACTIONS_RUNNER_DEBUG: true
|
||||||
BBB_URL: https://bbb-ci.test/bigbluebutton/api
|
BBB_URL: https://bbb-ci.test/bigbluebutton/api
|
||||||
BBB_SECRET: bbbci
|
BBB_SECRET: bbbci
|
||||||
|
run: npm run test-chromium-ci -- --shard ${{ matrix.shard }}
|
||||||
- name: Run Firefox tests
|
- name: Run Firefox tests
|
||||||
working-directory: ./bigbluebutton-tests/playwright
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
if: |
|
if: |
|
||||||
@ -365,13 +284,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sh -c '
|
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 {} \;
|
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
|
name: Upload blob report to GitHub Actions Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: blob-report-${{ matrix.shard }}
|
name: all-blob-reports
|
||||||
path: bigbluebutton-tests/playwright/blob-report
|
path: bigbluebutton-tests/playwright/blob-report
|
||||||
- if: failure()
|
- if: failure()
|
||||||
name: Prepare artifacts (configs and logs)
|
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
|
ls -t /root/*.tar.gz | head -1 | xargs -I '{}' cp '{}' /home/runner/work/bigbluebutton/bigbluebutton/bbb-logs.tar.gz
|
||||||
EOF
|
EOF
|
||||||
- if: failure()
|
- if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bbb-configs-${{ env.MATRIX_SHARD_UNDERSCORED }}
|
name: bbb-configs
|
||||||
path: configs
|
path: configs
|
||||||
- if: failure()
|
- if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bbb-logs-${{ env.MATRIX_SHARD_UNDERSCORED }}
|
name: bbb-logs
|
||||||
path: ./bbb-logs.tar.gz
|
path: ./bbb-logs.tar.gz
|
||||||
upload-report:
|
upload-report:
|
||||||
if: always() && !contains(github.event.head_commit.message, 'Merge pull request')
|
if: always()
|
||||||
needs: install-and-run-tests
|
needs: install-and-run-tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
hasReportData: ${{ needs.install-and-run-tests.result == 'success' || needs.install-and-run-tests.result == 'failure' }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Upload blob report
|
- uses: actions/setup-node@v3
|
||||||
if: ${{ env.hasReportData }}
|
with:
|
||||||
uses: ./.github/actions/upload-blob-report
|
node-version: 18
|
||||||
- name: Remove unnecessary artifact
|
- name: Install dependencies
|
||||||
uses: geekyeggo/delete-artifact@v5
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
|
run: npm ci
|
||||||
|
- name: Download all blob reports from GitHub Actions Artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: all-blob-reports
|
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
|
- name: Write PR data for auto-comment
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
working-directory: ./
|
working-directory: ./
|
||||||
@ -433,7 +363,7 @@ jobs:
|
|||||||
echo ${{ github.run_id }} > ./pr-comment-data/workflow_id
|
echo ${{ github.run_id }} > ./pr-comment-data/workflow_id
|
||||||
- name: Upload PR data for auto-comment
|
- name: Upload PR data for auto-comment
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: pr-comment-data
|
name: pr-comment-data
|
||||||
path: 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:
|
jobs:
|
||||||
main:
|
main:
|
||||||
permissions:
|
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
|
runs-on: ubuntu-latest
|
||||||
concurrency:
|
|
||||||
group: github-api-request
|
|
||||||
cancel-in-progress: false
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check for dirty pull requests
|
- name: Check for dirty pull requests
|
||||||
uses: eps1lon/actions-label-merge-conflict@v3
|
uses: eps1lon/actions-label-merge-conflict@releases/2.x
|
||||||
with:
|
with:
|
||||||
dirtyLabel: "status: conflict"
|
dirtyLabel: "status: conflict"
|
||||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
commentOnDirty: |
|
commentOnDirty: |
|
||||||
This pull request has conflicts ☹
|
This pull request has conflicts ☹
|
||||||
Please resolve those so we can review the pull request.
|
Please resolve those so we can review the pull request.
|
||||||
Thanks.
|
Thanks.
|
||||||
|
15
.github/workflows/deploy-docs.yml
vendored
15
.github/workflows/deploy-docs.yml
vendored
@ -7,6 +7,7 @@ on:
|
|||||||
- 'develop'
|
- 'develop'
|
||||||
paths:
|
paths:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
- '.github/**'
|
||||||
|
|
||||||
# Do not build the docs concurrently
|
# Do not build the docs concurrently
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -22,20 +23,20 @@ jobs:
|
|||||||
working-directory: ./docs
|
working-directory: ./docs
|
||||||
steps:
|
steps:
|
||||||
# Setup
|
# Setup
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
cache: npm
|
cache: yarn
|
||||||
cache-dependency-path: ./docs/package-lock.json
|
cache-dependency-path: ./docs/yarn.lock
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
# Build static docs
|
# Build static docs
|
||||||
- name: Build all versions
|
- name: Build all versions
|
||||||
run: ./build.sh
|
run: ./build.sh
|
||||||
- name: Build website
|
- name: Build website
|
||||||
run: npm run docusaurus build
|
run: yarn build
|
||||||
- name: upload build artifact
|
- name: upload build artifact
|
||||||
uses: actions/upload-pages-artifact@v1
|
uses: actions/upload-pages-artifact@v1
|
||||||
with:
|
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/*
|
artifacts/*
|
||||||
bbb-presentation-video.zip
|
bbb-presentation-video.zip
|
||||||
bbb-presentation-video
|
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
|
# define which docker image to use for builds
|
||||||
default:
|
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.
|
# 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
|
# it then checks an API endpoint on the package server to find out for which of
|
||||||
@ -102,16 +102,6 @@ bbb-html5-build:
|
|||||||
script:
|
script:
|
||||||
- build/setup-inside-docker.sh bbb-html5
|
- 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:
|
bbb-learning-dashboard-build:
|
||||||
extends: .build_job
|
extends: .build_job
|
||||||
script:
|
script:
|
||||||
@ -122,6 +112,11 @@ bbb-libreoffice-docker-build:
|
|||||||
script:
|
script:
|
||||||
- build/setup-inside-docker.sh bbb-libreoffice-docker
|
- 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:
|
bbb-mkclean-build:
|
||||||
extends: .build_job
|
extends: .build_job
|
||||||
script:
|
script:
|
||||||
@ -182,11 +177,6 @@ bbb-webrtc-recorder-build:
|
|||||||
script:
|
script:
|
||||||
- build/setup-inside-docker.sh bbb-webrtc-recorder
|
- 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:
|
bbb-transcription-controller-build:
|
||||||
extends: .build_job
|
extends: .build_job
|
||||||
script:
|
script:
|
||||||
|
12
README.md
12
README.md
@ -1,20 +1,20 @@
|
|||||||
BigBlueButton
|
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.
|
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)
|
* Online tutoring (one-to-one)
|
||||||
* Flipped classrooms (recording content ahead of your session)
|
* Flipped classrooms (recording content ahead of your session)
|
||||||
* Group collaboration (many-to-many)
|
* Group collaboration (many-to-many)
|
||||||
* Online classes (one-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.5.x (or earlier) | :x: |
|
||||||
| 2.6.x | :white_check_mark: |
|
| 2.6.x | :white_check_mark: |
|
||||||
| 2.7.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.
|
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
|
## 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
|
- 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.
|
- or e-mailing security@bigbluebutton.org with as much detail as possible.
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.bigbluebutton.build
|
package org.bigbluebutton.build
|
||||||
|
|
||||||
import sbt._
|
import sbt._
|
||||||
|
import Keys._
|
||||||
|
|
||||||
object Dependencies {
|
object Dependencies {
|
||||||
|
|
||||||
@ -12,8 +13,8 @@ object Dependencies {
|
|||||||
val scalactic = "3.0.8"
|
val scalactic = "3.0.8"
|
||||||
|
|
||||||
// Libraries
|
// Libraries
|
||||||
val pekkoVersion = "1.0.1"
|
val akkaVersion = "2.6.17"
|
||||||
val pekkoHttpVersion = "1.0.0"
|
val akkaHttpVersion = "10.2.7"
|
||||||
val gson = "2.8.9"
|
val gson = "2.8.9"
|
||||||
val jackson = "2.13.5"
|
val jackson = "2.13.5"
|
||||||
val logback = "1.2.13"
|
val logback = "1.2.13"
|
||||||
@ -27,24 +28,18 @@ object Dependencies {
|
|||||||
// BigBlueButton
|
// BigBlueButton
|
||||||
val bbbCommons = "0.0.22-SNAPSHOT"
|
val bbbCommons = "0.0.22-SNAPSHOT"
|
||||||
|
|
||||||
// Database
|
|
||||||
val slick = "3.4.1"
|
|
||||||
val postgresql = "42.5.0"
|
|
||||||
val slickPg = "0.21.1"
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val scalaTest = "3.2.11"
|
val scalaTest = "3.2.11"
|
||||||
val mockito = "2.23.0"
|
val mockito = "2.23.0"
|
||||||
val akkaTestKit = "2.6.0"
|
val akkaTestKit = "2.6.0"
|
||||||
val jacksonDataFormat = "2.13.5"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Compile {
|
object Compile {
|
||||||
val scalaLibrary = "org.scala-lang" % "scala-library" % Versions.scala
|
val scalaLibrary = "org.scala-lang" % "scala-library" % Versions.scala
|
||||||
val scalaCompiler = "org.scala-lang" % "scala-compiler" % Versions.scala
|
val scalaCompiler = "org.scala-lang" % "scala-compiler" % Versions.scala
|
||||||
|
|
||||||
val pekkoActor = "org.apache.pekko" %% "pekko-actor" % Versions.pekkoVersion
|
val akkaActor = "com.typesafe.akka" % "akka-actor_2.13" % Versions.akkaVersion
|
||||||
val pekkoSlf4j = "org.apache.pekko" %% "pekko-slf4j" % Versions.pekkoVersion
|
val akkaSl4fj = "com.typesafe.akka" % "akka-slf4j_2.13" % Versions.akkaVersion
|
||||||
|
|
||||||
val googleGson = "com.google.code.gson" % "gson" % Versions.gson
|
val googleGson = "com.google.code.gson" % "gson" % Versions.gson
|
||||||
val jacksonModule = "com.fasterxml.jackson.module" %% "jackson-module-scala" % Versions.jackson
|
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 commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
|
||||||
val sprayJson = "io.spray" % "spray-json_2.13" % Versions.spray
|
val sprayJson = "io.spray" % "spray-json_2.13" % Versions.spray
|
||||||
|
|
||||||
val pekkoStream = "org.apache.pekko" %% "pekko-stream" % Versions.pekkoVersion
|
val akkaStream = "com.typesafe.akka" %% "akka-stream" % Versions.akkaVersion
|
||||||
val pekkoHttp = "org.apache.pekko" %% "pekko-http" % Versions.pekkoHttpVersion
|
val akkaHttp = "com.typesafe.akka" %% "akka-http" % Versions.akkaHttpVersion
|
||||||
val pekkoHttpSprayJson = "org.apache.pekko" %% "pekko-http-spray-json" % Versions.pekkoHttpVersion
|
val akkaHttpSprayJson = "com.typesafe.akka" %% "akka-http-spray-json" % Versions.akkaHttpVersion
|
||||||
|
|
||||||
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
|
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
|
||||||
|
|
||||||
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons
|
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 {
|
object Test {
|
||||||
@ -88,9 +75,9 @@ object Dependencies {
|
|||||||
val runtime = Seq(
|
val runtime = Seq(
|
||||||
Compile.scalaLibrary,
|
Compile.scalaLibrary,
|
||||||
Compile.scalaCompiler,
|
Compile.scalaCompiler,
|
||||||
Compile.pekkoActor,
|
Compile.akkaActor,
|
||||||
Compile.pekkoSlf4j,
|
Compile.akkaSl4fj,
|
||||||
Compile.pekkoStream,
|
Compile.akkaStream,
|
||||||
Compile.googleGson,
|
Compile.googleGson,
|
||||||
Compile.jacksonModule,
|
Compile.jacksonModule,
|
||||||
Compile.quicklens,
|
Compile.quicklens,
|
||||||
@ -98,13 +85,7 @@ object Dependencies {
|
|||||||
Compile.commonsCodec,
|
Compile.commonsCodec,
|
||||||
Compile.sprayJson,
|
Compile.sprayJson,
|
||||||
Compile.apacheLang,
|
Compile.apacheLang,
|
||||||
Compile.pekkoHttp,
|
Compile.akkaHttp,
|
||||||
Compile.pekkoHttpSprayJson,
|
Compile.akkaHttpSprayJson,
|
||||||
Compile.bbbCommons,
|
Compile.bbbCommons) ++ testing
|
||||||
Compile.slick,
|
|
||||||
Compile.slickHikaricp,
|
|
||||||
Compile.slickPg,
|
|
||||||
Compile.slickPgSprayJson,
|
|
||||||
Compile.postgresql,
|
|
||||||
Compile.jacksonDataFormat) ++ testing
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package org.bigbluebutton
|
package org.bigbluebutton
|
||||||
|
|
||||||
import org.apache.pekko.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||||
import org.apache.pekko.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.{ ApiResponseFailure, ApiResponseSuccess, UserInfosApiMsg }
|
import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus }
|
||||||
import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus, UserInfoService }
|
|
||||||
import spray.json._
|
import spray.json._
|
||||||
|
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
import ExecutionContext.Implicits.global
|
import ExecutionContext.Implicits.global
|
||||||
|
|
||||||
@ -65,7 +63,7 @@ trait JsonSupportProtocolMeetingInfoResponse extends SprayJsonSupport with Defau
|
|||||||
implicit val meetingInfoResponseJsonFormat = jsonFormat1(MeetingInfoResponse)
|
implicit val meetingInfoResponseJsonFormat = jsonFormat1(MeetingInfoResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService, userInfoService: UserInfoService)
|
class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService)
|
||||||
extends JsonSupportProtocolHealthResponse
|
extends JsonSupportProtocolHealthResponse
|
||||||
with JsonSupportProtocolMeetingInfoResponse {
|
with JsonSupportProtocolMeetingInfoResponse {
|
||||||
|
|
||||||
@ -126,28 +124,5 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService, user
|
|||||||
}
|
}
|
||||||
complete(entityFuture)
|
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
|
package org.bigbluebutton
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
import org.apache.pekko.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
import org.apache.pekko.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
import org.bigbluebutton.common2.redis.{MessageSender, RedisConfig, RedisPublisher}
|
import org.bigbluebutton.common2.redis.{ MessageSender, RedisConfig, RedisPublisher }
|
||||||
import org.bigbluebutton.core._
|
import org.bigbluebutton.core._
|
||||||
import org.bigbluebutton.core.bus._
|
import org.bigbluebutton.core.bus._
|
||||||
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
||||||
import org.bigbluebutton.core2.AnalyticsActor
|
import org.bigbluebutton.core2.AnalyticsActor
|
||||||
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
|
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.common2.bus.IncomingJsonMessageBus
|
||||||
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService, UserInfoService}
|
import org.bigbluebutton.service.{ HealthzService, MeetingInfoActor, MeetingInfoService }
|
||||||
|
|
||||||
object Boot extends App with SystemConfiguration {
|
object Boot extends App with SystemConfiguration {
|
||||||
|
|
||||||
@ -50,6 +52,8 @@ object Boot extends App with SystemConfiguration {
|
|||||||
|
|
||||||
val meetingInfoService = MeetingInfoService(system, meetingInfoActorRef)
|
val meetingInfoService = MeetingInfoService(system, meetingInfoActorRef)
|
||||||
|
|
||||||
|
val apiService = new ApiService(healthzService, meetingInfoService)
|
||||||
|
|
||||||
val redisRecorderActor = system.actorOf(
|
val redisRecorderActor = system.actorOf(
|
||||||
RedisRecorderActor.props(system, redisConfig, healthzService),
|
RedisRecorderActor.props(system, redisConfig, healthzService),
|
||||||
"redisRecorderActor"
|
"redisRecorderActor"
|
||||||
@ -65,12 +69,6 @@ object Boot extends App with SystemConfiguration {
|
|||||||
"LearningDashboardActor"
|
"LearningDashboardActor"
|
||||||
)
|
)
|
||||||
|
|
||||||
val graphqlConnectionsActor = system.actorOf(
|
|
||||||
GraphqlConnectionsActor.props(system, eventBus, outGW),
|
|
||||||
"GraphqlConnectionsActor"
|
|
||||||
)
|
|
||||||
|
|
||||||
ClientSettings.loadClientSettingsFromFile()
|
|
||||||
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
|
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
|
||||||
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
||||||
|
|
||||||
@ -87,15 +85,9 @@ object Boot extends App with SystemConfiguration {
|
|||||||
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
|
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
|
||||||
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
|
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")
|
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
|
||||||
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
||||||
|
|
||||||
val userInfoService = UserInfoService(system, bbbActor)
|
|
||||||
val apiService = new ApiService(healthzService, meetingInfoService, userInfoService)
|
|
||||||
|
|
||||||
val redisMessageHandlerActor = system.actorOf(ReceivedJsonMsgHandlerActor.props(bbbMsgBus, incomingJsonMessageBus))
|
val redisMessageHandlerActor = system.actorOf(ReceivedJsonMsgHandlerActor.props(bbbMsgBus, incomingJsonMessageBus))
|
||||||
incomingJsonMessageBus.subscribe(redisMessageHandlerActor, toAkkaAppsJsonChannel)
|
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
|
package org.bigbluebutton
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, Routing }
|
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, Routing }
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.{ MeetingStatus2x }
|
import org.bigbluebutton.core2.{ MeetingStatus2x }
|
||||||
@ -23,7 +21,7 @@ object LockSettingsUtil {
|
|||||||
liveMeeting: LiveMeeting,
|
liveMeeting: LiveMeeting,
|
||||||
outGW: OutMsgRouter,
|
outGW: OutMsgRouter,
|
||||||
vu: VoiceUserState, mute: Boolean
|
vu: VoiceUserState, mute: Boolean
|
||||||
)(implicit context: ActorContext): Unit = {
|
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||||
VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, mute)
|
VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, mute)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +29,7 @@ object LockSettingsUtil {
|
|||||||
disableMic: Boolean,
|
disableMic: Boolean,
|
||||||
liveMeeting: LiveMeeting,
|
liveMeeting: LiveMeeting,
|
||||||
outGW: OutMsgRouter
|
outGW: OutMsgRouter
|
||||||
)(implicit context: ActorContext): Unit = {
|
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||||
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
|
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
|
||||||
Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user =>
|
Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user =>
|
||||||
if (user.role == Roles.VIEWER_ROLE && !vu.listenOnly && user.locked) {
|
if (user.role == Roles.VIEWER_ROLE && !vu.listenOnly && user.locked) {
|
||||||
@ -50,7 +48,7 @@ object LockSettingsUtil {
|
|||||||
def enforceLockSettingsForAllVoiceUsers(
|
def enforceLockSettingsForAllVoiceUsers(
|
||||||
liveMeeting: LiveMeeting,
|
liveMeeting: LiveMeeting,
|
||||||
outGW: OutMsgRouter
|
outGW: OutMsgRouter
|
||||||
)(implicit context: ActorContext): Unit = {
|
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||||
applyMutingOfUsers(permissions.disableMic, liveMeeting, outGW)
|
applyMutingOfUsers(permissions.disableMic, liveMeeting, outGW)
|
||||||
}
|
}
|
||||||
@ -59,7 +57,7 @@ object LockSettingsUtil {
|
|||||||
voiceUser: VoiceUserState,
|
voiceUser: VoiceUserState,
|
||||||
liveMeeting: LiveMeeting,
|
liveMeeting: LiveMeeting,
|
||||||
outGW: OutMsgRouter
|
outGW: OutMsgRouter
|
||||||
)(implicit context: ActorContext): Unit = {
|
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||||
|
|
||||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||||
if (permissions.disableMic) {
|
if (permissions.disableMic) {
|
||||||
@ -80,7 +78,7 @@ object LockSettingsUtil {
|
|||||||
intUserId: String,
|
intUserId: String,
|
||||||
liveMeeting: LiveMeeting,
|
liveMeeting: LiveMeeting,
|
||||||
outGW: OutMsgRouter
|
outGW: OutMsgRouter
|
||||||
)(implicit context: ActorContext): Unit = {
|
)(implicit context: akka.actor.ActorContext): Unit = {
|
||||||
val voiceUser = VoiceUsers.findWithIntId(liveMeeting.voiceUsers, intUserId)
|
val voiceUser = VoiceUsers.findWithIntId(liveMeeting.voiceUsers, intUserId)
|
||||||
voiceUser.foreach { vu =>
|
voiceUser.foreach { vu =>
|
||||||
// Make sure that listen only user is muted. (ralam dec 6, 2019
|
// Make sure that listen only user is muted. (ralam dec 6, 2019
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package org.bigbluebutton
|
package org.bigbluebutton
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import scala.util.Try
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
|
||||||
|
|
||||||
import scala.util.{ Failure, Success, Try }
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
trait SystemConfiguration {
|
trait SystemConfiguration {
|
||||||
@ -41,16 +38,12 @@ trait SystemConfiguration {
|
|||||||
|
|
||||||
lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings")
|
lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings")
|
||||||
lazy val voiceConfRecordCodec = Try(config.getString("voiceConf.recordCodec")).getOrElse("wav")
|
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 checkVoiceRecordingInterval = Try(config.getInt("voiceConf.checkRecordingInterval")).getOrElse(19)
|
||||||
lazy val syncVoiceUsersStatusInterval = Try(config.getInt("voiceConf.syncUserStatusInterval")).getOrElse(43)
|
lazy val syncVoiceUsersStatusInterval = Try(config.getInt("voiceConf.syncUserStatusInterval")).getOrElse(43)
|
||||||
lazy val ejectRogueVoiceUsers = Try(config.getBoolean("voiceConf.ejectRogueVoiceUsers")).getOrElse(true)
|
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 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 toggleListenOnlyAfterMuteTimer = Try(config.getInt("voiceConf.toggleListenOnlyAfterMuteTimer")).getOrElse(4)
|
||||||
lazy val transparentListenOnlyThreshold = Try(config.getInt("voiceConf.transparentListenOnlyThreshold")).getOrElse(0)
|
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)
|
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 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
|
// Grab the "interface" parameter from the http config
|
||||||
val httpHost = config.getString("http.interface")
|
val httpHost = config.getString("http.interface")
|
||||||
// Grab the "port" parameter from the http config
|
// Grab the "port" parameter from the http config
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package org.bigbluebutton.core
|
package org.bigbluebutton.core
|
||||||
|
|
||||||
import java.io.{ PrintWriter, StringWriter }
|
import java.io.{ PrintWriter, StringWriter }
|
||||||
import org.apache.pekko.actor._
|
import akka.actor._
|
||||||
import org.apache.pekko.actor.ActorLogging
|
import akka.actor.ActorLogging
|
||||||
import org.apache.pekko.actor.SupervisorStrategy.Resume
|
import akka.actor.SupervisorStrategy.Resume
|
||||||
import org.apache.pekko.util.Timeout
|
import akka.util.Timeout
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import org.bigbluebutton.core.bus._
|
import org.bigbluebutton.core.bus._
|
||||||
@ -13,9 +13,6 @@ import org.bigbluebutton.SystemConfiguration
|
|||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import org.bigbluebutton.common2.msgs._
|
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.running.RunningMeeting
|
||||||
import org.bigbluebutton.core.util.ColorPicker
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.RunningMeetings
|
import org.bigbluebutton.core2.RunningMeetings
|
||||||
@ -46,13 +43,6 @@ class BigBlueButtonActor(
|
|||||||
|
|
||||||
private val meetings = new RunningMeetings
|
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) {
|
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||||
case e: Exception => {
|
case e: Exception => {
|
||||||
val sw: StringWriter = new StringWriter()
|
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() {
|
override def preStart() {
|
||||||
bbbMsgBus.subscribe(self, meetingManagerChannel)
|
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() {
|
override def postStop() {
|
||||||
@ -85,76 +63,35 @@ class BigBlueButtonActor(
|
|||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
// Internal messages
|
// Internal messages
|
||||||
case BBBTasksExecutor => handleMeetingTasksExecutor()
|
|
||||||
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
|
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
|
||||||
|
|
||||||
//Api messages
|
|
||||||
case msg: GetUserApiMsg => handleGetUserApiMsg(msg, sender)
|
|
||||||
|
|
||||||
// 2x messages
|
// 2x messages
|
||||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||||
case _ => // do nothing
|
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 = {
|
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||||
msg.core match {
|
msg.core match {
|
||||||
|
|
||||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||||
case m: RegisterUserSessionTokenReqMsg => handleRegisterUserSessionTokenReqMsg(m)
|
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||||
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
|
||||||
case _: UserGraphqlConnectionEstablishedSysMsg => //Ignore
|
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
||||||
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
|
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
||||||
case _: CheckGraphqlMiddlewareAlivePongSysMsg => //Ignore
|
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||||
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)
|
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
||||||
} yield {
|
} yield {
|
||||||
log.debug("FORWARDING Register user message")
|
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)
|
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 = {
|
private def handleCheckAlivePingSysMsg(msg: CheckAlivePingSysMsg): Unit = {
|
||||||
val event = MsgBuilder.buildCheckAlivePingSysMsg(msg.body.system, msg.body.bbbWebTimestamp, System.currentTimeMillis())
|
val event = MsgBuilder.buildCheckAlivePingSysMsg(msg.body.system, msg.body.bbbWebTimestamp, System.currentTimeMillis())
|
||||||
healthzService.sendPubSubStatusMessage(msg.body.akkaAppsTimestamp, System.currentTimeMillis())
|
healthzService.sendPubSubStatusMessage(msg.body.akkaAppsTimestamp, System.currentTimeMillis())
|
||||||
outGW.send(event)
|
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 = {
|
private def handleDestroyMeeting(msg: DestroyMeetingInternalMsg): Unit = {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -260,16 +185,6 @@ class BigBlueButtonActor(
|
|||||||
context.stop(m.actorRef)
|
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
|
//Remove ColorPicker idx of the meeting
|
||||||
ColorPicker.reset(m.props.meetingProp.intId)
|
ColorPicker.reset(m.props.meetingProp.intId)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.bigbluebutton.core.api
|
package org.bigbluebutton.core.api
|
||||||
|
|
||||||
import org.bigbluebutton.core.apps.users.UserEstablishedGraphqlConnectionInternalMsgHdlr
|
|
||||||
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
|
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
|
||||||
import spray.json.JsObject
|
import spray.json.JsObject
|
||||||
case class InMessageHeader(name: String)
|
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.
|
* Audit message sent to meeting to trigger updating clients of meeting time remaining.
|
||||||
* @param meetingId
|
* @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 ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||||
case class DestroyMeetingInternalMsg(meetingId: 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
|
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
|
* Sent by breakout room to parent meeting to obtain padId
|
||||||
* @param presenterId
|
* @param breakoutId
|
||||||
|
* @param filename
|
||||||
*/
|
*/
|
||||||
case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMessage
|
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
|
||||||
|
|
||||||
/**
|
// DeskShare
|
||||||
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
|
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||||
* @param userId
|
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 UserClosedAllGraphqlConnectionsInternalMsg(userId: 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
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
@ -20,9 +20,10 @@ object BreakoutModel {
|
|||||||
captureSlidesFilename: String,
|
captureSlidesFilename: String,
|
||||||
allPages: Boolean,
|
allPages: Boolean,
|
||||||
presId: String,
|
presId: String,
|
||||||
|
sourcePresentationFilename: String,
|
||||||
): BreakoutRoom2x = {
|
): BreakoutRoom2x = {
|
||||||
new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false,
|
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
|
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] = {
|
def getHistory(): Map[String, TranscriptVO] = {
|
||||||
transcripts
|
transcripts
|
||||||
}
|
}
|
||||||
@ -71,6 +97,20 @@ class CaptionModel {
|
|||||||
locale
|
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 = {
|
def isUserCaptionOwner(userId: String, name: String): Boolean = {
|
||||||
var isOwner: Boolean = false;
|
var isOwner: Boolean = false;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import org.bigbluebutton.core2.message.handlers.guests._
|
|||||||
|
|
||||||
trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
|
trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
|
||||||
with GuestsWaitingApprovedMsgHdlr
|
with GuestsWaitingApprovedMsgHdlr
|
||||||
|
with GuestWaitingLeftMsgHdlr
|
||||||
with UpdatePositionInWaitingQueueReqMsgHdlr
|
with UpdatePositionInWaitingQueueReqMsgHdlr
|
||||||
with SetGuestPolicyMsgHdlr
|
with SetGuestPolicyMsgHdlr
|
||||||
with SetGuestLobbyMessageMsgHdlr
|
with SetGuestLobbyMessageMsgHdlr
|
||||||
|
@ -88,12 +88,8 @@ object PermissionCheck extends SystemConfiguration {
|
|||||||
|
|
||||||
UsersApp.ejectUserFromMeeting(outGW, liveMeeting, userId, ejectedBy, reason, EjectReasonCode.PERMISSION_FAILED, ban = false)
|
UsersApp.ejectUserFromMeeting(outGW, liveMeeting, userId, ejectedBy, reason, EjectReasonCode.PERMISSION_FAILED, ban = false)
|
||||||
|
|
||||||
// Force reconnection with graphql to refresh permissions
|
// send a system message to force disconnection
|
||||||
for {
|
Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason, outGW)
|
||||||
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
|
|
||||||
} yield {
|
|
||||||
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, reason, outGW)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// TODO: get this object a context so it can use the akka logging system
|
// 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}")
|
println(s"Skipping violation ejection of ${userId} trying to ${reason} in ${meetingId}")
|
||||||
|
@ -18,7 +18,7 @@ class PresentationModel {
|
|||||||
presentations.values.toVector
|
presentations.values.toVector
|
||||||
}
|
}
|
||||||
|
|
||||||
def getCurrentPresentation(): Option[Presentation] = {
|
def getCurrentPresentation(): Option[Presentation] = { // todo remove
|
||||||
presentations.values find (p => p.current)
|
presentations.values find (p => p.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ object TimerModel {
|
|||||||
stopwatch: Boolean = true,
|
stopwatch: Boolean = true,
|
||||||
time: Int = 0,
|
time: Int = 0,
|
||||||
accumulated: Int = 0,
|
accumulated: Int = 0,
|
||||||
track: String = "noTrack",
|
track: String = "",
|
||||||
): Unit = {
|
): Unit = {
|
||||||
model.stopwatch = stopwatch
|
model.stopwatch = stopwatch
|
||||||
model.time = time
|
model.time = time
|
||||||
@ -14,16 +14,20 @@ object TimerModel {
|
|||||||
model.track = track
|
model.track = track
|
||||||
}
|
}
|
||||||
|
|
||||||
def reset(model: TimerModel) : Unit = {
|
def reset(model: TimerModel, stopwatch: Boolean, time: Int, accumulated: Int, startedAt: Long, track: String) : Unit = {
|
||||||
model.accumulated = 0
|
model.stopwatch = stopwatch
|
||||||
model.startedAt = if (model.running) System.currentTimeMillis() else 0
|
model.time = time
|
||||||
|
model.accumulated = accumulated
|
||||||
|
model.startedAt = startedAt
|
||||||
|
model.track = track
|
||||||
|
model.endedAt = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
def setIsActive(model: TimerModel, active: Boolean): Unit = {
|
def setIsActive(model: TimerModel, active: Boolean): Unit = {
|
||||||
model.isActive = active
|
model.isActive = active
|
||||||
}
|
}
|
||||||
|
|
||||||
def getIsActive(model: TimerModel): Boolean = {
|
def getIsACtive(model: TimerModel): Boolean = {
|
||||||
model.isActive
|
model.isActive
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,39 +48,10 @@ object TimerModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def setRunning(model: TimerModel, running: Boolean): Unit = {
|
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
|
model.running = running
|
||||||
}
|
}
|
||||||
|
|
||||||
def resetTimerIfFinished(model: TimerModel) = {
|
def getRunning(model: TimerModel): Boolean = {
|
||||||
// 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 = {
|
|
||||||
model.running
|
model.running
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +59,7 @@ object TimerModel {
|
|||||||
model.stopwatch = stopwatch
|
model.stopwatch = stopwatch
|
||||||
}
|
}
|
||||||
|
|
||||||
def isStopwatch(model: TimerModel): Boolean = {
|
def getStopwatch(model: TimerModel): Boolean = {
|
||||||
model.stopwatch
|
model.stopwatch
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,14 +78,23 @@ object TimerModel {
|
|||||||
def getTime(model: TimerModel): Int = {
|
def getTime(model: TimerModel): Int = {
|
||||||
model.time
|
model.time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def setEndedAt(model: TimerModel, timestamp: Long): Unit = {
|
||||||
|
model.endedAt = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
def getEndedAt(model: TimerModel): Long = {
|
||||||
|
model.endedAt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimerModel {
|
class TimerModel {
|
||||||
private var startedAt: Long = 0
|
private var startedAt: Long = 0
|
||||||
|
private var endedAt: Long = 0
|
||||||
private var accumulated: Int = 0
|
private var accumulated: Int = 0
|
||||||
private var running: Boolean = false
|
private var running: Boolean = false
|
||||||
private var time: Int = 0
|
private var time: Int = 0
|
||||||
private var stopwatch: Boolean = true
|
private var stopwatch: Boolean = true
|
||||||
private var track: String = "noTrack"
|
private var track: String = ""
|
||||||
private var isActive: Boolean = false
|
private var isActive: Boolean = false
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import scala.collection.immutable.HashMap
|
|||||||
import org.bigbluebutton.common2.msgs.AnnotationVO
|
import org.bigbluebutton.common2.msgs.AnnotationVO
|
||||||
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
|
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
|
||||||
import org.bigbluebutton.SystemConfiguration
|
import org.bigbluebutton.SystemConfiguration
|
||||||
import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresPageWritersDAO }
|
|
||||||
|
|
||||||
class WhiteboardModel extends SystemConfiguration {
|
class WhiteboardModel extends SystemConfiguration {
|
||||||
private var _whiteboards = new HashMap[String, Whiteboard]()
|
private var _whiteboards = new HashMap[String, Whiteboard]()
|
||||||
@ -46,80 +45,33 @@ class WhiteboardModel extends SystemConfiguration {
|
|||||||
k -> newValue
|
k -> newValue
|
||||||
}).toMap
|
}).toMap
|
||||||
|
|
||||||
def addAnnotations(wbId: String, meetingId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||||
val wb = getWhiteboard(wbId)
|
|
||||||
|
|
||||||
var annotationsAdded = Array[AnnotationVO]()
|
var annotationsAdded = Array[AnnotationVO]()
|
||||||
var annotationsDiffAdded = Array[AnnotationVO]()
|
val wb = getWhiteboard(wbId)
|
||||||
var newAnnotationsMap = wb.annotationsMap
|
var newAnnotationsMap = wb.annotationsMap
|
||||||
|
|
||||||
for (annotation <- annotations) {
|
for (annotation <- annotations) {
|
||||||
val oldAnnotation = wb.annotationsMap.get(annotation.id)
|
val oldAnnotation = wb.annotationsMap.get(annotation.id)
|
||||||
if (oldAnnotation.isDefined) {
|
if (!oldAnnotation.isEmpty) {
|
||||||
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
|
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
val mergedAnnotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo)
|
val newAnnotation = oldAnnotation.get.copy(annotationInfo = 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)
|
|
||||||
newAnnotationsMap += (annotation.id -> newAnnotation)
|
newAnnotationsMap += (annotation.id -> newAnnotation)
|
||||||
annotationsAdded :+= newAnnotation
|
annotationsAdded :+= annotation
|
||||||
annotationsDiffAdded :+= annotation
|
println(s"Updated annotation onpage [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||||
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
|
||||||
} else {
|
} else {
|
||||||
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
|
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
|
||||||
}
|
}
|
||||||
} else if (annotation.annotationInfo.contains("type")) {
|
} else if (annotation.annotationInfo.contains("type")) {
|
||||||
newAnnotationsMap += (annotation.id -> annotation)
|
newAnnotationsMap += (annotation.id -> annotation)
|
||||||
annotationsAdded :+= annotation
|
annotationsAdded :+= annotation
|
||||||
annotationsDiffAdded :+= annotation
|
|
||||||
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||||
} else {
|
} 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)
|
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||||
saveWhiteboard(newWb)
|
saveWhiteboard(newWb)
|
||||||
annotationsDiffAdded
|
annotationsAdded
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def getHistory(wbId: String): Array[AnnotationVO] = {
|
def getHistory(wbId: String): Array[AnnotationVO] = {
|
||||||
@ -127,42 +79,33 @@ class WhiteboardModel extends SystemConfiguration {
|
|||||||
wb.annotationsMap.values.toArray
|
wb.annotationsMap.values.toArray
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteAnnotations(wbId: String, meetingId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||||
val wb = getWhiteboard(wbId)
|
|
||||||
|
|
||||||
var annotationsIdsRemoved = Array[String]()
|
var annotationsIdsRemoved = Array[String]()
|
||||||
|
val wb = getWhiteboard(wbId)
|
||||||
var newAnnotationsMap = wb.annotationsMap
|
var newAnnotationsMap = wb.annotationsMap
|
||||||
|
|
||||||
for (annotationId <- annotationsIds) {
|
for (annotationId <- annotationsIds) {
|
||||||
val annotation = wb.annotationsMap.get(annotationId)
|
val annotation = wb.annotationsMap.get(annotationId)
|
||||||
|
|
||||||
if (annotation.isDefined) {
|
if (!annotation.isEmpty) {
|
||||||
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
|
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
newAnnotationsMap -= annotationId
|
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
|
annotationsIdsRemoved :+= annotationId
|
||||||
} else {
|
} 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.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||||
// Update whiteboard and save
|
saveWhiteboard(newWb)
|
||||||
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
|
|
||||||
saveWhiteboard(updatedWb)
|
|
||||||
|
|
||||||
PresAnnotationDAO.delete(meetingId, userId, annotationsIdsRemoved)
|
|
||||||
|
|
||||||
annotationsIdsRemoved
|
annotationsIdsRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
def modifyWhiteboardAccess(meetingId: String, wbId: String, multiUser: Array[String]) {
|
def modifyWhiteboardAccess(wbId: String, multiUser: Array[String]) {
|
||||||
val wb = getWhiteboard(wbId)
|
val wb = getWhiteboard(wbId)
|
||||||
val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
|
val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
|
||||||
PresPageWritersDAO.updateMultiuser(meetingId, newWb)
|
|
||||||
saveWhiteboard(newWb)
|
saveWhiteboard(newWb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.audiocaptions
|
package org.bigbluebutton.core.apps.audiocaptions
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
|
|
||||||
class AudioCaptionsApp2x(implicit val context: ActorContext)
|
class AudioCaptionsApp2x(implicit val context: ActorContext)
|
||||||
extends UpdateTranscriptPubMsgHdlr
|
extends UpdateTranscriptPubMsgHdlr
|
||||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.audiocaptions
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.UserTranscriptionErrorDAO
|
|
||||||
import org.bigbluebutton.core.models.AudioCaptions
|
import org.bigbluebutton.core.models.AudioCaptions
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -24,8 +23,5 @@ trait TranscriptionProviderErrorMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
broadcastEvent(msg.header.userId, msg.body.errorCode, msg.body.errorMessage)
|
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
|
package org.bigbluebutton.core.apps.audiocaptions
|
||||||
|
|
||||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsStringOrElse
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.CaptionDAO
|
import org.bigbluebutton.core.models.AudioCaptions
|
||||||
import org.bigbluebutton.core.models.{AudioCaptions, UserState, Pads, Users2x}
|
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
trait UpdateTranscriptPubMsgHdlr {
|
trait UpdateTranscriptPubMsgHdlr {
|
||||||
this: AudioCaptionsApp2x =>
|
this: AudioCaptionsApp2x =>
|
||||||
@ -26,17 +22,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
bus.outGW.send(msgEvent)
|
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
|
// Adapt to the current captions' recording process
|
||||||
def editTranscript(
|
def editTranscript(
|
||||||
userId: String,
|
userId: String,
|
||||||
@ -78,13 +63,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
|
|
||||||
val transcript = AudioCaptions.parseTranscript(msg.body.transcript)
|
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(
|
broadcastEvent(
|
||||||
msg.header.userId,
|
msg.header.userId,
|
||||||
msg.body.transcriptId,
|
msg.body.transcriptId,
|
||||||
@ -92,31 +70,6 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
msg.body.locale,
|
msg.body.locale,
|
||||||
msg.body.result,
|
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 SendMessageToAllBreakoutRoomsMsgHdlr
|
||||||
with SendMessageToBreakoutRoomInternalMsgHdlr
|
with SendMessageToBreakoutRoomInternalMsgHdlr
|
||||||
with RequestBreakoutJoinURLReqMsgHdlr
|
with RequestBreakoutJoinURLReqMsgHdlr
|
||||||
with SetBreakoutRoomInviteDismissedReqMsgHdlr
|
|
||||||
with SendBreakoutUsersUpdateMsgHdlr
|
with SendBreakoutUsersUpdateMsgHdlr
|
||||||
with TransferUserToMeetingRequestHdlr
|
with TransferUserToMeetingRequestHdlr
|
||||||
with EndBreakoutRoomInternalMsgHdlr
|
with EndBreakoutRoomInternalMsgHdlr
|
||||||
@ -63,23 +62,13 @@ object BreakoutRoomsUtil extends SystemConfiguration {
|
|||||||
checksum(apiCall.concat(baseString).concat(sharedSecret))
|
checksum(apiCall.concat(baseString).concat(sharedSecret))
|
||||||
}
|
}
|
||||||
|
|
||||||
def joinParams(
|
def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutMeetingId: String,
|
||||||
username: String,
|
password: String): (collection.immutable.Map[String, String], collection.immutable.Map[String, 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"
|
|
||||||
val params = collection.immutable.HashMap(
|
val params = collection.immutable.HashMap(
|
||||||
"fullName" -> urlEncode(username),
|
"fullName" -> urlEncode(username),
|
||||||
"userID" -> urlEncode(userId),
|
"userID" -> urlEncode(userId),
|
||||||
"isBreakout" -> urlEncode(isBreakout.toString()),
|
"isBreakout" -> urlEncode(isBreakout.toString()),
|
||||||
"meetingID" -> urlEncode(breakoutMeetingId),
|
"meetingID" -> urlEncode(breakoutMeetingId),
|
||||||
"avatarURL" -> urlEncode(avatarURL),
|
|
||||||
"userdata-bbb_parent_room_moderator" -> urlEncode(moderator.toString()),
|
|
||||||
"password" -> urlEncode(password),
|
"password" -> urlEncode(password),
|
||||||
"redirect" -> urlEncode("true")
|
"redirect" -> urlEncode("true")
|
||||||
)
|
)
|
||||||
|
@ -41,15 +41,8 @@ object BreakoutHdlrHelpers extends SystemConfiguration {
|
|||||||
for {
|
for {
|
||||||
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
||||||
apiCall = "join"
|
apiCall = "join"
|
||||||
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(
|
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
|
||||||
user.name,
|
externalMeetingId, liveMeeting.props.password.moderatorPass)
|
||||||
userId + "-" + roomSequence,
|
|
||||||
true,
|
|
||||||
externalMeetingId,
|
|
||||||
user.avatar,
|
|
||||||
user.role,
|
|
||||||
liveMeeting.props.password.moderatorPass
|
|
||||||
)
|
|
||||||
// We generate a first url with redirect -> true
|
// We generate a first url with redirect -> true
|
||||||
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
|
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
|
||||||
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
|
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
|
||||||
@ -125,7 +118,7 @@ object BreakoutHdlrHelpers extends SystemConfiguration {
|
|||||||
|
|
||||||
eventBus.publish(BigBlueButtonEvent(
|
eventBus.publish(BigBlueButtonEvent(
|
||||||
liveMeeting.props.breakoutProps.parentId,
|
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)
|
breakoutUsers, breakoutVoiceUsers)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.breakout
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.BreakoutRoomCreatedInternalMsg
|
import org.bigbluebutton.core.api.BreakoutRoomCreatedInternalMsg
|
||||||
import org.bigbluebutton.core.apps.BreakoutModel
|
import org.bigbluebutton.core.apps.BreakoutModel
|
||||||
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
|
||||||
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
|
|
||||||
@ -22,12 +21,9 @@ trait BreakoutRoomCreatedMsgHdlr {
|
|||||||
} yield {
|
} yield {
|
||||||
val updatedRoom = sendBreakoutRoomStarted(startedRoom)
|
val updatedRoom = sendBreakoutRoomStarted(startedRoom)
|
||||||
var updatedModel = breakoutModel.update(updatedRoom)
|
var updatedModel = breakoutModel.update(updatedRoom)
|
||||||
// BreakoutRoomDAO.updateRoomStarted(room.id)
|
|
||||||
|
|
||||||
// We postpone sending invitation until all breakout rooms have been created
|
// We postpone sending invitation until all breakout rooms have been created
|
||||||
if (updatedModel.hasAllStarted()) {
|
if (updatedModel.hasAllStarted()) {
|
||||||
updatedModel = updatedModel.copy(startedOn = Some(System.currentTimeMillis()))
|
updatedModel = updatedModel.copy(startedOn = Some(System.currentTimeMillis()))
|
||||||
BreakoutRoomDAO.updateRoomsStarted(room.parentId)
|
|
||||||
updatedModel = sendBreakoutRoomsList(updatedModel)
|
updatedModel = sendBreakoutRoomsList(updatedModel)
|
||||||
}
|
}
|
||||||
updatedModel
|
updatedModel
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
import org.bigbluebutton.core.api.BreakoutRoomEndedInternalMsg
|
import org.bigbluebutton.core.api.BreakoutRoomEndedInternalMsg
|
||||||
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO }
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
@ -37,9 +36,7 @@ trait BreakoutRoomEndedInternalMsgHdlr {
|
|||||||
Vector()
|
Vector()
|
||||||
)
|
)
|
||||||
outGW.send(notifyEvent)
|
outGW.send(notifyEvent)
|
||||||
NotificationDAO.insert(notifyEvent)
|
|
||||||
|
|
||||||
BreakoutRoomDAO.updateRoomsEnded(liveMeeting.props.meetingProp.intId)
|
|
||||||
state.update(None)
|
state.update(None)
|
||||||
} else {
|
} else {
|
||||||
state.update(Some(model))
|
state.update(Some(model))
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.BreakoutRoomUsersUpdateInternalMsg
|
import org.bigbluebutton.core.api.BreakoutRoomUsersUpdateInternalMsg
|
||||||
import org.bigbluebutton.core.db.{ BreakoutRoomUserDAO, UserBreakoutRoomDAO }
|
import org.bigbluebutton.core.domain.{ BreakoutRoom2x, MeetingState2x }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
|
||||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
|
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
|
|
||||||
@ -13,11 +13,24 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
|
|||||||
|
|
||||||
def handleBreakoutRoomUsersUpdateInternalMsg(msg: BreakoutRoomUsersUpdateInternalMsg, state: MeetingState2x): MeetingState2x = {
|
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 {
|
val breakoutModel = for {
|
||||||
model <- state.breakout
|
model <- state.breakout
|
||||||
room <- model.find(msg.breakoutId)
|
room <- model.find(msg.breakoutId)
|
||||||
} yield {
|
} yield {
|
||||||
val updatedRoom = room.copy(users = msg.users, voiceUsers = msg.voiceUsers)
|
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)
|
//Update user lastActivityTime in parent room (to avoid be ejected while is in Breakout room)
|
||||||
for {
|
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)
|
model.update(updatedRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,13 @@ package org.bigbluebutton.core.apps.breakout
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
||||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.getRedirectUrls
|
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.{ getRedirectUrls }
|
||||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||||
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, NotificationDAO}
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.EjectReasonCode
|
import org.bigbluebutton.core.models.{ EjectReasonCode }
|
||||||
import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter}
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||||
|
|
||||||
trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -29,6 +28,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
|||||||
for {
|
for {
|
||||||
breakoutModel <- state.breakout
|
breakoutModel <- state.breakout
|
||||||
} yield {
|
} yield {
|
||||||
|
|
||||||
//Eject user from room From
|
//Eject user from room From
|
||||||
for {
|
for {
|
||||||
roomFrom <- breakoutModel.rooms.get(msg.body.fromBreakoutId)
|
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
|
//Get join URL for room To
|
||||||
val redirectToHtml5JoinURL = (
|
val redirectToHtml5JoinURL = (
|
||||||
for {
|
for {
|
||||||
@ -49,6 +46,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
|||||||
} yield redirectToHtml5JoinURL
|
} yield redirectToHtml5JoinURL
|
||||||
).getOrElse("")
|
).getOrElse("")
|
||||||
|
|
||||||
|
|
||||||
BreakoutHdlrHelpers.sendChangeUserBreakoutMsg(
|
BreakoutHdlrHelpers.sendChangeUserBreakoutMsg(
|
||||||
outGW,
|
outGW,
|
||||||
meetingId,
|
meetingId,
|
||||||
@ -58,15 +56,6 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
|||||||
redirectToHtml5JoinURL,
|
redirectToHtml5JoinURL,
|
||||||
)
|
)
|
||||||
|
|
||||||
//Update database
|
|
||||||
BreakoutRoomUserDAO.updateRoomChanged(
|
|
||||||
meetingId,
|
|
||||||
msg.body.userId,
|
|
||||||
msg.body.fromBreakoutId,
|
|
||||||
msg.body.toBreakoutId,
|
|
||||||
redirectToHtml5JoinURL,
|
|
||||||
removePreviousRoomFromDb)
|
|
||||||
|
|
||||||
//Send notification to moved User
|
//Send notification to moved User
|
||||||
for {
|
for {
|
||||||
roomFrom <- breakoutModel.rooms.get(msg.body.fromBreakoutId)
|
roomFrom <- breakoutModel.rooms.get(msg.body.fromBreakoutId)
|
||||||
@ -82,7 +71,6 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
|||||||
Vector(roomTo.shortName)
|
Vector(roomTo.shortName)
|
||||||
)
|
)
|
||||||
outGW.send(notifyUserEvent)
|
outGW.send(notifyUserEvent)
|
||||||
NotificationDAO.insert(notifyUserEvent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse}
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{BreakoutModel, PermissionCheck, RightsManagementTrait}
|
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.domain.{BreakoutRoom2x, MeetingState2x}
|
|
||||||
import org.bigbluebutton.core.models.PresentationInPod
|
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
|
import org.bigbluebutton.core.running.MeetingActor
|
||||||
|
|
||||||
trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||||
@ -17,10 +15,6 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
def handleCreateBreakoutRoomsCmdMsg(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
|
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")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("breakoutRooms")) {
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
val reason = "Breakout rooms is disabled for this meeting."
|
val reason = "Breakout rooms is disabled for this meeting."
|
||||||
@ -32,15 +26,6 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
|
||||||
reason, outGW, liveMeeting)
|
reason, outGW, liveMeeting)
|
||||||
state
|
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 {
|
} else {
|
||||||
state.breakout match {
|
state.breakout match {
|
||||||
case Some(breakout) =>
|
case Some(breakout) =>
|
||||||
@ -69,9 +54,9 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
|
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
|
||||||
|
|
||||||
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName,
|
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName,
|
||||||
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
|
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
|
||||||
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename,
|
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename,
|
||||||
room.allPages, roomPresId)
|
room.allPages, roomPresId, room.sourcePresentationFilename)
|
||||||
|
|
||||||
rooms = rooms + (breakout.id -> breakout)
|
rooms = rooms + (breakout.id -> breakout)
|
||||||
}
|
}
|
||||||
@ -88,18 +73,18 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
breakout.freeJoin,
|
breakout.freeJoin,
|
||||||
liveMeeting.props.voiceProp.dialNumber,
|
liveMeeting.props.voiceProp.dialNumber,
|
||||||
breakout.voiceConf,
|
breakout.voiceConf,
|
||||||
msg.body.durationInMinutes,
|
msg.body.durationInMinutes * 60,
|
||||||
liveMeeting.props.password.moderatorPass,
|
liveMeeting.props.password.moderatorPass,
|
||||||
liveMeeting.props.password.viewerPass,
|
liveMeeting.props.password.viewerPass,
|
||||||
breakout.presId,
|
breakout.presId,
|
||||||
roomSlides,
|
roomSlides,
|
||||||
|
breakout.sourcePresentationFilename,
|
||||||
msg.body.record,
|
msg.body.record,
|
||||||
liveMeeting.props.breakoutProps.privateChatEnabled,
|
liveMeeting.props.breakoutProps.privateChatEnabled,
|
||||||
breakout.captureNotes,
|
breakout.captureNotes,
|
||||||
breakout.captureSlides,
|
breakout.captureSlides,
|
||||||
breakout.captureNotesFilename,
|
breakout.captureNotesFilename,
|
||||||
breakout.captureSlidesFilename,
|
breakout.captureSlidesFilename,
|
||||||
pluginProp = liveMeeting.props.pluginProp,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val event = buildCreateBreakoutRoomSysCmdMsg(liveMeeting.props.meetingProp.intId, roomDetail)
|
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)
|
val breakoutModel = new BreakoutModel(None, msg.body.durationInMinutes * 60, rooms, msg.body.sendInviteToModerators)
|
||||||
BreakoutRoomDAO.insert(breakoutModel, liveMeeting)
|
|
||||||
state.update(Some(breakoutModel))
|
state.update(Some(breakoutModel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@ package org.bigbluebutton.core.apps.breakout
|
|||||||
|
|
||||||
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
|
||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
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.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.Sender
|
import org.bigbluebutton.core2.message.senders.Sender
|
||||||
|
|
||||||
@ -28,12 +27,8 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
|
|||||||
msg.reasonCode,
|
msg.reasonCode,
|
||||||
msg.ban
|
msg.ban
|
||||||
)
|
)
|
||||||
|
// send a system message to force disconnection
|
||||||
//TODO inform reason
|
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
|
||||||
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 users update to parent meeting
|
//send users update to parent meeting
|
||||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
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.domain.{ MeetingEndReason, MeetingState2x }
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
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 {
|
trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -21,20 +20,25 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||||
state
|
state
|
||||||
} else {
|
} 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(
|
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||||
meetingId,
|
meetingId,
|
||||||
"info",
|
"info",
|
||||||
"rooms",
|
"rooms",
|
||||||
"app.toast.breakoutRoomEnded",
|
"app.toast.breakoutRoomEnded",
|
||||||
"Message when the breakout room is ended",
|
"Message when the breakout room is ended",
|
||||||
Vector()
|
Vector()
|
||||||
)
|
)
|
||||||
outGW.send(notifyEvent)
|
outGW.send(notifyEvent)
|
||||||
NotificationDAO.insert(notifyEvent)
|
|
||||||
|
}
|
||||||
|
|
||||||
BreakoutRoomDAO.updateRoomsEnded(meetingId)
|
|
||||||
state.update(None)
|
state.update(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
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.{ CaptureSharedNotesReqInternalMsg, CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
||||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
|
||||||
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
|
|
||||||
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus }
|
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 }
|
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||||
|
|
||||||
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||||
@ -23,80 +19,12 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (liveMeeting.props.breakoutProps.captureNotes) {
|
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)
|
log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
|
||||||
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system")
|
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.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.BreakoutRoomUserDAO
|
import org.bigbluebutton.core.models.{ Users2x, Roles }
|
||||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
|
||||||
|
|
||||||
trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
|
trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -24,9 +23,6 @@ trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
|
|||||||
requesterUser <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
requesterUser <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
} yield {
|
} yield {
|
||||||
if (requesterUser.role == Roles.MODERATOR_ROLE || room.freeJoin) {
|
if (requesterUser.role == Roles.MODERATOR_ROLE || room.freeJoin) {
|
||||||
|
|
||||||
BreakoutRoomUserDAO.insertBreakoutRoom(requesterUser.intId, room, liveMeeting)
|
|
||||||
|
|
||||||
BreakoutHdlrHelpers.sendJoinURL(
|
BreakoutHdlrHelpers.sendJoinURL(
|
||||||
liveMeeting,
|
liveMeeting,
|
||||||
outGW,
|
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
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
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.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||||
import org.bigbluebutton.core.db.NotificationDAO
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
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.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||||
|
|
||||||
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -33,7 +32,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
|||||||
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
|
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
|
||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
|
|
||||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||||
msg.header.userId,
|
msg.header.userId,
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
"info",
|
"info",
|
||||||
@ -42,8 +41,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
|||||||
"Message for chat sent successfully",
|
"Message for chat sent successfully",
|
||||||
Vector(s"${breakoutModel.rooms.size}")
|
Vector(s"${breakoutModel.rooms.size}")
|
||||||
)
|
)
|
||||||
outGW.send(notifyUserEvent)
|
outGW.send(notifyModeratorEvent)
|
||||||
NotificationDAO.insert(notifyUserEvent)
|
|
||||||
|
|
||||||
log.debug("Sending message '{}' to all breakout rooms in meeting {}", msg.body.msg, props.meetingProp.intId)
|
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
|
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.api.SendMessageToBreakoutRoomInternalMsg
|
||||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
@ -18,9 +18,9 @@ trait SendMessageToBreakoutRoomInternalMsgHdlr {
|
|||||||
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
||||||
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
||||||
} yield {
|
} yield {
|
||||||
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), msg.msg, replyToMessageId = "")
|
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), true, msg.msg)
|
||||||
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser, emphasizedText = true)
|
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser)
|
||||||
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
|
val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm)
|
||||||
|
|
||||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||||
liveMeeting.props.meetingProp.intId,
|
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.UserDAO
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.VoiceUsers
|
import org.bigbluebutton.core.models.VoiceUsers
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
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."
|
val reason = "No permission to transfer user to voice breakout."
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
processTransferUserToMeetingRequest(msg)
|
processRequest(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
def processTransferUserToMeetingRequest(msg: TransferUserToMeetingRequestMsg) {
|
def processRequest(msg: TransferUserToMeetingRequestMsg) {
|
||||||
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
|
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
|
||||||
// want to transfer from parent meeting to breakout
|
// want to transfer from parent meeting to breakout
|
||||||
for {
|
for {
|
||||||
@ -33,7 +32,6 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
|||||||
from <- getVoiceConf(msg.body.fromMeetingId, model)
|
from <- getVoiceConf(msg.body.fromMeetingId, model)
|
||||||
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
||||||
} yield {
|
} yield {
|
||||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
|
||||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
@ -55,7 +53,6 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
|||||||
room <- model.find(msg.body.fromMeetingId)
|
room <- model.find(msg.body.fromMeetingId)
|
||||||
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
|
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
|
||||||
} yield {
|
} yield {
|
||||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
|
||||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
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.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||||
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO, NotificationDAO }
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||||
@ -63,10 +62,9 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
|||||||
Vector(s"${msg.body.timeInMinutes}")
|
Vector(s"${msg.body.timeInMinutes}")
|
||||||
)
|
)
|
||||||
outGW.send(notifyEvent)
|
outGW.send(notifyEvent)
|
||||||
NotificationDAO.insert(notifyEvent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||||
msg.header.userId,
|
msg.header.userId,
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
"info",
|
"info",
|
||||||
@ -75,12 +73,9 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
|||||||
"Sent to the moderator that requested breakout duration change",
|
"Sent to the moderator that requested breakout duration change",
|
||||||
Vector(s"${msg.body.timeInMinutes}")
|
Vector(s"${msg.body.timeInMinutes}")
|
||||||
)
|
)
|
||||||
outGW.send(notifyUserEvent)
|
outGW.send(notifyModeratorEvent)
|
||||||
NotificationDAO.insert(notifyUserEvent)
|
|
||||||
|
|
||||||
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
|
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)
|
breakoutModel.setTime(newDurationInSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,11 +83,12 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
|||||||
val event = buildUpdateBreakoutRoomsTimeEvtMsg(msg.body.timeInMinutes)
|
val event = buildUpdateBreakoutRoomsTimeEvtMsg(msg.body.timeInMinutes)
|
||||||
outGW.send(event)
|
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 {
|
updatedModel match {
|
||||||
case Some(model) => {
|
case Some(model) => state.update(Some(model))
|
||||||
state.update(Some(model))
|
case None => state
|
||||||
}
|
|
||||||
case None => state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package org.bigbluebutton.core.apps.caption
|
package org.bigbluebutton.core.apps.caption
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
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.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.{ CaptionDAO, CaptionLocaleDAO, CaptionTypes }
|
|
||||||
|
|
||||||
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
|
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
|
||||||
val log = Logging(context.system, getClass)
|
val log = Logging(context.system, getClass)
|
||||||
@ -15,10 +14,18 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
|
|||||||
liveMeeting.captionModel.getHistory()
|
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 = {
|
def editCaptionHistory(liveMeeting: LiveMeeting, userId: String, startIndex: Integer, endIndex: Integer, name: String, text: String): Boolean = {
|
||||||
liveMeeting.captionModel.editHistory(userId, startIndex, endIndex, name, text)
|
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 = {
|
def isUserCaptionOwner(liveMeeting: LiveMeeting, userId: String, name: String): Boolean = {
|
||||||
liveMeeting.captionModel.isUserCaptionOwner(userId, name)
|
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 handle(msg: SendCaptionHistoryReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
def broadcastEvent(msg: SendCaptionHistoryReqMsg, history: Map[String, TranscriptVO]): Unit = {
|
def broadcastEvent(msg: SendCaptionHistoryReqMsg, history: Map[String, TranscriptVO]): Unit = {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
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))
|
broadcastEvent(msg, getCaptionHistory(liveMeeting))
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle(msg: AddCaptionLocalePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
def handle(msg: UpdateCaptionOwnerPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
def broadcastAddCaptionLocaleEvent(locale: String, userId: String): Unit = {
|
def broadcastUpdateCaptionOwnerEvent(name: String, locale: String, newOwnerId: String): Unit = {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
|
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||||
val envelope = BbbCoreEnvelope(AddCaptionLocaleEvtMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(UpdateCaptionOwnerEvtMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(AddCaptionLocaleEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
|
val header = BbbClientMsgHeader(UpdateCaptionOwnerEvtMsg.NAME, liveMeeting.props.meetingProp.intId, newOwnerId)
|
||||||
|
|
||||||
val body = AddCaptionLocaleEvtMsgBody(locale)
|
val body = UpdateCaptionOwnerEvtMsgBody(name, locale, newOwnerId)
|
||||||
val event = AddCaptionLocaleEvtMsg(header, body)
|
val event = UpdateCaptionOwnerEvtMsg(header, body)
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
bus.outGW.send(msgEvent)
|
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)) {
|
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
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)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} 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
|
package org.bigbluebutton.core.apps.chat
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
|
|
||||||
class ChatApp2x(implicit val context: ActorContext)
|
class ChatApp2x(implicit val context: ActorContext)
|
||||||
extends GetChatHistoryReqMsgHdlr
|
extends GetChatHistoryReqMsgHdlr
|
||||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.chat
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ ChatModel, PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ ChatModel, PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.ChatMessageDAO
|
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
|
||||||
@ -32,8 +31,6 @@ trait ClearPublicChatHistoryPubMsgHdlr extends LogHelper with RightsManagementTr
|
|||||||
val newState = for {
|
val newState = for {
|
||||||
gc <- state.groupChats.find(msg.body.chatId)
|
gc <- state.groupChats.find(msg.body.chatId)
|
||||||
} yield {
|
} 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)
|
broadcastEvent(msg)
|
||||||
val newGc = gc.clearMessages()
|
val newGc = gc.clearMessages()
|
||||||
val gcs = state.groupChats.update(newGc)
|
val gcs = state.groupChats.update(newGc)
|
||||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.chat
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.ChatUserDAO
|
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
|
||||||
|
|
||||||
trait UserTypingPubMsgHdlr extends LogHelper {
|
trait UserTypingPubMsgHdlr extends LogHelper {
|
||||||
@ -17,7 +16,6 @@ trait UserTypingPubMsgHdlr extends LogHelper {
|
|||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
}
|
}
|
||||||
ChatUserDAO.updateUserTyping(liveMeeting.props.meetingProp.intId, msg.body.chatId, msg.header.userId)
|
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.externalvideo
|
package org.bigbluebutton.core.apps.externalvideo
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
|
|
||||||
class ExternalVideoApp2x(implicit val context: ActorContext)
|
class ExternalVideoApp2x(implicit val context: ActorContext)
|
||||||
extends StartExternalVideoPubMsgHdlr
|
extends StartExternalVideoPubMsgHdlr
|
||||||
|
@ -4,8 +4,7 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x.requestBroadcastStop
|
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x.{ requestBroadcastStop }
|
||||||
import org.bigbluebutton.core.db.ExternalVideoDAO
|
|
||||||
|
|
||||||
trait StartExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
trait StartExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||||
this: ExternalVideoApp2x =>
|
this: ExternalVideoApp2x =>
|
||||||
@ -40,7 +39,6 @@ trait StartExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
|||||||
requestBroadcastStop(bus.outGW, liveMeeting)
|
requestBroadcastStop(bus.outGW, liveMeeting)
|
||||||
|
|
||||||
ExternalVideoModel.setURL(liveMeeting.externalVideoModel, msg.body.externalVideoUrl)
|
ExternalVideoModel.setURL(liveMeeting.externalVideoModel, msg.body.externalVideoUrl)
|
||||||
ExternalVideoDAO.insert(liveMeeting.props.meetingProp.intId, msg.body.externalVideoUrl)
|
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.externalvideo
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ ExternalVideoModel, PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.ExternalVideoDAO
|
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
|
|
||||||
@ -20,8 +19,6 @@ trait StopExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
|||||||
} else {
|
} else {
|
||||||
ExternalVideoModel.clear(liveMeeting.externalVideoModel)
|
ExternalVideoModel.clear(liveMeeting.externalVideoModel)
|
||||||
|
|
||||||
ExternalVideoDAO.updateStoppedSharing(liveMeeting.props.meetingProp.intId)
|
|
||||||
|
|
||||||
//broadcastEvent
|
//broadcastEvent
|
||||||
val msgEvent = MsgBuilder.buildStopExternalVideoEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val msgEvent = MsgBuilder.buildStopExternalVideoEvtMsg(liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
|
@ -3,8 +3,7 @@ package org.bigbluebutton.core.apps.externalvideo
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
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 {
|
trait UpdateExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ trait UpdateExternalVideoPubMsgHdlr extends RightsManagementTrait {
|
|||||||
val reason = "You need to be the presenter to update external video"
|
val reason = "You need to be the presenter to update external video"
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
ExternalVideoDAO.update(liveMeeting.props.meetingProp.intId, msg.body.status, msg.body.rate, msg.body.time, msg.body.state)
|
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import org.bigbluebutton.core.models.GroupChat
|
|||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.apps.PermissionCheck
|
import org.bigbluebutton.core.apps.PermissionCheck
|
||||||
import org.bigbluebutton.SystemConfiguration
|
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.Users2x
|
||||||
import org.bigbluebutton.core.models.Roles
|
import org.bigbluebutton.core.models.Roles
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
@ -51,33 +49,27 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
state
|
state
|
||||||
} else {
|
} else {
|
||||||
GroupChatApp.getGroupChatOfUsers(msg.header.userId, msg.body.users, state) match {
|
val newState = for {
|
||||||
case Some(groupChat) =>
|
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||||
ChatUserDAO.updateChatVisible(msg.header.meetingId, groupChat.id, msg.header.userId, visible = true)
|
} yield {
|
||||||
state
|
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m))
|
||||||
case None =>
|
val users = {
|
||||||
val newState = for {
|
if (msg.body.access == GroupChatAccess.PRIVATE) {
|
||||||
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
val cu = msg.body.users.toSet + msg.header.userId
|
||||||
} yield {
|
cu.flatMap(u => GroupChatApp.findGroupChatUser(u, liveMeeting.users2x)).toVector
|
||||||
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m, emphasizedText = false))
|
} else {
|
||||||
val users = {
|
Vector.empty
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
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
|
package org.bigbluebutton.core.apps.groupchats
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMessageType, GroupChatMsgFromUser, GroupChatMsgToUser, GroupChatUser }
|
import org.bigbluebutton.common2.msgs.{ GroupChatAccess, GroupChatMsgFromUser, GroupChatMsgToUser, GroupChatUser }
|
||||||
import org.bigbluebutton.core.db.ChatMessageDAO
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
object GroupChatApp {
|
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"
|
val MAIN_PUBLIC_CHAT = "MAIN-PUBLIC-GROUP-CHAT"
|
||||||
|
|
||||||
@ -20,10 +15,10 @@ object GroupChatApp {
|
|||||||
GroupChatFactory.create(gcId, access, createBy, users, msgs)
|
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 now = System.currentTimeMillis()
|
||||||
val id = GroupChatFactory.genId()
|
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 = {
|
def toMessageToUser(msg: GroupChatMessage): GroupChatMsgToUser = {
|
||||||
@ -31,32 +26,12 @@ object GroupChatApp {
|
|||||||
sender = msg.sender, chatEmphasizedText = msg.chatEmphasizedText, message = msg.message)
|
sender = msg.sender, chatEmphasizedText = msg.chatEmphasizedText, message = msg.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
def addGroupChatMessage(meetingId: String, chat: GroupChat, chats: GroupChats,
|
def addGroupChatMessage(chat: GroupChat, chats: GroupChats,
|
||||||
msg: GroupChatMessage, messageType: String = GroupChatMessageType.DEFAULT): GroupChats = {
|
msg: GroupChatMessage): 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
val c = chat.add(msg)
|
val c = chat.add(msg)
|
||||||
chats.update(c)
|
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] = {
|
def findGroupChatUser(userId: String, users: Users2x): Option[GroupChatUser] = {
|
||||||
Users2x.findWithIntId(users, userId) match {
|
Users2x.findWithIntId(users, userId) match {
|
||||||
case Some(u) => Some(GroupChatUser(u.intId, u.name, u.role))
|
case Some(u) => Some(GroupChatUser(u.intId, u.name, u.role))
|
||||||
@ -94,9 +69,9 @@ object GroupChatApp {
|
|||||||
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
|
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
|
||||||
chat <- state.groupChats.find(chatId)
|
chat <- state.groupChats.find(chatId)
|
||||||
} yield {
|
} yield {
|
||||||
val emphasizedText = sender.role == Roles.MODERATOR_ROLE
|
|
||||||
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg, emphasizedText)
|
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg)
|
||||||
val gcs1 = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm1)
|
val gcs1 = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm1)
|
||||||
state.update(gcs1)
|
state.update(gcs1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,9 +82,9 @@ object GroupChatApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sender = GroupChatUser(SystemUser.ID)
|
val sender = GroupChatUser(SystemUser.ID)
|
||||||
val h1 = GroupChatMsgFromUser(correlationId = "cor1", sender = sender, message = "Hello Foo!", replyToMessageId = "")
|
val h1 = GroupChatMsgFromUser(correlationId = "cor1", sender = sender, message = "Hello Foo!")
|
||||||
val h2 = GroupChatMsgFromUser(correlationId = "cor2", sender = sender, message = "Hello Bar!", replyToMessageId = "")
|
val h2 = GroupChatMsgFromUser(correlationId = "cor2", sender = sender, message = "Hello Bar!")
|
||||||
val h3 = GroupChatMsgFromUser(correlationId = "cor3", sender = sender, message = "Hello Baz!", replyToMessageId = "")
|
val h3 = GroupChatMsgFromUser(correlationId = "cor3", sender = sender, message = "Hello Baz!")
|
||||||
val state1 = addH(state, SystemUser.ID, liveMeeting, h1)
|
val state1 = addH(state, SystemUser.ID, liveMeeting, h1)
|
||||||
val state2 = addH(state1, SystemUser.ID, liveMeeting, h2)
|
val state2 = addH(state1, SystemUser.ID, liveMeeting, h2)
|
||||||
val state3 = addH(state2, SystemUser.ID, liveMeeting, h3)
|
val state3 = addH(state2, SystemUser.ID, liveMeeting, h3)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.groupchats
|
package org.bigbluebutton.core.apps.groupchats
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
|
|
||||||
class GroupChatHdlrs(implicit val context: ActorContext)
|
class GroupChatHdlrs(implicit val context: ActorContext)
|
||||||
extends CreateGroupChatReqMsgHdlr
|
extends CreateGroupChatReqMsgHdlr
|
||||||
@ -9,13 +9,7 @@ class GroupChatHdlrs(implicit val context: ActorContext)
|
|||||||
with GetGroupChatMsgsReqMsgHdlr
|
with GetGroupChatMsgsReqMsgHdlr
|
||||||
with GetGroupChatsReqMsgHdlr
|
with GetGroupChatsReqMsgHdlr
|
||||||
with SendGroupChatMessageMsgHdlr
|
with SendGroupChatMessageMsgHdlr
|
||||||
with EditGroupChatMessageReqMsgHdlr
|
with SyncGetGroupChatsInfoMsgHdlr {
|
||||||
with DeleteGroupChatMessageReqMsgHdlr
|
|
||||||
with SendGroupChatMessageReactionReqMsgHdlr
|
|
||||||
with DeleteGroupChatMessageReactionReqMsgHdlr
|
|
||||||
with SendGroupChatMessageFromApiSysPubMsgHdlr
|
|
||||||
with SetGroupChatVisibleReqMsgHdlr
|
|
||||||
with SetGroupChatLastSeenReqMsgHdlr {
|
|
||||||
|
|
||||||
val log = Logging(context.system, getClass)
|
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
|
package org.bigbluebutton.core.apps.groupchats
|
||||||
|
|
||||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsBooleanOrElse
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.PermissionCheck
|
import org.bigbluebutton.core.apps.PermissionCheck
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
@ -16,27 +15,13 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
|||||||
def handle(msg: SendGroupChatMessageMsg, state: MeetingState2x,
|
def handle(msg: SendGroupChatMessageMsg, state: MeetingState2x,
|
||||||
liveMeeting: LiveMeeting, bus: MessageBus): 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 chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
|
||||||
val replyChatMessageDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("replyChatMessage")
|
|
||||||
var chatLocked: Boolean = false
|
var chatLocked: Boolean = false
|
||||||
var chatLockedForUser: Boolean = false
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
groupChat <- state.groupChats.find(msg.body.chatId)
|
groupChat <- state.groupChats.find(msg.body.chatId)
|
||||||
} yield {
|
} yield {
|
||||||
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
|
|
||||||
chatLockedForUser = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.role != Roles.MODERATOR_ROLE && user.locked) {
|
if (user.role != Roles.MODERATOR_ROLE && user.locked) {
|
||||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||||
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
if (groupChat.access == GroupChatAccess.PRIVATE) {
|
||||||
@ -45,7 +30,7 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
|||||||
case None => false
|
case None => false
|
||||||
})
|
})
|
||||||
// don't lock private chats that involve a moderator
|
// don't lock private chats that involve a moderator
|
||||||
if (modMembers.isEmpty) {
|
if (modMembers.length == 0) {
|
||||||
chatLocked = permissions.disablePrivChat
|
chatLocked = permissions.disablePrivChat
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -54,50 +39,32 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chatDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
|
if (!chatDisabled && !(applyPermissionCheck && chatLocked)) {
|
||||||
val newState = for {
|
val newState = for {
|
||||||
sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||||
chat <- state.groupChats.find(msg.body.chatId)
|
chat <- state.groupChats.find(msg.body.chatId)
|
||||||
} yield {
|
} yield {
|
||||||
val chatIsPrivate = chat.access == GroupChatAccess.PRIVATE;
|
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) {
|
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||||
val moderatorChatEmphasizedEnabled = getConfigPropertyValueByPathAsBooleanOrElse(
|
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg)
|
||||||
liveMeeting.clientSettings,
|
val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm)
|
||||||
"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 event = buildGroupChatMessageBroadcastEvtMsg(
|
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
msg.header.userId, msg.body.chatId, gcMessage
|
msg.header.userId, msg.body.chatId, gcm
|
||||||
)
|
)
|
||||||
|
|
||||||
bus.outGW.send(event)
|
bus.outGW.send(event)
|
||||||
|
|
||||||
state.update(updatedGroupChat)
|
state.update(gcs)
|
||||||
} else {
|
} else {
|
||||||
val reason = "User isn't a participant of the chat"
|
val reason = "User isn't a participant of the chat"
|
||||||
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newState match {
|
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.core.running.OutMsgRouter
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
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 {
|
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
||||||
this: LayoutApp2x =>
|
this: LayoutApp2x =>
|
||||||
@ -36,7 +35,6 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
|||||||
Layouts.setPresentationVideoRate(liveMeeting.layouts, msg.body.presentationVideoRate)
|
Layouts.setPresentationVideoRate(liveMeeting.layouts, msg.body.presentationVideoRate)
|
||||||
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
||||||
|
|
||||||
LayoutDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, liveMeeting.layouts)
|
|
||||||
sendBroadcastLayoutEvtMsg(msg.header.userId)
|
sendBroadcastLayoutEvtMsg(msg.header.userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +71,6 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
|||||||
Vector()
|
Vector()
|
||||||
)
|
)
|
||||||
outGW.send(notifyEvent)
|
outGW.send(notifyEvent)
|
||||||
NotificationDAO.insert(notifyEvent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.bigbluebutton.core.apps.layout
|
package org.bigbluebutton.core.apps.layout
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
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.running.OutMsgRouter
|
||||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.LayoutDAO
|
|
||||||
|
|
||||||
trait BroadcastPushLayoutMsgHdlr extends RightsManagementTrait {
|
trait BroadcastPushLayoutMsgHdlr extends RightsManagementTrait {
|
||||||
this: LayoutApp2x =>
|
this: LayoutApp2x =>
|
||||||
@ -21,7 +20,6 @@ trait BroadcastPushLayoutMsgHdlr extends RightsManagementTrait {
|
|||||||
Layouts.setPushLayout(liveMeeting.layouts, msg.body.pushLayout)
|
Layouts.setPushLayout(liveMeeting.layouts, msg.body.pushLayout)
|
||||||
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
Layouts.setRequestedBy(liveMeeting.layouts, msg.header.userId)
|
||||||
|
|
||||||
LayoutDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, liveMeeting.layouts)
|
|
||||||
sendBroadcastPushLayoutEvtMsg(msg.header.userId)
|
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.{ SharedNotesRevDAO }
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -10,6 +9,7 @@ trait PadContentSysMsgHdlr {
|
|||||||
this: PadsApp2x =>
|
this: PadsApp2x =>
|
||||||
|
|
||||||
def handle(msg: PadContentSysMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
def handle(msg: PadContentSysMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
|
|
||||||
def broadcastEvent(externalId: String, padId: String, rev: String, start: Int, end: Int, text: String): 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 routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||||
val envelope = BbbCoreEnvelope(PadContentEvtMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(PadContentEvtMsg.NAME, routing)
|
||||||
@ -22,18 +22,8 @@ trait PadContentSysMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, msg.body.padId, msg.body.rev, msg.body.start, msg.body.end, msg.body.text)
|
||||||
SharedNotesRevDAO.update(
|
case _ =>
|
||||||
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 _ =>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.SharedNotesDAO
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -23,12 +22,8 @@ trait PadCreatedEvtMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, group.userId, msg.body.padId, msg.body.name)
|
||||||
Pads.setPadId(liveMeeting.pads, group.externalId, msg.body.padId)
|
case _ =>
|
||||||
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 _ =>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,22 @@ trait PadGroupCreatedEvtMsgHdlr {
|
|||||||
this: PadsApp2x =>
|
this: PadsApp2x =>
|
||||||
|
|
||||||
def handle(msg: PadGroupCreatedEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
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 {
|
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||||
case Some(group) => {
|
case Some(group) => {
|
||||||
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId)
|
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId)
|
||||||
|
broadcastEvent(msg.body.externalId, group.model, group.name, group.userId)
|
||||||
//Group was created, now request to create the pad
|
|
||||||
PadslHdlrHelpers.broadcastPadCreateCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.groupId, msg.body.externalId)
|
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.pads
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.CaptionDAO
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
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)
|
broadcastEditCaptionHistoryEvent(msg.body.userId, msg.body.start, msg.body.end, group.name, locale, msg.body.text)
|
||||||
val tail = liveMeeting.captionModel.getTextTail(group.name)
|
val tail = liveMeeting.captionModel.getTextTail(group.name)
|
||||||
broadcastPadTailEvent(group.externalId, tail)
|
broadcastPadTailEvent(group.externalId, tail)
|
||||||
CaptionDAO.insertOrUpdatePadCaption(liveMeeting.props.meetingProp.intId, locale, msg.body.userId, tail)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.apps.pads
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.SharedNotesDAO
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -30,11 +29,8 @@ trait PadPinnedReqMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, msg.body.pinned)
|
||||||
SharedNotesDAO.updatePinned(liveMeeting.props.meetingProp.intId, msg.body.externalId, msg.body.pinned)
|
case _ =>
|
||||||
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.SharedNotesSessionDAO
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -23,11 +22,8 @@ trait PadSessionCreatedEvtMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||||
SharedNotesSessionDAO.insert(liveMeeting.props.meetingProp.intId, group.externalId, msg.body.userId, msg.body.sessionId)
|
case _ =>
|
||||||
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.SharedNotesSessionDAO
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -23,11 +22,8 @@ trait PadSessionDeletedSysMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||||
SharedNotesSessionDAO.delete(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.sessionId)
|
case _ =>
|
||||||
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.{ SharedNotesRevDAO }
|
|
||||||
import org.bigbluebutton.core.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -23,12 +22,8 @@ trait PadUpdatedSysMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||||
case Some(group) => {
|
case Some(group) => broadcastEvent(group.externalId, msg.body.padId, msg.body.userId, msg.body.rev, msg.body.changeset)
|
||||||
Pads.setRev(liveMeeting.pads, group.externalId, msg.body.rev)
|
case _ =>
|
||||||
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 _ =>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package org.bigbluebutton.core.apps.pads
|
package org.bigbluebutton.core.apps.pads
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
|
|
||||||
class PadsApp2x(implicit val context: ActorContext)
|
class PadsApp2x(implicit val context: ActorContext)
|
||||||
extends PadGroupCreatedEvtMsgHdlr
|
extends PadCreateGroupReqMsgHdlr
|
||||||
|
with PadGroupCreatedEvtMsgHdlr
|
||||||
with PadCreateReqMsgHdlr
|
with PadCreateReqMsgHdlr
|
||||||
with PadCreatedEvtMsgHdlr
|
with PadCreatedEvtMsgHdlr
|
||||||
with PadCreateSessionReqMsgHdlr
|
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
|
package org.bigbluebutton.core.apps.polls
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
|
|
||||||
class PollApp2x(implicit val context: ActorContext)
|
class PollApp2x(implicit val context: ActorContext)
|
||||||
extends GetCurrentPollReqMsgHdlr
|
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.models.Polls
|
import org.bigbluebutton.core.models.Polls
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||||
import org.bigbluebutton.core.models.Users2x
|
import org.bigbluebutton.core.models.Users2x
|
||||||
|
|
||||||
trait RespondToPollReqMsgHdlr {
|
trait RespondToPollReqMsgHdlr {
|
||||||
@ -12,12 +12,45 @@ trait RespondToPollReqMsgHdlr {
|
|||||||
|
|
||||||
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
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 {
|
for {
|
||||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||||
msg.body.questionId, msg.body.answerIds, liveMeeting)
|
msg.body.questionId, msg.body.answerIds, liveMeeting)
|
||||||
} yield {
|
} yield {
|
||||||
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
||||||
for {
|
for {
|
||||||
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
||||||
} yield {
|
} yield {
|
||||||
@ -25,14 +58,14 @@ trait RespondToPollReqMsgHdlr {
|
|||||||
answerId <- msg.body.answerIds
|
answerId <- msg.body.answerIds
|
||||||
} yield {
|
} yield {
|
||||||
val answerText = poll.questions(0).answers.get(answerId).key
|
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 {
|
for {
|
||||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||||
} yield {
|
} 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 {
|
} else {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package org.bigbluebutton.core.apps.polls
|
package org.bigbluebutton.core.apps.polls
|
||||||
|
|
||||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsIntOrElse
|
|
||||||
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.models.Polls
|
import org.bigbluebutton.core.models.Polls
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||||
import org.bigbluebutton.core.models.Users2x
|
import org.bigbluebutton.core.models.Users2x
|
||||||
|
|
||||||
trait RespondToTypedPollReqMsgHdlr {
|
trait RespondToTypedPollReqMsgHdlr {
|
||||||
@ -13,60 +12,43 @@ trait RespondToTypedPollReqMsgHdlr {
|
|||||||
|
|
||||||
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
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) &&
|
if (Polls.isResponsePollType(msg.body.pollId, liveMeeting.polls) &&
|
||||||
!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) &&
|
Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false &&
|
||||||
!Polls.hasUserAlreadyAddedTypedAnswer(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
|
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`
|
for {
|
||||||
val maxTypedAnswerLength = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.poll.maxTypedAnswerLength", 45)
|
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||||
val answer = msg.body.answer.substring(0, Math.min(msg.body.answer.length, maxTypedAnswerLength))
|
} yield {
|
||||||
|
broadcastUserRespondedToTypedPollRespMsg(msg, pollId, msg.body.answer, presenter.intId)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 {
|
} 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)
|
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.domain.SimplePollResultOutVO
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.Polls
|
import org.bigbluebutton.core.models.Polls
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.{ChatMessageDAO, JsonUtils, NotificationDAO}
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
|
||||||
import spray.json.DefaultJsonProtocol.jsonFormat2
|
|
||||||
|
|
||||||
trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
||||||
this: PollApp2x =>
|
this: PollApp2x =>
|
||||||
@ -37,7 +34,6 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
|||||||
Vector()
|
Vector()
|
||||||
)
|
)
|
||||||
bus.outGW.send(notifyEvent)
|
bus.outGW.send(notifyEvent)
|
||||||
NotificationDAO.insert(notifyEvent)
|
|
||||||
|
|
||||||
// SendWhiteboardAnnotationPubMsg
|
// SendWhiteboardAnnotationPubMsg
|
||||||
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
@ -58,27 +54,6 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
|
|||||||
for {
|
for {
|
||||||
(result, annotationProp) <- Polls.handleShowPollResultReqMsg(state, msg.header.userId, msg.body.pollId, liveMeeting)
|
(result, annotationProp) <- Polls.handleShowPollResultReqMsg(state, msg.header.userId, msg.body.pollId, liveMeeting)
|
||||||
} yield {
|
} 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)
|
broadcastEvent(msg, result, annotationProp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.presentation
|
package org.bigbluebutton.core.apps.presentation
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import akka.event.Logging
|
||||||
import org.bigbluebutton.core.apps.Presentation
|
import org.bigbluebutton.core.apps.Presentation
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package org.bigbluebutton.core.apps.presentationpod
|
package org.bigbluebutton.core.apps.presentationpod
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg }
|
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, CaptureSharedNotesReqInternalMsg }
|
||||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
|
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||||
@ -45,20 +44,19 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
||||||
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
|
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
|
||||||
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
|
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)
|
NewPresFileAvailableMsg(header, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildBroadcastNewPresFileAvailable(newPresFileAvailableMsg: NewPresFileAvailableMsg, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
def buildBroadcastNewPresFileAvailable(newPresFileAvailableMsg: NewPresFileAvailableMsg, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, "not-used")
|
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 header = BbbClientMsgHeader(NewPresFileAvailableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
|
||||||
val body = NewPresFileAvailableEvtMsgBody(
|
val body = NewPresFileAvailableEvtMsgBody(
|
||||||
annotatedFileURI = newPresFileAvailableMsg.body.annotatedFileURI,
|
annotatedFileURI = newPresFileAvailableMsg.body.annotatedFileURI,
|
||||||
originalFileURI = newPresFileAvailableMsg.body.originalFileURI,
|
originalFileURI = newPresFileAvailableMsg.body.originalFileURI,
|
||||||
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI,
|
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI, presId = newPresFileAvailableMsg.body.presId,
|
||||||
presId = newPresFileAvailableMsg.body.presId,
|
|
||||||
fileStateType = newPresFileAvailableMsg.body.fileStateType
|
fileStateType = newPresFileAvailableMsg.body.fileStateType
|
||||||
)
|
)
|
||||||
val event = NewPresFileAvailableEvtMsg(header, body)
|
val event = NewPresFileAvailableEvtMsg(header, body)
|
||||||
@ -85,11 +83,11 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
BbbCommonEnvCoreMsg(envelope, event)
|
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 routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
|
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentId, userId)
|
||||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
|
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentId)
|
||||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||||
BbbCommonEnvCoreMsg(envelope, event)
|
BbbCommonEnvCoreMsg(envelope, event)
|
||||||
}
|
}
|
||||||
@ -103,11 +101,13 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
val whiteboardId = s"${presId}/${pageNumber.toString}"
|
val whiteboardId = s"${presId}/${pageNumber.toString}"
|
||||||
val presentationPage: PresentationPage = currentPres.get.pages(whiteboardId)
|
val presentationPage: PresentationPage = currentPres.get.pages(whiteboardId)
|
||||||
val width: Double = presentationPage.width
|
val xOffset: Double = presentationPage.xOffset
|
||||||
val height: Double = presentationPage.height
|
val yOffset: Double = presentationPage.yOffset
|
||||||
|
val widthRatio: Double = presentationPage.widthRatio
|
||||||
|
val heightRatio: Double = presentationPage.heightRatio
|
||||||
val whiteboardHistory: Array[AnnotationVO] = liveMeeting.wbModel.getHistory(whiteboardId)
|
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)
|
getPresentationPagesForExport(pages, pageCount, presId, currentPres, liveMeeting, storeAnnotationPages :+ page)
|
||||||
} else {
|
} else {
|
||||||
getPresentationPagesForExport(pages, pageCount, presId, currentPres, liveMeeting, storeAnnotationPages)
|
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 pages: List[Int] = m.body.pages // Desired presentation pages for export
|
||||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
|
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 storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||||
|
|
||||||
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
|
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
|
||||||
@ -203,22 +203,15 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
val filename = m.filename
|
val filename = m.filename
|
||||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
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
|
// 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))
|
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(parentMeetingId, userId, presentationUploadToken, 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 (liveMeeting.props.meetingProp.disabledFeatures.contains("importPresentationWithAnnotationsFromBreakoutRooms")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("importPresentationWithAnnotationsFromBreakoutRooms")) {
|
||||||
log.error(s"Capturing breakout rooms slides disabled in meeting ${meetingId}.")
|
log.error(s"Capturing breakout rooms slides disabled in meeting ${meetingId}.")
|
||||||
} else if (currentPres.isEmpty) {
|
} else if (currentPres.isEmpty) {
|
||||||
log.error(s"No presentation set in meeting ${meetingId}")
|
log.error(s"No presentation set in meeting ${meetingId}")
|
||||||
pres = pres.copy(errorMsgKey = "204")
|
|
||||||
bus.outGW.send(buildBroadcastPresentationConversionUpdateEvtMsg(parentMeetingId, "204", jobId, filename, presentationUploadToken))
|
bus.outGW.send(buildBroadcastPresentationConversionUpdateEvtMsg(parentMeetingId, "204", jobId, filename, presentationUploadToken))
|
||||||
PresPresentationDAO.updateConversionStarted(parentMeetingId, pres)
|
|
||||||
} else {
|
} else {
|
||||||
val allPages: Boolean = m.allPages
|
val allPages: Boolean = m.allPages
|
||||||
val pageCount = currentPres.get.pages.size
|
val pageCount = currentPres.get.pages.size
|
||||||
@ -229,7 +222,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
|
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
|
||||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
|
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 storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||||
|
|
||||||
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
|
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
|
||||||
@ -243,13 +236,9 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val annotations = new StoredAnnotations(jobId, presId, storeAnnotationPages)
|
val annotations = new StoredAnnotations(jobId, presId, storeAnnotationPages)
|
||||||
bus.outGW.send(buildStoreAnnotationsInRedisSysMsg(annotations, liveMeeting))
|
bus.outGW.send(buildStoreAnnotationsInRedisSysMsg(annotations, liveMeeting))
|
||||||
} else {
|
} else {
|
||||||
pres = pres.copy(errorMsgKey = "204")
|
|
||||||
|
|
||||||
// Notify that no content is available to capture
|
// Notify that no content is available to capture
|
||||||
bus.outGW.send(buildBroadcastPresentationConversionUpdateEvtMsg(parentMeetingId, "204", jobId, filename, presentationUploadToken))
|
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={}",
|
"Received NewPresFileAvailableMsg meetingId={} presId={}",
|
||||||
liveMeeting.props.meetingProp.intId, m.body.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))
|
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 = {
|
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))
|
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.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.PresPresentationDAO
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.PresentationInPod
|
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
||||||
@ -32,30 +30,7 @@ trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
|||||||
bus.outGW.send(msgEvent)
|
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)
|
broadcastEvent(msg)
|
||||||
|
state
|
||||||
newState match {
|
|
||||||
case Some(ns) => ns
|
|
||||||
case None => state
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,17 @@ package org.bigbluebutton.core.apps.presentationpod
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.PresPresentationDAO
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.PresentationInPod
|
import org.bigbluebutton.core.models.PresentationInPod
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
trait PresentationConversionCompletedSysPubMsgHdlr {
|
trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||||
|
|
||||||
this: PresentationPodHdlrs =>
|
this: PresentationPodHdlrs =>
|
||||||
|
|
||||||
def handle(
|
def handle(
|
||||||
msg: PresentationConversionCompletedSysPubMsg,
|
msg: PresentationConversionCompletedSysPubMsg, state: MeetingState2x,
|
||||||
state: MeetingState2x,
|
liveMeeting: LiveMeeting, bus: MessageBus
|
||||||
liveMeeting: LiveMeeting,
|
|
||||||
bus: MessageBus
|
|
||||||
): MeetingState2x = {
|
): MeetingState2x = {
|
||||||
|
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
@ -25,7 +23,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
|||||||
pres <- pod.getPresentation(msg.body.presentation.id)
|
pres <- pod.getPresentation(msg.body.presentation.id)
|
||||||
} yield {
|
} yield {
|
||||||
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres, temporaryPresentationId,
|
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres, temporaryPresentationId,
|
||||||
msg.body.presentation.defaultPresentation, msg.body.presentation.filenameConverted)
|
msg.body.presentation.isInitialPresentation, msg.body.presentation.filenameConverted)
|
||||||
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(
|
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(
|
||||||
bus,
|
bus,
|
||||||
meetingId,
|
meetingId,
|
||||||
@ -48,13 +46,11 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
|||||||
originalDownloadableExtension
|
originalDownloadableExtension
|
||||||
)
|
)
|
||||||
|
|
||||||
val presWithConvertedName = PresentationInPod(pres.id, pres.name, default = msg.body.presentation.defaultPresentation,
|
val presWithConvertedName = PresentationInPod(pres.id, pres.name, pres.current, pres.pages,
|
||||||
pres.current, pres.pages, pres.downloadable, pres.downloadFileExtension, pres.removable, msg.body.presentation.filenameConverted,
|
pres.downloadable, pres.removable, msg.body.presentation.filenameConverted)
|
||||||
uploadCompleted = true, numPages = pres.numPages, errorDetails = Map.empty)
|
|
||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, presWithConvertedName)
|
pods = pods.addPresentationToPod(pod.id, presWithConvertedName)
|
||||||
|
|
||||||
PresPresentationDAO.updatePages(presWithConvertedName)
|
|
||||||
state.update(pods)
|
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