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 51b77f4d3f07..50d27ad645e7 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 e3ad9be5869b..9454db88c319 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 f0e671311bcd..e02174ffaea5 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 e0966b6bb968..7bac20ee8bcd 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 f89f75b0a771..e51e3d0d14f7 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 27aed4a1e164..f189fe7dda05 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 void userJoinedVoice(UserJoinedVoice message) { } 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 7b8d26d6a2da..f82ced11b57e 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 @@ boolean record = processRecordMeeting(params.get(ApiParams.RECORD)); 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 @@ boolean record = processRecordMeeting(params.get(ApiParams.RECORD)); .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 void setWebcamsOnlyForModerator(boolean webcamsOnlyForModerator) { 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 cade8f4c4b28..0c0df937f6da 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 ce745e1966ab..a2a8fec5d725 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 a1c93cd31aa0..0430f03ae82d 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 fafcbec3357c..f273cdf85b94 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 fd6445e05c5e..feb7b6b578f8 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 a9337f82cc3a..2cd8c75eea1c 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 4af9acd0a52e..99daf99adaab 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 f12f6104afa9..4d2e85177b9f 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 0f993dd5bfe4..57f25f6ed8a3 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 4ca35b290a7a..8ed9a4b17738 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 f4226e13772b..a6ba7c34e5be 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 131f2bd84833..658ec84000e5 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 376d32b5a83b..dbabc221be06 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 4fe99c78bc8d..fe7140607b97 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 . +