From 6d8ae0bf3e209864dbfe0c6a7cee58cd483ed6af Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:26:38 +0530 Subject: [PATCH 1/5] [FIX][ENTERPRISE] Race condition on Omnichannel visitor abandoned callback (#22413) * Fix Visitor Inactivity monitor not working * Add comment * Fix tslint issue Co-authored-by: Renato Becker --- .../server/hooks/setPredictedVisitorAbandonmentTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js b/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js index 14f20fe55640..bef56c2ff4a5 100644 --- a/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js +++ b/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js @@ -23,4 +23,4 @@ callbacks.add('afterSaveMessage', function(message, room) { setPredictedVisitorAbandonmentTime(room); } return message; -}, callbacks.priority.HIGH, 'save-visitor-inactivity'); +}, callbacks.priority.MEDIUM, 'save-visitor-inactivity'); // This hook priority should always be less than the priority of hook "save-last-visitor-message-timestamp" bcs, the room.v.lastMessage property set there is being used here for determinting visitor abandonment From d0fcad237df223c495ecaf2f528060485aa636a0 Mon Sep 17 00:00:00 2001 From: Lucas Sartor Chauvin Date: Tue, 29 Jun 2021 13:24:18 -0300 Subject: [PATCH 2/5] [NEW] `roles.delete` endpoint (#22497) * create roles.delete endpoint * add tests for new endpoint Co-authored-by: pierre-lehnen-rc <55164754+pierre-lehnen-rc@users.noreply.github.com> --- app/api/server/v1/roles.js | 32 ++++++++++++++ tests/end-to-end/api/13-roles.js | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/app/api/server/v1/roles.js b/app/api/server/v1/roles.js index 5f2b39f624ba..e66e54959873 100644 --- a/app/api/server/v1/roles.js +++ b/app/api/server/v1/roles.js @@ -181,3 +181,35 @@ API.v1.addRoute('roles.update', { authRequired: true }, { }); }, }); + +API.v1.addRoute('roles.delete', { authRequired: true }, { + post() { + check(this.bodyParams, { + roleId: String, + }); + + if (!hasPermission(this.userId, 'access-permissions')) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); + } + + const role = Roles.findOneByIdOrName(this.bodyParams.roleId); + + if (!role) { + throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); + } + + if (role.protected) { + throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role'); + } + + const existingUsers = Roles.findUsersInRole(role._id, role.scope); + + if (existingUsers && existingUsers.count() > 0) { + throw new Meteor.Error('error-role-in-use', 'Cannot delete role because it\'s in use'); + } + + Roles.remove(role._id); + + return API.v1.success(); + }, +}); diff --git a/tests/end-to-end/api/13-roles.js b/tests/end-to-end/api/13-roles.js index a05b5206eea2..207aa83b2294 100644 --- a/tests/end-to-end/api/13-roles.js +++ b/tests/end-to-end/api/13-roles.js @@ -31,6 +31,26 @@ function createRole(name, scope, description) { }); } +function addUserToRole(roleId, username) { + return new Promise((resolve) => { + request.post(api('roles.addUserToRole')) + .set(credentials) + .send({ + roleName: roleId, + username, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('role._id', roleId); + }) + .end((err, req) => { + resolve(req.body.role); + }); + }); +} + describe('[Roles]', function() { this.retries(0); @@ -310,4 +330,59 @@ describe('[Roles]', function() { .end(done); }); }); + + describe('POST [/roles.delete]', () => { + let roleWithUser; + let roleWithoutUser; + before(async () => { + roleWithUser = await createRole(`roleWithUser-${ Date.now() }`, 'Users'); + roleWithoutUser = await createRole(`roleWithoutUser-${ Date.now() }`, 'Users'); + + await addUserToRole(roleWithUser._id, login.user); + }); + + it('should delete a role that it is not being used', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: roleWithoutUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('should NOT delete a role that it is protected', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: 'admin', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.nested.property('error', 'Cannot delete a protected role [error-role-protected]'); + }) + .end(done); + }); + + it('should NOT delete a role that it is being used', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: roleWithUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.nested.property('error', 'Cannot delete role because it\'s in use [error-role-in-use]'); + }) + .end(done); + }); + }); }); From 2a4f8d41887baf577162d5db706020050c334926 Mon Sep 17 00:00:00 2001 From: Gabriel Engel Date: Tue, 29 Jun 2021 14:11:20 -0300 Subject: [PATCH 3/5] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 004facb098ad..5dced245645e 100644 --- a/README.md +++ b/README.md @@ -80,12 +80,12 @@ We're hiring developers, support people, and product managers all the time. Plea ## Get the Latest News -* Twitter - Follow [Rocket.Chat](https://twitter.com/RocketChat) -* Blog - Get the latest updates from the [Rocket.Chat blog](https://rocket.chat/blog/). -* Facebook - Follow [Rocket.Chat](https://www.facebook.com/RocketChatApp/) -* LinkedIn - Follow [Rocket.Chat](https://www.linkedin.com/company/rocket-chat/) -* Youtube - Follow [Rocket.Chat](https://www.youtube.com/channel/UCin9nv7mUjoqrRiwrzS5UVQ) -* Email - Subscribe to our [newsletter](https://rocket.chat/newsletter/) +* [Twitter](https://twitter.com/RocketChat) +* [Blog](https://rocket.chat/blog/). +* [Facebook](https://www.facebook.com/RocketChatApp/) +* [LinkedIn](https://www.linkedin.com/company/rocket-chat/) +* [Youtube](https://www.youtube.com/channel/UCin9nv7mUjoqrRiwrzS5UVQ) +* [Email Newsletter](https://rocket.chat/newsletter/) Any other questions, mail us at [info@rocket.chat](info@rocket.chat). We’d love to meet you! From ebb45c25a3bf0da30fec9c7e651ea7609008a7f6 Mon Sep 17 00:00:00 2001 From: Gabriel Engel Date: Tue, 29 Jun 2021 14:14:18 -0300 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dced245645e..dbbfb98a7c5b 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ We're hiring developers, support people, and product managers all the time. Plea ## Get the Latest News * [Twitter](https://twitter.com/RocketChat) -* [Blog](https://rocket.chat/blog/). +* [Blog](https://rocket.chat/blog/) * [Facebook](https://www.facebook.com/RocketChatApp/) * [LinkedIn](https://www.linkedin.com/company/rocket-chat/) * [Youtube](https://www.youtube.com/channel/UCin9nv7mUjoqrRiwrzS5UVQ) From 1ad0e60e9982b15efa18366b5ed8cfdfbbc222e8 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:34:35 -0300 Subject: [PATCH 5/5] [IMPROVE] Message deletion modal #22508 Co-authored-by: dougfabris --- app/ui/client/lib/chatMessages.js | 52 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/app/ui/client/lib/chatMessages.js b/app/ui/client/lib/chatMessages.js index b6ee2b825f75..90aab4734fa9 100644 --- a/app/ui/client/lib/chatMessages.js +++ b/app/ui/client/lib/chatMessages.js @@ -28,6 +28,8 @@ import { hasAtLeastOnePermission } from '../../../authorization/client'; import { Messages, Rooms, ChatMessage, ChatSubscription } from '../../../models/client'; import { emoji } from '../../../emoji/client'; import { generateTriggerId } from '../../../ui-message/client/ActionManager'; +import { imperativeModal } from '../../../../client/lib/imperativeModal'; +import GenericModal from '../../../../client/components/GenericModal'; const messageBoxState = { @@ -463,24 +465,7 @@ export class ChatMessages { prid: { $exists: true }, }); - modal.open({ - title: t('Are_you_sure'), - text: room ? t('The_message_is_a_discussion_you_will_not_be_able_to_recover') : t('You_will_not_be_able_to_recover'), - type: 'warning', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: t('Yes_delete_it'), - cancelButtonText: t('Cancel'), - html: false, - }, () => { - modal.open({ - title: t('Deleted'), - text: t('Your_entry_has_been_deleted'), - type: 'success', - timer: 1000, - showConfirmButton: false, - }); - + const onConfirm = () => { if (this.editing.id === message._id) { this.clearEditing(); } @@ -489,12 +474,41 @@ export class ChatMessages { this.$input.focus(); done(); - }, () => { + + imperativeModal.open({ + component: GenericModal, + props: { + title: t('Deleted'), + children: t('Your_entry_has_been_deleted'), + variant: 'success', + onConfirm: imperativeModal.close, + onClose: imperativeModal.close, + }, + }); + + setTimeout(imperativeModal.close, 1000); + }; + + const onCloseModal = () => { + imperativeModal.close(); if (this.editing.id === message._id) { this.clearEditing(); } this.$input.focus(); done(); + }; + + imperativeModal.open({ + component: GenericModal, + props: { + title: t('Are_you_sure'), + children: room ? t('The_message_is_a_discussion_you_will_not_be_able_to_recover') : t('You_will_not_be_able_to_recover'), + variant: 'danger', + confirmText: t('Yes_delete_it'), + onConfirm, + onClose: onCloseModal, + onCancel: onCloseModal, + }, }); }