Merge remote-tracking branch 'upstream/master' into refactor-api-users
* upstream/master: (47 commits) Added ExitApplicationEvent.as HTML 5 - changed css for divider No more display the LoggedOutWindow.mxml if the user has explicitly clicked on yes to confirm the logout action. Link "enableEmojiStatus" enabled/disabled to the emoji button. initialize html5StunTurn properly - complication from PR 3628 fix bug preventing stun/turn call fix lint issues rework userPermissions to allow mixture of role/status based actions Handle exception when sending message to client HTML5 - changed divider of record button Change the emoji menu icon to the happy face. don't show promote option for presenter user' remove glow prop and use className to change style reorganize user actions clean up improve userPermissions by rearranging logic allow for mute/unmute by moderator rename css class Display breakout rooms user names in row tooltip. fix setPresenter and kick user ...
This commit is contained in:
commit
c1d0797d92
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core.pubsub.receivers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoMessage;
|
||||
import org.bigbluebutton.common.messages.GetSlideInfoMessage;
|
||||
@ -15,19 +14,18 @@ import org.bigbluebutton.common.messages.SendCursorUpdateMessage;
|
||||
import org.bigbluebutton.common.messages.SendPageCountErrorMessage;
|
||||
import org.bigbluebutton.common.messages.SendSlideGeneratedMessage;
|
||||
import org.bigbluebutton.common.messages.SharePresentationMessage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
public class PresentationMessageListener implements MessageHandler {
|
||||
|
||||
public static final String OFFICE_DOC_CONVERSION_SUCCESS_KEY = "OFFICE_DOC_CONVERSION_SUCCESS";
|
||||
public static final String OFFICE_DOC_CONVERSION_FAILED_KEY = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static final String OFFICE_DOC_CONVERSION_INVALID_KEY = "OFFICE_DOC_CONVERSION_INVALID";
|
||||
public static final String SUPPORTED_DOCUMENT_KEY = "SUPPORTED_DOCUMENT";
|
||||
public static final String UNSUPPORTED_DOCUMENT_KEY = "UNSUPPORTED_DOCUMENT";
|
||||
public static final String PAGE_COUNT_FAILED_KEY = "PAGE_COUNT_FAILED";
|
||||
@ -145,13 +143,14 @@ public class PresentationMessageListener implements MessageHandler {
|
||||
String conference = (String) map.get("conference");
|
||||
String messageKey = (String) map.get("messageKey");
|
||||
|
||||
if (messageKey.equalsIgnoreCase(OFFICE_DOC_CONVERSION_SUCCESS_KEY) ||
|
||||
messageKey.equalsIgnoreCase(OFFICE_DOC_CONVERSION_FAILED_KEY) ||
|
||||
messageKey.equalsIgnoreCase(SUPPORTED_DOCUMENT_KEY) ||
|
||||
messageKey.equalsIgnoreCase(UNSUPPORTED_DOCUMENT_KEY) ||
|
||||
messageKey.equalsIgnoreCase(GENERATING_THUMBNAIL_KEY) ||
|
||||
messageKey.equalsIgnoreCase(GENERATED_THUMBNAIL_KEY) ||
|
||||
messageKey.equalsIgnoreCase(PAGE_COUNT_FAILED_KEY)){
|
||||
if (messageKey.equalsIgnoreCase(OFFICE_DOC_CONVERSION_SUCCESS_KEY) ||
|
||||
messageKey.equalsIgnoreCase(OFFICE_DOC_CONVERSION_FAILED_KEY) ||
|
||||
messageKey.equalsIgnoreCase(OFFICE_DOC_CONVERSION_INVALID_KEY) ||
|
||||
messageKey.equalsIgnoreCase(SUPPORTED_DOCUMENT_KEY) ||
|
||||
messageKey.equalsIgnoreCase(UNSUPPORTED_DOCUMENT_KEY) ||
|
||||
messageKey.equalsIgnoreCase(GENERATING_THUMBNAIL_KEY) ||
|
||||
messageKey.equalsIgnoreCase(GENERATED_THUMBNAIL_KEY) ||
|
||||
messageKey.equalsIgnoreCase(PAGE_COUNT_FAILED_KEY)){
|
||||
|
||||
sendConversionUpdate(messageKey, conference, code, presId, filename);
|
||||
} else if(messageKey.equalsIgnoreCase(PAGE_COUNT_EXCEEDED_KEY)){
|
||||
|
@ -20,14 +20,14 @@
|
||||
|
||||
# BigBlueButton integration information
|
||||
#----------------------------------------------------
|
||||
# This URL is where the BBB client is accessible.
|
||||
# This URL is where the BBB client is accessible.
|
||||
bigbluebuttonURL=http://localhost/bigbluebutton
|
||||
# Salt which is used by 3rd-party apps to authenticate api calls
|
||||
bigbluebuttonSalt=bbb_salt
|
||||
|
||||
# LTI basic information
|
||||
#----------------------------------------------------
|
||||
# This URL is where the LTI plugin is accessible. It can be a different server than the BigBluebutton one
|
||||
# This URL is where the LTI plugin is accessible. It can be a different server than the BigBluebutton one
|
||||
# Only the hostname or IP address is required, plus the port number in case it is other than port 80
|
||||
# e.g. localhost or localhost:port
|
||||
ltiEndPoint=localhost
|
||||
@ -41,6 +41,9 @@ ltiMode=extended
|
||||
# Defines if LTI credentials are required
|
||||
# Format: [false|<true>]
|
||||
ltiRestrictedAccess=true
|
||||
# Sets all the meetings to be recorded by default
|
||||
# Format: [<false>|true]
|
||||
ltiAllRecordedByDefault=false
|
||||
|
||||
#----------------------------------------------------
|
||||
# Inject configuration values into BigbluebuttonSrvice beans
|
||||
@ -53,4 +56,4 @@ beans.ltiService.endPoint=${ltiEndPoint}
|
||||
beans.ltiService.consumers=${ltiConsumers}
|
||||
beans.ltiService.mode=${ltiMode}
|
||||
beans.ltiService.restrictedAccess=${ltiRestrictedAccess}
|
||||
|
||||
beans.ltiService.recordedByDefault=${ltiAllRecordedByDefault}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package org.bigbluebutton
|
||||
/*
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
@ -83,8 +83,8 @@ class ToolController {
|
||||
result = doJoinMeeting(params)
|
||||
} else {
|
||||
log.debug "LTI service running in extended mode."
|
||||
if ( !Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) ) {
|
||||
log.debug "No bbb_record parameter was sent; immediately redirecting to BBB session!"
|
||||
if ( !Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) && !ltiService.allRecordedByDefault() ) {
|
||||
log.debug "Parameter custom_record was not sent; immediately redirecting to BBB session!"
|
||||
result = doJoinMeeting(params)
|
||||
}
|
||||
}
|
||||
@ -222,7 +222,7 @@ class ToolController {
|
||||
log.debug "Overriding default welcome message with: [" + welcome + "]"
|
||||
}
|
||||
|
||||
if ( params.containsKey(Parameter.CUSTOM_RECORD) && Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) ) {
|
||||
if ( params.containsKey(Parameter.CUSTOM_RECORD) && Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) || ltiService.allRecordedByDefault() ) {
|
||||
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.record") + "</b><br>"
|
||||
log.debug "Adding record warning to welcome message, welcome is now: [" + welcome + "]"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package org.bigbluebutton
|
||||
/*
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
@ -92,7 +92,7 @@ class BigbluebuttonService {
|
||||
Integer duration = 0
|
||||
if( "extended".equals(mode) ){
|
||||
voiceBridge = getValidatedBBBVoiceBridge(params.get(Parameter.CUSTOM_VOICEBRIDGE))
|
||||
record = getValidatedBBBRecord(params.get(Parameter.CUSTOM_RECORD))
|
||||
record = getValidatedBBBRecord(params.get(Parameter.CUSTOM_RECORD)) || ltiService.allRecordedByDefault()
|
||||
duration = getValidatedBBBDuration(params.get(Parameter.CUSTOM_DURATION))
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ class BigbluebuttonService {
|
||||
private String getValidatedUserId(String userId){
|
||||
return (userId == null)? "": userId
|
||||
}
|
||||
|
||||
|
||||
private Integer getValidatedBBBVoiceBridge(String voiceBridge){
|
||||
return (voiceBridge != null )? voiceBridge.toInteger(): 0
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package org.bigbluebutton
|
||||
/*
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
@ -32,9 +32,10 @@ class LtiService {
|
||||
def consumers = "demo:welcome"
|
||||
def mode = "simple"
|
||||
def restrictedAccess = "true"
|
||||
def recordedByDefault = "false"
|
||||
|
||||
Map<String, String> consumerMap
|
||||
|
||||
|
||||
def retrieveIconEndpoint() {
|
||||
return endPoint.replaceFirst("tool", "images/icon.ico")
|
||||
}
|
||||
@ -42,16 +43,16 @@ class LtiService {
|
||||
def retrieveBasicLtiEndpoint() {
|
||||
return endPoint
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getConsumer(consumerId) {
|
||||
Map<String, String> consumer = null
|
||||
|
||||
|
||||
if( this.consumerMap.containsKey(consumerId) ){
|
||||
consumer = new HashMap<String, String>()
|
||||
consumer.put("key", consumerId);
|
||||
consumer.put("secret", this.consumerMap.get(consumerId))
|
||||
}
|
||||
|
||||
|
||||
return consumer
|
||||
}
|
||||
|
||||
@ -66,19 +67,19 @@ class LtiService {
|
||||
this.consumerMap.put(consumer[0], consumer[1])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public String sign(String sharedSecret, String data) throws Exception
|
||||
{
|
||||
Mac mac = setKey(sharedSecret)
|
||||
|
||||
|
||||
// Signed String must be BASE64 encoded.
|
||||
byte[] signBytes = mac.doFinal(data.getBytes("UTF8"));
|
||||
String signature = encodeBase64(signBytes);
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
private Mac setKey(String sharedSecret) throws Exception
|
||||
{
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
@ -137,10 +138,14 @@ class LtiService {
|
||||
log.debug("Exception: Message=" + e.getMessage())
|
||||
}
|
||||
|
||||
return ssl_enabled
|
||||
return ssl_enabled
|
||||
}
|
||||
|
||||
def boolean hasRestrictedAccess() {
|
||||
return Boolean.parseBoolean(this.restrictedAccess);
|
||||
}
|
||||
|
||||
def boolean allRecordedByDefault() {
|
||||
return Boolean.parseBoolean(this.recordedByDefault);
|
||||
}
|
||||
}
|
||||
|
@ -21,4 +21,5 @@ package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
public interface ClientMessage {
|
||||
|
||||
String getMessageName();
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ package org.bigbluebutton.red5.client.messaging;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
@ -44,6 +43,8 @@ import org.red5.server.util.ScopeUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
public class ConnectionInvokerService {
|
||||
private static Logger log = Red5LoggerFactory.getLogger(ConnectionInvokerService.class, "bigbluebutton");
|
||||
@ -75,15 +76,21 @@ public class ConnectionInvokerService {
|
||||
ClientMessage message;
|
||||
try {
|
||||
message = messages.take();
|
||||
sendMessageToClient(message);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Took message from queue: " + message.getMessageName());
|
||||
}
|
||||
sendMessageToClient(message);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Sent message to client: " + message.getMessageName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Marker sendingException = MarkerFactory.getMarker("SENDING_EXCEPTION");
|
||||
log.error(sendingException, "Exception while sending message to client.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
exec.execute(sender);
|
||||
exec.execute(sender);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@ -92,6 +99,9 @@ public class ConnectionInvokerService {
|
||||
}
|
||||
|
||||
public void sendMessage(final ClientMessage message) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Queue message: " + message.getMessageName());
|
||||
}
|
||||
messages.offer(message);
|
||||
}
|
||||
|
||||
@ -124,7 +134,7 @@ public class ConnectionInvokerService {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisconnectAllClientsMessage(DisconnectAllClientsMessage msg) {
|
||||
@ -152,9 +162,9 @@ public class ConnectionInvokerService {
|
||||
log.info("Disconnecting user=[{}] from meeting=[{}]", msg.getUserId(), msg.getMeetingId());
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendSharedObjectMessage(SharedObjectClientMessage msg) {
|
||||
IScope meetingScope = getScope(msg.getMeetingID());
|
||||
@ -167,7 +177,8 @@ public class ConnectionInvokerService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void sendDirectMessage(final DirectClientMessage msg) {
|
||||
if (log.isTraceEnabled()) {
|
||||
Gson gson = new Gson();
|
||||
@ -200,9 +211,9 @@ public class ConnectionInvokerService {
|
||||
log.info("Cannot send message=[" + msg.getMessageName() + "] to [" + userId
|
||||
+ "] as no such session on meeting=[" + msg.getMeetingID() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* We need to add a way to cancel sending when the thread is blocked.
|
||||
@ -218,6 +229,7 @@ public class ConnectionInvokerService {
|
||||
f.get(timeLeft, TimeUnit.NANOSECONDS);
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("ExecutionException while sending direct message on connection[" + userId + "]");
|
||||
log.warn("ExcecutionException cause: " + e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted exception while sending direct message on connection[" + userId + "]");
|
||||
Thread.currentThread().interrupt();
|
||||
@ -226,7 +238,7 @@ public class ConnectionInvokerService {
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void sendBroadcastMessage(final BroadcastClientMessage msg) {
|
||||
if (log.isTraceEnabled()) {
|
||||
Gson gson = new Gson();
|
||||
@ -241,11 +253,13 @@ public class ConnectionInvokerService {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
params.add(msg.getMessageName());
|
||||
params.add(msg.getMessage());
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(msg.getMessage());
|
||||
log.trace("Broadcast message: " + msg.getMessageName() + " msg=" + json);
|
||||
}
|
||||
|
||||
ServiceUtils.invokeOnAllScopeConnections(meetingScope, "onMessageFromServer", params.toArray(), null);
|
||||
}
|
||||
}
|
||||
@ -266,10 +280,10 @@ public class ConnectionInvokerService {
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("ExecutionException while sending broadcast message[" + msg.getMessageName() + "]");
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted exception while sending direct message[" + msg.getMessageName() + "]");
|
||||
log.warn("Interrupted exception while sending broadcast message[" + msg.getMessageName() + "]");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException e) {
|
||||
log.warn("Timeout exception while sending direct message[" + msg.getMessageName() + "]");
|
||||
log.warn("Timeout exception while sending broadcast message[" + msg.getMessageName() + "]");
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
|
@ -11,4 +11,8 @@ public class DisconnectAllClientsMessage implements ClientMessage {
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectAllClientsMessage";
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,7 @@ package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
public class DisconnectAllMessage implements ClientMessage {
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectAllMessage";
|
||||
}
|
||||
}
|
||||
|
@ -17,4 +17,8 @@ public class DisconnectClientMessage implements ClientMessage {
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectClientMessage";
|
||||
}
|
||||
}
|
||||
|
@ -1061,6 +1061,11 @@ EmojiGrid {
|
||||
horizontalGap: 6;
|
||||
}
|
||||
|
||||
RoomActionsRenderer {
|
||||
paddingLeft : 5;
|
||||
paddingRight : 5;
|
||||
}
|
||||
|
||||
.breakoutRoomUserWindowHeadingStyle {
|
||||
fontWeight: bold;
|
||||
}
|
||||
|
@ -196,6 +196,7 @@ bbb.presentation.uploaded = uploaded.
|
||||
bbb.presentation.document.supported = The uploaded document is supported. Starting to convert...
|
||||
bbb.presentation.document.converted = Successfully converted the office document.
|
||||
bbb.presentation.error.document.convert.failed = Error: Unable to convert the office document.
|
||||
bbb.presentation.error.document.convert.invalid = Please convert this document to PDF first.
|
||||
bbb.presentation.error.io = IO Error: Please contact administrator.
|
||||
bbb.presentation.error.security = Security Error: Please contact administrator.
|
||||
bbb.presentation.error.convert.notsupported = Error: The uploaded document is unsupported. Please upload a compatible file.
|
||||
@ -641,10 +642,10 @@ bbb.lockSettings.lockOnJoin=Lock On Join
|
||||
|
||||
bbb.users.breakout.breakoutRooms = Breakout Rooms
|
||||
bbb.users.breakout.updateBreakoutRooms = Update Breakout Rooms
|
||||
bbb.users.breakout.remainingTimeBreakout = {0}: <b>{1} remaining</b>
|
||||
bbb.users.breakout.remainingTimeParent = <b>{1} remaining</b>
|
||||
bbb.users.breakout.timer = <b>{0}</b>
|
||||
bbb.users.breakout.timer.toolTip = Time left for breakout rooms
|
||||
bbb.users.breakout.calculatingRemainingTime = Calculating remaining time...
|
||||
bbb.users.breakout.remainingTimeEnded = Time ended, breakout room will close.
|
||||
bbb.users.breakout.closing = Closing
|
||||
bbb.users.breakout.rooms = Rooms
|
||||
bbb.users.breakout.roomsCombo.accessibilityName = Number of rooms to create
|
||||
bbb.users.breakout.room = Room
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
var userID, callerIdName=null, conferenceVoiceBridge, userAgent=null, userMicMedia, userWebcamMedia, currentSession=null, callTimeout, callActive, callICEConnected, iceConnectedTimeout, callFailCounter, callPurposefullyEnded, uaConnected, transferTimeout, iceGatheringTimeout;
|
||||
var inEchoTest = true;
|
||||
var html5StunTurn = {};
|
||||
var html5StunTurn = null;
|
||||
|
||||
function webRTCCallback(message) {
|
||||
switch (message.status) {
|
||||
@ -47,8 +47,10 @@ function callIntoConference(voiceBridge, callback, isListenOnly, stunTurn = null
|
||||
|
||||
// if additional stun configuration is passed, store the information
|
||||
if (stunTurn != null) {
|
||||
html5StunTurn['stunServers'] = stunTurn.stun;
|
||||
html5StunTurn['turnServers'] = stunTurn.turn;
|
||||
html5StunTurn = {
|
||||
stunServers: stunTurn.stun,
|
||||
turnServers: stunTurn.turn,
|
||||
};
|
||||
}
|
||||
|
||||
// reset callerIdName
|
||||
|
@ -65,6 +65,7 @@ package org.bigbluebutton.core
|
||||
/** For Conversion Update Events **/
|
||||
public static const OFFICE_DOC_CONVERSION_SUCCESS:String = "OfficeDocConversionSuccessEvent";
|
||||
public static const OFFICE_DOC_CONVERSION_FAILED:String = "OfficeDocConversionFailedEvent";
|
||||
public static const OFFICE_DOC_CONVERSION_INVALID:String = "OfficeDocConversionInvalidEvent";
|
||||
public static const SUPPORTED_DOCUMENT:String = "SupportedDocEvent";
|
||||
public static const UNSUPPORTED_DOCUMENT:String = "UnsupportedDocEvent";
|
||||
public static const PAGE_COUNT_FAILED:String = "PageCountFailedEvent";
|
||||
|
@ -36,13 +36,10 @@ package org.bigbluebutton.core {
|
||||
timer.addEventListener(TimerEvent.TIMER, function():void {
|
||||
var remainingSeconds:int = timer.repeatCount - timer.currentCount;
|
||||
var formattedTime:String = (Math.floor(remainingSeconds / 60)) + ":" + (remainingSeconds % 60 >= 10 ? "" : "0") + (remainingSeconds % 60);
|
||||
label.htmlText = ResourceUtil.getInstance().getString(
|
||||
UserManager.getInstance().getConference().isBreakout ? 'bbb.users.breakout.remainingTimeBreakout' : 'bbb.users.breakout.remainingTimeParent',
|
||||
[UserManager.getInstance().getConference().meetingName, formattedTime]
|
||||
);
|
||||
label.htmlText = ResourceUtil.getInstance().getString('bbb.users.breakout.timer', [formattedTime]);
|
||||
});
|
||||
timer.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
|
||||
label.text = ResourceUtil.getInstance().getString('bbb.users.breakout.remainingTimeEnded');
|
||||
label.text = ResourceUtil.getInstance().getString('bbb.users.breakout.closing');
|
||||
});
|
||||
} else {
|
||||
timer.stop();
|
||||
|
@ -51,6 +51,7 @@ package org.bigbluebutton.main.api
|
||||
import org.bigbluebutton.modules.present.events.CreatingThumbnailsEvent;
|
||||
import org.bigbluebutton.modules.present.events.GetListOfPresentationsReply;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertInvalidEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertSuccessEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadEvent;
|
||||
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
|
||||
@ -317,6 +318,12 @@ package org.bigbluebutton.main.api
|
||||
payload.eventName = EventConstants.OFFICE_DOC_CONVERSION_SUCCESS;
|
||||
broadcastEvent(payload);
|
||||
}
|
||||
|
||||
public function handleOfficeDocConversionInvalid(event:OfficeDocConvertInvalidEvent):void{
|
||||
var payload:Object = new Object();
|
||||
payload.eventName = EventConstants.OFFICE_DOC_CONVERSION_INVALID;
|
||||
broadcastEvent(payload);
|
||||
}
|
||||
|
||||
public function handleOfficeDocConversionFailed(event:OfficeDocConvertFailedEvent):void{
|
||||
var payload:Object = new Object();
|
||||
|
@ -23,32 +23,33 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<EventMap xmlns="http://mate.asfusion.com/" xmlns:mx="http://www.adobe.com/2006/mxml">
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import org.bigbluebutton.core.EventConstants;
|
||||
import org.bigbluebutton.core.events.AmIPresenterQueryEvent;
|
||||
import org.bigbluebutton.core.events.AmISharingWebcamQueryEvent;
|
||||
import org.bigbluebutton.core.events.GetMyUserInfoRequestEvent;
|
||||
import org.bigbluebutton.core.events.IsUserPublishingCamRequest;
|
||||
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
|
||||
import org.bigbluebutton.main.api.ExternalApiCalls;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.LogoutEvent;
|
||||
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
|
||||
import org.bigbluebutton.main.events.UserJoinedEvent;
|
||||
import org.bigbluebutton.main.events.UserLeftEvent;
|
||||
import org.bigbluebutton.main.model.users.events.BroadcastStartedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.BroadcastStoppedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionCompletedEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionPageCountError;
|
||||
import org.bigbluebutton.modules.present.events.ConversionPageCountMaxed;
|
||||
import org.bigbluebutton.modules.present.events.ConversionSupportedDocEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionUnsupportedDocEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionUpdateEvent;
|
||||
import org.bigbluebutton.modules.present.events.CreatingThumbnailsEvent;
|
||||
import org.bigbluebutton.modules.present.events.GetListOfPresentationsReply;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertSuccessEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadEvent;
|
||||
import org.bigbluebutton.core.EventConstants;
|
||||
import org.bigbluebutton.core.events.AmIPresenterQueryEvent;
|
||||
import org.bigbluebutton.core.events.AmISharingWebcamQueryEvent;
|
||||
import org.bigbluebutton.core.events.GetMyUserInfoRequestEvent;
|
||||
import org.bigbluebutton.core.events.IsUserPublishingCamRequest;
|
||||
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
|
||||
import org.bigbluebutton.main.api.ExternalApiCalls;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.LogoutEvent;
|
||||
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
|
||||
import org.bigbluebutton.main.events.UserJoinedEvent;
|
||||
import org.bigbluebutton.main.events.UserLeftEvent;
|
||||
import org.bigbluebutton.main.model.users.events.BroadcastStartedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.BroadcastStoppedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionCompletedEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionPageCountError;
|
||||
import org.bigbluebutton.modules.present.events.ConversionPageCountMaxed;
|
||||
import org.bigbluebutton.modules.present.events.ConversionSupportedDocEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionUnsupportedDocEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionUpdateEvent;
|
||||
import org.bigbluebutton.modules.present.events.CreatingThumbnailsEvent;
|
||||
import org.bigbluebutton.modules.present.events.GetListOfPresentationsReply;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertInvalidEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertSuccessEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadEvent;
|
||||
]]>
|
||||
</mx:Script>
|
||||
<!--
|
||||
@ -149,6 +150,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<MethodInvoker generator="{ExternalApiCalls}" method="handleOfficeDocConversionSuccess" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{OfficeDocConvertInvalidEvent.OFFICE_DOC_CONVERT_INVALID}" >
|
||||
<MethodInvoker generator="{ExternalApiCalls}" method="handleOfficeDocConversionInvalid" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{OfficeDocConvertFailedEvent.OFFICE_DOC_CONVERT_FAILED}" >
|
||||
<MethodInvoker generator="{ExternalApiCalls}" method="handleOfficeDocConversionFailed" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 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.events {
|
||||
import flash.events.Event;
|
||||
|
||||
public class ExitApplicationEvent extends Event {
|
||||
|
||||
public static const EXIT_APPLICATION:String = "EXIT_APPLICATION";
|
||||
|
||||
public function ExitApplicationEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = false) {
|
||||
super(type, bubbles, cancelable);
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ package org.bigbluebutton.main.model.users {
|
||||
import org.bigbluebutton.common.Role;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.model.Config;
|
||||
import org.bigbluebutton.core.model.MeetingModel;
|
||||
import org.bigbluebutton.core.vo.CameraSettingsVO;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
|
||||
@ -50,6 +51,8 @@ package org.bigbluebutton.main.model.users {
|
||||
|
||||
public var isBreakout:Boolean;
|
||||
|
||||
public var iAskedToLogout:Boolean
|
||||
|
||||
[Bindable]
|
||||
public var record:Boolean;
|
||||
|
||||
@ -197,8 +200,13 @@ package org.bigbluebutton.main.model.users {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function userIsModerator(userId:String):Boolean {
|
||||
var user:BBBUser = getUser(userId);
|
||||
return user != null && user.role == Role.MODERATOR;
|
||||
}
|
||||
|
||||
public function getPresenter():BBBUser {
|
||||
var p:BBBUser;
|
||||
for (var i:int = 0; i < users.length; i++) {
|
||||
@ -409,14 +417,19 @@ package org.bigbluebutton.main.model.users {
|
||||
}
|
||||
users.refresh();
|
||||
}
|
||||
|
||||
public function sharedWebcam(userId:String, stream:String):void {
|
||||
var aUser:BBBUser = getUser(userId);
|
||||
if (aUser != null) {
|
||||
aUser.sharedWebcam(stream)
|
||||
}
|
||||
users.refresh();
|
||||
}
|
||||
|
||||
public function sharedWebcam(userId:String, stream:String):void {
|
||||
var webcamsOnlyForModerator:Boolean = MeetingModel.getInstance().meeting.webcamsOnlyForModerator;
|
||||
if (!webcamsOnlyForModerator ||
|
||||
(webcamsOnlyForModerator && (amIModerator() || userIsModerator(userId)))
|
||||
) {
|
||||
var aUser:BBBUser = getUser(userId);
|
||||
if (aUser != null) {
|
||||
aUser.sharedWebcam(stream)
|
||||
}
|
||||
users.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public function unsharedWebcam(userId:String, stream:String):void {
|
||||
var aUser:BBBUser = getUser(userId);
|
||||
|
@ -18,7 +18,8 @@
|
||||
*/
|
||||
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;
|
||||
@ -27,13 +28,12 @@ package org.bigbluebutton.main.model.users
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.Responder;
|
||||
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;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.model.ConferenceParameters;
|
||||
|
@ -26,6 +26,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
x="168" y="86" layout="vertical" width="400" height="110" horizontalAlign="center">
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.managers.PopUpManager;
|
||||
@ -35,6 +37,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
import org.bigbluebutton.main.events.ExitApplicationEvent;
|
||||
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
@ -62,11 +65,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function exitApplication():void {
|
||||
if (!UserManager.getInstance().getConference().isBreakout) {
|
||||
navigateToURL(new URLRequest(BBB.getLogoutURL()), "_self");
|
||||
} else {
|
||||
ExternalInterface.call("window.close");
|
||||
}
|
||||
var d:Dispatcher = new Dispatcher();
|
||||
d.dispatchEvent(new ExitApplicationEvent(ExitApplicationEvent.EXIT_APPLICATION));
|
||||
}
|
||||
|
||||
private function handleComplete(e:Event):void {
|
||||
|
@ -51,6 +51,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mate:Listener type="{ConnectionFailedEvent.CONNECTION_CLOSED}" method="attemptReconnect" />
|
||||
<mate:Listener type="{ConnectionFailedEvent.UNKNOWN_REASON}" method="attemptReconnect" />
|
||||
<mate:Listener type="{ConnectionFailedEvent.CONNECTION_REJECTED}" method="attemptReconnect" />
|
||||
<mate:Listener type="{ExitApplicationEvent.EXIT_APPLICATION}" method="handleExitApplicationEvent" />
|
||||
<mate:Listener type="{ConfigLoadedEvent.CONFIG_LOADED_EVENT}" method="initOptions" />
|
||||
<mate:Listener type="{FlashMicSettingsEvent.FLASH_MIC_SETTINGS}" method="handleFlashMicSettingsEvent" />
|
||||
<mate:Listener type="{WebRTCEchoTestEvent.WEBRTC_ECHO_TEST_CONNECTING}" method="handleWebRTCEchoTestConnectingEvent" />
|
||||
@ -77,6 +78,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.TextEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.controls.Alert;
|
||||
@ -97,6 +99,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.common.events.OpenWindowEvent;
|
||||
import org.bigbluebutton.common.events.ToolbarButtonEvent;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
@ -105,6 +108,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.main.events.BreakoutRoomEvent;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.main.events.ConfigLoadedEvent;
|
||||
import org.bigbluebutton.main.events.ExitApplicationEvent;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.events.MeetingNotFoundEvent;
|
||||
import org.bigbluebutton.main.events.ModuleLoadEvent;
|
||||
@ -120,7 +124,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.users.views.BreakoutRoomSettings;
|
||||
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
|
||||
private static const LOGGER:ILogger = getClassLogger(MainApplicationShell);
|
||||
|
||||
@ -507,53 +510,64 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function handleMeetingNotFoundEvent(e:MeetingNotFoundEvent):void {
|
||||
showlogoutWindow(ResourceUtil.getInstance().getString('bbb.mainshell.meetingNotFound'));
|
||||
}
|
||||
|
||||
private function showlogoutWindow(reason:String):void {
|
||||
if (layoutOptions!= null && layoutOptions.showLogoutWindow) {
|
||||
if (logoutWindow != null) return;
|
||||
logoutWindow = LoggedOutWindow(PopUpManager.createPopUp( mdiCanvas, LoggedOutWindow, true));
|
||||
|
||||
var point1:Point = new Point();
|
||||
// Calculate position of TitleWindow in Application's coordinates.
|
||||
point1.x = width/2;
|
||||
point1.y = height/2;
|
||||
logoutWindow.x = point1.x - (logoutWindow.width/2);
|
||||
logoutWindow.y = point1.y - (logoutWindow.height/2);
|
||||
|
||||
logoutWindow.setReason(reason);
|
||||
mdiCanvas.removeAllPopUps();
|
||||
removeToolBars();
|
||||
} else {
|
||||
mdiCanvas.removeAllPopUps();
|
||||
removeToolBars();
|
||||
var pageHost:String = FlexGlobals.topLevelApplication.url.split("/")[0];
|
||||
var pageURL:String = FlexGlobals.topLevelApplication.url.split("/")[2];
|
||||
LOGGER.debug("SingOut to [{0}//{1}/bigbluebutton/api/signOut]", [pageHost, pageURL]);
|
||||
var request:URLRequest = new URLRequest(pageHost + "//" + pageURL + "/bigbluebutton/api/signOut");
|
||||
var urlLoader:URLLoader = new URLLoader();
|
||||
urlLoader.addEventListener(Event.COMPLETE, handleLogoutComplete);
|
||||
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLogoutError);
|
||||
urlLoader.load(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes toolbars from the display list.
|
||||
* Used only when the user completely logged out.
|
||||
*/
|
||||
private function removeToolBars():void{
|
||||
this.removeChild(toolbar);
|
||||
this.removeChild(controlBar);
|
||||
}
|
||||
|
||||
|
||||
private function handleLogout(e:ConnectionFailedEvent):void {
|
||||
if (e is ConnectionFailedEvent) {
|
||||
showlogoutWindow((e as ConnectionFailedEvent).type);
|
||||
}
|
||||
else showlogoutWindow("You have logged out of the conference");
|
||||
}
|
||||
|
||||
|
||||
private function showlogoutWindow(reason:String):void {
|
||||
if (layoutOptions!= null && layoutOptions.showLogoutWindow) {
|
||||
if (UserManager.getInstance().getConference().iAskedToLogout) {
|
||||
handleExitApplicationEvent();
|
||||
return;
|
||||
}
|
||||
if (logoutWindow != null) return;
|
||||
logoutWindow = LoggedOutWindow(PopUpManager.createPopUp( mdiCanvas, LoggedOutWindow, true));
|
||||
|
||||
var point1:Point = new Point();
|
||||
// Calculate position of TitleWindow in Application's coordinates.
|
||||
point1.x = width/2;
|
||||
point1.y = height/2;
|
||||
logoutWindow.x = point1.x - (logoutWindow.width/2);
|
||||
logoutWindow.y = point1.y - (logoutWindow.height/2);
|
||||
|
||||
logoutWindow.setReason(reason);
|
||||
mdiCanvas.removeAllPopUps();
|
||||
removeToolBars();
|
||||
} else {
|
||||
mdiCanvas.removeAllPopUps();
|
||||
removeToolBars();
|
||||
var pageHost:String = FlexGlobals.topLevelApplication.url.split("/")[0];
|
||||
var pageURL:String = FlexGlobals.topLevelApplication.url.split("/")[2];
|
||||
LOGGER.debug("SingOut to [{0}//{1}/bigbluebutton/api/signOut]", [pageHost, pageURL]);
|
||||
var request:URLRequest = new URLRequest(pageHost + "//" + pageURL + "/bigbluebutton/api/signOut");
|
||||
var urlLoader:URLLoader = new URLLoader();
|
||||
urlLoader.addEventListener(Event.COMPLETE, handleLogoutComplete);
|
||||
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLogoutError);
|
||||
urlLoader.load(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes toolbars from the display list.
|
||||
* Used only when the user completely logged out.
|
||||
*/
|
||||
private function removeToolBars():void {
|
||||
this.removeChild(toolbar);
|
||||
this.removeChild(controlBar);
|
||||
}
|
||||
|
||||
private function handleLogout(e:ConnectionFailedEvent):void {
|
||||
if (e is ConnectionFailedEvent) {
|
||||
showlogoutWindow((e as ConnectionFailedEvent).type);
|
||||
} else
|
||||
showlogoutWindow("You have logged out of the conference");
|
||||
}
|
||||
|
||||
private function handleExitApplicationEvent(e:ExitApplicationEvent = null):void {
|
||||
if (!UserManager.getInstance().getConference().isBreakout) {
|
||||
navigateToURL(new URLRequest(BBB.getLogoutURL()), "_self");
|
||||
} else {
|
||||
ExternalInterface.call("window.close");
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToLogoutUrl ():void {
|
||||
var logoutURL:String = BBB.getLogoutURL();
|
||||
var request:URLRequest = new URLRequest(logoutURL);
|
||||
@ -591,12 +605,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function handleWebRTCCallEndedEvent(e:WebRTCCallEvent):void {
|
||||
lblWebRTC.visible = lblWebRTC.includeInLayout = false;
|
||||
}
|
||||
|
||||
private function handleInvalidAuthToken(event:InvalidAuthTokenEvent):void {
|
||||
showlogoutWindow(ResourceUtil.getInstance().getString('bbb.mainshell.invalidAuthToken'));
|
||||
globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT));
|
||||
}
|
||||
|
||||
|
||||
private function handleInvalidAuthToken(event:InvalidAuthTokenEvent):void {
|
||||
showlogoutWindow(ResourceUtil.getInstance().getString('bbb.mainshell.invalidAuthToken'));
|
||||
globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT));
|
||||
}
|
||||
|
||||
private function handleRemoveToolbarComponent(event:ToolbarButtonEvent):void {
|
||||
if (addedBtns.contains(event.button as UIComponent))
|
||||
addedBtns.removeChild(event.button as UIComponent);
|
||||
|
@ -54,6 +54,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.common.events.ToolbarButtonEvent;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ConfigEvent;
|
||||
import org.bigbluebutton.main.events.LogoutEvent;
|
||||
@ -238,6 +239,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function alertLogout(e:CloseEvent):void {
|
||||
// Check to see if the YES button was pressed.
|
||||
if (e.detail==Alert.YES) {
|
||||
UserManager.getInstance().getConference().iAskedToLogout = true;
|
||||
/*
|
||||
* If doLogout() is called immediately there is a null exception in AlertAccImpl
|
||||
* line 185, but if we delay calling doLogout() until the next frame the Alert
|
||||
|
@ -782,7 +782,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mx:HBox id="timerBox" styleName="breakoutRoomTimerBox"
|
||||
includeInLayout="false" visible="false"
|
||||
width="100%" height="0">
|
||||
<mx:Label id="timerLabel" text="{ResourceUtil.getInstance().getString('bbb.users.breakout.calculatingRemainingTime')}"/>
|
||||
<mx:Label id="timerLabel"
|
||||
text="{ResourceUtil.getInstance().getString('bbb.users.breakout.calculatingRemainingTime')}"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.users.breakout.timer.toolTip')}"/>
|
||||
</mx:HBox>
|
||||
</mx:VBox>
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.bigbluebutton.modules.present.events {
|
||||
import flash.events.Event;
|
||||
|
||||
public class OfficeDocConvertInvalidEvent extends Event {
|
||||
public static const OFFICE_DOC_CONVERT_INVALID:String = "presentation office doc convert aborted event";
|
||||
|
||||
public function OfficeDocConvertInvalidEvent() {
|
||||
super(OFFICE_DOC_CONVERT_INVALID, true, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,8 @@ package org.bigbluebutton.modules.present.services
|
||||
public class Constants
|
||||
{
|
||||
public static const OFFICE_DOC_CONVERSION_SUCCESS_KEY:String = "OFFICE_DOC_CONVERSION_SUCCESS";
|
||||
public static const OFFICE_DOC_CONVERSION_FAILED_KEY:String = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static const OFFICE_DOC_CONVERSION_FAILED_KEY:String = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static const OFFICE_DOC_CONVERSION_INVALID_KEY:String = "OFFICE_DOC_CONVERSION_INVALID";
|
||||
public static const SUPPORTED_DOCUMENT_KEY:String = "SUPPORTED_DOCUMENT";
|
||||
public static const UNSUPPORTED_DOCUMENT_KEY:String = "UNSUPPORTED_DOCUMENT";
|
||||
public static const PAGE_COUNT_FAILED_KEY:String = "PAGE_COUNT_FAILED";
|
||||
|
@ -34,6 +34,7 @@ package org.bigbluebutton.modules.present.services.messaging
|
||||
import org.bigbluebutton.modules.present.events.ConversionUpdateEvent;
|
||||
import org.bigbluebutton.modules.present.events.CreatingThumbnailsEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertInvalidEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertSuccessEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadEvent;
|
||||
import org.bigbluebutton.modules.present.model.PresentationModel;
|
||||
@ -243,6 +244,9 @@ package org.bigbluebutton.modules.present.services.messaging
|
||||
case Constants.OFFICE_DOC_CONVERSION_FAILED_KEY :
|
||||
dispatcher.dispatchEvent(new OfficeDocConvertFailedEvent());
|
||||
break;
|
||||
case Constants.OFFICE_DOC_CONVERSION_INVALID_KEY :
|
||||
dispatcher.dispatchEvent(new OfficeDocConvertInvalidEvent());
|
||||
break;
|
||||
case Constants.SUPPORTED_DOCUMENT_KEY :
|
||||
dispatcher.dispatchEvent(new ConversionSupportedDocEvent());
|
||||
break;
|
||||
|
@ -33,8 +33,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mate:Listener type="{ConversionCompletedEvent.CONVERSION_COMPLETED}" method="handleConversionCompleted" />
|
||||
<mate:Listener type="{ConversionUpdateEvent.CONVERSION_UPDATE}" method="handleConvertUpdate" />
|
||||
<mate:Listener type="{CreatingThumbnailsEvent.CREATING_THUMBNAILS}" method="handleThumbnailsProgressEvent" />
|
||||
<mate:Listener type="{OfficeDocConvertFailedEvent.OFFICE_DOC_CONVERT_FAILED}" method="handleOfficeDocumentConversionFailed"/>
|
||||
<mate:Listener type="{OfficeDocConvertSuccessEvent.OFFICE_DOC_CONVERT_SUCCESS}" method="handleOfficeDocumentConversionSuccess"/>
|
||||
<mate:Listener type="{OfficeDocConvertFailedEvent.OFFICE_DOC_CONVERT_FAILED}" method="handleOfficeDocumentConversionFailed"/>
|
||||
<mate:Listener type="{OfficeDocConvertInvalidEvent.OFFICE_DOC_CONVERT_INVALID}" method="handleOfficeDocumentConversionInvalid"/>
|
||||
<mate:Listener type="{OfficeDocConvertSuccessEvent.OFFICE_DOC_CONVERT_SUCCESS}" method="handleOfficeDocumentConversionSuccess"/>
|
||||
<mate:Listener type="{ConversionSupportedDocEvent.SUPPORTED_DOC}" method="handleSupportedDocument"/>
|
||||
<mate:Listener type="{ConversionUnsupportedDocEvent.UNSUPPORTED_DOC}" method="handleUnsupportedDocument"/>
|
||||
<mate:Listener type="{ConversionPageCountError.PAGE_COUNT_ERROR}" method="handlePageCountFailed"/>
|
||||
@ -44,10 +45,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<![CDATA[
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.utils.StringUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
|
||||
import org.as3commons.lang.StringUtils;
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.Images;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.modules.present.commands.UploadFileCommand;
|
||||
import org.bigbluebutton.modules.present.events.ConversionCompletedEvent;
|
||||
import org.bigbluebutton.modules.present.events.ConversionPageCountError;
|
||||
@ -57,6 +60,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.present.events.ConversionUpdateEvent;
|
||||
import org.bigbluebutton.modules.present.events.CreatingThumbnailsEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertInvalidEvent;
|
||||
import org.bigbluebutton.modules.present.events.OfficeDocConvertSuccessEvent;
|
||||
import org.bigbluebutton.modules.present.events.RemovePresentationEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadCompletedEvent;
|
||||
@ -203,7 +207,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function handleOfficeDocumentConversionFailed(e:OfficeDocConvertFailedEvent):void {
|
||||
enableControls();
|
||||
displayAlert(ResourceUtil.getInstance().getString('bbb.presentation.error.document.convert.failed'));
|
||||
}
|
||||
}
|
||||
|
||||
private function handleOfficeDocumentConversionInvalid(e:OfficeDocConvertInvalidEvent):void {
|
||||
enableControls();
|
||||
displayAlert(ResourceUtil.getInstance().getString('bbb.presentation.error.document.convert.invalid'));
|
||||
}
|
||||
|
||||
private function handleOfficeDocumentConversionSuccess(e:OfficeDocConvertSuccessEvent):void {
|
||||
progressBar.label = ResourceUtil.getInstance().getString('bbb.presentation.document.converted');
|
||||
@ -248,7 +257,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function displayAlert(error:String, message:String = null):void {
|
||||
var okLabel:String = ResourceUtil.getInstance().getString('bbb.presentation.ok');
|
||||
progressBar.setStyle("color", 0xFF0000);
|
||||
progressBar.label = error + message;
|
||||
progressBar.label = error;
|
||||
if (!StringUtils.isEmpty(message)) {
|
||||
progressBar.label += message;
|
||||
}
|
||||
okCancelBtn.label = "Ok";
|
||||
}
|
||||
|
||||
|
@ -548,18 +548,11 @@ package org.bigbluebutton.modules.users.services
|
||||
private function handleEmojiStatusHand(msg: Object): void {
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
UserManager.getInstance().getConference().emojiStatus(map.userId, map.emojiStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleUserSharedWebcam(msg:Object):void {
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
if (!MeetingModel.getInstance().meeting.webcamsOnlyForModerator) {
|
||||
UserManager.getInstance().getConference().sharedWebcam(map.userId, map.webcamStream);
|
||||
} else if (
|
||||
UserManager.getInstance().getConference().amIModerator() ||
|
||||
(UserManager.getInstance().getConference().getUser(map.userId) != null && UserManager.getInstance().getConference().getUser(map.userId).role == Role.MODERATOR)
|
||||
) {
|
||||
UserManager.getInstance().getConference().sharedWebcam(map.userId, map.webcamStream);
|
||||
}
|
||||
UserManager.getInstance().getConference().sharedWebcam(map.userId, map.webcamStream);
|
||||
}
|
||||
|
||||
private function handleUserUnsharedWebcam(msg: Object):void {
|
||||
|
@ -140,10 +140,12 @@
|
||||
|
||||
amIModerator = UserManager.getInstance().getConference().amIModerator();
|
||||
amIPresenter = UserManager.getInstance().getConference().amIPresenter;
|
||||
|
||||
|
||||
settingsBtn.visible = settingsBtn.includeInLayout = partOptions.enableSettingsButton && amIModerator;
|
||||
closeRoomsBtn.visible = closeRoomsBtn.includeInLayout = amIModerator;
|
||||
|
||||
|
||||
emojiStatusBtn.visible = emojiStatusBtn.includeInLayout = partOptions.enableEmojiStatus;
|
||||
|
||||
BindingUtils.bindSetter(updateNumberofUsers, users, "length");
|
||||
|
||||
this.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
|
||||
@ -206,10 +208,6 @@
|
||||
resourcesChanged();
|
||||
}
|
||||
|
||||
private function changeButtons(presenter:Boolean):void {
|
||||
emojiStatusBtn.visible = emojiStatusBtn.includeInLayout = partOptions.enableEmojiStatus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Work around for a bug with the users grid. When you click on one of the buttons in an item renderer the client
|
||||
* locks up briefly and any mouse movements while the client is locked up are ignored. This means that roll outs
|
||||
@ -602,6 +600,15 @@
|
||||
private function breakoutRoomNameLabelFunction(item:Object, column:DataGridColumn) : String {
|
||||
return ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room') + " " + item.sequence;
|
||||
}
|
||||
|
||||
private function breakoutRoomsToolTip(item:Object):String {
|
||||
var room:BreakoutRoom = item as BreakoutRoom;
|
||||
var names:Array = [];
|
||||
for (var i:int = 0; i < room.users.length; i++) {
|
||||
names.push(room.users.getItemAt(i)["name"]);
|
||||
}
|
||||
return names.join("\n");
|
||||
}
|
||||
]]>
|
||||
</mx:Script>
|
||||
|
||||
@ -629,17 +636,24 @@
|
||||
width="100%" height="180">
|
||||
<mx:HBox width="100%">
|
||||
<mx:Label styleName="breakoutRoomUserWindowHeadingStyle" text="{ResourceUtil.getInstance().getString('bbb.users.breakout.breakoutRooms')}"/>
|
||||
<mx:Label styleName="breakoutRoomUserWindowHeadingStyle" width="100%" textAlign="right" id="breakoutTimeLabel" text="..."/>
|
||||
<mx:Label styleName="breakoutRoomUserWindowHeadingStyle" width="100%" textAlign="right" id="breakoutTimeLabel"
|
||||
text="..." toolTip="{ResourceUtil.getInstance().getString('bbb.users.breakout.timer.toolTip')}"/>
|
||||
</mx:HBox>
|
||||
|
||||
<mx:DataGrid id="roomsGrid" editable="false" sortableColumns="false" dataProvider="{breakoutRoomsList}"
|
||||
<mx:DataGrid id="roomsGrid" editable="false" sortableColumns="false"
|
||||
dataProvider="{breakoutRoomsList}" dataTipFunction="breakoutRoomsToolTip"
|
||||
dragEnabled="false" width="100%" height="100%" draggableColumns="false"
|
||||
accessibilityName="{ResourceUtil.getInstance().getString('bbb.users.breakout.breakoutRooms')}">
|
||||
<mx:columns>
|
||||
<mx:DataGridColumn labelFunction="breakoutRoomNameLabelFunction" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room')}" />
|
||||
<mx:DataGridColumn dataField="numberOfUsers" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.users')}"/>
|
||||
<mx:DataGridColumn dataField="meetingId" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}"
|
||||
<mx:DataGridColumn labelFunction="breakoutRoomNameLabelFunction"
|
||||
showDataTips="true"
|
||||
headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room')}" />
|
||||
<mx:DataGridColumn dataField="numberOfUsers"
|
||||
showDataTips="true"
|
||||
headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.users')}"/>
|
||||
<mx:DataGridColumn dataField="meetingId"
|
||||
visible="{amIModerator}"
|
||||
headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}"
|
||||
itemRenderer="org.bigbluebutton.modules.users.views.RoomActionsRenderer"/>
|
||||
</mx:columns>
|
||||
</mx:DataGrid>
|
||||
@ -649,12 +663,11 @@
|
||||
</mx:VBox>
|
||||
|
||||
<mx:ControlBar width="100%">
|
||||
<mx:Button id="emojiStatusBtn" icon="{images.emoji_raiseHand}" width="30" height="30"
|
||||
<mx:Button id="emojiStatusBtn" icon="{images.emoji_happy}" width="30" height="30"
|
||||
accessibilityName="{ResourceUtil.getInstance().getString('bbb.users.emojiStatusBtn.toolTip')}"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.users.emojiStatusBtn.toolTip')}" click="openEmojiStatusMenu()"
|
||||
visible="true" />
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.users.emojiStatusBtn.toolTip')}" click="openEmojiStatusMenu()" />
|
||||
<mx:Button id="settingsBtn" icon="{images.users_settings}" width="30" height="30"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.users.settings.buttonTooltip')}" click="openSettings()" visible="true" />
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.users.settings.buttonTooltip')}" click="openSettings()" />
|
||||
<mx:VBox>
|
||||
<mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.roomMuted.text')}" visible="{roomMuted}" includeInLayout="{roomMuted}" />
|
||||
<mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.roomLocked.text')}" visible="{roomLocked}" includeInLayout="{roomLocked}" />
|
||||
|
@ -106,7 +106,20 @@ function joinVoiceCallSIP(options) {
|
||||
turn: m.turns,
|
||||
};
|
||||
|
||||
callIntoConference(extension, function () {}, options.isListenOnly, st);
|
||||
callIntoConference(extension, function (audio) {
|
||||
switch (audio.status) {
|
||||
case 'failed':
|
||||
let audioFailed = new CustomEvent('bbb.webrtc.failed', {
|
||||
status: 'Failed' });
|
||||
window.dispatchEvent(audioFailed);
|
||||
break;
|
||||
case 'mediafail':
|
||||
let mediaFailed = new CustomEvent('bbb.webrtc.mediaFailed', {
|
||||
status: 'MediaFailed' });
|
||||
window.dispatchEvent(mediaFailed);
|
||||
break;
|
||||
}
|
||||
}, options.isListenOnly, st);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { appendMessageHeader } from '/imports/api/common/server/helpers';
|
||||
Meteor.methods({
|
||||
//meetingId: the meeting where the user is
|
||||
//newPresenterId: the userid of the new presenter
|
||||
//requesterSetPresenter: the userid of the user that wants to change the presenter
|
||||
//requesterUserId: the userid of the user that wants to change the presenter
|
||||
//newPresenterName: user name of the new presenter
|
||||
//authToken: the authToken of the user that wants to kick
|
||||
setUserPresenter(
|
||||
@ -13,7 +13,7 @@ Meteor.methods({
|
||||
newPresenterId,
|
||||
newPresenterName) {
|
||||
const REDIS_CONFIG = Meteor.settings.redis;
|
||||
const { meetingId, requesterSetPresenter, requesterToken } = credentials;
|
||||
const { meetingId, requesterUserId } = credentials;
|
||||
let message;
|
||||
if (isAllowedTo('setPresenter', credentials)) {
|
||||
message = {
|
||||
@ -21,7 +21,7 @@ Meteor.methods({
|
||||
new_presenter_id: newPresenterId,
|
||||
new_presenter_name: newPresenterName,
|
||||
meeting_id: meetingId,
|
||||
assigned_by: requesterSetPresenter,
|
||||
assigned_by: requesterUserId,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -81,6 +81,8 @@
|
||||
"app.actionsBar.emojiMenu.thumbsupDesc": "Change your status to thumbs up",
|
||||
"app.actionsBar.emojiMenu.thumbsdownLabel": "Thumbs down",
|
||||
"app.actionsBar.emojiMenu.thumbsdownDesc": "Change your status to thumbs down",
|
||||
"app.audioNotification.audioFailedMessage": "Your audio connection failed to connect. Try again.",
|
||||
"app.audioNotification.mediaFailedMessage": "getUserMicMedia failed, Only secure origins are allowed",
|
||||
"app.breakoutJoinConfirmation.title": "Join Breakout Room",
|
||||
"app.breakoutJoinConfirmation.message": "Do you want to join",
|
||||
"app.breakoutJoinConfirmation.confirmLabel": "Join",
|
||||
|
@ -27,6 +27,8 @@ const moderator = {
|
||||
// muting
|
||||
muteSelf: true,
|
||||
unmuteSelf: true,
|
||||
muteOther: true,
|
||||
unmuteOther: true,
|
||||
|
||||
logoutSelf: true,
|
||||
|
||||
@ -76,9 +78,9 @@ const viewer = function (meetingId, userId) {
|
||||
// muting
|
||||
muteSelf: true,
|
||||
unmuteSelf:
|
||||
!((meeting = Meetings.findOne({ meetingId: meetingId })) != null &&
|
||||
!((meeting = Meetings.findOne({ meetingId })) != null &&
|
||||
meeting.roomLockSettings.disableMic) ||
|
||||
!((user = Users.findOne({ meetingId: meetingId, userId: userId })) != null &&
|
||||
!((user = Users.findOne({ meetingId, userId })) != null &&
|
||||
user.user.locked),
|
||||
|
||||
logoutSelf: true,
|
||||
@ -88,15 +90,15 @@ const viewer = function (meetingId, userId) {
|
||||
subscribeChat: true,
|
||||
|
||||
//chat
|
||||
chatPublic: !((meeting = Meetings.findOne({ meetingId: meetingId })) != null &&
|
||||
chatPublic: !((meeting = Meetings.findOne({ meetingId })) != null &&
|
||||
meeting.roomLockSettings.disablePublicChat) ||
|
||||
!((user = Users.findOne({ meetingId: meetingId, userId: userId })) != null &&
|
||||
!((user = Users.findOne({ meetingId, userId })) != null &&
|
||||
user.user.locked) ||
|
||||
(user != null && user.user.presenter),
|
||||
|
||||
chatPrivate: !((meeting = Meetings.findOne({ meetingId: meetingId })) != null &&
|
||||
chatPrivate: !((meeting = Meetings.findOne({ meetingId })) != null &&
|
||||
meeting.roomLockSettings.disablePrivateChat) ||
|
||||
!((user = Users.findOne({ meetingId: meetingId, userId: userId })) != null &&
|
||||
!((user = Users.findOne({ meetingId, userId })) != null &&
|
||||
user.user.locked) ||
|
||||
(user != null && user.user.presenter),
|
||||
|
||||
@ -120,70 +122,42 @@ export function isAllowedTo(action, credentials) {
|
||||
const userId = credentials.requesterUserId;
|
||||
const authToken = credentials.requesterToken;
|
||||
|
||||
let user;
|
||||
let validated;
|
||||
|
||||
user = Users.findOne({
|
||||
meetingId: meetingId,
|
||||
userId: userId,
|
||||
});
|
||||
if (user != null) {
|
||||
validated = user.validated;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`in isAllowedTo: action-${action}, userId=${userId}, ` +
|
||||
`authToken=${authToken} validated:${validated}`
|
||||
);
|
||||
user = Users.findOne({
|
||||
meetingId: meetingId,
|
||||
userId: userId,
|
||||
const user = Users.findOne({
|
||||
meetingId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// logger.info "user=" + JSON.stringify user
|
||||
if ((user != null) && authToken === user.authToken) { // check if the user is who he claims to be
|
||||
if (user.validated && user.clientType === 'HTML5') {
|
||||
const allowedToInitiateRequest =
|
||||
null != user &&
|
||||
authToken === user.authToken &&
|
||||
user.validated &&
|
||||
'HTML5' === user.clientType &&
|
||||
null != user.user;
|
||||
|
||||
// PRESENTER
|
||||
// check presenter specific actions or fallback to regular viewer actions
|
||||
if (user.user != null && user.user.presenter) {
|
||||
logger.info('user permissions presenter case');
|
||||
return presenter[action] || viewer(meetingId, userId)[action] || false;
|
||||
if (allowedToInitiateRequest) {
|
||||
let result = false;
|
||||
|
||||
// VIEWER
|
||||
} else if (user.user != null && user.user.role === 'VIEWER') {
|
||||
logger.info('user permissions viewer case');
|
||||
return viewer(meetingId, userId)[action] || false;
|
||||
|
||||
// MODERATOR
|
||||
} else if (user.user != null && user.user.role === 'MODERATOR') {
|
||||
logger.info('user permissions moderator case');
|
||||
return moderator[action] || false;
|
||||
} else {
|
||||
logger.warn(`UNSUCCESSFULL ATTEMPT FROM userid=${userId} to perform:${action}`);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// user was not validated
|
||||
if (action === 'logoutSelf') {
|
||||
// on unsuccessful sign-in
|
||||
logger.warn(
|
||||
'a user was successfully removed from the ' +
|
||||
'meeting following an unsuccessful login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// check role specific actions
|
||||
if ('MODERATOR' === user.user.role) {
|
||||
logger.debug('user permissions moderator case');
|
||||
result = result || moderator[action];
|
||||
} else if ('VIEWER' === user.user.role) {
|
||||
logger.debug('user permissions viewer case');
|
||||
result = result || viewer(meetingId, userId)[action];
|
||||
}
|
||||
} else {
|
||||
logger.error(
|
||||
`in meetingId=${meetingId} userId=${userId} tried to perform ${action} ` +
|
||||
`without permission${'\n..while the authToken was ' +
|
||||
(user != null && user.authToken != null ? user.authToken : void 0) +
|
||||
" and the user's object is " + (JSON.stringify(user))}`
|
||||
);
|
||||
|
||||
// check presenter actions
|
||||
if (user.user.presenter) {
|
||||
logger.debug('user permissions presenter case');
|
||||
result = result || presenter[action];
|
||||
}
|
||||
|
||||
logger.debug(`attempt from userId=${userId} to perform:${action}, allowed=${result}`);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
logger.error(`FAILED due to permissions:${action} ${JSON.stringify(credentials)}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -5,12 +5,16 @@ import styles from '../styles.scss';
|
||||
export default class MuteAudio extends React.Component {
|
||||
|
||||
render() {
|
||||
const { isInAudio, isMuted, callback } = this.props;
|
||||
const { isInAudio, isMuted, callback, isTalking} = this.props;
|
||||
let label = !isMuted ? 'Mute' : 'Unmute';
|
||||
let icon = !isMuted ? 'audio-off' : 'audio';
|
||||
let icon = !isMuted ? 'audio' : 'audio-off';
|
||||
let className = !isInAudio ? styles.invisible : null;
|
||||
let tabIndex = !isInAudio ? -1 : 0;
|
||||
|
||||
if (isInAudio && isTalking) {
|
||||
className = styles.circleGlow;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={callback}
|
||||
|
@ -19,6 +19,8 @@ export default createContainer((params) => {
|
||||
const user = Users.findOne({ userId: userId }).user;
|
||||
const isMuted = user.voiceUser.muted;
|
||||
const isInAudio = user.voiceUser.joined;
|
||||
const isTalking = user.voiceUser.talking;
|
||||
|
||||
let callback = () => {};
|
||||
|
||||
if (isInAudio && !isMuted) {
|
||||
@ -33,6 +35,7 @@ export default createContainer((params) => {
|
||||
isInAudio,
|
||||
isMuted,
|
||||
callback,
|
||||
isTalking,
|
||||
};
|
||||
return data;
|
||||
}, MuteAudioContainer);
|
||||
|
@ -30,3 +30,7 @@
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.circleGlow > :first-child{
|
||||
box-shadow: 0 0 .15rem #FFF !important;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import LoadingScreen from '../loading-screen/component';
|
||||
import KickedScreen from '../kicked-screen/component';
|
||||
|
||||
import NotificationsBarContainer from '../notifications-bar/container';
|
||||
import AudioNotificationContainer from '../audio-notification/container';
|
||||
|
||||
import LocalStorage from '/imports/ui/services/storage/local.js';
|
||||
|
||||
@ -200,6 +201,7 @@ export default class App extends Component {
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<AudioNotificationContainer />
|
||||
<NotificationsBarContainer />
|
||||
<section className={styles.wrapper}>
|
||||
{this.renderUserList()}
|
||||
|
@ -53,6 +53,7 @@ export default class Audio extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ModalBase
|
||||
isTransparent={true}
|
||||
isOpen={true}
|
||||
onHide={null}
|
||||
onShow={null}
|
||||
|
56
bigbluebutton-html5/imports/ui/components/audio-notification/component.jsx
Executable file
56
bigbluebutton-html5/imports/ui/components/audio-notification/component.jsx
Executable file
@ -0,0 +1,56 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import styles from './styles.scss';
|
||||
import cx from 'classnames';
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
|
||||
const COLORS = [
|
||||
'default', 'primary', 'danger', 'success',
|
||||
];
|
||||
|
||||
const propTypes = {
|
||||
color: PropTypes.oneOf(COLORS),
|
||||
message: PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
color: 'default',
|
||||
};
|
||||
|
||||
export default class AudioNotification extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
this.props.handleClose();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { color, message } = this.props;
|
||||
|
||||
if(!color || !message ){
|
||||
return null;
|
||||
}else{
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
className={cx(styles.audioNotifications, styles[this.props.color])}>
|
||||
{message}
|
||||
<Button className={styles.closeBtn}
|
||||
label={'Close'}
|
||||
icon={'close'}
|
||||
size={'sm'}
|
||||
circle={true}
|
||||
hideLabel={true}
|
||||
onClick={this.handleClose}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioNotification.propTypes = propTypes;
|
||||
AudioNotification.defaultProps = defaultProps;
|
81
bigbluebutton-html5/imports/ui/components/audio-notification/container.jsx
Executable file
81
bigbluebutton-html5/imports/ui/components/audio-notification/container.jsx
Executable file
@ -0,0 +1,81 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { createContainer } from 'meteor/react-meteor-data';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import AudioNotification from './component';
|
||||
import styles from './styles.scss';
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
audioFailed: {
|
||||
id: 'app.audioNotification.audioFailedMessage',
|
||||
description: 'The audio could not connect, Try again',
|
||||
},
|
||||
mediaFailed: {
|
||||
id: 'app.audioNotification.mediaFailedMessage',
|
||||
description: 'Could not access getUserMicMedia, Try again',
|
||||
},
|
||||
});
|
||||
|
||||
class AudioNotificationContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.color = null;
|
||||
this.message = null;
|
||||
|
||||
this.state = {
|
||||
status: null,
|
||||
}
|
||||
|
||||
this.handleAudioFailure = this.handleAudioFailure.bind(this);
|
||||
this.handleMediaFailure = this.handleMediaFailure.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("bbb.webrtc.failed", this.handleAudioFailure);
|
||||
window.addEventListener("bbb.webrtc.mediaFailed", this.handleMediaFailure);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("bbb.webrtc.failed", this.handleAudioFailure);
|
||||
window.removeEventListener("bbb.webrtc.mediaFailed", this.handleMediaFailure);
|
||||
}
|
||||
|
||||
handleClose(){
|
||||
this.color = null;
|
||||
this.message = null;
|
||||
this.setState({status: null});
|
||||
}
|
||||
|
||||
handleAudioFailure() {
|
||||
this.message = this.props.audioFailure;
|
||||
this.setState({status: 'failed'});
|
||||
}
|
||||
|
||||
handleMediaFailure() {
|
||||
this.message = this.props.mediaFailure;
|
||||
this.setState({status: 'failed'});
|
||||
}
|
||||
|
||||
render() {
|
||||
const handleClose = this.handleClose;
|
||||
this.color = 'danger';
|
||||
|
||||
return(
|
||||
<AudioNotification
|
||||
color={this.color}
|
||||
message={this.message}
|
||||
handleClose={this.handleClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(createContainer(({ intl }) => {
|
||||
let messages = {};
|
||||
messages.audioFailure = intl.formatMessage(intlMessages.audioFailed);
|
||||
messages.mediaFailure = intl.formatMessage(intlMessages.mediaFailed);
|
||||
return messages;
|
||||
}, AudioNotificationContainer));
|
63
bigbluebutton-html5/imports/ui/components/audio-notification/styles.scss
Executable file
63
bigbluebutton-html5/imports/ui/components/audio-notification/styles.scss
Executable file
@ -0,0 +1,63 @@
|
||||
@import "../../stylesheets/variables/_all";
|
||||
|
||||
$nb-default-color: $color-gray;
|
||||
$nb-default-bg: $color-white;
|
||||
$nb-default-border: $color-white;
|
||||
|
||||
$nb-primary-color: $color-white;
|
||||
$nb-primary-bg: $color-primary;
|
||||
$nb-primary-border: $color-primary;
|
||||
|
||||
$nb-success-color: $color-white;
|
||||
$nb-success-bg: $color-success;
|
||||
$nb-success-border: $color-success;
|
||||
|
||||
$nb-danger-color: $color-white;
|
||||
$nb-danger-bg: $color-danger;
|
||||
$nb-danger-border: $color-danger;
|
||||
|
||||
.audioNotifications {
|
||||
padding: $line-height-computed / 2;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.closeBtn {
|
||||
position: absolute;
|
||||
right: 1.65em;
|
||||
top: .5em;
|
||||
}
|
||||
|
||||
// Modifies the close button style
|
||||
Button.closeBtn span:first-child {
|
||||
color: $color-gray-light;
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
@mixin nb-variant($color, $background, $border) {
|
||||
color: $color;
|
||||
background-color: $background;
|
||||
border-color: $border;
|
||||
}
|
||||
|
||||
.default {
|
||||
@include nb-variant($nb-default-color, $nb-default-bg, $nb-default-border);
|
||||
}
|
||||
|
||||
.primary {
|
||||
@include nb-variant($nb-primary-color, $nb-primary-bg, $nb-primary-border);
|
||||
}
|
||||
|
||||
.success {
|
||||
@include nb-variant($nb-success-color, $nb-success-bg, $nb-success-border);
|
||||
}
|
||||
|
||||
.danger {
|
||||
@include nb-variant($nb-danger-color, $nb-danger-bg, $nb-danger-border);
|
||||
}
|
@ -95,6 +95,7 @@ export default class Button extends BaseButton {
|
||||
} = this.props;
|
||||
|
||||
let propClassNames = {};
|
||||
|
||||
propClassNames[styles.button] = true;
|
||||
propClassNames[styles[size]] = true;
|
||||
propClassNames[styles[color]] = true;
|
||||
|
@ -259,7 +259,6 @@ $btn-jumbo-padding: $jumbo-padding-y $jumbo-padding-x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.circle {
|
||||
$btn-sm-padding-x: nth($btn-sm-padding, 2) / 2.75;
|
||||
$btn-md-padding-x: nth($btn-md-padding, 2) / 2.75;
|
||||
|
6
bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
Normal file → Executable file
6
bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
Normal file → Executable file
@ -7,6 +7,7 @@ const propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onShow: PropTypes.func,
|
||||
onHide: PropTypes.func,
|
||||
isTransparent: PropTypes.bool
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -41,12 +42,15 @@ export default class ModalBase extends Component {
|
||||
onShow,
|
||||
onHide,
|
||||
className,
|
||||
isTransparent,
|
||||
} = this.props;
|
||||
|
||||
let styleOverlay = (isTransparent) ? styles.transparentOverlay : styles.overlay;
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
className={cx(styles.modal, className)}
|
||||
overlayClassName={styles.overlay}
|
||||
overlayClassName={styleOverlay}
|
||||
portalClassName={styles.portal}
|
||||
isOpen={isOpen}
|
||||
onAfterOpen={this.handleAfterOpen}
|
||||
|
@ -20,11 +20,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
.transparentOverlay {
|
||||
background: transparentize($color-gray-dark, .40) !important;
|
||||
}
|
||||
|
||||
.overlay, .transparentOverlay {
|
||||
z-index: 1000;
|
||||
// background: transparentize($color-white, .35);
|
||||
background: #fff;
|
||||
// background: transparentize($color-gray-dark, .40);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -82,7 +82,6 @@ class NavBar extends Component {
|
||||
</div>
|
||||
<div className={styles.center}>
|
||||
{this.renderPresentationTitle()}
|
||||
<span className={styles.divider}></span>
|
||||
<RecordingIndicator beingRecorded={beingRecorded}/>
|
||||
</div>
|
||||
<div className={styles.right}>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import styles from './styles.scss';
|
||||
import cx from 'classnames';
|
||||
|
||||
export default class RecordingIndicator extends Component {
|
||||
constructor(props) {
|
||||
@ -9,11 +8,11 @@ export default class RecordingIndicator extends Component {
|
||||
|
||||
render() {
|
||||
const { beingRecorded } = this.props;
|
||||
let classNames = {};
|
||||
classNames[styles.indicator] = beingRecorded;
|
||||
|
||||
return (
|
||||
<span className={cx(classNames)}></span>
|
||||
);
|
||||
if (!beingRecorded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (<div className={styles.indicator}></div>);
|
||||
}
|
||||
};
|
||||
|
@ -1,12 +1,22 @@
|
||||
@import "../../../stylesheets/variables/_all";
|
||||
|
||||
.indicator {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: $font-size-base;
|
||||
height: $font-size-base;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $color-white;
|
||||
margin-left: $line-height-computed;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: calc( -1px - #{($line-height-computed / 2)});
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
|
@ -39,14 +39,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
background: $color-white;
|
||||
height: $font-size-base * 1.25;
|
||||
width: 1px;
|
||||
margin: 0 $line-height-computed / 2;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.btnWithNotificationDot {
|
||||
position: relative;
|
||||
|
||||
|
@ -28,11 +28,12 @@ export default class ApplicationMenu extends BaseMenu {
|
||||
<div className={styles.row} role='presentation'>
|
||||
<label>
|
||||
<input type='checkbox'
|
||||
tabIndex='7'
|
||||
onChange={this.checkBoxHandler.bind(this, "audioNotifChat")}
|
||||
checked={this.state.audioNotifChat}
|
||||
aria-labelledby='audioNotifLabel'
|
||||
aria-describedby='audioNotifDesc' />
|
||||
tabIndex='7'
|
||||
onChange={this.checkBoxHandler.bind(this, "audioNotifChat")}
|
||||
checked={this.state.audioNotifChat}
|
||||
aria-labelledby='audioNotifLabel'
|
||||
aria-describedby='audioNotifDesc'
|
||||
/>
|
||||
Audio notifications for chat
|
||||
</label>
|
||||
<div id='audioNotifLabel' hidden>Audio notifications</div>
|
||||
|
@ -224,27 +224,22 @@ const userActions = {
|
||||
},
|
||||
setPresenter: {
|
||||
label: 'Make Presenter',
|
||||
handler: user => callServer('setUserPresenter', user.userid, user.name),
|
||||
handler: user => callServer('setUserPresenter', user.id, user.name),
|
||||
icon: 'presentation',
|
||||
},
|
||||
promote: {
|
||||
label: 'Promote',
|
||||
handler: user => console.log('missing promote', user),
|
||||
icon: 'promote',
|
||||
},
|
||||
kick: {
|
||||
label: 'Kick User',
|
||||
handler: user => callServer('kickUser', user.userid),
|
||||
handler: user => callServer('kickUser', user.id),
|
||||
icon: 'kick-user',
|
||||
},
|
||||
mute: {
|
||||
label: 'Mute Audio',
|
||||
handler: user=> callServer('muteUser', Auth.userID),
|
||||
handler: user=> callServer('muteUser', user.id),
|
||||
icon: 'mute',
|
||||
},
|
||||
unmute: {
|
||||
label: 'Unmute Audio',
|
||||
handler: user=> callServer('unmuteUser', Auth.userID),
|
||||
handler: user=> callServer('unmuteUser', user.id),
|
||||
icon: 'unmute',
|
||||
},
|
||||
};
|
||||
|
@ -108,35 +108,29 @@ class UserListItem extends Component {
|
||||
openChat,
|
||||
clearStatus,
|
||||
setPresenter,
|
||||
promote,
|
||||
kick,
|
||||
mute,
|
||||
unmute,
|
||||
} = userActions;
|
||||
|
||||
let muteAudio, unmuteAudio;
|
||||
const hasAuthority = currentUser.isModerator || user.isCurrent;
|
||||
let allowedToChatPrivately = !user.isCurrent;
|
||||
let allowedToMuteAudio = hasAuthority && user.isVoiceUser && user.isMuted;
|
||||
let allowedToUnmuteAudio = hasAuthority && user.isVoiceUser && !user.isMuted;
|
||||
let allowedToResetStatus = hasAuthority;
|
||||
|
||||
// Check the state of joining the audio currently for current user
|
||||
if (user.isCurrent && user.isVoiceUser) {
|
||||
if (user.isMuted) {
|
||||
muteAudio = true;
|
||||
} else {
|
||||
unmuteAudio = true;
|
||||
}
|
||||
}
|
||||
// if currentUser is a moderator, allow kicking other users
|
||||
let allowedToKick = currentUser.isModerator && !user.isCurrent;
|
||||
|
||||
// if currentUser is a moderator or user is currently logged in,
|
||||
// can clear status from the userlist.
|
||||
let allowedToResetStatus = currentUser.isModerator || user.isCurrent ? true : false;
|
||||
let allowedToSetPresenter = (currentUser.isModerator || currentUser.isPresenter) && !user.isPresenter;
|
||||
|
||||
return _.compact([
|
||||
(!user.isCurrent ? this.renderUserAction(openChat, router, user) : null),
|
||||
(muteAudio ? this.renderUserAction(unmute, user) : null),
|
||||
(unmuteAudio ? this.renderUserAction(mute, user) : null),
|
||||
(allowedToChatPrivately ? this.renderUserAction(openChat, router, user) : null),
|
||||
(allowedToMuteAudio ? this.renderUserAction(unmute, user) : null),
|
||||
(allowedToUnmuteAudio ? this.renderUserAction(mute, user) : null),
|
||||
(allowedToResetStatus ? 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),
|
||||
(allowedToSetPresenter ? this.renderUserAction(setPresenter, user) : null),
|
||||
(allowedToKick ? this.renderUserAction(kick, user) : null),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -162,9 +156,6 @@ class UserListItem extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
user,
|
||||
currentUser,
|
||||
userActions,
|
||||
compact,
|
||||
} = this.props;
|
||||
|
||||
|
@ -22,10 +22,11 @@ dependencies {
|
||||
compile 'commons-codec:commons-codec:1.10'
|
||||
compile 'com.google.code.gson:gson:1.7.1'
|
||||
compile 'commons-httpclient:commons-httpclient:3.1'
|
||||
compile 'org.apache.poi:poi-ooxml:3.15'
|
||||
compile 'com.zaxxer:nuprocess:1.1.0'
|
||||
|
||||
compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT'
|
||||
|
||||
|
||||
// Logging
|
||||
// Commenting out as it results in build failure (ralam - may 11, 2014)
|
||||
//compile 'ch.qos.logback:logback-core:1.0.9@jar'
|
||||
@ -53,7 +54,7 @@ dependencies {
|
||||
compile 'commons-codec:commons-codec:1.3'
|
||||
compile 'commons-httpclient:commons-httpclient:3.1'
|
||||
compile 'commons-io:commons-io:1.4'
|
||||
compile 'com.artofsolving:jodconverter:2.2.1'
|
||||
compile 'com.artofsolving:jodconverter:2.2.1'
|
||||
|
||||
compile 'org.apache.geronimo.specs:geronimo-j2ee-connector_1.5_spec:1.0'
|
||||
compile 'org.openoffice:unoil:3.2.1'
|
||||
|
@ -31,7 +31,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="imageToSwfSlidesGenerationService" ref="imageToSwfSlidesGenerationService"/>
|
||||
</bean>
|
||||
|
||||
<bean id="officeToPdfConversionService" class="org.bigbluebutton.presentation.imp.OfficeToPdfConversionService"/>
|
||||
<bean id="officeDocumentValidator" class="org.bigbluebutton.presentation.imp.OfficeDocumentValidator"/>
|
||||
|
||||
<bean id="officeToPdfConversionService" class="org.bigbluebutton.presentation.imp.OfficeToPdfConversionService">
|
||||
<property name="officeDocumentValidator" ref="officeDocumentValidator"/>
|
||||
</bean>
|
||||
|
||||
<bean id="pageExtractor" class="org.bigbluebutton.presentation.imp.GhostscriptPageExtractor">
|
||||
<property name="ghostscriptExec" value="${ghostScriptExec}"/>
|
||||
|
@ -20,20 +20,19 @@
|
||||
package org.bigbluebutton.presentation;
|
||||
|
||||
public class ConversionMessageConstants {
|
||||
private ConversionMessageConstants() {}
|
||||
|
||||
public static final String OFFICE_DOC_CONVERSION_SUCCESS_KEY = "OFFICE_DOC_CONVERSION_SUCCESS";
|
||||
public static final String OFFICE_DOC_CONVERSION_FAILED_KEY = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static final String SUPPORTED_DOCUMENT_KEY = "SUPPORTED_DOCUMENT";
|
||||
public static final String UNSUPPORTED_DOCUMENT_KEY = "UNSUPPORTED_DOCUMENT";
|
||||
public static final String PAGE_COUNT_FAILED_KEY = "PAGE_COUNT_FAILED";
|
||||
public static final String PAGE_COUNT_EXCEEDED_KEY = "PAGE_COUNT_EXCEEDED";
|
||||
public static final String GENERATED_SLIDE_KEY = "GENERATED_SLIDE";
|
||||
public static final String GENERATING_THUMBNAIL_KEY = "GENERATING_THUMBNAIL";
|
||||
public static final String GENERATED_THUMBNAIL_KEY = "GENERATED_THUMBNAIL";
|
||||
public static final String GENERATING_TEXTFILES_KEY = "GENERATING_TEXTFILES";
|
||||
public static final String GENERATED_TEXTFILES_KEY = "GENERATED_TEXTFILES";
|
||||
public static final String GENERATING_SVGIMAGES_KEY = "GENERATING_SVGIMAGES";
|
||||
public static final String GENERATED_SVGIMAGES_KEY = "GENERATED_SVGIMAGES";
|
||||
public static final String CONVERSION_COMPLETED_KEY = "CONVERSION_COMPLETED";
|
||||
public static final String OFFICE_DOC_CONVERSION_SUCCESS_KEY = "OFFICE_DOC_CONVERSION_SUCCESS";
|
||||
public static final String OFFICE_DOC_CONVERSION_FAILED_KEY = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static final String OFFICE_DOC_CONVERSION_INVALID_KEY = "OFFICE_DOC_CONVERSION_INVALID";
|
||||
public static final String SUPPORTED_DOCUMENT_KEY = "SUPPORTED_DOCUMENT";
|
||||
public static final String UNSUPPORTED_DOCUMENT_KEY = "UNSUPPORTED_DOCUMENT";
|
||||
public static final String PAGE_COUNT_FAILED_KEY = "PAGE_COUNT_FAILED";
|
||||
public static final String PAGE_COUNT_EXCEEDED_KEY = "PAGE_COUNT_EXCEEDED";
|
||||
public static final String GENERATED_SLIDE_KEY = "GENERATED_SLIDE";
|
||||
public static final String GENERATING_THUMBNAIL_KEY = "GENERATING_THUMBNAIL";
|
||||
public static final String GENERATED_THUMBNAIL_KEY = "GENERATED_THUMBNAIL";
|
||||
public static final String GENERATING_TEXTFILES_KEY = "GENERATING_TEXTFILES";
|
||||
public static final String GENERATED_TEXTFILES_KEY = "GENERATED_TEXTFILES";
|
||||
public static final String GENERATING_SVGIMAGES_KEY = "GENERATING_SVGIMAGES";
|
||||
public static final String GENERATED_SVGIMAGES_KEY = "GENERATED_SVGIMAGES";
|
||||
public static final String CONVERSION_COMPLETED_KEY = "CONVERSION_COMPLETED";
|
||||
}
|
||||
|
@ -27,57 +27,64 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DocumentConversionServiceImp implements DocumentConversionService {
|
||||
private static Logger log = LoggerFactory.getLogger(DocumentConversionServiceImp.class);
|
||||
|
||||
private MessagingService messagingService;
|
||||
private OfficeToPdfConversionService officeToPdfConversionService;
|
||||
private PdfToSwfSlidesGenerationService pdfToSwfSlidesGenerationService;
|
||||
private ImageToSwfSlidesGenerationService imageToSwfSlidesGenerationService;
|
||||
|
||||
public void processDocument(UploadedPresentation pres) {
|
||||
SupportedDocumentFilter sdf = new SupportedDocumentFilter(messagingService);
|
||||
log.info("Start presentation conversion. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " name=" + pres.getName());
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(DocumentConversionServiceImp.class);
|
||||
|
||||
if (sdf.isSupported(pres)) {
|
||||
String fileType = pres.getFileType();
|
||||
|
||||
if (SupportedFileTypes.isOfficeFile(fileType)) {
|
||||
officeToPdfConversionService.convertOfficeToPdf(pres);
|
||||
OfficeToPdfConversionSuccessFilter ocsf = new OfficeToPdfConversionSuccessFilter(messagingService);
|
||||
if (ocsf.didConversionSucceed(pres)) {
|
||||
// Successfully converted to pdf. Call the process again, this time it should be handled by
|
||||
// the PDF conversion service.
|
||||
processDocument(pres);
|
||||
}
|
||||
} else if (SupportedFileTypes.isPdfFile(fileType)) {
|
||||
pdfToSwfSlidesGenerationService.generateSlides(pres);
|
||||
} else if (SupportedFileTypes.isImageFile(fileType)) {
|
||||
imageToSwfSlidesGenerationService.generateSlides(pres);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: error log
|
||||
}
|
||||
|
||||
log.info("End presentation conversion. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " name=" + pres.getName());
|
||||
private MessagingService messagingService;
|
||||
private OfficeToPdfConversionService officeToPdfConversionService;
|
||||
private PdfToSwfSlidesGenerationService pdfToSwfSlidesGenerationService;
|
||||
private ImageToSwfSlidesGenerationService imageToSwfSlidesGenerationService;
|
||||
|
||||
}
|
||||
|
||||
public void setMessagingService(MessagingService m) {
|
||||
messagingService = m;
|
||||
}
|
||||
|
||||
public void setOfficeToPdfConversionService(OfficeToPdfConversionService s) {
|
||||
officeToPdfConversionService = s;
|
||||
}
|
||||
|
||||
public void setPdfToSwfSlidesGenerationService(PdfToSwfSlidesGenerationService s) {
|
||||
pdfToSwfSlidesGenerationService = s;
|
||||
}
|
||||
|
||||
public void setImageToSwfSlidesGenerationService(ImageToSwfSlidesGenerationService s) {
|
||||
imageToSwfSlidesGenerationService = s;
|
||||
}
|
||||
public void processDocument(UploadedPresentation pres) {
|
||||
SupportedDocumentFilter sdf = new SupportedDocumentFilter(messagingService);
|
||||
log.info("Start presentation conversion. meetingId=" + pres.getMeetingId()
|
||||
+ " presId=" + pres.getId() + " name=" + pres.getName());
|
||||
|
||||
if (sdf.isSupported(pres)) {
|
||||
String fileType = pres.getFileType();
|
||||
|
||||
if (SupportedFileTypes.isOfficeFile(fileType)) {
|
||||
pres = officeToPdfConversionService.convertOfficeToPdf(pres);
|
||||
OfficeToPdfConversionSuccessFilter ocsf = new OfficeToPdfConversionSuccessFilter(
|
||||
messagingService);
|
||||
if (ocsf.didConversionSucceed(pres)) {
|
||||
// Successfully converted to pdf. Call the process again, this time it
|
||||
// should be handled by
|
||||
// the PDF conversion service.
|
||||
processDocument(pres);
|
||||
}
|
||||
} else if (SupportedFileTypes.isPdfFile(fileType)) {
|
||||
pdfToSwfSlidesGenerationService.generateSlides(pres);
|
||||
} else if (SupportedFileTypes.isImageFile(fileType)) {
|
||||
imageToSwfSlidesGenerationService.generateSlides(pres);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: error log
|
||||
}
|
||||
|
||||
log.info("End presentation conversion. meetingId=" + pres.getMeetingId()
|
||||
+ " presId=" + pres.getId() + " name=" + pres.getName());
|
||||
|
||||
}
|
||||
|
||||
public void setMessagingService(MessagingService m) {
|
||||
messagingService = m;
|
||||
}
|
||||
|
||||
public void setOfficeToPdfConversionService(OfficeToPdfConversionService s) {
|
||||
officeToPdfConversionService = s;
|
||||
}
|
||||
|
||||
public void setPdfToSwfSlidesGenerationService(
|
||||
PdfToSwfSlidesGenerationService s) {
|
||||
pdfToSwfSlidesGenerationService = s;
|
||||
}
|
||||
|
||||
public void setImageToSwfSlidesGenerationService(
|
||||
ImageToSwfSlidesGenerationService s) {
|
||||
imageToSwfSlidesGenerationService = s;
|
||||
}
|
||||
}
|
||||
|
@ -30,49 +30,58 @@ import org.slf4j.LoggerFactory;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class OfficeToPdfConversionSuccessFilter {
|
||||
private static Logger log = LoggerFactory.getLogger(OfficeToPdfConversionSuccessFilter.class);
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(OfficeToPdfConversionSuccessFilter.class);
|
||||
|
||||
private final MessagingService messagingService;
|
||||
|
||||
public OfficeToPdfConversionSuccessFilter(MessagingService m) {
|
||||
messagingService = m;
|
||||
}
|
||||
|
||||
public boolean didConversionSucceed(UploadedPresentation pres) {
|
||||
notifyProgressListener(pres);
|
||||
return pres.isLastStepSuccessful();
|
||||
}
|
||||
private final MessagingService messagingService;
|
||||
|
||||
private void notifyProgressListener(UploadedPresentation pres) {
|
||||
Map<String, Object> msg = new HashMap<String, Object>();
|
||||
msg.put("conference", pres.getMeetingId());
|
||||
msg.put("room", pres.getMeetingId());
|
||||
msg.put("returnCode", "CONVERT");
|
||||
msg.put("presentationId", pres.getId());
|
||||
msg.put("presentationName", pres.getId());
|
||||
msg.put("filename", pres.getName());
|
||||
|
||||
if (pres.isLastStepSuccessful()) {
|
||||
log.info("Notifying of OFFICE_DOC_CONVERSION_SUCCESS for " + pres.getUploadedFile().getAbsolutePath());
|
||||
msg.put("message", "Office document successfully converted.");
|
||||
msg.put("messageKey", "OFFICE_DOC_CONVERSION_SUCCESS");
|
||||
} else {
|
||||
log.info("Notifying of OFFICE_DOC_CONVERSION_FAILED for " + pres.getUploadedFile().getAbsolutePath());
|
||||
msg.put("message", "Failed to convert Office document.");
|
||||
msg.put("messageKey", "OFFICE_DOC_CONVERSION_FAILED");
|
||||
}
|
||||
|
||||
sendNotification(msg);
|
||||
}
|
||||
|
||||
private void sendNotification(Map<String, Object> msg) {
|
||||
if (messagingService != null){
|
||||
Gson gson = new Gson();
|
||||
String updateMsg = gson.toJson(msg);
|
||||
log.debug("sending: " + updateMsg);
|
||||
messagingService.send(MessagingConstants.TO_PRESENTATION_CHANNEL, updateMsg);
|
||||
} else {
|
||||
log.warn("MessagingService has not been set!.");
|
||||
}
|
||||
}
|
||||
private static Map<String, String> conversionMessagesMap;
|
||||
|
||||
public OfficeToPdfConversionSuccessFilter(MessagingService m) {
|
||||
messagingService = m;
|
||||
conversionMessagesMap = new HashMap<String, String>();
|
||||
conversionMessagesMap.put(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_SUCCESS_KEY,
|
||||
"Office document successfully converted.");
|
||||
conversionMessagesMap.put(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_FAILED_KEY,
|
||||
"Failed to convert Office document.");
|
||||
conversionMessagesMap.put(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_INVALID_KEY,
|
||||
"Invalid Office document detected, it will not be converted.");
|
||||
}
|
||||
|
||||
public boolean didConversionSucceed(UploadedPresentation pres) {
|
||||
notifyProgressListener(pres);
|
||||
return pres
|
||||
.getConversionStatus() == ConversionMessageConstants.OFFICE_DOC_CONVERSION_SUCCESS_KEY;
|
||||
}
|
||||
|
||||
private void notifyProgressListener(UploadedPresentation pres) {
|
||||
Map<String, Object> msg = new HashMap<String, Object>();
|
||||
msg.put("conference", pres.getMeetingId());
|
||||
msg.put("room", pres.getMeetingId());
|
||||
msg.put("returnCode", "CONVERT");
|
||||
msg.put("presentationId", pres.getId());
|
||||
msg.put("presentationName", pres.getId());
|
||||
msg.put("filename", pres.getName());
|
||||
msg.put("message", conversionMessagesMap.get(pres.getConversionStatus()));
|
||||
msg.put("messageKey", pres.getConversionStatus());
|
||||
|
||||
log.info("Notifying of " + pres.getConversionStatus() + " for "
|
||||
+ pres.getUploadedFile().getAbsolutePath());
|
||||
sendNotification(msg);
|
||||
}
|
||||
|
||||
private void sendNotification(Map<String, Object> msg) {
|
||||
if (messagingService != null) {
|
||||
Gson gson = new Gson();
|
||||
String updateMsg = gson.toJson(msg);
|
||||
log.debug("sending: " + updateMsg);
|
||||
messagingService.send(MessagingConstants.TO_PRESENTATION_CHANNEL,
|
||||
updateMsg);
|
||||
} else {
|
||||
log.warn("MessagingService has not been set!.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,71 +22,68 @@ package org.bigbluebutton.presentation;
|
||||
import java.io.File;
|
||||
|
||||
public final class UploadedPresentation {
|
||||
private final String meetingId;
|
||||
private final String id;
|
||||
private final String name;
|
||||
private File uploadedFile;
|
||||
private String fileType = "unknown";
|
||||
private int numberOfPages = 0;
|
||||
private boolean lastStepSuccessful = false;
|
||||
private final String baseUrl;
|
||||
|
||||
public UploadedPresentation(String meetingId, String id,
|
||||
String name,
|
||||
String baseUrl) {
|
||||
this.meetingId = meetingId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
private final String meetingId;
|
||||
private final String id;
|
||||
private final String name;
|
||||
private File uploadedFile;
|
||||
private String fileType = "unknown";
|
||||
private int numberOfPages = 0;
|
||||
private String conversionStatus;
|
||||
private final String baseUrl;
|
||||
|
||||
public File getUploadedFile() {
|
||||
return uploadedFile;
|
||||
}
|
||||
public UploadedPresentation(String meetingId, String id, String name,
|
||||
String baseUrl) {
|
||||
this.meetingId = meetingId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public void setUploadedFile(File uploadedFile) {
|
||||
this.uploadedFile = uploadedFile;
|
||||
}
|
||||
public File getUploadedFile() {
|
||||
return uploadedFile;
|
||||
}
|
||||
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setUploadedFile(File uploadedFile) {
|
||||
this.uploadedFile = uploadedFile;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getNumberOfPages() {
|
||||
return numberOfPages;
|
||||
}
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setNumberOfPages(int numberOfPages) {
|
||||
this.numberOfPages = numberOfPages;
|
||||
}
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public boolean isLastStepSuccessful() {
|
||||
return lastStepSuccessful;
|
||||
}
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public void setLastStepSuccessful(boolean lastStepSuccessful) {
|
||||
this.lastStepSuccessful = lastStepSuccessful;
|
||||
}
|
||||
|
||||
|
||||
public int getNumberOfPages() {
|
||||
return numberOfPages;
|
||||
}
|
||||
|
||||
public void setNumberOfPages(int numberOfPages) {
|
||||
this.numberOfPages = numberOfPages;
|
||||
}
|
||||
|
||||
public String getConversionStatus() {
|
||||
return conversionStatus;
|
||||
}
|
||||
|
||||
public void setConversionStatus(String conversionStatus) {
|
||||
this.conversionStatus = conversionStatus;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
package org.bigbluebutton.presentation.imp;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.Predicate;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.xslf.usermodel.XMLSlideShow;
|
||||
import org.apache.poi.xslf.usermodel.XSLFPictureData;
|
||||
import org.bigbluebutton.presentation.FileTypeConstants;
|
||||
import org.bigbluebutton.presentation.UploadedPresentation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class OfficeDocumentValidator {
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(OfficeDocumentValidator.class);
|
||||
|
||||
public boolean isValid(UploadedPresentation pres) {
|
||||
boolean valid = true;
|
||||
if (FilenameUtils.isExtension(pres.getUploadedFile().getName(),
|
||||
FileTypeConstants.PPTX)) {
|
||||
XMLSlideShow xmlSlideShow;
|
||||
try {
|
||||
xmlSlideShow = new XMLSlideShow(
|
||||
new FileInputStream(pres.getUploadedFile()));
|
||||
valid &= !embedsEmf(xmlSlideShow);
|
||||
valid &= !containsTinyTileBackground(xmlSlideShow);
|
||||
// Close the resource once we finished reading it
|
||||
xmlSlideShow.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot open PPTX file " + pres.getName());
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slide-show file embeds any EMF document
|
||||
*
|
||||
* @param xmlSlideShow
|
||||
* @return
|
||||
*/
|
||||
private boolean embedsEmf(XMLSlideShow xmlSlideShow) {
|
||||
EmfPredicate emfPredicate = new EmfPredicate();
|
||||
ArrayList<XSLFPictureData> embeddedEmfFiles = (ArrayList<XSLFPictureData>) CollectionUtils
|
||||
.select(xmlSlideShow.getPictureData(), emfPredicate);
|
||||
if (embeddedEmfFiles.size() > 0) {
|
||||
log.warn(
|
||||
"Found " + embeddedEmfFiles.size() + " EMF files in presentation.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slide-show contains a small background tile image
|
||||
*
|
||||
* @param xmlSlideShow
|
||||
* @return
|
||||
*/
|
||||
private boolean containsTinyTileBackground(XMLSlideShow xmlSlideShow) {
|
||||
TinyTileBackgroundPredicate tinyTileCondition = new TinyTileBackgroundPredicate();
|
||||
ArrayList<XSLFPictureData> tileImage = (ArrayList<XSLFPictureData>) CollectionUtils
|
||||
.select(xmlSlideShow.getPictureData(), tinyTileCondition);
|
||||
if (tileImage.size() > 0) {
|
||||
log.warn("Found small background tile image.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private final class EmfPredicate implements Predicate<XSLFPictureData> {
|
||||
@Override
|
||||
public boolean evaluate(XSLFPictureData img) {
|
||||
return img.getContentType().equals("image/x-emf");
|
||||
}
|
||||
}
|
||||
|
||||
private final class TinyTileBackgroundPredicate
|
||||
implements Predicate<XSLFPictureData> {
|
||||
@Override
|
||||
public boolean evaluate(XSLFPictureData img) {
|
||||
return img.getContentType() != null
|
||||
&& img.getContentType().equals("image/jpeg")
|
||||
&& LittleEndian.getLong(img.getChecksum()) == 4114937224L;
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@
|
||||
package org.bigbluebutton.presentation.imp;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.bigbluebutton.presentation.ConversionMessageConstants;
|
||||
import org.bigbluebutton.presentation.PageConverter;
|
||||
import org.bigbluebutton.presentation.SupportedFileTypes;
|
||||
import org.bigbluebutton.presentation.UploadedPresentation;
|
||||
@ -27,44 +29,64 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class OfficeToPdfConversionService {
|
||||
private static Logger log = LoggerFactory.getLogger(OfficeToPdfConversionService.class);
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(OfficeToPdfConversionService.class);
|
||||
|
||||
private OfficeDocumentValidator officeDocumentValidator;
|
||||
|
||||
/*
|
||||
* Convert the Office document to PDF. If successful, update
|
||||
* Convert the Office document to PDF. If successful, update
|
||||
* UploadPresentation.uploadedFile with the new PDF out and
|
||||
* UploadPresentation.lastStepSuccessful to TRUE.
|
||||
*/
|
||||
public UploadedPresentation convertOfficeToPdf(UploadedPresentation pres) {
|
||||
initialize(pres);
|
||||
if (SupportedFileTypes.isOfficeFile(pres.getFileType())) {
|
||||
File pdfOutput = setupOutputPdfFile(pres);
|
||||
boolean valid = officeDocumentValidator.isValid(pres);
|
||||
if (!valid) {
|
||||
log.warn("Problems detected prior to converting the file to PDF.");
|
||||
pres.setConversionStatus(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_INVALID_KEY);
|
||||
return pres;
|
||||
}
|
||||
File pdfOutput = setupOutputPdfFile(pres);
|
||||
if (convertOfficeDocToPdf(pres, pdfOutput)) {
|
||||
log.info("Successfully converted office file to pdf.");
|
||||
makePdfTheUploadedFileAndSetStepAsSuccess(pres, pdfOutput);
|
||||
} else {
|
||||
log.warn("Failed to convert " + pres.getUploadedFile().getAbsolutePath() + " to Pdf.");
|
||||
log.warn("Failed to convert " + pres.getUploadedFile().getAbsolutePath()
|
||||
+ " to Pdf.");
|
||||
}
|
||||
}
|
||||
return pres;
|
||||
}
|
||||
|
||||
public void initialize(UploadedPresentation pres) {
|
||||
pres.setLastStepSuccessful(false);
|
||||
pres.setConversionStatus(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_FAILED_KEY);
|
||||
}
|
||||
|
||||
private File setupOutputPdfFile(UploadedPresentation pres) {
|
||||
private File setupOutputPdfFile(UploadedPresentation pres) {
|
||||
File presentationFile = pres.getUploadedFile();
|
||||
String filenameWithoutExt = presentationFile.getAbsolutePath().substring(0, presentationFile.getAbsolutePath().lastIndexOf("."));
|
||||
String filenameWithoutExt = presentationFile.getAbsolutePath().substring(0,
|
||||
presentationFile.getAbsolutePath().lastIndexOf("."));
|
||||
return new File(filenameWithoutExt + ".pdf");
|
||||
}
|
||||
|
||||
private boolean convertOfficeDocToPdf(UploadedPresentation pres, File pdfOutput) {
|
||||
private boolean convertOfficeDocToPdf(UploadedPresentation pres,
|
||||
File pdfOutput) {
|
||||
PageConverter converter = new Office2PdfPageConverter();
|
||||
return converter.convert(pres.getUploadedFile(), pdfOutput, 0, pres);
|
||||
}
|
||||
|
||||
private void makePdfTheUploadedFileAndSetStepAsSuccess(UploadedPresentation pres, File pdf) {
|
||||
private void makePdfTheUploadedFileAndSetStepAsSuccess(
|
||||
UploadedPresentation pres, File pdf) {
|
||||
pres.setUploadedFile(pdf);
|
||||
pres.setLastStepSuccessful(true);
|
||||
pres.setConversionStatus(
|
||||
ConversionMessageConstants.OFFICE_DOC_CONVERSION_SUCCESS_KEY);
|
||||
}
|
||||
|
||||
public void setOfficeDocumentValidator(OfficeDocumentValidator v) {
|
||||
officeDocumentValidator = v;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ module BigBlueButton
|
||||
raise MissingDirectoryException, "Directory not found #{to_dir}" if not BigBlueButton.dir_exists?(to_dir)
|
||||
raise FileNotFoundException, "No recording for #{meeting_id} in #{from_dir}" if Dir.glob("#{from_dir}").empty?
|
||||
|
||||
Dir.glob("#{from_dir}/#{meeting_id}-*.flv").each { |file|
|
||||
Dir.glob("#{from_dir}/#{meeting_id}-*").each { |file|
|
||||
puts "deskshare #{file} to #{to_dir}"
|
||||
FileUtils.cp(file, to_dir)
|
||||
}
|
||||
|
@ -105,6 +105,22 @@ def check_webcam_files(raw_dir, meeting_id)
|
||||
end
|
||||
|
||||
def check_deskshare_files(raw_dir, meeting_id)
|
||||
meeting_dir = "#{raw_dir}/#{meeting_id}"
|
||||
|
||||
BigBlueButton.logger.info("Repairing red5 serialized streams")
|
||||
cp="/usr/share/red5/red5-server.jar:/usr/share/red5/lib/*"
|
||||
if File.directory?("#{meeting_dir}/deskshare")
|
||||
FileUtils.cd("#{meeting_dir}/deskshare") do
|
||||
Dir.glob("*.flv.ser").each do |ser|
|
||||
BigBlueButton.logger.info("Repairing #{ser}")
|
||||
ret = BigBlueButton.exec_ret('java', '-cp', cp, 'org.red5.io.flv.impl.FLVWriter', ser, '0', '7')
|
||||
if ret != 0
|
||||
BigBlueButton.logger.warn("Failed to repair #{ser}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desktops = BigBlueButton::Events.get_start_deskshare_events("#{raw_dir}/#{meeting_id}/events.xml")
|
||||
desktops.each do |desktop|
|
||||
raw_desktop_file = "#{raw_dir}/#{meeting_id}/deskshare/#{desktop[:stream]}"
|
||||
|
Loading…
Reference in New Issue
Block a user