From e2adf24546a72e0fc604901f28385ba4e4fdb180 Mon Sep 17 00:00:00 2001 From: Pedro Beschorner Marin Date: Mon, 14 Sep 2020 20:13:47 -0300 Subject: [PATCH] Support for avatar images Use the former Flash client avatarURL join param to replace the name initials avatar from the user list, chat, waiting guests and connection status list. It is possible to define a defaultAvatarURL at bbb-web and enable/disable it --- .../apps/users/RegisterUserReqMsgHdlr.scala | 2 +- .../core/models/GuestsWaiting.scala | 2 +- .../core2/message/senders/MsgBuilder.scala | 4 +-- .../core2/testdata/FakeTestData.scala | 4 +-- .../common2/msgs/GuestsMsgs.scala | 2 +- .../org/bigbluebutton/api/MeetingService.java | 2 +- .../api/ParamsProcessorUtil.java | 9 +++++- .../message-list-item/component.jsx | 1 + .../imports/ui/components/chat/service.js | 8 ++++- .../connection-status/modal/component.jsx | 3 +- .../connection-status/modal/styles.scss | 2 +- .../components/connection-status/service.js | 3 ++ .../ui/components/user-avatar/component.jsx | 29 +++++++++++++++---- .../ui/components/user-avatar/styles.scss | 22 +++++++++++++- .../chat-list-item/chat-avatar/component.jsx | 5 +++- .../user-list/chat-list-item/component.jsx | 1 + .../ui/components/user-list/service.js | 1 + .../user-dropdown/component.jsx | 2 ++ .../ui/components/waiting-users/component.jsx | 4 ++- .../grails-app/conf/bigbluebutton.properties | 5 ++-- .../grails-app/conf/spring/resources.xml | 1 + 21 files changed, 89 insertions(+), 23 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala index 51b77f4d3f..50d27ad645 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala @@ -56,7 +56,7 @@ trait RegisterUserReqMsgHdlr { val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW) UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID) case GuestStatus.WAIT => - val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.authed) + val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.authed) addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId) case GuestStatus.DENY => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala index e3ad9be586..9454db88c3 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala @@ -51,7 +51,7 @@ class GuestsWaiting { def setGuestPolicy(policy: GuestPolicy) = guestPolicy = policy } -case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, authenticated: Boolean) +case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, authenticated: Boolean) case class GuestPolicy(policy: String, setBy: String) object GuestPolicyType { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index f0e671311b..e02174ffae 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -44,7 +44,7 @@ object MsgBuilder { val envelope = BbbCoreEnvelope(GetGuestsWaitingApprovalRespMsg.NAME, routing) val header = BbbClientMsgHeader(GetGuestsWaitingApprovalRespMsg.NAME, meetingId, userId) - val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.authenticated)) + val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.authenticated)) val body = GetGuestsWaitingApprovalRespMsgBody(guestsWaiting) val event = GetGuestsWaitingApprovalRespMsg(header, body) @@ -56,7 +56,7 @@ object MsgBuilder { val envelope = BbbCoreEnvelope(GuestsWaitingForApprovalEvtMsg.NAME, routing) val header = BbbClientMsgHeader(GuestsWaitingForApprovalEvtMsg.NAME, meetingId, userId) - val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.authenticated)) + val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.authenticated)) val body = GuestsWaitingForApprovalEvtMsgBody(guestsWaiting) val event = GuestsWaitingForApprovalEvtMsg(header, body) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala index e0966b6bb9..7bac20ee8b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala @@ -20,13 +20,13 @@ trait FakeTestData { val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, talking = false, listenOnly = false) Users2x.add(liveMeeting.users2x, guest1) - val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, guest1.authed) + val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", guest1.authed) GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1) val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false, talking = false, listenOnly = false) Users2x.add(liveMeeting.users2x, guest2) - val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, guest2.authed) + val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", guest2.authed) GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2) val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala index f89f75b0a7..e51e3d0d14 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala @@ -19,7 +19,7 @@ case class GetGuestsWaitingApprovalRespMsg( body: GetGuestsWaitingApprovalRespMsgBody ) extends BbbCoreMsg case class GetGuestsWaitingApprovalRespMsgBody(guests: Vector[GuestWaitingVO]) -case class GuestWaitingVO(intId: String, name: String, role: String, guest: Boolean, authenticated: Boolean) +case class GuestWaitingVO(intId: String, name: String, role: String, guest: Boolean, avatar: String, authenticated: Boolean) /** * Message sent to client for list of guest waiting for approval. This is sent when diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java index 27aed4a1e1..f189fe7dda 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java @@ -911,7 +911,7 @@ public class MeetingService implements MessageListener { } else { if (message.userId.startsWith("v_")) { // A dial-in user joined the meeting. Dial-in users by convention has userId that starts with "v_". - User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "no-avatar-url", + User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "", true, GuestPolicy.ALLOW, "DIAL-IN"); vuser.setVoiceJoined(true); m.userJoined(vuser); diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java index 7b8d26d6a2..f82ced11b5 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java @@ -78,6 +78,7 @@ public class ParamsProcessorUtil { private Boolean moderatorsJoinViaHTML5Client; private Boolean attendeesJoinViaHTML5Client; private Boolean allowRequestsWithoutSession; + private Boolean useDefaultAvatar = false; private String defaultAvatarURL; private String defaultConfigURL; private String defaultGuestPolicy; @@ -454,6 +455,8 @@ public class ParamsProcessorUtil { externalMeetingId = externalHash + "-" + timeStamp; } + String avatarURL = useDefaultAvatar ? defaultAvatarURL : ""; + // Create the meeting with all passed in parameters. Meeting meeting = new Meeting.Builder(externalMeetingId, internalMeetingId, createTime).withName(meetingName) @@ -464,7 +467,7 @@ public class ParamsProcessorUtil { .withBannerText(bannerText).withBannerColor(bannerColor) .withTelVoice(telVoice).withWebVoice(webVoice) .withDialNumber(dialNumber) - .withDefaultAvatarURL(defaultAvatarURL) + .withDefaultAvatarURL(avatarURL) .withAutoStartRecording(autoStartRec) .withAllowStartStopRecording(allowStartStoptRec) .withWebcamsOnlyForModerator(webcamsOnlyForMod) @@ -937,6 +940,10 @@ public class ParamsProcessorUtil { this.webcamsOnlyForModerator = webcamsOnlyForModerator; } + public void setUseDefaultAvatar(Boolean value) { + this.useDefaultAvatar = value; + } + public void setdefaultAvatarURL(String url) { this.defaultAvatarURL = url; } diff --git a/bigbluebutton-html5/imports/ui/components/chat/message-list/message-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/message-list/message-list-item/component.jsx index cade8f4c4b..0c0df937f6 100644 --- a/bigbluebutton-html5/imports/ui/components/chat/message-list/message-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/message-list/message-list-item/component.jsx @@ -126,6 +126,7 @@ class MessageListItem extends Component { className={styles.avatar} color={user.color} moderator={user.isModerator} + avatar={user.avatar} > {user.name.toLowerCase().slice(0, 2)} diff --git a/bigbluebutton-html5/imports/ui/components/chat/service.js b/bigbluebutton-html5/imports/ui/components/chat/service.js index ce745e1966..a2a8fec5d7 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/service.js +++ b/bigbluebutton-html5/imports/ui/components/chat/service.js @@ -50,13 +50,18 @@ const mapGroupMessage = (message) => { const sender = Users.findOne({ userId: message.sender }, { fields: { - color: 1, role: 1, name: 1, connectionStatus: 1, + color: 1, + role: 1, + name: 1, + avatar: 1, + connectionStatus: 1, }, }); const { color, role, name, + avatar, connectionStatus, } = sender; @@ -64,6 +69,7 @@ const mapGroupMessage = (message) => { color, isModerator: role === ROLE_MODERATOR, name, + avatar, isOnline: connectionStatus === CONNECTION_STATUS_ONLINE, }; diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx index a1c93cd31a..0430f03ae8 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx @@ -84,8 +84,9 @@ class ConnectionStatusComponent extends PureComponent {
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.scss b/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.scss index fafcbec335..f273cdf85b 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.scss @@ -82,7 +82,7 @@ justify-content: center; align-items: center; - .icon { + .initials { min-width: 2.25rem; height: 2.25rem; } diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/service.js b/bigbluebutton-html5/imports/ui/components/connection-status/service.js index fd6445e05c..feb7b6b578 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/service.js +++ b/bigbluebutton-html5/imports/ui/components/connection-status/service.js @@ -87,6 +87,7 @@ const getConnectionStatus = () => { userId: 1, name: 1, role: 1, + avatar: 1, color: 1, connectionStatus: 1, }, @@ -96,6 +97,7 @@ const getConnectionStatus = () => { userId, name, role, + avatar, color, connectionStatus: userStatus, } = user; @@ -105,6 +107,7 @@ const getConnectionStatus = () => { if (status) { result.push({ name, + avatar, offline: userStatus === 'offline', you: Auth.userID === userId, moderator: role === ROLE_MODERATOR, diff --git a/bigbluebutton-html5/imports/ui/components/user-avatar/component.jsx b/bigbluebutton-html5/imports/ui/components/user-avatar/component.jsx index a9337f82cc..2cd8c75eea 100755 --- a/bigbluebutton-html5/imports/ui/components/user-avatar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-avatar/component.jsx @@ -14,6 +14,8 @@ const propTypes = { voice: PropTypes.bool, noVoice: PropTypes.bool, color: PropTypes.string, + emoji: PropTypes.bool, + avatar: PropTypes.string, className: PropTypes.string, }; @@ -26,6 +28,8 @@ const defaultProps = { voice: false, noVoice: false, color: '#000', + emoji: false, + avatar: '', className: null, }; @@ -38,6 +42,8 @@ const UserAvatar = ({ listenOnly, color, voice, + emoji, + avatar, noVoice, className, }) => ( @@ -60,14 +66,27 @@ const UserAvatar = ({ >
- -
- {children} -
+ {avatar.length !== 0 && !emoji + ? ( +
+ +
+ ) : ( +
+ {children} +
+ ) + }
); diff --git a/bigbluebutton-html5/imports/ui/components/user-avatar/styles.scss b/bigbluebutton-html5/imports/ui/components/user-avatar/styles.scss index 4af9acd0a5..99daf99ada 100755 --- a/bigbluebutton-html5/imports/ui/components/user-avatar/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/user-avatar/styles.scss @@ -13,7 +13,8 @@ .avatar { position: relative; - padding-bottom: 2rem; + height: 2.25rem; + min-width: 2.25rem; border-radius: 50%; text-align: center; font-size: .85rem; @@ -166,6 +167,25 @@ @include indicatorStyles(); } +.image { + display: flex; + height: 2rem; + width: 2rem; + + .img { + object-fit: cover; + overflow: hidden; + } + + .circle { + border-radius: 50%; + } + + .square { + border-radius: 3px; + } +} + .content { color: var(--user-avatar-text); top: 50%; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/chat-avatar/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/chat-avatar/component.jsx index f12f6104af..4d2e85177b 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/chat-avatar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/chat-avatar/component.jsx @@ -12,11 +12,14 @@ const defaultProps = { }; const ChatAvatar = (props) => { - const { color, name, isModerator } = props; + const { + color, name, avatar, isModerator, + } = props; return ( {name.toLowerCase().slice(0, 2)} diff --git a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx index 0f993dd5bf..57f25f6ed8 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx @@ -96,6 +96,7 @@ const ChatListItem = (props) => { )} diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index 4ca35b290a..8ed9a4b177 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -256,6 +256,7 @@ const getActiveChats = (chatID) => { const activeChat = op; activeChat.unreadCounter = UnreadMessages.count(op.userId); activeChat.name = op.name; + activeChat.avatar = op.avatar; activeChat.isModerator = op.role === ROLE_MODERATOR; activeChat.lastActivity = idsWithTimeStamp[`${op.userId}`]; return activeChat; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx index f4226e1377..a6ba7c34e5 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx @@ -538,6 +538,8 @@ class UserDropdown extends PureComponent { voice={voiceUser.isVoiceUser} noVoice={!voiceUser.isVoiceUser} color={user.color} + emoji={user.emoji !== 'none'} + avatar={user.avatar} > { userInBreakout diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx index 131f2bd848..658ec84000 100755 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx @@ -66,13 +66,14 @@ const getNameInitials = (name) => { return nameInitials.replace(/^\w/, c => c.toUpperCase()); } -const renderGuestUserItem = (name, color, handleAccept, handleDeny, role, sequence, userId, intl) => ( +const renderGuestUserItem = (name, color, handleAccept, handleDeny, role, sequence, userId, avatar, intl) => (
{getNameInitials(name)} @@ -123,6 +124,7 @@ const renderPendingUsers = (message, usersArray, action, intl) => { user.role, idx + 1, user.intId, + user.avatar, intl, ))}
diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties index 376d32b5a8..dbabc221be 100755 --- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties +++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties @@ -254,9 +254,8 @@ html5ClientUrl=${bigbluebutton.web.serverURL}/html5client/join # The url for where the guest will poll if approved to join or not. defaultGuestWaitURL=${bigbluebutton.web.serverURL}/client/guest-wait.html -# The default avatar image to display if nothing is passed on the JOIN API (avatarURL) -# call. This avatar is displayed if the user isn't sharing the webcam and -# the option (displayAvatar) is enabled in config.xml +# The default avatar image to display. +useDefaultAvatar=false defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png # The URL of the default configuration diff --git a/bigbluebutton-web/grails-app/conf/spring/resources.xml b/bigbluebutton-web/grails-app/conf/spring/resources.xml index 4fe99c78bc..fe7140607b 100755 --- a/bigbluebutton-web/grails-app/conf/spring/resources.xml +++ b/bigbluebutton-web/grails-app/conf/spring/resources.xml @@ -133,6 +133,7 @@ with BigBlueButton; if not, see . +