2017-10-12 06:17:42 +08:00
|
|
|
import flat from 'flat';
|
2019-03-20 04:06:13 +08:00
|
|
|
import {
|
|
|
|
check,
|
|
|
|
Match,
|
|
|
|
} from 'meteor/check';
|
2020-07-28 04:33:10 +08:00
|
|
|
import SanitizeHTML from 'sanitize-html';
|
2021-05-27 03:09:25 +08:00
|
|
|
import Meetings, {
|
|
|
|
RecordMeetings,
|
|
|
|
ExternalVideoMeetings,
|
2022-01-21 01:50:16 +08:00
|
|
|
LayoutMeetings,
|
2021-05-27 03:09:25 +08:00
|
|
|
} from '/imports/api/meetings';
|
2016-10-22 00:27:47 +08:00
|
|
|
import Logger from '/imports/startup/server/logger';
|
2021-10-16 03:07:13 +08:00
|
|
|
import { initPads } from '/imports/api/pads/server/helpers';
|
2023-05-11 04:03:20 +08:00
|
|
|
import createTimer from '/imports/api/timer/server/methods/createTimer';
|
2021-10-16 03:07:13 +08:00
|
|
|
import { initCaptions } from '/imports/api/captions/server/helpers';
|
2019-10-23 09:26:25 +08:00
|
|
|
import { addAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
|
2019-10-25 04:48:03 +08:00
|
|
|
import { addCursorStreamer } from '/imports/api/cursor/server/streamer';
|
2020-08-28 18:58:55 +08:00
|
|
|
import { addExternalVideoStreamer } from '/imports/api/external-videos/server/streamer';
|
2023-06-08 09:10:07 +08:00
|
|
|
import addUserReactionsObserver from '/imports/api/user-reaction/server/helpers';
|
2021-07-19 22:02:58 +08:00
|
|
|
import { LAYOUT_TYPE } from '/imports/ui/components/layout/enums';
|
2016-10-22 00:27:47 +08:00
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
const addExternalVideo = async (meetingId) => {
|
2021-05-27 03:09:25 +08:00
|
|
|
const selector = { meetingId };
|
|
|
|
|
|
|
|
const modifier = {
|
|
|
|
meetingId,
|
|
|
|
externalVideoUrl: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
2023-03-14 00:59:52 +08:00
|
|
|
const { numberAffected } = await ExternalVideoMeetings.upsertAsync(selector, modifier);
|
2021-05-27 03:09:25 +08:00
|
|
|
|
|
|
|
if (numberAffected) {
|
|
|
|
Logger.verbose(`Added external video meetingId=${meetingId}`);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
Logger.error(`Adding external video: ${err}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
const addLayout = async (meetingId, layout) => {
|
2022-01-21 01:50:16 +08:00
|
|
|
const selector = { meetingId };
|
|
|
|
|
|
|
|
const modifier = {
|
|
|
|
meetingId,
|
|
|
|
layout,
|
2022-02-05 02:03:39 +08:00
|
|
|
layoutUpdatedAt: new Date().getTime(),
|
|
|
|
presentationIsOpen: true,
|
2022-03-23 02:29:49 +08:00
|
|
|
isResizing: false,
|
2022-02-05 02:03:39 +08:00
|
|
|
cameraPosition: 'contentTop',
|
|
|
|
focusedCamera: 'none',
|
|
|
|
presentationVideoRate: 0,
|
2022-03-09 23:09:56 +08:00
|
|
|
pushLayout: false,
|
2022-01-21 01:50:16 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
2023-03-14 00:59:52 +08:00
|
|
|
const { numberAffected } = await LayoutMeetings.upsertAsync(selector, modifier);
|
2022-01-21 01:50:16 +08:00
|
|
|
|
|
|
|
if (numberAffected) {
|
|
|
|
Logger.verbose(`Added layout meetingId=${meetingId}`, numberAffected);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
Logger.error(`Adding layout: ${err}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
export default async function addMeeting(meeting) {
|
2017-10-12 06:17:42 +08:00
|
|
|
const meetingId = meeting.meetingProp.intId;
|
2016-10-22 00:27:47 +08:00
|
|
|
|
|
|
|
check(meetingId, String);
|
2017-10-12 06:17:42 +08:00
|
|
|
check(meeting, {
|
|
|
|
breakoutProps: {
|
|
|
|
sequence: Number,
|
2018-05-09 01:44:45 +08:00
|
|
|
freeJoin: Boolean,
|
2017-10-12 06:17:42 +08:00
|
|
|
breakoutRooms: Array,
|
|
|
|
parentId: String,
|
2019-04-10 21:44:34 +08:00
|
|
|
record: Boolean,
|
|
|
|
privateChatEnabled: Boolean,
|
2022-09-20 23:43:13 +08:00
|
|
|
captureNotes: Boolean,
|
2022-10-20 01:09:11 +08:00
|
|
|
captureSlides: Boolean,
|
2023-01-17 06:00:27 +08:00
|
|
|
captureNotesFilename: String,
|
|
|
|
captureSlidesFilename: String,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
meetingProp: {
|
|
|
|
intId: String,
|
|
|
|
extId: String,
|
2022-03-02 23:58:02 +08:00
|
|
|
meetingCameraCap: Number,
|
2022-04-15 03:47:36 +08:00
|
|
|
maxPinnedCameras: Number,
|
2017-10-12 06:17:42 +08:00
|
|
|
isBreakout: Boolean,
|
|
|
|
name: String,
|
2022-02-08 02:55:45 +08:00
|
|
|
disabledFeatures: Array,
|
2022-04-05 03:52:43 +08:00
|
|
|
notifyRecordingIsOn: Boolean,
|
2022-09-12 22:04:13 +08:00
|
|
|
presentationUploadExternalDescription: String,
|
|
|
|
presentationUploadExternalUrl: String,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
usersProp: {
|
2022-10-18 04:30:53 +08:00
|
|
|
maxUsers: Number,
|
|
|
|
maxUserConcurrentAccesses: Number,
|
2017-10-12 06:17:42 +08:00
|
|
|
webcamsOnlyForModerator: Boolean,
|
2022-02-22 21:33:26 +08:00
|
|
|
userCameraCap: Number,
|
2017-10-12 06:17:42 +08:00
|
|
|
guestPolicy: String,
|
2019-04-10 04:52:48 +08:00
|
|
|
authenticatedGuest: Boolean,
|
2024-06-04 22:36:46 +08:00
|
|
|
allowPromoteGuestToModerator: Boolean,
|
2019-04-23 23:30:55 +08:00
|
|
|
allowModsToUnmuteUsers: Boolean,
|
2021-12-14 03:15:19 +08:00
|
|
|
allowModsToEjectCameras: Boolean,
|
2021-07-19 22:02:58 +08:00
|
|
|
meetingLayout: String,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
durationProps: {
|
|
|
|
createdTime: Number,
|
|
|
|
duration: Number,
|
|
|
|
createdDate: String,
|
|
|
|
meetingExpireIfNoUserJoinedInMinutes: Number,
|
|
|
|
meetingExpireWhenLastUserLeftInMinutes: Number,
|
2018-08-03 22:03:16 +08:00
|
|
|
userInactivityInspectTimerInMinutes: Number,
|
|
|
|
userInactivityThresholdInMinutes: Number,
|
|
|
|
userActivitySignResponseDelayInMinutes: Number,
|
2021-05-18 04:52:59 +08:00
|
|
|
endWhenNoModerator: Boolean,
|
|
|
|
endWhenNoModeratorDelayInMinutes: Number,
|
2019-01-24 00:13:03 +08:00
|
|
|
timeRemaining: Number,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
welcomeProp: {
|
|
|
|
welcomeMsg: String,
|
|
|
|
modOnlyMessage: String,
|
|
|
|
welcomeMsgTemplate: String,
|
|
|
|
},
|
2018-12-12 04:37:31 +08:00
|
|
|
recordProp: Match.ObjectIncluding({
|
2017-10-12 06:17:42 +08:00
|
|
|
allowStartStopRecording: Boolean,
|
|
|
|
autoStartRecording: Boolean,
|
2019-02-20 06:29:23 +08:00
|
|
|
record: Boolean,
|
2018-12-12 04:37:31 +08:00
|
|
|
}),
|
2017-10-12 06:17:42 +08:00
|
|
|
password: {
|
|
|
|
viewerPass: String,
|
|
|
|
moderatorPass: String,
|
2021-08-25 22:38:35 +08:00
|
|
|
learningDashboardAccessToken: String,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
voiceProp: {
|
|
|
|
voiceConf: String,
|
|
|
|
dialNumber: String,
|
|
|
|
telVoice: String,
|
2017-12-23 04:22:24 +08:00
|
|
|
muteOnStart: Boolean,
|
2017-10-12 06:17:42 +08:00
|
|
|
},
|
|
|
|
metadataProp: Object,
|
2019-04-10 21:44:34 +08:00
|
|
|
lockSettingsProps: {
|
|
|
|
disableCam: Boolean,
|
|
|
|
disableMic: Boolean,
|
|
|
|
disablePrivateChat: Boolean,
|
|
|
|
disablePublicChat: Boolean,
|
2021-10-16 03:07:13 +08:00
|
|
|
disableNotes: Boolean,
|
2019-08-10 07:45:26 +08:00
|
|
|
hideUserList: Boolean,
|
2019-04-10 21:44:34 +08:00
|
|
|
lockOnJoin: Boolean,
|
|
|
|
lockOnJoinConfigurable: Boolean,
|
2022-03-22 02:25:41 +08:00
|
|
|
hideViewersCursor: Boolean,
|
2022-12-13 07:26:03 +08:00
|
|
|
hideViewersAnnotation: Boolean,
|
2019-04-10 21:44:34 +08:00
|
|
|
},
|
2020-12-12 05:36:06 +08:00
|
|
|
systemProps: {
|
|
|
|
html5InstanceId: Number,
|
|
|
|
},
|
2022-02-08 03:00:13 +08:00
|
|
|
groups: Array,
|
2017-10-12 06:17:42 +08:00
|
|
|
});
|
2016-10-22 00:27:47 +08:00
|
|
|
|
2019-08-22 01:42:37 +08:00
|
|
|
const {
|
|
|
|
recordProp,
|
|
|
|
...restProps
|
|
|
|
} = meeting;
|
|
|
|
|
|
|
|
const newMeeting = restProps;
|
2019-02-20 06:29:23 +08:00
|
|
|
|
2016-10-22 00:27:47 +08:00
|
|
|
const selector = {
|
2016-10-24 19:20:30 +08:00
|
|
|
meetingId,
|
2016-10-22 00:27:47 +08:00
|
|
|
};
|
|
|
|
|
2019-04-10 21:44:34 +08:00
|
|
|
newMeeting.lockSettingsProps = Object.assign(meeting.lockSettingsProps, { setBy: 'temp' });
|
2018-11-10 06:03:25 +08:00
|
|
|
|
2019-04-06 02:33:09 +08:00
|
|
|
const meetingEnded = false;
|
2019-03-20 04:06:13 +08:00
|
|
|
|
2020-07-28 04:33:10 +08:00
|
|
|
let { welcomeMsg } = newMeeting.welcomeProp;
|
2020-08-11 04:59:09 +08:00
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
const sanitizeTextInChat = (original) => SanitizeHTML(original, {
|
2020-08-11 04:59:09 +08:00
|
|
|
allowedTags: ['a', 'b', 'br', 'i', 'img', 'li', 'small', 'span', 'strong', 'u', 'ul'],
|
2020-07-28 04:33:10 +08:00
|
|
|
allowedAttributes: {
|
2022-04-05 03:53:01 +08:00
|
|
|
a: ['href', 'target'],
|
2020-08-11 04:59:09 +08:00
|
|
|
img: ['src', 'width', 'height'],
|
2020-07-28 04:33:10 +08:00
|
|
|
},
|
2020-07-30 04:08:47 +08:00
|
|
|
allowedSchemes: ['https'],
|
2022-04-05 03:53:01 +08:00
|
|
|
allowedSchemesByTag: {
|
2023-03-14 00:59:52 +08:00
|
|
|
a: ['https', 'mailto', 'tel'],
|
|
|
|
},
|
2020-07-28 04:33:10 +08:00
|
|
|
});
|
2020-08-11 04:59:09 +08:00
|
|
|
|
|
|
|
const sanitizedWelcomeText = sanitizeTextInChat(welcomeMsg);
|
2020-07-29 23:31:00 +08:00
|
|
|
welcomeMsg = sanitizedWelcomeText.replace(
|
2019-02-23 06:08:44 +08:00
|
|
|
'href="event:',
|
|
|
|
'href="',
|
|
|
|
);
|
|
|
|
|
2019-02-26 01:01:28 +08:00
|
|
|
const insertBlankTarget = (s, i) => `${s.substr(0, i)} target="_blank"${s.substr(i)}`;
|
|
|
|
const linkWithoutTarget = new RegExp('<a href="(.*?)">', 'g');
|
|
|
|
|
2022-04-05 03:53:01 +08:00
|
|
|
do {
|
|
|
|
linkWithoutTarget.test(welcomeMsg);
|
|
|
|
|
|
|
|
if (linkWithoutTarget.lastIndex > 0) {
|
|
|
|
welcomeMsg = insertBlankTarget(
|
|
|
|
welcomeMsg,
|
|
|
|
linkWithoutTarget.lastIndex - 1,
|
|
|
|
);
|
2023-03-14 00:59:52 +08:00
|
|
|
linkWithoutTarget.lastIndex -= 1;
|
2022-04-05 03:53:01 +08:00
|
|
|
}
|
|
|
|
} while (linkWithoutTarget.lastIndex > 0);
|
2019-02-20 06:29:23 +08:00
|
|
|
|
2020-07-29 23:31:00 +08:00
|
|
|
newMeeting.welcomeProp.welcomeMsg = welcomeMsg;
|
|
|
|
|
|
|
|
// note: as of July 2020 `modOnlyMessage` is not published to the client side.
|
|
|
|
// We are sanitizing this data simply to prevent future potential usage
|
|
|
|
// At the moment `modOnlyMessage` is obtained from client side as a response to Enter API
|
2020-08-11 04:59:09 +08:00
|
|
|
newMeeting.welcomeProp.modOnlyMessage = sanitizeTextInChat(newMeeting.welcomeProp.modOnlyMessage);
|
2020-07-29 23:31:00 +08:00
|
|
|
|
2022-02-26 01:56:03 +08:00
|
|
|
const { meetingLayout } = meeting.usersProp;
|
|
|
|
|
2016-10-22 00:27:47 +08:00
|
|
|
const modifier = {
|
2023-03-14 00:59:52 +08:00
|
|
|
$set: {
|
2019-03-20 04:06:13 +08:00
|
|
|
meetingId,
|
2019-04-06 02:33:09 +08:00
|
|
|
meetingEnded,
|
2022-02-26 01:56:03 +08:00
|
|
|
layout: LAYOUT_TYPE[meetingLayout] || 'smart',
|
2019-05-22 22:44:17 +08:00
|
|
|
publishedPoll: false,
|
2020-10-03 01:29:27 +08:00
|
|
|
guestLobbyMessage: '',
|
2021-04-15 23:12:10 +08:00
|
|
|
randomlySelectedUser: [],
|
2023-03-14 00:59:52 +08:00
|
|
|
...flat(newMeeting, {
|
|
|
|
safe: true,
|
|
|
|
}),
|
|
|
|
},
|
2016-10-22 00:27:47 +08:00
|
|
|
};
|
|
|
|
|
2021-02-16 23:12:25 +08:00
|
|
|
if (!process.env.BBB_HTML5_ROLE || process.env.BBB_HTML5_ROLE === 'frontend') {
|
|
|
|
addAnnotationsStreamer(meetingId);
|
|
|
|
addCursorStreamer(meetingId);
|
2021-02-21 19:04:39 +08:00
|
|
|
addExternalVideoStreamer(meetingId);
|
2021-02-16 23:12:25 +08:00
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
// we don't want to fully process the create meeting message
|
|
|
|
// in frontend since it can lead to duplication of meetings in mongo.
|
2021-02-16 23:12:25 +08:00
|
|
|
if (process.env.BBB_HTML5_ROLE === 'frontend') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-27 00:23:57 +08:00
|
|
|
try {
|
2023-03-14 00:59:52 +08:00
|
|
|
const {
|
|
|
|
insertedId,
|
|
|
|
numberAffected,
|
|
|
|
} = await RecordMeetings.upsertAsync(selector, { meetingId, ...recordProp });
|
2020-11-27 00:23:57 +08:00
|
|
|
|
|
|
|
if (insertedId) {
|
|
|
|
Logger.info(`Added record prop id=${meetingId}`);
|
|
|
|
} else if (numberAffected) {
|
|
|
|
Logger.info(`Upserted record prop id=${meetingId}`);
|
2016-10-22 00:27:47 +08:00
|
|
|
}
|
2020-11-27 00:23:57 +08:00
|
|
|
} catch (err) {
|
|
|
|
Logger.error(`Adding record prop to collection: ${err}`);
|
|
|
|
}
|
|
|
|
|
2023-03-14 00:59:52 +08:00
|
|
|
await addExternalVideo(meetingId);
|
|
|
|
await addLayout(meetingId, LAYOUT_TYPE[meetingLayout] || 'smart');
|
2021-05-27 03:09:25 +08:00
|
|
|
|
2020-11-27 00:23:57 +08:00
|
|
|
try {
|
2023-03-14 00:59:52 +08:00
|
|
|
const { insertedId, numberAffected } = await Meetings.upsertAsync(selector, modifier);
|
2016-10-22 00:27:47 +08:00
|
|
|
|
|
|
|
if (insertedId) {
|
2017-10-13 03:07:02 +08:00
|
|
|
Logger.info(`Added meeting id=${meetingId}`);
|
2023-05-11 04:03:20 +08:00
|
|
|
// Init Timer collection
|
|
|
|
createTimer(meetingId);
|
2022-03-10 03:23:38 +08:00
|
|
|
if (newMeeting.meetingProp.disabledFeatures.indexOf('sharedNotes') === -1) {
|
|
|
|
initPads(meetingId);
|
|
|
|
}
|
2022-03-11 02:02:20 +08:00
|
|
|
if (newMeeting.meetingProp.disabledFeatures.indexOf('captions') === -1) {
|
2023-03-14 00:59:52 +08:00
|
|
|
await initCaptions(meetingId);
|
2022-03-11 02:02:20 +08:00
|
|
|
}
|
2023-05-26 04:32:27 +08:00
|
|
|
if (newMeeting.meetingProp.disabledFeatures.indexOf('reactions') === -1) {
|
|
|
|
await addUserReactionsObserver(meetingId);
|
|
|
|
}
|
2020-11-27 00:23:57 +08:00
|
|
|
} else if (numberAffected) {
|
2017-10-13 03:07:02 +08:00
|
|
|
Logger.info(`Upserted meeting id=${meetingId}`);
|
2016-10-22 00:27:47 +08:00
|
|
|
}
|
2020-11-27 00:23:57 +08:00
|
|
|
} catch (err) {
|
|
|
|
Logger.error(`Adding meeting to collection: ${err}`);
|
|
|
|
}
|
2017-06-03 03:25:02 +08:00
|
|
|
}
|