Merge remote-tracking branch 'upstream/master' into EmojiReworked

This commit is contained in:
Lajellu 2016-09-22 12:12:38 -07:00
commit 3a0b4e3fef
65 changed files with 642 additions and 470 deletions

View File

@ -29,7 +29,7 @@ import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUserToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.inbound.InboundConnectionFailure;
@ -128,7 +128,7 @@ public class ConnectionManager {
}
}
public void tranfer(TransferUsetToMeetingCommand tutmc) {
public void tranfer(TransferUserToMeetingCommand tutmc) {
Client c = manager.getESLClient();
if (c.canSend()) {
c.sendAsyncApiCommand(tutmc.getCommand(), tutmc.getCommandArgs());

View File

@ -31,7 +31,7 @@ import org.bigbluebutton.freeswitch.voice.freeswitch.actions.FreeswitchCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUserToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
public class FreeswitchApplication {
@ -48,9 +48,12 @@ public class FreeswitchApplication {
private final String USER = "0"; /* not used for now */
private volatile boolean sendMessages = false;
private final String audioProfile;
public FreeswitchApplication(ConnectionManager manager) {
public FreeswitchApplication(ConnectionManager manager, String profile) {
this.manager = manager;
this.audioProfile = profile;
}
private void queueMessage(FreeswitchCommand command) {
@ -62,12 +65,13 @@ public class FreeswitchApplication {
}
}
public void transferUserToMeeting(String voiceConfId,
String targetVoiceConfId, String voiceUserId) {
TransferUsetToMeetingCommand tutmc = new TransferUsetToMeetingCommand(
voiceConfId, targetVoiceConfId, voiceUserId, USER);
queueMessage(tutmc);
}
public void transferUserToMeeting(String voiceConfId,
String targetVoiceConfId, String voiceUserId) {
TransferUserToMeetingCommand tutmc = new TransferUserToMeetingCommand(
voiceConfId, targetVoiceConfId, voiceUserId, this.audioProfile,
USER);
queueMessage(tutmc);
}
public void start() {
sendMessages = true;
@ -153,8 +157,8 @@ public class FreeswitchApplication {
EjectAllUsersCommand cmd = (EjectAllUsersCommand) command;
System.out.println("Sending EjectAllUsersCommand for conference = [" + cmd.getRoom() + "]");
manager.ejectAll(cmd);
} else if (command instanceof TransferUsetToMeetingCommand) {
TransferUsetToMeetingCommand cmd = (TransferUsetToMeetingCommand) command;
} else if (command instanceof TransferUserToMeetingCommand) {
TransferUserToMeetingCommand cmd = (TransferUserToMeetingCommand) command;
System.out.println("Sending TransferUsetToMeetingCommand for conference = ["
+ cmd.getRoom() + "]");
manager.tranfer(cmd);

View File

@ -19,21 +19,23 @@
package org.bigbluebutton.freeswitch.voice.freeswitch.actions;
public class TransferUsetToMeetingCommand extends FreeswitchCommand {
public class TransferUserToMeetingCommand extends FreeswitchCommand {
private final String targetRoom;
private final String participant;
private final String targetRoom;
private final String participant;
private final String audioProfile;
public TransferUsetToMeetingCommand(String room, String targetRoom,
String participant, String requesterId) {
super(room, requesterId);
this.targetRoom = targetRoom;
this.participant = participant;
}
public TransferUserToMeetingCommand(String room, String targetRoom,
String participant, String profile, String requesterId) {
super(room, requesterId);
this.targetRoom = targetRoom;
this.participant = participant;
this.audioProfile = profile;
}
@Override
public String getCommandArgs() {
return room + SPACE + "transfer" + SPACE + targetRoom + SPACE
+ participant;
}
@Override
public String getCommandArgs() {
return room + SPACE + "transfer" + SPACE + targetRoom + "@"
+ this.audioProfile + SPACE + participant;
}
}

View File

@ -24,6 +24,9 @@ freeswitch {
port=8021
password="ClueCon"
}
conf {
profile="cdquality"
}
}
redis {

View File

@ -31,7 +31,7 @@ object Boot extends App with SystemConfiguration {
connManager.start()
val fsApplication = new FreeswitchApplication(connManager)
val fsApplication = new FreeswitchApplication(connManager, fsProfile)
fsApplication.start()
val redisMsgReceiver = new RedisMessageReceiver(fsApplication)

View File

@ -10,6 +10,7 @@ trait SystemConfiguration {
lazy val eslHost = Try(config.getString("freeswitch.esl.host")).getOrElse("127.0.0.1")
lazy val eslPort = Try(config.getInt("freeswitch.esl.port")).getOrElse(8021)
lazy val eslPassword = Try(config.getString("freeswitch.esl.password")).getOrElse("ClueCon")
lazy val fsProfile = Try(config.getString("freeswitch.conf.profile")).getOrElse("cdquality")
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)

View File

@ -4,7 +4,7 @@
proxy_pass http://127.0.0.1:8080;
proxy_redirect default;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Forwarded-Proto $scheme;
# Allow 30M uploaded presentation document.
client_max_body_size 30m;

View File

@ -16,6 +16,7 @@ import org.bigbluebutton.screenshare.client.ExitCode;
import org.bigbluebutton.screenshare.client.ScreenShareInfo;
import org.bigbluebutton.screenshare.client.net.NetworkConnectionListener;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
@ -144,25 +145,27 @@ public class FfmpegScreenshare {
frame = grabber.grabImage();
if (frame != null) {
try {
long timestamp = now - startTime;
// Override timestamp from system screen grabber. Otherwise, we will have skewed recorded file.
// FfmpegFrameRecorder needs to propagate this timestamp into the avpacket sent to the server.
// ralam - Sept. 14, 2016
frame.timestamp = timestamp;
//System.out.println("frame timestamp=[" + frame.timestamp + "] ");
mainRecorder.record(frame);
} catch (Exception e) {
//System.out.println("CaptureScreen Exception 1");
if (!ignoreDisconnect) {
listener.networkConnectionException(ExitCode.INTERNAL_ERROR, null);
}
}
}
} catch (Exception e1) {
listener.networkConnectionException(ExitCode.INTERNAL_ERROR, null);
}
long sleepFramerate = (long) (1000 / frameRate);
long timestamp = now - startTime;
mainRecorder.setTimestamp(timestamp * 1000);
// System.out.println("i=[" + i + "] timestamp=[" + timestamp + "]");
//System.out.println("timestamp=[" + timestamp + "]");
mainRecorder.setFrameNumber(frameNumber);
// System.out.println("[ENCODER] encoded image " + frameNumber + " in " + (System.currentTimeMillis() - now));

View File

@ -688,7 +688,7 @@ public class FFmpegFrameRecorder extends FrameRecorder {
if (oc != null) {
try {
/* flush all the buffers */
while (video_st != null && ifmt_ctx == null && recordImage(0, 0, 0, 0, 0, AV_PIX_FMT_NONE, (Buffer[])null));
while (video_st != null && ifmt_ctx == null && recordImage(0, 0, 0, 0, 0, AV_PIX_FMT_NONE, 0, (Buffer[])null));
while (audio_st != null && ifmt_ctx == null && recordSamples(0, 0, (Buffer[])null));
if (interleaved && video_st != null && audio_st != null) {
@ -710,11 +710,11 @@ public class FFmpegFrameRecorder extends FrameRecorder {
}
public void record(Frame frame, int pixelFormat) throws Exception {
if (frame == null || (frame.image == null && frame.samples == null)) {
recordImage(0, 0, 0, 0, 0, pixelFormat, (Buffer[])null);
recordImage(0, 0, 0, 0, 0, pixelFormat, frame.timestamp, (Buffer[])null);
} else {
if (frame.image != null) {
frame.keyFrame = recordImage(frame.imageWidth, frame.imageHeight, frame.imageDepth,
frame.imageChannels, frame.imageStride, pixelFormat, frame.image);
frame.imageChannels, frame.imageStride, pixelFormat, frame.timestamp, frame.image);
}
if (frame.samples != null) {
frame.keyFrame = recordSamples(frame.sampleRate, frame.audioChannels, frame.samples);
@ -722,7 +722,8 @@ public class FFmpegFrameRecorder extends FrameRecorder {
}
}
public boolean recordImage(int width, int height, int depth, int channels, int stride, int pixelFormat, Buffer ... image) throws Exception {
public boolean recordImage(int width, int height, int depth, int channels, int stride,
int pixelFormat, long frameTimestamp, Buffer ... image) throws Exception {
if (video_st == null) {
throw new Exception("No video output stream (Is imageWidth > 0 && imageHeight > 0 and has start() been called?)");
}
@ -808,10 +809,15 @@ public class FFmpegFrameRecorder extends FrameRecorder {
/* if zero size, it means the image was buffered */
if (got_video_packet[0] != 0) {
if (video_pkt.pts() != AV_NOPTS_VALUE) {
video_pkt.pts(av_rescale_q(video_pkt.pts(), video_c.time_base(), video_st.time_base()));
// Override timestamp from system screen grabber. Otherwise, we will have skewed recorded file.
// FfmpegFrameRecorder needs to propagate this timestamp into the avpacket sent to the server.
// ralam - Sept. 14, 2016
video_pkt.pts(frameTimestamp);
//video_pkt.pts(av_rescale_q(video_pkt.pts(), video_c.time_base(), video_st.time_base()));
}
if (video_pkt.dts() != AV_NOPTS_VALUE) {
video_pkt.dts(av_rescale_q(video_pkt.dts(), video_c.time_base(), video_st.time_base()));
video_pkt.dts(frameTimestamp);
//video_pkt.dts(av_rescale_q(video_pkt.dts(), video_c.time_base(), video_st.time_base()));
}
video_pkt.stream_index(video_st.index());
} else {

View File

@ -26,7 +26,7 @@ import org.bigbluebutton.red5.pubsub.MessagePublisher;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.Red5;
import org.slf4j.Logger;
public class CaptionService {
private static Logger log = Red5LoggerFactory.getLogger( CaptionService.class, "bigbluebutton" );
@ -59,15 +59,29 @@ public class CaptionService {
}
public void editCaptionHistory(Map<String, Object> msg) {
int startIndex = (Integer) msg.get("startIndex");
int endIndex = (Integer) msg.get("endIndex");
String locale = msg.get("locale").toString();
String localeCode = msg.get("localeCode").toString();
String text = msg.get("text").toString();
String meetingID = Red5.getConnectionLocal().getScope().getName();
String userID = getBbbSession().getInternalUserID();
Integer startIndex;
if (msg.get("startIndex") instanceof Double) {
Double tempStartIndex = (Double) msg.get("startIndex");
startIndex = tempStartIndex.intValue();
} else {
startIndex = (Integer) msg.get("startIndex");
}
Integer endIndex;
if (msg.get("endIndex") instanceof Double) {
Double tempEndIndex = (Double) msg.get("endIndex");
endIndex = tempEndIndex.intValue();
} else {
endIndex = (Integer) msg.get("endIndex");
}
red5InGW.editCaptionHistory(meetingID, userID, startIndex, endIndex, locale, localeCode, text);
}
}

View File

@ -41,9 +41,23 @@ public class PollingService {
String meetingID = Red5.getConnectionLocal().getScope().getName();
String userId = getBbbSession().getInternalUserID();
String pollId = (String) message.get("pollId");
Integer questionId = (Integer) message.get("answerId");
Integer answerId = (Integer) message.get("answerId");
Integer questionId;
if (message.get("answerId") instanceof Double) {
Double tempQuestionId = (Double) message.get("answerId");
questionId = tempQuestionId.intValue();
} else {
questionId = (Integer) message.get("answerId");
}
Integer answerId;
if (message.get("answerId") instanceof Double) {
Double tempAnswerId = (Double) message.get("answerId");
answerId = tempAnswerId.intValue();
} else {
answerId = (Integer) message.get("answerId");
}
red5GW.votePoll(meetingID, userId, pollId, questionId, answerId);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 391 B

View File

@ -322,12 +322,12 @@ bbb.screensharePublish.helpText.MacChrome1 = 1. Locate 'screenshare.jnlp'
bbb.screensharePublish.helpText.MacChrome2 = 2. Select 'Show In Finder'
bbb.screensharePublish.helpText.MacChrome3 = 3. Right-click and select 'Open'
bbb.screensharePublish.helpText.MacChrome4 = 4. Select 'Open' (if prompted)
bbb.screensharePublish.helpText.LinuxFirefox1 = Placeholder linuxFirefox1
bbb.screensharePublish.helpText.LinuxFirefox2 = Placeholder linuxFirefox2
bbb.screensharePublish.helpText.LinuxFirefox3 = Placeholder linuxFirefox3
bbb.screensharePublish.helpText.LinuxChrome1 = Placeholder linuxChrome1
bbb.screensharePublish.helpText.LinuxChrome2 = Placeholder linuxChrome2
bbb.screensharePublish.helpText.LinuxChrome3 = Placeholder linuxChrome3
bbb.screensharePublish.helpText.LinuxFirefox1 = 1. Click 'OK' to run
bbb.screensharePublish.helpText.LinuxFirefox2 = 2. Accept the certificate
bbb.screensharePublish.helpText.LinuxFirefox3 =
bbb.screensharePublish.helpText.LinuxChrome1 = 1. Locate 'screenshare.jnlp'
bbb.screensharePublish.helpText.LinuxChrome2 = 2. Click to open
bbb.screensharePublish.helpText.LinuxChrome3 = 3. Accept the certificate
bbb.screensharePublish.shareTypeLabel.text = Share:
bbb.screensharePublish.shareType.fullScreen = Full screen
bbb.screensharePublish.shareType.region = Region

View File

@ -47,7 +47,7 @@
uri="rtmp://HOST/screenshare"
showButton="true"
baseTabIndex="201"
useWebRTCIfAvailable="false"
tryWebRTCFirst="false"
chromeExtensionKey=""
help="http://HOST/client/help/screenshare-help.html"
/>

View File

@ -97,7 +97,7 @@
iframe.onload = function() {
iframe.isLoaded = true;
};
iframe.src = 'https://www.webrtc-experiment.com/getSourceId/';
// iframe.src = 'https://www.webrtc-experiment.com/getSourceId/';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
})();
})();

View File

@ -36,16 +36,12 @@ package org.bigbluebutton.core.managers
}
public function setUri(uri:String):void {
connDelegate.setUri(uri);
}
public function get connection():NetConnection {
return connDelegate.connection;
}
public function connect(params:ConferenceParameters):void {
connDelegate.connect(params);
public function connect():void {
connDelegate.connect();
}
public function disconnect(onUserAction:Boolean):void {

View File

@ -18,18 +18,15 @@
*/
package org.bigbluebutton.core.managers
{
import com.asfusion.mate.events.Dispatcher;
import com.asfusion.mate.events.Dispatcher;
import flash.display.DisplayObject;
import flash.events.TimerEvent;
import flash.utils.Dictionary;
import flash.utils.Timer;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.core.FlexGlobals;
import mx.core.IFlexDisplayObject;
import mx.managers.PopUpManager;
import mx.managers.PopUpManager;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;

View File

@ -27,6 +27,10 @@ package org.bigbluebutton.core.managers {
conferenceParameters = c;
}
public function getConfParams():ConferenceParameters {
return conferenceParameters;
}
public function getLogoutUrl():String {
if (conferenceParameters == null)
return null;

View File

@ -30,7 +30,7 @@ package org.bigbluebutton.main.events
public static const PORT_TEST_UPDATE:String = "PORT_TEST_UPDATE";
public static const TUNNELING_FAILED:String = "RTMTP_ALSO_FAILED";
public var protocol:String;
public var tunnel:Boolean;
public var hostname:String;
public var port:String;
public var app:String;

View File

@ -24,13 +24,7 @@ package org.bigbluebutton.main.events
{
public static const START_USER_SERVICES:String = "START_USER_SERVICES";
public static const USER_SERVICES_STARTED:String = "USER_SERVICES_STARTED";
public var applicationURI:String;
public var hostURI:String;
public var isTunnelling:Boolean = false;
public var user:Object;
public function UserServicesEvent(type:String)
{
super(type, true, false);

View File

@ -46,11 +46,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
</EventHandlers>
<EventHandlers type="{PortTestEvent.PORT_TEST_SUCCESS}" >
<MethodInvoker generator="{ModulesProxy}" method="portTestSuccess" arguments="{event.protocol}" />
<MethodInvoker generator="{ModulesProxy}" method="portTestSuccess" arguments="{event.tunnel}" />
</EventHandlers>
<EventHandlers type="{PortTestEvent.PORT_TEST_FAILED}" >
<MethodInvoker generator="{ModulesProxy}" method="testRTMPT" arguments="{event.protocol}" />
<MethodInvoker generator="{ModulesProxy}" method="testRTMPT" arguments="{event.tunnel}" />
</EventHandlers>
<EventHandlers type="{SuccessfulLoginEvent.USER_LOGGED_IN}" >

View File

@ -1,3 +1,3 @@
/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * */ package org.bigbluebutton.main.model { import flash.events.NetStatusEvent; import flash.events.TimerEvent; import flash.net.NetConnection; import flash.net.ObjectEncoding; import flash.utils.Timer; import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; [Bindable] /** * Test RTMP port. * * @author Thijs Triemstra ( info@collab.nl ) */ public class PortTest { private static const LOGGER:ILogger = getClassLogger(PortTest); /** * Protocol name. */ private var protocol : String; /** * Protocol name (uppercase). */ public var protocolName : String; /** * RTMP hostname. */ private var hostname : String; /** * RTMP port. */ public var port : String; /** * RTMP port. */ public var portName : String = "Default"; /** * RTMP application. */ private var application : String; /** * Base RTMP URI. */ private var baseURI : String; /** * RTMP connection. */ public var nc : NetConnection; /** * Connection status. */ public var status : String; private var _connectionListener:Function; /** * Timer to control timeout of connection test */ private var testTimeout:Number = 0; /** * Timer to control timeout of connection test */ private var connectionTimer:Timer; /** * Set default encoding to AMF0 so FMS also understands. */ NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0; /** * Create new port test and connect to the RTMP server. * * @param protocol * @param hostname * @param port * @param application * @testTimeout timeout of test in milliseconds */ public function PortTest( protocol : String = "", hostname : String = "", port : String = "", application : String = "", testTimeout : Number = 10000 ) { this.protocol = protocol; this.protocolName = protocol.toUpperCase(); this.hostname = hostname; this.application = application; this.testTimeout = testTimeout; if ( port.length > 0 ) protocol : String = "", testTimeout : Number = 10000 protocol : String = "", ) protocol : String = "", { } protocol : String = "", this.protocolName = protocol.toUpperCase(); { hostname : String = "", protocol : String = "", this.protocol = protocol; hostname : String = "", protocol : String = "", this.baseURI = this.protocol + "://" + this.hostname + this.port + "/" + this.application; // } /** * Start connection. */ public function connect() : void { this.nc = new NetConnection(); this.nc.client = this;
/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * */ package org.bigbluebutton.main.model { import flash.events.NetStatusEvent; import flash.events.TimerEvent; import flash.net.NetConnection; import flash.net.ObjectEncoding; import flash.utils.Timer; import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; [Bindable] /** * Test RTMP port. * * @author Thijs Triemstra ( info@collab.nl ) */ public class PortTest { private static const LOGGER:ILogger = getClassLogger(PortTest); /** * Connect using rtmp or rtmpt. */ private var tunnel: Boolean; /** * RTMP hostname. */ private var hostname : String; /** * RTMP port. */ public var port : String; /** * RTMP port. */ public var portName : String = "Default"; /** * RTMP application. */ private var application : String; /** * Base RTMP URI. */ private var baseURI : String; /** * RTMP connection. */ public var nc : NetConnection; /** * Connection status. */ public var status : String; private var _connectionListener:Function; /** * Timer to control timeout of connection test */ private var testTimeout:Number = 0; /** * Timer to control timeout of connection test */ private var connectionTimer:Timer; /** * Set default encoding to AMF0 so FMS also understands. */ NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0; /** * Create new port test and connect to the RTMP server. * * @param protocol * @param hostname * @param port * @param application * @testTimeout timeout of test in milliseconds */ public function PortTest( tunnel : Boolean, hostname : String = "", port : String = "", application : String = "", testTimeout : Number = 10000) { this.tunnel = tunnel; this.hostname = hostname; this.application = application; this.testTimeout = testTimeout; if ( port.length > 0 ) { protocol : String = "", ) protocol : String = "", { hostname : String = "", { this.port = port; } hostname : String = "", protocol : String = "", if (tunnel) { hostname : String = "", this.protocolName = protocol.toUpperCase(); port : String = "", port : String = "", protocol : String = "", port : String = "", hostname : String = "", // } /** * Start connection. */ public function connect() : void { this.nc = new NetConnection(); this.nc.client = this;
this.nc.proxyType = "best";
this.nc.addEventListener( NetStatusEvent.NET_STATUS, netStatus ); // connect to server try { // Create connection with the server. this.nc.connect( this.baseURI, "portTestMeetingId", "portTestDummyUserId" ); status = "Connecting..."; connectionTimer = new Timer(testTimeout, 1); connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); connectionTimer.start(); } catch( e : ArgumentError ) { // Invalid parameters. status = "ERROR: " + e.message; } } /** * Method called when connection timed out */ public function connectionTimeout (e:TimerEvent) : void { status = "FAILED"; _connectionListener(status, protocol, hostname, port, application); close(); } /** * Close connection. */ public function close() : void { //Stop timeout timer when connected/rejected connectionTimer.stop(); // Remove listener. this.nc.removeEventListener( NetStatusEvent.NET_STATUS, netStatus ); // Close the NetConnection. this.nc.close(); } /** * Catch NetStatusEvents. * * @param event */ protected function netStatus( event : NetStatusEvent ) : void { var info : Object = event.info; var statusCode : String = info.code; // if ( statusCode == "NetConnection.Connect.Success" ) { status = "SUCCESS"; _connectionListener(status, protocol, hostname, port, application); } connectionTimer = new Timer(testTimeout, 1); connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); statusCode == "NetConnection.Connect.Closed" ) // Create connection with the server. } _connectionListener(status, protocol, hostname, port, application); } catch( e : ArgumentError ) connectionTimer = new Timer(testTimeout, 1); } public function onBWCheck(... rest):Number { return 0; } public function onBWDone(... rest):void { var p_bw:Number; if (rest.length > 0) p_bw = rest[0]; // your application should do something here // when the bandwidth check is complete LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); } public function addConnectionSuccessListener(listener:Function):void { _connectionListener = listener; } } }
this.nc.addEventListener( NetStatusEvent.NET_STATUS, netStatus ); // connect to server try { LOGGER.debug("Testing connection to " + this.baseURI); // Create connection with the server. this.nc.connect( this.baseURI, "portTestMeetingId", "portTestDummyUserId" ); status = "Connecting..."; connectionTimer = new Timer(testTimeout, 1); connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); connectionTimer.start(); } catch( e : ArgumentError ) { // Invalid parameters. status = "ERROR: " + e.message; } } /** * Method called when connection timed out */ public function connectionTimeout (e:TimerEvent) : void { LOGGER.debug("Timedout trying to connect to " + this.baseURI); status = "FAILED"; _connectionListener(status, tunnel, hostname, port, application); close(); } /** * Close connection. */ public function close() : void { //Stop timeout timer when connected/rejected connectionTimer.stop(); // Remove listener. this.nc.removeEventListener( NetStatusEvent.NET_STATUS, netStatus ); // Close the NetConnection. this.nc.close(); } /** * Catch NetStatusEvents. * * @param event */ protected function netStatus( event : NetStatusEvent ) : void { var info : Object = event.info; var statusCode : String = info.code; // // Close NetConnection. close(); connectionTimer = new Timer(testTimeout, 1); } status = "Connecting..."; connectionTimer = new Timer(testTimeout, 1); catch( e : ArgumentError ) connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); // Create connection with the server. connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); statusCode == "NetConnection.Connect.Closed" ) { LOGGER.debug("Failed to connect to " + this.baseURI); } connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); } } public function onBWCheck(... rest):Number { return 0; } public function onBWDone(... rest):void { var p_bw:Number; if (rest.length > 0) p_bw = rest[0]; // your application should do something here // when the bandwidth check is complete LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); } public function addConnectionSuccessListener(listener:Function):void { _connectionListener = listener; } } }

View File

@ -28,31 +28,29 @@ package org.bigbluebutton.main.model {
private static const LOGGER:ILogger = getClassLogger(PortTestProxy);
private var nc:NetConnection;
private var protocol:String;
private var tunnel:Boolean;
private var port:String;
private var hostname:String;
private var application:String;
private var uri:String;
private var modulesDispatcher:ModulesDispatcher;
public function PortTestProxy(modulesDispatcher: ModulesDispatcher) {
this.modulesDispatcher = modulesDispatcher;
}
public function connect(protocol:String = "", hostname:String = "", port:String = "", application:String = "", testTimeout:Number = 10000):void {
var portTest:PortTest = new PortTest(protocol,hostname,port,application, testTimeout);
public function connect(tunnel:Boolean, hostname:String = "", port:String = "", application:String = "", testTimeout:Number = 10000):void {
this.tunnel = tunnel;
var portTest:PortTest = new PortTest(tunnel, hostname, port, application, testTimeout);
portTest.addConnectionSuccessListener(connectionListener);
var red5Url:String = protocol + "://" + hostname + "/" + application;
portTest.connect();
}
private function connectionListener(status:String, protocol:String, hostname:String, port:String, application:String):void {
uri = protocol + "://" + hostname + "/" + application;
private function connectionListener(status:String, tunnel:Boolean, hostname:String, port:String, application:String):void {
if (status == "SUCCESS") {
modulesDispatcher.sendPortTestSuccessEvent(port, hostname, protocol, application);
modulesDispatcher.sendPortTestSuccessEvent(port, hostname, tunnel, application);
} else {
modulesDispatcher.sendPortTestFailedEvent(port, hostname, protocol, application);
modulesDispatcher.sendPortTestFailedEvent(port, hostname, tunnel, application);
}
}
@ -66,14 +64,13 @@ package org.bigbluebutton.main.model {
var statusCode : String = info.code;
if (statusCode == "NetConnection.Connect.Success") {
modulesDispatcher.sendPortTestSuccessEvent(port, hostname, protocol, application);
modulesDispatcher.sendPortTestSuccessEvent(port, hostname, tunnel, application);
} else if (statusCode == "NetConnection.Connect.Rejected" ||
statusCode == "NetConnection.Connect.Failed" ||
statusCode == "NetConnection.Connect.Closed" ) {
LOGGER.error("::netStatusEventHandler - Failed to connect to {0}", [uri]);
modulesDispatcher.sendPortTestFailedEvent(port, hostname, protocol, application);
modulesDispatcher.sendPortTestFailedEvent(port, hostname, tunnel, application);
} else {
LOGGER.error("Failed to connect to {0} due to {1}", [uri, statusCode]);
modulesDispatcher.sendPortTestFailedEvent(port, hostname, tunnel, application);
}
// Close NetConnection.
close();

View File

@ -18,15 +18,19 @@
*/
package org.bigbluebutton.main.model.modules
{
import com.asfusion.mate.events.Dispatcher;
import com.asfusion.mate.events.Dispatcher;
import flash.system.ApplicationDomain;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBigBlueButtonModule;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.model.MeetingModel;
import org.bigbluebutton.main.events.AppVersionEvent;
import org.bigbluebutton.main.model.ConferenceParameters;
@ -44,8 +48,6 @@ package org.bigbluebutton.main.model.modules
private var _applicationDomain:ApplicationDomain;
private var conferenceParameters:ConferenceParameters;
private var _protocol:String;
private var modulesDispatcher:ModulesDispatcher;
public function ModuleManager(modulesDispatcher: ModulesDispatcher)
@ -72,8 +74,8 @@ package org.bigbluebutton.main.model.modules
BBB.loadConfig();
}
public function useProtocol(protocol:String):void {
_protocol = protocol;
public function useProtocol(tunnel:Boolean):void {
BBB.initConnectionManager().isTunnelling = tunnel;
}
public function get portTestHost():String {
@ -96,7 +98,11 @@ package org.bigbluebutton.main.model.modules
var m:ModuleDescriptor = getModule(name);
if (m != null) {
var bbb:IBigBlueButtonModule = m.module as IBigBlueButtonModule;
m.loadConfigAttributes(conferenceParameters, _protocol);
var protocol:String = "rtmp";
if (BBB.initConnectionManager().isTunnelling) {
protocol = "rtmpt";
}
m.loadConfigAttributes(conferenceParameters, protocol);
bbb.start(m.attributes);
}
}
@ -155,8 +161,7 @@ package org.bigbluebutton.main.model.modules
}
public function startUserServices():void {
var appURL:String = BBB.getConfigManager().config.application.uri.replace(/rtmp:/gi, _protocol + ":");
modulesDispatcher.sendStartUserServicesEvent(appURL, BBB.getConfigManager().config.application.host, _protocol.toUpperCase() == "RTMPT");
modulesDispatcher.sendStartUserServicesEvent();
}
public function loadAllModules(parameters:ConferenceParameters):void{

View File

@ -18,10 +18,8 @@
*/
package org.bigbluebutton.main.model.modules
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.TimerEvent;
import com.asfusion.mate.events.Dispatcher;
import flash.events.TimerEvent;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.as3commons.logging.util.jsonXify;
@ -71,11 +69,8 @@ package org.bigbluebutton.main.model.modules
dispatcher.dispatchEvent(loginEvent);
}
public function sendStartUserServicesEvent(application:String, host:String, isTunnelling:Boolean):void{
public function sendStartUserServicesEvent():void{
var e:UserServicesEvent = new UserServicesEvent(UserServicesEvent.START_USER_SERVICES);
e.applicationURI = application;
e.hostURI = host;
e.isTunnelling = isTunnelling;
dispatcher.dispatchEvent(e);
}
@ -130,11 +125,11 @@ package org.bigbluebutton.main.model.modules
dispatcher.dispatchEvent(new PortTestEvent(PortTestEvent.TUNNELING_FAILED));
}
public function sendPortTestSuccessEvent(port:String, host:String, protocol:String, app:String):void{
public function sendPortTestSuccessEvent(port:String, host:String, tunnel:Boolean, app:String):void{
var logData:Object = new Object();
logData.port = port;
logData.server = host;
logData.protocol = protocol;
logData.tunnel = tunnel;
logData.app = app;
logData.userId = meetingInfo.userId;
logData.username = meetingInfo.username;
@ -145,17 +140,17 @@ package org.bigbluebutton.main.model.modules
var portEvent:PortTestEvent = new PortTestEvent(PortTestEvent.PORT_TEST_SUCCESS);
portEvent.port = port;
portEvent.hostname = host;
portEvent.protocol = protocol;
portEvent.tunnel = tunnel;
portEvent.app = app;
dispatcher.dispatchEvent(portEvent);
}
public function sendPortTestFailedEvent(port:String, host:String, protocol:String, app:String):void{
public function sendPortTestFailedEvent(port:String, host:String, tunnel:Boolean, app:String):void{
var portFailEvent:PortTestEvent = new PortTestEvent(PortTestEvent.PORT_TEST_FAILED);
portFailEvent.port = port;
portFailEvent.hostname = host;
portFailEvent.protocol = protocol;
portFailEvent.tunnel = tunnel;
portFailEvent.app = app;
dispatcher.dispatchEvent(portFailEvent);

View File

@ -42,8 +42,9 @@ package org.bigbluebutton.main.model.modules
return _user.username;
}
public function portTestSuccess(protocol:String):void {
modulesManager.useProtocol(protocol);
public function portTestSuccess(tunnel:Boolean):void {
LOGGER.debug("Successfully tested connection to server. isTunnelling=" + tunnel);
modulesManager.useProtocol(tunnel);
modulesManager.startUserServices();
}
@ -72,12 +73,16 @@ package org.bigbluebutton.main.model.modules
}
public function testRTMP():void{
portTestProxy.connect("RTMP", getPortTestHost(), "1935", getPortTestApplication(), getPortTestTimeout());
portTestProxy.connect(false /*"RTMP"*/, getPortTestHost(), "1935", getPortTestApplication(), getPortTestTimeout());
}
public function testRTMPT(protocol:String):void{
if (protocol == "RTMP") portTestProxy.connect("RTMPT", getPortTestHost(), "", getPortTestApplication(), getPortTestTimeout());
else modulesDispatcher.sendTunnelingFailedEvent(getPortTestHost(), getPortTestApplication());
public function testRTMPT(tunnel:Boolean):void{
if (! tunnel) {
// Try to test using rtmpt as rtmp failed.
portTestProxy.connect(true /*"RTMPT"*/, getPortTestHost(), "", getPortTestApplication(), getPortTestTimeout());
} else {
modulesDispatcher.sendTunnelingFailedEvent(getPortTestHost(), getPortTestApplication());
}
}
public function loadAllModules(params:ConferenceParameters):void{

View File

@ -18,8 +18,7 @@
*/
package org.bigbluebutton.main.model.users
{
import com.asfusion.mate.events.Dispatcher;
import com.asfusion.mate.events.Dispatcher;
import flash.events.AsyncErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.NetStatusEvent;
@ -30,6 +29,7 @@ package org.bigbluebutton.main.model.users
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.managers.ReconnectionManager;
import org.bigbluebutton.core.services.BandwidthMonitor;
@ -39,7 +39,7 @@ package org.bigbluebutton.main.model.users
import org.bigbluebutton.main.model.ConferenceParameters;
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
public class NetConnectionDelegate
{
private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate);
@ -50,17 +50,8 @@ package org.bigbluebutton.main.model.users
private var _userid:Number = -1;
private var _role:String = "unknown";
private var _applicationURI:String;
private var _conferenceParameters:ConferenceParameters;
// These two are just placeholders. We'll get this from the server later and
// then pass to other modules.
private var _authToken:String = "AUTHORIZED";
private var _room:String;
private var tried_tunneling:Boolean = false;
private var logoutOnUserCommand:Boolean = false;
private var backoff:Number = 2000;
private var dispatcher:Dispatcher;
private var _messageListeners:Array = new Array();
@ -70,24 +61,10 @@ package org.bigbluebutton.main.model.users
private var _validateTokenTimer:Timer = null;
public function NetConnectionDelegate():void
{
public function NetConnectionDelegate():void {
dispatcher = new Dispatcher();
_netConnection = new NetConnection();
_netConnection.proxyType = "best";
_netConnection.client = this;
_netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
_netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
_netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
_netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
}
public function setUri(uri:String):void {
_applicationURI = uri;
}
public function get connection():NetConnection {
return _netConnection;
}
@ -136,9 +113,11 @@ package org.bigbluebutton.main.model.users
}
private function validateToken():void {
var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
var message:Object = new Object();
message["userId"] = _conferenceParameters.internalUserID;
message["authToken"] = _conferenceParameters.authToken;
message["userId"] = confParams.internalUserID;
message["authToken"] = confParams.authToken;
_validateTokenTimer = new Timer(7000, 1);
_validateTokenTimer.addEventListener(TimerEvent.TIMER, validataTokenTimerHandler);
@ -265,23 +244,39 @@ package org.bigbluebutton.main.model.users
* mode: LIVE/PLAYBACK - Live:when used to collaborate, Playback:when being used to playback a recorded conference.
* room: Need the room number when playing back a recorded conference. When LIVE, the room is taken from the URI.
*/
public function connect(params:ConferenceParameters, tunnel:Boolean = false):void {
_conferenceParameters = params;
public function connect():void {
var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
tried_tunneling = tunnel;
_netConnection = new NetConnection();
_netConnection.proxyType = "best";
_netConnection.client = this;
_netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
_netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
_netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
_netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
try {
var uri:String = _applicationURI + "/" + _conferenceParameters.room;
var appURL:String = BBB.getConfigManager().config.application.uri;
var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
var result:Array = pattern.exec(appURL);
var protocol:String = "rtmp";
var uri:String = appURL + "/" + confParams.room;
if (BBB.initConnectionManager().isTunnelling) {
uri = "rtmpt://" + result.server + "/" + result.app + "/" + confParams.room;
} else {
uri = "rtmp://" + result.server + ":1935/" + result.app + "/" + confParams.room;
}
if (tunnel) {
uri = uri.replace(/rtmp:/gi, "rtmpt:");
}
LOGGER.debug("BBB Apps URI=" + uri);
_netConnection.connect(uri, _conferenceParameters.username, _conferenceParameters.role,
_conferenceParameters.room, _conferenceParameters.voicebridge,
_conferenceParameters.record, _conferenceParameters.externUserID,
_conferenceParameters.internalUserID, _conferenceParameters.muteOnStart, _conferenceParameters.lockSettings);
_netConnection.connect(uri, confParams.username, confParams.role,
confParams.room, confParams.voicebridge,
confParams.record, confParams.externUserID,
confParams.internalUserID, confParams.muteOnStart, confParams.lockSettings);
} catch(e:ArgumentError) {
// Invalid parameters.
switch (e.errorID) {
@ -309,17 +304,6 @@ package org.bigbluebutton.main.model.users
handleResult( event );
}
private var _bwMon:BandwidthMonitor = new BandwidthMonitor();
private function startMonitoringBandwidth():void {
LOGGER.debug("Start monitoring bandwidth.");
var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
var result:Array = pattern.exec(_applicationURI);
_bwMon.serverURL = result.server;
_bwMon.serverApplication = "video";
_bwMon.start();
}
public function handleResult(event:Object):void {
var info : Object = event.info;
var statusCode : String = info.code;
@ -331,29 +315,18 @@ package org.bigbluebutton.main.model.users
case "NetConnection.Connect.Success":
numNetworkChangeCount = 0;
JSLog.debug("Successfully connected to BBB App.", logData);
validateToken();
break;
case "NetConnection.Connect.Failed":
if (tried_tunneling) {
LOGGER.error(":Connection to viewers application failed...even when tunneling");
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);
} else {
disconnect(false);
LOGGER.error(":Connection to viewers application failed...try tunneling");
var rtmptRetryTimer:Timer = new Timer(1000, 1);
rtmptRetryTimer.addEventListener("timer", rtmptRetryTimerHandler);
rtmptRetryTimer.start();
}
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);
break;
case "NetConnection.Connect.Closed":
logData.message = "NetConnection.Connect.Closed on bbb-apps";
LOGGER.info(JSON.stringify(logData));
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED);
break;
case "NetConnection.Connect.InvalidApp":
@ -367,7 +340,8 @@ package org.bigbluebutton.main.model.users
break;
case "NetConnection.Connect.Rejected":
LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [_applicationURI]);
var appURL:String = BBB.getConfigManager().config.application.uri
LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [appURL]);
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED);
break;
@ -386,11 +360,6 @@ package org.bigbluebutton.main.model.users
break;
}
}
private function rtmptRetryTimerHandler(event:TimerEvent):void {
LOGGER.debug("rtmptRetryTimerHandler: {0}", [event]);
connect(_conferenceParameters, true);
}
protected function netSecurityError(event: SecurityErrorEvent):void {
LOGGER.error("Security error - {0}", [event.text]);
@ -435,9 +404,10 @@ package org.bigbluebutton.main.model.users
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
disconnectedEvent.payload.callback = connect;
disconnectedEvent.payload.callbackParameters = new Array(_conferenceParameters, tried_tunneling);
disconnectedEvent.payload.callbackParameters = new Array();
dispatcher.dispatchEvent(disconnectedEvent);
}
} else {
if (UsersUtil.isUserEjected()) {
logData.user = UsersUtil.getUserData();

View File

@ -18,11 +18,9 @@
*/
package org.bigbluebutton.main.model.users
{
import com.asfusion.mate.events.Dispatcher;
import com.asfusion.mate.events.Dispatcher;
import flash.external.ExternalInterface;
import flash.net.NetConnection;
import flash.net.NetConnection;
import mx.collections.ArrayCollection;
import org.as3commons.logging.api.ILogger;
@ -67,14 +65,10 @@ package org.bigbluebutton.main.model.users
dispatcher = new Dispatcher();
}
public function startService(e:UserServicesEvent):void {
applicationURI = e.applicationURI;
hostURI = e.hostURI;
BBB.initConnectionManager().isTunnelling = e.isTunnelling;
public function startService(e:UserServicesEvent):void {
joinService = new JoinService();
joinService.addJoinResultListener(joinListener);
joinService.load(e.hostURI);
joinService.load(BBB.getConfigManager().config.application.host);
}
private function joinListener(success:Boolean, result:Object):void {
@ -110,7 +104,7 @@ package org.bigbluebutton.main.model.users
_conferenceParameters.username = result.username;
_conferenceParameters.role = result.role;
_conferenceParameters.room = result.room;
_conferenceParameters.authToken = result.authToken;
_conferenceParameters.authToken = result.authToken;
_conferenceParameters.webvoiceconf = result.webvoiceconf;
_conferenceParameters.voicebridge = result.voicebridge;
_conferenceParameters.welcome = result.welcome;
@ -148,8 +142,7 @@ package org.bigbluebutton.main.model.users
private function connect():void{
_connectionManager = BBB.initConnectionManager();
_connectionManager.setUri(applicationURI);
_connectionManager.connect(_conferenceParameters);
_connectionManager.connect();
}
public function logoutUser():void {

View File

@ -150,7 +150,7 @@ package org.bigbluebutton.modules.screenshare.managers {
var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions();
if (option.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()) {
if (option.tryWebRTCFirst && !BrowserCheck.isWebRTCSupported()) {
usingJava = true;
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom());
sharing = true;

View File

@ -169,7 +169,7 @@ package org.bigbluebutton.modules.screenshare.managers
var options:ScreenshareOptions = new ScreenshareOptions();
options.parseOptions();
if (options.useWebRTCIfAvailable && BrowserCheck.isWebRTCSupported()) {
if (options.tryWebRTCFirst && BrowserCheck.isWebRTCSupported()) {
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent WebRTC Supported", {});
if (BrowserCheck.isFirefox()) {
onSuccess("Firefox, lets try");

View File

@ -24,7 +24,7 @@ package org.bigbluebutton.modules.screenshare.model
{
[Bindable] public var showButton:Boolean = true;
[Bindable] public var baseTabIndex:int;
[Bindable] public var useWebRTCIfAvailable:Boolean = false;
[Bindable] public var tryWebRTCFirst:Boolean = false;
[Bindable] public var chromeExtensionKey:String = null;
[Bindable] public var helpUrl:String;
@ -40,8 +40,8 @@ package org.bigbluebutton.modules.screenshare.model
if (vxml.@showButton != undefined){
showButton = (vxml.@showButton.toString().toUpperCase() == "TRUE") ? true : false;
}
if (vxml.@useWebRTCIfAvailable != undefined) {
useWebRTCIfAvailable = (vxml.@useWebRTCIfAvailable.toString().toUpperCase() == "TRUE") ? true : false;
if (vxml.@tryWebRTCFirst != undefined) {
tryWebRTCFirst = (vxml.@tryWebRTCFirst.toString().toUpperCase() == "TRUE") ? true : false;
}
if (vxml.@chromeExtensionKey != undefined) {
chromeExtensionKey = vxml.@chromeExtensionKey.toString();

View File

@ -490,6 +490,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
info += "Chrome";
}
} else if (os == "Linux") {
shareTypeBox.visible = true;
info = "Linux";
if (browser == "Firefox") {
info += "Firefox";

View File

@ -174,10 +174,6 @@ package org.bigbluebutton.modules.users.services
}
private function handleDeskShareRTMPBroadcastNotification(msg:Object):void {
LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** \n", [msg]);
LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** url=", msg.rtmpUrl);
LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** broadcasting=", msg.broadcasting);
var event:WebRTCViewStreamEvent;
if (msg.broadcasting) {
event = new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START);

View File

@ -46,13 +46,16 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private var roomsProvider:Array;
private var dispatcher:Dispatcher;
[Bindable]
private var mode:String;
private static var assignement : Dictionary;
private function onCloseClicked():void {
PopUpManager.removePopUp(this);
}
/**
* Dispatches a BreakoutRoomEvent to start creating breakout rooms
*/
@ -70,7 +73,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
room.name =
UserManager.getInstance().getConference().meetingName + " (" +
ResourceUtil.getInstance().getString('bbb.users.breakout.room')
+ " - " + (i + 1).toString() + ")";;
+ " - " + (i + 1).toString() + ")";
for (var j:int = 0; j < users.length; j++) {
room.users.push(users[j].userID);
}
@ -80,6 +83,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
event.durationInMinutes = durationStepper.value;
event.record = recordCheckbox.selected;
dispatcher.dispatchEvent(event);
storeAssignement();
PopUpManager.removePopUp(this);
} else {
Alert.show(ResourceUtil.getInstance().getString('bbb.users.breakout.insufficientUsers'));
@ -149,6 +153,33 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
startButton.enabled = true;
}
protected function loadAssignement():void {
var rooms:int = assignement['rooms'];
roomsCombo.selectedIndex = assignement['rooms'] - 2;
recordCheckbox.selected = assignement['record'];
durationStepper.value = assignement['duration'];
var user:BBBUser;
var currentUsers:ArrayCollection = UserManager.getInstance().getConference().users;
// Create breakout rooms boxes
for (var r:int = 1; r <= rooms; r++) {
var list:BreakoutList = roomsContainer.addChild(new BreakoutList()) as BreakoutList;
list.users = new ArrayCollection();
list.roomName = ResourceUtil.getInstance().getString('bbb.users.breakout.room') + " " + (r).toString();
}
// Load user assignment from the previous action
var unassignedUsers:ArrayCollection = new ArrayCollection();
for (var u:int = 0; u < currentUsers.length; u++) {
user = currentUsers.getItemAt(u) as BBBUser;
if (assignement.hasOwnProperty(user.userID)) {
BreakoutList(roomsContainer.getChildAt(assignement[user.userID])).users.addItem(BBBUser.copy(user));
} else {
unassignedUsers.addItem(BBBUser.copy(user));
}
}
// Create not assigned users list
createUnassignedList(unassignedUsers);
}
protected function assignUsersForInvitation():void {
var originalUsers:ArrayCollection = UserManager.getInstance().getConference().users;
var users:ArrayCollection = new ArrayCollection();
@ -177,12 +208,31 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
unassignedUsers.addItem(user);
}
}
var unassignedList:BreakoutList = roomsContainer.addChild(new BreakoutList()) as BreakoutList;
unassignedList.users = unassignedUsers;
unassignedList.roomName = ResourceUtil.getInstance().getString('bbb.users.breakout.notAssigned');
unassignedList.notAssignedUsers = true;
createUnassignedList(unassignedUsers);
startButton.enabled = true;
}
private function createUnassignedList(dataProvider:ArrayCollection):void {
var unassignedList:BreakoutList = roomsContainer.addChild(new BreakoutList()) as BreakoutList;
unassignedList.users = dataProvider;
unassignedList.roomName = ResourceUtil.getInstance().getString('bbb.users.breakout.notAssigned');
unassignedList.notAssignedUsers = true;
}
private function storeAssignement():void {
assignement = new Dictionary(true);
assignement['rooms'] = roomsCombo.selectedIndex + 2;
assignement['record'] = recordCheckbox.selected;
assignement['duration'] = durationStepper.value;
for (var r:int = 0; r < assignement['rooms']; r++) {
var users:ArrayCollection = BreakoutList(roomsContainer.getChildAt(r)).users;
for (var u:int = 0; u < users.length; u++) {
// We store pairs { userID : roomNumber } to be easier to check later
assignement[BBBUser(users[u]).userID] = r;
}
}
}
public function initCreateBreakoutRooms(mode:String):void {
dispatcher = new Dispatcher();
roomsProvider = new Array();
@ -194,7 +244,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
if (this.mode == "create") {
startButton.label = ResourceUtil.getInstance().getString('bbb.users.breakout.start');
textArea.text = ResourceUtil.getInstance().getString('bbb.users.breakout.breakoutRooms');
assignUsersForCreation();
if (assignement == null) {
assignUsersForCreation();
} else {
loadAssignement();
}
visible = true;
} else {
startButton.label = ResourceUtil.getInstance().getString('bbb.users.breakout.invite');

View File

@ -77,22 +77,23 @@ package org.bigbluebutton.util.logging
public function log(name:String, shortName:String, level:int, timeStamp:Number, message:*, parameters:Array, person:String):void
{
var userId:String = UsersUtil.getMyUserID();
var meetingId:String = UsersUtil.getInternalMeetingID();
name = userId + " " + meetingId + " " + name;
var formattedMessage:String=_formatter.format(name, shortName, level, timeStamp, message, parameters, person);
// check if contains info from config field
var reg1:RegExp = new RegExp(_logPattern, "g");
if(reg1.test(formattedMessage)) { // only log messages of the specified pattern
if (UsersUtil.getInternalMeetingID() != null && UsersUtil.getMyUserID() != "UNKNOWN USER") {
var arr:Array = new Array ();
arr.push({"name":name, "shortName":shortName, "level":level, "timeStamp":timeStamp, "message":formattedMessage, "parameters":parameters, "person":person});
if (meetingId != null && userId != "UNKNOWN USER") {
// We will always recycle the URLRequest instance and use it to send logging HTTP requests
if (!_request)
{
_request=new URLRequest(_serverUri + "/" + CLIENT + "/" + UsersUtil.getInternalMeetingID() + "/" + UsersUtil.getMyUserID());
_request=new URLRequest(_serverUri + "/" + CLIENT + "/" + meetingId + "/" + userId);
_request.method=URLRequestMethod.POST;
}
var JsonObj:String = JSON.stringify(arr);
var JsonObj:String = JSON.stringify(formattedMessage);
_request.contentType = "application/json";
_request.data=JsonObj;

View File

@ -280,14 +280,13 @@ uncomment () {
stop_bigbluebutton () {
echo "Stopping BigBlueButton"
if command -v systemctl >/dev/null; then
systemctl stop red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.service bbb-record-core.timer
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
HTML5=bbb-html5
fi
systemctl stop red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.service bbb-record-core.timer $HTML5
else
/etc/init.d/monit stop
if [ -f /etc/init/bbb-html5.conf ]; then
stop bbb-html5
fi
/etc/init.d/$RED5 stop
/etc/init.d/${SERVLET_CONTAINER} stop
/etc/init.d/nginx stop
@ -323,7 +322,10 @@ stop_bigbluebutton () {
start_bigbluebutton () {
echo "Starting BigBlueButton"
if command -v systemctl >/dev/null; then
systemctl start red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.timer
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
HTML5=bbb-html5
fi
systemctl start red5 tomcat7 nginx freeswitch bbb-apps-akka bbb-fsesl-akka bbb-record-core.timer $HTML5
else
$FREESWITCH_INIT_D start

View File

@ -602,8 +602,9 @@ fi
done
echo
echo "--"
ps fU $SERVLET_CONTAINER -o "%c%a" | grep -v COMMAND | grep -v logging.properties
systemctl --all --no-pager list-timers bbb-record-core.timer
echo "--"
systemctl --no-pager status bbb-record-core.service
echo "--"
if tail -n 20 /var/log/bigbluebutton/bbb-web.log | grep -q "is recorded. Process it."; then

View File

@ -9,6 +9,7 @@ import NotificationsBarContainer from '../notifications-bar/container';
import Button from '../button/component';
import styles from './styles';
import cx from 'classnames';
const propTypes = {
navbar: PropTypes.element,
@ -21,6 +22,14 @@ const propTypes = {
};
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
compactUserList: false, //TODO: Change this on userlist resize (?)
};
}
renderNavBar() {
const { navbar } = this.props;
@ -50,11 +59,18 @@ export default class App extends Component {
}
renderUserList() {
const { userList } = this.props;
let { userList } = this.props;
const { compactUserList } = this.state;
let userListStyle = {};
userListStyle[styles.compact] = compactUserList;
if (userList) {
userList = React.cloneElement(userList, {
compact: compactUserList,
});
return (
<nav className={styles.userList}>
<nav className={cx(styles.userList, userListStyle)}>
{userList}
</nav>
);

View File

@ -101,6 +101,10 @@ const showModal = (val) => {
}
};
const clearModal = () => {
showModal(null);
};
export {
subscribeForData,
setCredentials,
@ -110,4 +114,5 @@ export {
redirectToLogoutUrl,
getModal,
showModal,
clearModal,
};

View File

@ -97,11 +97,7 @@ $actionsbar-height: 50px; // TODO: Change to ActionsBar real height
.userList {
@extend %full-page;
z-index: 2;
overflow: hidden;
@include mq($small-only) {
padding-top: $navbar-height;
}
overflow: visible;
@include mq($small-only) {
padding-top: $navbar-height;
@ -117,11 +113,15 @@ $actionsbar-height: 50px; // TODO: Change to ActionsBar real height
}
}
.compact {
flex-basis: 4.6rem;
}
.chat {
@extend %full-page;
z-index: 3;
@include mq($small-only) {
z-index: 3;
padding-top: $navbar-height;
}

View File

@ -10,9 +10,8 @@
.closeChat {
text-decoration: none;
text-transform: capitalize;
}
.header {
margin-bottom: $line-height-computed;
}

View File

@ -3,6 +3,7 @@ import { findDOMNode } from 'react-dom';
import styles from './styles';
import DropdownTrigger from './trigger/component';
import DropdownContent from './content/component';
import cx from 'classnames';
const FOCUSABLE_CHILDREN = `[tabindex]:not([tabindex="-1"]), a, input, button`;
@ -48,19 +49,37 @@ export default class Dropdown extends Component {
this.state = { isOpen: false, };
this.handleShow = this.handleShow.bind(this);
this.handleHide = this.handleHide.bind(this);
this.handleStateCallback = this.handleStateCallback.bind(this);
this.handleToggle = this.handleToggle.bind(this);
this.handleWindowClick = this.handleWindowClick.bind(this);
}
componentDidUpdate(prevProps, prevState) {
if (prevState.isOpen !== this.props.isOpen
&& this.state.isOpen !== this.props.isOpen) {
this.setState({ isOpen: this.props.isOpen }, this.handleStateCallback);
}
}
handleStateCallback() {
const { onShow, onHide } = this.props;
if (this.state.isOpen && onShow) {
onShow();
} else if (onHide) {
onHide();
}
}
handleShow() {
this.setState({ isOpen: true });
this.setState({ isOpen: true }, this.handleStateCallback);
const contentElement = findDOMNode(this.refs.content);
contentElement.querySelector(FOCUSABLE_CHILDREN).focus();
}
handleHide() {
this.setState({ isOpen: false });
this.setState({ isOpen: false }, this.handleStateCallback);
const triggerElement = findDOMNode(this.refs.trigger);
triggerElement.focus();
}
@ -93,7 +112,7 @@ export default class Dropdown extends Component {
}
render() {
const { children } = this.props;
const { children, className, style } = this.props;
let trigger = children.find(x => x.type === DropdownTrigger);
let content = children.find(x => x.type === DropdownContent);
@ -114,7 +133,7 @@ export default class Dropdown extends Component {
});
return (
<div className={styles.dropdown}>
<div style={style} className={cx(styles.dropdown, className)}>
{trigger}
{content}
</div>

View File

@ -23,12 +23,8 @@ const defaultProps = {
};
export default class DropdownContent extends Component {
constructor(props) {
super(props);
}
render() {
const { placement, className, children } = this.props;
const { placement, className, children, style } = this.props;
const { dropdownToggle, dropdownShow, dropdownHide } = this.props;
let placementName = placement.split(' ').join('-');
@ -41,6 +37,7 @@ export default class DropdownContent extends Component {
return (
<div
style={style}
aria-expanded={this.props['aria-expanded']}
className={cx(styles.content, styles[placementName], className)}>
{boundChildren}

View File

@ -1,5 +1,6 @@
import React, { Component, PropTypes, Children, cloneElement } from 'react';
import styles from './styles';
import cx from 'classnames';
import KEY_CODES from '/imports/utils/keyCodes';
@ -84,6 +85,7 @@ export default class DropdownList extends Component {
const { dropdownHide } = this.props;
this.setState({ activeItemIndex: null });
dropdownHide();
if (typeof callback === 'function') {
@ -92,7 +94,9 @@ export default class DropdownList extends Component {
}
render() {
const boundChildren = Children.map(this.props.children,
const { children, style, className } = this.props;
const boundChildren = Children.map(children,
(item, i) => {
if (item.type === ListSeparator) {
return item;
@ -122,7 +126,7 @@ export default class DropdownList extends Component {
});
return (
<ul className={styles.list} role="menu">
<ul style={style} className={cx(styles.list, className)} role="menu">
{boundChildren}
</ul>
);

View File

@ -1,6 +1,7 @@
import React, { Component, PropTypes } from 'react';
import styles from '../styles';
import _ from 'underscore';
import cx from 'classnames';
import Icon from '/imports/ui/components/icon/component';
@ -22,14 +23,15 @@ export default class DropdownListItem extends Component {
const { icon, label } = this.props;
return [
(<Icon iconName={icon} key="icon" className={styles.itemIcon}/>),
(icon ? <Icon iconName={icon} key="icon" className={styles.itemIcon}/> : null),
(<span className={styles.itemLabel} key="label">{label}</span>),
];
}
render() {
const { label, description, children,
injectRef, tabIndex, onClick, onKeyDown, } = this.props;
injectRef, tabIndex, onClick, onKeyDown,
className, style, } = this.props;
return (
<li
@ -39,7 +41,8 @@ export default class DropdownListItem extends Component {
tabIndex={tabIndex}
aria-labelledby={this.labelID}
aria-describedby={this.descID}
className={styles.item}
className={cx(styles.item, className)}
style={style}
role="menuitem">
{
children ? children

View File

@ -1,8 +1,10 @@
import React, { Component, PropTypes } from 'react';
import styles from '../styles';
import cx from 'classnames';
export default class DropdownListSeparator extends Component {
render() {
return <li className={styles.separator} role="separator" />;
const { style, className } = this.props;
return <li style={style} className={cx(styles.separator, className)} role="separator" />;
}
}

View File

@ -2,6 +2,7 @@
$dropdown-bg: $color-white;
$dropdown-color: $color-text;
$caret-shadow-color: $color-gray;
$dropdown-caret-width: 12px;
$dropdown-caret-height: 8px;
@ -20,7 +21,7 @@ $dropdown-caret-height: 8px;
// min-width: 150px;
z-index: 1000;
&:after {
&:after, &:before {
content: '';
position: absolute;
width: 0;
@ -49,13 +50,17 @@ $dropdown-caret-height: 8px;
transform: translateX(-50%);
margin-bottom: $dropdown-caret-height * 1.25;
&:after {
&:before, &:after {
border-left: $dropdown-caret-width solid transparent;
border-right: $dropdown-caret-width solid transparent;
border-top: $dropdown-caret-height solid $dropdown-bg;
bottom: 0;
margin-bottom: -($dropdown-caret-height);
}
&:before {
border-top: $dropdown-caret-height solid $caret-shadow-color;
}
}
%up-caret {
@ -64,13 +69,17 @@ $dropdown-caret-height: 8px;
transform: translateX(-50%);
margin-top: $dropdown-caret-height * 1.25;
&:after {
&:before, &:after {
border-left: $dropdown-caret-width solid transparent;
border-right: $dropdown-caret-width solid transparent;
border-bottom: $dropdown-caret-height solid $dropdown-bg;
margin-top: -($dropdown-caret-height);
top: 0;
}
&:before {
border-bottom: $dropdown-caret-height solid $caret-shadow-color;
}
}
%right-caret {
@ -78,7 +87,7 @@ $dropdown-caret-height: 8px;
transform: translateX(-100%) translateY(-50%);
left: -($dropdown-caret-height * 1.25);
&:after {
&:before, &:after{
border-top: $dropdown-caret-width solid transparent;
border-bottom: $dropdown-caret-width solid transparent;
border-left: $dropdown-caret-height solid $dropdown-bg;
@ -86,6 +95,10 @@ $dropdown-caret-height: 8px;
top: 50%;
right: 0;
}
&:before {
border-left: $dropdown-caret-height solid $caret-shadow-color;
}
}
%left-caret {
@ -93,7 +106,7 @@ $dropdown-caret-height: 8px;
transform: translateX(100%) translateY(-50%);
right: -($dropdown-caret-height * 1.25);
&:after {
&:before, &:after {
border-top: $dropdown-caret-width solid transparent;
border-bottom: $dropdown-caret-width solid transparent;
border-right: $dropdown-caret-height solid $dropdown-bg;
@ -101,10 +114,14 @@ $dropdown-caret-height: 8px;
top: 50%;
left: 0;
}
&:before {
border-right: $dropdown-caret-height solid $caret-shadow-color;
}
}
%horz-center-caret {
&:after {
&:after, &:before {
margin-left: -($dropdown-caret-width);
}
}
@ -113,7 +130,7 @@ $dropdown-caret-height: 8px;
transform: translateX(-100%);
left: 100%;
&:after {
&:after, &:before {
right: $dropdown-caret-width / 2;
}
}
@ -123,13 +140,13 @@ $dropdown-caret-height: 8px;
right: 100%;
left: auto;
&:after {
&:after, &:before {
left: $dropdown-caret-width / 2;
}
}
%vert-center-caret {
&:after {
&:after, &:before {
margin-top: -($dropdown-caret-width);
}
}
@ -137,7 +154,7 @@ $dropdown-caret-height: 8px;
%vert-top-caret {
top: 0;
&:after {
&:after, &:before {
top: 0;
margin-top: $dropdown-caret-width / 2;
}
@ -147,7 +164,7 @@ $dropdown-caret-height: 8px;
top: auto;
bottom: 0;
&:after {
&:after, &:before {
top: auto;
bottom: $dropdown-caret-width / 2;
}

View File

@ -1,5 +1,6 @@
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import cx from 'classnames';
import KEY_CODES from '/imports/utils/keyCodes';
@ -20,7 +21,7 @@ export default class DropdownTrigger extends Component {
}
handleKeyDown(event) {
const { dropdownShow, dropdownHide } = this.props;
const { dropdownShow, dropdownHide, } = this.props;
if ([KEY_CODES.SPACE, KEY_CODES.ENTER].includes(event.which)) {
event.preventDefault();
@ -40,13 +41,16 @@ export default class DropdownTrigger extends Component {
}
render() {
const { children } = this.props;
const { children, style, className, } = this.props;
const TriggerComponent = React.Children.only(children);
const TriggerComponentBounded = React.cloneElement(children, {
onClick: this.handleClick,
onKeyDown: this.handleKeyDown,
'aria-haspopup': true,
tabIndex: '0',
style: style,
className: cx(children.props.className, className),
});
return TriggerComponentBounded;

View File

@ -1,4 +1,5 @@
import React, { Component, PropTypes } from 'react';
import { clearModal } from '/imports/ui/components/app/service';
import ModalBase from './base/component';
import Button from '../button/component';
import styles from './styles.scss';
@ -45,6 +46,7 @@ export default class Modal extends Component {
handleDismiss() {
this.setState({ isOpen: false });
clearModal();
}
handleConfirm() {

View File

@ -25,6 +25,7 @@ class ChatListItem extends Component {
const {
chat,
openChat,
compact,
} = this.props;
const linkPath = [PRIVATE_CHAT_PATH, chat.id].join('');
@ -33,11 +34,11 @@ class ChatListItem extends Component {
linkClasses[styles.active] = chat.id === openChat;
return (
<li className={cx(styles.chatListItem, linkClasses)} {...this.props}>
<li className={cx(styles.chatListItem, linkClasses)}>
<Link to={linkPath} className={styles.chatListItemLink}>
{chat.icon ? this.renderChatIcon() : this.renderChatAvatar()}
<div className={styles.chatName}>
<h3 className={styles.chatNameMain}>{chat.name}</h3>
{!compact ? <h3 className={styles.chatNameMain}>{chat.name}</h3> : null }
</div>
{(chat.unreadCounter > 0) ?
<div className={styles.unreadMessages}>

View File

@ -28,6 +28,9 @@ const listTransition = {
class UserList extends Component {
constructor(props) {
super(props);
this.state = {
compact: this.props.compact,
};
}
render() {
@ -42,13 +45,16 @@ class UserList extends Component {
renderHeader() {
return (
<div className={styles.header}>
<h2 className={styles.headerTitle}>
<FormattedMessage
id="app.userlist.participantsTitle"
description="Title for the Header"
defaultMessage="Participants"
/>
</h2>
{
!this.state.compact ?
<h2 className={styles.headerTitle}>
<FormattedMessage
id="app.userlist.participantsTitle"
description="Title for the Header"
defaultMessage="Participants"
/>
</h2> : null
}
</div>
);
}
@ -70,13 +76,16 @@ class UserList extends Component {
return (
<div className={styles.messages}>
<h3 className={styles.smallTitle}>
<FormattedMessage
id="app.userlist.messagesTitle"
description="Title for the messages list"
defaultMessage="Messages"
/>
</h3>
{
!this.state.compact ?
<h3 className={styles.smallTitle}>
<FormattedMessage
id="app.userlist.messagesTitle"
description="Title for the messages list"
defaultMessage="Messages"
/>
</h3> : <hr className={styles.separator}></hr>
}
<div className={styles.scrollableList}>
<ReactCSSTransitionGroup
transitionName={listTransition}
@ -90,6 +99,7 @@ class UserList extends Component {
className={cx(styles.chatsList, styles.scrollableList)}>
{openChats.map(chat => (
<ChatListItem
compact={this.state.compact}
key={chat.id}
openChat={openChat}
chat={chat} />
@ -105,18 +115,22 @@ class UserList extends Component {
users,
currentUser,
userActions,
compact,
} = this.props;
return (
<div className={styles.participants}>
<h3 className={styles.smallTitle}>
<FormattedMessage
id="app.userlist.participantsTitle"
description="Title for the Participants list"
defaultMessage="Participants"
/>
&nbsp;({this.props.users.length})
</h3>
{
!this.state.compact ?
<h3 className={styles.smallTitle}>
<FormattedMessage
id="app.userlist.participantsTitle"
description="Title for the Participants list"
defaultMessage="Participants"
/>
&nbsp;({users.length})
</h3> : <hr className={styles.separator}></hr>
}
<ReactCSSTransitionGroup
transitionName={listTransition}
transitionAppear={true}
@ -127,8 +141,10 @@ class UserList extends Component {
transitionLeaveTimeout={0}
component="ul"
className={cx(styles.participantsList, styles.scrollableList)}>
{users.map(user => (
{
users.map(user => (
<UserListItem
compact={this.state.compact}
key={user.id}
user={user}
currentUser={currentUser}

View File

@ -6,10 +6,25 @@ import UserList from './component.jsx';
class UserListContainer extends Component {
render() {
const {
compact,
users,
currentUser,
openChats,
openChat,
userActions,
children,
} = this.props;
return (
<UserList
{...this.props}>
{this.props.children}
compact={compact}
users={users}
currentUser={currentUser}
openChats={openChats}
openChat={openChat}
userActions={userActions}>
{children}
</UserList>
);
}

View File

@ -43,7 +43,7 @@ $user-icons-color-hover: $color-gray;
padding-left: 0.5rem;
padding-right: 0rem;
margin-left: 0.7rem;
margin-top: 0.9rem;
margin-top: 0.3rem;
display: flex;
flex-flow: row;
transition: all 0.3s;
@ -102,6 +102,7 @@ $user-icons-color-hover: $color-gray;
.participantsList,
.chatsList {
@extend .lists;
overflow-x: hidden;
flex-shrink: 1;
}
@ -130,6 +131,13 @@ $user-icons-color-hover: $color-gray;
flex-shrink: 1;
}
.separator {
margin: 1rem auto;
width: 2.2rem;
border: 0;
border-top: 1px solid $color-gray-lighter;
}
.enter, .appear {
opacity: 0.01;
}

View File

@ -2,11 +2,19 @@ import React, { Component } from 'react';
import UserAvatar from '/imports/ui/components/user-avatar/component';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import Icon from '/imports/ui/components/icon/component';
import UserActions from './user-actions/component';
import { findDOMNode } from 'react-dom';
import { withRouter } from 'react-router';
import { defineMessages, injectIntl } from 'react-intl';
import styles from './styles.scss';
import cx from 'classnames';
import _ from 'underscore';
import Dropdown from '/imports/ui/components/dropdown/component';
import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
import DropdownContent from '/imports/ui/components/dropdown/content/component';
import DropdownList from '/imports/ui/components/dropdown/list/component';
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
import DropdownListSeparator from '/imports/ui/components/dropdown/list/separator/component';
const propTypes = {
user: React.PropTypes.shape({
@ -74,51 +82,148 @@ class UserListItem extends Component {
super(props);
this.state = {
visibleActions: false,
isActionsOpen: false,
};
this.handleToggleActions = this.handleToggleActions.bind(this);
this.handleClickOutsideDropdown = this.handleClickOutsideDropdown.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.onActionsShow = this.onActionsShow.bind(this);
this.onActionsHide = this.onActionsHide.bind(this);
}
handleClickOutsideDropdown(e) {
const node = findDOMNode(this);
const shouldUpdateState = e.target !== node &&
!node.contains(e.target) &&
this.state.visibleActions;
if (shouldUpdateState) {
this.setState({ visibleActions: false });
}
handleScroll() {
this.setState({
isActionsOpen: false,
});
}
handleToggleActions() {
this.setState({ visibleActions: !this.state.visibleActions });
getAvailableActions() {
const {
currentUser,
user,
userActions,
router,
} = this.props;
const {
openChat,
clearStatus,
setPresenter,
promote,
kick,
} = userActions;
return _.compact([
(!user.isCurrent ? this.renderUserAction(openChat, router, user) : null),
(currentUser.isModerator ? this.renderUserAction(clearStatus, user) : null),
(currentUser.isModerator ? this.renderUserAction(setPresenter, user) : null),
(currentUser.isModerator ? this.renderUserAction(promote, user) : null),
(currentUser.isModerator ? this.renderUserAction(kick, user) : null),
]);
}
onActionsShow() {
const dropdown = findDOMNode(this.refs.dropdown);
this.setState({
contentTop: `${dropdown.offsetTop - dropdown.parentElement.parentElement.scrollTop}px`,
isActionsOpen: true,
active: true,
});
findDOMNode(this).parentElement.addEventListener('scroll', this.handleScroll, false);
}
onActionsHide() {
this.setState({
active: false,
isActionsOpen: false,
});
findDOMNode(this).parentElement.removeEventListener('scroll', this.handleScroll, false);
}
render() {
const {
user,
currentUser,
userActions,
compact,
} = this.props;
let userItemContentsStyle = {};
userItemContentsStyle[styles.userItemContentsCompact] = compact;
userItemContentsStyle[styles.active] = this.state.active;
return (
<li onClick={this.handleToggleActions.bind(this, user)}
className={styles.userListItem} {...this.props}>
<div className={styles.userItemContents}>
<UserAvatar user={this.props.user}/>
{this.renderUserName()}
{this.renderUserIcons()}
</div>
{this.renderUserActions()}
<li
className={cx(styles.userListItem, userItemContentsStyle)}>
{this.renderUserContents()}
</li>
);
}
renderUserContents() {
const {
user,
} = this.props;
let actions = this.getAvailableActions();
let contents = (
<div tabIndex={0} className={styles.userItemContents}>
<UserAvatar user={user}/>
{this.renderUserName()}
{this.renderUserIcons()}
</div>
);
if (!actions.length) {
return contents;
}
return (
<Dropdown
isOpen={this.state.isActionsOpen}
ref="dropdown"
onShow={this.onActionsShow}
onHide={this.onActionsHide}
className={styles.dropdown}>
<DropdownTrigger>
{contents}
</DropdownTrigger>
<DropdownContent
style={{
top: this.state.contentTop,
}}
className={styles.dropdownContent}
placement="right top">
<DropdownList>
{
[
(<DropdownListItem
className={styles.actionsHeader}
key={_.uniqueId('action-header')}
label={user.name}
defaultMessage={user.name}/>),
(<DropdownListSeparator key={_.uniqueId('action-separator')} />),
].concat(actions)
}
</DropdownList>
</DropdownContent>
</Dropdown>
);
}
renderUserName() {
const {
user,
intl,
compact,
} = this.props;
if (compact) {
return;
}
let userNameSub = [];
if (user.isPresenter) {
userNameSub.push(intl.formatMessage(messages.presenter));
@ -145,8 +250,13 @@ class UserListItem extends Component {
renderUserIcons() {
const {
user,
compact,
} = this.props;
if (compact) {
return;
}
let audioChatIcon = null;
if (user.isVoiceUser || user.isListenOnly) {
if (user.isMuted) {
@ -168,34 +278,22 @@ class UserListItem extends Component {
);
}
renderUserActions() {
renderUserAction(action, ...parameters) {
const {
user,
currentUser,
userActions,
user,
} = this.props;
let visibleActions = null;
if (this.state.visibleActions) {
visibleActions = <UserActions
user={user}
currentUser={currentUser}
userActions={userActions}/>;
}
return (
<ReactCSSTransitionGroup
transitionName={userActionsTransition}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionAppearTimeout={0}
transitionEnterTimeout={0}
transitionLeaveTimeout={0}
>
{visibleActions}
</ReactCSSTransitionGroup>
const userAction = (
<DropdownListItem key={_.uniqueId('action-item-')}
icon={action.icon}
label={action.label}
defaultMessage={action.label}
onClick={action.handler.bind(this, ...parameters)}
/>
);
return userAction;
}
}

View File

@ -17,6 +17,18 @@
opacity: 1;
}
}
&:last-child {
margin-bottom: 0.5rem;
}
}
.active {
background-color: $list-item-bg-hover;
outline: none;
}
.userItemContentsCompact {
}
.userName {
@ -35,7 +47,6 @@
line-height: 1rem;
overflow: hidden;
text-overflow: ellipsis;
text-transform: capitalize;
white-space: nowrap;
transition: all 0.3s;
}
@ -74,7 +85,7 @@
}
.userItemContents {
flex-grow: 1;
flex-grow: 0;
display: flex;
flex-flow: row;
}
@ -129,3 +140,20 @@
transition: all 300ms;
transform: translateY(-100%);
}
.dropdown {
position: static;
}
.dropdownContent {
cursor: default;
}
.actionsHeader {
color: $color-gray;
&:hover {
color: $color-gray !important;
cursor: default;
}
}

View File

@ -1,76 +0,0 @@
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import Icon from '/imports/ui/components/icon/component';
import styles from './styles.scss';
const propTypes = {
user: React.PropTypes.shape({
id: React.PropTypes.string.isRequired,
}).isRequired,
currentUser: React.PropTypes.shape({
isModerator: React.PropTypes.bool.isRequired,
}).isRequired,
userActions: React.PropTypes.shape().isRequired,
};
const defaultProps = {
};
class UserActions extends Component {
constructor(props) {
super(props);
}
render() {
const {
user,
currentUser,
router,
} = this.props;
const {
openChat,
clearStatus,
setPresenter,
promote,
kick,
} = this.props.userActions;
return (
<div key={user.id} className={styles.userItemActions}>
<ul className={styles.userActionsList}>
{!user.isCurrent ? this.renderUserAction(openChat, router, user) : null}
{currentUser.isModerator ? this.renderUserAction(clearStatus, user) : null}
{currentUser.isModerator ? this.renderUserAction(setPresenter, user) : null}
{currentUser.isModerator ? this.renderUserAction(promote, user) : null}
{currentUser.isModerator ? this.renderUserAction(kick, user) : null}
</ul>
</div>
);
}
renderUserAction(action, ...parameters) {
const currentUser = this.props.currentUser;
const user = this.props.user;
const userAction = (
<li onClick={action.handler.bind(this, ...parameters)}
className={styles.userActionsItem}>
<Icon iconName={action.icon} className={styles.actionIcon}/>
<span className={styles.actionText}>
{action.label}
</span>
</li>
);
return userAction;
}
}
UserActions.propTypes = propTypes;
UserActions.defaultProps = defaultProps;
export default withRouter(UserActions);

View File

@ -1,42 +0,0 @@
@import '../styles.scss';
.userItemActions {
overflow: hidden;
// display: none;
}
.userActionsList {
list-style: none;
padding: 0;
font-size: 0.9rem;
}
.userActionsItem {
margin-top: 0.25rem;
padding: 0.2rem 0;
display: flex;
cursor: pointer;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
transition: background-color 0.3s;
&:hover, &:focus {
background-color: darken($user-list-bg, 14%);
}
&:first-child {
margin-top: 0.5rem;
}
}
.actionIcon {
color: $color-gray-light;
line-height: 1.1rem;
flex-basis: 1.7rem;
font-weight: bold;
}
.actionText {
color: $color-gray;
padding: 0 0.6rem;
}

View File

@ -14,7 +14,7 @@ app:
bbbServerVersion: "1.0"
copyrightYear: "2015"
html5ClientBuild: "NNNN"
defaultWelcomeMessage: Welcome to %%CONFNAME%%!\r\rFor help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.\r\rTo join the audio bridge click the gear icon (upper-right hand corner). Use a headset to avoid causing background noise for others.\r\r\r
defaultWelcomeMessage: Welcome to %%CONFNAME%%!<br /><br />For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br /><br />To join the audio bridge click the gear icon (upper-right hand corner). Use a headset to avoid causing background noise for others.<br /><br /><br />
lockOnJoin: true
defaultWelcomeMessageFooter: This server is running a build of <a href="http://docs.bigbluebutton.org/1.0/10overview.html" target="_blank"><u>BigBlueButton 1.0</u></a>.

View File

@ -408,9 +408,7 @@ public class ParamsProcessorUtil {
}
public String getDefaultConfigXML() {
if (defaultConfigXML == null) {
defaultConfigXML = getConfig(defaultConfigURL);
}
defaultConfigXML = getConfig(defaultConfigURL);
return defaultConfigXML;
}