From 3e3165ca6db8941fc6a6952eb91e778568820cca Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 26 May 2021 09:57:22 -0300 Subject: [PATCH 1/2] [FIX][ENTERPRISE] Omnichannel Monitors can't forward chats to departments that they are not supervising (#22142) Co-authored-by: Diego Sampaio Co-authored-by: Renato Becker Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- .../imports/server/rest/departments.js | 10 ++++-- app/livechat/server/api/lib/departments.js | 31 ++++++++++++++----- app/models/server/raw/Users.js | 5 +-- client/components/AutoCompleteDepartment.js | 6 ++-- .../omnichannel/DepartmentAutoComplete.js | 13 +++++--- .../omnichannel/currentChats/FilterByText.js | 7 ++++- .../departments/DepartmentsRoute.js | 14 ++++++--- .../departments/EditDepartmentWithData.js | 7 +++-- .../RealTimeMonitoringPage.js | 1 + .../hooks/applyDepartmentRestrictions.ts | 11 +++++++ .../hooks/checkAgentBeforeTakeInquiry.js | 2 +- .../livechat-enterprise/server/hooks/index.js | 1 + .../server/lib/Department.js | 1 + ee/app/models/server/index.js | 1 - .../server/models/LivechatDepartment.js | 11 ------- ee/app/models/server/models/LivechatRooms.js | 1 - .../models/server/raw/LivechatDepartment.js | 17 ---------- ee/app/models/server/raw/LivechatRooms.js | 1 - .../BusinessHoursMultipleContainer.js | 5 ++- .../additionalForms/DepartmentForwarding.js | 3 +- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + packages/rocketchat-i18n/i18n/pt.i18n.json | 3 +- 22 files changed, 91 insertions(+), 61 deletions(-) create mode 100644 ee/app/livechat-enterprise/server/hooks/applyDepartmentRestrictions.ts delete mode 100644 ee/app/models/server/raw/LivechatDepartment.js diff --git a/app/livechat/imports/server/rest/departments.js b/app/livechat/imports/server/rest/departments.js index cc7408517294..24fb7640742e 100644 --- a/app/livechat/imports/server/rest/departments.js +++ b/app/livechat/imports/server/rest/departments.js @@ -10,12 +10,14 @@ API.v1.addRoute('livechat/department', { authRequired: true }, { get() { const { offset, count } = this.getPaginationItems(); const { sort } = this.parseJsonQuery(); - const { text, enabled } = this.queryParams; + + const { text, enabled, onlyMyDepartments } = this.queryParams; const departments = Promise.await(findDepartments({ userId: this.userId, text, enabled, + onlyMyDepartments: onlyMyDepartments === 'true', pagination: { offset, count, @@ -59,10 +61,13 @@ API.v1.addRoute('livechat/department/:_id', { authRequired: true }, { _id: String, }); + const { onlyMyDepartments } = this.queryParams; + const { department, agents } = Promise.await(findDepartmentById({ userId: this.userId, departmentId: this.urlParams._id, includeAgents: this.queryParams.includeAgents && this.queryParams.includeAgents === 'true', + onlyMyDepartments: onlyMyDepartments === 'true', })); const result = { department }; @@ -137,7 +142,7 @@ API.v1.addRoute('livechat/department/:_id', { authRequired: true }, { API.v1.addRoute('livechat/department.autocomplete', { authRequired: true }, { get() { - const { selector } = this.queryParams; + const { selector, onlyMyDepartments } = this.queryParams; if (!selector) { return API.v1.failure('The \'selector\' param is required'); } @@ -145,6 +150,7 @@ API.v1.addRoute('livechat/department.autocomplete', { authRequired: true }, { return API.v1.success(Promise.await(findDepartmentsToAutocomplete({ uid: this.userId, selector: JSON.parse(selector), + onlyMyDepartments: onlyMyDepartments === 'true', }))); }, }); diff --git a/app/livechat/server/api/lib/departments.js b/app/livechat/server/api/lib/departments.js index 68103b804d72..4af8725abe23 100644 --- a/app/livechat/server/api/lib/departments.js +++ b/app/livechat/server/api/lib/departments.js @@ -1,17 +1,23 @@ import { escapeRegExp } from '../../../../../lib/escapeRegExp'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models/server/raw'; +import { callbacks } from '../../../../callbacks/server'; -export async function findDepartments({ userId, text, enabled, pagination: { offset, count, sort } }) { + +export async function findDepartments({ userId, onlyMyDepartments = false, text, enabled, pagination: { offset, count, sort } }) { if (!await hasPermissionAsync(userId, 'view-livechat-departments') && !await hasPermissionAsync(userId, 'view-l-room')) { throw new Error('error-not-authorized'); } - const query = { + let query = { ...enabled && { enabled: Boolean(enabled) }, ...text && { name: new RegExp(escapeRegExp(text), 'i') }, }; + if (onlyMyDepartments) { + query = callbacks.run('livechat.applyDepartmentRestrictions', query, { userId }); + } + const cursor = LivechatDepartment.find(query, { sort: sort || { name: 1 }, skip: offset, @@ -30,13 +36,20 @@ export async function findDepartments({ userId, text, enabled, pagination: { off }; } -export async function findDepartmentById({ userId, departmentId, includeAgents = true }) { +export async function findDepartmentById({ userId, departmentId, includeAgents = true, onlyMyDepartments = false }) { const canViewLivechatDepartments = await hasPermissionAsync(userId, 'view-livechat-departments'); if (!canViewLivechatDepartments && !await hasPermissionAsync(userId, 'view-l-room')) { throw new Error('error-not-authorized'); } + + let query = { _id: departmentId }; + + if (onlyMyDepartments) { + query = callbacks.run('livechat.applyDepartmentRestrictions', query, { userId }); + } + const result = { - department: await LivechatDepartment.findOneById(departmentId), + department: await LivechatDepartment.findOne(query), }; if (includeAgents && canViewLivechatDepartments) { result.agents = await LivechatDepartmentAgents.find({ departmentId }).toArray(); @@ -45,23 +58,27 @@ export async function findDepartmentById({ userId, departmentId, includeAgents = return result; } -export async function findDepartmentsToAutocomplete({ uid, selector }) { +export async function findDepartmentsToAutocomplete({ uid, selector, onlyMyDepartments = false }) { if (!await hasPermissionAsync(uid, 'view-livechat-departments') && !await hasPermissionAsync(uid, 'view-l-room')) { return { items: [] }; } - const { exceptions = [], conditions = {} } = selector; + const { exceptions = [] } = selector; + let { conditions = {} } = selector; const options = { fields: { _id: 1, name: 1, }, - limit: 10, sort: { name: 1, }, }; + if (onlyMyDepartments) { + conditions = callbacks.run('livechat.applyDepartmentRestrictions', conditions, { userId: uid }); + } + const items = await LivechatDepartment.findByNameRegexWithExceptionsAndConditions(selector.term, exceptions, conditions, options).toArray(); return { items, diff --git a/app/models/server/raw/Users.js b/app/models/server/raw/Users.js index 0fce3cb47bec..b662da903e93 100644 --- a/app/models/server/raw/Users.js +++ b/app/models/server/raw/Users.js @@ -154,6 +154,7 @@ export class UsersRaw extends BaseRaw { $and: [ { $eq: ['$u._id', '$$id'] }, { $eq: ['$open', true] }, + { $ne: ['$onHold', true] }, { ...department && { $eq: ['$department', department] } }, ], }, @@ -196,11 +197,11 @@ export class UsersRaw extends BaseRaw { return result.value; } - async getAgentAndAmountOngoingChats(userId, department) { + async getAgentAndAmountOngoingChats(userId) { const aggregate = [ { $match: { _id: userId, status: { $exists: true, $ne: 'offline' }, statusLivechat: 'available', roles: 'livechat-agent' } }, { $lookup: { from: 'rocketchat_subscription', localField: '_id', foreignField: 'u._id', as: 'subs' } }, - { $project: { agentId: '$_id', username: 1, lastAssignTime: 1, lastRoutingTime: 1, 'queueInfo.chats': { $size: { $filter: { input: '$subs', as: 'sub', cond: { $and: [{ $eq: ['$$sub.t', 'l'] }, { $eq: ['$$sub.open', true] }, { $ne: ['$$sub.onHold', true] }, { ...department && { $eq: ['$$sub.department', department] } }] } } } } } }, + { $project: { agentId: '$_id', username: 1, lastAssignTime: 1, lastRoutingTime: 1, 'queueInfo.chats': { $size: { $filter: { input: '$subs', as: 'sub', cond: { $and: [{ $eq: ['$$sub.t', 'l'] }, { $eq: ['$$sub.open', true] }, { $ne: ['$$sub.onHold', true] }] } } } } } }, { $sort: { 'queueInfo.chats': 1, lastAssignTime: 1, lastRoutingTime: 1, username: 1 } }, ]; diff --git a/client/components/AutoCompleteDepartment.js b/client/components/AutoCompleteDepartment.js index 5b00ba39dd90..ab4bc4429509 100644 --- a/client/components/AutoCompleteDepartment.js +++ b/client/components/AutoCompleteDepartment.js @@ -5,15 +5,15 @@ import { useTranslation } from '../contexts/TranslationContext'; import { useEndpointData } from '../hooks/useEndpointData'; const AutoCompleteDepartment = (props) => { + const { label, onlyMyDepartments = false } = props; + const t = useTranslation(); const [filter, setFilter] = useState(''); const { value: data } = useEndpointData( 'livechat/department', - useMemo(() => ({ text: filter }), [filter]), + useMemo(() => ({ text: filter, onlyMyDepartments }), [filter, onlyMyDepartments]), ); - const { label } = props; - const options = useMemo( () => (data && [ diff --git a/client/views/omnichannel/DepartmentAutoComplete.js b/client/views/omnichannel/DepartmentAutoComplete.js index 2f81350cf037..4c08207c4a08 100644 --- a/client/views/omnichannel/DepartmentAutoComplete.js +++ b/client/views/omnichannel/DepartmentAutoComplete.js @@ -4,17 +4,22 @@ import React, { memo, useMemo, useState } from 'react'; import { useEndpointData } from '../../hooks/useEndpointData'; -const query = (term = '', enabled = false) => ({ - selector: JSON.stringify({ term, ...(enabled && { conditions: { enabled } }) }), +const query = (term = '', enabled = false, onlyMyDepartments = false) => ({ + selector: JSON.stringify({ + term, + ...(enabled && { conditions: { enabled } }), + }), + onlyMyDepartments, }); const DepartmentAutoComplete = (props) => { - const { enabled } = props; + const { enabled, onlyMyDepartments = false } = props; const [filter, setFilter] = useState(''); const { value: data } = useEndpointData( 'livechat/department.autocomplete', - useMemo(() => query(filter, enabled), [enabled, filter]), + useMemo(() => query(filter, enabled, onlyMyDepartments), [onlyMyDepartments, enabled, filter]), ); + const options = useMemo( () => (data && diff --git a/client/views/omnichannel/currentChats/FilterByText.js b/client/views/omnichannel/currentChats/FilterByText.js index c68b073f81f5..95302bd5480e 100644 --- a/client/views/omnichannel/currentChats/FilterByText.js +++ b/client/views/omnichannel/currentChats/FilterByText.js @@ -123,7 +123,12 @@ const FilterByText = ({ setFilter, reload, ...props }) => { - + diff --git a/client/views/omnichannel/departments/DepartmentsRoute.js b/client/views/omnichannel/departments/DepartmentsRoute.js index af839b630d4a..e4c8963c0b2f 100644 --- a/client/views/omnichannel/departments/DepartmentsRoute.js +++ b/client/views/omnichannel/departments/DepartmentsRoute.js @@ -14,7 +14,7 @@ import RemoveDepartmentButton from './RemoveDepartmentButton'; const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); -const useQuery = ({ text, itemsPerPage, current }, [column, direction]) => +const useQuery = ({ text, itemsPerPage, current }, [column, direction], onlyMyDepartments) => useMemo( () => ({ fields: JSON.stringify({ name: 1, username: 1, emails: 1, avatarETag: 1 }), @@ -25,20 +25,26 @@ const useQuery = ({ text, itemsPerPage, current }, [column, direction]) => }), ...(itemsPerPage && { count: itemsPerPage }), ...(current && { offset: current }), + onlyMyDepartments, }), - [text, itemsPerPage, current, column, direction], + [text, itemsPerPage, current, column, direction, onlyMyDepartments], ); function DepartmentsRoute() { const t = useTranslation(); const canViewDepartments = usePermission('manage-livechat-departments'); - const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 }); + const [params, setParams] = useState({ + text: '', + current: 0, + itemsPerPage: 25, + }); const [sort, setSort] = useState(['name', 'asc']); const debouncedParams = useDebouncedValue(params, 500); const debouncedSort = useDebouncedValue(sort, 500); - const query = useQuery(debouncedParams, debouncedSort); + const onlyMyDepartments = true; + const query = useQuery(debouncedParams, debouncedSort, onlyMyDepartments); const departmentsRoute = useRoute('omnichannel-departments'); const context = useRouteParameter('context'); const id = useRouteParameter('id'); diff --git a/client/views/omnichannel/departments/EditDepartmentWithData.js b/client/views/omnichannel/departments/EditDepartmentWithData.js index 3876c72c1041..6c7410997c66 100644 --- a/client/views/omnichannel/departments/EditDepartmentWithData.js +++ b/client/views/omnichannel/departments/EditDepartmentWithData.js @@ -8,16 +8,17 @@ import { AsyncStatePhase } from '../../../hooks/useAsyncState'; import { useEndpointData } from '../../../hooks/useEndpointData'; import EditDepartment from './EditDepartment'; +const param = { onlyMyDepartments: true }; function EditDepartmentWithData({ id, reload, title }) { const t = useTranslation(); - const { value: data, phase: state, error } = useEndpointData(`livechat/department/${id}`); + const { value: data, phase: state, error } = useEndpointData(`livechat/department/${id}`, param); if ([state].includes(AsyncStatePhase.LOADING)) { return ; } - if (error) { - return {t('User_not_found')}; + if (error || (id && !data?.department)) { + return {t('Department_not_found')}; } return ; } diff --git a/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js b/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js index 700727e54a3e..15b6c9f78c59 100644 --- a/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js +++ b/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js @@ -84,6 +84,7 @@ const RealTimeMonitoringPage = () => { placeholder={t('All')} value={department} onChange={setDepartment} + onlyMyDepartments /> diff --git a/ee/app/livechat-enterprise/server/hooks/applyDepartmentRestrictions.ts b/ee/app/livechat-enterprise/server/hooks/applyDepartmentRestrictions.ts new file mode 100644 index 000000000000..2a17e6082e6f --- /dev/null +++ b/ee/app/livechat-enterprise/server/hooks/applyDepartmentRestrictions.ts @@ -0,0 +1,11 @@ +import { callbacks } from '../../../../../app/callbacks/server'; +import { addQueryRestrictionsToDepartmentsModel } from '../lib/query.helper'; +import { hasRole } from '../../../../../app/authorization/server/functions/hasRole'; + +callbacks.add('livechat.applyDepartmentRestrictions', (originalQuery = {}, { userId } = { userId: null }) => { + if (!userId || !hasRole(userId, 'livechat-monitor')) { + return originalQuery; + } + + return addQueryRestrictionsToDepartmentsModel(originalQuery); +}, callbacks.priority.HIGH, 'livechat-apply-department-restrictions'); diff --git a/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js b/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js index b7fe80a22142..5b54dafc623c 100644 --- a/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js +++ b/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js @@ -27,7 +27,7 @@ callbacks.add('livechat.checkAgentBeforeTakeInquiry', async ({ agent, inquiry, o return agent; } - const user = await Users.getAgentAndAmountOngoingChats(agentId, departmentId); + const user = await Users.getAgentAndAmountOngoingChats(agentId); if (!user) { return null; } diff --git a/ee/app/livechat-enterprise/server/hooks/index.js b/ee/app/livechat-enterprise/server/hooks/index.js index 49401508333b..303d3b2e12d9 100644 --- a/ee/app/livechat-enterprise/server/hooks/index.js +++ b/ee/app/livechat-enterprise/server/hooks/index.js @@ -18,3 +18,4 @@ import './onBusinessHourStart'; import './onAgentAssignmentFailed'; import './afterOnHoldChatResumed'; import './afterReturnRoomAsInquiry'; +import './applyDepartmentRestrictions'; diff --git a/ee/app/livechat-enterprise/server/lib/Department.js b/ee/app/livechat-enterprise/server/lib/Department.js index 2661fadf1ca3..654d760a1201 100644 --- a/ee/app/livechat-enterprise/server/lib/Department.js +++ b/ee/app/livechat-enterprise/server/lib/Department.js @@ -8,6 +8,7 @@ export const findAllDepartmentsAvailable = async (unitId, offset, count, text) = const filterReg = new RegExp(escapeRegExp(text), 'i'); const cursor = LivechatDepartment.find({ + type: { $ne: 'u' }, $or: [{ ancestors: { $in: [[unitId], null, []] } }, { ancestors: { $exists: false } }], ...text && { name: filterReg }, diff --git a/ee/app/models/server/index.js b/ee/app/models/server/index.js index 4c68936741e2..ef40dfadc8c0 100644 --- a/ee/app/models/server/index.js +++ b/ee/app/models/server/index.js @@ -11,7 +11,6 @@ import './models/LivechatDepartment'; import './models/LivechatRooms'; import './models/LivechatInquiry'; import './models/Messages'; -import './raw/LivechatDepartment'; import './raw/LivechatRooms'; import './raw/LivechatDepartmentAgents'; diff --git a/ee/app/models/server/models/LivechatDepartment.js b/ee/app/models/server/models/LivechatDepartment.js index 2e4785282d91..f27d04c9c684 100644 --- a/ee/app/models/server/models/LivechatDepartment.js +++ b/ee/app/models/server/models/LivechatDepartment.js @@ -1,21 +1,10 @@ import { LivechatDepartment } from '../../../../../app/models/server/models/LivechatDepartment'; -import { logger } from '../../../livechat-enterprise/server/lib/logger'; -import { addQueryRestrictionsToDepartmentsModel } from '../../../livechat-enterprise/server/lib/query.helper'; import { overwriteClassOnLicense } from '../../../license/server'; const { find, findOne, update, remove } = LivechatDepartment.prototype; -const applyRestrictions = (method) => function(originalFn, originalQuery, ...args) { - const query = addQueryRestrictionsToDepartmentsModel(originalQuery); - logger.queries.debug(() => `LivechatDepartment.${ method } - ${ JSON.stringify(query) }`); - return originalFn.call(this, query, ...args); -}; overwriteClassOnLicense('livechat-enterprise', LivechatDepartment, { - find: applyRestrictions('find'), - findOne: applyRestrictions('findOne'), - update: applyRestrictions('update'), - remove: applyRestrictions('remove'), unfilteredFind(originalFn, ...args) { return find.apply(this, args); }, diff --git a/ee/app/models/server/models/LivechatRooms.js b/ee/app/models/server/models/LivechatRooms.js index 69144374584e..36517ab86747 100644 --- a/ee/app/models/server/models/LivechatRooms.js +++ b/ee/app/models/server/models/LivechatRooms.js @@ -11,7 +11,6 @@ const applyRestrictions = (method) => function(originalFn, originalQuery, ...arg overwriteClassOnLicense('livechat-enterprise', LivechatRooms, { find: applyRestrictions('find'), - findOne: applyRestrictions('findOne'), update: applyRestrictions('update'), remove: applyRestrictions('remove'), updateDepartmentAncestorsById(originalFn, _id, departmentAncestors) { diff --git a/ee/app/models/server/raw/LivechatDepartment.js b/ee/app/models/server/raw/LivechatDepartment.js deleted file mode 100644 index 54c565c40d08..000000000000 --- a/ee/app/models/server/raw/LivechatDepartment.js +++ /dev/null @@ -1,17 +0,0 @@ -import { LivechatDepartmentRaw } from '../../../../../app/models/server/raw/LivechatDepartment'; -import { logger } from '../../../livechat-enterprise/server/lib/logger'; -import { addQueryRestrictionsToDepartmentsModel } from '../../../livechat-enterprise/server/lib/query.helper'; -import { overwriteClassOnLicense } from '../../../license/server'; - -const applyRestrictions = (method) => function(originalFn, originalQuery, ...args) { - const query = addQueryRestrictionsToDepartmentsModel(originalQuery); - logger.queries.debug(() => `LivechatDepartmentRaw.${ method } - ${ JSON.stringify(query) }`); - return originalFn.call(this, query, ...args); -}; - -overwriteClassOnLicense('livechat-enterprise', LivechatDepartmentRaw, { - find: applyRestrictions('find'), - findOne: applyRestrictions('findOne'), - update: applyRestrictions('update'), - remove: applyRestrictions('remove'), -}); diff --git a/ee/app/models/server/raw/LivechatRooms.js b/ee/app/models/server/raw/LivechatRooms.js index 7bb8e1191e9e..afc085309c12 100644 --- a/ee/app/models/server/raw/LivechatRooms.js +++ b/ee/app/models/server/raw/LivechatRooms.js @@ -11,7 +11,6 @@ const applyRestrictions = (method) => function(originalFn, originalQuery, ...arg overwriteClassOnLicense('livechat-enterprise', LivechatRoomsRaw, { find: applyRestrictions('find'), - findOne: applyRestrictions('findOne'), update: applyRestrictions('update'), remove: applyRestrictions('remove'), updateDepartmentAncestorsById(originalFn, _id, departmentAncestors) { diff --git a/ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer.js b/ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer.js index a20bcaabc200..fe42e5fc93bf 100644 --- a/ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer.js +++ b/ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer.js @@ -20,7 +20,10 @@ const BusinessHoursMultipleContainer = ({ className, hasChangesAndIsValid = () => {}, }) => { - const { value: data, phase: state } = useEndpointData('livechat/department'); + const onlyMyDepartments = true; + const params = useMemo(() => ({ onlyMyDepartments }), [onlyMyDepartments]); + + const { value: data, phase: state } = useEndpointData('livechat/department', params); const { values, handlers, hasUnsavedChanges } = useForm(getInitialData(initialData)); diff --git a/ee/client/omnichannel/additionalForms/DepartmentForwarding.js b/ee/client/omnichannel/additionalForms/DepartmentForwarding.js index b2a77b038e83..569d5dea88cb 100644 --- a/ee/client/omnichannel/additionalForms/DepartmentForwarding.js +++ b/ee/client/omnichannel/additionalForms/DepartmentForwarding.js @@ -4,9 +4,10 @@ import React, { useMemo } from 'react'; import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useEndpointData } from '../../../../client/hooks/useEndpointData'; +const param = { onlyMyDepartments: true }; export const DepartmentForwarding = ({ departmentId, value, handler, label, placeholder }) => { const t = useTranslation(); - const { value: data } = useEndpointData('livechat/department'); + const { value: data } = useEndpointData('livechat/department', param); const options = useMemo( () => diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 8fbe96269981..ad246123a121 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1333,6 +1333,7 @@ "Deleted": "Deleted!", "Department": "Department", "Department_name": "Department name", + "Department_not_found": "Department not found", "Department_removed": "Department removed", "Departments": "Departments", "Deployment_ID": "Deployment ID", diff --git a/packages/rocketchat-i18n/i18n/pt.i18n.json b/packages/rocketchat-i18n/i18n/pt.i18n.json index 25ba116bb617..7fd03d3f5336 100644 --- a/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -1047,6 +1047,7 @@ "delete-user_description": "PermissΓ£o para apagar utilizadores", "Deleted": "Apagado!", "Department": "Departamento", + "Department_not_found": "Departmento nΓ£o encontrado", "Department_removed": "Departamento removido", "Departments": "Departamentos", "Deployment_ID": "ID de Desenvolvimento", @@ -3261,4 +3262,4 @@ "Your_question": "A sua pergunta", "Your_server_link": "O link do seu servidor", "Your_workspace_is_ready": "O seu espaΓ§o de trabalho estΓ‘ pronto a usar πŸŽ‰" -} \ No newline at end of file +} From 881e040f863f6536bd31c7ded85c3949ce46a93d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 26 May 2021 10:02:39 -0300 Subject: [PATCH 2/2] Bump version to 3.14.3 --- .docker/Dockerfile.rhel | 2 +- .github/history.json | 43 ++++++++++++++++++++++++++ .snapcraft/resources/prepareRocketChat | 2 +- .snapcraft/snap/snapcraft.yaml | 2 +- HISTORY.md | 19 ++++++++++++ app/utils/rocketchat.info | 2 +- package-lock.json | 2 +- package.json | 2 +- 8 files changed, 68 insertions(+), 6 deletions(-) diff --git a/.docker/Dockerfile.rhel b/.docker/Dockerfile.rhel index 484d85b9d88d..9702a0aea98a 100644 --- a/.docker/Dockerfile.rhel +++ b/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 3.14.2 +ENV RC_VERSION 3.14.3 MAINTAINER buildmaster@rocket.chat diff --git a/.github/history.json b/.github/history.json index 4c2fc56abeed..eef60014bdbc 100644 --- a/.github/history.json +++ b/.github/history.json @@ -59632,6 +59632,49 @@ ] } ] + }, + "3.12.6": { + "node_version": "12.18.4", + "npm_version": "6.14.8", + "apps_engine_version": "1.23.0", + "mongo_versions": [ + "3.4", + "3.6", + "4.0" + ], + "pull_requests": [] + }, + "3.13.4": { + "node_version": "12.21.0", + "npm_version": "6.14.8", + "apps_engine_version": "1.24.1", + "mongo_versions": [ + "3.4", + "3.6", + "4.0" + ], + "pull_requests": [] + }, + "3.14.3": { + "node_version": "12.22.1", + "npm_version": "6.14.1", + "apps_engine_version": "1.25.0", + "mongo_versions": [ + "3.4", + "3.6", + "4.0" + ], + "pull_requests": [ + { + "pr": "22142", + "title": "[FIX][ENTERPRISE] Omnichannel Monitors can't forward chats to departments that they are not supervising", + "userLogin": "sampaiodiego", + "contributors": [ + "renatobecker", + "murtaza98" + ] + } + ] } } } \ No newline at end of file diff --git a/.snapcraft/resources/prepareRocketChat b/.snapcraft/resources/prepareRocketChat index e32f4883fbcd..e5ead5d3c059 100755 --- a/.snapcraft/resources/prepareRocketChat +++ b/.snapcraft/resources/prepareRocketChat @@ -1,6 +1,6 @@ #!/bin/bash -curl -SLf "https://releases.rocket.chat/3.14.2/download/" -o rocket.chat.tgz +curl -SLf "https://releases.rocket.chat/3.14.3/download/" -o rocket.chat.tgz tar xf rocket.chat.tgz --strip 1 diff --git a/.snapcraft/snap/snapcraft.yaml b/.snapcraft/snap/snapcraft.yaml index 3aa4a655b048..fb11a341d6b0 100644 --- a/.snapcraft/snap/snapcraft.yaml +++ b/.snapcraft/snap/snapcraft.yaml @@ -7,7 +7,7 @@ # 5. `snapcraft snap` name: rocketchat-server -version: 3.14.2 +version: 3.14.3 summary: Rocket.Chat server description: Have your own Slack like online chat, built with Meteor. https://rocket.chat/ confinement: strict diff --git a/HISTORY.md b/HISTORY.md index e1f2a7b0a479..5296295f92eb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,23 @@ +# 3.14.3 +`2021-05-26 Β· 1 πŸ› Β· 2 πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»` + +### Engine versions +- Node: `12.22.1` +- NPM: `6.14.1` +- MongoDB: `3.4, 3.6, 4.0` +- Apps-Engine: `1.25.0` + +### πŸ› Bug fixes + + +- **ENTERPRISE:** Omnichannel Monitors can't forward chats to departments that they are not supervising ([#22142](https://github.com/RocketChat/Rocket.Chat/pull/22142)) + +### πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» Core Team πŸ€“ + +- [@murtaza98](https://github.com/murtaza98) +- [@renatobecker](https://github.com/renatobecker) + # 3.14.2 `2021-05-25 Β· 1 πŸ› Β· 1 πŸ” Β· 4 πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»` diff --git a/app/utils/rocketchat.info b/app/utils/rocketchat.info index a5f30bb6236c..72c08cc7ca30 100644 --- a/app/utils/rocketchat.info +++ b/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "3.14.2" + "version": "3.14.3" } diff --git a/package-lock.json b/package-lock.json index e757b01ff3ac..e480c820c88f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "Rocket.Chat", - "version": "3.14.2", + "version": "3.14.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 44d8b367fc84..aa98a9cb90b6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Rocket.Chat", "description": "The Ultimate Open Source WebChat Platform", - "version": "3.14.2", + "version": "3.14.3", "author": { "name": "Rocket.Chat", "url": "https://rocket.chat/"