Merge remote-tracking branch 'bigbluebutton/v2.0.x-release' into client-pdf-export
# Conflicts: # bigbluebutton-client/branding/default/style/css/assets/swf/v2_skin.fla # bigbluebutton-client/branding/default/style/css/assets/swf/v2_skin.swf
This commit is contained in:
commit
99e55b000a
@ -67,6 +67,10 @@ class ReceivedJsonMsgHdlrActor(val msgFromAkkaAppsEventBus: MsgFromAkkaAppsEvent
|
||||
route[CheckAlivePongSysMsg](envelope, jsonNode)
|
||||
case UserEmojiChangedEvtMsg.NAME =>
|
||||
route[UserEmojiChangedEvtMsg](envelope, jsonNode)
|
||||
case PresenterUnassignedEvtMsg.NAME =>
|
||||
route[PresenterUnassignedEvtMsg](envelope, jsonNode)
|
||||
case PresenterAssignedEvtMsg.NAME =>
|
||||
route[PresenterAssignedEvtMsg](envelope, jsonNode)
|
||||
case UserJoinedMeetingEvtMsg.NAME =>
|
||||
route[UserJoinedMeetingEvtMsg](envelope, jsonNode)
|
||||
case UserLeftMeetingEvtMsg.NAME =>
|
||||
|
@ -26,6 +26,8 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
|
||||
case m: MeetingDestroyedEvtMsg => handleMeetingDestroyedEvtMsg(m)
|
||||
case m: CheckAlivePongSysMsg => handleCheckAlivePongSysMsg(m)
|
||||
case m: UserEmojiChangedEvtMsg => handleUserEmojiChangedEvtMsg(m)
|
||||
case m: PresenterUnassignedEvtMsg => handlePresenterUnassignedEvtMsg(m)
|
||||
case m: PresenterAssignedEvtMsg => handlePresenterAssignedEvtMsg(m)
|
||||
case m: UserJoinedMeetingEvtMsg => handleUserJoinedMeetingEvtMsg(m)
|
||||
case m: UserLeftMeetingEvtMsg => handleUserLeftMeetingEvtMsg(m)
|
||||
case m: UserJoinedVoiceConfToClientEvtMsg => handleUserJoinedVoiceConfToClientEvtMsg(m)
|
||||
@ -76,6 +78,14 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
|
||||
|
||||
}
|
||||
|
||||
def handlePresenterUnassignedEvtMsg(msg: PresenterUnassignedEvtMsg): Unit = {
|
||||
olgMsgGW.handle(new UserStatusChanged(msg.header.meetingId, msg.body.intId, "presenter", "false"))
|
||||
}
|
||||
|
||||
def handlePresenterAssignedEvtMsg(msg: PresenterAssignedEvtMsg): Unit = {
|
||||
olgMsgGW.handle(new UserStatusChanged(msg.header.meetingId, msg.body.presenterId, "presenter", "true"))
|
||||
}
|
||||
|
||||
def handleUserEmojiChangedEvtMsg(msg: UserEmojiChangedEvtMsg): Unit = {
|
||||
//listener.handle(new UserStatusChanged(meetingId, userid, status, value))
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
@namespace chat "org.bigbluebutton.modules.chat.views.*";
|
||||
@namespace skins "org.bigbluebutton.skins.*";
|
||||
@namespace phonecomponents "org.bigbluebutton.modules.phone.views.components.*";
|
||||
@namespace tabBarClasses "flexlib.controls.tabBarClasses.*";
|
||||
|
||||
/*
|
||||
//------------------------------
|
||||
@ -125,7 +126,6 @@ mx|Application {
|
||||
*/
|
||||
|
||||
mx|ApplicationControlBar {
|
||||
borderSkin : ClassReference(null);
|
||||
cornerRadius : 0;
|
||||
fillAlphas : 1, 1;
|
||||
fillColors : #FFFFFF, #FFFFFF;
|
||||
@ -176,7 +176,8 @@ phonecomponents|MuteMeButton {
|
||||
}
|
||||
|
||||
.defaultControlBarStyle {
|
||||
color : #2A2D33;
|
||||
backgroundColor : #F1F3F7;
|
||||
color : #2A2D33;
|
||||
}
|
||||
|
||||
.darkControlBarStyle {
|
||||
@ -977,6 +978,7 @@ flexlib|MDIWindow {
|
||||
maximizeBtnStyleName : "mdiMaximizeButtonStyle";
|
||||
restoreBtnStyleName : "mdiMaximizeButtonStyle";
|
||||
minimizeBtnStyleName : "mdiMinimizeButtonStyle";
|
||||
closeBtnStyleName : "mdiCloseButtonStyle";
|
||||
|
||||
windowControlsClass : ClassReference("org.bigbluebutton.skins.WindowControlsContainer");
|
||||
|
||||
@ -1007,6 +1009,13 @@ flexlib|MDIWindow {
|
||||
disabledSkin : Embed(source="assets/swf/v2_skin.swf", symbol="MDI_Button_Minimize_Disabled");
|
||||
}
|
||||
|
||||
.mdiCloseButtonStyle {
|
||||
upSkin : Embed(source="assets/swf/v2_skin.swf", symbol="MDI_Button_Close_Up");
|
||||
overSkin : Embed(source="assets/swf/v2_skin.swf", symbol="MDI_Button_Close_Over");
|
||||
downSkin : Embed(source="assets/swf/v2_skin.swf", symbol="MDI_Button_Close_Down");
|
||||
disabledSkin : Embed(source="assets/swf/v2_skin.swf", symbol="MDI_Button_Close_Disabled");
|
||||
}
|
||||
|
||||
.mdiWindowTitle {
|
||||
color : #2A2D33;
|
||||
fontSize : 12;
|
||||
@ -1473,28 +1482,38 @@ mx|Slider {
|
||||
*/
|
||||
|
||||
flexlib|SuperTabNavigator {
|
||||
borderColor : #CDD4DB;
|
||||
borderColor : #CDD4DB;
|
||||
tabStyleName : "defaultTabStyle";
|
||||
}
|
||||
|
||||
mx|Tab, .defaultTabStyle {
|
||||
borderColor : #CDD4DB;
|
||||
color : #2A2D33;
|
||||
cornerRadius : 0;
|
||||
fillColorUp : #E8EFF3;
|
||||
fillColorOver : #CDD4DB;
|
||||
fillColorDown : #ACB2B7;
|
||||
fillColorDisabled : #F0F2F6;
|
||||
iconVisible : false;
|
||||
paddingBottom : 0;
|
||||
paddingTop : 0;
|
||||
textIndent : 4;
|
||||
skin : ClassReference("org.bigbluebutton.skins.TabSkin");
|
||||
.defaultTabStyle {
|
||||
borderColor : #CDD4DB;
|
||||
color : #2A2D33;
|
||||
cornerRadius : 0;
|
||||
fillColorUp : #E8EFF3;
|
||||
fillColorOver : #CDD4DB;
|
||||
fillColorDown : #ACB2B7;
|
||||
fillColorDisabled : #F0F2F6;
|
||||
iconVisible : false;
|
||||
paddingBottom : 0;
|
||||
paddingTop : 0;
|
||||
textIndent : 4;
|
||||
skin : ClassReference("org.bigbluebutton.skins.TabSkin");
|
||||
tabCloseButtonStyleName : "tabCloseButton";
|
||||
}
|
||||
|
||||
.highlightedTabStyle {
|
||||
color : #DF2721;
|
||||
iconColor : #DF2721;
|
||||
iconVisible : true;
|
||||
color : #DF2721;
|
||||
iconColor : #DF2721;
|
||||
iconVisible : true;
|
||||
tabCloseButtonStyleName : "tabCloseButton";
|
||||
}
|
||||
|
||||
.tabCloseButton {
|
||||
upSkin : Embed(source="assets/swf/v2_skin.swf", symbol="Tab_Button_Close_Up");
|
||||
overSkin : Embed(source="assets/swf/v2_skin.swf", symbol="Tab_Button_Close_Over");
|
||||
downSkin : Embed(source="assets/swf/v2_skin.swf", symbol="Tab_Button_Close_Down");
|
||||
disabledSkin : Embed(source="assets/swf/v2_skin.swf", symbol="Tab_Button_Close_Disabled");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1576,6 +1595,9 @@ users|StatusItemRenderer {
|
||||
iconMoodThumbsUp : Embed(source="assets/swf/v2_skin.swf", symbol="Icon_User_Mood_ThumbsUp");
|
||||
iconMoodThumbsDown : Embed(source="assets/swf/v2_skin.swf", symbol="Icon_User_Mood_ThumbsDown");
|
||||
iconMoodApplause : Embed(source="assets/swf/v2_skin.swf", symbol="Icon_User_Mood_Applause");
|
||||
verticalAlign : middle;
|
||||
horizontalAlign : center;
|
||||
horizontalGap : 2;
|
||||
}
|
||||
|
||||
users|MediaItemRenderer {
|
||||
@ -1591,7 +1613,7 @@ users|MediaItemRenderer {
|
||||
iconEject : Embed(source="assets/swf/v2_skin.swf", symbol="Icon_User_Eject");
|
||||
glowFilterColor : #1070D7;
|
||||
verticalAlign : middle;
|
||||
horizontalGap : 8;
|
||||
horizontalGap : 2;
|
||||
horizontalAlign : center;
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -38,10 +38,9 @@
|
||||
|
||||
package org.bigbluebutton.skins {
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import flash.display.Graphics;
|
||||
import flash.utils.describeType;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
|
||||
import mx.core.EdgeMetrics;
|
||||
import mx.core.UIComponent;
|
||||
import mx.skins.Border;
|
||||
|
@ -322,6 +322,7 @@ bbb.chat.saveBtn.toolTip = Save chat
|
||||
bbb.chat.saveBtn.accessibilityName = Save chat in text file
|
||||
bbb.chat.saveBtn.label = Save
|
||||
bbb.chat.save.complete = Chat successfully saved
|
||||
bbb.chat.save.ioerror = Chat not saved. Try saving again.
|
||||
bbb.chat.save.filename = public-chat
|
||||
bbb.chat.copyBtn.toolTip = Copy chat
|
||||
bbb.chat.copyBtn.accessibilityName = Copy chat to clipboard
|
||||
@ -473,6 +474,7 @@ bbb.layout.combo.customName = Custom layout
|
||||
bbb.layout.combo.remote = Remote
|
||||
bbb.layout.window.name = Layout name
|
||||
bbb.layout.save.complete = Layouts were successfully saved
|
||||
bbb.layout.save.ioerror = Layouts were not saved. Try saving again.
|
||||
bbb.layout.load.complete = Layouts were successfully loaded
|
||||
bbb.layout.load.failed = Unable to load the layouts
|
||||
bbb.layout.sync = Your layout has been sent to all participants
|
||||
@ -553,6 +555,7 @@ bbb.sharedNotes.typing.double = {0} and {1} are typing...
|
||||
bbb.sharedNotes.typing.multiple = Several people are typing...
|
||||
bbb.sharedNotes.save.toolTip = Save notes to file
|
||||
bbb.sharedNotes.save.complete = Notes were successfully saved
|
||||
bbb.sharedNotes.save.ioerror = Notes were not saved. Try saving again.
|
||||
bbb.sharedNotes.save.htmlLabel = Formatted text (.html)
|
||||
bbb.sharedNotes.save.txtLabel = Plain text (.txt)
|
||||
bbb.sharedNotes.new.label = Create
|
||||
@ -865,54 +868,3 @@ bbb.users.roomsGrid.join = Join
|
||||
bbb.users.roomsGrid.noUsers = No users is this room
|
||||
|
||||
bbb.langSelector.default=Default language
|
||||
bbb.langSelector.ar=Arabic
|
||||
bbb.langSelector.az_AZ=Azerbaijani
|
||||
bbb.langSelector.eu_EU=Basque
|
||||
bbb.langSelector.bn_BN=Bengali
|
||||
bbb.langSelector.bg_BG=Bulgarian
|
||||
bbb.langSelector.ca_ES=Catalan
|
||||
bbb.langSelector.zh_CN=Chinese (Simplified)
|
||||
bbb.langSelector.zh_TW=Chinese (Traditional)
|
||||
bbb.langSelector.hr_HR=Croatian
|
||||
bbb.langSelector.cs_CZ=Czech
|
||||
bbb.langSelector.da_DK=Danish
|
||||
bbb.langSelector.nl_NL=Dutch
|
||||
bbb.langSelector.en_US=English
|
||||
bbb.langSelector.et_EE=Estonian
|
||||
bbb.langSelector.fa_IR=Farsi
|
||||
bbb.langSelector.fi_FI=Finnish
|
||||
bbb.langSelector.fr_FR=French
|
||||
bbb.langSelector.fr_CA=French (Canadian)
|
||||
bbb.langSelector.ff_SN=Fulah
|
||||
bbb.langSelector.de_DE=German
|
||||
bbb.langSelector.el_GR=Greek
|
||||
bbb.langSelector.he_IL=Hebrew
|
||||
bbb.langSelector.hu_HU=Hungarian
|
||||
bbb.langSelector.id_ID=Indonesian
|
||||
bbb.langSelector.it_IT=Italian
|
||||
bbb.langSelector.ja_JP=Japanese
|
||||
bbb.langSelector.ko_KR=Korean
|
||||
bbb.langSelector.lv_LV=Latvian
|
||||
bbb.langSelector.lt_LT=Lithuania
|
||||
bbb.langSelector.mn_MN=Mongolian
|
||||
bbb.langSelector.ne_NE=Nepali
|
||||
bbb.langSelector.no_NO=Norwegian
|
||||
bbb.langSelector.pl_PL=Polish
|
||||
bbb.langSelector.pt_BR=Portuguese (Brazilian)
|
||||
bbb.langSelector.pt_PT=Portuguese
|
||||
bbb.langSelector.ro_RO=Romanian
|
||||
bbb.langSelector.ru_RU=Russian
|
||||
bbb.langSelector.sr_SR=Serbian (Cyrillic)
|
||||
bbb.langSelector.sr_RS=Serbian (Latin)
|
||||
bbb.langSelector.si_LK=Sinhala
|
||||
bbb.langSelector.sk_SK=Slovak
|
||||
bbb.langSelector.sl_SL=Slovenian
|
||||
bbb.langSelector.es_ES=Spanish
|
||||
bbb.langSelector.es_LA=Spanish (Latin American)
|
||||
bbb.langSelector.sv_SE=Swedish
|
||||
bbb.langSelector.th_TH=Thai
|
||||
bbb.langSelector.tr_TR=Turkish
|
||||
bbb.langSelector.uk_UA=Ukrainian
|
||||
bbb.langSelector.vi_VN=Vietnamese
|
||||
bbb.langSelector.cy_GB=Welsh
|
||||
bbb.langSelector.oc=Occitan
|
||||
|
@ -1,54 +1,54 @@
|
||||
<?xml version="1.0" ?>
|
||||
<locales>
|
||||
<locale code="ar" name="Arabic" direction="rtl"/>
|
||||
<locale code="az_AZ" name="Azerbaijani"/>
|
||||
<locale code="eu_EU" name="Basque" />
|
||||
<locale code="bn_BN" name="Bengali" />
|
||||
<locale code="bg_BG" name="Bulgarian" />
|
||||
<locale code="ca_ES" name="Catalan" />
|
||||
<locale code="zh_CN" name="Chinese (Simplified)"/>
|
||||
<locale code="zh_TW" name="Chinese (Traditional)"/>
|
||||
<locale code="hr_HR" name="Croatian" />
|
||||
<locale code="cs_CZ" name="Czech" />
|
||||
<locale code="da_DK" name="Danish" />
|
||||
<locale code="nl_NL" name="Dutch" />
|
||||
<locale code="en_US" name="English"/>
|
||||
<locale code="et_EE" name="Estonian"/>
|
||||
<locale code="fa_IR" name="Farsi" />
|
||||
<locale code="fi_FI" name="Finnish"/>
|
||||
<locale code="fr_FR" name="French"/>
|
||||
<locale code="fr_CA" name="French (Canadian)"/>
|
||||
<locale code="ff_SN" name="Fulah"/>
|
||||
<locale code="de_DE" name="German"/>
|
||||
<locale code="el_GR" name="Greek"/>
|
||||
<locale code="he_IL" name="Hebrew" direction="rtl"/>
|
||||
<locale code="hu_HU" name="Hungarian"/>
|
||||
<locale code="id_ID" name="Indonesian"/>
|
||||
<locale code="it_IT" name="Italian"/>
|
||||
<locale code="ja_JP" name="Japanese"/>
|
||||
<locale code="ko_KR" name="Korean"/>
|
||||
<locale code="lv_LV" name="Latvian"/>
|
||||
<locale code="lt_LT" name="Lithuania"/>
|
||||
<locale code="mn_MN" name="Mongolian"/>
|
||||
<locale code="ne_NE" name="Nepali"/>
|
||||
<locale code="no_NO" name="Norwegian"/>
|
||||
<locale code="oc" name="Occitan"/>
|
||||
<locale code="pl_PL" name="Polish"/>
|
||||
<locale code="pt_BR" name="Portuguese (Brazilian)"/>
|
||||
<locale code="pt_PT" name="Portuguese"/>
|
||||
<locale code="ro_RO" name="Romanian"/>
|
||||
<locale code="ru_RU" name="Russian"/>
|
||||
<locale code="sr_SR" name="Serbian (Cyrillic)"/>
|
||||
<locale code="sr_RS" name="Serbian (Latin)"/>
|
||||
<locale code="si_LK" name="Sinhala"/>
|
||||
<locale code="sk_SK" name="Slovak"/>
|
||||
<locale code="sl_SL" name="Slovenian"/>
|
||||
<locale code="es_ES" name="Spanish"/>
|
||||
<locale code="es_LA" name="Spanish (Latin American)"/>
|
||||
<locale code="sv_SE" name="Swedish"/>
|
||||
<locale code="th_TH" name="Thai"/>
|
||||
<locale code="tr_TR" name="Turkish"/>
|
||||
<locale code="uk_UA" name="Ukrainian"/>
|
||||
<locale code="vi_VN" name="Vietnamese"/>
|
||||
<locale code="cy_GB" name="Welsh"/>
|
||||
<locale code="ar" name="Arabic" native="العربيّة" direction="rtl"/>
|
||||
<locale code="az_AZ" name="Azerbaijani" native="Azərbaycan"/>
|
||||
<locale code="eu_EU" name="Basque" native="Euskara"/>
|
||||
<locale code="bn_BN" name="Bengali" native="বাংলা (baɛṅlā)"/>
|
||||
<locale code="bg_BG" name="Bulgarian" native="Български"/>
|
||||
<locale code="ca_ES" name="Catalan" native="Català"/>
|
||||
<locale code="zh_CN" name="Chinese (Simplified)" native="中文(简体)"/>
|
||||
<locale code="zh_TW" name="Chinese (Traditional)" native="中文(繁體)"/>
|
||||
<locale code="hr_HR" name="Croatian" native="Hrvatski"/>
|
||||
<locale code="cs_CZ" name="Czech" native="Čeština"/>
|
||||
<locale code="da_DK" name="Danish" native="Dansk"/>
|
||||
<locale code="nl_NL" name="Dutch" native="Nederlands"/>
|
||||
<locale code="en_US" name="English" native="English"/>
|
||||
<locale code="et_EE" name="Estonian" native="Eesti"/>
|
||||
<locale code="fa_IR" name="Farsi" native="فارسى"/>
|
||||
<locale code="fi_FI" name="Finnish" native="Suomi"/>
|
||||
<locale code="fr_FR" name="French" native="Français"/>
|
||||
<locale code="fr_CA" name="French (Canadian)" native="Français (Canadien)"/>
|
||||
<locale code="ff_SN" name="Fula" native="Fulfulde"/>
|
||||
<locale code="de_DE" name="German" native="Deutsch"/>
|
||||
<locale code="el_GR" name="Greek" native="Ελληνικά"/>
|
||||
<locale code="he_IL" name="Hebrew" native="עִבְרִית" direction="rtl"/>
|
||||
<locale code="hu_HU" name="Hungarian" native="Magyar"/>
|
||||
<locale code="id_ID" name="Indonesian" native="Indonesia"/>
|
||||
<locale code="it_IT" name="Italian" native="Italiano"/>
|
||||
<locale code="ja_JP" name="Japanese" native="日本語"/>
|
||||
<locale code="ko_KR" name="Korean" native="한국어"/>
|
||||
<locale code="lv_LV" name="Latvian" native="Latviešu"/>
|
||||
<locale code="lt_LT" name="Lithuania" native="Lietuvių"/>
|
||||
<locale code="mn_MN" name="Mongolian" native="Монгол"/>
|
||||
<locale code="ne_NE" name="Nepali" native="नेपाली"/>
|
||||
<locale code="no_NO" name="Norwegian" native="Norsk"/>
|
||||
<locale code="oc" name="Occitan" native="Occitan"/>
|
||||
<locale code="pl_PL" name="Polish" native="Polski"/>
|
||||
<locale code="pt_BR" name="Portuguese (Brazilian)" native="Português (Brasileiro)"/>
|
||||
<locale code="pt_PT" name="Portuguese" native="Português"/>
|
||||
<locale code="ro_RO" name="Romanian" native="Român"/>
|
||||
<locale code="ru_RU" name="Russian" native="Русский"/>
|
||||
<locale code="sr_SR" name="Serbian (Cyrillic)" native="Српски (ћирилица)"/>
|
||||
<locale code="sr_RS" name="Serbian (Latin)" native="Srpski (latinica)"/>
|
||||
<locale code="si_LK" name="Sinhala" native="සිංහල"/>
|
||||
<locale code="sk_SK" name="Slovak" native="Slovenčina"/>
|
||||
<locale code="sl_SL" name="Slovenian" native="Slovenščina"/>
|
||||
<locale code="es_ES" name="Spanish" native="Español"/>
|
||||
<locale code="es_LA" name="Spanish (Latin American)" native="Español (Latinoamericano)"/>
|
||||
<locale code="sv_SE" name="Swedish" native="Svenska"/>
|
||||
<locale code="th_TH" name="Thai" native="ภาษาไทย"/>
|
||||
<locale code="tr_TR" name="Turkish" native="Türkçe"/>
|
||||
<locale code="uk_UA" name="Ukrainian" native="Українська"/>
|
||||
<locale code="vi_VN" name="Vietnamese" native="Tiếng việt"/>
|
||||
<locale code="cy_GB" name="Welsh" native="Cymraeg"/>
|
||||
</locales>
|
||||
|
@ -25,7 +25,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
xmlns:mate="http://mate.asfusion.com/"
|
||||
dataProvider="{ResourceUtil.getInstance().locales}"
|
||||
change="changeLanguage()"
|
||||
labelField="name"
|
||||
labelField="native"
|
||||
rowCount="15" width="120" height="22">
|
||||
|
||||
<fx:Declarations>
|
||||
|
@ -640,7 +640,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
browserPermissionHelper.currentState = "firefoxMic";
|
||||
}
|
||||
browserPermissionHelper.x = 50;
|
||||
browserPermissionHelper.y = 150;
|
||||
browserPermissionHelper.y = 200;
|
||||
}
|
||||
} else if (browser == "Chrome") {
|
||||
if (browserPermissionHelper) {
|
||||
@ -929,8 +929,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
height="150"
|
||||
width="100%">
|
||||
<mx:Canvas id="backgroundPlaceHolder" width="100%" height="100%" />
|
||||
<views:LoadingBar id="progressBar" horizontalCenter="0" verticalCenter="0" width="280" height="15" labelWidth="280" textAlign="center"/>
|
||||
<views:QuotesView id="quoteView" horizontalCenter="0" y="{this.height/2 - quoteView.height - 100}"/>
|
||||
<mx:VBox horizontalCenter="0" verticalCenter="0" horizontalAlign="center" verticalGap="20">
|
||||
<views:QuotesView id="quoteView"/>
|
||||
<views:LoadingBar id="progressBar" width="280" height="15" labelWidth="280" textAlign="center"/>
|
||||
</mx:VBox>
|
||||
</views:MainCanvas>
|
||||
|
||||
<mx:ControlBar width="100%" height="{footerHeight}" styleName="defaultControlBarStyle" id="controlBar" paddingTop="2" paddingBottom="2" verticalAlign="middle" visible="{_showFooter}" includeInLayout="{showFooterOpt}" >
|
||||
|
@ -59,7 +59,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
quote = "Research is creating new knowledge."
|
||||
}
|
||||
if (StringUtils.isEmpty(attribution)) {
|
||||
quote = "Neil Armstrong"
|
||||
attribution = "Neil Armstrong"
|
||||
}
|
||||
quoteLabel.text = "“ " + quote + " ”";
|
||||
quoteAttribution.text = attribution;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bigbluebutton.modules.chat.services
|
||||
{
|
||||
import flash.events.Event;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.net.FileReference;
|
||||
|
||||
import mx.controls.Alert;
|
||||
@ -23,6 +24,9 @@ package org.bigbluebutton.modules.chat.services
|
||||
fileRef.addEventListener(Event.COMPLETE, function(evt:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.chat.save.complete'), "", Alert.OK);
|
||||
});
|
||||
fileRef.addEventListener(IOErrorEvent.IO_ERROR, function(evt:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.chat.save.ioerror'), "", Alert.OK);
|
||||
});
|
||||
|
||||
var cr:String = String.fromCharCode(13);
|
||||
var lf:String = String.fromCharCode(10);
|
||||
|
@ -23,6 +23,7 @@ package org.bigbluebutton.modules.layout.managers
|
||||
import flash.display.DisplayObject;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.net.FileReference;
|
||||
import flash.utils.Timer;
|
||||
@ -57,7 +58,7 @@ package org.bigbluebutton.modules.layout.managers
|
||||
import org.bigbluebutton.modules.layout.model.LayoutLoader;
|
||||
import org.bigbluebutton.modules.layout.model.LayoutModel;
|
||||
import org.bigbluebutton.modules.layout.views.CustomLayoutNameWindow;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
public class LayoutManager extends EventDispatcher {
|
||||
private static const LOGGER:ILogger = getClassLogger(LayoutManager);
|
||||
@ -142,6 +143,9 @@ package org.bigbluebutton.modules.layout.managers
|
||||
_fileRef.addEventListener(Event.COMPLETE, function(e:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.layout.save.complete'), "", Alert.OK, _canvas);
|
||||
});
|
||||
_fileRef.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.layout.save.ioerror'), "", Alert.OK, _canvas);
|
||||
});
|
||||
_fileRef.save(_layoutModel.toString(), "layouts.xml");
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
layout="absolute"
|
||||
creationComplete="onCreationComplete()"
|
||||
keyUp="keyUpHandler(event)"
|
||||
width="250" xmlns:common="org.bigbluebutton.common.*">
|
||||
width="240" xmlns:common="org.bigbluebutton.common.*">
|
||||
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
@ -80,24 +80,23 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
]]>
|
||||
</fx:Script>
|
||||
|
||||
<mx:VBox width="100%" height="100%" paddingTop="15">
|
||||
<mx:VBox width="100%" height="100%" paddingTop="5" horizontalAlign="center">
|
||||
<common:AdvancedLabel text="{ResourceUtil.getInstance().getString('bbb.sharedNotes.createNoteWindow.label')}"
|
||||
styleName="titleWindowStyle"
|
||||
maxWidth="{this.width - 10}" />
|
||||
<mx:HBox width="100%"
|
||||
height="100%"
|
||||
verticalAlign="middle">
|
||||
<mx:TextInput id="textInput"
|
||||
restrict="a-zA-Z0-9 "
|
||||
maxChars="20"
|
||||
width="100%"
|
||||
text="{sharedNoteTitle}" />
|
||||
<mx:Button id="btnNew"
|
||||
click="btnNew_clickHandler(event)"
|
||||
height="30"
|
||||
label="{ResourceUtil.getInstance().getString('bbb.sharedNotes.new.label')}"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.sharedNotes.new.toolTip')}" />
|
||||
</mx:HBox>
|
||||
width="180" />
|
||||
<mx:TextInput id="textInput"
|
||||
restrict="a-zA-Z0-9 "
|
||||
maxChars="20"
|
||||
height="26"
|
||||
width="100%"
|
||||
text="{sharedNoteTitle}" />
|
||||
<mx:Button id="btnNew"
|
||||
click="btnNew_clickHandler(event)"
|
||||
styleName="mainActionButton"
|
||||
height="30"
|
||||
width="100%"
|
||||
label="{ResourceUtil.getInstance().getString('bbb.sharedNotes.new.label')}"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.sharedNotes.new.toolTip')}" />
|
||||
</mx:VBox>
|
||||
|
||||
<mx:Button id="closeButton" click="onCancelClicked()" styleName="titleWindowCloseButton"
|
||||
|
@ -318,11 +318,14 @@
|
||||
}
|
||||
|
||||
protected function saveNotes(title:String, text:String, extension:String):void {
|
||||
var filename:String = title.replace(/\s+/g, '-').toLowerCase() + "." + extension;
|
||||
var filename:String = title.replace(/\s+/g, '_').toLowerCase() + "_" + LiveMeeting.inst().meeting.name.replace(/\s+/g, '_').toLowerCase() + "_" + new Date().time.toString() + "." + extension;
|
||||
var fileRef:FileReference = new FileReference();
|
||||
fileRef.addEventListener(Event.COMPLETE, function(e:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.sharedNotes.save.complete'), "", Alert.OK);
|
||||
});
|
||||
fileRef.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event):void {
|
||||
Alert.show(ResourceUtil.getInstance().getString('bbb.sharedNotes.save.ioerror'), "", Alert.OK);
|
||||
});
|
||||
|
||||
var cr:String = String.fromCharCode(13);
|
||||
var lf:String = String.fromCharCode(10);
|
||||
@ -528,7 +531,7 @@
|
||||
}
|
||||
|
||||
protected function btnToolbar_clickHandler(event:MouseEvent):void {
|
||||
richTextEditor.showControlBar = !richTextEditor.showControlBar;
|
||||
btnFormat.selected = richTextEditor.showControlBar = !richTextEditor.showControlBar;
|
||||
}
|
||||
|
||||
public function getPrefferedPosition():String {
|
||||
@ -616,11 +619,6 @@
|
||||
toolTip="{options.maxNoteLength - richTextEditor.text.length > 0 ?
|
||||
ResourceUtil.getInstance().getString('bbb.sharedNotes.remaining.tooltip') :
|
||||
ResourceUtil.getInstance().getString('bbb.sharedNotes.full.tooltip')}"/>
|
||||
<mx:Button id="btnSettings"
|
||||
styleName="sharedNotesSettings"
|
||||
width="26" height="26"
|
||||
click="btnSettings_clickHandler(event)"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.sharedNotes.settings.toolTip')}"/>
|
||||
<mx:Button id="btnFormat"
|
||||
styleName="sharedNotesFormatButtonStyle"
|
||||
width="26" height="26"
|
||||
@ -634,6 +632,11 @@
|
||||
height="26"
|
||||
click="btnSave_clickHandler(event)"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.sharedNotes.save.toolTip')}"/>
|
||||
<mx:Button id="btnSettings"
|
||||
styleName="sharedNotesSettings"
|
||||
width="26" height="26"
|
||||
click="btnSettings_clickHandler(event)"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.sharedNotes.settings.toolTip')}"/>
|
||||
</mx:HBox>
|
||||
</mx:VBox>
|
||||
</CustomMdiWindow>
|
||||
|
@ -24,10 +24,7 @@
|
||||
xmlns:fx="http://ns.adobe.com/mxml/2009"
|
||||
xmlns:mate="http://mate.asfusion.com/"
|
||||
creationComplete="onCreationComplete()"
|
||||
verticalScrollPolicy="off" horizontalScrollPolicy="off"
|
||||
verticalAlign="middle"
|
||||
horizontalGap="8"
|
||||
horizontalAlign="center">
|
||||
verticalScrollPolicy="off" horizontalScrollPolicy="off">
|
||||
|
||||
<fx:Declarations>
|
||||
<mate:Listener type="{UsersRollEvent.USER_ROLL_OVER}" method="onRollOver" />
|
||||
|
@ -166,9 +166,9 @@ $Id: $
|
||||
private const LISTEN_TO_BREAKOUT_ROOM:String = "Listen To Breakout Room";
|
||||
private const JOIN_BREAKOUT_ROOM:String = "Join Breakout Room";
|
||||
|
||||
private const STATUS_GRID_WIDTH:int = 56;
|
||||
private const STATUS_GRID_WIDTH:int = 50;
|
||||
private const NAME_GRID_MIN_WIDTH:int = 80;
|
||||
private const MEDIA_GRID_WIDTH:int = 118;
|
||||
private const MEDIA_GRID_WIDTH:int = 100;
|
||||
|
||||
private var muteMeRolled:Boolean = false;
|
||||
|
||||
|
@ -116,6 +116,7 @@ package org.bigbluebutton.util.i18n
|
||||
locales.push({
|
||||
code: DEFAULT_LOCALE_IDENTIFIER,
|
||||
name: "",
|
||||
native: "",
|
||||
direction: "ltr"
|
||||
});
|
||||
|
||||
@ -123,6 +124,7 @@ package org.bigbluebutton.util.i18n
|
||||
locales.push({
|
||||
code: locale.@code.toString(),
|
||||
name: locale.@name.toString(),
|
||||
native: locale.@native.toString(),
|
||||
direction: locale.@direction.valueOf().toString().toLowerCase() == "rtl" ? "rtl" : "ltr"
|
||||
});
|
||||
}
|
||||
@ -177,13 +179,7 @@ package org.bigbluebutton.util.i18n
|
||||
}
|
||||
|
||||
private function reloadLocaleNames():void {
|
||||
for each (var item:* in locales) {
|
||||
if (item.code == DEFAULT_LOCALE_IDENTIFIER) {
|
||||
item.name = ResourceUtil.getInstance().getString("bbb.langSelector." + item.code, null, getDefaultLocale());
|
||||
} else {
|
||||
item.name = ResourceUtil.getInstance().getString("bbb.langSelector." + item.code, null, preferredLocale);
|
||||
}
|
||||
}
|
||||
locales[0].name = locales[0].native = ResourceUtil.getInstance().getString("bbb.langSelector." + locales[0].code, null, getDefaultLocale());
|
||||
locales.sort(localesCompareFunction);
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,14 @@
|
||||
height: 1.5rem;
|
||||
padding: 0;
|
||||
border-radius: 2rem;
|
||||
background-color: #4E5A66;
|
||||
background-color: #DF2721;
|
||||
-webkit-transition: all 0.2s ease;
|
||||
-moz-transition: all 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.react-toggle--checked .react-toggle-track {
|
||||
background-color: #4E5A66;
|
||||
background-color: #008081;
|
||||
}
|
||||
|
||||
.react-toggle-track-check {
|
||||
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('breakouts');
|
@ -1,13 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
|
||||
import handleCreateBreakout from './handlers/createBreakout';
|
||||
import handleBreakoutStarted from './handlers/breakoutStarted';
|
||||
import handleBreakoutJoinURL from './handlers/breakoutJoinURL';
|
||||
import handleUpdateTimeRemaining from './handlers/updateTimeRemaining';
|
||||
import handleBreakoutClosed from './handlers/breakoutClosed';
|
||||
|
||||
RedisPubSub.on('CreateBreakoutRoomRequest', handleCreateBreakout);
|
||||
RedisPubSub.on('BreakoutRoomStarted', handleBreakoutStarted);
|
||||
RedisPubSub.on('BreakoutRoomJoinURL', handleBreakoutJoinURL);
|
||||
RedisPubSub.on('BreakoutRoomsTimeRemainingUpdate', handleUpdateTimeRemaining);
|
||||
RedisPubSub.on('BreakoutRoomClosed', handleBreakoutClosed);
|
@ -1,10 +0,0 @@
|
||||
import clearBreakouts from '../modifiers/clearBreakouts';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function handleBreakoutClosed({ payload }) {
|
||||
const meetingId = payload.meetingId;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
return clearBreakouts(meetingId);
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
import xml2js from 'xml2js';
|
||||
import url from 'url';
|
||||
const xmlParser = new xml2js.Parser();
|
||||
|
||||
const getUrlParams = (urlToParse) => {
|
||||
const options = { parseQueryString: true };
|
||||
const parsedUrl = url.parse(urlToParse, options);
|
||||
return parsedUrl.query;
|
||||
};
|
||||
|
||||
export default function handleBreakoutJoinURL({ payload }) {
|
||||
const REDIS_CONFIG = Meteor.settings.redis;
|
||||
const CLIENT_HTML = 'HTML5';
|
||||
|
||||
const {
|
||||
noRedirectJoinURL,
|
||||
} = payload;
|
||||
|
||||
check(noRedirectJoinURL, String);
|
||||
|
||||
const urlParams = getUrlParams(noRedirectJoinURL);
|
||||
|
||||
const selector = {
|
||||
externalMeetingId: urlParams.meetingID,
|
||||
};
|
||||
|
||||
let breakout = Breakouts.findOne(selector);
|
||||
|
||||
const res = Meteor.http.call('get', noRedirectJoinURL);
|
||||
xmlParser.parseString(res.content, (err, parsedXML) => {
|
||||
if (err) {
|
||||
return Logger.error(`An Error occured when parsing xml response for: ${noRedirectJoinURL}`);
|
||||
}
|
||||
|
||||
breakout = Breakouts.findOne(selector);
|
||||
|
||||
const { response } = parsedXML;
|
||||
const users = breakout.users;
|
||||
|
||||
const user = {
|
||||
userId: payload.userId,
|
||||
urlParams: {
|
||||
meetingId: response.meeting_id[0],
|
||||
userId: response.user_id[0],
|
||||
authToken: response.auth_token[0],
|
||||
},
|
||||
};
|
||||
|
||||
const userExists = users.find(u => user.userId === u.userId);
|
||||
|
||||
if (userExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modifier = {
|
||||
$push: {
|
||||
users: user,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding breakout to collection: ${err}`);
|
||||
}
|
||||
|
||||
const {
|
||||
insertedId,
|
||||
} = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Added breakout id=${urlParams.meetingID}`);
|
||||
}
|
||||
|
||||
return Logger.info(`Upserted breakout id=${urlParams.meetingID}`);
|
||||
};
|
||||
|
||||
return Breakouts.upsert(selector, modifier, cb);
|
||||
});
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function handleBreakoutRoomStarted({ payload }) {
|
||||
const {
|
||||
meetingId,
|
||||
timeRemaining,
|
||||
externalMeetingId,
|
||||
} = payload;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
const selector = {
|
||||
breakoutMeetingId: meetingId,
|
||||
};
|
||||
|
||||
modifier = {
|
||||
$set: {
|
||||
users: [],
|
||||
timeRemaining: Number(timeRemaining),
|
||||
externalMeetingId,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`updating breakout: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info('Updated timeRemaining and externalMeetingId ' +
|
||||
`for breakout id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Breakouts.update(selector, modifier, cb);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import addBreakout from '../modifiers/addBreakout';
|
||||
|
||||
export default function handleCreateBreakout({ payload }) {
|
||||
const { breakoutMeetingId } = payload;
|
||||
|
||||
check(breakoutMeetingId, String);
|
||||
|
||||
return addBreakout(payload);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function handleUpdateTimeRemaining({ payload }) {
|
||||
const {
|
||||
meetingId,
|
||||
timeRemaining,
|
||||
} = payload;
|
||||
|
||||
check(meetingId, String);
|
||||
check(timeRemaining, Number);
|
||||
|
||||
const selector = {
|
||||
parentMeetingId: meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
timeRemaining,
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
multi: true,
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating breakouts: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info('Updated breakout time remaining for breakouts ' +
|
||||
`where parentMeetingId=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Breakouts.update(selector, modifier, options, cb);
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function addBreakout(payload) {
|
||||
const {
|
||||
breakoutMeetingId,
|
||||
parentMeetingId,
|
||||
name,
|
||||
} = payload;
|
||||
|
||||
check(breakoutMeetingId, String);
|
||||
check(parentMeetingId, String);
|
||||
check(name, String);
|
||||
|
||||
const selector = { breakoutMeetingId };
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
breakoutMeetingId,
|
||||
parentMeetingId,
|
||||
name,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding breakout to collection: ${err}`);
|
||||
}
|
||||
|
||||
const {
|
||||
insertedId,
|
||||
} = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Added breakout id=${breakoutMeetingId}`);
|
||||
}
|
||||
|
||||
return Logger.info(`Upserted breakout id=${breakoutMeetingId}`);
|
||||
};
|
||||
|
||||
return Breakouts.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import removeMeeting from '/imports/api/1.1/meetings/server/modifiers/removeMeeting';
|
||||
|
||||
export default function clearBreakouts(meetingId) {
|
||||
if (meetingId) {
|
||||
const selector = {
|
||||
breakoutMeetingId: meetingId,
|
||||
};
|
||||
|
||||
const cb = () => {
|
||||
Logger.info(`Cleared Breakouts (${meetingId})`);
|
||||
removeMeeting(meetingId);
|
||||
};
|
||||
|
||||
return Breakouts.remove(selector, cb);
|
||||
}
|
||||
|
||||
return Breakouts.remove({}, Logger.info('Cleared Breakouts (all)'));
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import Breakouts from '/imports/api/1.1/breakouts';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function breakouts(credentials) {
|
||||
const {
|
||||
meetingId,
|
||||
requesterUserId,
|
||||
} = credentials;
|
||||
|
||||
return Breakouts.find({
|
||||
$or: [
|
||||
{ breakoutMeetingId: meetingId },
|
||||
{
|
||||
users: {
|
||||
$elemMatch: { userId: requesterUserId },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundBreakouts = breakouts.bind(this);
|
||||
return mapToAcl('subscriptions.breakouts', boundBreakouts)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('breakouts', publish);
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('captions');
|
@ -1,8 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handleCaptionHistory from './handlers/captionHistory';
|
||||
import handleCaptionUpdate from './handlers/captionUpdate';
|
||||
import handleCaptionOwnerUpdate from './handlers/captionOwnerUpdate';
|
||||
|
||||
RedisPubSub.on('send_caption_history_reply_message', handleCaptionHistory);
|
||||
RedisPubSub.on('edit_caption_history_message', handleCaptionUpdate);
|
||||
RedisPubSub.on('update_caption_owner_message', handleCaptionOwnerUpdate);
|
@ -1,65 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import { inReplyToHTML5Client } from '/imports/api/common/server/helpers';
|
||||
import addCaption from '../modifiers/addCaption';
|
||||
|
||||
export default function handleCaptionHistory({ payload }) {
|
||||
if (!inReplyToHTML5Client({ payload })) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SERVER_CONFIG = Meteor.settings.app;
|
||||
const CAPTION_CHUNK_LENGTH = SERVER_CONFIG.captionsChunkLength || 1000;
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const locale = payload.locale;
|
||||
const captionHistory = payload.caption_history;
|
||||
|
||||
check(meetingId, String);
|
||||
check(captionHistory, Object);
|
||||
|
||||
const captionsAdded = [];
|
||||
_.each(captionHistory, (caption, locale) => {
|
||||
const ownerId = caption[0];
|
||||
let captions = caption[1].slice(0);
|
||||
const chunks = [];
|
||||
|
||||
if (captions.length === 0) {
|
||||
chunks.push('');
|
||||
} else {
|
||||
while (captions.length > 0) {
|
||||
if (captions.length > CAPTION_CHUNK_LENGTH) {
|
||||
chunks.push(captions.slice(0, CAPTION_CHUNK_LENGTH));
|
||||
captions = captions.slice(CAPTION_CHUNK_LENGTH);
|
||||
} else {
|
||||
chunks.push(captions);
|
||||
captions = captions.slice(captions.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectorToRemove = {
|
||||
meetingId,
|
||||
locale,
|
||||
'captionHistory.index': { $gt: (chunks.length - 1) },
|
||||
};
|
||||
|
||||
Captions.remove(selectorToRemove);
|
||||
|
||||
chunks.forEach((captions, index) => {
|
||||
const captionHistoryObject = {
|
||||
locale,
|
||||
ownerId,
|
||||
captions,
|
||||
index,
|
||||
next: (index < chunks.length - 1) ? index + 1 : undefined,
|
||||
};
|
||||
|
||||
captionsAdded.push(addCaption(meetingId, locale, captionHistoryObject));
|
||||
});
|
||||
});
|
||||
|
||||
return captionsAdded;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import addCaption from '../modifiers/addCaption';
|
||||
|
||||
export default function handleCaptionOwnerUpdate({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const locale = payload.locale;
|
||||
const ownerId = payload.owner_id;
|
||||
|
||||
check(meetingId, String);
|
||||
check(locale, String);
|
||||
check(ownerId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
locale,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
'captionHistory.ownerId': ownerId,
|
||||
},
|
||||
};
|
||||
|
||||
const Caption = Captions.findOne(selector);
|
||||
|
||||
if (!Caption) {
|
||||
const captionHistory = {
|
||||
ownerId,
|
||||
captions: '',
|
||||
index: 0,
|
||||
next: null,
|
||||
};
|
||||
|
||||
return addCaption(meetingId, locale, captionHistory);
|
||||
}
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating captions owner: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.verbose(`Update caption owner locale=${locale} meeting=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Captions.update(selector, modifier, { multi: true }, cb);
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import addCaption from '../modifiers/addCaption';
|
||||
|
||||
export default function handleCaptionUpdate({ payload }) {
|
||||
const SERVER_CONFIG = Meteor.settings.app;
|
||||
const CAPTION_CHUNK_LENGTH = SERVER_CONFIG.captionsChunkLength || 1000;
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const locale = payload.locale;
|
||||
|
||||
check(meetingId, String);
|
||||
check(locale, String);
|
||||
|
||||
const captionsObjects = Captions.find({
|
||||
meetingId,
|
||||
locale,
|
||||
}, {
|
||||
sort: {
|
||||
locale: 1,
|
||||
'captionHistory.index': 1,
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
const objectsToUpdate = [];
|
||||
if (captionsObjects != null) {
|
||||
let startIndex;
|
||||
let endIndex;
|
||||
let length = 0;
|
||||
let current = captionsObjects[0];
|
||||
|
||||
// looking for a start index and end index
|
||||
// (end index only for the case when they are in the same block)
|
||||
while (current != null) {
|
||||
length += current.captionHistory.captions.length;
|
||||
|
||||
// if length is bigger than start index - we found our start index
|
||||
if (length >= payload.start_index && startIndex == undefined) {
|
||||
// check if it's a new character somewhere in the middle of captions text
|
||||
if (length - 1 >= payload.start_index) {
|
||||
startIndex = payload.start_index - (length - current.captionHistory.captions.length);
|
||||
|
||||
// check to see if the end_index is in the same object as start_index
|
||||
if (length - 1 >= payload.end_index) {
|
||||
endIndex = payload.end_index - (length - current.captionHistory.captions.length);
|
||||
const _captions = current.captionHistory.captions;
|
||||
current.captionHistory.captions = _captions.slice(0, startIndex) +
|
||||
payload.text +
|
||||
_captions.slice(endIndex);
|
||||
objectsToUpdate.push(current);
|
||||
break;
|
||||
|
||||
// end index is not in the same object as start_index, we will find it later
|
||||
} else {
|
||||
current.captionHistory.captions = current.captionHistory.captions.slice(0, startIndex) +
|
||||
payload.text;
|
||||
objectsToUpdate.push(current);
|
||||
break;
|
||||
}
|
||||
|
||||
// separate case for appending new characters to the very end of the string
|
||||
} else if (current.captionHistory.next == null &&
|
||||
length == payload.start_index &&
|
||||
length == payload.start_index) {
|
||||
startIndex = 1;
|
||||
endIndex = 1;
|
||||
current.captionHistory.captions += payload.text;
|
||||
objectsToUpdate.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
current = captionsObjects[current.captionHistory.next];
|
||||
}
|
||||
|
||||
// looking for end index here if it wasn't in the same object as start index
|
||||
if (startIndex != undefined && endIndex == undefined) {
|
||||
current = captionsObjects[current.captionHistory.next];
|
||||
while (current != null) {
|
||||
length += current.captionHistory.captions.length;
|
||||
|
||||
// check to see if the end_index is in the current object
|
||||
if (length - 1 >= payload.end_index) {
|
||||
endIndex = payload.end_index - (length - current.captionHistory.captions.length);
|
||||
current.captionHistory.captions = current.captionHistory.captions.slice(endIndex);
|
||||
objectsToUpdate.push(current);
|
||||
|
||||
break;
|
||||
|
||||
// if end_index wasn't in the current object, that means this whole object was deleted
|
||||
// initializing string to ''
|
||||
} else {
|
||||
current.captionHistory.captions = '';
|
||||
objectsToUpdate.push(current);
|
||||
}
|
||||
|
||||
current = captionsObjects[current.captionHistory.next];
|
||||
}
|
||||
}
|
||||
|
||||
// looking for the strings which exceed the limit and split them into multiple objects
|
||||
let maxIndex = captionsObjects.length - 1;
|
||||
for (i = 0; i < objectsToUpdate.length; i++) {
|
||||
if (objectsToUpdate[i].captionHistory.captions.length > CAPTION_CHUNK_LENGTH) {
|
||||
// string is too large. Check if the next object exists and if it can
|
||||
// accomodate the part of the string that exceeds the limits
|
||||
const _nextIndex = objectsToUpdate[i].captionHistory.next;
|
||||
if (_nextIndex != null &&
|
||||
captionsObjects[_nextIndex].captionHistory.captions.length < CAPTION_CHUNK_LENGTH) {
|
||||
const extraString = objectsToUpdate[i].captionHistory.captions.slice(CAPTION_CHUNK_LENGTH);
|
||||
|
||||
// could assign it directly, but our linter complained
|
||||
let _captions = objectsToUpdate[i].captionHistory.captions;
|
||||
_captions = _captions.slice(0, CAPTION_CHUNK_LENGTH);
|
||||
objectsToUpdate[i].captionHistory.captions = _captions;
|
||||
|
||||
// check to see if the next object was added to objectsToUpdate array
|
||||
if (objectsToUpdate[i + 1] != null &&
|
||||
objectsToUpdate[i].captionHistory.next == objectsToUpdate[i + 1].captionHistory.index) {
|
||||
objectsToUpdate[i + 1].captionHistory.captions = extraString +
|
||||
objectsToUpdate[i + 1].captionHistory.captions;
|
||||
|
||||
// next object wasn't added to objectsToUpdate array, adding it from captionsObjects array.
|
||||
} else {
|
||||
const nextObj = captionsObjects[objectsToUpdate[i].captionHistory.next];
|
||||
nextObj.captionHistory.captions = extraString + nextObj.captionHistory.captions;
|
||||
objectsToUpdate.push(nextObj);
|
||||
}
|
||||
|
||||
// next object was full already, so we create another and insert it in between them
|
||||
} else {
|
||||
// need to take a current object out of the objectsToUpdate and add it back after
|
||||
// every other object, so that Captions collection could be updated in a proper order
|
||||
const tempObj = objectsToUpdate.splice(i, 1);
|
||||
let extraString = tempObj[0].captionHistory.captions.slice(CAPTION_CHUNK_LENGTH);
|
||||
|
||||
tempObj[0].captionHistory.captions =
|
||||
tempObj[0].captionHistory.captions.slice(0, CAPTION_CHUNK_LENGTH);
|
||||
|
||||
maxIndex += 1;
|
||||
const tempIndex = tempObj[0].captionHistory.next;
|
||||
tempObj[0].captionHistory.next = maxIndex;
|
||||
|
||||
while (extraString.length != 0) {
|
||||
const entry = {
|
||||
meetingId,
|
||||
locale,
|
||||
captionHistory: {
|
||||
locale,
|
||||
ownerId: tempObj[0].captionHistory.ownerId,
|
||||
captions: extraString.slice(0, CAPTION_CHUNK_LENGTH),
|
||||
index: maxIndex,
|
||||
next: null,
|
||||
},
|
||||
};
|
||||
maxIndex += 1;
|
||||
extraString = extraString.slice(CAPTION_CHUNK_LENGTH);
|
||||
if (extraString.length > 0) {
|
||||
entry.captionHistory.next = maxIndex;
|
||||
} else {
|
||||
entry.captionHistory.next = tempIndex;
|
||||
}
|
||||
|
||||
objectsToUpdate.push(entry);
|
||||
}
|
||||
|
||||
objectsToUpdate.push(tempObj[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const captionsAdded = [];
|
||||
objectsToUpdate.forEach((entry) => {
|
||||
const { _id, meetingId, locale, captionHistory } = entry;
|
||||
captionsAdded.push(addCaption(meetingId, locale, captionHistory, _id));
|
||||
});
|
||||
|
||||
return captionsAdded;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function addCaption(meetingId, locale, captionHistory, id = false) {
|
||||
check(meetingId, String);
|
||||
check(locale, String);
|
||||
check(captionHistory, Object);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
locale,
|
||||
};
|
||||
|
||||
if (id) {
|
||||
selector._id = id;
|
||||
} else {
|
||||
selector['captionHistory.index'] = captionHistory.index;
|
||||
}
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
meetingId,
|
||||
locale,
|
||||
'captionHistory.locale': locale,
|
||||
'captionHistory.ownerId': captionHistory.ownerId,
|
||||
'captionHistory.captions': captionHistory.captions,
|
||||
'captionHistory.next': captionHistory.next,
|
||||
'captionHistory.index': captionHistory.index,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding caption to collection: ${err}`);
|
||||
}
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.verbose(`Added caption locale=${locale} meeting=${meetingId}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.verbose(`Upserted caption locale=${locale} meeting=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Captions.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function clearCaptions(meetingId) {
|
||||
if (meetingId) {
|
||||
return Captions.remove({ meetingId }, Logger.info(`Cleared Captions (${meetingId})`));
|
||||
}
|
||||
|
||||
return Captions.remove({}, Logger.info('Cleared Captions (all)'));
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import Captions from '/imports/api/1.1/captions';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function captions(credentials) {
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
Logger.verbose(`Publishing Captions for ${meetingId} ${requesterUserId} ${requesterToken}`);
|
||||
|
||||
return Captions.find({ meetingId });
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundCaptions = captions.bind(this);
|
||||
return mapToAcl('subscriptions.captions', boundCaptions)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('captions', publish);
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('chat');
|
@ -1,7 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handleChatMessage from './handlers/chatMessage';
|
||||
import handleChatHistory from './handlers/chatHistory';
|
||||
|
||||
RedisPubSub.on('get_chat_history_reply', handleChatHistory);
|
||||
RedisPubSub.on('send_public_chat_message', handleChatMessage);
|
||||
RedisPubSub.on('send_private_chat_message', handleChatMessage);
|
@ -1,24 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import { inReplyToHTML5Client } from '/imports/api/common/server/helpers';
|
||||
import addChat from '../modifiers/addChat';
|
||||
|
||||
export default function handleChatHistory({ payload }) {
|
||||
if (!inReplyToHTML5Client({ payload })) {
|
||||
return;
|
||||
}
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const chatHistory = payload.chat_history || [];
|
||||
|
||||
check(meetingId, String);
|
||||
check(chatHistory, Array);
|
||||
|
||||
const chatsAdded = [];
|
||||
|
||||
chatHistory.forEach((message) => {
|
||||
chatsAdded.push(addChat(meetingId, message));
|
||||
});
|
||||
|
||||
return chatsAdded;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import addChat from '../modifiers/addChat';
|
||||
|
||||
export default function handleChatMessage({ payload, header }) {
|
||||
const message = payload.message;
|
||||
const meetingId = payload.meeting_id;
|
||||
|
||||
check(meetingId, String);
|
||||
check(message, Object);
|
||||
|
||||
// use current_time instead of message.from_time so that the
|
||||
// chats from Flash and HTML5 have uniform times
|
||||
message.from_time = +(header.current_time);
|
||||
|
||||
return addChat(meetingId, message);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
Meteor.methods({});
|
@ -1,63 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import RegexWebUrl from '/imports/utils/regex-weburl';
|
||||
|
||||
const HTML_SAFE_MAP = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
};
|
||||
|
||||
const PUBLIC_CHAT_TYPE = 'PUBLIC_CHAT';
|
||||
|
||||
const parseMessage = (message) => {
|
||||
message = message || '';
|
||||
message = message.trim();
|
||||
|
||||
// Replace <br/> with \n\r
|
||||
message = message.replace(/<br\s*[\/]?>/gi, '\n\r');
|
||||
|
||||
// Sanitize. See: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/
|
||||
message = message.replace(/[<>'"]/g, c => HTML_SAFE_MAP[c]);
|
||||
|
||||
// Replace flash links to flash valid ones
|
||||
message = message.replace(RegexWebUrl, "<a href='event:$&'><u>$&</u></a>");
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
export default function sendChat(credentials, message) {
|
||||
const REDIS_CONFIG = Meteor.settings.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.chat;
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public;
|
||||
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
check(message, Object);
|
||||
|
||||
let actionName = message.to_userid === requesterUserId ? 'chatSelf' : 'chatPrivate';
|
||||
let eventName = 'send_private_chat_message';
|
||||
|
||||
message.message = parseMessage(message.message);
|
||||
|
||||
if (message.chat_type === PUBLIC_CHAT_TYPE) {
|
||||
eventName = 'send_public_chat_message';
|
||||
actionName = 'chatPublic';
|
||||
}
|
||||
|
||||
const payload = {
|
||||
message,
|
||||
meeting_id: meetingId,
|
||||
requester_id: message.from_userid,
|
||||
};
|
||||
|
||||
return RedisPubSub.publish(CHANNEL, eventName, payload);
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import Chat from '/imports/api/1.1/chat';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import { BREAK_LINE } from '/imports/utils/lineEndings.js';
|
||||
|
||||
const parseMessage = (message) => {
|
||||
message = message || '';
|
||||
|
||||
// Replace \r and \n to <br/>
|
||||
message = message.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, `$1${BREAK_LINE}$2`);
|
||||
|
||||
// Replace flash links to html valid ones
|
||||
message = message.split('<a href=\'event:').join('<a target="_blank" href=\'');
|
||||
message = message.split('<a href="event:').join('<a target="_blank" href="');
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
export default function addChat(meetingId, message) {
|
||||
// manually convert time from 1.408645053653E12 to 1408645053653 if necessary
|
||||
// (this is the time_from that the Flash client outputs)
|
||||
message.from_time = +(message.from_time.toString().split('.').join('').split('E')[0]);
|
||||
message.message = parseMessage(message.message);
|
||||
|
||||
const fromUserId = message.from_userid;
|
||||
const toUserId = message.to_userid;
|
||||
|
||||
check(fromUserId, String);
|
||||
check(toUserId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
'message.from_time': message.from_time,
|
||||
'message.from_userid': message.from_userid,
|
||||
'message.to_userid': message.to_userid,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
meetingId,
|
||||
message: {
|
||||
chat_type: message.chat_type,
|
||||
message: message.message,
|
||||
to_username: message.to_username,
|
||||
from_tz_offset: message.from_tz_offset,
|
||||
from_color: message.from_color,
|
||||
to_userid: message.to_userid,
|
||||
from_userid: message.from_userid,
|
||||
from_time: message.from_time,
|
||||
from_username: message.from_username,
|
||||
from_lang: message.from_lang,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding chat to collection: ${err}`);
|
||||
}
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
|
||||
if (insertedId) {
|
||||
const to = message.to_username || 'PUBLIC';
|
||||
return Logger.info(`Added chat id=${insertedId} from=${message.from_username} to=${to}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Chat.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import Chat from '/imports/api/1.1/chat';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function clearChats(meetingId) {
|
||||
if (meetingId) {
|
||||
return Chat.remove({ meetingId }, Logger.info(`Cleared Chats (${meetingId})`));
|
||||
}
|
||||
|
||||
return Chat.remove({}, Logger.info('Cleared Chats (all)'));
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import Chat from '/imports/api/1.1/chat';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import { BREAK_LINE } from '/imports/utils/lineEndings.js';
|
||||
|
||||
/**
|
||||
* Remove any system message from the user with userId.
|
||||
*
|
||||
* @param {string} meetingId
|
||||
* @param {string} userId
|
||||
*/
|
||||
export default function clearUserSystemMessages(meetingId, userId) {
|
||||
check(meetingId, String);
|
||||
check(userId, String);
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
'message.from_userid': CHAT_CONFIG.type_system,
|
||||
'message.to_userid': userId,
|
||||
};
|
||||
|
||||
return Chat.remove(selector, Logger.info(`Removing system messages from: (${userId})`));
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import Chat from '/imports/api/1.1/chat';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function chat(credentials) {
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public;
|
||||
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
Logger.info(`Publishing chat for ${meetingId} ${requesterUserId} ${requesterToken}`);
|
||||
|
||||
return Chat.find({
|
||||
$or: [
|
||||
{
|
||||
'message.chat_type': PUBLIC_CHAT_TYPE,
|
||||
meetingId,
|
||||
}, {
|
||||
'message.from_userid': requesterUserId,
|
||||
meetingId,
|
||||
}, {
|
||||
'message.to_userid': requesterUserId,
|
||||
meetingId,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundChat = chat.bind(this);
|
||||
return mapToAcl('subscriptions.chat', boundChat)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('chat', publish);
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('cursor');
|
@ -1,4 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handleCursorUpdate from './handlers/cursorUpdate';
|
||||
|
||||
RedisPubSub.on('presentation_cursor_updated_message', handleCursorUpdate);
|
@ -1,15 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import updateCursor from '../modifiers/updateCursor';
|
||||
|
||||
export default function handleCursorUpdate({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const x = payload.x_percent;
|
||||
const y = payload.y_percent;
|
||||
|
||||
check(meetingId, String);
|
||||
check(x, Number);
|
||||
check(y, Number);
|
||||
|
||||
return updateCursor(meetingId, x, y);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
// import { isAllowedTo } from '/imports/startup/server/userPermissions';
|
||||
import Presentations from '/imports/api/1.1/presentations';
|
||||
|
||||
export default function publishCursorUpdate(credentials, coordinates) {
|
||||
const REDIS_CONFIG = Meteor.settings.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.presentation;
|
||||
const EVENT_NAME = 'send_cursor_update';
|
||||
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
check(coordinates, {
|
||||
xPercent: Number,
|
||||
yPercent: Number,
|
||||
});
|
||||
|
||||
// if (!isAllowedTo('moveCursor', credentials)) {
|
||||
// throw new Meteor.Error('not-allowed', `You are not allowed to move the Cursor`);
|
||||
// }
|
||||
|
||||
const Presentation = Presentations.findOne({
|
||||
meetingId,
|
||||
'presentation.current': true,
|
||||
});
|
||||
|
||||
if (!Presentation) {
|
||||
throw new Meteor.Error(
|
||||
'presentation-not-found', 'You need a presentation to be able to move the cursor');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
x_percent: coordinates.xPercent,
|
||||
meeting_id: meetingId,
|
||||
y_percent: coordinates.yPercent,
|
||||
};
|
||||
|
||||
return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Cursor from '/imports/api/1.1/cursor';
|
||||
import updateCursor from './updateCursor';
|
||||
|
||||
export default function initializeCursor(meetingId) {
|
||||
check(meetingId, String);
|
||||
|
||||
return updateCursor(meetingId, 0, 0);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Cursor from '/imports/api/1.1/cursor';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function updateCursor(meetingId, x = 0, y = 0) {
|
||||
check(meetingId, String);
|
||||
check(x, Number);
|
||||
check(y, Number);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
meetingId,
|
||||
x,
|
||||
y,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Upserting cursor to collection: ${err}`);
|
||||
}
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Initialized cursor meeting=${meetingId}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.debug(`Updated cursor meeting=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Cursor.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import Cursor from '/imports/api/1.1/cursor';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function cursor(credentials) {
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
Logger.debug(`Publishing Cursor for ${meetingId} ${requesterUserId} ${requesterToken}`);
|
||||
|
||||
return Cursor.find({ meetingId });
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundCursor = cursor.bind(this);
|
||||
return mapToAcl('subscriptions.cursor', boundCursor)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('cursor', publish);
|
||||
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('deskshare');
|
@ -1,5 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import incomingDeskshareEvent from './handlers/incomingDeskshareEvent';
|
||||
|
||||
RedisPubSub.on('desk_share_notify_viewers_rtmp', incomingDeskshareEvent);
|
||||
RedisPubSub.on('desk_share_notify_a_single_viewer', incomingDeskshareEvent);
|
@ -1,24 +0,0 @@
|
||||
import Deskshare from '/imports/api/1.1/deskshare';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import modifyDeskshareStatus from '../modifiers/modifyDeskshareStatus';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function incomingDeskshareEvent({ payload }) {
|
||||
check(payload, Object);
|
||||
check(payload.meeting_id, String);
|
||||
check(payload.broadcasting, Boolean);
|
||||
check(payload.vh, Number);
|
||||
check(payload.vw, Number);
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const voiceBridge = Meetings.findOne({ meetingId }).voiceConf;
|
||||
|
||||
const deskShareInfo = {
|
||||
vw: payload.vw,
|
||||
vh: payload.vh,
|
||||
voiceBridge, // payload.voice_bridge
|
||||
broadcasting: payload.broadcasting,
|
||||
};
|
||||
modifyDeskshareStatus(meetingId, deskShareInfo);
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
import './eventHandlers';
|
||||
import './publishers';
|
@ -1,14 +0,0 @@
|
||||
import Deskshare from '/imports/api/1.1/deskshare';
|
||||
import { logger } from '/imports/startup/server/logger';
|
||||
|
||||
export function clearDeskshareCollection(meetingId) {
|
||||
if (meetingId != null) {
|
||||
Deskshare.remove({ meetingId }, function () {
|
||||
logger.info(`cleared Deskshare Collection (meetingId: ${this.meetingId}!)`);
|
||||
});
|
||||
} else {
|
||||
Deskshare.remove({}, () => {
|
||||
logger.info('cleared Deskshare Collection (all meetings)!');
|
||||
});
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import Deskshare from '/imports/api/1.1/deskshare';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Users from '/imports/api/1.1/users';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function modifyDeskshareStatus(meetingId, deskshareInfo) {
|
||||
check(meetingId, String);
|
||||
const presenter = Users.findOne({ meetingId, 'user.presenter': true });
|
||||
check(presenter, Object);
|
||||
check(presenter.user.userid, String);
|
||||
const startedById = presenter.user.userid;
|
||||
|
||||
Deskshare.upsert({ meetingId }, { $set: {
|
||||
broadcasting: deskshareInfo.broadcasting,
|
||||
timestamp: 'now',
|
||||
vw: deskshareInfo.vw,
|
||||
vh: deskshareInfo.vh,
|
||||
voiceBridge: deskshareInfo.voiceBridge,
|
||||
startedBy: startedById,
|
||||
} });
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import Deskshare from '/imports/api/1.1/deskshare';
|
||||
import { logger } from '/imports/startup/server/logger';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function deskshare(credentials) {
|
||||
const { meetingId } = credentials;
|
||||
logger.info(`publishing deskshare for ${meetingId}`);
|
||||
return Deskshare.find({ meetingId });
|
||||
}
|
||||
|
||||
|
||||
function publish(...args) {
|
||||
const boundDeskshare = deskshare.bind(this);
|
||||
return mapToAcl('subscriptions.deskshare', boundDeskshare)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('deskshare', publish);
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('meetings');
|
@ -1,17 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handleMeetingDestruction from './handlers/meetingDestruction';
|
||||
import handleRecordingStatusChange from './handlers/recordingStatusChange';
|
||||
import handlePermissionSettingsChange from './handlers/permissionSettingsChange';
|
||||
import handleMeetingCreation from './handlers/meetingCreation';
|
||||
import handleGetAllMeetings from './handlers/getAllMeetings';
|
||||
import handleStunTurnReply from './handlers/stunTurnReply';
|
||||
|
||||
RedisPubSub.on('meeting_destroyed_event', handleMeetingDestruction);
|
||||
RedisPubSub.on('meeting_ended_message', handleMeetingDestruction);
|
||||
RedisPubSub.on('end_and_kick_all_message', handleMeetingDestruction);
|
||||
RedisPubSub.on('disconnect_all_users_message', handleMeetingDestruction);
|
||||
RedisPubSub.on('recording_status_changed_message', handleRecordingStatusChange);
|
||||
RedisPubSub.on('new_permission_settings', handlePermissionSettingsChange);
|
||||
RedisPubSub.on('meeting_created_message', handleMeetingCreation);
|
||||
RedisPubSub.on('get_all_meetings_reply_message', handleGetAllMeetings);
|
||||
RedisPubSub.on('send_stun_turn_info_reply_message', handleStunTurnReply);
|
@ -1,36 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import addMeeting from '../modifiers/addMeeting';
|
||||
import removeMeeting from '../modifiers/removeMeeting';
|
||||
|
||||
export default function handleGetAllMeetings({ payload }) {
|
||||
let meetings = payload.meetings;
|
||||
|
||||
check(meetings, Array);
|
||||
|
||||
// We need to map the meetings payload because for some reason this payload
|
||||
// is different than the `meeting_created_message` one
|
||||
meetings = meetings.map(m => ({
|
||||
meeting_id: m.meetingID,
|
||||
name: m.meetingName,
|
||||
recorded: m.recorded,
|
||||
voice_conf: m.voiceBridge,
|
||||
duration: m.duration,
|
||||
}));
|
||||
|
||||
const meetingsIds = meetings.map(m => m.meeting_id);
|
||||
|
||||
const meetingsToRemove = Meetings.find({
|
||||
meetingId: { $nin: meetingsIds },
|
||||
}).fetch();
|
||||
|
||||
meetingsToRemove.forEach(meeting => removeMeeting(meeting.meetingId));
|
||||
|
||||
const meetingsAdded = [];
|
||||
meetings.forEach((meeting) => {
|
||||
meetingsAdded.push(addMeeting(meeting));
|
||||
});
|
||||
|
||||
return meetingsAdded;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import addMeeting from '../modifiers/addMeeting';
|
||||
|
||||
export default function handleMeetingCreation({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
return addMeeting(payload);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import removeMeeting from '../modifiers/removeMeeting';
|
||||
|
||||
export default function handleMeetingDestruction({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
return removeMeeting(meetingId);
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import lockAllViewersMic from '/imports/api/1.1/users/server/modifiers/lockAllViewersMic';
|
||||
|
||||
export default function handlePermissionSettingsChange({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const permissions = payload.permissions;
|
||||
|
||||
check(meetingId, String);
|
||||
check(permissions, Object);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const Meeting = Meetings.findOne(selector);
|
||||
|
||||
if (!Meeting) {
|
||||
throw new Meteor.Error('meeting-not-found', `Meeting id=${meetingId} was not found`);
|
||||
}
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
roomLockSettings: {
|
||||
disablePrivateChat: permissions.disablePrivateChat,
|
||||
disableCam: permissions.disableCam,
|
||||
disableMic: permissions.disableMic,
|
||||
lockOnJoin: permissions.lockOnJoin,
|
||||
lockedLayout: permissions.lockedLayout,
|
||||
disablePublicChat: permissions.disablePublicChat,
|
||||
lockOnJoinConfigurable: permissions.lockOnJoinConfigurable,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating meeting permissions: ${err}`);
|
||||
}
|
||||
|
||||
if (permissions.disableMic) {
|
||||
lockAllViewersMic(meetingId);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Updated meeting permissions id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Meetings.update(selector, modifier, cb);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
|
||||
export default function handleRecordingStatusChange({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const intendedForRecording = payload.recorded;
|
||||
const currentlyBeingRecorded = payload.recording;
|
||||
|
||||
check(meetingId, String);
|
||||
check(intendedForRecording, Boolean);
|
||||
check(currentlyBeingRecorded, Boolean);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
intendedForRecording,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
currentlyBeingRecorded,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating meeting recording status: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Updated meeting recording status id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Meetings.update(selector, modifier, cb);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
|
||||
export default function handleStunTurnReply({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const { stuns, turns } = payload;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
stuns,
|
||||
turns,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating meeting stuns/turns: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Updated meeting stuns/turns id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Meetings.update(selector, modifier, cb);
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
Meteor.methods({
|
||||
});
|
@ -1,55 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import initializeCursor from '/imports/api/1.1/cursor/server/modifiers/initializeCursor';
|
||||
|
||||
export default function addMeeting(meeting) {
|
||||
const APP_CONFIG = Meteor.settings.public.app;
|
||||
const meetingId = meeting.meeting_id;
|
||||
|
||||
check(meeting, Object);
|
||||
check(meetingId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
meetingId,
|
||||
meetingName: meeting.name,
|
||||
intendedForRecording: meeting.recorded,
|
||||
currentlyBeingRecorded: false,
|
||||
voiceConf: meeting.voice_conf,
|
||||
duration: meeting.duration,
|
||||
roomLockSettings: {
|
||||
disablePrivateChat: false,
|
||||
disableCam: false,
|
||||
disableMic: false,
|
||||
lockOnJoin: APP_CONFIG.lockOnJoin,
|
||||
lockedLayout: false,
|
||||
disablePublicChat: false,
|
||||
lockOnJoinConfigurable: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding meeting to collection: ${err}`);
|
||||
}
|
||||
|
||||
initializeCursor(meetingId);
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Added meeting id=${meetingId}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Upserted meeting id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Meetings.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import Meetings from '/imports/api/1.1/chat';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import removeMeeting from './removeMeeting';
|
||||
|
||||
import clearUsers from '/imports/api/1.1/users/server/modifiers/clearUsers';
|
||||
import clearChats from '/imports/api/1.1/chat/server/modifiers/clearChats';
|
||||
import clearBreakouts from '/imports/api/1.1/breakouts/server/modifiers/clearBreakouts';
|
||||
import clearShapes from '/imports/api/1.1/shapes/server/modifiers/clearShapes';
|
||||
import clearSlides from '/imports/api/1.1/slides/server/modifiers/clearSlides';
|
||||
import clearPolls from '/imports/api/1.1/polls/server/modifiers/clearPolls';
|
||||
import clearCursor from '/imports/api/1.1/cursor/server/modifiers/clearCursor';
|
||||
import clearCaptions from '/imports/api/1.1/captions/server/modifiers/clearCaptions';
|
||||
import clearPresentations from '/imports/api/1.1/presentations/server/modifiers/clearPresentations';
|
||||
|
||||
export default function clearMeetings() {
|
||||
return Meetings.remove({}, (err) => {
|
||||
clearCaptions();
|
||||
clearChats();
|
||||
clearCursor();
|
||||
clearPresentations();
|
||||
clearBreakouts();
|
||||
clearPolls();
|
||||
clearShapes();
|
||||
clearSlides();
|
||||
clearUsers();
|
||||
|
||||
return Logger.info('Cleared Meetings (all)');
|
||||
});
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
import clearUsers from '/imports/api/1.1/users/server/modifiers/clearUsers';
|
||||
import clearChats from '/imports/api/1.1/chat/server/modifiers/clearChats';
|
||||
import clearShapes from '/imports/api/1.1/shapes/server/modifiers/clearShapes';
|
||||
import clearSlides from '/imports/api/1.1/slides/server/modifiers/clearSlides';
|
||||
import clearPolls from '/imports/api/1.1/polls/server/modifiers/clearPolls';
|
||||
import clearCursor from '/imports/api/1.1/cursor/server/modifiers/clearCursor';
|
||||
import clearCaptions from '/imports/api/1.1/captions/server/modifiers/clearCaptions';
|
||||
import clearPresentations from '/imports/api/1.1/presentations/server/modifiers/clearPresentations';
|
||||
|
||||
export default function removeMeeting(meetingId) {
|
||||
check(meetingId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Removing meeting from collection: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
clearCaptions(meetingId);
|
||||
clearChats(meetingId);
|
||||
clearCursor(meetingId);
|
||||
clearPresentations(meetingId);
|
||||
clearPolls(meetingId);
|
||||
clearShapes(meetingId);
|
||||
clearSlides(meetingId);
|
||||
clearUsers(meetingId);
|
||||
|
||||
return Logger.info(`Removed meeting id=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Meetings.remove(selector, cb);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function meetings(credentials) {
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
Logger.info(`Publishing meeting=${meetingId} ${requesterUserId} ${requesterToken}`);
|
||||
|
||||
return Meetings.find({
|
||||
meetingId,
|
||||
});
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundMeetings = meetings.bind(this);
|
||||
return mapToAcl('subscriptions.meetings', boundMeetings)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('meetings', publish);
|
||||
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('polls');
|
@ -1,9 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handlePollStopped from './handlers/pollStopped';
|
||||
import handlePollStarted from './handlers/pollStarted';
|
||||
import handleUserVoted from './handlers/userVoted';
|
||||
|
||||
RedisPubSub.on('poll_show_result_message', handlePollStopped);
|
||||
RedisPubSub.on('poll_started_message', handlePollStarted);
|
||||
RedisPubSub.on('poll_stopped_message', handlePollStopped);
|
||||
RedisPubSub.on('user_voted_poll_message', handleUserVoted);
|
@ -1,16 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import addPoll from '../modifiers/addPoll';
|
||||
|
||||
export default function pollStarted({ payload }) {
|
||||
check(payload, Object);
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const requesterId = payload.requester_id;
|
||||
const poll = payload.poll;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterId, String);
|
||||
check(poll, Object);
|
||||
|
||||
return addPoll(meetingId, requesterId, poll);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import removePoll from '../modifiers/removePoll';
|
||||
import clearPolls from '../modifiers/clearPolls';
|
||||
|
||||
export default function pollStopped({ payload }) {
|
||||
check(payload, Object);
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const poll = payload.poll;
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
if (poll) {
|
||||
const pollId = poll.id;
|
||||
|
||||
check(pollId, String);
|
||||
|
||||
return removePoll(meetingId, pollId);
|
||||
}
|
||||
|
||||
return clearPolls(meetingId);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import updateVotes from '../modifiers/updateVotes';
|
||||
|
||||
export default function userVoted({ payload }) {
|
||||
check(payload, Object);
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const poll = payload.poll;
|
||||
const requesterId = payload.presenter_id;
|
||||
|
||||
check(meetingId, String);
|
||||
check(poll, Object);
|
||||
check(requesterId, String);
|
||||
|
||||
return updateVotes(poll, meetingId, requesterId);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import publishVote1x from './methods/publishVote';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
Meteor.methods(mapToAcl(['methods.publishVote'], {
|
||||
publishVote1x,
|
||||
}));
|
@ -1,56 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import { check } from 'meteor/check';
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function publishVote(credentials, pollId, pollAnswerId) { // TODO discuss location
|
||||
const REDIS_CONFIG = Meteor.settings.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.polling;
|
||||
const EVENT_NAME = 'vote_poll_user_request_message';
|
||||
|
||||
const { meetingId, requesterUserId } = credentials;
|
||||
|
||||
const currentPoll = Polls.findOne({
|
||||
users: requesterUserId,
|
||||
meetingId,
|
||||
'poll.answers.id': pollAnswerId,
|
||||
'poll.id': pollId,
|
||||
});
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(pollAnswerId, Number);
|
||||
check(currentPoll.meetingId, String);
|
||||
|
||||
const payload = {
|
||||
meeting_id: currentPoll.meetingId,
|
||||
user_id: requesterUserId,
|
||||
poll_id: currentPoll.poll.id,
|
||||
question_id: 0,
|
||||
answer_id: pollAnswerId,
|
||||
};
|
||||
|
||||
const selector = {
|
||||
users: requesterUserId,
|
||||
meetingId,
|
||||
'poll.answers.id': pollAnswerId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$pull: {
|
||||
users: requesterUserId,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating Polls collection: ${err}`);
|
||||
}
|
||||
|
||||
return Logger.info(`Updating Polls collection (meetingId: ${meetingId},
|
||||
pollId: ${currentPoll.poll.id}!)`);
|
||||
};
|
||||
|
||||
Polls.update(selector, modifier, cb);
|
||||
return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import Meetings from '/imports/api/1.1/meetings';
|
||||
import Users from '/imports/api/1.1/users';
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function addPoll(meetingId, requesterId, poll) {
|
||||
check(poll, Object);
|
||||
check(requesterId, String);
|
||||
check(meetingId, String);
|
||||
|
||||
let selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const options = {
|
||||
fields: {
|
||||
'user.userid': 1,
|
||||
_id: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const userIds = Users.find(selector, options)
|
||||
.fetch()
|
||||
.map(user => user.user.userid);
|
||||
|
||||
selector = {
|
||||
meetingId,
|
||||
requester: requesterId,
|
||||
'poll.id': poll.id,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
meetingId,
|
||||
poll,
|
||||
requester: requesterId,
|
||||
users: userIds,
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err != null) {
|
||||
return Logger.error(`Adding Poll to collection: ${poll.id}`);
|
||||
}
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Added Poll id=${poll.id}`);
|
||||
}
|
||||
|
||||
return Logger.info(`Upserted Poll id=${poll.id}`);
|
||||
};
|
||||
|
||||
return Polls.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function clearPolls(meetingId) {
|
||||
if (meetingId) {
|
||||
return Polls.remove({ meetingId }, Logger.info(`Cleared Polls (${meetingId})`));
|
||||
}
|
||||
|
||||
return Polls.remove({}, Logger.info('Cleared Polls (all)'));
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function removePoll(meetingId, pollId) {
|
||||
check(meetingId, String);
|
||||
check(pollId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
'poll.id': pollId,
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Removing Poll from collection: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Removed Poll id=${pollId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Polls.remove(selector, cb);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function updateVotes(poll, meetingId, requesterId) {
|
||||
check(meetingId, String);
|
||||
check(requesterId, String);
|
||||
check(poll, Object);
|
||||
|
||||
const {
|
||||
id,
|
||||
answers,
|
||||
} = poll;
|
||||
|
||||
const numResponders = poll.num_responders;
|
||||
const numRespondents = poll.num_respondents;
|
||||
|
||||
check(id, String);
|
||||
check(answers, Array);
|
||||
|
||||
check(numResponders, Number);
|
||||
check(numRespondents, Number);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
requester: requesterId,
|
||||
'poll.id': id,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
requester: requesterId,
|
||||
poll: {
|
||||
answers,
|
||||
num_responders: numResponders,
|
||||
num_respondents: numRespondents,
|
||||
id,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Updating Polls collection: ${err}`);
|
||||
}
|
||||
|
||||
Logger.info(`Updating Polls collection (meetingId: ${meetingId}, pollId: ${id}!)`);
|
||||
};
|
||||
|
||||
return Polls.update(selector, modifier, cb);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Polls from '/imports/api/1.1/polls';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function polls(credentials) {
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
users: requesterUserId,
|
||||
};
|
||||
|
||||
return Polls.find(selector);
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundPolls = polls.bind(this);
|
||||
return mapToAcl('subscriptions.polls', boundPolls)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('polls', publish);
|
||||
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('presentations');
|
@ -1,8 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handlePresentationRemove from './handlers/presentationRemove';
|
||||
import handlePresentationChange from './handlers/presentationChange';
|
||||
import handlePresentationInfoReply from './handlers/presentationInfoReply';
|
||||
|
||||
RedisPubSub.on('presentation_removed_message', handlePresentationRemove);
|
||||
RedisPubSub.on('presentation_shared_message', handlePresentationChange);
|
||||
RedisPubSub.on('get_presentation_info_reply', handlePresentationInfoReply);
|
@ -1,44 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Presentations from './../../';
|
||||
|
||||
import addPresentation from '../modifiers/addPresentation';
|
||||
|
||||
const clearCurrentPresentation = (meetingId, presentationId) => {
|
||||
const selector = {
|
||||
meetingId,
|
||||
presentationId: { $ne: presentationId },
|
||||
'presentation.current': true,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: { 'presentation.current': false },
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Unsetting the current presentation: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info('Unsetted as current presentation');
|
||||
}
|
||||
};
|
||||
|
||||
return Presentations.update(selector, modifier, cb);
|
||||
};
|
||||
|
||||
export default function handlePresentationChange({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const presentation = payload.presentation;
|
||||
|
||||
check(meetingId, String);
|
||||
check(presentation, Object);
|
||||
|
||||
// We need to clear the flag of the older current presentation ¯\_(ツ)_/¯
|
||||
if (presentation.current) {
|
||||
clearCurrentPresentation(meetingId, presentation.id);
|
||||
}
|
||||
|
||||
return addPresentation(meetingId, presentation);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
import { inReplyToHTML5Client } from '/imports/api/common/server/helpers';
|
||||
import Presentations from './../../';
|
||||
|
||||
import addPresentation from '../modifiers/addPresentation';
|
||||
import removePresentation from '../modifiers/removePresentation';
|
||||
|
||||
export default function handlePresentationInfoReply({ payload }) {
|
||||
if (!inReplyToHTML5Client({ payload })) {
|
||||
return;
|
||||
}
|
||||
|
||||
const meetingId = payload.meeting_id;
|
||||
const presentations = payload.presentations;
|
||||
|
||||
check(meetingId, String);
|
||||
check(presentations, Array);
|
||||
|
||||
const presentationsIds = presentations.map(_ => _.id);
|
||||
const presentationsToRemove = Presentations.find({
|
||||
meetingId,
|
||||
'presentation.id': { $nin: presentationsIds },
|
||||
}).fetch();
|
||||
|
||||
presentationsToRemove.forEach(p => removePresentation(meetingId, p.presentation.id));
|
||||
|
||||
const presentationsAdded = [];
|
||||
presentations.forEach((presentation) => {
|
||||
presentationsAdded.push(addPresentation(meetingId, presentation));
|
||||
});
|
||||
|
||||
return presentationsAdded;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
import removePresentation from '../modifiers/removePresentation';
|
||||
|
||||
export default function handlePresentationRemove({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const presentationId = payload.presentation_id;
|
||||
|
||||
check(meetingId, String);
|
||||
check(presentationId, String);
|
||||
|
||||
return removePresentation(meetingId, presentationId);
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
Meteor.methods({
|
||||
});
|
@ -1,53 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Presentations from './../../';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
import addSlide from '/imports/api/1.1/slides/server/modifiers/addSlide';
|
||||
|
||||
const addSlides = (meetingId, presentationId, slides) => {
|
||||
const slidesAdded = [];
|
||||
|
||||
slides.forEach((slide) => {
|
||||
slidesAdded.push(addSlide(meetingId, presentationId, slide));
|
||||
});
|
||||
|
||||
return slidesAdded;
|
||||
};
|
||||
|
||||
export default function addPresentation(meetingId, presentation) {
|
||||
check(meetingId, String);
|
||||
check(presentation, Object);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
'presentation.id': presentation.id,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
meetingId,
|
||||
'presentation.id': presentation.id,
|
||||
'presentation.name': presentation.name,
|
||||
'presentation.current': presentation.current,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Adding presentation to collection: ${err}`);
|
||||
}
|
||||
|
||||
addSlides(meetingId, presentation.id, presentation.pages);
|
||||
|
||||
const { insertedId } = numChanged;
|
||||
if (insertedId) {
|
||||
return Logger.info(`Added presentation id=${presentation.id} meeting=${meetingId}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
return Logger.info(`Upserted presentation id=${presentation.id} meeting=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Presentations.upsert(selector, modifier, cb);
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Presentations from './../../';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function changeCurrentPresentation(meetingId, presentationId) {
|
||||
check(meetingId, String);
|
||||
check(presentationId, String);
|
||||
|
||||
const oldCurrent = {
|
||||
selector: {
|
||||
meetingId,
|
||||
'presentation.current': true,
|
||||
},
|
||||
modifier: {
|
||||
$set: { 'presentation.current': false },
|
||||
},
|
||||
callback: (err) => {
|
||||
if (err) {
|
||||
return Logger.error(`Unsetting the current presentation: ${err}`);
|
||||
}
|
||||
|
||||
return Logger.info('Unsetted as current presentation');
|
||||
},
|
||||
};
|
||||
|
||||
const newCurrent = {
|
||||
selector: {
|
||||
meetingId,
|
||||
'presentation.id': presentationId,
|
||||
},
|
||||
modifier: {
|
||||
$set: { 'presentation.current': true },
|
||||
},
|
||||
callback: (err) => {
|
||||
if (err) {
|
||||
return Logger.error(`Setting as current presentation id=${presentationId}: ${err}`);
|
||||
}
|
||||
|
||||
return Logger.info(`Setted as current presentation id=${presentationId}`);
|
||||
},
|
||||
};
|
||||
|
||||
const oldPresentation = Presentations.findOne(oldCurrent.selector);
|
||||
const newPresentation = Presentations.findOne(newCurrent.selector);
|
||||
|
||||
if (newPresentation) {
|
||||
Presentations.update(newPresentation._id, newCurrent.modifier, newCurrent.callback);
|
||||
}
|
||||
|
||||
if (oldPresentation) {
|
||||
Presentations.update(oldPresentation._id, oldCurrent.modifier, oldCurrent.callback);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import Presentations from './../../';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function clearPresentations(meetingId) {
|
||||
if (meetingId) {
|
||||
return Presentations.remove({ meetingId },
|
||||
Logger.info(`Cleared Presentations (${meetingId})`));
|
||||
}
|
||||
return Presentations.remove({}, Logger.info('Cleared Presentations (all)'));
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
import Presentations from './../../';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
import clearSlidesPresentation from '/imports/api/1.1/slides/server/modifiers/clearSlidesPresentation';
|
||||
|
||||
export default function removePresentation(meetingId, presentationId) {
|
||||
check(meetingId, String);
|
||||
check(presentationId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
'presentation.id': presentationId,
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Removing presentation from collection: ${err}`);
|
||||
}
|
||||
|
||||
if (numChanged) {
|
||||
clearSlidesPresentation(meetingId, presentationId);
|
||||
return Logger.info(`Removed presentation id=${presentationId} meeting=${meetingId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return Presentations.remove(selector, cb);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Presentations from './../';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import mapToAcl from '/imports/startup/mapToAcl';
|
||||
|
||||
function presentations(credentials) {
|
||||
const { meetingId, requesterUserId, requesterToken } = credentials;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
check(requesterToken, String);
|
||||
|
||||
Logger.info(`Publishing Presentations for ${meetingId} ${requesterUserId} ${requesterToken}`);
|
||||
|
||||
return Presentations.find({ meetingId });
|
||||
}
|
||||
|
||||
function publish(...args) {
|
||||
const boundPresentations = presentations.bind(this);
|
||||
return mapToAcl('subscriptions.presentations', boundPresentations)(args);
|
||||
}
|
||||
|
||||
Meteor.publish('presentations', publish);
|
||||
|
@ -1 +0,0 @@
|
||||
export default new Mongo.Collection('shapes');
|
@ -1,10 +0,0 @@
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import handleWhiteboardGetReply from './handlers/whiteboardGetReply';
|
||||
import handleWhiteboardSend from './handlers/whiteboardSend';
|
||||
import handleWhiteboardCleared from './handlers/whiteboardCleared';
|
||||
import handleWhiteboardUndo from './handlers/whiteboardUndo';
|
||||
|
||||
RedisPubSub.on('get_whiteboard_shapes_reply', handleWhiteboardGetReply);
|
||||
RedisPubSub.on('send_whiteboard_shape_message', handleWhiteboardSend);
|
||||
RedisPubSub.on('whiteboard_cleared_message', handleWhiteboardCleared);
|
||||
RedisPubSub.on('undo_whiteboard_request', handleWhiteboardUndo);
|
@ -1,13 +0,0 @@
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
import clearShapesWhiteboard from '../modifiers/clearShapesWhiteboard';
|
||||
|
||||
export default function handleWhiteboardCleared({ payload }) {
|
||||
const meetingId = payload.meeting_id;
|
||||
const whiteboardId = payload.whiteboard_id;
|
||||
|
||||
check(meetingId, String);
|
||||
check(whiteboardId, String);
|
||||
|
||||
return clearShapesWhiteboard(meetingId, whiteboardId);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user