diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartCustomPollReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartCustomPollReqMsgHdlr.scala index 0cba314262..755bfdc01c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartCustomPollReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartCustomPollReqMsgHdlr.scala @@ -29,7 +29,7 @@ trait StartCustomPollReqMsgHdlr extends RightsManagementTrait { PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) } else { for { - pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleChoice, msg.body.answers, liveMeeting) + pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleResponse, msg.body.answers, liveMeeting) } yield { broadcastEvent(msg, pvo) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartPollReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartPollReqMsgHdlr.scala index 6a9210317f..704782e41d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartPollReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/StartPollReqMsgHdlr.scala @@ -30,7 +30,7 @@ trait StartPollReqMsgHdlr extends RightsManagementTrait { PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) } else { for { - pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleChoice, liveMeeting) + pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleResponse, liveMeeting) } yield { broadcastEvent(msg, pvo) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala index 44dac73641..391a79c479 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala @@ -73,12 +73,12 @@ case class MeetingStatus(startEndTimeStatus: StartEndTimeStatus, recordingStatus case class Meeting2x(defaultProps: DefaultProps, meetingStatus: MeetingStatus) case class SimpleAnswerOutVO(id: Int, key: String) -case class SimplePollOutVO(id: String, isMultipleChoice: Boolean, answers: Array[SimpleAnswerOutVO]) +case class SimplePollOutVO(id: String, isMultipleResponse: Boolean, answers: Array[SimpleAnswerOutVO]) case class SimpleVoteOutVO(id: Int, key: String, numVotes: Int) case class SimplePollResultOutVO(id: String, answers: Array[SimpleVoteOutVO], numRespondents: Int, numResponders: Int) case class Responder(userId: String, name: String) case class AnswerVO(id: Int, key: String, text: Option[String], responders: Option[Array[Responder]]) -case class QuestionVO(id: Int, questionType: String, isMultipleChoice: Boolean, questionText: Option[String], answers: Option[Array[AnswerVO]]) +case class QuestionVO(id: Int, questionType: String, isMultipleResponse: Boolean, questionText: Option[String], answers: Option[Array[AnswerVO]]) case class PollVO(id: String, questions: Array[QuestionVO], title: Option[String], started: Boolean, stopped: Boolean, showResult: Boolean) case class UserVO(id: String, externalId: String, name: String, role: String, diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PollsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PollsMsgs.scala index aa6bfcdbd7..d1f5d518cf 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PollsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PollsMsgs.scala @@ -44,11 +44,11 @@ case class ShowPollResultReqMsgBody(requesterId: String, pollId: String) object StartCustomPollReqMsg { val NAME = "StartCustomPollReqMsg" } case class StartCustomPollReqMsg(header: BbbClientMsgHeader, body: StartCustomPollReqMsgBody) extends StandardMsg -case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleChoice: Boolean, answers: Seq[String]) +case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleResponse: Boolean, answers: Seq[String]) object StartPollReqMsg { val NAME = "StartPollReqMsg" } case class StartPollReqMsg(header: BbbClientMsgHeader, body: StartPollReqMsgBody) extends StandardMsg -case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleChoice: Boolean) +case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleResponse: Boolean) object StopPollReqMsg { val NAME = "StopPollReqMsg" } case class StopPollReqMsg(header: BbbClientMsgHeader, body: StopPollReqMsgBody) extends StandardMsg diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js b/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js index cbad087e70..e4761abeec 100644 --- a/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js +++ b/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js @@ -2,7 +2,7 @@ import RedisPubSub from '/imports/startup/server/redis'; import { check } from 'meteor/check'; import { extractCredentials } from '/imports/api/common/server/helpers'; -export default function startPoll(pollType, pollId, isMultipleChoice, answers) { +export default function startPoll(pollType, pollId, isMultipleResponse, answers) { const REDIS_CONFIG = Meteor.settings.private.redis; const CHANNEL = REDIS_CONFIG.channels.toAkkaApps; @@ -17,7 +17,7 @@ export default function startPoll(pollType, pollId, isMultipleChoice, answers) { requesterId: requesterUserId, pollId: `${pollId}/${new Date().getTime()}`, pollType, - isMultipleChoice, + isMultipleResponse, }; if (pollType === 'custom') { diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js index 28e9f2db79..f57608f0b8 100644 --- a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js +++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js @@ -15,7 +15,7 @@ export default function addPoll(meetingId, requesterId, poll) { key: String, }, ], - isMultipleChoice: Boolean + isMultipleResponse: Boolean, }); const userSelector = { diff --git a/bigbluebutton-html5/imports/ui/components/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/component.jsx index dfb518258f..f7a4c2a34d 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/poll/component.jsx @@ -86,10 +86,10 @@ const intlMessages = defineMessages({ id: 'app.poll.a5', description: 'label for A / B / C / D / E poll', }, - enableMultipleChoiceLabel : { - id: 'app.poll.enableMultipleChoiceLabel', + enableMultipleResponseLabel: { + id: 'app.poll.enableMultipleResponseLabel', description: 'label for checkbox to enable multiple choice', - } + }, }); const CHAT_ENABLED = Meteor.settings.public.chat.enabled; @@ -104,13 +104,13 @@ class Poll extends Component { customPollReq: false, isPolling: false, customPollValues: [], - isMultipleChoice: false, + isMultipleResponse: false, }; this.inputEditor = []; this.toggleCustomFields = this.toggleCustomFields.bind(this); - this.toggleIsMultipleChoice = this.toggleIsMultipleChoice.bind(this); + this.toggleIsMultipleResponse = this.toggleIsMultipleResponse.bind(this); this.renderQuickPollBtns = this.renderQuickPollBtns.bind(this); this.renderCustomView = this.renderCustomView.bind(this); this.renderInputFields = this.renderInputFields.bind(this); @@ -166,16 +166,16 @@ class Poll extends Component { return this.setState({ customPollReq: !customPollReq }); } - toggleIsMultipleChoice() { - const { isMultipleChoice } = this.state; - return this.setState({ isMultipleChoice: !isMultipleChoice }); + toggleIsMultipleResponse() { + const { isMultipleResponse } = this.state; + return this.setState({ isMultipleResponse: !isMultipleResponse }); } renderQuickPollBtns() { const { isMeteorConnected, pollTypes, startPoll, intl, } = this.props; - const { isMultipleChoice } = this.state; + const { isMultipleResponse } = this.state; const btns = pollTypes.map((type) => { if (type === 'custom') return false; @@ -195,7 +195,7 @@ class Poll extends Component { key={_.uniqueId('quick-poll-')} onClick={() => { Session.set('pollInitiated', true); - this.setState({ isPolling: true }, () => startPoll(type, isMultipleChoice)); + this.setState({ isPolling: true }, () => startPoll(type, isMultipleResponse)); }} />); }); @@ -205,7 +205,7 @@ class Poll extends Component { renderCustomView() { const { intl, startCustomPoll } = this.props; - const { isMultipleChoice } = this.state; + const { isMultipleResponse } = this.state; const isDisabled = _.compact(this.inputEditor).length < 1; return ( @@ -215,7 +215,7 @@ class Poll extends Component { onClick={() => { if (this.inputEditor.length > 0) { Session.set('pollInitiated', true); - this.setState({ isPolling: true }, () => startCustomPoll('custom', isMultipleChoice, _.compact(this.inputEditor))); + this.setState({ isPolling: true }, () => startCustomPoll('custom', isMultipleResponse, _.compact(this.inputEditor))); } }} label={intl.formatMessage(intlMessages.startCustomLabel)} @@ -285,19 +285,19 @@ class Poll extends Component { renderPollOptions() { const { isMeteorConnected, intl } = this.props; - const { customPollReq, isMultipleChoice } = this.state; + const { customPollReq, isMultipleResponse } = this.state; return (
-
diff --git a/bigbluebutton-html5/imports/ui/components/poll/container.jsx b/bigbluebutton-html5/imports/ui/components/poll/container.jsx index ffdf2dcf30..685c4c76d0 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/poll/container.jsx @@ -22,9 +22,9 @@ export default withTracker(() => { const pollId = currentSlide ? currentSlide.id : PUBLIC_CHAT_KEY; - const startPoll = (type, isMultipleChoice) => makeCall('startPoll', type, pollId, isMultipleChoice); + const startPoll = (type, isMultipleResponse) => makeCall('startPoll', type, pollId, isMultipleResponse); - const startCustomPoll = (type, isMultipleChoice, answers) => makeCall('startPoll', type, pollId, isMultipleChoice, answers); + const startCustomPoll = (type, isMultipleResponse, answers) => makeCall('startPoll', type, pollId, isMultipleResponse, answers); const stopPoll = () => makeCall('stopPoll'); diff --git a/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx index cd0b5cb020..6207e91d7c 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx @@ -65,10 +65,10 @@ class LiveResult extends PureComponent { if (responses) { const response = responses.find(r => r.userId === user.userId); if (response) { - let answerKeys = []; + const answerKeys = []; response.answerIds.forEach((answerId) => { answerKeys.push(answers[answerId].key); - }) + }); answer = answerKeys.join(', '); } } @@ -198,14 +198,14 @@ class LiveResult extends PureComponent { onClick={() => { Session.set('pollInitiated', false); Service.publishPoll(); - const { answers, numRespondents } = currentPoll; + const { answers, numResponders } = currentPoll; let responded = 0; let resultString = 'bbb-published-poll-\n'; answers.map((item) => { responded += item.numVotes; return item; - }).map((item) => { - const numResponded = responded === numRespondents ? numRespondents : responded; + }).forEach((item) => { + const numResponded = numResponders || responded; const pct = Math.round(item.numVotes / numResponded * 100); const pctFotmatted = `${Number.isNaN(pct) ? 0 : pct}%`; resultString += `${item.key}: ${item.numVotes || 0} | ${pctFotmatted}\n`; diff --git a/bigbluebutton-html5/imports/ui/components/polling/component.jsx b/bigbluebutton-html5/imports/ui/components/polling/component.jsx index 1383cded3a..4a00b40e60 100644 --- a/bigbluebutton-html5/imports/ui/components/polling/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/polling/component.jsx @@ -4,9 +4,9 @@ import Button from '/imports/ui/components/button/component'; import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component'; import { defineMessages, injectIntl } from 'react-intl'; import cx from 'classnames'; +import { Meteor } from 'meteor/meteor'; import { styles } from './styles.scss'; import AudioService from '/imports/ui/components/audio/service'; -import {Meteor} from "meteor/meteor"; const intlMessages = defineMessages({ pollingTitleLabel: { @@ -18,8 +18,8 @@ const intlMessages = defineMessages({ pollAnswerDesc: { id: 'app.polling.pollAnswerDesc', }, - pollSendLabel: { - id: 'app.polling.pollSendLabel', + pollSubmitLabel: { + id: 'app.polling.pollSubmitLabel', }, }); @@ -28,8 +28,8 @@ class Polling extends Component { super(props); this.state = { - checkedAnswers: [] - } + checkedAnswers: [], + }; this.play = this.play.bind(this); this.renderButtonAnswers = this.renderButtonAnswers.bind(this); @@ -66,52 +66,52 @@ class Polling extends Component { } return ( +
+
+
+ {intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })} +
+
); - }) + }); } handleCheckboxChange(pollId, answerId) { - const {checkedAnswers} = this.state + const { checkedAnswers } = this.state; if (checkedAnswers.includes(answerId)) { - checkedAnswers.splice(checkedAnswers.indexOf(answerId), 1) + checkedAnswers.splice(checkedAnswers.indexOf(answerId), 1); } else { - checkedAnswers.push(answerId) + checkedAnswers.push(answerId); } checkedAnswers.sort(); } handleSubmit(pollId) { - const {handleVote} = this.props; - const {checkedAnswers} = this.state - handleVote(pollId, checkedAnswers) + const { handleVote } = this.props; + const { checkedAnswers } = this.state; + handleVote(pollId, checkedAnswers); } renderCheckboxAnswers() { @@ -132,48 +132,48 @@ class Polling extends Component { } return ( +
+ this.handleCheckboxChange(poll.pollId, pollAnswer.id)} + aria-labelledby={`pollAnswerLabel${pollAnswer.key}`} + aria-describedby={`pollAnswerDesc${pollAnswer.key}`} + /> +
- this.handleCheckboxChange(poll.pollId, pollAnswer.id)} - aria-labelledby={`pollAnswerLabel${pollAnswer.key}`} - aria-describedby={`pollAnswerDesc${pollAnswer.key}`} - /> - -
- {intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })} -
-
- {intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })} -
+ {intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
+
+ {intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })} +
+
); })}
- ) + ); } render() { @@ -207,7 +207,7 @@ class Polling extends Component { {intl.formatMessage(intlMessages.pollingTitleLabel)}
- {poll.isMultipleChoice ? this.renderCheckboxAnswers() : this.renderButtonAnswers()} + {poll.isMultipleResponse ? this.renderCheckboxAnswers() : this.renderButtonAnswers()}
); diff --git a/bigbluebutton-html5/imports/ui/components/polling/service.js b/bigbluebutton-html5/imports/ui/components/polling/service.js index e45d8c62b7..109715db55 100644 --- a/bigbluebutton-html5/imports/ui/components/polling/service.js +++ b/bigbluebutton-html5/imports/ui/components/polling/service.js @@ -31,7 +31,7 @@ const mapPolls = () => { poll: { answers: poll.answers, pollId: poll.id, - isMultipleChoice: poll.isMultipleChoice, + isMultipleResponse: poll.isMultipleResponse, stackOptions, }, pollExists: true, diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx index ae58d937a4..4c19c6d545 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import PollService from '/imports/ui/components/poll/service'; import { injectIntl, defineMessages } from 'react-intl'; import styles from './styles'; -import { prototype } from 'clipboard'; const intlMessages = defineMessages({ pollResultAria: { @@ -66,7 +65,7 @@ class PollDrawComponent extends Component { // rendering / rerendering the text objects const { annotation } = this.props; - const { points, result } = annotation; + const { points, result, numResponders } = annotation; const { slideWidth, slideHeight, intl } = this.props; // x1 and y1 - coordinates of the top left corner of the annotation @@ -84,18 +83,14 @@ class PollDrawComponent extends Component { const width = ((initialWidth - 0.001) / 100) * slideWidth; const height = ((initialHeight - 0.001) / 100) * slideHeight; - let votesTotal = 0; - let maxNumVotes = 0; const textArray = []; // counting the total number of votes, finding the biggest number of votes - result.reduce((previousValue, currentValue) => { - votesTotal = previousValue + currentValue.numVotes; - if (maxNumVotes < currentValue.numVotes) { - maxNumVotes = currentValue.numVotes; + const maxNumVotes = result.reduce((previousValue, currentValue) => { + if (previousValue < currentValue.numVotes) { + return currentValue.numVotes; } - - return votesTotal; + return previousValue; }, 0); // filling the textArray with data to display @@ -129,11 +124,11 @@ class PollDrawComponent extends Component { } _tempArray.push(_result.key, `${_result.numVotes}`); - if (votesTotal === 0) { + if (numResponders === 0) { _tempArray.push('0%'); _tempArray.push(i); } else { - const percResult = (_result.numVotes / votesTotal) * 100; + const percResult = (_result.numVotes / numResponders) * 100; _tempArray.push(`${Math.round(percResult)}%`); _tempArray.push(i); } @@ -604,7 +599,7 @@ class PollDrawComponent extends Component { const { prepareToDisplay, textArray } = this.state; let ariaResultLabel = `${intl.formatMessage(intlMessages.pollResultAria)}: `; - textArray.map((t, idx) => { + textArray.forEach((t, idx) => { const pollLine = t.slice(0, -1); ariaResultLabel += `${idx > 0 ? ' |' : ''} ${pollLine.join(' | ')}`; }); diff --git a/bigbluebutton-html5/private/locales/de.json b/bigbluebutton-html5/private/locales/de.json index d46d031d53..72d661daac 100644 --- a/bigbluebutton-html5/private/locales/de.json +++ b/bigbluebutton-html5/private/locales/de.json @@ -213,7 +213,7 @@ "app.presentationUploder.clearErrors": "Fehler löschen", "app.presentationUploder.clearErrorsDesc": "Löscht fehlgeschlagene Präsentationsuploads", "app.poll.pollPaneTitle": "Umfrage", - "app.poll.enableMultipleChoiceLabel": "Multiple-Choice?", + "app.poll.enableMultipleResponseLabel": "Mehrere Antworten pro Teilnehmer zulassen?", "app.poll.quickPollTitle": "Schnellumfrage", "app.poll.hidePollDesc": "Versteckt das Umfragemenü", "app.poll.customPollInstruction": "Um eine benutzerdefinierte Umfrage zu erstellen, wählen Sie die Schaltfläche unten und geben Sie Ihre Optionen ein.", @@ -253,6 +253,7 @@ "app.polling.pollingTitle": "Umfrageoptionen", "app.polling.pollAnswerLabel": "Umfrageantwort {0}", "app.polling.pollAnswerDesc": "Diese Option auswählen für Umfrage {0}", + "app.polling.pollSubmitLabel": "Abgeben", "app.failedMessage": "Es gibt Verbindungsprobleme mit dem Server.", "app.downloadPresentationButton.label": "Ursprüngliche Version der Präsentation herunterladen", "app.connectingMessage": "Verbinde...", diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index ffe02d1880..7b5ed61ba3 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -213,7 +213,7 @@ "app.presentationUploder.clearErrors": "Clear errors", "app.presentationUploder.clearErrorsDesc": "Clears failed presentation uploads", "app.poll.pollPaneTitle": "Polling", - "app.poll.enableMultipleChoiceLabel": "Multiple response?", + "app.poll.enableMultipleResponseLabel": "Allow multiple answers per respondent?", "app.poll.quickPollTitle": "Quick Poll", "app.poll.hidePollDesc": "Hides the poll menu pane", "app.poll.customPollInstruction": "To create a custom poll, select the button below and input your options.", @@ -256,6 +256,7 @@ "app.polling.pollingTitle": "Polling options", "app.polling.pollAnswerLabel": "Poll answer {0}", "app.polling.pollAnswerDesc": "Select this option to vote for {0}", + "app.polling.pollSubmitLabel": "Submit", "app.failedMessage": "Apologies, trouble connecting to the server.", "app.downloadPresentationButton.label": "Download the original presentation", "app.connectingMessage": "Connecting ...",