Merge pull request #15390 from ramonlsouza/merge25-26-jul14
chore: Merge v2.5.3 into v2.6
This commit is contained in:
commit
f2e25768c7
@ -83,7 +83,7 @@ public class ThumbnailCreatorImp implements ThumbnailCreator {
|
||||
COMMAND = IMAGEMAGICK_DIR + File.separatorChar + "convert -thumbnail 150x150 " + source + " " + dest;
|
||||
} else {
|
||||
dest = thumbsDir.getAbsolutePath() + File.separatorChar + TEMP_THUMB_NAME + "-" + page; // the "-x.png" is appended automagically
|
||||
COMMAND = "pdftocairo -png -scale-to 150 " + source + " " + dest;
|
||||
COMMAND = "pdftocairo -png -scale-to 150 -cropbox " + source + " " + dest;
|
||||
}
|
||||
|
||||
//System.out.println(COMMAND);
|
||||
|
@ -53,6 +53,24 @@ export function getSumOfTime(eventsArr) {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function getJoinTime(eventsArr) {
|
||||
return eventsArr.reduce((prevVal, elem) => {
|
||||
if (prevVal === 0 || elem.registeredOn < prevVal) {
|
||||
return elem.registeredOn;
|
||||
}
|
||||
return prevVal;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function getLeaveTime(eventsArr) {
|
||||
return eventsArr.reduce((prevVal, elem) => {
|
||||
if (elem.leftOn > prevVal) {
|
||||
return elem.leftOn;
|
||||
}
|
||||
return prevVal;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function tsToHHmmss(ts) {
|
||||
return (new Date(ts).toISOString().substr(11, 8));
|
||||
}
|
||||
@ -121,6 +139,8 @@ export function makeUserCSVData(users, polls, intl) {
|
||||
const user = userValues[i];
|
||||
const webcam = getSumOfTime(user.webcams);
|
||||
const duration = getSumOfTime(Object.values(user.intIds));
|
||||
const joinTime = getJoinTime(Object.values(user.intIds));
|
||||
const leaveTime = getLeaveTime(Object.values(user.intIds));
|
||||
|
||||
const userData = {
|
||||
name: user.name,
|
||||
@ -138,7 +158,7 @@ export function makeUserCSVData(users, polls, intl) {
|
||||
raiseHand: filterUserEmojis(user, 'raiseHand').length,
|
||||
answers: Object.keys(user.answers).length,
|
||||
emojis: filterUserEmojis(user, skipEmojis).length,
|
||||
registeredOn: intl.formatDate(user.registeredOn, {
|
||||
registeredOn: intl.formatDate(joinTime, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
@ -146,14 +166,14 @@ export function makeUserCSVData(users, polls, intl) {
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}),
|
||||
leftOn: intl.formatDate(user.leftOn, {
|
||||
leftOn: leaveTime > 0 ? intl.formatDate(leaveTime, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}),
|
||||
}) : '-',
|
||||
duration: tsToHHmmss(duration),
|
||||
};
|
||||
|
||||
|
@ -1 +1 @@
|
||||
git clone --branch v1.2.1 --depth 1 https://github.com/bigbluebutton/bbb-pads bbb-pads
|
||||
git clone --branch v1.2.2 --depth 1 https://github.com/bigbluebutton/bbb-pads bbb-pads
|
||||
|
@ -276,7 +276,7 @@ usage() {
|
||||
echo "Configuration:"
|
||||
echo " --version Display BigBlueButton version (packages)"
|
||||
echo " --setip <IP/hostname> Set IP/hostname for BigBlueButton"
|
||||
echo " --setsecret <secret> Change the shared secret in bigbluebutton.properties"
|
||||
echo " --setsecret <secret> Change the shared secret in /etc/bigbluebutton/bbb-web.properties"
|
||||
echo " --set-port-range MIN-MAX Change UDP port range used for audio/video/screenshare"
|
||||
echo
|
||||
echo "Monitoring:"
|
||||
@ -377,17 +377,17 @@ start_bigbluebutton () {
|
||||
|
||||
if systemctl list-units --full -all | grep -q $TOMCAT_USER.service; then
|
||||
TOMCAT_SERVICE=$TOMCAT_USER
|
||||
|
||||
systemctl start $TOMCAT_SERVICE || {
|
||||
echo
|
||||
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI."
|
||||
echo "# Run the command:"
|
||||
echo "# sudo journalctl -u $TOMCAT_SERVICE"
|
||||
echo "# To better understand the ERROR"
|
||||
}
|
||||
fi
|
||||
|
||||
systemctl start bigbluebutton.target
|
||||
systemctl start $TOMCAT_SERVICE || {
|
||||
echo
|
||||
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI."
|
||||
echo "# Run the command:"
|
||||
echo "# sudo journalctl -u $TOMCAT_SERVICE"
|
||||
echo "# To better understand the ERROR"
|
||||
}
|
||||
|
||||
systemctl start bigbluebutton.target nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
|
||||
|
||||
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
|
||||
systemctl start mongod
|
||||
|
@ -1,4 +1,5 @@
|
||||
import addSystemMsg from '../../../group-chat-msg/server/modifiers/addSystemMsg';
|
||||
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';
|
||||
|
||||
export default function sendPollChatMsg({ body }, meetingId) {
|
||||
const { poll } = body;
|
||||
@ -10,9 +11,14 @@ export default function sendPollChatMsg({ body }, meetingId) {
|
||||
const SYSTEM_CHAT_TYPE = CHAT_CONFIG.type_system;
|
||||
|
||||
const pollResultData = poll;
|
||||
const answers = pollResultData.answers.reduce(caseInsensitiveReducer, []);
|
||||
|
||||
const extra = {
|
||||
type: 'poll',
|
||||
pollResultData,
|
||||
pollResultData: {
|
||||
...pollResultData,
|
||||
answers,
|
||||
},
|
||||
};
|
||||
|
||||
const payload = {
|
||||
|
@ -32,10 +32,6 @@ const intlMessages = defineMessages({
|
||||
id: 'app.actionsBar.actionsDropdown.desktopShareLabel',
|
||||
description: 'Desktop Share option label',
|
||||
},
|
||||
lockedDesktopShareLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.lockedDesktopShareLabel',
|
||||
description: 'Desktop locked Share option label',
|
||||
},
|
||||
stopDesktopShareLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.stopDesktopShareLabel',
|
||||
description: 'Stop Desktop Share option label',
|
||||
@ -158,11 +154,10 @@ const ScreenshareButton = ({
|
||||
</Styled.ScreenShareModal>,
|
||||
);
|
||||
|
||||
const screenshareLocked = screenshareDataSavingSetting
|
||||
? intlMessages.desktopShareLabel : intlMessages.lockedDesktopShareLabel;
|
||||
const screenshareLabel = intlMessages.desktopShareLabel;
|
||||
|
||||
const vLabel = isVideoBroadcasting
|
||||
? intlMessages.stopDesktopShareLabel : screenshareLocked;
|
||||
? intlMessages.stopDesktopShareLabel : screenshareLabel;
|
||||
|
||||
const vDescr = isVideoBroadcasting
|
||||
? intlMessages.stopDesktopShareDesc : intlMessages.desktopShareDesc;
|
||||
@ -171,13 +166,12 @@ const ScreenshareButton = ({
|
||||
&& ( !isMobile || isMobileApp)
|
||||
&& amIPresenter;
|
||||
|
||||
const dataTest = !screenshareDataSavingSetting ? 'screenshareLocked'
|
||||
: isVideoBroadcasting ? 'stopScreenShare' : 'startScreenShare';
|
||||
const dataTest = isVideoBroadcasting ? 'stopScreenShare' : 'startScreenShare';
|
||||
|
||||
return shouldAllowScreensharing
|
||||
? (
|
||||
<Button
|
||||
disabled={(!isMeteorConnected && !isVideoBroadcasting) || !screenshareDataSavingSetting}
|
||||
disabled={(!isMeteorConnected && !isVideoBroadcasting)}
|
||||
icon={isVideoBroadcasting ? 'desktop' : 'desktop_off'}
|
||||
data-test={dataTest}
|
||||
label={intl.formatMessage(vLabel)}
|
||||
|
@ -26,6 +26,7 @@ const AudioModalButton = styled(Button)`
|
||||
|
||||
// Modifies the audio button icon colour
|
||||
& span:first-child {
|
||||
display: inline-block;
|
||||
color: #1b3c4b;
|
||||
background-color: #f1f8ff;
|
||||
box-shadow: none;
|
||||
@ -46,6 +47,7 @@ const AudioModalButton = styled(Button)`
|
||||
|
||||
// Modifies the button label text
|
||||
& span:last-child {
|
||||
display: block;
|
||||
color: black;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
|
@ -2,9 +2,9 @@ import React, { PureComponent } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
||||
import _ from 'lodash';
|
||||
import BBBMenu from "/imports/ui/components/common/menu/component";
|
||||
import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||
import { getDateString } from '/imports/utils/string-utils';
|
||||
import Trigger from "/imports/ui/components/common/control-header/right/component";
|
||||
import Trigger from '/imports/ui/components/common/control-header/right/component';
|
||||
|
||||
import ChatService from '../service';
|
||||
import { addNewAlert } from '../../screenreader-alert/service';
|
||||
|
@ -97,7 +97,7 @@ const Adapter = () => {
|
||||
more info: https://github.com/bigbluebutton/bigbluebutton/issues/11842 */
|
||||
useEffect(() => {
|
||||
if (users[Auth.meetingID] && users[Auth.meetingID][Auth.userID]) {
|
||||
if (currentUserData?.role !== users[Auth.meetingID][Auth.userID].role) {
|
||||
if (currentUserData?.role !== users[Auth.meetingID][Auth.userID]?.role) {
|
||||
prevUserData = currentUserData;
|
||||
}
|
||||
currentUserData = users[Auth.meetingID][Auth.userID];
|
||||
|
@ -30,7 +30,15 @@ const Adapter = () => {
|
||||
},
|
||||
});
|
||||
},
|
||||
removed: () => {},
|
||||
removed: (obj) => {
|
||||
ChatLogger.debug('usersAdapter::observe::removed', obj);
|
||||
dispatch({
|
||||
type: ACTIONS.REMOVED,
|
||||
value: {
|
||||
user: obj,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
@ -43,19 +43,32 @@ const reducer = (state, action) => {
|
||||
ChatLogger.debug('UsersContextProvider::reducer::removed', { ...action });
|
||||
|
||||
const { user } = action.value;
|
||||
if (state[user.meetingId][user.userId]) {
|
||||
const stateUser = state[user.meetingId][user.userId];
|
||||
if (stateUser) {
|
||||
const newState = { ...state };
|
||||
delete newState[user.meetingId][user.userId];
|
||||
newState[user.meetingId][user.userId] = {
|
||||
...stateUser,
|
||||
loggedOut: true,
|
||||
};
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
return state;
|
||||
return state;
|
||||
}
|
||||
|
||||
// USER PERSISTENT DATA
|
||||
case ACTIONS.ADDED_USER_PERSISTENT_DATA: {
|
||||
const { user } = action.value;
|
||||
if (state[user.meetingId] && state[user.meetingId][user.userId]) {
|
||||
if (state[user.meetingId][user.userId].loggedOut) {
|
||||
const newState = { ...state };
|
||||
newState[user.meetingId][user.userId] = {
|
||||
...state[user.meetingId][user.userId],
|
||||
loggedOut: false,
|
||||
};
|
||||
|
||||
return newState;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -627,6 +627,7 @@ class ConnectionStatusComponent extends PureComponent {
|
||||
<Styled.Copy
|
||||
disabled={!hasNetworkData}
|
||||
role="button"
|
||||
data-test="copyStats"
|
||||
onClick={this.copyNetworkData.bind(this)}
|
||||
onKeyPress={this.copyNetworkData.bind(this)}
|
||||
tabIndex={0}
|
||||
|
@ -5,6 +5,10 @@ import logger from '/imports/startup/client/logger';
|
||||
const Cursor = new Mongo.Collection(null);
|
||||
let cursorStreamListener = null;
|
||||
|
||||
export const clearCursors = () => {
|
||||
Cursor.remove({});
|
||||
};
|
||||
|
||||
function updateCursor(userId, payload) {
|
||||
const selector = {
|
||||
userId,
|
||||
|
@ -5,6 +5,7 @@ import Settings from '/imports/ui/services/settings';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import { isExternalVideoEnabled, isScreenSharingEnabled } from '/imports/ui/services/features';
|
||||
import { ACTIONS } from '../layout/enums';
|
||||
import UserService from '/imports/ui/components/user-list/service';
|
||||
|
||||
const LAYOUT_CONFIG = Meteor.settings.public.layout;
|
||||
const KURENTO_CONFIG = Meteor.settings.public.kurento;
|
||||
@ -26,7 +27,7 @@ function shouldShowWhiteboard() {
|
||||
|
||||
function shouldShowScreenshare() {
|
||||
const { viewScreenshare } = Settings.dataSaving;
|
||||
return isScreenSharingEnabled() && viewScreenshare && isVideoBroadcasting();
|
||||
return isScreenSharingEnabled() && (viewScreenshare || UserService.isUserPresenter()) && isVideoBroadcasting();
|
||||
}
|
||||
|
||||
function shouldShowExternalVideo() {
|
||||
|
@ -20,10 +20,6 @@ const PollContainer = ({ ...props }) => {
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
|
||||
|
||||
const isPollSecret = Session.get('secretPoll') || false;
|
||||
Meteor.subscribe('current-poll', isPollSecret, amIPresenter);
|
||||
|
||||
const usernames = {};
|
||||
|
||||
@ -35,16 +31,18 @@ const PollContainer = ({ ...props }) => {
|
||||
<Poll
|
||||
{...{ layoutContextDispatch, sidebarContentPanel, ...props }}
|
||||
usernames={usernames}
|
||||
amIPresenter={amIPresenter}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTracker(() => {
|
||||
export default withTracker(({ amIPresenter }) => {
|
||||
const isPollSecret = Session.get('secretPoll') || false;
|
||||
const currentPresentation = Presentations.findOne({
|
||||
current: true,
|
||||
}, { fields: { podId: 1 } }) || {};
|
||||
|
||||
Meteor.subscribe('current-poll', isPollSecret, amIPresenter);
|
||||
|
||||
const currentSlide = PresentationService.getCurrentSlide(currentPresentation.podId);
|
||||
|
||||
const pollId = currentSlide ? currentSlide.id : PUBLIC_CHAT_KEY;
|
||||
@ -58,6 +56,7 @@ export default withTracker(() => {
|
||||
const stopPoll = () => makeCall('stopPoll');
|
||||
|
||||
return {
|
||||
isPollSecret,
|
||||
currentSlide,
|
||||
pollTypes,
|
||||
startPoll,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { CurrentPoll } from '/imports/api/polls';
|
||||
import { escapeHtml } from '/imports/utils/string-utils';
|
||||
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
const POLL_AVATAR_COLOR = '#3B48A9';
|
||||
@ -89,7 +88,7 @@ const getPollResultsText = (isDefaultPoll, answers, numRespondents, intl) => {
|
||||
answers.map((item) => {
|
||||
responded += item.numVotes;
|
||||
return item;
|
||||
}).reduce(caseInsensitiveReducer, []).forEach((item) => {
|
||||
}).forEach((item) => {
|
||||
const numResponded = responded === numRespondents ? numRespondents : responded;
|
||||
const pct = Math.round((item.numVotes / numResponded) * 100);
|
||||
const pctBars = '|'.repeat((pct * MAX_POLL_RESULT_BARS) / 100);
|
||||
|
@ -26,6 +26,7 @@ import DEFAULT_VALUES from '../layout/defaultValues';
|
||||
import { colorContentBackground } from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import { addNewAlert } from '../screenreader-alert/service';
|
||||
import { clearCursors } from '/imports/ui/components/cursor/service';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
presentationLabel: {
|
||||
@ -163,6 +164,8 @@ class Presentation extends PureComponent {
|
||||
presentationBounds,
|
||||
numCameras,
|
||||
intl,
|
||||
multiUser,
|
||||
clearFakeAnnotations,
|
||||
} = this.props;
|
||||
|
||||
const { presentationWidth, presentationHeight } = this.state;
|
||||
@ -170,8 +173,14 @@ class Presentation extends PureComponent {
|
||||
const {
|
||||
numCameras: prevNumCameras,
|
||||
presentationBounds: prevPresentationBounds,
|
||||
multiUser: prevMultiUser,
|
||||
} = prevProps;
|
||||
|
||||
if (prevMultiUser && !multiUser) {
|
||||
clearFakeAnnotations();
|
||||
clearCursors();
|
||||
}
|
||||
|
||||
if (numCameras !== prevNumCameras) {
|
||||
this.onResize();
|
||||
}
|
||||
|
@ -138,5 +138,6 @@ export default lockContextContainer(
|
||||
removeWhiteboardGlobalAccess: WhiteboardService.removeGlobalAccess,
|
||||
multiUserSize: WhiteboardService.getMultiUserSize(currentSlide?.id),
|
||||
isViewersCursorLocked,
|
||||
clearFakeAnnotations: WhiteboardService.clearFakeAnnotations,
|
||||
};
|
||||
})(PresentationContainer));
|
||||
|
@ -6,6 +6,7 @@ import lockContextContainer from "/imports/ui/components/lock-viewers/context/co
|
||||
import { UsersContext } from "/imports/ui/components/components-data/users-context/context";
|
||||
import CursorService from "./service";
|
||||
import Cursor from "./component";
|
||||
import WhiteboardService from "/imports/ui/components/whiteboard/service";
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
@ -34,10 +35,12 @@ const CursorContainer = (props) => {
|
||||
|
||||
export default lockContextContainer(
|
||||
withTracker((params) => {
|
||||
const { cursorId, userLocks } = params;
|
||||
const { cursorId, userLocks, whiteboardId, presenter } = params;
|
||||
const isViewersCursorLocked = userLocks?.hideViewersCursor;
|
||||
const cursor = CursorService.getCurrentCursor(cursorId);
|
||||
if (cursor) {
|
||||
const hasPermission = presenter || WhiteboardService.hasMultiUserAccess(whiteboardId, cursor.userId);
|
||||
|
||||
if (cursor&& hasPermission) {
|
||||
const {
|
||||
xPercent: cursorX,
|
||||
yPercent: cursorY,
|
||||
|
@ -5,6 +5,7 @@ import { toPng } from 'html-to-image';
|
||||
import { toast } from 'react-toastify';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import Styled from './styles';
|
||||
import BBBMenu from "/imports/ui/components/common/menu/component";
|
||||
import TooltipContainer from '/imports/ui/components/common/tooltip/container';
|
||||
import { ACTIONS } from '/imports/ui/components/layout/enums';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
@ -93,6 +94,7 @@ const PresentationMenu = (props) => {
|
||||
layoutContextDispatch,
|
||||
meetingName,
|
||||
isIphone,
|
||||
isRTL
|
||||
} = props;
|
||||
|
||||
const [state, setState] = useState({
|
||||
@ -260,46 +262,34 @@ const PresentationMenu = (props) => {
|
||||
|
||||
return (
|
||||
<Styled.Right>
|
||||
<TooltipContainer title={intl.formatMessage(intlMessages.optionsLabel)}>
|
||||
<Styled.DropdownButton
|
||||
state={isDropdownOpen ? 'open' : 'closed'}
|
||||
aria-label={intl.formatMessage(intlMessages.optionsLabel)}
|
||||
data-test="whiteboardOptionsButton"
|
||||
onClick={() => setIsDropdownOpen((isOpen) => !isOpen)}
|
||||
>
|
||||
<Styled.ButtonIcon iconName="more" />
|
||||
</Styled.DropdownButton>
|
||||
</TooltipContainer>
|
||||
{ isDropdownOpen && (
|
||||
<>
|
||||
<Styled.Overlay onClick={() => setIsDropdownOpen(false)} />
|
||||
<Styled.Dropdown
|
||||
ref={dropdownRef}
|
||||
onBlur={() => setIsDropdownOpen(false)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<Styled.List>
|
||||
{ options.map((option) => {
|
||||
const {
|
||||
label, onClick, key, dataTest,
|
||||
} = option;
|
||||
|
||||
return (
|
||||
<Styled.ListItem
|
||||
{...{
|
||||
onClick,
|
||||
key,
|
||||
'data-test': dataTest ?? '',
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Styled.ListItem>
|
||||
);
|
||||
}) }
|
||||
</Styled.List>
|
||||
</Styled.Dropdown>
|
||||
</>
|
||||
) }
|
||||
<BBBMenu
|
||||
trigger={
|
||||
<TooltipContainer title={intl.formatMessage(intlMessages.optionsLabel)}>
|
||||
<Styled.DropdownButton
|
||||
state={isDropdownOpen ? 'open' : 'closed'}
|
||||
aria-label={intl.formatMessage(intlMessages.optionsLabel)}
|
||||
data-test="whiteboardOptionsButton"
|
||||
onClick={() => {
|
||||
setIsDropdownOpen((isOpen) => !isOpen)
|
||||
}}
|
||||
>
|
||||
<Styled.ButtonIcon iconName="more" />
|
||||
</Styled.DropdownButton>
|
||||
</TooltipContainer>
|
||||
}
|
||||
opts={{
|
||||
id: "default-dropdown-menu",
|
||||
keepMounted: true,
|
||||
transitionDuration: 0,
|
||||
elevation: 3,
|
||||
getContentAnchorEl: null,
|
||||
fullwidth: "true",
|
||||
anchorOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
|
||||
transformOrigin: { vertical: 'top', horizontal: isRTL ? 'right' : 'left' },
|
||||
container: fullscreenRef
|
||||
}}
|
||||
actions={getAvailableOptions()}
|
||||
/>
|
||||
</Styled.Right>
|
||||
);
|
||||
};
|
||||
|
@ -12,6 +12,7 @@ const PresentationMenuContainer = (props) => {
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
const { elementId } = props;
|
||||
const isFullscreen = currentElement === elementId;
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
|
||||
return (
|
||||
<PresentationMenu
|
||||
@ -21,6 +22,7 @@ const PresentationMenuContainer = (props) => {
|
||||
currentGroup,
|
||||
isFullscreen,
|
||||
layoutContextDispatch,
|
||||
isRTL,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -123,54 +123,11 @@ const Line = styled.div`
|
||||
padding: ${lgPaddingX} 0;
|
||||
`;
|
||||
|
||||
const List = styled.ul`
|
||||
list-style-type: none;
|
||||
padding: ${mdPaddingY} ${borderSize};
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
|
||||
[dir="rtl"] & {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
|
||||
const ListItem = styled.li`
|
||||
padding: ${mdPaddingY} ${mdPaddingX};
|
||||
|
||||
&:hover {
|
||||
background-color: ${colorPrimary};
|
||||
color: white;
|
||||
}
|
||||
`;
|
||||
|
||||
const Dropdown = styled.div`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 117%;
|
||||
background-color: ${colorWhite};
|
||||
z-index: 1000;
|
||||
box-shadow: 0 0 10px 1px ${colorGrayLightest};
|
||||
border-radius: ${borderRadius};
|
||||
|
||||
[dir="rtl"] & {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonIcon = styled(Icon)`
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const Overlay = styled.div`
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 999;
|
||||
cursor: auto;
|
||||
`;
|
||||
|
||||
export default {
|
||||
DropdownButton,
|
||||
Right,
|
||||
@ -178,9 +135,5 @@ export default {
|
||||
StatusIcon,
|
||||
ToastIcon,
|
||||
Line,
|
||||
List,
|
||||
Dropdown,
|
||||
ListItem,
|
||||
ButtonIcon,
|
||||
Overlay,
|
||||
};
|
||||
|
@ -399,6 +399,7 @@ class PresentationToolbar extends PureComponent {
|
||||
) : null}
|
||||
<Styled.FitToWidthButton
|
||||
role="button"
|
||||
data-test="fitToWidthButton"
|
||||
aria-describedby={fitToWidth ? 'fitPageDesc' : 'fitWidthDesc'}
|
||||
aria-label={
|
||||
fitToWidth
|
||||
|
@ -47,6 +47,7 @@ const SidebarContent = (props) => {
|
||||
resizableEdge,
|
||||
contextDispatch,
|
||||
sidebarContentPanel,
|
||||
amIPresenter,
|
||||
} = props;
|
||||
|
||||
const [resizableWidth, setResizableWidth] = useState(width);
|
||||
@ -141,7 +142,7 @@ const SidebarContent = (props) => {
|
||||
{sidebarContentPanel === PANELS.BREAKOUT && <BreakoutRoomContainer />}
|
||||
{sidebarContentPanel === PANELS.WAITING_USERS && <WaitingUsersPanel />}
|
||||
<Styled.Poll style={{ minWidth, top: '0', display: pollDisplay }} id="pollPanel">
|
||||
<PollContainer smallSidebar={smallSidebar} />
|
||||
<PollContainer smallSidebar={smallSidebar} amIPresenter={amIPresenter} />
|
||||
</Styled.Poll>
|
||||
</Resizable>
|
||||
);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import SidebarContent from './component';
|
||||
import { layoutSelectInput, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import { UsersContext } from '../components-data/users-context/context';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
|
||||
const SidebarContentContainer = () => {
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
@ -10,11 +12,16 @@ const SidebarContentContainer = () => {
|
||||
|
||||
if (sidebarContentOutput.display === false) return null;
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
|
||||
|
||||
return (
|
||||
<SidebarContent
|
||||
{...sidebarContentOutput}
|
||||
contextDispatch={layoutContextDispatch}
|
||||
sidebarContentPanel={sidebarContentPanel}
|
||||
amIPresenter={amIPresenter}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -632,7 +632,7 @@ const sortUsersByLastName = (a, b) => {
|
||||
return sortUsersByName(aUser, bUser);
|
||||
};
|
||||
|
||||
const isUserPresenter = (userId) => {
|
||||
const isUserPresenter = (userId = Auth.userID) => {
|
||||
const user = Users.findOne({ userId },
|
||||
{ fields: { presenter: 1 } });
|
||||
return user ? user.presenter : false;
|
||||
|
@ -20,7 +20,7 @@ const clearPreview = (annotation) => {
|
||||
UnsentAnnotations.remove({ id: annotation });
|
||||
};
|
||||
|
||||
function clearFakeAnnotations() {
|
||||
const clearFakeAnnotations = () => {
|
||||
UnsentAnnotations.remove({});
|
||||
Annotations.remove({ id: /-fake/g });
|
||||
}
|
||||
@ -396,5 +396,5 @@ export {
|
||||
getCurrentPres,
|
||||
removeShapes,
|
||||
changeCurrentSlide,
|
||||
getCurSlide,
|
||||
clearFakeAnnotations,
|
||||
};
|
||||
|
@ -49,5 +49,5 @@ export function isLayoutsEnabled() {
|
||||
}
|
||||
|
||||
export function isVirtualBackgroundsEnabled() {
|
||||
return getDisabledFeatures().indexOf('virtualBackgrounds') === -1;
|
||||
return getDisabledFeatures().indexOf('virtualBackgrounds') === -1 && Meteor.settings.public.virtualBackgrounds.enabled;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ const MODELS = {
|
||||
};
|
||||
|
||||
const {
|
||||
enabled: VIRTUAL_BACKGROUND_ENABLED = true,
|
||||
thumbnailsPath: THUMBNAILS_PATH = '/resources/images/virtual-backgrounds/thumbnails/',
|
||||
fileNames: IMAGE_NAMES = ['home.jpg', 'coffeeshop.jpg', 'board.jpg'],
|
||||
storedOnBBB: IS_STORED_ON_BBB = true,
|
||||
|
@ -454,7 +454,7 @@
|
||||
"app.settings.main.save.label.description": "Saves the changes and closes the settings menu",
|
||||
"app.settings.dataSavingTab.label": "Data savings",
|
||||
"app.settings.dataSavingTab.webcam": "Enable other participants webcams",
|
||||
"app.settings.dataSavingTab.screenShare": "Enable desktop sharing",
|
||||
"app.settings.dataSavingTab.screenShare": "Enable other participants desktop sharing",
|
||||
"app.settings.dataSavingTab.description": "To save your bandwidth adjust what's currently being displayed.",
|
||||
"app.settings.save-notification.label": "Settings have been saved",
|
||||
"app.statusNotifier.lowerHands": "Lower Hands",
|
||||
@ -474,7 +474,6 @@
|
||||
"app.actionsBar.actionsDropdown.presentationLabel": "Manage presentations",
|
||||
"app.actionsBar.actionsDropdown.initPollLabel": "Initiate a poll",
|
||||
"app.actionsBar.actionsDropdown.desktopShareLabel": "Share your screen",
|
||||
"app.actionsBar.actionsDropdown.lockedDesktopShareLabel": "Screenshare locked",
|
||||
"app.actionsBar.actionsDropdown.stopDesktopShareLabel": "Stop sharing your screen",
|
||||
"app.actionsBar.actionsDropdown.presentationDesc": "Upload your presentation",
|
||||
"app.actionsBar.actionsDropdown.initPollDesc": "Initiate a poll",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1046,7 +1046,7 @@
|
||||
"app.learningDashboard.statusTimelineTable.thumbnail": "Vignette de présentation",
|
||||
"app.learningDashboard.errors.invalidToken": "Jeton de session invalide",
|
||||
"app.learningDashboard.errors.dataUnavailable": "Les données ne sont plus disponibles",
|
||||
"mobileApp.portals.list.empty.addFirstPortal.label": "Ajoutez votre premier portail en utilisant le bouton ci-dessus,",
|
||||
"mobileApp.portals.list.empty.addFirstPortal.label": "Ajoutez votre premier portail en utilisant le bouton ci-dessus",
|
||||
"mobileApp.portals.list.empty.orUseOurDemoServer.label": "ou utilisez notre serveur de démo.",
|
||||
"mobileApp.portals.list.add.button.label": "Ajouter un portail",
|
||||
"mobileApp.portals.fields.name.label": "Nom du portail",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"app.home.greeting": "Vaša prezentacija započet će uskoro ...",
|
||||
"app.chat.submitLabel": "Pošalji poruku",
|
||||
"app.chat.inputPlaceholder": "Poruke [0]",
|
||||
"app.chat.inputPlaceholder": "Poruke {0}",
|
||||
"app.chat.titlePublic": "Javni chat",
|
||||
"app.chat.titlePrivate": "Privatni chat s {0}",
|
||||
"app.chat.partnerDisconnected": "{0} je napustio sastanak",
|
||||
@ -105,7 +105,7 @@
|
||||
"app.meeting.meetingTimeRemaining": "Preostalo vrijeme sastanka: {0}",
|
||||
"app.meeting.alertMeetingEndsUnderMinutesSingular": "Sastanak prestaje za jednu minutu.",
|
||||
"app.meeting.alertMeetingEndsUnderMinutesPlural": "Sastanak prestaje za {0} minuta/e.",
|
||||
"app.meeting.alertBreakoutEndsUnderMinutesPlural": "Breakout soba se zatvara za [0] minuta/e.",
|
||||
"app.meeting.alertBreakoutEndsUnderMinutesPlural": "Breakout soba se zatvara za {0} minuta/e.",
|
||||
"app.meeting.alertBreakoutEndsUnderMinutesSingular": "Breakout soba se zatvara za 1 minutu.",
|
||||
"app.presentation.hide": "Skrij prezentaciju",
|
||||
"app.presentation.notificationLabel": "Trenutačna prezentacija",
|
||||
@ -138,7 +138,7 @@
|
||||
"app.presentationUploder.tableHeading.filename": "Datoteka",
|
||||
"app.presentationUploder.tableHeading.options": "Postavke",
|
||||
"app.presentationUploder.tableHeading.status": "Status",
|
||||
"app.presentationUploder.uploading": "Prijenos na poslužitelj [0] [1]",
|
||||
"app.presentationUploder.uploading": "Prijenos na poslužitelj {0} {1}",
|
||||
"app.presentationUploder.item" : "Stavka",
|
||||
"app.presentationUploder.itemPlural" : "Stavke",
|
||||
"app.presentationUploder.clearErrors": "Ukloni pogreške",
|
||||
@ -188,7 +188,7 @@
|
||||
"app.polling.pollQuestionTitle": "Anketno pitanje",
|
||||
"app.polling.submitLabel": "Predaj",
|
||||
"app.polling.responsePlaceholder": "Unesite odgovor",
|
||||
"app.polling.pollAnswerLabel": "Odgovor na anketu [0]",
|
||||
"app.polling.pollAnswerLabel": "Odgovor na anketu {0}",
|
||||
"app.downloadPresentationButton.label": "Preuzmi originalnu prezentaciju",
|
||||
"app.connectingMessage": "Spajanje ...",
|
||||
"app.retryNow": "Pokušajte ponovno",
|
||||
@ -299,7 +299,7 @@
|
||||
"app.actionsBar.emojiMenu.thumbsUpDesc": "Promijenite svoj status u 'Palac gore'",
|
||||
"app.actionsBar.emojiMenu.thumbsDownLabel": "Palac dolje",
|
||||
"app.actionsBar.emojiMenu.thumbsDownDesc": "Promijenite svoj status u 'Palac dolje'",
|
||||
"app.actionsBar.currentStatusDesc": "trenutačni status [0]",
|
||||
"app.actionsBar.currentStatusDesc": "trenutačni status {0}",
|
||||
"app.audioNotification.closeLabel": "Zatvori",
|
||||
"app.breakoutJoinConfirmation.title": "Priključi se u breakout sobu",
|
||||
"app.breakoutJoinConfirmation.message": "Želite li se priključiti",
|
||||
|
@ -173,6 +173,7 @@
|
||||
"app.presentation.options.fullscreen": "Tela cheia",
|
||||
"app.presentation.options.exitFullscreen": "Sair de tela cheia",
|
||||
"app.presentation.options.minimize": "Minimizar",
|
||||
"app.presentation.options.snapshot": "Salvar imagem do slide atual",
|
||||
"app.presentation.options.downloading": "Downloading...",
|
||||
"app.presentation.options.downloaded": "Donwload da apresentação atual encerrado",
|
||||
"app.presentation.options.downloadFailed": "Não foi possível fazer o download da apresentação atual",
|
||||
|
@ -1043,7 +1043,9 @@
|
||||
"app.learningDashboard.errors.dataUnavailable": "Данные больше не доступны",
|
||||
"mobileApp.portals.fields.url.label": "URL сервера",
|
||||
"mobileApp.portals.addPortalPopup.confirm.button.label": "Сохранить",
|
||||
"mobileApp.portals.addPortalPopup.validation.portalNameAlreadyExists": "Имя уже используется"
|
||||
"mobileApp.portals.addPortalPopup.validation.emptyFields": "Поля, обязательные для заполнения",
|
||||
"mobileApp.portals.addPortalPopup.validation.portalNameAlreadyExists": "Имя уже используется",
|
||||
"mobileApp.portals.addPortalPopup.validation.urlInvalid": "Ошибка при загрузке страницы. Проверьте URL и Интернет соединение."
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,14 @@ const { test } = require('@playwright/test');
|
||||
const { Audio } = require('./audio');
|
||||
|
||||
test.describe.parallel('Audio', () => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#listen-only-mode-automated
|
||||
test('Join audio with Listen Only @ci', async ({ browser, page }) => {
|
||||
const audio = new Audio(browser, page);
|
||||
await audio.init(true, false);
|
||||
await audio.joinAudio();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#join-audio-automated
|
||||
test('Join audio with Microphone @ci', async ({ browser, page }) => {
|
||||
const audio = new Audio(browser, page);
|
||||
await audio.init(true, false);
|
||||
|
@ -9,6 +9,7 @@ test.describe.parallel('Breakout', () => {
|
||||
await create.create();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#moderators-creating-breakout-rooms-and-assiging-users-automated
|
||||
test('Join Breakout room @ci', async ({ browser, context, page }) => {
|
||||
const join = new Join(browser, context);
|
||||
await join.initPages(page);
|
||||
|
@ -3,12 +3,14 @@ const { Chat } = require('./chat');
|
||||
const { PrivateChat } = require('./privateChat');
|
||||
|
||||
test.describe.parallel('Chat', () => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#public-message-automated
|
||||
test('Send public message @ci', async ({ browser, page }) => {
|
||||
const chat = new Chat(browser, page);
|
||||
await chat.init(true, true);
|
||||
await chat.sendPublicMessage();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#private-message-automated
|
||||
test('Send private message @ci', async ({ browser, context, page }) => {
|
||||
const privateChat = new PrivateChat(browser, context);
|
||||
await privateChat.initPages(page);
|
||||
@ -34,12 +36,14 @@ test.describe.parallel('Chat', () => {
|
||||
await chat.saveChat(testInfo);
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#chat-character-limit-automated
|
||||
test('Verify character limit', async ({ browser, page }) => {
|
||||
const chat = new Chat(browser, page);
|
||||
await chat.init(true, true);
|
||||
await chat.characterLimit();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#sending-empty-chat-message-automated
|
||||
test('Not able to send an empty message', async ({ browser, page }) => {
|
||||
const chat = new Chat(browser, page);
|
||||
await chat.init(true, true);
|
||||
|
@ -3,7 +3,7 @@ const { MultiUsers } = require('../user/multiusers');
|
||||
const e = require('../core/elements');
|
||||
const { ELEMENT_WAIT_TIME } = require('../core/constants');
|
||||
const { openConnectionStatus, checkNetworkStatus } = require('./util');
|
||||
|
||||
const { sleep } = require('../core/helpers');
|
||||
|
||||
class ConnectionStatus extends MultiUsers {
|
||||
constructor(browser, context) {
|
||||
@ -38,6 +38,24 @@ class ConnectionStatus extends MultiUsers {
|
||||
const status = this.modPage.getLocator(e.connectionStatusItemUser);
|
||||
await expect(status).toHaveCount(1);
|
||||
}
|
||||
|
||||
async linkToSettingsTest() {
|
||||
await openConnectionStatus(this.modPage);
|
||||
await this.modPage.page.evaluate(() => window.dispatchEvent(new CustomEvent('socketstats', { detail: { rtt: 2000 } })));
|
||||
await this.modPage.hasElement(e.connectionStatusLinkToSettings);
|
||||
await this.modPage.waitAndClick(e.connectionStatusLinkToSettings);
|
||||
await this.modPage.waitForSelector(e.dataSavingsTab);
|
||||
}
|
||||
|
||||
async copyStatsTest(context) {
|
||||
await openConnectionStatus(this.modPage);
|
||||
await this.modPage.hasElementEnabled(e.copyStats);
|
||||
await this.modPage.waitAndClick(e.copyStats);
|
||||
await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_URL });
|
||||
const copiedText = await this.modPage.page.evaluate(async () => navigator.clipboard.readText());
|
||||
const check = copiedText.includes("audioCurrentUploadRate");
|
||||
await expect(check).toBeTruthy();
|
||||
}
|
||||
}
|
||||
|
||||
exports.ConnectionStatus = ConnectionStatus;
|
||||
|
@ -19,4 +19,17 @@ test.describe.parallel('Connection Status', () => {
|
||||
await connectionStatus.initModPage(page);
|
||||
await connectionStatus.reportUserInConnectionIssues();
|
||||
});
|
||||
|
||||
test('Go to settings modal', async ({ browser, context, page }) => {
|
||||
const connectionStatus = new ConnectionStatus(browser, context);
|
||||
await connectionStatus.initModPage(page);
|
||||
await connectionStatus.linkToSettingsTest();
|
||||
});
|
||||
|
||||
test('Copy stats', async ({ browser, context, page }, testInfo) => {
|
||||
test.fixme(testInfo.project.use.headless, 'Only works in headed mode');
|
||||
const connectionStatus = new ConnectionStatus(browser, context);
|
||||
await connectionStatus.initModPage(page);
|
||||
await connectionStatus.copyStatsTest(context);
|
||||
});
|
||||
});
|
||||
|
@ -155,6 +155,7 @@ exports.pollYesNoAbstentionBtn = 'button[data-test="pollYesNoAbstentionBtn"]';
|
||||
exports.currentSlideImg = 'img[id="slide-background-shape_image"]';
|
||||
exports.uploadPresentationFileName = 'uploadTest.png';
|
||||
exports.presentationPlaceholderLabel = 'There is no currently active presentation';
|
||||
exports.noPresentationLabel = 'There is no currently active presentation';
|
||||
exports.startScreenSharing = 'button[data-test="startScreenShare"]';
|
||||
exports.stopScreenSharing = 'button[data-test="stopScreenShare"]';
|
||||
exports.managePresentations = 'li[data-test="managePresentations"]';
|
||||
@ -179,9 +180,11 @@ exports.videoModalInput = 'input[id="video-modal-input"]';
|
||||
exports.startShareVideoBtn = 'button[data-test="startNewVideo"]';
|
||||
exports.videoPlayer = 'div[data-test="videoPlayer"]';
|
||||
exports.presentationTitle = 'h1[data-test="presentationTitle"]';
|
||||
exports.fitToWidthButton = 'button[data-test="fitToWidthButton"]';
|
||||
// YouTube frame
|
||||
exports.youtubeLink = 'https://www.youtube.com/watch?v=Hso8yLzkqj8&ab_channel=BigBlueButton';
|
||||
exports.youtubeFrame = 'iframe[title^="YouTube"]';
|
||||
// The title we match for here is the title of the test video specified by youtubeLink
|
||||
exports.youtubeFrame = 'iframe[title~="GreenLight"]';
|
||||
exports.ytFrameTitle = 'a[class^="ytp-title-link"]';
|
||||
// Toasts
|
||||
exports.statingUploadPresentationToast = 'To be uploaded ...';
|
||||
@ -192,9 +195,11 @@ exports.presentationUploadedToast = 'Current presentation';
|
||||
exports.languageSelector = 'select[id="langSelector"]';
|
||||
exports.messageTitle = 'h2[data-test="messageTitle"]';
|
||||
exports.notesTitle = 'h2[data-test="notesTitle"]';
|
||||
exports.dataSavingsTab = 'span[id="dataSaving"]';
|
||||
|
||||
// User
|
||||
const userAvatar = 'div[data-test="userAvatar"]';
|
||||
const networkDataContainer = 'div[data-test="networkDataContainer"]';
|
||||
exports.userAvatar = userAvatar;
|
||||
exports.moderatorAvatar = 'div[data-test="moderatorAvatar"]';
|
||||
exports.viewerAvatar = 'div[data-test="viewerAvatar"]';
|
||||
@ -212,14 +217,16 @@ exports.userListToggleBtn = 'button[data-test="toggleUserList"]';
|
||||
exports.mobileUser = 'span[data-test="mobileUser"]';
|
||||
exports.connectionStatusBtn = 'button[data-test="connectionStatusButton"]';
|
||||
exports.connectionStatusModal = 'div[data-test="connectionStatusModal"]';
|
||||
exports.copyStats = 'span[data-test="copyStats"]';
|
||||
exports.dataSavingScreenshare = 'input[data-test="dataSavingScreenshare"]';
|
||||
exports.screenshareLocked = 'button[data-test="screenshareLocked"]';
|
||||
exports.connectionStatusItemEmpty = 'div[data-test="connectionStatusItemEmpty"]';
|
||||
exports.connectionStatusTab2 = 'div[data-tab="2"]';
|
||||
exports.connectionStatusItemUser = 'div[data-test="connectionStatusItemUser"]';
|
||||
exports.connectionStatusLinkToSettings = `${networkDataContainer} span[role="button"]`;
|
||||
exports.dataSavingWebcams = 'input[data-test="dataSavingWebcams"]';
|
||||
exports.connectionStatusOfflineUser = 'div[data-test="offlineUser"]';
|
||||
exports.connectionDataContainer = 'div[data-test="networkDataContainer"]';
|
||||
exports.connectionDataContainer = networkDataContainer;
|
||||
exports.avatarsWrapperAvatar = 'div[data-test="avatarsWrapperAvatar"]';
|
||||
exports.guestPolicyLabel = 'li[data-test="guestPolicyLabel"]';
|
||||
exports.downloadUserNamesList = 'li[data-test="downloadUserNamesList"]';
|
||||
|
@ -16,9 +16,9 @@ async function createMeeting(params, customParameter) {
|
||||
const meetingID = `random-${getRandomInt(1000000, 10000000).toString()}`;
|
||||
const mp = params.moderatorPW;
|
||||
const ap = params.attendeePW;
|
||||
const query = customParameter !== undefined ? `name=${meetingID}&meetingID=${meetingID}&attendeePW=${ap}&moderatorPW=${mp}&joinViaHtml5=true`
|
||||
const query = customParameter !== undefined ? `name=${meetingID}&meetingID=${meetingID}&attendeePW=${ap}&moderatorPW=${mp}`
|
||||
+ `&allowStartStopRecording=true&${customParameter}&autoStartRecording=false&welcome=${params.welcome}`
|
||||
: `name=${meetingID}&meetingID=${meetingID}&attendeePW=${ap}&moderatorPW=${mp}&joinViaHtml5=true`
|
||||
: `name=${meetingID}&meetingID=${meetingID}&attendeePW=${ap}&moderatorPW=${mp}`
|
||||
+ `&allowStartStopRecording=true&autoStartRecording=false&welcome=${params.welcome}`;
|
||||
const apicall = `create${query}${params.secret}`;
|
||||
const checksum = sha1(apicall);
|
||||
@ -29,8 +29,8 @@ async function createMeeting(params, customParameter) {
|
||||
|
||||
function getJoinURL(meetingID, params, moderator, customParameter) {
|
||||
const pw = moderator ? params.moderatorPW : params.attendeePW;
|
||||
const query = customParameter !== undefined ? `fullName=${params.fullName}&joinViaHtml5=true&meetingID=${meetingID}&password=${pw}&${customParameter}`
|
||||
: `fullName=${params.fullName}&joinViaHtml5=true&meetingID=${meetingID}&password=${pw}`;
|
||||
const query = customParameter !== undefined ? `fullName=${params.fullName}&meetingID=${meetingID}&password=${pw}&${customParameter}`
|
||||
: `fullName=${params.fullName}&meetingID=${meetingID}&password=${pw}`;
|
||||
const apicall = `join${query}${params.secret}`;
|
||||
const checksum = sha1(apicall);
|
||||
return `${params.server}/join?${query}&checksum=${checksum}`;
|
||||
|
@ -167,7 +167,7 @@ class Page {
|
||||
}
|
||||
|
||||
async hasElementEnabled(selector, timeout = ELEMENT_WAIT_TIME) {
|
||||
const locator = this.getLocator(selector);
|
||||
const locator = this.getLocator(`${selector}:not([disabled])`);
|
||||
await expect(locator).toBeEnabled({ timeout });
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ class ChatNotifications extends MultiUsers {
|
||||
}
|
||||
|
||||
async publicChatNotification() {
|
||||
await util.waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await openSettings(this.modPage);
|
||||
await util.enableChatPopup(this.modPage);
|
||||
await util.saveSettings(this.modPage);
|
||||
@ -24,7 +23,6 @@ class ChatNotifications extends MultiUsers {
|
||||
}
|
||||
|
||||
async privateChatNotification() {
|
||||
await util.waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await openSettings(this.modPage);
|
||||
await util.enableChatPopup(this.modPage);
|
||||
await util.saveSettings(this.modPage);
|
||||
|
@ -31,13 +31,13 @@ test.describe.parallel('Notifications', () => {
|
||||
test.describe.parallel('Chat', () => {
|
||||
test('Public Chat notification', async ({ browser, context, page }) => {
|
||||
const chatNotifications = new ChatNotifications(browser, context);
|
||||
await chatNotifications.initPages(page);
|
||||
await chatNotifications.initPages(page, true);
|
||||
await chatNotifications.publicChatNotification();
|
||||
});
|
||||
|
||||
test('Private Chat notification', async ({ browser, context, page }) => {
|
||||
const chatNotifications = new ChatNotifications(browser, context);
|
||||
await chatNotifications.initPages(page);
|
||||
await chatNotifications.initPages(page, true);
|
||||
await chatNotifications.privateChatNotification();
|
||||
});
|
||||
});
|
||||
@ -51,7 +51,7 @@ test.describe.parallel('Notifications', () => {
|
||||
|
||||
test('Presentation upload notification', async ({ browser, context, page }) => { // this test is unstable, there's an apparent timing issue around the visibility of smallToastMsg
|
||||
const presenterNotifications = new PresenterNotifications(browser, context);
|
||||
await presenterNotifications.initPages(page);
|
||||
await presenterNotifications.initPages(page, true);
|
||||
await presenterNotifications.fileUploaderNotification();
|
||||
});
|
||||
|
||||
|
@ -19,8 +19,7 @@ class PresenterNotifications extends MultiUsers {
|
||||
}
|
||||
|
||||
async fileUploaderNotification() {
|
||||
await util.waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await utilPresentation.uploadPresentation(this.modPage, e.pdfFileName, UPLOAD_PDF_WAIT_TIME);
|
||||
await utilPresentation.uploadSinglePresentation(this.modPage, e.pdfFileName, UPLOAD_PDF_WAIT_TIME);
|
||||
await util.checkNotificationText(this.userPage, e.presentationUploadedToast);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ class Polling extends MultiUsers {
|
||||
}
|
||||
|
||||
async createPoll() {
|
||||
await waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await util.startPoll(this.modPage);
|
||||
await this.modPage.hasElement(e.pollMenuButton);
|
||||
}
|
||||
@ -29,7 +28,7 @@ class Polling extends MultiUsers {
|
||||
|
||||
async quickPoll() {
|
||||
await waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await utilPresentation.uploadPresentation(this.modPage, e.questionSlideFileName);
|
||||
await utilPresentation.uploadSinglePresentation(this.modPage, e.questionSlideFileName);
|
||||
|
||||
await this.modPage.waitAndClick(e.quickPoll);
|
||||
await this.modPage.waitForSelector(e.pollMenuButton);
|
||||
@ -87,7 +86,7 @@ class Polling extends MultiUsers {
|
||||
await waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
await util.startPoll(this.modPage);
|
||||
|
||||
await utilPresentation.uploadPresentation(this.modPage, e.questionSlideFileName);
|
||||
await utilPresentation.uploadSinglePresentation(this.modPage, e.questionSlideFileName);
|
||||
await this.modPage.waitAndClick(e.publishPollingLabel);
|
||||
|
||||
// Check poll results
|
||||
|
@ -5,16 +5,18 @@ test.describe.parallel('Polling', () => {
|
||||
test.describe.parallel('Manage', () => {
|
||||
test('Create Poll @ci', async ({ browser, context, page }) => {
|
||||
const polling = new Polling(browser, context);
|
||||
await polling.initPages(page);
|
||||
await polling.initPages(page, true);
|
||||
await polling.createPoll();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#start-an-anonymous-poll-automated
|
||||
test('Create anonymous poll @ci', async ({ browser, context, page }) => {
|
||||
const polling = new Polling(browser, context);
|
||||
await polling.initPages(page);
|
||||
await polling.pollAnonymous();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#quick-poll-option-automated
|
||||
test('Create quick poll - from the slide', async ({ browser, context, page }) => {
|
||||
const polling = new Polling(browser, context);
|
||||
await polling.initPages(page);
|
||||
|
@ -2,11 +2,10 @@ const { expect, default: test } = require('@playwright/test');
|
||||
const { MultiUsers } = require('../user/multiusers');
|
||||
const Page = require('../core/page');
|
||||
const e = require('../core/elements');
|
||||
const { checkSvgIndex, getSlideOuterHtml, uploadPresentation } = require('./util.js');
|
||||
const { checkSvgIndex, getSlideOuterHtml, uploadSinglePresentation, uploadMultiplePresentations } = require('./util.js');
|
||||
const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
|
||||
const { sleep } = require('../core/helpers');
|
||||
const { getSettings } = require('../core/settings');
|
||||
const { waitAndClearDefaultPresentationNotification } = require('../notifications/util');
|
||||
|
||||
class Presentation extends MultiUsers {
|
||||
constructor(browser, context) {
|
||||
@ -61,15 +60,14 @@ class Presentation extends MultiUsers {
|
||||
await userFrame.hasElement('video');
|
||||
}
|
||||
|
||||
async uploadPresentationTest() {
|
||||
await waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
async uploadSinglePresentationTest() {
|
||||
await this.modPage.waitForSelector(e.skipSlide);
|
||||
|
||||
const modSlides0 = await getSlideOuterHtml(this.modPage);
|
||||
const userSlides0 = await getSlideOuterHtml(this.userPage);
|
||||
await expect(modSlides0).toEqual(userSlides0);
|
||||
|
||||
await uploadPresentation(this.modPage, e.uploadPresentationFileName);
|
||||
await uploadSinglePresentation(this.modPage, e.uploadPresentationFileName);
|
||||
|
||||
const modSlides1 = await getSlideOuterHtml(this.modPage);
|
||||
const userSlides1 = await getSlideOuterHtml(this.userPage);
|
||||
@ -79,6 +77,34 @@ class Presentation extends MultiUsers {
|
||||
await expect(userSlides0).not.toEqual(userSlides1);
|
||||
}
|
||||
|
||||
async uploadMultiplePresentationsTest() {
|
||||
await this.modPage.waitForSelector(e.skipSlide);
|
||||
|
||||
const modSlides0 = await this.modPage.page.evaluate(getSvgOuterHtml);
|
||||
const userSlides0 = await this.userPage.page.evaluate(getSvgOuterHtml);
|
||||
await expect(modSlides0).toEqual(userSlides0);
|
||||
|
||||
await uploadMultiplePresentations(this.modPage, [e.uploadPresentationFileName, e.questionSlideFileName]);
|
||||
|
||||
const modSlides1 = await this.userPage.page.evaluate(async () => document.querySelector('svg g g g').outerHTML);
|
||||
const userSlides1 = await this.modPage.page.evaluate(async () => document.querySelector('svg g g g').outerHTML);
|
||||
await expect(modSlides1).toEqual(userSlides1);
|
||||
|
||||
await expect(modSlides0).not.toEqual(modSlides1);
|
||||
await expect(userSlides0).not.toEqual(userSlides1);
|
||||
}
|
||||
|
||||
async fitToWidthTest() {
|
||||
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
|
||||
await this.modPage.waitForSelector(e.skipSlide);
|
||||
await this.modPage.waitAndClick(e.userListToggleBtn);
|
||||
await uploadSinglePresentation(this.modPage, e.uploadPresentationFileName);
|
||||
const width1 = await this.modPage.page.locator(e.whiteboard).getAttribute("width");
|
||||
await this.modPage.waitAndClick(e.fitToWidthButton);
|
||||
const width2 = await this.modPage.page.locator(e.whiteboard).getAttribute("width");
|
||||
await expect(Number(width2) > Number(width1)).toBeTruthy();
|
||||
}
|
||||
|
||||
async allowAndDisallowDownload(testInfo) {
|
||||
const { presentationDownloadable } = getSettings();
|
||||
test.fail(!presentationDownloadable, 'Presentation download is disable');
|
||||
|
@ -20,11 +20,25 @@ test.describe.parallel('Presentation', () => {
|
||||
await presentation.startExternalVideo();
|
||||
});
|
||||
|
||||
test('Presentation fit to width', async ({ browser, context, page }) => {
|
||||
const presentation = new Presentation(browser, context);
|
||||
await presentation.initPages(page);
|
||||
await presentation.fitToWidthTest();
|
||||
});
|
||||
|
||||
test.describe.parallel('Manage', () => {
|
||||
test('Upload presentation @ci', async ({ browser, context, page }) => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#uploading-a-presentation-automated
|
||||
test('Upload single presentation @ci', async ({ browser, context, page }) => {
|
||||
const presentation = new Presentation(browser, context);
|
||||
await presentation.initPages(page);
|
||||
await presentation.uploadPresentationTest();
|
||||
await presentation.initPages(page, true);
|
||||
await presentation.uploadSinglePresentationTest();
|
||||
});
|
||||
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#uploading-multiple-presentations-automated
|
||||
test('Upload multiple presentations', async ({ browser, context, page }) => {
|
||||
const presentation = new Presentation(browser, context);
|
||||
await presentation.initPages(page, true);
|
||||
await presentation.uploadMultiplePresentationsTest();
|
||||
});
|
||||
|
||||
test.skip('Allow and disallow presentation download @ci', async ({ browser, context, page }, testInfo) => {
|
||||
|
@ -16,7 +16,7 @@ async function getSlideOuterHtml(testPage) {
|
||||
}, [e.currentSlideImg]);
|
||||
}
|
||||
|
||||
async function uploadPresentation(test, fileName, uploadTimeout = ELEMENT_WAIT_LONGER_TIME) {
|
||||
async function uploadSinglePresentation(test, fileName, uploadTimeout = ELEMENT_WAIT_LONGER_TIME) {
|
||||
await test.waitAndClick(e.actions);
|
||||
await test.waitAndClick(e.managePresentations);
|
||||
await test.waitForSelector(e.fileUpload);
|
||||
@ -29,6 +29,20 @@ async function uploadPresentation(test, fileName, uploadTimeout = ELEMENT_WAIT_L
|
||||
await test.hasText(e.smallToastMsg, e.presentationUploadedToast, uploadTimeout);
|
||||
}
|
||||
|
||||
async function uploadMultiplePresentations(test, fileNames, uploadTimeout = ELEMENT_WAIT_LONGER_TIME) {
|
||||
await test.waitAndClick(e.actions);
|
||||
await test.waitAndClick(e.managePresentations);
|
||||
await test.waitForSelector(e.fileUpload);
|
||||
|
||||
await test.page.setInputFiles(e.fileUpload, fileNames.map(function(fileName) { return path.join(__dirname, `../core/media/${fileName}`); }));
|
||||
await test.hasText('body', e.statingUploadPresentationToast);
|
||||
|
||||
await test.waitAndClick(e.confirmManagePresentation);
|
||||
await test.hasText(e.presentationStatusInfo, [e.convertingPresentationFileToast], uploadTimeout);
|
||||
await test.hasText(e.smallToastMsg, e.presentationUploadedToast, uploadTimeout);
|
||||
}
|
||||
|
||||
exports.checkSvgIndex = checkSvgIndex;
|
||||
exports.getSlideOuterHtml = getSlideOuterHtml;
|
||||
exports.uploadPresentation = uploadPresentation;
|
||||
exports.uploadSinglePresentation = uploadSinglePresentation;
|
||||
exports.uploadMultiplePresentations = uploadMultiplePresentations;
|
||||
|
@ -13,8 +13,11 @@ class MultiUsers {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
async initPages(page1) {
|
||||
async initPages(page1, waitAndClearDefaultPresentationNotificationModPage = false) {
|
||||
await this.initModPage(page1);
|
||||
if (waitAndClearDefaultPresentationNotificationModPage) {
|
||||
await waitAndClearDefaultPresentationNotification(this.modPage);
|
||||
}
|
||||
await this.initUserPage();
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,10 @@ const iPhone11 = devices['iPhone 11'];
|
||||
|
||||
test.describe.parallel('User', () => {
|
||||
test.describe.parallel('Actions', () => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#set-status--raise-hand-automated
|
||||
test('Raise and lower Hand Toast', async ({ browser, context, page }) => {
|
||||
const multiusers = new MultiUsers(browser, context);
|
||||
await multiusers.initModPage(page);
|
||||
await multiusers.initModPage(page, true);
|
||||
await multiusers.raiseAndLowerHand();
|
||||
});
|
||||
|
||||
@ -23,6 +24,7 @@ test.describe.parallel('User', () => {
|
||||
});
|
||||
|
||||
test.describe.parallel('List', () => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#set-status--raise-hand-automated
|
||||
test('Change user status @ci', async ({ browser, page }) => {
|
||||
const status = new Status(browser, page);
|
||||
await status.init(true, true);
|
||||
|
@ -2,6 +2,7 @@ const { test } = require('@playwright/test');
|
||||
const { Webcam } = require('./webcam');
|
||||
|
||||
test.describe.parallel('Webcam @ci', () => {
|
||||
// https://docs.bigbluebutton.org/2.5/release-tests.html#joining-webcam-automated
|
||||
test('Shares webcam', async ({ browser, page }) => {
|
||||
const webcam = new Webcam(browser, page);
|
||||
await webcam.init(true, true);
|
||||
|
@ -2,6 +2,21 @@
|
||||
|
||||
set +x
|
||||
|
||||
removeOldOverride() {
|
||||
service_name=$1
|
||||
# check if override file has been modified. If not it can be safely removed
|
||||
if [ -f "/etc/systemd/system/${service_name}.service.d/override.conf" ] ; then
|
||||
if echo "d32a00b9a2669b3fe757b8de3470e358 /etc/systemd/system/${service_name}.service.d/override.conf" | md5sum -c --quiet 2>/dev/null >/dev/null ; then
|
||||
rm -f "/etc/systemd/system/${service_name}.service.d/override.conf"
|
||||
fi
|
||||
fi
|
||||
if [ -d "/etc/systemd/system/${service_name}.service.d" ]; then
|
||||
if [ $(ls "/etc/systemd/system/${service_name}.service.d" |wc -l) = 0 ]; then
|
||||
rmdir "/etc/systemd/system/${service_name}.service.d"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
BIGBLUEBUTTON_USER=bigbluebutton
|
||||
|
||||
if ! id freeswitch >/dev/null 2>&1; then
|
||||
@ -137,5 +152,12 @@ fi
|
||||
# Fix permissions for logging
|
||||
chown bigbluebutton:bigbluebutton /var/log/bbb-fsesl-akka
|
||||
|
||||
# cleanup old overrides
|
||||
|
||||
removeOldOverride bbb-apps-akka
|
||||
removeOldOverride bbb-fsesl-akka
|
||||
removeOldOverride bbb-transcode-akka
|
||||
|
||||
|
||||
# Load the overrides
|
||||
systemctl daemon-reload
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=Etherpad Server
|
||||
Wants=redis-server.service
|
||||
After=syslog.target network.target
|
||||
After=syslog.target network.target redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton HTML5 service
|
||||
Wants=redis.service mongod.service disable-transparent-huge-pages.service bbb-pads.service
|
||||
After=redis.service mongod.service disable-transparent-huge-pages.service bbb-pads.service syslog.target network.target
|
||||
Wants=redis-server.service mongod.service disable-transparent-huge-pages.service bbb-pads.service
|
||||
After=redis-server.service mongod.service disable-transparent-huge-pages.service bbb-pads.service syslog.target network.target
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton Pads
|
||||
Wants=redis.service etherpad.service
|
||||
After=syslog.target network.target
|
||||
Wants=redis-server.service etherpad.service
|
||||
After=syslog.target network.target redis-server.service etherpad.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,8 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton Web Application
|
||||
Requires=network.target
|
||||
After=redis.service
|
||||
Wants=redis-server.service
|
||||
After=redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton Webhooks
|
||||
Wants=redis-server.service
|
||||
After=syslog.target network.target
|
||||
After=syslog.target network.target redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton WebRTC SFU
|
||||
Wants=redis-server.service
|
||||
After=syslog.target network.target freeswitch.service kurento-media-server.service
|
||||
After=syslog.target network.target freeswitch.service kurento-media-server.service redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -37,6 +37,6 @@ gem 'bbbevents', '~> 1.2'
|
||||
gem 'rake', '>= 12.3', '<14'
|
||||
|
||||
group :test, optional: true do
|
||||
gem 'rubocop', '~> 0.79.0'
|
||||
gem 'rubocop', '~> 1.31.1'
|
||||
gem 'minitest', '~> 5.14.1'
|
||||
end
|
||||
|
@ -17,11 +17,11 @@ GEM
|
||||
ffi (1.15.5)
|
||||
i18n (1.10.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
java_properties (0.0.4)
|
||||
journald-logger (3.1.0)
|
||||
journald-native (~> 1.0)
|
||||
journald-native (1.0.12)
|
||||
json (2.6.2)
|
||||
jwt (2.3.0)
|
||||
locale (2.1.3)
|
||||
loofah (2.18.0)
|
||||
@ -52,19 +52,26 @@ GEM
|
||||
redis (4.6.0)
|
||||
redis-namespace (1.8.2)
|
||||
redis (>= 3.0.4)
|
||||
regexp_parser (2.5.0)
|
||||
resque (2.0.0)
|
||||
mono_logger (~> 1.0)
|
||||
multi_json (~> 1.0)
|
||||
redis-namespace (~> 1.6)
|
||||
sinatra (>= 0.9.2)
|
||||
vegas (~> 0.1.2)
|
||||
rubocop (0.79.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
rexml (3.2.5)
|
||||
rubocop (1.31.1)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
parser (>= 3.1.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.18.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.18.0)
|
||||
parser (>= 3.1.1.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
@ -77,7 +84,7 @@ GEM
|
||||
tilt (2.0.10)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
unicode-display_width (1.6.1)
|
||||
unicode-display_width (2.2.0)
|
||||
vegas (0.1.11)
|
||||
rack (>= 1.0.0)
|
||||
|
||||
@ -102,7 +109,7 @@ DEPENDENCIES
|
||||
rb-inotify (~> 0.10)
|
||||
redis (~> 4.1)
|
||||
resque (~> 2.0.0)
|
||||
rubocop (~> 0.79.0)
|
||||
rubocop (~> 1.31.1)
|
||||
rubyzip (~> 2.0)
|
||||
|
||||
BUNDLED WITH
|
||||
|
@ -51,7 +51,7 @@ module BigBlueButton
|
||||
temp_out = "#{File.dirname(png_out)}/temp-#{File.basename(png_out, '.png')}"
|
||||
status = BigBlueButton.execute(
|
||||
[
|
||||
'pdftocairo', '-png', '-f', page_num.to_s, '-l', page_num.to_s, '-scale-to', scale.to_s, '-singlefile',
|
||||
'pdftocairo', '-png', '-f', page_num.to_s, '-l', page_num.to_s, '-scale-to', scale.to_s, '-singlefile', '-cropbox',
|
||||
pdf_presentation, temp_out,
|
||||
],
|
||||
false
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton resque worker for recordings
|
||||
Wants=redis.service
|
||||
After=redis.service
|
||||
Wants=redis-server.service
|
||||
After=redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,5 +1,7 @@
|
||||
[Unit]
|
||||
Description=BigBlueButton recording processing starter
|
||||
Wants=redis-server.service
|
||||
After=redis-server.service
|
||||
PartOf=bigbluebutton.target
|
||||
|
||||
[Service]
|
||||
|
Loading…
Reference in New Issue
Block a user