Add users-settings collection and handle dynamic configuration

This commit is contained in:
João Francisco Siebel 2018-09-13 15:09:30 -03:00
parent 45cd830f96
commit 4c35608c2b
27 changed files with 183 additions and 20 deletions

View File

@ -2,6 +2,7 @@ import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
import clearUsers from '/imports/api/users/server/modifiers/clearUsers';
import clearUsersSettings from '/imports/api/users-settings/server/modifiers/clearUsersSettings';
import clearGroupChat from '/imports/api/group-chat/server/modifiers/clearGroupChat';
import clearBreakouts from '/imports/api/breakouts/server/modifiers/clearBreakouts';
import clearAnnotations from '/imports/api/annotations/server/modifiers/clearAnnotations';
@ -21,6 +22,7 @@ export default function removeMeeting(meetingId) {
clearAnnotations(meetingId);
clearSlides(meetingId);
clearUsers(meetingId);
clearUsersSettings(meetingId);
clearVoiceUsers(meetingId);
return Logger.info(`Cleared Meetings with id ${meetingId}`);

View File

@ -0,0 +1,11 @@
import { Meteor } from 'meteor/meteor';
const UserSettings = new Mongo.Collection('users-settings');
if (Meteor.isServer) {
UserSettings._ensureIndex({
meetingId: 1, userId: 1,
});
}
export default UserSettings;

View File

@ -0,0 +1,2 @@
import './methods';
import './publishers';

View File

@ -0,0 +1,6 @@
import { Meteor } from 'meteor/meteor';
import addUserSettings from './methods/addUserSettings';
Meteor.methods({
addUserSettings,
});

View File

@ -0,0 +1,12 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import addUserSetting from '/imports/api/users-settings/server/modifiers/addUserSetting';
export default function addUserSettings(credentials, meetingId, userId, setting, value) {
check(meetingId, String);
check(userId, String);
check(setting, String);
check(value, Match.Any);
return addUserSetting(meetingId, userId, setting, value);
}

View File

@ -0,0 +1,34 @@
import { check } from 'meteor/check';
import UserSettings from '/imports/api/users-settings';
import Logger from '/imports/startup/server/logger';
export default function addUserSetting(meetingId, userId, setting, value) {
check(meetingId, String);
check(userId, String);
check(setting, String);
check(value, Match.Any);
const selector = {
meetingId,
userId,
setting,
};
const modifier = {
$set: {
meetingId,
userId,
setting,
value,
},
};
const cb = (err) => {
if (err) {
return Logger.error(`Adding user setting to collection: ${err}`);
}
return Logger.verbose(`Upserted user setting for meetingId=${meetingId} userId=${userId} setting=${setting}`);
};
return UserSettings.upsert(selector, modifier, cb);
}

View File

@ -0,0 +1,6 @@
import UserSettings from '/imports/api/users-settings';
import Logger from '/imports/startup/server/logger';
export default function clearUsersSettings(meetingId) {
return UserSettings.remove({ meetingId }, Logger.info(`Cleared User Settings (${meetingId})`));
}

View File

@ -0,0 +1,23 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import UserSettings from '/imports/api/users-settings';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function userSettings(credentials) {
const { meetingId, requesterUserId } = credentials;
check(meetingId, String);
check(requesterUserId, String);
Logger.info(`Publishing user settings for user=${requesterUserId}`);
return UserSettings.find({ meetingId, userId: requesterUserId });
}
function publish(...args) {
const boundUserSettings = userSettings.bind(this);
return mapToAcl('subscriptions.users-settings', boundUserSettings)(args);
}
Meteor.publish('users-settings', publish);

View File

@ -1,7 +1,7 @@
import Auth from '/imports/ui/services/auth';
import SessionStorage from '/imports/ui/services/storage/session';
import { setCustomLogoUrl } from '/imports/ui/components/user-list/service';
import { log } from '/imports/ui/services/api';
import { log, makeCall } from '/imports/ui/services/api';
import deviceInfo from '/imports/utils/deviceInfo';
import logger from '/imports/startup/client/logger';
@ -67,6 +67,21 @@ export function joinRouteHandler(nextState, replace, callback) {
const handledHTML5Parameters = [
'html5recordingbot',
// APP
'autoJoin', // OK
'listenOnlyMode', // OK
'forceListenOnly', // OK
'skipCheck', // OK
'clientTitle', // OK
'lockOnJoin', // NOT IMPLEMENTED YET
'askForFeedbackOnLogout', // OK
// BRANDING
'displayBrandingArea', // OK
// KURENTO
'enableScreensharing', // OK
'enableVideo', // OK
'enableVideoStats', // COULDN'T TEST
// WHITEBOARD
];
if (handledHTML5Parameters.indexOf(key) === -1) {
return acc;
@ -79,6 +94,8 @@ export function joinRouteHandler(nextState, replace, callback) {
log('error', `Caught: ${e.message}`);
}
makeCall('addUserSettings', meetingID, internalUserID, key, value);
return { ...acc, [key]: value };
}, {}) : {};

View File

@ -119,7 +119,7 @@ Base.defaultProps = defaultProps;
const SUBSCRIPTIONS_NAME = [
'users', 'meetings', 'polls', 'presentations',
'slides', 'captions', 'breakouts', 'voiceUsers', 'whiteboard-multi-user', 'screenshare',
'group-chat', 'presentation-pods',
'group-chat', 'presentation-pods', 'users-settings',
];
const BaseContainer = withRouter(withTracker(({ params, router }) => {

View File

@ -18,6 +18,8 @@ class ActionsBar extends React.PureComponent {
isUserModerator,
recordSettingsList,
toggleRecording,
screenSharingCheck,
enableVideo,
} = this.props;
const {
@ -45,7 +47,7 @@ class ActionsBar extends React.PureComponent {
</div>
<div className={isUserPresenter ? cx(styles.centerWithActions, actionBarClasses) : styles.center}>
<AudioControlsContainer />
{Meteor.settings.public.kurento.enableVideo ?
{enableVideo ?
<JoinVideoOptionsContainer
handleJoinVideo={handleJoinVideo}
handleCloseVideo={handleExitVideo}
@ -56,6 +58,7 @@ class ActionsBar extends React.PureComponent {
handleUnshareScreen,
isVideoBroadcasting,
isUserPresenter,
screenSharingCheck,
}}
/>
</div>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import getFromUserSettings from '/imports/ui/services/users-settings';
import ActionsBar from './component';
import Service from './service';
import VideoService from '../video-provider/service';
@ -17,4 +18,6 @@ export default withTracker(() => ({
isVideoBroadcasting: isVideoBroadcasting(),
recordSettingsList: Service.recordSettingsList(),
toggleRecording: Service.toggleRecording,
screenSharingCheck: getFromUserSettings('enableScreensharing', Meteor.settings.public.kurento.enableScreensharing),
enableVideo: getFromUserSettings('enableVideo', Meteor.settings.public.kurento.enableVideo),
}))(ActionsBarContainer);

View File

@ -13,6 +13,7 @@ const propTypes = {
handleShareScreen: PropTypes.func.isRequired,
handleUnshareScreen: PropTypes.func.isRequired,
isVideoBroadcasting: PropTypes.bool.isRequired,
screenSharingCheck: PropTypes.bool.isRequired,
};
const intlMessages = defineMessages({
@ -43,7 +44,7 @@ const isMobileBrowser = (BROWSER_RESULTS ? BROWSER_RESULTS.mobile : false) ||
(BROWSER_RESULTS && BROWSER_RESULTS.os ?
BROWSER_RESULTS.os.includes('Android') : // mobile flag doesn't always work
false);
const screenSharingCheck = Meteor.settings.public.kurento.enableScreensharing;
const ICE_CONNECTION_FAILED = 'ICE connection failed';
const DesktopShare = ({
@ -52,6 +53,7 @@ const DesktopShare = ({
handleUnshareScreen,
isVideoBroadcasting,
isUserPresenter,
screenSharingCheck,
}) => {
const onFail = (error) => {
switch (error) {

View File

@ -2,6 +2,7 @@ import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withModalMounter } from '/imports/ui/components/modal/service';
import browser from 'browser-detect';
import getFromUserSettings from '/imports/ui/services/users-settings';
import AudioModal from './component';
import Service from '../service';
@ -9,10 +10,13 @@ const AudioModalContainer = props => <AudioModal {...props} />;
const APP_CONFIG = Meteor.settings.public.app;
const { listenOnlyMode, forceListenOnly, skipCheck } = APP_CONFIG;
export default withModalMounter(withTracker(({ mountModal }) =>
({
export default withModalMounter(withTracker(({ mountModal }) => {
const listenOnlyMode = getFromUserSettings('listenOnlyMode', APP_CONFIG.listenOnlyMode);
const forceListenOnly = getFromUserSettings('forceListenOnly', APP_CONFIG.forceListenOnly);
const skipCheck = getFromUserSettings('skipCheck', APP_CONFIG.skipCheck);
return ({
closeModal: () => {
if (!Service.isConnecting()) mountModal(null);
},
@ -58,4 +62,5 @@ export default withModalMounter(withTracker(({ mountModal }) =>
joinFullAudioEchoTest: !listenOnlyMode && !skipCheck,
forceListenOnlyAttendee: listenOnlyMode && forceListenOnly && !Service.isUserModerator(),
isIOSChrome: browser().name === 'crios',
}))(AudioModalContainer));
});
})(AudioModalContainer));

View File

@ -5,6 +5,7 @@ import { injectIntl, defineMessages } from 'react-intl';
import _ from 'lodash';
import Breakouts from '/imports/api/breakouts';
import { notify } from '/imports/ui/services/notification';
import getFromUserSettings from '/imports/ui/services/users-settings';
import Service from './service';
import AudioModalContainer from './audio-modal/container';
@ -77,7 +78,7 @@ let didMountAutoJoin = false;
export default withModalMounter(injectIntl(withTracker(({ mountModal, intl }) => {
const APP_CONFIG = Meteor.settings.public.app;
const { autoJoin } = APP_CONFIG;
const autoJoin = getFromUserSettings('autoJoin', APP_CONFIG.autoJoin);
const openAudioModal = mountModal.bind(
null,
<AudioModalContainer />,

View File

@ -2,6 +2,7 @@ import React from 'react';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import getFromUserSettings from '/imports/ui/services/users-settings';
import LogoutConfirmation from './component';
import {
isModerator,
@ -14,7 +15,7 @@ const LogoutConfirmationContainer = props => (
export default withRouter(withTracker(({ router }) => {
const APP_CONFIG = Meteor.settings.public.app;
const shouldShowFeedback = !meetingIsBreakout() && APP_CONFIG.askForFeedbackOnLogout;
const shouldShowFeedback = !meetingIsBreakout() && getFromUserSettings('askForFeedbackOnLogout', APP_CONFIG.askForFeedbackOnLogout);
const showFeedback = shouldShowFeedback ? () => router.push('/ended/430') : () => router.push('/logout');
return {

View File

@ -5,6 +5,7 @@ import Settings from '/imports/ui/services/settings';
import { defineMessages, injectIntl } from 'react-intl';
import { notify } from '/imports/ui/services/notification';
import VideoService from '/imports/ui/components/video-provider/service';
import getFromUserSettings from '/imports/ui/services/users-settings';
import Media from './component';
import MediaService, { getSwapLayout } from './service';
import PresentationPodsContainer from '../presentation-pod/container';
@ -120,7 +121,7 @@ export default withTracker(() => {
data.hideOverlay = hidePresentation;
}
const { enableVideo } = Meteor.settings.public.kurento;
const enableVideo = getFromUserSettings('enableVideo', Meteor.settings.public.kurento.enableVideo);
const autoShareWebcam = SessionStorage.getItem('metadata').html5autosharewebcam || false;
if (enableVideo && autoShareWebcam) {

View File

@ -6,6 +6,7 @@ import Users from '/imports/api/users';
import Settings from '/imports/ui/services/settings';
import VideoService from '/imports/ui/components/video-provider/service';
import PollingService from '/imports/ui/components/polling/service';
import getFromUserSettings from '/imports/ui/services/users-settings';
const getPresentationInfo = () => {
const currentPresentation = Presentations.findOne({
@ -24,11 +25,11 @@ function shouldShowWhiteboard() {
}
function shouldShowScreenshare() {
return isVideoBroadcasting() && Meteor.settings.public.kurento.enableScreensharing;
return isVideoBroadcasting() && getFromUserSettings('enableScreensharing', Meteor.settings.public.kurento.enableScreensharing);
}
function shouldShowOverlay() {
return Meteor.settings.public.kurento.enableVideo;
return getFromUserSettings('enableVideo', Meteor.settings.public.kurento.enableVideo);
}
const swapLayout = {

View File

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Auth from '/imports/ui/services/auth';
import Button from '/imports/ui/components/button/component';
import getFromUserSettings from '/imports/ui/services/users-settings';
import Rating from './rating/component';
import { styles } from './styles';
@ -82,7 +83,7 @@ class MeetingEnded extends React.PureComponent {
};
this.setSelectedStar = this.setSelectedStar.bind(this);
this.sendFeedback = this.sendFeedback.bind(this);
this.shouldShowFeedback = Meteor.settings.public.app.askForFeedbackOnLogout;
this.shouldShowFeedback = getFromUserSettings('askForFeedbackOnLogout', Meteor.settings.public.app.askForFeedbackOnLogout);
}
setSelectedStar(starNumber) {
this.setState({

View File

@ -5,6 +5,7 @@ import { withRouter } from 'react-router';
import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
import getFromUserSettings from '/imports/ui/services/users-settings';
import userListService from '../user-list/service';
import ChatService from '../chat/service';
import Service from './service';
@ -12,7 +13,6 @@ import NavBar from './component';
const PUBLIC_CONFIG = Meteor.settings.public;
const PUBLIC_GROUP_CHAT_ID = PUBLIC_CONFIG.chat.public_group_id;
const CLIENT_TITLE = PUBLIC_CONFIG.app.clientTitle;
const NavBarContainer = ({ children, ...props }) => (
<NavBar {...props}>
@ -21,6 +21,8 @@ const NavBarContainer = ({ children, ...props }) => (
);
export default withRouter(withTracker(({ location, router }) => {
const CLIENT_TITLE = getFromUserSettings('clientTitle', PUBLIC_CONFIG.app.clientTitle);
let meetingTitle;
let meetingRecorded;

View File

@ -28,8 +28,9 @@ const propTypes = {
changeRole: PropTypes.func.isRequired,
roving: PropTypes.func.isRequired,
getGroupChatPrivate: PropTypes.func.isRequired,
showBranding: PropTypes.bool.isRequired,
};
const SHOW_BRANDING = Meteor.settings.public.app.branding.displayBrandingArea;
const defaultProps = {
compact: false,
isBreakoutRoom: false,
@ -67,12 +68,13 @@ class UserList extends Component {
handleEmojiChange,
getEmojiList,
getEmoji,
showBranding,
} = this.props;
return (
<div className={styles.userList}>
{
SHOW_BRANDING
showBranding
&& !this.props.compact
&& CustomLogoUrl
? <CustomLogo CustomLogoUrl={CustomLogoUrl} /> : null

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
import Meetings from '/imports/api/meetings';
import getFromUserSettings from '/imports/ui/services/users-settings';
import Service from './service';
import UserList from './component';
@ -51,4 +52,5 @@ export default withTracker(({ chatID, compact }) => ({
handleEmojiChange: Service.setEmojiStatus,
getEmojiList: Service.getEmojiList(),
getEmoji: Service.getEmoji(),
showBranding: getFromUserSettings('displayBrandingArea', Meteor.settings.public.app.branding.displayBrandingArea),
}))(UserListContainer);

View File

@ -1,5 +1,6 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import getFromUserSettings from '/imports/ui/services/users-settings';
import VideoProvider from './component';
import VideoService from './service';
@ -12,5 +13,5 @@ export default withTracker(() => ({
userId: VideoService.userId(),
sessionToken: VideoService.sessionToken(),
userName: VideoService.userName(),
enableVideoStats: Meteor.settings.public.kurento.enableVideoStats,
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
}))(VideoProviderContainer);

View File

@ -0,0 +1,22 @@
import Auth from '/imports/ui/services/auth';
import UserSettings from '/imports/api/users-settings';
export default function getFromUserSettings(setting, defaultValue) {
const { meetingID: meetingId, userID: userId } = Auth;
const selector = {
meetingId,
userId,
setting,
};
const userSetting = UserSettings.findOne(selector);
console.log('XIMIRA', selector, userSetting);
if (userSetting !== undefined) {
return userSetting.value;
}
return defaultValue;
}

View File

@ -111,7 +111,8 @@
"whiteboard-multi-user",
"presentation-pods",
"group-chat",
"group-chat-msg"
"group-chat-msg",
"users-settings"
],
"methods": [
"listenOnlyToggle",

View File

@ -111,7 +111,8 @@
"whiteboard-multi-user",
"presentation-pods",
"group-chat",
"group-chat-msg"
"group-chat-msg",
"users-settings"
],
"methods": [
"listenOnlyToggle",

View File

@ -14,6 +14,7 @@ import '/imports/api/breakouts/server';
import '/imports/api/group-chat/server';
import '/imports/api/group-chat-msg/server';
import '/imports/api/screenshare/server';
import '/imports/api/users-settings/server';
import '/imports/api/voice-users/server';
import '/imports/api/whiteboard-multi-user/server';
import '/imports/api/video/server';