From ca2ad30e12df3b0b9f5d8f3f268fe4d907260004 Mon Sep 17 00:00:00 2001
From: germanocaumo <germanocaumo@gmail.com>
Date: Tue, 11 May 2021 18:01:03 +0000
Subject: [PATCH] fix(record): add missing info to polling events

Add PollPublishedRecordEvent with all infos (also fix duplicated handlePollStoppedEvtMsg)
Add answer text to UserRespondedToPollRecordEvent (useful for custom answers)
Add type in PollStartedRecordEvent
---
 .../apps/polls/RespondToPollReqMsgHdlr.scala  | 11 ++--
 .../org/bigbluebutton/core/models/Polls.scala | 12 ++---
 .../events/PollPublishedRecordEvent.scala     | 53 +++++++++++++++++++
 .../events/PollStartedRecordEvent.scala       |  5 ++
 .../UserRespondedToPollRecordEvent.scala      |  5 ++
 .../endpoint/redis/RedisRecorderActor.scala   | 21 +++++---
 .../common2/domain/Meeting2x.scala            |  2 +-
 .../common2/msgs/PollsMsgs.scala              |  2 +-
 .../api/polls/server/handlers/userVoted.js    |  1 +
 .../imports/ui/components/poll/component.jsx  | 22 ++++----
 .../ui/components/polling/component.jsx       |  4 +-
 bigbluebutton-html5/public/locales/pt_BR.json |  2 +-
 12 files changed, 107 insertions(+), 33 deletions(-)
 create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollPublishedRecordEvent.scala

diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToPollReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToPollReqMsgHdlr.scala
index e422ca0d42..4796ac11ab 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToPollReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToPollReqMsgHdlr.scala
@@ -23,12 +23,12 @@ trait RespondToPollReqMsgHdlr {
       bus.outGW.send(msgEvent)
     }
 
-    def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int): Unit = {
+    def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, answer: String): Unit = {
       val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
       val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
       val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
 
-      val body = UserRespondedToPollRecordMsgBody(pollId, answerId)
+      val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer)
       val event = UserRespondedToPollRecordMsg(header, body)
       val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
       bus.outGW.send(msgEvent)
@@ -50,7 +50,12 @@ trait RespondToPollReqMsgHdlr {
         msg.body.questionId, msg.body.answerId, liveMeeting)
     } yield {
       broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
-      broadcastUserRespondedToPollRecordMsg(msg, pollId, msg.body.answerId)
+      for {
+        simplePoll <- Polls.getSimplePoll(pollId, liveMeeting.polls)
+      } yield {
+        val answerText = simplePoll.answers(msg.body.answerId).key
+        broadcastUserRespondedToPollRecordMsg(msg, pollId, msg.body.answerId, answerText)
+      }
 
       for {
         presenter <- Users2x.findPresenter(liveMeeting.users2x)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Polls.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Polls.scala
index 0170f6200b..b419931635 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Polls.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Polls.scala
@@ -18,7 +18,7 @@ object Polls {
       val numRespondents: Int = Users2x.numUsers(lm.users2x) - 1 // subtract the presenter
 
       for {
-        poll <- PollFactory.createPoll(stampedPollId, pollType, numRespondents, None)
+        poll <- PollFactory.createPoll(stampedPollId, pollType, numRespondents, None, Some(question))
       } yield {
         lm.polls.save(poll)
         poll
@@ -172,7 +172,7 @@ object Polls {
     def createPoll(stampedPollId: String): Option[Poll] = {
       val numRespondents: Int = Users2x.numUsers(lm.users2x) - 1 // subtract the presenter
       for {
-        poll <- PollFactory.createPoll(stampedPollId, pollType, numRespondents, Some(answers))
+        poll <- PollFactory.createPoll(stampedPollId, pollType, numRespondents, Some(answers), Some(question))
       } yield {
         lm.polls.save(poll)
         poll
@@ -435,7 +435,7 @@ object PollType {
   val CustomPollType = "CUSTOM"
   val LetterPollType = "A-"
   val NumberPollType = "1-"
-  val ResponsePollType = "RP"
+  val ResponsePollType = "R-"
 }
 
 object PollFactory {
@@ -561,12 +561,12 @@ object PollFactory {
     questionOption
   }
 
-  def createPoll(id: String, pollType: String, numRespondents: Int, answers: Option[Seq[String]]): Option[Poll] = {
+  def createPoll(id: String, pollType: String, numRespondents: Int, answers: Option[Seq[String]], title: Option[String]): Option[Poll] = {
     var poll: Option[Poll] = None
 
     createQuestion(pollType, answers) match {
       case Some(question) => {
-        poll = Some(new Poll(id, Array(question), numRespondents, None))
+        poll = Some(new Poll(id, Array(question), numRespondents, title))
       }
       case None => poll = None
     }
@@ -644,7 +644,7 @@ class Poll(val id: String, val questions: Array[Question], val numRespondents: I
   }
 
   def toSimplePollResultOutVO(): SimplePollResultOutVO = {
-    new SimplePollResultOutVO(id, questions(0).toSimpleVotesOutVO(), numRespondents, _numResponders)
+    new SimplePollResultOutVO(id, title, questions(0).toSimpleVotesOutVO(), numRespondents, _numResponders)
   }
 }
 
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollPublishedRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollPublishedRecordEvent.scala
new file mode 100755
index 0000000000..091c512204
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollPublishedRecordEvent.scala
@@ -0,0 +1,53 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation; either version 3.0 of the License, or (at your option) any later
+ * version.
+ *
+ * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.bigbluebutton.core.record.events
+
+import org.bigbluebutton.common2.domain.SimpleVoteOutVO
+import org.bigbluebutton.common2.util.JsonUtil
+
+class PollPublishedRecordEvent extends AbstractPollRecordEvent {
+  import PollPublishedRecordEvent._
+
+  setEvent("PollPublishedRecordEvent")
+
+  def setQuestion(question: String) {
+    eventMap.put(QUESTION, question)
+  }
+
+  def setAnswers(answers: Array[SimpleVoteOutVO]) {
+    eventMap.put(ANSWERS, JsonUtil.toJson(answers))
+  }
+
+  def setNumRespondents(numRespondents: Int) {
+    eventMap.put(NUM_RESPONDENTS, Integer.toString(numRespondents))
+  }
+
+  def setNumResponders(numResponders: Int) {
+    eventMap.put(NUM_RESPONDERS, Integer.toString(numResponders))
+  }
+}
+
+object PollPublishedRecordEvent {
+  protected final val USER_ID = "userId"
+  protected final val QUESTION = "question"
+  protected final val ANSWERS = "answers"
+  protected final val NUM_RESPONDENTS = "numRespondents"
+  protected final val NUM_RESPONDERS = "numResponders"
+}
\ No newline at end of file
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollStartedRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollStartedRecordEvent.scala
index 06e6db0795..db4a2d44d5 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollStartedRecordEvent.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/PollStartedRecordEvent.scala
@@ -38,10 +38,15 @@ class PollStartedRecordEvent extends AbstractPollRecordEvent {
   def setAnswers(answers: Array[SimpleAnswerOutVO]) {
     eventMap.put(ANSWERS, JsonUtil.toJson(answers))
   }
+
+  def setType(pollType: String) {
+    eventMap.put(TYPE, pollType)
+  }
 }
 
 object PollStartedRecordEvent {
   protected final val USER_ID = "userId"
   protected final val QUESTION = "question"
   protected final val ANSWERS = "answers"
+  protected final val TYPE = "type"
 }
\ No newline at end of file
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/UserRespondedToPollRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/UserRespondedToPollRecordEvent.scala
index ea955895b7..508e58af6f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/UserRespondedToPollRecordEvent.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/UserRespondedToPollRecordEvent.scala
@@ -31,9 +31,14 @@ class UserRespondedToPollRecordEvent extends AbstractPollRecordEvent {
   def setAnswerId(answerId: Int) {
     eventMap.put(ANSWER_ID, Integer.toString(answerId))
   }
+
+  def setAnswer(answer: String) {
+    eventMap.put(ANSWER, answer)
+  }
 }
 
 object UserRespondedToPollRecordEvent {
   protected final val USER_ID = "userId"
   protected final val ANSWER_ID = "answerId"
+  protected final val ANSWER = "answer"
 }
\ No newline at end of file
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
index 58b16e1fd6..74571505ff 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
@@ -557,6 +557,7 @@ class RedisRecorderActor(
     ev.setPollId(msg.body.pollId)
     ev.setQuestion(msg.body.question)
     ev.setAnswers(msg.body.poll.answers)
+    ev.setType(msg.body.pollType)
 
     record(msg.header.meetingId, ev.toMap.asJava)
   }
@@ -566,23 +567,27 @@ class RedisRecorderActor(
     ev.setPollId(msg.body.pollId)
     ev.setUserId(msg.header.userId)
     ev.setAnswerId(msg.body.answerId)
+    ev.setAnswer(msg.body.answer)
 
     record(msg.header.meetingId, ev.toMap.asJava)
   }
 
   private def handlePollStoppedEvtMsg(msg: PollStoppedEvtMsg): Unit = {
-    pollStoppedRecordHelper(msg.header.meetingId, msg.body.pollId)
-  }
+    val ev = new PollStoppedRecordEvent()
+    ev.setPollId(msg.body.pollId)
 
-  private def handlePollShowResultEvtMsg(msg: PollShowResultEvtMsg): Unit = {
-    pollStoppedRecordHelper(msg.header.meetingId, msg.body.pollId)
+    record(msg.header.meetingId, ev.toMap.asJava)
   }
 
-  private def pollStoppedRecordHelper(meetingId: String, pollId: String): Unit = {
-    val ev = new PollStoppedRecordEvent()
-    ev.setPollId(pollId)
+  private def handlePollShowResultEvtMsg(msg: PollShowResultEvtMsg): Unit = {
+    val ev = new PollPublishedRecordEvent()
+    ev.setPollId(msg.body.pollId)
+    ev.setQuestion(msg.body.poll.title.getOrElse(""))
+    ev.setAnswers(msg.body.poll.answers)
+    ev.setNumRespondents(msg.body.poll.numRespondents)
+    ev.setNumResponders(msg.body.poll.numResponders)
 
-    record(meetingId, ev.toMap.asJava)
+    record(msg.header.meetingId, ev.toMap.asJava)
   }
 
   private def checkRecordingDBStatus(): Unit = {
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 67f842e615..3863338020 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
@@ -75,7 +75,7 @@ case class Meeting2x(defaultProps: DefaultProps, meetingStatus: MeetingStatus)
 case class SimpleAnswerOutVO(id: Int, key: String)
 case class SimplePollOutVO(id: String, 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 SimplePollResultOutVO(id: String, title: Option[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, multiResponse: Boolean, questionText: Option[String], answers: Option[Array[AnswerVO]])
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 efc6c00880..ee209cd9b2 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
@@ -28,7 +28,7 @@ case class PollUpdatedEvtMsgBody(pollId: String, poll: SimplePollResultOutVO)
 
 object UserRespondedToPollRecordMsg { val NAME = "UserRespondedToPollRecordMsg" }
 case class UserRespondedToPollRecordMsg(header: BbbClientMsgHeader, body: UserRespondedToPollRecordMsgBody) extends BbbCoreMsg
-case class UserRespondedToPollRecordMsgBody(pollId: String, answerId: Int)
+case class UserRespondedToPollRecordMsgBody(pollId: String, answerId: Int, answer: String)
 
 object RespondToPollReqMsg { val NAME = "RespondToPollReqMsg" }
 case class RespondToPollReqMsg(header: BbbClientMsgHeader, body: RespondToPollReqMsgBody) extends StandardMsg
diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js
index d73d8b6790..0ba233ae72 100644
--- a/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js
+++ b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js
@@ -7,6 +7,7 @@ export default function userVoted({ body }, meetingId) {
   check(meetingId, String);
   check(poll, {
     id: String,
+    title: String,
     answers: [
       {
         id: Number,
diff --git a/bigbluebutton-html5/imports/ui/components/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/component.jsx
index b3ee657046..5629c5ac7b 100644
--- a/bigbluebutton-html5/imports/ui/components/poll/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/poll/component.jsx
@@ -231,7 +231,7 @@ class Poll extends Component {
     const { optList, type, error } = this.state;
     const list = [...optList];
     const validatedVal = validateInput(e.target.value).replace(/\s{2,}/g, ' ');
-    const clearError = validatedVal.length > 0 && type !== 'RP';
+    const clearError = validatedVal.length > 0 && type !== 'R-';
     list[index] = { val: validatedVal };
     this.setState({ optList: list, error: clearError ? null : error });
   }
@@ -239,7 +239,7 @@ class Poll extends Component {
   handleTextareaChange(e) {
     const { type, error } = this.state;
     const validatedQuestion = validateInput(e.target.value);
-    const clearError = validatedQuestion.length > 0 && type === 'RP';
+    const clearError = validatedQuestion.length > 0 && type === 'R-';
     this.setState({ question: validateInput(e.target.value), error: clearError ? null : error });
   }
 
@@ -365,7 +365,7 @@ class Poll extends Component {
               )
               : <div style={{ width: '40px' }} />}
           </div>
-          {!hasVal && type !== 'RP' && error ? (
+          {!hasVal && type !== 'R-' && error ? (
             <div className={styles.inputError}>{error}</div>
           ) : (
             <div className={styles.errorSpacer}>&nbsp;</div>
@@ -427,7 +427,7 @@ class Poll extends Component {
             maxLength={QUESTION_MAX_INPUT_CHARS}
             placeholder={intl.formatMessage(intlMessages.questionLabel)}
           />
-          {(type === 'RP' && question.length === 0 && error) ? (
+          {(type === 'R-' && question.length === 0 && error) ? (
             <div className={styles.inputError}>{error}</div>
           ) : (
             <div className={styles.errorSpacer}>&nbsp;</div>
@@ -485,8 +485,8 @@ class Poll extends Component {
           <Button
             label={intl.formatMessage(intlMessages.userResponse)}
             color="default"
-            onClick={() => { this.setState({ type: 'RP' }); }}
-            className={cx(styles.pBtn, styles.fullWidth, { [styles.selectedBtnWhite]: type === 'RP' })}
+            onClick={() => { this.setState({ type: 'R-' }); }}
+            className={cx(styles.pBtn, styles.fullWidth, { [styles.selectedBtnWhite]: type === 'R-' })}
           />
         </div>
         { type
@@ -494,7 +494,7 @@ class Poll extends Component {
             <div data-test="responseChoices">
               <h4>{intl.formatMessage(intlMessages.responseChoices)}</h4>
               {
-                type === 'RP'
+                type === 'R-'
                 && (
                   <div>
                     <span>{intl.formatMessage(intlMessages.typedResponseDesc)}</span>
@@ -508,7 +508,7 @@ class Poll extends Component {
                 )
               }
               {
-                (defaultPoll || type === 'RP')
+                (defaultPoll || type === 'R-')
                 && (
                   <div style={{
                     display: 'flex',
@@ -540,8 +540,8 @@ class Poll extends Component {
                         });
 
                         let err = null;
-                        if (type === 'RP' && question.length === 0) err = intl.formatMessage(intlMessages.questionErr);
-                        if (!hasVal && type !== 'RP') err = intl.formatMessage(intlMessages.optionErr);
+                        if (type === 'R-' && question.length === 0) err = intl.formatMessage(intlMessages.questionErr);
+                        if (!hasVal && type !== 'R-') err = intl.formatMessage(intlMessages.optionErr);
                         if (err) return this.setState({ error: err });
 
                         return this.setState({ isPolling: true }, () => {
@@ -563,7 +563,7 @@ class Poll extends Component {
                       }}
                     />
                     {
-                      FILE_DRAG_AND_DROP_ENABLED && type !== 'RP' && this.renderDragDrop()
+                      FILE_DRAG_AND_DROP_ENABLED && type !== 'R-' && this.renderDragDrop()
                     }
                   </div>
                 )
diff --git a/bigbluebutton-html5/imports/ui/components/polling/component.jsx b/bigbluebutton-html5/imports/ui/components/polling/component.jsx
index 399635a75b..a47c684803 100644
--- a/bigbluebutton-html5/imports/ui/components/polling/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/polling/component.jsx
@@ -129,7 +129,7 @@ class Polling extends Component {
             )
           }
           {
-            poll.pollType !== 'RP' && (
+            poll.pollType !== 'R-' && (
               <span>
                 {
                   question.length === 0
@@ -184,7 +184,7 @@ class Polling extends Component {
             )
           }
           {
-            poll.pollType === 'RP'
+            poll.pollType === 'R-'
             && (
               <div className={styles.typedResponseWrapper}>
                 <input
diff --git a/bigbluebutton-html5/public/locales/pt_BR.json b/bigbluebutton-html5/public/locales/pt_BR.json
index 139b351bb0..61ad9140e5 100644
--- a/bigbluebutton-html5/public/locales/pt_BR.json
+++ b/bigbluebutton-html5/public/locales/pt_BR.json
@@ -766,7 +766,7 @@
     "app.createBreakoutRoom.randomlyAssign": "Atribuir aleatoriamente",
     "app.createBreakoutRoom.endAllBreakouts": "Encerrar todas as salas de apoio",
     "app.createBreakoutRoom.roomName": "{0} (Sala - {1})",
-    "app.createBreakoutRoom.doneLabel": "Finalizado",
+    "app.createBreakoutRoom.doneLabel": "Confirmar",
     "app.createBreakoutRoom.nextLabel": "Próximo",
     "app.createBreakoutRoom.minusRoomTime": "Diminuir o tempo da sala de apoio para",
     "app.createBreakoutRoom.addRoomTime": "Aumentar o tempo da sala de apoio para",
-- 
GitLab