diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PresentationMessageListener.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PresentationMessageListener.java
index d91e5f38ded2a98c00aff0903d89c8865ac62305..f3d2ac2dcb86114b8635d788cfbced6982f4c345 100755
--- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PresentationMessageListener.java
+++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PresentationMessageListener.java
@@ -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)){
diff --git a/bbb-lti/grails-app/conf/lti-config.properties b/bbb-lti/grails-app/conf/lti-config.properties
index c2a898c2d5a8f1efc3f804cc9487a1ccd0c5776d..6984fce90df8f13dabb302aa1fa86434e59903b5 100644
--- a/bbb-lti/grails-app/conf/lti-config.properties
+++ b/bbb-lti/grails-app/conf/lti-config.properties
@@ -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}
diff --git a/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy b/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
index fbf2574b934fabc52b72424446c47223102d5e7e..600218fedfb3fb3544939bf3798492c7fb3d19d5 100644
--- a/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
+++ b/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
@@ -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 + "]"
         }
diff --git a/bbb-lti/grails-app/services/org/bigbluebutton/BigbluebuttonService.groovy b/bbb-lti/grails-app/services/org/bigbluebutton/BigbluebuttonService.groovy
index 1a9d90e29febb4a902ba56db71d97efb5e9cab98..297a6ce55422c33579812f4de11ead16c2791793 100644
--- a/bbb-lti/grails-app/services/org/bigbluebutton/BigbluebuttonService.groovy
+++ b/bbb-lti/grails-app/services/org/bigbluebutton/BigbluebuttonService.groovy
@@ -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
     }
diff --git a/bbb-lti/grails-app/services/org/bigbluebutton/LtiService.groovy b/bbb-lti/grails-app/services/org/bigbluebutton/LtiService.groovy
index 2048d06383b243f2df2467df6ad3cce834b95d65..c78c127ba0eecd608ccd347761b02c4cb495a86b 100644
--- a/bbb-lti/grails-app/services/org/bigbluebutton/LtiService.groovy
+++ b/bbb-lti/grails-app/services/org/bigbluebutton/LtiService.groovy
@@ -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);
+    }
 }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ClientMessage.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ClientMessage.java
index 627712d50bc42a5401291e3f0ee9bc1ffea969e5..94db9f56fabd58d684cc2b6efb921ba8b98fc35e 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ClientMessage.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ClientMessage.java
@@ -21,4 +21,5 @@ package org.bigbluebutton.red5.client.messaging;
 
 public interface ClientMessage {
 
+    String getMessageName();
 }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
index 2d23b2118c7bbf49c41e6262b7571179ec8610e0..89471e30cce9ac8602615f44d7fd6c429406cddd 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
@@ -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);     
     } 
   }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllClientsMessage.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllClientsMessage.java
index ddedc077c5d646546fd8f7d11fea278e352a2a55..d3f833842953ba4034a946fe3acbfac25e7d8038 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllClientsMessage.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllClientsMessage.java
@@ -11,4 +11,8 @@ public class DisconnectAllClientsMessage implements ClientMessage {
 	public String getMeetingId() {
 		return meetingId;
 	}
+
+	public String getMessageName() {
+		return "DisconnectAllClientsMessage";
+	}
 }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllMessage.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllMessage.java
index 080d0ee6e2a8abf3fa7e7979c10777c55c5ad52e..38fcbddc3a703439cf12608c4554f4ebd7eec913 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllMessage.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectAllMessage.java
@@ -2,4 +2,7 @@ package org.bigbluebutton.red5.client.messaging;
 
 public class DisconnectAllMessage implements ClientMessage {
 
+    public String getMessageName() {
+        return "DisconnectAllMessage";
+    }
 }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectClientMessage.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectClientMessage.java
index f0a9ba73ff34d8a8fe122fb97f2d6ca90c885264..fe8ce941263444f2fcdc5413b3d4c73b0eff5723 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectClientMessage.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/DisconnectClientMessage.java
@@ -17,4 +17,8 @@ public class DisconnectClientMessage implements ClientMessage {
 	public String getUserId() {
 		return userId;
 	}
+
+	public String getMessageName() {
+		return "DisconnectClientMessage";
+	}
 }
diff --git a/bigbluebutton-client/branding/default/style/css/BBBDefault.css b/bigbluebutton-client/branding/default/style/css/BBBDefault.css
index 80da61a02d50572f8dc0000103a5fe52b1e431d4..fdda4d8cc12ea576a6ff4ffb467ec19ce346b717 100755
--- a/bigbluebutton-client/branding/default/style/css/BBBDefault.css
+++ b/bigbluebutton-client/branding/default/style/css/BBBDefault.css
@@ -1061,6 +1061,11 @@ EmojiGrid {
 	horizontalGap: 6;
 }
 
+RoomActionsRenderer {
+	paddingLeft  : 5;
+	paddingRight : 5;
+}
+
 .breakoutRoomUserWindowHeadingStyle {
 	fontWeight: bold;
 }
diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties
index a8b2abb91ad4b4a97fcd915e2e49db3ff56d3928..fa6e9638ca21aaa64c925c16444ce98c19a91ba9 100755
--- a/bigbluebutton-client/locale/en_US/bbbResources.properties
+++ b/bigbluebutton-client/locale/en_US/bbbResources.properties
@@ -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
diff --git a/bigbluebutton-client/resources/prod/lib/bbb_webrtc_bridge_sip.js b/bigbluebutton-client/resources/prod/lib/bbb_webrtc_bridge_sip.js
index 066aeedadfddd641e3b4e87d6f2e7ef780d0f747..05d07355da9193bc7b83609b76dc5f39cc9766be 100755
--- a/bigbluebutton-client/resources/prod/lib/bbb_webrtc_bridge_sip.js
+++ b/bigbluebutton-client/resources/prod/lib/bbb_webrtc_bridge_sip.js
@@ -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
diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/EventConstants.as b/bigbluebutton-client/src/org/bigbluebutton/core/EventConstants.as
index d292cba3b61df05fdd9bcfccb0ecf46f2dbaeac2..99e0d3f532d8b0c69d0e8fa76c4d1120313bde49 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/core/EventConstants.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/core/EventConstants.as
@@ -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";
diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/TimerUtil.as b/bigbluebutton-client/src/org/bigbluebutton/core/TimerUtil.as
index 1ff3cc9ed38042a30868521d422a8e6e4b8a12c3..53df479fb403f3b61935536d5a0ac074cf03f0a5 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/core/TimerUtil.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/core/TimerUtil.as
@@ -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();
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCalls.as b/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCalls.as
index 6a7e2123e4879d7719f60472df49122c9594db1a..a6bd4d1d864c078f8e55e0fdf9699f2c1370b575 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCalls.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCalls.as
@@ -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();
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/maps/ExternalApiEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/api/maps/ExternalApiEventMap.mxml
index 4d7dac46e19e837bfbcfe6b70b93d75efc1a5dd0..663eec955b2132a156990f3ec8b950958e8eb569 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/api/maps/ExternalApiEventMap.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/maps/ExternalApiEventMap.mxml
@@ -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>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/ExitApplicationEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/ExitApplicationEvent.as
new file mode 100644
index 0000000000000000000000000000000000000000..312ab69be400f046282f59151e0d9a37b8705fb1
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/ExitApplicationEvent.as
@@ -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);
+        }
+    }
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
index c6b8a1202250a839c72ab9e469503aca87a9e3c9..91cad9a61f065aa100e11173d0915ac9f3fbdcf9 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
@@ -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);
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
index ba6043e0d05df3c7c619d928bed8829031f91b86..3e97d8f4a7e194effb6ca028ca2b23e15614802d 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
@@ -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;
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/LoggedOutWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/LoggedOutWindow.mxml
index 1453adab799b262efacdb3897d614b283e364836..5254556d829630f57992ce1963ca8d2c61a4995e 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/LoggedOutWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/LoggedOutWindow.mxml
@@ -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 {
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
index f0a90158456fa05ffe1b75d8eb8d111d0dfe9598..fba02f7be2d4375fa5c9c5bd2225ef5ce9a21b6f 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
@@ -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);
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml
index f73c205eb38f9be439a7efa1612abfbb035c3bc6..79b90afdbadbfade20c7d0e8ef64ea33693fd21a 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml
@@ -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 
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatBox.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatBox.mxml
index 152718c8e43b9dcf35a33d2844ba3615c064ab4b..705544162c3ecc45ce8af54b4d8e528f58c25f38 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatBox.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatBox.mxml
@@ -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>
 
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/present/events/OfficeDocConvertInvalidEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/present/events/OfficeDocConvertInvalidEvent.as
new file mode 100644
index 0000000000000000000000000000000000000000..c3bf2e68a2c52566279b2e7673b5b18f73b260c2
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/present/events/OfficeDocConvertInvalidEvent.as
@@ -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);
+        }
+    }
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/Constants.as b/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/Constants.as
index 68717fa2da3d38ae69a6d1545144827bdb2c3dc5..ee73d68e692377c5372cd1acbb7b50f44fb84e7d 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/Constants.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/Constants.as
@@ -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";
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/messaging/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/messaging/MessageReceiver.as
index c7a89d1b3970bb8b3e48712ead9deb24e27d8c1e..3dce38d50ddb1198d83ad51ac6c9417721fb520f 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/messaging/MessageReceiver.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/present/services/messaging/MessageReceiver.as
@@ -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;
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/present/ui/views/FileUploadWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/present/ui/views/FileUploadWindow.mxml
index 410e40c51e043ae9827744d4fdb5644b72204f91..990a666584178b4a3fe67fe7ffd6d6cf8ed94aca 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/present/ui/views/FileUploadWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/present/ui/views/FileUploadWindow.mxml
@@ -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";
       }
 
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
index ea047ef44c45dca16224b68ed07941e7fa7680ac..ec7f8b4b51c5056ba42e28f8b8da4e74849baf35 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
@@ -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 {  
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
index ad652990da7f395add122120aba899e054af4f06..bcfeda8a92d3ebaf0170c333a31afb20a31a6050 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
@@ -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}" />
diff --git a/bigbluebutton-html5/imports/api/phone/index.js b/bigbluebutton-html5/imports/api/phone/index.js
index 7cd680ca22da61f582b105e0803a7044a8a4bf6c..f5e17501e4b901ef2c53ca391b03d13978e1e691 100755
--- a/bigbluebutton-html5/imports/api/phone/index.js
+++ b/bigbluebutton-html5/imports/api/phone/index.js
@@ -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;
   }
 }
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/setUserPresenter.js b/bigbluebutton-html5/imports/api/users/server/methods/setUserPresenter.js
index 23848c54280dae060cda59eb7b2a3c039427602b..220d542f60c585ea38ca6e605278de09626bbdb6 100755
--- a/bigbluebutton-html5/imports/api/users/server/methods/setUserPresenter.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/setUserPresenter.js
@@ -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,
         },
       };
 
diff --git a/bigbluebutton-html5/imports/locales/en.json b/bigbluebutton-html5/imports/locales/en.json
index f219046e68762adab632117ed9d9228dea97ccda..25442f286d1f4887ad1eab678694b87441d8ea65 100755
--- a/bigbluebutton-html5/imports/locales/en.json
+++ b/bigbluebutton-html5/imports/locales/en.json
@@ -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",
diff --git a/bigbluebutton-html5/imports/startup/server/userPermissions.js b/bigbluebutton-html5/imports/startup/server/userPermissions.js
index 8129963e7c95d33884143f890e1d8f5b14eb8f2f..5011b0d80a3a504134b8972cc6df27dfb197742b 100755
--- a/bigbluebutton-html5/imports/startup/server/userPermissions.js
+++ b/bigbluebutton-html5/imports/startup/server/userPermissions.js
@@ -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,
+  const user = Users.findOne({
+    meetingId,
+    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 allowedToInitiateRequest =
+    null != user &&
+    authToken === user.authToken &&
+    user.validated &&
+    'HTML5' === user.clientType &&
+    null != user.user;
+
+  if (allowedToInitiateRequest) {
+    let result = 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];
+    }
 
-  // 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') {
-
-      // 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;
-
-      // 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 presenter actions
+    if (user.user.presenter) {
+      logger.debug('user permissions presenter case');
+      result = result || presenter[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))}`
-    );
 
+    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;
   }
+
 };
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/component.jsx
index 6548ab3a9970727e627d746b23436dcb5b1a17d0..b2a0e9bc378af93be62e0d24bcd64e0c0e4f92e9 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/component.jsx
@@ -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}
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/container.jsx
index 9a8ad360884ad3df95df95a425e9b378de2d36c7..176bf187d38acda731c39b2ab0719dbf6e3acc7d 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/mute-button/container.jsx
@@ -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);
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss b/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss
index 04f69baa3d0c52de8347bfeea400d4ccc552508d..08cabd778238c58c5c85cb6219d3971b9eec43e4 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss
@@ -30,3 +30,7 @@
 .invisible {
   visibility: hidden;
 }
+
+.circleGlow > :first-child{
+    box-shadow: 0 0 .15rem #FFF !important;
+}
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 13f4d49fd0b437dcabfe83a8239f726858b2712f..1a8d49d3ecc5c8b1276e9cdb3a36c145f5b9185e 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -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()}
diff --git a/bigbluebutton-html5/imports/ui/components/audio-modal/component.jsx b/bigbluebutton-html5/imports/ui/components/audio-modal/component.jsx
index 87d3d74157765e1eb6fb8b2eea783e20bc283543..9a827c971f3b22e59b7203cc127961edf3c0273a 100755
--- a/bigbluebutton-html5/imports/ui/components/audio-modal/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio-modal/component.jsx
@@ -53,6 +53,7 @@ export default class Audio extends React.Component {
   render() {
     return (
       <ModalBase
+        isTransparent={true}
         isOpen={true}
         onHide={null}
         onShow={null}
diff --git a/bigbluebutton-html5/imports/ui/components/audio-notification/component.jsx b/bigbluebutton-html5/imports/ui/components/audio-notification/component.jsx
new file mode 100755
index 0000000000000000000000000000000000000000..f9dbb42410402359ad4731bc875b4e3902e51cca
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/audio-notification/component.jsx
@@ -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;
diff --git a/bigbluebutton-html5/imports/ui/components/audio-notification/container.jsx b/bigbluebutton-html5/imports/ui/components/audio-notification/container.jsx
new file mode 100755
index 0000000000000000000000000000000000000000..eb802a453447ee1b574d982a59d1a18d07588c42
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/audio-notification/container.jsx
@@ -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));
diff --git a/bigbluebutton-html5/imports/ui/components/audio-notification/styles.scss b/bigbluebutton-html5/imports/ui/components/audio-notification/styles.scss
new file mode 100755
index 0000000000000000000000000000000000000000..03a98eb6dba4ae46199e7be038f0f074618719fb
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/audio-notification/styles.scss
@@ -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);
+}
diff --git a/bigbluebutton-html5/imports/ui/components/button/component.jsx b/bigbluebutton-html5/imports/ui/components/button/component.jsx
index 3b0f03e635a0b9a5ae1ff03fcab0ab3bbd1c8e07..c4f16c1efce238043b65398351cb48884d12c65d 100755
--- a/bigbluebutton-html5/imports/ui/components/button/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/button/component.jsx
@@ -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;
diff --git a/bigbluebutton-html5/imports/ui/components/button/styles.scss b/bigbluebutton-html5/imports/ui/components/button/styles.scss
index bc164c369610a585253c0290258690b1d1937f98..4d7b84befbd7a34c5ef5a8964481c52ebe7fea9b 100755
--- a/bigbluebutton-html5/imports/ui/components/button/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/button/styles.scss
@@ -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;
diff --git a/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
old mode 100644
new mode 100755
index ed6e2c57f45a1ea324a0edf60b1229968bec35df..c1b82ce8e88ed1fd02b45dd65c11e88838bd26b2
--- a/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
@@ -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}
diff --git a/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss b/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
index 17d08d3cc6bb0f3d71a0c46b72051c30ab8543f1..d3384dee0165219a721db61de4a31a0e92719175 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
@@ -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;
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
index 0db954613c53f0d79b88928359f58242841046cd..6f3ff8f7d5c8e3c2f20dcbfcfe30bcd0554ee2a5 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
@@ -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}>
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/component.jsx
index 2e13d6cf070ed49a54e5f537f6d30cfddab512e2..c23cd33c1f18cecc861c4ec3ee6ffa8d57a58797 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/component.jsx
@@ -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>);
   }
 };
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/styles.scss b/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/styles.scss
index f744600dc0ab59f9ab304822362e1a9073f8bb4a..8ff2652d9e0a1bc5a505f56ad77f300d19da0bc5 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/recording-indicator/styles.scss
@@ -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: '';
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
index c3c6be05675e8d9962e68a47658dc95a271b7ae0..80aa5ef1338b1816635efaf89fd7f6cc8321d951 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
@@ -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;
 
diff --git a/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx b/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx
index 9ac179e2e84183a998dd46d723d13cec4d780f4d..86e545c7448be77318006c867c53c714208b0ba3 100755
--- a/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx
@@ -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>
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js
index 745ab68fce7fc02012cb4992453bb0fa676c6b3a..b892909c031bf2832255beb85927803922f4b331 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/service.js
+++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js
@@ -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',
   },
 };
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-item/component.jsx
index 065669459098ef708b7198364f2be8d8228554aa..d40287927b66b279575910eb4c2e041cdd229378 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-item/component.jsx
@@ -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;
 
diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle
index 588866d978f8b0817f50e50bda2e38bb5969a83d..ce6c71534b855548c196e9dcc4fd72fbdbb20c7b 100755
--- a/bigbluebutton-web/build.gradle
+++ b/bigbluebutton-web/build.gradle
@@ -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'
diff --git a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml
index 148c479fcafec9c25d19a4caef259243718c1350..3218d0782f7c5de3c8388806e1ffec02b8eb806b 100755
--- a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml
+++ b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml
@@ -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}"/>
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ConversionMessageConstants.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ConversionMessageConstants.java
index 53aa6b3c0c7a3988ce59f905cda783ea08a10c80..463d52d2efdd09c1aa323bd85e977882ce46c442 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ConversionMessageConstants.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ConversionMessageConstants.java
@@ -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";
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/DocumentConversionServiceImp.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/DocumentConversionServiceImp.java
index 4a1d74ab79c958daaa37ef7627cb70d9c0e4d37c..ce0a002fbd8525a1c45d986583e149744d37afb1 100644
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/DocumentConversionServiceImp.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/DocumentConversionServiceImp.java
@@ -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;
+  }
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/OfficeToPdfConversionSuccessFilter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/OfficeToPdfConversionSuccessFilter.java
index 6564edf6fa4dffbd9676c27254a6aef25ccbe952..51ab8cbb5e0edc502b5c9191ae26cf06b8fd1ea6 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/OfficeToPdfConversionSuccessFilter.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/OfficeToPdfConversionSuccessFilter.java
@@ -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!.");
+    }
+  }
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/UploadedPresentation.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/UploadedPresentation.java
index ffa5da576c38d1d509f1c4e85c0c260b175bd160..9f01e55b050cd0c558a1b2d44550377bb6376d78 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/UploadedPresentation.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/UploadedPresentation.java
@@ -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;
-	}
-
-	public File getUploadedFile() {
-		return uploadedFile;
-	}
-
-	public void setUploadedFile(File uploadedFile) {
-		this.uploadedFile = uploadedFile;
-	}
-
-	public String getMeetingId() {
-		return meetingId;
-	}
-	
-	public String getId() {
-		return id;
-	}
-
-	public String getName() {
-		return name;
-	}
-	
-	public String getBaseUrl() {
-		return baseUrl;
-	}
-
-	public String getFileType() {
-		return fileType;
-	}
-
-	public void setFileType(String fileType) {
-		this.fileType = fileType;
-	}
-
-	public int getNumberOfPages() {
-		return numberOfPages;
-	}
-
-	public void setNumberOfPages(int numberOfPages) {
-		this.numberOfPages = numberOfPages;
-	}
-
-	public boolean isLastStepSuccessful() {
-		return lastStepSuccessful;
-	}
-
-	public void setLastStepSuccessful(boolean lastStepSuccessful) {
-		this.lastStepSuccessful = lastStepSuccessful;
-	}
-	
-	
+  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 UploadedPresentation(String meetingId, String id, String name,
+      String baseUrl) {
+    this.meetingId = meetingId;
+    this.id = id;
+    this.name = name;
+    this.baseUrl = baseUrl;
+  }
+
+  public File getUploadedFile() {
+    return uploadedFile;
+  }
+
+  public void setUploadedFile(File uploadedFile) {
+    this.uploadedFile = uploadedFile;
+  }
+
+  public String getMeetingId() {
+    return meetingId;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getBaseUrl() {
+    return baseUrl;
+  }
+
+  public String getFileType() {
+    return fileType;
+  }
+
+  public void setFileType(String fileType) {
+    this.fileType = fileType;
+  }
+
+  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;
+  }
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeDocumentValidator.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeDocumentValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bc8284892b61314fc7617c76fc384750da9096e
--- /dev/null
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeDocumentValidator.java
@@ -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;
+    }
+  }
+}
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java
index 9dd17f4f17f7478dfb9173a569f0a21c6c86b20f..a74bfebf651b8fd9894d87aed44c434e2d246abe 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java
@@ -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;
   }
 }
diff --git a/record-and-playback/core/lib/recordandplayback/deskshare_archiver.rb b/record-and-playback/core/lib/recordandplayback/deskshare_archiver.rb
index 2b85c699504a551cb00ddf48c4fc5aced8b9996d..ee880067ac4486972a72ef390c63fe1a0860901e 100755
--- a/record-and-playback/core/lib/recordandplayback/deskshare_archiver.rb
+++ b/record-and-playback/core/lib/recordandplayback/deskshare_archiver.rb
@@ -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)
       }         
diff --git a/record-and-playback/core/scripts/sanity/sanity.rb b/record-and-playback/core/scripts/sanity/sanity.rb
index 0dc2889b2fe4baf7dbbc5ee4ba5133ed0c1ae853..cf11b5e8aa7f860cf8347c065c2d86e06b7274e9 100755
--- a/record-and-playback/core/scripts/sanity/sanity.rb
+++ b/record-and-playback/core/scripts/sanity/sanity.rb
@@ -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]}"