From acd15eb100fbbcacb05f85cc3739c87f7b0183f4 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Sun, 6 Oct 2024 18:54:40 +0100 Subject: [PATCH] feat: Allow to send a Kudos to a Space - MEED-7568 - Meeds-io/MIPs158 This change will allow for a user to send a kudos to a space where it can redact on it. --- .../activity/KudosActivityTypePlugin.java | 3 + .../java/io/meeds/kudos/dao/KudosDAO.java | 10 +-- .../GamificationIntegrationListener.java | 12 +++- .../io/meeds/kudos/service/KudosService.java | 19 +++++- .../io/meeds/kudos/storage/KudosStorage.java | 10 +-- .../KudosNotification_en.properties | 1 + .../meeds/kudos/service/KudosServiceTest.java | 17 ++++- .../src/main/webapp/vue-app/js/Kudos.js | 62 ++++++++++++++----- .../main/webapp/vue-app/js/KudosIdentity.js | 6 +- .../vue-app/kudos/components/KudosApp.vue | 38 +++++++----- .../kudos/components/PopoverKudosButton.vue | 36 ++++++++--- .../kudos/components/SendKudosComposer.vue | 2 - .../components/NewUserKudosNotification.vue | 9 ++- 13 files changed, 165 insertions(+), 60 deletions(-) diff --git a/kudos-services/src/main/java/io/meeds/kudos/activity/KudosActivityTypePlugin.java b/kudos-services/src/main/java/io/meeds/kudos/activity/KudosActivityTypePlugin.java index 95878cccb..c826cdd17 100644 --- a/kudos-services/src/main/java/io/meeds/kudos/activity/KudosActivityTypePlugin.java +++ b/kudos-services/src/main/java/io/meeds/kudos/activity/KudosActivityTypePlugin.java @@ -18,6 +18,8 @@ */ package io.meeds.kudos.activity; +import static io.meeds.kudos.service.utils.Utils.SPACE_ACCOUNT_TYPE; + import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -60,6 +62,7 @@ public boolean isEnableNotification(ExoSocialActivity activity, String username) } else { Kudos kudos = this.kudosService.getKudosByActivityId(Long.parseLong(activity.getId().replace("comment", ""))); return kudos != null + && !SPACE_ACCOUNT_TYPE.equals(kudos.getReceiverType()) && !StringUtils.equals(kudos.getReceiverId(), username) && !StringUtils.equals(kudos.getSenderId(), username); } diff --git a/kudos-services/src/main/java/io/meeds/kudos/dao/KudosDAO.java b/kudos-services/src/main/java/io/meeds/kudos/dao/KudosDAO.java index 38f5eb1c3..4c8ae3118 100644 --- a/kudos-services/src/main/java/io/meeds/kudos/dao/KudosDAO.java +++ b/kudos-services/src/main/java/io/meeds/kudos/dao/KudosDAO.java @@ -40,11 +40,11 @@ List findByCreatedDateBetweenAndEntityTypeOrderByCreatedDateDesc(lo List findByEntityTypeAndEntityIdOrderByCreatedDateDesc(int entityType, long entityId, Limit limit); - List findByCreatedDateBetweenAndReceiverIdAndIsReceiverUserOrderByCreatedDateDesc(long startDateInSeconds, - long endDateInSeconds, - long receiverId, - boolean isReceiverUser, - Limit limit); + List findByCreatedDateBetweenAndReceiverIdAndIsReceiverUserOrderByIdDesc(long startDateInSeconds, + long endDateInSeconds, + long receiverId, + boolean isReceiverUser, + Limit limit); List findByCreatedDateBetweenAndSenderIdOrderByCreatedDateDesc(long startDateInSeconds, long endDateInSeconds, diff --git a/kudos-services/src/main/java/io/meeds/kudos/listener/GamificationIntegrationListener.java b/kudos-services/src/main/java/io/meeds/kudos/listener/GamificationIntegrationListener.java index 5bf07b9c4..65633f78a 100644 --- a/kudos-services/src/main/java/io/meeds/kudos/listener/GamificationIntegrationListener.java +++ b/kudos-services/src/main/java/io/meeds/kudos/listener/GamificationIntegrationListener.java @@ -36,6 +36,7 @@ import org.exoplatform.services.listener.Event; import org.exoplatform.services.listener.Listener; import org.exoplatform.services.listener.ListenerService; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; import io.meeds.kudos.model.Kudos; import io.meeds.kudos.service.KudosService; @@ -70,7 +71,9 @@ public void onEvent(Event event) { private void saveSendKudosAchievement(Kudos kudos, String eventName) { Map gam = buildGamificationEventDetails(GAMIFICATION_SEND_KUDOS_EVENT_NAME, kudos.getSenderId(), + OrganizationIdentityProvider.NAME, kudos.getReceiverId(), + kudos.getReceiverType(), String.valueOf(kudos.getActivityId())); listenerService.broadcast(getGamificationEventName(eventName), gam, String.valueOf(kudos.getTechnicalId())); } @@ -78,7 +81,9 @@ private void saveSendKudosAchievement(Kudos kudos, String eventName) { private void saveRecieveKudosAchievement(Kudos kudos, String eventName) { Map gam = buildGamificationEventDetails(GAMIFICATION_RECEIVE_KUDOS_EVENT_NAME, kudos.getReceiverId(), + kudos.getReceiverType(), kudos.getSenderId(), + OrganizationIdentityProvider.NAME, String.valueOf(kudos.getActivityId())); listenerService.broadcast(getGamificationEventName(eventName), gam, String.valueOf(kudos.getTechnicalId())); } @@ -93,15 +98,18 @@ private String getGamificationEventName(String eventName) { private Map buildGamificationEventDetails(String gamificationEventName, String earnerId, + String earnerType, String receiverId, + String receiverType, String objectId) { Map gam = new HashMap<>(); gam.put("objectType", GAMIFICATION_OBJECT_TYPE); gam.put("objectId", objectId); gam.put("ruleTitle", gamificationEventName); - gam.put("senderId", earnerId); // matches the gamification's - // earner id + gam.put("senderId", earnerId); // matches the gamification's earner id + gam.put("senderType", earnerType); gam.put("receiverId", receiverId); + gam.put("receiverType", receiverType); return gam; } diff --git a/kudos-services/src/main/java/io/meeds/kudos/service/KudosService.java b/kudos-services/src/main/java/io/meeds/kudos/service/KudosService.java index 11aee5889..fe7379b27 100644 --- a/kudos-services/src/main/java/io/meeds/kudos/service/KudosService.java +++ b/kudos-services/src/main/java/io/meeds/kudos/service/KudosService.java @@ -25,6 +25,7 @@ import static io.meeds.kudos.service.utils.Utils.KUDOS_SCOPE; import static io.meeds.kudos.service.utils.Utils.KUDOS_SENT_EVENT; import static io.meeds.kudos.service.utils.Utils.SETTINGS_KEY_NAME; +import static io.meeds.kudos.service.utils.Utils.SPACE_ACCOUNT_TYPE; import static io.meeds.kudos.service.utils.Utils.USER_ACCOUNT_TYPE; import static io.meeds.kudos.service.utils.Utils.getActivityId; import static io.meeds.kudos.service.utils.Utils.getCurrentPeriod; @@ -189,7 +190,14 @@ public Kudos createKudos(Kudos kudos, String currentUser) throws IllegalAccessEx throw new ObjectNotFoundException("Space not found"); } else if (!canSendKudosInSpace(kudos, space, currentUser)) { throw new IllegalAccessException("User cannot redact on space"); + } else if (!isActivityComment(kudos) + && SPACE_ACCOUNT_TYPE.equals(kudos.getReceiverType()) + && !isReceiverSpaceTargetAudience(kudos, space)) { + throw new IllegalAccessException("Target space isn't the space receiving the kudos"); } + } else if (!isActivityComment(kudos) + && SPACE_ACCOUNT_TYPE.equals(kudos.getReceiverType())) { + throw new IllegalAccessException("Target space isn't the space receiving the kudos"); } KudosPeriod currentPeriod = getCurrentKudosPeriod(); @@ -232,8 +240,8 @@ public Kudos createKudos(Kudos kudos, String currentUser) throws IllegalAccessEx * @return true if can redact on space or if is a comment/reply on an existing activity */ public boolean canSendKudosInSpace(Kudos kudos, Space space, String username) { - return (isActivityComment(kudos) && spaceService.canViewSpace(space, username)) - || spaceService.canRedactOnSpace(space, username); + return spaceService.canRedactOnSpace(space, username) + || (isActivityComment(kudos) && spaceService.canViewSpace(space, username)); } /** @@ -571,6 +579,13 @@ public KudosPeriod getKudosPeriodOfTime(KudosPeriodType periodType, long dateInS return periodType.getPeriodOfTime(timeFromSeconds(dateInSeconds)); } + private boolean isReceiverSpaceTargetAudience(Kudos kudos, Space space) { + String receiverId = kudos.getReceiverId(); + Space targetSpaceAudience = getSpace(receiverId); + return targetSpaceAudience != null + && StringUtils.equals(space.getId(), targetSpaceAudience.getId()); + } + private Object checkStatusAndGetReceiver(String type, String id) { if (USER_ACCOUNT_TYPE.equals(type) || OrganizationIdentityProvider.NAME.equals(type)) { Identity identity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, id); diff --git a/kudos-services/src/main/java/io/meeds/kudos/storage/KudosStorage.java b/kudos-services/src/main/java/io/meeds/kudos/storage/KudosStorage.java index 0b390c0af..b9936c985 100644 --- a/kudos-services/src/main/java/io/meeds/kudos/storage/KudosStorage.java +++ b/kudos-services/src/main/java/io/meeds/kudos/storage/KudosStorage.java @@ -163,11 +163,11 @@ public List getKudosByPeriodAndReceiver(KudosPeriod kudosPeriod, String r return Collections.emptyList(); } List kudosEntities = - kudosDAO.findByCreatedDateBetweenAndReceiverIdAndIsReceiverUserOrderByCreatedDateDesc(kudosPeriod.getStartDateInSeconds(), - kudosPeriod.getEndDateInSeconds(), - identityId, - isReceiverUser, - Limit.of(limit)); + kudosDAO.findByCreatedDateBetweenAndReceiverIdAndIsReceiverUserOrderByIdDesc(kudosPeriod.getStartDateInSeconds(), + kudosPeriod.getEndDateInSeconds(), + identityId, + isReceiverUser, + Limit.of(limit)); if (kudosEntities != null) { List kudosList = new ArrayList<>(); for (KudosEntity kudosEntity : kudosEntities) { diff --git a/kudos-services/src/main/resources/locale/notification/KudosNotification_en.properties b/kudos-services/src/main/resources/locale/notification/KudosNotification_en.properties index 65bd12076..4f6ee2660 100644 --- a/kudos-services/src/main/resources/locale/notification/KudosNotification_en.properties +++ b/kudos-services/src/main/resources/locale/notification/KudosNotification_en.properties @@ -8,6 +8,7 @@ Notification.title.KudosReceiverNotificationPlugin=New kudos received Notification.kudos.onActivity=on activity Notification.kudos.received=You have received a kudos from {0} Notification.kudos.spaceReceived={0} has received a Kudos from {1} +Notification.kudos.spaceReceived.web=The space {1} has received a Kudos from {0} Notification.label.SayHello=Hi Notification.label.ViewFullDiscussion=View the full discussion Notification.plugin.NewUserKudosButtonTitle=Say welcome diff --git a/kudos-services/src/test/java/io/meeds/kudos/service/KudosServiceTest.java b/kudos-services/src/test/java/io/meeds/kudos/service/KudosServiceTest.java index 0e21d4d80..4584205a4 100644 --- a/kudos-services/src/test/java/io/meeds/kudos/service/KudosServiceTest.java +++ b/kudos-services/src/test/java/io/meeds/kudos/service/KudosServiceTest.java @@ -383,9 +383,21 @@ public void testSendKudosToSpace() { assertEquals(0, list.size()); SpaceServiceMock.setRedactor(SENDER_REMOTE_ID); + assertThrows(IllegalAccessException.class, () -> kudosService.createKudos(kudosToSend, SENDER_REMOTE_ID)); + + kudosToSend.setSpacePrettyName(spaceRemoteId); + + kudosToSend.setReceiverId("space2"); + assertThrows(IllegalAccessException.class, () -> kudosService.createKudos(kudosToSend, SENDER_REMOTE_ID)); + + kudosToSend.setReceiverId("NotFoundSpace"); + assertThrows(IllegalAccessException.class, () -> kudosService.createKudos(kudosToSend, SENDER_REMOTE_ID)); + + kudosToSend.setReceiverId(spaceRemoteId); Kudos kudos; try { kudos = kudosService.createKudos(kudosToSend, SENDER_REMOTE_ID); + assertNotNull(kudos); } finally { SpaceServiceMock.setRedactor(null); } @@ -401,17 +413,18 @@ public void testSendKudosToSpace() { assertEquals(kudos.getTimeInSeconds(), retrievedKudos.getTimeInSeconds()); assertEquals(kudos.hashCode(), retrievedKudos.hashCode()); + restartTransaction(); Kudos kudosToSendOnActivity = newKudosDTO(); kudosToSendOnActivity.setReceiverType(SpaceIdentityProvider.NAME); kudosToSendOnActivity.setReceiverId(spaceRemoteId); kudosToSendOnActivity.setReceiverIdentityId(null); - kudosToSendOnActivity.setReceiverIdentityId(null); kudosToSendOnActivity.setEntityType(KudosEntityType.ACTIVITY.name()); kudosToSendOnActivity.setEntityId(String.valueOf(retrievedKudos.getActivityId())); SpaceServiceMock.setMember(SENDER_REMOTE_ID); try { - kudosService.createKudos(kudosToSendOnActivity, SENDER_REMOTE_ID); + kudos = kudosService.createKudos(kudosToSendOnActivity, SENDER_REMOTE_ID); + assertNotNull(kudos); } finally { SpaceServiceMock.setMember(null); } diff --git a/kudos-webapps/src/main/webapp/vue-app/js/Kudos.js b/kudos-webapps/src/main/webapp/vue-app/js/Kudos.js index d7b6205a8..f8a46d667 100644 --- a/kudos-webapps/src/main/webapp/vue-app/js/Kudos.js +++ b/kudos-webapps/src/main/webapp/vue-app/js/Kudos.js @@ -187,7 +187,7 @@ export function getPeriodDates(date, periodType) { } export function registerExternalExtensions(title) { - const profileExtensionAction = { + extensionRegistry.registerExtension('profile-extension', 'action', { id: 'profile-kudos', title: title, icon: 'fa fa-award uiIconKudos uiIconLightBlue', @@ -206,8 +206,26 @@ export function registerExternalExtensions(title) { }})); } }, - }; - extensionRegistry.registerExtension('profile-extension', 'action', profileExtensionAction); + }); + extensionRegistry.registerExtension('profile-extension', 'action', { + id: 'space-kudos', + title: title, + icon: 'fa-award', + order: 10, + iconOnly: true, + enabled: space => !space.username && space.canRedactOnSpace, + click: space => { + document.dispatchEvent(new CustomEvent('exo-kudos-open-send-modal', {detail: { + id: space.id, + type: 'SPACE_PROFILE', + parentId: '', + readOnlySpace: true, + owner: eXo.env.portal.userName, + spacePrettyName: space.prettyName, + spaceId: space.id, + }})); + }, + }); } export function registerOverviewExtension() { @@ -280,6 +298,12 @@ export function registerActivityActionExtension() { rank: 30, }); + extensionRegistry.registerComponent('SpacePopover', 'space-popover-action', { + id: 'kudos', + vueComponent: Vue.options.components['popover-kudos-button'], + rank: 30, + }); + // Register predefined activity types extensionRegistry.registerExtension('activity', 'type', { type: 'exokudos:activity', @@ -302,19 +326,29 @@ export function registerActivityActionExtension() { getTitle: activityOrComment => { const kudos = activityOrComment && activityOrComment.kudos; if (kudos) { - const receiverIdentity = { - 'id': kudos.receiverIdentityId, - 'username': kudos.receiverId, - 'fullName': kudos.receiverFullName, - 'avatar': kudos.receiverAvatar, - 'position': kudos.receiverPosition, - 'external': String(kudos.externalReceiver), - 'enabled': String(kudos.enabledReceiver), + const receiverIdentity = kudos.receiverType === 'user' && { + id: kudos.receiverIdentityId, + username: kudos.receiverId, + fullName: kudos.receiverFullName, + avatar: kudos.receiverAvatar, + position: kudos.receiverPosition, + external: String(kudos.externalReceiver), + enabled: String(kudos.enabledReceiver), + identityType: kudos.receiverType, + } || { + id: kudos.receiverIdentityId, + prettyName: kudos.receiverId, + displayName: kudos.receiverFullName, + avatarUrl: kudos.receiverAvatar, + external: String(kudos.externalReceiver), + enabled: String(kudos.enabledReceiver), + identityType: kudos.receiverType, }; + const url = kudos.receiverType === 'user' ? `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}/profile/${kudos.receiverId}` : `${eXo.env.portal.context}/s/${kudos.receiverIdentityId}`; return { key: 'NewKudosSentActivityComment.activity_kudos_title', params: { - 0: `${kudos.receiverFullName}` + 0: `${kudos.receiverFullName}` }, }; } @@ -325,8 +359,8 @@ export function registerActivityActionExtension() { return activityOrComment.body; }, getSummary: activityOrComment => { - const summary = (activityOrComment.templateParams && activityOrComment.templateParams.kudosMessage) - || (activityOrComment.kudos && activityOrComment.kudos.message) + const summary = activityOrComment?.templateParams?.kudosMessage + || activityOrComment?.kudos?.message || ''; return summary.includes('') && summary.split('')[0] || summary; }, diff --git a/kudos-webapps/src/main/webapp/vue-app/js/KudosIdentity.js b/kudos-webapps/src/main/webapp/vue-app/js/KudosIdentity.js index 3b9af510c..e15fb7c46 100644 --- a/kudos-webapps/src/main/webapp/vue-app/js/KudosIdentity.js +++ b/kudos-webapps/src/main/webapp/vue-app/js/KudosIdentity.js @@ -66,7 +66,7 @@ export function getIdentityDetails(urlId, type, remoteId) { ownerDetails.avatar = identityDetails.avatar; ownerDetails.username = identityDetails.username; ownerDetails.position = identityDetails.position; - ownerDetails.external = identityDetails.external; + ownerDetails.external = identityDetails.external === 'true'; ownerDetails.enabled = identityDetails.enabled; } else { ownerDetails.notAuthorized = true; @@ -80,7 +80,9 @@ export function getIdentityDetails(urlId, type, remoteId) { if (identityDetails) { ownerDetails.identityId = identityDetails.id; ownerDetails.fullname = identityDetails.displayName; - ownerDetails.avatar = identityDetails.avatar; + ownerDetails.avatar = identityDetails.avatarUrl; + ownerDetails.external = false; + ownerDetails.enabled = true; } else { ownerDetails.notAuthorized = true; } diff --git a/kudos-webapps/src/main/webapp/vue-app/kudos/components/KudosApp.vue b/kudos-webapps/src/main/webapp/vue-app/kudos/components/KudosApp.vue index 2f2b67ad2..60d31852a 100644 --- a/kudos-webapps/src/main/webapp/vue-app/kudos/components/KudosApp.vue +++ b/kudos-webapps/src/main/webapp/vue-app/kudos/components/KudosApp.vue @@ -66,6 +66,8 @@ :labels="receiverSuggesterLabels" :search-options="searchOptions" :type-of-relations="typeOfRelation" + :include-spaces="!spaceId || postInYourSpacesChoice" + only-redactor include-users name="kudosReceiver" width="220" @@ -130,7 +132,6 @@ - diff --git a/kudos-webapps/src/main/webapp/vue-app/kudos/components/SendKudosComposer.vue b/kudos-webapps/src/main/webapp/vue-app/kudos/components/SendKudosComposer.vue index 21ede28f7..d0619b80d 100644 --- a/kudos-webapps/src/main/webapp/vue-app/kudos/components/SendKudosComposer.vue +++ b/kudos-webapps/src/main/webapp/vue-app/kudos/components/SendKudosComposer.vue @@ -47,8 +47,6 @@ export default { openSendKudosDrawer() { document.dispatchEvent(new CustomEvent('activity-composer-closed')); document.dispatchEvent(new CustomEvent('exo-kudos-open-send-modal', {detail: { - id: eXo.env.portal.userIdentityId, - type: 'USER_PROFILE', parentId: '', owner: eXo.env.portal.userName, spaceURL: eXo.env.portal.spaceUrl, diff --git a/kudos-webapps/src/main/webapp/vue-app/notification-extension/components/NewUserKudosNotification.vue b/kudos-webapps/src/main/webapp/vue-app/notification-extension/components/NewUserKudosNotification.vue index 7d94f5d73..4c2a5a859 100644 --- a/kudos-webapps/src/main/webapp/vue-app/notification-extension/components/NewUserKudosNotification.vue +++ b/kudos-webapps/src/main/webapp/vue-app/notification-extension/components/NewUserKudosNotification.vue @@ -1,8 +1,8 @@