diff --git a/bbb-api-demo/src/main/webapp/demo_iframe.jsp b/bbb-api-demo/src/main/webapp/demo_iframe.jsp
index 3cd02f6fda6db623efc735311ae0a3e3a8e19f5f..e8aafb61d2849581eee2c88b0f9705ab10e31b0d 100644
--- a/bbb-api-demo/src/main/webapp/demo_iframe.jsp
+++ b/bbb-api-demo/src/main/webapp/demo_iframe.jsp
@@ -133,10 +133,11 @@ muteButton.id = 'muteButton';
 
 function getInitialState() {
   document.getElementById('client-content').contentWindow.postMessage('c_recording_status', '*');
-  document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
+  document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
 }
 
 function handleMessage(e) {
+  let neverJoinedAudio = true;
   switch (e) {
     case 'readyToConnect': {
       // get initial state
@@ -160,11 +161,18 @@ function handleMessage(e) {
     case 'notInAudio': {
       muteButton.innerHTML = 'Not in audio';
       document.getElementById('muteButton').disabled = true;
+      if (neverJoinedAudio) {
+        // poll every 1 sec to check if we joined audio
+        setTimeout(function(){
+          document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
+        }, 1000);}
       break;
     }
     case 'joinedAudio': {
+      neverJoinedAudio = false;
       muteButton.innerHTML = '';
-      document.getElementById('muteButton').disabled = false; getInitialState();
+      document.getElementById('muteButton').disabled = false;
+      document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
       break;
     }
     default: console.log('neither', { e });
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods.js b/bigbluebutton-html5/imports/api/meetings/server/methods.js
old mode 100644
new mode 100755
index 74401c1b1544f0637d7245e0574139d6ccdf3165..960ef8082efae97b4e628dc50ec470774f70c746
--- a/bigbluebutton-html5/imports/api/meetings/server/methods.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods.js
@@ -2,9 +2,13 @@ import { Meteor } from 'meteor/meteor';
 import endMeeting from './methods/endMeeting';
 import toggleRecording from './methods/toggleRecording';
 import transferUser from './methods/transferUser';
+import toggleLockSettings from './methods/toggleLockSettings';
+import toggleWebcamsOnlyForModerator from './methods/toggleWebcamsOnlyForModerator';
 
 Meteor.methods({
   endMeeting,
   toggleRecording,
+  toggleLockSettings,
   transferUser,
+  toggleWebcamsOnlyForModerator,
 });
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
new file mode 100755
index 0000000000000000000000000000000000000000..ee59cf102e9ed1b70e138abff731bbe21d08edce
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
@@ -0,0 +1,37 @@
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import RedisPubSub from '/imports/startup/server/redis';
+
+export default function toggleLockSettings(credentials, meeting) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'ChangeLockSettingsInMeetingCmdMsg';
+
+  const { meetingId, requesterUserId } = credentials;
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(meeting.lockSettingsProp, {
+    disableCam: Boolean,
+    disableMic: Boolean,
+    disablePrivChat: Boolean,
+    disablePubChat: Boolean,
+    lockedLayout: Boolean,
+    lockOnJoin: Boolean,
+    lockOnJoinConfigurable: Boolean,
+    setBy: String,
+  });
+
+  const payload = {
+    disableCam: meeting.lockSettingsProp.disableCam,
+    disableMic: meeting.lockSettingsProp.disableMic,
+    disablePrivChat: meeting.lockSettingsProp.disablePrivChat,
+    disablePubChat: meeting.lockSettingsProp.disablePubChat,
+    lockedLayout: meeting.lockSettingsProp.lockedLayout,
+    lockOnJoin: meeting.lockSettingsProp.lockOnJoin,
+    lockOnJoinConfigurable: meeting.lockSettingsProp.lockOnJoinConfigurable,
+    setBy: requesterUserId,
+  };
+
+  RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
new file mode 100755
index 0000000000000000000000000000000000000000..75f052de76503bc36967c966e157d4cbcb6a063d
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
@@ -0,0 +1,22 @@
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import RedisPubSub from '/imports/startup/server/redis';
+
+export default function toggleWebcamsOnlyForModerator(credentials, meeting) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'UpdateWebcamsOnlyForModeratorCmdMsg';
+
+  const { meetingId, requesterUserId } = credentials;
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(meeting.usersProp.webcamsOnlyForModerator, Boolean);
+
+  const payload = {
+    webcamsOnlyForModerator: meeting.usersProp.webcamsOnlyForModerator,
+    setBy: requesterUserId,
+  };
+
+  RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
index a8023da3626343ddc0c50c913caba9062494b8e0..f8011fd0d32225429338b6252eefd78af4307bc8 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
@@ -69,10 +69,22 @@ export default function addMeeting(meeting) {
     meetingId,
   };
 
+  const lockSettingsProp = {
+    disableCam: false,
+    disableMic: false,
+    disablePrivChat: false,
+    disablePubChat: false,
+    lockOnJoin: true,
+    lockOnJoinConfigurable: false,
+    lockedLayout: false,
+    setBy: 'temp',
+  };
+
   const modifier = {
     $set: Object.assign(
       { meetingId },
       flat(meeting, { safe: true }),
+      { lockSettingsProp },
     ),
   };
 
diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/presenterAssigned.js b/bigbluebutton-html5/imports/api/users/server/handlers/presenterAssigned.js
index 105772c23263026b5baed5bd935bf5d74512e3f9..ddd232040867e1ae3c7d21ff9dc22971b542f76b 100644
--- a/bigbluebutton-html5/imports/api/users/server/handlers/presenterAssigned.js
+++ b/bigbluebutton-html5/imports/api/users/server/handlers/presenterAssigned.js
@@ -1,10 +1,13 @@
 import Users from '/imports/api/users';
+import PresentationPods from '/imports/api/presentation-pods';
 import changeRole from '/imports/api/users/server/modifiers/changeRole';
+import assignPresenter from '../methods/assignPresenter';
 
-export default function handlePresenterAssigned({ body }, meetingId) {
+export default function handlePresenterAssigned(credentials, meetingId) {
   const USER_CONFIG = Meteor.settings.public.user;
   const ROLE_PRESENTER = USER_CONFIG.role_presenter;
 
+  const { body } = credentials;
   const { presenterId, assignedBy } = body;
 
   changeRole(ROLE_PRESENTER, true, presenterId, meetingId, assignedBy);
@@ -15,11 +18,34 @@ export default function handlePresenterAssigned({ body }, meetingId) {
     presenter: true,
   };
 
+  const defaultPodSelector = {
+    podId: 'DEFAULT_PRESENTATION_POD',
+  };
+
   const prevPresenter = Users.findOne(selector);
 
   // no previous presenters
+  // The below code is responsible for set Meeting presenter to be default pod presenter as well.
+  // It's been handled here because right now akka-apps don't handle all cases scenarios.
   if (!prevPresenter) {
-    return true;
+    const currentDefaultPodPresenter = PresentationPods.findOne(defaultPodSelector);
+
+    const { currentPresenterId } = currentDefaultPodPresenter;
+
+    const podPresenterCredentials = {
+      meetingId,
+      requesterUserId: assignedBy,
+    };
+
+    if (currentDefaultPodPresenter.currentPresenterId !== '') {
+      const oldPresenter = Users.findOne({ userId: currentPresenterId });
+
+      if (oldPresenter.connectionStatus === 'offline') {
+        return assignPresenter(podPresenterCredentials, presenterId);
+      }
+      return true;
+    }
+    return assignPresenter(podPresenterCredentials, presenterId);
   }
 
   return changeRole(ROLE_PRESENTER, false, prevPresenter.userId, meetingId, assignedBy);
diff --git a/bigbluebutton-html5/imports/startup/server/logger.js b/bigbluebutton-html5/imports/startup/server/logger.js
old mode 100755
new mode 100644
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 865c0278339cdf30936bc7ab442068cf668c3b89..bada062b60acda6b11a2bdf8a9aca362dbb639fd 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -8,6 +8,7 @@ import Resizable from 're-resizable';
 import browser from 'browser-detect';
 import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container';
 import PollingContainer from '/imports/ui/components/polling/container';
+import PollContainer from '/imports/ui/components/poll/container';
 import ToastContainer from '../toast/container';
 import ModalContainer from '../modal/container';
 import NotificationsBarContainer from '../notifications-bar/container';
@@ -16,7 +17,7 @@ import ChatAlertContainer from '../chat/alert/container';
 import { styles } from './styles';
 import UserListContainer from '../user-list/container';
 import ChatContainer from '../chat/container';
-import PollContainer from '/imports/ui/components/poll/container';
+
 
 const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
 const USERLIST_COMPACT_WIDTH = 50;
@@ -327,7 +328,7 @@ class App extends Component {
 
   render() {
     const {
-      userListIsOpen, customStyle, customStyleUrl,
+      userListIsOpen, customStyle, customStyleUrl, micsLocked,
     } = this.props;
     const { enableResize } = this.state;
 
@@ -349,11 +350,11 @@ class App extends Component {
         </section>
         <PollingContainer />
         <ModalContainer />
-        <AudioContainer />
+        {micsLocked ? null : <AudioContainer />}
         <ToastContainer />
         <ChatAlertContainer />
-        { customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null }
-        { customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null }
+        {customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null}
+        {customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null}
       </main>
     );
   }
diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx
index 600aee257851fedf1a23fe30c9df4aa3645db74a..748cb89be8ec13593c8c45b0b900197986026cd7 100755
--- a/bigbluebutton-html5/imports/ui/components/app/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx
@@ -70,6 +70,7 @@ const AppContainer = (props) => {
 
 export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
   const currentUser = Users.findOne({ userId: Auth.userID });
+  const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
   const isMeetingBreakout = meetingIsBreakout();
 
   if (!currentUser.approved) {
@@ -117,6 +118,7 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
     pollIsOpen: Session.get('isPollOpen') && Session.get('isUserListOpen'),
     customStyle: getFromUserSettings('customStyle', false),
     customStyleUrl: getFromUserSettings('customStyleUrl', false),
+    micsLocked: (currentUser.locked && meeting.lockSettingsProp.disableMic),
   };
 })(AppContainer)));
 
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
index b2334ede83a8d269b2a0f1313bdae8309f8543db..6211484d91a0473c476140b9080374fd5fee53ab 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
@@ -3,6 +3,9 @@ import { withTracker } from 'meteor/react-meteor-data';
 import { withModalMounter } from '/imports/ui/components/modal/service';
 import AudioManager from '/imports/ui/services/audio-manager';
 import { makeCall } from '/imports/ui/services/api';
+import Users from '/imports/api/users/';
+import Meetings from '/imports/api/meetings';
+import Auth from '/imports/ui/services/auth';
 import AudioControls from './component';
 import AudioModalContainer from '../audio-modal/container';
 import Service from '../service';
@@ -15,11 +18,12 @@ const processToggleMuteFromOutside = (e) => {
       makeCall('toggleSelfVoice');
       break;
     }
+    case 'get_audio_joined_status': {
+      const audioJoinedState = AudioManager.isConnected ? 'joinedAudio' : 'notInAudio';
+      this.window.parent.postMessage({ response: audioJoinedState }, '*');
+      break;
+    }
     case 'c_mute_status': {
-      if (!AudioManager.isUsingAudio()) {
-        this.window.parent.postMessage({ response: 'notInAudio' }, '*');
-        return;
-      }
       const muteState = AudioManager.isMuted ? 'selfMuted' : 'selfUnmuted';
       this.window.parent.postMessage({ response: muteState }, '*');
       break;
@@ -39,6 +43,12 @@ export default withModalMounter(withTracker(({ mountModal }) =>
     disable: Service.isConnecting() || Service.isHangingUp(),
     glow: Service.isTalking() && !Service.isMuted(),
     handleToggleMuteMicrophone: () => Service.toggleMuteMicrophone(),
-    handleJoinAudio: () => mountModal(<AudioModalContainer />),
+    handleJoinAudio: () => {
+      const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
+      const currentUser = Users.findOne({ userId: Auth.userID });
+      const micsLocked = (currentUser.locked && meeting.lockSettingsProp.disableMic);
+
+      return micsLocked ? Service.joinListenOnly() : mountModal(<AudioModalContainer />);
+    },
     handleLeaveAudio: () => Service.exitAudio(),
   }))(AudioControlsContainer));
diff --git a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx
index 800d6409e03e0c8c38cb70244c0725cd48cc92bb..297ee48d84444c471f344c70296ae62d1131c216 100644
--- a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 import { Meteor } from 'meteor/meteor';
 import Button from '/imports/ui/components/button/component';
+import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
 import { styles } from './styles';
 
 const intlMessages = defineMessages({
@@ -66,7 +67,7 @@ class ErrorScreen extends React.PureComponent {
         <div className={styles.content}>
           <Button
             size="sm"
-            onClick={() => Session.set('isMeetingEnded', true)}
+            onClick={logoutRouteHandler}
             label={intl.formatMessage(intlMessages.leave)}
           />
         </div>
diff --git a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
index 55c515d1bdb26568cc3d5fd389e999c1128c6161..f1bfd5c9e311aab365bb2d2a841a921ca4d13dd6 100644
--- a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
@@ -71,6 +71,7 @@ class JoinHandler extends Component {
       return resp;
     };
 
+    const setLogoutURL = (url) => Auth.logoutURL = url;
     const setLogoURL = (resp) => {
       setCustomLogoUrl(resp.customLogoURL);
       return resp;
@@ -94,11 +95,12 @@ class JoinHandler extends Component {
         .then(response => response.json())
         .then(({ response }) => response)
         .then((resp) => {
+          setLogoutURL(resp.logoutURL);
           if (resp.returncode !== 'FAILED') {
             logger.info(`User successfully went through main.joinRouteHandler with [${resp}].`);
             return resolve(resp);
           }
-          const e = new Error('Session not found');
+          const e = new Error('Session not found');          
           logger.error(`User faced [${e}] on main.joinRouteHandler. Error was:`, JSON.stringify(resp));
           return reject(e);
         });
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx
new file mode 100755
index 0000000000000000000000000000000000000000..7c89ec1f1bbd035f3a1316caded8e3081dc9efb3
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx
@@ -0,0 +1,251 @@
+import React, { Component } from 'react';
+import { defineMessages, injectIntl } from 'react-intl';
+import Button from '/imports/ui/components/button/component';
+import Toggle from '/imports/ui/components/switch/component';
+import cx from 'classnames';
+import ModalBase from '/imports/ui/components/modal/base/component';
+import { styles } from './styles';
+
+const intlMessages = defineMessages({
+  lockViewersTitle: {
+    id: 'app.lock-viewers.title',
+    description: 'lock-viewers title',
+  },
+  closeLabel: {
+    id: 'app.shortcut-help.closeLabel',
+    description: 'label for close button',
+  },
+  closeDesc: {
+    id: 'app.shortcut-help.closeDesc',
+    description: 'description for close button',
+  },
+  lockViewersDescription: {
+    id: 'app.lock-viewers.description',
+    description: 'description for lock viewers feature',
+  },
+  featuresLable: {
+    id: 'app.lock-viewers.featuresLable',
+    description: 'features label',
+  },
+  lockStatusLabel: {
+    id: 'app.lock-viewers.lockStatusLabel',
+    description: 'description for close button',
+  },
+  webcamLabel: {
+    id: 'app.lock-viewers.webcamLabel',
+    description: 'description for close button',
+  },
+  otherViewersWebcamLabel: {
+    id: 'app.lock-viewers.otherViewersWebcamLabel',
+    description: 'description for close button',
+  },
+  microphoneLable: {
+    id: 'app.lock-viewers.microphoneLable',
+    description: 'description for close button',
+  },
+  publicChatLabel: {
+    id: 'app.lock-viewers.PublicChatLabel',
+    description: 'description for close button',
+  },
+  privateChatLable: {
+    id: 'app.lock-viewers.PrivateChatLable',
+    description: 'description for close button',
+  },
+  layoutLable: {
+    id: 'app.lock-viewers.Layout',
+    description: 'description for close button',
+  },
+});
+
+class LockViewersComponent extends Component {
+  constructor(props) {
+    super(props);
+    const {
+      closeModal,
+      toggleLockSettings,
+      toggleWebcamsOnlyForModerator,
+    } = props;
+
+    this.closeModal = closeModal;
+    this.toggleLockSettings = toggleLockSettings;
+    this.toggleWebcamsOnlyForModerator = toggleWebcamsOnlyForModerator;
+  }
+
+  render() {
+    const { intl, meeting } = this.props;
+
+    return (
+      <ModalBase
+        overlayClassName={styles.overlay}
+        className={styles.modal}
+        onRequestClose={this.closeModal}
+      >
+
+        <div className={styles.container}>
+          <div className={styles.header}>
+            <div className={styles.title}>{intl.formatMessage(intlMessages.lockViewersTitle)}</div>
+            <Button
+              data-test="modalBaseCloseButton"
+              className={styles.closeBtn}
+              label={intl.formatMessage(intlMessages.closeLabel)}
+              icon="close"
+              size="md"
+              hideLabel
+              onClick={this.closeModal}
+            />
+          </div>
+          <div className={styles.description}>
+            {`${intl.formatMessage(intlMessages.lockViewersDescription)}`}
+          </div>
+
+          <div className={styles.form}>
+            <header className={styles.subHeader}>
+              <div className={styles.bold}>{intl.formatMessage(intlMessages.featuresLable)}</div>
+              <div className={styles.bold}>{intl.formatMessage(intlMessages.lockStatusLabel)}</div>
+            </header>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.webcamLabel)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.lockSettingsProp.disableCam}
+                    onChange={() => {
+                      meeting.lockSettingsProp.disableCam =
+                        !meeting.lockSettingsProp.disableCam;
+                      this.toggleLockSettings(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.webcamLabel)}
+                  />
+                </div>
+              </div>
+            </div>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.usersProp.webcamsOnlyForModerator}
+                    onChange={() => {
+                      meeting.usersProp.webcamsOnlyForModerator =
+                        !meeting.usersProp.webcamsOnlyForModerator;
+                      this.toggleWebcamsOnlyForModerator(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
+                  />
+                </div>
+              </div>
+            </div>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.microphoneLable)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.lockSettingsProp.disableMic}
+                    onChange={() => {
+                      meeting.lockSettingsProp.disableMic =
+                        !meeting.lockSettingsProp.disableMic;
+                      this.toggleLockSettings(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.microphoneLable)}
+                  />
+                </div>
+              </div>
+            </div>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.publicChatLabel)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.lockSettingsProp.disablePubChat}
+                    onChange={() => {
+                      meeting.lockSettingsProp.disablePubChat =
+                        !meeting.lockSettingsProp.disablePubChat;
+                      this.toggleLockSettings(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.publicChatLabel)}
+                  />
+                </div>
+              </div>
+            </div>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.privateChatLable)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.lockSettingsProp.disablePrivChat}
+                    onChange={() => {
+                      meeting.lockSettingsProp.disablePrivChat =
+                        !meeting.lockSettingsProp.disablePrivChat;
+                      this.toggleLockSettings(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.privateChatLable)}
+                  />
+                </div>
+              </div>
+            </div>
+            <div className={styles.row}>
+              <div className={styles.col} aria-hidden="true">
+                <div className={styles.formElement}>
+                  <div className={styles.label}>
+                    {intl.formatMessage(intlMessages.layoutLable)}
+                  </div>
+                </div>
+              </div>
+              <div className={styles.col}>
+                <div className={cx(styles.formElement, styles.pullContentRight)}>
+                  <Toggle
+                    icons={false}
+                    defaultChecked={meeting.lockSettingsProp.lockedLayout}
+                    onChange={() => {
+                      meeting.lockSettingsProp.lockedLayout =
+                        !meeting.lockSettingsProp.lockedLayout;
+                      this.toggleLockSettings(meeting);
+                    }}
+                    ariaLabel={intl.formatMessage(intlMessages.layoutLable)}
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </ModalBase>
+    );
+  }
+}
+
+export default injectIntl(LockViewersComponent);
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
new file mode 100755
index 0000000000000000000000000000000000000000..2ce4edcc89e00bac16de77a4dbc5c48e4f60b8ea
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { withTracker } from 'meteor/react-meteor-data';
+import { withModalMounter } from '/imports/ui/components/modal/service';
+import { makeCall } from '/imports/ui/services/api';
+import Meetings from '/imports/api/meetings';
+import Auth from '/imports/ui/services/auth';
+import LockViewersComponent from './component';
+
+const LockViewersContainer = props => <LockViewersComponent {...props} />;
+
+export default withModalMounter(withTracker(({ mountModal }) => ({
+  closeModal() {
+    mountModal(null);
+  },
+
+  toggleLockSettings(meeting) {
+    makeCall('toggleLockSettings', meeting);
+  },
+
+  toggleWebcamsOnlyForModerator(meeting) {
+    makeCall('toggleWebcamsOnlyForModerator', meeting);
+  },
+  meeting: (Meetings.findOne({ meetingId: Auth.meetingID })),
+}))(LockViewersContainer));
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss b/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss
new file mode 100755
index 0000000000000000000000000000000000000000..078b36e6daa2ef8be0ca1eaad6c887953eda040e
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss
@@ -0,0 +1,118 @@
+@import '/imports/ui/stylesheets/mixins/focus';
+@import '/imports/ui/stylesheets/variables/_all';
+@import "/imports/ui/components/modal/simple/styles";
+
+:root {
+  --modal-margin: 3rem;
+  --title-position-left: 2.2rem;
+  --closeBtn-position-left: 2.5rem;
+}
+
+.title {
+  position: relative;
+  left: var(--title-position-left);
+  color: var(--color-gray-dark);
+  font-weight: bold;
+  font-size: var(--font-size-large);
+  text-align: center;
+}
+
+.form {
+  display: flex;
+  flex-flow: column;
+}
+
+.container {
+  margin: var(--modal-margin);
+  margin-bottom: var(--lg-padding-x);
+}
+
+.subHeader {
+  display: flex;
+  flex-flow: row;
+  flex-grow: 1;
+  justify-content: space-between;
+  color: var(--color-gray-label);
+  font-size: var(--font-size-base);
+  margin-bottom: var(--title-position-left);
+}
+
+.modal {
+  @extend .modal;
+  padding: var(--jumbo-padding-y);
+}
+
+.overlay {
+  @extend .overlay;
+}
+
+.description {
+  text-align: center;
+  color: var(--color-gray);
+  margin-bottom: var(--jumbo-padding-y)
+}
+
+.row {
+  display: flex;
+  flex-flow: row;
+  flex-grow: 1;
+  justify-content: space-between;
+  margin-bottom: var(--md-padding-x);
+}
+
+.col {
+  display: flex;
+  flex-grow: 1;
+  flex-basis: 0;
+  margin-right: var(--md-padding-x);
+}
+
+.label {
+  color: var(--color-gray-label);
+  font-size: var(--font-size-small);
+  margin-bottom: var(--lg-padding-y);
+}
+
+.formElement {
+  position: relative;
+  display: flex;
+  flex-flow: column;
+  flex-grow: 1;
+}
+
+.pullContentRight {
+  display: flex;
+  justify-content: flex-end;
+  flex-flow: row;
+}
+
+.bold {
+  font-weight: bold;
+}
+
+.closeBtn {
+  position: relative;
+  background-color: var(--color-white);
+  left: var(--closeBtn-position-left);
+  bottom: var(--closeBtn-position-left);
+
+  i {
+    color: var(--color-gray-light);
+   }
+  
+  &:focus,
+  &:hover {
+      background-color: var(--color-gray-lighter);
+    i {
+      color: var(--color-gray);
+    }
+  }
+}
+
+.header {
+  margin: 0;
+  padding: 0;
+  border: none;
+  line-height: var(--title-position-left);
+  margin-bottom: var(--lg-padding-y);
+}
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index 8d91aa3c755a47091426bbdeb8654e9f8557bfa7..da34c76529daceaae9077e8b8611f75caa33f844 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -5,6 +5,7 @@ import { Meteor } from 'meteor/meteor';
 import Auth from '/imports/ui/services/auth';
 import Button from '/imports/ui/components/button/component';
 import getFromUserSettings from '/imports/ui/services/users-settings';
+import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
 import Rating from './rating/component';
 import { styles } from './styles';
 
@@ -77,19 +78,6 @@ class MeetingEnded extends React.PureComponent {
     const comment = textarea.value;
     return comment;
   }
-
-  static logoutRouteHandler() {
-    Auth.logout()
-      .then((logoutURL = window.location.origin) => {
-        const protocolPattern = /^((http|https):\/\/)/;
-
-        window.location.href =
-          protocolPattern.test(logoutURL) ?
-            logoutURL :
-            `http://${logoutURL}`;
-      });
-  }
-
   constructor(props) {
     super(props);
     this.state = {
@@ -116,7 +104,7 @@ class MeetingEnded extends React.PureComponent {
     } = this.state;
 
     if (selected <= 0) {
-      MeetingEnded.logoutRouteHandler();
+      logoutRouteHandler();
       return;
     }
 
@@ -138,7 +126,7 @@ class MeetingEnded extends React.PureComponent {
 
     fetch(url, options)
       .finally(() => {
-        MeetingEnded.logoutRouteHandler();
+        logoutRouteHandler();
       });
   }
 
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/styles.scss b/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
index d8de801266d015d2bfbf6c4b70f90fdc9399518c..50e54ffaf1e03fc3259b88ea863cba5b177194ad 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
@@ -121,6 +121,11 @@
   color: var(--color-gray-light);
 }
 
+.userListColumn {
+  @extend %flex-column;
+  min-height: 0;
+}
+
 .enter,
 .appear {
   opacity: 0.01;
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
index 6ab7b7e20fe87ce91b77de1c08f0f4b40b084027..6c671c9f3a88438f609c0b7b07d562423b6f3442 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
@@ -12,7 +12,8 @@
 .container{
   display: flex;
   align-items: center;
-  margin-bottom: .3rem;
+  margin-bottom: var(--lg-padding-y);
+  margin-top: var(--sm-padding-x);
 }
 
 .scrollableList {
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
index b3907e93290f68d1b38faa39d4140b235196442d..2009826c847eb655f9f4928ae792802876a89db1 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
@@ -175,7 +175,7 @@ class UserParticipants extends Component {
     } = this.props;
 
     return (
-      <div>
+      <div className={styles.userListColumn}>
         {
           !compact ?
             <div className={styles.container}>
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
index 3e689940efa29dc8d15164b34155a0cea5c0f528..69c2551259fbbf05e45d55e35b4b5e32043a64a0 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
@@ -9,6 +9,7 @@ import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
 import DropdownContent from '/imports/ui/components/dropdown/content/component';
 import DropdownList from '/imports/ui/components/dropdown/list/component';
 import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
+import LockViewersContainer from '/imports/ui/components/lock-viewers/container';
 import { styles } from './styles';
 
 const propTypes = {
@@ -19,7 +20,6 @@ const propTypes = {
   toggleMuteAllUsers: PropTypes.func.isRequired,
   toggleMuteAllUsersExceptPresenter: PropTypes.func.isRequired,
   toggleStatus: PropTypes.func.isRequired,
-  toggleLockView: PropTypes.func.isRequired,
 };
 
 const intlMessages = defineMessages({
@@ -83,7 +83,8 @@ class UserOptions extends Component {
   }
 
   componentWillMount() {
-    const { intl, isMeetingMuted } = this.props;
+    const { intl, isMeetingMuted, mountModal } = this.props;
+
     this.menuItems = _.compact([
       (<DropdownListItem
         key={_.uniqueId('list-item-')}
@@ -111,7 +112,7 @@ class UserOptions extends Component {
         icon="lock"
         label={intl.formatMessage(intlMessages.lockViewersLabel)}
         description={intl.formatMessage(intlMessages.lockViewersDesc)}
-        onClick={this.props.toggleLockView}
+        onClick={() => mountModal(<LockViewersContainer />)}
       />),
     ]);
 
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
index 59561dab0d68b6c8b63d9e3d42f5b3cd2899ce1f..cf5af07ef7fd03c58eb1b2b82f7c11597c1daffe 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
@@ -1,6 +1,5 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import logger from '/imports/startup/client/logger';
 import Auth from '/imports/ui/services/auth';
 import mapUser from '/imports/ui/services/user/mapUser';
 import Users from '/imports/api/users/';
@@ -26,7 +25,6 @@ export default class UserOptionsContainer extends Component {
 
     this.muteMeeting = this.muteMeeting.bind(this);
     this.muteAllUsersExceptPresenter = this.muteAllUsersExceptPresenter.bind(this);
-    this.handleLockView = this.handleLockView.bind(this);
     this.handleClearStatus = this.handleClearStatus.bind(this);
   }
 
@@ -44,11 +42,6 @@ export default class UserOptionsContainer extends Component {
     muteAllExceptPresenter(currentUser.userId);
   }
 
-  handleLockView() {
-    // Temporary lock method, will be changed in future PR
-    logger.info('handleLockView function');
-  }
-
   handleClearStatus() {
     const { users, setEmojiStatus } = this.props;
 
@@ -71,7 +64,6 @@ export default class UserOptionsContainer extends Component {
         <UserOptions
           toggleMuteAllUsers={this.muteMeeting}
           toggleMuteAllUsersExceptPresenter={this.muteAllUsersExceptPresenter}
-          toggleLockView={this.handleLockView}
           toggleStatus={this.handleClearStatus}
           isMeetingMuted={this.state.meetingMuted}
         /> : null
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
index 1c7fe511e3766b0b6d26e05705b8f1ab6ba7b07b..24095302e509c2d404bbe031ffa32d9e0732c251 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
@@ -388,7 +388,7 @@ class VideoProvider extends Component {
   }
 
   async createWebRTCPeer(id, shareWebcam) {
-    const { meetingId, sessionToken } = this.props;
+    const { meetingId, sessionToken, voiceBridge } = this.props;
     let iceServers = [];
 
     try {
@@ -448,6 +448,7 @@ class VideoProvider extends Component {
             sdpOffer: offerSdp,
             cameraId: id,
             meetingId,
+            voiceBridge,
           };
           this.sendMessage(message);
 
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
index 12bab47d132c9406c24351b9571084d172acd7a9..eb6bbd51d1bc99f4e58ee856baa8451bd36b963e 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
@@ -14,4 +14,5 @@ export default withTracker(() => ({
   sessionToken: VideoService.sessionToken(),
   userName: VideoService.userName(),
   enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
+  voiceBridge: VideoService.voiceBridge(),
 }))(VideoProviderContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/service.js b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
index 5c672116d78bf63326d99ea03bc6e6e5b77f7067..0637da6234d9000cd5f9b17a2638f49cfe97845f 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/service.js
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
@@ -126,6 +126,11 @@ class VideoService {
     return Auth.sessionToken;
   }
 
+  voiceBridge() {
+    const voiceBridge = Meetings.findOne({ meetingId: Auth.meetingID }).voiceProp.voiceConf;
+    return voiceBridge;
+  }
+
   isConnected() {
     return this.isConnected;
   }
@@ -157,4 +162,5 @@ export default {
   meetingId: () => videoService.meetingId(),
   getAllUsersVideo: () => videoService.getAllUsersVideo(),
   sessionToken: () => videoService.sessionToken(),
+  voiceBridge: () => videoService.voiceBridge(),
 };
diff --git a/bigbluebutton-html5/imports/utils/logoutRouteHandler.js b/bigbluebutton-html5/imports/utils/logoutRouteHandler.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0febc21367d1bad7ef5475b4d9df34a3526c949
--- /dev/null
+++ b/bigbluebutton-html5/imports/utils/logoutRouteHandler.js
@@ -0,0 +1,15 @@
+import Auth from '/imports/ui/services/auth';
+
+const logoutRouteHandler = () => {
+  Auth.logout()
+    .then((logoutURL = window.location.origin) => {
+      const protocolPattern = /^((http|https):\/\/)/;
+
+      window.location.href =
+        protocolPattern.test(logoutURL) ?
+          logoutURL :
+          `http://${logoutURL}`;
+    });
+};
+
+export default logoutRouteHandler;
diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json
index 7e390556857e62c8b575d5c16d99af51a5184a1f..abc96fb5fd3ec269087ef70abbe9bc798fb0f0d8 100755
--- a/bigbluebutton-html5/private/locales/en.json
+++ b/bigbluebutton-html5/private/locales/en.json
@@ -359,6 +359,16 @@
     "app.shortcut-help.closePrivateChat": "Close Private Chat",
     "app.shortcut-help.openActions": "Open Actions Menu",
     "app.shortcut-help.openStatus": "Open Status Menu",
+    "app.lock-viewers.title": "Lock Viewers",
+    "app.lock-viewers.description": "These options enable you to restrict certain features available to viewers, such as locking out their ability to use private chat. (These restrictions do no apply to moderators)",
+    "app.lock-viewers.featuresLable": "Feature",
+    "app.lock-viewers.lockStatusLabel": "Locked Status",
+    "app.lock-viewers.webcamLabel": "Webcam",
+    "app.lock-viewers.otherViewersWebcamLabel": "See other viewsers webcams",
+    "app.lock-viewers.microphoneLable": "Microphone",
+    "app.lock-viewers.PublicChatLabel": "Public Chat",
+    "app.lock-viewers.PrivateChatLable": "Private Chat",
+    "app.lock-viewers.Layout": "Layout",
     "app.videoPreview.cameraLabel": "Camera",
     "app.videoPreview.cancelLabel": "Cancel",
     "app.videoPreview.closeLabel": "Close",
diff --git a/build_script.sh b/build_script.sh
index 42265a3dba3e0322c18354c508a13986dff56ce5..a83ccde4eab531a1c81288712d1b5f4129101f64 100755
--- a/build_script.sh
+++ b/build_script.sh
@@ -8,8 +8,17 @@ if [[ $files = *"bigbluebutton-html5"* ]]; then
   meteor npm install
   if [ $1 = linter ]
   then
+    html5_files=""
+    list=$(echo $files | tr " " "\n")
+    for file in $list
+    do
+      if [[ $file = bigbluebutton-html5* ]] ; then
+        html5_files+=" $file"
+      fi
+    done
+
     cd ..
-    bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $files
+    bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $html5_files
   elif [ $1 = acceptance_tests ]
   then
     {