diff --git a/.github/actions/build-docker-image-service/action.yml b/.github/actions/build-docker-image-service/action.yml new file mode 100644 index 000000000000..aba20175b940 --- /dev/null +++ b/.github/actions/build-docker-image-service/action.yml @@ -0,0 +1,69 @@ +name: 'Build Micro Services Docker image' +description: 'Build Rocket.Chat Micro Services Docker images' + +inputs: + docker-tag: + required: true + service: + required: true + username: + required: false + password: + required: false + +outputs: + image-name: + value: ${{ steps.build-image.outputs.image-name }} + +runs: + using: "composite" + steps: + # - shell: bash + # name: Free disk space + # run: | + # sudo swapoff -a + # sudo rm -f /swapfile + # sudo apt clean + # docker rmi $(docker image ls -aq) + # df -h + + - shell: bash + id: build-image + run: | + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") + + IMAGE_TAG="${{ inputs.docker-tag }}" + + IMAGE_NAME="ghcr.io/${LOWERCASE_REPOSITORY}/${{ inputs.service }}-service:${IMAGE_TAG}" + + echo "Building Docker image for service: ${{ inputs.service }}:${IMAGE_TAG}" + + if [[ "${{ inputs.service }}" == "ddp-streamer" ]]; then + DOCKERFILE_PATH="./ee/apps/ddp-streamer/Dockerfile" + else + DOCKERFILE_PATH="./apps/meteor/ee/server/services/Dockerfile" + fi + + docker build \ + --build-arg SERVICE=${{ inputs.service }} \ + -t ${IMAGE_NAME} \ + -f ${DOCKERFILE_PATH} \ + . + + echo "::set-output name=image-name::${IMAGE_NAME}" + + - name: Login to GitHub Container Registry + if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ inputs.username }} + password: ${{ inputs.password }} + + - name: Publish image + shell: bash + if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' + run: | + echo "Push Docker image: ${{ steps.build-image.outputs.image-name }}" + + docker push ${{ steps.build-image.outputs.image-name }} diff --git a/.github/actions/build-docker-image/action.yml b/.github/actions/build-docker-image/action.yml new file mode 100644 index 000000000000..240c502efd1c --- /dev/null +++ b/.github/actions/build-docker-image/action.yml @@ -0,0 +1,88 @@ +name: 'Build Docker image' +description: 'Build Rocket.Chat Docker image' + +inputs: + root-dir: + required: true + docker-tag: + required: true + release: + required: true + username: + required: false + password: + required: false + +outputs: + image-name: + value: ${{ steps.build-image.outputs.image-name }} + +runs: + using: composite + steps: + # - shell: bash + # name: Free disk space + # run: | + # sudo swapoff -a + # sudo rm -f /swapfile + # sudo apt clean + # docker rmi $(docker image ls -aq) + # df -h + + - shell: bash + id: build-image + run: | + cd ${{ inputs.root-dir }} + + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") + + IMAGE_NAME_BASE="ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${{ inputs.docker-tag }}" + + IMAGE_NAME="${IMAGE_NAME_BASE}.${{ inputs.release }}" + + echo "Build Docker image ${IMAGE_NAME}" + + DOCKER_PATH="${GITHUB_WORKSPACE}/apps/meteor/.docker" + if [[ '${{ inputs.release }}' = 'preview' ]]; then + DOCKER_PATH="${DOCKER_PATH}-mongo" + fi; + + DOCKERFILE_PATH="${DOCKER_PATH}/Dockerfile" + if [[ '${{ inputs.release }}' = 'alpine' ]]; then + DOCKERFILE_PATH="${DOCKERFILE_PATH}.${{ inputs.release }}" + fi; + + echo "Copy Dockerfile for release: ${{ inputs.release }}" + cp $DOCKERFILE_PATH ./Dockerfile + if [ -e ${DOCKER_PATH}/entrypoint.sh ]; then + cp ${DOCKER_PATH}/entrypoint.sh . + fi; + + echo "Build ${{ inputs.release }} Docker image" + docker build -t $IMAGE_NAME . + + echo "::set-output name=image-name-base::${IMAGE_NAME_BASE}" + echo "::set-output name=image-name::${IMAGE_NAME}" + + - name: Login to GitHub Container Registry + if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ inputs.username }} + password: ${{ inputs.password }} + + - name: Publish image + shell: bash + if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' + run: | + echo "Push Docker image: ${{ steps.build-image.outputs.image-name }}" + + docker push ${{ steps.build-image.outputs.image-name }} + + if [[ '${{ inputs.release }}' = 'official' ]]; then + echo "Push release official without variant" + + docker tag ${{ steps.build-image.outputs.image-name }} ${{ steps.build-image.outputs.image-name-base }} + docker push ${{ steps.build-image.outputs.image-name-base }} + fi; diff --git a/.github/history-manual.json b/.github/history-manual.json index b89cc95d0f63..a951279b0465 100644 --- a/.github/history-manual.json +++ b/.github/history-manual.json @@ -189,5 +189,12 @@ "contributors": [ "ggazzo" ] + }], + "4.8.2": [{ + "title": "[FIX] Security Hotfix (https://docs.rocket.chat/guides/security/security-updates)", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] }] } diff --git a/.github/history.json b/.github/history.json index 4999aea0a0f9..a711382653c8 100644 --- a/.github/history.json +++ b/.github/history.json @@ -76747,6 +76747,120 @@ "4.0", "4.2" ], + "pull_requests": [] + }, + "4.8.0-rc.4": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "25689", + "title": "Regression: App event listeners broke Slackbridge integration and importers", + "userLogin": "d-gubert", + "description": "Some event listeners triggered by Apps were calling `Meteor.user()` in functions that could run outside of Meteor environment", + "milestone": "4.8.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25686", + "title": "[FIX] Fix max-width message block", + "userLogin": "ggazzo", + "milestone": "4.8.0", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25673", + "title": "[FIX] Change form body parameter charset to UTF-8 to fix issue #25456", + "userLogin": "divinespear", + "description": "since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0.\r\n\r\n![Screenshot from 2022-05-28 16-26-06](https://user-images.githubusercontent.com/126630/170815447-1f3bd579-243a-42d3-86f6-814aeaa30ce9.png)", + "milestone": "4.8.0", + "contributors": [ + "divinespear" + ] + }, + { + "pr": "25687", + "title": "Regression: Fix sort field files.list", + "userLogin": "ggazzo", + "milestone": "4.8.0", + "contributors": [ + "ggazzo", + "albuquerquefabio", + "web-flow" + ] + }, + { + "pr": "25684", + "title": "[IMPROVE] add warnings for federation setup", + "userLogin": "carlosrodrigues94", + "contributors": [ + "carlosrodrigues94" + ] + }, + { + "pr": "25683", + "title": "[FIX] Prevent federation crash on invite users as a non-owner user", + "userLogin": "MarcosSpessatto", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "25653", + "title": "Regression: Broken components on Federation and Engagement dashboards", + "userLogin": "tassoevan", + "description": "For reasons I've no clue, any client import path matching `**/data/**` will not be included in the final bundle, failing silently on transpiling/bundling.", + "milestone": "4.8.0", + "contributors": [ + "tassoevan", + "gabriellsh" + ] + } + ] + }, + "4.8.0-rc.5": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "25700", + "title": "Chore: Update Apps-Engine and Fuselage", + "userLogin": "d-gubert", + "milestone": "4.8.0", + "contributors": [ + "d-gubert" + ] + } + ] + }, + "4.8.0": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], "pull_requests": [ { "pr": "25580", @@ -76883,93 +76997,282 @@ "yash-rajpal", "MartinSchoeler" ] - }, + } + ] + }, + "3.18.6": { + "mongo_versions": [ + "3.4", + "3.6", + "4.0", + "4.2" + ], + "pull_requests": [] + }, + "4.1.6": { + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "25380", - "title": "Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window", - "userLogin": "filipemarins", - "description": "Fix: livechat room not opening.", - "milestone": "4.7.0", + "pr": "24553", + "title": "[FIX] Omnichannel managers can't join chats in progress", + "userLogin": "renatobecker", + "milestone": "4.5.0", "contributors": [ - "filipemarins" + "renatobecker", + "murtaza98", + "web-flow" ] }, { - "pr": "25314", - "title": "Regression: Fix size of custom emoji and render emoji on thread message preview", - "userLogin": "filipemarins", + "pr": "24592", + "title": "Regression: Fix in-correct room status shown to agents", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "filipemarins", - "gabriellsh" + "murtaza98" ] - }, + } + ] + }, + "4.4.4": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "25371", - "title": "Chore: Bump fuselage", - "userLogin": "gabriellsh", + "pr": "25580", + "title": "Release 4.7.2", + "userLogin": "d-gubert", "contributors": [ - "gabriellsh" + "tiagoevanp", + "d-gubert", + "MartinSchoeler", + "ggazzo", + "cauefcr", + "geekgonecrazy" ] }, { - "pr": "25336", - "title": "Chore: Add options to debug stdout and rate limiter", - "userLogin": "sampaiodiego", + "pr": "25544", + "title": "[FIX] Initial User not added to default channel", + "userLogin": "geekgonecrazy", + "description": "If injecting initial user. The user wasn’t added to the default General channel", + "milestone": "4.7.2", "contributors": [ - "sampaiodiego" + "geekgonecrazy", + "web-flow" ] }, { - "pr": "25368", - "title": "Regression: Fix English i18n react text", - "userLogin": "d-gubert", - "description": "Incorrect text in reaction tooltip has been fixed", - "milestone": "4.7.0", + "pr": "25520", + "title": "[FIX] User abandonment setting was not working doe to failing event hook", + "userLogin": "cauefcr", + "description": "A setting watcher and the query for grabbing abandoned chats were broken, now they're not.", + "milestone": "4.7.2", "contributors": [ - "d-gubert" + "cauefcr", + "tiagoevanp" ] }, { - "pr": "25349", - "title": "Regression: Rocket.Chat Webapp not loading.", - "userLogin": "pierre-lehnen-rc", + "pr": "25495", + "title": "[FIX] Dynamic load matrix is enabled and handle failure ", + "userLogin": "ggazzo", + "milestone": "4.7.2", "contributors": [ - "pierre-lehnen-rc", - "gabriellsh" + "ggazzo", + "geekgonecrazy" ] }, { - "pr": "25317", - "title": "Regression: Fix multi line is not showing an empty line between lines", - "userLogin": "filipemarins", - "milestone": "4.7.0", + "pr": "25409", + "title": "[FIX] One of the triggers was not working correctly", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", "contributors": [ - "filipemarins", - "gabriellsh" + "MartinSchoeler", + "tiagoevanp" ] }, { - "pr": "25320", - "title": "Regression: bump onboarding-ui version", - "userLogin": "guijun13", - "description": "- Bump to 'next' the onboarding-ui package from fuselage.\r\n- Update from 'companyEmail' to 'email' adminData usage types", + "pr": "25407", + "title": "[FIX] UI/UX issues on Live Chat widget", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", "contributors": [ - "guijun13" + "MartinSchoeler", + "dougfabris" ] }, { - "pr": "25335", - "title": "Chore: Create README.md for Rest Typings", - "userLogin": "ggazzo", + "pr": "25312", + "title": "Chore: Add Livechat repo into Monorepo packages", + "userLogin": "tiagoevanp", + "milestone": "4.7.2", "contributors": [ + "tiagoevanp", "ggazzo", - "web-flow" + "web-flow", + "MartinSchoeler" ] }, { - "pr": "25327", - "title": "Regression: Messages in new message template Crashing.", - "userLogin": "gabriellsh", + "pr": "25510", + "title": "Release 4.7.1", + "userLogin": "d-gubert", + "contributors": [ + "felipe-menelau", + "d-gubert", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25471", + "title": "[FIX] Spotlight results showing usernames instead of real names", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25434", + "title": "[FIX] LDAP sync removing users from channels when multiple groups are mapped to it", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25441", + "title": "[NEW] Use setting to determine if initial general channel is needed", + "userLogin": "felipe-menelau", + "description": "- Adds flag responsible for overwriting #general channel creation", + "milestone": "4.7.1", + "contributors": [ + "felipe-menelau", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "25390", + "title": "Release 4.7.0", + "userLogin": "d-gubert", + "contributors": [ + "sampaiodiego", + "web-flow", + "lingohub[bot]", + "dependabot[bot]", + "ggazzo", + "dougfabris", + "gabriellsh", + "tmontini", + "debdutdeb", + "Himanshu664", + "yash-rajpal", + "MartinSchoeler" + ] + }, + { + "pr": "25380", + "title": "Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window", + "userLogin": "filipemarins", + "description": "Fix: livechat room not opening.", + "milestone": "4.7.0", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25314", + "title": "Regression: Fix size of custom emoji and render emoji on thread message preview", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25371", + "title": "Chore: Bump fuselage", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25336", + "title": "Chore: Add options to debug stdout and rate limiter", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25368", + "title": "Regression: Fix English i18n react text", + "userLogin": "d-gubert", + "description": "Incorrect text in reaction tooltip has been fixed", + "milestone": "4.7.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25349", + "title": "Regression: Rocket.Chat Webapp not loading.", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc", + "gabriellsh" + ] + }, + { + "pr": "25317", + "title": "Regression: Fix multi line is not showing an empty line between lines", + "userLogin": "filipemarins", + "milestone": "4.7.0", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25320", + "title": "Regression: bump onboarding-ui version", + "userLogin": "guijun13", + "description": "- Bump to 'next' the onboarding-ui package from fuselage.\r\n- Update from 'companyEmail' to 'email' adminData usage types", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25335", + "title": "Chore: Create README.md for Rest Typings", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25327", + "title": "Regression: Messages in new message template Crashing.", + "userLogin": "gabriellsh", "contributors": [ "gabriellsh" ] @@ -79730,104 +80033,6 @@ "debdutdeb" ] }, - { - "pr": "24459", - "title": "Release 4.4.2", - "userLogin": "pierre-lehnen-rc", - "contributors": [ - "dougfabris", - "pierre-lehnen-rc", - "sampaiodiego" - ] - }, - { - "pr": "24450", - "title": "[FIX] OAuth mismatch redirect_uri error", - "userLogin": "sampaiodiego", - "milestone": "4.4.2", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "24453", - "title": "Chore: bump fuselage version", - "userLogin": "dougfabris", - "milestone": "4.4.2", - "contributors": [ - "dougfabris" - ] - }, - { - "pr": "24432", - "title": "Release 4.4.1", - "userLogin": "pierre-lehnen-rc", - "contributors": [ - "sampaiodiego", - "pierre-lehnen-rc", - "dougfabris", - "ostjen" - ] - }, - { - "pr": "24387", - "title": "[FIX] Slash commands previews not working", - "userLogin": "ostjen", - "milestone": "4.4.1", - "contributors": [ - "ostjen" - ] - }, - { - "pr": "24381", - "title": "[FIX] Add ?close to OAuth callback url", - "userLogin": "sampaiodiego", - "milestone": "4.4.1", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "24409", - "title": "[FIX] Startup errors creating indexes", - "userLogin": "sampaiodiego", - "description": "Fix `bio` and `prid` startup index creation errors.", - "milestone": "4.4.1", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "24401", - "title": "[FIX] Outgoing webhook without scripts not saving messages", - "userLogin": "sampaiodiego", - "milestone": "4.4.1", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "24407", - "title": "[FIX] Skip cloud steps for registered servers on setup wizard", - "userLogin": "dougfabris", - "milestone": "4.4.1", - "contributors": [ - "dougfabris", - "tassoevan", - "gabriellsh", - "web-flow" - ] - }, - { - "pr": "24418", - "title": "[FIX] Oembed request not respecting payload limit", - "userLogin": "sampaiodiego", - "milestone": "4.4.1", - "contributors": [ - "sampaiodiego", - "web-flow" - ] - }, { "pr": "24651", "title": "Chore: Update Apps-Engine", @@ -80893,1320 +81098,1234 @@ ] }, { - "pr": "24342", - "title": "Release 4.4.0", + "pr": "25022", + "title": "[FIX] Proxy settings being ignored", + "userLogin": "pierre-lehnen-rc", + "description": "Modify Meteor's `HTTP.call` to add back proxy support", + "milestone": "4.6.1", + "contributors": [ + "pierre-lehnen-rc", + "sampaiodiego" + ] + }, + { + "pr": "25067", + "title": "[FIX] NPS never finishing sending results", "userLogin": "sampaiodiego", + "milestone": "4.6.1", "contributors": [ - "sampaiodiego", - "web-flow", - "KevLehman", - "murtaza98", - "arshxyz", - "sidmohanty11", - "matheusbsilva137", - "Aman-Maheshwari", - "MartinSchoeler", - "ostjen", - "tassoevan", - "d-gubert", - "debdutdeb", - "yash-rajpal", - "dougfabris", - "aswinidev" + "sampaiodiego" + ] + } + ] + }, + "4.4.5": { + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "25022", + "title": "[FIX] Proxy settings being ignored", + "userLogin": "pierre-lehnen-rc", + "description": "Modify Meteor's `HTTP.call` to add back proxy support", + "milestone": "4.6.1", + "contributors": [ + "pierre-lehnen-rc", + "sampaiodiego" ] }, { - "pr": "24340", - "title": "Release 4.3.3", + "pr": "25067", + "title": "[FIX] NPS never finishing sending results", "userLogin": "sampaiodiego", + "milestone": "4.6.1", "contributors": [ "sampaiodiego" ] }, { - "pr": "24335", - "title": "Chore: Update Apps-Engine version", + "pr": "25580", + "title": "Release 4.7.2", "userLogin": "d-gubert", - "milestone": "4.4.0", "contributors": [ - "d-gubert" + "tiagoevanp", + "d-gubert", + "MartinSchoeler", + "ggazzo", + "cauefcr", + "geekgonecrazy" ] }, { - "pr": "24322", - "title": "Regression: Fix OmnichannelAppSourceRoomIcon sizes", - "userLogin": "tiagoevanp", - "milestone": "4.4.0", + "pr": "25544", + "title": "[FIX] Initial User not added to default channel", + "userLogin": "geekgonecrazy", + "description": "If injecting initial user. The user wasn’t added to the default General channel", + "milestone": "4.7.2", "contributors": [ - "tiagoevanp", + "geekgonecrazy", "web-flow" ] }, { - "pr": "24324", - "title": "Regression: Standalone register path failing when saving data", - "userLogin": "dougfabris", - "milestone": "4.4.0", + "pr": "25520", + "title": "[FIX] User abandonment setting was not working doe to failing event hook", + "userLogin": "cauefcr", + "description": "A setting watcher and the query for grabbing abandoned chats were broken, now they're not.", + "milestone": "4.7.2", "contributors": [ - "dougfabris", - "web-flow" + "cauefcr", + "tiagoevanp" ] }, { - "pr": "24316", - "title": "[FIX] Discussions not loading message history if not joined", - "userLogin": "gabriellsh", + "pr": "25495", + "title": "[FIX] Dynamic load matrix is enabled and handle failure ", + "userLogin": "ggazzo", + "milestone": "4.7.2", "contributors": [ - "gabriellsh" + "ggazzo", + "geekgonecrazy" ] }, { - "pr": "24289", - "title": "Regression: Create migration to fix index issue at boot", - "userLogin": "KevLehman", + "pr": "25409", + "title": "[FIX] One of the triggers was not working correctly", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", "contributors": [ - "KevLehman" + "MartinSchoeler", + "tiagoevanp" ] }, { - "pr": "24272", - "title": "Regression: Discussion room crashing", - "userLogin": "gabriellsh", - "milestone": "4.4.0", + "pr": "25407", + "title": "[FIX] UI/UX issues on Live Chat widget", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", "contributors": [ - "gabriellsh", - "web-flow", + "MartinSchoeler", "dougfabris" ] }, { - "pr": "24298", - "title": "Regression: Update tap-i18n package", - "userLogin": "sampaiodiego", - "description": "Fix the issue breaking IE11.", + "pr": "25312", + "title": "Chore: Add Livechat repo into Monorepo packages", + "userLogin": "tiagoevanp", + "milestone": "4.7.2", "contributors": [ - "sampaiodiego" + "tiagoevanp", + "ggazzo", + "web-flow", + "MartinSchoeler" ] }, { - "pr": "24302", - "title": "Regression: Fix pino child log levels", - "userLogin": "sampaiodiego", + "pr": "25510", + "title": "Release 4.7.1", + "userLogin": "d-gubert", "contributors": [ - "sampaiodiego" + "felipe-menelau", + "d-gubert", + "pierre-lehnen-rc" ] }, { - "pr": "24259", - "title": "Regression: Fix Alpine release tag", - "userLogin": "sampaiodiego", + "pr": "25471", + "title": "[FIX] Spotlight results showing usernames instead of real names", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", "contributors": [ - "sampaiodiego", - "web-flow", - "debdutdeb" + "pierre-lehnen-rc" ] }, { - "pr": "23676", - "title": "[IMPROVE] Setup Wizard Registration Flow", - "userLogin": "dougfabris", - "description": "This pull request brings a few improvements in our setup wizard flow, the very first contact with a Rocket.Chat. Some of them: \r\n- A brand new visual design;\r\n- Form validation improves;\r\n- Allow users to navigate back to all steps;\r\n- Optimized steps to register your workspace or keep standalone. And many more!\r\n\r\n\r\n![Kapture 2022-01-20 at 11 19 47](https://user-images.githubusercontent.com/27704687/150356868-425666b4-511f-4690-9ce5-e61b839b1d19.gif)", - "milestone": "4.4.0", + "pr": "25434", + "title": "[FIX] LDAP sync removing users from channels when multiple groups are mapped to it", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", "contributors": [ - "dougfabris", - "gabriellsh", - "tassoevan", - "web-flow" + "pierre-lehnen-rc" ] }, { - "pr": "24254", - "title": "Chore: Slash Commands Join to Typescript", - "userLogin": "eduardofcabrera", - "description": "Convert the slash commands .js files to .ts files.", + "pr": "25441", + "title": "[NEW] Use setting to determine if initial general channel is needed", + "userLogin": "felipe-menelau", + "description": "- Adds flag responsible for overwriting #general channel creation", + "milestone": "4.7.1", "contributors": [ - "eduardofcabrera", - "ostjen", + "felipe-menelau", + "sampaiodiego", "web-flow" ] }, { - "pr": "24288", - "title": "Regression: Fix Default Business hour overriding other Business Hours", - "userLogin": "murtaza98", + "pr": "25390", + "title": "Release 4.7.0", + "userLogin": "d-gubert", "contributors": [ - "murtaza98", + "sampaiodiego", "web-flow", - "KevLehman" + "lingohub[bot]", + "dependabot[bot]", + "ggazzo", + "dougfabris", + "gabriellsh", + "tmontini", + "debdutdeb", + "Himanshu664", + "yash-rajpal", + "MartinSchoeler" ] }, { - "pr": "24268", - "title": "i18n: Language update from LingoHub 🤖 on 2022-01-24Z", - "userLogin": "lingohub[bot]", + "pr": "25380", + "title": "Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window", + "userLogin": "filipemarins", + "description": "Fix: livechat room not opening.", + "milestone": "4.7.0", "contributors": [ - null + "filipemarins" ] }, { - "pr": "24276", - "title": "Regression: Fix incompatibility of apps http requests", - "userLogin": "d-gubert", - "description": "HTTP GET and HEAD requests made with an empty object as `data` were breaking, as the bridge converted this to the request's body as `'{}'` but meteor's new lib doesn't allow for body content on either of this request methods.\n\nTo maintain compatibility, we forced an empty body whenever we have a GET or HEAD request. This was probably the case previously, with the body of requests made with this methods being ignored either before being sent or in the third party server receiving the request", - "milestone": "4.4.0", + "pr": "25314", + "title": "Regression: Fix size of custom emoji and render emoji on thread message preview", + "userLogin": "filipemarins", "contributors": [ - "d-gubert" + "filipemarins", + "gabriellsh" ] }, { - "pr": "24177", - "title": "[IMPROVE] lib/Statistics improved and metrics collector", - "userLogin": "albuquerquefabio", - "description": "- On `statistics` object the property `get` is an async function now.\r\n- We need to collect additional data of feature activation through the statistics collector.\r\n - Some codes were splitted into another file just to organize.", + "pr": "25371", + "title": "Chore: Bump fuselage", + "userLogin": "gabriellsh", "contributors": [ - "albuquerquefabio", - "ostjen", - "web-flow", - "tassoevan" + "gabriellsh" ] }, { - "pr": "24218", - "title": "[FIX] Fixing the changing custom status behavior", - "userLogin": "AllanPazRibeiro", - "milestone": "4.4.0", + "pr": "25336", + "title": "Chore: Add options to debug stdout and rate limiter", + "userLogin": "sampaiodiego", "contributors": [ - "AllanPazRibeiro", - "web-flow", - "d-gubert" + "sampaiodiego" ] }, { - "pr": "24269", - "title": "Regression: Align Omni-Source icon sizes with designs", - "userLogin": "murtaza98", + "pr": "25368", + "title": "Regression: Fix English i18n react text", + "userLogin": "d-gubert", + "description": "Incorrect text in reaction tooltip has been fixed", + "milestone": "4.7.0", "contributors": [ - "murtaza98", - "KevLehman", - "web-flow" + "d-gubert" ] }, { - "pr": "24267", - "title": "Regression: Fix Inactive Departments still visible on Livechat", - "userLogin": "murtaza98", + "pr": "25349", + "title": "Regression: Rocket.Chat Webapp not loading.", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "murtaza98" + "pierre-lehnen-rc", + "gabriellsh" ] }, { - "pr": "24262", - "title": "[FIX] Solved Report Message Blank ", - "userLogin": "nishant23122000", - "description": "After resolving issue #24261 :\r\n\r\nhttps://user-images.githubusercontent.com/53515714/150629459-5f0a9cf6-9b0e-417f-8fc1-44c810bd5428.mp4", - "contributors": [ - "nishant23122000" - ] - }, - { - "pr": "23958", - "title": "[FIX] Errors on advanced sync prevent LDAP users from logging in", - "userLogin": "pierre-lehnen-rc", + "pr": "25317", + "title": "Regression: Fix multi line is not showing an empty line between lines", + "userLogin": "filipemarins", + "milestone": "4.7.0", "contributors": [ - "pierre-lehnen-rc", - "ostjen", - "web-flow" + "filipemarins", + "gabriellsh" ] }, { - "pr": "24187", - "title": "Chore: Convert model LoginServiceConfiguration to raw", - "userLogin": "rodrigok", + "pr": "25320", + "title": "Regression: bump onboarding-ui version", + "userLogin": "guijun13", + "description": "- Bump to 'next' the onboarding-ui package from fuselage.\r\n- Update from 'companyEmail' to 'email' adminData usage types", "contributors": [ - "rodrigok", - "albuquerquefabio" + "guijun13" ] }, { - "pr": "23804", - "title": "[FIX] Make canned responses popup dependent on Canned_responses_enabled setting", - "userLogin": "KevLehman", + "pr": "25335", + "title": "Chore: Create README.md for Rest Typings", + "userLogin": "ggazzo", "contributors": [ - "KevLehman", - "sampaiodiego", + "ggazzo", "web-flow" ] }, { - "pr": "24126", - "title": "[FIX] Wrong german translation for 2FA-Promt", - "userLogin": "mbreslein-thd", - "milestone": "4.4.0", - "contributors": [ - "mbreslein-thd", - "web-flow", - "dougfabris" - ] - }, - { - "pr": "24182", - "title": "Bump follow-redirects from 1.14.5 to 1.14.7 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "25327", + "title": "Regression: Messages in new message template Crashing.", + "userLogin": "gabriellsh", "contributors": [ - "dependabot[bot]", - "web-flow" + "gabriellsh" ] }, { - "pr": "24242", - "title": "Chore: Update pino and pino-pretty", + "pr": "25323", + "title": "Regression: Better MongoDB connection management for micro services", "userLogin": "sampaiodiego", + "milestone": "4.7.0", "contributors": [ - "sampaiodiego", - "web-flow" + "sampaiodiego" ] }, { - "pr": "24252", - "title": "[FIX] Avoid updating all rooms with visitor abandonment queries", - "userLogin": "KevLehman", - "milestone": "4.4.0", + "pr": "25250", + "title": "Regression: Validate empty fields for Message template", + "userLogin": "gabriellsh", + "milestone": "4.8.0", "contributors": [ - "KevLehman", - "web-flow" + "gabriellsh" ] }, { - "pr": "12548", - "title": "Add: Alpine image as option for build", + "pr": "25319", + "title": "Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings", "userLogin": "geekgonecrazy", - "milestone": "4.0.0", + "description": "The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work.\r\n\r\nThis temporarily switches to a fork of the matrix-appservice-bridge package.\r\n\r\nMade changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine).", + "milestone": "4.7.0", "contributors": [ "geekgonecrazy", "web-flow", - "LuluGO", - "ggazzo", - "rodrigok", - "sampaiodiego" + "d-gubert" ] }, { - "pr": "24248", - "title": "[FIX] Fixed broken links in setup wizard", - "userLogin": "Himanshu664", - "milestone": "4.4.0", + "pr": "25255", + "title": "Regression: Change preference to be default legacy messages", + "userLogin": "gabriellsh", + "milestone": "4.8.0", "contributors": [ - "Himanshu664", - "dougfabris", - "web-flow" + "gabriellsh" ] }, { - "pr": "24241", - "title": "[FIX] Apps Contextual Bar not carrying title and room information ", - "userLogin": "thassiov", - "description": "Fixes:\r\n\r\n- the app's name being rendered instead of the view's title,\r\n- the room's information (`IRoom`) wasn't being sent to the app when a `block action` happened\r\n\r\nFixed behavior with correct view title and room information included in the block action event:\r\n\r\nhttps://user-images.githubusercontent.com/733282/150420847-59bfcf8a-24a9-4dc5-8609-0d92dba38b70.mp4", - "milestone": "4.4.0", + "pr": "25306", + "title": "Regression: Fix reply button not working when hideFlexTab is enabled", + "userLogin": "filipemarins", "contributors": [ - "thassiov", - "web-flow" + "filipemarins", + "gabriellsh" ] }, { - "pr": "24233", - "title": "Chore: Bump fuselage hooks", - "userLogin": "dougfabris", - "milestone": "4.4.0", + "pr": "25311", + "title": "Regression: Add eslint package to micro services Dockerfile", + "userLogin": "sampaiodiego", + "milestone": "4.7.0", "contributors": [ - "dougfabris", - "gabriellsh", - "web-flow" + "sampaiodiego" ] }, { - "pr": "24243", - "title": "Regression: Remove extra call to `useOutsideClick` hook not following the function signature", - "userLogin": "tassoevan", - "description": "It migrates `client/sidebar/header/actions/Search` component to TypeScript and mitigates a invalid call to `Array.prototype.every`:\r\n\r\n![image](https://user-images.githubusercontent.com/2263066/150441397-3ff403b2-10c1-4a29-b37f-892d7d4a9252.png)", + "pr": "25218", + "title": "Chore: ensure scripts use cross-env and ignore some dirs (ROC-54)", + "userLogin": "souzaramon", + "description": "- data and test-failure should be ignored\r\n- ensure scripts use cross-env", "contributors": [ - "tassoevan" + "souzaramon" ] }, { - "pr": "24235", - "title": "[FIX] Change canned response model index to match other definition", - "userLogin": "KevLehman", - "milestone": "4.4.0", + "pr": "25313", + "title": "Regression: Revert Bugsnag version", + "userLogin": "ggazzo", "contributors": [ - "KevLehman", - "web-flow" + "ggazzo" ] }, { - "pr": "24239", - "title": "[IMPROVE] Show Channel Icons on Room Header & Info panels", - "userLogin": "murtaza98", - "description": "Updates Omnichannel Header & room Info component to render the source info\r\nBuilt on top of https://github.com/RocketChat/Rocket.Chat/pull/24237", - "milestone": "4.4.0", + "pr": "25305", + "title": "Regression: eslint not running on packages", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "d-gubert", - "murtaza98", - "KevLehman", - "web-flow" + "pierre-lehnen-rc", + "ggazzo" ] }, { - "pr": "24098", - "title": "[FIX] openUserInfo not working after changing room types", - "userLogin": "grahhnt", - "milestone": "4.4.0", + "pr": "25299", + "title": "Regression: Add `isPending` status to message", + "userLogin": "filipemarins", "contributors": [ - "grahhnt", - "web-flow", - "dougfabris" + "filipemarins" ] }, { - "pr": "24232", - "title": "Chore: Bump Livechat package version to 1.12.0", - "userLogin": "tiagoevanp", - "milestone": "4.4.0", + "pr": "25301", + "title": "Regression: Shows error if micro service cannot connect to Mongo", + "userLogin": "sampaiodiego", "contributors": [ - "tiagoevanp", - "KevLehman", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23818", - "title": "[NEW] App empty states component, category filter and empty states error variation implementations", - "userLogin": "rique223", - "description": "Created and implemented the category filters component:\r\nDemo gif:\r\n![categories_filter_demo](https://user-images.githubusercontent.com/43561537/148579731-1de83bf8-91ce-47e7-b6e5-7781384fdef9.gif)\r\n\r\nCreated and implemented the empty states(States on fuselage) component:\r\nDemo gif:\r\n![empty_states_demo](https://user-images.githubusercontent.com/43561537/148579930-49c2ff69-88f4-4a57-a24a-060868d76209.gif)\r\n\r\nImplemented a variations system for the empty states component and created a error message for network outage:\r\nDemo gif:\r\n![empty_states_variation_demo](https://user-images.githubusercontent.com/43561537/148580047-39adf8ef-2ee0-4c3e-8709-5faea4a5e335.gif)", - "milestone": "4.4.0", + "pr": "25287", + "title": "Regression: Use exact Node version on micro services Docker images", + "userLogin": "sampaiodiego", "contributors": [ - "rique223", - "ggazzo" + "sampaiodiego" ] }, { - "pr": "24176", - "title": "[IMPROVE] Rewrite Omnichannel Queue Page to React", - "userLogin": "tiagoevanp", - "description": "![image](https://user-images.githubusercontent.com/17487063/149458880-03c201ab-11cd-4c71-82aa-51bd557d3b6e.png)", - "milestone": "4.4.0", + "pr": "25286", + "title": "Chore: Add root package.json to houston files", + "userLogin": "d-gubert", + "description": "See title", "contributors": [ - "tiagoevanp", - "KevLehman" + "d-gubert" ] }, { - "pr": "24162", - "title": "[NEW][EE] Allow to filter departments by Business Units on Livechat", - "userLogin": "murtaza98", - "milestone": "4.4.0", + "pr": "25284", + "title": "Chore: Sync with master", + "userLogin": "d-gubert", "contributors": [ - "murtaza98", - "web-flow", - "renatobecker" + "sampaiodiego", + "d-gubert", + "web-flow" ] }, { - "pr": "24112", - "title": "[FIX][EE] Agent cannot change status to Available despite being within open business hours", - "userLogin": "murtaza98", + "pr": "25269", + "title": "Chore: Minor dependency updates", + "userLogin": "ggazzo", "contributors": [ - "murtaza98", - "KevLehman", + "ggazzo", + "sampaiodiego", "web-flow" ] }, { - "pr": "24211", - "title": "Regression: Fix handling of http requests in apps bridge", - "userLogin": "d-gubert", - "description": "Changes made during Meteor upgrade broke HTTP requests made in Rocket.Chat Apps", + "pr": "25224", + "title": "Chore: Add yarn plugin to check node and yarn version", + "userLogin": "ggazzo", "contributors": [ - "d-gubert", + "ggazzo", "web-flow" ] }, { - "pr": "24217", - "title": "Release 4.3.2", - "userLogin": "sampaiodiego", + "pr": "25235", + "title": "Release 4.6.3", + "userLogin": "d-gubert", "contributors": [ - "matheusbsilva137", "sampaiodiego", - "pierre-lehnen-rc", - "geekgonecrazy", - "d-gubert", - "dougfabris" + "d-gubert" ] }, { - "pr": "24068", - "title": "[FIX] Integration section crashing opening in My Account", - "userLogin": "dougfabris", - "milestone": "4.3.2", + "pr": "25220", + "title": "[FIX] Desktop notification on multi-instance environments", + "userLogin": "sampaiodiego", + "milestone": "4.6.3", "contributors": [ - "dougfabris", - "tassoevan", - "web-flow", - "gabriellsh" + "sampaiodiego" ] }, { - "pr": "24171", - "title": "Chore: Update Apps-Engine to 1.29.2", + "pr": "25280", + "title": "Chore: Remove package-lock.json from houston files", "userLogin": "d-gubert", - "milestone": "4.3.2", + "description": "Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore", "contributors": [ "d-gubert" ] }, { - "pr": "24158", - "title": "[FIX] App Framework Enable hanging indefinitely", - "userLogin": "geekgonecrazy", - "milestone": "4.3.2", + "pr": "25260", + "title": "[FIX] Adjust email label in Setup Wizard i18n files", + "userLogin": "guijun13", + "description": "- remove 'Company' label on onboarding email keys in certain languages", "contributors": [ - "geekgonecrazy", - "d-gubert", - "web-flow" + "guijun13" ] }, { - "pr": "24090", - "title": "[FIX] CSV Importer failing to import users", - "userLogin": "pierre-lehnen-rc", - "description": "- Update use of `setRealName` function to `_setRealName`.", - "milestone": "4.3.2", + "pr": "25275", + "title": "Chore: Fix return type warnings", + "userLogin": "KevLehman", "contributors": [ - "pierre-lehnen-rc", - "matheusbsilva137", - "web-flow" + "KevLehman" ] }, { - "pr": "24142", - "title": "[FIX][ENTERPRISE] Leading slashes in Engagement Dashboard API requests", - "userLogin": "matheusbsilva137", - "description": "- Remove trailing slashes from Engagement Dashboard API requests;", - "milestone": "4.3.2", + "pr": "23870", + "title": "[NEW] Expand Apps Engine's environment variable allowed list", + "userLogin": "cuonghuunguyen", + "milestone": "4.7.0", "contributors": [ - "matheusbsilva137" + null, + "debdutdeb", + "web-flow", + "cuonghuunguyen", + "dougfabris" ] }, { - "pr": "24108", - "title": "Release 4.3.1", + "pr": "25273", + "title": "Regression: Fix federation Matrix bridge startup", "userLogin": "sampaiodiego", + "milestone": "4.7.0", "contributors": [ - "murtaza98", - "sampaiodiego", - "MartinSchoeler", - "tassoevan", - "d-gubert" + "sampaiodiego" ] }, { - "pr": "24107", - "title": "[FIX][APPS] Action buttons not removed when app is disabled or uninstalled", - "userLogin": "d-gubert", - "description": "Fixes a problem where action buttons registered by any app would not be removed if the app was disabled or uninstalled", - "milestone": "4.3.1", + "pr": "25092", + "title": "[FIX] Message preview not available for queued chats", + "userLogin": "murtaza98", + "milestone": "4.7.0", "contributors": [ - "d-gubert", - "web-flow" + "murtaza98", + "KevLehman" ] }, { - "pr": "24105", - "title": "[FIX][APPS] Prevents emails from being sent when apps framework is disabled", - "userLogin": "d-gubert", - "description": "Introduction of new event `IPreEmailSent` was breaking the email function when the Apps-Engine framework was disabled in the administration", - "milestone": "4.3.1", + "pr": "23688", + "title": "[NEW] Alpha Matrix Federation", + "userLogin": "alansikora", + "description": "Experimental support for Matrix Federation with a Bridge\r\n\r\nhttps://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4", + "milestone": "4.7.0", "contributors": [ - "d-gubert" + "alansikora", + "geekgonecrazy", + "MarcosSpessatto", + "rodrigok" ] }, { - "pr": "24096", - "title": "[FIX] Ensure Firefox 91 ESR support", - "userLogin": "tassoevan", - "description": "It:\r\n- Adds `Firefox ESR` to `browserslist`;\r\n- Upgrades `@rocket.chat/fuselage-hooks` to overcome a bug related to Firefox implementation of `ResizeObserver` API.", - "milestone": "4.3.1", + "pr": "25259", + "title": "Chore: Bump Fuselage packages", + "userLogin": "dougfabris", + "milestone": "4.7.0", "contributors": [ - "tassoevan", - "web-flow" + "dougfabris" ] }, { - "pr": "24091", - "title": "Chore: Update Livechat to 1.11.1", - "userLogin": "MartinSchoeler", - "milestone": "4.3.1", + "pr": "25261", + "title": "[FIX] Incorrect websocket url in livechat widget", + "userLogin": "debdutdeb", + "milestone": "4.7.0", "contributors": [ - "MartinSchoeler" + "debdutdeb" ] }, { - "pr": "24067", - "title": "[FIX] Omnichannel enabled setting not working when creating rooms", - "userLogin": "murtaza98", - "milestone": "4.3.1", + "pr": "25007", + "title": "[FIX] Showing Blank Message Inside Report", + "userLogin": "nishant23122000", + "description": "https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4", "contributors": [ - "murtaza98", - "web-flow" + "nishant23122000" ] }, { - "pr": "23981", - "title": "[FIX] Enter not working on modal's multi-line input", - "userLogin": "murtaza98", - "description": "Right now, if we try to press enter for a new line on multi-line modal input... it auto triggers the submit event. This PR fixes this behaviour by not submitting the modal in case the enter was pressed within an input text with multiline expected", - "milestone": "4.3.1", + "pr": "25251", + "title": "Regression: Add select message to system message and thread preview and allow select on legacy template", + "userLogin": "filipemarins", + "milestone": "4.7.0", "contributors": [ - "murtaza98", + "filipemarins", + "ggazzo", "web-flow", - "tiagoevanp" + "gabriellsh", + "dougfabris" ] }, { - "pr": "24039", - "title": "[FIX] Omnichannel Current chats pagination not working", - "userLogin": "murtaza98", - "milestone": "4.3.1", + "pr": "25239", + "title": "[FIX] Add katex render to new message react template", + "userLogin": "filipemarins", + "milestone": "4.7.0", "contributors": [ - "murtaza98", - "KevLehman", - "web-flow" + "filipemarins", + "ggazzo", + "dougfabris" ] }, { - "pr": "24204", - "title": "[FIX] MAU when using micro services", - "userLogin": "sampaiodiego", + "pr": "25257", + "title": "Chore: Update Livechat to the last version", + "userLogin": "tiagoevanp", "contributors": [ - "sampaiodiego" + "tiagoevanp" ] }, { - "pr": "24210", - "title": "[IMPROVE] Limit recent emojis to 27", - "userLogin": "sampaiodiego", - "description": "Limits the recent emoji list to a maximum of 3 rows instead of listing every emoji you've used so far.\r\n\r\n![image](https://user-images.githubusercontent.com/8591547/150033087-92721b76-9203-42fe-ac2e-5b9eca50edab.png)", + "pr": "24515", + "title": "[FIX] Custom sound error toast messages", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "sampaiodiego" + "Himanshu664", + "dougfabris" ] }, { - "pr": "24169", - "title": "Chore: Update Omnichannel widget version to 1.11.2", - "userLogin": "tiagoevanp", + "pr": "25211", + "title": "Regression: Avatar not loading on first direct message", + "userLogin": "filipemarins", + "description": "fix avatar not loading on a first direct message", + "milestone": "4.7.0", "contributors": [ - "tiagoevanp", - "web-flow" + "filipemarins", + "ggazzo" ] }, { - "pr": "24165", - "title": "Chore: Replace Blaze templates", - "userLogin": "tassoevan", - "description": "It replaces some templates used by login and invitation flows with React components. It also drops `main` template, allowing `appLayout` to just handle components now.", + "pr": "25254", + "title": "Regression: Show username and real name on the message system", + "userLogin": "filipemarins", "contributors": [ - "tassoevan", - "ggazzo" + "filipemarins" ] }, { - "pr": "23591", - "title": "Chore: Removing hubot from docker-compose", - "userLogin": "geekgonecrazy", - "description": "Remove hubot from docker-compose. This is forcing everyone to spin up Hubot every time they deploy Rocket.Chat and not that many people are using it. So we are wasting resources on peoples machines by forcing it", + "pr": "25217", + "title": "[IMPROVE] Performance for some Omnichannel features", + "userLogin": "KevLehman", "contributors": [ - "geekgonecrazy", - "debdutdeb", - "web-flow" + "KevLehman" ] }, { - "pr": "24168", - "title": "[IMPROVE] Admin page header buttons consistency", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/149371746-66e5e6e4-5c8e-46d7-b230-ecbc4502b665.png)\r\n![image](https://user-images.githubusercontent.com/27704687/149371759-c3d948af-d877-486c-a263-da12c0b70185.png)\r\n![image](https://user-images.githubusercontent.com/27704687/149371769-09b0623d-a5c5-43e0-a4ef-73ba0bcf1730.png)\r\n![image](https://user-images.githubusercontent.com/27704687/149371782-b1b898c7-3aad-47ee-8c5c-cf9cb816d72b.png)\r\n![image](https://user-images.githubusercontent.com/27704687/149371796-b88514d2-3c8d-4d9d-a45b-24f48783e95c.png)\r\n\r\n\r\n### after\r\n![Screen Shot 2022-01-13 at 13 38 00](https://user-images.githubusercontent.com/27704687/149371084-668d5f14-e03e-4cdd-8763-058db9c2f16c.png)\r\n![Screen Shot 2022-01-13 at 13 38 18](https://user-images.githubusercontent.com/27704687/149371126-23a059cb-efa7-4ffb-970b-da23d8742bb1.png)\r\n![Screen Shot 2022-01-13 at 13 38 38](https://user-images.githubusercontent.com/27704687/149371181-c8bbbbbd-ed6d-48b4-844f-09fdce0080b6.png)\r\n![Screen Shot 2022-01-13 at 13 38 59](https://user-images.githubusercontent.com/27704687/149371232-3d292f5e-e8b0-41e1-b065-90a80a5f08ce.png)\r\n![Screen Shot 2022-01-13 at 13 39 08](https://user-images.githubusercontent.com/27704687/149371263-64fd09e4-456e-48ee-9976-83f42b90e4d9.png)", - "milestone": "4.4.0", + "pr": "25200", + "title": "[FIX] room creation fails if app framework is disabled", + "userLogin": "debdutdeb", + "milestone": "4.7.0", "contributors": [ - "dougfabris", - "web-flow" + "debdutdeb" ] }, { - "pr": "24193", - "title": "i18n: Language update from LingoHub 🤖 on 2022-01-17Z", - "userLogin": "lingohub[bot]", + "pr": "24565", + "title": "[IMPROVE] Add OTR Room States", + "userLogin": "yash-rajpal", + "description": "Earlier OTR room uses only 2 states, we need more states to support future features. \r\nThis adds more states for the OTR contextualBar.\r\n\r\n- Expired\r\n\"Screen\r\n\r\n- Declined\r\nScreen Shot 2022-04-20 at 13 49 28\r\n\r\n- Error\r\n\"Screen", + "milestone": "4.7.0", "contributors": [ - null, - "sampaiodiego", - "web-flow" + "yash-rajpal", + "dougfabris" ] }, { - "pr": "24068", - "title": "[FIX] Integration section crashing opening in My Account", - "userLogin": "dougfabris", - "milestone": "4.3.2", + "pr": "25170", + "title": "[FIX] Client disconnection on network loss", + "userLogin": "amolghode1981", + "description": "Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online\r\nunless agent explicitly logs off.\r\nAgent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways.\r\n1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off\r\nin the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh.\r\n2. Second reason is when computer goes in sleep mode.\r\n3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped.\r\n\r\nSolution:\r\nThe idea is to detect the network disconnection and start the start the attempts to reconnect.\r\nThe detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not\r\ncall onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are\r\nused.\r\n\r\nThe number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to\r\nreconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped.\r\n\r\nWhen the server is disconnected, it should be indicated on the phone button.", "contributors": [ - "dougfabris", - "tassoevan", - "web-flow", - "gabriellsh" + "amolghode1981", + "KevLehman" ] }, { - "pr": "24044", - "title": "[IMPROVE] Rewrite roomNotFound to React Component", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/147608307-468e6955-5db4-40c5-86a7-91448ac03427.png)\r\n![image](https://user-images.githubusercontent.com/27704687/147608377-d979adf5-615f-4180-8587-449369bf87f8.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/149158027-e39bc0a0-4c33-465b-83e0-873e558a037b.png)\r\n![image](https://user-images.githubusercontent.com/27704687/149157692-3e73c0b4-1759-430c-b1c4-b521e47d774d.png)", - "milestone": "4.4.0", + "pr": "25244", + "title": "[FIX] Read receipts show with color gray when not read yet", + "userLogin": "filipemarins", "contributors": [ - "dougfabris", - "tassoevan", - "web-flow", + "filipemarins", "gabriellsh" ] }, { - "pr": "24186", - "title": "Regression: Enable custom emoji on admin custom status page", - "userLogin": "AllanPazRibeiro", - "contributors": [ - "AllanPazRibeiro", - "web-flow" + "pr": "25230", + "title": "[FIX] VoIP disabled/enabled sequence puts voip agent in error state", + "userLogin": "amolghode1981", + "description": "Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side)\r\n\r\nIt was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk.\r\n\r\nSolution:\r\n\r\n1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value.\r\n2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance.\r\n3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler.", + "contributors": [ + "amolghode1981", + "KevLehman" ] }, { - "pr": "24075", - "title": "Chore: Update Meteor to 2.5.3", - "userLogin": "sampaiodiego", + "pr": "25087", + "title": "[NEW] Add expire index to integration history", + "userLogin": "geekgonecrazy", + "milestone": "4.7.0", "contributors": [ - "sampaiodiego", - "web-flow", - "ggazzo" + "geekgonecrazy" ] }, { - "pr": "24060", - "title": "[NEW] Apple Login", - "userLogin": "ggazzo", + "pr": "24521", + "title": "Chore: update OTR icon", + "userLogin": "kibonusp", + "description": "I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage.", + "milestone": "4.7.0", "contributors": [ - "ggazzo", - "sampaiodiego", - "web-flow" + "kibonusp", + "yash-rajpal", + "web-flow", + "tassoevan" ] }, { - "pr": "24171", - "title": "Chore: Update Apps-Engine to 1.29.2", - "userLogin": "d-gubert", - "milestone": "4.3.2", + "pr": "25237", + "title": "[FIX] Toolbox hiding under contextual bar", + "userLogin": "gabriellsh", "contributors": [ - "d-gubert" + "gabriellsh" ] }, { - "pr": "24170", - "title": "[NEW] Enabling emoji on custom status", - "userLogin": "AllanPazRibeiro", + "pr": "25231", + "title": "[IMPROVE] Added MaxNickNameLength and MaxBioLength constants", + "userLogin": "aakash-gitdev", "contributors": [ - "AllanPazRibeiro" + "aakash-gitdev", + "web-flow", + "gabriellsh" ] }, { - "pr": "24158", - "title": "[FIX] App Framework Enable hanging indefinitely", - "userLogin": "geekgonecrazy", - "milestone": "4.3.2", + "pr": "25220", + "title": "[FIX] Desktop notification on multi-instance environments", + "userLogin": "sampaiodiego", + "milestone": "4.6.3", "contributors": [ - "geekgonecrazy", - "d-gubert", - "web-flow" + "sampaiodiego" ] }, { - "pr": "24090", - "title": "[FIX] CSV Importer failing to import users", - "userLogin": "pierre-lehnen-rc", - "description": "- Update use of `setRealName` function to `_setRealName`.", - "milestone": "4.3.2", + "pr": "25175", + "title": "[FIX] Reply button behavior on broadcast channel", + "userLogin": "filipemarins", + "description": "Hide reply button for the user that sent the message", "contributors": [ - "pierre-lehnen-rc", - "matheusbsilva137", + "filipemarins", "web-flow" ] }, { - "pr": "24142", - "title": "[FIX][ENTERPRISE] Leading slashes in Engagement Dashboard API requests", - "userLogin": "matheusbsilva137", - "description": "- Remove trailing slashes from Engagement Dashboard API requests;", - "milestone": "4.3.2", + "pr": "25216", + "title": "[FIX] Read receipts showing before message read", + "userLogin": "filipemarins", "contributors": [ - "matheusbsilva137" + "filipemarins" ] }, { - "pr": "24127", - "title": "i18n: Language update from LingoHub 🤖 on 2022-01-10Z", - "userLogin": "lingohub[bot]", + "pr": "25222", + "title": "[FIX] Add reaction not working in legacy messages", + "userLogin": "gabriellsh", "contributors": [ - null + "gabriellsh" ] }, { - "pr": "24133", - "title": "Chore: Migrate useOutsideClick to fuselage-hooks", - "userLogin": "juliajforesti", + "pr": "25223", + "title": "Chore: Add error boundary to message component", + "userLogin": "gabriellsh", + "description": "Not crash the whole application if something goes wrong in the MessageList component.\r\n\r\n![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png)", "contributors": [ - "juliajforesti", - "ggazzo" + "gabriellsh" ] }, { - "pr": "24123", - "title": "Chore: Include REG_TOKEN in docker-compose", - "userLogin": "geekgonecrazy", + "pr": "25130", + "title": "Chore: Update Livechat version", + "userLogin": "tiagoevanp", "contributors": [ - "geekgonecrazy", - "web-flow" + "tiagoevanp" ] }, { - "pr": "24117", - "title": "[FIX] Custom Emoji Image preview", - "userLogin": "sidmohanty11", - "description": "Before,\r\n\r\n![custom-img-preview-rc3](https://user-images.githubusercontent.com/73601258/148431936-c82d4200-69b1-484b-8be2-d72f5c28202b.png)\r\n\r\nAfter,\r\n\r\n![custom-img-preview-rc1](https://user-images.githubusercontent.com/73601258/148431955-8842a2e3-b9f3-4d68-b0d8-c5444419f767.png)\r\n\r\nalso if any error, (for example - if we upload a video mp4 file) \r\n\r\n![custom-img-preview-rc2](https://user-images.githubusercontent.com/73601258/148431998-64bc1fbb-9958-495c-89c1-61df06adec75.png)", + "pr": "25073", + "title": "[FIX] AgentOverview analytics wrong departmentId parameter", + "userLogin": "paulobernardoaf", + "description": "When filtering the analytics charts by department, data would not appear because the object:\r\n```js\r\n{\r\n value: \"department-id\",\r\n label: \"department-name\"\r\n}\r\n```\r\nwas being used in the `departmentId` parameter.\r\n\r\n- Before:\r\n![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png)\r\n\r\n- After:\r\n![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png)", + "milestone": "4.7.0", "contributors": [ - "sidmohanty11", - "dougfabris", - "web-flow" + "paulobernardoaf" ] }, { - "pr": "24078", - "title": "[IMPROVE] Added a Reset Button in the Account Profile Page", - "userLogin": "aswinidev", + "pr": "25056", + "title": "[FIX] Close room when dismiss wrap up call modal", + "userLogin": "tiagoevanp", + "milestone": "4.7.0", "contributors": [ - "aswinidev", - "web-flow", - "murtaza98" + "tiagoevanp" ] }, { - "pr": "24118", - "title": "Revert: \"[IMPROVE] Throw 404 error in invalid endpoints\"", - "userLogin": "matheusbsilva137", + "pr": "25208", + "title": "Regression: yarn dev triggers build dependencies", + "userLogin": "ggazzo", "contributors": [ - "matheusbsilva137", + "ggazzo", "web-flow" ] }, { - "pr": "24070", - "title": "[IMPROVE] Rewrite AddWebdavAccountModal to React Component ", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/147777054-bf2f84e4-5226-4ebc-ab6e-287b83889b85.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/147769132-2b938ae8-aba3-4230-876d-572e46268b9a.png)", + "pr": "24714", + "title": "[FIX] Added invalid password error message", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "dougfabris", - "gabriellsh", - "web-flow", - "tassoevan" + "Himanshu664", + "dougfabris" ] }, { - "pr": "21181", - "title": "[FIX] Password error should not be shown when selecting set random password", - "userLogin": "yash-rajpal", - "description": "We should not keep `password` as required field when we check set random password field. In this password should not be required", - "milestone": "4.4.0", + "pr": "25196", + "title": "Chore: Tests with Playwright (task: ROC-28, 09-channels)", + "userLogin": "tmontini", "contributors": [ - "yash-rajpal", - "pierre-lehnen-rc", - "dougfabris", - "web-flow" + "tmontini" ] }, { - "pr": "22400", - "title": "Chore: Apply generics to infer types of useForm hook", - "userLogin": "tassoevan", + "pr": "25174", + "title": "Chore: Template to generate packages", + "userLogin": "ggazzo", + "description": "```\r\nnpx hygen package new test\r\n```", "contributors": [ - "tassoevan", - "web-flow" + "ggazzo", + "web-flow", + "sampaiodiego" ] }, { - "pr": "24023", - "title": "Chore: Remove unused assets", - "userLogin": "tassoevan", + "pr": "25193", + "title": "Regression: Fix micro services Docker build", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan", "sampaiodiego", + "ggazzo", "web-flow" ] }, { - "pr": "24020", - "title": "Chore: Replace `isEmail` with `validateEmail`", - "userLogin": "tassoevan", - "description": "Follows #23816.", + "pr": "25191", + "title": "Release 4.6.2", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan", - "web-flow" + "sidmohanty11", + "sampaiodiego" ] }, { - "pr": "24111", - "title": "Chore: Fix Houston `getNodeNpmVersions` regex to correctly get Node and Npm complete versions", - "userLogin": "debdutdeb", + "pr": "25101", + "title": "[FIX] Database indexes not being created", + "userLogin": "sampaiodiego", + "milestone": "4.6.2", "contributors": [ - "debdutdeb" + "sampaiodiego" ] }, { - "pr": "23456", - "title": "Chore: Move `callbacks` to /lib", - "userLogin": "tassoevan", - "description": "It moves to `/lib`, migrates to TypeScript, and deprecates the `callbacks` API.", + "pr": "24933", + "title": "[FIX] Deactivating user breaks if user is the only room owner", + "userLogin": "sidmohanty11", + "description": "## Before\r\n\r\nhttps://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4\r\n\r\n\r\n\r\n## After\r\nhttps://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4", + "milestone": "4.6.2", "contributors": [ - "tassoevan" + "sidmohanty11", + "sampaiodiego" ] }, { - "pr": "24107", - "title": "[FIX][APPS] Action buttons not removed when app is disabled or uninstalled", - "userLogin": "d-gubert", - "description": "Fixes a problem where action buttons registered by any app would not be removed if the app was disabled or uninstalled", - "milestone": "4.3.1", + "pr": "25095", + "title": "Release 4.6.1", + "userLogin": "sampaiodiego", "contributors": [ - "d-gubert", - "web-flow" + "dougfabris", + "sampaiodiego", + "gabriellsh", + "yash-rajpal", + "pierre-lehnen-rc" ] }, { - "pr": "24105", - "title": "[FIX][APPS] Prevents emails from being sent when apps framework is disabled", - "userLogin": "d-gubert", - "description": "Introduction of new event `IPreEmailSent` was breaking the email function when the Apps-Engine framework was disabled in the administration", - "milestone": "4.3.1", + "pr": "25022", + "title": "[FIX] Proxy settings being ignored", + "userLogin": "pierre-lehnen-rc", + "description": "Modify Meteor's `HTTP.call` to add back proxy support", + "milestone": "4.6.1", "contributors": [ - "d-gubert" + "pierre-lehnen-rc", + "sampaiodiego" ] }, { - "pr": "24096", - "title": "[FIX] Ensure Firefox 91 ESR support", - "userLogin": "tassoevan", - "description": "It:\r\n- Adds `Firefox ESR` to `browserslist`;\r\n- Upgrades `@rocket.chat/fuselage-hooks` to overcome a bug related to Firefox implementation of `ResizeObserver` API.", - "milestone": "4.3.1", + "pr": "25082", + "title": "[FIX] Invitation links don't redirect to the registration form", + "userLogin": "yash-rajpal", + "milestone": "4.6.1", "contributors": [ - "tassoevan", - "web-flow" + "yash-rajpal" ] }, { - "pr": "24022", - "title": "Chore: Update copyright notices", - "userLogin": "tassoevan", - "description": "Update date range in copyright notices to `2015-2022`.", + "pr": "25069", + "title": "[FIX] FormData uploads not working", + "userLogin": "gabriellsh", + "milestone": "4.6.1", "contributors": [ - "tassoevan" + "gabriellsh", + "dougfabris" ] }, { - "pr": "23817", - "title": "[IMPROVE] Importer text for CSV upload file format", - "userLogin": "ostjen", + "pr": "25067", + "title": "[FIX] NPS never finishing sending results", + "userLogin": "sampaiodiego", + "milestone": "4.6.1", "contributors": [ - "ostjen", - "tassoevan", - "web-flow" + "sampaiodiego" ] }, { - "pr": "24091", - "title": "Chore: Update Livechat to 1.11.1", - "userLogin": "MartinSchoeler", - "milestone": "4.3.1", + "pr": "25050", + "title": "[FIX] Upgrade Tab showing for a split second", + "userLogin": "gabriellsh", + "milestone": "4.6.1", "contributors": [ - "MartinSchoeler" + "gabriellsh" ] }, { - "pr": "24067", - "title": "[FIX] Omnichannel enabled setting not working when creating rooms", - "userLogin": "murtaza98", - "milestone": "4.3.1", + "pr": "25055", + "title": "[FIX] UserAutoComplete not rendering UserAvatar correctly", + "userLogin": "dougfabris", + "description": "### before\r\n![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png)\r\n\r\n### after\r\n![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png)", + "milestone": "4.6.1", "contributors": [ - "murtaza98", - "web-flow" + "dougfabris" ] }, { - "pr": "23981", - "title": "[FIX] Enter not working on modal's multi-line input", - "userLogin": "murtaza98", - "description": "Right now, if we try to press enter for a new line on multi-line modal input... it auto triggers the submit event. This PR fixes this behaviour by not submitting the modal in case the enter was pressed within an input text with multiline expected", - "milestone": "4.3.1", + "pr": "25180", + "title": "Chore: Remove duplicated useUserRoom", + "userLogin": "dougfabris", + "milestone": "4.7.0", "contributors": [ - "murtaza98", - "web-flow", - "tiagoevanp" + "dougfabris" ] }, { - "pr": "23605", - "title": "[IMPROVE] Add Rocket.Chat version to User-Agent header for oembed requests", - "userLogin": "sidmohanty11", + "pr": "25167", + "title": "Chore: TS migration SortList", + "userLogin": "ggazzo", "contributors": [ - "sidmohanty11" + "ggazzo" ] }, { - "pr": "24051", - "title": "[FIX] wrong new userInfo during user creation", - "userLogin": "Aman-Maheshwari", + "pr": "24933", + "title": "[FIX] Deactivating user breaks if user is the only room owner", + "userLogin": "sidmohanty11", + "description": "## Before\r\n\r\nhttps://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4\r\n\r\n\r\n\r\n## After\r\nhttps://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4", + "milestone": "4.6.2", "contributors": [ - "Aman-Maheshwari", - "web-flow", - "murtaza98" + "sidmohanty11", + "sampaiodiego" ] }, { - "pr": "24053", - "title": "[IMPROVE] Throw 404 error in invalid endpoints", - "userLogin": "matheusbsilva137", - "description": "- Throw 404 error when trying to call invalid endpoints.", + "pr": "25181", + "title": "Regression: Fix services Docker build on CI", + "userLogin": "sampaiodiego", "contributors": [ - "matheusbsilva137", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23970", - "title": "[FIX] Filter ability for admin room checkboxes", - "userLogin": "sidmohanty11", - "description": "Now,\r\n\r\nhttps://user-images.githubusercontent.com/73601258/146380812-d3aa5561-64e1-4515-a639-3b6d87432ae4.mp4\r\n\r\nBefore,\r\n\r\nhttps://user-images.githubusercontent.com/73601258/146385538-85a70fce-9974-40e0-8757-eda1a5d411b7.mp4", - "milestone": "4.4.0", + "pr": "25089", + "title": "[FIX] UserCard sanitization", + "userLogin": "dougfabris", + "description": "- Rewrites the component to TS\r\n- Fixes some visual issues\r\n\r\n### before\r\n![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png)\r\n\r\n### after\r\n![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png)", + "milestone": "4.7.0", "contributors": [ - "sidmohanty11", - "yash-rajpal", - "web-flow", - "matheusbsilva137" + "dougfabris" ] }, { - "pr": "24024", - "title": "[FIX] Message Erasure Type \"Keep\" Messages not working", - "userLogin": "arshxyz", + "pr": "25085", + "title": "Chore: move definitions to packages", + "userLogin": "ggazzo", + "milestone": "3.7.0", "contributors": [ - "arshxyz", - "murtaza98", - "web-flow" + "ggazzo" ] }, { - "pr": "24039", - "title": "[FIX] Omnichannel Current chats pagination not working", - "userLogin": "murtaza98", - "milestone": "4.3.1", + "pr": "25168", + "title": "Regression: CI playwright", + "userLogin": "ggazzo", "contributors": [ - "murtaza98", - "KevLehman", - "web-flow" + "ggazzo" ] }, { - "pr": "23954", - "title": "Chore: Update mem to 8.1.1", - "userLogin": "sampaiodiego", + "pr": "25125", + "title": "Chore: Convert NotificationStatus to TS", + "userLogin": "jeanfbrito", "contributors": [ - "sampaiodiego", - "web-flow" + "jeanfbrito", + "ggazzo" ] }, { - "pr": "24015", - "title": "[FIX] MongoError during startup saying \"ns not found\"", - "userLogin": "sampaiodiego", + "pr": "25148", + "title": "[FIX] Message menu action not working on legacy messages.", + "userLogin": "gabriellsh", "contributors": [ - "sampaiodiego" + "gabriellsh" ] }, { - "pr": "24054", - "title": "Chore: add script to fix code with prettier", - "userLogin": "KevLehman", + "pr": "25122", + "title": "Chore: Tests with Playwright (task: All works)", + "userLogin": "weslley543", "contributors": [ - "KevLehman" + "weslley543" ] }, { - "pr": "24000", - "title": "Chore: Prettier for us all", + "pr": "25129", + "title": "Chore: Remove old files from removed Omnichannel feature", "userLogin": "sampaiodiego", "contributors": [ "sampaiodiego" ] }, { - "pr": "24049", - "title": "Merge master into develop & Set version to 4.4.0-develop", - "userLogin": "sampaiodiego", + "pr": "25128", + "title": "Chore: Convert admin custom sound to tsx", + "userLogin": "ggazzo", "contributors": [ - "ggazzo", - "sampaiodiego", - "web-flow" + "ggazzo" ] }, { - "pr": "24047", - "title": "Release 4.3.0", - "userLogin": "sampaiodiego", + "pr": "25126", + "title": "Chore: Migrate oauth2server to typescript", + "userLogin": "KevLehman", "contributors": [ - "sampaiodiego", - "web-flow", - "Aman-Maheshwari", - "juliajforesti", - "ggazzo", - "dougfabris", - "goyome", - "tiagoevanp", - "KevLehman", - "matheusbsilva137", - "rique223", - "lingohub[bot]", - "gabriellsh", - "cuonghuunguyen", - "tassoevan", - "murtaza98", - "MartinSchoeler", - "dependabot[bot]" + "KevLehman" ] }, { - "pr": "24046", - "title": "Chore: Bump fuselage 0.31.0", - "userLogin": "ggazzo", + "pr": "25123", + "title": "Chore: Convert LivechatAgentActivity to raw model and TS", + "userLogin": "sampaiodiego", "contributors": [ - "ggazzo", - "tassoevan" + "sampaiodiego" ] }, { - "pr": "24045", - "title": "Chore: Update Apps-Engine to latest", - "userLogin": "d-gubert", - "milestone": "4.3.0", + "pr": "25124", + "title": "Chore: Remove unused Drone CI files", + "userLogin": "geekgonecrazy", "contributors": [ - "d-gubert" + "geekgonecrazy", + "web-flow" ] }, { - "pr": "24021", - "title": "Chore: Replace typography", + "pr": "25121", + "title": "Chore: Convert Mailer to TS", "userLogin": "juliajforesti", "contributors": [ "juliajforesti", - "ggazzo" + "sampaiodiego" ] }, { - "pr": "23940", - "title": "Release 4.2.2", + "pr": "25107", + "title": "Regression: Fix CI monorepo build", "userLogin": "ggazzo", "contributors": [ - "qwertiko", "ggazzo" ] }, { - "pr": "23347", - "title": "[FIX] creating room with federated member", - "userLogin": "qwertiko", - "milestone": "4.2.2", + "pr": "25074", + "title": "Chore: Monorepo ", + "userLogin": "ggazzo", + "milestone": "3.7.0", "contributors": [ - "qwertiko", - "web-flow" + "sampaiodiego", + "ggazzo" ] }, { - "pr": "23917", - "title": "Release 4.2.1", - "userLogin": "sampaiodiego", + "pr": "25097", + "title": "[IMPROVE] Rename upgrade tab routes", + "userLogin": "guijun13", + "description": "Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test.", "contributors": [ - "tiagoevanp", - "sampaiodiego", - "matheusbsilva137", - "rique223", - "dougfabris", - "tassoevan", - "MartinSchoeler" + "guijun13" ] }, { - "pr": "23913", - "title": "Chore: Update Livechat", - "userLogin": "MartinSchoeler", - "milestone": "4.2.1", - "contributors": [ - "MartinSchoeler" - ] - }, - { - "pr": "23796", - "title": "[FIX] Segmentation fault on CentOS 7 due to outdated `sharp`", - "userLogin": "tassoevan", - "description": "Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`.\r\n\r\nSuggested as a fix for versions `4.0.x` and `4.1.x`.", - "milestone": "4.2.1", + "pr": "25076", + "title": "Bump eslint-plugin-anti-trojan-source from 1.0.6 to 1.1.0", + "userLogin": "dependabot[bot]", "contributors": [ - "tassoevan" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23857", - "title": "[FIX] teams.removeMembers client usage", - "userLogin": "dougfabris", - "milestone": "4.2.1", + "pr": "24936", + "title": "[FIX] End call button disappearing when on-hold", + "userLogin": "tiagoevanp", "contributors": [ - "ggazzo", - "dougfabris" + "tiagoevanp" ] }, { - "pr": "23861", - "title": "[FIX] Wrong button for non trial apps", - "userLogin": "rique223", - "description": "This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. \r\nBefore:\r\n![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) \r\nAfter:\r\n![image](https://user-images.githubusercontent.com/43561537/144687924-1a3eb3a7-783f-4450-abd2-1efa0de64658.png)", - "milestone": "4.2.1", + "pr": "24932", + "title": "[FIX] Use correct room property for call ended at", + "userLogin": "MartinSchoeler", "contributors": [ - "rique223" + "MartinSchoeler" ] }, { - "pr": "23859", - "title": "[FIX] Error when creating an inactive user in admin panel", - "userLogin": "matheusbsilva137", - "description": "- Fix `usersInRole` array used to send email to activate a user.", - "milestone": "4.2.1", + "pr": "25022", + "title": "[FIX] Proxy settings being ignored", + "userLogin": "pierre-lehnen-rc", + "description": "Modify Meteor's `HTTP.call` to add back proxy support", + "milestone": "4.6.1", "contributors": [ - "matheusbsilva137" + "pierre-lehnen-rc", + "sampaiodiego" ] }, { - "pr": "23821", - "title": "[NEW] Create new setting to clean local storage at end of chats", - "userLogin": "tiagoevanp", - "description": "Include setting to handle with and clear the localStorage on Livechat widget. \r\n\r\n![image](https://user-images.githubusercontent.com/17487063/144171179-95f7cf41-0192-4532-bedf-99e0b01f2c61.png)\r\n\r\nThis behavior is only possible to happen when https://github.com/RocketChat/Rocket.Chat.Livechat/pull/666 merged and released.", - "milestone": "4.2.1", + "pr": "25082", + "title": "[FIX] Invitation links don't redirect to the registration form", + "userLogin": "yash-rajpal", + "milestone": "4.6.1", "contributors": [ - "tiagoevanp" + "yash-rajpal" ] }, { - "pr": "24035", - "title": "Regression: Ensure room action buttons only appear inside menu", - "userLogin": "d-gubert", - "description": "Currently, action buttons registered by apps to appear in the ROOM_ACTION context show in the first position of the list, but since they don't have an icon they are effectively invisible in the tab bar.\r\n\r\nHere we change the order configuration of the button so we make sure it only shows inside the room menu", - "milestone": "4.3.0", + "pr": "23971", + "title": "[NEW] Message Template React Component", + "userLogin": "ggazzo", + "description": "Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template).\r\n\r\n\r\n\r\n![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png)\r\nIn case you encounter any problems, or want to compare, temporarily it is possible to use the old version\r\n\r\n\"image\"", "contributors": [ - "d-gubert" + "ggazzo", + "sampaiodiego" ] }, { - "pr": "24030", - "title": "i18n: Language update from LingoHub 🤖 on 2021-12-27Z", - "userLogin": "lingohub[bot]", + "pr": "25069", + "title": "[FIX] FormData uploads not working", + "userLogin": "gabriellsh", + "milestone": "4.6.1", "contributors": [ - null, - "sampaiodiego", - "web-flow" + "gabriellsh", + "dougfabris" ] }, { - "pr": "24033", - "title": "Regression: Add optional chaining to possibly undefined fields", - "userLogin": "KevLehman", + "pr": "19866", + "title": "[FIX] Video and Audio not skipping forward", + "userLogin": "MartinSchoeler", "contributors": [ - "KevLehman" + "MartinSchoeler", + "tassoevan", + "web-flow", + "dougfabris" ] }, { - "pr": "24034", - "title": "Regression: Remove dangling console.log", - "userLogin": "tassoevan", - "description": "A empty array have been printed to console due to a promise chained to `console.log` and `console.error` calls, probably for debugging purposes.", - "milestone": "4.3.0", + "pr": "25067", + "title": "[FIX] NPS never finishing sending results", + "userLogin": "sampaiodiego", + "milestone": "4.6.1", "contributors": [ - "tassoevan" + "sampaiodiego" ] }, { - "pr": "24018", - "title": "Regression: Remove self from fallback departments dropdown", - "userLogin": "KevLehman", + "pr": "24405", + "title": "[IMPROVE] Add tooltip to sidebar room menu", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "KevLehman", - "tiagoevanp", - "web-flow" + "Himanshu664", + "web-flow", + "dougfabris" ] }, { - "pr": "24019", - "title": "Regression: addAction verification breaking rooms", - "userLogin": "dougfabris", + "pr": "24431", + "title": "[IMPROVE] Added tooltip options for message menu", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ + "Himanshu664", "dougfabris" ] }, { - "pr": "24009", - "title": "Regression: Let Meteor.absoluteUrl.defaultOptions.rootUrl as baseURI", - "userLogin": "tassoevan", - "milestone": "4.3.0", + "pr": "24166", + "title": "[FIX] Replace encrypted text to Encrypted Message Placeholder", + "userLogin": "yash-rajpal", + "description": "### before \r\n![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png)\r\n\r\n### after\r\n\"Screenshot", + "milestone": "4.7.0", "contributors": [ - "tassoevan" + "yash-rajpal", + "albuquerquefabio", + "web-flow" ] }, { - "pr": "24012", - "title": "Regression: Add migration for omni rooms with no source", - "userLogin": "murtaza98", - "description": "Add a migration to add source property to all the omnichannel rooms which don't have it yet. All these rooms will have source type as `other`", + "pr": "24984", + "title": "[FIX] Prevent sequential messages edited icon to hide on hover", + "userLogin": "dougfabris", + "description": "### before\r\n\"Screen\r\n\r\n### after\r\n\"Screen", + "milestone": "4.7.0", "contributors": [ - "murtaza98" + "dougfabris" ] }, { - "pr": "24008", - "title": "Regression: Fix omnichannel empty source usage", - "userLogin": "ggazzo", + "pr": "25024", + "title": "[IMPROVE] Improve active/hover colors in account sidebar", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "ggazzo" + "Himanshu664" ] }, { - "pr": "23924", - "title": "[IMPROVE] Webdav methods sanitization", - "userLogin": "dougfabris", - "description": "The improvement modify `server_url` and `user_id` params into `serverURL` and `userId` more suitable to our camelCase pattern. Also converts the webdav methods into .ts helping us to prevent issues in the next modal rewrites efforts.", + "pr": "24856", + "title": "[FIX] Full error message is visible", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "dougfabris" + "Himanshu664", + "tassoevan" ] }, { - "pr": "23875", - "title": "Chore: update docker image base to latest node 12 patch", + "pr": "24708", + "title": "Chore: Cancel running jobs if PR is updated", "userLogin": "debdutdeb", "contributors": [ "debdutdeb", @@ -82215,2730 +82334,7163 @@ ] }, { - "pr": "24007", - "title": "[IMPROVE] Replace SortListItem and CreateListItem with ListItem", - "userLogin": "juliajforesti", + "pr": "24900", + "title": "Chore: organize test files and fix code coverage", + "userLogin": "tmontini", "contributors": [ - "juliajforesti", - "ggazzo" + null, + "tmontini", + "rodrigok" ] }, { - "pr": "23912", - "title": "[NEW] Show Omnichannel room icon based on source definition", - "userLogin": "AllanPazRibeiro", - "milestone": "4.3.0", + "pr": "24464", + "title": "Chore: Missing keys in APIsDisplay", + "userLogin": "dougfabris", + "milestone": "4.7.0", "contributors": [ - "AllanPazRibeiro", - "ggazzo", - "d-gubert", - "web-flow" + "dougfabris", + "tassoevan" ] }, { - "pr": "23925", - "title": "[NEW][APPS] Add new email event for apps", - "userLogin": "d-gubert", - "description": "Introduces a new event called before an email is sent by the Mailer. Apps can intercept and modify the email that will be sent, or even prevent it from being sent altogether. For more details, check https://github.com/RocketChat/Rocket.Chat.Apps-engine/pull/461/files#diff-301e8a58164edbf315da2a43c4923f153dbc909573de1e60aa9f730f7488ac82", - "milestone": "4.3.0", + "pr": "25057", + "title": "Bump ejson from 2.2.1 to 2.2.2", + "userLogin": "dependabot[bot]", "contributors": [ - "d-gubert" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "19640", - "title": "[FIX] Changes on department agents should mark form as dirty", - "userLogin": "rafaelblink", - "milestone": "4.3.0", + "pr": "25053", + "title": "Chore: Remove Alpine image deps after using them", + "userLogin": "sampaiodiego", "contributors": [ - "rafaelblink", - "web-flow", - "gabriellsh", - "renatobecker", - "dougfabris" + "sampaiodiego" ] }, { - "pr": "23904", - "title": "[FIX] Jitsi call already ended", - "userLogin": "Aman-Maheshwari", - "description": "- Fix Jitsi timeout update -- which caused the \"Jitsi call already ended\" error when trying to join a call some time after its creation;", + "pr": "25052", + "title": "Bump pino and pino-pretty", + "userLogin": "sampaiodiego", "contributors": [ - "Aman-Maheshwari", - "yash-rajpal" + "sampaiodiego" ] }, { - "pr": "23939", - "title": "[NEW][EE] Introduce fallback department support", - "userLogin": "KevLehman", - "milestone": "4.3.0", + "pr": "25050", + "title": "[FIX] Upgrade Tab showing for a split second", + "userLogin": "gabriellsh", + "milestone": "4.6.1", "contributors": [ - "KevLehman" + "gabriellsh" ] }, { - "pr": "23959", - "title": "[FIX] teams.leave client usage", + "pr": "25055", + "title": "[FIX] UserAutoComplete not rendering UserAvatar correctly", "userLogin": "dougfabris", - "milestone": "4.3.0", + "description": "### before\r\n![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png)\r\n\r\n### after\r\n![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png)", + "milestone": "4.6.1", "contributors": [ "dougfabris" ] }, { - "pr": "23679", - "title": "[NEW][APPS] Allow Rocket.Chat Apps to register custom action buttons", - "userLogin": "d-gubert", - "description": "Add an action button manager that allows apps to register custom action buttons that trigger interaction callbacks in them", - "milestone": "4.3.0", - "contributors": [ - "d-gubert", + "pr": "25031", + "title": "Chore: TS conversion folder client", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", "web-flow" ] }, { - "pr": "23995", - "title": "[FIX] Prevent the app from crashing when you look at the notification preferences of a room in which you are not a member", - "userLogin": "dougfabris", - "description": "Before, when you look at the notification rules of a room in which you are not a member the app crashed, i corrected this problem.\r\nIndeed, there was a not check if the subscription was undefined", - "milestone": "4.3.0", + "pr": "24991", + "title": "Bump minimist from 1.2.5 to 1.2.6 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "dougfabris" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23843", - "title": "[NEW][APPS] Allow apps to open contextual bar", - "userLogin": "thassiov", - "description": "Opens a contextual bar using app ui interactions (`CONTEXTUAL_BAR_OPEN`)\r\n\r\nhttps://user-images.githubusercontent.com/733282/146704076-d2d115f2-6ca6-4ed0-b450-81be580889a4.mp4", - "milestone": "4.3.0", + "pr": "25002", + "title": "Bump template-file from 6.0.0 to 6.0.1", + "userLogin": "dependabot[bot]", "contributors": [ - "thassiov" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23786", - "title": "Chore: Enable prefer-optional-chain ESLint rule for TypeScript files", - "userLogin": "tassoevan", - "description": "> Code is bad. It rots. It requires periodic maintenance. It has bugs that need to be found. New features mean old code has to be adapted.\r\n> The more code you have, the more places there are for bugs to hide. The longer checkouts or compiles take. The longer it takes a new employee to make sense of your system. If you have to refactor there's more stuff to move around.\r\n> Furthermore, more code often means less flexibility and functionality. This is counter-intuitive, but a lot of times a simple, elegant solution is faster and more general than the plodding mess of code produced by a programmer of lesser talent.\r\n> Code is produced by engineers. To make more code requires more engineers. Engineers have n^2 communication costs, and all that code they add to the system, while expanding its capability, also increases a whole basket of costs.\r\n> You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs.\r\n\r\n— [Rich Skrenta][1]\r\n\r\nMixing two problem domains in code is prone to errors. In this small example\r\n\r\n```ts\r\ndeclare const y: { z: unknown } | undefined;\r\n\r\nconst x = y && y.z;\r\n```\r\n\r\nwe're (1) checking the nullity of `y` and (2) attributing `y.z` to `x`, where (2) is _clearly_ the main problem we're solving with code. The optional chaining is a good technique to handle nullity as a mere implementation detail:\r\n\r\n```ts\r\ndeclare const y: { z: unknown } | undefined;\r\n\r\nconst x = y?.z;\r\n```\r\n\r\nAttributing `y.z` to `x` is more easily readable than the nullity check of `y`.\r\n\r\nThis PR aims to add `@typescript-eslint/prefer-optional-chain` rule to ESlint configuration at warning level.", + "pr": "25042", + "title": "Bump body-parser from 1.19.2 to 1.20.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "tassoevan", - "sampaiodiego", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23996", - "title": "Chore: Remove the `mobile-download-file` permission", - "userLogin": "matheusbsilva137", - "description": "- Remove the `mobile-download-file` permission and its descriptions.", + "pr": "25043", + "title": "i18n: Language update from LingoHub 🤖 on 2022-04-04Z", + "userLogin": "lingohub[bot]", "contributors": [ - "matheusbsilva137" + null ] }, { - "pr": "23823", - "title": "[FIX] LDAP Sync doing nothing when set to only import new users.", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.3.0", + "pr": "25028", + "title": "Merge master into develop & Set version to 4.7.0-develop", + "userLogin": "sampaiodiego", "contributors": [ - "pierre-lehnen-rc" + "AllanPazRibeiro", + "sampaiodiego", + "web-flow" ] }, { - "pr": "23816", - "title": "Chore: Centralize email validation functionality", - "userLogin": "KevLehman", - "description": "- Create lib for validating emails\r\n- Modify places that validate emails to use the new central function", + "pr": "25027", + "title": "Release 4.6.0", + "userLogin": "sampaiodiego", "contributors": [ + "pierre-lehnen-rc", + "aswinidev", + "web-flow", + "renatobecker", + "sampaiodiego", + "dependabot[bot]", + "lingohub[bot]", + "matheusbsilva137", + "amolghode1981", + "debdutdeb", + "eduardofcabrera", + "juliajforesti", + "tiagoevanp", "KevLehman" ] }, { - "pr": "23972", - "title": "[NEW][APPS] getUserUnreadMessageCount Bridge", - "userLogin": "ggazzo", + "pr": "25021", + "title": "Bump @rocket.chat/emitter from 0.31.4 to 0.31.9 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ggazzo", - "d-gubert" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23993", - "title": "Chore: Deleted LivechatPageVisited", - "userLogin": "ostjen", + "pr": "25020", + "title": "Bump @rocket.chat/ui-kit from 0.31.4 to 0.31.9 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ostjen" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23987", - "title": "[FIX] Broken links present in some languages", - "userLogin": "aswinidev", + "pr": "25019", + "title": "Bump @rocket.chat/message-parser from 0.31.4 to 0.31.9 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "aswinidev" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23846", - "title": "Chore: added last login to users.list", - "userLogin": "ostjen", + "pr": "25018", + "title": "Bump @rocket.chat/string-helpers from 0.31.4 to 0.31.9 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ostjen" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23574", - "title": "[FIX] Email notifications settings not being honored on new DMs", - "userLogin": "ostjen", + "pr": "25017", + "title": "Regression: Add createdOTR index", + "userLogin": "sampaiodiego", "contributors": [ - "ostjen", - "web-flow", "sampaiodiego" ] }, { - "pr": "23805", - "title": "[FIX] Headers already sent error when user data download is disabled", + "pr": "24998", + "title": "Release 4.5.5", "userLogin": "sampaiodiego", - "description": "When using the export message tool when trying to download the file using the link sent via email if the feature \"Export User Data\" is disabled an error was being thrown causing the request to halt.\r\n\r\nThis is the error shown in the logs:\r\n```\r\n=== UnHandledPromiseRejection ===\r\nError [ERR_HTTP_HEADERS_SENT] [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client\r\n at ServerResponse.setHeader (_http_outgoing.js:530:11)\r\n at ServerResponse.res.setHeader (/app/bundle/programs/server/npm/node_modules/meteor/simple_json-routes/node_modules/connect/lib/patch.js:134:22)\r\n at app/user-data-download/server/exportDownload.js:14:7\r\n at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40 {\r\n code: 'ERR_HTTP_HEADERS_SENT'\r\n}\r\n---------------------------------\r\nErrors like this can cause oplog processing errors.\r\nSetting EXIT_UNHANDLEDPROMISEREJECTION will cause the process to exit allowing your service to automatically restart the process\r\nFuture node.js versions will automatically exit the process\r\n=================================\r\n```", "contributors": [ - "sampaiodiego" + "MartinSchoeler", + "sampaiodiego", + "filipemarins", + "tiagoevanp" ] }, { - "pr": "23991", - "title": "i18n: Language update from LingoHub 🤖 on 2021-12-20Z", - "userLogin": "lingohub[bot]", + "pr": "24994", + "title": "[FIX] High CPU usage caused by CallProvider", + "userLogin": "tiagoevanp", + "description": "Remove infinity loop inside useVoipClient hook.\r\n\r\n#closes #24970", + "milestone": "4.5.5", "contributors": [ - null + "ggazzo", + "tiagoevanp" ] }, { - "pr": "23901", - "title": "[FIX] broken `Word Placement Anywhere` and `Run on edits` toggles in integration page", - "userLogin": "aswinidev", - "milestone": "4.3.0", + "pr": "24955", + "title": "[FIX] Multiple issues starting a new DM", + "userLogin": "filipemarins", + "description": "When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details.\r\n\r\nbefore:\r\nhttps://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4\r\n\r\nafter:\r\nhttps://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4", + "milestone": "4.5.5", "contributors": [ - "aswinidev" + "filipemarins", + "pierre-lehnen-rc" ] }, { - "pr": "23973", - "title": "[FIX] OTR not working", - "userLogin": "gabriellsh", - "description": "A rule on the user notification streamer was changed recently, and the check for writing on the streamer was wrong. Changed it to allow all logged users.", - "milestone": "4.3.0", + "pr": "24990", + "title": "Chore: Update Livechat", + "userLogin": "MartinSchoeler", + "milestone": "4.5.5", "contributors": [ - "gabriellsh" + "MartinSchoeler" ] }, { - "pr": "23833", - "title": "[FIX] Add missing .png to clipboard uploaded file name", - "userLogin": "dougfabris", - "milestone": "4.3.0", + "pr": "24938", + "title": "Release 4.5.4", + "userLogin": "AllanPazRibeiro", "contributors": [ - "dougfabris", - "gabriellsh" + "geekgonecrazy", + "AllanPazRibeiro" ] }, { - "pr": "23974", - "title": "Chore: Use only LivechatTriggerRaw model", - "userLogin": "sampaiodiego", + "pr": "24930", + "title": "[FIX] SAML Force name to string", + "userLogin": "geekgonecrazy", + "milestone": "4.5.4", "contributors": [ - "sampaiodiego" + "geekgonecrazy", + "web-flow", + "pierre-lehnen-rc" ] }, { - "pr": "23948", - "title": "[FIX] Missing edit icon in sequential thread messages", + "pr": "25015", + "title": "Chore: Bump Fuselage packages", "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/146083450-ca6d7197-dc55-4058-8212-943b42c82473.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/146083055-36c9731a-33c6-483a-93a5-1355d8689e3a.png)", - "milestone": "4.3.0", + "description": "It uses the last stable version of Fuselage packages.", + "milestone": "4.6.0", "contributors": [ - "dougfabris" + "dougfabris", + "tassoevan" ] }, { - "pr": "23910", - "title": "[FIX] Removing Edit message from messageBox on room changed", - "userLogin": "yash-rajpal", - "description": "Removing edit message from messageBox and local storage on messageBox destroyed.", - "milestone": "4.3.0", + "pr": "24999", + "title": "Regression: Custom roles displaying ID instead of name on some admin screens", + "userLogin": "pierre-lehnen-rc", + "description": "![image](https://user-images.githubusercontent.com/55164754/160981416-555bcaa1-c075-4260-937c-64523472da43.png)\r\n![image](https://user-images.githubusercontent.com/55164754/160981452-6eae4e74-8425-4073-8256-472aba72b9db.png)", + "milestone": "4.6.0", "contributors": [ - "yash-rajpal" + "pierre-lehnen-rc", + "dougfabris", + "web-flow" ] }, { - "pr": "23945", - "title": "[IMPROVE] Allow e-mail channel to be used without default department.", - "userLogin": "cauefcr", - "description": "Due to a missing condition in the e-mail input processing, Rocket.Chat was unable to receive e-mails from e-mail channels that did not have a default department.", + "pr": "24835", + "title": "[NEW] Upgrade Tab", + "userLogin": "gabriellsh", + "description": "![image](https://user-images.githubusercontent.com/27704687/160172260-c656282e-a487-4092-948d-d11c9bacb598.png)", + "milestone": "4.6.0", "contributors": [ - "cauefcr", - "murtaza98", - "web-flow" + "gabriellsh", + "dougfabris", + "web-flow", + "tassoevan", + "pierre-lehnen-rc" ] }, { - "pr": "23466", - "title": "Bump mailparser from 3.2.0 to 3.4.0", - "userLogin": "dependabot[bot]", + "pr": "24980", + "title": "Regression: Error is raised when there's no Asterisk queue available yet", + "userLogin": "amolghode1981", + "milestone": "4.6.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "amolghode1981" ] }, { - "pr": "23853", - "title": "Bump thehanimo/pr-title-checker from 1.2 to 1.3.4", - "userLogin": "dependabot[bot]", + "pr": "24994", + "title": "[FIX] High CPU usage caused by CallProvider", + "userLogin": "tiagoevanp", + "description": "Remove infinity loop inside useVoipClient hook.\r\n\r\n#closes #24970", + "milestone": "4.5.5", "contributors": [ - "dependabot[bot]", - "web-flow", - "sampaiodiego" + "ggazzo", + "tiagoevanp" ] }, { - "pr": "23882", - "title": "[FIX] Custom emoji route in admin", - "userLogin": "sidmohanty11", - "description": "https://user-images.githubusercontent.com/73601258/144975689-912cfd73-da16-433c-899a-4d4ffac8e146.mp4", - "milestone": "4.3.0", - "contributors": [ - "sidmohanty11", - "dougfabris" + "pr": "24955", + "title": "[FIX] Multiple issues starting a new DM", + "userLogin": "filipemarins", + "description": "When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details.\r\n\r\nbefore:\r\nhttps://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4\r\n\r\nafter:\r\nhttps://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4", + "milestone": "4.5.5", + "contributors": [ + "filipemarins", + "pierre-lehnen-rc" ] }, { - "pr": "23888", - "title": "[FIX] Popover position for arabic languages", - "userLogin": "yash-rajpal", - "milestone": "4.3.0", + "pr": "24969", + "title": "Chore: Storybook mocking and examples improved", + "userLogin": "tassoevan", + "description": "- Stories from `ee/` included;\r\n- Differentiate root story kinds;\r\n- Mocking of `ServerContext` via Storybook parameters.", "contributors": [ - "yash-rajpal" + "tassoevan" ] }, { - "pr": "23347", - "title": "[FIX] creating room with federated member", - "userLogin": "qwertiko", - "milestone": "4.2.2", + "pr": "24990", + "title": "Chore: Update Livechat", + "userLogin": "MartinSchoeler", + "milestone": "4.5.5", "contributors": [ - "qwertiko", - "web-flow" + "MartinSchoeler" ] }, { - "pr": "23930", - "title": "i18n: Language update from LingoHub 🤖 on 2021-12-13Z", - "userLogin": "lingohub[bot]", + "pr": "24989", + "title": "Revert: [NEW] Engagement Statistics", + "userLogin": "sampaiodiego", "contributors": [ - null + "sampaiodiego", + "web-flow" ] }, { - "pr": "21025", - "title": "[NEW][APPS] Possibility to set room closer via Apps LivechatBridge.closeRoom", - "userLogin": "cuonghuunguyen", - "description": "Add an optional param named `closer` into `LivechatBridge.closeRoom` so that it will be possible to close the room and send a close room message with the correct room closer.\r\nIf the param is not passed, use the room visitor as the room closer.", + "pr": "24897", + "title": "[FIX] Room archived/unarchived system messages aren't sent when editing room settings", + "userLogin": "matheusbsilva137", + "description": "- Send the \"Room archived\" and \"Room unarchived\" system messages when editing room settings (and not only when rooms are archived/unarchived with the slash-command);\r\n- Fix the \"Hide System Messages\" option for the \"Room archived\" and \"Room unarchived\" system messages;", "contributors": [ - null, - "d-gubert" + "matheusbsilva137" ] }, { - "pr": "23860", - "title": "[FIX] Custom status doesn't update properly", - "userLogin": "dougfabris", + "pr": "24925", + "title": "Chore: add some missing REST definitions", + "userLogin": "gerzonc", + "description": "On the [mobile client](https://github.com/RocketChat/Rocket.Chat.ReactNative), we made an effort to collect more `REST API` definitions that are missing on the server side during our migration to TypeScript. Since we're both migrating to TypeScript, we thought it would be a good idea to share those so you guys can benefit from our initiative.", "contributors": [ - "dougfabris", - "gabriellsh" + "gerzonc" ] }, { - "pr": "23921", - "title": "Bump cookie-parser from 1.4.5 to 1.4.6 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "24971", + "title": "i18n: Language update from LingoHub 🤖 on 2022-03-28Z", + "userLogin": "lingohub[bot]", "contributors": [ - "dependabot[bot]", - "web-flow" + null ] }, { - "pr": "23914", - "title": "Chore: Fix hasRole warning", - "userLogin": "sampaiodiego", + "pr": "24921", + "title": "[FIX] Register with Secret URL", + "userLogin": "yash-rajpal", "contributors": [ - "sampaiodiego" + "yash-rajpal" ] }, { - "pr": "23922", - "title": "Chore: Update pino deps", + "pr": "24948", + "title": "Regression: Fix unexpected errors breaking ddp-streamer", "userLogin": "sampaiodiego", "contributors": [ "sampaiodiego" ] }, { - "pr": "23689", - "title": "Bump path-parse from 1.0.6 to 1.0.7", - "userLogin": "dependabot[bot]", - "contributors": [ - "dependabot[bot]", - "web-flow" - ] - }, - { - "pr": "23526", - "title": "Bump @rocket.chat/string-helpers from 0.29.0 to 0.30.1 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "24320", + "title": "[FIX] LDAP avatars being rotated according to metadata even if the setting to rotate uploads is off", + "userLogin": "matheusbsilva137", + "description": "- Use the `FileUpload_RotateImages` setting (**Administration > File Upload > Rotate images on upload**) to control whether avatars should be rotated automatically based on their data (XEIF);\r\n- Display the avatar image preview (orientation) according to the `FileUpload_RotateImages` setting.", + "milestone": "4.6.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "matheusbsilva137", + "web-flow", + "pierre-lehnen-rc" ] }, { - "pr": "23289", - "title": "Bump pm2 from 5.1.1 to 5.1.2 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "24930", + "title": "[FIX] SAML Force name to string", + "userLogin": "geekgonecrazy", + "milestone": "4.5.4", "contributors": [ - "dependabot[bot]", - "web-flow" + "geekgonecrazy", + "web-flow", + "pierre-lehnen-rc" ] }, { - "pr": "23913", - "title": "Chore: Update Livechat", + "pr": "24908", + "title": "Regression: Call doesn't stop ringing after agent unregistration", "userLogin": "MartinSchoeler", - "milestone": "4.2.1", + "milestone": "4.6.0", "contributors": [ "MartinSchoeler" ] }, { - "pr": "23908", - "title": "[FIX] Translations for App Select Settings not working", - "userLogin": "murtaza98", - "description": "Derived from PR https://github.com/RocketChat/Rocket.Chat/pull/19238", + "pr": "24777", + "title": "[NEW] Engagement Statistics", + "userLogin": "eduardofcabrera", "contributors": [ - "murtaza98" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23848", - "title": "[FIX] DMs being created with username instead of user's name", - "userLogin": "gabriellsh", - "milestone": "4.3.0", + "pr": "24920", + "title": "Regression: Fix account service login expiration", + "userLogin": "sampaiodiego", "contributors": [ - "gabriellsh" + "sampaiodiego" ] }, { - "pr": "23879", - "title": "[IMPROVE] Update \"Message Erasure Type\" setting's description", - "userLogin": "matheusbsilva137", - "description": "- Improves the \"Message Erasure Type\" setting's description by providing more details regarding the expected behavior of each option (\"Keep Messages and User Name\", \"Delete All Messages\" and \"Remove link between user and messages\");\r\n- Remove outdated translations (for this setting's description).", + "pr": "24867", + "title": "[FIX] Duplicated \"jump to message\" button on starred messages", + "userLogin": "Himanshu664", "contributors": [ - "matheusbsilva137" + "Himanshu664" ] }, { - "pr": "23796", - "title": "[FIX] Segmentation fault on CentOS 7 due to outdated `sharp`", - "userLogin": "tassoevan", - "description": "Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`.\r\n\r\nSuggested as a fix for versions `4.0.x` and `4.1.x`.", - "milestone": "4.2.1", + "pr": "24860", + "title": "[FIX] External search providers not working", + "userLogin": "tkurz", "contributors": [ - "tassoevan" + "tkurz" ] }, { - "pr": "23819", - "title": "[IMPROVE] Omnichannel Visitor Endpoints error handling", - "userLogin": "KevLehman", + "pr": "24052", + "title": "[FIX] Several issues related to custom roles", + "userLogin": "pierre-lehnen-rc", + "description": "- Throw an error when trying to delete a role (User or Subscription role) that are still being used;\r\n- Fix \"Invalid Role\" error for custom roles in Role Editing sidebar;\r\n- Fix \"Users in Role\" screen for custom roles.", + "milestone": "4.6.0", "contributors": [ - "KevLehman", + "pierre-lehnen-rc", + "matheusbsilva137", "web-flow" ] }, { - "pr": "23857", - "title": "[FIX] teams.removeMembers client usage", - "userLogin": "dougfabris", - "milestone": "4.2.1", + "pr": "24781", + "title": "[NEW] Telemetry Events", + "userLogin": "eduardofcabrera", "contributors": [ - "ggazzo", - "dougfabris" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23862", - "title": "Regression: Toolbox render item", - "userLogin": "dougfabris", + "pr": "24887", + "title": "[IMPROVE] Adding new statistics related to voip and omnichannel", + "userLogin": "cauefcr", + "description": "- Total of Canned response messages sent\r\n- Total of tags used\r\n- Last-Chatted Agent Preferred (enabled/disabled)\r\n- Assign new conversations to the contact manager (enabled/disabled)\r\n- How to handle Visitor Abandonment setting\r\n- Amount of chats placed on hold\r\n- VoIP Enabled\r\n- Amount of VoIP Calls\r\n- Amount of VoIP Extensions connected\r\n- Amount of Calls placed on hold (1x per call)\r\n- Fixed Session Aggregation type definitions", + "milestone": "4.6.0", "contributors": [ - "dougfabris" + "cauefcr", + "KevLehman" ] }, { - "pr": "23558", - "title": "[FIX] Fix no message size limit for method sendMessageLivechat", - "userLogin": "cuonghuunguyen", + "pr": "24911", + "title": "Chore: Remove old scripts", + "userLogin": "sampaiodiego", "contributors": [ - null + "sampaiodiego" ] }, { - "pr": "23791", - "title": "[FIX] Modal keeps state if reset too fast.", + "pr": "24898", + "title": "[FIX] DDP Rate Limiter Translation key", "userLogin": "gabriellsh", - "description": "~Queued updates so the Modal has a chance to close.~\r\nUsed a random key to ensure modal doesn't keep it's state.", - "milestone": "4.3.0", + "description": "Before:\r\n\"image\"\r\n\r\n\r\nNow:\r\n\"image\"", "contributors": [ "gabriellsh" ] }, { - "pr": "23877", - "title": "Regression: Missing padding in popover with custom template", - "userLogin": "dougfabris", - "description": "![Screen Shot 2021-12-06 at 14 16 40](https://user-images.githubusercontent.com/27704687/144891474-a5bf982e-56af-46df-b472-adf9d999ce02.png)", + "pr": "24831", + "title": "[FIX][ENTERPRISE] Notifications not being sent by ddp-streamer", + "userLogin": "sampaiodiego", "contributors": [ - "dougfabris" + "sampaiodiego" ] }, { - "pr": "23873", - "title": "i18n: Language update from LingoHub 🤖 on 2021-12-06Z", - "userLogin": "lingohub[bot]", + "pr": "24884", + "title": "Release 4.5.3", + "userLogin": "AllanPazRibeiro", "contributors": [ - null, - "sampaiodiego" + "tiagoevanp", + "sampaiodiego", + "KevLehman", + "amolghode1981", + "ggazzo" ] }, { - "pr": "23861", - "title": "[FIX] Wrong button for non trial apps", - "userLogin": "rique223", - "description": "This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. \r\nBefore:\r\n![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) \r\nAfter:\r\n![image](https://user-images.githubusercontent.com/43561537/144687924-1a3eb3a7-783f-4450-abd2-1efa0de64658.png)", - "milestone": "4.2.1", + "pr": "24901", + "title": "[FIX] Custom script not being fired", + "userLogin": "ggazzo", + "milestone": "4.5.3", "contributors": [ - "rique223" + "ggazzo" ] }, { - "pr": "23859", - "title": "[FIX] Error when creating an inactive user in admin panel", - "userLogin": "matheusbsilva137", - "description": "- Fix `usersInRole` array used to send email to activate a user.", - "milestone": "4.2.1", + "pr": "24877", + "title": "Chore: Fix MongoDB versions on release notes", + "userLogin": "sampaiodiego", + "milestone": "4.5.3", "contributors": [ - "matheusbsilva137" + "sampaiodiego", + "web-flow" ] }, { - "pr": "23822", - "title": "Chore: Create script to add new migrations", + "pr": "24864", + "title": "[FIX] Disable voip button when call is in progress", "userLogin": "KevLehman", - "description": "- Create NPM script to add new migrations\r\n- TODO: Infer next migration number from file list", + "milestone": "4.5.3", "contributors": [ "KevLehman" ] }, { - "pr": "23712", - "title": "[FIX] No hover effect for items in kebab menu.", - "userLogin": "Aman-Maheshwari", - "milestone": "4.3.0", + "pr": "24863", + "title": "[FIX] Broken build caused by PRs modifying same file differently", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "Aman-Maheshwari", - "dougfabris" + "KevLehman", + "tiagoevanp" ] }, { - "pr": "23821", - "title": "[NEW] Create new setting to clean local storage at end of chats", + "pr": "24838", + "title": "[FIX] [VOIP] SidebarFooter component ", "userLogin": "tiagoevanp", - "description": "Include setting to handle with and clear the localStorage on Livechat widget. \r\n\r\n![image](https://user-images.githubusercontent.com/17487063/144171179-95f7cf41-0192-4532-bedf-99e0b01f2c61.png)\r\n\r\nThis behavior is only possible to happen when https://github.com/RocketChat/Rocket.Chat.Livechat/pull/666 merged and released.", - "milestone": "4.2.1", + "description": "- Improve the CallProvider code;\r\n- Adjust the text case of the VoIP component on the FooterSidebar;\r\n- Fix the bad behavior with the changes in queue's name.", + "milestone": "4.5.3", "contributors": [ "tiagoevanp" ] }, { - "pr": "23215", - "title": "[FIX] Add CSP to authorize auto-close of CAS login window", - "userLogin": "goyome", - "description": "Add the hash of the JS inside the page that won't close ( window.close(); )", - "milestone": "4.1.0", - "contributors": [ - "goyome", - "pierre-lehnen-rc", - "web-flow" - ] - }, - { - "pr": "23831", - "title": "[FIX] Missing custom user status ellipsis", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/144270229-baca14f5-e168-42b7-86d1-e7217be561a9.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/144274255-39216e69-8283-45c5-8a77-b835d284f655.png)", + "pr": "24837", + "title": "[IMPROVE] Standarize queue behavior for managers and agents when subscribing", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "dougfabris" + "KevLehman" ] }, { - "pr": "23781", - "title": "[IMPROVE] Rewrite remove room invite modal", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/143086855-1904dbf3-fb32-4318-b744-95390d68ada2.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/143086365-d96fc9d2-12c6-4123-96a6-d8fd91cdf93a.png)", - "milestone": "4.3.0", + "pr": "24829", + "title": "[FIX] Show only enabled departments on forward", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "dougfabris", - "gabriellsh" + "KevLehman" ] }, { - "pr": "23839", - "title": "Chore: Change Menu props to accept next fuselage version", - "userLogin": "ggazzo", + "pr": "24799", + "title": "[FIX] Wrong param usage on queue summary call", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "ggazzo" + "KevLehman" ] }, { - "pr": "23756", - "title": "Chore: Replace new typography", - "userLogin": "juliajforesti", + "pr": "24789", + "title": "[FIX] VoIP button gets disabled whenever user status changes", + "userLogin": "amolghode1981", + "milestone": "4.5.3", "contributors": [ - "juliajforesti", - "ggazzo" + "amolghode1981" ] }, { - "pr": "23690", - "title": "[FIX] error after properly deleting user from admin panel", - "userLogin": "Aman-Maheshwari", - "milestone": "4.3.0", + "pr": "24752", + "title": "[FIX] Show call icon only when user has extension associated", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "Aman-Maheshwari", - "dougfabris" + "KevLehman" ] }, { - "pr": "23827", - "title": "Merge master into develop & Set version to 4.3.0-develop", - "userLogin": "sampaiodiego", + "pr": "24748", + "title": "[IMPROVE] UX - VoIP Call Component", + "userLogin": "tiagoevanp", + "milestone": "4.5.3", "contributors": [ - "sampaiodiego", - "web-flow" + "tiagoevanp" ] }, { - "pr": "23825", - "title": "Release 4.2.0", - "userLogin": "sampaiodiego", + "pr": "24814", + "title": "Release 4.5.2", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "sampaiodiego", - "web-flow", - "rique223", - "matheusbsilva137", + "MartinSchoeler", + "pierre-lehnen-rc", "tassoevan", + "debdutdeb", "KevLehman", - "lingohub[bot]", - "d-gubert", - "dudanogueira", - "ostjen", - "pierre-lehnen-rc", - "renatobecker", - "cauefcr", - "ggazzo", - "rodrigok", - "tiagoevanp", - "bhardwajaditya", - "Aman-Maheshwari", - "dougfabris" + "murtaza98", + "sampaiodiego", + "juliajforesti" ] }, { - "pr": "23774", - "title": "Regression: Add trash to raw models", - "userLogin": "sampaiodiego", - "milestone": "4.2.0", + "pr": "24812", + "title": "[FIX] Revert AutoComplete", + "userLogin": "juliajforesti", + "milestone": "4.5.2", "contributors": [ - "sampaiodiego", + "juliajforesti", "ggazzo" ] }, { - "pr": "23820", - "title": "[FIX] LDAP users being disabled when an AD security policy is enabled", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.2.0", + "pr": "24809", + "title": "Regression: Fix ParentRoomWithEndpointData in loop", + "userLogin": "sampaiodiego", + "milestone": "4.5.2", "contributors": [ - "pierre-lehnen-rc" + "sampaiodiego" ] }, { - "pr": "23815", - "title": "Regression: \"When is the chat busier\" and \"Users by time of day\" charts are not working", - "userLogin": "matheusbsilva137", - "description": "- Fix \"When is the chat busier\" (Hours) and \"Users by time of day\" charts, which weren't displaying any data;", - "milestone": "4.2.0", + "pr": "24732", + "title": "[FIX] `PaginatedSelectFiltered` not handling changes", + "userLogin": "tassoevan", + "milestone": "4.5.2", "contributors": [ - "murtaza98", - "matheusbsilva137", - "web-flow" + "tassoevan" ] }, { - "pr": "23812", - "title": "i18n: Language update from LingoHub 🤖 on 2021-11-29Z", - "userLogin": "lingohub[bot]", - "milestone": "4.2.0", + "pr": "24805", + "title": "[FIX] Critical: Incorrect visitor getting assigned to a chat from apps", + "userLogin": "murtaza98", + "milestone": "4.5.2", "contributors": [ - null, - "sampaiodiego" + "murtaza98" ] }, { - "pr": "23813", - "title": "Regression: Mark Livechat WebRTC video calling as alpha", + "pr": "24804", + "title": "[FIX] \"livechat/webrtc.call\" endpoint not working", "userLogin": "murtaza98", - "description": "![image](https://user-images.githubusercontent.com/34130764/143832378-82b99a72-23e8-4115-8b28-a0d210de598b.png)", - "milestone": "4.2.0", + "milestone": "4.5.2", "contributors": [ "murtaza98" ] }, { - "pr": "23803", - "title": "Regression: Current Chats not Filtering", - "userLogin": "MartinSchoeler", - "milestone": "4.2.0", + "pr": "24792", + "title": "[FIX] VoipExtensionsPage component call", + "userLogin": "KevLehman", + "milestone": "4.5.2", "contributors": [ - "MartinSchoeler" + "KevLehman" ] }, { - "pr": "23802", - "title": "Regression: Add @rocket.chat/emitter to EE services", - "userLogin": "sampaiodiego", + "pr": "24705", + "title": "[FIX] Broken multiple OAuth integrations", + "userLogin": "debdutdeb", + "milestone": "4.5.2", "contributors": [ - "sampaiodiego" + "debdutdeb" ] }, { - "pr": "23793", - "title": "Regression: Include files on EE services build", - "userLogin": "sampaiodiego", + "pr": "24623", + "title": "[FIX] Opening a new DM from user card", + "userLogin": "tassoevan", + "description": "A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component.", + "milestone": "4.5.2", "contributors": [ - "sampaiodiego" + "tassoevan" ] }, { - "pr": "23789", - "title": "Regression: Fix sort param on omnichannel endpoints", - "userLogin": "KevLehman", + "pr": "24750", + "title": "[IMPROVE] Voip Extensions disabled state", + "userLogin": "MartinSchoeler", + "milestone": "4.5.2", "contributors": [ - "KevLehman" + "MartinSchoeler" ] }, { - "pr": "23778", - "title": "Regression: Fix incorrect API path for livechat calls", - "userLogin": "murtaza98", - "milestone": "4.2.0", + "pr": "24782", + "title": "Release 4.5.1", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "murtaza98" + "renatobecker", + "pierre-lehnen-rc", + "sampaiodiego", + "matheusbsilva137", + "amolghode1981", + "juliajforesti", + "tiagoevanp", + "KevLehman", + "MartinSchoeler", + "Aman-Maheshwari", + "cuonghuunguyen" ] }, { - "pr": "23775", - "title": "Regression: Fix LDAP sync route", - "userLogin": "ggazzo", + "pr": "24760", + "title": "[FIX] Apple login script being loaded even when Apple Login is disabled.", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.5.1", "contributors": [ - "ggazzo", - "sampaiodiego" + "pierre-lehnen-rc" ] }, { - "pr": "23769", - "title": "Chore: Update settings.ts", - "userLogin": "ggazzo", + "pr": "24754", + "title": "Chore: Update Livechat", + "userLogin": "MartinSchoeler", + "milestone": "4.5.1", "contributors": [ - "ggazzo", - "web-flow", - "sampaiodiego" + "MartinSchoeler" ] }, { - "pr": "23565", - "title": "[FIX] Registration not possible when any user is blocked for multiple failed logins", - "userLogin": "ostjen", + "pr": "24683", + "title": "[FIX] no id of room closer in livechat-close message", + "userLogin": "cuonghuunguyen", + "milestone": "4.5.1", "contributors": [ - "ostjen" + null ] }, { - "pr": "23770", - "title": "Regression: Fix sendMessagesToAdmins not in Fiber", - "userLogin": "sampaiodiego", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "23771", - "title": "Chore: Remove duplicated 'name' key from rate limiter logs", - "userLogin": "sampaiodiego", - "contributors": [ - "sampaiodiego" - ] - }, - { - "pr": "23761", - "title": "[NEW] Enable LDAP manual sync to deployments without EE license", - "userLogin": "rodrigok", - "description": "Open the Enterprise LDAP API that executes background sync to be used without any Enterprise License and enforce 2FA requirements.", - "milestone": "4.2.0", + "pr": "23795", + "title": "[FIX] Reload roomslist after successful deletion of a room from admin panel.", + "userLogin": "Aman-Maheshwari", + "description": "Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`.\r\nThis allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent.\r\n\r\nAlso added a succes toast message after the successful deletion of room.", + "milestone": "4.5.1", "contributors": [ - "rodrigok", - "ggazzo", - "web-flow" + "Aman-Maheshwari", + "web-flow", + "tassoevan" ] }, { - "pr": "23732", - "title": "[NEW] Rate limiting for user registering", - "userLogin": "ostjen", - "milestone": "4.2.0", + "pr": "24743", + "title": "[FIX] System messages are sent when adding or removing a group from a team", + "userLogin": "matheusbsilva137", + "description": "- Do not send system messages when adding or removing a new or existing _group_ from a team.", + "milestone": "4.5.1", "contributors": [ - "ostjen" + "matheusbsilva137" ] }, { - "pr": "23675", - "title": "Chore: add index on appId + associations for apps_persistence collection", - "userLogin": "KevLehman", + "pr": "24737", + "title": "[FIX] Typo and placeholder on wrap up call modal", + "userLogin": "MartinSchoeler", + "milestone": "4.5.1", "contributors": [ - "KevLehman" + "MartinSchoeler" ] }, { - "pr": "23768", - "title": "Chore: Bump Rocket.Chat@livechat to 1.10", + "pr": "24680", + "title": "[FIX] Show only available agents on extension association modal", "userLogin": "KevLehman", - "milestone": "4.2.0", - "contributors": [ - "KevLehman" - ] - }, - { - "pr": "23766", - "title": "[IMPROVE] Improve the add user drop down for add a user in create channel modal for UserAutoCompleteMultiple", - "userLogin": "dougfabris", - "description": "Seeing only the name of the person you are not adding is not practical in my opinion because two people can have the same name. Moreover, you can't see the username of the person you want to add in the dropdown. So I changed that and created another selection of users to show the username as well. I made this change so that it would appear in the key place for creating a room and adding a user.\r\n\r\nBefore:\r\n\r\nhttps://user-images.githubusercontent.com/45966964/115287805-faac8d00-a150-11eb-871f-147ab011ced0.mp4\r\n\r\n\r\nAfter:\r\n\r\nhttps://user-images.githubusercontent.com/45966964/115287664-d2249300-a150-11eb-8cf6-0e04730b425d.mp4", - "milestone": "4.2.0", + "milestone": "4.5.1", "contributors": [ - "Jeanstaquet", - "web-flow", - "dougfabris" + "KevLehman", + "tiagoevanp" ] }, { - "pr": "23533", - "title": "[FIX] New specific endpoint for contactChatHistoryMessages with right permissions", + "pr": "24607", + "title": "[FIX] VoIP Enable/Disable setting on CallContext/CallProvider Notifications", "userLogin": "tiagoevanp", - "description": "Anyone with 'View Omnichannel Rooms' permission can see the History Messages.", - "milestone": "4.2.0", + "milestone": "4.5.1", "contributors": [ "tiagoevanp", "web-flow", - "KevLehman", - "ggazzo" + "tassoevan" ] }, { - "pr": "23588", - "title": "[FIX][ENTERPRISE] OAuth \"Merge Roles\" removes roles from users", - "userLogin": "matheusbsilva137", - "description": "- Fix OAuth \"Merge Roles\": the \"Merge Roles\" option now synchronize only the roles described in the \"**Roles to Sync**\" setting available in each Custom OAuth settings' group (instead of replacing users' roles by their OAuth roles);\r\n- Fix \"Merge Roles\" and \"Channel Mapping\" not being performed/updated on OAuth login.", + "pr": "24677", + "title": "[FIX] Components for user search", + "userLogin": "juliajforesti", + "milestone": "4.5.1", "contributors": [ - "matheusbsilva137", + "juliajforesti", + "tassoevan", "web-flow" ] }, { - "pr": "23547", - "title": "[IMPROVE] Engagement Dashboard", - "userLogin": "tassoevan", - "description": "- Adds helpers `onToggledFeature` for server and client code to handle license activation/deactivation without server restart;\r\n- Replaces usage of `useEndpointData` with `useQuery` (from [React Query](https://react-query.tanstack.com/));\r\n- Introduces `view-engagement-dashboard` permission.", + "pr": "24657", + "title": "[FIX] Voip Stream Reinitialization Error", + "userLogin": "amolghode1981", + "milestone": "4.5.1", "contributors": [ - "tassoevan" + "amolghode1981" ] }, { - "pr": "23004", - "title": "[NEW] Audio and Video calling in Livechat", - "userLogin": "murtaza98", + "pr": "24696", + "title": "[FIX] Room's message count not being incremented on import", + "userLogin": "matheusbsilva137", + "description": "- Fix rooms' message counter not being incremented on message import.", + "milestone": "4.5.1", "contributors": [ - "dhruvjain99", - "murtaza98", - "Deepak-learner" + "matheusbsilva137" ] }, { - "pr": "23758", - "title": "Chore: Type omnichannel models", - "userLogin": "KevLehman", + "pr": "24674", + "title": "[FIX] Missing username on messages imported from Slack", + "userLogin": "matheusbsilva137", + "description": "- Fix missing sender's username on messages imported from Slack.", + "milestone": "4.5.1", "contributors": [ - "KevLehman", - "ggazzo" + "matheusbsilva137" ] }, { - "pr": "23737", - "title": "[NEW] Allow registering by REG_TOKEN environment variable", - "userLogin": "geekgonecrazy", - "description": "You can provide the REG_TOKEN environment variable containing a registration token and it will automatically register to your cloud account. This simplifies the registration flow", + "pr": "24590", + "title": "[FIX] Duplicated 'name' log key", + "userLogin": "sampaiodiego", + "milestone": "4.5.1", "contributors": [ - "geekgonecrazy" + "sampaiodiego" ] }, { - "pr": "23686", - "title": "[NEW] Permission for download/uploading files on mobile", - "userLogin": "ostjen", + "pr": "24661", + "title": "[FIX] Typo in wrap-up term", + "userLogin": "renatobecker", + "milestone": "4.5.1", "contributors": [ - "ostjen" + "renatobecker" ] }, { - "pr": "23735", - "title": "[IMPROVE] Stricter API types", - "userLogin": "tassoevan", - "description": "It:\r\n- Adds stricter types for `API`;\r\n- Enables types for `urlParams`;\r\n- Removes mandatory passage of `undefined` payload on client;\r\n- Corrects some regressions;\r\n- Reassures my belief in TypeScript supremacy.", + "pr": "24606", + "title": "[FIX] Push privacy config to not show username not being respected", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan", - "ggazzo" + "sampaiodiego" ] }, { - "pr": "23757", - "title": "Regression: Units endpoint to TS", + "pr": "24901", + "title": "[FIX] Custom script not being fired", "userLogin": "ggazzo", + "milestone": "4.5.3", "contributors": [ "ggazzo" ] }, { - "pr": "23750", - "title": "[NEW] REST endpoints to manage Omnichannel Business Units", - "userLogin": "KevLehman", - "description": "Basic documentation about endpoints can be found at https://www.postman.com/kaleman960/workspace/rocketchat-public-api/request/3865466-71502450-8c8f-42b4-8954-1cd3d01fcb0c", + "pr": "24896", + "title": "[FIX] Wrong business hour behavior", + "userLogin": "murtaza98", + "milestone": "4.6.0", "contributors": [ - "KevLehman" + "murtaza98" ] }, { - "pr": "23738", - "title": "[FIX] Autofocus on search input in admin", - "userLogin": "gabriellsh", - "description": "Removed \"generic\" autofocus on sidenav template.", + "pr": "24845", + "title": "[FIX] Ignore customClass on messages", + "userLogin": "sampaiodiego", "contributors": [ - "gabriellsh" + "sampaiodiego" ] }, { - "pr": "23745", - "title": "Chore: Generic Table ", - "userLogin": "ggazzo", + "pr": "24879", + "title": "[FIX] Apple OAuth", + "userLogin": "sampaiodiego", "contributors": [ - "ggazzo" + "sampaiodiego", + "web-flow" ] }, { - "pr": "21314", - "title": "[FIX] User logout after reset E2E key", - "userLogin": "siva2204", - "description": "user Is logged out after they Reset E2E key ,so that they can sign in again to generate new key.", + "pr": "24895", + "title": "i18n: Language update from LingoHub 🤖 on 2022-03-21Z", + "userLogin": "lingohub[bot]", "contributors": [ - "siva2204", - "tassoevan" + null ] }, { - "pr": "23739", - "title": "[FIX] Await promise to handle error when attempting to transfer a room", + "pr": "24749", + "title": "[IMPROVE] New omnichannel statistics and async statistics processing.", + "userLogin": "cauefcr", + "description": "https://app.clickup.com/t/1z4zg4e", + "contributors": [ + "cauefcr" + ] + }, + { + "pr": "24882", + "title": "[FIX] Missing dependency on useEffect at CallProvider", "userLogin": "KevLehman", "contributors": [ "KevLehman" ] }, { - "pr": "23673", - "title": "[FIX][ENTERPRISE] Private rooms and discussions can't be audited", - "userLogin": "matheusbsilva137", - "description": "- Add Private rooms (groups) and Discussions to the Message Auditing (Channels) autocomplete;\r\n- Update \"Channels\" tab name to \"Rooms\".", + "pr": "24877", + "title": "Chore: Fix MongoDB versions on release notes", + "userLogin": "sampaiodiego", + "milestone": "4.5.3", "contributors": [ - "matheusbsilva137", - "gabriellsh" + "sampaiodiego", + "web-flow" ] }, { - "pr": "23734", - "title": "[FIX] Missing user roles in edit user tab", - "userLogin": "dougfabris", + "pr": "24779", + "title": "[FIX] auto-join team channels not honoring user preferences", + "userLogin": "ostjen", "contributors": [ - "dougfabris" + "ostjen" ] }, { - "pr": "23733", - "title": "[FIX] Discussions created inside discussions", - "userLogin": "ggazzo", + "pr": "24869", + "title": "Bump pino from 7.8.1 to 7.9.1 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ggazzo" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23694", - "title": "[NEW] Allow Omnichannel statistics to be collected.", - "userLogin": "cauefcr", - "description": "This PR adds the possibility for business stakeholders to see what is actually being used of the Omnichannel integrations.", + "pr": "24870", + "title": "Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "cauefcr", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23725", - "title": "[IMPROVE] Re-naming department query param for Twilio", - "userLogin": "murtaza98", - "description": "Since the endpoint supports both, department ID and department Name, so we're renaming it to reflect the same. `departmentName` -> `department`", + "pr": "24864", + "title": "[FIX] Disable voip button when call is in progress", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "murtaza98" + "KevLehman" ] }, { - "pr": "23468", - "title": "[FIX] Fixed E2E default room settings not being honoured", - "userLogin": "ostjen", + "pr": "24863", + "title": "[FIX] Broken build caused by PRs modifying same file differently", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "ostjen", - "TheDigitalEagle", - "web-flow", - "tassoevan" + "KevLehman", + "tiagoevanp" ] }, { - "pr": "23659", - "title": "[FIX] broken avatar preview when changing avatar", - "userLogin": "Aman-Maheshwari", + "pr": "24850", + "title": "Regression: Role Sync not always working", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "Aman-Maheshwari" + "pierre-lehnen-rc" ] }, { - "pr": "23705", - "title": "[FIX] Prevent UserAction.addStream without Subscription", + "pr": "24838", + "title": "[FIX] [VOIP] SidebarFooter component ", "userLogin": "tiagoevanp", - "description": "When you take an Omnichannel chat from queue, the guest's typing information will appear.", + "description": "- Improve the CallProvider code;\r\n- Adjust the text case of the VoIP component on the FooterSidebar;\r\n- Fix the bad behavior with the changes in queue's name.", + "milestone": "4.5.3", "contributors": [ - "ggazzo", "tiagoevanp" ] }, { - "pr": "23499", - "title": "[FIX] PhotoSwipe crashing on show", - "userLogin": "tassoevan", - "description": "Waits for initial content to load before showing it.", - "contributors": [ - "tassoevan", - "dougfabris" - ] - }, - { - "pr": "23695", - "title": "Chore: add `no-bidi` rule", + "pr": "24837", + "title": "[IMPROVE] Standarize queue behavior for managers and agents when subscribing", "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ "KevLehman" ] }, { - "pr": "23711", - "title": "[FIX] Fix typo in FR translation", - "userLogin": "Cormoran96", + "pr": "24789", + "title": "[FIX] VoIP button gets disabled whenever user status changes", + "userLogin": "amolghode1981", + "milestone": "4.5.3", "contributors": [ - "Cormoran96" + "amolghode1981" ] }, { - "pr": "23706", - "title": "Chore: Mocha testing configuration", - "userLogin": "tassoevan", - "description": "We've been writing integration tests for the REST API quite regularly, but we can't say the same for UI-related modules. This PR is based on the assumption that _improving the developer experience on writing tests_ would increase our coverage and promote the adoption even for newcomers.\r\n\r\nHere as summary of the proposal:\r\n\r\n- Change Mocha configuration files:\r\n - Add a base configuration (`.mocharc.base.json`);\r\n - Rename the configuration for REST API tests (`mocha_end_to_end.opts.js -> .mocharc.api.js`);\r\n - Add a configuration for client modules (`.mocharc.client.js`);\r\n - Enable ESLint for them.\r\n- Add a Mocha test command exclusive for client modules (`npm run testunit-client`);\r\n- Enable fast watch mode:\r\n - Configure `ts-node` to only transpile code (skip type checking);\r\n - Define a list of files to be watched.\r\n- Configure `mocha` environment on ESLint only for test files (required when using Mocha's globals);\r\n- Adopt Chai as our assertion library:\r\n - Unify the setup of Chai plugins (`chai-spies`, `chai-datetime`, `chai-dom`);\r\n - Replace `assert` with `chai`;\r\n - Replace `chai.expect` with `expect`.\r\n- Enable integration tests with React components:\r\n - Enable JSX support on our default Babel configuration;\r\n - Adopt [testing library](https://testing-library.com/).", + "pr": "24799", + "title": "[FIX] Wrong param usage on queue summary call", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "tassoevan", - "KevLehman", - "ggazzo" + "KevLehman" ] }, { - "pr": "23701", - "title": "Chore: Api definitions", - "userLogin": "ggazzo", + "pr": "24829", + "title": "[FIX] Show only enabled departments on forward", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "tassoevan", - "ggazzo", - "web-flow" + "KevLehman" ] }, { - "pr": "23703", - "title": "[FIX][ENTERPRISE] Replace all occurrences of a placeholder on string instead of just first one", - "userLogin": "KevLehman", + "pr": "24823", + "title": "i18n: Language update from LingoHub 🤖 on 2022-03-14Z", + "userLogin": "lingohub[bot]", "contributors": [ - "KevLehman" + null, + "sampaiodiego", + "web-flow" ] }, { - "pr": "23380", - "title": "[FIX] Horizontal rule render for inline marked options", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/140845391-f84863de-34c5-40c9-86e9-5661fc8d6305.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/140845062-8ffd297b-5430-4aa8-82a1-ec2bdaa5e265.png)", + "pr": "24833", + "title": "Bump @types/mailparser from 3.0.2 to 3.4.0", + "userLogin": "dependabot[bot]", "contributors": [ - "dougfabris" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23641", - "title": "[FIX] Omnichannel webhooks can't be saved", - "userLogin": "Aman-Maheshwari", + "pr": "24832", + "title": "Bump @types/clipboard from 2.0.1 to 2.0.7", + "userLogin": "dependabot[bot]", "contributors": [ - "Aman-Maheshwari" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23595", - "title": "[FIX] Omnichannel business hours page breaking navigation", - "userLogin": "Aman-Maheshwari", + "pr": "24822", + "title": "Bump @types/nodemailer from 6.4.2 to 6.4.4", + "userLogin": "dependabot[bot]", "contributors": [ - "Aman-Maheshwari", - "tiagoevanp", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23626", - "title": "[IMPROVE] Allow override of default department for SMS Livechat sessions", - "userLogin": "bhardwajaditya", + "pr": "24821", + "title": "Bump body-parser from 1.19.0 to 1.19.2", + "userLogin": "dependabot[bot]", "contributors": [ - "bhardwajaditya" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23691", - "title": "[FIX] Omnichannel contact center navigation", - "userLogin": "tiagoevanp", - "description": "Derives from: https://github.com/RocketChat/Rocket.Chat/pull/23656\r\n\r\nThis PR includes a different approach to solving navigation problems following the same code structure and UI definitions of other \"ActionButtons\" components in Sidebar.", + "pr": "24820", + "title": "Bump @types/ws from 8.5.2 to 8.5.3 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "tiagoevanp" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23692", - "title": "Regression: Improve AggregationCursor types", - "userLogin": "ggazzo", + "pr": "24764", + "title": "Chore: Add E2E tests for livechat/visitor", + "userLogin": "Muramatsu2602", + "description": "- Create a new test suite file under tests/end-to-end/api/livechat\r\n- Create tests for the following endpoints:\r\n + livechat/visitor (create visitor, update visitor, add custom fields to visitors)", "contributors": [ - "ggazzo" + "Muramatsu2602", + "KevLehman" ] }, { - "pr": "23696", - "title": "Chore: Remove useCallbacks", - "userLogin": "tassoevan", + "pr": "24729", + "title": "Chore: Add E2E tests for livechat/room.close", + "userLogin": "Muramatsu2602", + "description": "* Create a new test suite file under tests/end-to-end/api/livechat\r\n * Create tests for the following endpoint:\r\n\t + ivechat/room.close", "contributors": [ - "tassoevan" + "Muramatsu2602", + "web-flow", + "KevLehman" ] }, { - "pr": "23387", - "title": "[IMPROVE] Reduce complexity in some functions", - "userLogin": "tassoevan", - "description": "Overhauls all places where eslint's `complexity` rule is disabled.", + "pr": "24785", + "title": "[FIX] German translation for Monitore", + "userLogin": "JMoVS", "contributors": [ - "tassoevan" + "JMoVS", + "web-flow" ] }, { - "pr": "23633", - "title": "Chore: Convert Fiber models to async Step 1", - "userLogin": "rodrigok", + "pr": "24812", + "title": "[FIX] Revert AutoComplete", + "userLogin": "juliajforesti", + "milestone": "4.5.2", "contributors": [ - "rodrigok", - "sampaiodiego" + "juliajforesti", + "ggazzo" ] }, { - "pr": "23671", - "title": "Release 4.1.2", - "userLogin": "ggazzo", + "pr": "24747", + "title": "Chore: APIClass types", + "userLogin": "felipe-rod123", + "description": "This pull request creates a new `restivus` module (.d.ts) for the `api.js` file.", "contributors": [ - "KevLehman", - "ggazzo", - "renatobecker", - "matheusbsilva137" + "felipe-rod123", + "ggazzo" ] }, { - "pr": "23487", - "title": "[FIX] Notifications are not being filtered", - "userLogin": "matheusbsilva137", - "description": "- Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value;\r\n - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`);\r\n - Rename 'mobileNotifications' user's preference to 'pushNotifications'.", - "milestone": "4.1.2", + "pr": "24801", + "title": "Bump is-svg from 4.3.1 to 4.3.2", + "userLogin": "dependabot[bot]", "contributors": [ - "matheusbsilva137" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23661", - "title": "[FIX] Performance issues when running Omnichannel job queue dispatcher", - "userLogin": "renatobecker", - "milestone": "4.1.2", + "pr": "24803", + "title": "Bump prometheus-gc-stats from 0.6.2 to 0.6.3", + "userLogin": "dependabot[bot]", "contributors": [ - "renatobecker" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23587", - "title": "[FIX] Omnichannel status being changed on page refresh", - "userLogin": "KevLehman", - "milestone": "4.1.2", + "pr": "24810", + "title": "Chore: Skip local services changes when shutting down duplicated services", + "userLogin": "sampaiodiego", "contributors": [ - "KevLehman" + "sampaiodiego" ] }, { - "pr": "23389", - "title": "[NEW] Permissions for interacting with Omnichannel Contact Center", - "userLogin": "cauefcr", - "description": "Adds a new permission, one that allows for control over user access to Omnichannel Contact Center,", + "pr": "24629", + "title": "[FIX] \"Match error\" when converting a team to a channel", + "userLogin": "matheusbsilva137", + "description": "- Fix \"Match error\" when trying to convert a channel to a team;", + "milestone": "4.6.0", "contributors": [ - "cauefcr", + "matheusbsilva137", "web-flow" ] }, { - "pr": "23587", - "title": "[FIX] Omnichannel status being changed on page refresh", - "userLogin": "KevLehman", - "milestone": "4.1.2", + "pr": "24809", + "title": "Regression: Fix ParentRoomWithEndpointData in loop", + "userLogin": "sampaiodiego", + "milestone": "4.5.2", "contributors": [ - "KevLehman" + "sampaiodiego" ] }, { - "pr": "23661", - "title": "[FIX] Performance issues when running Omnichannel job queue dispatcher", - "userLogin": "renatobecker", - "milestone": "4.1.2", + "pr": "24397", + "title": "Chore: Get Settings Statistics", + "userLogin": "albuquerquefabio", "contributors": [ - "renatobecker" + "albuquerquefabio" ] }, { - "pr": "23645", - "title": "Release 4.1.1", - "userLogin": "sampaiodiego", + "pr": "24732", + "title": "[FIX] `PaginatedSelectFiltered` not handling changes", + "userLogin": "tassoevan", + "milestone": "4.5.2", "contributors": [ - "pierre-lehnen-rc", - "sampaiodiego", - "dudanogueira", - "d-gubert" + "tassoevan" ] }, { - "pr": "23607", - "title": "[FIX] App update flow failing in HA setups", - "userLogin": "d-gubert", - "description": "The flow for app updates is broken in specific scenarios with HA setups. Here we change the method calls in the Apps-Engine to avoid race conditions", - "milestone": "4.1.1", + "pr": "24628", + "title": "Chore: converted more hooks to typescript", + "userLogin": "felipe-rod123", + "description": "Converted some functions on `client/hooks/` from JavaScript to Typescript.", "contributors": [ - "d-gubert" + "felipe-rod123", + "ggazzo" ] }, { - "pr": "23627", - "title": "[FIX] LDAP users not being re-activated on login", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.1.1", + "pr": "24506", + "title": "Chore: added settings endpoint types", + "userLogin": "felipe-rod123", + "description": "Created typing for endpoint definitions on `settings.ts`.", "contributors": [ - "pierre-lehnen-rc" + "felipe-rod123", + "ggazzo", + "web-flow" ] }, { - "pr": "23608", - "title": "[FIX] Advanced LDAP Sync Features", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.1.1", + "pr": "24226", + "title": "[FIX] Handle Other Formats inside Upload Avatar", + "userLogin": "nishant23122000", + "description": "After resolving issue #24213 : \r\n\r\n\r\nhttps://user-images.githubusercontent.com/53515714/150325012-91413025-786e-4ce0-ae75-629f6b05b024.mp4", + "milestone": "4.6.0", "contributors": [ - "pierre-lehnen-rc", - "web-flow" + "nishant23122000", + "debdutdeb", + "web-flow", + "murtaza98" ] }, { - "pr": "23608", - "title": "[FIX] Advanced LDAP Sync Features", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.1.1", + "pr": "24424", + "title": "[FIX] Prune Message issue", + "userLogin": "nishant23122000", + "milestone": "4.6.0", "contributors": [ - "pierre-lehnen-rc", + "nishant23122000", + "debdutdeb", "web-flow" ] }, { - "pr": "23627", - "title": "[FIX] LDAP users not being re-activated on login", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.1.1", + "pr": "24805", + "title": "[FIX] Critical: Incorrect visitor getting assigned to a chat from apps", + "userLogin": "murtaza98", + "milestone": "4.5.2", "contributors": [ - "pierre-lehnen-rc" + "murtaza98" ] }, { - "pr": "23576", - "title": "[FIX] \"to users\" not working in export message", - "userLogin": "ostjen", + "pr": "24804", + "title": "[FIX] \"livechat/webrtc.call\" endpoint not working", + "userLogin": "murtaza98", + "milestone": "4.5.2", "contributors": [ - "ostjen", - "web-flow" + "murtaza98" ] }, { - "pr": "23607", - "title": "[FIX] App update flow failing in HA setups", - "userLogin": "d-gubert", - "description": "The flow for app updates is broken in specific scenarios with HA setups. Here we change the method calls in the Apps-Engine to avoid race conditions", - "milestone": "4.1.1", + "pr": "24507", + "title": "Chore: added Server Instances endpoint types", + "userLogin": "felipe-rod123", + "description": "Created typing for endpoint definitions on `instances.ts`.", "contributors": [ - "d-gubert" + "felipe-rod123" ] }, { - "pr": "23566", - "title": "[FIX] Apps scheduler \"losing\" jobs after server restart", - "userLogin": "d-gubert", - "description": "If a job is scheduled and the server restarted, said job won't be executed, giving the impression it's been lost.\r\n\r\nWhat happens is that the scheduler is only started when some app tries to schedule an app - if that happens, all jobs that are \"late\" will be executed; if that doesn't happen, no job will run.\r\n\r\nThis PR starts the apps scheduler right after all apps have been loaded", + "pr": "24758", + "title": "[FIX] Prevent call button toggle when user is on call", + "userLogin": "KevLehman", "contributors": [ - "d-gubert" + "KevLehman" ] }, { - "pr": "23603", - "title": "i18n: Language update from LingoHub 🤖 on 2021-11-01Z", - "userLogin": "lingohub[bot]", + "pr": "24800", + "title": "Regression: Register services right away", + "userLogin": "sampaiodiego", "contributors": [ - null, "sampaiodiego" ] }, { - "pr": "23498", - "title": "[NEW] Show on-hold metrics on analytics pages and current chats", + "pr": "24792", + "title": "[FIX] VoipExtensionsPage component call", "userLogin": "KevLehman", + "milestone": "4.5.2", "contributors": [ "KevLehman" ] }, { - "pr": "23452", - "title": "Chore: Rearrange module typings", - "userLogin": "tassoevan", - "description": "- Move all external module declarations (definitions and augmentations) to `/definition/externals`;\r\n- ~Symlink some modules on `/definition/externals` to `/ee/server/services/definition/externals`~ Share types with `/ee/server/services`;\r\n- Use TypeScript as server code entrypoint.", + "pr": "24384", + "title": "Chore: Convert server functions from javascript to typescript", + "userLogin": "felipe-rod123", + "description": "This pull request will be used to rewrite some functions on the Chat Engine to Typescript, in order to increase security and specify variable types on the code.", "contributors": [ - "tassoevan" + "felipe-rod123", + "ggazzo" ] }, { - "pr": "23487", - "title": "[FIX] Notifications are not being filtered", - "userLogin": "matheusbsilva137", - "description": "- Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value;\r\n - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`);\r\n - Rename 'mobileNotifications' user's preference to 'pushNotifications'.", - "milestone": "4.1.2", + "pr": "24705", + "title": "[FIX] Broken multiple OAuth integrations", + "userLogin": "debdutdeb", + "milestone": "4.5.2", "contributors": [ - "matheusbsilva137" + "debdutdeb" ] }, { - "pr": "23542", - "title": "[IMPROVE] MKP12 - New UI - Merge Apps and Marketplace Tabs and Content", - "userLogin": "rique223", - "description": "Merged the Marketplace and Apps page into a single page with a tabs component that changes between Markeplace and installed apps.\r\n![page merging](https://user-images.githubusercontent.com/43561537/138516558-f86d62e6-1a5c-4817-a229-a1b876323960.gif)", + "pr": "24793", + "title": "[FIX][ENTERPRISE] Auto reload feature of ddp-streamer micro service", + "userLogin": "sampaiodiego", "contributors": [ - "ggazzo", - "dougfabris" + "sampaiodiego" ] }, { - "pr": "23586", - "title": "Merge master into develop & Set version to 4.2.0-develop", - "userLogin": "sampaiodiego", + "pr": "24783", + "title": "Bump pino from 7.8.0 to 7.8.1 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "sampaiodiego", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23581", - "title": "Release 4.1.0", - "userLogin": "sampaiodiego", + "pr": "23121", + "title": "Bump jschardet from 1.6.0 to 3.0.0", + "userLogin": "dependabot[bot]", "contributors": [ - "sampaiodiego", - "web-flow", - "tassoevan", - "dougfabris", - "Darshilp326", - "ostjen", - "d-gubert", - "wolbernd", - "rodrigok", - "matheusbsilva137", - "Aman-Maheshwari", - "Sing-Li", - "murtaza98", - "yash-rajpal", - "badbart", - "AbhJ", - "dragoneena12" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23554", - "title": "Release 4.0.5", - "userLogin": "sampaiodiego", + "pr": "24752", + "title": "[FIX] Show call icon only when user has extension associated", + "userLogin": "KevLehman", + "milestone": "4.5.3", "contributors": [ - "pierre-lehnen-rc", - "sampaiodiego" + "KevLehman" ] }, { - "pr": "23541", - "title": "[FIX] OAuth login not working on mobile app", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.0.5", + "pr": "24623", + "title": "[FIX] Opening a new DM from user card", + "userLogin": "tassoevan", + "description": "A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component.", + "milestone": "4.5.2", "contributors": [ - "pierre-lehnen-rc" + "tassoevan" ] }, { - "pr": "23532", - "title": "Release 4.0.4", - "userLogin": "sampaiodiego", + "pr": "24750", + "title": "[IMPROVE] Voip Extensions disabled state", + "userLogin": "MartinSchoeler", + "milestone": "4.5.2", "contributors": [ - "KevLehman", - "sampaiodiego", - "matheusbsilva137" + "MartinSchoeler" ] }, { - "pr": "23411", - "title": "[FIX] SAML Users' roles being reset to default on login", - "userLogin": "matheusbsilva137", - "description": "- Remove `roles` field update on `insertOrUpdateSAMLUser` function;\r\n- Add SAML `syncRoles` event;", - "milestone": "4.0.4", + "pr": "24748", + "title": "[IMPROVE] UX - VoIP Call Component", + "userLogin": "tiagoevanp", + "milestone": "4.5.3", "contributors": [ - "matheusbsilva137", - "pierre-lehnen-rc" + "tiagoevanp" ] }, { - "pr": "23522", - "title": "[FIX] Queue error handling and unlocking behavior", - "userLogin": "KevLehman", - "milestone": "4.0.4", + "pr": "24753", + "title": "Chore: Micro services fixes and cleanup", + "userLogin": "sampaiodiego", "contributors": [ - "KevLehman" + "sampaiodiego" ] }, { - "pr": "23496", - "title": "Release 4.0.3", - "userLogin": "sampaiodiego", + "pr": "24756", + "title": "Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests", + "userLogin": "ggazzo", "contributors": [ - "KevLehman", - "sampaiodiego", - "thassiov" + "ggazzo" ] }, { - "pr": "23418", - "title": "[FIX][APPS] Communication problem when updating and uninstalling apps in cluster", - "userLogin": "thassiov", - "description": "- Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place.\r\n- Also shows a warning message on uninstalls inside a cluster. As there are many servers writing to the same place, some race conditions may occur. This prevents problems related to terminating the process in the middle due to errors being thrown and leaving the server in a faulty state.", - "milestone": "4.0.3", + "pr": "24760", + "title": "[FIX] Apple login script being loaded even when Apple Login is disabled.", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.5.1", "contributors": [ - "thassiov" + "pierre-lehnen-rc" ] }, { - "pr": "23473", - "title": "[FIX] Server crashing when Routing method is not available at start", - "userLogin": "KevLehman", - "milestone": "4.0.3", + "pr": "24754", + "title": "Chore: Update Livechat", + "userLogin": "MartinSchoeler", + "milestone": "4.5.1", "contributors": [ - "KevLehman", - "web-flow" + "MartinSchoeler" ] }, { - "pr": "23460", - "title": "Release 4.0.2", - "userLogin": "sampaiodiego", + "pr": "24683", + "title": "[FIX] no id of room closer in livechat-close message", + "userLogin": "cuonghuunguyen", + "milestone": "4.5.1", "contributors": [ - "murtaza98", - "sampaiodiego", - "Aman-Maheshwari" + null ] }, { - "pr": "23377", - "title": "[FIX] Attachment buttons overlap in mobile view", - "userLogin": "Aman-Maheshwari", - "milestone": "4.0.2", - "contributors": [ - "Aman-Maheshwari" - ] - }, - { - "pr": "23393", - "title": "[FIX] user/agent upload not working via Apps Engine after 3.16.0", - "userLogin": "murtaza98", - "description": "Fixes #22974", - "milestone": "4.0.2", + "pr": "24771", + "title": "Chore: fix grammatical errors in Features", + "userLogin": "aadishJ01", "contributors": [ - "murtaza98" + "aadishJ01", + "web-flow" ] }, { - "pr": "23404", - "title": "[FIX][ENTERPRISE] Omnichannel agent is not leaving the room when a forwarded chat is queued", - "userLogin": "murtaza98", - "milestone": "4.0.2", + "pr": "24759", + "title": "Chore: Fix grammatical errors in Code of Conduct", + "userLogin": "aadishJ01", "contributors": [ - "murtaza98", + "aadishJ01", "web-flow" ] }, { - "pr": "23396", - "title": "[FIX] Prevent starting Omni-Queue if Omnichannel is disabled", - "userLogin": "murtaza98", - "description": "Whenever the Routing system setting changes, and omnichannel is disabled, then we shouldn't start the queue.", - "milestone": "4.0.2", + "pr": "23795", + "title": "[FIX] Reload roomslist after successful deletion of a room from admin panel.", + "userLogin": "Aman-Maheshwari", + "description": "Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`.\r\nThis allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent.\r\n\r\nAlso added a succes toast message after the successful deletion of room.", + "milestone": "4.5.1", "contributors": [ - "murtaza98" + "Aman-Maheshwari", + "web-flow", + "tassoevan" ] }, { - "pr": "23386", - "title": "Release 4.0.1", - "userLogin": "sampaiodiego", + "pr": "24743", + "title": "[FIX] System messages are sent when adding or removing a group from a team", + "userLogin": "matheusbsilva137", + "description": "- Do not send system messages when adding or removing a new or existing _group_ from a team.", + "milestone": "4.5.1", "contributors": [ - "rodrigok", - "sampaiodiego", - "ostjen", - "wolbernd", - "d-gubert", "matheusbsilva137" ] }, { - "pr": "23378", - "title": "[FIX] Users' `roles` and `type` being reset to default on LDAP DataSync", - "userLogin": "matheusbsilva137", - "description": "- Update `roles` and `type` fields only if they are specified in the data imported from LDAP (otherwise, no changes are applied).", - "milestone": "4.0.1", + "pr": "24544", + "title": "Chore: Fix Cypress tests", + "userLogin": "rodrigok", "contributors": [ - "matheusbsilva137", - "sampaiodiego" + "rodrigok", + "tassoevan", + "dougfabris" ] }, { - "pr": "23374", - "title": "[FIX] imported migration v240", - "userLogin": "ostjen", - "milestone": "4.0.1", + "pr": "24737", + "title": "[FIX] Typo and placeholder on wrap up call modal", + "userLogin": "MartinSchoeler", + "milestone": "4.5.1", "contributors": [ - "ostjen" + "MartinSchoeler" ] }, { - "pr": "23375", - "title": "Chore: Update Apps-Engine version", - "userLogin": "d-gubert", - "milestone": "4.0.1", + "pr": "24739", + "title": "[IMPROVE][ENTERPRISE] Don't start presence monitor when running micro services", + "userLogin": "sampaiodiego", "contributors": [ - "d-gubert" + "sampaiodiego" ] }, { - "pr": "23366", - "title": "[FIX] BigBlueButton integration error due to missing file import", - "userLogin": "wolbernd", - "description": "Fixes BigBlueButton integration", - "milestone": "4.0.1", + "pr": "24738", + "title": "[FIX][ENTERPRISE] DDP streamer not sending data to all clients", + "userLogin": "sampaiodiego", "contributors": [ - "wolbernd", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23372", - "title": "[FIX] unwanted toastr error message when deleting user", - "userLogin": "ostjen", - "milestone": "4.0.1", + "pr": "24680", + "title": "[FIX] Show only available agents on extension association modal", + "userLogin": "KevLehman", + "milestone": "4.5.1", "contributors": [ - "ostjen", - "tassoevan", - "web-flow" + "KevLehman", + "tiagoevanp" ] }, { - "pr": "23379", - "title": "[FIX] resumeToken not working", + "pr": "24710", + "title": "[FIX] DDP streamer errors", "userLogin": "sampaiodiego", - "milestone": "4.0.1", "contributors": [ "sampaiodiego" ] }, { - "pr": "23381", - "title": "[FIX] MongoDB deprecation link", + "pr": "24724", + "title": "[FIX][ENTERPRISE] Presence micro service logic", "userLogin": "sampaiodiego", - "milestone": "4.0.1", "contributors": [ "sampaiodiego" ] }, { - "pr": "23382", - "title": "[FIX] LDAP not stoping after wrong password", - "userLogin": "rodrigok", - "milestone": "4.0.1", + "pr": "24717", + "title": "i18n: Language update from LingoHub 🤖 on 2022-03-07Z", + "userLogin": "lingohub[bot]", "contributors": [ - "rodrigok" + null, + "sampaiodiego", + "web-flow" ] }, { - "pr": "23577", - "title": "Regression: Debounce call based on params on omnichannel queue dispatch", - "userLogin": "KevLehman", + "pr": "24607", + "title": "[FIX] VoIP Enable/Disable setting on CallContext/CallProvider Notifications", + "userLogin": "tiagoevanp", + "milestone": "4.5.1", "contributors": [ - "KevLehman" + "tiagoevanp", + "web-flow", + "tassoevan" ] }, { - "pr": "23556", - "title": "Regression: Prevent settings from getting updated", + "pr": "24726", + "title": "Chore: Improve logger to allow log of `unknown` values", "userLogin": "sampaiodiego", "contributors": [ "sampaiodiego" ] }, { - "pr": "23568", - "title": "Regression: Routing method not available when called from listeners at startup", - "userLogin": "KevLehman", - "milestone": "4.1.0", + "pr": "24677", + "title": "[FIX] Components for user search", + "userLogin": "juliajforesti", + "milestone": "4.5.1", "contributors": [ - "KevLehman" + "juliajforesti", + "tassoevan", + "web-flow" ] }, { - "pr": "23391", - "title": "Bump: fuselage 0.30.1", - "userLogin": "ggazzo", + "pr": "24542", + "title": "[FIX] Date Message Export Filter Fix", + "userLogin": "eduardofcabrera", + "description": "Fix message export filter to get all messages between \"from date\" and \"to date\", including \"to date\".", "contributors": [ - "dougfabris" + "eduardofcabrera", + "web-flow" ] }, { - "pr": "23564", - "title": "Revert: \"[FIX] Apps scheduler \"losing\" jobs after server restart\"", - "userLogin": "d-gubert", + "pr": "24709", + "title": "[FIX] API Error preventing adding an email to users without one (like bot/app users)", + "userLogin": "debdutdeb", "contributors": [ - "d-gubert", - "web-flow" + "debdutdeb" ] }, { - "pr": "23539", - "title": "[FIX] Apps scheduler \"losing\" jobs after server restart", - "userLogin": "d-gubert", - "description": "If a job is scheduled and the server restarted, said job won't be executed, giving the impression it's been lost.\r\n\r\nWhat happens is that the scheduler is only started when some app tries to schedule an app - if that happens, all jobs that are \"late\" will be executed; if that doesn't happen, no job will run.\r\n\r\nThis PR starts the apps scheduler right after all apps have been loaded", + "pr": "24716", + "title": "Bump ts-node from 10.6.0 to 10.7.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "d-gubert" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23552", - "title": "Regression: Mail body contains `undefined` text", - "userLogin": "tassoevan", - "description": "### Before\r\n![image](https://user-images.githubusercontent.com/2263066/138733018-10449892-5c2d-46fb-9355-00e98e0d6c9f.png)\r\n\r\n### After\r\n![image](https://user-images.githubusercontent.com/2263066/138733074-a1b88a77-bf64-41c3-a6c3-ac9e1cb63de1.png)", + "pr": "24476", + "title": "[FIX] Nextcloud OAuth for incomplete token URL", + "userLogin": "debdutdeb", + "milestone": "4.6.0", "contributors": [ - "tassoevan", - "sampaiodiego" + "debdutdeb" ] }, { - "pr": "23541", - "title": "[FIX] OAuth login not working on mobile app", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.0.5", + "pr": "24657", + "title": "[FIX] Voip Stream Reinitialization Error", + "userLogin": "amolghode1981", + "milestone": "4.5.1", "contributors": [ - "pierre-lehnen-rc" + "amolghode1981" ] }, { - "pr": "23531", - "title": "Regression: Waiting_queue setting not being applied due to missing module key", - "userLogin": "KevLehman", + "pr": "24698", + "title": "Bump pino-pretty from 7.5.2 to 7.5.3 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23528", - "title": "Regression: Settings order", - "userLogin": "ggazzo", + "pr": "24696", + "title": "[FIX] Room's message count not being incremented on import", + "userLogin": "matheusbsilva137", + "description": "- Fix rooms' message counter not being incremented on message import.", + "milestone": "4.5.1", "contributors": [ - "ggazzo" + "matheusbsilva137" ] }, { - "pr": "23529", - "title": "Regression: watchByRegex without Fibers", - "userLogin": "ggazzo", + "pr": "23824", + "title": "Chore: Improvements on role syncing (ldap, oauth and saml)", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "ggazzo" + "pierre-lehnen-rc", + "tassoevan" ] }, { - "pr": "23524", - "title": "Chore: Fix some TS warnings", - "userLogin": "KevLehman", + "pr": "24689", + "title": "Bump pino-pretty from 7.5.1 to 7.5.2 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23521", - "title": "[FIX] Delay start of email inbox", - "userLogin": "KevLehman", + "pr": "24674", + "title": "[FIX] Missing username on messages imported from Slack", + "userLogin": "matheusbsilva137", + "description": "- Fix missing sender's username on messages imported from Slack.", + "milestone": "4.5.1", "contributors": [ - "KevLehman" + "matheusbsilva137" ] }, { - "pr": "23495", - "title": "Chore: Make omnichannel settings dependent on omnichannel being enabled", - "userLogin": "KevLehman", + "pr": "24642", + "title": "Bump actions/setup-node from 2 to 3", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman", - "web-flow", - "sampaiodiego" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23523", - "title": "Chore: Update Livechat Package", - "userLogin": "MartinSchoeler", + "pr": "24644", + "title": "i18n: Language update from LingoHub 🤖 on 2022-02-28Z", + "userLogin": "lingohub[bot]", "contributors": [ - "MartinSchoeler" + null, + "sampaiodiego", + "web-flow" ] }, { - "pr": "23411", - "title": "[FIX] SAML Users' roles being reset to default on login", - "userLogin": "matheusbsilva137", - "description": "- Remove `roles` field update on `insertOrUpdateSAMLUser` function;\r\n- Add SAML `syncRoles` event;", - "milestone": "4.0.4", + "pr": "24590", + "title": "[FIX] Duplicated 'name' log key", + "userLogin": "sampaiodiego", + "milestone": "4.5.1", "contributors": [ - "matheusbsilva137", - "pierre-lehnen-rc" + "sampaiodiego" ] }, { - "pr": "23522", - "title": "[FIX] Queue error handling and unlocking behavior", - "userLogin": "KevLehman", - "milestone": "4.0.4", + "pr": "24668", + "title": "Bump actions/checkout from 2 to 3", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23314", - "title": "[FIX] MONGO_OPTIONS being ignored for oplog connection", - "userLogin": "cuonghuunguyen", + "pr": "24574", + "title": "Chore(deps-dev): Bump @types/mock-require from 2.0.0 to 2.0.1", + "userLogin": "dependabot[bot]", "contributors": [ - "cuonghuunguyen", - "sampaiodiego", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23392", - "title": "[IMPROVE] Allow Omnichannel to handle huge queues ", - "userLogin": "KevLehman", + "pr": "24667", + "title": "Bump ts-node from 10.5.0 to 10.6.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23515", - "title": "[IMPROVE] Make Livechat Instructions setting multi-line", - "userLogin": "murtaza98", - "description": "Since now we're supporting markdown text on this field (via this PR - https://github.com/RocketChat/Rocket.Chat.Livechat/pull/648), it would be nice to make this setting multiline so users can have more space to edit the text\r\n![image](https://user-images.githubusercontent.com/34130764/138146712-13e4968b-5312-4d53-b44c-b5699c5e49c1.png)", + "pr": "24666", + "title": "Bump @types/ws from 8.2.3 to 8.5.2 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "murtaza98" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23505", - "title": "Chore: Improve watch OAuth settings logic", - "userLogin": "ggazzo", - "description": "Just prevent to perform 200 deletions for registers that not even exist", + "pr": "24640", + "title": "Bump url-parse from 1.5.7 to 1.5.10", + "userLogin": "dependabot[bot]", "contributors": [ - "ggazzo" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23518", - "title": "Regression: Incorrect behavior in jump to recent message button", - "userLogin": "dougfabris", + "pr": "24653", + "title": "Merge master into develop & Set version to 4.6.0-develop", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "dougfabris" + "pierre-lehnen-rc", + "web-flow" ] }, { - "pr": "23519", - "title": "Regression: Fix enterprise setting validation", - "userLogin": "ggazzo", + "pr": "24652", + "title": "Release 4.5.0", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "ggazzo" + "sampaiodiego", + "web-flow", + "aswinidev", + "debdutdeb", + "dependabot[bot]", + "lingohub[bot]", + "ostjen", + "KevLehman", + "dougfabris", + "LucasFASouza", + "felipe-rod123", + "guijun13", + "pierre-lehnen-rc", + "filipemarins", + "matheusbsilva137", + "gabriellsh" ] }, { - "pr": "23514", - "title": "Chore: Ensure all permissions are created up to this point", - "userLogin": "sampaiodiego", + "pr": "24661", + "title": "[FIX] Typo in wrap-up term", + "userLogin": "renatobecker", + "milestone": "4.5.1", "contributors": [ - "sampaiodiego" + "renatobecker" ] }, { - "pr": "23469", - "title": "[FIX] useEndpointAction replace by useEndpointActionExperimental", - "userLogin": "tiagoevanp", + "pr": "24028", + "title": "[IMPROVE] Updated links in readme", + "userLogin": "aswinidev", "contributors": [ - "tiagoevanp" + "aswinidev", + "web-flow", + "debdutdeb" ] }, { - "pr": "23394", - "title": "[FIX] Omni-Webhook's retry mechanism going in infinite loop", - "userLogin": "murtaza98", + "pr": "24651", + "title": "Chore: Update Apps-Engine", + "userLogin": "d-gubert", + "milestone": "4.5.0", "contributors": [ - "murtaza98" + "d-gubert" ] }, { - "pr": "23511", - "title": "Regression: Fix user typings style", - "userLogin": "ggazzo", + "pr": "24649", + "title": "Regression: Refresh server connection when MI server settings change", + "userLogin": "KevLehman", + "milestone": "4.5.0", "contributors": [ - "ggazzo" + "KevLehman" ] }, { - "pr": "23510", - "title": "Chore: Update pino and pino-pretty", - "userLogin": "sampaiodiego", + "pr": "24648", + "title": "Regression: Prevent button from losing state when rerendering", + "userLogin": "KevLehman", + "milestone": "4.5.0", "contributors": [ - "sampaiodiego" + "KevLehman" ] }, { - "pr": "23506", - "title": "Regression: Prevent Settings Unit Test Error ", - "userLogin": "ggazzo", + "pr": "24585", + "title": "Regression: Error setting user avatars and mentioning rooms on Slack Import", + "userLogin": "matheusbsilva137", + "description": "- Fix `Mentioned room not found` error when importing rooms from Slack;\r\n- Fix `Forbidden` error when setting avatars for users imported from Slack (on user import/creation);\r\n- Fix incorrect message count on imported rooms;\r\n- Fix missing username on messages imported from Slack;", "contributors": [ - "ggazzo" + "matheusbsilva137", + "pierre-lehnen-rc" ] }, { - "pr": "23486", - "title": "i18n: Language update from LingoHub 🤖 on 2021-10-18Z", - "userLogin": "lingohub[bot]", + "pr": "24647", + "title": "Regression: Fix wrong tab name for VoIP settings", + "userLogin": "renatobecker", + "milestone": "4.5.0", "contributors": [ - null, - "KevLehman" + "renatobecker" ] }, { - "pr": "23376", - "title": "Bump url-parse from 1.4.7 to 1.5.3", - "userLogin": "dependabot[bot]", + "pr": "24646", + "title": "Regression: Server crashing if Voip credentials are invalid", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "murtaza98" ] }, { - "pr": "23172", - "title": "[FIX] Rewrite missing webRTC feature", - "userLogin": "dougfabris", + "pr": "24645", + "title": "Regression: Extension List panel UI not aligned with designs", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "dougfabris", - "tassoevan" + "murtaza98" ] }, { - "pr": "23488", - "title": "Chore: Replace `promises` helper", - "userLogin": "tassoevan", + "pr": "24635", + "title": "Regression: Queue counter aggregator for incoming/hanged calls", + "userLogin": "amolghode1981", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "amolghode1981" ] }, { - "pr": "23210", - "title": "Chore: Startup Time", - "userLogin": "ggazzo", - "description": "The settings logic has been improved as a whole.\r\n\r\nAll the logic to get the data from the env var was confusing.\r\n\r\nSetting default values was tricky to understand.\r\n\r\nEvery time the server booted, all settings were updated and callbacks were called 2x or more (horrible for environments with multiple instances and generating a turbulent startup).\r\n\r\n`Settings.get(......, callback);` was deprecated. We now have better methods for each case.", - "milestone": "4.1.0", + "pr": "24630", + "title": "Regression: Fix double value on holdTime and empty msg on last message", + "userLogin": "MartinSchoeler", "contributors": [ - "ggazzo", - "sampaiodiego" + "MartinSchoeler", + "web-flow" ] }, { - "pr": "23494", - "title": "[FIX] Missing avatarETag in Room Avatars", - "userLogin": "dougfabris", - "description": "### before\r\n![before](https://user-images.githubusercontent.com/27704687/137982103-1f4c8d0c-b4e1-4604-ba0f-6e3881813577.gif)\r\n\r\n### after\r\n![Peek 2021-10-19 16-59](https://user-images.githubusercontent.com/27704687/137981755-61dad9c0-4c50-4af3-9337-4e7a2633023a.gif)", + "pr": "24624", + "title": "Regression: If Asterisk suddenly goes down, server has no way to know. Causes server to get stuck. Needs restart", + "userLogin": "amolghode1981", + "milestone": "4.5.0", "contributors": [ - "dougfabris" + "amolghode1981", + "KevLehman" ] }, { - "pr": "23491", - "title": "Chore: Move `isJSON` helper", - "userLogin": "tassoevan", + "pr": "24601", + "title": "Regression: Prevent connect to asterisk when VoIP is disabled", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "murtaza98", + "web-flow", + "KevLehman" ] }, { - "pr": "23497", - "title": "Update the community open call link in README", - "userLogin": "Sing-Li", + "pr": "24626", + "title": "Regression: Encode registration info as JWT when signing key is provided", + "userLogin": "KevLehman", + "milestone": "4.5.0", "contributors": [ - "Sing-Li", - "web-flow", - "geekgonecrazy" + "KevLehman" ] }, { - "pr": "23490", - "title": "Chore: Move `addMinutesToADate` helper", - "userLogin": "tassoevan", + "pr": "24625", + "title": "Regression: Fix time fields and wrap up in Voip Room Contexual bar", + "userLogin": "MartinSchoeler", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "MartinSchoeler" ] }, { - "pr": "23489", - "title": "Chore: Move `isEmail` helper", - "userLogin": "tassoevan", + "pr": "24592", + "title": "Regression: Fix in-correct room status shown to agents", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "murtaza98" ] }, { - "pr": "23228", - "title": "[FIX] Admins can't update or reset user avatars when the \"Allow User Avatar Change\" setting is off", - "userLogin": "matheusbsilva137", - "description": "- Allow admins (or any other user with the `edit-other-user-avatar` permission) to update or reset user avatars even when the \"Allow User Avatar Change\" setting is off.", + "pr": "24619", + "title": "Regression: Do not show toast on incoming voip calls", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "matheusbsilva137", - "web-flow", - "tassoevan" + "murtaza98", + "web-flow" ] }, { - "pr": "23473", - "title": "[FIX] Server crashing when Routing method is not available at start", - "userLogin": "KevLehman", - "milestone": "4.0.3", + "pr": "24616", + "title": "Regression: Fix incoming voip call ringtone is not ringing", + "userLogin": "murtaza98", "contributors": [ - "KevLehman", + "murtaza98", "web-flow" ] }, { - "pr": "22949", - "title": "[FIX] Avoid last admin deactivate itself", - "userLogin": "ostjen", - "description": "Co-authored-by: @Kartik18g", + "pr": "24610", + "title": "Regression: Mark all rooms as read modal closing instantly.", + "userLogin": "gabriellsh", "contributors": [ - "ostjen", - "web-flow", - null + "gabriellsh" ] }, { - "pr": "23418", - "title": "[FIX][APPS] Communication problem when updating and uninstalling apps in cluster", - "userLogin": "thassiov", - "description": "- Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place.\r\n- Also shows a warning message on uninstalls inside a cluster. As there are many servers writing to the same place, some race conditions may occur. This prevents problems related to terminating the process in the middle due to errors being thrown and leaving the server in a faulty state.", - "milestone": "4.0.3", + "pr": "24615", + "title": "Regression: Fix translation for call started message", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "thassiov" + "murtaza98", + "web-flow" ] }, { - "pr": "23474", - "title": "[FIX] Allow files with no/unknown extension to be uploaded", - "userLogin": "gabriellsh", - "description": "Let the server block uploads based on mime type if the browser can't recognize the extension.", + "pr": "24594", + "title": "Regression: Bunch of settings fixes for VoIP", + "userLogin": "MartinSchoeler", + "milestone": "4.5.0", "contributors": [ - "gabriellsh", - "dougfabris" + "MartinSchoeler", + "web-flow" ] }, { - "pr": "23455", - "title": "[IMPROVE] Rewrite read receipts to react.", + "pr": "24609", + "title": "Regression: Admin Sidebar colors inverted.", "userLogin": "gabriellsh", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/137547164-95089054-7639-4484-98b1-dad858d5e3a7.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/40830821/137206897-f31b75c5-d962-4595-844a-d7842dfe33a5.png)", + "milestone": "4.5.0", "contributors": [ - "gabriellsh", - "dougfabris" + "gabriellsh" ] }, { - "pr": "23471", - "title": "[FIX] Team's tag inaccessible for members", - "userLogin": "dougfabris", - "description": "### before\r\n![before_video](https://user-images.githubusercontent.com/27704687/137529529-d30b4f0b-af2b-4d81-ba38-e81b48e77519.gif)\r\n\r\n### after\r\n![after_video](https://user-images.githubusercontent.com/27704687/137529526-cf0bc6db-fa08-4bf9-8599-af647a0e3307.gif)", + "pr": "24602", + "title": "Regression: No audio when call comes from Skype/IP phone", + "userLogin": "amolghode1981", + "description": "The audio was not rendered because of re-rendering of react element based on\r\nqueueCounter and roomInfo. queueCounter and roomInfo cause the dom to re-render when call gets accepted\r\nbecause after accepting call, queueCounter changes or a room gets created.\r\nThe audio element gets recreated. But VoIP user probably holds the old one.\r\nThe behaviour is not predictable when such case happens. If everything gets cleanly setup,\r\neven if the audio element goes headless, it still continues to play the remote audio.\r\nBut in other cases, it is unreferenced the one on dom has its srcObject as null.\r\nThis causes no audio.\r\n\r\nThis fix provides a way to re-initialise the rendering elements in VoIP user\r\nand calls this function on useEffect() if the re-render has happen.", + "milestone": "4.5.0", "contributors": [ - "dougfabris", - "matheusbsilva137" + "amolghode1981" ] }, { - "pr": "23417", - "title": "[IMPROVE] Rewrite Edit Status Modal", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/136571489-76d9e218-440b-4e52-a802-508fc8a03d15.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/136571431-3b1a41c0-d419-437c-93c0-39c2e297dab5.png)", + "pr": "24596", + "title": "Regression: Fixes in Voice Contextual Bar and Directory", + "userLogin": "MartinSchoeler", + "milestone": "4.5.0", "contributors": [ - "dougfabris" + "MartinSchoeler" ] }, { - "pr": "23472", - "title": "[IMPROVE] Rewrite \"Message too long\" modal to react", - "userLogin": "gabriellsh", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/137536256-95ba71d2-4185-40b2-b13d-08138499c44b.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/40830821/137533488-e6c74a55-23c8-457e-a00b-f907767a3fe3.png)", + "pr": "24603", + "title": "Regression: Fix time format on Voip system messages", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "gabriellsh" + "murtaza98" ] }, { - "pr": "23462", - "title": "[FIX] Markdown quote message style", - "userLogin": "tiagoevanp", - "description": "Before:\r\n![image](https://user-images.githubusercontent.com/17487063/137496669-3abecab4-cf90-45cb-8b1b-d9411a5682dd.png)\r\n\r\nAfter:\r\n![image](https://user-images.githubusercontent.com/17487063/137496905-fd727f90-f707-4ec6-8139-ba2eb1a2146e.png)", + "pr": "24598", + "title": "Regression: VoIP service button displayed when VoIP is disabled", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "tiagoevanp" + "murtaza98" ] }, { - "pr": "22950", - "title": "[NEW] Stream to get individual presence updates", + "pr": "24581", + "title": "Regression: Add support to namespace within micro services", "userLogin": "sampaiodiego", "contributors": [ - "sampaiodiego", - "web-flow", - "ggazzo" + "sampaiodiego" ] }, { - "pr": "23396", - "title": "[FIX] Prevent starting Omni-Queue if Omnichannel is disabled", - "userLogin": "murtaza98", - "description": "Whenever the Routing system setting changes, and omnichannel is disabled, then we shouldn't start the queue.", - "milestone": "4.0.2", + "pr": "24583", + "title": "Regression: Error when trying to load name of dm rooms for avatars and notifications", + "userLogin": "pierre-lehnen-rc", "contributors": [ - "murtaza98" + "pierre-lehnen-rc" ] }, { - "pr": "23373", - "title": "[FIX] Merge teams as conversation when group by type is disabled", - "userLogin": "dougfabris", - "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/136074414-b23dfc49-7c6a-4fd3-b344-9973d4afe1ca.png)\r\n\r\n### after\r\n![image](https://user-images.githubusercontent.com/27704687/136074065-6f7483db-a506-4a94-ad9b-baf744b6b574.png)", + "pr": "24567", + "title": "[NEW] Marketplace sort filter", + "userLogin": "ujorgeleite", + "description": "Implemented a sort filter for the marketplace screen. This component sorts the marketplace apps list in 4 ways, alphabetical order(A-Z), inverse alphabetical order(Z-A), most recently updated(MRU), and least recent updated(LRU). Besides that, I've generalized some components and types to increase code reusability, renamed some helpers as well as deleted some useless ones, and inserted the necessary new translations on the English i18n dictionary.\r\nDemo gif:\r\n![Marketplace sort filter](https://user-images.githubusercontent.com/43561537/155033709-e07a6306-a85a-4f7f-9624-b53ba5dd7fa9.gif)", + "milestone": "4.5.0", "contributors": [ - "dougfabris" + "rique223", + "ujorgeleite" ] }, { - "pr": "23404", - "title": "[FIX][ENTERPRISE] Omnichannel agent is not leaving the room when a forwarded chat is queued", - "userLogin": "murtaza98", - "milestone": "4.0.2", + "pr": "23102", + "title": "[NEW] VoIP Support for Omnichannel", + "userLogin": "KevLehman", + "description": "- Created VoipService to manage VoIP connections and PBX connection\r\n- Created LivechatVoipService that will handle custom cases for livechat (creating rooms, assigning chats to queue, actions when call is finished, etc)\r\n- Created Basic interfaces to support new services and new model\r\n- Created Endpoints for management interfaces\r\n- Implemented asterisk connector on VoIP service\r\n- Created UI components to show calls incoming and to allow answering/rejecting calls\r\n- Added new settings to control call server/management server connection values\r\n- Added endpoints to associate Omnichannel Agents with PBX Extensions\r\n- Added support for event listening on server side, to get metadata about calls being received/ongoing\r\n- Created new pages to update settings & to see user-extension association\r\n- Created new page to see ongoing calls (and past calls)\r\n- Added support for remote hangup/hold on calls\r\n- Implemented call metrics calculation (hold time, waiting time, talk time)\r\n- Show a notificaiton when call is received", + "milestone": "4.5.0", "contributors": [ + "KevLehman", + "amolghode1981", + "web-flow", + "tiagoevanp", "murtaza98", - "web-flow" + "MartinSchoeler" ] }, { - "pr": "23419", - "title": "Chore: Partially migrate 2FA client code to TypeScript", - "userLogin": "tassoevan", - "description": "Additionally, hides `toastr` behind an module to handle UI's toast notifications.", + "pr": "24562", + "title": "Regression: Fix room not getting created due to null visitor status", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "murtaza98" ] }, { - "pr": "19457", - "title": "[FIX] Show jump to recent messages button", - "userLogin": "dragoneena12", - "description": "Show the button to scroll to the last message when you just moved up from last message.\r\n\r\n![image](https://user-images.githubusercontent.com/27704687/136297875-f07f07d2-945b-4365-ad4a-85138305cf50.png)", + "pr": "24573", + "title": "Chore: Bump Fuselage packages", + "userLogin": "tassoevan", + "description": "It uses the last stable version of Fuselage packages.", "contributors": [ - "dragoneena12", - "web-flow", - "dougfabris" + "tassoevan" ] }, { - "pr": "23342", - "title": "Chore: clean README", - "userLogin": "AbhJ", + "pr": "24558", + "title": "i18n: Language update from LingoHub 🤖 on 2022-02-21Z", + "userLogin": "lingohub[bot]", "contributors": [ - "AbhJ", + null, + "sampaiodiego", "web-flow" ] }, { - "pr": "23355", - "title": "Chore: Fixed a Typo in 11-admin.js test", - "userLogin": "badbart", + "pr": "24572", + "title": "[FIX] 2FA via email when logging in using OAuth", + "userLogin": "sampaiodiego", "contributors": [ - "badbart", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23405", - "title": "Chore: Document REST API endpoints (DNS)", - "userLogin": "tassoevan", - "description": "Describes endpoints for DNS on REST API using a JSDoc annotation compatible with OpenAPI spec.", + "pr": "24568", + "title": "Chore: Update Apps-Engine", + "userLogin": "d-gubert", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "d-gubert", + "web-flow" ] }, { - "pr": "23430", - "title": "Chore: Document REST API endpoints (E2E)", - "userLogin": "tassoevan", - "description": "Describes endpoints for end-to-end encryption on REST API using a JSDoc annotation compatible with OpenAPI spec.", + "pr": "24536", + "title": "Chore: roomTypes: Stop mixing client and server code together", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "pierre-lehnen-rc", + "tassoevan", + "web-flow" ] }, { - "pr": "23428", - "title": "Chore: Document REST API endpoints (Misc)", - "userLogin": "tassoevan", - "description": "Describes miscellaneous endpoints on REST API using a JSDoc annotation compatible with OpenAPI spec.", + "pr": "24529", + "title": "[IMPROVE] Replace AutoComplete in UserAutoComplete & UserAutoCompleteMultiple components", + "userLogin": "juliajforesti", + "description": "This PR replaces a deprecated fuselage's component `AutoComplete` in favor of `Select` and `MultiSelect` which fixes some of UX/UI issues in selecting users\r\n\r\n### before\r\n![Screen Shot 2022-02-19 at 13 33 28](https://user-images.githubusercontent.com/27704687/154809737-8181a06c-4f20-48ea-90f7-01e828b9a452.png)\r\n\r\n### after\r\n![Screen Shot 2022-02-19 at 13 30 58](https://user-images.githubusercontent.com/27704687/154809653-a8ec9a80-c0dd-4a25-9c00-0f96147d79e9.png)", "contributors": [ + "juliajforesti", + "dougfabris", "tassoevan" ] }, { - "pr": "20947", - "title": "[IMPROVE] Add markdown to custom fields in user Info", - "userLogin": "yash-rajpal", - "description": "Added markdown to custom fields to render links", + "pr": "24513", + "title": "Chore: Run tests using microservices deployment on CI", + "userLogin": "sampaiodiego", "contributors": [ - "yash-rajpal", - "dougfabris" + "sampaiodiego", + "web-flow", + "rodrigok" ] }, { - "pr": "23393", - "title": "[FIX] user/agent upload not working via Apps Engine after 3.16.0", - "userLogin": "murtaza98", - "description": "Fixes #22974", - "milestone": "4.0.2", + "pr": "24556", + "title": "Bump @types/ws from 8.2.2 to 8.2.3 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "murtaza98" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23377", - "title": "[FIX] Attachment buttons overlap in mobile view", - "userLogin": "Aman-Maheshwari", - "milestone": "4.0.2", + "pr": "24501", + "title": "Chore: Update fuselage deps to match monolith versions", + "userLogin": "sampaiodiego", "contributors": [ - "Aman-Maheshwari" + "sampaiodiego", + "web-flow" ] }, { - "pr": "23378", - "title": "[FIX] Users' `roles` and `type` being reset to default on LDAP DataSync", - "userLogin": "matheusbsilva137", - "description": "- Update `roles` and `type` fields only if they are specified in the data imported from LDAP (otherwise, no changes are applied).", - "milestone": "4.0.1", + "pr": "24538", + "title": "Bump adm-zip from 0.4.14 to 0.5.9", + "userLogin": "dependabot[bot]", "contributors": [ - "matheusbsilva137", - "sampaiodiego" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23382", - "title": "[FIX] LDAP not stoping after wrong password", - "userLogin": "rodrigok", - "milestone": "4.0.1", + "pr": "24454", + "title": "[IMPROVE] Purchase Type Filter for marketplace apps and Categories filter anchor refactoring", + "userLogin": "rique223", + "description": "Implemented a filter by purchase type(free or paid) component for the apps screen of the marketplace. Besides that, new entries on the dictionary, fixed some parts of the App type (purchaseType was typed as unknown and price as string), and created some helpers to work alongside the filter. Will be refactoring the categories filter anchor and then will open this PR for reviews.\r\n\r\nDemo gif:\r\n![purchaseTypeFIlter](https://user-images.githubusercontent.com/43561537/153101228-7b7ebdc3-2d34-420f-aa9d-f7cbc8d4b53f.gif)\r\n\r\nRefactored the categories filter anchor from a plain fuselage select to a select button with dynamic colors.\r\nDemo gif:\r\n![New categories filter anchor(PR)](https://user-images.githubusercontent.com/43561537/153422427-28012b7d-e0ec-45f4-861d-c9368c57ad04.gif)", "contributors": [ - "rodrigok" + "rique223", + "dougfabris", + "web-flow" ] }, { - "pr": "23381", - "title": "[FIX] MongoDB deprecation link", - "userLogin": "sampaiodiego", - "milestone": "4.0.1", + "pr": "24475", + "title": "[IMPROVE] Skip encryption for slash commands in E2E rooms", + "userLogin": "yash-rajpal", + "description": "Currently Slash Commands don't work in an E2EE room, as we encrypt the message before slash command is detected by the server, So removed encryption for slash commands in e2e rooms.", "contributors": [ - "sampaiodiego" + "yash-rajpal", + "albuquerquefabio", + "web-flow" ] }, { - "pr": "23385", - "title": "Chore: Remove dangling README file", - "userLogin": "tassoevan", - "description": "Removes the elderly `server/restapi/README.md`.", + "pr": "24304", + "title": "Chore: Js to ts slash commands archive", + "userLogin": "eduardofcabrera", + "description": "Convert Slash Commands archive files to typescript", "contributors": [ - "tassoevan" + "eduardofcabrera", + "web-flow" ] }, { - "pr": "23379", - "title": "[FIX] resumeToken not working", - "userLogin": "sampaiodiego", - "milestone": "4.0.1", + "pr": "24114", + "title": "[NEW] E2E password generator", + "userLogin": "ostjen", "contributors": [ - "sampaiodiego" + "ostjen", + "web-flow", + "eduardofcabrera", + "tassoevan" ] }, { - "pr": "23372", - "title": "[FIX] unwanted toastr error message when deleting user", - "userLogin": "ostjen", - "milestone": "4.0.1", + "pr": "24553", + "title": "[FIX] Omnichannel managers can't join chats in progress", + "userLogin": "renatobecker", + "milestone": "4.5.0", "contributors": [ - "ostjen", - "tassoevan", + "renatobecker", + "murtaza98", "web-flow" ] }, { - "pr": "23370", - "title": "Chore: Migrate some React components/hooks to TypeScript", - "userLogin": "tassoevan", - "description": "Just low-hanging fruits.", + "pr": "24559", + "title": "[FIX] Room context tabs not working in Omnichannel current chats page", + "userLogin": "murtaza98", + "milestone": "4.5.0", "contributors": [ - "tassoevan" + "murtaza98" ] }, { - "pr": "23366", - "title": "[FIX] BigBlueButton integration error due to missing file import", - "userLogin": "wolbernd", - "description": "Fixes BigBlueButton integration", - "milestone": "4.0.1", + "pr": "24173", + "title": "[FIX] respect `Accounts_Registration_Users_Default_Roles` setting", + "userLogin": "debdutdeb", + "description": "- Fix `user` role being added as default regardless of the `Accounts_Registration_Users_Default_Roles` setting.", + "milestone": "4.5.0", "contributors": [ - "wolbernd", - "web-flow" + "debdutdeb", + "web-flow", + "matheusbsilva137" ] }, { - "pr": "23375", - "title": "Chore: Update Apps-Engine version", - "userLogin": "d-gubert", - "milestone": "4.0.1", + "pr": "24485", + "title": "[FIX] Skip admin info in setup wizard for servers with admin registered", + "userLogin": "dougfabris", "contributors": [ - "d-gubert" + "dougfabris", + "tassoevan" ] }, { - "pr": "23374", - "title": "[FIX] imported migration v240", - "userLogin": "ostjen", - "milestone": "4.0.1", + "pr": "24537", + "title": "Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ostjen" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "22941", - "title": "[IMPROVE] optimized groups.listAll response time", + "pr": "24209", + "title": "[IMPROVE] Team system messages feedback", "userLogin": "ostjen", - "description": "groups.listAll endpoint was having performance issues, specially when the total number of groups was high. This happened because the endpoint was loading all objects in memory then using splice to paginate, instead of paginating beforehand.\r\n\r\nConsidering 70k groups, this was the performance improvement:\r\n\r\nbefore\r\n![image](https://user-images.githubusercontent.com/28611993/129601314-bdf89337-79fa-4446-9f44-95264af4adb3.png)\r\n\r\nafter\r\n![image](https://user-images.githubusercontent.com/28611993/129601358-5872e166-f923-4c1c-b21d-eb9507365ecf.png)", + "description": "- Delete some keys that aren't being used (eg: User_left_female).\r\n- Add new Teams' system messages:\r\n - `added-user-to-team`: **added** @\\user to this Team;\r\n - `removed-user-from-team`: **removed** @\\user from this Team;\r\n - `user-converted-to-team`: **converted** #\\room to a Team;\r\n - `user-converted-to-channel`: **converted** #\\room to a Channel;\r\n - `user-removed-room-from-team`: **removed** @\\user from this Team;\r\n - `user-deleted-room-from-team`: **deleted** #\\room from this Team;\r\n - `user-added-room-to-team`: **deleted** #\\room to this Team;\r\n- Add the corresponding options to hide each new system message and the missing `ujt` and `ult` hide options.", + "milestone": "4.5.0", "contributors": [ "ostjen", "tassoevan", - "web-flow" - ] - }, - { - "pr": "21673", - "title": "[IMPROVE] Update toast message for remove and delete rooms from teams", - "userLogin": "Darshilp326", - "description": "Added success messages to remove and delete room actions.\r\n\r\nBEFORE\r\n\r\nhttps://user-images.githubusercontent.com/55157259/115400805-520d3480-a207-11eb-9be9-8db9b6cda590.mp4\r\n\r\nAFTER\r\n\r\nhttps://user-images.githubusercontent.com/55157259/115400846-5afe0600-a207-11eb-9479-1d8e92fdc787.mp4", - "contributors": [ - "Darshilp326", - "dougfabris" + "web-flow", + "dougfabris", + "matheusbsilva137" ] }, { - "pr": "23213", - "title": "[FIX] Read only description in team creation", - "userLogin": "dougfabris", - "description": "![image](https://user-images.githubusercontent.com/27704687/133608433-8ca788a3-71a8-4d40-8c40-8156ab03c606.png)\r\n\r\n![image](https://user-images.githubusercontent.com/27704687/133608400-4cdc7a67-95e5-46c6-8c65-29ab107cd314.png)", + "pr": "24467", + "title": "Chore: Improve PR title validation regex", + "userLogin": "sampaiodiego", "contributors": [ - "dougfabris" + "sampaiodiego", + "web-flow", + "debdutdeb" ] }, { - "pr": "23364", - "title": "Chore: Upgrade Storybook", - "userLogin": "tassoevan", + "pr": "24058", + "title": "Bump date-fns from 2.24.0 to 2.28.0", + "userLogin": "dependabot[bot]", "contributors": [ - "tassoevan" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23360", - "title": "Chore: Move components away from /app/", - "userLogin": "tassoevan", - "description": "We currently do NOT recommend placing React components under `/app`.", + "pr": "24508", + "title": "[FIX] Read receipts showing first messages of the room as read even if not read by everyone", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan" + "sampaiodiego", + "web-flow", + "dougfabris" ] }, { - "pr": "23361", - "title": "Chore: Document REST API endpoints (banners)", - "userLogin": "tassoevan", - "description": "Describes endpoints for banners on REST API using a JSDoc annotation compatible with OpenAPI spec.", + "pr": "24530", + "title": "Chore: Remove storybook build job from CI", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan" + "sampaiodiego" ] }, { - "pr": "23339", - "title": "Release 4.0.0", - "userLogin": "sampaiodiego", + "pr": "24528", + "title": "Bump url-parse from 1.5.3 to 1.5.7", + "userLogin": "dependabot[bot]", "contributors": [ - "sampaiodiego", - "web-flow", - "lingohub[bot]", "dependabot[bot]", - "graywolf336", - "MarcosSpessatto", - "murtaza98", - "KevLehman" + "web-flow" ] }, { - "pr": "23362", - "title": "Merge master into develop & Set version to 4.1.0-develop", - "userLogin": "sampaiodiego", + "pr": "24333", + "title": "Chore: Add description to global OTR setting", + "userLogin": "pedrogssouza", "contributors": [ - "sampaiodiego", - "ggazzo", + "pedrogssouza", + "yash-rajpal", "web-flow" ] }, { - "pr": "23328", - "title": "Regression: invalid `call` import", - "userLogin": "tassoevan", + "pr": "24382", + "title": "[IMPROVE] OTR system messages", + "userLogin": "yash-rajpal", + "description": "OTR system messages to indicate key refresh and joining chat to users.", "contributors": [ - "tassoevan" + "yash-rajpal", + "web-flow" ] }, { - "pr": "23331", - "title": "Regression: LDAP: Handle base authentication and prevent crash", - "userLogin": "rodrigok", - "description": "When AD requires TLS the auth crashes the server if StartTLS is not set, the error shows at the end because the code was not waiting on this operation.", - "milestone": "4.0.0", + "pr": "24121", + "title": "[IMPROVE] Descriptive tooltip for Encrypted Key on Room Header", + "userLogin": "yash-rajpal", + "milestone": "4.5.0", "contributors": [ - "rodrigok", - "pierre-lehnen-rc", + "yash-rajpal", "web-flow" ] }, { - "pr": "23334", - "title": "Regression: invalid `call` import", - "userLogin": "tassoevan", + "pr": "24522", + "title": "Bump express from 4.17.2 to 4.17.3 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "tassoevan", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23321", - "title": "Regression: LDAP User Data Sync not always working", - "userLogin": "pierre-lehnen-rc", + "pr": "24518", + "title": "Chore: `twoFactorRequired` signature", + "userLogin": "tassoevan", + "description": "Improved type checking for decorator `twoFactorRequired`.", "contributors": [ - "pierre-lehnen-rc" + "tassoevan" ] }, { - "pr": "23333", - "title": "Regression: Removed exclusive tests statement", - "userLogin": "ostjen", + "pr": "24517", + "title": "Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ostjen" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23322", - "title": "Regression: Blank screen in Jitsi video calls", - "userLogin": "matheusbsilva137", - "description": "- Fix Jitsi calls being disposed even when \"Open in new window\" setting is disabled;\r\n - Fix misspelling on `CallJitsWithData.js` file name.", + "pr": "24441", + "title": "[FIX] GDPR action to forget visitor data on request", + "userLogin": "KevLehman", "contributors": [ - "matheusbsilva137" + "KevLehman", + "murtaza98", + "web-flow" ] }, { - "pr": "23330", - "title": "Regression: SAML identifier mapping", - "userLogin": "pierre-lehnen-rc", + "pr": "24306", + "title": "Chore: Convert to typescript the slash commands create files", + "userLogin": "eduardofcabrera", + "description": "Convert Slash Commands create files to typescript.", "contributors": [ - "pierre-lehnen-rc" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23319", - "title": "[BREAK] Moved SAML custom field map to EE", - "userLogin": "ostjen", - "contributors": [ + "pr": "24325", + "title": "Chore: Convert to typescript the mute and unmute slash commands files", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript the mute and unmute slash commands files", + "contributors": [ + "eduardofcabrera", "ostjen", - "sampaiodiego", "web-flow" ] }, { - "pr": "23320", - "title": "Regression: \"Join\" button not working", - "userLogin": "tassoevan", + "pr": "24321", + "title": "Chore: Convert to typescript the me slashCommands files", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript the me slashCommands files", "contributors": [ - "tassoevan" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23318", - "title": "Regression: Add default value when no cookies are present", - "userLogin": "KevLehman", + "pr": "23512", + "title": "Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "KevLehman" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23317", - "title": "Regression: Request seats url", - "userLogin": "gabriellsh", + "pr": "24311", + "title": "Chore: Convert to typescript the slash commands invite files", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript the slash commands invite files", "contributors": [ - "gabriellsh" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23310", - "title": "[BREAK] Webhook will fail if user is not part of the channel", - "userLogin": "sampaiodiego", - "description": "Remove deprecated behavior added by https://github.com/RocketChat/Rocket.Chat/pull/18024 that accepts webhook integrations sending messages even if the user is not part of the channel.\r\n\r\nStarting from 4.0.0 the webhook request will fail with `error-not-allowed` error:\r\n\r\n```\r\n{\"success\":false,\"error\":\"error-not-allowed\"}\r\n```", + "pr": "24509", + "title": "Bump vm2 from 3.9.5 to 3.9.7 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "sampaiodiego" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23311", - "title": "Regression: LDAP Channel/Role Sync not working", - "userLogin": "pierre-lehnen-rc", + "pr": "24451", + "title": "[IMPROVE] ChatBox Text to File Description", + "userLogin": "eduardofcabrera", + "description": "The text content from chatbox goes to the file description when drag and drop a file.", "contributors": [ - "pierre-lehnen-rc" + "eduardofcabrera", + "ostjen", + "web-flow", + "dougfabris" ] }, { - "pr": "23312", - "title": "Regression: Request seats link", - "userLogin": "gabriellsh", + "pr": "24461", + "title": "Chore: Update Meteor to 2.5.6", + "userLogin": "sampaiodiego", "contributors": [ - "gabriellsh" + "sampaiodiego", + "rodrigok", + "web-flow" ] }, { - "pr": "23306", - "title": "Regression: LDAP Issues", - "userLogin": "pierre-lehnen-rc", + "pr": "24477", + "title": "Chore: Update ws package", + "userLogin": "sampaiodiego", "contributors": [ - "pierre-lehnen-rc" + "sampaiodiego", + "web-flow" ] }, { - "pr": "23302", - "title": "[BREAK] Remove cordova compatibility setting", - "userLogin": "ostjen", + "pr": "24498", + "title": "Bump underscore.string from 3.3.5 to 3.3.6 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "ostjen" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23308", - "title": "Regression: Fix Bugsnag not started error", - "userLogin": "sampaiodiego", + "pr": "24491", + "title": "Bump follow-redirects from 1.14.7 to 1.14.8 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "sampaiodiego" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23307", - "title": "Regression: Change some logs to new format", - "userLogin": "KevLehman", - "milestone": "3.18.2", + "pr": "24493", + "title": "i18n: Language update from LingoHub 🤖 on 2022-02-14Z", + "userLogin": "lingohub[bot]", "contributors": [ - "KevLehman" + null ] }, { - "pr": "23297", - "title": "Regression: Create new loggers based on server log level", - "userLogin": "sampaiodiego", + "pr": "24331", + "title": "Chore: Convert to typescript the unarchive slash commands files", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript the unarchive slash commands files", "contributors": [ - "sampaiodiego" + "eduardofcabrera", + "ostjen", + "web-flow" ] }, { - "pr": "23304", - "title": "Regression: Fix channel icons on queue", - "userLogin": "MartinSchoeler", - "milestone": "4.0.0", + "pr": "24483", + "title": "[IMPROVE] Add tooltips on action buttons of Canned Response message composer", + "userLogin": "LucasFASouza", + "description": "The tooltips were missing on the action buttons of CR message composer.\r\n\r\n![image](https://user-images.githubusercontent.com/32396925/153620327-91107245-4b47-4d39-a99a-6da6d1cf5734.png)\r\n\r\nUsers can now feel more encouraged to use these actions knowing what they are supposed to do.", "contributors": [ - "KevLehman", - "MartinSchoeler" + "LucasFASouza", + "tiagoevanp", + "web-flow" ] }, { - "pr": "23280", - "title": "[FIX] Update visitor info on email reception based on current inbox settings", - "userLogin": "KevLehman", - "milestone": "3.18.2", + "pr": "24196", + "title": "Chore: Delete unused file (NewAdminInfoPage.js)", + "userLogin": "gabriellsh", + "description": "Just removing a duplicated/unused file.", + "milestone": "4.5.0", "contributors": [ - "KevLehman", - "murtaza98", + "gabriellsh", "web-flow" ] }, { - "pr": "23286", - "title": "Regression: Fix app storage migration", - "userLogin": "thassiov", - "description": "The previous version of this migration didn't take into consideration apps that were installed prior to [Rocket.Chat@3.8.0](https://github.com/RocketChat/Rocket.Chat/releases/tag/3.8.0), which [removed the typescript compiler from the server](https://github.com/RocketChat/Rocket.Chat/pull/18687) and into the CLI. As a result, the zip files inside each installed app's document in the database had typescript files in them instead of the now required javascript files.\r\n\r\nAs the new strategy of source code storage for apps changes the way the app is loaded, those zip files containing the source code are read everytime the app is started (or [in this particular case, updated](https://github.com/RocketChat/Rocket.Chat/pull/23286/files#diff-caf9f7a22478639e58d6514be039140a42ce1ab2d999c3efe5678c38ee36d0ccR43)), and as the zips' contents were wrong, the operation was failing.\r\n\r\nThe fix extract the data from old apps and creates new zip files with the compiled `js` already present.", + "pr": "24388", + "title": "[IMPROVE][ENTERPRISE] Improve how micro services are loaded", + "userLogin": "ggazzo", "contributors": [ - "thassiov" + "ggazzo", + "sampaiodiego" ] }, { - "pr": "23278", - "title": "Regression: Seats Cap banner not being disabled if not enterprise", - "userLogin": "gabriellsh", + "pr": "24458", + "title": "[IMPROVE] Add return button in chats opened from the list of current chats", + "userLogin": "LucasFASouza", + "description": "The new return button for Omnichannel chats came out with release 3.15 but the feature was only available for chats that were opened from Omnichannel Contact Center.\r\nNow, the same UI/UX is supported for chats opened from Current Chats list.\r\n\r\n![image](https://user-images.githubusercontent.com/32396925/153283190-bd5c9748-c36b-4874-a704-6043afc7e3a1.png)\r\n\r\nThe chat now opens in the Omnichannel settings and has the return button so the user can go back to the Current Chats list.\r\n\r\n![image](https://user-images.githubusercontent.com/32396925/153285591-fad8e4a0-d2ea-4a02-8b2a-15e383b3c876.png)", "contributors": [ - "gabriellsh" + "LucasFASouza", + "tiagoevanp", + "web-flow" ] }, { - "pr": "23218", - "title": "[FIX] Sidebar not closing when clicking in Home or Directory on mobile view", - "userLogin": "dougfabris", - "description": "### Additional fixed\r\n- Merge Burger menu components into a single component\r\n- Show a badge with no-read messages in the Burger Button:\r\n![image](https://user-images.githubusercontent.com/27704687/133679378-20fea2c0-4ac1-4b4e-886e-45154cc6afea.png)\r\n- remove useSidebarClose hook", + "pr": "24469", + "title": "Bump express from 4.17.1 to 4.17.2 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "dougfabris", - "ggazzo", + "dependabot[bot]", "web-flow" ] }, { - "pr": "23281", - "title": "Regression: wrong settings order", - "userLogin": "pierre-lehnen-rc", + "pr": "24472", + "title": "Bump cookie from 0.4.1 to 0.4.2 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "pierre-lehnen-rc" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "22407", - "title": "[FIX] Prevent users to edit an existing role when adding a new one with the same name used before.", - "userLogin": "dougfabris", - "description": "### before\r\n![Peek 2021-07-13 16-31](https://user-images.githubusercontent.com/27704687/125513721-953d84f4-1c95-45ca-80e1-b00992b874f6.gif)\r\n\r\n### after\r\n![Peek 2021-07-13 16-34](https://user-images.githubusercontent.com/27704687/125514098-91ee8014-51e5-4c62-9027-5538acf57d08.gif)", + "pr": "24275", + "title": "[IMPROVE] Close modal on esc and outside click", + "userLogin": "gabriellsh", + "description": "This is a QUICK change in order to close modals pressing Esc button and clicking outside of it **intentionally**.", + "milestone": "4.5.0", "contributors": [ - null, - "lucassartor", + "gabriellsh", "dougfabris", - "ggazzo", - "web-flow", - "pierre-lehnen-rc", "tassoevan" ] }, { - "pr": "23282", - "title": "Regression: Missing i18n key", - "userLogin": "pierre-lehnen-rc", + "pr": "24435", + "title": "Chore(deps-dev): Bump ts-node from 10.0.0 to 10.5.0 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "pierre-lehnen-rc" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "23201", - "title": "[BREAK] Moved advanced oAuth features to EE", - "userLogin": "ostjen", + "pr": "24041", + "title": "[IMPROVE] Add user to room on \"Click to Join!\" button press", + "userLogin": "matheusbsilva137", + "description": "- Add user to room on \"Click to Join!\" button press;\r\n- Display the \"Join\" button in discussions inside channels (keeping the behavior consistent with discussions inside groups).", "contributors": [ - "ostjen", + "matheusbsilva137", "web-flow", - "pierre-lehnen-rc" - ] - }, - { - "pr": "23256", - "title": "[IMPROVE] Better text for auth banner", - "userLogin": "g-thome", - "description": "Change the text in the banner warning for auth changes", - "contributors": [ - "g-thome", "tassoevan", - "web-flow", - "pierre-lehnen-rc" + "pierre-lehnen-rc", + "ostjen" ] }, { - "pr": "23090", - "title": "[NEW] Omnichannel source identification fields", - "userLogin": "d-gubert", - "description": "This PR adds new fields to the room schema that aids in the identification of the source that created an Omnichannel room, which can be either via livechat widget, SMS, app, etc.", - "milestone": "4.0.0", + "pr": "24310", + "title": "[FIX] Implement client errors on ddp-streamer", + "userLogin": "sampaiodiego", "contributors": [ - "d-gubert", - "KevLehman", - "web-flow", - "tiagoevanp", - "MartinSchoeler" + "sampaiodiego", + "web-flow" ] }, { - "pr": "23231", - "title": "Regression: LDAP Refactoring", - "userLogin": "pierre-lehnen-rc", + "pr": "23963", + "title": "Bump body-parser from 1.19.0 to 1.19.1 in /ee/server/services", + "userLogin": "dependabot[bot]", "contributors": [ - "pierre-lehnen-rc", - "web-flow", - "ggazzo" + "dependabot[bot]", + "web-flow" ] }, { - "pr": "22657", - "title": "[IMPROVE][APPS] New storage strategy for Apps-Engine file packages", + "pr": "23961", + "title": "Bump jaeger-client from 3.18.1 to 3.19.0 in /ee/server/services", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "24466", + "title": "[FIX] typo on register server tooltip of setup wizard", + "userLogin": "filipemarins", + "milestone": "4.5.0", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "24037", + "title": "[FIX] Inconsistent validation of user's access to rooms", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc", + "ostjen", + "web-flow" + ] + }, + { + "pr": "24450", + "title": "[FIX] OAuth mismatch redirect_uri error", + "userLogin": "sampaiodiego", + "milestone": "4.4.2", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "24305", + "title": "[FIX] Prevent Apps Bridge to remove visitor status from room", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman", + "d-gubert" + ] + }, + { + "pr": "24453", + "title": "Chore: bump fuselage version", + "userLogin": "dougfabris", + "milestone": "4.4.2", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "24253", + "title": "[FIX] Issues on selecting users when importing CSV", + "userLogin": "guijun13", + "description": "* Fix users selecting by fixing their _id\r\n* Add condition to disable 'Start importing' button if `usersCount`, `channelsCount` and `messageCount` equals 0, or if messageCount is alone\r\n* Remove `disabled={usersCount === 0}` on user Tab", + "contributors": [ + "guijun13", + "tassoevan", + "web-flow", + "pierre-lehnen-rc" + ] + }, + { + "pr": "24299", + "title": "Chore(deps): Bump node-fetch from 2.6.1 to 2.6.7 in /ee/server/services", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "24418", + "title": "[FIX] Oembed request not respecting payload limit", + "userLogin": "sampaiodiego", + "milestone": "4.4.1", + "contributors": [ + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "24429", + "title": "i18n: Language update from LingoHub 🤖 on 2022-02-07Z", + "userLogin": "lingohub[bot]", + "contributors": [ + null, + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "24407", + "title": "[FIX] Skip cloud steps for registered servers on setup wizard", + "userLogin": "dougfabris", + "milestone": "4.4.1", + "contributors": [ + "dougfabris", + "tassoevan", + "gabriellsh", + "web-flow" + ] + }, + { + "pr": "24410", + "title": "Chore: Convert JS files to Typescript", + "userLogin": "felipe-rod123", + "description": "This pull request converts 26 more files from Javascript to Typescript, to check variable types and increase validation on the code.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "24369", + "title": "[IMPROVE] Convert tag edit with department data to tsx", + "userLogin": "LucasFASouza", + "contributors": [ + "LucasFASouza", + "tiagoevanp", + "web-flow" + ] + }, + { + "pr": "24401", + "title": "[FIX] Outgoing webhook without scripts not saving messages", + "userLogin": "sampaiodiego", + "milestone": "4.4.1", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "24334", + "title": "[IMPROVE] CloudLoginModal visual consistency", + "userLogin": "dougfabris", + "description": "### before\r\n![image](https://user-images.githubusercontent.com/27704687/151585064-dc6a1e29-9903-4241-8fbd-dfbe6c55fbef.png)\r\n\r\n### after\r\n![Screen Shot 2022-01-28 at 13 32 02](https://user-images.githubusercontent.com/27704687/151585101-75b98502-9aae-4198-bc3e-4956750e5d8b.png)", + "milestone": "4.5.0", + "contributors": [ + "dougfabris", + "gabriellsh", + "web-flow" + ] + }, + { + "pr": "24409", + "title": "[FIX] Startup errors creating indexes", + "userLogin": "sampaiodiego", + "description": "Fix `bio` and `prid` startup index creation errors.", + "milestone": "4.4.1", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "24406", + "title": "Chore: Unify ILivechatAgent with ILivechatAgentRecord", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "24381", + "title": "[FIX] Add ?close to OAuth callback url", + "userLogin": "sampaiodiego", + "milestone": "4.4.1", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "24387", + "title": "[FIX] Slash commands previews not working", + "userLogin": "ostjen", + "milestone": "4.4.1", + "contributors": [ + "ostjen" + ] + }, + { + "pr": "24357", + "title": "i18n: Language update from LingoHub 🤖 on 2022-01-31Z", + "userLogin": "lingohub[bot]", + "contributors": [ + null + ] + }, + { + "pr": "24341", + "title": "Bump simple-get from 4.0.0 to 4.0.1", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "24366", + "title": "Chore: Set Docker image tag to latest only when really latest", + "userLogin": "debdutdeb", + "contributors": [ + "debdutdeb", + "web-flow" + ] + }, + { + "pr": "24109", + "title": "[IMPROVE] Added a new \"All\" tab which shows all integrations in Integrations", + "userLogin": "aswinidev", + "milestone": "4.5.0", + "contributors": [ + "aswinidev", + "dougfabris", + "web-flow" + ] + }, + { + "pr": "24363", + "title": "Merge master into develop & Set version to 4.5.0-develop", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "web-flow" + ] + } + ] + }, + "4.5.7": { + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [] + }, + "5.0.0-rc.0": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "26107", + "title": "Chore: move fork of cas module to the monorepo", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25681", + "title": "Chore: Add Agenda fork to the monorepo", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc", + "ggazzo" + ] + }, + { + "pr": "25624", + "title": "Chore: Bump deps", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "pierre-lehnen-rc", + "web-flow" + ] + }, + { + "pr": "25791", + "title": "[NEW][ENTERPRISE] Device Management", + "userLogin": "yash-rajpal", + "milestone": "5.0.0", + "contributors": [ + "yash-rajpal", + "web-flow", + "albuquerquefabio", + "debdutdeb" + ] + }, + { + "pr": "26040", + "title": "Chore: `refactor/tsc-perf`", + "userLogin": "tassoevan", + "milestone": "5.0.0", + "contributors": [ + "tassoevan", + "ggazzo", + "pierre-lehnen-rc", + "web-flow" + ] + }, + { + "pr": "26100", + "title": "[BREAK] Upgrade to version 5.0 can be done only from version 4.x", + "userLogin": "sampaiodiego", + "milestone": "5.0.0", + "contributors": [ + "sampaiodiego", + "pierre-lehnen-rc" + ] + }, + { + "pr": "26098", + "title": "[BREAK] Remove support to old MongoDB versions", + "userLogin": "sampaiodiego", + "description": "As per MongoDB Lifecycle Schedules (https://www.mongodb.com/support-policy/lifecycles) we're removing official support to MongoDB versions **3.6 and 4.0** that have already reached end-of-life.\r\n\r\nAs MongoDB 4.2 was a \"supported\" version before Rocket.Chat 5.0, we'll continue supporting it, but will be flagged as deprecated. We recommend upgrading to MongoDB 4.4+.\r\n\r\nHere are official docs on how to upgrade to some of the supported versions:\r\n\r\n- https://www.mongodb.com/docs/manual/release-notes/4.2-upgrade-replica-set/\r\n- https://www.mongodb.com/docs/v4.4/release-notes/4.4-upgrade-replica-set/\r\n- https://www.mongodb.com/docs/manual/release-notes/5.0-upgrade-replica-set/", + "milestone": "5.0.0", + "contributors": [ + "sampaiodiego", + "KevLehman", + "debdutdeb", + "web-flow" + ] + }, + { + "pr": "25847", + "title": "[NEW] Matrix Federation UX improvements", + "userLogin": "alansikora", + "milestone": "5.0.0", + "contributors": [ + "MarcosSpessatto", + "web-flow", + "carlosrodrigues94", + "alansikora" + ] + }, + { + "pr": "26081", + "title": "[NEW][ENTERPRISE] Introducing dial pad component into sidebar, calls table, contextual bar", + "userLogin": "aleksandernsilva", + "description": "This PR adds a new call button that can be used from Sidebar & Contact Center. This also enables Omnichannel agents to make outbound calls from within Rocket.Chat.\r\n\r\nDepending on your server and call server configuration, you can do international calling, national and domestic calling.\r\n\r\nThe buttons on Contact Center allows an agent to call an existing number without having to type the number again.", + "milestone": "5.0.0", + "contributors": [ + "ggazzo", + "aleksandernsilva", + "KevLehman" + ] + }, + { + "pr": "26053", + "title": "Chore: Settings UI issue", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "26064", + "title": "Chore: Adding default message parser template", + "userLogin": "hugocostadev", + "milestone": "5.0.0", + "contributors": [ + "hugocostadev", + "gabriellsh", + "sampaiodiego" + ] + }, + { + "pr": "26099", + "title": "Regression: [VideoConference] If the caller loses connection, direct calls are never canceled", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "26094", + "title": "Chore: Handle errors on index creation", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "26095", + "title": "Chore: fix watermark condition", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "24534", + "title": "[FIX] Validate room access", + "userLogin": "albuquerquefabio", + "description": "The request must be blocked If the user has no permission to view rooms.", + "milestone": "5.0.0", + "contributors": [ + "albuquerquefabio", + "web-flow", + "yash-rajpal", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25570", + "title": "[BREAK] VideoConference", + "userLogin": "dougfabris", + "description": "In this PR we're deprecating the Video Conference functionality from the core of the application and introducing a **new video conference flow**:\r\n\r\n\r\n\r\nNow the video conference feature will be agnostic so you'll be able to set the provider such as **Jisti** and **BBB** as apps from our marketplace: \r\n\r\n\r\n\r\nVideo conferences settings are now global, allowing you to set the default provider\r\n\r\n\r\n\r\n### [Enterprise Features]\r\n- Video Conferences List\r\n\r\n\r\n- Ringing function for direct messages\r\n\r\n\r\n\r\n", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "web-flow", + "dougfabris" + ] + }, + { + "pr": "26083", + "title": "[FIX] Undefined headers on API Client", + "userLogin": "yash-rajpal", + "milestone": "5.0.0", + "contributors": [ + "yash-rajpal" + ] + }, + { + "pr": "26067", + "title": "Regression: Add Error boundary to katex render component", + "userLogin": "gabriellsh", + "milestone": "5.0.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "26084", + "title": "Chore: Allow endpoints to optionally require authentication", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "26088", + "title": "Regression: Unhandled Exceptions metric causing a secondary exception", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "26057", + "title": "[FIX] Unable to close chats when comments is disabled", + "userLogin": "murtaza98", + "description": "Fixes https://github.com/RocketChat/Rocket.Chat/issues/25954", + "milestone": "5.0.0", + "contributors": [ + "murtaza98", + "web-flow" + ] + }, + { + "pr": "26086", + "title": "Chore: Room access validation may be called without user information", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25491", + "title": "[IMPROVE] Avoid using omnichannel-queue collection", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "murtaza98" + ] + }, + { + "pr": "26087", + "title": "[FIX] Remove duplicated property _USERNAMES from createDirectRoom.ts", + "userLogin": "felipe-rod123", + "description": "This pull request removes the duplicated property `_USERNAMES` from `apps/meteor/app/lib/server/functions/createDirectRoom.ts`, using only the existing property `roomInfo.usernames`.", + "contributors": [ + "felipe-rod123" + ] + }, + { + "pr": "26085", + "title": "Chore: Improve footer Template", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "tiagoevanp" + ] + }, + { + "pr": "26055", + "title": "Regression: Fix call direction", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25949", + "title": "[NEW][APPS] Allow dispatchment of actions from input elements", + "userLogin": "thassiov", + "description": "This allows for apps receiving block actions when a user types on a plain text input field or selects an item from the static. A debounce of 700 ms is done when listening for typing action so the app is not flooded with actions.\r\n\r\n\r\nhttps://user-images.githubusercontent.com/733282/174858175-5ea53046-c791-493e-859b-b80431e94ffa.mp4", + "milestone": "5.0.0", + "contributors": [ + "ggazzo", + "thassiov", + "d-gubert" + ] + }, + { + "pr": "26077", + "title": "Regression: Revert Livechat packages upgrades/removals that were causing issues", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "26079", + "title": "Regression: Users Table loading state", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "26074", + "title": "Regression: Fix import endpoints", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "20913", + "title": "[BREAK] Suspend push notifications when login token is invalidated", + "userLogin": "g-thome", + "description": "link the auth token to the push token", + "milestone": "5.0.0", + "contributors": [ + "g-thome", + "sampaiodiego", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25724", + "title": "[FIX] Not showing edit message button when blocking edit after N minutes", + "userLogin": "matthias4217", + "description": "Previously, in Rocketchat 4.7.0 and later, as mentioned in https://github.com/RocketChat/Rocket.Chat/issues/25478, the edit button was not displayed on the interface in the minute after having sent a message. This is now fixed : messages can be edited right after sending them.", + "milestone": "5.0.0", + "contributors": [ + "matthias4217", + "sampaiodiego" + ] + }, + { + "pr": "25331", + "title": "[FIX] Misaligned username on Room Info card for omnichannel chats", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "26075", + "title": "Chore: Revert `yarn dev` implementation", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "26063", + "title": "Regression: Contact manager endpoint usage", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25965", + "title": "[NEW] Create releases tab in the marketplace app info page", + "userLogin": "rique223", + "description": "Added a Releases tab to the app info page of installed marketplace apps. This tab will show all the released versions of a given app with its version number, release date in humanized form, and the changelog of this given release with the information provided by the publisher, this changelog accepts and renders markdown. Also refactored some component names and logic for maintainability reasons.\r\nDemo gif:\r\n![app-releases-tab-final](https://user-images.githubusercontent.com/43561537/176228928-651074ce-1f8b-4531-95be-1dd107938bf3.gif)", + "milestone": "5.0.0", + "contributors": [ + "rique223" + ] + }, + { + "pr": "26071", + "title": "Regression: `yarn dev` not working", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "26070", + "title": "Chore: Close tooltip on click", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "26069", + "title": "Chore: Make kodiak merge message empty", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25930", + "title": "[FIX] Too many watchers in dev environment.", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25855", + "title": "[FIX] Update subscription on update team member", + "userLogin": "LucianoPierdona", + "description": "Added update to subscription when a team member is updated on `teams.updateMember`", + "milestone": "5.0.0", + "contributors": [ + "LucianoPierdona", + "matheusbsilva137", + "web-flow" + ] + }, + { + "pr": "25988", + "title": "Regression: Add appId prop to slashcommand", + "userLogin": "tapiarafael", + "description": "Pass the appId when present to the slashcommand array. This avoid problems with contextual bar and modals not opening.", + "milestone": "5.0.0", + "contributors": [ + "tapiarafael" + ] + }, + { + "pr": "26065", + "title": "Chore: Convert useSidebarPaletteColor", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "26058", + "title": "[FIX] Error \"numRequestsAllowed\" property in rateLimiter for REST API endpoint when upgrading", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "26051", + "title": "[FIX] Remove duplicated icon bell when is thread main message", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "26059", + "title": "Chore: Convert normalizeMessagesForUser", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25916", + "title": "Chore: ui-client package", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "26056", + "title": "Regression: Invalid Voip host issue preventing agents connecting to asterisk", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "22588", + "title": "[FIX] Direct Reply", + "userLogin": "ggazzo", + "milestone": "5.0.0", + "contributors": [ + "ggazzo", + "dougfabris" + ] + }, + { + "pr": "25913", + "title": "[NEW][APPS] Allow apps to modify a subset of global settings", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25844", + "title": "[NEW] Community Edition Watermark", + "userLogin": "hugocostadev", + "milestone": "5.0.0", + "contributors": [ + "hugocostadev", + "tassoevan", + "ggazzo" + ] + }, + { + "pr": "25889", + "title": "[BREAK] remove unused endpoints and restify others", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "murtaza98" + ] + }, + { + "pr": "25993", + "title": "[IMPROVE] VoIP admin page cleanup: remove unused settings", + "userLogin": "cauefcr", + "description": "https://app.clickup.com/t/2n4m61m", + "milestone": "5.0.0", + "contributors": [ + "cauefcr", + "murtaza98", + "web-flow", + "KevLehman" + ] + }, + { + "pr": "26054", + "title": "Regression: Fix micro services", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "26052", + "title": "Regression: Fix threads list", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25966", + "title": "[NEW] VoIP Input/Output Device Selection", + "userLogin": "MartinSchoeler", + "milestone": "5.0.0", + "contributors": [ + "amolghode1981", + "web-flow", + "MartinSchoeler", + "KevLehman", + "aleksandernsilva" + ] + }, + { + "pr": "25929", + "title": "Chore: Account/Profile to TS", + "userLogin": "yash-rajpal", + "contributors": [ + "yash-rajpal", + "ggazzo" + ] + }, + { + "pr": "26048", + "title": "Chore: Add missing Swedish livechat translations", + "userLogin": "joakimaho", + "description": "Added missing Swedish translations.", + "contributors": [ + "joakimaho", + "web-flow", + "sampaiodiego" + ] + }, + { + "pr": "25970", + "title": "[IMPROVE] Expand the feature set of the new message rendering", + "userLogin": "tassoevan", + "description": "- Everything inside a new package (`@rocket.chat/gazzodown`);\r\n- KaTeX support;\r\n- Highlighted Words support;\r\n- Emoji rendering expanded;\r\n- Code rendering fixed", + "contributors": [ + "tassoevan", + "gabriellsh" + ] + }, + { + "pr": "26036", + "title": "Chore: Bump fuselage and update icon", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25937", + "title": "[NEW][APPS] Allowing apps to register authenticated routes", + "userLogin": "d-gubert", + "description": "Adds adaptations that allow apps to declare an API endpoint that requires authorization from Rocket.Chat prior to executing", + "milestone": "5.0.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25960", + "title": "[NEW] Enable outbound calling for EE (#25843)", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "amolghode1981", + "web-flow", + "KevLehman", + "murtaza98", + "aleksandernsilva", + "tiagoevanp", + "ggazzo" + ] + }, + { + "pr": "26047", + "title": "Chore: Introduce new index to query active livechat conversations for cloud scaling", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25934", + "title": "[FIX] Importer fails to download files from URLs with query string params", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "ggazzo" + ] + }, + { + "pr": "26007", + "title": "[IMPROVE] Moved call hold/unhold to EE", + "userLogin": "aleksandernsilva", + "description": "This PR adds a restriction, enabling the feature to hold/unhold calls only for Enterprise Edition users.", + "milestone": "5.0.0", + "contributors": [ + "aleksandernsilva", + "murtaza98" + ] + }, + { + "pr": "25505", + "title": "[NEW] Engagement Metrics - Phase 2", + "userLogin": "matheusbsilva137", + "description": "Add the following new statistics (metrics):\r\n - Total Broadcast rooms\r\n - Total rooms with an active Livestream;\r\n - Total triggered emails;\r\n - Total subscription roles;\r\n - Total User Roles;\r\n - Total uncaught exceptions;\r\n - `homeTitleChanged`: boolean value to indicate whether the `Layout_Home_Title` setting has been changed;\r\n - `homeBodyChanged`: boolean value to indicate whether the `Layout_Home_Body` setting has been changed;\r\n - `customCSSChanged`: boolean value to indicate whether the `theme-custom-css` setting has been changed;\r\n - `onLogoutCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_On_Logout` setting has been changed;\r\n - `loggedOutCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_Logged_Out` setting has been changed;\r\n - `loggedInCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_Logged_In` setting has been changed;\r\n - `matrixBridgeEnabled`: boolean value to indicate whether the Matrix bridge has been enabled;", + "milestone": "5.0.0", + "contributors": [ + "matheusbsilva137", + "web-flow" + ] + }, + { + "pr": "26035", + "title": "Chore: Convert usePreventDefault, useQueryOptions, useShortcutOpenMenu", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25919", + "title": "[FIX] Importer files are unnecessarily transferred over the network.", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "ggazzo" + ] + }, + { + "pr": "26038", + "title": "Chore: test turbo params", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "26023", + "title": "Chore: Create a token for each action", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25622", + "title": "Chore: Migrate oembed to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman", + "ggazzo" + ] + }, + { + "pr": "26024", + "title": "Regression: Fix voip call wrap-up model not working", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "26001", + "title": "Chore: Updating Apps-Engine ", + "userLogin": "d-gubert", + "milestone": "5.0.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25643", + "title": "[IMPROVE] Differ Voip calls from Incoming and Outgoing", + "userLogin": "murtaza98", + "description": "Updated this column and its respective endpoints to support inbound/outfound call definitions\r\n![image](https://user-images.githubusercontent.com/34130764/170512008-34202ed8-3ed4-4c28-baa5-25efc17543d5.png)", + "milestone": "5.0.0", + "contributors": [ + "murtaza98", + "web-flow", + "KevLehman" + ] + }, + { + "pr": "24379", + "title": "[FIX] Append path To Route For Custom Emoji", + "userLogin": "nishant23122000", + "milestone": "5.0.0", + "contributors": [ + "nishant23122000", + "web-flow" + ] + }, + { + "pr": "25875", + "title": "[IMPROVE] Moved call wrap up modal to EE", + "userLogin": "aleksandernsilva", + "description": "This PR adds a restriction, enabling the feature to display the call wrap up modal only for Enterprise Edition users.", + "contributors": [ + "aleksandernsilva", + "tiagoevanp" + ] + }, + { + "pr": "26015", + "title": "Chore: Major refactors in pageobjects", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "26002", + "title": "[BREAK] Remove show message in main thread preference", + "userLogin": "dougfabris", + "description": "This PR removes the confusion between the `show message in main thread` and the function `also to send to channel`. In the past, we used the `show message in main thread` as a solution to help users to understand the thread feature, as this feature is now mature enough there's no reason to maintain this preference. \r\n\r\nSend the thread message to the main channel or just inside of the thread, should be a decision from the user where the function `also send to channel` appears. Because of that, and because of a bunch of requests and issues we received, we're introducing a new preference `also send thread to channel` where users will be able to decide the behavior of the checkbox. \r\n\r\n![image](https://user-images.githubusercontent.com/27704687/175655594-023c5907-adc8-4924-ba7d-467608d06fec.png)\r\n\r\nNow there are three behavior options\r\n- `Default`: when it unchecks after sending the first message\r\n\r\n\r\n- `Always`: stay checked for all messages\r\n\r\n\r\n- `Never`: stay unchecked for all messages\r\n", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "26008", + "title": "Regression: Webhook Integration Creation + string error toast msg", + "userLogin": "dudanogueira", + "milestone": "5.0.0", + "contributors": [ + "dudanogueira", + "debdutdeb", + "web-flow" + ] + }, + { + "pr": "25958", + "title": "Chore: convert e2e to ts", + "userLogin": "felipe-rod123", + "description": "Converted the `apps/meteor/app/api/server/v1/e2e.js` to ts and created endpoint typings on the `packages/rest-typings/src/v1/e2e` folder.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25928", + "title": "Regression: Room Endpoint Call Issues", + "userLogin": "LucianoPierdona", + "description": "This PR fixes small management bugs related with channels, rooms and teams", + "milestone": "5.0.0", + "contributors": [ + "LucianoPierdona" + ] + }, + { + "pr": "25984", + "title": "Chore: Fixes e2e playwright intermittences", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon", + "ggazzo", + "weslley543" + ] + }, + { + "pr": "26004", + "title": "Chore: Fuselage update", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25963", + "title": "Chore: Small fix on callProvider", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp", + "ggazzo" + ] + }, + { + "pr": "26000", + "title": "[FIX] Initial members value on Create Channel Modal", + "userLogin": "dougfabris", + "description": "#### before\r\n![Screen Shot 2022-06-24 at 11 58 22](https://user-images.githubusercontent.com/27704687/175562315-221dbc9a-5695-4259-a8f7-644e2ff0ab36.png)\r\n\r\n#### after\r\n![Screen Shot 2022-06-24 at 11 59 38](https://user-images.githubusercontent.com/27704687/175562510-a4a6be49-bbd2-4aeb-aedb-a5a7a6f1159d.png)", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25756", + "title": "Chore: Migrate LivechatVisitors model to raw", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "ggazzo", + "sampaiodiego" + ] + }, + { + "pr": "25994", + "title": "Chore: VoIP Context", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25983", + "title": "Chore: Fuselage update", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25987", + "title": "[FIX] sidebar colors", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25990", + "title": "Regression: Non-reactive routes", + "userLogin": "tassoevan", + "description": "When `Tracker.autorun()` calls are nested, it's possible that an invalidation at the parent render the children non-reactive due to synchronous calls. To avoid that under the callback given by `useSyncExternalStore`, we schedule an `onStoreChange` callback call to not make it reside at the same backtrace.", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25982", + "title": "[BREAK] use urlParams on omnichannel/agent/extension/", + "userLogin": "MartinSchoeler", + "contributors": [ + "MartinSchoeler", + "ggazzo" + ] + }, + { + "pr": "25925", + "title": "[FIX] toolbox menu behind thread component", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25475", + "title": "[FIX] Sort by scope or creation date not working on canned responses list", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25969", + "title": "Chore: Colors", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25980", + "title": "Revert \"[BREAK] use urlParams on omnichannel/agent/extension/\"", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25874", + "title": "[BREAK] use urlParams on omnichannel/agent/extension/", + "userLogin": "MartinSchoeler", + "milestone": "5.0.0", + "contributors": [ + "MartinSchoeler" + ] + }, + { + "pr": "25974", + "title": "Regression: Fix e2e CI", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25964", + "title": "Chore: Update poplib", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25961", + "title": "Regression: Set `offset` and `count` optional on `ChatGetThreadsListSchema`", + "userLogin": "LucianoPierdona", + "contributors": [ + "LucianoPierdona" + ] + }, + { + "pr": "25758", + "title": "Chore: Model Typings", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25962", + "title": "Chore: Introduce Modal Region", + "userLogin": "dougfabris", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "11744", + "title": "[NEW] Accept quoted slash command arguments", + "userLogin": "Hudell", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "Hudell", + "web-flow", + "d-gubert", + "sampaiodiego" + ] + }, + { + "pr": "25956", + "title": "Chore: convert import.js endpoints to TS", + "userLogin": "felipe-rod123", + "description": "Converted the `apps/meteor/app/api/server/v1/import.js` to ts and created endpoint typings on the `packages/rest-typings/src/v1/import` folder.", + "contributors": [ + "felipe-rod123" + ] + }, + { + "pr": "25626", + "title": "[NEW] Colors Palette - Buttons", + "userLogin": "juliajforesti", + "contributors": [ + null, + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25672", + "title": "Chore: Upgrade and remove unnecessary Livechat dependencies", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp", + "ggazzo" + ] + }, + { + "pr": "25739", + "title": "[NEW] Marketplace security tab app info page", + "userLogin": "rique223", + "description": "Created a new security tab for installed apps that displays information related to the given app security policies, terms of services, and necessary permissions for the use of the app.\r\nDemo gif:\r\n![privacy-tab](https://user-images.githubusercontent.com/43561537/173878394-333057d4-3c7e-434e-a3ca-d3e08f33c7bc.gif)", + "contributors": [ + "rique223" + ] + }, + { + "pr": "25950", + "title": "Regression: Fix blackscreen after app install", + "userLogin": "rique223", + "description": "Fixed an error where the client screen would go black after installing an app. This was hapenning because the handleAppAddedOrUpdated function from the AppsProvider had a wrong type for the return of the getAppFromMarketplace function.\r\n\r\nDemo gifs:\r\n\r\nBefore\r\n![app-install-error-before](https://user-images.githubusercontent.com/43561537/174861410-024dff74-b5d9-49ba-ae67-849f1ff239a9.gif)\r\n\r\nAfter:\r\n![app-install-error-after](https://user-images.githubusercontent.com/43561537/174861448-58b071e6-8e1b-4391-b49a-44b68249acbf.gif)", + "contributors": [ + "rique223" + ] + }, + { + "pr": "25907", + "title": "Chore: Improve CI cache", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "sampaiodiego" + ] + }, + { + "pr": "25876", + "title": "Regression: Re-add view logs button", + "userLogin": "rique223", + "description": "Re-added the view logs button to the appMenu component so that the user can go directly from the marketplace list of apps to the app info page with the logs tab already open.\r\nDemo gif:\r\n![re-add-view-logs-button](https://user-images.githubusercontent.com/43561537/173681990-86c8a93c-bb2e-4540-824d-b7fbb3161356.gif)", + "contributors": [ + "rique223" + ] + }, + { + "pr": "25920", + "title": "Chore: `@rocket.chat/favicon`", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25947", + "title": "[FIX] VOIP CallContext snapshot infinite loop", + "userLogin": "hugocostadev", + "description": "The application was crashing due to an error on the `useCallerInfo()` hook.\r\nThe error was: \r\n![image](https://user-images.githubusercontent.com/20212776/174823914-4832e5dd-c91a-4ae4-9d1f-1b960bcd372c.png)\r\n![image](https://user-images.githubusercontent.com/20212776/174823982-cb543fe0-663f-4530-bb94-0720653ca897.png)\r\n\r\nTo prevent this issue to happen it was added a cached and out-of-scope snapshot variable to the hook using `useSyncExternalStore`", + "contributors": [ + "hugocostadev", + "web-flow" + ] + }, + { + "pr": "25931", + "title": "Regression: Docker image publish", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25936", + "title": "Revert: \"Chore: Collect e2e coverage\"", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon", + "web-flow" + ] + }, + { + "pr": "25743", + "title": "Chore: Collect e2e coverage", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25923", + "title": "Regression: Unable to edit user details via admin panel", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25871", + "title": "[FIX] Members selection field on creating team modal", + "userLogin": "dougfabris", + "description": "- Fix: add members breaking when searching users\r\n\r\n![image](https://user-images.githubusercontent.com/27704687/121788070-b792f700-cba0-11eb-92b9-5833e1213c74.png)", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "dougfabris" + ] + }, + { + "pr": "25911", + "title": "Chore: Remove Imperative Modal from context ", + "userLogin": "MartinSchoeler", + "contributors": [ + "MartinSchoeler" + ] + }, + { + "pr": "25915", + "title": "Chore: Keep the option to run only the meteor app", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25873", + "title": "[FIX] Update chartjs usage to v3", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25830", + "title": "Chore: Rewrite AddUsers to TS", + "userLogin": "csuadev", + "contributors": [ + "csuadev", + "ggazzo", + "web-flow", + "kodiakhq[bot]", + "dougfabris", + "yash-rajpal" + ] + }, + { + "pr": "25909", + "title": "Chore: Replace `useSubscription` with `useSyncExternalStore`", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25556", + "title": "Chore: Run tests on docker", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25914", + "title": "Chore: Convert RoomMenu", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25868", + "title": "[NEW] Create Team with a member list of usernames", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "dougfabris" + ] + }, + { + "pr": "25754", + "title": "Chore: Convert apps/meteor/client/sidebar/search", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25747", + "title": "Chore: Split useUserInfoActions into small hooks", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "gabriellsh" + ] + }, + { + "pr": "25910", + "title": "Chore: Watch for package changes", + "userLogin": "gabriellsh", + "description": "With the current `dev` pipeline, whenever we modify a package (e.g. `api-client`), we have to kill the meteor proccess and run `yarn dev` again in order for the changes to be compiled and the new output to be used by meteor.\r\n\r\nThis has the drawback of taking a little longer to run the dev environment, since we can't cache a watched buid. In the other hand, it reduces the friction of modifying internal packages since we don't need to rebuild the project for changes to take effect.\r\n\r\nThis will enable us to move more things to separate packages without affecting the dev experience too much.", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25358", + "title": "Chore: Convert assets endpoint to Typescript", + "userLogin": "MarcosSpessatto", + "contributors": [ + "MarcosSpessatto", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25635", + "title": "Chore: Convert users endpoints ", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow", + "tassoevan", + "felipe-rod123" + ] + }, + { + "pr": "25891", + "title": "[FIX] Settings not being overwritten to their default values", + "userLogin": "sampaiodiego", + "milestone": "4.8.2", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25872", + "title": "[FIX] Update import from `csv-parse`", + "userLogin": "LucianoPierdona", + "description": "This PR updates the importing of `csv-parse` because the used method wasn't working anymore, we were receiving the following error:\r\n\r\n`error: \"this.csvParser is not a function\"`", + "contributors": [ + "LucianoPierdona" + ] + }, + { + "pr": "25893", + "title": "Regression: TOTP Modal with new rest api package", + "userLogin": "yash-rajpal", + "milestone": "5.0.0", + "contributors": [ + "yash-rajpal", + "ggazzo" + ] + }, + { + "pr": "25884", + "title": "Chore: create a e2e test guideline", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25870", + "title": "Chore: Fix correct unit test to api files", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25840", + "title": "Chore: convert apps/meteor/app/api/server/lib/ files to TS", + "userLogin": "felipe-rod123", + "description": "This pull request converts files on `apps/meteor/app/api/server/lib/` to Typescript.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25869", + "title": "[FIX] Kebab menu clicking issue ", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "tassoevan" + ] + }, + { + "pr": "25887", + "title": "Regression: Fix extended sidebar item", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "dougfabris" + ] + }, + { + "pr": "25690", + "title": "Chore: Translate admin helpers to TS", + "userLogin": "rique223", + "contributors": [ + "rique223", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25503", + "title": "Chore: convert communication methods to Typescript", + "userLogin": "felipe-rod123", + "description": "Convert files from `apps/meteor/app/apps/server/communication/` to ts.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25867", + "title": "Chore: update pageobjects to use es6 getters and remove export default", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25016", + "title": "[BREAK] Deactivated team members are added to auto-join rooms", + "userLogin": "matheusbsilva137", + "description": "- Do not add deactivated users to auto-join rooms.", + "contributors": [ + "matheusbsilva137" + ] + }, + { + "pr": "25714", + "title": "Chore: Broken Storybook", + "userLogin": "tiagoevanp", + "description": "There is another small improvement on the way we got storybook files.", + "contributors": [ + "tiagoevanp", + "tassoevan", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25603", + "title": "[FIX] User avatar reseting and getting random image", + "userLogin": "guijun13", + "description": "- fixes user avatar not being saved after editing the user profile issue\r\n- fixes user avatar not getting another user picture due to database deletion error", + "contributors": [ + "guijun13", + "filipemarins", + "matheusbsilva137" + ] + }, + { + "pr": "25863", + "title": "Chore: Remove old rest api code", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25719", + "title": "Chore(deps): Bump sharp from 0.30.4 to 0.30.6", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25634", + "title": "Chore: Convert sidebar/item", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo" + ] + }, + { + "pr": "25858", + "title": "Chore: Rewrite RoomWithData", + "userLogin": "dougfabris", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25860", + "title": "Chore: remove unused locators from e2e tests", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25410", + "title": "[FIX] fixes HTML sanitizing error.", + "userLogin": "MartinSchoeler", + "description": "If the user sent a HTML message over our product to a livechat user the HTML would get rendered on the message box, this prevents it from happening.", + "milestone": "5.0.0", + "contributors": [ + "MartinSchoeler", + "dougfabris", + "cauefcr" + ] + }, + { + "pr": "25698", + "title": "Chore: Rewrite Admin UsersTable to Typescript", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "tassoevan", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25813", + "title": "Chore: add _id and name options to JSON Schemas", + "userLogin": "felipe-rod123", + "description": "This pull request adds the `roomId` and `roomName` options for the Ajv JSON Schemas on the `packages/rest-typings/src/v1/channels/` and `packages/rest-typings/src/v1/dm/` folders.", + "contributors": [ + "felipe-rod123", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25831", + "title": "[BREAK] Chore: Remove unused tokenpass integration code ", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25842", + "title": "Chore: Fix version on develop branch ", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25632", + "title": "Chore: RouteGroup for My Account sidebar ", + "userLogin": "yash-rajpal", + "description": "Refactoring My Accounts routes. Allows to add \"my account\" routes for EE.", + "milestone": "5.0.0", + "contributors": [ + "yash-rajpal", + "dougfabris" + ] + }, + { + "pr": "25713", + "title": "[FIX] Attachments and OEmbed margins", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25835", + "title": "Chore: Typescript Sidebar RoomList", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25835", + "title": "Chore: Typescript Sidebar RoomList", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25835", + "title": "Chore: Typescript Sidebar RoomList", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25768", + "title": "[FIX] Client-generated sort parameters in channel directory ", + "userLogin": "BenWiederhake", + "contributors": [ + "BenWiederhake", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25637", + "title": "Chore: Add tests for agents screens", + "userLogin": "weslley543", + "contributors": [ + "weslley543", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25827", + "title": "Chore: Notification Preferences to TS", + "userLogin": "yash-rajpal", + "description": "- Notifications Preferences to TS.\r\n- Fix broken save action.", + "contributors": [ + "yash-rajpal", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25572", + "title": "Chore: Convert MemoizedSetting, Setting, Section", + "userLogin": "juliajforesti", + "contributors": [ + null, + "juliajforesti", + "tassoevan", + "web-flow" + ] + }, + { + "pr": "25834", + "title": "Regression: Fix users.create call", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25829", + "title": "Chore: Add auto label and improve Kodiak configuration", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25824", + "title": "Regression: Fix apps wrong typing", + "userLogin": "rique223", + "contributors": [ + "rique223", + "ggazzo" + ] + }, + { + "pr": "23426", + "title": "Chore: Remove compose from main repo", + "userLogin": "debdutdeb", + "contributors": [ + "debdutdeb" + ] + }, + { + "pr": "25733", + "title": "[FIX] `You and @yourUsername reacted with`title on reactions", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25820", + "title": "[FIX] AgentsPage pagination", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "25160", + "title": "Chore: Move voip's Wrap-up and On-hold functionality to EE (Backend)", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98", + "web-flow" + ] + }, + { + "pr": "25750", + "title": "[FIX] Access issue on chat.getThreadsList", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25819", + "title": "Chore: Remove snap files from Houston config", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25783", + "title": "[FIX] Voip endpoint permissions", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25451", + "title": "[FIX] allow only livechat-agents to be contact manager for any omnichannel contact ", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25810", + "title": "Chore: use params instead of URL building on livechat endpoints", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25809", + "title": "Regression: fix apps path", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25774", + "title": "[BREAK] Remove RDStation integration", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25469", + "title": "Chore: RestApiClient as Package", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25723", + "title": "[FIX] Wrong argument name preventing Omnichannel Chat Forward to User ", + "userLogin": "dudanogueira", + "milestone": "4.8.1", + "contributors": [ + "dudanogueira" + ] + }, + { + "pr": "25708", + "title": "[FIX] AccountBox checks for condition", + "userLogin": "tiagoevanp", + "milestone": "4.8.1", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "25797", + "title": "Chore: Fix CI", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25781", + "title": "[FIX] Fix prom-client new promise usage", + "userLogin": "KevLehman", + "milestone": "4.8.1", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25788", + "title": "[FIX] Discussion alphabetical ordering", + "userLogin": "hugocostadev", + "description": "Added a validation in the prop used for sorting (loweCaseName) checking for a prop that only exists in discussions (prid)", + "contributors": [ + "hugocostadev" + ] + }, + { + "pr": "25794", + "title": "Chore: Testing Kodiak feature", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25731", + "title": "[FIX] Broken Omnichannel>Agents page", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp", + "web-flow" + ] + }, + { + "pr": "25744", + "title": "[FIX] Sanitize styles in message", + "userLogin": "yash-rajpal", + "contributors": [ + "yash-rajpal" + ] + }, + { + "pr": "25536", + "title": "Chore: Convert to TS RoomAutoComplete", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo" + ] + }, + { + "pr": "25769", + "title": "Chore: API test on method GET with params as a number.", + "userLogin": "albuquerquefabio", + "milestone": "5.0.0", + "contributors": [ + "albuquerquefabio", + "web-flow" + ] + }, + { + "pr": "25350", + "title": "Chore: convert invites, misc and subscriptions to TS and create definitions", + "userLogin": "felipe-rod123", + "description": "Converted `apps/meteor/app/api/server/v1/invites.js`, `misc.js` and `subscriptions.js` to Typescript and created their endpoint definitions on the rest-typings folder.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25787", + "title": "Chore: Remove toastr package", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "25649", + "title": "[BREAK] Remove Blockstack authentication", + "userLogin": "tassoevan", + "description": "Blockstack authentication is broken and is preventing some dependencies to be up to date. As a migration to Stacks authentication is not trivial, we've opted for removing the authentication service.", + "milestone": "5.0.0", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25748", + "title": "[FIX] getUserMentionsByChannel method room permission", + "userLogin": "gabriellsh", + "milestone": "5.0.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25583", + "title": "[NEW] Fuselage ToastBar", + "userLogin": "dougfabris", + "description": "![Kapture 2022-05-20 at 14 50 19](https://user-images.githubusercontent.com/27704687/169584462-270e73aa-6dbe-4045-9847-d429125f15a6.gif)", + "milestone": "5.0.0", + "contributors": [ + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "25709", + "title": "[FIX] Thread Message Preview", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan", + "gabriellsh" + ] + }, + { + "pr": "25669", + "title": "[FIX] Bump meteor-node-stubs to version 1.2.3", + "userLogin": "Sh0uld", + "description": "With meteor-node-stubs version 1.2.3 a bug was fixed, which occured in issue #25460 and probably #25513 (last one not tested).\r\nFor the issue in meteor see: https://github.com/meteor/meteor/issues/11974", + "milestone": "4.8.1", + "contributors": [ + "Sh0uld", + "ggazzo" + ] + }, + { + "pr": "25680", + "title": "[IMPROVE] Refactor + unit tests for federation-v2", + "userLogin": "MarcosSpessatto", + "description": "The main goal for this PR is to add the ability to add tests in our current federation-v2 implementation.\r\nIn this PR, I've added only unit tests (80%), but the goal is to add other kinds of tests in the near future.\r\n\r\nAlso, I've created a diagram to show how this refactor was done, and how is the structure of the code\r\n\r\n![image](https://user-images.githubusercontent.com/15324204/171039619-22168000-3626-424e-b408-18dea540f786.png)", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "24796", + "title": "[FIX] user status Offline misnamed as Invisible in Custom Status edit dropdown menu", + "userLogin": "Kunalvrm555", + "milestone": "5.0.0", + "contributors": [ + "Kunalvrm555", + "web-flow", + "debdutdeb", + "dougfabris" + ] + }, + { + "pr": "25761", + "title": "Chore: Messages raw model rewrite to ts", + "userLogin": "debdutdeb", + "contributors": [ + "debdutdeb", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25501", + "title": "Chore: migrate katex to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25751", + "title": "Chore: AutoTranslate contextualBar rewrite", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25752", + "title": "Chore: Replace AnnouncementModal in favor of GenericModal", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25753", + "title": "Chore: Keyboard shortcuts contextualBar rewrite", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25757", + "title": "Chore: Prune Messages contextualBar rewrite", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25601", + "title": "Chore: add Ajv JSON Schema to api/v1", + "userLogin": "felipe-rod123", + "description": "This pull request adds Ajv JSON Schema validation to `apps/meteor/app/api/server/v1/` and `packages/rest-typings/src/v1/`, where needed.", + "contributors": [ + "felipe-rod123", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25755", + "title": "Chore: Update package.json update tsc memory ", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25749", + "title": "Chore: remove duplicated NotFoundPage.js", + "userLogin": "dougfabris", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25630", + "title": "Chore: command's endpoints", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25741", + "title": "Chore: Fix incorrect checksum for agenda package (cause of breaking develop builds)", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25730", + "title": "Chore: Remove duplicate checksumBehavior key from yarn file", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25393", + "title": "[FIX] Custom emoji reaction size", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25696", + "title": "Chore: Test for department screen", + "userLogin": "weslley543", + "contributors": [ + "weslley543" + ] + }, + { + "pr": "25697", + "title": "Chore: Taking out Blaze from routes with `MainLayout` ", + "userLogin": "tassoevan", + "description": "While working with @guijun13 on the new homepage I saw we're still rendering a Blaze template even to just embedded components into `MainLayout`. This PR addresses it.", + "milestone": "5.0.0", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25564", + "title": "Chore: Remove all cypress tests, configs and references", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25612", + "title": "Chore: adjust in some configurations", + "userLogin": "weslley543", + "contributors": [ + "weslley543", + "web-flow" + ] + }, + { + "pr": "25567", + "title": "Chore: migrate-to-pw-16-discussion", + "userLogin": "weslley543", + "contributors": [ + "weslley543" + ] + }, + { + "pr": "25712", + "title": "[FIX] Unnecessary padding on teams channels footer", + "userLogin": "dougfabris", + "description": "#### before\r\n\r\n\r\n### after\r\n", + "milestone": "5.0.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25631", + "title": "[FIX] Messages spacing", + "userLogin": "hugocostadev", + "description": "Adding `sequential` prop to Message component from Fuselage", + "contributors": [ + "hugocostadev", + "gabriellsh" + ] + }, + { + "pr": "25633", + "title": "Chore: Custom Sounds Endpoints", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25682", + "title": "[FIX] User's with non-agent role shown on voip agent association model", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98" + ] + }, + { + "pr": "25667", + "title": "Chore: Convert CreateChannelWithData", + "userLogin": "juliajforesti", + "contributors": [ + null, + "juliajforesti" + ] + }, + { + "pr": "25587", + "title": "Chore: Convert UserAutoCompleteMultiple", + "userLogin": "juliajforesti", + "contributors": [ + null, + "juliajforesti" + ] + }, + { + "pr": "25658", + "title": "Chore: Converting files from app/livechat folder from JS to TS", + "userLogin": "amolghode1981", + "description": "Converting files from apps/meteor/app/livechat/lib/ from JS to TS", + "contributors": [ + "amolghode1981" + ] + }, + { + "pr": "25581", + "title": "Chore: Convert sidebar/header/actions", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25665", + "title": "Chore: Converting omnichannel installation files to ts", + "userLogin": "aleksandernsilva", + "description": "This PR converts the omnichannel/installation folder from js to ts", + "contributors": [ + "aleksandernsilva" + ] + }, + { + "pr": "25511", + "title": "Chore: Convert to TS omnichannel/agent", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo" + ] + }, + { + "pr": "25429", + "title": "Chore: Convert components/sidebar to TS", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo" + ] + }, + { + "pr": "25671", + "title": "Chore: Convert apps/meteor/client/sidebar/header/index", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25666", + "title": "Chore: Migrate some small helper functions to TypeScript", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25702", + "title": "Merge master into develop & Set version to 5.0.0", + "userLogin": "d-gubert", + "contributors": [ + "d-gubert", + "web-flow", + "felipe-menelau", + "pierre-lehnen-rc", + "tiagoevanp", + "MartinSchoeler", + "ggazzo", + "cauefcr", + "geekgonecrazy" + ] + }, + { + "pr": "25700", + "title": "Chore: Update Apps-Engine and Fuselage", + "userLogin": "d-gubert", + "milestone": "4.8.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25689", + "title": "Regression: App event listeners broke Slackbridge integration and importers", + "userLogin": "d-gubert", + "description": "Some event listeners triggered by Apps were calling `Meteor.user()` in functions that could run outside of Meteor environment", + "milestone": "4.8.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25686", + "title": "[FIX] Fix max-width message block", + "userLogin": "ggazzo", + "milestone": "4.8.0", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25673", + "title": "[FIX] Change form body parameter charset to UTF-8 to fix issue #25456", + "userLogin": "divinespear", + "description": "since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0.\r\n\r\n![Screenshot from 2022-05-28 16-26-06](https://user-images.githubusercontent.com/126630/170815447-1f3bd579-243a-42d3-86f6-814aeaa30ce9.png)", + "milestone": "4.8.0", + "contributors": [ + "divinespear" + ] + }, + { + "pr": "25687", + "title": "Regression: Fix sort field files.list", + "userLogin": "ggazzo", + "milestone": "4.8.0", + "contributors": [ + "ggazzo", + "albuquerquefabio", + "web-flow" + ] + }, + { + "pr": "25684", + "title": "[IMPROVE] add warnings for federation setup", + "userLogin": "carlosrodrigues94", + "contributors": [ + "carlosrodrigues94" + ] + }, + { + "pr": "25683", + "title": "[FIX] Prevent federation crash on invite users as a non-owner user", + "userLogin": "MarcosSpessatto", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "25653", + "title": "Regression: Broken components on Federation and Engagement dashboards", + "userLogin": "tassoevan", + "description": "For reasons I've no clue, any client import path matching `**/data/**` will not be included in the final bundle, failing silently on transpiling/bundling.", + "milestone": "4.8.0", + "contributors": [ + "tassoevan", + "gabriellsh" + ] + }, + { + "pr": "25663", + "title": "Regression: Update settings groups description", + "userLogin": "dougfabris", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25569", + "title": "[FIX] Click to join button Jitsi Call", + "userLogin": "hugocostadev", + "description": "Added `ToolboxProvider` to `MessageListProvider` and fixed actionLink.js open function exec", + "milestone": "4.8.0", + "contributors": [ + "hugocostadev", + "tassoevan", + "web-flow" + ] + }, + { + "pr": "25644", + "title": "Regression: Endpoint types with Ajv Coercing data types", + "userLogin": "albuquerquefabio", + "description": "Ajv Coercing data types should be `true` to accept all kinds of data requested.", + "contributors": [ + "albuquerquefabio" + ] + }, + { + "pr": "25618", + "title": "Regression: Change logic to check if connection is online on unstable networks", + "userLogin": "KevLehman", + "milestone": "4.8.0", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25639", + "title": "Regression: Missing settings group descriptions", + "userLogin": "dougfabris", + "description": "", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25648", + "title": "Chore: Rest API query parameters handling", + "userLogin": "ggazzo", + "milestone": "5.0.0", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25651", + "title": "Regression: VoIp wrap up modal not opening after call disconnect", + "userLogin": "aleksandernsilva", + "description": "This PR fixes a bug preventing the wrap up call modal from being displayed after caller or agent ends the call.", + "milestone": "4.8.0", + "contributors": [ + "aleksandernsilva" + ] + }, + { + "pr": "25638", + "title": "[FIX] Remove 'total' text in admin info page", + "userLogin": "guijun13", + "description": "- Remove initial 'total' text from rooms and messages groups in the admin info page\r\n- Add 'total' before 'rooms' and 'messages' title on the same section. To use the new 'Total Rooms', was created a new key in the en.i18n.json file.", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25641", + "title": "Chore: Increase performance and security of integrations’ scripts", + "userLogin": "rodrigok", + "description": "Replace internal VM implementation with VM2 which implements many more mechanisms to ensure timeout, security and allow easier configuration for future improvements on the integrations' feature.", + "contributors": [ + "rodrigok", + "ggazzo" + ] + }, + { + "pr": "25613", + "title": "[FIX] Quote message spacing", + "userLogin": "hugocostadev", + "contributors": [ + "hugocostadev" + ] + }, + { + "pr": "25629", + "title": "Regression: Assets & Slack Bridge Setting Page not rendering", + "userLogin": "dougfabris", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25627", + "title": "Regression: Subscription menu not appearing for non installed but subscribed apps", + "userLogin": "rique223", + "description": "Fixed a problem on which the AppMenu component did not appear for apps that had an active subscription but weren't installed, now the rendering of the component is also based on the isSubscribed flag, and the appearance of the uninstall and enable/disable options are based on the app.installed flag so that the correct options appear on all the edge cases.\r\nDemo gif:\r\n![subscription-manager-fix](https://user-images.githubusercontent.com/43561537/170132040-dc8535c0-8056-4fb2-b008-afaece744868.gif)", + "milestone": "4.8.0", + "contributors": [ + "rique223" + ] + }, + { + "pr": "25521", + "title": "Chore: Rewrite im and dm endpoints to ts", + "userLogin": "albuquerquefabio", + "description": "- Endpoints rewritten to TS\r\n - dm.create\r\n - dm.delete\r\n - dm.close\r\n - dm.counters\r\n - dm.files\r\n - dm.history\r\n - dm.members\r\n - dm.messages\r\n - dm.messages.others\r\n - dm.list\r\n - dm.list.everyone\r\n - dm.open\r\n - dm.setTopic\r\n - im.create\r\n - im.delete\r\n - im.close\r\n - im.counters\r\n - im.files\r\n - im.history\r\n - im.members\r\n - im.messages\r\n - im.messages.others\r\n - im.list\r\n - im.list.everyone\r\n - im.open\r\n - im.setTopic\r\n- Some lines of code was refactored on `apps/meteor/app/api/server/v1/im.ts`\r\n- Unnecessary functions were deleted on `apps/meteor/app/lib/server/functions/getDirectMessageByNameOrIdWithOptionToJoin.ts`\r\n- New types was added on `apps/meteor/app/api/server/api.d.ts`", + "contributors": [ + "albuquerquefabio", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25617", + "title": "Chore: Update Apps-Engine version", + "userLogin": "d-gubert", + "milestone": "4.8.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25616", + "title": "[FIX] Message menu dropdown not working on Mobile Web", + "userLogin": "gabriellsh", + "milestone": "4.8.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25615", + "title": "[FIX] Fixing app contextual bar functionality", + "userLogin": "AllanPazRibeiro", + "milestone": "4.8.0", + "contributors": [ + "AllanPazRibeiro" + ] + }, + { + "pr": "25499", + "title": "[NEW] New button for network outage", + "userLogin": "amolghode1981", + "description": "When network outage happens it should be conveyed to the user with special icon. This icon should not be clickable.\r\nNetwork outage handling is handled in https://app.clickup.com/t/245c0d8 task.", + "contributors": [ + "amolghode1981" + ] + }, + { + "pr": "24711", + "title": "[NEW] Marketplace new app details page", + "userLogin": "rique223", + "description": "Change the app details page layout for the new marketplace UI. General Task: [MKP12 - New UI - App Detail Page](https://app.clickup.com/t/1na769h)\r\n\r\n## [MKP12 - Tab Navigation](https://app.clickup.com/t/2452f5u)\r\nNew tab navigation layout for the app details page. Now the app details page is divided into three sections, details, logs, and settings, that can each be accessed through a Tabs fuselage component.\r\n\r\nDemo gif:\r\n![tab_navigation_demo_gif](https://user-images.githubusercontent.com/43561537/157276436-3dab34c5-20da-4f5d-99d0-54c1c718ac1f.gif)\r\n\r\n## [MKP12 - Header](https://app.clickup.com/t/25rhm0x)\r\nImplemented a new header for the marketplaces app details page.\r\n-Changed the size of the app name;\r\n-Implemented the app description field on the header;\r\n-Changed the \"metadata\" section of the header(The part with the version and author information) now it also shows the last time the app was updated;\r\n-Created a chip that will show when an app is part of one or more bundles and inform which are the bundles;\r\n-Implemented a tooltip for the bundle chips;\r\n-Created a new button + data badge component to substitute the current App Status;\r\n-Changed the title of the \"purchase button\". Now it shows different text based on the \"purchase type\" of the app;\r\n-Created a new Pricing & Status display which shows the price when the app is not bought/installed and shows the app status(Enabled/Disabled) when it is bought/installed;\r\n-Changed the way the tabs are rendered, now if the app is not installed(and consequently doesn't have logs and settings tab) it will not render these tabs;\r\n\r\nDemo gif:\r\n![new-header-gif](https://user-images.githubusercontent.com/43561537/159064599-fd64dfe2-86a3-47da-81ba-1e83f1b87432.gif)\r\n\r\n## [MKP12 - Configuration Tab](https://app.clickup.com/t/2452gh4)\r\nDelivered together with the tab-navigation task. Changed the app settings from the details of the app to the new settings tab.\r\nDemo image:\r\n![New configuration tab](https://user-images.githubusercontent.com/43561537/160211324-95db0566-85bf-4dde-a814-3c6f23dcee4d.png)\r\n\r\n## [MKP12 - Log Tab](https://app.clickup.com/t/2452gg1)\r\nChanged the place of the app logs from the page to the new logs tab. Also changed some styles of the logs accordions to fit better with the new container.\r\n\r\nBefore:\r\n![Before](https://user-images.githubusercontent.com/43561537/160210302-148ce584-604f-40ff-8209-141667016163.png)\r\n\r\nAfter\r\n![After](https://user-images.githubusercontent.com/43561537/160210984-d4060c5a-f912-4ef9-87e3-fa459080e2d4.png)\r\n\r\n## [MKP12 - Page Header](https://app.clickup.com/t/29b0b12)\r\nChanged the design for the page header of the app details page from a title on the left with a save and back button on the right to a back arrow icon on the left side of the title with the save button still on the right. Also changed the title of the page from App details to Back.\r\nEdit: After some design reconsideration, the page title was changed to App Info.\r\nDemo gif:\r\n![new_page_header_app_details](https://user-images.githubusercontent.com/43561537/160937741-f5514f70-f43b-4400-8b2f-a5a26f95de9d.gif)\r\n\r\n## [MKP12 - Detail Tab](https://app.clickup.com/t/2452gf7)\r\nImplemented markdown on the description section of the app details page, now the description will show the detailedDescription.rendered (as rendered JSX) information in case it exists and show the description (a.k.a. short description) information in case it doesn't. Unfortunately, as of right now no app has a visual example of a markdown description and because of that, I will not be able to provide a demo image/gif for this PR.\r\n\r\n## [MKP12 - Slider Component](https://app.clickup.com/t/2452h26)\r\nCreated an image carousel component on the app details page. This component receives images from the apps/appId/screenshots endpoint and shows them on the content section of the app details of any apps that have screenshots registered, if the app has no screenshots it simply shows nothing where the carousel should be. This component is complete with keyboard arrow navigation on the \"open\" carousel, hover highlight on the carousel preview and close on esc press.\r\nDemo gif:\r\n![new_carousel_component](https://user-images.githubusercontent.com/43561537/167415212-9d8359c7-4132-4afa-a698-8be4ab1e1393.gif)", + "milestone": "4.8.0", + "contributors": [ + "rique223", + "web-flow", + "ggazzo", + "dougfabris" + ] + }, + { + "pr": "25108", + "title": "[IMPROVE] Unify voip streams into single stream", + "userLogin": "KevLehman", + "milestone": "4.8.0", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25444", + "title": "[FIX] Removing user also removes them from Omni collections", + "userLogin": "cauefcr", + "contributors": [ + "cauefcr", + "web-flow", + "KevLehman" + ] + }, + { + "pr": "25398", + "title": "[FIX] Upgrade tab loader in incorrect position", + "userLogin": "guijun13", + "description": "- Add invisible prop to iframe when loading state is active.", + "milestone": "4.8.0", + "contributors": [ + "guijun13", + "tassoevan" + ] + }, + { + "pr": "25436", + "title": "[NEW] Ability for RC server to check the business hour for a specific department", + "userLogin": "murtaza98", + "milestone": "4.8.0", + "contributors": [ + "murtaza98", + "tiagoevanp" + ] + }, + { + "pr": "25606", + "title": "Chore: Code Improvements for #25391", + "userLogin": "MartinSchoeler", + "milestone": "4.8.0", + "contributors": [ + "MartinSchoeler" + ] + }, + { + "pr": "25604", + "title": "[FIX] useCurrentChatTags is not a function", + "userLogin": "MartinSchoeler", + "milestone": "4.8.0", + "contributors": [ + "MartinSchoeler" + ] + }, + { + "pr": "25535", + "title": "[FIX] Pinned Message display cutting off information", + "userLogin": "hugocostadev", + "milestone": "4.8.0", + "contributors": [ + "hugocostadev", + "gabriellsh" + ] + }, + { + "pr": "25290", + "title": "Chore: Dependencies upgrade", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25605", + "title": "Chore: bump fuselage", + "userLogin": "dougfabris", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25457", + "title": "[NEW] Federation (Alpha Stabilization)", + "userLogin": "alansikora", + "milestone": "4.8.0", + "contributors": [ + "alansikora", + "MarcosSpessatto", + "web-flow", + "geekgonecrazy" + ] + }, + { + "pr": "24519", + "title": "Chore: Convert to typescript some functions from app/lib/server/functions", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript some functions from app/lib/server/functions and transfered theses files to server/lib", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25329", + "title": "[NEW] Add option to show mentions badge when show counter is disabled", + "userLogin": "marceloschmidt", + "contributors": [ + "marceloschmidt", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25391", + "title": "[FIX] Fixing Network connectivity issues with SIP client.", + "userLogin": "amolghode1981", + "description": "The previous PR https://github.com/RocketChat/Rocket.Chat/pull/25170 did not handle the issues completely.\r\nThis PR is expected to handle\r\n1. Clearing call related UI when the network is disconnected or switched.\r\n2. Do clean connectivity. There were few issues discovered in earlier implementation. e.g endpoint would randomly\r\nget disconnected after a while. This was due to the fact that the earlier socket disconnection caused the\r\nremoval of contact on asterisk. This should be fixed in this PR.\r\n3. This PR contains a lot of logs. This will be removed before the final merge.", + "milestone": "4.8.0", + "contributors": [ + "amolghode1981" + ] + }, + { + "pr": "25494", + "title": "[FIX] Ordered and unordered list styles, Line breaks.", + "userLogin": "gabriellsh", + "description": "Also removed the message.md cache from server, since changes in the parser might break messages in the future (and will in this specific case).", + "milestone": "4.8.0", + "contributors": [ + "gabriellsh", + "tassoevan", + "web-flow" + ] + }, + { + "pr": "25592", + "title": "Chore: Convert slashCommands to typescript", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "eduardofcabrera", + "ostjen", + "web-flow" + ] + }, + { + "pr": "25514", + "title": "[NEW] Get user's preferred language via apps", + "userLogin": "murtaza98", + "contributors": [ + "murtaza98", + "d-gubert" + ] + }, + { + "pr": "25383", + "title": "[NEW] Star message, report and delete message events", + "userLogin": "tapiarafael", + "contributors": [ + "tapiarafael", + "d-gubert" + ] + }, + { + "pr": "25234", + "title": "[NEW] Add new events after user login, logout and change his status", + "userLogin": "tapiarafael", + "contributors": [ + "tapiarafael", + "d-gubert" + ] + }, + { + "pr": "25337", + "title": "[NEW] Add new app events for pin, react and follow message", + "userLogin": "tapiarafael", + "contributors": [ + "tapiarafael", + "d-gubert" + ] + }, + { + "pr": "25591", + "title": "Chore: Convert AutoTranslate", + "userLogin": "PedroRorato", + "contributors": [ + "PedroRorato" + ] + }, + { + "pr": "25582", + "title": "Chore: Migrate retention-policy to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "24307", + "title": "Chore: Convert to typescript the slash commands help files", + "userLogin": "eduardofcabrera", + "description": "Convert to typescript the slash commands help files", + "contributors": [ + "eduardofcabrera", + "web-flow", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25589", + "title": "Chore: Convert Create Channel", + "userLogin": "juliajforesti", + "contributors": [ + null + ] + }, + { + "pr": "25586", + "title": "Chore: Convert additionalForms", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "MartinSchoeler" + ] + }, + { + "pr": "25425", + "title": "Chore: Rewrite autotranslate to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25165", + "title": "[NEW] Add user events for apps", + "userLogin": "tapiarafael", + "contributors": [ + "tapiarafael", + "d-gubert" + ] + }, + { + "pr": "25283", + "title": "[FIX] Integrations avatar attribute misuse", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25367", + "title": "Chore: Converting orchestrator.js to ts", + "userLogin": "AllanPazRibeiro", + "contributors": [ + "AllanPazRibeiro" + ] + }, + { + "pr": "25504", + "title": "Chore: convert marketplace price display component to use typescript", + "userLogin": "matheuslc", + "description": "**Marketplace apps listing page**\r\n![Screen Shot 2022-05-13 at 12 57 43](https://user-images.githubusercontent.com/4161171/168322189-67990fdf-a447-46dc-8f88-08b16c2a5416.png)\r\n\r\n**Apps detail page**\r\n![Screen Shot 2022-05-13 at 12 58 56](https://user-images.githubusercontent.com/4161171/168322241-505ee5bb-d3d8-4b0e-8757-873a1a65a6a6.png)", + "contributors": [ + "matheuslc" + ] + }, + { + "pr": "25554", + "title": "Chore: Convert apps/meteor/client/components/UserAutoComplete", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "ggazzo" + ] + }, + { + "pr": "25544", + "title": "[FIX] Initial User not added to default channel", + "userLogin": "geekgonecrazy", + "description": "If injecting initial user. The user wasn’t added to the default General channel", + "milestone": "4.7.2", + "contributors": [ + "geekgonecrazy", + "web-flow" + ] + }, + { + "pr": "25078", + "title": "[NEW] New stats rewrite", + "userLogin": "ostjen", + "description": "Add the following new statistics (**metrics**):\r\n\r\n- Total users with TOTP enabled;\r\n- Total users with 2FA enabled;\r\n- Total pinned messages;\r\n- Total starred messages;\r\n- Total email messages;\r\n- Total rooms with at least one starred message;\r\n- Total rooms with at least one pinned message;\r\n- Total encrypted rooms;\r\n- Total link invitations;\r\n- Total email invitations;\r\n- Logo change;\r\n- Number of rooms inside teams;\r\n- Number of default (auto-join) rooms inside teams;\r\n- Number of users created through link invitation;\r\n- Number of users created through manual entry;\r\n- Number of imported users (by import type);", + "contributors": [ + "ostjen", + "matheusbsilva137", + "sampaiodiego" + ] + }, + { + "pr": "25565", + "title": "Chore: Convert apps/meteor/client/views/admin/settings", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25520", + "title": "[FIX] User abandonment setting was not working doe to failing event hook", + "userLogin": "cauefcr", + "description": "A setting watcher and the query for grabbing abandoned chats were broken, now they're not.", + "milestone": "4.7.2", + "contributors": [ + "cauefcr", + "tiagoevanp" + ] + }, + { + "pr": "25558", + "title": "Test: Migrate 13-permissions from cypress to playwright", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25445", + "title": "[FIX] Add open user card to user avatar", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25495", + "title": "[FIX] Dynamic load matrix is enabled and handle failure ", + "userLogin": "ggazzo", + "milestone": "4.7.2", + "contributors": [ + "ggazzo", + "geekgonecrazy" + ] + }, + { + "pr": "25409", + "title": "[FIX] One of the triggers was not working correctly", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", + "contributors": [ + "MartinSchoeler", + "tiagoevanp" + ] + }, + { + "pr": "25555", + "title": "Regression: CI services build", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25381", + "title": "Chore: User set UTC offset", + "userLogin": "albuquerquefabio", + "contributors": [ + "albuquerquefabio", + "web-flow" + ] + }, + { + "pr": "24612", + "title": "[FIX] Rooms' names turn lower case on CSV import", + "userLogin": "guijun13", + "description": "* Change 'Settings' import to not get cached configs\r\n* Remove update `UI_Allow_room_names_with_special_chars` value", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25542", + "title": "Chore: migrate-to-pw-adjust-in-intermitences", + "userLogin": "weslley543", + "contributors": [ + "weslley543" + ] + }, + { + "pr": "23849", + "title": "[IMPROVE][ENTERPRISE] Allow mapping LDAP groups to multiple RC roles", + "userLogin": "matheusbsilva137", + "description": "- Add support to mapping LDAP groups to multiple roles (by specifying arrays in the \"User Data Group Map\" enterprise setting.", + "contributors": [ + "matheusbsilva137", + "pierre-lehnen-rc" + ] + }, + { + "pr": "25522", + "title": "Chore: Livechat change output level", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25326", + "title": "[NEW] Adding app button on user dropdown", + "userLogin": "AllanPazRibeiro", + "contributors": [ + "AllanPazRibeiro", + "d-gubert", + "web-flow" + ] + }, + { + "pr": "25523", + "title": "Chore: migrate from cypress to pw 14-setting-permission", + "userLogin": "weslley543", + "contributors": [ + "weslley543" + ] + }, + { + "pr": "25253", + "title": "Chore: Tests with Playwright (task: ROC-31, 12-settings)", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon", + "web-flow" + ] + }, + { + "pr": "25462", + "title": "Chore: Migrate 15-message-popup from cypress to playwright", + "userLogin": "souzaramon", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25427", + "title": "Chore: Convert apps/meteor/client/views/admin/settings/inputs folder", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25407", + "title": "[FIX] UI/UX issues on Live Chat widget", + "userLogin": "MartinSchoeler", + "milestone": "4.7.2", + "contributors": [ + "MartinSchoeler", + "dougfabris" + ] + }, + { + "pr": "25348", + "title": "Chore: Convert Admin -> Rooms to TS", + "userLogin": "yash-rajpal", + "contributors": [ + "yash-rajpal", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25509", + "title": "Chore: Migrate NotFoundPage to TS", + "userLogin": "hugocostadev", + "contributors": [ + "hugocostadev" + ] + }, + { + "pr": "25412", + "title": "[FIX] Unable to see channel member list by authorized channel roles", + "userLogin": "hugocostadev", + "contributors": [ + "hugocostadev" + ] + }, + { + "pr": "25519", + "title": "Regression: Fix services-image-build-check", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "25507", + "title": "Chore: Migrate spotify to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25508", + "title": "Chore: Reorder unreleased migrations", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25471", + "title": "[FIX] Spotlight results showing usernames instead of real names", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25434", + "title": "[FIX] LDAP sync removing users from channels when multiple groups are mapped to it", + "userLogin": "pierre-lehnen-rc", + "milestone": "4.7.1", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25413", + "title": "Chore: Move markdown message parser to a `callback`", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25448", + "title": "[FIX] Settings listeners not receiving overwritten values from env vars", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "25246", + "title": "Chore: Move ddp-streamer micro service to its own sub-repo", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25441", + "title": "[NEW] Use setting to determine if initial general channel is needed", + "userLogin": "felipe-menelau", + "description": "- Adds flag responsible for overwriting #general channel creation", + "milestone": "4.7.1", + "contributors": [ + "felipe-menelau", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "25439", + "title": "[IMPROVE] New admin settings Page", + "userLogin": "dougfabris", + "description": "![Screen Shot 2022-05-09 at 11 31 58](https://user-images.githubusercontent.com/27704687/167432811-f4970f23-5dae-48a0-a427-92269d08a859.png)", + "milestone": "4.8.0", + "contributors": [ + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "25473", + "title": "[FIX] Failure to update Integration History index", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc" + ] + }, + { + "pr": "25285", + "title": "Chore: Rewrite 2fa to typescript", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25468", + "title": "Chore: solve yarn issues from env var", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25446", + "title": "Chore: REST query and body params validation", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25416", + "title": "Chore: Tests with Playwright (task: ROC-66, Intermittent resolution in tests)", + "userLogin": "weslley543", + "contributors": [ + "weslley543", + "souzaramon" + ] + }, + { + "pr": "25298", + "title": "Chore: Convert email inbox feature to TypeScript", + "userLogin": "ujorgeleite", + "contributors": [ + "ujorgeleite", + "albuquerquefabio", + "web-flow", + "tassoevan" + ] + }, + { + "pr": "25442", + "title": "Chore: Move admin sidebarItems registration to the main file", + "userLogin": "dougfabris", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25449", + "title": "[FIX] Sanitize customUserStatus and fix infinite loop", + "userLogin": "dougfabris", + "description": "### Additional improves:\r\n- usage of RHF to avoid unnecessary Add and Edit components separately and form validation\r\n- usage of `GenericTableV2` and some hooks to avoid unnecessary code\r\n- fix `IUserStatus` type\r\n- improves in UI design\r\n- improves **empty** and **loading** state\r\n- improves files structure\r\n\r\n[LOOP ERROR ATTACHMENT]\r\n![Screen Shot 2022-05-09 at 19 42 53](https://user-images.githubusercontent.com/27704687/167510439-1980461c-a885-46d2-9a49-79da432c7521.png)", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25318", + "title": "[IMPROVE] Fix multiple bugs with Matrix bridge", + "userLogin": "MarcosSpessatto", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "25265", + "title": "Chore: Convert `UserStatusMenu` to TS", + "userLogin": "debdutdeb", + "contributors": [ + "debdutdeb", + "tassoevan" + ] + }, + { + "pr": "25443", + "title": "Chore: Chore add validation option to rest endpoints", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25279", + "title": "Chore: Add channel endpoints (rest-typings)", + "userLogin": "debdutdeb", + "contributors": [ + "debdutdeb", + "ggazzo" + ] + }, + { + "pr": "25432", + "title": "Chore: Dedicated package for UI contexts", + "userLogin": "tassoevan", + "description": "Moving our React contexts to a different package on the monorepo enable us to deliver components from another packages, because they work as a loose connection to the core APIs.", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25424", + "title": "Chore: Convert RoomForeword, TextCopy and RoomAvatarEditor to TS", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25418", + "title": "Chore: Rewrite action-links to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25421", + "title": "Chore: Rewrite mail-messages to ts", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25430", + "title": "Chore: Convert useUpdateAvatar to TS and type avatar endpoints", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25423", + "title": "[FIX] Change NPS Vote identifier + nps index to unique", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "22374", + "title": "[IMPROVE] Pass allowDiskUse to channel aggregations on engagement dashboard", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25431", + "title": "Chore: Manager Page Rewrite", + "userLogin": "MartinSchoeler", + "contributors": [ + "MartinSchoeler", + "ggazzo" + ] + }, + { + "pr": "25426", + "title": "Chore: Convert useFileInput to TS", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25420", + "title": "Chore: convert info to typescript", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25395", + "title": "Chore: Enable marketplace screenshots endpoint", + "userLogin": "matheuslc", + "contributors": [ + "matheuslc", + "web-flow" + ] + }, + { + "pr": "25312", + "title": "Chore: Add Livechat repo into Monorepo packages", + "userLogin": "tiagoevanp", + "milestone": "4.7.2", + "contributors": [ + "tiagoevanp", + "ggazzo", + "web-flow", + "MartinSchoeler" + ] + }, + { + "pr": "25303", + "title": "Chore: Rewrite Jitsi Contextualbar to TS", + "userLogin": "dougfabris", + "milestone": "4.8.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25372", + "title": "Chore: Convert AdminSideBar to ts", + "userLogin": "jeanfbrito", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25347", + "title": "Chore: Convert push endpoints to TS", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25397", + "title": "Chore: Add client folder to CODEOWNERS ", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25394", + "title": "Chore: Update Volta configuration", + "userLogin": "tassoevan", + "description": "[Volta](https://volta.sh/) need some extra configuration to work on monorepos.", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "25359", + "title": "Chore: Rewrite some Omnichannel files to TypeScript", + "userLogin": "tiagoevanp", + "description": "apps/meteor/client/components/Omnichannel/modals/*\r\napps/meteor/client/components/Omnichannel/Tags.js", + "contributors": [ + "tiagoevanp", + "ggazzo" + ] + }, + { + "pr": "25288", + "title": "Chore: Convert customUserStatus folder to ts", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25343", + "title": "Chore: Convert federationDashboard folder to ts", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25252", + "title": "Chore: Tests with Playwright (task: ROC-25, 06-message)", + "userLogin": "weslley543", + "contributors": [ + "weslley543", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "25345", + "title": "Chore: Convert client/views/admin/settings/groups folder to ts", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti" + ] + }, + { + "pr": "25342", + "title": "Chore: Convert getStatistics", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25276", + "title": "Chore: Add typings for /v1/webdav.getMyAccounts", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25274", + "title": "Chore: Convert customSounds folder to ts", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "dougfabris", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25277", + "title": "Chore: Convert Admin/OAuthApps to TS", + "userLogin": "yash-rajpal", + "description": "- Converts Admin/OAuthApps to TS.\r\n- migrated forms to react-hook-form", + "contributors": [ + "yash-rajpal", + "felipe-rod123", + "ggazzo" + ] + }, + { + "pr": "25278", + "title": "Chore: Add /v1/video-conference endpoint types", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "pierre-lehnen-rc", + "ggazzo" + ] + }, + { + "pr": "25380", + "title": "Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window", + "userLogin": "filipemarins", + "description": "Fix: livechat room not opening.", + "milestone": "4.7.0", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25314", + "title": "Regression: Fix size of custom emoji and render emoji on thread message preview", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25371", + "title": "Chore: Bump fuselage", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25336", + "title": "Chore: Add options to debug stdout and rate limiter", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25368", + "title": "Regression: Fix English i18n react text", + "userLogin": "d-gubert", + "description": "Incorrect text in reaction tooltip has been fixed", + "milestone": "4.7.0", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25349", + "title": "Regression: Rocket.Chat Webapp not loading.", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc", + "gabriellsh" + ] + }, + { + "pr": "25317", + "title": "Regression: Fix multi line is not showing an empty line between lines", + "userLogin": "filipemarins", + "milestone": "4.7.0", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25320", + "title": "Regression: bump onboarding-ui version", + "userLogin": "guijun13", + "description": "- Bump to 'next' the onboarding-ui package from fuselage.\r\n- Update from 'companyEmail' to 'email' adminData usage types", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25335", + "title": "Chore: Create README.md for Rest Typings", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25327", + "title": "Regression: Messages in new message template Crashing.", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25323", + "title": "Regression: Better MongoDB connection management for micro services", + "userLogin": "sampaiodiego", + "milestone": "4.7.0", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25250", + "title": "Regression: Validate empty fields for Message template", + "userLogin": "gabriellsh", + "milestone": "4.8.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25319", + "title": "Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings", + "userLogin": "geekgonecrazy", + "description": "The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work.\r\n\r\nThis temporarily switches to a fork of the matrix-appservice-bridge package.\r\n\r\nMade changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine).", + "milestone": "4.7.0", + "contributors": [ + "geekgonecrazy", + "web-flow", + "d-gubert" + ] + }, + { + "pr": "25255", + "title": "Regression: Change preference to be default legacy messages", + "userLogin": "gabriellsh", + "milestone": "4.8.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25306", + "title": "Regression: Fix reply button not working when hideFlexTab is enabled", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25311", + "title": "Regression: Add eslint package to micro services Dockerfile", + "userLogin": "sampaiodiego", + "milestone": "4.7.0", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25218", + "title": "Chore: ensure scripts use cross-env and ignore some dirs (ROC-54)", + "userLogin": "souzaramon", + "description": "- data and test-failure should be ignored\r\n- ensure scripts use cross-env", + "contributors": [ + "souzaramon" + ] + }, + { + "pr": "25313", + "title": "Regression: Revert Bugsnag version", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25305", + "title": "Regression: eslint not running on packages", + "userLogin": "pierre-lehnen-rc", + "contributors": [ + "pierre-lehnen-rc", + "ggazzo" + ] + }, + { + "pr": "25299", + "title": "Regression: Add `isPending` status to message", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25301", + "title": "Regression: Shows error if micro service cannot connect to Mongo", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25287", + "title": "Regression: Use exact Node version on micro services Docker images", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25286", + "title": "Chore: Add root package.json to houston files", "userLogin": "d-gubert", - "description": "This is an enabler for our initiative to support NPM packages in the Apps-Engine. \r\n\r\nCurrently, the packages (zip files) for Rocket.Chat Apps are stored as a base64 encoded string in a document in the database, which constrains us due to the size limit of a document in MongoDB (16Mb).\r\n\r\nWhen we allow apps to include NPM packages, the size of the App package itself will be potentially _very large_ (I'm looking at you `node_modules`). Thus we'll be changing the strategy to store apps either with GridFS or the host's File System itself.", - "milestone": "4.0.0", + "description": "See title", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25284", + "title": "Chore: Sync with master", + "userLogin": "d-gubert", + "contributors": [ + "sampaiodiego", + "d-gubert", + "web-flow" + ] + }, + { + "pr": "25269", + "title": "Chore: Minor dependency updates", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "25224", + "title": "Chore: Add yarn plugin to check node and yarn version", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25280", + "title": "Chore: Remove package-lock.json from houston files", + "userLogin": "d-gubert", + "description": "Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore", + "contributors": [ + "d-gubert" + ] + }, + { + "pr": "25260", + "title": "[FIX] Adjust email label in Setup Wizard i18n files", + "userLogin": "guijun13", + "description": "- remove 'Company' label on onboarding email keys in certain languages", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25275", + "title": "Chore: Fix return type warnings", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "23870", + "title": "[NEW] Expand Apps Engine's environment variable allowed list", + "userLogin": "cuonghuunguyen", + "milestone": "4.7.0", + "contributors": [ + null, + "debdutdeb", + "web-flow", + "cuonghuunguyen", + "dougfabris" + ] + }, + { + "pr": "25273", + "title": "Regression: Fix federation Matrix bridge startup", + "userLogin": "sampaiodiego", + "milestone": "4.7.0", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25092", + "title": "[FIX] Message preview not available for queued chats", + "userLogin": "murtaza98", + "milestone": "4.7.0", + "contributors": [ + "murtaza98", + "KevLehman" + ] + }, + { + "pr": "23688", + "title": "[NEW] Alpha Matrix Federation", + "userLogin": "alansikora", + "description": "Experimental support for Matrix Federation with a Bridge\r\n\r\nhttps://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4", + "milestone": "4.7.0", + "contributors": [ + "alansikora", + "geekgonecrazy", + "MarcosSpessatto", + "rodrigok" + ] + }, + { + "pr": "25259", + "title": "Chore: Bump Fuselage packages", + "userLogin": "dougfabris", + "milestone": "4.7.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25261", + "title": "[FIX] Incorrect websocket url in livechat widget", + "userLogin": "debdutdeb", + "milestone": "4.7.0", + "contributors": [ + "debdutdeb" + ] + }, + { + "pr": "25007", + "title": "[FIX] Showing Blank Message Inside Report", + "userLogin": "nishant23122000", + "description": "https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4", + "contributors": [ + "nishant23122000" + ] + }, + { + "pr": "25251", + "title": "Regression: Add select message to system message and thread preview and allow select on legacy template", + "userLogin": "filipemarins", + "milestone": "4.7.0", + "contributors": [ + "filipemarins", + "ggazzo", + "web-flow", + "gabriellsh", + "dougfabris" + ] + }, + { + "pr": "25239", + "title": "[FIX] Add katex render to new message react template", + "userLogin": "filipemarins", + "milestone": "4.7.0", + "contributors": [ + "filipemarins", + "ggazzo", + "dougfabris" + ] + }, + { + "pr": "25257", + "title": "Chore: Update Livechat to the last version", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "24515", + "title": "[FIX] Custom sound error toast messages", + "userLogin": "Himanshu664", + "milestone": "4.7.0", + "contributors": [ + "Himanshu664", + "dougfabris" + ] + }, + { + "pr": "25211", + "title": "Regression: Avatar not loading on first direct message", + "userLogin": "filipemarins", + "description": "fix avatar not loading on a first direct message", + "milestone": "4.7.0", + "contributors": [ + "filipemarins", + "ggazzo" + ] + }, + { + "pr": "25254", + "title": "Regression: Show username and real name on the message system", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25217", + "title": "[IMPROVE] Performance for some Omnichannel features", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25200", + "title": "[FIX] room creation fails if app framework is disabled", + "userLogin": "debdutdeb", + "milestone": "4.7.0", + "contributors": [ + "debdutdeb" + ] + }, + { + "pr": "24565", + "title": "[IMPROVE] Add OTR Room States", + "userLogin": "yash-rajpal", + "description": "Earlier OTR room uses only 2 states, we need more states to support future features. \r\nThis adds more states for the OTR contextualBar.\r\n\r\n- Expired\r\n\"Screen\r\n\r\n- Declined\r\nScreen Shot 2022-04-20 at 13 49 28\r\n\r\n- Error\r\n\"Screen", + "milestone": "4.7.0", + "contributors": [ + "yash-rajpal", + "dougfabris" + ] + }, + { + "pr": "25170", + "title": "[FIX] Client disconnection on network loss", + "userLogin": "amolghode1981", + "description": "Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online\r\nunless agent explicitly logs off.\r\nAgent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways.\r\n1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off\r\nin the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh.\r\n2. Second reason is when computer goes in sleep mode.\r\n3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped.\r\n\r\nSolution:\r\nThe idea is to detect the network disconnection and start the start the attempts to reconnect.\r\nThe detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not\r\ncall onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are\r\nused.\r\n\r\nThe number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to\r\nreconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped.\r\n\r\nWhen the server is disconnected, it should be indicated on the phone button.", + "contributors": [ + "amolghode1981", + "KevLehman" + ] + }, + { + "pr": "25244", + "title": "[FIX] Read receipts show with color gray when not read yet", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins", + "gabriellsh" + ] + }, + { + "pr": "25230", + "title": "[FIX] VoIP disabled/enabled sequence puts voip agent in error state", + "userLogin": "amolghode1981", + "description": "Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side)\r\n\r\nIt was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk.\r\n\r\nSolution:\r\n\r\n1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value.\r\n2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance.\r\n3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler.", + "contributors": [ + "amolghode1981", + "KevLehman" + ] + }, + { + "pr": "25087", + "title": "[NEW] Add expire index to integration history", + "userLogin": "geekgonecrazy", + "milestone": "4.7.0", + "contributors": [ + "geekgonecrazy" + ] + }, + { + "pr": "24521", + "title": "Chore: update OTR icon", + "userLogin": "kibonusp", + "description": "I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage.", + "milestone": "4.7.0", + "contributors": [ + "kibonusp", + "yash-rajpal", + "web-flow", + "tassoevan" + ] + }, + { + "pr": "25237", + "title": "[FIX] Toolbox hiding under contextual bar", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25231", + "title": "[IMPROVE] Added MaxNickNameLength and MaxBioLength constants", + "userLogin": "aakash-gitdev", + "contributors": [ + "aakash-gitdev", + "web-flow", + "gabriellsh" + ] + }, + { + "pr": "25220", + "title": "[FIX] Desktop notification on multi-instance environments", + "userLogin": "sampaiodiego", + "milestone": "4.6.3", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25175", + "title": "[FIX] Reply button behavior on broadcast channel", + "userLogin": "filipemarins", + "description": "Hide reply button for the user that sent the message", + "contributors": [ + "filipemarins", + "web-flow" + ] + }, + { + "pr": "25216", + "title": "[FIX] Read receipts showing before message read", + "userLogin": "filipemarins", + "contributors": [ + "filipemarins" + ] + }, + { + "pr": "25222", + "title": "[FIX] Add reaction not working in legacy messages", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25223", + "title": "Chore: Add error boundary to message component", + "userLogin": "gabriellsh", + "description": "Not crash the whole application if something goes wrong in the MessageList component.\r\n\r\n![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png)", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25130", + "title": "Chore: Update Livechat version", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "25073", + "title": "[FIX] AgentOverview analytics wrong departmentId parameter", + "userLogin": "paulobernardoaf", + "description": "When filtering the analytics charts by department, data would not appear because the object:\r\n```js\r\n{\r\n value: \"department-id\",\r\n label: \"department-name\"\r\n}\r\n```\r\nwas being used in the `departmentId` parameter.\r\n\r\n- Before:\r\n![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png)\r\n\r\n- After:\r\n![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png)", + "milestone": "4.7.0", + "contributors": [ + "paulobernardoaf" + ] + }, + { + "pr": "25056", + "title": "[FIX] Close room when dismiss wrap up call modal", + "userLogin": "tiagoevanp", + "milestone": "4.7.0", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "25208", + "title": "Regression: yarn dev triggers build dependencies", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "24714", + "title": "[FIX] Added invalid password error message", + "userLogin": "Himanshu664", + "milestone": "4.7.0", + "contributors": [ + "Himanshu664", + "dougfabris" + ] + }, + { + "pr": "25196", + "title": "Chore: Tests with Playwright (task: ROC-28, 09-channels)", + "userLogin": "tmontini", + "contributors": [ + "tmontini" + ] + }, + { + "pr": "25174", + "title": "Chore: Template to generate packages", + "userLogin": "ggazzo", + "description": "```\r\nnpx hygen package new test\r\n```", + "contributors": [ + "ggazzo", + "web-flow", + "sampaiodiego" + ] + }, + { + "pr": "25193", + "title": "Regression: Fix micro services Docker build", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "25180", + "title": "Chore: Remove duplicated useUserRoom", + "userLogin": "dougfabris", + "milestone": "4.7.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25167", + "title": "Chore: TS migration SortList", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "24933", + "title": "[FIX] Deactivating user breaks if user is the only room owner", + "userLogin": "sidmohanty11", + "description": "## Before\r\n\r\nhttps://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4\r\n\r\n\r\n\r\n## After\r\nhttps://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4", + "milestone": "4.6.2", + "contributors": [ + "sidmohanty11", + "sampaiodiego" + ] + }, + { + "pr": "25181", + "title": "Regression: Fix services Docker build on CI", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25089", + "title": "[FIX] UserCard sanitization", + "userLogin": "dougfabris", + "description": "- Rewrites the component to TS\r\n- Fixes some visual issues\r\n\r\n### before\r\n![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png)\r\n\r\n### after\r\n![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png)", + "milestone": "4.7.0", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25085", + "title": "Chore: move definitions to packages", + "userLogin": "ggazzo", + "milestone": "3.7.0", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25168", + "title": "Regression: CI playwright", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25125", + "title": "Chore: Convert NotificationStatus to TS", + "userLogin": "jeanfbrito", + "contributors": [ + "jeanfbrito", + "ggazzo" + ] + }, + { + "pr": "25148", + "title": "[FIX] Message menu action not working on legacy messages.", + "userLogin": "gabriellsh", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25122", + "title": "Chore: Tests with Playwright (task: All works)", + "userLogin": "weslley543", + "contributors": [ + "weslley543" + ] + }, + { + "pr": "25129", + "title": "Chore: Remove old files from removed Omnichannel feature", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25128", + "title": "Chore: Convert admin custom sound to tsx", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25126", + "title": "Chore: Migrate oauth2server to typescript", + "userLogin": "KevLehman", + "contributors": [ + "KevLehman" + ] + }, + { + "pr": "25123", + "title": "Chore: Convert LivechatAgentActivity to raw model and TS", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25124", + "title": "Chore: Remove unused Drone CI files", + "userLogin": "geekgonecrazy", + "contributors": [ + "geekgonecrazy", + "web-flow" + ] + }, + { + "pr": "25121", + "title": "Chore: Convert Mailer to TS", + "userLogin": "juliajforesti", + "contributors": [ + "juliajforesti", + "sampaiodiego" + ] + }, + { + "pr": "25107", + "title": "Regression: Fix CI monorepo build", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "25074", + "title": "Chore: Monorepo ", + "userLogin": "ggazzo", + "milestone": "3.7.0", + "contributors": [ + "sampaiodiego", + "ggazzo" + ] + }, + { + "pr": "25097", + "title": "[IMPROVE] Rename upgrade tab routes", + "userLogin": "guijun13", + "description": "Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test.", + "contributors": [ + "guijun13" + ] + }, + { + "pr": "25076", + "title": "Bump eslint-plugin-anti-trojan-source from 1.0.6 to 1.1.0", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "24936", + "title": "[FIX] End call button disappearing when on-hold", + "userLogin": "tiagoevanp", + "contributors": [ + "tiagoevanp" + ] + }, + { + "pr": "24932", + "title": "[FIX] Use correct room property for call ended at", + "userLogin": "MartinSchoeler", + "contributors": [ + "MartinSchoeler" + ] + }, + { + "pr": "25022", + "title": "[FIX] Proxy settings being ignored", + "userLogin": "pierre-lehnen-rc", + "description": "Modify Meteor's `HTTP.call` to add back proxy support", + "milestone": "4.6.1", + "contributors": [ + "pierre-lehnen-rc", + "sampaiodiego" + ] + }, + { + "pr": "25082", + "title": "[FIX] Invitation links don't redirect to the registration form", + "userLogin": "yash-rajpal", + "milestone": "4.6.1", + "contributors": [ + "yash-rajpal" + ] + }, + { + "pr": "23971", + "title": "[NEW] Message Template React Component", + "userLogin": "ggazzo", + "description": "Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template).\r\n\r\n\r\n\r\n![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png)\r\nIn case you encounter any problems, or want to compare, temporarily it is possible to use the old version\r\n\r\n\"image\"", + "contributors": [ + "ggazzo", + "sampaiodiego" + ] + }, + { + "pr": "25069", + "title": "[FIX] FormData uploads not working", + "userLogin": "gabriellsh", + "milestone": "4.6.1", + "contributors": [ + "gabriellsh", + "dougfabris" + ] + }, + { + "pr": "19866", + "title": "[FIX] Video and Audio not skipping forward", + "userLogin": "MartinSchoeler", "contributors": [ - "d-gubert", + "MartinSchoeler", + "tassoevan", "web-flow", - "thassiov" + "dougfabris" ] }, { - "pr": "23243", - "title": "[FIX] Modals is cutting pixels of the content", + "pr": "25067", + "title": "[FIX] NPS never finishing sending results", + "userLogin": "sampaiodiego", + "milestone": "4.6.1", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "24405", + "title": "[IMPROVE] Add tooltip to sidebar room menu", + "userLogin": "Himanshu664", + "milestone": "4.7.0", + "contributors": [ + "Himanshu664", + "web-flow", + "dougfabris" + ] + }, + { + "pr": "24431", + "title": "[IMPROVE] Added tooltip options for message menu", + "userLogin": "Himanshu664", + "milestone": "4.7.0", + "contributors": [ + "Himanshu664", + "dougfabris" + ] + }, + { + "pr": "24166", + "title": "[FIX] Replace encrypted text to Encrypted Message Placeholder", + "userLogin": "yash-rajpal", + "description": "### before \r\n![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png)\r\n\r\n### after\r\n\"Screenshot", + "milestone": "4.7.0", + "contributors": [ + "yash-rajpal", + "albuquerquefabio", + "web-flow" + ] + }, + { + "pr": "24984", + "title": "[FIX] Prevent sequential messages edited icon to hide on hover", "userLogin": "dougfabris", - "description": "Fuselage Dependency: [543](https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/543)\r\n![image](https://user-images.githubusercontent.com/27704687/134049227-3cd1deed-34ba-454f-a95e-e99b79a7a7b9.png)", + "description": "### before\r\n\"Screen\r\n\r\n### after\r\n\"Screen", + "milestone": "4.7.0", "contributors": [ "dougfabris" ] }, { - "pr": "23232", - "title": "[IMPROVE] Load code highlighting languages on demand and fixes on new message parser", - "userLogin": "ggazzo", - "description": "Now we have this setting called 'Code highlighting languages list' where you can define the languages that you want to be loaded by default.", + "pr": "25024", + "title": "[IMPROVE] Improve active/hover colors in account sidebar", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "ggazzo", - "sampaiodiego" + "Himanshu664" ] }, { - "pr": "23223", - "title": "[BREAK][ENTERPRISE] Missing headers in CSV files downloaded from the Engagement Dashboard", - "userLogin": "matheusbsilva137", - "description": "- Add headers to all CSV files downloaded from the \"Messages\" and \"Channels\" tabs from the Engagement Dashboard;\r\n - Add headers to the CSV file downloaded from the \"Users by time of day\" section (in the \"Users\" tab).", + "pr": "24856", + "title": "[FIX] Full error message is visible", + "userLogin": "Himanshu664", + "milestone": "4.7.0", "contributors": [ - "matheusbsilva137", - "casalsgh", - "web-flow", + "Himanshu664", "tassoevan" ] }, { - "pr": "23074", - "title": "[FIX] transfer message when tranferring room by Apps Engine", - "userLogin": "cuonghuunguyen", - "milestone": "4.0.0", + "pr": "24708", + "title": "Chore: Cancel running jobs if PR is updated", + "userLogin": "debdutdeb", "contributors": [ - "cuonghuunguyen", - "KevLehman", + "debdutdeb", + "sampaiodiego", "web-flow" ] }, { - "pr": "22392", - "title": "[NEW] Add activity indicators for Uploading and Recording using new API; Support thread context; Deprecate the old typing API", - "userLogin": "sumukhah", - "milestone": "4.0.0", + "pr": "24900", + "title": "Chore: organize test files and fix code coverage", + "userLogin": "tmontini", "contributors": [ - "sumukhah", + null, + "tmontini", "rodrigok" ] }, { - "pr": "23236", - "title": "Bump ejson from 2.2.1 to 2.2.2 in /ee/server/services", + "pr": "24464", + "title": "Chore: Missing keys in APIsDisplay", + "userLogin": "dougfabris", + "milestone": "4.7.0", + "contributors": [ + "dougfabris", + "tassoevan" + ] + }, + { + "pr": "25057", + "title": "Bump ejson from 2.2.1 to 2.2.2", "userLogin": "dependabot[bot]", "contributors": [ "dependabot[bot]", @@ -84946,934 +89498,1159 @@ ] }, { - "pr": "23056", - "title": "[FIX] Remove doubled \"Canned Responses\" strings", - "userLogin": "matheusbsilva137", - "description": "- Remove doubled canned response setting introduced in #22703 (by setting id change);\r\n - Update \"Canned Responses\" keys to \"Canned_Responses\".", + "pr": "25053", + "title": "Chore: Remove Alpine image deps after using them", + "userLogin": "sampaiodiego", "contributors": [ - "matheusbsilva137", - "web-flow", - "tassoevan" + "sampaiodiego" ] }, { - "pr": "23110", - "title": "[FIX] Can't edit profile information if any field update setting is disabled", - "userLogin": "matheusbsilva137", - "description": "- Check which fields have been updated before throwing errors in `validateUserEditing`.", + "pr": "25052", + "title": "Bump pino and pino-pretty", + "userLogin": "sampaiodiego", "contributors": [ - "matheusbsilva137", - "web-flow", - "casalsgh" + "sampaiodiego" + ] + }, + { + "pr": "25050", + "title": "[FIX] Upgrade Tab showing for a split second", + "userLogin": "gabriellsh", + "milestone": "4.6.1", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "25055", + "title": "[FIX] UserAutoComplete not rendering UserAvatar correctly", + "userLogin": "dougfabris", + "description": "### before\r\n![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png)\r\n\r\n### after\r\n![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png)", + "milestone": "4.6.1", + "contributors": [ + "dougfabris" + ] + }, + { + "pr": "25031", + "title": "Chore: TS conversion folder client", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "24991", + "title": "Bump minimist from 1.2.5 to 1.2.6 in /ee/server/services", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "25002", + "title": "Bump template-file from 6.0.0 to 6.0.1", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "25042", + "title": "Bump body-parser from 1.19.2 to 1.20.0 in /ee/server/services", + "userLogin": "dependabot[bot]", + "contributors": [ + "dependabot[bot]", + "web-flow" + ] + }, + { + "pr": "25043", + "title": "i18n: Language update from LingoHub 🤖 on 2022-04-04Z", + "userLogin": "lingohub[bot]", + "contributors": [ + null ] }, { - "pr": "23037", - "title": "[FIX] \"Read Only\" and \"Allow Reacting\" system messages are missing in rooms", - "userLogin": "matheusbsilva137", - "description": "- Add system message to notify changes on the **\"Read Only\"** setting;\r\n - Add system message to notify changes on the **\"Allow Reacting\"** setting;\r\n - Fix \"Allow Reacting\" setting's description (updated from \"Only authorized users can write new messages\" to \"Only authorized users can react to messages\").\r\n![system-messages](https://user-images.githubusercontent.com/36537004/130883527-9eb47fcd-c8e5-41fb-af34-5d99bd0a6780.PNG)", + "pr": "25028", + "title": "Merge master into develop & Set version to 4.7.0-develop", + "userLogin": "sampaiodiego", + "contributors": [ + "AllanPazRibeiro", + "sampaiodiego", + "web-flow" + ] + } + ] + }, + "4.6.4": { + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [] + }, + "4.7.3": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [] + }, + "4.7.4": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "553", + "title": "Load missed messages from opened rooms when reconnect", + "userLogin": "rodrigok", + "contributors": [ + "rodrigok" + ] + } + ] + }, + "4.8.1": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "3.6", + "4.0", + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "25723", + "title": "[FIX] Wrong argument name preventing Omnichannel Chat Forward to User ", + "userLogin": "dudanogueira", + "milestone": "4.8.1", "contributors": [ - "matheusbsilva137", - "web-flow", - "ostjen", - "casalsgh" + "dudanogueira" ] }, { - "pr": "23277", - "title": "[BREAK] Remove old migrations up to version 2.4.14", - "userLogin": "sampaiodiego", - "description": "To update to version 4.0.0 you'll need to be running at least version 3.0.0, otherwise you might loose some database migrations which might have unexpected effects.\r\n\r\nThis aims to clean up the code, since upgrades jumping 2 major versions are too risky and hard to maintain, we'll keep only migration from that last major (in this case 3.x).", + "pr": "25669", + "title": "[FIX] Bump meteor-node-stubs to version 1.2.3", + "userLogin": "Sh0uld", + "description": "With meteor-node-stubs version 1.2.3 a bug was fixed, which occured in issue #25460 and probably #25513 (last one not tested).\r\nFor the issue in meteor see: https://github.com/meteor/meteor/issues/11974", + "milestone": "4.8.1", "contributors": [ - "sampaiodiego" + "Sh0uld", + "ggazzo" ] }, { - "pr": "23276", - "title": "[FIX] Logging out from other clients", - "userLogin": "sampaiodiego", + "pr": "25708", + "title": "[FIX] AccountBox checks for condition", + "userLogin": "tiagoevanp", + "milestone": "4.8.1", "contributors": [ - "sampaiodiego", - "pierre-lehnen-rc", - "web-flow" + "tiagoevanp" ] }, { - "pr": "23262", - "title": "[FIX] Avoid bots to be marked as unavailable when log off/login", + "pr": "25781", + "title": "[FIX] Fix prom-client new promise usage", "userLogin": "KevLehman", - "milestone": "4.0.0", + "milestone": "4.8.1", "contributors": [ - "KevLehman", - "murtaza98", - "web-flow" + "KevLehman" ] - }, + } + ] + }, + "5.0.0-rc.1": { + "node_version": "14.18.3", + "npm_version": "6.14.15", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23261", - "title": "[FIX] Stop queue when Omnichannel is disabled or the routing method does not support it", - "userLogin": "KevLehman", - "description": "- Add missing key logs\r\n- Stop queue (and logs) when livechat is disabled or when routing method does not support queue\r\n- Stop ignoring offline bot agents from delegation (previously, if a bot was offline, even with \"Assign new conversations to bot agent\" enabled, bot will be ignored and chat will be left in limbo (since bot was assigned, but offline).", - "milestone": "4.0.0", + "pr": "26127", + "title": "Regression: Close button on modals created via apps not working", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "KevLehman", "murtaza98", + "sampaiodiego", "web-flow" ] }, { - "pr": "23017", - "title": "[NEW] Seats Cap", - "userLogin": "tassoevan", - "description": "- Adding New Members\r\n - Awareness of seats usage while adding new members\r\n - Seats Cap about to be reached\r\n - Seats Cap reached\r\n - Request more seats\r\n- Warning Admins\r\n - System telling admins max seats are about to exceed\r\n - System telling admins max seats were exceed\r\n - Metric on Info Page\r\n - Request more seats\r\n- Warning Members\r\n - Invite link\r\n - Block creating new invite links\r\n - Block existing invite links (feedback on register process) \r\n - Register to Workspaces\r\n- Emails\r\n - System telling admins max seats are about to exceed\r\n - System telling admins max seats were exceed", + "pr": "26125", + "title": "Regression: Unable to click on UiKit buttons provided by apps", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "tassoevan", - "pierre-lehnen-rc", - "web-flow", - "ggazzo", - "gabriellsh", - "g-thome" + "murtaza98" ] }, { - "pr": "23269", - "title": "Chore: Update pino and pino-pretty", + "pr": "26140", + "title": "Regression: Fix assets format", "userLogin": "sampaiodiego", "contributors": [ - "sampaiodiego" + "sampaiodiego", + "ggazzo" ] }, { - "pr": "23255", - "title": "Chore: Make SMTP empty on docker-compose so registration won't hang out of the box", - "userLogin": "geekgonecrazy", + "pr": "26062", + "title": "Regression: Update error message on `useEndpointActionExperimental`", + "userLogin": "LucianoPierdona", + "description": "This PR changes the way we show an error message to the user on the `useEndpointActionExperimental` hook, previously for `Object` error messages it was being shown as undefined", "contributors": [ - "geekgonecrazy", + "LucianoPierdona", + "ggazzo", "web-flow" ] }, { - "pr": "23204", - "title": "[FIX] Wrap canned-responses endpoints with ee license", - "userLogin": "tiagoevanp", - "milestone": "4.0.0", + "pr": "26129", + "title": "Regression: All users in members list showing as federated", + "userLogin": "gabriellsh", + "milestone": "5.0.0", "contributors": [ - "tiagoevanp", - "web-flow", - "KevLehman" + "gabriellsh" ] }, { - "pr": "23150", - "title": "[FIX] Omnichannel transcript button without user's email", - "userLogin": "tiagoevanp", - "milestone": "4.0.0", + "pr": "26115", + "title": "Regression: Do not show federated tooltip on non-federated rooms", + "userLogin": "MarcosSpessatto", + "milestone": "5.0.0", "contributors": [ - "tiagoevanp", - "web-flow", - "KevLehman" + "MarcosSpessatto", + "ggazzo", + "web-flow" ] }, { - "pr": "23263", - "title": "Chore: Re-enable session tests on local after removal of mongo-unit", - "userLogin": "KevLehman", + "pr": "26117", + "title": "Regression: Users on new sessions are forced to re-configure 2fa", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", "contributors": [ - "KevLehman", - "sampaiodiego" + "pierre-lehnen-rc" ] }, { - "pr": "23107", - "title": "[BREAK] Moved role-sync and advanced SAML settings to EE", - "userLogin": "ostjen", + "pr": "26080", + "title": "Regression: Fix marketplace app apis visibility problem", + "userLogin": "rique223", + "description": "Solved a problem that showed an unwanted zero in place of the APIs section for apps that weren't installed/did not have an APIs section.\r\nBefore:\r\n![image](https://user-images.githubusercontent.com/43561537/176743542-8f5d2e97-48e7-4947-a82a-43c3a15ea0ac.png)\r\n\r\nAfter(non installed app):\r\n![image](https://user-images.githubusercontent.com/43561537/176744082-0139e15b-b03b-4c03-9267-9a17d14b70e9.png)\r\n\r\nAfter(installed app)\r\n![image](https://user-images.githubusercontent.com/43561537/176772870-c5382edc-59e6-42e4-8dfa-f1e4fd0267c0.png)", + "milestone": "5.0.0", "contributors": [ - "ostjen", - "web-flow", - "pierre-lehnen-rc" + "rique223" ] }, { - "pr": "23199", - "title": "[IMPROVE] Change occurences of Livechat to Omnichannel in ES translations were applicable", - "userLogin": "KevLehman", - "milestone": "4.0.0", + "pr": "26116", + "title": "Regression: Changing isEnterprise useQuery name to prevent conflict of queries", + "userLogin": "hugocostadev", + "description": "Changed the name of useQuery hook to prevent conflict of queries with same name.", + "milestone": "5.0.0", "contributors": [ - "KevLehman", - "web-flow" + "hugocostadev" ] }, { - "pr": "23230", - "title": "Regression: Log Sections not respecting Log Level setting", + "pr": "26101", + "title": "Regression: [VideoConference] Callee client behaves improperly when accepting a call from someone who lost the connection", "userLogin": "pierre-lehnen-rc", + "description": "If the caller loses connection and the callee accepts the call anyway, the client will wait for five seconds for confirmation that they can join the call. This PR improves the UI behavior during those five seconds.", + "milestone": "5.0.0", "contributors": [ "pierre-lehnen-rc" ] }, { - "pr": "22907", - "title": "[BREAK] Removed support of MongoDB 3.4; Deprecated MongoDB 3.6 and 4.0", - "userLogin": "ostjen", - "milestone": "4.0.0", - "contributors": [ - "ostjen", - "web-flow", - "rodrigok" - ] - }, - { - "pr": "23254", - "title": "Regression: Fix user registration stuck", + "pr": "26113", + "title": "Chore: Change stats to daily", "userLogin": "sampaiodiego", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego", - "tassoevan", - "web-flow" + "sampaiodiego" ] - }, + } + ] + }, + "5.0.0-rc.2": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23219", - "title": "[FIX] Mark agents as unavailable when they logout", - "userLogin": "KevLehman", - "milestone": "4.0.0", + "pr": "26184", + "title": "Regression: Align TypeScript version across workspaces", + "userLogin": "tassoevan", + "description": "Some places were still referring to TypeScript 4.3.4 instead of 4.5.5, so this PR targets it.", "contributors": [ - "KevLehman", - "renatobecker", - "web-flow", - "murtaza98" + "tassoevan" ] }, { - "pr": "23244", - "title": "[FIX] Toolbox click not working on Safari(iOS)", - "userLogin": "dougfabris", + "pr": "25991", + "title": "Chore: Update Meteor 2.7.3", + "userLogin": "sampaiodiego", + "milestone": "5.0.0", "contributors": [ - "ggazzo", - "dougfabris" + "sampaiodiego", + "ggazzo" ] }, { - "pr": "23185", - "title": "[FIX] Omnichannel On hold chats being forwarded to offline agents", - "userLogin": "murtaza98", - "milestone": "4.0.0", + "pr": "26153", + "title": "Chore: update avatar colors", + "userLogin": "juliajforesti", "contributors": [ - "murtaza98", - "web-flow" + "juliajforesti" ] }, { - "pr": "23171", - "title": "[BREAK] LDAP Refactoring", - "userLogin": "pierre-lehnen-rc", + "pr": "26119", + "title": "Regression: Added missing call button to contact center calls list", + "userLogin": "aleksandernsilva", + "description": "This PR adds a call button to the contact center calls list rows.", + "milestone": "5.0.0", "contributors": [ - "pierre-lehnen-rc", - "web-flow" + "aleksandernsilva", + "ggazzo" ] }, { - "pr": "23190", - "title": "[IMPROVE] Canned response admin settings", + "pr": "26138", + "title": "Regression: Calling info on VoipFooter when performing an outbound call", "userLogin": "tiagoevanp", - "milestone": "4.0.0", + "description": "![image](https://user-images.githubusercontent.com/17487063/177395438-a0b2d30a-e0e2-4a31-9b55-2c6c3216bbd7.png)", "contributors": [ - "tiagoevanp", - "web-flow", - "KevLehman" + "tiagoevanp" ] }, { - "pr": "23198", - "title": "Chore: Update Livechat widget to 1.9.4", - "userLogin": "MartinSchoeler", + "pr": "26135", + "title": "Regression: Added missing call button in the contact info contextual bar", + "userLogin": "aleksandernsilva", "contributors": [ - "MartinSchoeler", - "KevLehman", - "web-flow" + "aleksandernsilva" ] }, { - "pr": "23209", - "title": "[FIX] Save department agents ", - "userLogin": "tiagoevanp", - "milestone": "4.0.0", + "pr": "26141", + "title": "Regression: Emojis displaying as `:undefined:`", + "userLogin": "gabriellsh", "contributors": [ - "tiagoevanp", - "web-flow", - "KevLehman", - "casalsgh" + "gabriellsh" ] }, { - "pr": "23117", - "title": "[FIX] Wrong docs link on Omni-Webhook page", + "pr": "26111", + "title": "Regression: Correct call ringtones", "userLogin": "murtaza98", + "description": "- outbound-call-ringing ringtone: Should be played when the outbound call is initiated and not yet established(Current implementation is playing the incoming-call ringtone)\r\n- call-ended ringtone: Should be played whenever a call ends.", + "milestone": "5.0.0", "contributors": [ - "murtaza98", - "web-flow", - "KevLehman" - ] - }, - { - "pr": "23221", - "title": "[IMPROVE] Throw error if no appId is provided to useUIKitHandleAction", - "userLogin": "gabriellsh", - "contributors": [ - "gabriellsh" + "murtaza98" ] }, { - "pr": "22957", - "title": "[IMPROVE] Do not re-create General room on every server start", - "userLogin": "matheusbsilva137", - "description": "- Check the `Show_Setup_Wizard` Setting's value to control whether the general room should be created. This channel will only be created if the `Show_Setup_Wizard` Setting is 'pending'.", + "pr": "26097", + "title": "Regression: Update message reaction text", + "userLogin": "filipemarins", "contributors": [ - "matheusbsilva137", - "web-flow", - "casalsgh" + "filipemarins" ] }, { - "pr": "22985", - "title": "[NEW][APPS] Get livechat's room transcript via bridge method", - "userLogin": "thassiov", - "description": "Adds a new method for retrieving a room's transcript via a new method in the Livechat bridge", - "milestone": "4.0.0", + "pr": "26134", + "title": "Regression: Add better error messages when call fails", + "userLogin": "KevLehman", + "milestone": "5.0.0", "contributors": [ - "thassiov", - "web-flow", - "d-gubert" + "KevLehman" ] }, { - "pr": "23212", - "title": "Regression: `renderEmoji` helper referred as a template", - "userLogin": "tassoevan", + "pr": "26128", + "title": "Regression: Broken emoji picker on Livechat", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "tassoevan" + "murtaza98" ] - }, + } + ] + }, + "5.0.0-rc.3": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "22542", - "title": "Chore: Convert VerticalBar component to typescript", - "userLogin": "dougfabris", + "pr": "26201", + "title": "Chore: Info page", + "userLogin": "ggazzo", "contributors": [ - "dougfabris", - "gabriellsh", - "web-flow", - "tassoevan" + "ggazzo" ] }, { - "pr": "21176", - "title": "[FIX] Add missing custom fields to apps' users converter", - "userLogin": "cuonghuunguyen", - "milestone": "4.0.0", + "pr": "26199", + "title": "Regression: Fix command previews", + "userLogin": "d-gubert", + "description": "Fixes slash command previews not being showed", + "milestone": "5.0.0", "contributors": [ - "cuonghuunguyen", - "web-flow", - "d-gubert", - "thassiov", - "casalsgh" + "d-gubert" ] }, { - "pr": "23194", - "title": "Regression: Fix view logs admin screen", - "userLogin": "sampaiodiego", + "pr": "26205", + "title": "Chore: Change Apps-Engine version source for info", + "userLogin": "d-gubert", + "description": "Now that we're using `yarn`, the version stored in the `package.json` is no longer the resolved one, but it matches the input. This means that when we ran `yarn add @rocket.chat/apps-engine@alpha`, yarn saves `\"alpha\"` as the version of the package, while NPM added the resolved version for the tag, e.g. `\"1.33.0-alpha.6507\"`. This ends up breaking a few places where we need the Apps-Engine version for communication with the Marketplace.\r\n\r\nWith this PR we change the source of that info so the problem doesn't happen anymore.", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego", - "web-flow" + "d-gubert" ] }, { - "pr": "23162", - "title": "[BREAK] Remove deprecated endpoints", - "userLogin": "sampaiodiego", - "description": "The following REST endpoints were removed:\r\n\r\n- `/api/v1/emoji-custom`\r\n- `/api/v1/info`\r\n- `/api/v1/permissions`\r\n- `/api/v1/permissions.list`\r\n\r\nThe following Real time API Methods were removed:\r\n\r\n- `getFullUserData`\r\n- `getServerInfo`\r\n- `livechat:saveOfficeHours`", + "pr": "26148", + "title": "Regression: moving Community Watermark to `ee` folder", + "userLogin": "hugocostadev", + "description": "Due to legal reasons, the Watermark used in community Edition was moved to Enterprise folder `ee`", "contributors": [ - "sampaiodiego", - "tassoevan", - "web-flow" + "hugocostadev" ] }, { - "pr": "23205", - "title": "Regression: View Logs administration page crashing", - "userLogin": "tassoevan", - "description": "Fixes the `stdout.queue` endpoint; makes the components type-safe.", + "pr": "26136", + "title": "Regression: Send files with `enter` key", + "userLogin": "gabriellsh", "contributors": [ - "tassoevan", - "web-flow" + "gabriellsh" ] - }, + } + ] + }, + "5.0.0-rc.4": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23178", - "title": "Chore: Move client helpers", - "userLogin": "tassoevan", - "description": "Moves helper modules under `app/` to `client/lib/utils/`.", + "pr": "26226", + "title": "Regression: Fix files list endpoints", + "userLogin": "sampaiodiego", "contributors": [ - "tassoevan", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23200", - "title": "Chore: Change Ubuntu version to 20.04 on all GitHub Actions", + "pr": "26194", + "title": "Regression: Fix Omnichannel not working after meteor update", "userLogin": "KevLehman", + "description": "Fixed things:\r\n- Omnichannel Directory\r\n- Omnichannel Current Chats\r\n- Auto Selection Algo\r\n- Load Balance Algo\r\n- Manual Selection Algo\r\n- Livechat New Conversations\r\n\r\nOther fixed things:\r\n- Warning on fields deprecation\r\n- Warning on \"remove\" deprecation\r\n- Remove findAndModify usage", + "milestone": "5.0.0", "contributors": [ - "KevLehman", - "sampaiodiego" + "KevLehman" ] }, { - "pr": "23196", - "title": "Regression: Properly trickle-down state from UsersPage to UsersTable", - "userLogin": "tassoevan", - "description": "Spotted by @gabriellsh.", + "pr": "26160", + "title": "Regression: Empty URL previews in messages.", + "userLogin": "gabriellsh", "contributors": [ - "tassoevan", - "web-flow" + "gabriellsh" ] }, { - "pr": "23176", - "title": "[IMPROVE] Add missing pt-BR translations, fix typos and unify language", - "userLogin": "gabrieloliverio", + "pr": "26179", + "title": "Regression: OTR with new React Messages", + "userLogin": "yash-rajpal", + "description": "This PR solves 2 OTR issues with new react message components\r\n\r\n- disable the server side message parser for OTR messages\r\n- adds the stopwatch icon for otr messages\r\n\r\n### Before\r\n\"Screenshot\r\n\r\n### After\r\n\"Screenshot", "contributors": [ - "gabrieloliverio", - "sampaiodiego", + "yash-rajpal", "web-flow" ] }, { - "pr": "23032", - "title": "[FIX] User list not being updated after creation/deletion of user", - "userLogin": "ostjen", + "pr": "26216", + "title": "Regression: Replace contact center icon", + "userLogin": "filipemarins", + "milestone": "5.0.0", "contributors": [ - "ostjen", - "web-flow", - "tassoevan" + "filipemarins" ] }, { - "pr": "23187", - "title": "Chore: Upgrade limax", - "userLogin": "tassoevan", - "description": "Upgrades `limax` for faster slugify algorithm.", + "pr": "26093", + "title": "Regression: Fix rendered markdown styling on app info page details section", + "userLogin": "rique223", + "description": "Fixed two styling problems on the AppDetails markdown. The first one was a misuse of flex and the second was the fact that the withRichContent flag was missing on the box that received the markdown.\r\nDemo images:\r\nBefore:\r\n![image](https://user-images.githubusercontent.com/43561537/177857346-54476879-2618-452f-8585-1922dcbfa9c1.png)\r\n\r\nAfter:\r\n![image](https://user-images.githubusercontent.com/43561537/177857376-e96e4ad3-3410-4847-89b7-df074ff87b2f.png)\r\n\r\nClickup task: https://app.clickup.com/t/2rwq0q7", + "milestone": "5.0.0", "contributors": [ - "tassoevan" + "rique223" ] }, { - "pr": "23076", - "title": "[FIX] \"Parent channel or group\" search in discussions' creation throws \"Unexpected end of JSON input\" error", - "userLogin": "matheusbsilva137", - "description": "- Use `encodeURIComponent()` to encode values received by `_generateQueryFromParams()`.", + "pr": "26225", + "title": "[BREAK] Remove webRTC for channels/dm/groups", + "userLogin": "dougfabris", "contributors": [ - "matheusbsilva137", - "tassoevan", - "web-flow" + "dougfabris" ] }, { - "pr": "23108", - "title": "[BREAK] Stop sending audio notifications via stream", + "pr": "26223", + "title": "Regression: Meteor uses `projection` for its observes", "userLogin": "sampaiodiego", - "description": "Remove audio preferences and make them tied to desktop notification preferences.\r\n\r\nTL;DR: new message sounds will play only if you receive a desktop notification. you'll still be able to chose to not play any sound though", "contributors": [ "sampaiodiego" ] }, { - "pr": "23184", - "title": "i18n: Language update from LingoHub 🤖 on 2021-09-13Z", - "userLogin": "lingohub[bot]", + "pr": "26163", + "title": "Chore: Do not log integrations using `name` key", + "userLogin": "sampaiodiego", "contributors": [ - null + "sampaiodiego" ] }, { - "pr": "21779", - "title": "[FIX] Remove margin from quote inside quote", - "userLogin": "tiagoevanp", - "description": "![image](https://user-images.githubusercontent.com/17487063/116253926-4a89e600-a747-11eb-9172-f2ed1245fa1b.png)", + "pr": "26219", + "title": "Chore: Check for env var values and not just if they are set", + "userLogin": "sampaiodiego", "contributors": [ - "tiagoevanp", - "web-flow", "sampaiodiego" ] }, { - "pr": "23160", - "title": "[BREAK] Remove Google Vision features", - "userLogin": "sampaiodiego", - "description": "Google Vision features like \"block adult images\" or label detection were not being maintained and totally broken. So we decided to remove its feature and maybe in the future release the same features as an app.", + "pr": "26171", + "title": "Regression: UIKit buttons auth user validation", + "userLogin": "tapiarafael", + "description": "Fix the validation to match the new feature that allows apps to register auth-required routes.", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego" + "tapiarafael", + "d-gubert", + "web-flow" ] }, { - "pr": "23165", - "title": "Bump @storybook/react from 6.3.6 to 6.3.8", - "userLogin": "dependabot[bot]", + "pr": "26158", + "title": "Regression: Cannot logout when CallProvider is unregistered and mounted", + "userLogin": "KevLehman", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "KevLehman" ] }, { - "pr": "23163", - "title": "Bump jsrsasign from 10.3.0 to 10.4.0", - "userLogin": "dependabot[bot]", + "pr": "26159", + "title": "Regression: Change Audio settings for device settings as modal title", + "userLogin": "KevLehman", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "KevLehman", + "murtaza98", "web-flow" ] }, { - "pr": "23013", - "title": "[BREAK][ENTERPRISE] \"Download CSV\" button doesn't work in the Engagement Dashboard's Active Users section", - "userLogin": "tassoevan", - "description": "- Fix \"Download CSV\" button in the Engagement Dashboard's Active Users section;\r\n- Add column headers to the CSV file downloaded from the Engagement Dashboard's Active Users section;\r\n- Split the data in multiple CSV files.", + "pr": "26173", + "title": "Regression: Inline code and copyonly tag styles", + "userLogin": "gabriellsh", "contributors": [ - "matheusbsilva137", - "dougfabris", - "gabriellsh", - "web-flow", - "tassoevan" + "gabriellsh" ] }, { - "pr": "23014", - "title": "[BREAK][ENTERPRISE] CSV file downloaded in the Engagement Dashboard's New Users section contains undefined data", - "userLogin": "tassoevan", - "description": "- Fix CSV file downloaded in the Engagement Dashboard's New Users section;\r\n - Add column headers to the CSV file downloaded from the Engagement Dashboard's New Users section.", + "pr": "26152", + "title": "Regression: remove italic from reaction translation", + "userLogin": "filipemarins", "contributors": [ - "matheusbsilva137", - "gabriellsh", - "web-flow", - "tassoevan" + "filipemarins" ] }, { - "pr": "23139", - "title": "Bump supertest from 6.1.3 to 6.1.6", - "userLogin": "dependabot[bot]", + "pr": "26197", + "title": "Regression: Reverting @rocket.chat/mp3-encoder version to fix Audio Message", + "userLogin": "hugocostadev", + "description": "An unknow breaking change (still investigating) happened when upgrading the [@rocket.chat/mp3-encoder](https://github.com/RocketChat/fuselage/tree/develop/packages/mp3-encoder) package to version 0.25.0, because of that we revert the version to 0.24.0 the last know working version.", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "hugocostadev" ] - }, + } + ] + }, + "5.0.0-rc.5": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23152", - "title": "Chore: client endpoints typings", - "userLogin": "tassoevan", + "pr": "26170", + "title": "Regression: Burger menu showing arrow instead of burguer", + "userLogin": "gabriellsh", + "milestone": "5.0.0", "contributors": [ + "gabriellsh", "tassoevan", - "web-flow", - "ggazzo" + "web-flow" ] }, { - "pr": "23157", - "title": "Chore: Update pino and pino-pretty", - "userLogin": "sampaiodiego", + "pr": "26203", + "title": "Regression: Last_login translation key", + "userLogin": "dougfabris", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego" + "dougfabris" ] }, { - "pr": "23138", - "title": "Bump @rocket.chat/string-helpers from 0.27.0 to 0.29.0 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26209", + "title": "Regression: Livechat rooms not opening due to route desync", + "userLogin": "aleksandernsilva", + "description": "Due to route information only updating on `Tracker.afterFlush` (https://github.com/RocketChat/Rocket.Chat/pull/25990), we found out that calling the `tabBar.openUserInfo()` method at this point will cause a route change to the previous route instead of the current one, preventing livechat rooms from being opened.\r\n\r\nAs a provisory solution, we're delaying the opening of the contextual bar, which then ensures that the route info is up to date. Although this solution works, we need to find a more reliable way of ensuring consistent route changes with up-to-date information.\r\n\r\n### I'm definitely open for better looking alternatives. Please leave a comment if you have a better solution to share.", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "aleksandernsilva", + "ggazzo", + "web-flow", + "murtaza98" ] }, { - "pr": "22978", - "title": "[FIX] Inaccurate use of 'Mobile notifications' instead of 'Push notifications' in i18n strings", - "userLogin": "matheusbsilva137", - "description": "- Fix inaccurate use of 'Mobile notifications' (which is misleading in German) by 'Push notifications';\r\n - Update `'Notification_Mobile_Default_For'` key to `'Notification_Push_Default_For'` (and text to 'Send Push Notifications For' for English Language);\r\n - Update `'Accounts_Default_User_Preferences_mobileNotifications'` key to `'Accounts_Default_User_Preferences_pushNotifications'`;\r\n - Update `'Mobile_Notifications_Default_Alert'` key to `'Mobile_Push_Notifications_Default_Alert'`;", + "pr": "26232", + "title": "Regression: Admin Avatar Edit endpoint fix", + "userLogin": "hugocostadev", + "milestone": "5.0.0", "contributors": [ - "matheusbsilva137", - "web-flow", - "ostjen" + "hugocostadev" ] }, { - "pr": "23141", - "title": "Bump xml-crypto from 2.1.2 to 2.1.3", - "userLogin": "dependabot[bot]", + "pr": "26234", + "title": "Regression: Don't open mdm feature modal on registration page", + "userLogin": "yash-rajpal", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "yash-rajpal", "web-flow" ] }, { - "pr": "22975", - "title": "[IMPROVE] Change log format to JSON", - "userLogin": "sampaiodiego", + "pr": "26238", + "title": "Regression: Revert replace contact center icon", + "userLogin": "filipemarins", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego", - "web-flow" + "filipemarins" ] }, { - "pr": "23091", - "title": "Regression: Auth banner for EE", - "userLogin": "g-thome", - "description": "Dimisses auth banners assigned to EE admins and prevents new ones from appearing.", - "milestone": "3.18.1", + "pr": "26233", + "title": "Regression: Fix routing for public queue and visitor abandonment fiber usage", + "userLogin": "KevLehman", "contributors": [ - "g-thome", - "casalsgh", - "web-flow" + "KevLehman" ] }, { - "pr": "23023", - "title": "[IMPROVE][APPS] Return task ids when using the scheduler api", - "userLogin": "thassiov", - "description": "In the methods that create tasks (`scheduleRecurring` and `scheduleOnce`) return the `id` of the document created in the database so the user can cancel each task individually.", - "milestone": "4.0.0", + "pr": "26174", + "title": "Regression: Unavailable devices in unsupported browsers", + "userLogin": "MartinSchoeler", "contributors": [ - "thassiov", - "d-gubert", - "web-flow" + "MartinSchoeler", + "web-flow", + "KevLehman" ] }, { - "pr": "23104", - "title": "[FIX] Update bugsnag package", - "userLogin": "sampaiodiego", + "pr": "26102", + "title": "Chore: Remove unused migrations", + "userLogin": "debdutdeb", + "description": "After giving it some thought:\r\n\r\n- 234 through 240 are not going to be run anymore. Keeping them does not affect behavior of course, but this (removing) makes it easier to quickly glance at and understand what migrations are actually included in 5.x.y (especially in tag compare view or in general just checking the ref).\r\n\r\n- Also changed the file name of 233 to be more explicit at what it does so to not confuse with actual \"migrations\" without having to open the file. \r\n\r\n- The redirect to the documentation page (go.rocket....) is not yet set up, jfyi.", + "milestone": "5.0.0", "contributors": [ + "debdutdeb", "sampaiodiego" ] - }, + } + ] + }, + "5.0.0-rc.6": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23128", - "title": "Bump pm2 from 5.1.0 to 5.1.1 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26162", + "title": "Regression: Fix marketplace releases tab crash bug", + "userLogin": "rique223", + "description": "Fixed a bug where RC would crash because the marketplace releases tab was trying to display undefined data from manually installed apps. \r\nDemo gif:\r\n![app-releases-tab-crash-error](https://user-images.githubusercontent.com/43561537/177656489-325790d3-49e0-46c8-8ac2-1f74c6a309ad.gif)", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "rique223", + "ggazzo", + "geekgonecrazy", "web-flow" ] }, { - "pr": "23126", - "title": "Bump @types/ejson from 2.1.2 to 2.1.3 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26257", + "title": "Chore: Disabled icon colors on sidebar", + "userLogin": "juliajforesti", "contributors": [ - "dependabot[bot]", + "juliajforesti", "web-flow" ] }, { - "pr": "23109", - "title": "Chore: Remove non-used dependencies", + "pr": "26256", + "title": "Regression: get user from `req` on `ui.interaction` endpoints", "userLogin": "sampaiodiego", + "milestone": "5.0.0", "contributors": [ "sampaiodiego" ] }, { - "pr": "23095", - "title": "Bump @types/ws from 7.4.6 to 7.4.7 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26253", + "title": "Chore: Avoid unneeded permission updates when EE license is applied", + "userLogin": "sampaiodiego", + "milestone": "4.8.2", "contributors": [ - "dependabot[bot]", - "web-flow" + "sampaiodiego" ] }, { - "pr": "23068", - "title": "Bump tar from 6.1.0 to 6.1.11 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26242", + "title": "Regression: Fix scheduler not working", + "userLogin": "tapiarafael", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "tapiarafael" ] }, { - "pr": "23122", - "title": "Bump @types/imap from 0.8.34 to 0.8.35", - "userLogin": "dependabot[bot]", + "pr": "26073", + "title": "Regression: Link not scrolling to message", + "userLogin": "filipemarins", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "filipemarins", + "gabriellsh" ] - }, + } + ] + }, + "5.0.0-rc.7": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23120", - "title": "Bump csv-parse from 4.16.0 to 4.16.3", - "userLogin": "dependabot[bot]", + "pr": "26267", + "title": "Regression: Omni-chats not getting routed automatically to bots", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "murtaza98", "web-flow" ] }, { - "pr": "23123", - "title": "i18n: Language update from LingoHub 🤖 on 2021-09-06Z", - "userLogin": "lingohub[bot]", - "contributors": [ - null - ] - }, - { - "pr": "22177", - "title": "Bump juice from 5.2.0 to 8.0.0", - "userLogin": "dependabot[bot]", + "pr": "26172", + "title": "Regression: Cannot open Menu in searched message.", + "userLogin": "gabriellsh", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "gabriellsh", + "tassoevan", "web-flow" ] }, { - "pr": "23089", - "title": "[FIX] Change way emails are validated on livechat registerGuest method", - "userLogin": "KevLehman", - "milestone": "3.18.1", + "pr": "26235", + "title": "Regression: REST API calls at Engagement Dashboard", + "userLogin": "tassoevan", + "description": "Parameters for GET requests are *not* serialized as for other methods, therefore sending `Date` objects is not viable due to the way `Date.prototype.toString` works. This PR uses `Date.prototype.toISOString` explicitly to serialize dates.", + "milestone": "5.0.0", "contributors": [ - "KevLehman", - "web-flow" + "tassoevan" ] }, { - "pr": "23054", - "title": "[IMPROVE] Use PaginatedSelectFiltered in department edition", - "userLogin": "murtaza98", + "pr": "26237", + "title": "Regression: Call toggle missing network disconnection state", + "userLogin": "aleksandernsilva", + "description": "This PR brings back the network disconnection state to the voip call toggle button\r\n\r\n![image (4)](https://user-images.githubusercontent.com/6494543/178564719-f436505e-3ae3-4d69-ba5a-27ce8e8c5fba.png)", + "milestone": "5.0.0", "contributors": [ - "murtaza98", - "tiagoevanp", - "web-flow", - "KevLehman" + "aleksandernsilva" ] }, { - "pr": "23053", - "title": "[FIX] Add check before placing chat on-hold to confirm that contact sent last message", - "userLogin": "murtaza98", + "pr": "26258", + "title": "Chore: Update Apps-Engine version", + "userLogin": "d-gubert", + "description": "Bumping Apps-Engine version", + "milestone": "5.0.0", "contributors": [ - "murtaza98", - "web-flow", - "KevLehman" + "d-gubert" ] }, { - "pr": "22036", - "title": "Bump stylelint-order from 2.2.1 to 4.1.0", - "userLogin": "dependabot[bot]", + "pr": "26270", + "title": "Chore: Avoid set useless set UTC Offset", + "userLogin": "ggazzo", "contributors": [ - "dependabot[bot]", - "web-flow", - "sampaiodiego" + "ggazzo" ] }, { - "pr": "22527", - "title": "Bump iconv-lite from 0.4.24 to 0.6.3", - "userLogin": "dependabot[bot]", + "pr": "26139", + "title": "Regression: Sidebar icons spacing", + "userLogin": "guijun13", + "description": "- Fixed the sidebar icons ('display' and 'create new') spacing issue\r\n\r\nbefore:\r\n![image](https://user-images.githubusercontent.com/5263975/178897210-50615ea9-28d5-4b35-a93a-c5facea365e5.png)\r\n\r\n\r\n\r\nafter:\r\n\r\n![image](https://user-images.githubusercontent.com/5263975/178896945-1bf71112-8a01-4db6-9f9b-20ea778496f7.png)", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "guijun13", + "ggazzo" ] }, { - "pr": "22528", - "title": "Bump image-size from 0.6.3 to 1.0.0", - "userLogin": "dependabot[bot]", + "pr": "26188", + "title": "Chore: Hide deprecation query log on production", + "userLogin": "ggazzo", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", + "ggazzo", "web-flow" ] - }, + } + ] + }, + "5.0.0-rc.8": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "22532", - "title": "Bump ip-range-check from 0.0.2 to 0.2.0", - "userLogin": "dependabot[bot]", + "pr": "26049", + "title": "Regression: AutoTranslate on new message template", + "userLogin": "filipemarins", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "filipemarins", + "tassoevan" ] }, { - "pr": "23100", - "title": "[IMPROVE] Change HTTP and Method logs to level INFO", - "userLogin": "sampaiodiego", - "milestone": "3.18.1", + "pr": "26224", + "title": "Chore: Plan tag", + "userLogin": "gabriellsh", + "description": "Now we only have one plan tag for all plans \\/\r\n![image](https://user-images.githubusercontent.com/40830821/178366367-12388c4c-6822-4e41-be8d-ca306718be98.png)", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego" + "gabriellsh" ] }, { - "pr": "16050", - "title": "[BREAK] Remove patch info from endpoint /api/info for non-logged in users", + "pr": "26248", + "title": "Regression: Remove alpha tag and fix initialization process", "userLogin": "MarcosSpessatto", - "milestone": "4.0.0", + "milestone": "5.0.0", "contributors": [ - "MarcosSpessatto", - "tassoevan", - "web-flow", - "sampaiodiego" + "MarcosSpessatto" ] }, { - "pr": "23088", - "title": "Bump object-path from 0.11.5 to 0.11.6", - "userLogin": "dependabot[bot]", + "pr": "26255", + "title": "Regression: Device management table missing device icon and ip text ellipsis", + "userLogin": "csuadev", "contributors": [ - "dependabot[bot]", - "web-flow" + "csuadev", + "yash-rajpal" ] }, { - "pr": "22922", - "title": "Chore: Environmental variable for marketplace url", - "userLogin": "graywolf336", + "pr": "26252", + "title": "Regression: UserInfo/RoomInfo Menu", + "userLogin": "dougfabris", + "description": "**note**: next fuselage's version needed\r\n\r\n#### before\r\n![Screen Shot 2022-07-13 at 12 24 38](https://user-images.githubusercontent.com/27704687/178771262-d482b300-de80-4961-be2e-8c034480d237.png)\r\n\r\n#### after\r\n![Screen Shot 2022-07-13 at 12 25 39](https://user-images.githubusercontent.com/27704687/178771460-db10883b-aa6d-4254-82d4-8cadd6991ae8.png)", + "milestone": "5.0.0", "contributors": [ - "graywolf336" + "dougfabris" ] }, { - "pr": "22600", - "title": "Bump @types/cookie from 0.4.0 to 0.4.1 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26249", + "title": "Regression: Federated users not showing as federated in Room Members", + "userLogin": "gabriellsh", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "gabriellsh", + "MarcosSpessatto" ] }, { - "pr": "22598", - "title": "Bump @types/express from 4.17.12 to 4.17.13 in /ee/server/services", - "userLogin": "dependabot[bot]", + "pr": "26239", + "title": "Regression: Removed CE watermark from VoipFooter", + "userLogin": "aleksandernsilva", + "description": "The objective of this change is to remove the CE watermark **only** during an active call. The CE watermark will be displayed normally in all other scenarios. Bellow you can see a demonstration of the expected behavior:\r\n\r\n![ce-watermark-removed-voip](https://user-images.githubusercontent.com/6494543/178615342-8049a2a8-d331-46a9-a8f1-8461ae341b50.gif)", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "aleksandernsilva" ] }, { - "pr": "22673", - "title": "Bump actions/stale from 3.0.19 to 4", - "userLogin": "dependabot[bot]", + "pr": "26154", + "title": "Regression: Parse outbound phone number removing * putting + char", + "userLogin": "tiagoevanp", + "milestone": "5.0.0", "contributors": [ - "dependabot[bot]", - "web-flow" + "tiagoevanp" ] }, { - "pr": "23061", - "title": "i18n: Language update from LingoHub 🤖 on 2021-08-30Z", - "userLogin": "lingohub[bot]", + "pr": "26273", + "title": "Regression: Search on Member List", + "userLogin": "tassoevan", + "milestone": "5.0.0", "contributors": [ - null + "tassoevan", + "sampaiodiego" ] - }, + } + ] + }, + "5.0.0-rc.9": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ { - "pr": "23086", - "title": "Merge master into develop & Set version to 4.0.0", - "userLogin": "sampaiodiego", + "pr": "26050", + "title": "[FIX] Users without the `view-other-user-info` permission can't use the `users.list` endpoint", + "userLogin": "LucianoPierdona", + "description": "This PR fix the query when a normal users access `users.list`", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego", + "LucianoPierdona", + "tassoevan", "web-flow" ] }, { - "pr": "23079", - "title": "Chore: Remove wrong usages of `Meteor.wrapAsync`", - "userLogin": "sampaiodiego", + "pr": "26274", + "title": "Chore: Upgrade Fuselage packages to `next` dist-tag", + "userLogin": "tassoevan", + "description": "Upgrade Fuselage packages to the latest development versions.", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego" + "tassoevan" ] } ] }, - "4.8.0-rc.4": { - "node_version": "14.18.3", - "npm_version": "6.14.15", + "5.0.0-rc.10": { + "node_version": "14.19.3", + "npm_version": "6.14.17", "mongo_versions": [ - "3.6", - "4.0", "4.2", "4.4", "5.0" ], "pull_requests": [ { - "pr": "25689", - "title": "Regression: App event listeners broke Slackbridge integration and importers", - "userLogin": "d-gubert", - "description": "Some event listeners triggered by Apps were calling `Meteor.user()` in functions that could run outside of Meteor environment", - "milestone": "4.8.0", + "pr": "26311", + "title": "Regression: Add v1 to licenses.add endpoint", + "userLogin": "KevLehman", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "26183", + "title": "Chore: VideoConference UX/UI Refactor 1st Interaction ", + "userLogin": "pierre-lehnen-rc", + "milestone": "5.0.0", + "contributors": [ + "pierre-lehnen-rc", + "web-flow", + "debdutdeb", + "dougfabris", + "ggazzo" + ] + }, + { + "pr": "26295", + "title": "Regression: Clear user selection filter after selecting desired user.", + "userLogin": "gabriellsh", + "milestone": "5.0.0", + "contributors": [ + "gabriellsh" + ] + }, + { + "pr": "26304", + "title": "Regression: Fix permissions page pagination", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "26305", + "title": "Regression: Fix breaking omnichannel tests", + "userLogin": "murtaza98", "contributors": [ - "d-gubert" + "murtaza98", + "web-flow" ] }, { - "pr": "25686", - "title": "[FIX] Fix max-width message block", - "userLogin": "ggazzo", - "milestone": "4.8.0", + "pr": "26251", + "title": "Regression: Remove 4.0 version banner", + "userLogin": "hugocostadev", + "description": "Created a migration to disable and dismiss for all users the old 4.0 version banner.\r\nIt happened when a new admin user has been added.", + "milestone": "5.0.0", "contributors": [ - "ggazzo" + "hugocostadev", + "sampaiodiego", + "web-flow" ] }, { - "pr": "25673", - "title": "[FIX] Change form body parameter charset to UTF-8 to fix issue #25456", - "userLogin": "divinespear", - "description": "since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0.\r\n\r\n![Screenshot from 2022-05-28 16-26-06](https://user-images.githubusercontent.com/126630/170815447-1f3bd579-243a-42d3-86f6-814aeaa30ce9.png)", - "milestone": "4.8.0", + "pr": "26092", + "title": "Chore: Fix Omnichannel E2E tests not running", + "userLogin": "murtaza98", "contributors": [ - "divinespear" + "murtaza98", + "web-flow" ] }, { - "pr": "25687", - "title": "Regression: Fix sort field files.list", + "pr": "26294", + "title": "Chore: Remove TimeSync usage", "userLogin": "ggazzo", - "milestone": "4.8.0", "contributors": [ - "ggazzo", - "albuquerquefabio", + "ggazzo" + ] + }, + { + "pr": "26155", + "title": "Regression: Contact manager edit/view not working", + "userLogin": "KevLehman", + "description": "Basically, the Contact Center was working, but not the right way. This PR fixes:\r\n- Ability to select Contact Managers from dropdown\r\n- Ability to validate Contact Edits without requesting data a ton of times\r\n- Ability to remove Contact manager from a contact\r\n- Ability to see Contacts and Contact Managers on Contact View\r\n- Fix endpoints validation\r\n- Add validators (ajv) to endpoint, thou not being used yet (since we hit a special endpoint)", + "milestone": "5.0.0", + "contributors": [ + "KevLehman", + "murtaza98", "web-flow" ] }, { - "pr": "25684", - "title": "[IMPROVE] add warnings for federation setup", - "userLogin": "carlosrodrigues94", + "pr": "26245", + "title": "Chore: Tests refactor pageobjects", + "userLogin": "souzaramon", "contributors": [ - "carlosrodrigues94" + "souzaramon", + "weslley543", + "web-flow" ] }, { - "pr": "25683", - "title": "[FIX] Prevent federation crash on invite users as a non-owner user", - "userLogin": "MarcosSpessatto", + "pr": "26298", + "title": "Regression: Adjusted priority to run canned responses replace before new parser", + "userLogin": "aleksandernsilva", + "description": "Canned responses placeholders were not being replaced properly after we changed to the new md parser. \r\nThis fix changes the priority so that the canned responses replace logic runs before the parser, thus bringing back this functionality.\r\n\r\nBefore:\r\n\"Screen\r\n\r\nAfter:\r\n\"Screen", + "milestone": "5.0.0", "contributors": [ - "MarcosSpessatto" + "aleksandernsilva" ] }, { - "pr": "25653", - "title": "Regression: Broken components on Federation and Engagement dashboards", - "userLogin": "tassoevan", - "description": "For reasons I've no clue, any client import path matching `**/data/**` will not be included in the final bundle, failing silently on transpiling/bundling.", - "milestone": "4.8.0", + "pr": "26278", + "title": "Regression: Fix app icons breaking UI", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "tassoevan", - "gabriellsh" + "murtaza98", + "web-flow", + "tassoevan" ] } ] }, - "4.8.0-rc.5": { + "4.7.5": { "node_version": "14.18.3", "npm_version": "6.14.15", "mongo_versions": [ @@ -85883,19 +90660,9 @@ "4.4", "5.0" ], - "pull_requests": [ - { - "pr": "25700", - "title": "Chore: Update Apps-Engine and Fuselage", - "userLogin": "d-gubert", - "milestone": "4.8.0", - "contributors": [ - "d-gubert" - ] - } - ] + "pull_requests": [] }, - "4.8.0": { + "4.8.2": { "node_version": "14.18.3", "npm_version": "6.14.15", "mongo_versions": [ @@ -85907,142 +90674,222 @@ ], "pull_requests": [ { - "pr": "25580", - "title": "Release 4.7.2", - "userLogin": "d-gubert", + "pr": "26326", + "title": "Release 4.8.2", + "userLogin": "sampaiodiego", "contributors": [ - "tiagoevanp", - "d-gubert", - "MartinSchoeler", - "ggazzo", - "cauefcr", - "geekgonecrazy" + "sampaiodiego", + "matthias4217" ] }, { - "pr": "25544", - "title": "[FIX] Initial User not added to default channel", - "userLogin": "geekgonecrazy", - "description": "If injecting initial user. The user wasn’t added to the default General channel", - "milestone": "4.7.2", + "pr": "26253", + "title": "Chore: Avoid unneeded permission updates when EE license is applied", + "userLogin": "sampaiodiego", + "milestone": "4.8.2", "contributors": [ - "geekgonecrazy", - "web-flow" + "sampaiodiego" ] }, { - "pr": "25520", - "title": "[FIX] User abandonment setting was not working doe to failing event hook", - "userLogin": "cauefcr", - "description": "A setting watcher and the query for grabbing abandoned chats were broken, now they're not.", - "milestone": "4.7.2", + "pr": "25724", + "title": "[FIX] Not showing edit message button when blocking edit after N minutes", + "userLogin": "matthias4217", + "description": "Previously, in Rocketchat 4.7.0 and later, as mentioned in https://github.com/RocketChat/Rocket.Chat/issues/25478, the edit button was not displayed on the interface in the minute after having sent a message. This is now fixed : messages can be edited right after sending them.", + "milestone": "4.8.2", "contributors": [ - "cauefcr", - "tiagoevanp" + "matthias4217", + "sampaiodiego" ] }, { - "pr": "25495", - "title": "[FIX] Dynamic load matrix is enabled and handle failure ", + "pr": "26058", + "title": "[FIX] Error \"numRequestsAllowed\" property in rateLimiter for REST API endpoint when upgrading", + "userLogin": "sampaiodiego", + "milestone": "4.8.2", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25891", + "title": "[FIX] Settings not being overwritten to their default values", + "userLogin": "sampaiodiego", + "milestone": "4.8.2", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "25814", + "title": "Release 4.8.1", "userLogin": "ggazzo", - "milestone": "4.7.2", "contributors": [ + "KevLehman", "ggazzo", - "geekgonecrazy" + "tiagoevanp", + "Sh0uld", + "dudanogueira" + ] + } + ] + }, + "5.0.0-rc.11": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "26310", + "title": "Regression: fix `directory` endpoint not listing teams", + "userLogin": "carlosrodrigues94", + "milestone": "5.0.0", + "contributors": [ + "carlosrodrigues94", + "matheusbsilva137", + "web-flow", + "ggazzo" ] }, { - "pr": "25409", - "title": "[FIX] One of the triggers was not working correctly", - "userLogin": "MartinSchoeler", - "milestone": "4.7.2", + "pr": "26309", + "title": "Regression: Options overlapping input in Users Autocomplete", + "userLogin": "gabriellsh", + "milestone": "5.0.0", "contributors": [ - "MartinSchoeler", - "tiagoevanp" + "gabriellsh" ] }, { - "pr": "25407", - "title": "[FIX] UI/UX issues on Live Chat widget", - "userLogin": "MartinSchoeler", - "milestone": "4.7.2", + "pr": "26283", + "title": "Regression: Matrix Federation regressions", + "userLogin": "MarcosSpessatto", + "milestone": "5.0.0", "contributors": [ - "MartinSchoeler", - "dougfabris" + "MarcosSpessatto", + "web-flow", + "carlosrodrigues94", + "gabriellsh", + "ggazzo" ] }, { - "pr": "25312", - "title": "Chore: Add Livechat repo into Monorepo packages", + "pr": "26319", + "title": "Regression: Use fname instead real unique name for Voip", "userLogin": "tiagoevanp", - "milestone": "4.7.2", + "description": "Affect:\r\n- Voip room header\r\n- Contacts table\r\n- Contact info", + "milestone": "5.0.0", "contributors": [ - "tiagoevanp", - "ggazzo", - "web-flow", - "MartinSchoeler" + "tiagoevanp" ] }, { - "pr": "25510", - "title": "Release 4.7.1", - "userLogin": "d-gubert", + "pr": "26269", + "title": "Regression: Channel `type` icon on Engagement Dashboard", + "userLogin": "LucianoPierdona", + "description": "This PR fixes a bug on which the channel type is inverted.", + "milestone": "5.0.0", "contributors": [ - "felipe-menelau", - "d-gubert", - "pierre-lehnen-rc" + "LucianoPierdona", + "tassoevan", + "web-flow", + "ggazzo" ] }, { - "pr": "25471", - "title": "[FIX] Spotlight results showing usernames instead of real names", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.7.1", + "pr": "26315", + "title": "Regression: Fix job Id not returned by agenda", + "userLogin": "murtaza98", + "milestone": "5.0.0", "contributors": [ - "pierre-lehnen-rc" + "murtaza98", + "web-flow" ] }, { - "pr": "25434", - "title": "[FIX] LDAP sync removing users from channels when multiple groups are mapped to it", - "userLogin": "pierre-lehnen-rc", - "milestone": "4.7.1", + "pr": "26241", + "title": "Regression: Special characters on phone number", + "userLogin": "tiagoevanp", + "description": "PR Includes:\r\n- Keep focus on phone input of dial pad\r\n- Handle submit with \"Enter\" key\r\n- Remove mask and mandatory \"+\" char\r\n- Long press for \"0\"/\"+\" button", + "milestone": "5.0.0", "contributors": [ - "pierre-lehnen-rc" + "tiagoevanp", + "ggazzo", + "web-flow", + "filipemarins", + "KevLehman", + "aleksandernsilva" + ] + } + ] + }, + "5.0.0-rc.12": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [ + { + "pr": "26327", + "title": "Regression: Livechat not rendering UiKit messages with action buttons", + "userLogin": "murtaza98", + "milestone": "5.0.0", + "contributors": [ + "murtaza98", + "ggazzo", + "web-flow", + "aleksandernsilva" ] }, { - "pr": "25441", - "title": "[NEW] Use setting to determine if initial general channel is needed", - "userLogin": "felipe-menelau", - "description": "- Adds flag responsible for overwriting #general channel creation", - "milestone": "4.7.1", + "pr": "26325", + "title": "Chore: bump fuselage packages", + "userLogin": "dougfabris", + "milestone": "5.0.0", "contributors": [ - "felipe-menelau", - "sampaiodiego", - "web-flow" + "dougfabris", + "ggazzo" ] }, { - "pr": "25390", - "title": "Release 4.7.0", - "userLogin": "d-gubert", + "pr": "26322", + "title": "Chore: Update useSidebarPalette selectors", + "userLogin": "juliajforesti", + "milestone": "5.0.0", "contributors": [ - "sampaiodiego", + "juliajforesti", "web-flow", - "lingohub[bot]", - "dependabot[bot]", "ggazzo", - "dougfabris", - "gabriellsh", - "tmontini", - "debdutdeb", - "Himanshu664", - "yash-rajpal", - "MartinSchoeler" + "murtaza98" + ] + }, + { + "pr": "26328", + "title": "Regression: Fix get myself user data", + "userLogin": "sampaiodiego", + "milestone": "5.0.0", + "contributors": [ + "sampaiodiego" ] } ] + }, + "5.0.0": { + "node_version": "14.19.3", + "npm_version": "6.14.17", + "mongo_versions": [ + "4.2", + "4.4", + "5.0" + ], + "pull_requests": [] } } -} +} \ No newline at end of file diff --git a/.github/no-js-action-config.json b/.github/no-js-action-config.json index 5dd97fccaf67..45e82790b82e 100644 --- a/.github/no-js-action-config.json +++ b/.github/no-js-action-config.json @@ -1,5 +1,13 @@ { "added": { - "ignore": ["packages/accounts-linkedin/**/*", "packages/linkedin-oauth/**/*", "tests/cypress/integration/08-resolutions.spec.js", "**/.eslintrc.js", "packages/eslint-config/**", "**/babel.config.js"] + "ignore": [ + "packages/node-poplib/**/*", + "packages/accounts-linkedin/**/*", + "packages/linkedin-oauth/**/*", + "tests/cypress/integration/08-resolutions.spec.js", + "**/.eslintrc.js", + "packages/eslint-config/**", + "**/babel.config.js" + ] } } diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml index 2a453ccdb4d2..b02b09a62a91 100644 --- a/.github/workflows/auto-label.yml +++ b/.github/workflows/auto-label.yml @@ -8,4 +8,4 @@ jobs: steps: - uses: ggazzo/gh-action-auto-label@beta-5 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RC_AUTOLABEL_TOKEN }} diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index ecfb34c5b1e8..335cf9a25ee5 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -15,10 +15,49 @@ concurrency: env: CI: true - MONGO_URL: mongodb://localhost:27017 + MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true + MONGO_OPLOG_URL: mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true TOOL_NODE_FLAGS: --max_old_space_size=4096 + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} jobs: + release-versions: + runs-on: ubuntu-latest + outputs: + release: ${{ steps.by-tag.outputs.release }} + latest-release: ${{ steps.latest.outputs.latest-release }} + docker-tag: ${{ steps.docker.outputs.docker-tag }} + gh-docker-tag: ${{ steps.docker.outputs.gh-docker-tag }} + steps: + - id: by-tag + run: | + if echo "$GITHUB_REF_NAME" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' ; then + RELEASE="latest" + elif echo "$GITHUB_REF_NAME" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' ; then + RELEASE="release-candidate" + fi + echo "RELEASE: ${RELEASE}" + echo "::set-output name=release::${RELEASE}" + + - id: latest + run: | + LATEST_RELEASE="$( + git -c 'versionsort.suffix=-' ls-remote -t --exit-code --refs --sort=-v:refname "https://github.com/$GITHUB_REPOSITORY" '*' | + sed -En '1!q;s/^[[:xdigit:]]+[[:space:]]+refs\/tags\/(.+)/\1/gp' + )" + echo "LATEST_RELEASE: ${LATEST_RELEASE}" + echo "::set-output name=latest-release::${LATEST_RELEASE}" + + - id: docker + run: | + if [[ '${{ github.event_name }}' == 'pull_request' ]]; then + DOCKER_TAG="pr-${{ github.event.number }}" + else + DOCKER_TAG="gh-${{ github.run_id }}" + fi + echo "DOCKER_TAG: ${DOCKER_TAG}" + echo "::set-output name=gh-docker-tag::${DOCKER_TAG}" + build: runs-on: ubuntu-20.04 @@ -33,55 +72,41 @@ jobs: echo "github.event_name: ${{ github.event_name }}" cat $GITHUB_EVENT_PATH - - name: Use Node.js 14.18.3 - uses: actions/setup-node@v3 + - name: Set Swap Space + uses: pierotofy/set-swap-space@master with: - node-version: '14.18.3' + swap-size-gb: 4 - uses: actions/checkout@v3 + - name: Use Node.js 14.19.3 + uses: actions/setup-node@v3 + with: + node-version: '14.19.3' + cache: 'yarn' + - name: Free disk space run: | - sudo swapoff -a - sudo rm -f /swapfile sudo apt clean docker rmi $(docker image ls -aq) df -h - # TODO is this required? - # - name: check package-lock - # run: | - # npx package-lock-check - - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 - with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- - - # TODO change to use turbo cache - name: Cache meteor local uses: actions/cache@v2 with: path: ./apps/meteor/.meteor/local - key: ${{ runner.OS }}-meteor_cache-${{ hashFiles('apps/meteor/.meteor/versions') }} + key: meteor-local-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/versions') }} restore-keys: | - ${{ runner.os }}-meteor_cache- - ${{ runner.os }}- + meteor-local-cache-${{ runner.os }}- + - name: Cache meteor uses: actions/cache@v2 with: path: ~/.meteor - key: ${{ runner.OS }}-meteor-${{ hashFiles('apps/meteor/.meteor/release') }} + key: meteor-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/release') }} restore-keys: | - ${{ runner.os }}-meteor- - ${{ runner.os }}- + meteor-cache-${{ runner.os }}- + - name: Install Meteor run: | # Restore bin from cache @@ -114,13 +139,20 @@ jobs: - name: yarn install run: yarn - - run: yarn lint + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v1 + with: + repo-token: ${{ secrets.RC_TURBO_GH_TOKEN }} + server-token: ${{ secrets.TURBO_SERVER_TOKEN }} + + - name: Lint + run: yarn lint --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc' - - run: yarn turbo run translation-check + - name: Translation check + run: yarn turbo run translation-check --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc' - name: TS typecheck - run: | - yarn turbo run typecheck + run: yarn turbo run typecheck --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc' - name: Reset Meteor if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop' @@ -132,124 +164,211 @@ jobs: if: startsWith(github.ref, 'refs/pull/') == true env: METEOR_PROFILE: 1000 - run: | - yarn build:ci -- --debug --directory /tmp/build-test + run: yarn build:ci --api="http://127.0.0.1:9080" -- --debug --directory dist - name: Build Rocket.Chat if: startsWith(github.ref, 'refs/pull/') != true - run: | - yarn build:ci -- --directory /tmp/build-test + run: yarn build:ci --api="http://127.0.0.1:9080" -- --directory dist - name: Prepare build run: | - mkdir /tmp/build/ - cd /tmp/build-test - tar czf /tmp/build/Rocket.Chat.tar.gz bundle - cd /tmp/build-test/bundle/programs/server - npm install --production - cd /tmp - tar czf Rocket.Chat.test.tar.gz ./build-test - - - name: Store build for tests - uses: actions/upload-artifact@v2 - with: - name: build-test - path: /tmp/Rocket.Chat.test.tar.gz + cd apps/meteor/dist + tar czf /tmp/Rocket.Chat.tar.gz bundle - name: Store build uses: actions/upload-artifact@v2 + with: + name: build + path: /tmp/Rocket.Chat.tar.gz + + build-docker-preview: + runs-on: ubuntu-20.04 + needs: [build, release-versions] + if: github.event_name == 'release' || github.ref == 'refs/heads/develop' + steps: + - uses: actions/checkout@v3 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'yarn' + + - name: Restore build + uses: actions/download-artifact@v2 with: name: build path: /tmp/build + - name: Unpack build + run: | + cd /tmp/build + tar xzf Rocket.Chat.tar.gz + rm Rocket.Chat.tar.gz + + - name: Build Docker image + id: build-docker-image-preview + uses: ./.github/actions/build-docker-image + with: + root-dir: /tmp/build + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + release: preview + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} + test: runs-on: ubuntu-20.04 - needs: build + needs: [build, release-versions] strategy: matrix: - node-version: ['14.18.3'] - mongodb-version: ['3.6', '4.0', '4.2', '4.4', '5.0'] + node-version: ['14.19.3'] + mongodb-version: ['4.2', '4.4', '5.0'] steps: - name: Launch MongoDB - uses: wbari/start-mongoDB@v0.2 - with: - mongoDBVersion: ${{ matrix.mongodb-version }} --replSet=rs0 - - - name: Restore build for tests - uses: actions/download-artifact@v2 + uses: supercharge/mongodb-github-action@1.7.0 with: - name: build-test - path: /tmp + mongodb-version: ${{ matrix.mongodb-version }} + mongodb-replica-set: rs0 - - name: Decompress build - run: | - cd /tmp - tar xzf Rocket.Chat.test.tar.gz - cd - + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: 'yarn' - name: Setup Chrome - run: | - npm i chromedriver + run: npm i chromedriver + + - name: yarn install + run: yarn + + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v1 + with: + repo-token: ${{ secrets.RC_TURBO_GH_TOKEN }} + server-token: ${{ secrets.TURBO_SERVER_TOKEN }} + + - name: Unit Test + run: yarn testunit --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc' + + - name: Restore build + uses: actions/download-artifact@v2 + with: + name: build + path: /tmp/build - - name: Configure Replica Set + - name: Unpack build run: | - docker exec mongo mongo --eval 'rs.initiate({_id:"rs0", members: [{"_id":1, "host":"localhost:27017"}]})' - docker exec mongo mongo --eval 'rs.status()' + cd /tmp/build + tar xzf Rocket.Chat.tar.gz + rm Rocket.Chat.tar.gz - - uses: actions/checkout@v3 + - name: Build Docker image + id: build-docker-image + if: matrix.mongodb-version != '5.0' + uses: ./.github/actions/build-docker-image + with: + root-dir: /tmp/build + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + release: official + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 + - name: Build Alpine Docker image + id: build-docker-image-alpine + if: matrix.mongodb-version == '5.0' + uses: ./.github/actions/build-docker-image with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- + root-dir: /tmp/build + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + release: alpine + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - name: Yarn install - # if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true' - run: yarn + # TODO move startup/restart to its own github action + - name: Start up Rocket.Chat + run: | + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - - name: Unit Test - run: yarn testunit + # test alpine image on mongo 5.0 (no special reason to be mongo 5.0 but we need to test alpine at least once) + if [[ '${{ matrix.mongodb-version }}' = '5.0' ]]; then + IMAGE_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.alpine" + else + IMAGE_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.official" + fi; - - name: Install Playwright + IMAGE_NAME="ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${IMAGE_TAG}" + + docker run --name rocketchat -d \ + --link mongodb \ + -p 3000:3000 \ + -e TEST_MODE=true \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + ${IMAGE_NAME} + + until echo "$(docker logs rocketchat)" | grep -q "SERVER RUNNING"; do + echo "Waiting Rocket.Chat to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done + + - name: E2E Test API run: | + docker logs rocketchat --tail=50 + cd ./apps/meteor - npx playwright install --with-deps + for i in $(seq 1 5); do + docker stop rocketchat + docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()' - - name: E2E Test API - env: - TEST_MODE: 'true' - MONGO_URL: mongodb://localhost:27017/rocketchat - MONGO_OPLOG_URL: mongodb://localhost:27017/local + NOW=$(date "+%Y-%m-%dT%H:%M:%SZ") + echo $NOW + + docker start rocketchat + + until echo "$(docker logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do + echo "Waiting Rocket.Chat to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done + + npm run testapi && s=0 && break || s=$? && docker logs rocketchat --tail=100; + done; + exit $s + + - name: Install Playwright run: | cd ./apps/meteor - echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc - Xvfb -screen 0 1024x768x24 :99 & - for i in $(seq 1 5); do (docker exec mongo mongo rocketchat --eval 'db.dropDatabase()') && npm run testci -- --test=testapi && s=0 && break || s=$? && sleep 1; done; (exit $s) + npx playwright install --with-deps - name: E2E Test UI - env: - TEST_MODE: 'true' - MONGO_URL: mongodb://localhost:27017/rocketchat - MONGO_OPLOG_URL: mongodb://localhost:27017/local run: | - cd ./apps/meteor echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc Xvfb -screen 0 1024x768x24 :99 & - docker exec mongo mongo rocketchat --eval 'db.dropDatabase()' && npm run testci -- --test=test:playwright + + docker logs rocketchat --tail=50 + + docker stop rocketchat + docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()' + + NOW=$(date "+%Y-%m-%dT%H:%M:%SZ") + echo $NOW + + docker start rocketchat + + until echo "$(docker logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do + echo "Waiting Rocket.Chat to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done + + cd ./apps/meteor + npm run test:e2e - name: Store playwright test trace uses: actions/upload-artifact@v2 @@ -260,327 +379,316 @@ jobs: test-ee: runs-on: ubuntu-20.04 - needs: build + needs: [build, release-versions] strategy: matrix: - node-version: ['14.18.3'] - mongodb-version: ['4.4'] + node-version: ['14.19.3'] + mongodb-version-ee: ['4.4'] steps: - name: Launch MongoDB - uses: wbari/start-mongoDB@v0.2 + uses: supercharge/mongodb-github-action@1.7.0 with: - mongoDBVersion: ${{ matrix.mongodb-version }} --replSet=rs0 + mongodb-version: ${{ matrix.mongodb-version-ee }} + mongodb-replica-set: rs0 - name: Launch NATS run: sudo docker run --name nats -d -p 4222:4222 nats:2.4 - - name: Restore build for tests - uses: actions/download-artifact@v2 - with: - name: build-test - path: /tmp - - - name: Decompress build - run: | - cd /tmp - tar xzf Rocket.Chat.test.tar.gz - cd - + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: 'yarn' - name: Setup Chrome - run: | - npm i chromedriver - - - name: Configure Replica Set - run: | - docker exec mongo mongo --eval 'rs.initiate({_id:"rs0", members: [{"_id":1, "host":"localhost:27017"}]})' - docker exec mongo mongo --eval 'rs.status()' - - - uses: actions/checkout@v3 + run: npm i chromedriver - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v1 with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- + repo-token: ${{ secrets.RC_TURBO_GH_TOKEN }} + server-token: ${{ secrets.TURBO_SERVER_TOKEN }} - - name: Yarn install + - name: yarn install run: yarn - - name: Build micro services - run: yarn build - - - name: E2E Test API - env: - TEST_MODE: 'true' - MONGO_URL: mongodb://localhost:27017/rocketchat - MONGO_OPLOG_URL: mongodb://localhost:27017/local - ENTERPRISE_LICENSE: ${{ secrets.ENTERPRISE_LICENSE }} - TRANSPORTER: nats://localhost:4222 - SKIP_PROCESS_EVENT_REGISTRATION: 'true' - run: | - echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc - Xvfb -screen 0 1024x768x24 :99 & - - cd ./apps/meteor/ - - for i in $(seq 1 5); do (docker exec mongo mongo rocketchat --eval 'db.dropDatabase()') && npm run testci -- --enterprise --test=testapi && s=0 && break || s=$? && sleep 1; done; (exit $s) + - name: Unit Test + run: yarn testunit --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc' - - name: Install Playwright - run: | - cd ./apps/meteor/ - npx playwright install --with-deps + - name: Restore build + uses: actions/download-artifact@v2 + with: + name: build + path: /tmp/build - - name: E2E Test UI - env: - TEST_MODE: 'true' - MONGO_URL: mongodb://localhost:27017/rocketchat - MONGO_OPLOG_URL: mongodb://localhost:27017/local - ENTERPRISE_LICENSE: ${{ secrets.ENTERPRISE_LICENSE }} - TRANSPORTER: nats://localhost:4222 - TEST_API_URL: http://localhost:4000 - OVERWRITE_SETTING_Site_Url: http://localhost:4000 - SKIP_PROCESS_EVENT_REGISTRATION: 'true' + - name: Unpack build run: | - echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc - Xvfb -screen 0 1024x768x24 :99 & - - cd ./apps/meteor - - docker exec mongo mongo rocketchat --eval 'db.dropDatabase()' && npm run testci -- --enterprise --test=test:playwright:ee + cd /tmp/build + tar xzf Rocket.Chat.tar.gz + rm Rocket.Chat.tar.gz - - name: Store playwright test trace - uses: actions/upload-artifact@v2 - if: failure() + - name: Build Docker image + id: build-docker-image + uses: ./.github/actions/build-docker-image with: - name: ee-playwright-test-trace - path: ./apps/meteor/tests/e2e/test-failures* - - # notification: - # runs-on: ubuntu-20.04 - # needs: test - - # steps: - # - name: Rocket.Chat Notification - # uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@1.1.1 - # with: - # type: ${{ job.status }} - # job_name: '**Build and Test**' - # url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - # commit: true - # token: ${{ secrets.GITHUB_TOKEN }} - - build-image-pr: - runs-on: ubuntu-20.04 - if: github.event.pull_request.head.repo.full_name == github.repository - - strategy: - matrix: - release: ['official', 'preview'] - - steps: - - uses: actions/checkout@v3 + root-dir: /tmp/build + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + release: official + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + - name: 'Build Docker image: account' + uses: ./.github/actions/build-docker-image-service with: - registry: ghcr.io + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + service: account username: ${{ secrets.CR_USER }} password: ${{ secrets.CR_PAT }} - - name: Free disk space - run: | - sudo swapoff -a - sudo rm -f /swapfile - sudo apt clean - docker rmi $(docker image ls -aq) - df -h - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 + - name: 'Build Docker image: authorization' + uses: ./.github/actions/build-docker-image-service with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + service: authorization + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - name: Cache meteor local - uses: actions/cache@v2 + - name: 'Build Docker image: ddp-streamer' + uses: ./.github/actions/build-docker-image-service with: - path: ./apps/meteor/.meteor/local - key: ${{ runner.OS }}-meteor_cache-${{ hashFiles('.meteor/versions') }} - restore-keys: | - ${{ runner.os }}-meteor_cache- - ${{ runner.os }}- - - name: Cache meteor - uses: actions/cache@v2 + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + service: ddp-streamer + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} + + - name: 'Build Docker image: presence' + uses: ./.github/actions/build-docker-image-service with: - path: ~/.meteor - key: ${{ runner.OS }}-meteor-${{ hashFiles('.meteor/release') }} - restore-keys: | - ${{ runner.os }}-meteor- - ${{ runner.os }}- - - name: Use Node.js 14.18.3 - uses: actions/setup-node@v3 + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + service: presence + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} + + - name: 'Build Docker image: stream-hub' + uses: ./.github/actions/build-docker-image-service with: - node-version: '14.18.3' + docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + service: stream-hub + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - name: Install Meteor + - name: Launch Traefik run: | - # Restore bin from cache - set +e - METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor) - METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET") - set -e - LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor - if [ -e $LAUNCHER ] - then - echo "Cached Meteor bin found, restoring it" - sudo cp "$LAUNCHER" "/usr/local/bin/meteor" - else - echo "No cached Meteor bin found." - fi - - # only install meteor if bin isn't found - command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh + docker run --name traefik -d \ + -p 3000:80 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + traefik:2.7 \ + --providers.docker=true - - name: Versions + # TODO move startup/restart to its own github action + - name: Start up Rocket.Chat run: | - npm --versions - yarn -v - node -v - meteor --version - meteor npm --versions - meteor node -v - git version + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - - name: Yarn install - # if: steps.cache-nodemodules.outputs.cache-hit != 'true' - run: yarn + docker run --name rocketchat -d \ + --link mongodb \ + --link nats \ + -e TEST_MODE=true \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + -e ENTERPRISE_LICENSE="${{ secrets.ENTERPRISE_LICENSE }}" \ + -e SKIP_PROCESS_EVENT_REGISTRATION=true \ + --label 'traefik.http.routers.rocketchat.rule=PathPrefix(`/`)' \ + ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${{ needs.release-versions.outputs.gh-docker-tag }}.official + + # spin up all micro services + docker run --name ddp-streamer -d \ + --link mongodb \ + --link nats \ + -e PORT=4000 \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + --label 'traefik.http.services.ddp-streamer.loadbalancer.server.port=4000' \ + --label 'traefik.http.routers.ddp-streamer.rule=PathPrefix(`/websocket`) || PathPrefix(`/sockjs`)' \ + ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${{ needs.release-versions.outputs.gh-docker-tag }} + + - name: 'Start service: stream-hub' + run: | + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - # To reduce memory need during actual build, build the packages solely first - # - name: Build a Meteor cache - # run: | - # # to do this we can clear the main files and it build the rest - # echo "" > server/main.ts - # echo "" > client/main.ts - # sed -i.backup 's/rocketchat:livechat/#rocketchat:livechat/' .meteor/packages - # meteor build --server-only --debug --directory /tmp/build-temp - # git checkout -- server/main.ts client/main.ts .meteor/packages + docker run --name stream-hub -d \ + --link mongodb \ + --link nats \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + ghcr.io/${LOWERCASE_REPOSITORY}/stream-hub-service:${{ needs.release-versions.outputs.gh-docker-tag }} - - name: Build Rocket.Chat - run: yarn build:ci -- --directory /tmp/build-pr + until echo "$(docker logs stream-hub)" | grep -q "NetworkBroker started successfully"; do + echo "Waiting 'stream-hub' to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done - - name: Build Docker image for PRs + - name: 'Start service: account' run: | - cd /tmp/build-pr + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") + docker run --name account -d \ + --link mongodb \ + --link nats \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + ghcr.io/${LOWERCASE_REPOSITORY}/account-service:${{ needs.release-versions.outputs.gh-docker-tag }} + + until echo "$(docker logs account)" | grep -q "NetworkBroker started successfully"; do + echo "Waiting 'account' to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done + + - name: 'Start service: authorization' + run: | LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - IMAGE_NAME="rocket.chat" - if [[ '${{ matrix.release }}' = 'preview' ]]; then - IMAGE_NAME="${IMAGE_NAME}.preview" - fi; - IMAGE_NAME="ghcr.io/${LOWERCASE_REPOSITORY}/${IMAGE_NAME}:pr-${{ github.event.number }}" + docker run --name authorization -d \ + --link mongodb \ + --link nats \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + ghcr.io/${LOWERCASE_REPOSITORY}/authorization-service:${{ needs.release-versions.outputs.gh-docker-tag }} - echo "Build official Docker image ${IMAGE_NAME}" + until echo "$(docker logs authorization)" | grep -q "NetworkBroker started successfully"; do + echo "Waiting 'authorization' to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done - DOCKER_PATH="${GITHUB_WORKSPACE}/apps/meteor/.docker" - if [[ '${{ matrix.release }}' = 'preview' ]]; then - DOCKER_PATH="${DOCKER_PATH}-mongo" - fi; + - name: 'Start service: presence' + run: | + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - echo "Build ${{ matrix.release }} Docker image" - cp ${DOCKER_PATH}/Dockerfile . - if [ -e ${DOCKER_PATH}/entrypoint.sh ]; then - cp ${DOCKER_PATH}/entrypoint.sh . - fi; + docker run --name presence -d \ + --link mongodb \ + --link nats \ + -e "MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true" \ + -e "MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true" \ + -e TRANSPORTER=nats://nats:4222 \ + -e MOLECULER_LOG_LEVEL=info \ + ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${{ needs.release-versions.outputs.gh-docker-tag }} + + until echo "$(docker logs presence)" | grep -q "NetworkBroker started successfully"; do + echo "Waiting 'presence' to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done - docker build -t $IMAGE_NAME . - docker push $IMAGE_NAME + - name: E2E Test API + run: | + cd ./apps/meteor + for i in $(seq 1 5); do + docker stop rocketchat + docker stop stream-hub + docker stop account + docker stop authorization + docker stop ddp-streamer + docker stop presence + + docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()' + + NOW=$(date "+%Y-%m-%dT%H:%M:%SZ") + echo $NOW + + docker start rocketchat + docker start stream-hub + docker start account + docker start authorization + docker start ddp-streamer + docker start presence + + until echo "$(docker logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do + echo "Waiting Rocket.Chat to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done + + docker logs rocketchat --tail=50 + docker logs stream-hub --tail=50 + docker logs account --tail=50 + docker logs authorization --tail=50 + docker logs ddp-streamer --tail=50 + docker logs presence --tail=50 + + npm run testapi && s=0 && break || s=$? && docker logs rocketchat --tail=100 && docker logs authorization --tail=50; + done; + exit $s - services-image-build-check: - runs-on: ubuntu-20.04 - if: github.event.pull_request.head.repo.full_name == github.repository + - name: Install Playwright + run: | + cd ./apps/meteor + npx playwright install --with-deps - strategy: - matrix: - service: ['ddp-streamer'] + - name: E2E Test UI + run: | + echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc + Xvfb -screen 0 1024x768x24 :99 & - steps: - - uses: actions/checkout@v3 + docker logs rocketchat --tail=50 - - name: Use Node.js 14.18.3 - uses: actions/setup-node@v3 - with: - node-version: '14.18.3' + docker stop rocketchat + docker stop stream-hub + docker stop account + docker stop authorization + docker stop ddp-streamer + docker stop presence - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 - with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- + docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()' - - name: Build Docker images - env: - IMAGE_TAG: check - run: | - yarn - yarn build + NOW=$(date "+%Y-%m-%dT%H:%M:%SZ") + echo $NOW - echo "Building Docker image for service: ${{ matrix.service }}:${IMAGE_TAG}" + docker start rocketchat + docker start stream-hub + docker start account + docker start authorization + docker start ddp-streamer + docker start presence - docker build \ - --build-arg SERVICE=${{ matrix.service }} \ - -t rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} \ - -f ./ee/apps/ddp-streamer/Dockerfile \ - . + until echo "$(docker logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do + echo "Waiting Rocket.Chat to start up" + ((c++)) && ((c==10)) && exit 1 + sleep 10 + done - release-versions: - runs-on: ubuntu-latest - outputs: - release: ${{ steps.by-tag.outputs.release }} - latest-release: ${{ steps.latest.outputs.latest-release }} - steps: - - id: by-tag - run: | - if echo "$GITHUB_REF_NAME" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' ; then - RELEASE="latest" - elif echo "$GITHUB_REF_NAME" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' ; then - RELEASE="release-candidate" - fi - echo "RELEASE: ${RELEASE}" - echo "::set-output name=release::${RELEASE}" + docker logs rocketchat --tail=50 + docker logs stream-hub --tail=50 + docker logs account --tail=50 + docker logs authorization --tail=50 + docker logs ddp-streamer --tail=50 + docker logs presence --tail=50 - - id: latest - run: | - LATEST_RELEASE="$( - git -c 'versionsort.suffix=-' ls-remote -t --exit-code --refs --sort=-v:refname "https://github.com/$GITHUB_REPOSITORY" '*' | - sed -En '1!q;s/^[[:xdigit:]]+[[:space:]]+refs\/tags\/(.+)/\1/gp' - )" - echo "LATEST_RELEASE: ${LATEST_RELEASE}" - echo "::set-output name=latest-release::${LATEST_RELEASE}" + cd ./apps/meteor + IS_EE=true npm run test:e2e + + - name: Store playwright test trace + uses: actions/upload-artifact@v2 + if: failure() + with: + name: playwright-test-trace + path: ./apps/meteor/tests/e2e/test-failures* deploy: runs-on: ubuntu-20.04 @@ -646,7 +754,7 @@ jobs: aws s3 cp $ROCKET_DEPLOY_DIR/ s3://download.rocket.chat/build/ --recursive curl -H "Content-Type: application/json" -H "X-Update-Token: $UPDATE_TOKEN" -d \ - "{\"nodeVersion\": \"14.18.3\", \"compatibleMongoVersions\": [\"3.6\", \"4.0\", \"4.2\", \"4.4\", \"5.0\"], \"commit\": \"$GITHUB_SHA\", \"tag\": \"$RC_VERSION\", \"branch\": \"$GIT_BRANCH\", \"artifactName\": \"$ARTIFACT_NAME\", \"releaseType\": \"$RC_RELEASE\"}" \ + "{\"nodeVersion\": \"14.19.3\", \"compatibleMongoVersions\": [\"4.2\", \"4.4\", \"5.0\"], \"commit\": \"$GITHUB_SHA\", \"tag\": \"$RC_VERSION\", \"branch\": \"$GIT_BRANCH\", \"artifactName\": \"$ARTIFACT_NAME\", \"releaseType\": \"$RC_RELEASE\"}" \ https://releases.rocket.chat/update # Makes build fail if the release isn't there @@ -661,114 +769,88 @@ jobs: -d '{"tag":"'$GIT_TAG'"}' fi - image-build: + docker-image-publish: runs-on: ubuntu-20.04 - needs: [deploy, release-versions] + needs: [deploy, build-docker-preview, release-versions] strategy: matrix: - # this is current a mix of variants and different images + # this is currently a mix of variants and different images release: ['official', 'preview', 'alpine'] env: IMAGE_NAME: 'rocketchat/rocket.chat' steps: - - uses: actions/checkout@v3 - - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - - name: Restore build - uses: actions/download-artifact@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 with: - name: build - path: /tmp/build + registry: ghcr.io + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} - - name: Unpack build and prepare Docker files + - name: Get Docker image name + id: gh-docker run: | - cd /tmp/build - tar xzf Rocket.Chat.tar.gz - rm Rocket.Chat.tar.gz - - DOCKER_PATH="${GITHUB_WORKSPACE}/apps/meteor/.docker" - if [[ '${{ matrix.release }}' = 'preview' ]]; then - DOCKER_PATH="${DOCKER_PATH}-mongo" - fi; + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - DOCKERFILE_PATH="${DOCKER_PATH}/Dockerfile" - if [[ '${{ matrix.release }}' = 'alpine' ]]; then - DOCKERFILE_PATH="${DOCKERFILE_PATH}.${{ matrix.release }}" - fi; + GH_IMAGE_NAME="ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${{ needs.release-versions.outputs.gh-docker-tag }}.${{ matrix.release }}" - echo "Copy Dockerfile for release: ${{ matrix.release }}" - cp $DOCKERFILE_PATH ./Dockerfile - if [ -e ${DOCKER_PATH}/entrypoint.sh ]; then - cp ${DOCKER_PATH}/entrypoint.sh . - fi; + echo "GH_IMAGE_NAME: $GH_IMAGE_NAME" - - name: Build Docker image for tag - if: github.event_name == 'release' - run: | - cd /tmp/build + echo "::set-output name=gh-image-name::${GH_IMAGE_NAME}" - DOCKER_TAG=$GITHUB_REF_NAME + - name: Pull Docker image + run: docker pull ${{ steps.gh-docker.outputs.gh-image-name }} + - name: Publish Docker image + run: | if [[ '${{ matrix.release }}' = 'preview' ]]; then IMAGE_NAME="${IMAGE_NAME}.preview" fi; + # 'develop' or 'tag' + DOCKER_TAG=$GITHUB_REF_NAME + # append the variant name to docker tag if [[ '${{ matrix.release }}' = 'alpine' ]]; then DOCKER_TAG="${DOCKER_TAG}-${{ matrix.release }}" fi; - RELEASE="${{ needs.release-versions.outputs.release }}" - - if [[ '${{ matrix.release }}' = 'alpine' ]]; then - RELEASE="${RELEASE}-${{ matrix.release }}" - fi; - echo "IMAGE_NAME: $IMAGE_NAME" echo "DOCKER_TAG: $DOCKER_TAG" - echo "RELEASE: $RELEASE" - # build and push the specific tag version - docker build -t $IMAGE_NAME:$DOCKER_TAG . + # tag and push the specific tag version + docker tag ${{ steps.gh-docker.outputs.gh-image-name }} $IMAGE_NAME:$DOCKER_TAG docker push $IMAGE_NAME:$DOCKER_TAG - if [[ $RELEASE == 'latest' ]]; then - if [[ '${{ needs.release-versions.outputs.latest-release }}' == $GITHUB_REF_NAME ]]; then - docker tag $IMAGE_NAME:$DOCKER_TAG $IMAGE_NAME:$RELEASE - docker push $IMAGE_NAME:$RELEASE - fi - else - docker tag $IMAGE_NAME:$DOCKER_TAG $IMAGE_NAME:$RELEASE - docker push $IMAGE_NAME:$RELEASE - fi - - - name: Build Docker image for develop - if: github.ref == 'refs/heads/develop' - run: | - cd /tmp/build - - DOCKER_TAG=develop + if [[ $GITHUB_REF == refs/tags/* ]]; then + RELEASE="${{ needs.release-versions.outputs.release }}" - if [[ '${{ matrix.release }}' = 'preview' ]]; then - IMAGE_NAME="${IMAGE_NAME}.preview" - fi; + if [[ '${{ matrix.release }}' = 'alpine' ]]; then + RELEASE="${RELEASE}-${{ matrix.release }}" + fi; - if [[ '${{ matrix.release }}' = 'alpine' ]]; then - DOCKER_TAG="${DOCKER_TAG}-${{ matrix.release }}" - fi; + echo "RELEASE: $RELEASE" - docker build -t $IMAGE_NAME:$DOCKER_TAG . - docker push $IMAGE_NAME:$DOCKER_TAG + if [[ $RELEASE == 'latest' ]]; then + if [[ '${{ needs.release-versions.outputs.latest-release }}' == $GITHUB_REF_NAME ]]; then + docker tag ${{ steps.gh-docker.outputs.gh-image-name }} $IMAGE_NAME:$RELEASE + docker push $IMAGE_NAME:$RELEASE + fi + else + docker tag ${{ steps.gh-docker.outputs.gh-image-name }} $IMAGE_NAME:$RELEASE + docker push $IMAGE_NAME:$RELEASE + fi + fi - services-image-build: + services-docker-image-publish: runs-on: ubuntu-20.04 needs: [deploy, release-versions] @@ -777,57 +859,35 @@ jobs: service: ['account', 'authorization', 'ddp-streamer', 'presence', 'stream-hub'] steps: - - uses: actions/checkout@v3 - - - name: Use Node.js 14.18.3 - uses: actions/setup-node@v3 - with: - node-version: '14.18.3' - - uses: c-hive/gha-yarn-cache@v2 - - name: Cache turbo - id: cache-turbo - uses: actions/cache@v2 - with: - path: | - ./node_modules/.turbo - key: ${{ runner.OS }}-turbo-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turbo- - ${{ runner.os }}- - - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - - name: Build Docker images + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ secrets.CR_USER }} + password: ${{ secrets.CR_PAT }} + + - name: Publish Docker images run: | - # defines image tag - if [[ $GITHUB_REF == refs/tags/* ]]; then - IMAGE_TAG="${GITHUB_REF#refs/tags/}" - else - IMAGE_TAG="${GITHUB_REF#refs/heads/}" - fi + LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]") - # first install repo dependencies - yarn - yarn build + IMAGE_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}" - echo "Building Docker image for service: ${{ matrix.service }}:${IMAGE_TAG}" + GH_IMAGE_NAME="ghcr.io/${LOWERCASE_REPOSITORY}/${{ matrix.service }}-service:${IMAGE_TAG}" - if [[ "${{ matrix.service }}" == "ddp-streamer" ]]; then - DOCKERFILE_PATH="./ee/apps/ddp-streamer/Dockerfile" - else - DOCKERFILE_PATH="./apps/meteor/ee/server/services/Dockerfile" - fi + echo "GH_IMAGE_NAME: $GH_IMAGE_NAME" + + docker pull $GH_IMAGE_NAME - docker build \ - --build-arg SERVICE=${{ matrix.service }} \ - -t rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} \ - -f ${DOCKERFILE_PATH} \ - . + # 'develop' or 'tag' + DOCKER_TAG=$GITHUB_REF_NAME + docker tag $GH_IMAGE_NAME rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} docker push rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} if [[ $GITHUB_REF == refs/tags/* ]]; then diff --git a/.github/workflows/no-new-js-files.yml b/.github/workflows/no-new-js-files.yml index 6a7473643de3..77045946b52c 100644 --- a/.github/workflows/no-new-js-files.yml +++ b/.github/workflows/no-new-js-files.yml @@ -1,4 +1,4 @@ -name: "JS file preventer" +name: 'JS file preventer' on: pull_request: types: [opened, synchronize] diff --git a/.github/workflows/pr-title-checker.yml b/.github/workflows/pr-title-checker.yml index c31a3aef54d2..7c3ec70779c2 100644 --- a/.github/workflows/pr-title-checker.yml +++ b/.github/workflows/pr-title-checker.yml @@ -1,4 +1,4 @@ -name: "PR Title Checker" +name: 'PR Title Checker' on: pull_request: types: [opened, edited] @@ -9,4 +9,4 @@ jobs: steps: - uses: thehanimo/pr-title-checker@v1.3.4 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RC_TITLE_CHECKER }} diff --git a/.kodiak.toml b/.kodiak.toml index 3865c8954586..b2f9e45da47e 100644 --- a/.kodiak.toml +++ b/.kodiak.toml @@ -8,11 +8,11 @@ block_on_neutral_required_check_runs = true blocking_labels = ["stat: needs QA", "Invalid PR Title"] prioritize_ready_to_merge = true - [merge.message] -title = "pull_request_title" # default: "github_default" -body = "pull_request_body" # default: "github_default" +title = "pull_request_title" +body = "empty" include_coauthors=true + [merge.automerge_dependencies] versions = ["minor", "patch"] usernames = ["dependabot"] diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bc6251d4020..3a27ecd598c5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,11 @@ { "eslint.workingDirectories": [ { - "directory": "packages/*", + "pattern": "packages/*", "changeProcessCWD": true }, { - "directory": "apps/meteor", + "pattern": "apps/*", "changeProcessCWD": true } ], diff --git a/.yarnrc.yml b/.yarnrc.yml index 1d00b70c6e57..18948c0be5b9 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -11,4 +11,5 @@ plugins: spec: '@yarnpkg/plugin-typescript' yarnPath: .yarn/releases/yarn-3.2.0.cjs -checksumBehavior: 'ignore' +checksumBehavior: 'update' +enableImmutableInstalls: false diff --git a/HISTORY.md b/HISTORY.md index a0e244868ff3..6155150e03f0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,175 +1,311 @@ -# 4.8.0 -`2022-05-31 · 16 🎉 · 13 🚀 · 55 🐛 · 151 🔍 · 52 👩‍💻👨‍💻` +# 5.0.0 +`2022-07-22 · 14 ️️️⚠️ · 33 🎉 · 20 🚀 · 110 🐛 · 389 🔍 · 63 👩‍💻👨‍💻` ### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Node: `14.19.3` +- NPM: `6.14.17` +- MongoDB: `4.2, 4.4, 5.0` -### 🎉 New features +### ⚠️ BREAKING CHANGES -- Ability for RC server to check the business hour for a specific department ([#25436](https://github.com/RocketChat/Rocket.Chat/pull/25436)) +- Chore: Remove unused tokenpass integration code ([#25831](https://github.com/RocketChat/Rocket.Chat/pull/25831)) -- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) +- Deactivated team members are added to auto-join rooms ([#25016](https://github.com/RocketChat/Rocket.Chat/pull/25016)) -- Add new app events for pin, react and follow message ([#25337](https://github.com/RocketChat/Rocket.Chat/pull/25337)) + - Do not add deactivated users to auto-join rooms. -- Add new events after user login, logout and change his status ([#25234](https://github.com/RocketChat/Rocket.Chat/pull/25234)) +- Remove Blockstack authentication ([#25649](https://github.com/RocketChat/Rocket.Chat/pull/25649)) -- Add option to show mentions badge when show counter is disabled ([#25329](https://github.com/RocketChat/Rocket.Chat/pull/25329)) + Blockstack authentication is broken and is preventing some dependencies to be up to date. As a migration to Stacks authentication is not trivial, we've opted for removing the authentication service. -- Add user events for apps ([#25165](https://github.com/RocketChat/Rocket.Chat/pull/25165)) +- Remove RDStation integration ([#25774](https://github.com/RocketChat/Rocket.Chat/pull/25774)) -- Adding app button on user dropdown ([#25326](https://github.com/RocketChat/Rocket.Chat/pull/25326)) +- Remove show message in main thread preference ([#26002](https://github.com/RocketChat/Rocket.Chat/pull/26002)) -- Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) + This PR removes the confusion between the `show message in main thread` and the function `also to send to channel`. In the past, we used the `show message in main thread` as a solution to help users to understand the thread feature, as this feature is now mature enough there's no reason to maintain this preference. + + Send the thread message to the main channel or just inside of the thread, should be a decision from the user where the function `also send to channel` appears. Because of that, and because of a bunch of requests and issues we received, we're introducing a new preference `also send thread to channel` where users will be able to decide the behavior of the checkbox. + + ![image](https://user-images.githubusercontent.com/27704687/175655594-023c5907-adc8-4924-ba7d-467608d06fec.png) + + Now there are three behavior options + - `Default`: when it unchecks after sending the first message + + + - `Always`: stay checked for all messages + + + - `Never`: stay unchecked for all messages + - Experimental support for Matrix Federation with a Bridge +- Remove support to old MongoDB versions ([#26098](https://github.com/RocketChat/Rocket.Chat/pull/26098)) - https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 + As per MongoDB Lifecycle Schedules (https://www.mongodb.com/support-policy/lifecycles) we're removing official support to MongoDB versions **3.6 and 4.0** that have already reached end-of-life. + + As MongoDB 4.2 was a "supported" version before Rocket.Chat 5.0, we'll continue supporting it, but will be flagged as deprecated. We recommend upgrading to MongoDB 4.4+. + + Here are official docs on how to upgrade to some of the supported versions: + + - https://www.mongodb.com/docs/manual/release-notes/4.2-upgrade-replica-set/ + - https://www.mongodb.com/docs/v4.4/release-notes/4.4-upgrade-replica-set/ + - https://www.mongodb.com/docs/manual/release-notes/5.0-upgrade-replica-set/ -- Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) +- remove unused endpoints and restify others ([#25889](https://github.com/RocketChat/Rocket.Chat/pull/25889)) -- Federation (Alpha Stabilization) ([#25457](https://github.com/RocketChat/Rocket.Chat/pull/25457)) +- Remove webRTC for channels/dm/groups ([#26225](https://github.com/RocketChat/Rocket.Chat/pull/26225)) -- Get user's preferred language via apps ([#25514](https://github.com/RocketChat/Rocket.Chat/pull/25514)) +- Suspend push notifications when login token is invalidated ([#20913](https://github.com/RocketChat/Rocket.Chat/pull/20913) by [@g-thome](https://github.com/g-thome)) -- Marketplace new app details page ([#24711](https://github.com/RocketChat/Rocket.Chat/pull/24711)) + link the auth token to the push token - Change the app details page layout for the new marketplace UI. General Task: [MKP12 - New UI - App Detail Page](https://app.clickup.com/t/1na769h) +- Upgrade to version 5.0 can be done only from version 4.x ([#26100](https://github.com/RocketChat/Rocket.Chat/pull/26100)) - ## [MKP12 - Tab Navigation](https://app.clickup.com/t/2452f5u) - New tab navigation layout for the app details page. Now the app details page is divided into three sections, details, logs, and settings, that can each be accessed through a Tabs fuselage component. +- use urlParams on omnichannel/agent/extension/ ([#25982](https://github.com/RocketChat/Rocket.Chat/pull/25982)) - Demo gif: - ![tab_navigation_demo_gif](https://user-images.githubusercontent.com/43561537/157276436-3dab34c5-20da-4f5d-99d0-54c1c718ac1f.gif) +- use urlParams on omnichannel/agent/extension/ ([#25874](https://github.com/RocketChat/Rocket.Chat/pull/25874)) - ## [MKP12 - Header](https://app.clickup.com/t/25rhm0x) - Implemented a new header for the marketplaces app details page. - -Changed the size of the app name; - -Implemented the app description field on the header; - -Changed the "metadata" section of the header(The part with the version and author information) now it also shows the last time the app was updated; - -Created a chip that will show when an app is part of one or more bundles and inform which are the bundles; - -Implemented a tooltip for the bundle chips; - -Created a new button + data badge component to substitute the current App Status; - -Changed the title of the "purchase button". Now it shows different text based on the "purchase type" of the app; - -Created a new Pricing & Status display which shows the price when the app is not bought/installed and shows the app status(Enabled/Disabled) when it is bought/installed; - -Changed the way the tabs are rendered, now if the app is not installed(and consequently doesn't have logs and settings tab) it will not render these tabs; +- use urlParams on omnichannel/agent/extension/" ([#25980](https://github.com/RocketChat/Rocket.Chat/pull/25980)) - Demo gif: - ![new-header-gif](https://user-images.githubusercontent.com/43561537/159064599-fd64dfe2-86a3-47da-81ba-1e83f1b87432.gif) +- VideoConference ([#25570](https://github.com/RocketChat/Rocket.Chat/pull/25570)) - ## [MKP12 - Configuration Tab](https://app.clickup.com/t/2452gh4) - Delivered together with the tab-navigation task. Changed the app settings from the details of the app to the new settings tab. - Demo image: - ![New configuration tab](https://user-images.githubusercontent.com/43561537/160211324-95db0566-85bf-4dde-a814-3c6f23dcee4d.png) + In this PR we're deprecating the Video Conference functionality from the core of the application and introducing a **new video conference flow**: + + + + Now the video conference feature will be agnostic so you'll be able to set the provider such as **Jisti** and **BBB** as apps from our marketplace: + + + + Video conferences settings are now global, allowing you to set the default provider + + + + ### [Enterprise Features] + - Video Conferences List + + + - Ringing function for direct messages + + + + - ## [MKP12 - Log Tab](https://app.clickup.com/t/2452gg1) - Changed the place of the app logs from the page to the new logs tab. Also changed some styles of the logs accordions to fit better with the new container. +### 🎉 New features - Before: - ![Before](https://user-images.githubusercontent.com/43561537/160210302-148ce584-604f-40ff-8209-141667016163.png) - After - ![After](https://user-images.githubusercontent.com/43561537/160210984-d4060c5a-f912-4ef9-87e3-fa459080e2d4.png) +- **APPS:** Allow apps to modify a subset of global settings ([#25913](https://github.com/RocketChat/Rocket.Chat/pull/25913)) - ## [MKP12 - Page Header](https://app.clickup.com/t/29b0b12) - Changed the design for the page header of the app details page from a title on the left with a save and back button on the right to a back arrow icon on the left side of the title with the save button still on the right. Also changed the title of the page from App details to Back. - Edit: After some design reconsideration, the page title was changed to App Info. - Demo gif: - ![new_page_header_app_details](https://user-images.githubusercontent.com/43561537/160937741-f5514f70-f43b-4400-8b2f-a5a26f95de9d.gif) +- **APPS:** Allow dispatchment of actions from input elements ([#25949](https://github.com/RocketChat/Rocket.Chat/pull/25949)) - ## [MKP12 - Detail Tab](https://app.clickup.com/t/2452gf7) - Implemented markdown on the description section of the app details page, now the description will show the detailedDescription.rendered (as rendered JSX) information in case it exists and show the description (a.k.a. short description) information in case it doesn't. Unfortunately, as of right now no app has a visual example of a markdown description and because of that, I will not be able to provide a demo image/gif for this PR. + This allows for apps receiving block actions when a user types on a plain text input field or selects an item from the static. A debounce of 700 ms is done when listening for typing action so the app is not flooded with actions. + + + https://user-images.githubusercontent.com/733282/174858175-5ea53046-c791-493e-859b-b80431e94ffa.mp4 - ## [MKP12 - Slider Component](https://app.clickup.com/t/2452h26) - Created an image carousel component on the app details page. This component receives images from the apps/appId/screenshots endpoint and shows them on the content section of the app details of any apps that have screenshots registered, if the app has no screenshots it simply shows nothing where the carousel should be. This component is complete with keyboard arrow navigation on the "open" carousel, hover highlight on the carousel preview and close on esc press. - Demo gif: - ![new_carousel_component](https://user-images.githubusercontent.com/43561537/167415212-9d8359c7-4132-4afa-a698-8be4ab1e1393.gif) +- **APPS:** Allowing apps to register authenticated routes ([#25937](https://github.com/RocketChat/Rocket.Chat/pull/25937)) -- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) + Adds adaptations that allow apps to declare an API endpoint that requires authorization from Rocket.Chat prior to executing + +- **ENTERPRISE:** Device Management ([#25791](https://github.com/RocketChat/Rocket.Chat/pull/25791)) - Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). +- **ENTERPRISE:** Introducing dial pad component into sidebar, calls table, contextual bar ([#26081](https://github.com/RocketChat/Rocket.Chat/pull/26081)) + This PR adds a new call button that can be used from Sidebar & Contact Center. This also enables Omnichannel agents to make outbound calls from within Rocket.Chat. + + Depending on your server and call server configuration, you can do international calling, national and domestic calling. + + The buttons on Contact Center allows an agent to call an existing number without having to type the number again. +- Ability for RC server to check the business hour for a specific department ([#25436](https://github.com/RocketChat/Rocket.Chat/pull/25436)) - ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) - In case you encounter any problems, or want to compare, temporarily it is possible to use the old version +- Accept quoted slash command arguments ([#11744](https://github.com/RocketChat/Rocket.Chat/pull/11744) by [@Hudell](https://github.com/Hudell)) - image +- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) -- New button for network outage ([#25499](https://github.com/RocketChat/Rocket.Chat/pull/25499)) +- Add new app events for pin, react and follow message ([#25337](https://github.com/RocketChat/Rocket.Chat/pull/25337)) - When network outage happens it should be conveyed to the user with special icon. This icon should not be clickable. - Network outage handling is handled in https://app.clickup.com/t/245c0d8 task. +- Add new events after user login, logout and change his status ([#25234](https://github.com/RocketChat/Rocket.Chat/pull/25234)) -- New stats rewrite ([#25078](https://github.com/RocketChat/Rocket.Chat/pull/25078) by [@ostjen](https://github.com/ostjen)) +- Add option to show mentions badge when show counter is disabled ([#25329](https://github.com/RocketChat/Rocket.Chat/pull/25329)) - Add the following new statistics (**metrics**): +- Add user events for apps ([#25165](https://github.com/RocketChat/Rocket.Chat/pull/25165)) +- Adding app button on user dropdown ([#25326](https://github.com/RocketChat/Rocket.Chat/pull/25326)) - - Total users with TOTP enabled; +- Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) - - Total users with 2FA enabled; + Experimental support for Matrix Federation with a Bridge + + https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 - - Total pinned messages; +- Colors Palette - Buttons ([#25626](https://github.com/RocketChat/Rocket.Chat/pull/25626)) - - Total starred messages; +- Community Edition Watermark ([#25844](https://github.com/RocketChat/Rocket.Chat/pull/25844)) - - Total email messages; +- Create releases tab in the marketplace app info page ([#25965](https://github.com/RocketChat/Rocket.Chat/pull/25965)) - - Total rooms with at least one starred message; + Added a Releases tab to the app info page of installed marketplace apps. This tab will show all the released versions of a given app with its version number, release date in humanized form, and the changelog of this given release with the information provided by the publisher, this changelog accepts and renders markdown. Also refactored some component names and logic for maintainability reasons. + Demo gif: + ![app-releases-tab-final](https://user-images.githubusercontent.com/43561537/176228928-651074ce-1f8b-4531-95be-1dd107938bf3.gif) - - Total rooms with at least one pinned message; +- Create Team with a member list of usernames ([#25868](https://github.com/RocketChat/Rocket.Chat/pull/25868)) - - Total encrypted rooms; +- Enable outbound calling for EE (#25843) ([#25960](https://github.com/RocketChat/Rocket.Chat/pull/25960)) - - Total link invitations; +- Engagement Metrics - Phase 2 ([#25505](https://github.com/RocketChat/Rocket.Chat/pull/25505)) - - Total email invitations; + Add the following new statistics (metrics): + - Total Broadcast rooms + - Total rooms with an active Livestream; + - Total triggered emails; + - Total subscription roles; + - Total User Roles; + - Total uncaught exceptions; + - `homeTitleChanged`: boolean value to indicate whether the `Layout_Home_Title` setting has been changed; + - `homeBodyChanged`: boolean value to indicate whether the `Layout_Home_Body` setting has been changed; + - `customCSSChanged`: boolean value to indicate whether the `theme-custom-css` setting has been changed; + - `onLogoutCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_On_Logout` setting has been changed; + - `loggedOutCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_Logged_Out` setting has been changed; + - `loggedInCustomScriptChanged`: boolean value to indicate whether the `Custom_Script_Logged_In` setting has been changed; + - `matrixBridgeEnabled`: boolean value to indicate whether the Matrix bridge has been enabled; - - Logo change; +- Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) - - Number of custom script lines; +- Federation (Alpha Stabilization) ([#25457](https://github.com/RocketChat/Rocket.Chat/pull/25457)) - - Number of custom CSS lines; +- Fuselage ToastBar ([#25583](https://github.com/RocketChat/Rocket.Chat/pull/25583)) - - Number of rooms inside teams; + ![Kapture 2022-05-20 at 14 50 19](https://user-images.githubusercontent.com/27704687/169584462-270e73aa-6dbe-4045-9847-d429125f15a6.gif) - - Number of default (auto-join) rooms inside teams; +- Get user's preferred language via apps ([#25514](https://github.com/RocketChat/Rocket.Chat/pull/25514)) - - Number of users created through link invitation; +- Marketplace new app details page ([#24711](https://github.com/RocketChat/Rocket.Chat/pull/24711)) - - Number of users created through manual entry; + Change the app details page layout for the new marketplace UI. General Task: [MKP12 - New UI - App Detail Page](https://app.clickup.com/t/1na769h) + + ## [MKP12 - Tab Navigation](https://app.clickup.com/t/2452f5u) + New tab navigation layout for the app details page. Now the app details page is divided into three sections, details, logs, and settings, that can each be accessed through a Tabs fuselage component. + + Demo gif: + ![tab_navigation_demo_gif](https://user-images.githubusercontent.com/43561537/157276436-3dab34c5-20da-4f5d-99d0-54c1c718ac1f.gif) + + ## [MKP12 - Header](https://app.clickup.com/t/25rhm0x) + Implemented a new header for the marketplaces app details page. + -Changed the size of the app name; + -Implemented the app description field on the header; + -Changed the "metadata" section of the header(The part with the version and author information) now it also shows the last time the app was updated; + -Created a chip that will show when an app is part of one or more bundles and inform which are the bundles; + -Implemented a tooltip for the bundle chips; + -Created a new button + data badge component to substitute the current App Status; + -Changed the title of the "purchase button". Now it shows different text based on the "purchase type" of the app; + -Created a new Pricing & Status display which shows the price when the app is not bought/installed and shows the app status(Enabled/Disabled) when it is bought/installed; + -Changed the way the tabs are rendered, now if the app is not installed(and consequently doesn't have logs and settings tab) it will not render these tabs; + + Demo gif: + ![new-header-gif](https://user-images.githubusercontent.com/43561537/159064599-fd64dfe2-86a3-47da-81ba-1e83f1b87432.gif) + + ## [MKP12 - Configuration Tab](https://app.clickup.com/t/2452gh4) + Delivered together with the tab-navigation task. Changed the app settings from the details of the app to the new settings tab. + Demo image: + ![New configuration tab](https://user-images.githubusercontent.com/43561537/160211324-95db0566-85bf-4dde-a814-3c6f23dcee4d.png) + + ## [MKP12 - Log Tab](https://app.clickup.com/t/2452gg1) + Changed the place of the app logs from the page to the new logs tab. Also changed some styles of the logs accordions to fit better with the new container. + + Before: + ![Before](https://user-images.githubusercontent.com/43561537/160210302-148ce584-604f-40ff-8209-141667016163.png) + + After + ![After](https://user-images.githubusercontent.com/43561537/160210984-d4060c5a-f912-4ef9-87e3-fa459080e2d4.png) + + ## [MKP12 - Page Header](https://app.clickup.com/t/29b0b12) + Changed the design for the page header of the app details page from a title on the left with a save and back button on the right to a back arrow icon on the left side of the title with the save button still on the right. Also changed the title of the page from App details to Back. + Edit: After some design reconsideration, the page title was changed to App Info. + Demo gif: + ![new_page_header_app_details](https://user-images.githubusercontent.com/43561537/160937741-f5514f70-f43b-4400-8b2f-a5a26f95de9d.gif) + + ## [MKP12 - Detail Tab](https://app.clickup.com/t/2452gf7) + Implemented markdown on the description section of the app details page, now the description will show the detailedDescription.rendered (as rendered JSX) information in case it exists and show the description (a.k.a. short description) information in case it doesn't. Unfortunately, as of right now no app has a visual example of a markdown description and because of that, I will not be able to provide a demo image/gif for this PR. + + ## [MKP12 - Slider Component](https://app.clickup.com/t/2452h26) + Created an image carousel component on the app details page. This component receives images from the apps/appId/screenshots endpoint and shows them on the content section of the app details of any apps that have screenshots registered, if the app has no screenshots it simply shows nothing where the carousel should be. This component is complete with keyboard arrow navigation on the "open" carousel, hover highlight on the carousel preview and close on esc press. + Demo gif: + ![new_carousel_component](https://user-images.githubusercontent.com/43561537/167415212-9d8359c7-4132-4afa-a698-8be4ab1e1393.gif) - - Number of imported users (by import type); +- Marketplace security tab app info page ([#25739](https://github.com/RocketChat/Rocket.Chat/pull/25739)) -- Star message, report and delete message events ([#25383](https://github.com/RocketChat/Rocket.Chat/pull/25383)) + Created a new security tab for installed apps that displays information related to the given app security policies, terms of services, and necessary permissions for the use of the app. + Demo gif: + ![privacy-tab](https://user-images.githubusercontent.com/43561537/173878394-333057d4-3c7e-434e-a3ca-d3e08f33c7bc.gif) -### 🚀 Improvements +- Matrix Federation UX improvements ([#25847](https://github.com/RocketChat/Rocket.Chat/pull/25847)) +- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) -- **ENTERPRISE:** Allow mapping LDAP groups to multiple RC roles ([#23849](https://github.com/RocketChat/Rocket.Chat/pull/23849)) + Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). + + + + ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) + In case you encounter any problems, or want to compare, temporarily it is possible to use the old version + + image - - Add support to mapping LDAP groups to multiple roles (by specifying arrays in the "User Data Group Map" enterprise setting. +- New button for network outage ([#25499](https://github.com/RocketChat/Rocket.Chat/pull/25499)) -- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) + When network outage happens it should be conveyed to the user with special icon. This icon should not be clickable. + Network outage handling is handled in https://app.clickup.com/t/245c0d8 task. + +- New stats rewrite ([#25078](https://github.com/RocketChat/Rocket.Chat/pull/25078) by [@ostjen](https://github.com/ostjen)) + + Add the following new statistics (**metrics**): + + - Total users with TOTP enabled; + - Total users with 2FA enabled; + - Total pinned messages; + - Total starred messages; + - Total email messages; + - Total rooms with at least one starred message; + - Total rooms with at least one pinned message; + - Total encrypted rooms; + - Total link invitations; + - Total email invitations; + - Logo change; + - Number of rooms inside teams; + - Number of default (auto-join) rooms inside teams; + - Number of users created through link invitation; + - Number of users created through manual entry; + - Number of imported users (by import type); - Earlier OTR room uses only 2 states, we need more states to support future features. - This adds more states for the OTR contextualBar. +- Star message, report and delete message events ([#25383](https://github.com/RocketChat/Rocket.Chat/pull/25383)) + +- Use setting to determine if initial general channel is needed ([#25441](https://github.com/RocketChat/Rocket.Chat/pull/25441) by [@felipe-menelau](https://github.com/felipe-menelau)) + + - Adds flag responsible for overwriting #general channel creation +- VoIP Input/Output Device Selection ([#25966](https://github.com/RocketChat/Rocket.Chat/pull/25966)) + +### 🚀 Improvements - - Expired - Screen Shot 2022-04-20 at 13 55 52 +- **ENTERPRISE:** Allow mapping LDAP groups to multiple RC roles ([#23849](https://github.com/RocketChat/Rocket.Chat/pull/23849)) - - Declined - Screen Shot 2022-04-20 at 13 49 28 + - Add support to mapping LDAP groups to multiple roles (by specifying arrays in the "User Data Group Map" enterprise setting. +- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) - - Error + Earlier OTR room uses only 2 states, we need more states to support future features. + This adds more states for the OTR contextualBar. + + - Expired + Screen Shot 2022-04-20 at 13 55 52 + + - Declined + Screen Shot 2022-04-20 at 13 49 28 + + - Error Screen Shot 2022-04-20 at 13 55 26 - Add tooltip to sidebar room menu ([#24405](https://github.com/RocketChat/Rocket.Chat/pull/24405) by [@Himanshu664](https://github.com/Himanshu664)) @@ -180,10 +316,33 @@ - Added tooltip options for message menu ([#24431](https://github.com/RocketChat/Rocket.Chat/pull/24431) by [@Himanshu664](https://github.com/Himanshu664)) +- Avoid using omnichannel-queue collection ([#25491](https://github.com/RocketChat/Rocket.Chat/pull/25491)) + +- Differ Voip calls from Incoming and Outgoing ([#25643](https://github.com/RocketChat/Rocket.Chat/pull/25643)) + + Updated this column and its respective endpoints to support inbound/outfound call definitions + ![image](https://user-images.githubusercontent.com/34130764/170512008-34202ed8-3ed4-4c28-baa5-25efc17543d5.png) + +- Expand the feature set of the new message rendering ([#25970](https://github.com/RocketChat/Rocket.Chat/pull/25970)) + + - Everything inside a new package (`@rocket.chat/gazzodown`); + - KaTeX support; + - Highlighted Words support; + - Emoji rendering expanded; + - Code rendering fixed + - Fix multiple bugs with Matrix bridge ([#25318](https://github.com/RocketChat/Rocket.Chat/pull/25318)) - Improve active/hover colors in account sidebar ([#25024](https://github.com/RocketChat/Rocket.Chat/pull/25024) by [@Himanshu664](https://github.com/Himanshu664)) +- Moved call hold/unhold to EE ([#26007](https://github.com/RocketChat/Rocket.Chat/pull/26007)) + + This PR adds a restriction, enabling the feature to hold/unhold calls only for Enterprise Edition users. + +- Moved call wrap up modal to EE ([#25875](https://github.com/RocketChat/Rocket.Chat/pull/25875)) + + This PR adds a restriction, enabling the feature to display the call wrap up modal only for Enterprise Edition users. + - New admin settings Page ([#25439](https://github.com/RocketChat/Rocket.Chat/pull/25439)) ![Screen Shot 2022-05-09 at 11 31 58](https://user-images.githubusercontent.com/27704687/167432811-f4970f23-5dae-48a0-a427-92269d08a859.png) @@ -192,15 +351,34 @@ - Performance for some Omnichannel features ([#25217](https://github.com/RocketChat/Rocket.Chat/pull/25217)) +- Refactor + unit tests for federation-v2 ([#25680](https://github.com/RocketChat/Rocket.Chat/pull/25680)) + + The main goal for this PR is to add the ability to add tests in our current federation-v2 implementation. + In this PR, I've added only unit tests (80%), but the goal is to add other kinds of tests in the near future. + + Also, I've created a diagram to show how this refactor was done, and how is the structure of the code + + ![image](https://user-images.githubusercontent.com/15324204/171039619-22168000-3626-424e-b408-18dea540f786.png) + - Rename upgrade tab routes ([#25097](https://github.com/RocketChat/Rocket.Chat/pull/25097)) Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test. - Unify voip streams into single stream ([#25108](https://github.com/RocketChat/Rocket.Chat/pull/25108)) +- VoIP admin page cleanup: remove unused settings ([#25993](https://github.com/RocketChat/Rocket.Chat/pull/25993)) + + https://app.clickup.com/t/2n4m61m + ### 🐛 Bug fixes +- `You and @yourUsername reacted with`title on reactions ([#25733](https://github.com/RocketChat/Rocket.Chat/pull/25733)) + +- Access issue on chat.getThreadsList ([#25750](https://github.com/RocketChat/Rocket.Chat/pull/25750)) + +- AccountBox checks for condition ([#25708](https://github.com/RocketChat/Rocket.Chat/pull/25708)) + - Add katex render to new message react template ([#25239](https://github.com/RocketChat/Rocket.Chat/pull/25239)) - Add open user card to user avatar ([#25445](https://github.com/RocketChat/Rocket.Chat/pull/25445)) @@ -215,27 +393,40 @@ - AgentOverview analytics wrong departmentId parameter ([#25073](https://github.com/RocketChat/Rocket.Chat/pull/25073) by [@paulobernardoaf](https://github.com/paulobernardoaf)) - When filtering the analytics charts by department, data would not appear because the object: - ```js - { - value: "department-id", - label: "department-name" - } - ``` - was being used in the `departmentId` parameter. + When filtering the analytics charts by department, data would not appear because the object: + ```js + { + value: "department-id", + label: "department-name" + } + ``` + was being used in the `departmentId` parameter. + + - Before: + ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) + + - After: + ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) +- AgentsPage pagination ([#25820](https://github.com/RocketChat/Rocket.Chat/pull/25820)) - - Before: - ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) +- allow only livechat-agents to be contact manager for any omnichannel contact ([#25451](https://github.com/RocketChat/Rocket.Chat/pull/25451)) +- Append path To Route For Custom Emoji ([#24379](https://github.com/RocketChat/Rocket.Chat/pull/24379)) - - After: - ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) +- Attachments and OEmbed margins ([#25713](https://github.com/RocketChat/Rocket.Chat/pull/25713)) -- Change form body parameter charset to UTF-8 to fix issue #25456 ([#25673](https://github.com/RocketChat/Rocket.Chat/pull/25673) by [@divinespear](https://github.com/divinespear)) +- Broken Omnichannel>Agents page ([#25731](https://github.com/RocketChat/Rocket.Chat/pull/25731)) + +- Bump meteor-node-stubs to version 1.2.3 ([#25669](https://github.com/RocketChat/Rocket.Chat/pull/25669) by [@Sh0uld](https://github.com/Sh0uld)) - since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0. + With meteor-node-stubs version 1.2.3 a bug was fixed, which occured in issue #25460 and probably #25513 (last one not tested). + For the issue in meteor see: https://github.com/meteor/meteor/issues/11974 + +- Change form body parameter charset to UTF-8 to fix issue #25456 ([#25673](https://github.com/RocketChat/Rocket.Chat/pull/25673) by [@divinespear](https://github.com/divinespear)) + since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0. + ![Screenshot from 2022-05-28 16-26-06](https://user-images.githubusercontent.com/126630/170815447-1f3bd579-243a-42d3-86f6-814aeaa30ce9.png) - Change NPS Vote identifier + nps index to unique ([#25423](https://github.com/RocketChat/Rocket.Chat/pull/25423)) @@ -246,84 +437,138 @@ - Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) - Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online - unless agent explicitly logs off. - Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. - - 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off - in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. - - 2. Second reason is when computer goes in sleep mode. - - 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. - - Solution: - The idea is to detect the network disconnection and start the start the attempts to reconnect. - The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not - call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are - used. - - The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to - reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. - + Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online + unless agent explicitly logs off. + Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. + 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off + in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + 2. Second reason is when computer goes in sleep mode. + 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. + + Solution: + The idea is to detect the network disconnection and start the start the attempts to reconnect. + The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not + call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are + used. + + The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to + reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. + When the server is disconnected, it should be indicated on the phone button. +- Client-generated sort parameters in channel directory ([#25768](https://github.com/RocketChat/Rocket.Chat/pull/25768) by [@BenWiederhake](https://github.com/BenWiederhake)) + - Close room when dismiss wrap up call modal ([#25056](https://github.com/RocketChat/Rocket.Chat/pull/25056)) +- Custom emoji reaction size ([#25393](https://github.com/RocketChat/Rocket.Chat/pull/25393)) + - Custom sound error toast messages ([#24515](https://github.com/RocketChat/Rocket.Chat/pull/24515) by [@Himanshu664](https://github.com/Himanshu664)) - Deactivating user breaks if user is the only room owner ([#24933](https://github.com/RocketChat/Rocket.Chat/pull/24933) by [@sidmohanty11](https://github.com/sidmohanty11)) - ## Before + ## Before + + https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 + + + + ## After + https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 - https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 +- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- Direct Reply ([#22588](https://github.com/RocketChat/Rocket.Chat/pull/22588)) +- Discussion alphabetical ordering ([#25788](https://github.com/RocketChat/Rocket.Chat/pull/25788)) - ## After - https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 + Added a validation in the prop used for sorting (loweCaseName) checking for a prop that only exists in discussions (prid) -- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- Dynamic load matrix is enabled and handle failure ([#25495](https://github.com/RocketChat/Rocket.Chat/pull/25495)) - End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) +- Error "numRequestsAllowed" property in rateLimiter for REST API endpoint when upgrading ([#26058](https://github.com/RocketChat/Rocket.Chat/pull/26058)) + - Failure to update Integration History index ([#25473](https://github.com/RocketChat/Rocket.Chat/pull/25473)) - Fix max-width message block ([#25686](https://github.com/RocketChat/Rocket.Chat/pull/25686)) -- Fixing app contextual bar functionality ([#25615](https://github.com/RocketChat/Rocket.Chat/pull/25615)) +- Fix prom-client new promise usage ([#25781](https://github.com/RocketChat/Rocket.Chat/pull/25781)) -- Fixing Network connectivity issues with SIP client. ([#25391](https://github.com/RocketChat/Rocket.Chat/pull/25391)) +- fixes HTML sanitizing error. ([#25410](https://github.com/RocketChat/Rocket.Chat/pull/25410)) - The previous PR https://github.com/RocketChat/Rocket.Chat/pull/25170 did not handle the issues completely. - This PR is expected to handle + If the user sent a HTML message over our product to a livechat user the HTML would get rendered on the message box, this prevents it from happening. - 1. Clearing call related UI when the network is disconnected or switched. +- Fixing app contextual bar functionality ([#25615](https://github.com/RocketChat/Rocket.Chat/pull/25615)) - 2. Do clean connectivity. There were few issues discovered in earlier implementation. e.g endpoint would randomly - get disconnected after a while. This was due to the fact that the earlier socket disconnection caused the - removal of contact on asterisk. This should be fixed in this PR. +- Fixing Network connectivity issues with SIP client. ([#25391](https://github.com/RocketChat/Rocket.Chat/pull/25391)) + The previous PR https://github.com/RocketChat/Rocket.Chat/pull/25170 did not handle the issues completely. + This PR is expected to handle + 1. Clearing call related UI when the network is disconnected or switched. + 2. Do clean connectivity. There were few issues discovered in earlier implementation. e.g endpoint would randomly + get disconnected after a while. This was due to the fact that the earlier socket disconnection caused the + removal of contact on asterisk. This should be fixed in this PR. 3. This PR contains a lot of logs. This will be removed before the final merge. - FormData uploads not working ([#25069](https://github.com/RocketChat/Rocket.Chat/pull/25069)) - Full error message is visible ([#24856](https://github.com/RocketChat/Rocket.Chat/pull/24856) by [@Himanshu664](https://github.com/Himanshu664)) +- getUserMentionsByChannel method room permission ([#25748](https://github.com/RocketChat/Rocket.Chat/pull/25748)) + +- Importer fails to download files from URLs with query string params ([#25934](https://github.com/RocketChat/Rocket.Chat/pull/25934)) + +- Importer files are unnecessarily transferred over the network. ([#25919](https://github.com/RocketChat/Rocket.Chat/pull/25919)) + - Incorrect websocket url in livechat widget ([#25261](https://github.com/RocketChat/Rocket.Chat/pull/25261)) +- Initial members value on Create Channel Modal ([#26000](https://github.com/RocketChat/Rocket.Chat/pull/26000)) + + #### before + ![Screen Shot 2022-06-24 at 11 58 22](https://user-images.githubusercontent.com/27704687/175562315-221dbc9a-5695-4259-a8f7-644e2ff0ab36.png) + + #### after + ![Screen Shot 2022-06-24 at 11 59 38](https://user-images.githubusercontent.com/27704687/175562510-a4a6be49-bbd2-4aeb-aedb-a5a7a6f1159d.png) + +- Initial User not added to default channel ([#25544](https://github.com/RocketChat/Rocket.Chat/pull/25544)) + + If injecting initial user. The user wasn’t added to the default General channel + - Integrations avatar attribute misuse ([#25283](https://github.com/RocketChat/Rocket.Chat/pull/25283)) - Invitation links don't redirect to the registration form ([#25082](https://github.com/RocketChat/Rocket.Chat/pull/25082)) +- Kebab menu clicking issue ([#25869](https://github.com/RocketChat/Rocket.Chat/pull/25869)) + +- LDAP sync removing users from channels when multiple groups are mapped to it ([#25434](https://github.com/RocketChat/Rocket.Chat/pull/25434)) + +- Members selection field on creating team modal ([#25871](https://github.com/RocketChat/Rocket.Chat/pull/25871)) + + - Fix: add members breaking when searching users + + ![image](https://user-images.githubusercontent.com/27704687/121788070-b792f700-cba0-11eb-92b9-5833e1213c74.png) + - Message menu action not working on legacy messages. ([#25148](https://github.com/RocketChat/Rocket.Chat/pull/25148)) - Message menu dropdown not working on Mobile Web ([#25616](https://github.com/RocketChat/Rocket.Chat/pull/25616)) - Message preview not available for queued chats ([#25092](https://github.com/RocketChat/Rocket.Chat/pull/25092)) +- Messages spacing ([#25631](https://github.com/RocketChat/Rocket.Chat/pull/25631)) + + Adding `sequential` prop to Message component from Fuselage + +- Misaligned username on Room Info card for omnichannel chats ([#25331](https://github.com/RocketChat/Rocket.Chat/pull/25331)) + +- Not showing edit message button when blocking edit after N minutes ([#25724](https://github.com/RocketChat/Rocket.Chat/pull/25724) by [@matthias4217](https://github.com/matthias4217)) + + Previously, in Rocketchat 4.7.0 and later, as mentioned in https://github.com/RocketChat/Rocket.Chat/issues/25478, the edit button was not displayed on the interface in the minute after having sent a message. This is now fixed : messages can be edited right after sending them. + - NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) +- One of the triggers was not working correctly ([#25409](https://github.com/RocketChat/Rocket.Chat/pull/25409)) + - Ordered and unordered list styles, Line breaks. ([#25494](https://github.com/RocketChat/Rocket.Chat/pull/25494)) Also removed the message.md cache from server, since changes in the parser might break messages in the future (and will in this specific case). @@ -334,10 +579,10 @@ - Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) - ### before - Screen Shot 2022-03-29 at 13 35 56 - - ### after + ### before + Screen Shot 2022-03-29 at 13 35 56 + + ### after Screen Shot 2022-03-29 at 11 48 05 - Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) @@ -352,18 +597,23 @@ - Remove 'total' text in admin info page ([#25638](https://github.com/RocketChat/Rocket.Chat/pull/25638)) - - Remove initial 'total' text from rooms and messages groups in the admin info page - + - Remove initial 'total' text from rooms and messages groups in the admin info page - Add 'total' before 'rooms' and 'messages' title on the same section. To use the new 'Total Rooms', was created a new key in the en.i18n.json file. +- Remove duplicated icon bell when is thread main message ([#26051](https://github.com/RocketChat/Rocket.Chat/pull/26051)) + +- Remove duplicated property _USERNAMES from createDirectRoom.ts ([#26087](https://github.com/RocketChat/Rocket.Chat/pull/26087)) + + This pull request removes the duplicated property `_USERNAMES` from `apps/meteor/app/lib/server/functions/createDirectRoom.ts`, using only the existing property `roomInfo.usernames`. + - Removing user also removes them from Omni collections ([#25444](https://github.com/RocketChat/Rocket.Chat/pull/25444)) - Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) + + ### after Screenshot 2022-01-13 at 8 57 47 PM - Reply button behavior on broadcast channel ([#25175](https://github.com/RocketChat/Rocket.Chat/pull/25175)) @@ -374,39 +624,76 @@ - Rooms' names turn lower case on CSV import ([#24612](https://github.com/RocketChat/Rocket.Chat/pull/24612)) - * Change 'Settings' import to not get cached configs - + * Change 'Settings' import to not get cached configs * Remove update `UI_Allow_room_names_with_special_chars` value - Sanitize customUserStatus and fix infinite loop ([#25449](https://github.com/RocketChat/Rocket.Chat/pull/25449)) - ### Additional improves: + ### Additional improves: + - usage of RHF to avoid unnecessary Add and Edit components separately and form validation + - usage of `GenericTableV2` and some hooks to avoid unnecessary code + - fix `IUserStatus` type + - improves in UI design + - improves **empty** and **loading** state + - improves files structure + + [LOOP ERROR ATTACHMENT] + ![Screen Shot 2022-05-09 at 19 42 53](https://user-images.githubusercontent.com/27704687/167510439-1980461c-a885-46d2-9a49-79da432c7521.png) - - usage of RHF to avoid unnecessary Add and Edit components separately and form validation +- Sanitize styles in message ([#25744](https://github.com/RocketChat/Rocket.Chat/pull/25744)) - - usage of `GenericTableV2` and some hooks to avoid unnecessary code +- Settings listeners not receiving overwritten values from env vars ([#25448](https://github.com/RocketChat/Rocket.Chat/pull/25448)) - - fix `IUserStatus` type +- Settings not being overwritten to their default values ([#25891](https://github.com/RocketChat/Rocket.Chat/pull/25891)) - - improves in UI design +- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007)) - - improves **empty** and **loading** state + https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 - - improves files structure +- sidebar colors ([#25987](https://github.com/RocketChat/Rocket.Chat/pull/25987)) - [LOOP ERROR ATTACHMENT] - ![Screen Shot 2022-05-09 at 19 42 53](https://user-images.githubusercontent.com/27704687/167510439-1980461c-a885-46d2-9a49-79da432c7521.png) +- Sort by scope or creation date not working on canned responses list ([#25475](https://github.com/RocketChat/Rocket.Chat/pull/25475)) -- Settings listeners not receiving overwritten values from env vars ([#25448](https://github.com/RocketChat/Rocket.Chat/pull/25448)) +- Spotlight results showing usernames instead of real names ([#25471](https://github.com/RocketChat/Rocket.Chat/pull/25471)) -- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007) by [@nishant23122000](https://github.com/nishant23122000)) +- Thread Message Preview ([#25709](https://github.com/RocketChat/Rocket.Chat/pull/25709)) - https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 +- Too many watchers in dev environment. ([#25930](https://github.com/RocketChat/Rocket.Chat/pull/25930)) - Toolbox hiding under contextual bar ([#25237](https://github.com/RocketChat/Rocket.Chat/pull/25237)) +- toolbox menu behind thread component ([#25925](https://github.com/RocketChat/Rocket.Chat/pull/25925)) + +- UI/UX issues on Live Chat widget ([#25407](https://github.com/RocketChat/Rocket.Chat/pull/25407)) + +- Unable to close chats when comments is disabled ([#26057](https://github.com/RocketChat/Rocket.Chat/pull/26057)) + + Fixes https://github.com/RocketChat/Rocket.Chat/issues/25954 + - Unable to see channel member list by authorized channel roles ([#25412](https://github.com/RocketChat/Rocket.Chat/pull/25412)) +- Undefined headers on API Client ([#26083](https://github.com/RocketChat/Rocket.Chat/pull/26083)) + +- Unnecessary padding on teams channels footer ([#25712](https://github.com/RocketChat/Rocket.Chat/pull/25712)) + + #### before + + + ### after + + +- Update chartjs usage to v3 ([#25873](https://github.com/RocketChat/Rocket.Chat/pull/25873)) + +- Update import from `csv-parse` ([#25872](https://github.com/RocketChat/Rocket.Chat/pull/25872)) + + This PR updates the importing of `csv-parse` because the used method wasn't working anymore, we were receiving the following error: + + `error: "this.csvParser is not a function"` + +- Update subscription on update team member ([#25855](https://github.com/RocketChat/Rocket.Chat/pull/25855)) + + Added update to subscription when a team member is updated on `teams.updateMember` + - Upgrade tab loader in incorrect position ([#25398](https://github.com/RocketChat/Rocket.Chat/pull/25398)) - Add invisible prop to iframe when loading state is active. @@ -417,43 +704,73 @@ - useCurrentChatTags is not a function ([#25604](https://github.com/RocketChat/Rocket.Chat/pull/25604)) -- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) +- User abandonment setting was not working doe to failing event hook ([#25520](https://github.com/RocketChat/Rocket.Chat/pull/25520)) - ### before - ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) + A setting watcher and the query for grabbing abandoned chats were broken, now they're not. - ### after - ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) +- User avatar reseting and getting random image ([#25603](https://github.com/RocketChat/Rocket.Chat/pull/25603)) -- UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) + - fixes user avatar not being saved after editing the user profile issue + - fixes user avatar not getting another user picture due to database deletion error + +- user status Offline misnamed as Invisible in Custom Status edit dropdown menu ([#24796](https://github.com/RocketChat/Rocket.Chat/pull/24796) by [@Kunalvrm555](https://github.com/Kunalvrm555)) - - Rewrites the component to TS +- User's with non-agent role shown on voip agent association model ([#25682](https://github.com/RocketChat/Rocket.Chat/pull/25682)) - - Fixes some visual issues +- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) + + ### before + ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) + + ### after + ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) - ### before - ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) +- UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) - ### after + - Rewrites the component to TS + - Fixes some visual issues + + ### before + ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) + + ### after ![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png) -- Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) +- Users without the `view-other-user-info` permission can't use the `users.list` endpoint ([#26050](https://github.com/RocketChat/Rocket.Chat/pull/26050)) -- VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) + This PR fix the query when a normal users access `users.list` - Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) +- Validate room access ([#24534](https://github.com/RocketChat/Rocket.Chat/pull/24534)) - It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + The request must be blocked If the user has no permission to view rooms. - Solution: +- Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) +- VOIP CallContext snapshot infinite loop ([#25947](https://github.com/RocketChat/Rocket.Chat/pull/25947)) - 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + The application was crashing due to an error on the `useCallerInfo()` hook. + The error was: + ![image](https://user-images.githubusercontent.com/20212776/174823914-4832e5dd-c91a-4ae4-9d1f-1b960bcd372c.png) + ![image](https://user-images.githubusercontent.com/20212776/174823982-cb543fe0-663f-4530-bb94-0720653ca897.png) + + To prevent this issue to happen it was added a cached and out-of-scope snapshot variable to the hook using `useSyncExternalStore` - 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. +- VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) + Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) + + It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + + Solution: + + 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. 3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler. +- Voip endpoint permissions ([#25783](https://github.com/RocketChat/Rocket.Chat/pull/25783)) + +- Wrong argument name preventing Omnichannel Chat Forward to User ([#25723](https://github.com/RocketChat/Rocket.Chat/pull/25723)) +
🔍 Minor changes @@ -470,40 +787,108 @@ - Bump template-file from 6.0.0 to 6.0.1 ([#25002](https://github.com/RocketChat/Rocket.Chat/pull/25002) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: `@rocket.chat/favicon` ([#25920](https://github.com/RocketChat/Rocket.Chat/pull/25920)) + +- Chore: `refactor/tsc-perf` ([#26040](https://github.com/RocketChat/Rocket.Chat/pull/26040)) + +- Chore: Account/Profile to TS ([#25929](https://github.com/RocketChat/Rocket.Chat/pull/25929)) + +- Chore: add _id and name options to JSON Schemas ([#25813](https://github.com/RocketChat/Rocket.Chat/pull/25813)) + + This pull request adds the `roomId` and `roomName` options for the Ajv JSON Schemas on the `packages/rest-typings/src/v1/channels/` and `packages/rest-typings/src/v1/dm/` folders. + - Chore: Add /v1/video-conference endpoint types ([#25278](https://github.com/RocketChat/Rocket.Chat/pull/25278)) +- Chore: Add Agenda fork to the monorepo ([#25681](https://github.com/RocketChat/Rocket.Chat/pull/25681)) + +- Chore: add Ajv JSON Schema to api/v1 ([#25601](https://github.com/RocketChat/Rocket.Chat/pull/25601)) + + This pull request adds Ajv JSON Schema validation to `apps/meteor/app/api/server/v1/` and `packages/rest-typings/src/v1/`, where needed. + +- Chore: Add auto label and improve Kodiak configuration ([#25829](https://github.com/RocketChat/Rocket.Chat/pull/25829)) + - Chore: Add channel endpoints (rest-typings) ([#25279](https://github.com/RocketChat/Rocket.Chat/pull/25279)) - Chore: Add client folder to CODEOWNERS ([#25397](https://github.com/RocketChat/Rocket.Chat/pull/25397)) - Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) - Not crash the whole application if something goes wrong in the MessageList component. - + Not crash the whole application if something goes wrong in the MessageList component. + ![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png) +- Chore: Add Livechat repo into Monorepo packages ([#25312](https://github.com/RocketChat/Rocket.Chat/pull/25312)) + +- Chore: Add missing Swedish livechat translations ([#26048](https://github.com/RocketChat/Rocket.Chat/pull/26048) by [@joakimaho](https://github.com/joakimaho)) + + Added missing Swedish translations. + - Chore: Add options to debug stdout and rate limiter ([#25336](https://github.com/RocketChat/Rocket.Chat/pull/25336)) - Chore: Add root package.json to houston files ([#25286](https://github.com/RocketChat/Rocket.Chat/pull/25286)) See title +- Chore: Add tests for agents screens ([#25637](https://github.com/RocketChat/Rocket.Chat/pull/25637)) + - Chore: Add typings for /v1/webdav.getMyAccounts ([#25276](https://github.com/RocketChat/Rocket.Chat/pull/25276)) - Chore: Add yarn plugin to check node and yarn version ([#25224](https://github.com/RocketChat/Rocket.Chat/pull/25224)) +- Chore: Adding default message parser template ([#26064](https://github.com/RocketChat/Rocket.Chat/pull/26064)) + +- Chore: adjust in some configurations ([#25612](https://github.com/RocketChat/Rocket.Chat/pull/25612)) + +- Chore: Allow endpoints to optionally require authentication ([#26084](https://github.com/RocketChat/Rocket.Chat/pull/26084)) + +- Chore: API test on method GET with params as a number. ([#25769](https://github.com/RocketChat/Rocket.Chat/pull/25769)) + +- Chore: AutoTranslate contextualBar rewrite ([#25751](https://github.com/RocketChat/Rocket.Chat/pull/25751)) + +- Chore: Avoid set useless set UTC Offset ([#26270](https://github.com/RocketChat/Rocket.Chat/pull/26270)) + +- Chore: Avoid unneeded permission updates when EE license is applied ([#26253](https://github.com/RocketChat/Rocket.Chat/pull/26253)) + +- Chore: Broken Storybook ([#25714](https://github.com/RocketChat/Rocket.Chat/pull/25714)) + + There is another small improvement on the way we got storybook files. + +- Chore: Bump deps ([#25624](https://github.com/RocketChat/Rocket.Chat/pull/25624)) + - Chore: bump fuselage ([#25605](https://github.com/RocketChat/Rocket.Chat/pull/25605)) - Chore: Bump fuselage ([#25371](https://github.com/RocketChat/Rocket.Chat/pull/25371)) +- Chore: Bump fuselage and update icon ([#26036](https://github.com/RocketChat/Rocket.Chat/pull/26036)) + +- Chore: bump fuselage packages ([#26325](https://github.com/RocketChat/Rocket.Chat/pull/26325)) + - Chore: Bump Fuselage packages ([#25259](https://github.com/RocketChat/Rocket.Chat/pull/25259)) - Chore: Cancel running jobs if PR is updated ([#24708](https://github.com/RocketChat/Rocket.Chat/pull/24708)) +- Chore: Change Apps-Engine version source for info ([#26205](https://github.com/RocketChat/Rocket.Chat/pull/26205)) + + Now that we're using `yarn`, the version stored in the `package.json` is no longer the resolved one, but it matches the input. This means that when we ran `yarn add @rocket.chat/apps-engine@alpha`, yarn saves `"alpha"` as the version of the package, while NPM added the resolved version for the tag, e.g. `"1.33.0-alpha.6507"`. This ends up breaking a few places where we need the Apps-Engine version for communication with the Marketplace. + + With this PR we change the source of that info so the problem doesn't happen anymore. + +- Chore: Change stats to daily ([#26113](https://github.com/RocketChat/Rocket.Chat/pull/26113)) + +- Chore: Check for env var values and not just if they are set ([#26219](https://github.com/RocketChat/Rocket.Chat/pull/26219)) + - Chore: Chore add validation option to rest endpoints ([#25443](https://github.com/RocketChat/Rocket.Chat/pull/25443)) +- Chore: Close tooltip on click ([#26070](https://github.com/RocketChat/Rocket.Chat/pull/26070)) + - Chore: Code Improvements for #25391 ([#25606](https://github.com/RocketChat/Rocket.Chat/pull/25606)) +- Chore: Collect e2e coverage ([#25743](https://github.com/RocketChat/Rocket.Chat/pull/25743)) + +- Chore: Colors ([#25969](https://github.com/RocketChat/Rocket.Chat/pull/25969)) + +- Chore: command's endpoints ([#25630](https://github.com/RocketChat/Rocket.Chat/pull/25630)) + - Chore: Convert `UserStatusMenu` to TS ([#25265](https://github.com/RocketChat/Rocket.Chat/pull/25265)) - Chore: Convert additionalForms ([#25586](https://github.com/RocketChat/Rocket.Chat/pull/25586)) @@ -514,56 +899,99 @@ - Chore: Convert Admin/OAuthApps to TS ([#25277](https://github.com/RocketChat/Rocket.Chat/pull/25277)) - - Converts Admin/OAuthApps to TS. - + - Converts Admin/OAuthApps to TS. - migrated forms to react-hook-form - Chore: Convert AdminSideBar to ts ([#25372](https://github.com/RocketChat/Rocket.Chat/pull/25372)) +- Chore: convert apps/meteor/app/api/server/lib/ files to TS ([#25840](https://github.com/RocketChat/Rocket.Chat/pull/25840)) + + This pull request converts files on `apps/meteor/app/api/server/lib/` to Typescript. + - Chore: Convert apps/meteor/client/components/UserAutoComplete ([#25554](https://github.com/RocketChat/Rocket.Chat/pull/25554)) +- Chore: Convert apps/meteor/client/sidebar/header/index ([#25671](https://github.com/RocketChat/Rocket.Chat/pull/25671)) + +- Chore: Convert apps/meteor/client/sidebar/search ([#25754](https://github.com/RocketChat/Rocket.Chat/pull/25754)) + - Chore: Convert apps/meteor/client/views/admin/settings ([#25565](https://github.com/RocketChat/Rocket.Chat/pull/25565)) - Chore: Convert apps/meteor/client/views/admin/settings/inputs folder ([#25427](https://github.com/RocketChat/Rocket.Chat/pull/25427)) +- Chore: Convert assets endpoint to Typescript ([#25358](https://github.com/RocketChat/Rocket.Chat/pull/25358)) + - Chore: Convert AutoTranslate ([#25591](https://github.com/RocketChat/Rocket.Chat/pull/25591)) - Chore: Convert client/views/admin/settings/groups folder to ts ([#25345](https://github.com/RocketChat/Rocket.Chat/pull/25345)) +- Chore: convert communication methods to Typescript ([#25503](https://github.com/RocketChat/Rocket.Chat/pull/25503)) + + Convert files from `apps/meteor/app/apps/server/communication/` to ts. + +- Chore: Convert components/sidebar to TS ([#25429](https://github.com/RocketChat/Rocket.Chat/pull/25429)) + - Chore: Convert Create Channel ([#25589](https://github.com/RocketChat/Rocket.Chat/pull/25589)) +- Chore: Convert CreateChannelWithData ([#25667](https://github.com/RocketChat/Rocket.Chat/pull/25667)) + - Chore: Convert customSounds folder to ts ([#25274](https://github.com/RocketChat/Rocket.Chat/pull/25274)) - Chore: Convert customUserStatus folder to ts ([#25288](https://github.com/RocketChat/Rocket.Chat/pull/25288)) -- Chore: Convert email inbox feature to TypeScript ([#25298](https://github.com/RocketChat/Rocket.Chat/pull/25298)) +- Chore: convert e2e to ts ([#25958](https://github.com/RocketChat/Rocket.Chat/pull/25958)) + + Converted the `apps/meteor/app/api/server/v1/e2e.js` to ts and created endpoint typings on the `packages/rest-typings/src/v1/e2e` folder. + +- Chore: Convert email inbox feature to TypeScript ([#25298](https://github.com/RocketChat/Rocket.Chat/pull/25298) by [@ujorgeleite](https://github.com/ujorgeleite)) - Chore: Convert federationDashboard folder to ts ([#25343](https://github.com/RocketChat/Rocket.Chat/pull/25343)) - Chore: Convert getStatistics ([#25342](https://github.com/RocketChat/Rocket.Chat/pull/25342)) +- Chore: convert import.js endpoints to TS ([#25956](https://github.com/RocketChat/Rocket.Chat/pull/25956)) + + Converted the `apps/meteor/app/api/server/v1/import.js` to ts and created endpoint typings on the `packages/rest-typings/src/v1/import` folder. + - Chore: convert info to typescript ([#25420](https://github.com/RocketChat/Rocket.Chat/pull/25420)) +- Chore: convert invites, misc and subscriptions to TS and create definitions ([#25350](https://github.com/RocketChat/Rocket.Chat/pull/25350)) + + Converted `apps/meteor/app/api/server/v1/invites.js`, `misc.js` and `subscriptions.js` to Typescript and created their endpoint definitions on the rest-typings folder. + - Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) - Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) - Chore: convert marketplace price display component to use typescript ([#25504](https://github.com/RocketChat/Rocket.Chat/pull/25504)) - **Marketplace apps listing page** - ![Screen Shot 2022-05-13 at 12 57 43](https://user-images.githubusercontent.com/4161171/168322189-67990fdf-a447-46dc-8f88-08b16c2a5416.png) - - **Apps detail page** + **Marketplace apps listing page** + ![Screen Shot 2022-05-13 at 12 57 43](https://user-images.githubusercontent.com/4161171/168322189-67990fdf-a447-46dc-8f88-08b16c2a5416.png) + + **Apps detail page** ![Screen Shot 2022-05-13 at 12 58 56](https://user-images.githubusercontent.com/4161171/168322241-505ee5bb-d3d8-4b0e-8757-873a1a65a6a6.png) +- Chore: Convert MemoizedSetting, Setting, Section ([#25572](https://github.com/RocketChat/Rocket.Chat/pull/25572)) + +- Chore: Convert normalizeMessagesForUser ([#26059](https://github.com/RocketChat/Rocket.Chat/pull/26059)) + - Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) - Chore: Convert push endpoints to TS ([#25347](https://github.com/RocketChat/Rocket.Chat/pull/25347)) - Chore: Convert RoomForeword, TextCopy and RoomAvatarEditor to TS ([#25424](https://github.com/RocketChat/Rocket.Chat/pull/25424)) +- Chore: Convert RoomMenu ([#25914](https://github.com/RocketChat/Rocket.Chat/pull/25914)) + +- Chore: Convert sidebar/header/actions ([#25581](https://github.com/RocketChat/Rocket.Chat/pull/25581)) + +- Chore: Convert sidebar/item ([#25634](https://github.com/RocketChat/Rocket.Chat/pull/25634)) + - Chore: Convert slashCommands to typescript ([#25592](https://github.com/RocketChat/Rocket.Chat/pull/25592) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: Convert to TS omnichannel/agent ([#25511](https://github.com/RocketChat/Rocket.Chat/pull/25511)) + +- Chore: Convert to TS RoomAutoComplete ([#25536](https://github.com/RocketChat/Rocket.Chat/pull/25536)) + - Chore: Convert to typescript some functions from app/lib/server/functions ([#24519](https://github.com/RocketChat/Rocket.Chat/pull/24519)) Convert to typescript some functions from app/lib/server/functions and transfered theses files to server/lib @@ -574,54 +1002,133 @@ - Chore: Convert useFileInput to TS ([#25426](https://github.com/RocketChat/Rocket.Chat/pull/25426)) +- Chore: Convert usePreventDefault, useQueryOptions, useShortcutOpenMenu ([#26035](https://github.com/RocketChat/Rocket.Chat/pull/26035)) + +- Chore: Convert UserAutoCompleteMultiple ([#25587](https://github.com/RocketChat/Rocket.Chat/pull/25587)) + +- Chore: Convert users endpoints ([#25635](https://github.com/RocketChat/Rocket.Chat/pull/25635)) + +- Chore: Convert useSidebarPaletteColor ([#26065](https://github.com/RocketChat/Rocket.Chat/pull/26065)) + - Chore: Convert useUpdateAvatar to TS and type avatar endpoints ([#25430](https://github.com/RocketChat/Rocket.Chat/pull/25430)) +- Chore: Converting files from app/livechat folder from JS to TS ([#25658](https://github.com/RocketChat/Rocket.Chat/pull/25658)) + + Converting files from apps/meteor/app/livechat/lib/ from JS to TS + +- Chore: Converting omnichannel installation files to ts ([#25665](https://github.com/RocketChat/Rocket.Chat/pull/25665)) + + This PR converts the omnichannel/installation folder from js to ts + - Chore: Converting orchestrator.js to ts ([#25367](https://github.com/RocketChat/Rocket.Chat/pull/25367)) +- Chore: create a e2e test guideline ([#25884](https://github.com/RocketChat/Rocket.Chat/pull/25884)) + +- Chore: Create a token for each action ([#26023](https://github.com/RocketChat/Rocket.Chat/pull/26023)) + - Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) +- Chore: Custom Sounds Endpoints ([#25633](https://github.com/RocketChat/Rocket.Chat/pull/25633)) + - Chore: Dedicated package for UI contexts ([#25432](https://github.com/RocketChat/Rocket.Chat/pull/25432)) Moving our React contexts to a different package on the monorepo enable us to deliver components from another packages, because they work as a loose connection to the core APIs. - Chore: Dependencies upgrade ([#25290](https://github.com/RocketChat/Rocket.Chat/pull/25290)) +- Chore: Disabled icon colors on sidebar ([#26257](https://github.com/RocketChat/Rocket.Chat/pull/26257)) + +- Chore: Do not log integrations using `name` key ([#26163](https://github.com/RocketChat/Rocket.Chat/pull/26163)) + - Chore: Enable marketplace screenshots endpoint ([#25395](https://github.com/RocketChat/Rocket.Chat/pull/25395)) - Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) - - data and test-failure should be ignored - + - data and test-failure should be ignored - ensure scripts use cross-env -- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) +- Chore: Fix CI ([#25797](https://github.com/RocketChat/Rocket.Chat/pull/25797)) -- Chore: Increase performance and security of integrations’ scripts ([#25641](https://github.com/RocketChat/Rocket.Chat/pull/25641)) +- Chore: Fix correct unit test to api files ([#25870](https://github.com/RocketChat/Rocket.Chat/pull/25870)) - Replace internal VM implementation with VM2 which implements many more mechanisms to ensure timeout, security and allow easier configuration for future improvements on the integrations' feature. +- Chore: Fix incorrect checksum for agenda package (cause of breaking develop builds) ([#25741](https://github.com/RocketChat/Rocket.Chat/pull/25741)) -- Chore: Livechat change output level ([#25522](https://github.com/RocketChat/Rocket.Chat/pull/25522)) +- Chore: Fix Omnichannel E2E tests not running ([#26092](https://github.com/RocketChat/Rocket.Chat/pull/26092)) -- Chore: Manager Page Rewrite ([#25431](https://github.com/RocketChat/Rocket.Chat/pull/25431)) +- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) -- Chore: Migrate 15-message-popup from cypress to playwright ([#25462](https://github.com/RocketChat/Rocket.Chat/pull/25462)) +- Chore: Fix version on develop branch ([#25842](https://github.com/RocketChat/Rocket.Chat/pull/25842)) -- Chore: migrate from cypress to pw 14-setting-permission ([#25523](https://github.com/RocketChat/Rocket.Chat/pull/25523)) +- Chore: fix watermark condition ([#26095](https://github.com/RocketChat/Rocket.Chat/pull/26095)) -- Chore: Migrate NotFoundPage to TS ([#25509](https://github.com/RocketChat/Rocket.Chat/pull/25509)) +- Chore: Fixes e2e playwright intermittences ([#25984](https://github.com/RocketChat/Rocket.Chat/pull/25984)) -- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) +- Chore: Fuselage update ([#26004](https://github.com/RocketChat/Rocket.Chat/pull/26004)) -- Chore: Migrate retention-policy to ts ([#25582](https://github.com/RocketChat/Rocket.Chat/pull/25582)) +- Chore: Fuselage update ([#25983](https://github.com/RocketChat/Rocket.Chat/pull/25983)) -- Chore: Migrate spotify to ts ([#25507](https://github.com/RocketChat/Rocket.Chat/pull/25507)) +- Chore: Handle errors on index creation ([#26094](https://github.com/RocketChat/Rocket.Chat/pull/26094)) -- Chore: migrate-to-pw-adjust-in-intermitences ([#25542](https://github.com/RocketChat/Rocket.Chat/pull/25542)) +- Chore: Hide deprecation query log on production ([#26188](https://github.com/RocketChat/Rocket.Chat/pull/26188)) + +- Chore: Improve CI cache ([#25907](https://github.com/RocketChat/Rocket.Chat/pull/25907)) + +- Chore: Improve footer Template ([#26085](https://github.com/RocketChat/Rocket.Chat/pull/26085)) + +- Chore: Increase performance and security of integrations’ scripts ([#25641](https://github.com/RocketChat/Rocket.Chat/pull/25641)) + + Replace internal VM implementation with VM2 which implements many more mechanisms to ensure timeout, security and allow easier configuration for future improvements on the integrations' feature. + +- Chore: Info page ([#26201](https://github.com/RocketChat/Rocket.Chat/pull/26201)) + +- Chore: Introduce Modal Region ([#25962](https://github.com/RocketChat/Rocket.Chat/pull/25962)) + +- Chore: Introduce new index to query active livechat conversations for cloud scaling ([#26047](https://github.com/RocketChat/Rocket.Chat/pull/26047)) + +- Chore: Keep the option to run only the meteor app ([#25915](https://github.com/RocketChat/Rocket.Chat/pull/25915)) + +- Chore: Keyboard shortcuts contextualBar rewrite ([#25753](https://github.com/RocketChat/Rocket.Chat/pull/25753)) + +- Chore: Livechat change output level ([#25522](https://github.com/RocketChat/Rocket.Chat/pull/25522)) + +- Chore: Major refactors in pageobjects ([#26015](https://github.com/RocketChat/Rocket.Chat/pull/26015)) + +- Chore: Make kodiak merge message empty ([#26069](https://github.com/RocketChat/Rocket.Chat/pull/26069)) + +- Chore: Manager Page Rewrite ([#25431](https://github.com/RocketChat/Rocket.Chat/pull/25431)) + +- Chore: Messages raw model rewrite to ts ([#25761](https://github.com/RocketChat/Rocket.Chat/pull/25761)) + +- Chore: Migrate 15-message-popup from cypress to playwright ([#25462](https://github.com/RocketChat/Rocket.Chat/pull/25462)) + +- Chore: migrate from cypress to pw 14-setting-permission ([#25523](https://github.com/RocketChat/Rocket.Chat/pull/25523)) + +- Chore: migrate katex to ts ([#25501](https://github.com/RocketChat/Rocket.Chat/pull/25501)) + +- Chore: Migrate LivechatVisitors model to raw ([#25756](https://github.com/RocketChat/Rocket.Chat/pull/25756)) + +- Chore: Migrate NotFoundPage to TS ([#25509](https://github.com/RocketChat/Rocket.Chat/pull/25509)) + +- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) + +- Chore: Migrate oembed to ts ([#25622](https://github.com/RocketChat/Rocket.Chat/pull/25622)) + +- Chore: Migrate retention-policy to ts ([#25582](https://github.com/RocketChat/Rocket.Chat/pull/25582)) + +- Chore: Migrate some small helper functions to TypeScript ([#25666](https://github.com/RocketChat/Rocket.Chat/pull/25666)) + +- Chore: Migrate spotify to ts ([#25507](https://github.com/RocketChat/Rocket.Chat/pull/25507)) + +- Chore: migrate-to-pw-16-discussion ([#25567](https://github.com/RocketChat/Rocket.Chat/pull/25567)) + +- Chore: migrate-to-pw-adjust-in-intermitences ([#25542](https://github.com/RocketChat/Rocket.Chat/pull/25542)) - Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) - Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) +- Chore: Model Typings ([#25758](https://github.com/RocketChat/Rocket.Chat/pull/25758)) + - Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) - Chore: Move admin sidebarItems registration to the main file ([#25442](https://github.com/RocketChat/Rocket.Chat/pull/25442)) @@ -630,88 +1137,171 @@ - Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) +- Chore: move fork of cas module to the monorepo ([#26107](https://github.com/RocketChat/Rocket.Chat/pull/26107)) + - Chore: Move markdown message parser to a `callback` ([#25413](https://github.com/RocketChat/Rocket.Chat/pull/25413)) +- Chore: Move voip's Wrap-up and On-hold functionality to EE (Backend) ([#25160](https://github.com/RocketChat/Rocket.Chat/pull/25160)) + +- Chore: Notification Preferences to TS ([#25827](https://github.com/RocketChat/Rocket.Chat/pull/25827)) + + - Notifications Preferences to TS. + - Fix broken save action. + - Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) +- Chore: Plan tag ([#26224](https://github.com/RocketChat/Rocket.Chat/pull/26224)) + + Now we only have one plan tag for all plans \/ + ![image](https://user-images.githubusercontent.com/40830821/178366367-12388c4c-6822-4e41-be8d-ca306718be98.png) + +- Chore: Prune Messages contextualBar rewrite ([#25757](https://github.com/RocketChat/Rocket.Chat/pull/25757)) + +- Chore: Remove all cypress tests, configs and references ([#25564](https://github.com/RocketChat/Rocket.Chat/pull/25564)) + - Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) +- Chore: Remove compose from main repo ([#23426](https://github.com/RocketChat/Rocket.Chat/pull/23426)) + +- Chore: Remove duplicate checksumBehavior key from yarn file ([#25730](https://github.com/RocketChat/Rocket.Chat/pull/25730)) + +- Chore: remove duplicated NotFoundPage.js ([#25749](https://github.com/RocketChat/Rocket.Chat/pull/25749)) + - Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) +- Chore: Remove Imperative Modal from context ([#25911](https://github.com/RocketChat/Rocket.Chat/pull/25911)) + - Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) +- Chore: Remove old rest api code ([#25863](https://github.com/RocketChat/Rocket.Chat/pull/25863)) + - Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore +- Chore: Remove snap files from Houston config ([#25819](https://github.com/RocketChat/Rocket.Chat/pull/25819)) + +- Chore: Remove TimeSync usage ([#26294](https://github.com/RocketChat/Rocket.Chat/pull/26294)) + +- Chore: Remove toastr package ([#25787](https://github.com/RocketChat/Rocket.Chat/pull/25787)) + - Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) +- Chore: remove unused locators from e2e tests ([#25860](https://github.com/RocketChat/Rocket.Chat/pull/25860)) + +- Chore: Remove unused migrations ([#26102](https://github.com/RocketChat/Rocket.Chat/pull/26102)) + + After giving it some thought: + + - 234 through 240 are not going to be run anymore. Keeping them does not affect behavior of course, but this (removing) makes it easier to quickly glance at and understand what migrations are actually included in 5.x.y (especially in tag compare view or in general just checking the ref). + + - Also changed the file name of 233 to be more explicit at what it does so to not confuse with actual "migrations" without having to open the file. + + - The redirect to the documentation page (go.rocket....) is not yet set up, jfyi. + - Chore: Reorder unreleased migrations ([#25508](https://github.com/RocketChat/Rocket.Chat/pull/25508)) +- Chore: Replace `useSubscription` with `useSyncExternalStore` ([#25909](https://github.com/RocketChat/Rocket.Chat/pull/25909)) + +- Chore: Replace AnnouncementModal in favor of GenericModal ([#25752](https://github.com/RocketChat/Rocket.Chat/pull/25752)) + - Chore: Rest API query parameters handling ([#25648](https://github.com/RocketChat/Rocket.Chat/pull/25648)) - Chore: REST query and body params validation ([#25446](https://github.com/RocketChat/Rocket.Chat/pull/25446)) +- Chore: RestApiClient as Package ([#25469](https://github.com/RocketChat/Rocket.Chat/pull/25469)) + +- Chore: Revert `yarn dev` implementation ([#26075](https://github.com/RocketChat/Rocket.Chat/pull/26075)) + - Chore: Rewrite 2fa to typescript ([#25285](https://github.com/RocketChat/Rocket.Chat/pull/25285)) - Chore: Rewrite action-links to ts ([#25418](https://github.com/RocketChat/Rocket.Chat/pull/25418)) +- Chore: Rewrite AddUsers to TS ([#25830](https://github.com/RocketChat/Rocket.Chat/pull/25830) by [@kodiakhq[bot]](https://github.com/kodiakhq[bot])) + +- Chore: Rewrite Admin UsersTable to Typescript ([#25698](https://github.com/RocketChat/Rocket.Chat/pull/25698)) + - Chore: Rewrite autotranslate to ts ([#25425](https://github.com/RocketChat/Rocket.Chat/pull/25425)) - Chore: Rewrite im and dm endpoints to ts ([#25521](https://github.com/RocketChat/Rocket.Chat/pull/25521)) - - Endpoints rewritten to TS - - dm.create - - dm.delete - - dm.close - - dm.counters - - dm.files - - dm.history - - dm.members - - dm.messages - - dm.messages.others - - dm.list - - dm.list.everyone - - dm.open - - dm.setTopic - - im.create - - im.delete - - im.close - - im.counters - - im.files - - im.history - - im.members - - im.messages - - im.messages.others - - im.list - - im.list.everyone - - im.open - - im.setTopic - - - Some lines of code was refactored on `apps/meteor/app/api/server/v1/im.ts` - - - Unnecessary functions were deleted on `apps/meteor/app/lib/server/functions/getDirectMessageByNameOrIdWithOptionToJoin.ts` - + - Endpoints rewritten to TS + - dm.create + - dm.delete + - dm.close + - dm.counters + - dm.files + - dm.history + - dm.members + - dm.messages + - dm.messages.others + - dm.list + - dm.list.everyone + - dm.open + - dm.setTopic + - im.create + - im.delete + - im.close + - im.counters + - im.files + - im.history + - im.members + - im.messages + - im.messages.others + - im.list + - im.list.everyone + - im.open + - im.setTopic + - Some lines of code was refactored on `apps/meteor/app/api/server/v1/im.ts` + - Unnecessary functions were deleted on `apps/meteor/app/lib/server/functions/getDirectMessageByNameOrIdWithOptionToJoin.ts` - New types was added on `apps/meteor/app/api/server/api.d.ts` - Chore: Rewrite Jitsi Contextualbar to TS ([#25303](https://github.com/RocketChat/Rocket.Chat/pull/25303)) - Chore: Rewrite mail-messages to ts ([#25421](https://github.com/RocketChat/Rocket.Chat/pull/25421)) +- Chore: Rewrite RoomWithData ([#25858](https://github.com/RocketChat/Rocket.Chat/pull/25858)) + - Chore: Rewrite some Omnichannel files to TypeScript ([#25359](https://github.com/RocketChat/Rocket.Chat/pull/25359)) - apps/meteor/client/components/Omnichannel/modals/* + apps/meteor/client/components/Omnichannel/modals/* apps/meteor/client/components/Omnichannel/Tags.js +- Chore: Room access validation may be called without user information ([#26086](https://github.com/RocketChat/Rocket.Chat/pull/26086)) + +- Chore: RouteGroup for My Account sidebar ([#25632](https://github.com/RocketChat/Rocket.Chat/pull/25632)) + + Refactoring My Accounts routes. Allows to add "my account" routes for EE. + +- Chore: Run tests on docker ([#25556](https://github.com/RocketChat/Rocket.Chat/pull/25556)) + +- Chore: Settings UI issue ([#26053](https://github.com/RocketChat/Rocket.Chat/pull/26053)) + +- Chore: Small fix on callProvider ([#25963](https://github.com/RocketChat/Rocket.Chat/pull/25963)) + - Chore: solve yarn issues from env var ([#25468](https://github.com/RocketChat/Rocket.Chat/pull/25468)) +- Chore: Split useUserInfoActions into small hooks ([#25747](https://github.com/RocketChat/Rocket.Chat/pull/25747)) + - Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) +- Chore: Taking out Blaze from routes with `MainLayout` ([#25697](https://github.com/RocketChat/Rocket.Chat/pull/25697)) + + While working with @guijun13 on the new homepage I saw we're still rendering a Blaze template even to just embedded components into `MainLayout`. This PR addresses it. + - Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) + ``` + npx hygen package new test ``` - npx hygen package new test - ``` + +- Chore: Test for department screen ([#25696](https://github.com/RocketChat/Rocket.Chat/pull/25696)) + +- Chore: test turbo params ([#26038](https://github.com/RocketChat/Rocket.Chat/pull/26038)) + +- Chore: Testing Kodiak feature ([#25794](https://github.com/RocketChat/Rocket.Chat/pull/25794)) + +- Chore: Tests refactor pageobjects ([#26245](https://github.com/RocketChat/Rocket.Chat/pull/26245)) - Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) @@ -723,44 +1313,135 @@ - Chore: Tests with Playwright (task: ROC-66, Intermittent resolution in tests) ([#25416](https://github.com/RocketChat/Rocket.Chat/pull/25416)) +- Chore: Translate admin helpers to TS ([#25690](https://github.com/RocketChat/Rocket.Chat/pull/25690)) + - Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) - Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) +- Chore: ui-client package ([#25916](https://github.com/RocketChat/Rocket.Chat/pull/25916)) + - Chore: Update Apps-Engine and Fuselage ([#25700](https://github.com/RocketChat/Rocket.Chat/pull/25700)) - Chore: Update Apps-Engine version ([#25617](https://github.com/RocketChat/Rocket.Chat/pull/25617)) +- Chore: Update Apps-Engine version ([#26258](https://github.com/RocketChat/Rocket.Chat/pull/26258)) + + Bumping Apps-Engine version + +- Chore: update avatar colors ([#26153](https://github.com/RocketChat/Rocket.Chat/pull/26153)) + - Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) - Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) +- Chore: Update Meteor 2.7.3 ([#25991](https://github.com/RocketChat/Rocket.Chat/pull/25991)) + - Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. +- Chore: Update package.json update tsc memory ([#25755](https://github.com/RocketChat/Rocket.Chat/pull/25755)) + +- Chore: update pageobjects to use es6 getters and remove export default ([#25867](https://github.com/RocketChat/Rocket.Chat/pull/25867)) + +- Chore: Update poplib ([#25964](https://github.com/RocketChat/Rocket.Chat/pull/25964)) + +- Chore: Update useSidebarPalette selectors ([#26322](https://github.com/RocketChat/Rocket.Chat/pull/26322)) + - Chore: Update Volta configuration ([#25394](https://github.com/RocketChat/Rocket.Chat/pull/25394)) [Volta](https://volta.sh/) need some extra configuration to work on monorepos. +- Chore: Updating Apps-Engine ([#26001](https://github.com/RocketChat/Rocket.Chat/pull/26001)) + +- Chore: Upgrade and remove unnecessary Livechat dependencies ([#25672](https://github.com/RocketChat/Rocket.Chat/pull/25672)) + +- Chore: Upgrade Fuselage packages to `next` dist-tag ([#26274](https://github.com/RocketChat/Rocket.Chat/pull/26274)) + + Upgrade Fuselage packages to the latest development versions. + +- Chore: use params instead of URL building on livechat endpoints ([#25810](https://github.com/RocketChat/Rocket.Chat/pull/25810)) + - Chore: User set UTC offset ([#25381](https://github.com/RocketChat/Rocket.Chat/pull/25381)) +- Chore: VideoConference UX/UI Refactor 1st Interaction ([#26183](https://github.com/RocketChat/Rocket.Chat/pull/26183)) + +- Chore: VoIP Context ([#25994](https://github.com/RocketChat/Rocket.Chat/pull/25994)) + +- Chore: Watch for package changes ([#25910](https://github.com/RocketChat/Rocket.Chat/pull/25910)) + + With the current `dev` pipeline, whenever we modify a package (e.g. `api-client`), we have to kill the meteor proccess and run `yarn dev` again in order for the changes to be compiled and the new output to be used by meteor. + + This has the drawback of taking a little longer to run the dev environment, since we can't cache a watched buid. In the other hand, it reduces the friction of modifying internal packages since we don't need to rebuild the project for changes to take effect. + + This will enable us to move more things to separate packages without affecting the dev experience too much. + +- Chore(deps): Bump sharp from 0.30.4 to 0.30.6 ([#25719](https://github.com/RocketChat/Rocket.Chat/pull/25719) by [@dependabot[bot]](https://github.com/dependabot[bot])) + - i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) - Merge master into develop & Set version to 4.7.0-develop ([#25028](https://github.com/RocketChat/Rocket.Chat/pull/25028)) +- Merge master into develop & Set version to 5.0.0 ([#25702](https://github.com/RocketChat/Rocket.Chat/pull/25702) by [@felipe-menelau](https://github.com/felipe-menelau)) + +- Regression: Admin Avatar Edit endpoint fix ([#26232](https://github.com/RocketChat/Rocket.Chat/pull/26232)) + +- Regression: [VideoConference] Callee client behaves improperly when accepting a call from someone who lost the connection ([#26101](https://github.com/RocketChat/Rocket.Chat/pull/26101)) + + If the caller loses connection and the callee accepts the call anyway, the client will wait for five seconds for confirmation that they can join the call. This PR improves the UI behavior during those five seconds. + +- Regression: [VideoConference] If the caller loses connection, direct calls are never canceled ([#26099](https://github.com/RocketChat/Rocket.Chat/pull/26099)) + +- Regression: `yarn dev` not working ([#26071](https://github.com/RocketChat/Rocket.Chat/pull/26071)) + - Regression: Add `isPending` status to message ([#25299](https://github.com/RocketChat/Rocket.Chat/pull/25299)) +- Regression: Add appId prop to slashcommand ([#25988](https://github.com/RocketChat/Rocket.Chat/pull/25988)) + + Pass the appId when present to the slashcommand array. This avoid problems with contextual bar and modals not opening. + +- Regression: Add better error messages when call fails ([#26134](https://github.com/RocketChat/Rocket.Chat/pull/26134)) + +- Regression: Add Error boundary to katex render component ([#26067](https://github.com/RocketChat/Rocket.Chat/pull/26067)) + - Regression: Add eslint package to micro services Dockerfile ([#25311](https://github.com/RocketChat/Rocket.Chat/pull/25311)) - Regression: Add select message to system message and thread preview and allow select on legacy template ([#25251](https://github.com/RocketChat/Rocket.Chat/pull/25251)) +- Regression: Add v1 to licenses.add endpoint ([#26311](https://github.com/RocketChat/Rocket.Chat/pull/26311)) + +- Regression: Added missing call button in the contact info contextual bar ([#26135](https://github.com/RocketChat/Rocket.Chat/pull/26135)) + +- Regression: Added missing call button to contact center calls list ([#26119](https://github.com/RocketChat/Rocket.Chat/pull/26119)) + + This PR adds a call button to the contact center calls list rows. + +- Regression: Adjusted priority to run canned responses replace before new parser ([#26298](https://github.com/RocketChat/Rocket.Chat/pull/26298)) + + Canned responses placeholders were not being replaced properly after we changed to the new md parser. + This fix changes the priority so that the canned responses replace logic runs before the parser, thus bringing back this functionality. + + Before: + Screen Shot 2022-07-18 at 19 25 07 + + After: + Screen Shot 2022-07-18 at 19 26 09 + +- Regression: Align TypeScript version across workspaces ([#26184](https://github.com/RocketChat/Rocket.Chat/pull/26184)) + + Some places were still referring to TypeScript 4.3.4 instead of 4.5.5, so this PR targets it. + +- Regression: All users in members list showing as federated ([#26129](https://github.com/RocketChat/Rocket.Chat/pull/26129)) + - Regression: App event listeners broke Slackbridge integration and importers ([#25689](https://github.com/RocketChat/Rocket.Chat/pull/25689)) Some event listeners triggered by Apps were calling `Meteor.user()` in functions that could run outside of Meteor environment - Regression: Assets & Slack Bridge Setting Page not rendering ([#25629](https://github.com/RocketChat/Rocket.Chat/pull/25629)) +- Regression: AutoTranslate on new message template ([#26049](https://github.com/RocketChat/Rocket.Chat/pull/26049)) + - Regression: Avatar not loading on first direct message ([#25211](https://github.com/RocketChat/Rocket.Chat/pull/25211)) fix avatar not loading on a first direct message @@ -771,44 +1452,201 @@ For reasons I've no clue, any client import path matching `**/data/**` will not be included in the final bundle, failing silently on transpiling/bundling. -- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) +- Regression: Broken emoji picker on Livechat ([#26128](https://github.com/RocketChat/Rocket.Chat/pull/26128)) - - Bump to 'next' the onboarding-ui package from fuselage. +- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) + - Bump to 'next' the onboarding-ui package from fuselage. - Update from 'companyEmail' to 'email' adminData usage types +- Regression: Burger menu showing arrow instead of burguer ([#26170](https://github.com/RocketChat/Rocket.Chat/pull/26170)) + +- Regression: Call toggle missing network disconnection state ([#26237](https://github.com/RocketChat/Rocket.Chat/pull/26237)) + + This PR brings back the network disconnection state to the voip call toggle button + + ![image (4)](https://user-images.githubusercontent.com/6494543/178564719-f436505e-3ae3-4d69-ba5a-27ce8e8c5fba.png) + +- Regression: Calling info on VoipFooter when performing an outbound call ([#26138](https://github.com/RocketChat/Rocket.Chat/pull/26138)) + + ![image](https://user-images.githubusercontent.com/17487063/177395438-a0b2d30a-e0e2-4a31-9b55-2c6c3216bbd7.png) + +- Regression: Cannot logout when CallProvider is unregistered and mounted ([#26158](https://github.com/RocketChat/Rocket.Chat/pull/26158)) + +- Regression: Cannot open Menu in searched message. ([#26172](https://github.com/RocketChat/Rocket.Chat/pull/26172)) + +- Regression: Change Audio settings for device settings as modal title ([#26159](https://github.com/RocketChat/Rocket.Chat/pull/26159)) + - Regression: Change logic to check if connection is online on unstable networks ([#25618](https://github.com/RocketChat/Rocket.Chat/pull/25618)) - Regression: Change preference to be default legacy messages ([#25255](https://github.com/RocketChat/Rocket.Chat/pull/25255)) +- Regression: Changing isEnterprise useQuery name to prevent conflict of queries ([#26116](https://github.com/RocketChat/Rocket.Chat/pull/26116)) + + Changed the name of useQuery hook to prevent conflict of queries with same name. + +- Regression: Channel `type` icon on Engagement Dashboard ([#26269](https://github.com/RocketChat/Rocket.Chat/pull/26269)) + + This PR fixes a bug on which the channel type is inverted. + - Regression: CI playwright ([#25168](https://github.com/RocketChat/Rocket.Chat/pull/25168)) - Regression: CI services build ([#25555](https://github.com/RocketChat/Rocket.Chat/pull/25555)) +- Regression: Clear user selection filter after selecting desired user. ([#26295](https://github.com/RocketChat/Rocket.Chat/pull/26295)) + +- Regression: Close button on modals created via apps not working ([#26127](https://github.com/RocketChat/Rocket.Chat/pull/26127)) + +- Regression: Contact manager edit/view not working ([#26155](https://github.com/RocketChat/Rocket.Chat/pull/26155)) + + Basically, the Contact Center was working, but not the right way. This PR fixes: + - Ability to select Contact Managers from dropdown + - Ability to validate Contact Edits without requesting data a ton of times + - Ability to remove Contact manager from a contact + - Ability to see Contacts and Contact Managers on Contact View + - Fix endpoints validation + - Add validators (ajv) to endpoint, thou not being used yet (since we hit a special endpoint) + +- Regression: Contact manager endpoint usage ([#26063](https://github.com/RocketChat/Rocket.Chat/pull/26063)) + +- Regression: Correct call ringtones ([#26111](https://github.com/RocketChat/Rocket.Chat/pull/26111)) + + - outbound-call-ringing ringtone: Should be played when the outbound call is initiated and not yet established(Current implementation is playing the incoming-call ringtone) + - call-ended ringtone: Should be played whenever a call ends. + +- Regression: Device management table missing device icon and ip text ellipsis ([#26255](https://github.com/RocketChat/Rocket.Chat/pull/26255)) + +- Regression: Do not show federated tooltip on non-federated rooms ([#26115](https://github.com/RocketChat/Rocket.Chat/pull/26115)) + +- Regression: Docker image publish ([#25931](https://github.com/RocketChat/Rocket.Chat/pull/25931)) + +- Regression: Don't open mdm feature modal on registration page ([#26234](https://github.com/RocketChat/Rocket.Chat/pull/26234)) + +- Regression: Emojis displaying as `:undefined:` ([#26141](https://github.com/RocketChat/Rocket.Chat/pull/26141)) + +- Regression: Empty URL previews in messages. ([#26160](https://github.com/RocketChat/Rocket.Chat/pull/26160)) + - Regression: Endpoint types with Ajv Coercing data types ([#25644](https://github.com/RocketChat/Rocket.Chat/pull/25644)) Ajv Coercing data types should be `true` to accept all kinds of data requested. - Regression: eslint not running on packages ([#25305](https://github.com/RocketChat/Rocket.Chat/pull/25305)) +- Regression: Federated users not showing as federated in Room Members ([#26249](https://github.com/RocketChat/Rocket.Chat/pull/26249)) + +- Regression: fix `directory` endpoint not listing teams ([#26310](https://github.com/RocketChat/Rocket.Chat/pull/26310)) + +- Regression: Fix app icons breaking UI ([#26278](https://github.com/RocketChat/Rocket.Chat/pull/26278)) + +- Regression: fix apps path ([#25809](https://github.com/RocketChat/Rocket.Chat/pull/25809)) + +- Regression: Fix apps wrong typing ([#25824](https://github.com/RocketChat/Rocket.Chat/pull/25824)) + +- Regression: Fix assets format ([#26140](https://github.com/RocketChat/Rocket.Chat/pull/26140)) + +- Regression: Fix blackscreen after app install ([#25950](https://github.com/RocketChat/Rocket.Chat/pull/25950)) + + Fixed an error where the client screen would go black after installing an app. This was hapenning because the handleAppAddedOrUpdated function from the AppsProvider had a wrong type for the return of the getAppFromMarketplace function. + + Demo gifs: + + Before + ![app-install-error-before](https://user-images.githubusercontent.com/43561537/174861410-024dff74-b5d9-49ba-ae67-849f1ff239a9.gif) + + After: + ![app-install-error-after](https://user-images.githubusercontent.com/43561537/174861448-58b071e6-8e1b-4391-b49a-44b68249acbf.gif) + +- Regression: Fix breaking omnichannel tests ([#26305](https://github.com/RocketChat/Rocket.Chat/pull/26305)) + +- Regression: Fix call direction ([#26055](https://github.com/RocketChat/Rocket.Chat/pull/26055)) + - Regression: Fix CI monorepo build ([#25107](https://github.com/RocketChat/Rocket.Chat/pull/25107)) - Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window ([#25380](https://github.com/RocketChat/Rocket.Chat/pull/25380)) Fix: livechat room not opening. +- Regression: Fix command previews ([#26199](https://github.com/RocketChat/Rocket.Chat/pull/26199)) + + Fixes slash command previews not being showed + +- Regression: Fix e2e CI ([#25974](https://github.com/RocketChat/Rocket.Chat/pull/25974)) + - Regression: Fix English i18n react text ([#25368](https://github.com/RocketChat/Rocket.Chat/pull/25368)) Incorrect text in reaction tooltip has been fixed +- Regression: Fix extended sidebar item ([#25887](https://github.com/RocketChat/Rocket.Chat/pull/25887)) + - Regression: Fix federation Matrix bridge startup ([#25273](https://github.com/RocketChat/Rocket.Chat/pull/25273)) +- Regression: Fix files list endpoints ([#26226](https://github.com/RocketChat/Rocket.Chat/pull/26226)) + +- Regression: Fix get myself user data ([#26328](https://github.com/RocketChat/Rocket.Chat/pull/26328)) + +- Regression: Fix import endpoints ([#26074](https://github.com/RocketChat/Rocket.Chat/pull/26074)) + +- Regression: Fix job Id not returned by agenda ([#26315](https://github.com/RocketChat/Rocket.Chat/pull/26315)) + +- Regression: Fix marketplace app apis visibility problem ([#26080](https://github.com/RocketChat/Rocket.Chat/pull/26080)) + + Solved a problem that showed an unwanted zero in place of the APIs section for apps that weren't installed/did not have an APIs section. + Before: + ![image](https://user-images.githubusercontent.com/43561537/176743542-8f5d2e97-48e7-4947-a82a-43c3a15ea0ac.png) + + After(non installed app): + ![image](https://user-images.githubusercontent.com/43561537/176744082-0139e15b-b03b-4c03-9267-9a17d14b70e9.png) + + After(installed app) + ![image](https://user-images.githubusercontent.com/43561537/176772870-c5382edc-59e6-42e4-8dfa-f1e4fd0267c0.png) + +- Regression: Fix marketplace releases tab crash bug ([#26162](https://github.com/RocketChat/Rocket.Chat/pull/26162)) + + Fixed a bug where RC would crash because the marketplace releases tab was trying to display undefined data from manually installed apps. + Demo gif: + ![app-releases-tab-crash-error](https://user-images.githubusercontent.com/43561537/177656489-325790d3-49e0-46c8-8ac2-1f74c6a309ad.gif) + +- Regression: Fix micro services ([#26054](https://github.com/RocketChat/Rocket.Chat/pull/26054)) + - Regression: Fix micro services Docker build ([#25193](https://github.com/RocketChat/Rocket.Chat/pull/25193)) - Regression: Fix multi line is not showing an empty line between lines ([#25317](https://github.com/RocketChat/Rocket.Chat/pull/25317)) +- Regression: Fix Omnichannel not working after meteor update ([#26194](https://github.com/RocketChat/Rocket.Chat/pull/26194)) + + Fixed things: + - Omnichannel Directory + - Omnichannel Current Chats + - Auto Selection Algo + - Load Balance Algo + - Manual Selection Algo + - Livechat New Conversations + + Other fixed things: + - Warning on fields deprecation + - Warning on "remove" deprecation + - Remove findAndModify usage + +- Regression: Fix permissions page pagination ([#26304](https://github.com/RocketChat/Rocket.Chat/pull/26304)) + +- Regression: Fix rendered markdown styling on app info page details section ([#26093](https://github.com/RocketChat/Rocket.Chat/pull/26093)) + + Fixed two styling problems on the AppDetails markdown. The first one was a misuse of flex and the second was the fact that the withRichContent flag was missing on the box that received the markdown. + Demo images: + Before: + ![image](https://user-images.githubusercontent.com/43561537/177857346-54476879-2618-452f-8585-1922dcbfa9c1.png) + + After: + ![image](https://user-images.githubusercontent.com/43561537/177857376-e96e4ad3-3410-4847-89b7-df074ff87b2f.png) + + Clickup task: https://app.clickup.com/t/2rwq0q7 + - Regression: Fix reply button not working when hideFlexTab is enabled ([#25306](https://github.com/RocketChat/Rocket.Chat/pull/25306)) +- Regression: Fix routing for public queue and visitor abandonment fiber usage ([#26233](https://github.com/RocketChat/Rocket.Chat/pull/26233)) + +- Regression: Fix scheduler not working ([#26242](https://github.com/RocketChat/Rocket.Chat/pull/26242)) + - Regression: Fix services Docker build on CI ([#25181](https://github.com/RocketChat/Rocket.Chat/pull/25181)) - Regression: Fix services-image-build-check ([#25519](https://github.com/RocketChat/Rocket.Chat/pull/25519)) @@ -819,49 +1657,211 @@ - Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) - The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + + This temporarily switches to a fork of the matrix-appservice-bridge package. + + Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). - This temporarily switches to a fork of the matrix-appservice-bridge package. +- Regression: Fix threads list ([#26052](https://github.com/RocketChat/Rocket.Chat/pull/26052)) - Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). +- Regression: Fix users.create call ([#25834](https://github.com/RocketChat/Rocket.Chat/pull/25834)) + +- Regression: Fix voip call wrap-up model not working ([#26024](https://github.com/RocketChat/Rocket.Chat/pull/26024)) + +- Regression: get user from `req` on `ui.interaction` endpoints ([#26256](https://github.com/RocketChat/Rocket.Chat/pull/26256)) + +- Regression: Inline code and copyonly tag styles ([#26173](https://github.com/RocketChat/Rocket.Chat/pull/26173)) + +- Regression: Invalid Voip host issue preventing agents connecting to asterisk ([#26056](https://github.com/RocketChat/Rocket.Chat/pull/26056)) + +- Regression: Last_login translation key ([#26203](https://github.com/RocketChat/Rocket.Chat/pull/26203)) + +- Regression: Link not scrolling to message ([#26073](https://github.com/RocketChat/Rocket.Chat/pull/26073)) + +- Regression: Livechat not rendering UiKit messages with action buttons ([#26327](https://github.com/RocketChat/Rocket.Chat/pull/26327)) + +- Regression: Livechat rooms not opening due to route desync ([#26209](https://github.com/RocketChat/Rocket.Chat/pull/26209)) + + Due to route information only updating on `Tracker.afterFlush` (https://github.com/RocketChat/Rocket.Chat/pull/25990), we found out that calling the `tabBar.openUserInfo()` method at this point will cause a route change to the previous route instead of the current one, preventing livechat rooms from being opened. + + As a provisory solution, we're delaying the opening of the contextual bar, which then ensures that the route info is up to date. Although this solution works, we need to find a more reliable way of ensuring consistent route changes with up-to-date information. + + ### I'm definitely open for better looking alternatives. Please leave a comment if you have a better solution to share. + +- Regression: Matrix Federation regressions ([#26283](https://github.com/RocketChat/Rocket.Chat/pull/26283)) - Regression: Messages in new message template Crashing. ([#25327](https://github.com/RocketChat/Rocket.Chat/pull/25327)) +- Regression: Meteor uses `projection` for its observes ([#26223](https://github.com/RocketChat/Rocket.Chat/pull/26223)) + - Regression: Missing settings group descriptions ([#25639](https://github.com/RocketChat/Rocket.Chat/pull/25639)) +- Regression: moving Community Watermark to `ee` folder ([#26148](https://github.com/RocketChat/Rocket.Chat/pull/26148)) + + Due to legal reasons, the Watermark used in community Edition was moved to Enterprise folder `ee` + +- Regression: Non-reactive routes ([#25990](https://github.com/RocketChat/Rocket.Chat/pull/25990)) + + When `Tracker.autorun()` calls are nested, it's possible that an invalidation at the parent render the children non-reactive due to synchronous calls. To avoid that under the callback given by `useSyncExternalStore`, we schedule an `onStoreChange` callback call to not make it reside at the same backtrace. + +- Regression: Omni-chats not getting routed automatically to bots ([#26267](https://github.com/RocketChat/Rocket.Chat/pull/26267)) + +- Regression: Options overlapping input in Users Autocomplete ([#26309](https://github.com/RocketChat/Rocket.Chat/pull/26309)) + +- Regression: OTR with new React Messages ([#26179](https://github.com/RocketChat/Rocket.Chat/pull/26179)) + + This PR solves 2 OTR issues with new react message components + + - disable the server side message parser for OTR messages + - adds the stopwatch icon for otr messages + + ### Before + Screenshot 2022-07-08 at 12 58 08 AM + + ### After + Screenshot 2022-07-08 at 12 55 08 AM + +- Regression: Parse outbound phone number removing * putting + char ([#26154](https://github.com/RocketChat/Rocket.Chat/pull/26154)) + +- Regression: Re-add view logs button ([#25876](https://github.com/RocketChat/Rocket.Chat/pull/25876)) + + Re-added the view logs button to the appMenu component so that the user can go directly from the marketplace list of apps to the app info page with the logs tab already open. + Demo gif: + ![re-add-view-logs-button](https://user-images.githubusercontent.com/43561537/173681990-86c8a93c-bb2e-4540-824d-b7fbb3161356.gif) + +- Regression: Remove 4.0 version banner ([#26251](https://github.com/RocketChat/Rocket.Chat/pull/26251)) + + Created a migration to disable and dismiss for all users the old 4.0 version banner. + It happened when a new admin user has been added. + +- Regression: Remove alpha tag and fix initialization process ([#26248](https://github.com/RocketChat/Rocket.Chat/pull/26248)) + +- Regression: remove italic from reaction translation ([#26152](https://github.com/RocketChat/Rocket.Chat/pull/26152)) + +- Regression: Removed CE watermark from VoipFooter ([#26239](https://github.com/RocketChat/Rocket.Chat/pull/26239)) + + The objective of this change is to remove the CE watermark **only** during an active call. The CE watermark will be displayed normally in all other scenarios. Bellow you can see a demonstration of the expected behavior: + + ![ce-watermark-removed-voip](https://user-images.githubusercontent.com/6494543/178615342-8049a2a8-d331-46a9-a8f1-8461ae341b50.gif) + +- Regression: Replace contact center icon ([#26216](https://github.com/RocketChat/Rocket.Chat/pull/26216)) + +- Regression: REST API calls at Engagement Dashboard ([#26235](https://github.com/RocketChat/Rocket.Chat/pull/26235)) + + Parameters for GET requests are *not* serialized as for other methods, therefore sending `Date` objects is not viable due to the way `Date.prototype.toString` works. This PR uses `Date.prototype.toISOString` explicitly to serialize dates. + - Regression: Revert Bugsnag version ([#25313](https://github.com/RocketChat/Rocket.Chat/pull/25313)) +- Regression: Revert Livechat packages upgrades/removals that were causing issues ([#26077](https://github.com/RocketChat/Rocket.Chat/pull/26077)) + +- Regression: Revert replace contact center icon ([#26238](https://github.com/RocketChat/Rocket.Chat/pull/26238)) + +- Regression: Reverting @rocket.chat/mp3-encoder version to fix Audio Message ([#26197](https://github.com/RocketChat/Rocket.Chat/pull/26197)) + + An unknow breaking change (still investigating) happened when upgrading the [@rocket.chat/mp3-encoder](https://github.com/RocketChat/fuselage/tree/develop/packages/mp3-encoder) package to version 0.25.0, because of that we revert the version to 0.24.0 the last know working version. + - Regression: Rocket.Chat Webapp not loading. ([#25349](https://github.com/RocketChat/Rocket.Chat/pull/25349)) +- Regression: Room Endpoint Call Issues ([#25928](https://github.com/RocketChat/Rocket.Chat/pull/25928)) + + This PR fixes small management bugs related with channels, rooms and teams + +- Regression: Search on Member List ([#26273](https://github.com/RocketChat/Rocket.Chat/pull/26273)) + +- Regression: Send files with `enter` key ([#26136](https://github.com/RocketChat/Rocket.Chat/pull/26136)) + +- Regression: Set `offset` and `count` optional on `ChatGetThreadsListSchema` ([#25961](https://github.com/RocketChat/Rocket.Chat/pull/25961)) + - Regression: Show username and real name on the message system ([#25254](https://github.com/RocketChat/Rocket.Chat/pull/25254)) - Regression: Shows error if micro service cannot connect to Mongo ([#25301](https://github.com/RocketChat/Rocket.Chat/pull/25301)) +- Regression: Sidebar icons spacing ([#26139](https://github.com/RocketChat/Rocket.Chat/pull/26139)) + + - Fixed the sidebar icons ('display' and 'create new') spacing issue + + before: + ![image](https://user-images.githubusercontent.com/5263975/178897210-50615ea9-28d5-4b35-a93a-c5facea365e5.png) + + + + after: + + ![image](https://user-images.githubusercontent.com/5263975/178896945-1bf71112-8a01-4db6-9f9b-20ea778496f7.png) + +- Regression: Special characters on phone number ([#26241](https://github.com/RocketChat/Rocket.Chat/pull/26241)) + + PR Includes: + - Keep focus on phone input of dial pad + - Handle submit with "Enter" key + - Remove mask and mandatory "+" char + - Long press for "0"/"+" button + - Regression: Subscription menu not appearing for non installed but subscribed apps ([#25627](https://github.com/RocketChat/Rocket.Chat/pull/25627)) - Fixed a problem on which the AppMenu component did not appear for apps that had an active subscription but weren't installed, now the rendering of the component is also based on the isSubscribed flag, and the appearance of the uninstall and enable/disable options are based on the app.installed flag so that the correct options appear on all the edge cases. - Demo gif: + Fixed a problem on which the AppMenu component did not appear for apps that had an active subscription but weren't installed, now the rendering of the component is also based on the isSubscribed flag, and the appearance of the uninstall and enable/disable options are based on the app.installed flag so that the correct options appear on all the edge cases. + Demo gif: ![subscription-manager-fix](https://user-images.githubusercontent.com/43561537/170132040-dc8535c0-8056-4fb2-b008-afaece744868.gif) +- Regression: TOTP Modal with new rest api package ([#25893](https://github.com/RocketChat/Rocket.Chat/pull/25893)) + +- Regression: UIKit buttons auth user validation ([#26171](https://github.com/RocketChat/Rocket.Chat/pull/26171)) + + Fix the validation to match the new feature that allows apps to register auth-required routes. + +- Regression: Unable to click on UiKit buttons provided by apps ([#26125](https://github.com/RocketChat/Rocket.Chat/pull/26125)) + +- Regression: Unable to edit user details via admin panel ([#25923](https://github.com/RocketChat/Rocket.Chat/pull/25923)) + +- Regression: Unavailable devices in unsupported browsers ([#26174](https://github.com/RocketChat/Rocket.Chat/pull/26174)) + +- Regression: Unhandled Exceptions metric causing a secondary exception ([#26088](https://github.com/RocketChat/Rocket.Chat/pull/26088)) + +- Regression: Update error message on `useEndpointActionExperimental` ([#26062](https://github.com/RocketChat/Rocket.Chat/pull/26062)) + + This PR changes the way we show an error message to the user on the `useEndpointActionExperimental` hook, previously for `Object` error messages it was being shown as undefined + +- Regression: Update message reaction text ([#26097](https://github.com/RocketChat/Rocket.Chat/pull/26097)) + - Regression: Update settings groups description ([#25663](https://github.com/RocketChat/Rocket.Chat/pull/25663)) - Regression: Use exact Node version on micro services Docker images ([#25287](https://github.com/RocketChat/Rocket.Chat/pull/25287)) +- Regression: Use fname instead real unique name for Voip ([#26319](https://github.com/RocketChat/Rocket.Chat/pull/26319)) + + Affect: + - Voip room header + - Contacts table + - Contact info + +- Regression: UserInfo/RoomInfo Menu ([#26252](https://github.com/RocketChat/Rocket.Chat/pull/26252)) + + **note**: next fuselage's version needed + + #### before + ![Screen Shot 2022-07-13 at 12 24 38](https://user-images.githubusercontent.com/27704687/178771262-d482b300-de80-4961-be2e-8c034480d237.png) + + #### after + ![Screen Shot 2022-07-13 at 12 25 39](https://user-images.githubusercontent.com/27704687/178771460-db10883b-aa6d-4254-82d4-8cadd6991ae8.png) + +- Regression: Users on new sessions are forced to re-configure 2fa ([#26117](https://github.com/RocketChat/Rocket.Chat/pull/26117)) + +- Regression: Users Table loading state ([#26079](https://github.com/RocketChat/Rocket.Chat/pull/26079)) + - Regression: Validate empty fields for Message template ([#25250](https://github.com/RocketChat/Rocket.Chat/pull/25250)) - Regression: VoIp wrap up modal not opening after call disconnect ([#25651](https://github.com/RocketChat/Rocket.Chat/pull/25651)) This PR fixes a bug preventing the wrap up call modal from being displayed after caller or agent ends the call. -- Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208)) - -- Release 4.7.0 ([#25390](https://github.com/RocketChat/Rocket.Chat/pull/25390) +- Regression: Webhook Integration Creation + string error toast msg ([#26008](https://github.com/RocketChat/Rocket.Chat/pull/26008)) -- Release 4.7.1 ([#25510](https://github.com/RocketChat/Rocket.Chat/pull/25510)) +- Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208)) -- Release 4.7.2 ([#25580](https://github.com/RocketChat/Rocket.Chat/pull/25580)) +- Revert: "Chore: Collect e2e coverage" ([#25936](https://github.com/RocketChat/Rocket.Chat/pull/25936)) - Test: Migrate 13-permissions from cypress to playwright ([#25558](https://github.com/RocketChat/Rocket.Chat/pull/25558)) @@ -869,23 +1869,32 @@ ### 👩‍💻👨‍💻 Contributors 😍 +- [@BenWiederhake](https://github.com/BenWiederhake) - [@Himanshu664](https://github.com/Himanshu664) +- [@Hudell](https://github.com/Hudell) +- [@Kunalvrm555](https://github.com/Kunalvrm555) +- [@Sh0uld](https://github.com/Sh0uld) - [@aakash-gitdev](https://github.com/aakash-gitdev) - [@cuonghuunguyen](https://github.com/cuonghuunguyen) - [@dependabot[bot]](https://github.com/dependabot[bot]) - [@divinespear](https://github.com/divinespear) - [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@felipe-menelau](https://github.com/felipe-menelau) +- [@g-thome](https://github.com/g-thome) +- [@joakimaho](https://github.com/joakimaho) - [@kibonusp](https://github.com/kibonusp) -- [@lingohub[bot]](https://github.com/lingohub[bot]) -- [@nishant23122000](https://github.com/nishant23122000) +- [@kodiakhq[bot]](https://github.com/kodiakhq[bot]) +- [@matthias4217](https://github.com/matthias4217) - [@ostjen](https://github.com/ostjen) - [@paulobernardoaf](https://github.com/paulobernardoaf) - [@sidmohanty11](https://github.com/sidmohanty11) +- [@ujorgeleite](https://github.com/ujorgeleite) ### 👩‍💻👨‍💻 Core Team 🤓 - [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) - [@KevLehman](https://github.com/KevLehman) +- [@LucianoPierdona](https://github.com/LucianoPierdona) - [@MarcosSpessatto](https://github.com/MarcosSpessatto) - [@MartinSchoeler](https://github.com/MartinSchoeler) - [@PedroRorato](https://github.com/PedroRorato) @@ -895,10 +1904,11 @@ - [@amolghode1981](https://github.com/amolghode1981) - [@carlosrodrigues94](https://github.com/carlosrodrigues94) - [@cauefcr](https://github.com/cauefcr) +- [@csuadev](https://github.com/csuadev) - [@d-gubert](https://github.com/d-gubert) - [@debdutdeb](https://github.com/debdutdeb) - [@dougfabris](https://github.com/dougfabris) -- [@felipe-menelau](https://github.com/felipe-menelau) +- [@dudanogueira](https://github.com/dudanogueira) - [@felipe-rod123](https://github.com/felipe-rod123) - [@filipemarins](https://github.com/filipemarins) - [@gabriellsh](https://github.com/gabriellsh) @@ -912,6 +1922,7 @@ - [@matheusbsilva137](https://github.com/matheusbsilva137) - [@matheuslc](https://github.com/matheuslc) - [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) - [@rique223](https://github.com/rique223) - [@rodrigok](https://github.com/rodrigok) @@ -919,14 +1930,14 @@ - [@souzaramon](https://github.com/souzaramon) - [@tapiarafael](https://github.com/tapiarafael) - [@tassoevan](https://github.com/tassoevan) +- [@thassiov](https://github.com/thassiov) - [@tiagoevanp](https://github.com/tiagoevanp) - [@tmontini](https://github.com/tmontini) -- [@ujorgeleite](https://github.com/ujorgeleite) - [@weslley543](https://github.com/weslley543) - [@yash-rajpal](https://github.com/yash-rajpal) -# 4.7.4 -`2022-05-30 · 1 🐛 · 1 🔍 · 2 👩‍💻👨‍💻` +# 4.8.2 +`2022-07-21 · 4 🐛 · 3 🔍 · 7 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -936,40 +1947,43 @@ ### 🐛 Bug fixes +- Error "numRequestsAllowed" property in rateLimiter for REST API endpoint when upgrading ([#26058](https://github.com/RocketChat/Rocket.Chat/pull/26058)) + +- Not showing edit message button when blocking edit after N minutes ([#25724](https://github.com/RocketChat/Rocket.Chat/pull/25724) by [@matthias4217](https://github.com/matthias4217)) + + Previously, in Rocketchat 4.7.0 and later, as mentioned in https://github.com/RocketChat/Rocket.Chat/issues/25478, the edit button was not displayed on the interface in the minute after having sent a message. This is now fixed : messages can be edited right after sending them. + - Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) +- Settings not being overwritten to their default values ([#25891](https://github.com/RocketChat/Rocket.Chat/pull/25891)) +
🔍 Minor changes -- Load missed messages from opened rooms when reconnect ([#553](https://github.com/RocketChat/Rocket.Chat/pull/553)) +- Chore: Avoid unneeded permission updates when EE license is applied ([#26253](https://github.com/RocketChat/Rocket.Chat/pull/26253)) -
+- Release 4.8.1 ([#25814](https://github.com/RocketChat/Rocket.Chat/pull/25814) by [@Sh0uld](https://github.com/Sh0uld)) -### 👩‍💻👨‍💻 Core Team 🤓 - -- [@ggazzo](https://github.com/ggazzo) -- [@rodrigok](https://github.com/rodrigok) - -# 4.7.3 -`2022-05-20 · 1 🐛 · 1 👩‍💻👨‍💻` - -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Release 4.8.2 ([#26326](https://github.com/RocketChat/Rocket.Chat/pull/26326) by [@matthias4217](https://github.com/matthias4217)) -### 🐛 Bug fixes +
+### 👩‍💻👨‍💻 Contributors 😍 -- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) +- [@Sh0uld](https://github.com/Sh0uld) +- [@matthias4217](https://github.com/matthias4217) ### 👩‍💻👨‍💻 Core Team 🤓 +- [@KevLehman](https://github.com/KevLehman) +- [@dudanogueira](https://github.com/dudanogueira) - [@ggazzo](https://github.com/ggazzo) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tiagoevanp](https://github.com/tiagoevanp) -# 4.7.2 -`2022-05-20 · 5 🐛 · 1 🔍 · 6 👩‍💻👨‍💻` +# 4.8.1 +`2022-06-08 · 4 🐛 · 5 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -979,39 +1993,30 @@ ### 🐛 Bug fixes -- Dynamic load matrix is enabled and handle failure ([#25495](https://github.com/RocketChat/Rocket.Chat/pull/25495)) - -- Initial User not added to default channel ([#25544](https://github.com/RocketChat/Rocket.Chat/pull/25544)) - - If injecting initial user. The user wasn’t added to the default General channel - -- One of the triggers was not working correctly ([#25409](https://github.com/RocketChat/Rocket.Chat/pull/25409)) - -- UI/UX issues on Live Chat widget ([#25407](https://github.com/RocketChat/Rocket.Chat/pull/25407)) +- AccountBox checks for condition ([#25708](https://github.com/RocketChat/Rocket.Chat/pull/25708)) -- User abandonment setting was not working doe to failing event hook ([#25520](https://github.com/RocketChat/Rocket.Chat/pull/25520)) +- Bump meteor-node-stubs to version 1.2.3 ([#25669](https://github.com/RocketChat/Rocket.Chat/pull/25669) by [@Sh0uld](https://github.com/Sh0uld)) - A setting watcher and the query for grabbing abandoned chats were broken, now they're not. + With meteor-node-stubs version 1.2.3 a bug was fixed, which occured in issue #25460 and probably #25513 (last one not tested). + For the issue in meteor see: https://github.com/meteor/meteor/issues/11974 -
-🔍 Minor changes +- Fix prom-client new promise usage ([#25781](https://github.com/RocketChat/Rocket.Chat/pull/25781)) +- Wrong argument name preventing Omnichannel Chat Forward to User ([#25723](https://github.com/RocketChat/Rocket.Chat/pull/25723)) -- Chore: Add Livechat repo into Monorepo packages ([#25312](https://github.com/RocketChat/Rocket.Chat/pull/25312)) +### 👩‍💻👨‍💻 Contributors 😍 -
+- [@Sh0uld](https://github.com/Sh0uld) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@MartinSchoeler](https://github.com/MartinSchoeler) -- [@cauefcr](https://github.com/cauefcr) -- [@dougfabris](https://github.com/dougfabris) -- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@KevLehman](https://github.com/KevLehman) +- [@dudanogueira](https://github.com/dudanogueira) - [@ggazzo](https://github.com/ggazzo) - [@tiagoevanp](https://github.com/tiagoevanp) -# 4.7.1 -`2022-05-13 · 1 🎉 · 2 🐛 · 3 👩‍💻👨‍💻` +# 4.8.0 +`2022-05-31 · 16 🎉 · 13 🚀 · 55 🐛 · 151 🔍 · 52 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -1021,94 +2026,180 @@ ### 🎉 New features -- Use setting to determine if initial general channel is needed ([#25441](https://github.com/RocketChat/Rocket.Chat/pull/25441)) - - - Adds flag responsible for overwriting #general channel creation - -### 🐛 Bug fixes - - -- LDAP sync removing users from channels when multiple groups are mapped to it ([#25434](https://github.com/RocketChat/Rocket.Chat/pull/25434)) - -- Spotlight results showing usernames instead of real names ([#25471](https://github.com/RocketChat/Rocket.Chat/pull/25471)) - -### 👩‍💻👨‍💻 Core Team 🤓 +- Ability for RC server to check the business hour for a specific department ([#25436](https://github.com/RocketChat/Rocket.Chat/pull/25436)) -- [@felipe-menelau](https://github.com/felipe-menelau) -- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) -- [@sampaiodiego](https://github.com/sampaiodiego) +- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) -# 4.7.0 -`2022-05-04 · 4 🎉 · 7 🚀 · 33 🐛 · 69 🔍 · 35 👩‍💻👨‍💻` +- Add new app events for pin, react and follow message ([#25337](https://github.com/RocketChat/Rocket.Chat/pull/25337)) -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Add new events after user login, logout and change his status ([#25234](https://github.com/RocketChat/Rocket.Chat/pull/25234)) -### 🎉 New features +- Add option to show mentions badge when show counter is disabled ([#25329](https://github.com/RocketChat/Rocket.Chat/pull/25329)) +- Add user events for apps ([#25165](https://github.com/RocketChat/Rocket.Chat/pull/25165)) -- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) +- Adding app button on user dropdown ([#25326](https://github.com/RocketChat/Rocket.Chat/pull/25326)) - Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) - Experimental support for Matrix Federation with a Bridge - + Experimental support for Matrix Federation with a Bridge + https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 - Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) -- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) +- Federation (Alpha Stabilization) ([#25457](https://github.com/RocketChat/Rocket.Chat/pull/25457)) - Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). +- Get user's preferred language via apps ([#25514](https://github.com/RocketChat/Rocket.Chat/pull/25514)) +- Marketplace new app details page ([#24711](https://github.com/RocketChat/Rocket.Chat/pull/24711)) + Change the app details page layout for the new marketplace UI. General Task: [MKP12 - New UI - App Detail Page](https://app.clickup.com/t/1na769h) + + ## [MKP12 - Tab Navigation](https://app.clickup.com/t/2452f5u) + New tab navigation layout for the app details page. Now the app details page is divided into three sections, details, logs, and settings, that can each be accessed through a Tabs fuselage component. + + Demo gif: + ![tab_navigation_demo_gif](https://user-images.githubusercontent.com/43561537/157276436-3dab34c5-20da-4f5d-99d0-54c1c718ac1f.gif) + + ## [MKP12 - Header](https://app.clickup.com/t/25rhm0x) + Implemented a new header for the marketplaces app details page. + -Changed the size of the app name; + -Implemented the app description field on the header; + -Changed the "metadata" section of the header(The part with the version and author information) now it also shows the last time the app was updated; + -Created a chip that will show when an app is part of one or more bundles and inform which are the bundles; + -Implemented a tooltip for the bundle chips; + -Created a new button + data badge component to substitute the current App Status; + -Changed the title of the "purchase button". Now it shows different text based on the "purchase type" of the app; + -Created a new Pricing & Status display which shows the price when the app is not bought/installed and shows the app status(Enabled/Disabled) when it is bought/installed; + -Changed the way the tabs are rendered, now if the app is not installed(and consequently doesn't have logs and settings tab) it will not render these tabs; + + Demo gif: + ![new-header-gif](https://user-images.githubusercontent.com/43561537/159064599-fd64dfe2-86a3-47da-81ba-1e83f1b87432.gif) + + ## [MKP12 - Configuration Tab](https://app.clickup.com/t/2452gh4) + Delivered together with the tab-navigation task. Changed the app settings from the details of the app to the new settings tab. + Demo image: + ![New configuration tab](https://user-images.githubusercontent.com/43561537/160211324-95db0566-85bf-4dde-a814-3c6f23dcee4d.png) + + ## [MKP12 - Log Tab](https://app.clickup.com/t/2452gg1) + Changed the place of the app logs from the page to the new logs tab. Also changed some styles of the logs accordions to fit better with the new container. + + Before: + ![Before](https://user-images.githubusercontent.com/43561537/160210302-148ce584-604f-40ff-8209-141667016163.png) + + After + ![After](https://user-images.githubusercontent.com/43561537/160210984-d4060c5a-f912-4ef9-87e3-fa459080e2d4.png) + + ## [MKP12 - Page Header](https://app.clickup.com/t/29b0b12) + Changed the design for the page header of the app details page from a title on the left with a save and back button on the right to a back arrow icon on the left side of the title with the save button still on the right. Also changed the title of the page from App details to Back. + Edit: After some design reconsideration, the page title was changed to App Info. + Demo gif: + ![new_page_header_app_details](https://user-images.githubusercontent.com/43561537/160937741-f5514f70-f43b-4400-8b2f-a5a26f95de9d.gif) + + ## [MKP12 - Detail Tab](https://app.clickup.com/t/2452gf7) + Implemented markdown on the description section of the app details page, now the description will show the detailedDescription.rendered (as rendered JSX) information in case it exists and show the description (a.k.a. short description) information in case it doesn't. Unfortunately, as of right now no app has a visual example of a markdown description and because of that, I will not be able to provide a demo image/gif for this PR. + + ## [MKP12 - Slider Component](https://app.clickup.com/t/2452h26) + Created an image carousel component on the app details page. This component receives images from the apps/appId/screenshots endpoint and shows them on the content section of the app details of any apps that have screenshots registered, if the app has no screenshots it simply shows nothing where the carousel should be. This component is complete with keyboard arrow navigation on the "open" carousel, hover highlight on the carousel preview and close on esc press. + Demo gif: + ![new_carousel_component](https://user-images.githubusercontent.com/43561537/167415212-9d8359c7-4132-4afa-a698-8be4ab1e1393.gif) - ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) - In case you encounter any problems, or want to compare, temporarily it is possible to use the old version +- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) + Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). + + + + ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) + In case you encounter any problems, or want to compare, temporarily it is possible to use the old version + image -### 🚀 Improvements +- New button for network outage ([#25499](https://github.com/RocketChat/Rocket.Chat/pull/25499)) + When network outage happens it should be conveyed to the user with special icon. This icon should not be clickable. + Network outage handling is handled in https://app.clickup.com/t/245c0d8 task. -- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) +- New stats rewrite ([#25078](https://github.com/RocketChat/Rocket.Chat/pull/25078) by [@ostjen](https://github.com/ostjen)) + + Add the following new statistics (**metrics**): + + - Total users with TOTP enabled; + - Total users with 2FA enabled; + - Total pinned messages; + - Total starred messages; + - Total email messages; + - Total rooms with at least one starred message; + - Total rooms with at least one pinned message; + - Total encrypted rooms; + - Total link invitations; + - Total email invitations; + - Logo change; + - Number of custom script lines; + - Number of custom CSS lines; + - Number of rooms inside teams; + - Number of default (auto-join) rooms inside teams; + - Number of users created through link invitation; + - Number of users created through manual entry; + - Number of imported users (by import type); - Earlier OTR room uses only 2 states, we need more states to support future features. - This adds more states for the OTR contextualBar. +- Star message, report and delete message events ([#25383](https://github.com/RocketChat/Rocket.Chat/pull/25383)) +### 🚀 Improvements - - Expired - Screen Shot 2022-04-20 at 13 55 52 +- **ENTERPRISE:** Allow mapping LDAP groups to multiple RC roles ([#23849](https://github.com/RocketChat/Rocket.Chat/pull/23849)) - - Declined - Screen Shot 2022-04-20 at 13 49 28 + - Add support to mapping LDAP groups to multiple roles (by specifying arrays in the "User Data Group Map" enterprise setting. +- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) - - Error + Earlier OTR room uses only 2 states, we need more states to support future features. + This adds more states for the OTR contextualBar. + + - Expired + Screen Shot 2022-04-20 at 13 55 52 + + - Declined + Screen Shot 2022-04-20 at 13 49 28 + + - Error Screen Shot 2022-04-20 at 13 55 26 - Add tooltip to sidebar room menu ([#24405](https://github.com/RocketChat/Rocket.Chat/pull/24405) by [@Himanshu664](https://github.com/Himanshu664)) +- add warnings for federation setup ([#25684](https://github.com/RocketChat/Rocket.Chat/pull/25684)) + - Added MaxNickNameLength and MaxBioLength constants ([#25231](https://github.com/RocketChat/Rocket.Chat/pull/25231) by [@aakash-gitdev](https://github.com/aakash-gitdev)) - Added tooltip options for message menu ([#24431](https://github.com/RocketChat/Rocket.Chat/pull/24431) by [@Himanshu664](https://github.com/Himanshu664)) +- Fix multiple bugs with Matrix bridge ([#25318](https://github.com/RocketChat/Rocket.Chat/pull/25318)) + - Improve active/hover colors in account sidebar ([#25024](https://github.com/RocketChat/Rocket.Chat/pull/25024) by [@Himanshu664](https://github.com/Himanshu664)) +- New admin settings Page ([#25439](https://github.com/RocketChat/Rocket.Chat/pull/25439)) + + ![Screen Shot 2022-05-09 at 11 31 58](https://user-images.githubusercontent.com/27704687/167432811-f4970f23-5dae-48a0-a427-92269d08a859.png) + +- Pass allowDiskUse to channel aggregations on engagement dashboard ([#22374](https://github.com/RocketChat/Rocket.Chat/pull/22374)) + - Performance for some Omnichannel features ([#25217](https://github.com/RocketChat/Rocket.Chat/pull/25217)) - Rename upgrade tab routes ([#25097](https://github.com/RocketChat/Rocket.Chat/pull/25097)) Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test. +- Unify voip streams into single stream ([#25108](https://github.com/RocketChat/Rocket.Chat/pull/25108)) + ### 🐛 Bug fixes - Add katex render to new message react template ([#25239](https://github.com/RocketChat/Rocket.Chat/pull/25239)) +- Add open user card to user avatar ([#25445](https://github.com/RocketChat/Rocket.Chat/pull/25445)) + - Add reaction not working in legacy messages ([#25222](https://github.com/RocketChat/Rocket.Chat/pull/25222)) - Added invalid password error message ([#24714](https://github.com/RocketChat/Rocket.Chat/pull/24714) by [@Himanshu664](https://github.com/Himanshu664)) @@ -1119,45 +2210,52 @@ - AgentOverview analytics wrong departmentId parameter ([#25073](https://github.com/RocketChat/Rocket.Chat/pull/25073) by [@paulobernardoaf](https://github.com/paulobernardoaf)) - When filtering the analytics charts by department, data would not appear because the object: - ```js - { - value: "department-id", - label: "department-name" - } - ``` - was being used in the `departmentId` parameter. - - - - Before: - ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) - - - - After: + When filtering the analytics charts by department, data would not appear because the object: + ```js + { + value: "department-id", + label: "department-name" + } + ``` + was being used in the `departmentId` parameter. + + - Before: + ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) + + - After: ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) -- Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) - - Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online - unless agent explicitly logs off. - Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. +- Change form body parameter charset to UTF-8 to fix issue #25456 ([#25673](https://github.com/RocketChat/Rocket.Chat/pull/25673) by [@divinespear](https://github.com/divinespear)) - 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off - in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + since [mscdex/busboy](https://github.com/mscdex/busboy) 1.5.0, new option named `defParamCharset` for form body parameter encoding is added with default value `latin1`, so unicode filenames are broken since 4.7.0. + + ![Screenshot from 2022-05-28 16-26-06](https://user-images.githubusercontent.com/126630/170815447-1f3bd579-243a-42d3-86f6-814aeaa30ce9.png) - 2. Second reason is when computer goes in sleep mode. +- Change NPS Vote identifier + nps index to unique ([#25423](https://github.com/RocketChat/Rocket.Chat/pull/25423)) - 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. +- Click to join button Jitsi Call ([#25569](https://github.com/RocketChat/Rocket.Chat/pull/25569)) - Solution: - The idea is to detect the network disconnection and start the start the attempts to reconnect. - The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not - call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are - used. + Added `ToolboxProvider` to `MessageListProvider` and fixed actionLink.js open function exec - The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to - reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. +- Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) + Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online + unless agent explicitly logs off. + Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. + 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off + in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + 2. Second reason is when computer goes in sleep mode. + 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. + + Solution: + The idea is to detect the network disconnection and start the start the attempts to reconnect. + The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not + call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are + used. + + The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to + reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. + When the server is disconnected, it should be indicated on the phone button. - Close room when dismiss wrap up call modal ([#25056](https://github.com/RocketChat/Rocket.Chat/pull/25056)) @@ -1166,18 +2264,34 @@ - Deactivating user breaks if user is the only room owner ([#24933](https://github.com/RocketChat/Rocket.Chat/pull/24933) by [@sidmohanty11](https://github.com/sidmohanty11)) - ## Before + ## Before + + https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 + + + + ## After + https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 - https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 +- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) +- Failure to update Integration History index ([#25473](https://github.com/RocketChat/Rocket.Chat/pull/25473)) - ## After - https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 +- Fix max-width message block ([#25686](https://github.com/RocketChat/Rocket.Chat/pull/25686)) -- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- Fixing app contextual bar functionality ([#25615](https://github.com/RocketChat/Rocket.Chat/pull/25615)) -- End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) +- Fixing Network connectivity issues with SIP client. ([#25391](https://github.com/RocketChat/Rocket.Chat/pull/25391)) + + The previous PR https://github.com/RocketChat/Rocket.Chat/pull/25170 did not handle the issues completely. + This PR is expected to handle + 1. Clearing call related UI when the network is disconnected or switched. + 2. Do clean connectivity. There were few issues discovered in earlier implementation. e.g endpoint would randomly + get disconnected after a while. This was due to the fact that the earlier socket disconnection caused the + removal of contact on asterisk. This should be fixed in this PR. + 3. This PR contains a lot of logs. This will be removed before the final merge. - FormData uploads not working ([#25069](https://github.com/RocketChat/Rocket.Chat/pull/25069)) @@ -1185,36 +2299,57 @@ - Incorrect websocket url in livechat widget ([#25261](https://github.com/RocketChat/Rocket.Chat/pull/25261)) +- Integrations avatar attribute misuse ([#25283](https://github.com/RocketChat/Rocket.Chat/pull/25283)) + - Invitation links don't redirect to the registration form ([#25082](https://github.com/RocketChat/Rocket.Chat/pull/25082)) - Message menu action not working on legacy messages. ([#25148](https://github.com/RocketChat/Rocket.Chat/pull/25148)) +- Message menu dropdown not working on Mobile Web ([#25616](https://github.com/RocketChat/Rocket.Chat/pull/25616)) + - Message preview not available for queued chats ([#25092](https://github.com/RocketChat/Rocket.Chat/pull/25092)) - NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) -- Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) +- Ordered and unordered list styles, Line breaks. ([#25494](https://github.com/RocketChat/Rocket.Chat/pull/25494)) + + Also removed the message.md cache from server, since changes in the parser might break messages in the future (and will in this specific case). + +- Pinned Message display cutting off information ([#25535](https://github.com/RocketChat/Rocket.Chat/pull/25535)) + +- Prevent federation crash on invite users as a non-owner user ([#25683](https://github.com/RocketChat/Rocket.Chat/pull/25683)) - ### before - Screen Shot 2022-03-29 at 13 35 56 +- Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) - ### after + ### before + Screen Shot 2022-03-29 at 13 35 56 + + ### after Screen Shot 2022-03-29 at 11 48 05 - Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) Modify Meteor's `HTTP.call` to add back proxy support +- Quote message spacing ([#25613](https://github.com/RocketChat/Rocket.Chat/pull/25613)) + - Read receipts show with color gray when not read yet ([#25244](https://github.com/RocketChat/Rocket.Chat/pull/25244)) - Read receipts showing before message read ([#25216](https://github.com/RocketChat/Rocket.Chat/pull/25216)) -- Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) +- Remove 'total' text in admin info page ([#25638](https://github.com/RocketChat/Rocket.Chat/pull/25638)) + + - Remove initial 'total' text from rooms and messages groups in the admin info page + - Add 'total' before 'rooms' and 'messages' title on the same section. To use the new 'Total Rooms', was created a new key in the en.i18n.json file. + +- Removing user also removes them from Omni collections ([#25444](https://github.com/RocketChat/Rocket.Chat/pull/25444)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) +- Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) + + ### after Screenshot 2022-01-13 at 8 57 47 PM - Reply button behavior on broadcast channel ([#25175](https://github.com/RocketChat/Rocket.Chat/pull/25175)) @@ -1223,51 +2358,75 @@ - room creation fails if app framework is disabled ([#25200](https://github.com/RocketChat/Rocket.Chat/pull/25200)) -- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007) by [@nishant23122000](https://github.com/nishant23122000)) +- Rooms' names turn lower case on CSV import ([#24612](https://github.com/RocketChat/Rocket.Chat/pull/24612)) + + * Change 'Settings' import to not get cached configs + * Remove update `UI_Allow_room_names_with_special_chars` value + +- Sanitize customUserStatus and fix infinite loop ([#25449](https://github.com/RocketChat/Rocket.Chat/pull/25449)) + + ### Additional improves: + - usage of RHF to avoid unnecessary Add and Edit components separately and form validation + - usage of `GenericTableV2` and some hooks to avoid unnecessary code + - fix `IUserStatus` type + - improves in UI design + - improves **empty** and **loading** state + - improves files structure + + [LOOP ERROR ATTACHMENT] + ![Screen Shot 2022-05-09 at 19 42 53](https://user-images.githubusercontent.com/27704687/167510439-1980461c-a885-46d2-9a49-79da432c7521.png) + +- Settings listeners not receiving overwritten values from env vars ([#25448](https://github.com/RocketChat/Rocket.Chat/pull/25448)) + +- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007)) https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 - Toolbox hiding under contextual bar ([#25237](https://github.com/RocketChat/Rocket.Chat/pull/25237)) +- Unable to see channel member list by authorized channel roles ([#25412](https://github.com/RocketChat/Rocket.Chat/pull/25412)) + +- Upgrade tab loader in incorrect position ([#25398](https://github.com/RocketChat/Rocket.Chat/pull/25398)) + + - Add invisible prop to iframe when loading state is active. + - Upgrade Tab showing for a split second ([#25050](https://github.com/RocketChat/Rocket.Chat/pull/25050)) - Use correct room property for call ended at ([#24932](https://github.com/RocketChat/Rocket.Chat/pull/24932)) -- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) +- useCurrentChatTags is not a function ([#25604](https://github.com/RocketChat/Rocket.Chat/pull/25604)) - ### before - ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) +- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) - ### after + ### before + ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) + + ### after ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) - UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) - - Rewrites the component to TS - - - Fixes some visual issues - - ### before - ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) - - ### after + - Rewrites the component to TS + - Fixes some visual issues + + ### before + ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) + + ### after ![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png) - Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) - VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) - Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) - - It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. - - Solution: - - - 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. - - 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. - + Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) + + It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + + Solution: + + 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. 3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler.
@@ -1286,10 +2445,16 @@ - Bump template-file from 6.0.0 to 6.0.1 ([#25002](https://github.com/RocketChat/Rocket.Chat/pull/25002) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) +- Chore: Add /v1/video-conference endpoint types ([#25278](https://github.com/RocketChat/Rocket.Chat/pull/25278)) + +- Chore: Add channel endpoints (rest-typings) ([#25279](https://github.com/RocketChat/Rocket.Chat/pull/25279)) + +- Chore: Add client folder to CODEOWNERS ([#25397](https://github.com/RocketChat/Rocket.Chat/pull/25397)) - Not crash the whole application if something goes wrong in the MessageList component. +- Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) + Not crash the whole application if something goes wrong in the MessageList component. + ![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png) - Chore: Add options to debug stdout and rate limiter ([#25336](https://github.com/RocketChat/Rocket.Chat/pull/25336)) @@ -1298,81 +2463,259 @@ See title +- Chore: Add typings for /v1/webdav.getMyAccounts ([#25276](https://github.com/RocketChat/Rocket.Chat/pull/25276)) + - Chore: Add yarn plugin to check node and yarn version ([#25224](https://github.com/RocketChat/Rocket.Chat/pull/25224)) +- Chore: bump fuselage ([#25605](https://github.com/RocketChat/Rocket.Chat/pull/25605)) + - Chore: Bump fuselage ([#25371](https://github.com/RocketChat/Rocket.Chat/pull/25371)) - Chore: Bump Fuselage packages ([#25259](https://github.com/RocketChat/Rocket.Chat/pull/25259)) - Chore: Cancel running jobs if PR is updated ([#24708](https://github.com/RocketChat/Rocket.Chat/pull/24708)) -- Chore: Convert admin custom sound to tsx ([#25128](https://github.com/RocketChat/Rocket.Chat/pull/25128)) +- Chore: Chore add validation option to rest endpoints ([#25443](https://github.com/RocketChat/Rocket.Chat/pull/25443)) -- Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) +- Chore: Code Improvements for #25391 ([#25606](https://github.com/RocketChat/Rocket.Chat/pull/25606)) -- Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) +- Chore: Convert `UserStatusMenu` to TS ([#25265](https://github.com/RocketChat/Rocket.Chat/pull/25265)) -- Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) +- Chore: Convert additionalForms ([#25586](https://github.com/RocketChat/Rocket.Chat/pull/25586)) -- Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) +- Chore: Convert Admin -> Rooms to TS ([#25348](https://github.com/RocketChat/Rocket.Chat/pull/25348)) -- Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) +- Chore: Convert admin custom sound to tsx ([#25128](https://github.com/RocketChat/Rocket.Chat/pull/25128)) - - data and test-failure should be ignored +- Chore: Convert Admin/OAuthApps to TS ([#25277](https://github.com/RocketChat/Rocket.Chat/pull/25277)) - - ensure scripts use cross-env + - Converts Admin/OAuthApps to TS. + - migrated forms to react-hook-form -- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) +- Chore: Convert AdminSideBar to ts ([#25372](https://github.com/RocketChat/Rocket.Chat/pull/25372)) -- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) +- Chore: Convert apps/meteor/client/components/UserAutoComplete ([#25554](https://github.com/RocketChat/Rocket.Chat/pull/25554)) -- Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) +- Chore: Convert apps/meteor/client/views/admin/settings ([#25565](https://github.com/RocketChat/Rocket.Chat/pull/25565)) -- Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) +- Chore: Convert apps/meteor/client/views/admin/settings/inputs folder ([#25427](https://github.com/RocketChat/Rocket.Chat/pull/25427)) -- Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) +- Chore: Convert AutoTranslate ([#25591](https://github.com/RocketChat/Rocket.Chat/pull/25591)) -- Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) +- Chore: Convert client/views/admin/settings/groups folder to ts ([#25345](https://github.com/RocketChat/Rocket.Chat/pull/25345)) -- Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) +- Chore: Convert Create Channel ([#25589](https://github.com/RocketChat/Rocket.Chat/pull/25589)) -- Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) +- Chore: Convert customSounds folder to ts ([#25274](https://github.com/RocketChat/Rocket.Chat/pull/25274)) -- Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) +- Chore: Convert customUserStatus folder to ts ([#25288](https://github.com/RocketChat/Rocket.Chat/pull/25288)) -- Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) +- Chore: Convert email inbox feature to TypeScript ([#25298](https://github.com/RocketChat/Rocket.Chat/pull/25298) by [@ujorgeleite](https://github.com/ujorgeleite)) -- Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) +- Chore: Convert federationDashboard folder to ts ([#25343](https://github.com/RocketChat/Rocket.Chat/pull/25343)) - Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore +- Chore: Convert getStatistics ([#25342](https://github.com/RocketChat/Rocket.Chat/pull/25342)) -- Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) +- Chore: convert info to typescript ([#25420](https://github.com/RocketChat/Rocket.Chat/pull/25420)) -- Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) +- Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) -- Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) +- Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) - ``` - npx hygen package new test - ``` +- Chore: convert marketplace price display component to use typescript ([#25504](https://github.com/RocketChat/Rocket.Chat/pull/25504)) -- Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) + **Marketplace apps listing page** + ![Screen Shot 2022-05-13 at 12 57 43](https://user-images.githubusercontent.com/4161171/168322189-67990fdf-a447-46dc-8f88-08b16c2a5416.png) + + **Apps detail page** + ![Screen Shot 2022-05-13 at 12 58 56](https://user-images.githubusercontent.com/4161171/168322241-505ee5bb-d3d8-4b0e-8757-873a1a65a6a6.png) -- Chore: Tests with Playwright (task: ROC-28, 09-channels) ([#25196](https://github.com/RocketChat/Rocket.Chat/pull/25196)) +- Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) -- Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) +- Chore: Convert push endpoints to TS ([#25347](https://github.com/RocketChat/Rocket.Chat/pull/25347)) -- Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) +- Chore: Convert RoomForeword, TextCopy and RoomAvatarEditor to TS ([#25424](https://github.com/RocketChat/Rocket.Chat/pull/25424)) -- Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) +- Chore: Convert slashCommands to typescript ([#25592](https://github.com/RocketChat/Rocket.Chat/pull/25592) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) +- Chore: Convert to typescript some functions from app/lib/server/functions ([#24519](https://github.com/RocketChat/Rocket.Chat/pull/24519)) -- Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) + Convert to typescript some functions from app/lib/server/functions and transfered theses files to server/lib - I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. +- Chore: Convert to typescript the slash commands help files ([#24307](https://github.com/RocketChat/Rocket.Chat/pull/24307) by [@eduardofcabrera](https://github.com/eduardofcabrera)) -- i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) + Convert to typescript the slash commands help files + +- Chore: Convert useFileInput to TS ([#25426](https://github.com/RocketChat/Rocket.Chat/pull/25426)) + +- Chore: Convert useUpdateAvatar to TS and type avatar endpoints ([#25430](https://github.com/RocketChat/Rocket.Chat/pull/25430)) + +- Chore: Converting orchestrator.js to ts ([#25367](https://github.com/RocketChat/Rocket.Chat/pull/25367)) + +- Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) + +- Chore: Dedicated package for UI contexts ([#25432](https://github.com/RocketChat/Rocket.Chat/pull/25432)) + + Moving our React contexts to a different package on the monorepo enable us to deliver components from another packages, because they work as a loose connection to the core APIs. + +- Chore: Dependencies upgrade ([#25290](https://github.com/RocketChat/Rocket.Chat/pull/25290)) + +- Chore: Enable marketplace screenshots endpoint ([#25395](https://github.com/RocketChat/Rocket.Chat/pull/25395)) + +- Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) + + - data and test-failure should be ignored + - ensure scripts use cross-env + +- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) + +- Chore: Increase performance and security of integrations’ scripts ([#25641](https://github.com/RocketChat/Rocket.Chat/pull/25641)) + + Replace internal VM implementation with VM2 which implements many more mechanisms to ensure timeout, security and allow easier configuration for future improvements on the integrations' feature. + +- Chore: Livechat change output level ([#25522](https://github.com/RocketChat/Rocket.Chat/pull/25522)) + +- Chore: Manager Page Rewrite ([#25431](https://github.com/RocketChat/Rocket.Chat/pull/25431)) + +- Chore: Migrate 15-message-popup from cypress to playwright ([#25462](https://github.com/RocketChat/Rocket.Chat/pull/25462)) + +- Chore: migrate from cypress to pw 14-setting-permission ([#25523](https://github.com/RocketChat/Rocket.Chat/pull/25523)) + +- Chore: Migrate NotFoundPage to TS ([#25509](https://github.com/RocketChat/Rocket.Chat/pull/25509)) + +- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) + +- Chore: Migrate retention-policy to ts ([#25582](https://github.com/RocketChat/Rocket.Chat/pull/25582)) + +- Chore: Migrate spotify to ts ([#25507](https://github.com/RocketChat/Rocket.Chat/pull/25507)) + +- Chore: migrate-to-pw-adjust-in-intermitences ([#25542](https://github.com/RocketChat/Rocket.Chat/pull/25542)) + +- Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) + +- Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) + +- Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) + +- Chore: Move admin sidebarItems registration to the main file ([#25442](https://github.com/RocketChat/Rocket.Chat/pull/25442)) + +- Chore: Move ddp-streamer micro service to its own sub-repo ([#25246](https://github.com/RocketChat/Rocket.Chat/pull/25246)) + +- Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) + +- Chore: Move markdown message parser to a `callback` ([#25413](https://github.com/RocketChat/Rocket.Chat/pull/25413)) + +- Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) + +- Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) + +- Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) + +- Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) + +- Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) + + Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore + +- Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) + +- Chore: Reorder unreleased migrations ([#25508](https://github.com/RocketChat/Rocket.Chat/pull/25508)) + +- Chore: Rest API query parameters handling ([#25648](https://github.com/RocketChat/Rocket.Chat/pull/25648)) + +- Chore: REST query and body params validation ([#25446](https://github.com/RocketChat/Rocket.Chat/pull/25446)) + +- Chore: Rewrite 2fa to typescript ([#25285](https://github.com/RocketChat/Rocket.Chat/pull/25285)) + +- Chore: Rewrite action-links to ts ([#25418](https://github.com/RocketChat/Rocket.Chat/pull/25418)) + +- Chore: Rewrite autotranslate to ts ([#25425](https://github.com/RocketChat/Rocket.Chat/pull/25425)) + +- Chore: Rewrite im and dm endpoints to ts ([#25521](https://github.com/RocketChat/Rocket.Chat/pull/25521)) + + - Endpoints rewritten to TS + - dm.create + - dm.delete + - dm.close + - dm.counters + - dm.files + - dm.history + - dm.members + - dm.messages + - dm.messages.others + - dm.list + - dm.list.everyone + - dm.open + - dm.setTopic + - im.create + - im.delete + - im.close + - im.counters + - im.files + - im.history + - im.members + - im.messages + - im.messages.others + - im.list + - im.list.everyone + - im.open + - im.setTopic + - Some lines of code was refactored on `apps/meteor/app/api/server/v1/im.ts` + - Unnecessary functions were deleted on `apps/meteor/app/lib/server/functions/getDirectMessageByNameOrIdWithOptionToJoin.ts` + - New types was added on `apps/meteor/app/api/server/api.d.ts` + +- Chore: Rewrite Jitsi Contextualbar to TS ([#25303](https://github.com/RocketChat/Rocket.Chat/pull/25303)) + +- Chore: Rewrite mail-messages to ts ([#25421](https://github.com/RocketChat/Rocket.Chat/pull/25421)) + +- Chore: Rewrite some Omnichannel files to TypeScript ([#25359](https://github.com/RocketChat/Rocket.Chat/pull/25359)) + + apps/meteor/client/components/Omnichannel/modals/* + apps/meteor/client/components/Omnichannel/Tags.js + +- Chore: solve yarn issues from env var ([#25468](https://github.com/RocketChat/Rocket.Chat/pull/25468)) + +- Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) + +- Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) + + ``` + npx hygen package new test + ``` + +- Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) + +- Chore: Tests with Playwright (task: ROC-25, 06-message) ([#25252](https://github.com/RocketChat/Rocket.Chat/pull/25252)) + +- Chore: Tests with Playwright (task: ROC-28, 09-channels) ([#25196](https://github.com/RocketChat/Rocket.Chat/pull/25196)) + +- Chore: Tests with Playwright (task: ROC-31, 12-settings) ([#25253](https://github.com/RocketChat/Rocket.Chat/pull/25253)) + +- Chore: Tests with Playwright (task: ROC-66, Intermittent resolution in tests) ([#25416](https://github.com/RocketChat/Rocket.Chat/pull/25416)) + +- Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) + +- Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) + +- Chore: Update Apps-Engine and Fuselage ([#25700](https://github.com/RocketChat/Rocket.Chat/pull/25700)) + +- Chore: Update Apps-Engine version ([#25617](https://github.com/RocketChat/Rocket.Chat/pull/25617)) + +- Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) + +- Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) + +- Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) + + I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. + +- Chore: Update Volta configuration ([#25394](https://github.com/RocketChat/Rocket.Chat/pull/25394)) + + [Volta](https://volta.sh/) need some extra configuration to work on monorepos. + +- Chore: User set UTC offset ([#25381](https://github.com/RocketChat/Rocket.Chat/pull/25381)) + +- i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) - Merge master into develop & Set version to 4.7.0-develop ([#25028](https://github.com/RocketChat/Rocket.Chat/pull/25028)) @@ -1382,22 +2725,39 @@ - Regression: Add select message to system message and thread preview and allow select on legacy template ([#25251](https://github.com/RocketChat/Rocket.Chat/pull/25251)) +- Regression: App event listeners broke Slackbridge integration and importers ([#25689](https://github.com/RocketChat/Rocket.Chat/pull/25689)) + + Some event listeners triggered by Apps were calling `Meteor.user()` in functions that could run outside of Meteor environment + +- Regression: Assets & Slack Bridge Setting Page not rendering ([#25629](https://github.com/RocketChat/Rocket.Chat/pull/25629)) + - Regression: Avatar not loading on first direct message ([#25211](https://github.com/RocketChat/Rocket.Chat/pull/25211)) fix avatar not loading on a first direct message - Regression: Better MongoDB connection management for micro services ([#25323](https://github.com/RocketChat/Rocket.Chat/pull/25323)) -- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) +- Regression: Broken components on Federation and Engagement dashboards ([#25653](https://github.com/RocketChat/Rocket.Chat/pull/25653)) + + For reasons I've no clue, any client import path matching `**/data/**` will not be included in the final bundle, failing silently on transpiling/bundling. - - Bump to 'next' the onboarding-ui package from fuselage. +- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) + - Bump to 'next' the onboarding-ui package from fuselage. - Update from 'companyEmail' to 'email' adminData usage types +- Regression: Change logic to check if connection is online on unstable networks ([#25618](https://github.com/RocketChat/Rocket.Chat/pull/25618)) + - Regression: Change preference to be default legacy messages ([#25255](https://github.com/RocketChat/Rocket.Chat/pull/25255)) - Regression: CI playwright ([#25168](https://github.com/RocketChat/Rocket.Chat/pull/25168)) +- Regression: CI services build ([#25555](https://github.com/RocketChat/Rocket.Chat/pull/25555)) + +- Regression: Endpoint types with Ajv Coercing data types ([#25644](https://github.com/RocketChat/Rocket.Chat/pull/25644)) + + Ajv Coercing data types should be `true` to accept all kinds of data requested. + - Regression: eslint not running on packages ([#25305](https://github.com/RocketChat/Rocket.Chat/pull/25305)) - Regression: Fix CI monorepo build ([#25107](https://github.com/RocketChat/Rocket.Chat/pull/25107)) @@ -1420,18 +2780,26 @@ - Regression: Fix services Docker build on CI ([#25181](https://github.com/RocketChat/Rocket.Chat/pull/25181)) -- Regression: Fix size of custom emoji and render emoji on thread message preview ([#25314](https://github.com/RocketChat/Rocket.Chat/pull/25314)) +- Regression: Fix services-image-build-check ([#25519](https://github.com/RocketChat/Rocket.Chat/pull/25519)) -- Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) +- Regression: Fix size of custom emoji and render emoji on thread message preview ([#25314](https://github.com/RocketChat/Rocket.Chat/pull/25314)) - The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. +- Regression: Fix sort field files.list ([#25687](https://github.com/RocketChat/Rocket.Chat/pull/25687)) - This temporarily switches to a fork of the matrix-appservice-bridge package. +- Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) + The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + + This temporarily switches to a fork of the matrix-appservice-bridge package. + Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). - Regression: Messages in new message template Crashing. ([#25327](https://github.com/RocketChat/Rocket.Chat/pull/25327)) +- Regression: Missing settings group descriptions ([#25639](https://github.com/RocketChat/Rocket.Chat/pull/25639)) + + + - Regression: Revert Bugsnag version ([#25313](https://github.com/RocketChat/Rocket.Chat/pull/25313)) - Regression: Rocket.Chat Webapp not loading. ([#25349](https://github.com/RocketChat/Rocket.Chat/pull/25349)) @@ -1440,12 +2808,32 @@ - Regression: Shows error if micro service cannot connect to Mongo ([#25301](https://github.com/RocketChat/Rocket.Chat/pull/25301)) +- Regression: Subscription menu not appearing for non installed but subscribed apps ([#25627](https://github.com/RocketChat/Rocket.Chat/pull/25627)) + + Fixed a problem on which the AppMenu component did not appear for apps that had an active subscription but weren't installed, now the rendering of the component is also based on the isSubscribed flag, and the appearance of the uninstall and enable/disable options are based on the app.installed flag so that the correct options appear on all the edge cases. + Demo gif: + ![subscription-manager-fix](https://user-images.githubusercontent.com/43561537/170132040-dc8535c0-8056-4fb2-b008-afaece744868.gif) + +- Regression: Update settings groups description ([#25663](https://github.com/RocketChat/Rocket.Chat/pull/25663)) + - Regression: Use exact Node version on micro services Docker images ([#25287](https://github.com/RocketChat/Rocket.Chat/pull/25287)) - Regression: Validate empty fields for Message template ([#25250](https://github.com/RocketChat/Rocket.Chat/pull/25250)) +- Regression: VoIp wrap up modal not opening after call disconnect ([#25651](https://github.com/RocketChat/Rocket.Chat/pull/25651)) + + This PR fixes a bug preventing the wrap up call modal from being displayed after caller or agent ends the call. + - Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208)) +- Release 4.7.0 ([#25390](https://github.com/RocketChat/Rocket.Chat/pull/25390) by [@Himanshu664](https://github.com/Himanshu664) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@lingohub[bot]](https://github.com/lingohub[bot])) + +- Release 4.7.1 ([#25510](https://github.com/RocketChat/Rocket.Chat/pull/25510) by [@felipe-menelau](https://github.com/felipe-menelau)) + +- Release 4.7.2 ([#25580](https://github.com/RocketChat/Rocket.Chat/pull/25580)) + +- Test: Migrate 13-permissions from cypress to playwright ([#25558](https://github.com/RocketChat/Rocket.Chat/pull/25558)) +
### 👩‍💻👨‍💻 Contributors 😍 @@ -1454,10 +2842,15 @@ - [@aakash-gitdev](https://github.com/aakash-gitdev) - [@cuonghuunguyen](https://github.com/cuonghuunguyen) - [@dependabot[bot]](https://github.com/dependabot[bot]) +- [@divinespear](https://github.com/divinespear) +- [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@felipe-menelau](https://github.com/felipe-menelau) - [@kibonusp](https://github.com/kibonusp) -- [@nishant23122000](https://github.com/nishant23122000) +- [@lingohub[bot]](https://github.com/lingohub[bot]) +- [@ostjen](https://github.com/ostjen) - [@paulobernardoaf](https://github.com/paulobernardoaf) - [@sidmohanty11](https://github.com/sidmohanty11) +- [@ujorgeleite](https://github.com/ujorgeleite) ### 👩‍💻👨‍💻 Core Team 🤓 @@ -1465,562 +2858,609 @@ - [@KevLehman](https://github.com/KevLehman) - [@MarcosSpessatto](https://github.com/MarcosSpessatto) - [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@PedroRorato](https://github.com/PedroRorato) - [@alansikora](https://github.com/alansikora) - [@albuquerquefabio](https://github.com/albuquerquefabio) +- [@aleksandernsilva](https://github.com/aleksandernsilva) - [@amolghode1981](https://github.com/amolghode1981) +- [@carlosrodrigues94](https://github.com/carlosrodrigues94) +- [@cauefcr](https://github.com/cauefcr) - [@d-gubert](https://github.com/d-gubert) - [@debdutdeb](https://github.com/debdutdeb) - [@dougfabris](https://github.com/dougfabris) +- [@felipe-rod123](https://github.com/felipe-rod123) - [@filipemarins](https://github.com/filipemarins) - [@gabriellsh](https://github.com/gabriellsh) - [@geekgonecrazy](https://github.com/geekgonecrazy) - [@ggazzo](https://github.com/ggazzo) - [@guijun13](https://github.com/guijun13) +- [@hugocostadev](https://github.com/hugocostadev) - [@jeanfbrito](https://github.com/jeanfbrito) - [@juliajforesti](https://github.com/juliajforesti) +- [@marceloschmidt](https://github.com/marceloschmidt) +- [@matheusbsilva137](https://github.com/matheusbsilva137) +- [@matheuslc](https://github.com/matheuslc) - [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@rique223](https://github.com/rique223) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) - [@souzaramon](https://github.com/souzaramon) +- [@tapiarafael](https://github.com/tapiarafael) - [@tassoevan](https://github.com/tassoevan) - [@tiagoevanp](https://github.com/tiagoevanp) - [@tmontini](https://github.com/tmontini) - [@weslley543](https://github.com/weslley543) - [@yash-rajpal](https://github.com/yash-rajpal) -# 4.6.3 -`2022-04-19 · 1 🐛 · 1 👩‍💻👨‍💻` +# 4.7.4 +`2022-05-30 · 1 🐛 · 1 🔍 · 2 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` - NPM: `6.14.15` - MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` ### 🐛 Bug fixes -- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +
+🔍 Minor changes + + +- Load missed messages from opened rooms when reconnect ([#553](https://github.com/RocketChat/Rocket.Chat/pull/553)) + +
### 👩‍💻👨‍💻 Core Team 🤓 -- [@sampaiodiego](https://github.com/sampaiodiego) +- [@ggazzo](https://github.com/ggazzo) +- [@rodrigok](https://github.com/rodrigok) -# 4.6.2 -`2022-04-14 · 2 🐛 · 2 👩‍💻👨‍💻` +# 4.7.3 +`2022-05-20 · 1 🐛 · 1 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` - NPM: `6.14.15` - MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` ### 🐛 Bug fixes -- Database indexes not being created ([#25101](https://github.com/RocketChat/Rocket.Chat/pull/25101)) - -- Deactivating user breaks if user is the only room owner ([#24933](https://github.com/RocketChat/Rocket.Chat/pull/24933) by [@sidmohanty11](https://github.com/sidmohanty11)) - - ## Before - - https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 - - - - ## After - https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 - -### 👩‍💻👨‍💻 Contributors 😍 - -- [@sidmohanty11](https://github.com/sidmohanty11) +- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@sampaiodiego](https://github.com/sampaiodiego) +- [@ggazzo](https://github.com/ggazzo) -# 4.6.1 -`2022-04-07 · 6 🐛 · 5 👩‍💻👨‍💻` +# 4.7.2 +`2022-05-20 · 5 🐛 · 2 🔍 · 7 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` - NPM: `6.14.15` - MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` ### 🐛 Bug fixes -- FormData uploads not working ([#25069](https://github.com/RocketChat/Rocket.Chat/pull/25069)) +- Dynamic load matrix is enabled and handle failure ([#25495](https://github.com/RocketChat/Rocket.Chat/pull/25495)) -- Invitation links don't redirect to the registration form ([#25082](https://github.com/RocketChat/Rocket.Chat/pull/25082)) +- Initial User not added to default channel ([#25544](https://github.com/RocketChat/Rocket.Chat/pull/25544)) -- NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) + If injecting initial user. The user wasn’t added to the default General channel -- Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) +- One of the triggers was not working correctly ([#25409](https://github.com/RocketChat/Rocket.Chat/pull/25409)) - Modify Meteor's `HTTP.call` to add back proxy support +- UI/UX issues on Live Chat widget ([#25407](https://github.com/RocketChat/Rocket.Chat/pull/25407)) -- Upgrade Tab showing for a split second ([#25050](https://github.com/RocketChat/Rocket.Chat/pull/25050)) +- User abandonment setting was not working doe to failing event hook ([#25520](https://github.com/RocketChat/Rocket.Chat/pull/25520)) -- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) + A setting watcher and the query for grabbing abandoned chats were broken, now they're not. - ### before - ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) +
+🔍 Minor changes - ### after - ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) + +- Chore: Add Livechat repo into Monorepo packages ([#25312](https://github.com/RocketChat/Rocket.Chat/pull/25312)) + +- Release 4.7.2 ([#25580](https://github.com/RocketChat/Rocket.Chat/pull/25580)) + +
### 👩‍💻👨‍💻 Core Team 🤓 +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@cauefcr](https://github.com/cauefcr) +- [@d-gubert](https://github.com/d-gubert) - [@dougfabris](https://github.com/dougfabris) -- [@gabriellsh](https://github.com/gabriellsh) -- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) -- [@sampaiodiego](https://github.com/sampaiodiego) -- [@yash-rajpal](https://github.com/yash-rajpal) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@ggazzo](https://github.com/ggazzo) +- [@tiagoevanp](https://github.com/tiagoevanp) -# 4.6.0 -`2022-04-01 · 2 🎉 · 7 🚀 · 57 🐛 · 62 🔍 · 34 👩‍💻👨‍💻` +# 4.7.1 +`2022-05-13 · 1 🎉 · 2 🐛 · 1 🔍 · 4 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` - NPM: `6.14.15` - MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` ### 🎉 New features -- Telemetry Events ([#24781](https://github.com/RocketChat/Rocket.Chat/pull/24781) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Use setting to determine if initial general channel is needed ([#25441](https://github.com/RocketChat/Rocket.Chat/pull/25441) by [@felipe-menelau](https://github.com/felipe-menelau)) -- Upgrade Tab ([#24835](https://github.com/RocketChat/Rocket.Chat/pull/24835)) + - Adds flag responsible for overwriting #general channel creation - ![image](https://user-images.githubusercontent.com/27704687/160172260-c656282e-a487-4092-948d-d11c9bacb598.png) +### 🐛 Bug fixes -### 🚀 Improvements +- LDAP sync removing users from channels when multiple groups are mapped to it ([#25434](https://github.com/RocketChat/Rocket.Chat/pull/25434)) -- **ENTERPRISE:** Don't start presence monitor when running micro services ([#24739](https://github.com/RocketChat/Rocket.Chat/pull/24739)) +- Spotlight results showing usernames instead of real names ([#25471](https://github.com/RocketChat/Rocket.Chat/pull/25471)) -- Adding new statistics related to voip and omnichannel ([#24887](https://github.com/RocketChat/Rocket.Chat/pull/24887)) +
+🔍 Minor changes - - Total of Canned response messages sent - - Total of tags used +- Release 4.7.1 ([#25510](https://github.com/RocketChat/Rocket.Chat/pull/25510) by [@felipe-menelau](https://github.com/felipe-menelau)) - - Last-Chatted Agent Preferred (enabled/disabled) +
- - Assign new conversations to the contact manager (enabled/disabled) +### 👩‍💻👨‍💻 Contributors 😍 - - How to handle Visitor Abandonment setting +- [@felipe-menelau](https://github.com/felipe-menelau) - - Amount of chats placed on hold +### 👩‍💻👨‍💻 Core Team 🤓 - - VoIP Enabled +- [@d-gubert](https://github.com/d-gubert) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@sampaiodiego](https://github.com/sampaiodiego) - - Amount of VoIP Calls +# 4.7.0 +`2022-05-04 · 4 🎉 · 7 🚀 · 33 🐛 · 69 🔍 · 35 👩‍💻👨‍💻` - - Amount of VoIP Extensions connected +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` - - Amount of Calls placed on hold (1x per call) +### 🎉 New features - - Fixed Session Aggregation type definitions -- New omnichannel statistics and async statistics processing. ([#24749](https://github.com/RocketChat/Rocket.Chat/pull/24749)) +- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) - https://app.clickup.com/t/1z4zg4e +- Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) -- Standarize queue behavior for managers and agents when subscribing ([#24837](https://github.com/RocketChat/Rocket.Chat/pull/24837)) + Experimental support for Matrix Federation with a Bridge + + https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 -- Updated links in readme ([#24028](https://github.com/RocketChat/Rocket.Chat/pull/24028) by [@aswinidev](https://github.com/aswinidev)) +- Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) -- UX - VoIP Call Component ([#24748](https://github.com/RocketChat/Rocket.Chat/pull/24748)) +- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) -- Voip Extensions disabled state ([#24750](https://github.com/RocketChat/Rocket.Chat/pull/24750)) + Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). + + + + ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) + In case you encounter any problems, or want to compare, temporarily it is possible to use the old version + + image -### 🐛 Bug fixes +### 🚀 Improvements -- "livechat/webrtc.call" endpoint not working ([#24804](https://github.com/RocketChat/Rocket.Chat/pull/24804)) +- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) -- "Match error" when converting a team to a channel ([#24629](https://github.com/RocketChat/Rocket.Chat/pull/24629)) + Earlier OTR room uses only 2 states, we need more states to support future features. + This adds more states for the OTR contextualBar. + + - Expired + Screen Shot 2022-04-20 at 13 55 52 + + - Declined + Screen Shot 2022-04-20 at 13 49 28 + + - Error + Screen Shot 2022-04-20 at 13 55 26 - - Fix "Match error" when trying to convert a channel to a team; +- Add tooltip to sidebar room menu ([#24405](https://github.com/RocketChat/Rocket.Chat/pull/24405) by [@Himanshu664](https://github.com/Himanshu664)) -- **ENTERPRISE:** Auto reload feature of ddp-streamer micro service ([#24793](https://github.com/RocketChat/Rocket.Chat/pull/24793)) +- Added MaxNickNameLength and MaxBioLength constants ([#25231](https://github.com/RocketChat/Rocket.Chat/pull/25231) by [@aakash-gitdev](https://github.com/aakash-gitdev)) -- **ENTERPRISE:** DDP streamer not sending data to all clients ([#24738](https://github.com/RocketChat/Rocket.Chat/pull/24738)) +- Added tooltip options for message menu ([#24431](https://github.com/RocketChat/Rocket.Chat/pull/24431) by [@Himanshu664](https://github.com/Himanshu664)) -- **ENTERPRISE:** Notifications not being sent by ddp-streamer ([#24831](https://github.com/RocketChat/Rocket.Chat/pull/24831)) +- Improve active/hover colors in account sidebar ([#25024](https://github.com/RocketChat/Rocket.Chat/pull/25024) by [@Himanshu664](https://github.com/Himanshu664)) -- **ENTERPRISE:** Presence micro service logic ([#24724](https://github.com/RocketChat/Rocket.Chat/pull/24724)) +- Performance for some Omnichannel features ([#25217](https://github.com/RocketChat/Rocket.Chat/pull/25217)) -- **VOIP:** SidebarFooter component ([#24838](https://github.com/RocketChat/Rocket.Chat/pull/24838)) +- Rename upgrade tab routes ([#25097](https://github.com/RocketChat/Rocket.Chat/pull/25097)) - - Improve the CallProvider code; + Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test. - - Adjust the text case of the VoIP component on the FooterSidebar; +### 🐛 Bug fixes - - Fix the bad behavior with the changes in queue's name. -- `PaginatedSelectFiltered` not handling changes ([#24732](https://github.com/RocketChat/Rocket.Chat/pull/24732)) +- Add katex render to new message react template ([#25239](https://github.com/RocketChat/Rocket.Chat/pull/25239)) -- API Error preventing adding an email to users without one (like bot/app users) ([#24709](https://github.com/RocketChat/Rocket.Chat/pull/24709)) +- Add reaction not working in legacy messages ([#25222](https://github.com/RocketChat/Rocket.Chat/pull/25222)) -- Apple login script being loaded even when Apple Login is disabled. ([#24760](https://github.com/RocketChat/Rocket.Chat/pull/24760)) +- Added invalid password error message ([#24714](https://github.com/RocketChat/Rocket.Chat/pull/24714) by [@Himanshu664](https://github.com/Himanshu664)) -- Apple OAuth ([#24879](https://github.com/RocketChat/Rocket.Chat/pull/24879)) +- Adjust email label in Setup Wizard i18n files ([#25260](https://github.com/RocketChat/Rocket.Chat/pull/25260)) -- auto-join team channels not honoring user preferences ([#24779](https://github.com/RocketChat/Rocket.Chat/pull/24779) by [@ostjen](https://github.com/ostjen)) + - remove 'Company' label on onboarding email keys in certain languages -- Broken build caused by PRs modifying same file differently ([#24863](https://github.com/RocketChat/Rocket.Chat/pull/24863)) +- AgentOverview analytics wrong departmentId parameter ([#25073](https://github.com/RocketChat/Rocket.Chat/pull/25073) by [@paulobernardoaf](https://github.com/paulobernardoaf)) -- Broken multiple OAuth integrations ([#24705](https://github.com/RocketChat/Rocket.Chat/pull/24705)) + When filtering the analytics charts by department, data would not appear because the object: + ```js + { + value: "department-id", + label: "department-name" + } + ``` + was being used in the `departmentId` parameter. + + - Before: + ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) + + - After: + ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) -- Components for user search ([#24677](https://github.com/RocketChat/Rocket.Chat/pull/24677)) +- Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) -- Critical: Incorrect visitor getting assigned to a chat from apps ([#24805](https://github.com/RocketChat/Rocket.Chat/pull/24805)) + Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online + unless agent explicitly logs off. + Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. + 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off + in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + 2. Second reason is when computer goes in sleep mode. + 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. + + Solution: + The idea is to detect the network disconnection and start the start the attempts to reconnect. + The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not + call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are + used. + + The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to + reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. + + When the server is disconnected, it should be indicated on the phone button. -- Custom script not being fired ([#24901](https://github.com/RocketChat/Rocket.Chat/pull/24901)) +- Close room when dismiss wrap up call modal ([#25056](https://github.com/RocketChat/Rocket.Chat/pull/25056)) -- Date Message Export Filter Fix ([#24542](https://github.com/RocketChat/Rocket.Chat/pull/24542) by [@eduardofcabrera](https://github.com/eduardofcabrera)) +- Custom sound error toast messages ([#24515](https://github.com/RocketChat/Rocket.Chat/pull/24515) by [@Himanshu664](https://github.com/Himanshu664)) - Fix message export filter to get all messages between "from date" and "to date", including "to date". +- Deactivating user breaks if user is the only room owner ([#24933](https://github.com/RocketChat/Rocket.Chat/pull/24933) by [@sidmohanty11](https://github.com/sidmohanty11)) -- DDP Rate Limiter Translation key ([#24898](https://github.com/RocketChat/Rocket.Chat/pull/24898)) + ## Before + + https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 + + + + ## After + https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 - Before: - image +- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) +- End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) - Now: - image +- FormData uploads not working ([#25069](https://github.com/RocketChat/Rocket.Chat/pull/25069)) -- DDP streamer errors ([#24710](https://github.com/RocketChat/Rocket.Chat/pull/24710)) +- Full error message is visible ([#24856](https://github.com/RocketChat/Rocket.Chat/pull/24856) by [@Himanshu664](https://github.com/Himanshu664)) -- Disable voip button when call is in progress ([#24864](https://github.com/RocketChat/Rocket.Chat/pull/24864)) +- Incorrect websocket url in livechat widget ([#25261](https://github.com/RocketChat/Rocket.Chat/pull/25261)) -- Duplicated 'name' log key ([#24590](https://github.com/RocketChat/Rocket.Chat/pull/24590)) +- Invitation links don't redirect to the registration form ([#25082](https://github.com/RocketChat/Rocket.Chat/pull/25082)) -- Duplicated "jump to message" button on starred messages ([#24867](https://github.com/RocketChat/Rocket.Chat/pull/24867) by [@Himanshu664](https://github.com/Himanshu664)) +- Message menu action not working on legacy messages. ([#25148](https://github.com/RocketChat/Rocket.Chat/pull/25148)) -- External search providers not working ([#24860](https://github.com/RocketChat/Rocket.Chat/pull/24860) by [@tkurz](https://github.com/tkurz)) +- Message preview not available for queued chats ([#25092](https://github.com/RocketChat/Rocket.Chat/pull/25092)) -- German translation for Monitore ([#24785](https://github.com/RocketChat/Rocket.Chat/pull/24785) by [@JMoVS](https://github.com/JMoVS)) +- NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) -- Handle Other Formats inside Upload Avatar ([#24226](https://github.com/RocketChat/Rocket.Chat/pull/24226) by [@nishant23122000](https://github.com/nishant23122000)) +- Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) - After resolving issue #24213 : + ### before + Screen Shot 2022-03-29 at 13 35 56 + + ### after + Screen Shot 2022-03-29 at 11 48 05 +- Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) - https://user-images.githubusercontent.com/53515714/150325012-91413025-786e-4ce0-ae75-629f6b05b024.mp4 + Modify Meteor's `HTTP.call` to add back proxy support -- High CPU usage caused by CallProvider ([#24994](https://github.com/RocketChat/Rocket.Chat/pull/24994)) +- Read receipts show with color gray when not read yet ([#25244](https://github.com/RocketChat/Rocket.Chat/pull/25244)) - Remove infinity loop inside useVoipClient hook. +- Read receipts showing before message read ([#25216](https://github.com/RocketChat/Rocket.Chat/pull/25216)) - #closes #24970 +- Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) -- Ignore customClass on messages ([#24845](https://github.com/RocketChat/Rocket.Chat/pull/24845)) + ### before + ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) + + ### after + Screenshot 2022-01-13 at 8 57 47 PM -- LDAP avatars being rotated according to metadata even if the setting to rotate uploads is off ([#24320](https://github.com/RocketChat/Rocket.Chat/pull/24320)) +- Reply button behavior on broadcast channel ([#25175](https://github.com/RocketChat/Rocket.Chat/pull/25175)) - - Use the `FileUpload_RotateImages` setting (**Administration > File Upload > Rotate images on upload**) to control whether avatars should be rotated automatically based on their data (XEIF); + Hide reply button for the user that sent the message - - Display the avatar image preview (orientation) according to the `FileUpload_RotateImages` setting. +- room creation fails if app framework is disabled ([#25200](https://github.com/RocketChat/Rocket.Chat/pull/25200)) -- Missing dependency on useEffect at CallProvider ([#24882](https://github.com/RocketChat/Rocket.Chat/pull/24882)) +- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007)) -- Missing username on messages imported from Slack ([#24674](https://github.com/RocketChat/Rocket.Chat/pull/24674)) + https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 - - Fix missing sender's username on messages imported from Slack. +- Toolbox hiding under contextual bar ([#25237](https://github.com/RocketChat/Rocket.Chat/pull/25237)) -- Nextcloud OAuth for incomplete token URL ([#24476](https://github.com/RocketChat/Rocket.Chat/pull/24476)) +- Upgrade Tab showing for a split second ([#25050](https://github.com/RocketChat/Rocket.Chat/pull/25050)) -- no id of room closer in livechat-close message ([#24683](https://github.com/RocketChat/Rocket.Chat/pull/24683)) +- Use correct room property for call ended at ([#24932](https://github.com/RocketChat/Rocket.Chat/pull/24932)) -- Opening a new DM from user card ([#24623](https://github.com/RocketChat/Rocket.Chat/pull/24623)) +- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) - A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component. + ### before + ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) + + ### after + ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) -- Prevent call button toggle when user is on call ([#24758](https://github.com/RocketChat/Rocket.Chat/pull/24758)) +- UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) -- Prune Message issue ([#24424](https://github.com/RocketChat/Rocket.Chat/pull/24424) by [@nishant23122000](https://github.com/nishant23122000)) + - Rewrites the component to TS + - Fixes some visual issues + + ### before + ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) + + ### after + ![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png) -- Push privacy config to not show username not being respected ([#24606](https://github.com/RocketChat/Rocket.Chat/pull/24606)) +- Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) -- Register with Secret URL ([#24921](https://github.com/RocketChat/Rocket.Chat/pull/24921)) +- VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) -- Reload roomslist after successful deletion of a room from admin panel. ([#23795](https://github.com/RocketChat/Rocket.Chat/pull/23795) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) + Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) + + It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + + Solution: + + 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. + 3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler. - Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`. - This allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent. +
+🔍 Minor changes - Also added a succes toast message after the successful deletion of room. -- Revert AutoComplete ([#24812](https://github.com/RocketChat/Rocket.Chat/pull/24812)) +- Bump body-parser from 1.19.2 to 1.20.0 in /ee/server/services ([#25042](https://github.com/RocketChat/Rocket.Chat/pull/25042) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Room archived/unarchived system messages aren't sent when editing room settings ([#24897](https://github.com/RocketChat/Rocket.Chat/pull/24897)) +- Bump ejson from 2.2.1 to 2.2.2 ([#25057](https://github.com/RocketChat/Rocket.Chat/pull/25057) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Send the "Room archived" and "Room unarchived" system messages when editing room settings (and not only when rooms are archived/unarchived with the slash-command); +- Bump eslint-plugin-anti-trojan-source from 1.0.6 to 1.1.0 ([#25076](https://github.com/RocketChat/Rocket.Chat/pull/25076) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Fix the "Hide System Messages" option for the "Room archived" and "Room unarchived" system messages; +- Bump minimist from 1.2.5 to 1.2.6 in /ee/server/services ([#24991](https://github.com/RocketChat/Rocket.Chat/pull/24991) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- room message not load when is a new message ([#24955](https://github.com/RocketChat/Rocket.Chat/pull/24955)) +- Bump pino and pino-pretty ([#25052](https://github.com/RocketChat/Rocket.Chat/pull/25052)) - When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details. +- Bump template-file from 6.0.0 to 6.0.1 ([#25002](https://github.com/RocketChat/Rocket.Chat/pull/25002) by [@dependabot[bot]](https://github.com/dependabot[bot])) - before: - https://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4 +- Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) - after: - https://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4 + Not crash the whole application if something goes wrong in the MessageList component. + + ![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png) -- Room's message count not being incremented on import ([#24696](https://github.com/RocketChat/Rocket.Chat/pull/24696)) +- Chore: Add options to debug stdout and rate limiter ([#25336](https://github.com/RocketChat/Rocket.Chat/pull/25336)) - - Fix rooms' message counter not being incremented on message import. +- Chore: Add root package.json to houston files ([#25286](https://github.com/RocketChat/Rocket.Chat/pull/25286)) -- SAML Force name to string ([#24930](https://github.com/RocketChat/Rocket.Chat/pull/24930)) + See title -- Several issues related to custom roles ([#24052](https://github.com/RocketChat/Rocket.Chat/pull/24052)) +- Chore: Add yarn plugin to check node and yarn version ([#25224](https://github.com/RocketChat/Rocket.Chat/pull/25224)) - - Throw an error when trying to delete a role (User or Subscription role) that are still being used; +- Chore: Bump fuselage ([#25371](https://github.com/RocketChat/Rocket.Chat/pull/25371)) - - Fix "Invalid Role" error for custom roles in Role Editing sidebar; +- Chore: Bump Fuselage packages ([#25259](https://github.com/RocketChat/Rocket.Chat/pull/25259)) - - Fix "Users in Role" screen for custom roles. +- Chore: Cancel running jobs if PR is updated ([#24708](https://github.com/RocketChat/Rocket.Chat/pull/24708)) -- Show call icon only when user has extension associated ([#24752](https://github.com/RocketChat/Rocket.Chat/pull/24752)) +- Chore: Convert admin custom sound to tsx ([#25128](https://github.com/RocketChat/Rocket.Chat/pull/25128)) -- Show only available agents on extension association modal ([#24680](https://github.com/RocketChat/Rocket.Chat/pull/24680)) +- Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) -- Show only enabled departments on forward ([#24829](https://github.com/RocketChat/Rocket.Chat/pull/24829)) +- Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) -- System messages are sent when adding or removing a group from a team ([#24743](https://github.com/RocketChat/Rocket.Chat/pull/24743)) +- Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) - - Do not send system messages when adding or removing a new or existing _group_ from a team. +- Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) -- Typo and placeholder on wrap up call modal ([#24737](https://github.com/RocketChat/Rocket.Chat/pull/24737)) +- Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) -- Typo in wrap-up term ([#24661](https://github.com/RocketChat/Rocket.Chat/pull/24661)) + - data and test-failure should be ignored + - ensure scripts use cross-env -- VoIP button gets disabled whenever user status changes ([#24789](https://github.com/RocketChat/Rocket.Chat/pull/24789)) +- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) -- VoIP Enable/Disable setting on CallContext/CallProvider Notifications ([#24607](https://github.com/RocketChat/Rocket.Chat/pull/24607)) +- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) -- Voip Stream Reinitialization Error ([#24657](https://github.com/RocketChat/Rocket.Chat/pull/24657)) +- Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) -- VoipExtensionsPage component call ([#24792](https://github.com/RocketChat/Rocket.Chat/pull/24792)) +- Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) -- Wrong business hour behavior ([#24896](https://github.com/RocketChat/Rocket.Chat/pull/24896)) +- Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) -- Wrong param usage on queue summary call ([#24799](https://github.com/RocketChat/Rocket.Chat/pull/24799)) +- Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) -
-🔍 Minor changes +- Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) +- Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) -- Bump @rocket.chat/emitter from 0.31.4 to 0.31.9 in /ee/server/services ([#25021](https://github.com/RocketChat/Rocket.Chat/pull/25021) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) -- Bump @rocket.chat/message-parser from 0.31.4 to 0.31.9 in /ee/server/services ([#25019](https://github.com/RocketChat/Rocket.Chat/pull/25019) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) -- Bump @rocket.chat/string-helpers from 0.31.4 to 0.31.9 in /ee/server/services ([#25018](https://github.com/RocketChat/Rocket.Chat/pull/25018) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) -- Bump @rocket.chat/ui-kit from 0.31.4 to 0.31.9 in /ee/server/services ([#25020](https://github.com/RocketChat/Rocket.Chat/pull/25020) by [@dependabot[bot]](https://github.com/dependabot[bot])) + Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore -- Bump @types/clipboard from 2.0.1 to 2.0.7 ([#24832](https://github.com/RocketChat/Rocket.Chat/pull/24832) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) -- Bump @types/mailparser from 3.0.2 to 3.4.0 ([#24833](https://github.com/RocketChat/Rocket.Chat/pull/24833) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) -- Bump @types/nodemailer from 6.4.2 to 6.4.4 ([#24822](https://github.com/RocketChat/Rocket.Chat/pull/24822) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) -- Bump @types/ws from 8.2.3 to 8.5.2 in /ee/server/services ([#24666](https://github.com/RocketChat/Rocket.Chat/pull/24666) by [@dependabot[bot]](https://github.com/dependabot[bot])) + ``` + npx hygen package new test + ``` -- Bump @types/ws from 8.5.2 to 8.5.3 in /ee/server/services ([#24820](https://github.com/RocketChat/Rocket.Chat/pull/24820) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) -- Bump actions/checkout from 2 to 3 ([#24668](https://github.com/RocketChat/Rocket.Chat/pull/24668) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Tests with Playwright (task: ROC-28, 09-channels) ([#25196](https://github.com/RocketChat/Rocket.Chat/pull/25196)) -- Bump actions/setup-node from 2 to 3 ([#24642](https://github.com/RocketChat/Rocket.Chat/pull/24642) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) -- Bump body-parser from 1.19.0 to 1.19.2 ([#24821](https://github.com/RocketChat/Rocket.Chat/pull/24821) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) -- Bump is-svg from 4.3.1 to 4.3.2 ([#24801](https://github.com/RocketChat/Rocket.Chat/pull/24801) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) -- Bump jschardet from 1.6.0 to 3.0.0 ([#23121](https://github.com/RocketChat/Rocket.Chat/pull/23121) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) -- Bump pino from 7.8.0 to 7.8.1 in /ee/server/services ([#24783](https://github.com/RocketChat/Rocket.Chat/pull/24783) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) -- Bump pino from 7.8.1 to 7.9.1 in /ee/server/services ([#24869](https://github.com/RocketChat/Rocket.Chat/pull/24869) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump pino-pretty from 7.5.1 to 7.5.2 in /ee/server/services ([#24689](https://github.com/RocketChat/Rocket.Chat/pull/24689) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump pino-pretty from 7.5.2 to 7.5.3 in /ee/server/services ([#24698](https://github.com/RocketChat/Rocket.Chat/pull/24698) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services ([#24870](https://github.com/RocketChat/Rocket.Chat/pull/24870) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump prometheus-gc-stats from 0.6.2 to 0.6.3 ([#24803](https://github.com/RocketChat/Rocket.Chat/pull/24803) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump ts-node from 10.5.0 to 10.6.0 in /ee/server/services ([#24667](https://github.com/RocketChat/Rocket.Chat/pull/24667) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump ts-node from 10.6.0 to 10.7.0 in /ee/server/services ([#24716](https://github.com/RocketChat/Rocket.Chat/pull/24716) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Bump url-parse from 1.5.7 to 1.5.10 ([#24640](https://github.com/RocketChat/Rocket.Chat/pull/24640) by [@dependabot[bot]](https://github.com/dependabot[bot])) - -- Chore: Add E2E tests for livechat/room.close ([#24729](https://github.com/RocketChat/Rocket.Chat/pull/24729) by [@Muramatsu2602](https://github.com/Muramatsu2602)) - - * Create a new test suite file under tests/end-to-end/api/livechat - * Create tests for the following endpoint: - + ivechat/room.close - -- Chore: Add E2E tests for livechat/visitor ([#24764](https://github.com/RocketChat/Rocket.Chat/pull/24764) by [@Muramatsu2602](https://github.com/Muramatsu2602)) - - - Create a new test suite file under tests/end-to-end/api/livechat - - - Create tests for the following endpoints: - + livechat/visitor (create visitor, update visitor, add custom fields to visitors) - -- Chore: add some missing REST definitions ([#24925](https://github.com/RocketChat/Rocket.Chat/pull/24925) by [@gerzonc](https://github.com/gerzonc)) - - On the [mobile client](https://github.com/RocketChat/Rocket.Chat.ReactNative), we made an effort to collect more `REST API` definitions that are missing on the server side during our migration to TypeScript. Since we're both migrating to TypeScript, we thought it would be a good idea to share those so you guys can benefit from our initiative. - -- Chore: added Server Instances endpoint types ([#24507](https://github.com/RocketChat/Rocket.Chat/pull/24507)) - - Created typing for endpoint definitions on `instances.ts`. - -- Chore: added settings endpoint types ([#24506](https://github.com/RocketChat/Rocket.Chat/pull/24506)) - - Created typing for endpoint definitions on `settings.ts`. - -- Chore: APIClass types ([#24747](https://github.com/RocketChat/Rocket.Chat/pull/24747)) - - This pull request creates a new `restivus` module (.d.ts) for the `api.js` file. - -- Chore: Bump Fuselage packages ([#25015](https://github.com/RocketChat/Rocket.Chat/pull/25015)) - - It uses the last stable version of Fuselage packages. - -- Chore: Convert server functions from javascript to typescript ([#24384](https://github.com/RocketChat/Rocket.Chat/pull/24384)) - - This pull request will be used to rewrite some functions on the Chat Engine to Typescript, in order to increase security and specify variable types on the code. - -- Chore: converted more hooks to typescript ([#24628](https://github.com/RocketChat/Rocket.Chat/pull/24628)) - - Converted some functions on `client/hooks/` from JavaScript to Typescript. + I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. -- Chore: Fix Cypress tests ([#24544](https://github.com/RocketChat/Rocket.Chat/pull/24544)) +- i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) -- Chore: Fix grammatical errors in Code of Conduct ([#24759](https://github.com/RocketChat/Rocket.Chat/pull/24759) by [@aadishJ01](https://github.com/aadishJ01)) +- Merge master into develop & Set version to 4.7.0-develop ([#25028](https://github.com/RocketChat/Rocket.Chat/pull/25028)) -- Chore: fix grammatical errors in Features ([#24771](https://github.com/RocketChat/Rocket.Chat/pull/24771) by [@aadishJ01](https://github.com/aadishJ01)) +- Regression: Add `isPending` status to message ([#25299](https://github.com/RocketChat/Rocket.Chat/pull/25299)) -- Chore: Fix MongoDB versions on release notes ([#24877](https://github.com/RocketChat/Rocket.Chat/pull/24877)) +- Regression: Add eslint package to micro services Dockerfile ([#25311](https://github.com/RocketChat/Rocket.Chat/pull/25311)) -- Chore: Get Settings Statistics ([#24397](https://github.com/RocketChat/Rocket.Chat/pull/24397)) +- Regression: Add select message to system message and thread preview and allow select on legacy template ([#25251](https://github.com/RocketChat/Rocket.Chat/pull/25251)) -- Chore: Improve logger to allow log of `unknown` values ([#24726](https://github.com/RocketChat/Rocket.Chat/pull/24726)) +- Regression: Avatar not loading on first direct message ([#25211](https://github.com/RocketChat/Rocket.Chat/pull/25211)) -- Chore: Improvements on role syncing (ldap, oauth and saml) ([#23824](https://github.com/RocketChat/Rocket.Chat/pull/23824)) + fix avatar not loading on a first direct message -- Chore: Micro services fixes and cleanup ([#24753](https://github.com/RocketChat/Rocket.Chat/pull/24753)) +- Regression: Better MongoDB connection management for micro services ([#25323](https://github.com/RocketChat/Rocket.Chat/pull/25323)) -- Chore: Remove old scripts ([#24911](https://github.com/RocketChat/Rocket.Chat/pull/24911)) +- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) -- Chore: Skip local services changes when shutting down duplicated services ([#24810](https://github.com/RocketChat/Rocket.Chat/pull/24810)) + - Bump to 'next' the onboarding-ui package from fuselage. + - Update from 'companyEmail' to 'email' adminData usage types -- Chore: Storybook mocking and examples improved ([#24969](https://github.com/RocketChat/Rocket.Chat/pull/24969)) +- Regression: Change preference to be default legacy messages ([#25255](https://github.com/RocketChat/Rocket.Chat/pull/25255)) - - Stories from `ee/` included; +- Regression: CI playwright ([#25168](https://github.com/RocketChat/Rocket.Chat/pull/25168)) - - Differentiate root story kinds; +- Regression: eslint not running on packages ([#25305](https://github.com/RocketChat/Rocket.Chat/pull/25305)) - - Mocking of `ServerContext` via Storybook parameters. +- Regression: Fix CI monorepo build ([#25107](https://github.com/RocketChat/Rocket.Chat/pull/25107)) -- Chore: Update Livechat ([#24754](https://github.com/RocketChat/Rocket.Chat/pull/24754)) +- Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window ([#25380](https://github.com/RocketChat/Rocket.Chat/pull/25380)) -- Chore: Update Livechat ([#24990](https://github.com/RocketChat/Rocket.Chat/pull/24990)) + Fix: livechat room not opening. -- Chore(deps-dev): Bump @types/mock-require from 2.0.0 to 2.0.1 ([#24574](https://github.com/RocketChat/Rocket.Chat/pull/24574) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Regression: Fix English i18n react text ([#25368](https://github.com/RocketChat/Rocket.Chat/pull/25368)) -- i18n: Language update from LingoHub 🤖 on 2022-02-28Z ([#24644](https://github.com/RocketChat/Rocket.Chat/pull/24644)) + Incorrect text in reaction tooltip has been fixed -- i18n: Language update from LingoHub 🤖 on 2022-03-07Z ([#24717](https://github.com/RocketChat/Rocket.Chat/pull/24717)) +- Regression: Fix federation Matrix bridge startup ([#25273](https://github.com/RocketChat/Rocket.Chat/pull/25273)) -- i18n: Language update from LingoHub 🤖 on 2022-03-14Z ([#24823](https://github.com/RocketChat/Rocket.Chat/pull/24823)) +- Regression: Fix micro services Docker build ([#25193](https://github.com/RocketChat/Rocket.Chat/pull/25193)) -- i18n: Language update from LingoHub 🤖 on 2022-03-21Z ([#24895](https://github.com/RocketChat/Rocket.Chat/pull/24895)) +- Regression: Fix multi line is not showing an empty line between lines ([#25317](https://github.com/RocketChat/Rocket.Chat/pull/25317)) -- i18n: Language update from LingoHub 🤖 on 2022-03-28Z ([#24971](https://github.com/RocketChat/Rocket.Chat/pull/24971)) +- Regression: Fix reply button not working when hideFlexTab is enabled ([#25306](https://github.com/RocketChat/Rocket.Chat/pull/25306)) -- Merge master into develop & Set version to 4.6.0-develop ([#24653](https://github.com/RocketChat/Rocket.Chat/pull/24653)) +- Regression: Fix services Docker build on CI ([#25181](https://github.com/RocketChat/Rocket.Chat/pull/25181)) -- Regression: Add createdOTR index ([#25017](https://github.com/RocketChat/Rocket.Chat/pull/25017)) +- Regression: Fix size of custom emoji and render emoji on thread message preview ([#25314](https://github.com/RocketChat/Rocket.Chat/pull/25314)) -- Regression: Call doesn't stop ringing after agent unregistration ([#24908](https://github.com/RocketChat/Rocket.Chat/pull/24908)) +- Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) -- Regression: Custom roles displaying ID instead of name on some admin screens ([#24999](https://github.com/RocketChat/Rocket.Chat/pull/24999)) + The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + + This temporarily switches to a fork of the matrix-appservice-bridge package. + + Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). - ![image](https://user-images.githubusercontent.com/55164754/160981416-555bcaa1-c075-4260-937c-64523472da43.png) - ![image](https://user-images.githubusercontent.com/55164754/160981452-6eae4e74-8425-4073-8256-472aba72b9db.png) +- Regression: Messages in new message template Crashing. ([#25327](https://github.com/RocketChat/Rocket.Chat/pull/25327)) -- Regression: Error is raised when there's no Asterisk queue available yet ([#24980](https://github.com/RocketChat/Rocket.Chat/pull/24980)) +- Regression: Revert Bugsnag version ([#25313](https://github.com/RocketChat/Rocket.Chat/pull/25313)) -- Regression: Fix account service login expiration ([#24920](https://github.com/RocketChat/Rocket.Chat/pull/24920)) +- Regression: Rocket.Chat Webapp not loading. ([#25349](https://github.com/RocketChat/Rocket.Chat/pull/25349)) -- Regression: Fix ParentRoomWithEndpointData in loop ([#24809](https://github.com/RocketChat/Rocket.Chat/pull/24809)) +- Regression: Show username and real name on the message system ([#25254](https://github.com/RocketChat/Rocket.Chat/pull/25254)) -- Regression: Fix unexpected errors breaking ddp-streamer ([#24948](https://github.com/RocketChat/Rocket.Chat/pull/24948)) +- Regression: Shows error if micro service cannot connect to Mongo ([#25301](https://github.com/RocketChat/Rocket.Chat/pull/25301)) -- Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests ([#24756](https://github.com/RocketChat/Rocket.Chat/pull/24756)) +- Regression: Use exact Node version on micro services Docker images ([#25287](https://github.com/RocketChat/Rocket.Chat/pull/25287)) -- Regression: Register services right away ([#24800](https://github.com/RocketChat/Rocket.Chat/pull/24800)) +- Regression: Validate empty fields for Message template ([#25250](https://github.com/RocketChat/Rocket.Chat/pull/25250)) -- Regression: Role Sync not always working ([#24850](https://github.com/RocketChat/Rocket.Chat/pull/24850)) +- Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208))
### 👩‍💻👨‍💻 Contributors 😍 -- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) - [@Himanshu664](https://github.com/Himanshu664) -- [@JMoVS](https://github.com/JMoVS) -- [@Muramatsu2602](https://github.com/Muramatsu2602) -- [@aadishJ01](https://github.com/aadishJ01) -- [@aswinidev](https://github.com/aswinidev) +- [@aakash-gitdev](https://github.com/aakash-gitdev) +- [@cuonghuunguyen](https://github.com/cuonghuunguyen) - [@dependabot[bot]](https://github.com/dependabot[bot]) -- [@eduardofcabrera](https://github.com/eduardofcabrera) -- [@gerzonc](https://github.com/gerzonc) -- [@nishant23122000](https://github.com/nishant23122000) -- [@ostjen](https://github.com/ostjen) -- [@tkurz](https://github.com/tkurz) +- [@kibonusp](https://github.com/kibonusp) +- [@paulobernardoaf](https://github.com/paulobernardoaf) +- [@sidmohanty11](https://github.com/sidmohanty11) ### 👩‍💻👨‍💻 Core Team 🤓 +- [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) - [@KevLehman](https://github.com/KevLehman) +- [@MarcosSpessatto](https://github.com/MarcosSpessatto) - [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@alansikora](https://github.com/alansikora) - [@albuquerquefabio](https://github.com/albuquerquefabio) - [@amolghode1981](https://github.com/amolghode1981) -- [@cauefcr](https://github.com/cauefcr) +- [@d-gubert](https://github.com/d-gubert) - [@debdutdeb](https://github.com/debdutdeb) - [@dougfabris](https://github.com/dougfabris) -- [@felipe-rod123](https://github.com/felipe-rod123) - [@filipemarins](https://github.com/filipemarins) - [@gabriellsh](https://github.com/gabriellsh) - [@geekgonecrazy](https://github.com/geekgonecrazy) - [@ggazzo](https://github.com/ggazzo) +- [@guijun13](https://github.com/guijun13) +- [@jeanfbrito](https://github.com/jeanfbrito) - [@juliajforesti](https://github.com/juliajforesti) -- [@matheusbsilva137](https://github.com/matheusbsilva137) - [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) -- [@renatobecker](https://github.com/renatobecker) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) +- [@souzaramon](https://github.com/souzaramon) - [@tassoevan](https://github.com/tassoevan) - [@tiagoevanp](https://github.com/tiagoevanp) +- [@tmontini](https://github.com/tmontini) +- [@weslley543](https://github.com/weslley543) - [@yash-rajpal](https://github.com/yash-rajpal) -# 4.5.6 -`2022-04-07 · 2 🐛 · 2 👩‍💻👨‍💻` +# 4.6.3 +`2022-04-19 · 1 🐛 · 1 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -2031,19 +3471,14 @@ ### 🐛 Bug fixes -- NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) - -- Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) - - Modify Meteor's `HTTP.call` to add back proxy support +- Desktop notification on multi-instance environments ([#25220](https://github.com/RocketChat/Rocket.Chat/pull/25220)) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) - [@sampaiodiego](https://github.com/sampaiodiego) -# 4.5.5 -`2022-03-30 · 2 🐛 · 2 🔍 · 6 👩‍💻👨‍💻` +# 4.6.2 +`2022-04-14 · 2 🐛 · 2 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -2054,43 +3489,29 @@ ### 🐛 Bug fixes -- High CPU usage caused by CallProvider ([#24994](https://github.com/RocketChat/Rocket.Chat/pull/24994)) - - Remove infinity loop inside useVoipClient hook. - - #closes #24970 - -- Multiple issues starting a new DM ([#24955](https://github.com/RocketChat/Rocket.Chat/pull/24955)) - - When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details. - - before: - https://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4 - - after: - https://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4 - -
-🔍 Minor changes +- Database indexes not being created ([#25101](https://github.com/RocketChat/Rocket.Chat/pull/25101)) +- Deactivating user breaks if user is the only room owner ([#24933](https://github.com/RocketChat/Rocket.Chat/pull/24933) by [@sidmohanty11](https://github.com/sidmohanty11)) -- Chore: Update Livechat ([#24990](https://github.com/RocketChat/Rocket.Chat/pull/24990)) + ## Before + + https://user-images.githubusercontent.com/73601258/160000871-cfc2f2a5-2a59-4d27-8049-7754d003dd48.mp4 + + + + ## After + https://user-images.githubusercontent.com/73601258/159998287-681ab475-ff33-4282-82ff-db751c59a392.mp4 -- Release 4.5.5 ([#24998](https://github.com/RocketChat/Rocket.Chat/pull/24998)) +### 👩‍💻👨‍💻 Contributors 😍 -
+- [@sidmohanty11](https://github.com/sidmohanty11) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@MartinSchoeler](https://github.com/MartinSchoeler) -- [@filipemarins](https://github.com/filipemarins) -- [@ggazzo](https://github.com/ggazzo) -- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) - [@sampaiodiego](https://github.com/sampaiodiego) -- [@tiagoevanp](https://github.com/tiagoevanp) -# 4.5.4 -`2022-03-24 · 1 🐛 · 1 🔍 · 3 👩‍💻👨‍💻` +# 4.6.1 +`2022-04-07 · 6 🐛 · 5 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -2101,24 +3522,36 @@ ### 🐛 Bug fixes -- SAML Force name to string ([#24930](https://github.com/RocketChat/Rocket.Chat/pull/24930)) +- FormData uploads not working ([#25069](https://github.com/RocketChat/Rocket.Chat/pull/25069)) -
-🔍 Minor changes +- Invitation links don't redirect to the registration form ([#25082](https://github.com/RocketChat/Rocket.Chat/pull/25082)) +- NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) -- Release 4.5.4 ([#24938](https://github.com/RocketChat/Rocket.Chat/pull/24938)) +- Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) -
+ Modify Meteor's `HTTP.call` to add back proxy support + +- Upgrade Tab showing for a split second ([#25050](https://github.com/RocketChat/Rocket.Chat/pull/25050)) + +- UserAutoComplete not rendering UserAvatar correctly ([#25055](https://github.com/RocketChat/Rocket.Chat/pull/25055)) + + ### before + ![Screen Shot 2022-04-04 at 16 50 21](https://user-images.githubusercontent.com/27704687/161620921-800bf66a-806d-4f83-b2e1-073c34215001.png) + + ### after + ![Screen Shot 2022-04-04 at 16 49 00](https://user-images.githubusercontent.com/27704687/161620720-3e27774d-c241-46ca-b764-932a9295d709.png) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) -- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@dougfabris](https://github.com/dougfabris) +- [@gabriellsh](https://github.com/gabriellsh) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@yash-rajpal](https://github.com/yash-rajpal) -# 4.5.3 -`2022-03-21 · 2 🚀 · 8 🐛 · 1 🔍 · 5 👩‍💻👨‍💻` +# 4.6.0 +`2022-04-01 · 2 🎉 · 7 🚀 · 57 🐛 · 62 🔍 · 34 👩‍💻👨‍💻` ### Engine versions - Node: `14.18.3` @@ -2126,482 +3559,2970 @@ - MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` - Apps-Engine: `1.31.0` -### 🚀 Improvements +### 🎉 New features -- Standarize queue behavior for managers and agents when subscribing ([#24837](https://github.com/RocketChat/Rocket.Chat/pull/24837)) +- Telemetry Events ([#24781](https://github.com/RocketChat/Rocket.Chat/pull/24781) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- UX - VoIP Call Component ([#24748](https://github.com/RocketChat/Rocket.Chat/pull/24748)) +- Upgrade Tab ([#24835](https://github.com/RocketChat/Rocket.Chat/pull/24835)) -### 🐛 Bug fixes + ![image](https://user-images.githubusercontent.com/27704687/160172260-c656282e-a487-4092-948d-d11c9bacb598.png) +### 🚀 Improvements -- **VOIP:** SidebarFooter component ([#24838](https://github.com/RocketChat/Rocket.Chat/pull/24838)) - - Improve the CallProvider code; +- **ENTERPRISE:** Don't start presence monitor when running micro services ([#24739](https://github.com/RocketChat/Rocket.Chat/pull/24739)) - - Adjust the text case of the VoIP component on the FooterSidebar; +- Adding new statistics related to voip and omnichannel ([#24887](https://github.com/RocketChat/Rocket.Chat/pull/24887)) - - Fix the bad behavior with the changes in queue's name. + - Total of Canned response messages sent + - Total of tags used + - Last-Chatted Agent Preferred (enabled/disabled) + - Assign new conversations to the contact manager (enabled/disabled) + - How to handle Visitor Abandonment setting + - Amount of chats placed on hold + - VoIP Enabled + - Amount of VoIP Calls + - Amount of VoIP Extensions connected + - Amount of Calls placed on hold (1x per call) + - Fixed Session Aggregation type definitions -- Broken build caused by PRs modifying same file differently ([#24863](https://github.com/RocketChat/Rocket.Chat/pull/24863)) +- New omnichannel statistics and async statistics processing. ([#24749](https://github.com/RocketChat/Rocket.Chat/pull/24749)) -- Custom script not being fired ([#24901](https://github.com/RocketChat/Rocket.Chat/pull/24901)) + https://app.clickup.com/t/1z4zg4e -- Disable voip button when call is in progress ([#24864](https://github.com/RocketChat/Rocket.Chat/pull/24864)) +- Standarize queue behavior for managers and agents when subscribing ([#24837](https://github.com/RocketChat/Rocket.Chat/pull/24837)) -- Show call icon only when user has extension associated ([#24752](https://github.com/RocketChat/Rocket.Chat/pull/24752)) +- Updated links in readme ([#24028](https://github.com/RocketChat/Rocket.Chat/pull/24028) by [@aswinidev](https://github.com/aswinidev)) -- Show only enabled departments on forward ([#24829](https://github.com/RocketChat/Rocket.Chat/pull/24829)) +- UX - VoIP Call Component ([#24748](https://github.com/RocketChat/Rocket.Chat/pull/24748)) -- VoIP button gets disabled whenever user status changes ([#24789](https://github.com/RocketChat/Rocket.Chat/pull/24789)) +- Voip Extensions disabled state ([#24750](https://github.com/RocketChat/Rocket.Chat/pull/24750)) -- Wrong param usage on queue summary call ([#24799](https://github.com/RocketChat/Rocket.Chat/pull/24799)) +### 🐛 Bug fixes -
-🔍 Minor changes +- "livechat/webrtc.call" endpoint not working ([#24804](https://github.com/RocketChat/Rocket.Chat/pull/24804)) -- Chore: Fix MongoDB versions on release notes ([#24877](https://github.com/RocketChat/Rocket.Chat/pull/24877)) +- "Match error" when converting a team to a channel ([#24629](https://github.com/RocketChat/Rocket.Chat/pull/24629)) -
+ - Fix "Match error" when trying to convert a channel to a team; -### 👩‍💻👨‍💻 Core Team 🤓 +- **ENTERPRISE:** Auto reload feature of ddp-streamer micro service ([#24793](https://github.com/RocketChat/Rocket.Chat/pull/24793)) -- [@KevLehman](https://github.com/KevLehman) -- [@amolghode1981](https://github.com/amolghode1981) -- [@ggazzo](https://github.com/ggazzo) -- [@sampaiodiego](https://github.com/sampaiodiego) -- [@tiagoevanp](https://github.com/tiagoevanp) +- **ENTERPRISE:** DDP streamer not sending data to all clients ([#24738](https://github.com/RocketChat/Rocket.Chat/pull/24738)) -# 4.5.2 -`2022-03-12 · 1 🚀 · 7 🐛 · 1 🔍 · 8 👩‍💻👨‍💻` +- **ENTERPRISE:** Notifications not being sent by ddp-streamer ([#24831](https://github.com/RocketChat/Rocket.Chat/pull/24831)) -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` +- **ENTERPRISE:** Presence micro service logic ([#24724](https://github.com/RocketChat/Rocket.Chat/pull/24724)) -### 🚀 Improvements +- **VOIP:** SidebarFooter component ([#24838](https://github.com/RocketChat/Rocket.Chat/pull/24838)) + - Improve the CallProvider code; + - Adjust the text case of the VoIP component on the FooterSidebar; + - Fix the bad behavior with the changes in queue's name. -- Voip Extensions disabled state ([#24750](https://github.com/RocketChat/Rocket.Chat/pull/24750)) +- `PaginatedSelectFiltered` not handling changes ([#24732](https://github.com/RocketChat/Rocket.Chat/pull/24732)) -### 🐛 Bug fixes +- API Error preventing adding an email to users without one (like bot/app users) ([#24709](https://github.com/RocketChat/Rocket.Chat/pull/24709)) +- Apple login script being loaded even when Apple Login is disabled. ([#24760](https://github.com/RocketChat/Rocket.Chat/pull/24760)) -- "livechat/webrtc.call" endpoint not working ([#24804](https://github.com/RocketChat/Rocket.Chat/pull/24804)) +- Apple OAuth ([#24879](https://github.com/RocketChat/Rocket.Chat/pull/24879)) -- `PaginatedSelectFiltered` not handling changes ([#24732](https://github.com/RocketChat/Rocket.Chat/pull/24732)) +- auto-join team channels not honoring user preferences ([#24779](https://github.com/RocketChat/Rocket.Chat/pull/24779) by [@ostjen](https://github.com/ostjen)) + +- Broken build caused by PRs modifying same file differently ([#24863](https://github.com/RocketChat/Rocket.Chat/pull/24863)) - Broken multiple OAuth integrations ([#24705](https://github.com/RocketChat/Rocket.Chat/pull/24705)) +- Components for user search ([#24677](https://github.com/RocketChat/Rocket.Chat/pull/24677)) + - Critical: Incorrect visitor getting assigned to a chat from apps ([#24805](https://github.com/RocketChat/Rocket.Chat/pull/24805)) -- Opening a new DM from user card ([#24623](https://github.com/RocketChat/Rocket.Chat/pull/24623)) +- Custom script not being fired ([#24901](https://github.com/RocketChat/Rocket.Chat/pull/24901)) - A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component. +- Date Message Export Filter Fix ([#24542](https://github.com/RocketChat/Rocket.Chat/pull/24542) by [@eduardofcabrera](https://github.com/eduardofcabrera)) -- Revert AutoComplete ([#24812](https://github.com/RocketChat/Rocket.Chat/pull/24812)) + Fix message export filter to get all messages between "from date" and "to date", including "to date". -- VoipExtensionsPage component call ([#24792](https://github.com/RocketChat/Rocket.Chat/pull/24792)) +- DDP Rate Limiter Translation key ([#24898](https://github.com/RocketChat/Rocket.Chat/pull/24898)) -
-🔍 Minor changes + Before: + image + + + Now: + image +- DDP streamer errors ([#24710](https://github.com/RocketChat/Rocket.Chat/pull/24710)) -- Regression: Fix ParentRoomWithEndpointData in loop ([#24809](https://github.com/RocketChat/Rocket.Chat/pull/24809)) +- Disable voip button when call is in progress ([#24864](https://github.com/RocketChat/Rocket.Chat/pull/24864)) -
+- Duplicated 'name' log key ([#24590](https://github.com/RocketChat/Rocket.Chat/pull/24590)) -### 👩‍💻👨‍💻 Core Team 🤓 +- Duplicated "jump to message" button on starred messages ([#24867](https://github.com/RocketChat/Rocket.Chat/pull/24867) by [@Himanshu664](https://github.com/Himanshu664)) -- [@KevLehman](https://github.com/KevLehman) -- [@MartinSchoeler](https://github.com/MartinSchoeler) -- [@debdutdeb](https://github.com/debdutdeb) -- [@ggazzo](https://github.com/ggazzo) -- [@juliajforesti](https://github.com/juliajforesti) -- [@murtaza98](https://github.com/murtaza98) -- [@sampaiodiego](https://github.com/sampaiodiego) -- [@tassoevan](https://github.com/tassoevan) +- External search providers not working ([#24860](https://github.com/RocketChat/Rocket.Chat/pull/24860) by [@tkurz](https://github.com/tkurz)) -# 4.5.1 -`2022-03-09 · 13 🐛 · 2 🔍 · 12 👩‍💻👨‍💻` +- German translation for Monitore ([#24785](https://github.com/RocketChat/Rocket.Chat/pull/24785) by [@JMoVS](https://github.com/JMoVS)) -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` +- Handle Other Formats inside Upload Avatar ([#24226](https://github.com/RocketChat/Rocket.Chat/pull/24226)) -### 🐛 Bug fixes + After resolving issue #24213 : + + + https://user-images.githubusercontent.com/53515714/150325012-91413025-786e-4ce0-ae75-629f6b05b024.mp4 +- High CPU usage caused by CallProvider ([#24994](https://github.com/RocketChat/Rocket.Chat/pull/24994)) -- Apple login script being loaded even when Apple Login is disabled. ([#24760](https://github.com/RocketChat/Rocket.Chat/pull/24760)) + Remove infinity loop inside useVoipClient hook. + + #closes #24970 -- Components for user search ([#24677](https://github.com/RocketChat/Rocket.Chat/pull/24677)) +- Ignore customClass on messages ([#24845](https://github.com/RocketChat/Rocket.Chat/pull/24845)) -- Duplicated 'name' log key ([#24590](https://github.com/RocketChat/Rocket.Chat/pull/24590)) +- LDAP avatars being rotated according to metadata even if the setting to rotate uploads is off ([#24320](https://github.com/RocketChat/Rocket.Chat/pull/24320)) + + - Use the `FileUpload_RotateImages` setting (**Administration > File Upload > Rotate images on upload**) to control whether avatars should be rotated automatically based on their data (XEIF); + - Display the avatar image preview (orientation) according to the `FileUpload_RotateImages` setting. + +- Missing dependency on useEffect at CallProvider ([#24882](https://github.com/RocketChat/Rocket.Chat/pull/24882)) - Missing username on messages imported from Slack ([#24674](https://github.com/RocketChat/Rocket.Chat/pull/24674)) - Fix missing sender's username on messages imported from Slack. +- Nextcloud OAuth for incomplete token URL ([#24476](https://github.com/RocketChat/Rocket.Chat/pull/24476)) + - no id of room closer in livechat-close message ([#24683](https://github.com/RocketChat/Rocket.Chat/pull/24683)) -- Reload roomslist after successful deletion of a room from admin panel. ([#23795](https://github.com/RocketChat/Rocket.Chat/pull/23795) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) +- Opening a new DM from user card ([#24623](https://github.com/RocketChat/Rocket.Chat/pull/24623)) + + A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component. + +- Prevent call button toggle when user is on call ([#24758](https://github.com/RocketChat/Rocket.Chat/pull/24758)) + +- Prune Message issue ([#24424](https://github.com/RocketChat/Rocket.Chat/pull/24424)) - Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`. - This allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent. +- Push privacy config to not show username not being respected ([#24606](https://github.com/RocketChat/Rocket.Chat/pull/24606)) + +- Register with Secret URL ([#24921](https://github.com/RocketChat/Rocket.Chat/pull/24921)) + +- Reload roomslist after successful deletion of a room from admin panel. ([#23795](https://github.com/RocketChat/Rocket.Chat/pull/23795) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) + Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`. + This allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent. + Also added a succes toast message after the successful deletion of room. +- Revert AutoComplete ([#24812](https://github.com/RocketChat/Rocket.Chat/pull/24812)) + +- Room archived/unarchived system messages aren't sent when editing room settings ([#24897](https://github.com/RocketChat/Rocket.Chat/pull/24897)) + + - Send the "Room archived" and "Room unarchived" system messages when editing room settings (and not only when rooms are archived/unarchived with the slash-command); + - Fix the "Hide System Messages" option for the "Room archived" and "Room unarchived" system messages; + +- room message not load when is a new message ([#24955](https://github.com/RocketChat/Rocket.Chat/pull/24955)) + + When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details. + + before: + https://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4 + + after: + https://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4 + - Room's message count not being incremented on import ([#24696](https://github.com/RocketChat/Rocket.Chat/pull/24696)) - Fix rooms' message counter not being incremented on message import. +- SAML Force name to string ([#24930](https://github.com/RocketChat/Rocket.Chat/pull/24930)) + +- Several issues related to custom roles ([#24052](https://github.com/RocketChat/Rocket.Chat/pull/24052)) + + - Throw an error when trying to delete a role (User or Subscription role) that are still being used; + - Fix "Invalid Role" error for custom roles in Role Editing sidebar; + - Fix "Users in Role" screen for custom roles. + +- Show call icon only when user has extension associated ([#24752](https://github.com/RocketChat/Rocket.Chat/pull/24752)) + - Show only available agents on extension association modal ([#24680](https://github.com/RocketChat/Rocket.Chat/pull/24680)) +- Show only enabled departments on forward ([#24829](https://github.com/RocketChat/Rocket.Chat/pull/24829)) + - System messages are sent when adding or removing a group from a team ([#24743](https://github.com/RocketChat/Rocket.Chat/pull/24743)) - Do not send system messages when adding or removing a new or existing _group_ from a team. - Typo and placeholder on wrap up call modal ([#24737](https://github.com/RocketChat/Rocket.Chat/pull/24737)) -- Typo in wrap-up term ([#24661](https://github.com/RocketChat/Rocket.Chat/pull/24661)) +- Typo in wrap-up term ([#24661](https://github.com/RocketChat/Rocket.Chat/pull/24661)) + +- VoIP button gets disabled whenever user status changes ([#24789](https://github.com/RocketChat/Rocket.Chat/pull/24789)) + +- VoIP Enable/Disable setting on CallContext/CallProvider Notifications ([#24607](https://github.com/RocketChat/Rocket.Chat/pull/24607)) + +- Voip Stream Reinitialization Error ([#24657](https://github.com/RocketChat/Rocket.Chat/pull/24657)) + +- VoipExtensionsPage component call ([#24792](https://github.com/RocketChat/Rocket.Chat/pull/24792)) + +- Wrong business hour behavior ([#24896](https://github.com/RocketChat/Rocket.Chat/pull/24896)) + +- Wrong param usage on queue summary call ([#24799](https://github.com/RocketChat/Rocket.Chat/pull/24799)) + +
+🔍 Minor changes + + +- Bump @rocket.chat/emitter from 0.31.4 to 0.31.9 in /ee/server/services ([#25021](https://github.com/RocketChat/Rocket.Chat/pull/25021) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/message-parser from 0.31.4 to 0.31.9 in /ee/server/services ([#25019](https://github.com/RocketChat/Rocket.Chat/pull/25019) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/string-helpers from 0.31.4 to 0.31.9 in /ee/server/services ([#25018](https://github.com/RocketChat/Rocket.Chat/pull/25018) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/ui-kit from 0.31.4 to 0.31.9 in /ee/server/services ([#25020](https://github.com/RocketChat/Rocket.Chat/pull/25020) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/clipboard from 2.0.1 to 2.0.7 ([#24832](https://github.com/RocketChat/Rocket.Chat/pull/24832) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/mailparser from 3.0.2 to 3.4.0 ([#24833](https://github.com/RocketChat/Rocket.Chat/pull/24833) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/nodemailer from 6.4.2 to 6.4.4 ([#24822](https://github.com/RocketChat/Rocket.Chat/pull/24822) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/ws from 8.2.3 to 8.5.2 in /ee/server/services ([#24666](https://github.com/RocketChat/Rocket.Chat/pull/24666) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/ws from 8.5.2 to 8.5.3 in /ee/server/services ([#24820](https://github.com/RocketChat/Rocket.Chat/pull/24820) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump actions/checkout from 2 to 3 ([#24668](https://github.com/RocketChat/Rocket.Chat/pull/24668) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump actions/setup-node from 2 to 3 ([#24642](https://github.com/RocketChat/Rocket.Chat/pull/24642) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.0 to 1.19.2 ([#24821](https://github.com/RocketChat/Rocket.Chat/pull/24821) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump is-svg from 4.3.1 to 4.3.2 ([#24801](https://github.com/RocketChat/Rocket.Chat/pull/24801) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump jschardet from 1.6.0 to 3.0.0 ([#23121](https://github.com/RocketChat/Rocket.Chat/pull/23121) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino from 7.8.0 to 7.8.1 in /ee/server/services ([#24783](https://github.com/RocketChat/Rocket.Chat/pull/24783) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino from 7.8.1 to 7.9.1 in /ee/server/services ([#24869](https://github.com/RocketChat/Rocket.Chat/pull/24869) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.1 to 7.5.2 in /ee/server/services ([#24689](https://github.com/RocketChat/Rocket.Chat/pull/24689) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.2 to 7.5.3 in /ee/server/services ([#24698](https://github.com/RocketChat/Rocket.Chat/pull/24698) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services ([#24870](https://github.com/RocketChat/Rocket.Chat/pull/24870) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump prometheus-gc-stats from 0.6.2 to 0.6.3 ([#24803](https://github.com/RocketChat/Rocket.Chat/pull/24803) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump ts-node from 10.5.0 to 10.6.0 in /ee/server/services ([#24667](https://github.com/RocketChat/Rocket.Chat/pull/24667) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump ts-node from 10.6.0 to 10.7.0 in /ee/server/services ([#24716](https://github.com/RocketChat/Rocket.Chat/pull/24716) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump url-parse from 1.5.7 to 1.5.10 ([#24640](https://github.com/RocketChat/Rocket.Chat/pull/24640) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore: Add E2E tests for livechat/room.close ([#24729](https://github.com/RocketChat/Rocket.Chat/pull/24729) by [@Muramatsu2602](https://github.com/Muramatsu2602)) + + * Create a new test suite file under tests/end-to-end/api/livechat + * Create tests for the following endpoint: + + ivechat/room.close + +- Chore: Add E2E tests for livechat/visitor ([#24764](https://github.com/RocketChat/Rocket.Chat/pull/24764) by [@Muramatsu2602](https://github.com/Muramatsu2602)) + + - Create a new test suite file under tests/end-to-end/api/livechat + - Create tests for the following endpoints: + + livechat/visitor (create visitor, update visitor, add custom fields to visitors) + +- Chore: add some missing REST definitions ([#24925](https://github.com/RocketChat/Rocket.Chat/pull/24925) by [@gerzonc](https://github.com/gerzonc)) + + On the [mobile client](https://github.com/RocketChat/Rocket.Chat.ReactNative), we made an effort to collect more `REST API` definitions that are missing on the server side during our migration to TypeScript. Since we're both migrating to TypeScript, we thought it would be a good idea to share those so you guys can benefit from our initiative. + +- Chore: added Server Instances endpoint types ([#24507](https://github.com/RocketChat/Rocket.Chat/pull/24507)) + + Created typing for endpoint definitions on `instances.ts`. + +- Chore: added settings endpoint types ([#24506](https://github.com/RocketChat/Rocket.Chat/pull/24506)) + + Created typing for endpoint definitions on `settings.ts`. + +- Chore: APIClass types ([#24747](https://github.com/RocketChat/Rocket.Chat/pull/24747)) + + This pull request creates a new `restivus` module (.d.ts) for the `api.js` file. + +- Chore: Bump Fuselage packages ([#25015](https://github.com/RocketChat/Rocket.Chat/pull/25015)) + + It uses the last stable version of Fuselage packages. + +- Chore: Convert server functions from javascript to typescript ([#24384](https://github.com/RocketChat/Rocket.Chat/pull/24384)) + + This pull request will be used to rewrite some functions on the Chat Engine to Typescript, in order to increase security and specify variable types on the code. + +- Chore: converted more hooks to typescript ([#24628](https://github.com/RocketChat/Rocket.Chat/pull/24628)) + + Converted some functions on `client/hooks/` from JavaScript to Typescript. + +- Chore: Fix Cypress tests ([#24544](https://github.com/RocketChat/Rocket.Chat/pull/24544)) + +- Chore: Fix grammatical errors in Code of Conduct ([#24759](https://github.com/RocketChat/Rocket.Chat/pull/24759) by [@aadishJ01](https://github.com/aadishJ01)) + +- Chore: fix grammatical errors in Features ([#24771](https://github.com/RocketChat/Rocket.Chat/pull/24771) by [@aadishJ01](https://github.com/aadishJ01)) + +- Chore: Fix MongoDB versions on release notes ([#24877](https://github.com/RocketChat/Rocket.Chat/pull/24877)) + +- Chore: Get Settings Statistics ([#24397](https://github.com/RocketChat/Rocket.Chat/pull/24397)) + +- Chore: Improve logger to allow log of `unknown` values ([#24726](https://github.com/RocketChat/Rocket.Chat/pull/24726)) + +- Chore: Improvements on role syncing (ldap, oauth and saml) ([#23824](https://github.com/RocketChat/Rocket.Chat/pull/23824)) + +- Chore: Micro services fixes and cleanup ([#24753](https://github.com/RocketChat/Rocket.Chat/pull/24753)) + +- Chore: Remove old scripts ([#24911](https://github.com/RocketChat/Rocket.Chat/pull/24911)) + +- Chore: Skip local services changes when shutting down duplicated services ([#24810](https://github.com/RocketChat/Rocket.Chat/pull/24810)) + +- Chore: Storybook mocking and examples improved ([#24969](https://github.com/RocketChat/Rocket.Chat/pull/24969)) + + - Stories from `ee/` included; + - Differentiate root story kinds; + - Mocking of `ServerContext` via Storybook parameters. + +- Chore: Update Livechat ([#24754](https://github.com/RocketChat/Rocket.Chat/pull/24754)) + +- Chore: Update Livechat ([#24990](https://github.com/RocketChat/Rocket.Chat/pull/24990)) + +- Chore(deps-dev): Bump @types/mock-require from 2.0.0 to 2.0.1 ([#24574](https://github.com/RocketChat/Rocket.Chat/pull/24574) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- i18n: Language update from LingoHub 🤖 on 2022-02-28Z ([#24644](https://github.com/RocketChat/Rocket.Chat/pull/24644)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-07Z ([#24717](https://github.com/RocketChat/Rocket.Chat/pull/24717)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-14Z ([#24823](https://github.com/RocketChat/Rocket.Chat/pull/24823)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-21Z ([#24895](https://github.com/RocketChat/Rocket.Chat/pull/24895)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-28Z ([#24971](https://github.com/RocketChat/Rocket.Chat/pull/24971)) + +- Merge master into develop & Set version to 4.6.0-develop ([#24653](https://github.com/RocketChat/Rocket.Chat/pull/24653)) + +- Regression: Add createdOTR index ([#25017](https://github.com/RocketChat/Rocket.Chat/pull/25017)) + +- Regression: Call doesn't stop ringing after agent unregistration ([#24908](https://github.com/RocketChat/Rocket.Chat/pull/24908)) + +- Regression: Custom roles displaying ID instead of name on some admin screens ([#24999](https://github.com/RocketChat/Rocket.Chat/pull/24999)) + + ![image](https://user-images.githubusercontent.com/55164754/160981416-555bcaa1-c075-4260-937c-64523472da43.png) + ![image](https://user-images.githubusercontent.com/55164754/160981452-6eae4e74-8425-4073-8256-472aba72b9db.png) + +- Regression: Error is raised when there's no Asterisk queue available yet ([#24980](https://github.com/RocketChat/Rocket.Chat/pull/24980)) + +- Regression: Fix account service login expiration ([#24920](https://github.com/RocketChat/Rocket.Chat/pull/24920)) + +- Regression: Fix ParentRoomWithEndpointData in loop ([#24809](https://github.com/RocketChat/Rocket.Chat/pull/24809)) + +- Regression: Fix unexpected errors breaking ddp-streamer ([#24948](https://github.com/RocketChat/Rocket.Chat/pull/24948)) + +- Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests ([#24756](https://github.com/RocketChat/Rocket.Chat/pull/24756)) + +- Regression: Register services right away ([#24800](https://github.com/RocketChat/Rocket.Chat/pull/24800)) + +- Regression: Role Sync not always working ([#24850](https://github.com/RocketChat/Rocket.Chat/pull/24850)) + +
+ +### 👩‍💻👨‍💻 Contributors 😍 + +- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) +- [@Himanshu664](https://github.com/Himanshu664) +- [@JMoVS](https://github.com/JMoVS) +- [@Muramatsu2602](https://github.com/Muramatsu2602) +- [@aadishJ01](https://github.com/aadishJ01) +- [@aswinidev](https://github.com/aswinidev) +- [@dependabot[bot]](https://github.com/dependabot[bot]) +- [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@gerzonc](https://github.com/gerzonc) +- [@ostjen](https://github.com/ostjen) +- [@tkurz](https://github.com/tkurz) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@KevLehman](https://github.com/KevLehman) +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@albuquerquefabio](https://github.com/albuquerquefabio) +- [@amolghode1981](https://github.com/amolghode1981) +- [@cauefcr](https://github.com/cauefcr) +- [@debdutdeb](https://github.com/debdutdeb) +- [@dougfabris](https://github.com/dougfabris) +- [@felipe-rod123](https://github.com/felipe-rod123) +- [@filipemarins](https://github.com/filipemarins) +- [@gabriellsh](https://github.com/gabriellsh) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@ggazzo](https://github.com/ggazzo) +- [@juliajforesti](https://github.com/juliajforesti) +- [@matheusbsilva137](https://github.com/matheusbsilva137) +- [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@renatobecker](https://github.com/renatobecker) +- [@rodrigok](https://github.com/rodrigok) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) +- [@tiagoevanp](https://github.com/tiagoevanp) +- [@yash-rajpal](https://github.com/yash-rajpal) + +# 4.5.6 +`2022-04-07 · 2 🐛 · 2 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🐛 Bug fixes + + +- NPS never finishing sending results ([#25067](https://github.com/RocketChat/Rocket.Chat/pull/25067)) + +- Proxy settings being ignored ([#25022](https://github.com/RocketChat/Rocket.Chat/pull/25022)) + + Modify Meteor's `HTTP.call` to add back proxy support + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@sampaiodiego](https://github.com/sampaiodiego) + +# 4.5.5 +`2022-03-30 · 2 🐛 · 2 🔍 · 6 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🐛 Bug fixes + + +- High CPU usage caused by CallProvider ([#24994](https://github.com/RocketChat/Rocket.Chat/pull/24994)) + + Remove infinity loop inside useVoipClient hook. + + #closes #24970 + +- Multiple issues starting a new DM ([#24955](https://github.com/RocketChat/Rocket.Chat/pull/24955)) + + When the room object is searched for the first time, it does not exist on the front object yet (subscription), adding a fallback search for room list will guarantee to search the room details. + + before: + https://user-images.githubusercontent.com/9275105/160223241-d2319f3e-82c5-47d6-867f-695ab2361a17.mp4 + + after: + https://user-images.githubusercontent.com/9275105/160223244-84d0d2a1-3d95-464d-8b8a-e264b0d4d690.mp4 + +
+🔍 Minor changes + + +- Chore: Update Livechat ([#24990](https://github.com/RocketChat/Rocket.Chat/pull/24990)) + +- Release 4.5.5 ([#24998](https://github.com/RocketChat/Rocket.Chat/pull/24998)) + +
+ +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@filipemarins](https://github.com/filipemarins) +- [@ggazzo](https://github.com/ggazzo) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tiagoevanp](https://github.com/tiagoevanp) + +# 4.5.4 +`2022-03-24 · 1 🐛 · 1 🔍 · 3 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🐛 Bug fixes + + +- SAML Force name to string ([#24930](https://github.com/RocketChat/Rocket.Chat/pull/24930)) + +
+🔍 Minor changes + + +- Release 4.5.4 ([#24938](https://github.com/RocketChat/Rocket.Chat/pull/24938)) + +
+ +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) + +# 4.5.3 +`2022-03-21 · 2 🚀 · 8 🐛 · 1 🔍 · 5 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🚀 Improvements + + +- Standarize queue behavior for managers and agents when subscribing ([#24837](https://github.com/RocketChat/Rocket.Chat/pull/24837)) + +- UX - VoIP Call Component ([#24748](https://github.com/RocketChat/Rocket.Chat/pull/24748)) + +### 🐛 Bug fixes + + +- **VOIP:** SidebarFooter component ([#24838](https://github.com/RocketChat/Rocket.Chat/pull/24838)) + + - Improve the CallProvider code; + - Adjust the text case of the VoIP component on the FooterSidebar; + - Fix the bad behavior with the changes in queue's name. + +- Broken build caused by PRs modifying same file differently ([#24863](https://github.com/RocketChat/Rocket.Chat/pull/24863)) + +- Custom script not being fired ([#24901](https://github.com/RocketChat/Rocket.Chat/pull/24901)) + +- Disable voip button when call is in progress ([#24864](https://github.com/RocketChat/Rocket.Chat/pull/24864)) + +- Show call icon only when user has extension associated ([#24752](https://github.com/RocketChat/Rocket.Chat/pull/24752)) + +- Show only enabled departments on forward ([#24829](https://github.com/RocketChat/Rocket.Chat/pull/24829)) + +- VoIP button gets disabled whenever user status changes ([#24789](https://github.com/RocketChat/Rocket.Chat/pull/24789)) + +- Wrong param usage on queue summary call ([#24799](https://github.com/RocketChat/Rocket.Chat/pull/24799)) + +
+🔍 Minor changes + + +- Chore: Fix MongoDB versions on release notes ([#24877](https://github.com/RocketChat/Rocket.Chat/pull/24877)) + +
+ +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@KevLehman](https://github.com/KevLehman) +- [@amolghode1981](https://github.com/amolghode1981) +- [@ggazzo](https://github.com/ggazzo) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tiagoevanp](https://github.com/tiagoevanp) + +# 4.5.2 +`2022-03-12 · 1 🚀 · 7 🐛 · 1 🔍 · 8 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🚀 Improvements + + +- Voip Extensions disabled state ([#24750](https://github.com/RocketChat/Rocket.Chat/pull/24750)) + +### 🐛 Bug fixes + + +- "livechat/webrtc.call" endpoint not working ([#24804](https://github.com/RocketChat/Rocket.Chat/pull/24804)) + +- `PaginatedSelectFiltered` not handling changes ([#24732](https://github.com/RocketChat/Rocket.Chat/pull/24732)) + +- Broken multiple OAuth integrations ([#24705](https://github.com/RocketChat/Rocket.Chat/pull/24705)) + +- Critical: Incorrect visitor getting assigned to a chat from apps ([#24805](https://github.com/RocketChat/Rocket.Chat/pull/24805)) + +- Opening a new DM from user card ([#24623](https://github.com/RocketChat/Rocket.Chat/pull/24623)) + + A race condition on `useRoomIcon` -- delayed merge of rooms and subscriptions -- was causing a UI crash whenever someone tried to open a DM from the user card component. + +- Revert AutoComplete ([#24812](https://github.com/RocketChat/Rocket.Chat/pull/24812)) + +- VoipExtensionsPage component call ([#24792](https://github.com/RocketChat/Rocket.Chat/pull/24792)) + +
+🔍 Minor changes + + +- Regression: Fix ParentRoomWithEndpointData in loop ([#24809](https://github.com/RocketChat/Rocket.Chat/pull/24809)) + +
+ +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@KevLehman](https://github.com/KevLehman) +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@debdutdeb](https://github.com/debdutdeb) +- [@ggazzo](https://github.com/ggazzo) +- [@juliajforesti](https://github.com/juliajforesti) +- [@murtaza98](https://github.com/murtaza98) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) + +# 4.5.1 +`2022-03-09 · 13 🐛 · 2 🔍 · 12 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🐛 Bug fixes + + +- Apple login script being loaded even when Apple Login is disabled. ([#24760](https://github.com/RocketChat/Rocket.Chat/pull/24760)) + +- Components for user search ([#24677](https://github.com/RocketChat/Rocket.Chat/pull/24677)) + +- Duplicated 'name' log key ([#24590](https://github.com/RocketChat/Rocket.Chat/pull/24590)) + +- Missing username on messages imported from Slack ([#24674](https://github.com/RocketChat/Rocket.Chat/pull/24674)) + + - Fix missing sender's username on messages imported from Slack. + +- no id of room closer in livechat-close message ([#24683](https://github.com/RocketChat/Rocket.Chat/pull/24683)) + +- Reload roomslist after successful deletion of a room from admin panel. ([#23795](https://github.com/RocketChat/Rocket.Chat/pull/23795) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) + + Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`. + This allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent. + + Also added a succes toast message after the successful deletion of room. + +- Room's message count not being incremented on import ([#24696](https://github.com/RocketChat/Rocket.Chat/pull/24696)) + + - Fix rooms' message counter not being incremented on message import. + +- Show only available agents on extension association modal ([#24680](https://github.com/RocketChat/Rocket.Chat/pull/24680)) + +- System messages are sent when adding or removing a group from a team ([#24743](https://github.com/RocketChat/Rocket.Chat/pull/24743)) + + - Do not send system messages when adding or removing a new or existing _group_ from a team. + +- Typo and placeholder on wrap up call modal ([#24737](https://github.com/RocketChat/Rocket.Chat/pull/24737)) + +- Typo in wrap-up term ([#24661](https://github.com/RocketChat/Rocket.Chat/pull/24661)) + +- VoIP Enable/Disable setting on CallContext/CallProvider Notifications ([#24607](https://github.com/RocketChat/Rocket.Chat/pull/24607)) + +- Voip Stream Reinitialization Error ([#24657](https://github.com/RocketChat/Rocket.Chat/pull/24657)) + +
+🔍 Minor changes + + +- Chore: Update Livechat ([#24754](https://github.com/RocketChat/Rocket.Chat/pull/24754)) + +- Release 4.5.1 ([#24782](https://github.com/RocketChat/Rocket.Chat/pull/24782) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) & [@cuonghuunguyen](https://github.com/cuonghuunguyen)) + +
+ +### 👩‍💻👨‍💻 Contributors 😍 + +- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) +- [@cuonghuunguyen](https://github.com/cuonghuunguyen) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@KevLehman](https://github.com/KevLehman) +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@amolghode1981](https://github.com/amolghode1981) +- [@juliajforesti](https://github.com/juliajforesti) +- [@matheusbsilva137](https://github.com/matheusbsilva137) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@renatobecker](https://github.com/renatobecker) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) +- [@tiagoevanp](https://github.com/tiagoevanp) + +# 4.5.0 +`2022-02-28 · 3 🎉 · 15 🚀 · 19 🐛 · 72 🔍 · 30 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- Apps-Engine: `1.31.0` + +### 🎉 New features + + +- E2E password generator ([#24114](https://github.com/RocketChat/Rocket.Chat/pull/24114) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Marketplace sort filter ([#24567](https://github.com/RocketChat/Rocket.Chat/pull/24567) by [@ujorgeleite](https://github.com/ujorgeleite)) + + Implemented a sort filter for the marketplace screen. This component sorts the marketplace apps list in 4 ways, alphabetical order(A-Z), inverse alphabetical order(Z-A), most recently updated(MRU), and least recent updated(LRU). Besides that, I've generalized some components and types to increase code reusability, renamed some helpers as well as deleted some useless ones, and inserted the necessary new translations on the English i18n dictionary. + Demo gif: + ![Marketplace sort filter](https://user-images.githubusercontent.com/43561537/155033709-e07a6306-a85a-4f7f-9624-b53ba5dd7fa9.gif) + +- VoIP Support for Omnichannel ([#23102](https://github.com/RocketChat/Rocket.Chat/pull/23102)) + + - Created VoipService to manage VoIP connections and PBX connection + - Created LivechatVoipService that will handle custom cases for livechat (creating rooms, assigning chats to queue, actions when call is finished, etc) + - Created Basic interfaces to support new services and new model + - Created Endpoints for management interfaces + - Implemented asterisk connector on VoIP service + - Created UI components to show calls incoming and to allow answering/rejecting calls + - Added new settings to control call server/management server connection values + - Added endpoints to associate Omnichannel Agents with PBX Extensions + - Added support for event listening on server side, to get metadata about calls being received/ongoing + - Created new pages to update settings & to see user-extension association + - Created new page to see ongoing calls (and past calls) + - Added support for remote hangup/hold on calls + - Implemented call metrics calculation (hold time, waiting time, talk time) + - Show a notificaiton when call is received + +### 🚀 Improvements + + +- **ENTERPRISE:** Improve how micro services are loaded ([#24388](https://github.com/RocketChat/Rocket.Chat/pull/24388)) + +- Add return button in chats opened from the list of current chats ([#24458](https://github.com/RocketChat/Rocket.Chat/pull/24458) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The new return button for Omnichannel chats came out with release 3.15 but the feature was only available for chats that were opened from Omnichannel Contact Center. + Now, the same UI/UX is supported for chats opened from Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153283190-bd5c9748-c36b-4874-a704-6043afc7e3a1.png) + + The chat now opens in the Omnichannel settings and has the return button so the user can go back to the Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153285591-fad8e4a0-d2ea-4a02-8b2a-15e383b3c876.png) + +- Add tooltips on action buttons of Canned Response message composer ([#24483](https://github.com/RocketChat/Rocket.Chat/pull/24483) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The tooltips were missing on the action buttons of CR message composer. + + ![image](https://user-images.githubusercontent.com/32396925/153620327-91107245-4b47-4d39-a99a-6da6d1cf5734.png) + + Users can now feel more encouraged to use these actions knowing what they are supposed to do. + +- Add user to room on "Click to Join!" button press ([#24041](https://github.com/RocketChat/Rocket.Chat/pull/24041) by [@ostjen](https://github.com/ostjen)) + + - Add user to room on "Click to Join!" button press; + - Display the "Join" button in discussions inside channels (keeping the behavior consistent with discussions inside groups). + +- Added a new "All" tab which shows all integrations in Integrations ([#24109](https://github.com/RocketChat/Rocket.Chat/pull/24109) by [@aswinidev](https://github.com/aswinidev)) + +- ChatBox Text to File Description ([#24451](https://github.com/RocketChat/Rocket.Chat/pull/24451) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + The text content from chatbox goes to the file description when drag and drop a file. + +- Close modal on esc and outside click ([#24275](https://github.com/RocketChat/Rocket.Chat/pull/24275)) + + This is a QUICK change in order to close modals pressing Esc button and clicking outside of it **intentionally**. + +- CloudLoginModal visual consistency ([#24334](https://github.com/RocketChat/Rocket.Chat/pull/24334)) + + ### before + ![image](https://user-images.githubusercontent.com/27704687/151585064-dc6a1e29-9903-4241-8fbd-dfbe6c55fbef.png) + + ### after + ![Screen Shot 2022-01-28 at 13 32 02](https://user-images.githubusercontent.com/27704687/151585101-75b98502-9aae-4198-bc3e-4956750e5d8b.png) + +- Convert tag edit with department data to tsx ([#24369](https://github.com/RocketChat/Rocket.Chat/pull/24369) by [@LucasFASouza](https://github.com/LucasFASouza)) + +- Descriptive tooltip for Encrypted Key on Room Header ([#24121](https://github.com/RocketChat/Rocket.Chat/pull/24121)) + +- OTR system messages ([#24382](https://github.com/RocketChat/Rocket.Chat/pull/24382)) + + OTR system messages to indicate key refresh and joining chat to users. + +- Purchase Type Filter for marketplace apps and Categories filter anchor refactoring ([#24454](https://github.com/RocketChat/Rocket.Chat/pull/24454)) + + Implemented a filter by purchase type(free or paid) component for the apps screen of the marketplace. Besides that, new entries on the dictionary, fixed some parts of the App type (purchaseType was typed as unknown and price as string), and created some helpers to work alongside the filter. Will be refactoring the categories filter anchor and then will open this PR for reviews. + + Demo gif: + ![purchaseTypeFIlter](https://user-images.githubusercontent.com/43561537/153101228-7b7ebdc3-2d34-420f-aa9d-f7cbc8d4b53f.gif) + + Refactored the categories filter anchor from a plain fuselage select to a select button with dynamic colors. + Demo gif: + ![New categories filter anchor(PR)](https://user-images.githubusercontent.com/43561537/153422427-28012b7d-e0ec-45f4-861d-c9368c57ad04.gif) + +- Replace AutoComplete in UserAutoComplete & UserAutoCompleteMultiple components ([#24529](https://github.com/RocketChat/Rocket.Chat/pull/24529)) + + This PR replaces a deprecated fuselage's component `AutoComplete` in favor of `Select` and `MultiSelect` which fixes some of UX/UI issues in selecting users + + ### before + ![Screen Shot 2022-02-19 at 13 33 28](https://user-images.githubusercontent.com/27704687/154809737-8181a06c-4f20-48ea-90f7-01e828b9a452.png) + + ### after + ![Screen Shot 2022-02-19 at 13 30 58](https://user-images.githubusercontent.com/27704687/154809653-a8ec9a80-c0dd-4a25-9c00-0f96147d79e9.png) + +- Skip encryption for slash commands in E2E rooms ([#24475](https://github.com/RocketChat/Rocket.Chat/pull/24475)) + + Currently Slash Commands don't work in an E2EE room, as we encrypt the message before slash command is detected by the server, So removed encryption for slash commands in e2e rooms. + +- Team system messages feedback ([#24209](https://github.com/RocketChat/Rocket.Chat/pull/24209) by [@ostjen](https://github.com/ostjen)) + + - Delete some keys that aren't being used (eg: User_left_female). + - Add new Teams' system messages: + - `added-user-to-team`: **added** @\user to this Team; + - `removed-user-from-team`: **removed** @\user from this Team; + - `user-converted-to-team`: **converted** #\room to a Team; + - `user-converted-to-channel`: **converted** #\room to a Channel; + - `user-removed-room-from-team`: **removed** @\user from this Team; + - `user-deleted-room-from-team`: **deleted** #\room from this Team; + - `user-added-room-to-team`: **deleted** #\room to this Team; + - Add the corresponding options to hide each new system message and the missing `ujt` and `ult` hide options. + +### 🐛 Bug fixes + + +- 2FA via email when logging in using OAuth ([#24572](https://github.com/RocketChat/Rocket.Chat/pull/24572)) + +- Add ?close to OAuth callback url ([#24381](https://github.com/RocketChat/Rocket.Chat/pull/24381)) + +- GDPR action to forget visitor data on request ([#24441](https://github.com/RocketChat/Rocket.Chat/pull/24441)) + +- Implement client errors on ddp-streamer ([#24310](https://github.com/RocketChat/Rocket.Chat/pull/24310)) + +- Inconsistent validation of user's access to rooms ([#24037](https://github.com/RocketChat/Rocket.Chat/pull/24037) by [@ostjen](https://github.com/ostjen)) + +- Issues on selecting users when importing CSV ([#24253](https://github.com/RocketChat/Rocket.Chat/pull/24253)) + + * Fix users selecting by fixing their _id + * Add condition to disable 'Start importing' button if `usersCount`, `channelsCount` and `messageCount` equals 0, or if messageCount is alone + * Remove `disabled={usersCount === 0}` on user Tab + +- OAuth mismatch redirect_uri error ([#24450](https://github.com/RocketChat/Rocket.Chat/pull/24450)) + +- Oembed request not respecting payload limit ([#24418](https://github.com/RocketChat/Rocket.Chat/pull/24418)) + +- Omnichannel managers can't join chats in progress ([#24553](https://github.com/RocketChat/Rocket.Chat/pull/24553)) + +- Outgoing webhook without scripts not saving messages ([#24401](https://github.com/RocketChat/Rocket.Chat/pull/24401)) + +- Prevent Apps Bridge to remove visitor status from room ([#24305](https://github.com/RocketChat/Rocket.Chat/pull/24305)) + +- Read receipts showing first messages of the room as read even if not read by everyone ([#24508](https://github.com/RocketChat/Rocket.Chat/pull/24508)) + +- respect `Accounts_Registration_Users_Default_Roles` setting ([#24173](https://github.com/RocketChat/Rocket.Chat/pull/24173)) + + - Fix `user` role being added as default regardless of the `Accounts_Registration_Users_Default_Roles` setting. + +- Room context tabs not working in Omnichannel current chats page ([#24559](https://github.com/RocketChat/Rocket.Chat/pull/24559)) + +- Skip admin info in setup wizard for servers with admin registered ([#24485](https://github.com/RocketChat/Rocket.Chat/pull/24485)) + +- Skip cloud steps for registered servers on setup wizard ([#24407](https://github.com/RocketChat/Rocket.Chat/pull/24407)) + +- Slash commands previews not working ([#24387](https://github.com/RocketChat/Rocket.Chat/pull/24387) by [@ostjen](https://github.com/ostjen)) + +- Startup errors creating indexes ([#24409](https://github.com/RocketChat/Rocket.Chat/pull/24409)) + + Fix `bio` and `prid` startup index creation errors. + +- typo on register server tooltip of setup wizard ([#24466](https://github.com/RocketChat/Rocket.Chat/pull/24466)) + +
+🔍 Minor changes + + +- Bump @types/ws from 8.2.2 to 8.2.3 in /ee/server/services ([#24556](https://github.com/RocketChat/Rocket.Chat/pull/24556) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump adm-zip from 0.4.14 to 0.5.9 ([#24538](https://github.com/RocketChat/Rocket.Chat/pull/24538) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.0 to 1.19.1 in /ee/server/services ([#23963](https://github.com/RocketChat/Rocket.Chat/pull/23963) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services ([#24517](https://github.com/RocketChat/Rocket.Chat/pull/24517) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump cookie from 0.4.1 to 0.4.2 in /ee/server/services ([#24472](https://github.com/RocketChat/Rocket.Chat/pull/24472) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump date-fns from 2.24.0 to 2.28.0 ([#24058](https://github.com/RocketChat/Rocket.Chat/pull/24058) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump express from 4.17.1 to 4.17.2 in /ee/server/services ([#24469](https://github.com/RocketChat/Rocket.Chat/pull/24469) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump express from 4.17.2 to 4.17.3 in /ee/server/services ([#24522](https://github.com/RocketChat/Rocket.Chat/pull/24522) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump follow-redirects from 1.14.7 to 1.14.8 in /ee/server/services ([#24491](https://github.com/RocketChat/Rocket.Chat/pull/24491) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump jaeger-client from 3.18.1 to 3.19.0 in /ee/server/services ([#23961](https://github.com/RocketChat/Rocket.Chat/pull/23961) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services ([#24537](https://github.com/RocketChat/Rocket.Chat/pull/24537) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump simple-get from 4.0.0 to 4.0.1 ([#24341](https://github.com/RocketChat/Rocket.Chat/pull/24341) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services ([#23512](https://github.com/RocketChat/Rocket.Chat/pull/23512) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump underscore.string from 3.3.5 to 3.3.6 in /ee/server/services ([#24498](https://github.com/RocketChat/Rocket.Chat/pull/24498) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump url-parse from 1.5.3 to 1.5.7 ([#24528](https://github.com/RocketChat/Rocket.Chat/pull/24528) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump vm2 from 3.9.5 to 3.9.7 in /ee/server/services ([#24509](https://github.com/RocketChat/Rocket.Chat/pull/24509) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore: `twoFactorRequired` signature ([#24518](https://github.com/RocketChat/Rocket.Chat/pull/24518)) + + Improved type checking for decorator `twoFactorRequired`. + +- Chore: Add description to global OTR setting ([#24333](https://github.com/RocketChat/Rocket.Chat/pull/24333) by [@pedrogssouza](https://github.com/pedrogssouza)) + +- Chore: Bump Fuselage packages ([#24573](https://github.com/RocketChat/Rocket.Chat/pull/24573)) + + It uses the last stable version of Fuselage packages. + +- Chore: bump fuselage version ([#24453](https://github.com/RocketChat/Rocket.Chat/pull/24453)) + +- Chore: Convert JS files to Typescript ([#24410](https://github.com/RocketChat/Rocket.Chat/pull/24410)) + + This pull request converts 26 more files from Javascript to Typescript, to check variable types and increase validation on the code. + +- Chore: Convert to typescript the me slashCommands files ([#24321](https://github.com/RocketChat/Rocket.Chat/pull/24321) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the me slashCommands files + +- Chore: Convert to typescript the mute and unmute slash commands files ([#24325](https://github.com/RocketChat/Rocket.Chat/pull/24325) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the mute and unmute slash commands files + +- Chore: Convert to typescript the slash commands create files ([#24306](https://github.com/RocketChat/Rocket.Chat/pull/24306) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert Slash Commands create files to typescript. + +- Chore: Convert to typescript the slash commands invite files ([#24311](https://github.com/RocketChat/Rocket.Chat/pull/24311) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the slash commands invite files + +- Chore: Convert to typescript the unarchive slash commands files ([#24331](https://github.com/RocketChat/Rocket.Chat/pull/24331) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the unarchive slash commands files + +- Chore: Delete unused file (NewAdminInfoPage.js) ([#24196](https://github.com/RocketChat/Rocket.Chat/pull/24196)) + + Just removing a duplicated/unused file. + +- Chore: Improve PR title validation regex ([#24467](https://github.com/RocketChat/Rocket.Chat/pull/24467)) + +- Chore: Js to ts slash commands archive ([#24304](https://github.com/RocketChat/Rocket.Chat/pull/24304) by [@eduardofcabrera](https://github.com/eduardofcabrera)) + + Convert Slash Commands archive files to typescript + +- Chore: Remove storybook build job from CI ([#24530](https://github.com/RocketChat/Rocket.Chat/pull/24530)) + +- Chore: roomTypes: Stop mixing client and server code together ([#24536](https://github.com/RocketChat/Rocket.Chat/pull/24536)) + +- Chore: Run tests using microservices deployment on CI ([#24513](https://github.com/RocketChat/Rocket.Chat/pull/24513)) + +- Chore: Set Docker image tag to latest only when really latest ([#24366](https://github.com/RocketChat/Rocket.Chat/pull/24366)) + +- Chore: Unify ILivechatAgent with ILivechatAgentRecord ([#24406](https://github.com/RocketChat/Rocket.Chat/pull/24406)) + +- Chore: Update Apps-Engine ([#24568](https://github.com/RocketChat/Rocket.Chat/pull/24568)) + +- Chore: Update Apps-Engine ([#24651](https://github.com/RocketChat/Rocket.Chat/pull/24651)) + +- Chore: Update fuselage deps to match monolith versions ([#24501](https://github.com/RocketChat/Rocket.Chat/pull/24501)) + +- Chore: Update Meteor to 2.5.6 ([#24461](https://github.com/RocketChat/Rocket.Chat/pull/24461)) + +- Chore: Update ws package ([#24477](https://github.com/RocketChat/Rocket.Chat/pull/24477)) + +- Chore(deps-dev): Bump ts-node from 10.0.0 to 10.5.0 in /ee/server/services ([#24435](https://github.com/RocketChat/Rocket.Chat/pull/24435) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore(deps): Bump node-fetch from 2.6.1 to 2.6.7 in /ee/server/services ([#24299](https://github.com/RocketChat/Rocket.Chat/pull/24299) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- i18n: Language update from LingoHub 🤖 on 2022-01-31Z ([#24357](https://github.com/RocketChat/Rocket.Chat/pull/24357)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-07Z ([#24429](https://github.com/RocketChat/Rocket.Chat/pull/24429)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-14Z ([#24493](https://github.com/RocketChat/Rocket.Chat/pull/24493)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-21Z ([#24558](https://github.com/RocketChat/Rocket.Chat/pull/24558)) + +- Merge master into develop & Set version to 4.5.0-develop ([#24363](https://github.com/RocketChat/Rocket.Chat/pull/24363)) + +- Regression: Add support to namespace within micro services ([#24581](https://github.com/RocketChat/Rocket.Chat/pull/24581)) + +- Regression: Admin Sidebar colors inverted. ([#24609](https://github.com/RocketChat/Rocket.Chat/pull/24609)) + +- Regression: Bunch of settings fixes for VoIP ([#24594](https://github.com/RocketChat/Rocket.Chat/pull/24594)) + +- Regression: Do not show toast on incoming voip calls ([#24619](https://github.com/RocketChat/Rocket.Chat/pull/24619)) + +- Regression: Encode registration info as JWT when signing key is provided ([#24626](https://github.com/RocketChat/Rocket.Chat/pull/24626)) + +- Regression: Error setting user avatars and mentioning rooms on Slack Import ([#24585](https://github.com/RocketChat/Rocket.Chat/pull/24585)) + + - Fix `Mentioned room not found` error when importing rooms from Slack; + - Fix `Forbidden` error when setting avatars for users imported from Slack (on user import/creation); + - Fix incorrect message count on imported rooms; + - Fix missing username on messages imported from Slack; + +- Regression: Error when trying to load name of dm rooms for avatars and notifications ([#24583](https://github.com/RocketChat/Rocket.Chat/pull/24583)) + +- Regression: Extension List panel UI not aligned with designs ([#24645](https://github.com/RocketChat/Rocket.Chat/pull/24645)) + +- Regression: Fix double value on holdTime and empty msg on last message ([#24630](https://github.com/RocketChat/Rocket.Chat/pull/24630)) + +- Regression: Fix in-correct room status shown to agents ([#24592](https://github.com/RocketChat/Rocket.Chat/pull/24592)) + +- Regression: Fix incoming voip call ringtone is not ringing ([#24616](https://github.com/RocketChat/Rocket.Chat/pull/24616)) + +- Regression: Fix room not getting created due to null visitor status ([#24562](https://github.com/RocketChat/Rocket.Chat/pull/24562)) + +- Regression: Fix time fields and wrap up in Voip Room Contexual bar ([#24625](https://github.com/RocketChat/Rocket.Chat/pull/24625)) + +- Regression: Fix time format on Voip system messages ([#24603](https://github.com/RocketChat/Rocket.Chat/pull/24603)) + +- Regression: Fix translation for call started message ([#24615](https://github.com/RocketChat/Rocket.Chat/pull/24615)) + +- Regression: Fix wrong tab name for VoIP settings ([#24647](https://github.com/RocketChat/Rocket.Chat/pull/24647)) + +- Regression: Fixes in Voice Contextual Bar and Directory ([#24596](https://github.com/RocketChat/Rocket.Chat/pull/24596)) + +- Regression: If Asterisk suddenly goes down, server has no way to know. Causes server to get stuck. Needs restart ([#24624](https://github.com/RocketChat/Rocket.Chat/pull/24624)) + +- Regression: Mark all rooms as read modal closing instantly. ([#24610](https://github.com/RocketChat/Rocket.Chat/pull/24610)) + +- Regression: No audio when call comes from Skype/IP phone ([#24602](https://github.com/RocketChat/Rocket.Chat/pull/24602)) + + The audio was not rendered because of re-rendering of react element based on + queueCounter and roomInfo. queueCounter and roomInfo cause the dom to re-render when call gets accepted + because after accepting call, queueCounter changes or a room gets created. + The audio element gets recreated. But VoIP user probably holds the old one. + The behaviour is not predictable when such case happens. If everything gets cleanly setup, + even if the audio element goes headless, it still continues to play the remote audio. + But in other cases, it is unreferenced the one on dom has its srcObject as null. + This causes no audio. + + This fix provides a way to re-initialise the rendering elements in VoIP user + and calls this function on useEffect() if the re-render has happen. + +- Regression: Prevent button from losing state when rerendering ([#24648](https://github.com/RocketChat/Rocket.Chat/pull/24648)) + +- Regression: Prevent connect to asterisk when VoIP is disabled ([#24601](https://github.com/RocketChat/Rocket.Chat/pull/24601)) + +- Regression: Queue counter aggregator for incoming/hanged calls ([#24635](https://github.com/RocketChat/Rocket.Chat/pull/24635)) + +- Regression: Refresh server connection when MI server settings change ([#24649](https://github.com/RocketChat/Rocket.Chat/pull/24649)) + +- Regression: Server crashing if Voip credentials are invalid ([#24646](https://github.com/RocketChat/Rocket.Chat/pull/24646)) + +- Regression: VoIP service button displayed when VoIP is disabled ([#24598](https://github.com/RocketChat/Rocket.Chat/pull/24598)) + +
+ +### 👩‍💻👨‍💻 Contributors 😍 + +- [@LucasFASouza](https://github.com/LucasFASouza) +- [@aswinidev](https://github.com/aswinidev) +- [@dependabot[bot]](https://github.com/dependabot[bot]) +- [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@ostjen](https://github.com/ostjen) +- [@pedrogssouza](https://github.com/pedrogssouza) +- [@ujorgeleite](https://github.com/ujorgeleite) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@KevLehman](https://github.com/KevLehman) +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@albuquerquefabio](https://github.com/albuquerquefabio) +- [@amolghode1981](https://github.com/amolghode1981) +- [@d-gubert](https://github.com/d-gubert) +- [@debdutdeb](https://github.com/debdutdeb) +- [@dougfabris](https://github.com/dougfabris) +- [@felipe-rod123](https://github.com/felipe-rod123) +- [@filipemarins](https://github.com/filipemarins) +- [@gabriellsh](https://github.com/gabriellsh) +- [@ggazzo](https://github.com/ggazzo) +- [@guijun13](https://github.com/guijun13) +- [@juliajforesti](https://github.com/juliajforesti) +- [@matheusbsilva137](https://github.com/matheusbsilva137) +- [@murtaza98](https://github.com/murtaza98) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@renatobecker](https://github.com/renatobecker) +- [@rique223](https://github.com/rique223) +- [@rodrigok](https://github.com/rodrigok) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) +- [@tiagoevanp](https://github.com/tiagoevanp) +- [@yash-rajpal](https://github.com/yash-rajpal) + +# 4.4.5 +`2022-05-30 · 12 🎉 · 26 🚀 · 79 🐛 · 213 🔍 · 54 👩‍💻👨‍💻` + +### Engine versions +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` + +### 🎉 New features + + +- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) + +- Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) + + Experimental support for Matrix Federation with a Bridge + + https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 + +- E2E password generator ([#24114](https://github.com/RocketChat/Rocket.Chat/pull/24114) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Engagement Statistics ([#24989](https://github.com/RocketChat/Rocket.Chat/pull/24989)) + +- Engagement Statistics ([#24777](https://github.com/RocketChat/Rocket.Chat/pull/24777) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) + +- Marketplace sort filter ([#24567](https://github.com/RocketChat/Rocket.Chat/pull/24567) by [@ujorgeleite](https://github.com/ujorgeleite)) + + Implemented a sort filter for the marketplace screen. This component sorts the marketplace apps list in 4 ways, alphabetical order(A-Z), inverse alphabetical order(Z-A), most recently updated(MRU), and least recent updated(LRU). Besides that, I've generalized some components and types to increase code reusability, renamed some helpers as well as deleted some useless ones, and inserted the necessary new translations on the English i18n dictionary. + Demo gif: + ![Marketplace sort filter](https://user-images.githubusercontent.com/43561537/155033709-e07a6306-a85a-4f7f-9624-b53ba5dd7fa9.gif) + +- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) + + Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). + + + + ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) + In case you encounter any problems, or want to compare, temporarily it is possible to use the old version + + image + +- Telemetry Events ([#24781](https://github.com/RocketChat/Rocket.Chat/pull/24781) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Upgrade Tab ([#24835](https://github.com/RocketChat/Rocket.Chat/pull/24835)) + + ![image](https://user-images.githubusercontent.com/27704687/160172260-c656282e-a487-4092-948d-d11c9bacb598.png) + +- Use setting to determine if initial general channel is needed ([#25441](https://github.com/RocketChat/Rocket.Chat/pull/25441) by [@felipe-menelau](https://github.com/felipe-menelau)) + + - Adds flag responsible for overwriting #general channel creation + +- VoIP Support for Omnichannel ([#23102](https://github.com/RocketChat/Rocket.Chat/pull/23102)) + + - Created VoipService to manage VoIP connections and PBX connection + - Created LivechatVoipService that will handle custom cases for livechat (creating rooms, assigning chats to queue, actions when call is finished, etc) + - Created Basic interfaces to support new services and new model + - Created Endpoints for management interfaces + - Implemented asterisk connector on VoIP service + - Created UI components to show calls incoming and to allow answering/rejecting calls + - Added new settings to control call server/management server connection values + - Added endpoints to associate Omnichannel Agents with PBX Extensions + - Added support for event listening on server side, to get metadata about calls being received/ongoing + - Created new pages to update settings & to see user-extension association + - Created new page to see ongoing calls (and past calls) + - Added support for remote hangup/hold on calls + - Implemented call metrics calculation (hold time, waiting time, talk time) + - Show a notificaiton when call is received + +### 🚀 Improvements + + +- **ENTERPRISE:** Don't start presence monitor when running micro services ([#24739](https://github.com/RocketChat/Rocket.Chat/pull/24739)) + +- **ENTERPRISE:** Improve how micro services are loaded ([#24388](https://github.com/RocketChat/Rocket.Chat/pull/24388)) + +- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) + + Earlier OTR room uses only 2 states, we need more states to support future features. + This adds more states for the OTR contextualBar. + + - Expired + Screen Shot 2022-04-20 at 13 55 52 + + - Declined + Screen Shot 2022-04-20 at 13 49 28 + + - Error + Screen Shot 2022-04-20 at 13 55 26 + +- Add return button in chats opened from the list of current chats ([#24458](https://github.com/RocketChat/Rocket.Chat/pull/24458) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The new return button for Omnichannel chats came out with release 3.15 but the feature was only available for chats that were opened from Omnichannel Contact Center. + Now, the same UI/UX is supported for chats opened from Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153283190-bd5c9748-c36b-4874-a704-6043afc7e3a1.png) + + The chat now opens in the Omnichannel settings and has the return button so the user can go back to the Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153285591-fad8e4a0-d2ea-4a02-8b2a-15e383b3c876.png) + +- Add tooltip to sidebar room menu ([#24405](https://github.com/RocketChat/Rocket.Chat/pull/24405) by [@Himanshu664](https://github.com/Himanshu664)) + +- Add tooltips on action buttons of Canned Response message composer ([#24483](https://github.com/RocketChat/Rocket.Chat/pull/24483) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The tooltips were missing on the action buttons of CR message composer. + + ![image](https://user-images.githubusercontent.com/32396925/153620327-91107245-4b47-4d39-a99a-6da6d1cf5734.png) + + Users can now feel more encouraged to use these actions knowing what they are supposed to do. + +- Add user to room on "Click to Join!" button press ([#24041](https://github.com/RocketChat/Rocket.Chat/pull/24041) by [@ostjen](https://github.com/ostjen)) + + - Add user to room on "Click to Join!" button press; + - Display the "Join" button in discussions inside channels (keeping the behavior consistent with discussions inside groups). + +- Added a new "All" tab which shows all integrations in Integrations ([#24109](https://github.com/RocketChat/Rocket.Chat/pull/24109) by [@aswinidev](https://github.com/aswinidev)) + +- Added MaxNickNameLength and MaxBioLength constants ([#25231](https://github.com/RocketChat/Rocket.Chat/pull/25231) by [@aakash-gitdev](https://github.com/aakash-gitdev)) + +- Added tooltip options for message menu ([#24431](https://github.com/RocketChat/Rocket.Chat/pull/24431) by [@Himanshu664](https://github.com/Himanshu664)) + +- Adding new statistics related to voip and omnichannel ([#24887](https://github.com/RocketChat/Rocket.Chat/pull/24887)) + + - Total of Canned response messages sent + - Total of tags used + - Last-Chatted Agent Preferred (enabled/disabled) + - Assign new conversations to the contact manager (enabled/disabled) + - How to handle Visitor Abandonment setting + - Amount of chats placed on hold + - VoIP Enabled + - Amount of VoIP Calls + - Amount of VoIP Extensions connected + - Amount of Calls placed on hold (1x per call) + - Fixed Session Aggregation type definitions + +- ChatBox Text to File Description ([#24451](https://github.com/RocketChat/Rocket.Chat/pull/24451) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + The text content from chatbox goes to the file description when drag and drop a file. + +- Close modal on esc and outside click ([#24275](https://github.com/RocketChat/Rocket.Chat/pull/24275)) + + This is a QUICK change in order to close modals pressing Esc button and clicking outside of it **intentionally**. + +- CloudLoginModal visual consistency ([#24334](https://github.com/RocketChat/Rocket.Chat/pull/24334)) + + ### before + ![image](https://user-images.githubusercontent.com/27704687/151585064-dc6a1e29-9903-4241-8fbd-dfbe6c55fbef.png) + + ### after + ![Screen Shot 2022-01-28 at 13 32 02](https://user-images.githubusercontent.com/27704687/151585101-75b98502-9aae-4198-bc3e-4956750e5d8b.png) + +- Convert tag edit with department data to tsx ([#24369](https://github.com/RocketChat/Rocket.Chat/pull/24369) by [@LucasFASouza](https://github.com/LucasFASouza)) + +- Descriptive tooltip for Encrypted Key on Room Header ([#24121](https://github.com/RocketChat/Rocket.Chat/pull/24121)) + +- Improve active/hover colors in account sidebar ([#25024](https://github.com/RocketChat/Rocket.Chat/pull/25024) by [@Himanshu664](https://github.com/Himanshu664)) + +- New omnichannel statistics and async statistics processing. ([#24749](https://github.com/RocketChat/Rocket.Chat/pull/24749)) + + https://app.clickup.com/t/1z4zg4e + +- OTR system messages ([#24382](https://github.com/RocketChat/Rocket.Chat/pull/24382)) + + OTR system messages to indicate key refresh and joining chat to users. + +- Performance for some Omnichannel features ([#25217](https://github.com/RocketChat/Rocket.Chat/pull/25217)) + +- Purchase Type Filter for marketplace apps and Categories filter anchor refactoring ([#24454](https://github.com/RocketChat/Rocket.Chat/pull/24454)) + + Implemented a filter by purchase type(free or paid) component for the apps screen of the marketplace. Besides that, new entries on the dictionary, fixed some parts of the App type (purchaseType was typed as unknown and price as string), and created some helpers to work alongside the filter. Will be refactoring the categories filter anchor and then will open this PR for reviews. + + Demo gif: + ![purchaseTypeFIlter](https://user-images.githubusercontent.com/43561537/153101228-7b7ebdc3-2d34-420f-aa9d-f7cbc8d4b53f.gif) + + Refactored the categories filter anchor from a plain fuselage select to a select button with dynamic colors. + Demo gif: + ![New categories filter anchor(PR)](https://user-images.githubusercontent.com/43561537/153422427-28012b7d-e0ec-45f4-861d-c9368c57ad04.gif) + +- Rename upgrade tab routes ([#25097](https://github.com/RocketChat/Rocket.Chat/pull/25097)) + + Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test. + +- Replace AutoComplete in UserAutoComplete & UserAutoCompleteMultiple components ([#24529](https://github.com/RocketChat/Rocket.Chat/pull/24529)) + + This PR replaces a deprecated fuselage's component `AutoComplete` in favor of `Select` and `MultiSelect` which fixes some of UX/UI issues in selecting users + + ### before + ![Screen Shot 2022-02-19 at 13 33 28](https://user-images.githubusercontent.com/27704687/154809737-8181a06c-4f20-48ea-90f7-01e828b9a452.png) + + ### after + ![Screen Shot 2022-02-19 at 13 30 58](https://user-images.githubusercontent.com/27704687/154809653-a8ec9a80-c0dd-4a25-9c00-0f96147d79e9.png) + +- Skip encryption for slash commands in E2E rooms ([#24475](https://github.com/RocketChat/Rocket.Chat/pull/24475)) + + Currently Slash Commands don't work in an E2EE room, as we encrypt the message before slash command is detected by the server, So removed encryption for slash commands in e2e rooms. + +- Team system messages feedback ([#24209](https://github.com/RocketChat/Rocket.Chat/pull/24209) by [@ostjen](https://github.com/ostjen)) + + - Delete some keys that aren't being used (eg: User_left_female). + - Add new Teams' system messages: + - `added-user-to-team`: **added** @\user to this Team; + - `removed-user-from-team`: **removed** @\user from this Team; + - `user-converted-to-team`: **converted** #\room to a Team; + - `user-converted-to-channel`: **converted** #\room to a Channel; + - `user-removed-room-from-team`: **removed** @\user from this Team; + - `user-deleted-room-from-team`: **deleted** #\room from this Team; + - `user-added-room-to-team`: **deleted** #\room to this Team; + - Add the corresponding options to hide each new system message and the missing `ujt` and `ult` hide options. + +- Updated links in readme ([#24028](https://github.com/RocketChat/Rocket.Chat/pull/24028) by [@aswinidev](https://github.com/aswinidev)) + +### 🐛 Bug fixes + + +- "Match error" when converting a team to a channel ([#24629](https://github.com/RocketChat/Rocket.Chat/pull/24629)) + + - Fix "Match error" when trying to convert a channel to a team; + +- **ENTERPRISE:** Auto reload feature of ddp-streamer micro service ([#24793](https://github.com/RocketChat/Rocket.Chat/pull/24793)) + +- **ENTERPRISE:** DDP streamer not sending data to all clients ([#24738](https://github.com/RocketChat/Rocket.Chat/pull/24738)) + +- **ENTERPRISE:** Notifications not being sent by ddp-streamer ([#24831](https://github.com/RocketChat/Rocket.Chat/pull/24831)) + +- **ENTERPRISE:** Presence micro service logic ([#24724](https://github.com/RocketChat/Rocket.Chat/pull/24724)) + +- 2FA via email when logging in using OAuth ([#24572](https://github.com/RocketChat/Rocket.Chat/pull/24572)) + +- Add ?close to OAuth callback url ([#24381](https://github.com/RocketChat/Rocket.Chat/pull/24381)) + +- Add katex render to new message react template ([#25239](https://github.com/RocketChat/Rocket.Chat/pull/25239)) + +- Add reaction not working in legacy messages ([#25222](https://github.com/RocketChat/Rocket.Chat/pull/25222)) + +- Added invalid password error message ([#24714](https://github.com/RocketChat/Rocket.Chat/pull/24714) by [@Himanshu664](https://github.com/Himanshu664)) + +- Adjust email label in Setup Wizard i18n files ([#25260](https://github.com/RocketChat/Rocket.Chat/pull/25260)) + + - remove 'Company' label on onboarding email keys in certain languages + +- AgentOverview analytics wrong departmentId parameter ([#25073](https://github.com/RocketChat/Rocket.Chat/pull/25073) by [@paulobernardoaf](https://github.com/paulobernardoaf)) + + When filtering the analytics charts by department, data would not appear because the object: + ```js + { + value: "department-id", + label: "department-name" + } + ``` + was being used in the `departmentId` parameter. + + - Before: + ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) + + - After: + ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) + +- API Error preventing adding an email to users without one (like bot/app users) ([#24709](https://github.com/RocketChat/Rocket.Chat/pull/24709)) + +- Apple OAuth ([#24879](https://github.com/RocketChat/Rocket.Chat/pull/24879)) + +- auto-join team channels not honoring user preferences ([#24779](https://github.com/RocketChat/Rocket.Chat/pull/24779) by [@ostjen](https://github.com/ostjen)) + +- Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) + + Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online + unless agent explicitly logs off. + Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. + 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off + in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + 2. Second reason is when computer goes in sleep mode. + 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. + + Solution: + The idea is to detect the network disconnection and start the start the attempts to reconnect. + The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not + call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are + used. + + The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to + reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. + + When the server is disconnected, it should be indicated on the phone button. + +- Close room when dismiss wrap up call modal ([#25056](https://github.com/RocketChat/Rocket.Chat/pull/25056)) + +- Custom sound error toast messages ([#24515](https://github.com/RocketChat/Rocket.Chat/pull/24515) by [@Himanshu664](https://github.com/Himanshu664)) + +- Database indexes not being created ([#25101](https://github.com/RocketChat/Rocket.Chat/pull/25101)) + +- Date Message Export Filter Fix ([#24542](https://github.com/RocketChat/Rocket.Chat/pull/24542) by [@eduardofcabrera](https://github.com/eduardofcabrera)) + + Fix message export filter to get all messages between "from date" and "to date", including "to date". + +- DDP Rate Limiter Translation key ([#24898](https://github.com/RocketChat/Rocket.Chat/pull/24898)) + + Before: + image + + + Now: + image + +- DDP streamer errors ([#24710](https://github.com/RocketChat/Rocket.Chat/pull/24710)) + +- Duplicated "jump to message" button on starred messages ([#24867](https://github.com/RocketChat/Rocket.Chat/pull/24867) by [@Himanshu664](https://github.com/Himanshu664)) + +- Dynamic load matrix is enabled and handle failure ([#25495](https://github.com/RocketChat/Rocket.Chat/pull/25495)) + +- End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) + +- External search providers not working ([#24860](https://github.com/RocketChat/Rocket.Chat/pull/24860) by [@tkurz](https://github.com/tkurz)) + +- Full error message is visible ([#24856](https://github.com/RocketChat/Rocket.Chat/pull/24856) by [@Himanshu664](https://github.com/Himanshu664)) + +- GDPR action to forget visitor data on request ([#24441](https://github.com/RocketChat/Rocket.Chat/pull/24441)) + +- German translation for Monitore ([#24785](https://github.com/RocketChat/Rocket.Chat/pull/24785) by [@JMoVS](https://github.com/JMoVS)) + +- Handle Other Formats inside Upload Avatar ([#24226](https://github.com/RocketChat/Rocket.Chat/pull/24226)) + + After resolving issue #24213 : + + + https://user-images.githubusercontent.com/53515714/150325012-91413025-786e-4ce0-ae75-629f6b05b024.mp4 + +- Ignore customClass on messages ([#24845](https://github.com/RocketChat/Rocket.Chat/pull/24845)) + +- Implement client errors on ddp-streamer ([#24310](https://github.com/RocketChat/Rocket.Chat/pull/24310)) + +- Inconsistent validation of user's access to rooms ([#24037](https://github.com/RocketChat/Rocket.Chat/pull/24037) by [@ostjen](https://github.com/ostjen)) + +- Incorrect websocket url in livechat widget ([#25261](https://github.com/RocketChat/Rocket.Chat/pull/25261)) + +- Initial User not added to default channel ([#25544](https://github.com/RocketChat/Rocket.Chat/pull/25544)) + + If injecting initial user. The user wasn’t added to the default General channel + +- Issues on selecting users when importing CSV ([#24253](https://github.com/RocketChat/Rocket.Chat/pull/24253)) + + * Fix users selecting by fixing their _id + * Add condition to disable 'Start importing' button if `usersCount`, `channelsCount` and `messageCount` equals 0, or if messageCount is alone + * Remove `disabled={usersCount === 0}` on user Tab + +- LDAP avatars being rotated according to metadata even if the setting to rotate uploads is off ([#24320](https://github.com/RocketChat/Rocket.Chat/pull/24320)) + + - Use the `FileUpload_RotateImages` setting (**Administration > File Upload > Rotate images on upload**) to control whether avatars should be rotated automatically based on their data (XEIF); + - Display the avatar image preview (orientation) according to the `FileUpload_RotateImages` setting. + +- LDAP sync removing users from channels when multiple groups are mapped to it ([#25434](https://github.com/RocketChat/Rocket.Chat/pull/25434)) + +- Message menu action not working on legacy messages. ([#25148](https://github.com/RocketChat/Rocket.Chat/pull/25148)) + +- Message preview not available for queued chats ([#25092](https://github.com/RocketChat/Rocket.Chat/pull/25092)) + +- Missing dependency on useEffect at CallProvider ([#24882](https://github.com/RocketChat/Rocket.Chat/pull/24882)) + +- Nextcloud OAuth for incomplete token URL ([#24476](https://github.com/RocketChat/Rocket.Chat/pull/24476)) + +- OAuth mismatch redirect_uri error ([#24450](https://github.com/RocketChat/Rocket.Chat/pull/24450)) + +- Oembed request not respecting payload limit ([#24418](https://github.com/RocketChat/Rocket.Chat/pull/24418)) + +- Omnichannel managers can't join chats in progress ([#24553](https://github.com/RocketChat/Rocket.Chat/pull/24553)) + +- One of the triggers was not working correctly ([#25409](https://github.com/RocketChat/Rocket.Chat/pull/25409)) + +- Outgoing webhook without scripts not saving messages ([#24401](https://github.com/RocketChat/Rocket.Chat/pull/24401)) + +- Prevent Apps Bridge to remove visitor status from room ([#24305](https://github.com/RocketChat/Rocket.Chat/pull/24305)) + +- Prevent call button toggle when user is on call ([#24758](https://github.com/RocketChat/Rocket.Chat/pull/24758)) + +- Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) + + ### before + Screen Shot 2022-03-29 at 13 35 56 + + ### after + Screen Shot 2022-03-29 at 11 48 05 + +- Prune Message issue ([#24424](https://github.com/RocketChat/Rocket.Chat/pull/24424)) + +- Push privacy config to not show username not being respected ([#24606](https://github.com/RocketChat/Rocket.Chat/pull/24606)) + +- Read receipts show with color gray when not read yet ([#25244](https://github.com/RocketChat/Rocket.Chat/pull/25244)) + +- Read receipts showing before message read ([#25216](https://github.com/RocketChat/Rocket.Chat/pull/25216)) + +- Read receipts showing first messages of the room as read even if not read by everyone ([#24508](https://github.com/RocketChat/Rocket.Chat/pull/24508)) + +- Register with Secret URL ([#24921](https://github.com/RocketChat/Rocket.Chat/pull/24921)) + +- Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) + + ### before + ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) + + ### after + Screenshot 2022-01-13 at 8 57 47 PM + +- Reply button behavior on broadcast channel ([#25175](https://github.com/RocketChat/Rocket.Chat/pull/25175)) + + Hide reply button for the user that sent the message + +- respect `Accounts_Registration_Users_Default_Roles` setting ([#24173](https://github.com/RocketChat/Rocket.Chat/pull/24173)) + + - Fix `user` role being added as default regardless of the `Accounts_Registration_Users_Default_Roles` setting. + +- Room archived/unarchived system messages aren't sent when editing room settings ([#24897](https://github.com/RocketChat/Rocket.Chat/pull/24897)) + + - Send the "Room archived" and "Room unarchived" system messages when editing room settings (and not only when rooms are archived/unarchived with the slash-command); + - Fix the "Hide System Messages" option for the "Room archived" and "Room unarchived" system messages; + +- Room context tabs not working in Omnichannel current chats page ([#24559](https://github.com/RocketChat/Rocket.Chat/pull/24559)) + +- room creation fails if app framework is disabled ([#25200](https://github.com/RocketChat/Rocket.Chat/pull/25200)) + +- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +- Several issues related to custom roles ([#24052](https://github.com/RocketChat/Rocket.Chat/pull/24052)) + + - Throw an error when trying to delete a role (User or Subscription role) that are still being used; + - Fix "Invalid Role" error for custom roles in Role Editing sidebar; + - Fix "Users in Role" screen for custom roles. + +- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007)) + + https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 + +- Skip admin info in setup wizard for servers with admin registered ([#24485](https://github.com/RocketChat/Rocket.Chat/pull/24485)) + +- Skip cloud steps for registered servers on setup wizard ([#24407](https://github.com/RocketChat/Rocket.Chat/pull/24407)) + +- Slash commands previews not working ([#24387](https://github.com/RocketChat/Rocket.Chat/pull/24387) by [@ostjen](https://github.com/ostjen)) + +- Spotlight results showing usernames instead of real names ([#25471](https://github.com/RocketChat/Rocket.Chat/pull/25471)) + +- Startup errors creating indexes ([#24409](https://github.com/RocketChat/Rocket.Chat/pull/24409)) + + Fix `bio` and `prid` startup index creation errors. + +- Toolbox hiding under contextual bar ([#25237](https://github.com/RocketChat/Rocket.Chat/pull/25237)) + +- typo on register server tooltip of setup wizard ([#24466](https://github.com/RocketChat/Rocket.Chat/pull/24466)) + +- UI/UX issues on Live Chat widget ([#25407](https://github.com/RocketChat/Rocket.Chat/pull/25407)) + +- Use correct room property for call ended at ([#24932](https://github.com/RocketChat/Rocket.Chat/pull/24932)) + +- User abandonment setting was not working doe to failing event hook ([#25520](https://github.com/RocketChat/Rocket.Chat/pull/25520)) + + A setting watcher and the query for grabbing abandoned chats were broken, now they're not. + +- UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) + + - Rewrites the component to TS + - Fixes some visual issues + + ### before + ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) + + ### after + ![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png) + +- Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) + +- VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) + + Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) + + It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + + Solution: + + 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. + 3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler. + +- Wrong business hour behavior ([#24896](https://github.com/RocketChat/Rocket.Chat/pull/24896)) + +
+🔍 Minor changes + + +- Bump @rocket.chat/emitter from 0.31.4 to 0.31.9 in /ee/server/services ([#25021](https://github.com/RocketChat/Rocket.Chat/pull/25021) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/message-parser from 0.31.4 to 0.31.9 in /ee/server/services ([#25019](https://github.com/RocketChat/Rocket.Chat/pull/25019) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/string-helpers from 0.31.4 to 0.31.9 in /ee/server/services ([#25018](https://github.com/RocketChat/Rocket.Chat/pull/25018) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @rocket.chat/ui-kit from 0.31.4 to 0.31.9 in /ee/server/services ([#25020](https://github.com/RocketChat/Rocket.Chat/pull/25020) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/clipboard from 2.0.1 to 2.0.7 ([#24832](https://github.com/RocketChat/Rocket.Chat/pull/24832) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/mailparser from 3.0.2 to 3.4.0 ([#24833](https://github.com/RocketChat/Rocket.Chat/pull/24833) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/nodemailer from 6.4.2 to 6.4.4 ([#24822](https://github.com/RocketChat/Rocket.Chat/pull/24822) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/ws from 8.2.2 to 8.2.3 in /ee/server/services ([#24556](https://github.com/RocketChat/Rocket.Chat/pull/24556) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/ws from 8.2.3 to 8.5.2 in /ee/server/services ([#24666](https://github.com/RocketChat/Rocket.Chat/pull/24666) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump @types/ws from 8.5.2 to 8.5.3 in /ee/server/services ([#24820](https://github.com/RocketChat/Rocket.Chat/pull/24820) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump actions/checkout from 2 to 3 ([#24668](https://github.com/RocketChat/Rocket.Chat/pull/24668) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump actions/setup-node from 2 to 3 ([#24642](https://github.com/RocketChat/Rocket.Chat/pull/24642) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump adm-zip from 0.4.14 to 0.5.9 ([#24538](https://github.com/RocketChat/Rocket.Chat/pull/24538) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.0 to 1.19.1 in /ee/server/services ([#23963](https://github.com/RocketChat/Rocket.Chat/pull/23963) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.0 to 1.19.2 ([#24821](https://github.com/RocketChat/Rocket.Chat/pull/24821) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services ([#24517](https://github.com/RocketChat/Rocket.Chat/pull/24517) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump body-parser from 1.19.2 to 1.20.0 in /ee/server/services ([#25042](https://github.com/RocketChat/Rocket.Chat/pull/25042) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump cookie from 0.4.1 to 0.4.2 in /ee/server/services ([#24472](https://github.com/RocketChat/Rocket.Chat/pull/24472) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump date-fns from 2.24.0 to 2.28.0 ([#24058](https://github.com/RocketChat/Rocket.Chat/pull/24058) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump ejson from 2.2.1 to 2.2.2 ([#25057](https://github.com/RocketChat/Rocket.Chat/pull/25057) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump eslint-plugin-anti-trojan-source from 1.0.6 to 1.1.0 ([#25076](https://github.com/RocketChat/Rocket.Chat/pull/25076) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump express from 4.17.1 to 4.17.2 in /ee/server/services ([#24469](https://github.com/RocketChat/Rocket.Chat/pull/24469) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump express from 4.17.2 to 4.17.3 in /ee/server/services ([#24522](https://github.com/RocketChat/Rocket.Chat/pull/24522) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump follow-redirects from 1.14.7 to 1.14.8 in /ee/server/services ([#24491](https://github.com/RocketChat/Rocket.Chat/pull/24491) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump is-svg from 4.3.1 to 4.3.2 ([#24801](https://github.com/RocketChat/Rocket.Chat/pull/24801) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump jaeger-client from 3.18.1 to 3.19.0 in /ee/server/services ([#23961](https://github.com/RocketChat/Rocket.Chat/pull/23961) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump jschardet from 1.6.0 to 3.0.0 ([#23121](https://github.com/RocketChat/Rocket.Chat/pull/23121) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump minimist from 1.2.5 to 1.2.6 in /ee/server/services ([#24991](https://github.com/RocketChat/Rocket.Chat/pull/24991) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino and pino-pretty ([#25052](https://github.com/RocketChat/Rocket.Chat/pull/25052)) + +- Bump pino from 7.8.0 to 7.8.1 in /ee/server/services ([#24783](https://github.com/RocketChat/Rocket.Chat/pull/24783) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino from 7.8.1 to 7.9.1 in /ee/server/services ([#24869](https://github.com/RocketChat/Rocket.Chat/pull/24869) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.1 to 7.5.2 in /ee/server/services ([#24689](https://github.com/RocketChat/Rocket.Chat/pull/24689) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.2 to 7.5.3 in /ee/server/services ([#24698](https://github.com/RocketChat/Rocket.Chat/pull/24698) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services ([#24870](https://github.com/RocketChat/Rocket.Chat/pull/24870) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services ([#24537](https://github.com/RocketChat/Rocket.Chat/pull/24537) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump prometheus-gc-stats from 0.6.2 to 0.6.3 ([#24803](https://github.com/RocketChat/Rocket.Chat/pull/24803) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump simple-get from 4.0.0 to 4.0.1 ([#24341](https://github.com/RocketChat/Rocket.Chat/pull/24341) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services ([#23512](https://github.com/RocketChat/Rocket.Chat/pull/23512) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump template-file from 6.0.0 to 6.0.1 ([#25002](https://github.com/RocketChat/Rocket.Chat/pull/25002) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump ts-node from 10.5.0 to 10.6.0 in /ee/server/services ([#24667](https://github.com/RocketChat/Rocket.Chat/pull/24667) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump ts-node from 10.6.0 to 10.7.0 in /ee/server/services ([#24716](https://github.com/RocketChat/Rocket.Chat/pull/24716) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump underscore.string from 3.3.5 to 3.3.6 in /ee/server/services ([#24498](https://github.com/RocketChat/Rocket.Chat/pull/24498) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump url-parse from 1.5.3 to 1.5.7 ([#24528](https://github.com/RocketChat/Rocket.Chat/pull/24528) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump url-parse from 1.5.7 to 1.5.10 ([#24640](https://github.com/RocketChat/Rocket.Chat/pull/24640) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Bump vm2 from 3.9.5 to 3.9.7 in /ee/server/services ([#24509](https://github.com/RocketChat/Rocket.Chat/pull/24509) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore: `twoFactorRequired` signature ([#24518](https://github.com/RocketChat/Rocket.Chat/pull/24518)) + + Improved type checking for decorator `twoFactorRequired`. + +- Chore: Add description to global OTR setting ([#24333](https://github.com/RocketChat/Rocket.Chat/pull/24333) by [@pedrogssouza](https://github.com/pedrogssouza)) + +- Chore: Add E2E tests for livechat/room.close ([#24729](https://github.com/RocketChat/Rocket.Chat/pull/24729) by [@Muramatsu2602](https://github.com/Muramatsu2602)) + + * Create a new test suite file under tests/end-to-end/api/livechat + * Create tests for the following endpoint: + + ivechat/room.close + +- Chore: Add E2E tests for livechat/visitor ([#24764](https://github.com/RocketChat/Rocket.Chat/pull/24764) by [@Muramatsu2602](https://github.com/Muramatsu2602)) + + - Create a new test suite file under tests/end-to-end/api/livechat + - Create tests for the following endpoints: + + livechat/visitor (create visitor, update visitor, add custom fields to visitors) + +- Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) + + Not crash the whole application if something goes wrong in the MessageList component. + + ![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png) + +- Chore: Add Livechat repo into Monorepo packages ([#25312](https://github.com/RocketChat/Rocket.Chat/pull/25312)) + +- Chore: Add options to debug stdout and rate limiter ([#25336](https://github.com/RocketChat/Rocket.Chat/pull/25336)) + +- Chore: Add root package.json to houston files ([#25286](https://github.com/RocketChat/Rocket.Chat/pull/25286)) + + See title + +- Chore: add some missing REST definitions ([#24925](https://github.com/RocketChat/Rocket.Chat/pull/24925) by [@gerzonc](https://github.com/gerzonc)) + + On the [mobile client](https://github.com/RocketChat/Rocket.Chat.ReactNative), we made an effort to collect more `REST API` definitions that are missing on the server side during our migration to TypeScript. Since we're both migrating to TypeScript, we thought it would be a good idea to share those so you guys can benefit from our initiative. + +- Chore: Add yarn plugin to check node and yarn version ([#25224](https://github.com/RocketChat/Rocket.Chat/pull/25224)) + +- Chore: added Server Instances endpoint types ([#24507](https://github.com/RocketChat/Rocket.Chat/pull/24507)) + + Created typing for endpoint definitions on `instances.ts`. + +- Chore: added settings endpoint types ([#24506](https://github.com/RocketChat/Rocket.Chat/pull/24506)) + + Created typing for endpoint definitions on `settings.ts`. + +- Chore: APIClass types ([#24747](https://github.com/RocketChat/Rocket.Chat/pull/24747)) + + This pull request creates a new `restivus` module (.d.ts) for the `api.js` file. + +- Chore: Bump fuselage ([#25371](https://github.com/RocketChat/Rocket.Chat/pull/25371)) + +- Chore: Bump Fuselage packages ([#25259](https://github.com/RocketChat/Rocket.Chat/pull/25259)) + +- Chore: Bump Fuselage packages ([#25015](https://github.com/RocketChat/Rocket.Chat/pull/25015)) + + It uses the last stable version of Fuselage packages. + +- Chore: Bump Fuselage packages ([#24573](https://github.com/RocketChat/Rocket.Chat/pull/24573)) + + It uses the last stable version of Fuselage packages. + +- Chore: bump fuselage version ([#24453](https://github.com/RocketChat/Rocket.Chat/pull/24453)) + +- Chore: Cancel running jobs if PR is updated ([#24708](https://github.com/RocketChat/Rocket.Chat/pull/24708)) + +- Chore: Convert admin custom sound to tsx ([#25128](https://github.com/RocketChat/Rocket.Chat/pull/25128)) + +- Chore: Convert JS files to Typescript ([#24410](https://github.com/RocketChat/Rocket.Chat/pull/24410)) + + This pull request converts 26 more files from Javascript to Typescript, to check variable types and increase validation on the code. + +- Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) + +- Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) + +- Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) + +- Chore: Convert server functions from javascript to typescript ([#24384](https://github.com/RocketChat/Rocket.Chat/pull/24384)) + + This pull request will be used to rewrite some functions on the Chat Engine to Typescript, in order to increase security and specify variable types on the code. + +- Chore: Convert to typescript the me slashCommands files ([#24321](https://github.com/RocketChat/Rocket.Chat/pull/24321) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the me slashCommands files + +- Chore: Convert to typescript the mute and unmute slash commands files ([#24325](https://github.com/RocketChat/Rocket.Chat/pull/24325) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the mute and unmute slash commands files + +- Chore: Convert to typescript the slash commands create files ([#24306](https://github.com/RocketChat/Rocket.Chat/pull/24306) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert Slash Commands create files to typescript. + +- Chore: Convert to typescript the slash commands invite files ([#24311](https://github.com/RocketChat/Rocket.Chat/pull/24311) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the slash commands invite files + +- Chore: Convert to typescript the unarchive slash commands files ([#24331](https://github.com/RocketChat/Rocket.Chat/pull/24331) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + Convert to typescript the unarchive slash commands files + +- Chore: converted more hooks to typescript ([#24628](https://github.com/RocketChat/Rocket.Chat/pull/24628)) + + Converted some functions on `client/hooks/` from JavaScript to Typescript. + +- Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) + +- Chore: Delete unused file (NewAdminInfoPage.js) ([#24196](https://github.com/RocketChat/Rocket.Chat/pull/24196)) + + Just removing a duplicated/unused file. + +- Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) + + - data and test-failure should be ignored + - ensure scripts use cross-env + +- Chore: Fix Cypress tests ([#24544](https://github.com/RocketChat/Rocket.Chat/pull/24544)) + +- Chore: Fix grammatical errors in Code of Conduct ([#24759](https://github.com/RocketChat/Rocket.Chat/pull/24759) by [@aadishJ01](https://github.com/aadishJ01)) + +- Chore: fix grammatical errors in Features ([#24771](https://github.com/RocketChat/Rocket.Chat/pull/24771) by [@aadishJ01](https://github.com/aadishJ01)) + +- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) + +- Chore: Get Settings Statistics ([#24397](https://github.com/RocketChat/Rocket.Chat/pull/24397)) + +- Chore: Improve logger to allow log of `unknown` values ([#24726](https://github.com/RocketChat/Rocket.Chat/pull/24726)) + +- Chore: Improve PR title validation regex ([#24467](https://github.com/RocketChat/Rocket.Chat/pull/24467)) + +- Chore: Improvements on role syncing (ldap, oauth and saml) ([#23824](https://github.com/RocketChat/Rocket.Chat/pull/23824)) + +- Chore: Js to ts slash commands archive ([#24304](https://github.com/RocketChat/Rocket.Chat/pull/24304) by [@eduardofcabrera](https://github.com/eduardofcabrera)) + + Convert Slash Commands archive files to typescript + +- Chore: Micro services fixes and cleanup ([#24753](https://github.com/RocketChat/Rocket.Chat/pull/24753)) + +- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) + +- Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) + +- Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) + +- Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) + +- Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) + +- Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) + +- Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) + +- Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) + +- Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) + +- Chore: Remove old scripts ([#24911](https://github.com/RocketChat/Rocket.Chat/pull/24911)) + +- Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) + + Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore + +- Chore: Remove storybook build job from CI ([#24530](https://github.com/RocketChat/Rocket.Chat/pull/24530)) + +- Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) + +- Chore: roomTypes: Stop mixing client and server code together ([#24536](https://github.com/RocketChat/Rocket.Chat/pull/24536)) + +- Chore: Run tests using microservices deployment on CI ([#24513](https://github.com/RocketChat/Rocket.Chat/pull/24513)) + +- Chore: Set Docker image tag to latest only when really latest ([#24366](https://github.com/RocketChat/Rocket.Chat/pull/24366)) + +- Chore: Skip local services changes when shutting down duplicated services ([#24810](https://github.com/RocketChat/Rocket.Chat/pull/24810)) + +- Chore: Storybook mocking and examples improved ([#24969](https://github.com/RocketChat/Rocket.Chat/pull/24969)) + + - Stories from `ee/` included; + - Differentiate root story kinds; + - Mocking of `ServerContext` via Storybook parameters. + +- Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) + +- Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) + + ``` + npx hygen package new test + ``` + +- Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) + +- Chore: Tests with Playwright (task: ROC-28, 09-channels) ([#25196](https://github.com/RocketChat/Rocket.Chat/pull/25196)) + +- Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) + +- Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) + +- Chore: Unify ILivechatAgent with ILivechatAgentRecord ([#24406](https://github.com/RocketChat/Rocket.Chat/pull/24406)) + +- Chore: Update Apps-Engine ([#24651](https://github.com/RocketChat/Rocket.Chat/pull/24651)) + +- Chore: Update Apps-Engine ([#24568](https://github.com/RocketChat/Rocket.Chat/pull/24568)) + +- Chore: Update fuselage deps to match monolith versions ([#24501](https://github.com/RocketChat/Rocket.Chat/pull/24501)) + +- Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) + +- Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) + +- Chore: Update Meteor to 2.5.6 ([#24461](https://github.com/RocketChat/Rocket.Chat/pull/24461)) + +- Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) + + I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. + +- Chore: Update ws package ([#24477](https://github.com/RocketChat/Rocket.Chat/pull/24477)) + +- Chore(deps-dev): Bump @types/mock-require from 2.0.0 to 2.0.1 ([#24574](https://github.com/RocketChat/Rocket.Chat/pull/24574) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore(deps-dev): Bump ts-node from 10.0.0 to 10.5.0 in /ee/server/services ([#24435](https://github.com/RocketChat/Rocket.Chat/pull/24435) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- Chore(deps): Bump node-fetch from 2.6.1 to 2.6.7 in /ee/server/services ([#24299](https://github.com/RocketChat/Rocket.Chat/pull/24299) by [@dependabot[bot]](https://github.com/dependabot[bot])) + +- i18n: Language update from LingoHub 🤖 on 2022-01-31Z ([#24357](https://github.com/RocketChat/Rocket.Chat/pull/24357)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-07Z ([#24429](https://github.com/RocketChat/Rocket.Chat/pull/24429)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-14Z ([#24493](https://github.com/RocketChat/Rocket.Chat/pull/24493)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-21Z ([#24558](https://github.com/RocketChat/Rocket.Chat/pull/24558)) + +- i18n: Language update from LingoHub 🤖 on 2022-02-28Z ([#24644](https://github.com/RocketChat/Rocket.Chat/pull/24644)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-07Z ([#24717](https://github.com/RocketChat/Rocket.Chat/pull/24717)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-14Z ([#24823](https://github.com/RocketChat/Rocket.Chat/pull/24823)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-21Z ([#24895](https://github.com/RocketChat/Rocket.Chat/pull/24895)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-28Z ([#24971](https://github.com/RocketChat/Rocket.Chat/pull/24971)) + +- i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) + +- Merge master into develop & Set version to 4.5.0-develop ([#24363](https://github.com/RocketChat/Rocket.Chat/pull/24363)) + +- Merge master into develop & Set version to 4.6.0-develop ([#24653](https://github.com/RocketChat/Rocket.Chat/pull/24653)) + +- Merge master into develop & Set version to 4.7.0-develop ([#25028](https://github.com/RocketChat/Rocket.Chat/pull/25028)) + +- Regression: Add `isPending` status to message ([#25299](https://github.com/RocketChat/Rocket.Chat/pull/25299)) + +- Regression: Add createdOTR index ([#25017](https://github.com/RocketChat/Rocket.Chat/pull/25017)) + +- Regression: Add eslint package to micro services Dockerfile ([#25311](https://github.com/RocketChat/Rocket.Chat/pull/25311)) + +- Regression: Add select message to system message and thread preview and allow select on legacy template ([#25251](https://github.com/RocketChat/Rocket.Chat/pull/25251)) + +- Regression: Add support to namespace within micro services ([#24581](https://github.com/RocketChat/Rocket.Chat/pull/24581)) + +- Regression: Admin Sidebar colors inverted. ([#24609](https://github.com/RocketChat/Rocket.Chat/pull/24609)) + +- Regression: Avatar not loading on first direct message ([#25211](https://github.com/RocketChat/Rocket.Chat/pull/25211)) + + fix avatar not loading on a first direct message + +- Regression: Better MongoDB connection management for micro services ([#25323](https://github.com/RocketChat/Rocket.Chat/pull/25323)) + +- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) + + - Bump to 'next' the onboarding-ui package from fuselage. + - Update from 'companyEmail' to 'email' adminData usage types + +- Regression: Bunch of settings fixes for VoIP ([#24594](https://github.com/RocketChat/Rocket.Chat/pull/24594)) + +- Regression: Call doesn't stop ringing after agent unregistration ([#24908](https://github.com/RocketChat/Rocket.Chat/pull/24908)) + +- Regression: Change preference to be default legacy messages ([#25255](https://github.com/RocketChat/Rocket.Chat/pull/25255)) + +- Regression: CI playwright ([#25168](https://github.com/RocketChat/Rocket.Chat/pull/25168)) + +- Regression: Custom roles displaying ID instead of name on some admin screens ([#24999](https://github.com/RocketChat/Rocket.Chat/pull/24999)) + + ![image](https://user-images.githubusercontent.com/55164754/160981416-555bcaa1-c075-4260-937c-64523472da43.png) + ![image](https://user-images.githubusercontent.com/55164754/160981452-6eae4e74-8425-4073-8256-472aba72b9db.png) + +- Regression: Do not show toast on incoming voip calls ([#24619](https://github.com/RocketChat/Rocket.Chat/pull/24619)) + +- Regression: Encode registration info as JWT when signing key is provided ([#24626](https://github.com/RocketChat/Rocket.Chat/pull/24626)) + +- Regression: Error is raised when there's no Asterisk queue available yet ([#24980](https://github.com/RocketChat/Rocket.Chat/pull/24980)) + +- Regression: Error setting user avatars and mentioning rooms on Slack Import ([#24585](https://github.com/RocketChat/Rocket.Chat/pull/24585)) + + - Fix `Mentioned room not found` error when importing rooms from Slack; + - Fix `Forbidden` error when setting avatars for users imported from Slack (on user import/creation); + - Fix incorrect message count on imported rooms; + - Fix missing username on messages imported from Slack; + +- Regression: Error when trying to load name of dm rooms for avatars and notifications ([#24583](https://github.com/RocketChat/Rocket.Chat/pull/24583)) + +- Regression: eslint not running on packages ([#25305](https://github.com/RocketChat/Rocket.Chat/pull/25305)) + +- Regression: Extension List panel UI not aligned with designs ([#24645](https://github.com/RocketChat/Rocket.Chat/pull/24645)) + +- Regression: Fix account service login expiration ([#24920](https://github.com/RocketChat/Rocket.Chat/pull/24920)) + +- Regression: Fix CI monorepo build ([#25107](https://github.com/RocketChat/Rocket.Chat/pull/25107)) + +- Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window ([#25380](https://github.com/RocketChat/Rocket.Chat/pull/25380)) + + Fix: livechat room not opening. + +- Regression: Fix double value on holdTime and empty msg on last message ([#24630](https://github.com/RocketChat/Rocket.Chat/pull/24630)) + +- Regression: Fix English i18n react text ([#25368](https://github.com/RocketChat/Rocket.Chat/pull/25368)) + + Incorrect text in reaction tooltip has been fixed + +- Regression: Fix federation Matrix bridge startup ([#25273](https://github.com/RocketChat/Rocket.Chat/pull/25273)) + +- Regression: Fix in-correct room status shown to agents ([#24592](https://github.com/RocketChat/Rocket.Chat/pull/24592)) + +- Regression: Fix incoming voip call ringtone is not ringing ([#24616](https://github.com/RocketChat/Rocket.Chat/pull/24616)) + +- Regression: Fix micro services Docker build ([#25193](https://github.com/RocketChat/Rocket.Chat/pull/25193)) + +- Regression: Fix multi line is not showing an empty line between lines ([#25317](https://github.com/RocketChat/Rocket.Chat/pull/25317)) + +- Regression: Fix reply button not working when hideFlexTab is enabled ([#25306](https://github.com/RocketChat/Rocket.Chat/pull/25306)) + +- Regression: Fix room not getting created due to null visitor status ([#24562](https://github.com/RocketChat/Rocket.Chat/pull/24562)) + +- Regression: Fix services Docker build on CI ([#25181](https://github.com/RocketChat/Rocket.Chat/pull/25181)) + +- Regression: Fix size of custom emoji and render emoji on thread message preview ([#25314](https://github.com/RocketChat/Rocket.Chat/pull/25314)) + +- Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) + + The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + + This temporarily switches to a fork of the matrix-appservice-bridge package. + + Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). + +- Regression: Fix time fields and wrap up in Voip Room Contexual bar ([#24625](https://github.com/RocketChat/Rocket.Chat/pull/24625)) + +- Regression: Fix time format on Voip system messages ([#24603](https://github.com/RocketChat/Rocket.Chat/pull/24603)) + +- Regression: Fix translation for call started message ([#24615](https://github.com/RocketChat/Rocket.Chat/pull/24615)) + +- Regression: Fix unexpected errors breaking ddp-streamer ([#24948](https://github.com/RocketChat/Rocket.Chat/pull/24948)) + +- Regression: Fix wrong tab name for VoIP settings ([#24647](https://github.com/RocketChat/Rocket.Chat/pull/24647)) + +- Regression: Fixes in Voice Contextual Bar and Directory ([#24596](https://github.com/RocketChat/Rocket.Chat/pull/24596)) + +- Regression: If Asterisk suddenly goes down, server has no way to know. Causes server to get stuck. Needs restart ([#24624](https://github.com/RocketChat/Rocket.Chat/pull/24624)) + +- Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests ([#24756](https://github.com/RocketChat/Rocket.Chat/pull/24756)) + +- Regression: Mark all rooms as read modal closing instantly. ([#24610](https://github.com/RocketChat/Rocket.Chat/pull/24610)) + +- Regression: Messages in new message template Crashing. ([#25327](https://github.com/RocketChat/Rocket.Chat/pull/25327)) + +- Regression: No audio when call comes from Skype/IP phone ([#24602](https://github.com/RocketChat/Rocket.Chat/pull/24602)) + + The audio was not rendered because of re-rendering of react element based on + queueCounter and roomInfo. queueCounter and roomInfo cause the dom to re-render when call gets accepted + because after accepting call, queueCounter changes or a room gets created. + The audio element gets recreated. But VoIP user probably holds the old one. + The behaviour is not predictable when such case happens. If everything gets cleanly setup, + even if the audio element goes headless, it still continues to play the remote audio. + But in other cases, it is unreferenced the one on dom has its srcObject as null. + This causes no audio. + + This fix provides a way to re-initialise the rendering elements in VoIP user + and calls this function on useEffect() if the re-render has happen. + +- Regression: Prevent button from losing state when rerendering ([#24648](https://github.com/RocketChat/Rocket.Chat/pull/24648)) + +- Regression: Prevent connect to asterisk when VoIP is disabled ([#24601](https://github.com/RocketChat/Rocket.Chat/pull/24601)) + +- Regression: Queue counter aggregator for incoming/hanged calls ([#24635](https://github.com/RocketChat/Rocket.Chat/pull/24635)) + +- Regression: Refresh server connection when MI server settings change ([#24649](https://github.com/RocketChat/Rocket.Chat/pull/24649)) + +- Regression: Register services right away ([#24800](https://github.com/RocketChat/Rocket.Chat/pull/24800)) + +- Regression: Revert Bugsnag version ([#25313](https://github.com/RocketChat/Rocket.Chat/pull/25313)) + +- Regression: Rocket.Chat Webapp not loading. ([#25349](https://github.com/RocketChat/Rocket.Chat/pull/25349)) + +- Regression: Role Sync not always working ([#24850](https://github.com/RocketChat/Rocket.Chat/pull/24850)) + +- Regression: Server crashing if Voip credentials are invalid ([#24646](https://github.com/RocketChat/Rocket.Chat/pull/24646)) + +- Regression: Show username and real name on the message system ([#25254](https://github.com/RocketChat/Rocket.Chat/pull/25254)) + +- Regression: Shows error if micro service cannot connect to Mongo ([#25301](https://github.com/RocketChat/Rocket.Chat/pull/25301)) + +- Regression: Use exact Node version on micro services Docker images ([#25287](https://github.com/RocketChat/Rocket.Chat/pull/25287)) + +- Regression: Validate empty fields for Message template ([#25250](https://github.com/RocketChat/Rocket.Chat/pull/25250)) + +- Regression: VoIP service button displayed when VoIP is disabled ([#24598](https://github.com/RocketChat/Rocket.Chat/pull/24598)) + +- Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208)) + +- Release 4.5.0 ([#24652](https://github.com/RocketChat/Rocket.Chat/pull/24652) by [@LucasFASouza](https://github.com/LucasFASouza) & [@aswinidev](https://github.com/aswinidev) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@lingohub[bot]](https://github.com/lingohub[bot]) & [@ostjen](https://github.com/ostjen)) + +- Release 4.5.1 ([#24782](https://github.com/RocketChat/Rocket.Chat/pull/24782) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) & [@cuonghuunguyen](https://github.com/cuonghuunguyen)) + +- Release 4.5.2 ([#24814](https://github.com/RocketChat/Rocket.Chat/pull/24814)) + +- Release 4.5.3 ([#24884](https://github.com/RocketChat/Rocket.Chat/pull/24884)) + +- Release 4.5.4 ([#24938](https://github.com/RocketChat/Rocket.Chat/pull/24938)) + +- Release 4.5.5 ([#24998](https://github.com/RocketChat/Rocket.Chat/pull/24998)) + +- Release 4.6.0 ([#25027](https://github.com/RocketChat/Rocket.Chat/pull/25027) by [@aswinidev](https://github.com/aswinidev) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@eduardofcabrera](https://github.com/eduardofcabrera) & [@lingohub[bot]](https://github.com/lingohub[bot])) + +- Release 4.6.1 ([#25095](https://github.com/RocketChat/Rocket.Chat/pull/25095)) + +- Release 4.6.2 ([#25191](https://github.com/RocketChat/Rocket.Chat/pull/25191) by [@sidmohanty11](https://github.com/sidmohanty11)) + +- Release 4.6.3 ([#25235](https://github.com/RocketChat/Rocket.Chat/pull/25235)) + +- Release 4.7.0 ([#25390](https://github.com/RocketChat/Rocket.Chat/pull/25390) by [@Himanshu664](https://github.com/Himanshu664) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@lingohub[bot]](https://github.com/lingohub[bot])) + +- Release 4.7.1 ([#25510](https://github.com/RocketChat/Rocket.Chat/pull/25510) by [@felipe-menelau](https://github.com/felipe-menelau)) + +- Release 4.7.2 ([#25580](https://github.com/RocketChat/Rocket.Chat/pull/25580)) + +
+ +### 👩‍💻👨‍💻 Contributors 😍 + +- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) +- [@Himanshu664](https://github.com/Himanshu664) +- [@JMoVS](https://github.com/JMoVS) +- [@LucasFASouza](https://github.com/LucasFASouza) +- [@Muramatsu2602](https://github.com/Muramatsu2602) +- [@aadishJ01](https://github.com/aadishJ01) +- [@aakash-gitdev](https://github.com/aakash-gitdev) +- [@aswinidev](https://github.com/aswinidev) +- [@cuonghuunguyen](https://github.com/cuonghuunguyen) +- [@dependabot[bot]](https://github.com/dependabot[bot]) +- [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@felipe-menelau](https://github.com/felipe-menelau) +- [@gerzonc](https://github.com/gerzonc) +- [@kibonusp](https://github.com/kibonusp) +- [@lingohub[bot]](https://github.com/lingohub[bot]) +- [@ostjen](https://github.com/ostjen) +- [@paulobernardoaf](https://github.com/paulobernardoaf) +- [@pedrogssouza](https://github.com/pedrogssouza) +- [@sidmohanty11](https://github.com/sidmohanty11) +- [@tkurz](https://github.com/tkurz) +- [@ujorgeleite](https://github.com/ujorgeleite) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) +- [@KevLehman](https://github.com/KevLehman) +- [@MarcosSpessatto](https://github.com/MarcosSpessatto) +- [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@alansikora](https://github.com/alansikora) +- [@albuquerquefabio](https://github.com/albuquerquefabio) +- [@amolghode1981](https://github.com/amolghode1981) +- [@cauefcr](https://github.com/cauefcr) +- [@d-gubert](https://github.com/d-gubert) +- [@debdutdeb](https://github.com/debdutdeb) +- [@dougfabris](https://github.com/dougfabris) +- [@felipe-rod123](https://github.com/felipe-rod123) +- [@filipemarins](https://github.com/filipemarins) +- [@gabriellsh](https://github.com/gabriellsh) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@ggazzo](https://github.com/ggazzo) +- [@guijun13](https://github.com/guijun13) +- [@jeanfbrito](https://github.com/jeanfbrito) +- [@juliajforesti](https://github.com/juliajforesti) +- [@matheusbsilva137](https://github.com/matheusbsilva137) +- [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) +- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) +- [@renatobecker](https://github.com/renatobecker) +- [@rique223](https://github.com/rique223) +- [@rodrigok](https://github.com/rodrigok) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@souzaramon](https://github.com/souzaramon) +- [@tassoevan](https://github.com/tassoevan) +- [@tiagoevanp](https://github.com/tiagoevanp) +- [@tmontini](https://github.com/tmontini) +- [@weslley543](https://github.com/weslley543) +- [@yash-rajpal](https://github.com/yash-rajpal) + +# 4.4.4 +`2022-05-20 · 12 🎉 · 26 🚀 · 79 🐛 · 213 🔍 · 54 👩‍💻👨‍💻` + +### Engine versions +- Node: `14.18.3` +- NPM: `6.14.15` +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` + +### 🎉 New features + + +- Add expire index to integration history ([#25087](https://github.com/RocketChat/Rocket.Chat/pull/25087)) + +- Alpha Matrix Federation ([#23688](https://github.com/RocketChat/Rocket.Chat/pull/23688)) + + Experimental support for Matrix Federation with a Bridge + + https://user-images.githubusercontent.com/51996/164530391-e8b17ecd-a4d0-4ef8-a8b7-81230c1773d3.mp4 + +- E2E password generator ([#24114](https://github.com/RocketChat/Rocket.Chat/pull/24114) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Engagement Statistics ([#24989](https://github.com/RocketChat/Rocket.Chat/pull/24989)) + +- Engagement Statistics ([#24777](https://github.com/RocketChat/Rocket.Chat/pull/24777) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Expand Apps Engine's environment variable allowed list ([#23870](https://github.com/RocketChat/Rocket.Chat/pull/23870) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) + +- Marketplace sort filter ([#24567](https://github.com/RocketChat/Rocket.Chat/pull/24567) by [@ujorgeleite](https://github.com/ujorgeleite)) + + Implemented a sort filter for the marketplace screen. This component sorts the marketplace apps list in 4 ways, alphabetical order(A-Z), inverse alphabetical order(Z-A), most recently updated(MRU), and least recent updated(LRU). Besides that, I've generalized some components and types to increase code reusability, renamed some helpers as well as deleted some useless ones, and inserted the necessary new translations on the English i18n dictionary. + Demo gif: + ![Marketplace sort filter](https://user-images.githubusercontent.com/43561537/155033709-e07a6306-a85a-4f7f-9624-b53ba5dd7fa9.gif) + +- Message Template React Component ([#23971](https://github.com/RocketChat/Rocket.Chat/pull/23971)) + + Complete rewrite of the messages component in react. Visual changes should be minimal as well as user impact, with no break changes (unless you've customized the blaze template). + + + + ![Screen Shot 2022-04-05 at 11 14 18](https://user-images.githubusercontent.com/27704687/161774027-38dd9c7b-eeeb-45e2-b9d8-ea2a9be8486d.png) + In case you encounter any problems, or want to compare, temporarily it is possible to use the old version + + image + +- Telemetry Events ([#24781](https://github.com/RocketChat/Rocket.Chat/pull/24781) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + +- Upgrade Tab ([#24835](https://github.com/RocketChat/Rocket.Chat/pull/24835)) + + ![image](https://user-images.githubusercontent.com/27704687/160172260-c656282e-a487-4092-948d-d11c9bacb598.png) + +- Use setting to determine if initial general channel is needed ([#25441](https://github.com/RocketChat/Rocket.Chat/pull/25441) by [@felipe-menelau](https://github.com/felipe-menelau)) + + - Adds flag responsible for overwriting #general channel creation + +- VoIP Support for Omnichannel ([#23102](https://github.com/RocketChat/Rocket.Chat/pull/23102)) + + - Created VoipService to manage VoIP connections and PBX connection + - Created LivechatVoipService that will handle custom cases for livechat (creating rooms, assigning chats to queue, actions when call is finished, etc) + - Created Basic interfaces to support new services and new model + - Created Endpoints for management interfaces + - Implemented asterisk connector on VoIP service + - Created UI components to show calls incoming and to allow answering/rejecting calls + - Added new settings to control call server/management server connection values + - Added endpoints to associate Omnichannel Agents with PBX Extensions + - Added support for event listening on server side, to get metadata about calls being received/ongoing + - Created new pages to update settings & to see user-extension association + - Created new page to see ongoing calls (and past calls) + - Added support for remote hangup/hold on calls + - Implemented call metrics calculation (hold time, waiting time, talk time) + - Show a notificaiton when call is received + +### 🚀 Improvements + + +- **ENTERPRISE:** Don't start presence monitor when running micro services ([#24739](https://github.com/RocketChat/Rocket.Chat/pull/24739)) + +- **ENTERPRISE:** Improve how micro services are loaded ([#24388](https://github.com/RocketChat/Rocket.Chat/pull/24388)) + +- Add OTR Room States ([#24565](https://github.com/RocketChat/Rocket.Chat/pull/24565)) + + Earlier OTR room uses only 2 states, we need more states to support future features. + This adds more states for the OTR contextualBar. + + - Expired + Screen Shot 2022-04-20 at 13 55 52 + + - Declined + Screen Shot 2022-04-20 at 13 49 28 + + - Error + Screen Shot 2022-04-20 at 13 55 26 + +- Add return button in chats opened from the list of current chats ([#24458](https://github.com/RocketChat/Rocket.Chat/pull/24458) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The new return button for Omnichannel chats came out with release 3.15 but the feature was only available for chats that were opened from Omnichannel Contact Center. + Now, the same UI/UX is supported for chats opened from Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153283190-bd5c9748-c36b-4874-a704-6043afc7e3a1.png) + + The chat now opens in the Omnichannel settings and has the return button so the user can go back to the Current Chats list. + + ![image](https://user-images.githubusercontent.com/32396925/153285591-fad8e4a0-d2ea-4a02-8b2a-15e383b3c876.png) + +- Add tooltip to sidebar room menu ([#24405](https://github.com/RocketChat/Rocket.Chat/pull/24405) by [@Himanshu664](https://github.com/Himanshu664)) + +- Add tooltips on action buttons of Canned Response message composer ([#24483](https://github.com/RocketChat/Rocket.Chat/pull/24483) by [@LucasFASouza](https://github.com/LucasFASouza)) + + The tooltips were missing on the action buttons of CR message composer. + + ![image](https://user-images.githubusercontent.com/32396925/153620327-91107245-4b47-4d39-a99a-6da6d1cf5734.png) + + Users can now feel more encouraged to use these actions knowing what they are supposed to do. + +- Add user to room on "Click to Join!" button press ([#24041](https://github.com/RocketChat/Rocket.Chat/pull/24041) by [@ostjen](https://github.com/ostjen)) + + - Add user to room on "Click to Join!" button press; + - Display the "Join" button in discussions inside channels (keeping the behavior consistent with discussions inside groups). + +- Added a new "All" tab which shows all integrations in Integrations ([#24109](https://github.com/RocketChat/Rocket.Chat/pull/24109) by [@aswinidev](https://github.com/aswinidev)) + +- Added MaxNickNameLength and MaxBioLength constants ([#25231](https://github.com/RocketChat/Rocket.Chat/pull/25231) by [@aakash-gitdev](https://github.com/aakash-gitdev)) + +- Added tooltip options for message menu ([#24431](https://github.com/RocketChat/Rocket.Chat/pull/24431) by [@Himanshu664](https://github.com/Himanshu664)) + +- Adding new statistics related to voip and omnichannel ([#24887](https://github.com/RocketChat/Rocket.Chat/pull/24887)) + + - Total of Canned response messages sent + - Total of tags used + - Last-Chatted Agent Preferred (enabled/disabled) + - Assign new conversations to the contact manager (enabled/disabled) + - How to handle Visitor Abandonment setting + - Amount of chats placed on hold + - VoIP Enabled + - Amount of VoIP Calls + - Amount of VoIP Extensions connected + - Amount of Calls placed on hold (1x per call) + - Fixed Session Aggregation type definitions + +- ChatBox Text to File Description ([#24451](https://github.com/RocketChat/Rocket.Chat/pull/24451) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) + + The text content from chatbox goes to the file description when drag and drop a file. + +- Close modal on esc and outside click ([#24275](https://github.com/RocketChat/Rocket.Chat/pull/24275)) + + This is a QUICK change in order to close modals pressing Esc button and clicking outside of it **intentionally**. + +- CloudLoginModal visual consistency ([#24334](https://github.com/RocketChat/Rocket.Chat/pull/24334)) + + ### before + ![image](https://user-images.githubusercontent.com/27704687/151585064-dc6a1e29-9903-4241-8fbd-dfbe6c55fbef.png) + + ### after + ![Screen Shot 2022-01-28 at 13 32 02](https://user-images.githubusercontent.com/27704687/151585101-75b98502-9aae-4198-bc3e-4956750e5d8b.png) + +- Convert tag edit with department data to tsx ([#24369](https://github.com/RocketChat/Rocket.Chat/pull/24369) by [@LucasFASouza](https://github.com/LucasFASouza)) + +- Descriptive tooltip for Encrypted Key on Room Header ([#24121](https://github.com/RocketChat/Rocket.Chat/pull/24121)) + +- Improve active/hover colors in account sidebar ([#25024](https://github.com/RocketChat/Rocket.Chat/pull/25024) by [@Himanshu664](https://github.com/Himanshu664)) + +- New omnichannel statistics and async statistics processing. ([#24749](https://github.com/RocketChat/Rocket.Chat/pull/24749)) + + https://app.clickup.com/t/1z4zg4e + +- OTR system messages ([#24382](https://github.com/RocketChat/Rocket.Chat/pull/24382)) + + OTR system messages to indicate key refresh and joining chat to users. + +- Performance for some Omnichannel features ([#25217](https://github.com/RocketChat/Rocket.Chat/pull/25217)) + +- Purchase Type Filter for marketplace apps and Categories filter anchor refactoring ([#24454](https://github.com/RocketChat/Rocket.Chat/pull/24454)) + + Implemented a filter by purchase type(free or paid) component for the apps screen of the marketplace. Besides that, new entries on the dictionary, fixed some parts of the App type (purchaseType was typed as unknown and price as string), and created some helpers to work alongside the filter. Will be refactoring the categories filter anchor and then will open this PR for reviews. + + Demo gif: + ![purchaseTypeFIlter](https://user-images.githubusercontent.com/43561537/153101228-7b7ebdc3-2d34-420f-aa9d-f7cbc8d4b53f.gif) + + Refactored the categories filter anchor from a plain fuselage select to a select button with dynamic colors. + Demo gif: + ![New categories filter anchor(PR)](https://user-images.githubusercontent.com/43561537/153422427-28012b7d-e0ec-45f4-861d-c9368c57ad04.gif) + +- Rename upgrade tab routes ([#25097](https://github.com/RocketChat/Rocket.Chat/pull/25097)) + + Change 'upgrade tab' routes names from camelCase ('goFullyFeatured') to kebab-case ('go-fully-featured') due to URL naming consistency. Changed types, main function and test. + +- Replace AutoComplete in UserAutoComplete & UserAutoCompleteMultiple components ([#24529](https://github.com/RocketChat/Rocket.Chat/pull/24529)) + + This PR replaces a deprecated fuselage's component `AutoComplete` in favor of `Select` and `MultiSelect` which fixes some of UX/UI issues in selecting users + + ### before + ![Screen Shot 2022-02-19 at 13 33 28](https://user-images.githubusercontent.com/27704687/154809737-8181a06c-4f20-48ea-90f7-01e828b9a452.png) + + ### after + ![Screen Shot 2022-02-19 at 13 30 58](https://user-images.githubusercontent.com/27704687/154809653-a8ec9a80-c0dd-4a25-9c00-0f96147d79e9.png) + +- Skip encryption for slash commands in E2E rooms ([#24475](https://github.com/RocketChat/Rocket.Chat/pull/24475)) + + Currently Slash Commands don't work in an E2EE room, as we encrypt the message before slash command is detected by the server, So removed encryption for slash commands in e2e rooms. + +- Team system messages feedback ([#24209](https://github.com/RocketChat/Rocket.Chat/pull/24209) by [@ostjen](https://github.com/ostjen)) + + - Delete some keys that aren't being used (eg: User_left_female). + - Add new Teams' system messages: + - `added-user-to-team`: **added** @\user to this Team; + - `removed-user-from-team`: **removed** @\user from this Team; + - `user-converted-to-team`: **converted** #\room to a Team; + - `user-converted-to-channel`: **converted** #\room to a Channel; + - `user-removed-room-from-team`: **removed** @\user from this Team; + - `user-deleted-room-from-team`: **deleted** #\room from this Team; + - `user-added-room-to-team`: **deleted** #\room to this Team; + - Add the corresponding options to hide each new system message and the missing `ujt` and `ult` hide options. + +- Updated links in readme ([#24028](https://github.com/RocketChat/Rocket.Chat/pull/24028) by [@aswinidev](https://github.com/aswinidev)) + +### 🐛 Bug fixes + + +- "Match error" when converting a team to a channel ([#24629](https://github.com/RocketChat/Rocket.Chat/pull/24629)) + + - Fix "Match error" when trying to convert a channel to a team; + +- **ENTERPRISE:** Auto reload feature of ddp-streamer micro service ([#24793](https://github.com/RocketChat/Rocket.Chat/pull/24793)) + +- **ENTERPRISE:** DDP streamer not sending data to all clients ([#24738](https://github.com/RocketChat/Rocket.Chat/pull/24738)) + +- **ENTERPRISE:** Notifications not being sent by ddp-streamer ([#24831](https://github.com/RocketChat/Rocket.Chat/pull/24831)) + +- **ENTERPRISE:** Presence micro service logic ([#24724](https://github.com/RocketChat/Rocket.Chat/pull/24724)) + +- 2FA via email when logging in using OAuth ([#24572](https://github.com/RocketChat/Rocket.Chat/pull/24572)) + +- Add ?close to OAuth callback url ([#24381](https://github.com/RocketChat/Rocket.Chat/pull/24381)) + +- Add katex render to new message react template ([#25239](https://github.com/RocketChat/Rocket.Chat/pull/25239)) + +- Add reaction not working in legacy messages ([#25222](https://github.com/RocketChat/Rocket.Chat/pull/25222)) + +- Added invalid password error message ([#24714](https://github.com/RocketChat/Rocket.Chat/pull/24714) by [@Himanshu664](https://github.com/Himanshu664)) + +- Adjust email label in Setup Wizard i18n files ([#25260](https://github.com/RocketChat/Rocket.Chat/pull/25260)) + + - remove 'Company' label on onboarding email keys in certain languages + +- AgentOverview analytics wrong departmentId parameter ([#25073](https://github.com/RocketChat/Rocket.Chat/pull/25073) by [@paulobernardoaf](https://github.com/paulobernardoaf)) + + When filtering the analytics charts by department, data would not appear because the object: + ```js + { + value: "department-id", + label: "department-name" + } + ``` + was being used in the `departmentId` parameter. + + - Before: + ![image](https://user-images.githubusercontent.com/30026625/161832057-d96ffd21-a7dd-421e-bfaa-3b9f4a9127b2.png) + + - After: + ![image](https://user-images.githubusercontent.com/30026625/161831092-9ee77b51-b083-4f45-9c48-ab2e0511c4d6.png) + +- API Error preventing adding an email to users without one (like bot/app users) ([#24709](https://github.com/RocketChat/Rocket.Chat/pull/24709)) + +- Apple OAuth ([#24879](https://github.com/RocketChat/Rocket.Chat/pull/24879)) + +- auto-join team channels not honoring user preferences ([#24779](https://github.com/RocketChat/Rocket.Chat/pull/24779) by [@ostjen](https://github.com/ostjen)) + +- Client disconnection on network loss ([#25170](https://github.com/RocketChat/Rocket.Chat/pull/25170)) + + Agent gets disconnected (or Unregistered) from asterisk in multiple ways. The goal is that agent should remain online + unless agent explicitly logs off. + Agent can stop receiving calls in multiple ways due to network loss. Network loss can happen in following ways. + 1. User tries to switch the network. User experiences a glitch of disconnectivity. This can be simulated by turning the network off + in the network tab of chrome's dev tool. This can disconnect the UA if the disconnection happens just before the registration refresh. + 2. Second reason is when computer goes in sleep mode. + 3. Third reason is that when asterisk is crashed/in maintenance mode/explicitly stopped. + + Solution: + The idea is to detect the network disconnection and start the start the attempts to reconnect. + The detection of the disconnection does not happen in case#1. The SIPUA's UserAgent transport does not + call onDisconnected when network loss of such kind happens. To tackle this problem, window's online and offline event handlers are + used. + + The number of retries is configurable but ideally it is to be kept at -1. Whenever disconnection happens, it should keep on trying to + reconnect with increasing backoff time. This behaviour is useful when the asterisk is stopped. + + When the server is disconnected, it should be indicated on the phone button. + +- Close room when dismiss wrap up call modal ([#25056](https://github.com/RocketChat/Rocket.Chat/pull/25056)) + +- Custom sound error toast messages ([#24515](https://github.com/RocketChat/Rocket.Chat/pull/24515) by [@Himanshu664](https://github.com/Himanshu664)) + +- Database indexes not being created ([#25101](https://github.com/RocketChat/Rocket.Chat/pull/25101)) + +- Date Message Export Filter Fix ([#24542](https://github.com/RocketChat/Rocket.Chat/pull/24542) by [@eduardofcabrera](https://github.com/eduardofcabrera)) + + Fix message export filter to get all messages between "from date" and "to date", including "to date". + +- DDP Rate Limiter Translation key ([#24898](https://github.com/RocketChat/Rocket.Chat/pull/24898)) + + Before: + image + + + Now: + image + +- DDP streamer errors ([#24710](https://github.com/RocketChat/Rocket.Chat/pull/24710)) + +- Duplicated "jump to message" button on starred messages ([#24867](https://github.com/RocketChat/Rocket.Chat/pull/24867) by [@Himanshu664](https://github.com/Himanshu664)) + +- Dynamic load matrix is enabled and handle failure ([#25495](https://github.com/RocketChat/Rocket.Chat/pull/25495)) + +- End call button disappearing when on-hold ([#24936](https://github.com/RocketChat/Rocket.Chat/pull/24936)) + +- External search providers not working ([#24860](https://github.com/RocketChat/Rocket.Chat/pull/24860) by [@tkurz](https://github.com/tkurz)) + +- Full error message is visible ([#24856](https://github.com/RocketChat/Rocket.Chat/pull/24856) by [@Himanshu664](https://github.com/Himanshu664)) + +- GDPR action to forget visitor data on request ([#24441](https://github.com/RocketChat/Rocket.Chat/pull/24441)) + +- German translation for Monitore ([#24785](https://github.com/RocketChat/Rocket.Chat/pull/24785) by [@JMoVS](https://github.com/JMoVS)) + +- Handle Other Formats inside Upload Avatar ([#24226](https://github.com/RocketChat/Rocket.Chat/pull/24226)) + + After resolving issue #24213 : + + + https://user-images.githubusercontent.com/53515714/150325012-91413025-786e-4ce0-ae75-629f6b05b024.mp4 + +- Ignore customClass on messages ([#24845](https://github.com/RocketChat/Rocket.Chat/pull/24845)) + +- Implement client errors on ddp-streamer ([#24310](https://github.com/RocketChat/Rocket.Chat/pull/24310)) + +- Inconsistent validation of user's access to rooms ([#24037](https://github.com/RocketChat/Rocket.Chat/pull/24037) by [@ostjen](https://github.com/ostjen)) + +- Incorrect websocket url in livechat widget ([#25261](https://github.com/RocketChat/Rocket.Chat/pull/25261)) + +- Initial User not added to default channel ([#25544](https://github.com/RocketChat/Rocket.Chat/pull/25544)) + + If injecting initial user. The user wasn’t added to the default General channel + +- Issues on selecting users when importing CSV ([#24253](https://github.com/RocketChat/Rocket.Chat/pull/24253)) + + * Fix users selecting by fixing their _id + * Add condition to disable 'Start importing' button if `usersCount`, `channelsCount` and `messageCount` equals 0, or if messageCount is alone + * Remove `disabled={usersCount === 0}` on user Tab + +- LDAP avatars being rotated according to metadata even if the setting to rotate uploads is off ([#24320](https://github.com/RocketChat/Rocket.Chat/pull/24320)) + + - Use the `FileUpload_RotateImages` setting (**Administration > File Upload > Rotate images on upload**) to control whether avatars should be rotated automatically based on their data (XEIF); + - Display the avatar image preview (orientation) according to the `FileUpload_RotateImages` setting. + +- LDAP sync removing users from channels when multiple groups are mapped to it ([#25434](https://github.com/RocketChat/Rocket.Chat/pull/25434)) + +- Message menu action not working on legacy messages. ([#25148](https://github.com/RocketChat/Rocket.Chat/pull/25148)) + +- Message preview not available for queued chats ([#25092](https://github.com/RocketChat/Rocket.Chat/pull/25092)) + +- Missing dependency on useEffect at CallProvider ([#24882](https://github.com/RocketChat/Rocket.Chat/pull/24882)) + +- Nextcloud OAuth for incomplete token URL ([#24476](https://github.com/RocketChat/Rocket.Chat/pull/24476)) + +- OAuth mismatch redirect_uri error ([#24450](https://github.com/RocketChat/Rocket.Chat/pull/24450)) + +- Oembed request not respecting payload limit ([#24418](https://github.com/RocketChat/Rocket.Chat/pull/24418)) + +- Omnichannel managers can't join chats in progress ([#24553](https://github.com/RocketChat/Rocket.Chat/pull/24553)) + +- One of the triggers was not working correctly ([#25409](https://github.com/RocketChat/Rocket.Chat/pull/25409)) + +- Outgoing webhook without scripts not saving messages ([#24401](https://github.com/RocketChat/Rocket.Chat/pull/24401)) + +- Prevent Apps Bridge to remove visitor status from room ([#24305](https://github.com/RocketChat/Rocket.Chat/pull/24305)) + +- Prevent call button toggle when user is on call ([#24758](https://github.com/RocketChat/Rocket.Chat/pull/24758)) + +- Prevent sequential messages edited icon to hide on hover ([#24984](https://github.com/RocketChat/Rocket.Chat/pull/24984)) + + ### before + Screen Shot 2022-03-29 at 13 35 56 + + ### after + Screen Shot 2022-03-29 at 11 48 05 + +- Prune Message issue ([#24424](https://github.com/RocketChat/Rocket.Chat/pull/24424)) + +- Push privacy config to not show username not being respected ([#24606](https://github.com/RocketChat/Rocket.Chat/pull/24606)) + +- Read receipts show with color gray when not read yet ([#25244](https://github.com/RocketChat/Rocket.Chat/pull/25244)) + +- Read receipts showing before message read ([#25216](https://github.com/RocketChat/Rocket.Chat/pull/25216)) + +- Read receipts showing first messages of the room as read even if not read by everyone ([#24508](https://github.com/RocketChat/Rocket.Chat/pull/24508)) + +- Register with Secret URL ([#24921](https://github.com/RocketChat/Rocket.Chat/pull/24921)) + +- Replace encrypted text to Encrypted Message Placeholder ([#24166](https://github.com/RocketChat/Rocket.Chat/pull/24166)) + + ### before + ![image](https://user-images.githubusercontent.com/27704687/150807900-154a9cdb-ee13-4333-8628-f287ab914b40.png) + + ### after + Screenshot 2022-01-13 at 8 57 47 PM + +- Reply button behavior on broadcast channel ([#25175](https://github.com/RocketChat/Rocket.Chat/pull/25175)) + + Hide reply button for the user that sent the message + +- respect `Accounts_Registration_Users_Default_Roles` setting ([#24173](https://github.com/RocketChat/Rocket.Chat/pull/24173)) + + - Fix `user` role being added as default regardless of the `Accounts_Registration_Users_Default_Roles` setting. + +- Room archived/unarchived system messages aren't sent when editing room settings ([#24897](https://github.com/RocketChat/Rocket.Chat/pull/24897)) + + - Send the "Room archived" and "Room unarchived" system messages when editing room settings (and not only when rooms are archived/unarchived with the slash-command); + - Fix the "Hide System Messages" option for the "Room archived" and "Room unarchived" system messages; + +- Room context tabs not working in Omnichannel current chats page ([#24559](https://github.com/RocketChat/Rocket.Chat/pull/24559)) + +- room creation fails if app framework is disabled ([#25200](https://github.com/RocketChat/Rocket.Chat/pull/25200)) + +- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +- Several issues related to custom roles ([#24052](https://github.com/RocketChat/Rocket.Chat/pull/24052)) + + - Throw an error when trying to delete a role (User or Subscription role) that are still being used; + - Fix "Invalid Role" error for custom roles in Role Editing sidebar; + - Fix "Users in Role" screen for custom roles. + +- Showing Blank Message Inside Report ([#25007](https://github.com/RocketChat/Rocket.Chat/pull/25007)) + + https://user-images.githubusercontent.com/53515714/161038085-4a86c7ae-6751-4996-9767-b1c9e0331a6c.mp4 + +- Skip admin info in setup wizard for servers with admin registered ([#24485](https://github.com/RocketChat/Rocket.Chat/pull/24485)) + +- Skip cloud steps for registered servers on setup wizard ([#24407](https://github.com/RocketChat/Rocket.Chat/pull/24407)) + +- Slash commands previews not working ([#24387](https://github.com/RocketChat/Rocket.Chat/pull/24387) by [@ostjen](https://github.com/ostjen)) + +- Spotlight results showing usernames instead of real names ([#25471](https://github.com/RocketChat/Rocket.Chat/pull/25471)) + +- Startup errors creating indexes ([#24409](https://github.com/RocketChat/Rocket.Chat/pull/24409)) + + Fix `bio` and `prid` startup index creation errors. + +- Toolbox hiding under contextual bar ([#25237](https://github.com/RocketChat/Rocket.Chat/pull/25237)) + +- typo on register server tooltip of setup wizard ([#24466](https://github.com/RocketChat/Rocket.Chat/pull/24466)) + +- UI/UX issues on Live Chat widget ([#25407](https://github.com/RocketChat/Rocket.Chat/pull/25407)) + +- Use correct room property for call ended at ([#24932](https://github.com/RocketChat/Rocket.Chat/pull/24932)) + +- User abandonment setting was not working doe to failing event hook ([#25520](https://github.com/RocketChat/Rocket.Chat/pull/25520)) + + A setting watcher and the query for grabbing abandoned chats were broken, now they're not. + +- UserCard sanitization ([#25089](https://github.com/RocketChat/Rocket.Chat/pull/25089)) + + - Rewrites the component to TS + - Fixes some visual issues + + ### before + ![Screen Shot 2022-04-07 at 00 23 11](https://user-images.githubusercontent.com/27704687/162113925-5c9484d1-23e9-4623-8b86-3fbc71b461a1.png) + + ### after + ![Screen Shot 2022-04-07 at 00 07 13](https://user-images.githubusercontent.com/27704687/162112353-afd6aac6-b27c-4470-a642-631b8080d59e.png) + +- Video and Audio not skipping forward ([#19866](https://github.com/RocketChat/Rocket.Chat/pull/19866)) + +- VoIP disabled/enabled sequence puts voip agent in error state ([#25230](https://github.com/RocketChat/Rocket.Chat/pull/25230)) -- VoIP Enable/Disable setting on CallContext/CallProvider Notifications ([#24607](https://github.com/RocketChat/Rocket.Chat/pull/24607)) + Initially it was thought that the issue occurs because of the race condition while changing the client settings vs those settings reflected on server side. So a natural solution to solve this is to wait for setting change event 'private-settings-changed'. Then if 'VoIP_Enabled' is updated and it is true, set voipEnabled to true in useVoipClient.ts (on client side) + + It was realised that the race does not happen because of the database or server noticing the changes late. But because of the time taken to establish the AMI connection with Asterisk. + + Solution: + + 1. Change apps/meteor/app/voip/server/startup.ts. When VoIP_Enabled is changed, await for Voip.init() to complete and then broadcast connector.statuschanged with changed value. + 2. From apps/meteor/server/modules/listeners/listeners.module.ts use notifyLoggedInThisInstance to notify all logged in users on current instance. + 3. in apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts add the event handler that receives this event. Change voipEnabled from constant to state. Change this state based on the 'value' that is received by the handler. -- Voip Stream Reinitialization Error ([#24657](https://github.com/RocketChat/Rocket.Chat/pull/24657)) +- Wrong business hour behavior ([#24896](https://github.com/RocketChat/Rocket.Chat/pull/24896))
🔍 Minor changes -- Chore: Update Livechat ([#24754](https://github.com/RocketChat/Rocket.Chat/pull/24754)) - -- Release 4.5.1 ([#24782](https://github.com/RocketChat/Rocket.Chat/pull/24782) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) & [@cuonghuunguyen](https://github.com/cuonghuunguyen)) - -
- -### 👩‍💻👨‍💻 Contributors 😍 +- Bump @rocket.chat/emitter from 0.31.4 to 0.31.9 in /ee/server/services ([#25021](https://github.com/RocketChat/Rocket.Chat/pull/25021) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) -- [@cuonghuunguyen](https://github.com/cuonghuunguyen) +- Bump @rocket.chat/message-parser from 0.31.4 to 0.31.9 in /ee/server/services ([#25019](https://github.com/RocketChat/Rocket.Chat/pull/25019) by [@dependabot[bot]](https://github.com/dependabot[bot])) -### 👩‍💻👨‍💻 Core Team 🤓 +- Bump @rocket.chat/string-helpers from 0.31.4 to 0.31.9 in /ee/server/services ([#25018](https://github.com/RocketChat/Rocket.Chat/pull/25018) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- [@KevLehman](https://github.com/KevLehman) -- [@MartinSchoeler](https://github.com/MartinSchoeler) -- [@amolghode1981](https://github.com/amolghode1981) -- [@juliajforesti](https://github.com/juliajforesti) -- [@matheusbsilva137](https://github.com/matheusbsilva137) -- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) -- [@renatobecker](https://github.com/renatobecker) -- [@sampaiodiego](https://github.com/sampaiodiego) -- [@tassoevan](https://github.com/tassoevan) -- [@tiagoevanp](https://github.com/tiagoevanp) +- Bump @rocket.chat/ui-kit from 0.31.4 to 0.31.9 in /ee/server/services ([#25020](https://github.com/RocketChat/Rocket.Chat/pull/25020) by [@dependabot[bot]](https://github.com/dependabot[bot])) -# 4.5.0 -`2022-02-28 · 3 🎉 · 15 🚀 · 19 🐛 · 72 🔍 · 30 👩‍💻👨‍💻` +- Bump @types/clipboard from 2.0.1 to 2.0.7 ([#24832](https://github.com/RocketChat/Rocket.Chat/pull/24832) by [@dependabot[bot]](https://github.com/dependabot[bot])) -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.31.0` +- Bump @types/mailparser from 3.0.2 to 3.4.0 ([#24833](https://github.com/RocketChat/Rocket.Chat/pull/24833) by [@dependabot[bot]](https://github.com/dependabot[bot])) -### 🎉 New features +- Bump @types/nodemailer from 6.4.2 to 6.4.4 ([#24822](https://github.com/RocketChat/Rocket.Chat/pull/24822) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Bump @types/ws from 8.2.2 to 8.2.3 in /ee/server/services ([#24556](https://github.com/RocketChat/Rocket.Chat/pull/24556) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- E2E password generator ([#24114](https://github.com/RocketChat/Rocket.Chat/pull/24114) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Bump @types/ws from 8.2.3 to 8.5.2 in /ee/server/services ([#24666](https://github.com/RocketChat/Rocket.Chat/pull/24666) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Marketplace sort filter ([#24567](https://github.com/RocketChat/Rocket.Chat/pull/24567)) +- Bump @types/ws from 8.5.2 to 8.5.3 in /ee/server/services ([#24820](https://github.com/RocketChat/Rocket.Chat/pull/24820) by [@dependabot[bot]](https://github.com/dependabot[bot])) - Implemented a sort filter for the marketplace screen. This component sorts the marketplace apps list in 4 ways, alphabetical order(A-Z), inverse alphabetical order(Z-A), most recently updated(MRU), and least recent updated(LRU). Besides that, I've generalized some components and types to increase code reusability, renamed some helpers as well as deleted some useless ones, and inserted the necessary new translations on the English i18n dictionary. - Demo gif: - ![Marketplace sort filter](https://user-images.githubusercontent.com/43561537/155033709-e07a6306-a85a-4f7f-9624-b53ba5dd7fa9.gif) +- Bump actions/checkout from 2 to 3 ([#24668](https://github.com/RocketChat/Rocket.Chat/pull/24668) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- VoIP Support for Omnichannel ([#23102](https://github.com/RocketChat/Rocket.Chat/pull/23102)) +- Bump actions/setup-node from 2 to 3 ([#24642](https://github.com/RocketChat/Rocket.Chat/pull/24642) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created VoipService to manage VoIP connections and PBX connection +- Bump adm-zip from 0.4.14 to 0.5.9 ([#24538](https://github.com/RocketChat/Rocket.Chat/pull/24538) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created LivechatVoipService that will handle custom cases for livechat (creating rooms, assigning chats to queue, actions when call is finished, etc) +- Bump body-parser from 1.19.0 to 1.19.1 in /ee/server/services ([#23963](https://github.com/RocketChat/Rocket.Chat/pull/23963) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created Basic interfaces to support new services and new model +- Bump body-parser from 1.19.0 to 1.19.2 ([#24821](https://github.com/RocketChat/Rocket.Chat/pull/24821) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created Endpoints for management interfaces +- Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services ([#24517](https://github.com/RocketChat/Rocket.Chat/pull/24517) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Implemented asterisk connector on VoIP service +- Bump body-parser from 1.19.2 to 1.20.0 in /ee/server/services ([#25042](https://github.com/RocketChat/Rocket.Chat/pull/25042) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created UI components to show calls incoming and to allow answering/rejecting calls +- Bump cookie from 0.4.1 to 0.4.2 in /ee/server/services ([#24472](https://github.com/RocketChat/Rocket.Chat/pull/24472) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Added new settings to control call server/management server connection values +- Bump date-fns from 2.24.0 to 2.28.0 ([#24058](https://github.com/RocketChat/Rocket.Chat/pull/24058) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Added endpoints to associate Omnichannel Agents with PBX Extensions +- Bump ejson from 2.2.1 to 2.2.2 ([#25057](https://github.com/RocketChat/Rocket.Chat/pull/25057) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Added support for event listening on server side, to get metadata about calls being received/ongoing +- Bump eslint-plugin-anti-trojan-source from 1.0.6 to 1.1.0 ([#25076](https://github.com/RocketChat/Rocket.Chat/pull/25076) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created new pages to update settings & to see user-extension association +- Bump express from 4.17.1 to 4.17.2 in /ee/server/services ([#24469](https://github.com/RocketChat/Rocket.Chat/pull/24469) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Created new page to see ongoing calls (and past calls) +- Bump express from 4.17.2 to 4.17.3 in /ee/server/services ([#24522](https://github.com/RocketChat/Rocket.Chat/pull/24522) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Added support for remote hangup/hold on calls +- Bump follow-redirects from 1.14.7 to 1.14.8 in /ee/server/services ([#24491](https://github.com/RocketChat/Rocket.Chat/pull/24491) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Implemented call metrics calculation (hold time, waiting time, talk time) +- Bump is-svg from 4.3.1 to 4.3.2 ([#24801](https://github.com/RocketChat/Rocket.Chat/pull/24801) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Show a notificaiton when call is received +- Bump jaeger-client from 3.18.1 to 3.19.0 in /ee/server/services ([#23961](https://github.com/RocketChat/Rocket.Chat/pull/23961) by [@dependabot[bot]](https://github.com/dependabot[bot])) -### 🚀 Improvements +- Bump jschardet from 1.6.0 to 3.0.0 ([#23121](https://github.com/RocketChat/Rocket.Chat/pull/23121) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Bump minimist from 1.2.5 to 1.2.6 in /ee/server/services ([#24991](https://github.com/RocketChat/Rocket.Chat/pull/24991) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- **ENTERPRISE:** Improve how micro services are loaded ([#24388](https://github.com/RocketChat/Rocket.Chat/pull/24388)) +- Bump pino and pino-pretty ([#25052](https://github.com/RocketChat/Rocket.Chat/pull/25052)) -- Add return button in chats opened from the list of current chats ([#24458](https://github.com/RocketChat/Rocket.Chat/pull/24458) by [@LucasFASouza](https://github.com/LucasFASouza)) +- Bump pino from 7.8.0 to 7.8.1 in /ee/server/services ([#24783](https://github.com/RocketChat/Rocket.Chat/pull/24783) by [@dependabot[bot]](https://github.com/dependabot[bot])) - The new return button for Omnichannel chats came out with release 3.15 but the feature was only available for chats that were opened from Omnichannel Contact Center. - Now, the same UI/UX is supported for chats opened from Current Chats list. +- Bump pino from 7.8.1 to 7.9.1 in /ee/server/services ([#24869](https://github.com/RocketChat/Rocket.Chat/pull/24869) by [@dependabot[bot]](https://github.com/dependabot[bot])) - ![image](https://user-images.githubusercontent.com/32396925/153283190-bd5c9748-c36b-4874-a704-6043afc7e3a1.png) +- Bump pino-pretty from 7.5.1 to 7.5.2 in /ee/server/services ([#24689](https://github.com/RocketChat/Rocket.Chat/pull/24689) by [@dependabot[bot]](https://github.com/dependabot[bot])) - The chat now opens in the Omnichannel settings and has the return button so the user can go back to the Current Chats list. +- Bump pino-pretty from 7.5.2 to 7.5.3 in /ee/server/services ([#24698](https://github.com/RocketChat/Rocket.Chat/pull/24698) by [@dependabot[bot]](https://github.com/dependabot[bot])) - ![image](https://user-images.githubusercontent.com/32396925/153285591-fad8e4a0-d2ea-4a02-8b2a-15e383b3c876.png) +- Bump pino-pretty from 7.5.3 to 7.5.4 in /ee/server/services ([#24870](https://github.com/RocketChat/Rocket.Chat/pull/24870) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Add tooltips on action buttons of Canned Response message composer ([#24483](https://github.com/RocketChat/Rocket.Chat/pull/24483) by [@LucasFASouza](https://github.com/LucasFASouza)) +- Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services ([#24537](https://github.com/RocketChat/Rocket.Chat/pull/24537) by [@dependabot[bot]](https://github.com/dependabot[bot])) - The tooltips were missing on the action buttons of CR message composer. +- Bump prometheus-gc-stats from 0.6.2 to 0.6.3 ([#24803](https://github.com/RocketChat/Rocket.Chat/pull/24803) by [@dependabot[bot]](https://github.com/dependabot[bot])) - ![image](https://user-images.githubusercontent.com/32396925/153620327-91107245-4b47-4d39-a99a-6da6d1cf5734.png) +- Bump simple-get from 4.0.0 to 4.0.1 ([#24341](https://github.com/RocketChat/Rocket.Chat/pull/24341) by [@dependabot[bot]](https://github.com/dependabot[bot])) - Users can now feel more encouraged to use these actions knowing what they are supposed to do. +- Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services ([#23512](https://github.com/RocketChat/Rocket.Chat/pull/23512) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Add user to room on "Click to Join!" button press ([#24041](https://github.com/RocketChat/Rocket.Chat/pull/24041) by [@ostjen](https://github.com/ostjen)) +- Bump template-file from 6.0.0 to 6.0.1 ([#25002](https://github.com/RocketChat/Rocket.Chat/pull/25002) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Add user to room on "Click to Join!" button press; +- Bump ts-node from 10.5.0 to 10.6.0 in /ee/server/services ([#24667](https://github.com/RocketChat/Rocket.Chat/pull/24667) by [@dependabot[bot]](https://github.com/dependabot[bot])) - - Display the "Join" button in discussions inside channels (keeping the behavior consistent with discussions inside groups). +- Bump ts-node from 10.6.0 to 10.7.0 in /ee/server/services ([#24716](https://github.com/RocketChat/Rocket.Chat/pull/24716) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Added a new "All" tab which shows all integrations in Integrations ([#24109](https://github.com/RocketChat/Rocket.Chat/pull/24109) by [@aswinidev](https://github.com/aswinidev)) +- Bump underscore.string from 3.3.5 to 3.3.6 in /ee/server/services ([#24498](https://github.com/RocketChat/Rocket.Chat/pull/24498) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- ChatBox Text to File Description ([#24451](https://github.com/RocketChat/Rocket.Chat/pull/24451) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Bump url-parse from 1.5.3 to 1.5.7 ([#24528](https://github.com/RocketChat/Rocket.Chat/pull/24528) by [@dependabot[bot]](https://github.com/dependabot[bot])) - The text content from chatbox goes to the file description when drag and drop a file. +- Bump url-parse from 1.5.7 to 1.5.10 ([#24640](https://github.com/RocketChat/Rocket.Chat/pull/24640) by [@dependabot[bot]](https://github.com/dependabot[bot])) -- Close modal on esc and outside click ([#24275](https://github.com/RocketChat/Rocket.Chat/pull/24275)) +- Bump vm2 from 3.9.5 to 3.9.7 in /ee/server/services ([#24509](https://github.com/RocketChat/Rocket.Chat/pull/24509) by [@dependabot[bot]](https://github.com/dependabot[bot])) - This is a QUICK change in order to close modals pressing Esc button and clicking outside of it **intentionally**. +- Chore: `twoFactorRequired` signature ([#24518](https://github.com/RocketChat/Rocket.Chat/pull/24518)) -- CloudLoginModal visual consistency ([#24334](https://github.com/RocketChat/Rocket.Chat/pull/24334)) + Improved type checking for decorator `twoFactorRequired`. - ### before - ![image](https://user-images.githubusercontent.com/27704687/151585064-dc6a1e29-9903-4241-8fbd-dfbe6c55fbef.png) +- Chore: Add description to global OTR setting ([#24333](https://github.com/RocketChat/Rocket.Chat/pull/24333) by [@pedrogssouza](https://github.com/pedrogssouza)) - ### after - ![Screen Shot 2022-01-28 at 13 32 02](https://user-images.githubusercontent.com/27704687/151585101-75b98502-9aae-4198-bc3e-4956750e5d8b.png) +- Chore: Add E2E tests for livechat/room.close ([#24729](https://github.com/RocketChat/Rocket.Chat/pull/24729) by [@Muramatsu2602](https://github.com/Muramatsu2602)) -- Convert tag edit with department data to tsx ([#24369](https://github.com/RocketChat/Rocket.Chat/pull/24369) by [@LucasFASouza](https://github.com/LucasFASouza)) + * Create a new test suite file under tests/end-to-end/api/livechat + * Create tests for the following endpoint: + + ivechat/room.close -- Descriptive tooltip for Encrypted Key on Room Header ([#24121](https://github.com/RocketChat/Rocket.Chat/pull/24121)) +- Chore: Add E2E tests for livechat/visitor ([#24764](https://github.com/RocketChat/Rocket.Chat/pull/24764) by [@Muramatsu2602](https://github.com/Muramatsu2602)) -- OTR system messages ([#24382](https://github.com/RocketChat/Rocket.Chat/pull/24382)) + - Create a new test suite file under tests/end-to-end/api/livechat + - Create tests for the following endpoints: + + livechat/visitor (create visitor, update visitor, add custom fields to visitors) - OTR system messages to indicate key refresh and joining chat to users. +- Chore: Add error boundary to message component ([#25223](https://github.com/RocketChat/Rocket.Chat/pull/25223)) -- Purchase Type Filter for marketplace apps and Categories filter anchor refactoring ([#24454](https://github.com/RocketChat/Rocket.Chat/pull/24454)) + Not crash the whole application if something goes wrong in the MessageList component. + + ![image](https://user-images.githubusercontent.com/40830821/162269915-931c5c3c-c979-4234-b74c-371f67467ce0.png) - Implemented a filter by purchase type(free or paid) component for the apps screen of the marketplace. Besides that, new entries on the dictionary, fixed some parts of the App type (purchaseType was typed as unknown and price as string), and created some helpers to work alongside the filter. Will be refactoring the categories filter anchor and then will open this PR for reviews. +- Chore: Add Livechat repo into Monorepo packages ([#25312](https://github.com/RocketChat/Rocket.Chat/pull/25312)) - Demo gif: - ![purchaseTypeFIlter](https://user-images.githubusercontent.com/43561537/153101228-7b7ebdc3-2d34-420f-aa9d-f7cbc8d4b53f.gif) +- Chore: Add options to debug stdout and rate limiter ([#25336](https://github.com/RocketChat/Rocket.Chat/pull/25336)) - Refactored the categories filter anchor from a plain fuselage select to a select button with dynamic colors. - Demo gif: - ![New categories filter anchor(PR)](https://user-images.githubusercontent.com/43561537/153422427-28012b7d-e0ec-45f4-861d-c9368c57ad04.gif) +- Chore: Add root package.json to houston files ([#25286](https://github.com/RocketChat/Rocket.Chat/pull/25286)) -- Replace AutoComplete in UserAutoComplete & UserAutoCompleteMultiple components ([#24529](https://github.com/RocketChat/Rocket.Chat/pull/24529)) + See title - This PR replaces a deprecated fuselage's component `AutoComplete` in favor of `Select` and `MultiSelect` which fixes some of UX/UI issues in selecting users +- Chore: add some missing REST definitions ([#24925](https://github.com/RocketChat/Rocket.Chat/pull/24925) by [@gerzonc](https://github.com/gerzonc)) - ### before - ![Screen Shot 2022-02-19 at 13 33 28](https://user-images.githubusercontent.com/27704687/154809737-8181a06c-4f20-48ea-90f7-01e828b9a452.png) + On the [mobile client](https://github.com/RocketChat/Rocket.Chat.ReactNative), we made an effort to collect more `REST API` definitions that are missing on the server side during our migration to TypeScript. Since we're both migrating to TypeScript, we thought it would be a good idea to share those so you guys can benefit from our initiative. - ### after - ![Screen Shot 2022-02-19 at 13 30 58](https://user-images.githubusercontent.com/27704687/154809653-a8ec9a80-c0dd-4a25-9c00-0f96147d79e9.png) +- Chore: Add yarn plugin to check node and yarn version ([#25224](https://github.com/RocketChat/Rocket.Chat/pull/25224)) -- Skip encryption for slash commands in E2E rooms ([#24475](https://github.com/RocketChat/Rocket.Chat/pull/24475)) +- Chore: added Server Instances endpoint types ([#24507](https://github.com/RocketChat/Rocket.Chat/pull/24507)) - Currently Slash Commands don't work in an E2EE room, as we encrypt the message before slash command is detected by the server, So removed encryption for slash commands in e2e rooms. + Created typing for endpoint definitions on `instances.ts`. -- Team system messages feedback ([#24209](https://github.com/RocketChat/Rocket.Chat/pull/24209) by [@ostjen](https://github.com/ostjen)) +- Chore: added settings endpoint types ([#24506](https://github.com/RocketChat/Rocket.Chat/pull/24506)) - - Delete some keys that aren't being used (eg: User_left_female). + Created typing for endpoint definitions on `settings.ts`. - - Add new Teams' system messages: - - `added-user-to-team`: **added** @\user to this Team; - - `removed-user-from-team`: **removed** @\user from this Team; - - `user-converted-to-team`: **converted** #\room to a Team; - - `user-converted-to-channel`: **converted** #\room to a Channel; - - `user-removed-room-from-team`: **removed** @\user from this Team; - - `user-deleted-room-from-team`: **deleted** #\room from this Team; - - `user-added-room-to-team`: **deleted** #\room to this Team; +- Chore: APIClass types ([#24747](https://github.com/RocketChat/Rocket.Chat/pull/24747)) - - Add the corresponding options to hide each new system message and the missing `ujt` and `ult` hide options. + This pull request creates a new `restivus` module (.d.ts) for the `api.js` file. -### 🐛 Bug fixes +- Chore: Bump fuselage ([#25371](https://github.com/RocketChat/Rocket.Chat/pull/25371)) +- Chore: Bump Fuselage packages ([#25259](https://github.com/RocketChat/Rocket.Chat/pull/25259)) -- 2FA via email when logging in using OAuth ([#24572](https://github.com/RocketChat/Rocket.Chat/pull/24572)) +- Chore: Bump Fuselage packages ([#25015](https://github.com/RocketChat/Rocket.Chat/pull/25015)) -- Add ?close to OAuth callback url ([#24381](https://github.com/RocketChat/Rocket.Chat/pull/24381)) + It uses the last stable version of Fuselage packages. -- GDPR action to forget visitor data on request ([#24441](https://github.com/RocketChat/Rocket.Chat/pull/24441)) +- Chore: Bump Fuselage packages ([#24573](https://github.com/RocketChat/Rocket.Chat/pull/24573)) -- Implement client errors on ddp-streamer ([#24310](https://github.com/RocketChat/Rocket.Chat/pull/24310)) + It uses the last stable version of Fuselage packages. -- Inconsistent validation of user's access to rooms ([#24037](https://github.com/RocketChat/Rocket.Chat/pull/24037) by [@ostjen](https://github.com/ostjen)) +- Chore: bump fuselage version ([#24453](https://github.com/RocketChat/Rocket.Chat/pull/24453)) -- Issues on selecting users when importing CSV ([#24253](https://github.com/RocketChat/Rocket.Chat/pull/24253)) +- Chore: Cancel running jobs if PR is updated ([#24708](https://github.com/RocketChat/Rocket.Chat/pull/24708)) - * Fix users selecting by fixing their _id +- Chore: Convert admin custom sound to tsx ([#25128](https://github.com/RocketChat/Rocket.Chat/pull/25128)) - * Add condition to disable 'Start importing' button if `usersCount`, `channelsCount` and `messageCount` equals 0, or if messageCount is alone +- Chore: Convert JS files to Typescript ([#24410](https://github.com/RocketChat/Rocket.Chat/pull/24410)) - * Remove `disabled={usersCount === 0}` on user Tab + This pull request converts 26 more files from Javascript to Typescript, to check variable types and increase validation on the code. -- OAuth mismatch redirect_uri error ([#24450](https://github.com/RocketChat/Rocket.Chat/pull/24450)) +- Chore: Convert LivechatAgentActivity to raw model and TS ([#25123](https://github.com/RocketChat/Rocket.Chat/pull/25123)) -- Oembed request not respecting payload limit ([#24418](https://github.com/RocketChat/Rocket.Chat/pull/24418)) +- Chore: Convert Mailer to TS ([#25121](https://github.com/RocketChat/Rocket.Chat/pull/25121)) -- Omnichannel managers can't join chats in progress ([#24553](https://github.com/RocketChat/Rocket.Chat/pull/24553)) +- Chore: Convert NotificationStatus to TS ([#25125](https://github.com/RocketChat/Rocket.Chat/pull/25125)) -- Outgoing webhook without scripts not saving messages ([#24401](https://github.com/RocketChat/Rocket.Chat/pull/24401)) +- Chore: Convert server functions from javascript to typescript ([#24384](https://github.com/RocketChat/Rocket.Chat/pull/24384)) -- Prevent Apps Bridge to remove visitor status from room ([#24305](https://github.com/RocketChat/Rocket.Chat/pull/24305)) + This pull request will be used to rewrite some functions on the Chat Engine to Typescript, in order to increase security and specify variable types on the code. -- Read receipts showing first messages of the room as read even if not read by everyone ([#24508](https://github.com/RocketChat/Rocket.Chat/pull/24508)) +- Chore: Convert to typescript the me slashCommands files ([#24321](https://github.com/RocketChat/Rocket.Chat/pull/24321) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- respect `Accounts_Registration_Users_Default_Roles` setting ([#24173](https://github.com/RocketChat/Rocket.Chat/pull/24173)) + Convert to typescript the me slashCommands files - - Fix `user` role being added as default regardless of the `Accounts_Registration_Users_Default_Roles` setting. +- Chore: Convert to typescript the mute and unmute slash commands files ([#24325](https://github.com/RocketChat/Rocket.Chat/pull/24325) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- Room context tabs not working in Omnichannel current chats page ([#24559](https://github.com/RocketChat/Rocket.Chat/pull/24559)) + Convert to typescript the mute and unmute slash commands files -- Skip admin info in setup wizard for servers with admin registered ([#24485](https://github.com/RocketChat/Rocket.Chat/pull/24485)) +- Chore: Convert to typescript the slash commands create files ([#24306](https://github.com/RocketChat/Rocket.Chat/pull/24306) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- Skip cloud steps for registered servers on setup wizard ([#24407](https://github.com/RocketChat/Rocket.Chat/pull/24407)) + Convert Slash Commands create files to typescript. -- Slash commands previews not working ([#24387](https://github.com/RocketChat/Rocket.Chat/pull/24387) by [@ostjen](https://github.com/ostjen)) +- Chore: Convert to typescript the slash commands invite files ([#24311](https://github.com/RocketChat/Rocket.Chat/pull/24311) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- Startup errors creating indexes ([#24409](https://github.com/RocketChat/Rocket.Chat/pull/24409)) + Convert to typescript the slash commands invite files - Fix `bio` and `prid` startup index creation errors. +- Chore: Convert to typescript the unarchive slash commands files ([#24331](https://github.com/RocketChat/Rocket.Chat/pull/24331) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) -- typo on register server tooltip of setup wizard ([#24466](https://github.com/RocketChat/Rocket.Chat/pull/24466)) + Convert to typescript the unarchive slash commands files -
-🔍 Minor changes +- Chore: converted more hooks to typescript ([#24628](https://github.com/RocketChat/Rocket.Chat/pull/24628)) + Converted some functions on `client/hooks/` from JavaScript to Typescript. -- Bump @types/ws from 8.2.2 to 8.2.3 in /ee/server/services ([#24556](https://github.com/RocketChat/Rocket.Chat/pull/24556) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Create README.md for Rest Typings ([#25335](https://github.com/RocketChat/Rocket.Chat/pull/25335)) -- Bump adm-zip from 0.4.14 to 0.5.9 ([#24538](https://github.com/RocketChat/Rocket.Chat/pull/24538) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Delete unused file (NewAdminInfoPage.js) ([#24196](https://github.com/RocketChat/Rocket.Chat/pull/24196)) -- Bump body-parser from 1.19.0 to 1.19.1 in /ee/server/services ([#23963](https://github.com/RocketChat/Rocket.Chat/pull/23963) by [@dependabot[bot]](https://github.com/dependabot[bot])) + Just removing a duplicated/unused file. -- Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services ([#24517](https://github.com/RocketChat/Rocket.Chat/pull/24517) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: ensure scripts use cross-env and ignore some dirs (ROC-54) ([#25218](https://github.com/RocketChat/Rocket.Chat/pull/25218)) -- Bump cookie from 0.4.1 to 0.4.2 in /ee/server/services ([#24472](https://github.com/RocketChat/Rocket.Chat/pull/24472) by [@dependabot[bot]](https://github.com/dependabot[bot])) + - data and test-failure should be ignored + - ensure scripts use cross-env -- Bump date-fns from 2.24.0 to 2.28.0 ([#24058](https://github.com/RocketChat/Rocket.Chat/pull/24058) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Fix Cypress tests ([#24544](https://github.com/RocketChat/Rocket.Chat/pull/24544)) -- Bump express from 4.17.1 to 4.17.2 in /ee/server/services ([#24469](https://github.com/RocketChat/Rocket.Chat/pull/24469) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Fix grammatical errors in Code of Conduct ([#24759](https://github.com/RocketChat/Rocket.Chat/pull/24759) by [@aadishJ01](https://github.com/aadishJ01)) -- Bump express from 4.17.2 to 4.17.3 in /ee/server/services ([#24522](https://github.com/RocketChat/Rocket.Chat/pull/24522) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: fix grammatical errors in Features ([#24771](https://github.com/RocketChat/Rocket.Chat/pull/24771) by [@aadishJ01](https://github.com/aadishJ01)) -- Bump follow-redirects from 1.14.7 to 1.14.8 in /ee/server/services ([#24491](https://github.com/RocketChat/Rocket.Chat/pull/24491) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Fix return type warnings ([#25275](https://github.com/RocketChat/Rocket.Chat/pull/25275)) -- Bump jaeger-client from 3.18.1 to 3.19.0 in /ee/server/services ([#23961](https://github.com/RocketChat/Rocket.Chat/pull/23961) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Get Settings Statistics ([#24397](https://github.com/RocketChat/Rocket.Chat/pull/24397)) -- Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services ([#24537](https://github.com/RocketChat/Rocket.Chat/pull/24537) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Improve logger to allow log of `unknown` values ([#24726](https://github.com/RocketChat/Rocket.Chat/pull/24726)) -- Bump simple-get from 4.0.0 to 4.0.1 ([#24341](https://github.com/RocketChat/Rocket.Chat/pull/24341) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Improve PR title validation regex ([#24467](https://github.com/RocketChat/Rocket.Chat/pull/24467)) -- Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services ([#23512](https://github.com/RocketChat/Rocket.Chat/pull/23512) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Improvements on role syncing (ldap, oauth and saml) ([#23824](https://github.com/RocketChat/Rocket.Chat/pull/23824)) -- Bump underscore.string from 3.3.5 to 3.3.6 in /ee/server/services ([#24498](https://github.com/RocketChat/Rocket.Chat/pull/24498) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Js to ts slash commands archive ([#24304](https://github.com/RocketChat/Rocket.Chat/pull/24304) by [@eduardofcabrera](https://github.com/eduardofcabrera)) -- Bump url-parse from 1.5.3 to 1.5.7 ([#24528](https://github.com/RocketChat/Rocket.Chat/pull/24528) by [@dependabot[bot]](https://github.com/dependabot[bot])) + Convert Slash Commands archive files to typescript -- Bump vm2 from 3.9.5 to 3.9.7 in /ee/server/services ([#24509](https://github.com/RocketChat/Rocket.Chat/pull/24509) by [@dependabot[bot]](https://github.com/dependabot[bot])) +- Chore: Micro services fixes and cleanup ([#24753](https://github.com/RocketChat/Rocket.Chat/pull/24753)) -- Chore: `twoFactorRequired` signature ([#24518](https://github.com/RocketChat/Rocket.Chat/pull/24518)) +- Chore: Migrate oauth2server to typescript ([#25126](https://github.com/RocketChat/Rocket.Chat/pull/25126)) - Improved type checking for decorator `twoFactorRequired`. +- Chore: Minor dependency updates ([#25269](https://github.com/RocketChat/Rocket.Chat/pull/25269)) -- Chore: Add description to global OTR setting ([#24333](https://github.com/RocketChat/Rocket.Chat/pull/24333) by [@pedrogssouza](https://github.com/pedrogssouza)) +- Chore: Missing keys in APIsDisplay ([#24464](https://github.com/RocketChat/Rocket.Chat/pull/24464)) -- Chore: Bump Fuselage packages ([#24573](https://github.com/RocketChat/Rocket.Chat/pull/24573)) +- Chore: Monorepo ([#25074](https://github.com/RocketChat/Rocket.Chat/pull/25074)) - It uses the last stable version of Fuselage packages. +- Chore: move definitions to packages ([#25085](https://github.com/RocketChat/Rocket.Chat/pull/25085)) -- Chore: bump fuselage version ([#24453](https://github.com/RocketChat/Rocket.Chat/pull/24453)) +- Chore: organize test files and fix code coverage ([#24900](https://github.com/RocketChat/Rocket.Chat/pull/24900)) -- Chore: Convert JS files to Typescript ([#24410](https://github.com/RocketChat/Rocket.Chat/pull/24410)) +- Chore: Remove Alpine image deps after using them ([#25053](https://github.com/RocketChat/Rocket.Chat/pull/25053)) - This pull request converts 26 more files from Javascript to Typescript, to check variable types and increase validation on the code. +- Chore: Remove duplicated useUserRoom ([#25180](https://github.com/RocketChat/Rocket.Chat/pull/25180)) -- Chore: Convert to typescript the me slashCommands files ([#24321](https://github.com/RocketChat/Rocket.Chat/pull/24321) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: Remove old files from removed Omnichannel feature ([#25129](https://github.com/RocketChat/Rocket.Chat/pull/25129)) - Convert to typescript the me slashCommands files +- Chore: Remove old scripts ([#24911](https://github.com/RocketChat/Rocket.Chat/pull/24911)) -- Chore: Convert to typescript the mute and unmute slash commands files ([#24325](https://github.com/RocketChat/Rocket.Chat/pull/24325) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: Remove package-lock.json from houston files ([#25280](https://github.com/RocketChat/Rocket.Chat/pull/25280)) - Convert to typescript the mute and unmute slash commands files + Houston config in the `package.json` file still mentioned `package-lock.json`, but it doesn't exist anymore -- Chore: Convert to typescript the slash commands create files ([#24306](https://github.com/RocketChat/Rocket.Chat/pull/24306) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: Remove storybook build job from CI ([#24530](https://github.com/RocketChat/Rocket.Chat/pull/24530)) - Convert Slash Commands create files to typescript. +- Chore: Remove unused Drone CI files ([#25124](https://github.com/RocketChat/Rocket.Chat/pull/25124)) -- Chore: Convert to typescript the slash commands invite files ([#24311](https://github.com/RocketChat/Rocket.Chat/pull/24311) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: roomTypes: Stop mixing client and server code together ([#24536](https://github.com/RocketChat/Rocket.Chat/pull/24536)) - Convert to typescript the slash commands invite files +- Chore: Run tests using microservices deployment on CI ([#24513](https://github.com/RocketChat/Rocket.Chat/pull/24513)) -- Chore: Convert to typescript the unarchive slash commands files ([#24331](https://github.com/RocketChat/Rocket.Chat/pull/24331) by [@eduardofcabrera](https://github.com/eduardofcabrera) & [@ostjen](https://github.com/ostjen)) +- Chore: Set Docker image tag to latest only when really latest ([#24366](https://github.com/RocketChat/Rocket.Chat/pull/24366)) - Convert to typescript the unarchive slash commands files +- Chore: Skip local services changes when shutting down duplicated services ([#24810](https://github.com/RocketChat/Rocket.Chat/pull/24810)) -- Chore: Delete unused file (NewAdminInfoPage.js) ([#24196](https://github.com/RocketChat/Rocket.Chat/pull/24196)) +- Chore: Storybook mocking and examples improved ([#24969](https://github.com/RocketChat/Rocket.Chat/pull/24969)) - Just removing a duplicated/unused file. + - Stories from `ee/` included; + - Differentiate root story kinds; + - Mocking of `ServerContext` via Storybook parameters. -- Chore: Improve PR title validation regex ([#24467](https://github.com/RocketChat/Rocket.Chat/pull/24467)) +- Chore: Sync with master ([#25284](https://github.com/RocketChat/Rocket.Chat/pull/25284)) -- Chore: Js to ts slash commands archive ([#24304](https://github.com/RocketChat/Rocket.Chat/pull/24304) by [@eduardofcabrera](https://github.com/eduardofcabrera)) +- Chore: Template to generate packages ([#25174](https://github.com/RocketChat/Rocket.Chat/pull/25174)) - Convert Slash Commands archive files to typescript + ``` + npx hygen package new test + ``` -- Chore: Remove storybook build job from CI ([#24530](https://github.com/RocketChat/Rocket.Chat/pull/24530)) +- Chore: Tests with Playwright (task: All works) ([#25122](https://github.com/RocketChat/Rocket.Chat/pull/25122)) -- Chore: roomTypes: Stop mixing client and server code together ([#24536](https://github.com/RocketChat/Rocket.Chat/pull/24536)) +- Chore: Tests with Playwright (task: ROC-28, 09-channels) ([#25196](https://github.com/RocketChat/Rocket.Chat/pull/25196)) -- Chore: Run tests using microservices deployment on CI ([#24513](https://github.com/RocketChat/Rocket.Chat/pull/24513)) +- Chore: TS conversion folder client ([#25031](https://github.com/RocketChat/Rocket.Chat/pull/25031)) -- Chore: Set Docker image tag to latest only when really latest ([#24366](https://github.com/RocketChat/Rocket.Chat/pull/24366)) +- Chore: TS migration SortList ([#25167](https://github.com/RocketChat/Rocket.Chat/pull/25167)) - Chore: Unify ILivechatAgent with ILivechatAgentRecord ([#24406](https://github.com/RocketChat/Rocket.Chat/pull/24406)) -- Chore: Update Apps-Engine ([#24568](https://github.com/RocketChat/Rocket.Chat/pull/24568)) - - Chore: Update Apps-Engine ([#24651](https://github.com/RocketChat/Rocket.Chat/pull/24651)) +- Chore: Update Apps-Engine ([#24568](https://github.com/RocketChat/Rocket.Chat/pull/24568)) + - Chore: Update fuselage deps to match monolith versions ([#24501](https://github.com/RocketChat/Rocket.Chat/pull/24501)) +- Chore: Update Livechat to the last version ([#25257](https://github.com/RocketChat/Rocket.Chat/pull/25257)) + +- Chore: Update Livechat version ([#25130](https://github.com/RocketChat/Rocket.Chat/pull/25130)) + - Chore: Update Meteor to 2.5.6 ([#24461](https://github.com/RocketChat/Rocket.Chat/pull/24461)) +- Chore: update OTR icon ([#24521](https://github.com/RocketChat/Rocket.Chat/pull/24521) by [@kibonusp](https://github.com/kibonusp)) + + I changed the shredder icon in OTR contextual bar to the stopwatch icon, recently added to the fuselage. + - Chore: Update ws package ([#24477](https://github.com/RocketChat/Rocket.Chat/pull/24477)) +- Chore(deps-dev): Bump @types/mock-require from 2.0.0 to 2.0.1 ([#24574](https://github.com/RocketChat/Rocket.Chat/pull/24574) by [@dependabot[bot]](https://github.com/dependabot[bot])) + - Chore(deps-dev): Bump ts-node from 10.0.0 to 10.5.0 in /ee/server/services ([#24435](https://github.com/RocketChat/Rocket.Chat/pull/24435) by [@dependabot[bot]](https://github.com/dependabot[bot])) - Chore(deps): Bump node-fetch from 2.6.1 to 2.6.7 in /ee/server/services ([#24299](https://github.com/RocketChat/Rocket.Chat/pull/24299) by [@dependabot[bot]](https://github.com/dependabot[bot])) @@ -2614,66 +6535,151 @@ - i18n: Language update from LingoHub 🤖 on 2022-02-21Z ([#24558](https://github.com/RocketChat/Rocket.Chat/pull/24558)) +- i18n: Language update from LingoHub 🤖 on 2022-02-28Z ([#24644](https://github.com/RocketChat/Rocket.Chat/pull/24644)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-07Z ([#24717](https://github.com/RocketChat/Rocket.Chat/pull/24717)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-14Z ([#24823](https://github.com/RocketChat/Rocket.Chat/pull/24823)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-21Z ([#24895](https://github.com/RocketChat/Rocket.Chat/pull/24895)) + +- i18n: Language update from LingoHub 🤖 on 2022-03-28Z ([#24971](https://github.com/RocketChat/Rocket.Chat/pull/24971)) + +- i18n: Language update from LingoHub 🤖 on 2022-04-04Z ([#25043](https://github.com/RocketChat/Rocket.Chat/pull/25043)) + - Merge master into develop & Set version to 4.5.0-develop ([#24363](https://github.com/RocketChat/Rocket.Chat/pull/24363)) +- Merge master into develop & Set version to 4.6.0-develop ([#24653](https://github.com/RocketChat/Rocket.Chat/pull/24653)) + +- Merge master into develop & Set version to 4.7.0-develop ([#25028](https://github.com/RocketChat/Rocket.Chat/pull/25028)) + +- Regression: Add `isPending` status to message ([#25299](https://github.com/RocketChat/Rocket.Chat/pull/25299)) + +- Regression: Add createdOTR index ([#25017](https://github.com/RocketChat/Rocket.Chat/pull/25017)) + +- Regression: Add eslint package to micro services Dockerfile ([#25311](https://github.com/RocketChat/Rocket.Chat/pull/25311)) + +- Regression: Add select message to system message and thread preview and allow select on legacy template ([#25251](https://github.com/RocketChat/Rocket.Chat/pull/25251)) + - Regression: Add support to namespace within micro services ([#24581](https://github.com/RocketChat/Rocket.Chat/pull/24581)) - Regression: Admin Sidebar colors inverted. ([#24609](https://github.com/RocketChat/Rocket.Chat/pull/24609)) +- Regression: Avatar not loading on first direct message ([#25211](https://github.com/RocketChat/Rocket.Chat/pull/25211)) + + fix avatar not loading on a first direct message + +- Regression: Better MongoDB connection management for micro services ([#25323](https://github.com/RocketChat/Rocket.Chat/pull/25323)) + +- Regression: bump onboarding-ui version ([#25320](https://github.com/RocketChat/Rocket.Chat/pull/25320)) + + - Bump to 'next' the onboarding-ui package from fuselage. + - Update from 'companyEmail' to 'email' adminData usage types + - Regression: Bunch of settings fixes for VoIP ([#24594](https://github.com/RocketChat/Rocket.Chat/pull/24594)) -- Regression: Do not show toast on incoming voip calls ([#24619](https://github.com/RocketChat/Rocket.Chat/pull/24619)) +- Regression: Call doesn't stop ringing after agent unregistration ([#24908](https://github.com/RocketChat/Rocket.Chat/pull/24908)) -- Regression: Encode registration info as JWT when signing key is provided ([#24626](https://github.com/RocketChat/Rocket.Chat/pull/24626)) +- Regression: Change preference to be default legacy messages ([#25255](https://github.com/RocketChat/Rocket.Chat/pull/25255)) -- Regression: Error setting user avatars and mentioning rooms on Slack Import ([#24585](https://github.com/RocketChat/Rocket.Chat/pull/24585)) +- Regression: CI playwright ([#25168](https://github.com/RocketChat/Rocket.Chat/pull/25168)) + +- Regression: Custom roles displaying ID instead of name on some admin screens ([#24999](https://github.com/RocketChat/Rocket.Chat/pull/24999)) + + ![image](https://user-images.githubusercontent.com/55164754/160981416-555bcaa1-c075-4260-937c-64523472da43.png) + ![image](https://user-images.githubusercontent.com/55164754/160981452-6eae4e74-8425-4073-8256-472aba72b9db.png) + +- Regression: Do not show toast on incoming voip calls ([#24619](https://github.com/RocketChat/Rocket.Chat/pull/24619)) - - Fix `Mentioned room not found` error when importing rooms from Slack; +- Regression: Encode registration info as JWT when signing key is provided ([#24626](https://github.com/RocketChat/Rocket.Chat/pull/24626)) - - Fix `Forbidden` error when setting avatars for users imported from Slack (on user import/creation); +- Regression: Error is raised when there's no Asterisk queue available yet ([#24980](https://github.com/RocketChat/Rocket.Chat/pull/24980)) - - Fix incorrect message count on imported rooms; +- Regression: Error setting user avatars and mentioning rooms on Slack Import ([#24585](https://github.com/RocketChat/Rocket.Chat/pull/24585)) + - Fix `Mentioned room not found` error when importing rooms from Slack; + - Fix `Forbidden` error when setting avatars for users imported from Slack (on user import/creation); + - Fix incorrect message count on imported rooms; - Fix missing username on messages imported from Slack; - Regression: Error when trying to load name of dm rooms for avatars and notifications ([#24583](https://github.com/RocketChat/Rocket.Chat/pull/24583)) +- Regression: eslint not running on packages ([#25305](https://github.com/RocketChat/Rocket.Chat/pull/25305)) + - Regression: Extension List panel UI not aligned with designs ([#24645](https://github.com/RocketChat/Rocket.Chat/pull/24645)) +- Regression: Fix account service login expiration ([#24920](https://github.com/RocketChat/Rocket.Chat/pull/24920)) + +- Regression: Fix CI monorepo build ([#25107](https://github.com/RocketChat/Rocket.Chat/pull/25107)) + +- Regression: Fix clicking on visitor's chat in the sidebar does not display the chat window ([#25380](https://github.com/RocketChat/Rocket.Chat/pull/25380)) + + Fix: livechat room not opening. + - Regression: Fix double value on holdTime and empty msg on last message ([#24630](https://github.com/RocketChat/Rocket.Chat/pull/24630)) +- Regression: Fix English i18n react text ([#25368](https://github.com/RocketChat/Rocket.Chat/pull/25368)) + + Incorrect text in reaction tooltip has been fixed + +- Regression: Fix federation Matrix bridge startup ([#25273](https://github.com/RocketChat/Rocket.Chat/pull/25273)) + - Regression: Fix in-correct room status shown to agents ([#24592](https://github.com/RocketChat/Rocket.Chat/pull/24592)) - Regression: Fix incoming voip call ringtone is not ringing ([#24616](https://github.com/RocketChat/Rocket.Chat/pull/24616)) +- Regression: Fix micro services Docker build ([#25193](https://github.com/RocketChat/Rocket.Chat/pull/25193)) + +- Regression: Fix multi line is not showing an empty line between lines ([#25317](https://github.com/RocketChat/Rocket.Chat/pull/25317)) + +- Regression: Fix reply button not working when hideFlexTab is enabled ([#25306](https://github.com/RocketChat/Rocket.Chat/pull/25306)) + - Regression: Fix room not getting created due to null visitor status ([#24562](https://github.com/RocketChat/Rocket.Chat/pull/24562)) +- Regression: Fix services Docker build on CI ([#25181](https://github.com/RocketChat/Rocket.Chat/pull/25181)) + +- Regression: Fix size of custom emoji and render emoji on thread message preview ([#25314](https://github.com/RocketChat/Rocket.Chat/pull/25314)) + +- Regression: Fix the alpine image and dev UX installing matrix-rust-sdk-bindings ([#25319](https://github.com/RocketChat/Rocket.Chat/pull/25319)) + + The package only included a few pre-built which caused all macs to have to compile every time they installed and also caused our alpine not to work. + + This temporarily switches to a fork of the matrix-appservice-bridge package. + + Made changes to one of its child dependencies `matrix-rust-sdk-bindings` that adds pre-built binaries for mac and musl (for alpine). + - Regression: Fix time fields and wrap up in Voip Room Contexual bar ([#24625](https://github.com/RocketChat/Rocket.Chat/pull/24625)) - Regression: Fix time format on Voip system messages ([#24603](https://github.com/RocketChat/Rocket.Chat/pull/24603)) - Regression: Fix translation for call started message ([#24615](https://github.com/RocketChat/Rocket.Chat/pull/24615)) +- Regression: Fix unexpected errors breaking ddp-streamer ([#24948](https://github.com/RocketChat/Rocket.Chat/pull/24948)) + - Regression: Fix wrong tab name for VoIP settings ([#24647](https://github.com/RocketChat/Rocket.Chat/pull/24647)) - Regression: Fixes in Voice Contextual Bar and Directory ([#24596](https://github.com/RocketChat/Rocket.Chat/pull/24596)) - Regression: If Asterisk suddenly goes down, server has no way to know. Causes server to get stuck. Needs restart ([#24624](https://github.com/RocketChat/Rocket.Chat/pull/24624)) +- Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests ([#24756](https://github.com/RocketChat/Rocket.Chat/pull/24756)) + - Regression: Mark all rooms as read modal closing instantly. ([#24610](https://github.com/RocketChat/Rocket.Chat/pull/24610)) -- Regression: No audio when call comes from Skype/IP phone ([#24602](https://github.com/RocketChat/Rocket.Chat/pull/24602)) +- Regression: Messages in new message template Crashing. ([#25327](https://github.com/RocketChat/Rocket.Chat/pull/25327)) - The audio was not rendered because of re-rendering of react element based on - queueCounter and roomInfo. queueCounter and roomInfo cause the dom to re-render when call gets accepted - because after accepting call, queueCounter changes or a room gets created. - The audio element gets recreated. But VoIP user probably holds the old one. - The behaviour is not predictable when such case happens. If everything gets cleanly setup, - even if the audio element goes headless, it still continues to play the remote audio. - But in other cases, it is unreferenced the one on dom has its srcObject as null. - This causes no audio. +- Regression: No audio when call comes from Skype/IP phone ([#24602](https://github.com/RocketChat/Rocket.Chat/pull/24602)) - This fix provides a way to re-initialise the rendering elements in VoIP user + The audio was not rendered because of re-rendering of react element based on + queueCounter and roomInfo. queueCounter and roomInfo cause the dom to re-render when call gets accepted + because after accepting call, queueCounter changes or a room gets created. + The audio element gets recreated. But VoIP user probably holds the old one. + The behaviour is not predictable when such case happens. If everything gets cleanly setup, + even if the audio element goes headless, it still continues to play the remote audio. + But in other cases, it is unreferenced the one on dom has its srcObject as null. + This causes no audio. + + This fix provides a way to re-initialise the rendering elements in VoIP user and calls this function on useEffect() if the re-render has happen. - Regression: Prevent button from losing state when rerendering ([#24648](https://github.com/RocketChat/Rocket.Chat/pull/24648)) @@ -2684,85 +6690,116 @@ - Regression: Refresh server connection when MI server settings change ([#24649](https://github.com/RocketChat/Rocket.Chat/pull/24649)) +- Regression: Register services right away ([#24800](https://github.com/RocketChat/Rocket.Chat/pull/24800)) + +- Regression: Revert Bugsnag version ([#25313](https://github.com/RocketChat/Rocket.Chat/pull/25313)) + +- Regression: Rocket.Chat Webapp not loading. ([#25349](https://github.com/RocketChat/Rocket.Chat/pull/25349)) + +- Regression: Role Sync not always working ([#24850](https://github.com/RocketChat/Rocket.Chat/pull/24850)) + - Regression: Server crashing if Voip credentials are invalid ([#24646](https://github.com/RocketChat/Rocket.Chat/pull/24646)) +- Regression: Show username and real name on the message system ([#25254](https://github.com/RocketChat/Rocket.Chat/pull/25254)) + +- Regression: Shows error if micro service cannot connect to Mongo ([#25301](https://github.com/RocketChat/Rocket.Chat/pull/25301)) + +- Regression: Use exact Node version on micro services Docker images ([#25287](https://github.com/RocketChat/Rocket.Chat/pull/25287)) + +- Regression: Validate empty fields for Message template ([#25250](https://github.com/RocketChat/Rocket.Chat/pull/25250)) + - Regression: VoIP service button displayed when VoIP is disabled ([#24598](https://github.com/RocketChat/Rocket.Chat/pull/24598)) +- Regression: yarn dev triggers build dependencies ([#25208](https://github.com/RocketChat/Rocket.Chat/pull/25208)) + +- Release 4.5.0 ([#24652](https://github.com/RocketChat/Rocket.Chat/pull/24652) by [@LucasFASouza](https://github.com/LucasFASouza) & [@aswinidev](https://github.com/aswinidev) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@lingohub[bot]](https://github.com/lingohub[bot]) & [@ostjen](https://github.com/ostjen)) + +- Release 4.5.1 ([#24782](https://github.com/RocketChat/Rocket.Chat/pull/24782) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) & [@cuonghuunguyen](https://github.com/cuonghuunguyen)) + +- Release 4.5.2 ([#24814](https://github.com/RocketChat/Rocket.Chat/pull/24814)) + +- Release 4.5.3 ([#24884](https://github.com/RocketChat/Rocket.Chat/pull/24884)) + +- Release 4.5.4 ([#24938](https://github.com/RocketChat/Rocket.Chat/pull/24938)) + +- Release 4.5.5 ([#24998](https://github.com/RocketChat/Rocket.Chat/pull/24998)) + +- Release 4.6.0 ([#25027](https://github.com/RocketChat/Rocket.Chat/pull/25027) by [@aswinidev](https://github.com/aswinidev) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@eduardofcabrera](https://github.com/eduardofcabrera) & [@lingohub[bot]](https://github.com/lingohub[bot])) + +- Release 4.6.1 ([#25095](https://github.com/RocketChat/Rocket.Chat/pull/25095)) + +- Release 4.6.2 ([#25191](https://github.com/RocketChat/Rocket.Chat/pull/25191) by [@sidmohanty11](https://github.com/sidmohanty11)) + +- Release 4.6.3 ([#25235](https://github.com/RocketChat/Rocket.Chat/pull/25235)) + +- Release 4.7.0 ([#25390](https://github.com/RocketChat/Rocket.Chat/pull/25390) by [@Himanshu664](https://github.com/Himanshu664) & [@dependabot[bot]](https://github.com/dependabot[bot]) & [@lingohub[bot]](https://github.com/lingohub[bot])) + +- Release 4.7.1 ([#25510](https://github.com/RocketChat/Rocket.Chat/pull/25510) by [@felipe-menelau](https://github.com/felipe-menelau)) + +- Release 4.7.2 ([#25580](https://github.com/RocketChat/Rocket.Chat/pull/25580)) +
### 👩‍💻👨‍💻 Contributors 😍 +- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari) +- [@Himanshu664](https://github.com/Himanshu664) +- [@JMoVS](https://github.com/JMoVS) - [@LucasFASouza](https://github.com/LucasFASouza) +- [@Muramatsu2602](https://github.com/Muramatsu2602) +- [@aadishJ01](https://github.com/aadishJ01) +- [@aakash-gitdev](https://github.com/aakash-gitdev) - [@aswinidev](https://github.com/aswinidev) +- [@cuonghuunguyen](https://github.com/cuonghuunguyen) - [@dependabot[bot]](https://github.com/dependabot[bot]) - [@eduardofcabrera](https://github.com/eduardofcabrera) +- [@felipe-menelau](https://github.com/felipe-menelau) +- [@gerzonc](https://github.com/gerzonc) +- [@kibonusp](https://github.com/kibonusp) +- [@lingohub[bot]](https://github.com/lingohub[bot]) - [@ostjen](https://github.com/ostjen) +- [@paulobernardoaf](https://github.com/paulobernardoaf) - [@pedrogssouza](https://github.com/pedrogssouza) +- [@sidmohanty11](https://github.com/sidmohanty11) +- [@tkurz](https://github.com/tkurz) +- [@ujorgeleite](https://github.com/ujorgeleite) ### 👩‍💻👨‍💻 Core Team 🤓 +- [@AllanPazRibeiro](https://github.com/AllanPazRibeiro) - [@KevLehman](https://github.com/KevLehman) +- [@MarcosSpessatto](https://github.com/MarcosSpessatto) - [@MartinSchoeler](https://github.com/MartinSchoeler) +- [@alansikora](https://github.com/alansikora) - [@albuquerquefabio](https://github.com/albuquerquefabio) - [@amolghode1981](https://github.com/amolghode1981) +- [@cauefcr](https://github.com/cauefcr) - [@d-gubert](https://github.com/d-gubert) - [@debdutdeb](https://github.com/debdutdeb) - [@dougfabris](https://github.com/dougfabris) - [@felipe-rod123](https://github.com/felipe-rod123) - [@filipemarins](https://github.com/filipemarins) - [@gabriellsh](https://github.com/gabriellsh) +- [@geekgonecrazy](https://github.com/geekgonecrazy) - [@ggazzo](https://github.com/ggazzo) - [@guijun13](https://github.com/guijun13) +- [@jeanfbrito](https://github.com/jeanfbrito) - [@juliajforesti](https://github.com/juliajforesti) - [@matheusbsilva137](https://github.com/matheusbsilva137) - [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) - [@renatobecker](https://github.com/renatobecker) - [@rique223](https://github.com/rique223) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) +- [@souzaramon](https://github.com/souzaramon) - [@tassoevan](https://github.com/tassoevan) - [@tiagoevanp](https://github.com/tiagoevanp) -- [@ujorgeleite](https://github.com/ujorgeleite) +- [@tmontini](https://github.com/tmontini) +- [@weslley543](https://github.com/weslley543) - [@yash-rajpal](https://github.com/yash-rajpal) -# 4.4.5 - -`2022-05-30 · 1 🐛 · 1 👩‍💻👨‍💻` - -### Engine versions -- Node: `14.18.2` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.30.0` - -### 🐛 Bug fixes - - -- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) - -### 👩‍💻👨‍💻 Core Team 🤓 - -- [@ggazzo](https://github.com/ggazzo) - -# 4.4.4 -`2022-05-20 · 1 🐛 · 1 👩‍💻👨‍💻` - -### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` -- Apps-Engine: `1.30.0` - -### 🐛 Bug fixes - - -- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) - -### 👩‍💻👨‍💻 Core Team 🤓 - -- [@ggazzo](https://github.com/ggazzo) - # 4.4.3 `2022-04-07 · 2 🐛 · 2 👩‍💻👨‍💻` @@ -2878,16 +6915,16 @@ - App empty states component, category filter and empty states error variation implementations ([#23818](https://github.com/RocketChat/Rocket.Chat/pull/23818)) - Created and implemented the category filters component: - Demo gif: - ![categories_filter_demo](https://user-images.githubusercontent.com/43561537/148579731-1de83bf8-91ce-47e7-b6e5-7781384fdef9.gif) - - Created and implemented the empty states(States on fuselage) component: - Demo gif: - ![empty_states_demo](https://user-images.githubusercontent.com/43561537/148579930-49c2ff69-88f4-4a57-a24a-060868d76209.gif) - - Implemented a variations system for the empty states component and created a error message for network outage: - Demo gif: + Created and implemented the category filters component: + Demo gif: + ![categories_filter_demo](https://user-images.githubusercontent.com/43561537/148579731-1de83bf8-91ce-47e7-b6e5-7781384fdef9.gif) + + Created and implemented the empty states(States on fuselage) component: + Demo gif: + ![empty_states_demo](https://user-images.githubusercontent.com/43561537/148579930-49c2ff69-88f4-4a57-a24a-060868d76209.gif) + + Implemented a variations system for the empty states component and created a error message for network outage: + Demo gif: ![empty_states_variation_demo](https://user-images.githubusercontent.com/43561537/148580047-39adf8ef-2ee0-4c3e-8709-5faea4a5e335.gif) - Apple Login ([#24060](https://github.com/RocketChat/Rocket.Chat/pull/24060)) @@ -2903,42 +6940,41 @@ - Admin page header buttons consistency ([#24168](https://github.com/RocketChat/Rocket.Chat/pull/24168)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/149371746-66e5e6e4-5c8e-46d7-b230-ecbc4502b665.png) - ![image](https://user-images.githubusercontent.com/27704687/149371759-c3d948af-d877-486c-a263-da12c0b70185.png) - ![image](https://user-images.githubusercontent.com/27704687/149371769-09b0623d-a5c5-43e0-a4ef-73ba0bcf1730.png) - ![image](https://user-images.githubusercontent.com/27704687/149371782-b1b898c7-3aad-47ee-8c5c-cf9cb816d72b.png) - ![image](https://user-images.githubusercontent.com/27704687/149371796-b88514d2-3c8d-4d9d-a45b-24f48783e95c.png) - - - ### after - ![Screen Shot 2022-01-13 at 13 38 00](https://user-images.githubusercontent.com/27704687/149371084-668d5f14-e03e-4cdd-8763-058db9c2f16c.png) - ![Screen Shot 2022-01-13 at 13 38 18](https://user-images.githubusercontent.com/27704687/149371126-23a059cb-efa7-4ffb-970b-da23d8742bb1.png) - ![Screen Shot 2022-01-13 at 13 38 38](https://user-images.githubusercontent.com/27704687/149371181-c8bbbbbd-ed6d-48b4-844f-09fdce0080b6.png) - ![Screen Shot 2022-01-13 at 13 38 59](https://user-images.githubusercontent.com/27704687/149371232-3d292f5e-e8b0-41e1-b065-90a80a5f08ce.png) + ### before + ![image](https://user-images.githubusercontent.com/27704687/149371746-66e5e6e4-5c8e-46d7-b230-ecbc4502b665.png) + ![image](https://user-images.githubusercontent.com/27704687/149371759-c3d948af-d877-486c-a263-da12c0b70185.png) + ![image](https://user-images.githubusercontent.com/27704687/149371769-09b0623d-a5c5-43e0-a4ef-73ba0bcf1730.png) + ![image](https://user-images.githubusercontent.com/27704687/149371782-b1b898c7-3aad-47ee-8c5c-cf9cb816d72b.png) + ![image](https://user-images.githubusercontent.com/27704687/149371796-b88514d2-3c8d-4d9d-a45b-24f48783e95c.png) + + + ### after + ![Screen Shot 2022-01-13 at 13 38 00](https://user-images.githubusercontent.com/27704687/149371084-668d5f14-e03e-4cdd-8763-058db9c2f16c.png) + ![Screen Shot 2022-01-13 at 13 38 18](https://user-images.githubusercontent.com/27704687/149371126-23a059cb-efa7-4ffb-970b-da23d8742bb1.png) + ![Screen Shot 2022-01-13 at 13 38 38](https://user-images.githubusercontent.com/27704687/149371181-c8bbbbbd-ed6d-48b4-844f-09fdce0080b6.png) + ![Screen Shot 2022-01-13 at 13 38 59](https://user-images.githubusercontent.com/27704687/149371232-3d292f5e-e8b0-41e1-b065-90a80a5f08ce.png) ![Screen Shot 2022-01-13 at 13 39 08](https://user-images.githubusercontent.com/27704687/149371263-64fd09e4-456e-48ee-9976-83f42b90e4d9.png) - Importer text for CSV upload file format ([#23817](https://github.com/RocketChat/Rocket.Chat/pull/23817) by [@ostjen](https://github.com/ostjen)) - lib/Statistics improved and metrics collector ([#24177](https://github.com/RocketChat/Rocket.Chat/pull/24177) by [@ostjen](https://github.com/ostjen)) - - On `statistics` object the property `get` is an async function now. - - - We need to collect additional data of feature activation through the statistics collector. + - On `statistics` object the property `get` is an async function now. + - We need to collect additional data of feature activation through the statistics collector. - Some codes were splitted into another file just to organize. - Limit recent emojis to 27 ([#24210](https://github.com/RocketChat/Rocket.Chat/pull/24210)) - Limits the recent emoji list to a maximum of 3 rows instead of listing every emoji you've used so far. - + Limits the recent emoji list to a maximum of 3 rows instead of listing every emoji you've used so far. + ![image](https://user-images.githubusercontent.com/8591547/150033087-92721b76-9203-42fe-ac2e-5b9eca50edab.png) - Rewrite AddWebdavAccountModal to React Component ([#24070](https://github.com/RocketChat/Rocket.Chat/pull/24070)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/147777054-bf2f84e4-5226-4ebc-ab6e-287b83889b85.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/147777054-bf2f84e4-5226-4ebc-ab6e-287b83889b85.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/147769132-2b938ae8-aba3-4230-876d-572e46268b9a.png) - Rewrite Omnichannel Queue Page to React ([#24176](https://github.com/RocketChat/Rocket.Chat/pull/24176)) @@ -2947,32 +6983,28 @@ - Rewrite roomNotFound to React Component ([#24044](https://github.com/RocketChat/Rocket.Chat/pull/24044)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/147608307-468e6955-5db4-40c5-86a7-91448ac03427.png) - ![image](https://user-images.githubusercontent.com/27704687/147608377-d979adf5-615f-4180-8587-449369bf87f8.png) - - ### after - ![image](https://user-images.githubusercontent.com/27704687/149158027-e39bc0a0-4c33-465b-83e0-873e558a037b.png) + ### before + ![image](https://user-images.githubusercontent.com/27704687/147608307-468e6955-5db4-40c5-86a7-91448ac03427.png) + ![image](https://user-images.githubusercontent.com/27704687/147608377-d979adf5-615f-4180-8587-449369bf87f8.png) + + ### after + ![image](https://user-images.githubusercontent.com/27704687/149158027-e39bc0a0-4c33-465b-83e0-873e558a037b.png) ![image](https://user-images.githubusercontent.com/27704687/149157692-3e73c0b4-1759-430c-b1c4-b521e47d774d.png) - Setup Wizard Registration Flow ([#23676](https://github.com/RocketChat/Rocket.Chat/pull/23676)) - This pull request brings a few improvements in our setup wizard flow, the very first contact with a Rocket.Chat. Some of them: - - - A brand new visual design; - - - Form validation improves; - - - Allow users to navigate back to all steps; - - - Optimized steps to register your workspace or keep standalone. And many more! - - + This pull request brings a few improvements in our setup wizard flow, the very first contact with a Rocket.Chat. Some of them: + - A brand new visual design; + - Form validation improves; + - Allow users to navigate back to all steps; + - Optimized steps to register your workspace or keep standalone. And many more! + + ![Kapture 2022-01-20 at 11 19 47](https://user-images.githubusercontent.com/27704687/150356868-425666b4-511f-4690-9ce5-e61b839b1d19.gif) - Show Channel Icons on Room Header & Info panels ([#24239](https://github.com/RocketChat/Rocket.Chat/pull/24239)) - Updates Omnichannel Header & room Info component to render the source info + Updates Omnichannel Header & room Info component to render the source info Built on top of https://github.com/RocketChat/Rocket.Chat/pull/24237 - Throw 404 error in invalid endpoints ([#24053](https://github.com/RocketChat/Rocket.Chat/pull/24053)) @@ -3002,15 +7034,13 @@ - Apps Contextual Bar not carrying title and room information ([#24241](https://github.com/RocketChat/Rocket.Chat/pull/24241)) - Fixes: - - - - the app's name being rendered instead of the view's title, - - - the room's information (`IRoom`) wasn't being sent to the app when a `block action` happened - - Fixed behavior with correct view title and room information included in the block action event: - + Fixes: + + - the app's name being rendered instead of the view's title, + - the room's information (`IRoom`) wasn't being sent to the app when a `block action` happened + + Fixed behavior with correct view title and room information included in the block action event: + https://user-images.githubusercontent.com/733282/150420847-59bfcf8a-24a9-4dc5-8609-0d92dba38b70.mp4 - Avoid updating all rooms with visitor abandonment queries ([#24252](https://github.com/RocketChat/Rocket.Chat/pull/24252)) @@ -3023,26 +7053,24 @@ - Custom Emoji Image preview ([#24117](https://github.com/RocketChat/Rocket.Chat/pull/24117) by [@sidmohanty11](https://github.com/sidmohanty11)) - Before, - - ![custom-img-preview-rc3](https://user-images.githubusercontent.com/73601258/148431936-c82d4200-69b1-484b-8be2-d72f5c28202b.png) - - After, - - ![custom-img-preview-rc1](https://user-images.githubusercontent.com/73601258/148431955-8842a2e3-b9f3-4d68-b0d8-c5444419f767.png) - - also if any error, (for example - if we upload a video mp4 file) - + Before, + + ![custom-img-preview-rc3](https://user-images.githubusercontent.com/73601258/148431936-c82d4200-69b1-484b-8be2-d72f5c28202b.png) + + After, + + ![custom-img-preview-rc1](https://user-images.githubusercontent.com/73601258/148431955-8842a2e3-b9f3-4d68-b0d8-c5444419f767.png) + + also if any error, (for example - if we upload a video mp4 file) + ![custom-img-preview-rc2](https://user-images.githubusercontent.com/73601258/148431998-64bc1fbb-9958-495c-89c1-61df06adec75.png) - Discussions not loading message history if not joined ([#24316](https://github.com/RocketChat/Rocket.Chat/pull/24316)) - Ensure Firefox 91 ESR support ([#24096](https://github.com/RocketChat/Rocket.Chat/pull/24096)) - It: - - - Adds `Firefox ESR` to `browserslist`; - + It: + - Adds `Firefox ESR` to `browserslist`; - Upgrades `@rocket.chat/fuselage-hooks` to overcome a bug related to Firefox implementation of `ResizeObserver` API. - Enter not working on modal's multi-line input ([#23981](https://github.com/RocketChat/Rocket.Chat/pull/23981)) @@ -3053,12 +7081,12 @@ - Filter ability for admin room checkboxes ([#23970](https://github.com/RocketChat/Rocket.Chat/pull/23970) by [@sidmohanty11](https://github.com/sidmohanty11)) - Now, - - https://user-images.githubusercontent.com/73601258/146380812-d3aa5561-64e1-4515-a639-3b6d87432ae4.mp4 - - Before, - + Now, + + https://user-images.githubusercontent.com/73601258/146380812-d3aa5561-64e1-4515-a639-3b6d87432ae4.mp4 + + Before, + https://user-images.githubusercontent.com/73601258/146385538-85a70fce-9974-40e0-8757-eda1a5d411b7.mp4 - Fixed broken links in setup wizard ([#24248](https://github.com/RocketChat/Rocket.Chat/pull/24248) by [@Himanshu664](https://github.com/Himanshu664)) @@ -3085,10 +7113,10 @@ We should not keep `password` as required field when we check set random password field. In this password should not be required -- Solved Report Message Blank ([#24262](https://github.com/RocketChat/Rocket.Chat/pull/24262) by [@nishant23122000](https://github.com/nishant23122000)) - - After resolving issue #24261 : +- Solved Report Message Blank ([#24262](https://github.com/RocketChat/Rocket.Chat/pull/24262)) + After resolving issue #24261 : + https://user-images.githubusercontent.com/53515714/150629459-5f0a9cf6-9b0e-417f-8fc1-44c810bd5428.mp4 - Wrong german translation for 2FA-Promt ([#24126](https://github.com/RocketChat/Rocket.Chat/pull/24126) by [@mbreslein-thd](https://github.com/mbreslein-thd)) @@ -3199,8 +7227,8 @@ - Regression: Remove extra call to `useOutsideClick` hook not following the function signature ([#24243](https://github.com/RocketChat/Rocket.Chat/pull/24243)) - It migrates `client/sidebar/header/actions/Search` component to TypeScript and mitigates a invalid call to `Array.prototype.every`: - + It migrates `client/sidebar/header/actions/Search` component to TypeScript and mitigates a invalid call to `Array.prototype.every`: + ![image](https://user-images.githubusercontent.com/2263066/150441397-3ff403b2-10c1-4a29-b37f-892d7d4a9252.png) - Regression: Standalone register path failing when saving data ([#24324](https://github.com/RocketChat/Rocket.Chat/pull/24324)) @@ -3223,7 +7251,6 @@ - [@eduardofcabrera](https://github.com/eduardofcabrera) - [@grahhnt](https://github.com/grahhnt) - [@mbreslein-thd](https://github.com/mbreslein-thd) -- [@nishant23122000](https://github.com/nishant23122000) - [@ostjen](https://github.com/ostjen) - [@sidmohanty11](https://github.com/sidmohanty11) @@ -3243,6 +7270,7 @@ - [@juliajforesti](https://github.com/juliajforesti) - [@matheusbsilva137](https://github.com/matheusbsilva137) - [@murtaza98](https://github.com/murtaza98) +- [@nishant23122000](https://github.com/nishant23122000) - [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc) - [@renatobecker](https://github.com/renatobecker) - [@rique223](https://github.com/rique223) @@ -3355,10 +7383,8 @@ - Ensure Firefox 91 ESR support ([#24096](https://github.com/RocketChat/Rocket.Chat/pull/24096)) - It: - - - Adds `Firefox ESR` to `browserslist`; - + It: + - Adds `Firefox ESR` to `browserslist`; - Upgrades `@rocket.chat/fuselage-hooks` to overcome a bug related to Firefox implementation of `ResizeObserver` API. - Enter not working on modal's multi-line input ([#23981](https://github.com/RocketChat/Rocket.Chat/pull/23981)) @@ -3404,8 +7430,8 @@ - **APPS:** Allow apps to open contextual bar ([#23843](https://github.com/RocketChat/Rocket.Chat/pull/23843)) - Opens a contextual bar using app ui interactions (`CONTEXTUAL_BAR_OPEN`) - + Opens a contextual bar using app ui interactions (`CONTEXTUAL_BAR_OPEN`) + https://user-images.githubusercontent.com/733282/146704076-d2d115f2-6ca6-4ed0-b450-81be580889a4.mp4 - **APPS:** Allow Rocket.Chat Apps to register custom action buttons ([#23679](https://github.com/RocketChat/Rocket.Chat/pull/23679)) @@ -3416,7 +7442,7 @@ - **APPS:** Possibility to set room closer via Apps LivechatBridge.closeRoom ([#21025](https://github.com/RocketChat/Rocket.Chat/pull/21025)) - Add an optional param named `closer` into `LivechatBridge.closeRoom` so that it will be possible to close the room and send a close room message with the correct room closer. + Add an optional param named `closer` into `LivechatBridge.closeRoom` so that it will be possible to close the room and send a close room message with the correct room closer. If the param is not passed, use the room visitor as the room closer. - **EE:** Introduce fallback department support ([#23939](https://github.com/RocketChat/Rocket.Chat/pull/23939)) @@ -3436,8 +7462,7 @@ - Update "Message Erasure Type" setting's description ([#23879](https://github.com/RocketChat/Rocket.Chat/pull/23879)) - - Improves the "Message Erasure Type" setting's description by providing more details regarding the expected behavior of each option ("Keep Messages and User Name", "Delete All Messages" and "Remove link between user and messages"); - + - Improves the "Message Erasure Type" setting's description by providing more details regarding the expected behavior of each option ("Keep Messages and User Name", "Delete All Messages" and "Remove link between user and messages"); - Remove outdated translations (for this setting's description). - Webdav methods sanitization ([#23924](https://github.com/RocketChat/Rocket.Chat/pull/23924)) @@ -3479,23 +7504,23 @@ - Headers already sent error when user data download is disabled ([#23805](https://github.com/RocketChat/Rocket.Chat/pull/23805)) - When using the export message tool when trying to download the file using the link sent via email if the feature "Export User Data" is disabled an error was being thrown causing the request to halt. - - This is the error shown in the logs: - ``` - === UnHandledPromiseRejection === - Error [ERR_HTTP_HEADERS_SENT] [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client - at ServerResponse.setHeader (_http_outgoing.js:530:11) - at ServerResponse.res.setHeader (/app/bundle/programs/server/npm/node_modules/meteor/simple_json-routes/node_modules/connect/lib/patch.js:134:22) - at app/user-data-download/server/exportDownload.js:14:7 - at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40 { - code: 'ERR_HTTP_HEADERS_SENT' - } - --------------------------------- - Errors like this can cause oplog processing errors. - Setting EXIT_UNHANDLEDPROMISEREJECTION will cause the process to exit allowing your service to automatically restart the process - Future node.js versions will automatically exit the process - ================================= + When using the export message tool when trying to download the file using the link sent via email if the feature "Export User Data" is disabled an error was being thrown causing the request to halt. + + This is the error shown in the logs: + ``` + === UnHandledPromiseRejection === + Error [ERR_HTTP_HEADERS_SENT] [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client + at ServerResponse.setHeader (_http_outgoing.js:530:11) + at ServerResponse.res.setHeader (/app/bundle/programs/server/npm/node_modules/meteor/simple_json-routes/node_modules/connect/lib/patch.js:134:22) + at app/user-data-download/server/exportDownload.js:14:7 + at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40 { + code: 'ERR_HTTP_HEADERS_SENT' + } + --------------------------------- + Errors like this can cause oplog processing errors. + Setting EXIT_UNHANDLEDPROMISEREJECTION will cause the process to exit allowing your service to automatically restart the process + Future node.js versions will automatically exit the process + ================================= ``` - Jitsi call already ended ([#23904](https://github.com/RocketChat/Rocket.Chat/pull/23904) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) @@ -3506,23 +7531,23 @@ - Missing custom user status ellipsis ([#23831](https://github.com/RocketChat/Rocket.Chat/pull/23831)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/144270229-baca14f5-e168-42b7-86d1-e7217be561a9.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/144270229-baca14f5-e168-42b7-86d1-e7217be561a9.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/144274255-39216e69-8283-45c5-8a77-b835d284f655.png) - Missing edit icon in sequential thread messages ([#23948](https://github.com/RocketChat/Rocket.Chat/pull/23948)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/146083450-ca6d7197-dc55-4058-8212-943b42c82473.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/146083450-ca6d7197-dc55-4058-8212-943b42c82473.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/146083055-36c9731a-33c6-483a-93a5-1355d8689e3a.png) - Modal keeps state if reset too fast. ([#23791](https://github.com/RocketChat/Rocket.Chat/pull/23791)) - ~Queued updates so the Modal has a chance to close.~ + ~Queued updates so the Modal has a chance to close.~ Used a random key to ensure modal doesn't keep it's state. - OTR not working ([#23973](https://github.com/RocketChat/Rocket.Chat/pull/23973)) @@ -3537,8 +7562,8 @@ - Segmentation fault on CentOS 7 due to outdated `sharp` ([#23796](https://github.com/RocketChat/Rocket.Chat/pull/23796)) - Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`. - + Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`. + Suggested as a fix for versions `4.0.x` and `4.1.x`. - teams.leave client usage ([#23959](https://github.com/RocketChat/Rocket.Chat/pull/23959)) @@ -3551,10 +7576,10 @@ - Wrong button for non trial apps ([#23861](https://github.com/RocketChat/Rocket.Chat/pull/23861)) - This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. - Before: - ![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) - After: + This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. + Before: + ![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) + After: ![image](https://user-images.githubusercontent.com/43561537/144687924-1a3eb3a7-783f-4450-abd2-1efa0de64658.png)
@@ -3579,48 +7604,46 @@ - Chore: Centralize email validation functionality ([#23816](https://github.com/RocketChat/Rocket.Chat/pull/23816)) - - Create lib for validating emails - + - Create lib for validating emails - Modify places that validate emails to use the new central function - Chore: Change Menu props to accept next fuselage version ([#23839](https://github.com/RocketChat/Rocket.Chat/pull/23839)) - Chore: Create script to add new migrations ([#23822](https://github.com/RocketChat/Rocket.Chat/pull/23822)) - - Create NPM script to add new migrations - + - Create NPM script to add new migrations - TODO: Infer next migration number from file list - Chore: Deleted LivechatPageVisited ([#23993](https://github.com/RocketChat/Rocket.Chat/pull/23993) by [@ostjen](https://github.com/ostjen)) - Chore: Enable prefer-optional-chain ESLint rule for TypeScript files ([#23786](https://github.com/RocketChat/Rocket.Chat/pull/23786)) - > Code is bad. It rots. It requires periodic maintenance. It has bugs that need to be found. New features mean old code has to be adapted. - > The more code you have, the more places there are for bugs to hide. The longer checkouts or compiles take. The longer it takes a new employee to make sense of your system. If you have to refactor there's more stuff to move around. - > Furthermore, more code often means less flexibility and functionality. This is counter-intuitive, but a lot of times a simple, elegant solution is faster and more general than the plodding mess of code produced by a programmer of lesser talent. - > Code is produced by engineers. To make more code requires more engineers. Engineers have n^2 communication costs, and all that code they add to the system, while expanding its capability, also increases a whole basket of costs. - > You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs. - - — [Rich Skrenta][1] - - Mixing two problem domains in code is prone to errors. In this small example - - ```ts - declare const y: { z: unknown } | undefined; - - const x = y && y.z; - ``` - - we're (1) checking the nullity of `y` and (2) attributing `y.z` to `x`, where (2) is _clearly_ the main problem we're solving with code. The optional chaining is a good technique to handle nullity as a mere implementation detail: - - ```ts - declare const y: { z: unknown } | undefined; - - const x = y?.z; - ``` - - Attributing `y.z` to `x` is more easily readable than the nullity check of `y`. - + > Code is bad. It rots. It requires periodic maintenance. It has bugs that need to be found. New features mean old code has to be adapted. + > The more code you have, the more places there are for bugs to hide. The longer checkouts or compiles take. The longer it takes a new employee to make sense of your system. If you have to refactor there's more stuff to move around. + > Furthermore, more code often means less flexibility and functionality. This is counter-intuitive, but a lot of times a simple, elegant solution is faster and more general than the plodding mess of code produced by a programmer of lesser talent. + > Code is produced by engineers. To make more code requires more engineers. Engineers have n^2 communication costs, and all that code they add to the system, while expanding its capability, also increases a whole basket of costs. + > You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs. + + — [Rich Skrenta][1] + + Mixing two problem domains in code is prone to errors. In this small example + + ```ts + declare const y: { z: unknown } | undefined; + + const x = y && y.z; + ``` + + we're (1) checking the nullity of `y` and (2) attributing `y.z` to `x`, where (2) is _clearly_ the main problem we're solving with code. The optional chaining is a good technique to handle nullity as a mere implementation detail: + + ```ts + declare const y: { z: unknown } | undefined; + + const x = y?.z; + ``` + + Attributing `y.z` to `x` is more easily readable than the nullity check of `y`. + This PR aims to add `@typescript-eslint/prefer-optional-chain` rule to ESlint configuration at warning level. - Chore: Fix hasRole warning ([#23914](https://github.com/RocketChat/Rocket.Chat/pull/23914)) @@ -3663,8 +7686,8 @@ - Regression: Ensure room action buttons only appear inside menu ([#24035](https://github.com/RocketChat/Rocket.Chat/pull/24035)) - Currently, action buttons registered by apps to appear in the ROOM_ACTION context show in the first position of the list, but since they don't have an icon they are effectively invisible in the tab bar. - + Currently, action buttons registered by apps to appear in the ROOM_ACTION context show in the first position of the list, but since they don't have an icon they are effectively invisible in the tab bar. + Here we change the order configuration of the button so we make sure it only shows inside the room menu - Regression: Fix omnichannel empty source usage ([#24008](https://github.com/RocketChat/Rocket.Chat/pull/24008)) @@ -3767,18 +7790,18 @@ - Segmentation fault on CentOS 7 due to outdated `sharp` ([#23796](https://github.com/RocketChat/Rocket.Chat/pull/23796)) - Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`. - + Upgrades `sharp` to avoid a segmentation fault on CentOS 7 during startup related to `sharp.node` being loaded via `process.dlopen()`. + Suggested as a fix for versions `4.0.x` and `4.1.x`. - teams.removeMembers client usage ([#23857](https://github.com/RocketChat/Rocket.Chat/pull/23857)) - Wrong button for non trial apps ([#23861](https://github.com/RocketChat/Rocket.Chat/pull/23861)) - This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. - Before: - ![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) - After: + This PR solves a bug on the marketplace that was happening with WhatsApp where it was displaying a trial button even though it didn't have a free trial period. The new verification I've added checks if the app is subscription-based and then checks if it has 0 trial days in all of its tiers. If it does, it shows a subscribe button. If it doesn't, it displays a trial button. Also, I've exposed the itsEnterpriseOnly flag as an extra measure in the case of apps like Facebook Messenger that are enterprise-only and consequently should show the subscribe button. + Before: + ![image](https://user-images.githubusercontent.com/43561537/144687716-baef06ce-7a80-42fc-8393-b0283c0f349a.png) + After: ![image](https://user-images.githubusercontent.com/43561537/144687924-1a3eb3a7-783f-4450-abd2-1efa0de64658.png)
@@ -3849,28 +7872,26 @@ - Engagement Dashboard ([#23547](https://github.com/RocketChat/Rocket.Chat/pull/23547)) - - Adds helpers `onToggledFeature` for server and client code to handle license activation/deactivation without server restart; - - - Replaces usage of `useEndpointData` with `useQuery` (from [React Query](https://react-query.tanstack.com/)); - + - Adds helpers `onToggledFeature` for server and client code to handle license activation/deactivation without server restart; + - Replaces usage of `useEndpointData` with `useQuery` (from [React Query](https://react-query.tanstack.com/)); - Introduces `view-engagement-dashboard` permission. - Improve the add user drop down for add a user in create channel modal for UserAutoCompleteMultiple ([#23766](https://github.com/RocketChat/Rocket.Chat/pull/23766) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Seeing only the name of the person you are not adding is not practical in my opinion because two people can have the same name. Moreover, you can't see the username of the person you want to add in the dropdown. So I changed that and created another selection of users to show the username as well. I made this change so that it would appear in the key place for creating a room and adding a user. - - Before: - - https://user-images.githubusercontent.com/45966964/115287805-faac8d00-a150-11eb-871f-147ab011ced0.mp4 - - - After: - + Seeing only the name of the person you are not adding is not practical in my opinion because two people can have the same name. Moreover, you can't see the username of the person you want to add in the dropdown. So I changed that and created another selection of users to show the username as well. I made this change so that it would appear in the key place for creating a room and adding a user. + + Before: + + https://user-images.githubusercontent.com/45966964/115287805-faac8d00-a150-11eb-871f-147ab011ced0.mp4 + + + After: + https://user-images.githubusercontent.com/45966964/115287664-d2249300-a150-11eb-8cf6-0e04730b425d.mp4 - MKP12 - New UI - Merge Apps and Marketplace Tabs and Content ([#23542](https://github.com/RocketChat/Rocket.Chat/pull/23542)) - Merged the Marketplace and Apps page into a single page with a tabs component that changes between Markeplace and installed apps. + Merged the Marketplace and Apps page into a single page with a tabs component that changes between Markeplace and installed apps. ![page merging](https://user-images.githubusercontent.com/43561537/138516558-f86d62e6-1a5c-4817-a229-a1b876323960.gif) - Re-naming department query param for Twilio ([#23725](https://github.com/RocketChat/Rocket.Chat/pull/23725)) @@ -3883,16 +7904,11 @@ - Stricter API types ([#23735](https://github.com/RocketChat/Rocket.Chat/pull/23735)) - It: - - - Adds stricter types for `API`; - - - Enables types for `urlParams`; - - - Removes mandatory passage of `undefined` payload on client; - - - Corrects some regressions; - + It: + - Adds stricter types for `API`; + - Enables types for `urlParams`; + - Removes mandatory passage of `undefined` payload on client; + - Corrects some regressions; - Reassures my belief in TypeScript supremacy. ### 🐛 Bug fixes @@ -3902,14 +7918,12 @@ - **ENTERPRISE:** OAuth "Merge Roles" removes roles from users ([#23588](https://github.com/RocketChat/Rocket.Chat/pull/23588)) - - Fix OAuth "Merge Roles": the "Merge Roles" option now synchronize only the roles described in the "**Roles to Sync**" setting available in each Custom OAuth settings' group (instead of replacing users' roles by their OAuth roles); - + - Fix OAuth "Merge Roles": the "Merge Roles" option now synchronize only the roles described in the "**Roles to Sync**" setting available in each Custom OAuth settings' group (instead of replacing users' roles by their OAuth roles); - Fix "Merge Roles" and "Channel Mapping" not being performed/updated on OAuth login. - **ENTERPRISE:** Private rooms and discussions can't be audited ([#23673](https://github.com/RocketChat/Rocket.Chat/pull/23673)) - - Add Private rooms (groups) and Discussions to the Message Auditing (Channels) autocomplete; - + - Add Private rooms (groups) and Discussions to the Message Auditing (Channels) autocomplete; - Update "Channels" tab name to "Rooms". - **ENTERPRISE:** Replace all occurrences of a placeholder on string instead of just first one ([#23703](https://github.com/RocketChat/Rocket.Chat/pull/23703)) @@ -3922,10 +7936,10 @@ - Apps scheduler "losing" jobs after server restart ([#23566](https://github.com/RocketChat/Rocket.Chat/pull/23566)) - If a job is scheduled and the server restarted, said job won't be executed, giving the impression it's been lost. - - What happens is that the scheduler is only started when some app tries to schedule an app - if that happens, all jobs that are "late" will be executed; if that doesn't happen, no job will run. - + If a job is scheduled and the server restarted, said job won't be executed, giving the impression it's been lost. + + What happens is that the scheduler is only started when some app tries to schedule an app - if that happens, all jobs that are "late" will be executed; if that doesn't happen, no job will run. + This PR starts the apps scheduler right after all apps have been loaded - Autofocus on search input in admin ([#23738](https://github.com/RocketChat/Rocket.Chat/pull/23738)) @@ -3954,16 +7968,16 @@ - Notifications are not being filtered ([#23487](https://github.com/RocketChat/Rocket.Chat/pull/23487)) - - Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value; - - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`); + - Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value; + - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`); - Rename 'mobileNotifications' user's preference to 'pushNotifications'. - Omnichannel business hours page breaking navigation ([#23595](https://github.com/RocketChat/Rocket.Chat/pull/23595) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)) - Omnichannel contact center navigation ([#23691](https://github.com/RocketChat/Rocket.Chat/pull/23691)) - Derives from: https://github.com/RocketChat/Rocket.Chat/pull/23656 - + Derives from: https://github.com/RocketChat/Rocket.Chat/pull/23656 + This PR includes a different approach to solving navigation problems following the same code structure and UI definitions of other "ActionButtons" components in Sidebar. - Omnichannel status being changed on page refresh ([#23587](https://github.com/RocketChat/Rocket.Chat/pull/23587)) @@ -4000,40 +8014,32 @@ - Chore: Mocha testing configuration ([#23706](https://github.com/RocketChat/Rocket.Chat/pull/23706)) - We've been writing integration tests for the REST API quite regularly, but we can't say the same for UI-related modules. This PR is based on the assumption that _improving the developer experience on writing tests_ would increase our coverage and promote the adoption even for newcomers. - - Here as summary of the proposal: - - - - Change Mocha configuration files: - - Add a base configuration (`.mocharc.base.json`); - - Rename the configuration for REST API tests (`mocha_end_to_end.opts.js -> .mocharc.api.js`); - - Add a configuration for client modules (`.mocharc.client.js`); - - Enable ESLint for them. - - - Add a Mocha test command exclusive for client modules (`npm run testunit-client`); - - - Enable fast watch mode: - - Configure `ts-node` to only transpile code (skip type checking); - - Define a list of files to be watched. - - - Configure `mocha` environment on ESLint only for test files (required when using Mocha's globals); - - - Adopt Chai as our assertion library: - - Unify the setup of Chai plugins (`chai-spies`, `chai-datetime`, `chai-dom`); - - Replace `assert` with `chai`; - - Replace `chai.expect` with `expect`. - - - Enable integration tests with React components: - - Enable JSX support on our default Babel configuration; + We've been writing integration tests for the REST API quite regularly, but we can't say the same for UI-related modules. This PR is based on the assumption that _improving the developer experience on writing tests_ would increase our coverage and promote the adoption even for newcomers. + + Here as summary of the proposal: + + - Change Mocha configuration files: + - Add a base configuration (`.mocharc.base.json`); + - Rename the configuration for REST API tests (`mocha_end_to_end.opts.js -> .mocharc.api.js`); + - Add a configuration for client modules (`.mocharc.client.js`); + - Enable ESLint for them. + - Add a Mocha test command exclusive for client modules (`npm run testunit-client`); + - Enable fast watch mode: + - Configure `ts-node` to only transpile code (skip type checking); + - Define a list of files to be watched. + - Configure `mocha` environment on ESLint only for test files (required when using Mocha's globals); + - Adopt Chai as our assertion library: + - Unify the setup of Chai plugins (`chai-spies`, `chai-datetime`, `chai-dom`); + - Replace `assert` with `chai`; + - Replace `chai.expect` with `expect`. + - Enable integration tests with React components: + - Enable JSX support on our default Babel configuration; - Adopt [testing library](https://testing-library.com/). - Chore: Rearrange module typings ([#23452](https://github.com/RocketChat/Rocket.Chat/pull/23452)) - - Move all external module declarations (definitions and augmentations) to `/definition/externals`; - - - ~Symlink some modules on `/definition/externals` to `/ee/server/services/definition/externals`~ Share types with `/ee/server/services`; - + - Move all external module declarations (definitions and augmentations) to `/definition/externals`; + - ~Symlink some modules on `/definition/externals` to `/ee/server/services/definition/externals`~ Share types with `/ee/server/services`; - Use TypeScript as server code entrypoint. - Chore: Remove duplicated 'name' key from rate limiter logs ([#23771](https://github.com/RocketChat/Rocket.Chat/pull/23771)) @@ -4110,6 +8116,30 @@ - [@tassoevan](https://github.com/tassoevan) - [@tiagoevanp](https://github.com/tiagoevanp) +# 4.1.6 +`2022-06-02 · 1 🐛 · 1 🔍 · 2 👩‍💻👨‍💻` + +### Engine versions +- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` + +### 🐛 Bug fixes + + +- Omnichannel managers can't join chats in progress ([#24553](https://github.com/RocketChat/Rocket.Chat/pull/24553)) + +
+🔍 Minor changes + + +- Regression: Fix in-correct room status shown to agents ([#24592](https://github.com/RocketChat/Rocket.Chat/pull/24592)) + +
+ +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@murtaza98](https://github.com/murtaza98) +- [@renatobecker](https://github.com/renatobecker) + # 4.1.2 `2021-11-08 · 3 🐛 · 3 👩‍💻👨‍💻` @@ -4124,8 +8154,8 @@ - Notifications are not being filtered ([#23487](https://github.com/RocketChat/Rocket.Chat/pull/23487)) - - Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value; - - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`); + - Add a migration to update the `Accounts_Default_User_Preferences_pushNotifications` setting's value to the `Accounts_Default_User_Preferences_mobileNotifications` setting's value; + - Remove the `Accounts_Default_User_Preferences_mobileNotifications` setting (replaced by `Accounts_Default_User_Preferences_pushNotifications`); - Rename 'mobileNotifications' user's preference to 'pushNotifications'. - Omnichannel status being changed on page refresh ([#23587](https://github.com/RocketChat/Rocket.Chat/pull/23587)) @@ -4191,19 +8221,19 @@ - Make Livechat Instructions setting multi-line ([#23515](https://github.com/RocketChat/Rocket.Chat/pull/23515)) - Since now we're supporting markdown text on this field (via this PR - https://github.com/RocketChat/Rocket.Chat.Livechat/pull/648), it would be nice to make this setting multiline so users can have more space to edit the text + Since now we're supporting markdown text on this field (via this PR - https://github.com/RocketChat/Rocket.Chat.Livechat/pull/648), it would be nice to make this setting multiline so users can have more space to edit the text ![image](https://user-images.githubusercontent.com/34130764/138146712-13e4968b-5312-4d53-b44c-b5699c5e49c1.png) - optimized groups.listAll response time ([#22941](https://github.com/RocketChat/Rocket.Chat/pull/22941) by [@ostjen](https://github.com/ostjen)) - groups.listAll endpoint was having performance issues, specially when the total number of groups was high. This happened because the endpoint was loading all objects in memory then using splice to paginate, instead of paginating beforehand. - - Considering 70k groups, this was the performance improvement: - - before - ![image](https://user-images.githubusercontent.com/28611993/129601314-bdf89337-79fa-4446-9f44-95264af4adb3.png) - - after + groups.listAll endpoint was having performance issues, specially when the total number of groups was high. This happened because the endpoint was loading all objects in memory then using splice to paginate, instead of paginating beforehand. + + Considering 70k groups, this was the performance improvement: + + before + ![image](https://user-images.githubusercontent.com/28611993/129601314-bdf89337-79fa-4446-9f44-95264af4adb3.png) + + after ![image](https://user-images.githubusercontent.com/28611993/129601358-5872e166-f923-4c1c-b21d-eb9507365ecf.png) ### 🐛 Bug fixes @@ -4211,8 +8241,7 @@ - **APPS:** Communication problem when updating and uninstalling apps in cluster ([#23418](https://github.com/RocketChat/Rocket.Chat/pull/23418)) - - Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place. - + - Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place. - Also shows a warning message on uninstalls inside a cluster. As there are many servers writing to the same place, some race conditions may occur. This prevents problems related to terminating the process in the middle due to errors being thrown and leaving the server in a faulty state. - **ENTERPRISE:** Omnichannel agent is not leaving the room when a forwarded chat is queued ([#23404](https://github.com/RocketChat/Rocket.Chat/pull/23404)) @@ -4239,10 +8268,10 @@ - Markdown quote message style ([#23462](https://github.com/RocketChat/Rocket.Chat/pull/23462)) - Before: - ![image](https://user-images.githubusercontent.com/17487063/137496669-3abecab4-cf90-45cb-8b1b-d9411a5682dd.png) - - After: + Before: + ![image](https://user-images.githubusercontent.com/17487063/137496669-3abecab4-cf90-45cb-8b1b-d9411a5682dd.png) + + After: ![image](https://user-images.githubusercontent.com/17487063/137496905-fd727f90-f707-4ec6-8139-ba2eb1a2146e.png) - MONGO_OPTIONS being ignored for oplog connection ([#23314](https://github.com/RocketChat/Rocket.Chat/pull/23314) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) @@ -4261,8 +8290,8 @@ - Read only description in team creation ([#23213](https://github.com/RocketChat/Rocket.Chat/pull/23213)) - ![image](https://user-images.githubusercontent.com/27704687/133608433-8ca788a3-71a8-4d40-8c40-8156ab03c606.png) - + ![image](https://user-images.githubusercontent.com/27704687/133608433-8ca788a3-71a8-4d40-8c40-8156ab03c606.png) + ![image](https://user-images.githubusercontent.com/27704687/133608400-4cdc7a67-95e5-46c6-8c65-29ab107cd314.png) - resumeToken not working ([#23379](https://github.com/RocketChat/Rocket.Chat/pull/23379)) @@ -4271,8 +8300,7 @@ - SAML Users' roles being reset to default on login ([#23411](https://github.com/RocketChat/Rocket.Chat/pull/23411)) - - Remove `roles` field update on `insertOrUpdateSAMLUser` function; - + - Remove `roles` field update on `insertOrUpdateSAMLUser` function; - Add SAML `syncRoles` event; - Server crashing when Routing method is not available at start ([#23473](https://github.com/RocketChat/Rocket.Chat/pull/23473)) @@ -4353,14 +8381,14 @@ - Chore: Startup Time ([#23210](https://github.com/RocketChat/Rocket.Chat/pull/23210)) - The settings logic has been improved as a whole. - - All the logic to get the data from the env var was confusing. - - Setting default values was tricky to understand. - - Every time the server booted, all settings were updated and callbacks were called 2x or more (horrible for environments with multiple instances and generating a turbulent startup). - + The settings logic has been improved as a whole. + + All the logic to get the data from the env var was confusing. + + Setting default values was tricky to understand. + + Every time the server booted, all settings were updated and callbacks were called 2x or more (horrible for environments with multiple instances and generating a turbulent startup). + `Settings.get(......, callback);` was deprecated. We now have better methods for each case. - Chore: Update Apps-Engine version ([#23375](https://github.com/RocketChat/Rocket.Chat/pull/23375)) @@ -4383,10 +8411,10 @@ - Regression: Mail body contains `undefined` text ([#23552](https://github.com/RocketChat/Rocket.Chat/pull/23552)) - ### Before - ![image](https://user-images.githubusercontent.com/2263066/138733018-10449892-5c2d-46fb-9355-00e98e0d6c9f.png) - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/2263066/138733018-10449892-5c2d-46fb-9355-00e98e0d6c9f.png) + + ### After ![image](https://user-images.githubusercontent.com/2263066/138733074-a1b88a77-bf64-41c3-a6c3-ac9e1cb63de1.png) - Regression: Prevent settings from getting updated ([#23556](https://github.com/RocketChat/Rocket.Chat/pull/23556)) @@ -4477,8 +8505,7 @@ - SAML Users' roles being reset to default on login ([#23411](https://github.com/RocketChat/Rocket.Chat/pull/23411)) - - Remove `roles` field update on `insertOrUpdateSAMLUser` function; - + - Remove `roles` field update on `insertOrUpdateSAMLUser` function; - Add SAML `syncRoles` event;
@@ -4510,8 +8537,7 @@ - **APPS:** Communication problem when updating and uninstalling apps in cluster ([#23418](https://github.com/RocketChat/Rocket.Chat/pull/23418)) - - Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place. - + - Make the hook responsible for receiving app update events inside a cluster fetch the app's package (zip file) in the correct place. - Also shows a warning message on uninstalls inside a cluster. As there are many servers writing to the same place, some race conditions may occur. This prevents problems related to terminating the process in the middle due to errors being thrown and leaving the server in a faulty state. - Server crashing when Routing method is not available at start ([#23473](https://github.com/RocketChat/Rocket.Chat/pull/23473)) @@ -4638,20 +8664,18 @@ - **ENTERPRISE:** "Download CSV" button doesn't work in the Engagement Dashboard's Active Users section ([#23013](https://github.com/RocketChat/Rocket.Chat/pull/23013)) - - Fix "Download CSV" button in the Engagement Dashboard's Active Users section; - - - Add column headers to the CSV file downloaded from the Engagement Dashboard's Active Users section; - + - Fix "Download CSV" button in the Engagement Dashboard's Active Users section; + - Add column headers to the CSV file downloaded from the Engagement Dashboard's Active Users section; - Split the data in multiple CSV files. - **ENTERPRISE:** CSV file downloaded in the Engagement Dashboard's New Users section contains undefined data ([#23014](https://github.com/RocketChat/Rocket.Chat/pull/23014)) - - Fix CSV file downloaded in the Engagement Dashboard's New Users section; + - Fix CSV file downloaded in the Engagement Dashboard's New Users section; - Add column headers to the CSV file downloaded from the Engagement Dashboard's New Users section. - **ENTERPRISE:** Missing headers in CSV files downloaded from the Engagement Dashboard ([#23223](https://github.com/RocketChat/Rocket.Chat/pull/23223)) - - Add headers to all CSV files downloaded from the "Messages" and "Channels" tabs from the Engagement Dashboard; + - Add headers to all CSV files downloaded from the "Messages" and "Channels" tabs from the Engagement Dashboard; - Add headers to the CSV file downloaded from the "Users by time of day" section (in the "Users" tab). - LDAP Refactoring ([#23171](https://github.com/RocketChat/Rocket.Chat/pull/23171)) @@ -4666,24 +8690,17 @@ - Remove deprecated endpoints ([#23162](https://github.com/RocketChat/Rocket.Chat/pull/23162)) - The following REST endpoints were removed: - - - - `/api/v1/emoji-custom` - - - `/api/v1/info` - - - `/api/v1/permissions` - - - `/api/v1/permissions.list` - - The following Real time API Methods were removed: - - - - `getFullUserData` - - - `getServerInfo` - + The following REST endpoints were removed: + + - `/api/v1/emoji-custom` + - `/api/v1/info` + - `/api/v1/permissions` + - `/api/v1/permissions.list` + + The following Real time API Methods were removed: + + - `getFullUserData` + - `getServerInfo` - `livechat:saveOfficeHours` - Remove Google Vision features ([#23160](https://github.com/RocketChat/Rocket.Chat/pull/23160)) @@ -4692,8 +8709,8 @@ - Remove old migrations up to version 2.4.14 ([#23277](https://github.com/RocketChat/Rocket.Chat/pull/23277)) - To update to version 4.0.0 you'll need to be running at least version 3.0.0, otherwise you might loose some database migrations which might have unexpected effects. - + To update to version 4.0.0 you'll need to be running at least version 3.0.0, otherwise you might loose some database migrations which might have unexpected effects. + This aims to clean up the code, since upgrades jumping 2 major versions are too risky and hard to maintain, we'll keep only migration from that last major (in this case 3.x). - Remove patch info from endpoint /api/info for non-logged in users ([#16050](https://github.com/RocketChat/Rocket.Chat/pull/16050)) @@ -4702,18 +8719,18 @@ - Stop sending audio notifications via stream ([#23108](https://github.com/RocketChat/Rocket.Chat/pull/23108)) - Remove audio preferences and make them tied to desktop notification preferences. - + Remove audio preferences and make them tied to desktop notification preferences. + TL;DR: new message sounds will play only if you receive a desktop notification. you'll still be able to chose to not play any sound though - Webhook will fail if user is not part of the channel ([#23310](https://github.com/RocketChat/Rocket.Chat/pull/23310)) - Remove deprecated behavior added by https://github.com/RocketChat/Rocket.Chat/pull/18024 that accepts webhook integrations sending messages even if the user is not part of the channel. - - Starting from 4.0.0 the webhook request will fail with `error-not-allowed` error: - - ``` - {"success":false,"error":"error-not-allowed"} + Remove deprecated behavior added by https://github.com/RocketChat/Rocket.Chat/pull/18024 that accepts webhook integrations sending messages even if the user is not part of the channel. + + Starting from 4.0.0 the webhook request will fail with `error-not-allowed` error: + + ``` + {"success":false,"error":"error-not-allowed"} ``` ### 🎉 New features @@ -4731,26 +8748,23 @@ - Seats Cap ([#23017](https://github.com/RocketChat/Rocket.Chat/pull/23017) by [@g-thome](https://github.com/g-thome)) - - Adding New Members - - Awareness of seats usage while adding new members - - Seats Cap about to be reached - - Seats Cap reached - - Request more seats - - - Warning Admins - - System telling admins max seats are about to exceed - - System telling admins max seats were exceed - - Metric on Info Page - - Request more seats - - - Warning Members - - Invite link - - Block creating new invite links - - Block existing invite links (feedback on register process) - - Register to Workspaces - - - Emails - - System telling admins max seats are about to exceed + - Adding New Members + - Awareness of seats usage while adding new members + - Seats Cap about to be reached + - Seats Cap reached + - Request more seats + - Warning Admins + - System telling admins max seats are about to exceed + - System telling admins max seats were exceed + - Metric on Info Page + - Request more seats + - Warning Members + - Invite link + - Block creating new invite links + - Block existing invite links (feedback on register process) + - Register to Workspaces + - Emails + - System telling admins max seats are about to exceed - System telling admins max seats were exceed ### 🚀 Improvements @@ -4758,10 +8772,10 @@ - **APPS:** New storage strategy for Apps-Engine file packages ([#22657](https://github.com/RocketChat/Rocket.Chat/pull/22657)) - This is an enabler for our initiative to support NPM packages in the Apps-Engine. - - Currently, the packages (zip files) for Rocket.Chat Apps are stored as a base64 encoded string in a document in the database, which constrains us due to the size limit of a document in MongoDB (16Mb). - + This is an enabler for our initiative to support NPM packages in the Apps-Engine. + + Currently, the packages (zip files) for Rocket.Chat Apps are stored as a base64 encoded string in a document in the database, which constrains us due to the size limit of a document in MongoDB (16Mb). + When we allow apps to include NPM packages, the size of the App package itself will be potentially _very large_ (I'm looking at you `node_modules`). Thus we'll be changing the strategy to store apps either with GridFS or the host's File System itself. - **APPS:** Return task ids when using the scheduler api ([#23023](https://github.com/RocketChat/Rocket.Chat/pull/23023)) @@ -4801,9 +8815,9 @@ - "Read Only" and "Allow Reacting" system messages are missing in rooms ([#23037](https://github.com/RocketChat/Rocket.Chat/pull/23037) by [@ostjen](https://github.com/ostjen)) - - Add system message to notify changes on the **"Read Only"** setting; - - Add system message to notify changes on the **"Allow Reacting"** setting; - - Fix "Allow Reacting" setting's description (updated from "Only authorized users can write new messages" to "Only authorized users can react to messages"). + - Add system message to notify changes on the **"Read Only"** setting; + - Add system message to notify changes on the **"Allow Reacting"** setting; + - Fix "Allow Reacting" setting's description (updated from "Only authorized users can write new messages" to "Only authorized users can react to messages"). ![system-messages](https://user-images.githubusercontent.com/36537004/130883527-9eb47fcd-c8e5-41fb-af34-5d99bd0a6780.PNG) - Add check before placing chat on-hold to confirm that contact sent last message ([#23053](https://github.com/RocketChat/Rocket.Chat/pull/23053)) @@ -4818,9 +8832,9 @@ - Inaccurate use of 'Mobile notifications' instead of 'Push notifications' in i18n strings ([#22978](https://github.com/RocketChat/Rocket.Chat/pull/22978) by [@ostjen](https://github.com/ostjen)) - - Fix inaccurate use of 'Mobile notifications' (which is misleading in German) by 'Push notifications'; - - Update `'Notification_Mobile_Default_For'` key to `'Notification_Push_Default_For'` (and text to 'Send Push Notifications For' for English Language); - - Update `'Accounts_Default_User_Preferences_mobileNotifications'` key to `'Accounts_Default_User_Preferences_pushNotifications'`; + - Fix inaccurate use of 'Mobile notifications' (which is misleading in German) by 'Push notifications'; + - Update `'Notification_Mobile_Default_For'` key to `'Notification_Push_Default_For'` (and text to 'Send Push Notifications For' for English Language); + - Update `'Accounts_Default_User_Preferences_mobileNotifications'` key to `'Accounts_Default_User_Preferences_pushNotifications'`; - Update `'Mobile_Notifications_Default_Alert'` key to `'Mobile_Push_Notifications_Default_Alert'`; - Logging out from other clients ([#23276](https://github.com/RocketChat/Rocket.Chat/pull/23276)) @@ -4829,7 +8843,7 @@ - Modals is cutting pixels of the content ([#23243](https://github.com/RocketChat/Rocket.Chat/pull/23243)) - Fuselage Dependency: [543](https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/543) + Fuselage Dependency: [543](https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/543) ![image](https://user-images.githubusercontent.com/27704687/134049227-3cd1deed-34ba-454f-a95e-e99b79a7a7b9.png) - Omnichannel On hold chats being forwarded to offline agents ([#23185](https://github.com/RocketChat/Rocket.Chat/pull/23185)) @@ -4838,15 +8852,15 @@ - Prevent users to edit an existing role when adding a new one with the same name used before. ([#22407](https://github.com/RocketChat/Rocket.Chat/pull/22407) by [@lucassartor](https://github.com/lucassartor)) - ### before - ![Peek 2021-07-13 16-31](https://user-images.githubusercontent.com/27704687/125513721-953d84f4-1c95-45ca-80e1-b00992b874f6.gif) - - ### after + ### before + ![Peek 2021-07-13 16-31](https://user-images.githubusercontent.com/27704687/125513721-953d84f4-1c95-45ca-80e1-b00992b874f6.gif) + + ### after ![Peek 2021-07-13 16-34](https://user-images.githubusercontent.com/27704687/125514098-91ee8014-51e5-4c62-9027-5538acf57d08.gif) - Remove doubled "Canned Responses" strings ([#23056](https://github.com/RocketChat/Rocket.Chat/pull/23056)) - - Remove doubled canned response setting introduced in #22703 (by setting id change); + - Remove doubled canned response setting introduced in #22703 (by setting id change); - Update "Canned Responses" keys to "Canned_Responses". - Remove margin from quote inside quote ([#21779](https://github.com/RocketChat/Rocket.Chat/pull/21779)) @@ -4857,21 +8871,16 @@ - Sidebar not closing when clicking in Home or Directory on mobile view ([#23218](https://github.com/RocketChat/Rocket.Chat/pull/23218)) - ### Additional fixed - - - Merge Burger menu components into a single component - - - Show a badge with no-read messages in the Burger Button: - ![image](https://user-images.githubusercontent.com/27704687/133679378-20fea2c0-4ac1-4b4e-886e-45154cc6afea.png) - + ### Additional fixed + - Merge Burger menu components into a single component + - Show a badge with no-read messages in the Burger Button: + ![image](https://user-images.githubusercontent.com/27704687/133679378-20fea2c0-4ac1-4b4e-886e-45154cc6afea.png) - remove useSidebarClose hook - Stop queue when Omnichannel is disabled or the routing method does not support it ([#23261](https://github.com/RocketChat/Rocket.Chat/pull/23261)) - - Add missing key logs - - - Stop queue (and logs) when livechat is disabled or when routing method does not support queue - + - Add missing key logs + - Stop queue (and logs) when livechat is disabled or when routing method does not support queue - Stop ignoring offline bot agents from delegation (previously, if a bot was offline, even with "Assign new conversations to bot agent" enabled, bot will be ignored and chat will be left in limbo (since bot was assigned, but offline). - Toolbox click not working on Safari(iOS) ([#23244](https://github.com/RocketChat/Rocket.Chat/pull/23244)) @@ -4978,17 +8987,17 @@ - Regression: Blank screen in Jitsi video calls ([#23322](https://github.com/RocketChat/Rocket.Chat/pull/23322)) - - Fix Jitsi calls being disposed even when "Open in new window" setting is disabled; + - Fix Jitsi calls being disposed even when "Open in new window" setting is disabled; - Fix misspelling on `CallJitsWithData.js` file name. - Regression: Create new loggers based on server log level ([#23297](https://github.com/RocketChat/Rocket.Chat/pull/23297)) - Regression: Fix app storage migration ([#23286](https://github.com/RocketChat/Rocket.Chat/pull/23286)) - The previous version of this migration didn't take into consideration apps that were installed prior to [Rocket.Chat@3.8.0](https://github.com/RocketChat/Rocket.Chat/releases/tag/3.8.0), which [removed the typescript compiler from the server](https://github.com/RocketChat/Rocket.Chat/pull/18687) and into the CLI. As a result, the zip files inside each installed app's document in the database had typescript files in them instead of the now required javascript files. - - As the new strategy of source code storage for apps changes the way the app is loaded, those zip files containing the source code are read everytime the app is started (or [in this particular case, updated](https://github.com/RocketChat/Rocket.Chat/pull/23286/files#diff-caf9f7a22478639e58d6514be039140a42ce1ab2d999c3efe5678c38ee36d0ccR43)), and as the zips' contents were wrong, the operation was failing. - + The previous version of this migration didn't take into consideration apps that were installed prior to [Rocket.Chat@3.8.0](https://github.com/RocketChat/Rocket.Chat/releases/tag/3.8.0), which [removed the typescript compiler from the server](https://github.com/RocketChat/Rocket.Chat/pull/18687) and into the CLI. As a result, the zip files inside each installed app's document in the database had typescript files in them instead of the now required javascript files. + + As the new strategy of source code storage for apps changes the way the app is loaded, those zip files containing the source code are read everytime the app is started (or [in this particular case, updated](https://github.com/RocketChat/Rocket.Chat/pull/23286/files#diff-caf9f7a22478639e58d6514be039140a42ce1ab2d999c3efe5678c38ee36d0ccR43)), and as the zips' contents were wrong, the operation was failing. + The fix extract the data from old apps and creates new zip files with the compiled `js` already present. - Regression: Fix Bugsnag not started error ([#23308](https://github.com/RocketChat/Rocket.Chat/pull/23308)) @@ -5081,10 +9090,7 @@ `2022-05-30 · 1 🐛 · 1 👩‍💻👨‍💻` ### Engine versions -- Node: `12.22.1` -- NPM: `6.14.1` - MongoDB: `3.4, 3.6, 4.0, 4.2` -- Apps-Engine: `1.27.1` ### 🐛 Bug fixes @@ -5099,9 +9105,7 @@ `2022-05-26 · 1 🐛 · 1 👩‍💻👨‍💻` ### Engine versions -- Node: `14.18.3` -- NPM: `6.14.15` -- MongoDB: `3.6, 4.0, 4.2, 4.4, 5.0` +- MongoDB: `3.4, 3.6, 4.0, 4.2` ### 🐛 Bug fixes @@ -5200,10 +9204,8 @@ - **ENTERPRISE:** Maximum waiting time for chats in Omnichannel queue ([#22955](https://github.com/RocketChat/Rocket.Chat/pull/22955)) - - Add new settings to support closing chats that have been too long on waiting queue - - - Moved old settings to new "Queue Management" section - + - Add new settings to support closing chats that have been too long on waiting queue + - Moved old settings to new "Queue Management" section - Fix issue when closing a livechat room that caused client to not to know if room was open or not - Banner for the updates regarding authentication services ([#23055](https://github.com/RocketChat/Rocket.Chat/pull/23055) by [@g-thome](https://github.com/g-thome)) @@ -5218,10 +9220,10 @@ - Separate RegEx Settings for Channels and Usernames validation ([#21937](https://github.com/RocketChat/Rocket.Chat/pull/21937) by [@aditya-mitra](https://github.com/aditya-mitra)) - Now, there are 2 separate settings for validating names - One for **channels** and another for **usernames**. - - This change also removes the old `UTF8_Names_Validation` setting and adds 2 new settings `UTF8_User_Names_Validation` and `UTF8_Channel_Names_Validation`. - + Now, there are 2 separate settings for validating names - One for **channels** and another for **usernames**. + + This change also removes the old `UTF8_Names_Validation` setting and adds 2 new settings `UTF8_User_Names_Validation` and `UTF8_Channel_Names_Validation`. + https://user-images.githubusercontent.com/55396651/116969904-af5bb800-acd4-11eb-9fc4-dacac60cb08f.mp4 ### 🚀 Improvements @@ -5237,13 +9239,13 @@ - Rewrite File Upload Modal ([#22750](https://github.com/RocketChat/Rocket.Chat/pull/22750)) - Image preview: - ![image](https://user-images.githubusercontent.com/40830821/127223432-dccd2182-aec0-430f-8d70-03ac88aec791.png) - - Video preview: - ![image](https://user-images.githubusercontent.com/40830821/127225982-f8b21840-0d9c-4aff-a354-16188c7ed66e.png) - - Files larger than 10mb: + Image preview: + ![image](https://user-images.githubusercontent.com/40830821/127223432-dccd2182-aec0-430f-8d70-03ac88aec791.png) + + Video preview: + ![image](https://user-images.githubusercontent.com/40830821/127225982-f8b21840-0d9c-4aff-a354-16188c7ed66e.png) + + Files larger than 10mb: ![image](https://user-images.githubusercontent.com/40830821/127222611-5265040f-a06b-4ec5-b528-89b40e6a9072.png) - Types from currentChatsPage.tsx ([#22967](https://github.com/RocketChat/Rocket.Chat/pull/22967)) @@ -5259,14 +9261,14 @@ - "Users By Time of the Day" chart displays incorrect data for Local Timezone ([#22836](https://github.com/RocketChat/Rocket.Chat/pull/22836)) - - Add local timezone conversion to the "Users By Time of the Day" chart in the Engagement Dashboard; + - Add local timezone conversion to the "Users By Time of the Day" chart in the Engagement Dashboard; - Simplify date creations by using `endOf` and `startOf` methods. - Atlassian Crowd connection not working ([#22996](https://github.com/RocketChat/Rocket.Chat/pull/22996) by [@piotrkochan](https://github.com/piotrkochan)) - Audio recording doesn't stop in direct messages on channel switch ([#22880](https://github.com/RocketChat/Rocket.Chat/pull/22880)) - - Cancel audio recordings on message bar destroy event. + - Cancel audio recordings on message bar destroy event. ![test-22372](https://user-images.githubusercontent.com/36537004/128569780-d83747b0-fb9c-4dc6-9bc5-7ae573e720c8.gif) - Bad words falling if message is empty ([#22930](https://github.com/RocketChat/Rocket.Chat/pull/22930)) @@ -5291,23 +9293,21 @@ - Return transcript/dashboards based on timezone settings ([#22850](https://github.com/RocketChat/Rocket.Chat/pull/22850)) - - Added new setting to manage timezones - - - Applied new setting to omnichannel dashboards (realtime, analytics) [NOTE: Other dashboards aren't using this setting actually) - + - Added new setting to manage timezones + - Applied new setting to omnichannel dashboards (realtime, analytics) [NOTE: Other dashboards aren't using this setting actually) - Change getAnalyticsBetweenDate query to filter out system messages instead of substracting them - Tab margin style ([#22851](https://github.com/RocketChat/Rocket.Chat/pull/22851)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/128103848-2a25ba7e-0e59-4502-9bcd-2569cad9379a.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/128103848-2a25ba7e-0e59-4502-9bcd-2569cad9379a.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/128103633-ec7b93fc-4667-4dc9-bad3-bfffaff3974e.png) - Threads and discussions searches don't display proper results ([#22914](https://github.com/RocketChat/Rocket.Chat/pull/22914)) - - _Fix_ issue in discussions search (which wasn't working after a search with no results was made); + - _Fix_ issue in discussions search (which wasn't working after a search with no results was made); - _Improve_ discussions and threads searches: both searches (`chat.getDiscussions` and `chat.getThreadsList`) are now case insensitive (do NOT differ capital from lower letters) and match incomplete words or terms. - Threads List being requested more than expected ([#22879](https://github.com/RocketChat/Rocket.Chat/pull/22879)) @@ -5342,8 +9342,8 @@ - Chore: Script to start Rocket.Chat in HA mode during development ([#22398](https://github.com/RocketChat/Rocket.Chat/pull/22398)) - Sometimes we need to start Rocket.Chat in High-Availability mode (cluster) during development to test how a feature behaves or hunt down a bug. Currently, this involves a lot of commands with details that might be lost if you haven't done it in a while. - + Sometimes we need to start Rocket.Chat in High-Availability mode (cluster) during development to test how a feature behaves or hunt down a bug. Currently, this involves a lot of commands with details that might be lost if you haven't done it in a while. + This PR intends to provide a really simple way for us to start many instances of Rocket.Chat connected in a cluster. - Chore: Update Livechat widget to 1.9.4 ([#22990](https://github.com/RocketChat/Rocket.Chat/pull/22990)) @@ -5360,13 +9360,13 @@ - Regression: File upload name suggestion ([#22953](https://github.com/RocketChat/Rocket.Chat/pull/22953)) - Before: - ![image](https://user-images.githubusercontent.com/40830821/129774936-ecdbe9a1-5e3f-4a0a-ad1e-6f13eb15c60b.png) - ![image](https://user-images.githubusercontent.com/40830821/129775011-fb0df01d-74e4-41ae-bb47-dcf4cc17735e.png) - - - After: - ![image](https://user-images.githubusercontent.com/40830821/129774877-928a8aa0-c003-4e57-8b33-ea6accc32774.png) + Before: + ![image](https://user-images.githubusercontent.com/40830821/129774936-ecdbe9a1-5e3f-4a0a-ad1e-6f13eb15c60b.png) + ![image](https://user-images.githubusercontent.com/40830821/129775011-fb0df01d-74e4-41ae-bb47-dcf4cc17735e.png) + + + After: + ![image](https://user-images.githubusercontent.com/40830821/129774877-928a8aa0-c003-4e57-8b33-ea6accc32774.png) ![image](https://user-images.githubusercontent.com/40830821/129774972-d67debaf-0ce9-44fb-93cb-d7612dd18edf.png) - Regression: Fix creation of self-DMs ([#23015](https://github.com/RocketChat/Rocket.Chat/pull/23015)) @@ -5434,8 +9434,7 @@ - Fix Auto Selection algorithm on community edition ([#22991](https://github.com/RocketChat/Rocket.Chat/pull/22991)) - - When using the autoselection algo on community editions, all agents were marked as unavailable due to an unapplied filter - + - When using the autoselection algo on community editions, all agents were marked as unavailable due to an unapplied filter - Fixed an issue when both user & system setting to manange EE max number of chats allowed were set to 0
@@ -5475,7 +9474,7 @@ - Apps-Engine's scheduler failing to update run tasks ([#22882](https://github.com/RocketChat/Rocket.Chat/pull/22882)) - [Agenda](https://github.com/agenda/agenda), the library that manages scheduling, depended on setting a job property named `nextRunAt` as `undefined` to signal whether it should be run on schedule or not. [Rocket.Chat's current Mongo driver](https://github.com/RocketChat/Rocket.Chat/pull/22399) ignores `undefined` values when updating documents and this was causing jobs to never stop running as Agenda couldn't clear that property (set them as `undefined`). + [Agenda](https://github.com/agenda/agenda), the library that manages scheduling, depended on setting a job property named `nextRunAt` as `undefined` to signal whether it should be run on schedule or not. [Rocket.Chat's current Mongo driver](https://github.com/RocketChat/Rocket.Chat/pull/22399) ignores `undefined` values when updating documents and this was causing jobs to never stop running as Agenda couldn't clear that property (set them as `undefined`). This updates Rocket.Chat's dependency on Agenda.js to point to [a fork that fixes the problem](https://github.com/RocketChat/agenda/releases/tag/3.1.2). - Close omnichannel conversations when agent is deactivated ([#22917](https://github.com/RocketChat/Rocket.Chat/pull/22917)) @@ -5529,7 +9528,7 @@ - Monitoring Track messages' round trip time ([#22676](https://github.com/RocketChat/Rocket.Chat/pull/22676)) - Track messages' roundtrip time from backend saves time to the time when received back from the oplog allowing track of oplog slowness. + Track messages' roundtrip time from backend saves time to the time when received back from the oplog allowing track of oplog slowness. Prometheus metric: `rocketchat_messages_roundtrip_time` - REST endpoint to remove User from Role ([#20485](https://github.com/RocketChat/Rocket.Chat/pull/20485) by [@Cosnavel](https://github.com/Cosnavel) & [@lucassartor](https://github.com/lucassartor) & [@ostjen](https://github.com/ostjen)) @@ -5541,22 +9540,19 @@ - Change message deletion confirmation modal to toast ([#22544](https://github.com/RocketChat/Rocket.Chat/pull/22544)) - Changed a timed modal for a toast message + Changed a timed modal for a toast message ![image](https://user-images.githubusercontent.com/40830821/124192670-0646f900-da9c-11eb-941c-9ae35421f6ef.png) - Configuration for indices in Apps-Engine models ([#22705](https://github.com/RocketChat/Rocket.Chat/pull/22705)) - * Add `appId` field to the data saved by the Scheduler - - * Add `appId` index to `rocketchat_apps_persistence` model - - * Skip "trash collection" when deleting records from `rocketchat_apps_persistence` - - * Add a new setting to control for how long we should keep logs from the apps - - ![image](https://user-images.githubusercontent.com/1810309/126246666-907f9d98-1d84-4dfe-a80a-7dd874d36fa8.png) - - + * Add `appId` field to the data saved by the Scheduler + * Add `appId` index to `rocketchat_apps_persistence` model + * Skip "trash collection" when deleting records from `rocketchat_apps_persistence` + * Add a new setting to control for how long we should keep logs from the apps + + ![image](https://user-images.githubusercontent.com/1810309/126246666-907f9d98-1d84-4dfe-a80a-7dd874d36fa8.png) + + ![image](https://user-images.githubusercontent.com/1810309/126246655-2ce3cb5f-b2f5-456e-a9c4-beccd9b3ef41.png) - Make `shortcut` field of canned responses unique ([#22700](https://github.com/RocketChat/Rocket.Chat/pull/22700)) @@ -5579,38 +9575,37 @@ - Replace remaing discussion creation modals with React modal. ([#22448](https://github.com/RocketChat/Rocket.Chat/pull/22448)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/123840524-cbe72b80-d8e4-11eb-9ddb-23a9f9d90aac.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/123840524-cbe72b80-d8e4-11eb-9ddb-23a9f9d90aac.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/123840219-74e15680-d8e4-11eb-95aa-00a990ffe0e7.png) - Return open room if available for visitors ([#22742](https://github.com/RocketChat/Rocket.Chat/pull/22742)) - Rewrite Enter Encryption Password Modal ([#22456](https://github.com/RocketChat/Rocket.Chat/pull/22456)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/123182889-bbf3c580-d466-11eb-8d4d-9cfc3d224e33.png) - - ### after - ![image](https://user-images.githubusercontent.com/27704687/123182916-cada7800-d466-11eb-96ee-850be190d419.png) - - ### Aditional Improves: - + ### before + ![image](https://user-images.githubusercontent.com/27704687/123182889-bbf3c580-d466-11eb-8d4d-9cfc3d224e33.png) + + ### after + ![image](https://user-images.githubusercontent.com/27704687/123182916-cada7800-d466-11eb-96ee-850be190d419.png) + + ### Aditional Improves: - Added a visual validation in the password field - Rewrite OTR modals ([#22583](https://github.com/RocketChat/Rocket.Chat/pull/22583)) - ![image](https://user-images.githubusercontent.com/40830821/124513267-cb510800-ddb0-11eb-8165-f103029c348f.png) - ![image](https://user-images.githubusercontent.com/40830821/124513354-04897800-ddb1-11eb-96f4-41fe906ca0d7.png) + ![image](https://user-images.githubusercontent.com/40830821/124513267-cb510800-ddb0-11eb-8165-f103029c348f.png) + ![image](https://user-images.githubusercontent.com/40830821/124513354-04897800-ddb1-11eb-96f4-41fe906ca0d7.png) ![image](https://user-images.githubusercontent.com/40830821/124513395-1b2fcf00-ddb1-11eb-83e4-3f8f9b4676ba.png) - Rewrite Save Encryption Password Modal ([#22447](https://github.com/RocketChat/Rocket.Chat/pull/22447)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/122980201-c337a800-d36e-11eb-8e2b-68534cea8e1e.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/122980201-c337a800-d36e-11eb-8e2b-68534cea8e1e.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/122980409-f8dc9100-d36e-11eb-9c15-aff779c84a91.png) - Rewrite sidebar footer as React Component ([#22687](https://github.com/RocketChat/Rocket.Chat/pull/22687)) @@ -5625,12 +9620,12 @@ - Wrong error message when trying to create a blocked username ([#22452](https://github.com/RocketChat/Rocket.Chat/pull/22452) by [@lucassartor](https://github.com/lucassartor)) - When trying to create a user with a blocked username, the UI was showing generic error message that it wasn't very detailed. - - Old error message: - ![image](https://user-images.githubusercontent.com/49413772/123120080-6d203e80-d41a-11eb-8c87-64e34334c856.png) - - New error message: + When trying to create a user with a blocked username, the UI was showing generic error message that it wasn't very detailed. + + Old error message: + ![image](https://user-images.githubusercontent.com/49413772/123120080-6d203e80-d41a-11eb-8c87-64e34334c856.png) + + New error message: ![aaa](https://user-images.githubusercontent.com/49413772/123120251-8c1ed080-d41a-11eb-8dc2-d7484923d851.PNG) ### 🐛 Bug fixes @@ -5638,19 +9633,19 @@ - **ENTERPRISE:** Engagement Dashboard displaying incorrect data about active users ([#22381](https://github.com/RocketChat/Rocket.Chat/pull/22381)) - - Fix sessions' and users' grouping in the Engagement Dashboard API endpoints; - - Fix the data displayed in the charts from the "Active users", "Users by time of day" and "When is the chat busier?" sections of the Engagement Dashboard; + - Fix sessions' and users' grouping in the Engagement Dashboard API endpoints; + - Fix the data displayed in the charts from the "Active users", "Users by time of day" and "When is the chat busier?" sections of the Engagement Dashboard; - Replace label used to describe the amount of Active Users in the License section of the Info page. - **ENTERPRISE:** Make AutoSelect algo take current agent load in consideration ([#22611](https://github.com/RocketChat/Rocket.Chat/pull/22611)) - **ENTERPRISE:** Race condition on Omnichannel visitor abandoned callback ([#22413](https://github.com/RocketChat/Rocket.Chat/pull/22413)) - As you can see [here](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/ee/app/livechat-enterprise/server/lib/Helper.js#L127) the `predictedVisitorAbandonment` flag is not set if the room object doesn't have `v.lastMessageTs` property. So we need to always make sure the `v.lastMessageTs` is set before this method is called. - - Currently the `v.lastMessageTs` is being set in [this](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/app/livechat/server/hooks/saveLastVisitorMessageTs.js#L4) (lets call this **hook-1**) hook which has `HIGH` priority - and the `predictedVisitorAbandonment` check is inturn performed in [this](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js#L5) (let call this **hook-2**) hook which is also `HIGH` priority. - + As you can see [here](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/ee/app/livechat-enterprise/server/lib/Helper.js#L127) the `predictedVisitorAbandonment` flag is not set if the room object doesn't have `v.lastMessageTs` property. So we need to always make sure the `v.lastMessageTs` is set before this method is called. + + Currently the `v.lastMessageTs` is being set in [this](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/app/livechat/server/hooks/saveLastVisitorMessageTs.js#L4) (lets call this **hook-1**) hook which has `HIGH` priority + and the `predictedVisitorAbandonment` check is inturn performed in [this](https://github.com/RocketChat/Rocket.Chat/blob/857791c39c97b51b5b6fd3718e0c816959a81c3b/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.js#L5) (let call this **hook-2**) hook which is also `HIGH` priority. + So ideally we'd except the **hook-1** to be called b4 **hook-2**, however currently since both of them are at same priority, there is no way to control which one is executed first. Hence in this PR, I'm making the priority of **hook-2** as `MEDIUM` to keeping the priority of **hook-1** the same as b4, i.e. `HIGH`. This should make sure that the **hook-1** is always executed b4 **hook-2** - Admin page crashing when commit hash is null ([#22057](https://github.com/RocketChat/Rocket.Chat/pull/22057) by [@cprice-kgi](https://github.com/cprice-kgi)) @@ -5659,41 +9654,39 @@ - Blank screen in message auditing DM tab ([#22763](https://github.com/RocketChat/Rocket.Chat/pull/22763) by [@ostjen](https://github.com/ostjen)) - The DM tab in message auditing was displaying a blank screen, instead of the actual tab. - + The DM tab in message auditing was displaying a blank screen, instead of the actual tab. + ![image](https://user-images.githubusercontent.com/28611993/127041404-dfca7f6a-2b8b-4c15-9cbd-c6238fac0063.png) - Bugs in AutoCompleteDepartment ([#22414](https://github.com/RocketChat/Rocket.Chat/pull/22414)) - Call button is still displayed when the user doesn't have permission to use it ([#22170](https://github.com/RocketChat/Rocket.Chat/pull/22170)) - - Hide 'Call' buttons from the tab bar for muted users; - + - Hide 'Call' buttons from the tab bar for muted users; - Display an error when a muted user attempts to enter a call using the 'Click to Join!' button. - Can't see full user profile on team's room ([#22355](https://github.com/RocketChat/Rocket.Chat/pull/22355)) - ### before - ![before](https://user-images.githubusercontent.com/27704687/121966860-bbac4980-cd45-11eb-8d48-2b0457110fc7.gif) - - ### after - ![after](https://user-images.githubusercontent.com/27704687/121966870-bea73a00-cd45-11eb-9c89-ec52ac17e20f.gif) - - ### aditional fix :rocket: - + ### before + ![before](https://user-images.githubusercontent.com/27704687/121966860-bbac4980-cd45-11eb-8d48-2b0457110fc7.gif) + + ### after + ![after](https://user-images.githubusercontent.com/27704687/121966870-bea73a00-cd45-11eb-9c89-ec52ac17e20f.gif) + + ### aditional fix :rocket: - unnecessary `TeamsMembers` component removed - Cannot create a discussion from top left sidebar as a user ([#22618](https://github.com/RocketChat/Rocket.Chat/pull/22618) by [@lucassartor](https://github.com/lucassartor)) - When trying to create a discussion using the top left sidebar modal with an role that don't have the `view-other-user-channels ` permission, an empty list would be shown, which is a wrong behavior. - Also, when being able to use this modal, discussions were listed as options, which is also a wrong behavior as there can't be nested discussions. - - This PR looks to fix both these issues. - - **Old behavior:** - ![old](https://user-images.githubusercontent.com/49413772/124960017-3c333280-dff2-11eb-86cd-b2638311517e.png) - - **New behavior:** + When trying to create a discussion using the top left sidebar modal with an role that don't have the `view-other-user-channels ` permission, an empty list would be shown, which is a wrong behavior. + Also, when being able to use this modal, discussions were listed as options, which is also a wrong behavior as there can't be nested discussions. + + This PR looks to fix both these issues. + + **Old behavior:** + ![old](https://user-images.githubusercontent.com/49413772/124960017-3c333280-dff2-11eb-86cd-b2638311517e.png) + + **New behavior:** ![image](https://user-images.githubusercontent.com/49413772/124958882-05a8e800-dff1-11eb-8203-b34a4f1c98a0.png) - Channel is automatically getting added to the first option in move to team feature ([#22670](https://github.com/RocketChat/Rocket.Chat/pull/22670) by [@ostjen](https://github.com/ostjen)) @@ -5708,12 +9701,12 @@ - Create discussion modal - cancel button and invite users alignment ([#22718](https://github.com/RocketChat/Rocket.Chat/pull/22718) by [@ostjen](https://github.com/ostjen)) - Changes in "open discussion" modal - - > Added cancel button - > Fixed alignment in invite user - - + Changes in "open discussion" modal + + > Added cancel button + > Fixed alignment in invite user + + ![image](https://user-images.githubusercontent.com/28611993/126388304-6ac76574-6924-426e-843d-afd53dc1c874.png) - crush in the getChannelHistory method ([#22667](https://github.com/RocketChat/Rocket.Chat/pull/22667) by [@MaestroArt](https://github.com/MaestroArt)) @@ -5748,29 +9741,27 @@ - Quote message not working for Livechat visitors ([#22586](https://github.com/RocketChat/Rocket.Chat/pull/22586)) - ### Before: - ![image](https://user-images.githubusercontent.com/34130764/124583613-de2b1180-de70-11eb-82aa-18564b317626.png) - ### After: + ### Before: + ![image](https://user-images.githubusercontent.com/34130764/124583613-de2b1180-de70-11eb-82aa-18564b317626.png) + ### After: ![image](https://user-images.githubusercontent.com/34130764/124583775-12063700-de71-11eb-8ab5-b0169fac2d40.png) - Redirect to login after delete own account ([#22499](https://github.com/RocketChat/Rocket.Chat/pull/22499)) - Redirect the user to login after delete own account - - ### Aditional fixes: - - - Visual issue in password input on Delete Own Account Modal - - ### before - ![image](https://user-images.githubusercontent.com/27704687/123711503-f5ea1080-d846-11eb-96aa-8ed638ca665c.png) - - ### after + Redirect the user to login after delete own account + + ### Aditional fixes: + - Visual issue in password input on Delete Own Account Modal + + ### before + ![image](https://user-images.githubusercontent.com/27704687/123711503-f5ea1080-d846-11eb-96aa-8ed638ca665c.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/123711336-b3c0cf00-d846-11eb-9408-a686d8668ba5.png) - Remove stack traces from Meteor errors when debug setting is disabled ([#22699](https://github.com/RocketChat/Rocket.Chat/pull/22699)) - - Fix 'not iterable' errors in the `normalizeMessage` function; - + - Fix 'not iterable' errors in the `normalizeMessage` function; - Remove stack traces from errors thrown by the `jitsi:updateTimeout` (and other `Meteor.Error`s) method. - Rewrite CurrentChats to TS ([#22424](https://github.com/RocketChat/Rocket.Chat/pull/22424)) @@ -5859,16 +9850,15 @@ - Regression: Data in the "Active Users" section is delayed in 1 day ([#22794](https://github.com/RocketChat/Rocket.Chat/pull/22794)) - - Fix 1 day delay in the Engagement Dashboard's "Active Users" section; - - - Downgrade `@nivo/line` version. - **Expected behavior:** + - Fix 1 day delay in the Engagement Dashboard's "Active Users" section; + - Downgrade `@nivo/line` version. + **Expected behavior:** ![active-users-engagement-dashboard](https://user-images.githubusercontent.com/36537004/127372185-390dc42f-bc90-4841-a22b-731f0aafcafe.PNG) - Regression: Data in the "New Users" section is delayed in 1 day ([#22751](https://github.com/RocketChat/Rocket.Chat/pull/22751)) - - Update nivo version (which was causing errors in the bar chart); - - Fix 1 day delay in '7 days' and '30 days' periods; + - Update nivo version (which was causing errors in the bar chart); + - Fix 1 day delay in '7 days' and '30 days' periods; - Update tooltip theme. - Regression: Federation warnings on ci ([#22765](https://github.com/RocketChat/Rocket.Chat/pull/22765) by [@g-thome](https://github.com/g-thome)) @@ -5893,9 +9883,9 @@ - Regression: Fix tooltip style in the "Busiest Chat Times" chart ([#22813](https://github.com/RocketChat/Rocket.Chat/pull/22813)) - - Fix tooltip in the Engagement Dashboard's "Busiest Chat Times" chart (Hours). - - **Expected behavior:** + - Fix tooltip in the Engagement Dashboard's "Busiest Chat Times" chart (Hours). + + **Expected behavior:** ![busiest-times-ed](https://user-images.githubusercontent.com/36537004/127527827-465397ed-f089-4fb7-9ab2-6fa8cea6abdf.PNG) - Regression: Fix users not being able to see the scope of the canned m… ([#22760](https://github.com/RocketChat/Rocket.Chat/pull/22760)) @@ -5912,10 +9902,10 @@ - Regression: Prevent custom status from being visible in sequential messages ([#22733](https://github.com/RocketChat/Rocket.Chat/pull/22733)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/126641946-866dae96-1983-43a5-b689-b24670473ad0.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/126641946-866dae96-1983-43a5-b689-b24670473ad0.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/126641752-3163eb95-1cd4-4d99-a61a-4d06d9e7e13e.png) - Regression: Properly force newline in attachment fields ([#22727](https://github.com/RocketChat/Rocket.Chat/pull/22727)) @@ -6096,32 +10086,30 @@ - Add `teams.convertToChannel` endpoint ([#22188](https://github.com/RocketChat/Rocket.Chat/pull/22188)) - - Add new `teams.converToChannel` endpoint; - - - Update `ConvertToTeam` modal text (since this action can now be reversed); - + - Add new `teams.converToChannel` endpoint; + - Update `ConvertToTeam` modal text (since this action can now be reversed); - Remove corresponding team memberships when a team is deleted or converted to a channel; - Add setting to configure default role for user on manual registration ([#20650](https://github.com/RocketChat/Rocket.Chat/pull/20650) by [@lucassartor](https://github.com/lucassartor)) - Add an `admin` setting to determine the initial `role` for new users who registered manually (through the register form and via API, not using an authentication service), normally all new users are assigned to the `user` role. - - The setting can be found in `Admin`->`Accounts`->`Registration`. - - ![image](https://user-images.githubusercontent.com/49413772/107252603-47b70900-6a14-11eb-9cc6-df76720b7365.png) - The setting initial value is false, so the default behaviour stays the same while creating a new server or upgrading one. - - https://user-images.githubusercontent.com/49413772/107253220-ddeb2f00-6a14-11eb-85b4-f770dbbe4970.mp4 - + Add an `admin` setting to determine the initial `role` for new users who registered manually (through the register form and via API, not using an authentication service), normally all new users are assigned to the `user` role. + + The setting can be found in `Admin`->`Accounts`->`Registration`. + + ![image](https://user-images.githubusercontent.com/49413772/107252603-47b70900-6a14-11eb-9cc6-df76720b7365.png) + The setting initial value is false, so the default behaviour stays the same while creating a new server or upgrading one. + + https://user-images.githubusercontent.com/49413772/107253220-ddeb2f00-6a14-11eb-85b4-f770dbbe4970.mp4 + Video showing an example of the setting being used and creating an new user with the default roles via API. - Content-Security-Policy for inline scripts ([#20724](https://github.com/RocketChat/Rocket.Chat/pull/20724)) - Security policies were applied for inline scripts cases. Due to the libraries and components we use it is not possible to disable inline styles and images as they would break Oembeds and other libraries. - - - basically the inline scripts were moved to a js file - + Security policies were applied for inline scripts cases. Due to the libraries and components we use it is not possible to disable inline styles and images as they would break Oembeds and other libraries. + + + basically the inline scripts were moved to a js file + and besides that some suggars syntax like `addScript` and `addStyle` were added, this way the application already takes care of inserting the elements and providing the content automatically. - Open modals in side effects outside React ([#22247](https://github.com/RocketChat/Rocket.Chat/pull/22247)) @@ -6137,17 +10125,15 @@ - Add BBB and Jitsi to Team ([#22312](https://github.com/RocketChat/Rocket.Chat/pull/22312)) - Added 2 new settings: - - - `Admin > Video Conference > Big Blue Button > Enable for teams` - + Added 2 new settings: + - `Admin > Video Conference > Big Blue Button > Enable for teams` - `Admin > Video Conference > Jitsi > Enable in teams` - Add debouncing to units selects filters ([#22097](https://github.com/RocketChat/Rocket.Chat/pull/22097)) - Add modal to close chats when tags/comments are not required ([#22245](https://github.com/RocketChat/Rocket.Chat/pull/22245) by [@rafaelblink](https://github.com/rafaelblink)) - When neither tags or comments are required to close a livechat, show this modal instead: + When neither tags or comments are required to close a livechat, show this modal instead: ![Screen Shot 2021-05-20 at 7 33 19 PM](https://user-images.githubusercontent.com/20868078/119057741-6af23c80-b9a3-11eb-902f-f8a7458ad11c.png) - Fallback messages on contextual bar ([#22376](https://github.com/RocketChat/Rocket.Chat/pull/22376)) @@ -6170,10 +10156,10 @@ - Remove differentiation between public x private channels in sidebar ([#22160](https://github.com/RocketChat/Rocket.Chat/pull/22160)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/119752184-e7d55880-be72-11eb-9167-be2f305ddb3f.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/119752184-e7d55880-be72-11eb-9167-be2f305ddb3f.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/119752125-c8d6c680-be72-11eb-8444-2e0c7cb1c600.png) - Rewrite create direct modal ([#22209](https://github.com/RocketChat/Rocket.Chat/pull/22209)) @@ -6182,8 +10168,8 @@ - Rewrite Create Discussion Modal (only through sidebar) ([#22224](https://github.com/RocketChat/Rocket.Chat/pull/22224)) - This is only available by creating a new discussion when clicking on the sidebar button. Other places will be implemented afterwards. - + This is only available by creating a new discussion when clicking on the sidebar button. Other places will be implemented afterwards. + ![image](https://user-images.githubusercontent.com/40830821/120556093-6af63180-c3d2-11eb-97ea-63c5423049dc.png) - Send only relevant data via WebSocket ([#22258](https://github.com/RocketChat/Rocket.Chat/pull/22258)) @@ -6197,12 +10183,12 @@ - **EE:** Canned responses can't be deleted ([#22095](https://github.com/RocketChat/Rocket.Chat/pull/22095) by [@rafaelblink](https://github.com/rafaelblink)) - Deletion button has been removed from the edition option. - - ## Before - ![image](https://user-images.githubusercontent.com/2493803/119059416-9f1b2c80-b9a6-11eb-933a-4efa1ac0552a.png) - - ### After + Deletion button has been removed from the edition option. + + ## Before + ![image](https://user-images.githubusercontent.com/2493803/119059416-9f1b2c80-b9a6-11eb-933a-4efa1ac0552a.png) + + ### After ![Rocket Chat (2)](https://user-images.githubusercontent.com/2493803/119172517-72b1ef80-ba3c-11eb-9178-04a12176f312.gif) - **ENTERPRISE:** Omnichannel enterprise permissions being added back to its default roles ([#22322](https://github.com/RocketChat/Rocket.Chat/pull/22322)) @@ -6211,19 +10197,19 @@ - **ENTERPRISE:** Prevent Visitor Abandonment after forwarding chat ([#22243](https://github.com/RocketChat/Rocket.Chat/pull/22243)) - Currently the Visitor Abandonment timer isn't affected when the chat is forwarded. However this is affecting the UX in certain situations like eg: A bot forwarding a chat to an human agent - ![image](https://user-images.githubusercontent.com/34130764/120896383-e4925780-c63e-11eb-937e-ffd7c4836159.png) - + Currently the Visitor Abandonment timer isn't affected when the chat is forwarded. However this is affecting the UX in certain situations like eg: A bot forwarding a chat to an human agent + ![image](https://user-images.githubusercontent.com/34130764/120896383-e4925780-c63e-11eb-937e-ffd7c4836159.png) + To solve this issue, we'll now be stoping the Visitor Abandonment timer once a chat is forwarded. - **IMPROVE:** Prevent creation of duplicated roles and new `roles.update` endpoint ([#22279](https://github.com/RocketChat/Rocket.Chat/pull/22279) by [@lucassartor](https://github.com/lucassartor)) - Currently, the action of updating a role is broken: because roles have their `_id` = `name`, when updating a role there's no way to validate if the user is trying to update or create a new role with a name that already exists - which causes wrong behaviors, such as roles with the same name and not being able to update them. - - To proper fix this, this PR looks to change the creation of roles. Now, roles have a unique `_id` value and there's a endpoint to update roles: `/api/v1/roles.update`. - - Doing so, it's possible to validate on both endpoints (`roles.create` and `roles.update`) to not allow roles with duplicated names. - + Currently, the action of updating a role is broken: because roles have their `_id` = `name`, when updating a role there's no way to validate if the user is trying to update or create a new role with a name that already exists - which causes wrong behaviors, such as roles with the same name and not being able to update them. + + To proper fix this, this PR looks to change the creation of roles. Now, roles have a unique `_id` value and there's a endpoint to update roles: `/api/v1/roles.update`. + + Doing so, it's possible to validate on both endpoints (`roles.create` and `roles.update`) to not allow roles with duplicated names. + **OBS:** The unique id changes only reflect new roles, the standard roles (such as admin and user) still have `_id` = `name`, but new roles now **can't** have the same name as them. - `channels.history`, `groups.history` and `im.history` REST endpoints not respecting hide system message config ([#22364](https://github.com/RocketChat/Rocket.Chat/pull/22364)) @@ -6240,10 +10226,10 @@ - Can't delete file from Room's file list ([#22191](https://github.com/RocketChat/Rocket.Chat/pull/22191)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/120215931-bb239700-c20c-11eb-9494-d4bc017df390.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/120215931-bb239700-c20c-11eb-9494-d4bc017df390.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/120216113-f8882480-c20c-11eb-9afb-b127e66a43da.png) - Cancel button and success toast at Leave Team modal ([#22373](https://github.com/RocketChat/Rocket.Chat/pull/22373)) @@ -6254,10 +10240,10 @@ - Convert and Move team permission ([#22350](https://github.com/RocketChat/Rocket.Chat/pull/22350)) - ### before - https://user-images.githubusercontent.com/45966964/114909360-5c04f100-9e1d-11eb-9363-f308e5d0be68.mp4 - - ### after + ### before + https://user-images.githubusercontent.com/45966964/114909360-5c04f100-9e1d-11eb-9363-f308e5d0be68.mp4 + + ### after https://user-images.githubusercontent.com/45966964/114909388-61fad200-9e1d-11eb-9bbe-114b55954a9f.mp4 - CORS error while interacting with any action button on Livechat ([#22150](https://github.com/RocketChat/Rocket.Chat/pull/22150)) @@ -6276,50 +10262,50 @@ - Members tab visual issues ([#22138](https://github.com/RocketChat/Rocket.Chat/pull/22138)) - ## Before - ![image](https://user-images.githubusercontent.com/27704687/119558283-95fbd800-bd77-11eb-91b4-91821f365bf3.png) - - ## After + ## Before + ![image](https://user-images.githubusercontent.com/27704687/119558283-95fbd800-bd77-11eb-91b4-91821f365bf3.png) + + ## After ![image](https://user-images.githubusercontent.com/27704687/119558120-6947c080-bd77-11eb-8ecb-7fedc07afa82.png) - Memory leak generated by Stream Cast usage ([#22329](https://github.com/RocketChat/Rocket.Chat/pull/22329)) - Stream Cast uses a different approach to broadcast data to the instances, it uses the DDP subscription method that requires a collection on the other side, if no collection exists with the given name `broadcast-stream` it caches in memory waiting for the collection to be set later. The cache is cleared only when a reconnection happens. - + Stream Cast uses a different approach to broadcast data to the instances, it uses the DDP subscription method that requires a collection on the other side, if no collection exists with the given name `broadcast-stream` it caches in memory waiting for the collection to be set later. The cache is cleared only when a reconnection happens. + This PR overrides the function that processes the data for that specific connection, preventing the cache and everything else to be processed since we already have our low-level listener to process the data. - Message box hiding on mobile view (Safari) ([#22212](https://github.com/RocketChat/Rocket.Chat/pull/22212)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/120404256-5b1c1600-c31c-11eb-96e9-860e4132db5f.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/120404256-5b1c1600-c31c-11eb-96e9-860e4132db5f.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/120404406-acc4a080-c31c-11eb-9efb-c2ad88664fda.png) - Missing burger menu on direct messages ([#22211](https://github.com/RocketChat/Rocket.Chat/pull/22211)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/120403671-09bf5700-c31b-11eb-92a1-a2f589bd85fc.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/120403671-09bf5700-c31b-11eb-92a1-a2f589bd85fc.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/120403693-1643af80-c31b-11eb-8027-dbdc4f560647.png) - Missing Throbber while thread list is loading ([#22316](https://github.com/RocketChat/Rocket.Chat/pull/22316)) - ### before - List was starting with no results even if there's results: - - ![image](https://user-images.githubusercontent.com/27704687/121606744-1e8ba100-ca25-11eb-9b31-706fb998d05f.png) - - ### after + ### before + List was starting with no results even if there's results: + + ![image](https://user-images.githubusercontent.com/27704687/121606744-1e8ba100-ca25-11eb-9b31-706fb998d05f.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/121606635-e97f4e80-ca24-11eb-81f7-af8b0cc41c89.png) - Not possible to edit some messages inside threads ([#22325](https://github.com/RocketChat/Rocket.Chat/pull/22325)) - ### Before - ![before](https://user-images.githubusercontent.com/27704687/121755733-4eeb4200-caee-11eb-9d77-1b498c38c478.gif) - - ### After + ### Before + ![before](https://user-images.githubusercontent.com/27704687/121755733-4eeb4200-caee-11eb-9d77-1b498c38c478.gif) + + ### After ![after](https://user-images.githubusercontent.com/27704687/121755736-514d9c00-caee-11eb-9897-78fcead172f2.gif) - Notifications not using user's name ([#22309](https://github.com/RocketChat/Rocket.Chat/pull/22309)) @@ -6346,10 +10332,10 @@ - Sidebar not closing when clicking on a channel ([#22271](https://github.com/RocketChat/Rocket.Chat/pull/22271)) - ### before - ![before](https://user-images.githubusercontent.com/27704687/121074843-c6e20100-c7aa-11eb-88db-76e39b57b064.gif) - - ### after + ### before + ![before](https://user-images.githubusercontent.com/27704687/121074843-c6e20100-c7aa-11eb-88db-76e39b57b064.gif) + + ### after ![after](https://user-images.githubusercontent.com/27704687/121074860-cb0e1e80-c7aa-11eb-9e96-06d75044b763.gif) - Sound notification is not emitted when the Omnichannel chat comes from another department ([#22291](https://github.com/RocketChat/Rocket.Chat/pull/22291)) @@ -6360,9 +10346,9 @@ - Undefined error when forwarding chats to offline department ([#22154](https://github.com/RocketChat/Rocket.Chat/pull/22154) by [@rafaelblink](https://github.com/rafaelblink)) - ![Screen Shot 2021-05-26 at 5 29 17 PM](https://user-images.githubusercontent.com/59577424/119727520-c495b380-be48-11eb-88a2-158017c7ad0a.png) - - Omnichannel agents are facing the error shown above when forwarding chats to offline departments. + ![Screen Shot 2021-05-26 at 5 29 17 PM](https://user-images.githubusercontent.com/59577424/119727520-c495b380-be48-11eb-88a2-158017c7ad0a.png) + + Omnichannel agents are facing the error shown above when forwarding chats to offline departments. The error usually takes place when the routing system algorithm is **Manual Selection**. - Unread bar in channel flash quickly and then disappear ([#22275](https://github.com/RocketChat/Rocket.Chat/pull/22275)) @@ -6393,15 +10379,15 @@ - Chore: Change modals for remove user from team && leave team ([#22141](https://github.com/RocketChat/Rocket.Chat/pull/22141)) - ![image](https://user-images.githubusercontent.com/40830821/119576154-93f14380-bd8e-11eb-8885-f889f2939bf4.png) + ![image](https://user-images.githubusercontent.com/40830821/119576154-93f14380-bd8e-11eb-8885-f889f2939bf4.png) ![image](https://user-images.githubusercontent.com/40830821/119576219-b5eac600-bd8e-11eb-832c-ea7a17a56bdd.png) - Chore: Check PR Title on every submission ([#22140](https://github.com/RocketChat/Rocket.Chat/pull/22140)) - Chore: Enable push gateway only if the server is registered ([#22346](https://github.com/RocketChat/Rocket.Chat/pull/22346) by [@lucassartor](https://github.com/lucassartor)) - Currently, when creating an unregistered server, the default value of the push gateway setting is set to true and is disabled (it can't be changed unless the server is registered). This is a wrong behavior as an unregistered server **can't** use the push gateway. - + Currently, when creating an unregistered server, the default value of the push gateway setting is set to true and is disabled (it can't be changed unless the server is registered). This is a wrong behavior as an unregistered server **can't** use the push gateway. + This PR creates a validation to check if the server is registered when enabling the push gateway. That way, even if the push gateway setting is turned on, but the server is unregistered, the push gateway **won't** work - it will behave like it is off. - Chore: Enforce TypeScript on Storybook ([#22317](https://github.com/RocketChat/Rocket.Chat/pull/22317)) @@ -6418,7 +10404,7 @@ - Chore: Update delete team modal to new design ([#22127](https://github.com/RocketChat/Rocket.Chat/pull/22127)) - Now the modal has only 2 steps (steps 1 and 2 were merged) + Now the modal has only 2 steps (steps 1 and 2 were merged) ![image](https://user-images.githubusercontent.com/40830821/119414580-2e398480-bcc6-11eb-9a47-515568257974.png) - Language update from LingoHub 🤖 on 2021-05-31Z ([#22196](https://github.com/RocketChat/Rocket.Chat/pull/22196)) @@ -6445,10 +10431,10 @@ - Regression: Missing flexDirection on select field ([#22300](https://github.com/RocketChat/Rocket.Chat/pull/22300)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/121425905-532a2a80-c949-11eb-885f-e8ddaf5c8d5c.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/121425905-532a2a80-c949-11eb-885f-e8ddaf5c8d5c.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/121425770-283fd680-c949-11eb-8d94-86886f174599.png) - Regression: RoomProvider using wrong types ([#22370](https://github.com/RocketChat/Rocket.Chat/pull/22370)) @@ -6589,50 +10575,50 @@ - **ENTERPRISE:** Introduce Load Rotation routing algorithm for Omnichannel ([#22090](https://github.com/RocketChat/Rocket.Chat/pull/22090) by [@rafaelblink](https://github.com/rafaelblink)) - This PR introduces a new Auto Chat Distribution (ACD) algorithm for Omnichannel: **Load Rotation**. - The algorithm distributes chats to agents one by one, which means that when a new chat arrives, the agent with the oldest routing assignment time will be selected to serve the chat, regardless of the number of chats in progress each agent has. - + This PR introduces a new Auto Chat Distribution (ACD) algorithm for Omnichannel: **Load Rotation**. + The algorithm distributes chats to agents one by one, which means that when a new chat arrives, the agent with the oldest routing assignment time will be selected to serve the chat, regardless of the number of chats in progress each agent has. + ![Screen Shot 2021-05-20 at 5 17 40 PM](https://user-images.githubusercontent.com/59577424/119043752-c61a3400-b98f-11eb-8543-f3176879af1d.png) - Back button for Omnichannel ([#21647](https://github.com/RocketChat/Rocket.Chat/pull/21647) by [@rafaelblink](https://github.com/rafaelblink)) - New Message Parser ([#21962](https://github.com/RocketChat/Rocket.Chat/pull/21962)) - The objective is to put an end to the confusion that we face having multiple parsers, and the problems that this brings, it is still experimental then users need to choose to use it. - - The benefits are multiple. no more unexpected cases or grammatical collisions (in addition to more flexible nested cases like bold within link labels). - Besides, we no longer render raw html, instead we use components, so the xss attacks are over (the easy ones at least). Without further discoveries and at the fronted, we only reder what is delivered thus improving our performance. + The objective is to put an end to the confusion that we face having multiple parsers, and the problems that this brings, it is still experimental then users need to choose to use it. + + The benefits are multiple. no more unexpected cases or grammatical collisions (in addition to more flexible nested cases like bold within link labels). + Besides, we no longer render raw html, instead we use components, so the xss attacks are over (the easy ones at least). Without further discoveries and at the fronted, we only reder what is delivered thus improving our performance. This can be used in multiple places, (message, alert, sidenav and in the entire mobile application.) - Option to notify failed login attempts to a channel ([#21968](https://github.com/RocketChat/Rocket.Chat/pull/21968)) - Option to prevent users from using Invisible status ([#20084](https://github.com/RocketChat/Rocket.Chat/pull/20084) by [@lucassartor](https://github.com/lucassartor)) - Add an `admin` option to allow/disallow the `Invisible` status option from all users. This option is available in the `Accounts` section. - - ![2021-01-06-11-55-22](https://user-images.githubusercontent.com/49413772/103782988-ebc52300-5016-11eb-8a29-dd540c21e11c.gif) - - If the option is turned off, the `users.setStatus` endpoint is also restricted from users trying to change their status to `Invisible`, throwing the following error: - ```json - { - "success": false, - "error": "Invisible status is disabled [error-not-allowed]", - "stack": "Error: Invisible status is disabled [error-not-allowed]\n at DDPCommon.MethodInvocation. (app/api/server/v1/users.js:425:13)\n at packages/dispatch_run-as-user.js:211:14\n at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1234:12)\n at Object.Meteor.runAsUser (packages/dispatch_run-as-user.js:210:33)\n at Object.post (app/api/server/v1/users.js:415:10)\n at app/api/server/api.js:394:82\n at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1234:12)\n at Object._internalRouteActionHandler [as action] (app/api/server/api.js:394:39)\n at Route.share.Route.Route._callEndpoint (packages/nimble_restivus/lib/route.coffee:150:32)\n at packages/nimble_restivus/lib/route.coffee:59:33\n at packages/simple_json-routes.js:98:9", - "errorType": "error-not-allowed", - "details": { - "method": "users.setStatus" - } - } + Add an `admin` option to allow/disallow the `Invisible` status option from all users. This option is available in the `Accounts` section. + + ![2021-01-06-11-55-22](https://user-images.githubusercontent.com/49413772/103782988-ebc52300-5016-11eb-8a29-dd540c21e11c.gif) + + If the option is turned off, the `users.setStatus` endpoint is also restricted from users trying to change their status to `Invisible`, throwing the following error: + ```json + { + "success": false, + "error": "Invisible status is disabled [error-not-allowed]", + "stack": "Error: Invisible status is disabled [error-not-allowed]\n at DDPCommon.MethodInvocation. (app/api/server/v1/users.js:425:13)\n at packages/dispatch_run-as-user.js:211:14\n at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1234:12)\n at Object.Meteor.runAsUser (packages/dispatch_run-as-user.js:210:33)\n at Object.post (app/api/server/v1/users.js:415:10)\n at app/api/server/api.js:394:82\n at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1234:12)\n at Object._internalRouteActionHandler [as action] (app/api/server/api.js:394:39)\n at Route.share.Route.Route._callEndpoint (packages/nimble_restivus/lib/route.coffee:150:32)\n at packages/nimble_restivus/lib/route.coffee:59:33\n at packages/simple_json-routes.js:98:9", + "errorType": "error-not-allowed", + "details": { + "method": "users.setStatus" + } + } ``` - Paginated and Filtered selects on new/edit unit ([#22052](https://github.com/RocketChat/Rocket.Chat/pull/22052) by [@rafaelblink](https://github.com/rafaelblink)) - REQUIRES https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/447 - - Adds infinite scrolling selects to the units edit/create with the ability to be filtered by text as well - - ![Screen Shot 2021-05-17 at 9 24 19 AM](https://user-images.githubusercontent.com/20868078/118487999-abc32a80-b6f1-11eb-8d58-d031111ea0fb.png) - + REQUIRES https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/447 + + Adds infinite scrolling selects to the units edit/create with the ability to be filtered by text as well + + ![Screen Shot 2021-05-17 at 9 24 19 AM](https://user-images.githubusercontent.com/20868078/118487999-abc32a80-b6f1-11eb-8d58-d031111ea0fb.png) + This Affects the monitors and departments inputs - Remove exif metadata from uploaded files ([#22044](https://github.com/RocketChat/Rocket.Chat/pull/22044)) @@ -6660,17 +10646,13 @@ - Inconsistent and misleading 2FA settings ([#22042](https://github.com/RocketChat/Rocket.Chat/pull/22042) by [@lucassartor](https://github.com/lucassartor)) - Currently, there are some inconsistencies and incorrect behaviors on the 2FA settings, such as: - - - - When disabling the TOTP 2FA, all 2FA are disabled; - - - There are no option to disable only the TOTP 2FA; - - - If 2FA are disabled, the other settings aren't blocked (the e-mail 2FA setting, for example); - - - It lacks some labels to warn the user of some specific 2FA options. - + Currently, there are some inconsistencies and incorrect behaviors on the 2FA settings, such as: + + - When disabling the TOTP 2FA, all 2FA are disabled; + - There are no option to disable only the TOTP 2FA; + - If 2FA are disabled, the other settings aren't blocked (the e-mail 2FA setting, for example); + - It lacks some labels to warn the user of some specific 2FA options. + This PR looks to fix those issues. - LDAP port setting input type to allow only numbers ([#21912](https://github.com/RocketChat/Rocket.Chat/pull/21912) by [@Deepak-learner](https://github.com/Deepak-learner)) @@ -6692,16 +10674,16 @@ - **APPS:** Scheduler duplicating recurrent tasks after server restart ([#21866](https://github.com/RocketChat/Rocket.Chat/pull/21866)) - Reintroduces the old method for creating recurring tasks in the apps' scheduler bridge to ensure tasks won't be duplicated. - - By introducing the [`skipImmediate` property option](https://github.com/RocketChat/Rocket.Chat/pull/21353) at the [`scheduleRecurring`](https://github.com/RocketChat/Rocket.Chat/blob/f8171f464ed8a7487795651767695fb33a1c709e/app/apps/server/bridges/scheduler.js#L119) method, the `every` method from _agenda.js_, which ensured no duplicates were created, was removed in favor of a more manual procedure. The new procedure was not taking into account the management of duplicates and as a result multiple copies of the same task could be created and they would get executed at the same time. - + Reintroduces the old method for creating recurring tasks in the apps' scheduler bridge to ensure tasks won't be duplicated. + + By introducing the [`skipImmediate` property option](https://github.com/RocketChat/Rocket.Chat/pull/21353) at the [`scheduleRecurring`](https://github.com/RocketChat/Rocket.Chat/blob/f8171f464ed8a7487795651767695fb33a1c709e/app/apps/server/bridges/scheduler.js#L119) method, the `every` method from _agenda.js_, which ensured no duplicates were created, was removed in favor of a more manual procedure. The new procedure was not taking into account the management of duplicates and as a result multiple copies of the same task could be created and they would get executed at the same time. + In the case of server restarts, every time this event happened and the app had the `startupSetting` configured to use _recurring tasks_, they would get recreated the same number of times. In the case of a server that restarts frequently (_n_ times), there would be the same (_n_) number of tasks duplicated (and running) in the system. - **ENTERPRISE:** Omnichannel Monitors can't forward chats to departments that they are not supervising ([#22128](https://github.com/RocketChat/Rocket.Chat/pull/22128)) - Currently, Omnichannel Monitors just can't forward chats to a department that is part of a `Business Unit` they're not supervising. This issue is causing critical problems on customer operations since this behaviour is not by design. - The reason this issue is taking place is that, by design, Monitors just have access to departments related to the `Business Units` they're monitoring, but this restriction is designed only for Omnichannel management areas, which means in case the monitor is, also, an agent, they're supposed to be able to forward a chat to any available departments regardless the `Business Units` it's associated with. + Currently, Omnichannel Monitors just can't forward chats to a department that is part of a `Business Unit` they're not supervising. This issue is causing critical problems on customer operations since this behaviour is not by design. + The reason this issue is taking place is that, by design, Monitors just have access to departments related to the `Business Units` they're monitoring, but this restriction is designed only for Omnichannel management areas, which means in case the monitor is, also, an agent, they're supposed to be able to forward a chat to any available departments regardless the `Business Units` it's associated with. So, initially, the restriction was implemented on the `Department Model` and, now, we're implementing the logic properly and introducing a new parameter to department endpoints, so the client will define which type of departments it needs. - **ENTERPRISE:** Omnichannel Monitors can't forward chats to departments that they are not supervising ([#22142](https://github.com/RocketChat/Rocket.Chat/pull/22142)) @@ -6740,18 +10722,18 @@ - Correcting a the wrong Archived label in edit room ([#21717](https://github.com/RocketChat/Rocket.Chat/pull/21717) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - ![image](https://user-images.githubusercontent.com/45966964/116584997-3cd78a80-a918-11eb-81fa-8a7eb5318ae9.png) - + ![image](https://user-images.githubusercontent.com/45966964/116584997-3cd78a80-a918-11eb-81fa-8a7eb5318ae9.png) + A label exists for Archived, and it has not been used. So I replaced it with the existing one. the label 'Archived' does not exist. - Custom OAuth not being completely deleted ([#21637](https://github.com/RocketChat/Rocket.Chat/pull/21637) by [@siva2204](https://github.com/siva2204)) - Directory Table's Sort Function ([#21921](https://github.com/RocketChat/Rocket.Chat/pull/21921)) - ### TableRow Margin Issue: - ![image](https://user-images.githubusercontent.com/27704687/116907348-d6a07f80-ac17-11eb-9411-edfe0906bfe1.png) - - ### Table Sort Action Issue: + ### TableRow Margin Issue: + ![image](https://user-images.githubusercontent.com/27704687/116907348-d6a07f80-ac17-11eb-9411-edfe0906bfe1.png) + + ### Table Sort Action Issue: ![directory](https://user-images.githubusercontent.com/27704687/116907441-f20b8a80-ac17-11eb-8790-bfce19e89a67.gif) - Discussion names showing a random value ([#22172](https://github.com/RocketChat/Rocket.Chat/pull/22172)) @@ -6762,54 +10744,54 @@ - Emails being sent with HTML entities getting escaped multiple times ([#21994](https://github.com/RocketChat/Rocket.Chat/pull/21994) by [@bhavayAnand9](https://github.com/bhavayAnand9)) - fixes an issue where if password contains special HTML character like &, in the email it would end up something like `&` - - - password was going through multiple escapeHTML function calls - `secure&123 => secure&123 => secure&123 + fixes an issue where if password contains special HTML character like &, in the email it would end up something like `&` + + + password was going through multiple escapeHTML function calls + `secure&123 => secure&123 => secure&123 ` - Error when you look at the members list of a room in which you are not a member ([#21952](https://github.com/RocketChat/Rocket.Chat/pull/21952) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Before, when you look at the members of a room in which you are not a member the app crashed, i corrected this problem. - Indeed, there was a check on each currentSubscription. to see if it was not undefined except on currentSubscription.blocker - + Before, when you look at the members of a room in which you are not a member the app crashed, i corrected this problem. + Indeed, there was a check on each currentSubscription. to see if it was not undefined except on currentSubscription.blocker + https://user-images.githubusercontent.com/45966964/117087470-d3101400-ad4f-11eb-8f44-0ebca830a4d8.mp4 - errors when viewing a room that you're not subscribed to ([#21984](https://github.com/RocketChat/Rocket.Chat/pull/21984)) - Files list will not show deleted files. ([#21732](https://github.com/RocketChat/Rocket.Chat/pull/21732) by [@Darshilp326](https://github.com/Darshilp326)) - When you delete files from the header option, deleted files will not be shown. - + When you delete files from the header option, deleted files will not be shown. + https://user-images.githubusercontent.com/55157259/115730786-38552400-a3a4-11eb-9684-7f510920db66.mp4 - Fixed the fact that when a team was deleted, not all channels were unlinked from the team ([#21942](https://github.com/RocketChat/Rocket.Chat/pull/21942) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Fixed the fact that when a team was deleted, not all channels were unlinked from the team. Only the first room of the rooms list was unlinked. - - After the fix, there is nos more errors: - - + Fixed the fact that when a team was deleted, not all channels were unlinked from the team. Only the first room of the rooms list was unlinked. + + After the fix, there is nos more errors: + + https://user-images.githubusercontent.com/45966964/117055182-2a47c180-ad1b-11eb-806f-07fb3fa7ec12.mp4 - Fixing Jitsi call ended Issue. ([#21808](https://github.com/RocketChat/Rocket.Chat/pull/21808)) - The new rewrite in react of contextual call component broke the Jitsi "click to join" messages. The issue being after 10 seconds of initiating the call, the message "click to join" always returned "Call Ended" even if the call was still going on. - This was due to the fact that after closing the contextual bar, the react component gets unmounted and we are not able to keep track of ongoing call and increase jitsi room timeout. - - This PR solves this issue by using the setInterval methods on component will unmount. When the call component unmounts, we keep on checking the state of jitsi call and based on conditions increase the jitsi room timeout. After the call is ended all setInterval calls are closed. - + The new rewrite in react of contextual call component broke the Jitsi "click to join" messages. The issue being after 10 seconds of initiating the call, the message "click to join" always returned "Call Ended" even if the call was still going on. + This was due to the fact that after closing the contextual bar, the react component gets unmounted and we are not able to keep track of ongoing call and increase jitsi room timeout. + + This PR solves this issue by using the setInterval methods on component will unmount. When the call component unmounts, we keep on checking the state of jitsi call and based on conditions increase the jitsi room timeout. After the call is ended all setInterval calls are closed. + This PR also removes the implementation of HEARTBEAT events of JitsiBridge. This is because this is no longer needed and all logic is being taken care of by the unmount function. - Handle NPS errors instead of throwing them ([#21945](https://github.com/RocketChat/Rocket.Chat/pull/21945)) - Header Tag Visual Issues ([#21991](https://github.com/RocketChat/Rocket.Chat/pull/21991)) - ### Normal - ![image](https://user-images.githubusercontent.com/27704687/117504793-69635600-af59-11eb-8b79-9d8f631490ee.png) - - ### Hover + ### Normal + ![image](https://user-images.githubusercontent.com/27704687/117504793-69635600-af59-11eb-8b79-9d8f631490ee.png) + + ### Hover ![image](https://user-images.githubusercontent.com/27704687/117504934-97489a80-af59-11eb-87c3-0a62731e9ce3.png) - Horizontal scrollbar not showing on tables ([#21852](https://github.com/RocketChat/Rocket.Chat/pull/21852)) @@ -6818,17 +10800,17 @@ - iFrame size on embedded videos ([#21992](https://github.com/RocketChat/Rocket.Chat/pull/21992)) - ### Before - ![image](https://user-images.githubusercontent.com/27704687/117508802-8bf86d80-af5f-11eb-9eb8-29e55b73eac5.png) - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/27704687/117508802-8bf86d80-af5f-11eb-9eb8-29e55b73eac5.png) + + ### After ![image](https://user-images.githubusercontent.com/27704687/117508870-a4688800-af5f-11eb-9176-7f24de5fc424.png) - Incorrect error message when opening channel in anonymous read ([#22066](https://github.com/RocketChat/Rocket.Chat/pull/22066) by [@lucassartor](https://github.com/lucassartor)) - Every time you open a public channel with threads in it when using anonymous read an `Incorrect User` error will be thrown. - This is an incorrect behaviour as everything that is public should be valid for an anonymous user. - + Every time you open a public channel with threads in it when using anonymous read an `Incorrect User` error will be thrown. + This is an incorrect behaviour as everything that is public should be valid for an anonymous user. + Some files are adapted to that and have already removed this kind of incorrect error, but there are some that need some fix, this PR aims to do that. - Incorrect Team's Info spacing ([#22021](https://github.com/RocketChat/Rocket.Chat/pull/22021)) @@ -6841,21 +10823,19 @@ - Make the FR translation consistent with the 'room' translation + typos ([#21913](https://github.com/RocketChat/Rocket.Chat/pull/21913) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - In the FR translation files, there were two terms that were used to refer to **'room'**: - - - 'salon' (149 times used) - - ![image](https://user-images.githubusercontent.com/45966964/116829860-ac62a980-aba6-11eb-8212-e6f15ed0af82.png) - - - - 'salle' (46 times used) - - ![image](https://user-images.githubusercontent.com/45966964/116829871-be444c80-aba6-11eb-9b42-e213fee6586a.png) - - The problem is that both were used in the same context and sometimes even in the same option list. - However, since 'salon' is a better translation and was also in the majority, I used the translation 'salon' wherever 'salle' was marked. - - For example: + In the FR translation files, there were two terms that were used to refer to **'room'**: + - 'salon' (149 times used) + + ![image](https://user-images.githubusercontent.com/45966964/116829860-ac62a980-aba6-11eb-8212-e6f15ed0af82.png) + + - 'salle' (46 times used) + + ![image](https://user-images.githubusercontent.com/45966964/116829871-be444c80-aba6-11eb-9b42-e213fee6586a.png) + + The problem is that both were used in the same context and sometimes even in the same option list. + However, since 'salon' is a better translation and was also in the majority, I used the translation 'salon' wherever 'salle' was marked. + + For example: ![image](https://user-images.githubusercontent.com/45966964/116830523-1da45b80-abab-11eb-81f8-5225d51cecc6.png) - Maximum 25 channels can be loaded in the teams' channels list ([#21708](https://github.com/RocketChat/Rocket.Chat/pull/21708) by [@Jeanstaquet](https://github.com/Jeanstaquet)) @@ -6870,8 +10850,8 @@ - No warning message is sent when user is removed from a team's main channel ([#21949](https://github.com/RocketChat/Rocket.Chat/pull/21949)) - - Send a warning message to a team's main channel when a user is removed from the team; - - Trigger events while removing a user from a team's main channel; + - Send a warning message to a team's main channel when a user is removed from the team; + - Trigger events while removing a user from a team's main channel; - Fix `usersCount` field in the team's main room when a user is removed from the team (`usersCount` is now decreased by 1). - Not possible accept video call if "Hide right sidebar with click" is enabled ([#22175](https://github.com/RocketChat/Rocket.Chat/pull/22175)) @@ -6892,14 +10872,14 @@ - Prevent the userInfo tab to return 'User not found' each time if a certain member of a DM group has been deleted ([#21970](https://github.com/RocketChat/Rocket.Chat/pull/21970) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Prevent the userInfo tab to return 'User not found' if a member of a DM group has been deleted. - This happens if the user that has been deleted is the one originally displayed on the userInfo tab in a DM group with >2 users. - + Prevent the userInfo tab to return 'User not found' if a member of a DM group has been deleted. + This happens if the user that has been deleted is the one originally displayed on the userInfo tab in a DM group with >2 users. + https://user-images.githubusercontent.com/45966964/117221081-db785580-ae08-11eb-9b33-2314a99eb037.mp4 - Prune messages not cleaning up unread threads ([#21326](https://github.com/RocketChat/Rocket.Chat/pull/21326) by [@renancleyson-dev](https://github.com/renancleyson-dev)) - Fixes permanent unread messages when admin prune at least two different thread messages in the room that were unread by some user. + Fixes permanent unread messages when admin prune at least two different thread messages in the room that were unread by some user. ![screencapture-localhost-3000-channel-general-thread-2021-03-26-13_17_16](https://user-images.githubusercontent.com/43624243/112678973-62b9cd00-8e4a-11eb-9af9-56f17cc66baf.png) - Redirect on remove user from channel by user profile tab ([#21951](https://github.com/RocketChat/Rocket.Chat/pull/21951)) @@ -6910,8 +10890,8 @@ - Removed fields from User Info for which the user doesn't have permissions. ([#20923](https://github.com/RocketChat/Rocket.Chat/pull/20923) by [@Darshilp326](https://github.com/Darshilp326)) - Removed LastLogin, CreatedAt and Roles for users who don't have permission. - + Removed LastLogin, CreatedAt and Roles for users who don't have permission. + https://user-images.githubusercontent.com/55157259/109381351-f2c62e80-78ff-11eb-9289-e11072bf62f8.mp4 - Replace `query` param by `name`, `username` and `status` on the `teams.members` endpoint ([#21539](https://github.com/RocketChat/Rocket.Chat/pull/21539)) @@ -6924,39 +10904,39 @@ - Unable to edit a 'direct' room setting in the admin due to the room name ([#21636](https://github.com/RocketChat/Rocket.Chat/pull/21636) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - When you are in the admin and want to change a room 'd' setting, it doesn't work because it takes into account the name that is set automatically and therefore tries to save that name. Since the name is not valid and should not be registered, we cannot change the setting for the 'd' room. - I made sure that when you want to change a setting in a 'd' room, that you don't take the name into account - - - https://user-images.githubusercontent.com/45966964/115150919-cd85af00-a06a-11eb-9667-ef3dcfc5adb6.mp4 - - + When you are in the admin and want to change a room 'd' setting, it doesn't work because it takes into account the name that is set automatically and therefore tries to save that name. Since the name is not valid and should not be registered, we cannot change the setting for the 'd' room. + I made sure that when you want to change a setting in a 'd' room, that you don't take the name into account + + + https://user-images.githubusercontent.com/45966964/115150919-cd85af00-a06a-11eb-9667-ef3dcfc5adb6.mp4 + + Behind the scene, the name is not saved - Unable to edit a user who does not have an email via the admin or via the user's profile ([#21626](https://github.com/RocketChat/Rocket.Chat/pull/21626) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - If a user does not have an email address, they cannot change it via their profile or via the admin. I fixed this issue. I have created several profiles and there was one that didn't have an email, I don't know how I did it, I am working on it. I had not modified the db to delete his email, hence the fix - - in admin - - https://user-images.githubusercontent.com/45966964/115112617-9b9b1c80-9f86-11eb-8e3a-950c3c1a1746.mp4 - - - - in the user profile - + If a user does not have an email address, they cannot change it via their profile or via the admin. I fixed this issue. I have created several profiles and there was one that didn't have an email, I don't know how I did it, I am working on it. I had not modified the db to delete his email, hence the fix + + in admin + + https://user-images.githubusercontent.com/45966964/115112617-9b9b1c80-9f86-11eb-8e3a-950c3c1a1746.mp4 + + + + in the user profile + https://user-images.githubusercontent.com/45966964/115112620-a0f86700-9f86-11eb-97b1-56eaba42216b.mp4 - Unable to get channels, sort by most recent message ([#21701](https://github.com/RocketChat/Rocket.Chat/pull/21701) by [@sumukhah](https://github.com/sumukhah)) - Unable to update app manually ([#21215](https://github.com/RocketChat/Rocket.Chat/pull/21215)) - It allows for update of apps using a zip file. - - When installing apps using the zip file, either by url or the file form, if the app was already installed, an error would be thrown stating the condition and forbidding the installation. Now, when sending a zip file of an app that is already installed, the user is presented with the following modal: - - ![2021-04-30-113936_627x235_scrot](https://user-images.githubusercontent.com/733282/116711383-2cbbbb80-a9a9-11eb-8c77-22d6802cb9f5.png) - + It allows for update of apps using a zip file. + + When installing apps using the zip file, either by url or the file form, if the app was already installed, an error would be thrown stating the condition and forbidding the installation. Now, when sending a zip file of an app that is already installed, the user is presented with the following modal: + + ![2021-04-30-113936_627x235_scrot](https://user-images.githubusercontent.com/733282/116711383-2cbbbb80-a9a9-11eb-8c77-22d6802cb9f5.png) + If the app also requires permissions to be reviewed, the modal that handles permission reviews will be shown after this one is accepted. - Unpin message reactivity ([#22029](https://github.com/RocketChat/Rocket.Chat/pull/22029)) @@ -6967,20 +10947,20 @@ - User Impersonation through sendMessage API ([#20391](https://github.com/RocketChat/Rocket.Chat/pull/20391) by [@lucassartor](https://github.com/lucassartor)) - Create a new permission: `message-impersonate`. For new installs only bot role will have the permission and for updating installs the permission will also be given to user role, so it won't break running deployments. - - If a message is being sent with `avatar` or `alias` properties, it validates if the sender has the `message-impersonate` permission, if not, an error is throwed: - ```json - { - "success": false, - "error": "Not enough permission", - "stack": "Error: Not enough permission\n ..." - } + Create a new permission: `message-impersonate`. For new installs only bot role will have the permission and for updating installs the permission will also be given to user role, so it won't break running deployments. + + If a message is being sent with `avatar` or `alias` properties, it validates if the sender has the `message-impersonate` permission, if not, an error is throwed: + ```json + { + "success": false, + "error": "Not enough permission", + "stack": "Error: Not enough permission\n ..." + } ``` - Visibility of burger menu on certain width ([#20736](https://github.com/RocketChat/Rocket.Chat/pull/20736)) - Burger was not visible on a certain width, specifically between 600 to 780. if width is more than 780px sidebar is shown, if less than 600 then burger icon was shown. But it wasn't shown between 600px to 780 px. + Burger was not visible on a certain width, specifically between 600 to 780. if width is more than 780px sidebar is shown, if less than 600 then burger icon was shown. But it wasn't shown between 600px to 780 px. It was because for showing burger icon we were only checking for `isMobile` which is lenght only less than 600. So i added one more check for condition if length is less than 780 px. - When closing chats a comment is always required ([#21947](https://github.com/RocketChat/Rocket.Chat/pull/21947)) @@ -6995,8 +10975,8 @@ - Wrong icon on "Move to team" option in the channel info actions ([#21944](https://github.com/RocketChat/Rocket.Chat/pull/21944)) - ![image](https://user-images.githubusercontent.com/40830821/117061659-d9bf6c80-acf8-11eb-8e29-be47e702dedd.png) - + ![image](https://user-images.githubusercontent.com/40830821/117061659-d9bf6c80-acf8-11eb-8e29-be47e702dedd.png) + Depends on https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/444
@@ -7013,10 +10993,8 @@ - Add two more test cases to the slash-command test suite ([#21317](https://github.com/RocketChat/Rocket.Chat/pull/21317) by [@EduardoPicolo](https://github.com/EduardoPicolo)) - Added two more test cases to the slash-command test suite: - - - 'should return an error when the command does not exist''; - + Added two more test cases to the slash-command test suite: + - 'should return an error when the command does not exist''; - 'should return an error when no command is provided'; - Bump actions/stale from v3.0.8 to v3.0.18 ([#21877](https://github.com/RocketChat/Rocket.Chat/pull/21877) by [@dependabot[bot]](https://github.com/dependabot[bot])) @@ -7051,9 +11029,9 @@ - i18n: Add missing translation string in account preference ([#21448](https://github.com/RocketChat/Rocket.Chat/pull/21448) by [@sumukhah](https://github.com/sumukhah)) - "Test Desktop Notifications" was missing in translation, Added to the file. - Screenshot 2021-04-05 at 3 58 01 PM - + "Test Desktop Notifications" was missing in translation, Added to the file. + Screenshot 2021-04-05 at 3 58 01 PM + Screenshot 2021-04-05 at 3 58 32 PM - i18n: Correct a typo in German ([#21711](https://github.com/RocketChat/Rocket.Chat/pull/21711) by [@Jeanstaquet](https://github.com/Jeanstaquet)) @@ -7080,10 +11058,10 @@ - Regression: discussions display on sidebar ([#22157](https://github.com/RocketChat/Rocket.Chat/pull/22157)) - ### group by type active - ![image](https://user-images.githubusercontent.com/27704687/119741996-37a92500-be5d-11eb-8b36-4067a7a229f1.png) - - ### group by type inactive + ### group by type active + ![image](https://user-images.githubusercontent.com/27704687/119741996-37a92500-be5d-11eb-8b36-4067a7a229f1.png) + + ### group by type inactive ![image](https://user-images.githubusercontent.com/27704687/119742054-56a7b700-be5d-11eb-8810-e31d4216f573.png) - regression: fix departments with empty ancestors not being returned ([#22068](https://github.com/RocketChat/Rocket.Chat/pull/22068)) @@ -7094,8 +11072,8 @@ - regression: Fix Users list in the Administration ([#22034](https://github.com/RocketChat/Rocket.Chat/pull/22034) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - The app crashed if no custom fields for user profiles have been created by the admin. I fixed this issue. This bug was introduced by a recent commit. - + The app crashed if no custom fields for user profiles have been created by the admin. I fixed this issue. This bug was introduced by a recent commit. + https://user-images.githubusercontent.com/45966964/118210838-5b3a9b80-b46b-11eb-9fe5-5b813848190c.mp4 - Regression: Improve migration 225 ([#22099](https://github.com/RocketChat/Rocket.Chat/pull/22099)) @@ -7112,7 +11090,7 @@ - Regression: not allowed to edit roles due to a new verification ([#22159](https://github.com/RocketChat/Rocket.Chat/pull/22159)) - introduced by https://github.com/RocketChat/Rocket.Chat/pull/21905 + introduced by https://github.com/RocketChat/Rocket.Chat/pull/21905 ![Peek 2021-05-26 22-21](https://user-images.githubusercontent.com/27704687/119750970-b9567e00-be70-11eb-9d52-04c8595950df.gif) - regression: Select Team Modal margin ([#22030](https://github.com/RocketChat/Rocket.Chat/pull/22030)) @@ -7123,10 +11101,10 @@ - Regression: Visual issue on sort list item ([#22158](https://github.com/RocketChat/Rocket.Chat/pull/22158)) - ### before - ![image](https://user-images.githubusercontent.com/27704687/119743703-d84d1400-be60-11eb-97cc-c8256b2c8b07.png) - - ### after + ### before + ![image](https://user-images.githubusercontent.com/27704687/119743703-d84d1400-be60-11eb-97cc-c8256b2c8b07.png) + + ### after ![image](https://user-images.githubusercontent.com/27704687/119743638-b18edd80-be60-11eb-828d-22cc5e1b2f5b.png) - Release 3.14.2 ([#22135](https://github.com/RocketChat/Rocket.Chat/pull/22135)) @@ -7307,17 +11285,17 @@ - MongoDB: `3.4, 3.6, 4.0` - Apps-Engine: `1.25.0` -### 🎉 New features - - -- Paginated and Filtered selects on new/edit unit ([#22052](https://github.com/RocketChat/Rocket.Chat/pull/22052) by [@rafaelblink](https://github.com/rafaelblink)) - - REQUIRES https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/447 +### 🎉 New features - Adds infinite scrolling selects to the units edit/create with the ability to be filtered by text as well - ![Screen Shot 2021-05-17 at 9 24 19 AM](https://user-images.githubusercontent.com/20868078/118487999-abc32a80-b6f1-11eb-8d58-d031111ea0fb.png) +- Paginated and Filtered selects on new/edit unit ([#22052](https://github.com/RocketChat/Rocket.Chat/pull/22052) by [@rafaelblink](https://github.com/rafaelblink)) + REQUIRES https://github.com/RocketChat/Rocket.Chat.Fuselage/pull/447 + + Adds infinite scrolling selects to the units edit/create with the ability to be filtered by text as well + + ![Screen Shot 2021-05-17 at 9 24 19 AM](https://user-images.githubusercontent.com/20868078/118487999-abc32a80-b6f1-11eb-8d58-d031111ea0fb.png) + This Affects the monitors and departments inputs ### 🚀 Improvements @@ -7393,24 +11371,18 @@ - New set of rules for client code ([#21318](https://github.com/RocketChat/Rocket.Chat/pull/21318)) - This _small_ PR does the following: - - - - Now **React** is the web client's first-class citizen, being **loaded before Blaze**. Thus, `BlazeLayout` calls render templates inside of a React component (`BlazeLayoutWrapper`); - - - Main client startup code, including polyfills, is written in **TypeScript**; - - - At the moment, routes are treated as regular startup code; it's expected that `FlowRouter` will be deprecated in favor of a new routing library; - - - **React** was updated to major version **17**, deprecating the usage of `React` as namespace (e.g. use `memo()` instead of `React.memo()`); - - - The `client/` and `ee/client/` directory are linted with a **custom ESLint configuration** that includes: - - **Prettier**; - - `react-hooks/*` rules for TypeScript files; - - `react/no-multi-comp`, enforcing the rule of **one single React component per module**; - - `react/display-name`, which enforces that **React components must have a name for debugging**; - - `import/named`, avoiding broken named imports. - + This _small_ PR does the following: + + - Now **React** is the web client's first-class citizen, being **loaded before Blaze**. Thus, `BlazeLayout` calls render templates inside of a React component (`BlazeLayoutWrapper`); + - Main client startup code, including polyfills, is written in **TypeScript**; + - At the moment, routes are treated as regular startup code; it's expected that `FlowRouter` will be deprecated in favor of a new routing library; + - **React** was updated to major version **17**, deprecating the usage of `React` as namespace (e.g. use `memo()` instead of `React.memo()`); + - The `client/` and `ee/client/` directory are linted with a **custom ESLint configuration** that includes: + - **Prettier**; + - `react-hooks/*` rules for TypeScript files; + - `react/no-multi-comp`, enforcing the rule of **one single React component per module**; + - `react/display-name`, which enforces that **React components must have a name for debugging**; + - `import/named`, avoiding broken named imports. - A bunch of components were refactored to match the new ESLint rules. - On Hold system messages ([#21360](https://github.com/RocketChat/Rocket.Chat/pull/21360) by [@rafaelblink](https://github.com/rafaelblink)) @@ -7419,15 +11391,12 @@ - Password history ([#21607](https://github.com/RocketChat/Rocket.Chat/pull/21607)) - - Store each user's previously used passwords in a `passwordHistory` field (in the `users` record); - - - Users' previously used passwords are stored in their `passwordHistory` even when the setting is disabled; - - - Add "Password History" setting -- when enabled, it blocks users from reusing their most recent passwords; - - - Convert `comparePassword` file to TypeScript. - - ![Password_Change](https://user-images.githubusercontent.com/36537004/115035168-ac726200-9ea2-11eb-93c6-fc8182ba5f3f.png) + - Store each user's previously used passwords in a `passwordHistory` field (in the `users` record); + - Users' previously used passwords are stored in their `passwordHistory` even when the setting is disabled; + - Add "Password History" setting -- when enabled, it blocks users from reusing their most recent passwords; + - Convert `comparePassword` file to TypeScript. + + ![Password_Change](https://user-images.githubusercontent.com/36537004/115035168-ac726200-9ea2-11eb-93c6-fc8182ba5f3f.png) ![Password_History](https://user-images.githubusercontent.com/36537004/115035175-ad0af880-9ea2-11eb-9f40-94c6327a9854.png) - REST endpoint `teams.update` ([#21134](https://github.com/RocketChat/Rocket.Chat/pull/21134) by [@g-thome](https://github.com/g-thome)) @@ -7445,18 +11414,14 @@ - Add error messages to the creation of channels or usernames containing reserved words ([#21016](https://github.com/RocketChat/Rocket.Chat/pull/21016)) - Display error messages when the user attempts to create or edit users' or channels' names with any of the following words (**case-insensitive**): - - - admin; - - - administrator; - - - system; - - - user. - ![create-channel](https://user-images.githubusercontent.com/36537004/110132223-b421ef80-7da9-11eb-82bc-f0d4e1df967f.png) - ![register-username](https://user-images.githubusercontent.com/36537004/110132234-b71ce000-7da9-11eb-904e-580233625951.png) - ![change-channel](https://user-images.githubusercontent.com/36537004/110143057-96f31e00-7db5-11eb-994a-39ae9e63392e.png) + Display error messages when the user attempts to create or edit users' or channels' names with any of the following words (**case-insensitive**): + - admin; + - administrator; + - system; + - user. + ![create-channel](https://user-images.githubusercontent.com/36537004/110132223-b421ef80-7da9-11eb-82bc-f0d4e1df967f.png) + ![register-username](https://user-images.githubusercontent.com/36537004/110132234-b71ce000-7da9-11eb-904e-580233625951.png) + ![change-channel](https://user-images.githubusercontent.com/36537004/110143057-96f31e00-7db5-11eb-994a-39ae9e63392e.png) ![change-username](https://user-images.githubusercontent.com/36537004/110143065-98244b00-7db5-11eb-9d13-afc5dc9866de.png) - add permission check when adding a channel to a team ([#21689](https://github.com/RocketChat/Rocket.Chat/pull/21689) by [@g-thome](https://github.com/g-thome)) @@ -7481,8 +11446,7 @@ - Resize custom emojis on upload instead of saving at max res ([#21593](https://github.com/RocketChat/Rocket.Chat/pull/21593)) - - Create new MediaService (ideally, should be in charge of all media-related operations) - + - Create new MediaService (ideally, should be in charge of all media-related operations) - Resize emojis to 128x128 ### 🐛 Bug fixes @@ -7502,25 +11466,25 @@ - Allows more than 25 discussions/files to be loaded in the contextualbar ([#21511](https://github.com/RocketChat/Rocket.Chat/pull/21511) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - In some places, you could not load more than 25 threads/discussions/files on the screen when searching the lists in the contextualbar. - Threads & list are numbered for a better view of the solution - - + In some places, you could not load more than 25 threads/discussions/files on the screen when searching the lists in the contextualbar. + Threads & list are numbered for a better view of the solution + + https://user-images.githubusercontent.com/45966964/114222225-93335800-996e-11eb-833f-568e83129aae.mp4 - Allows more than 25 threads to be loaded, fixes #21507 ([#21508](https://github.com/RocketChat/Rocket.Chat/pull/21508) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Allows to display more than 25 users maximum in the users list ([#21518](https://github.com/RocketChat/Rocket.Chat/pull/21518) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - Now when you scroll to the bottom of the users list, it shows more users. Before the fix, the limit for the query for loadMore was calculated so that no additional users could be loaded. - - Before - - https://user-images.githubusercontent.com/45966964/114249739-baece500-999b-11eb-9bb0-3a5bcee18ad8.mp4 - - After - - + Now when you scroll to the bottom of the users list, it shows more users. Before the fix, the limit for the query for loadMore was calculated so that no additional users could be loaded. + + Before + + https://user-images.githubusercontent.com/45966964/114249739-baece500-999b-11eb-9bb0-3a5bcee18ad8.mp4 + + After + + https://user-images.githubusercontent.com/45966964/114249895-364e9680-999c-11eb-985c-47aedc763488.mp4 - App installation from marketplace not correctly displaying the permissions ([#21470](https://github.com/RocketChat/Rocket.Chat/pull/21470)) @@ -7587,19 +11551,19 @@ - Margins on contextual bar information ([#21457](https://github.com/RocketChat/Rocket.Chat/pull/21457)) - ### Room - **Before** - ![image](https://user-images.githubusercontent.com/27704687/115080812-ba8fa500-9ed9-11eb-9078-3625603bf92b.png) - - **After** - ![image](https://user-images.githubusercontent.com/27704687/115080966-e9a61680-9ed9-11eb-929f-6516c1563e99.png) - - ### Livechat + ### Room + **Before** + ![image](https://user-images.githubusercontent.com/27704687/115080812-ba8fa500-9ed9-11eb-9078-3625603bf92b.png) + + **After** + ![image](https://user-images.githubusercontent.com/27704687/115080966-e9a61680-9ed9-11eb-929f-6516c1563e99.png) + + ### Livechat ![image](https://user-images.githubusercontent.com/27704687/113640101-1859fc80-9651-11eb-88f8-09a899953988.png) - Message Block ordering ([#21464](https://github.com/RocketChat/Rocket.Chat/pull/21464)) - Reactions should come before reply button. + Reactions should come before reply button. ![image](https://user-images.githubusercontent.com/40830821/113748926-6f0e1780-96df-11eb-93a5-ddcfa891413e.png) - Message link null corrupts message rendering ([#21579](https://github.com/RocketChat/Rocket.Chat/pull/21579) by [@g-thome](https://github.com/g-thome)) @@ -7652,19 +11616,15 @@ - Typos/missing elements in the French translation ([#21525](https://github.com/RocketChat/Rocket.Chat/pull/21525) by [@Jeanstaquet](https://github.com/Jeanstaquet)) - - I have corrected some typos in the translation - - - I added a translation for missing words - - - I took the opportunity to correct a mistranslated word - - - Test_Desktop_Notifications was missing in the EN and FR file + - I have corrected some typos in the translation + - I added a translation for missing words + - I took the opportunity to correct a mistranslated word + - Test_Desktop_Notifications was missing in the EN and FR file ![image](https://user-images.githubusercontent.com/45966964/114290186-e7792d80-9a7d-11eb-8164-3b5e72e93703.png) - Updating a message causing URLs to be parsed even within markdown code ([#21489](https://github.com/RocketChat/Rocket.Chat/pull/21489)) - - Fix `updateMessage` to avoid parsing URLs inside markdown - + - Fix `updateMessage` to avoid parsing URLs inside markdown - Honor `parseUrls` property when updating messages - Use async await in TeamChannels delete channel action ([#21534](https://github.com/RocketChat/Rocket.Chat/pull/21534)) @@ -7677,8 +11637,8 @@ - Wrong user in user info ([#21451](https://github.com/RocketChat/Rocket.Chat/pull/21451)) - Fixed some race conditions in admin. - + Fixed some race conditions in admin. + Self DMs used to be created with the userId duplicated. Sometimes rooms can have 2 equal uids, but it's a self DM. Fixed a getter so this isn't a problem anymore.
@@ -7687,30 +11647,22 @@ - Doc: Corrected links to documentation of rocket.chat README.md ([#20478](https://github.com/RocketChat/Rocket.Chat/pull/20478) by [@joshi008](https://github.com/joshi008)) - The link for documentation in the readme was previously https://rocket.chat/docs/ while that was not working and according to the website it was https://docs.rocket.chat/ - The link for deployment methods in readme was corrected from https://rocket.chat/docs/installation/paas-deployments/ to https://docs.rocket.chat/installation/paas-deployments + The link for documentation in the readme was previously https://rocket.chat/docs/ while that was not working and according to the website it was https://docs.rocket.chat/ + The link for deployment methods in readme was corrected from https://rocket.chat/docs/installation/paas-deployments/ to https://docs.rocket.chat/installation/paas-deployments Some more links to the documentations were giving 404 error which hence updated. - [Improve] Remove useless tabbar options from Omnichannel rooms ([#21561](https://github.com/RocketChat/Rocket.Chat/pull/21561) by [@rafaelblink](https://github.com/rafaelblink)) - A React-based replacement for BlazeLayout ([#21527](https://github.com/RocketChat/Rocket.Chat/pull/21527)) - - The Meteor package **`kadira:blaze-layout` was removed**; - - - A **global subscription** for the current application layout (**`appLayout`**) replaces `BlazeLayout` entirely; - - - The **`#react-root` element** is rendered on server-side instead of dynamically injected into the DOM tree; - - - The **"page loading" throbber** is now rendered on the React tree; - - - The **`renderRouteComponent` helper was removed**; - - - Some code run without any criteria on **`main` template** module was moved into **client startup modules**; - - - React portals used to embed Blaze templates have their own subscription (**`blazePortals`**); - - - Some **route components were refactored** to remove a URL path trap originally disabled by `renderRouteComponent`; - + - The Meteor package **`kadira:blaze-layout` was removed**; + - A **global subscription** for the current application layout (**`appLayout`**) replaces `BlazeLayout` entirely; + - The **`#react-root` element** is rendered on server-side instead of dynamically injected into the DOM tree; + - The **"page loading" throbber** is now rendered on the React tree; + - The **`renderRouteComponent` helper was removed**; + - Some code run without any criteria on **`main` template** module was moved into **client startup modules**; + - React portals used to embed Blaze templates have their own subscription (**`blazePortals`**); + - Some **route components were refactored** to remove a URL path trap originally disabled by `renderRouteComponent`; - A new component to embed the DOM nodes generated by **`RoomManager`** was created. - Add ')' after Date and Time in DB migration ([#21519](https://github.com/RocketChat/Rocket.Chat/pull/21519) by [@im-adithya](https://github.com/im-adithya)) @@ -7733,8 +11685,8 @@ - Chore: Meteor update to 2.1.1 ([#21494](https://github.com/RocketChat/Rocket.Chat/pull/21494)) - Basically Node update to version 12.22.1 - + Basically Node update to version 12.22.1 + Meteor change log https://github.com/meteor/meteor/blob/devel/History.md#v211-2021-04-06 - Chore: Remove control character from room model operation ([#21493](https://github.com/RocketChat/Rocket.Chat/pull/21493)) @@ -7743,8 +11695,7 @@ - Fix: Missing module `eventemitter3` for micro services ([#21611](https://github.com/RocketChat/Rocket.Chat/pull/21611)) - - Fix error when running micro services after version 3.12 - + - Fix error when running micro services after version 3.12 - Fix build of docker image version latest for micro services - Language update from LingoHub 🤖 on 2021-04-05Z ([#21446](https://github.com/RocketChat/Rocket.Chat/pull/21446)) @@ -7757,12 +11708,9 @@ - QoL improvements to add channel to team flow ([#21778](https://github.com/RocketChat/Rocket.Chat/pull/21778)) - - Fixed canAccessRoom validation - - - Added e2e tests - - - Removed channels that user cannot add to the team from autocomplete suggestions - + - Fixed canAccessRoom validation + - Added e2e tests + - Removed channels that user cannot add to the team from autocomplete suggestions - Improved error messages - Regression: Bold, italic and strike render (Original markdown) ([#21747](https://github.com/RocketChat/Rocket.Chat/pull/21747)) @@ -7785,10 +11733,10 @@ - Regression: Legacy Banner Position ([#21598](https://github.com/RocketChat/Rocket.Chat/pull/21598)) - ### Before: - ![image](https://user-images.githubusercontent.com/27704687/114961773-dc3c4e00-9e3f-11eb-9a32-e882db3fbfbc.png) - - ### After + ### Before: + ![image](https://user-images.githubusercontent.com/27704687/114961773-dc3c4e00-9e3f-11eb-9a32-e882db3fbfbc.png) + + ### After ![image](https://user-images.githubusercontent.com/27704687/114961673-a6976500-9e3f-11eb-9238-a12870d7db8f.png) - regression: Markdown broken on safari ([#21780](https://github.com/RocketChat/Rocket.Chat/pull/21780)) @@ -7998,62 +11946,56 @@ - **APPS:** New event interfaces for pre/post user leaving a room ([#20917](https://github.com/RocketChat/Rocket.Chat/pull/20917) by [@lucassartor](https://github.com/lucassartor)) - Added events and errors that trigger when a user leaves a room. + Added events and errors that trigger when a user leaves a room. That way it can communicate with the Apps-Engine by the `IPreRoomUserLeave` and `IPostRoomUserLeave` event interfaces. - **Enterprise:** Omnichannel On-Hold Queue ([#20945](https://github.com/RocketChat/Rocket.Chat/pull/20945)) - ### About this feature - This feature has been introduced to deal with Inactive chats. A chat is considered Inactive if an Omnichannel End User (aka Visitor) has not replied back to an agent in some time. These types of inactive chats become very important when an organisation has a limit set for `Max Simultaneous Chats per agent` which is defined by the following setting :point_down: , as more number of Inactive chats would directly affect an agent's productivity. - ![image](https://user-images.githubusercontent.com/34130764/111533003-4d7ad980-878c-11eb-8c1c-2796678a07db.png) - - Before this feature, we only had one option to deal with such Inactive/Abandoned chats - which was to auto close abandoned chats via this setting :point_down: - ![image](https://user-images.githubusercontent.com/34130764/111534353-e65e2480-878d-11eb-82a5-71368064ef45.png) - - however closing a chat isn't a best option for some cases. Let me take an example to explain a scenario - - > An agent is assisting a customer for installing a very huge software which is likely to take more than 20-30 minutes to download. In such scenarios closing a chat isn't the best approach since even after the lengthy download the customer might still need some assist from the agent. - > So basically this chat is going to block the agent's queue until the customer is able to finish his time-consuming download task in which he/she doesn't require any agent's assistance. Due to the `Max Simultaneous Chats per agent` limit, the agent is also not able to use this extra time to help other customer thus affecting his overall productivity. - - **So how does the On-Hold feature solve this problem?** - With the On-Hold feature, an agent is now able to place a chat on-hold. On-Hold chats **don’t count towards the maximum number of concurrent chats** an agent can have. So in our above example, the agent can simply now place the customer on-hold for 20-30 minutes until the customer downloads the software and within this time, the agent can serve other customers - hence increasing the productivity of an agent. - - ---------------------------------------- - ### Working of the new On-Hold feature - - #### How can you place a chat on Hold ? - - A chat can be placed on-hold via 2 means - - 1. Automatically place Abandoned chats On-hold - ![image](https://user-images.githubusercontent.com/34130764/111537074-06431780-8791-11eb-8d23-99f5d9f8ec45.png) - Via this :top: option you can define a timer which will get started when a customer sends a message. If we don't receive any message from the customer within this timer, the timer will get expired and the chat will be considered as Abandoned. - ![image](https://user-images.githubusercontent.com/34130764/111537346-53bf8480-8791-11eb-8dc7-260633b4e98f.png) - The via this :top: setting you can choose to automatically place this abandoned chat On Hold - - 2. Manually place a chat On Hold - As an admin, you can allow an agent to manually place a chat on-hold. To do so, you'll need to turn on this :point_down: setting - ![image](https://user-images.githubusercontent.com/34130764/111537545-97b28980-8791-11eb-86fd-db45b87e9cc1.png) - Now an agent will be able to see a new `On Hold` button within their `Visitor Info Panel` like this :point_down: , provided the agent has sent the last message - ![image](https://user-images.githubusercontent.com/34130764/111537853-f24be580-8791-11eb-9561-d77ba430c625.png) - - #### How can you resume a On Hold chat ? - An On Hold chat can be resumed via 2 means - - - 1. If the Customer sends a message - If the Customer / Omnichannel End User sends a message to the On Hold chat, the On Hold chat will get automatically resumed. - - 2. Manually by agent - An Agent can manually resume the On Hold chat via clicking the `Resume` button in the bottom of a chat room. - ![image](https://user-images.githubusercontent.com/34130764/111538666-f88e9180-8792-11eb-8d14-01453b8e3db0.png) - - #### What would happen if the agent already reached maximum chats, and a On-Hold chat gets resumed ? - Based on how the chat was resumed, there are multiple cases are each case is dealt differently - - - - If an agent manually tries to resume the On Hold chat, he/she will get an error saying `Maximum Simultaneous chat limit reached` - + ### About this feature + This feature has been introduced to deal with Inactive chats. A chat is considered Inactive if an Omnichannel End User (aka Visitor) has not replied back to an agent in some time. These types of inactive chats become very important when an organisation has a limit set for `Max Simultaneous Chats per agent` which is defined by the following setting :point_down: , as more number of Inactive chats would directly affect an agent's productivity. + ![image](https://user-images.githubusercontent.com/34130764/111533003-4d7ad980-878c-11eb-8c1c-2796678a07db.png) + + Before this feature, we only had one option to deal with such Inactive/Abandoned chats - which was to auto close abandoned chats via this setting :point_down: + ![image](https://user-images.githubusercontent.com/34130764/111534353-e65e2480-878d-11eb-82a5-71368064ef45.png) + + however closing a chat isn't a best option for some cases. Let me take an example to explain a scenario + + > An agent is assisting a customer for installing a very huge software which is likely to take more than 20-30 minutes to download. In such scenarios closing a chat isn't the best approach since even after the lengthy download the customer might still need some assist from the agent. + > So basically this chat is going to block the agent's queue until the customer is able to finish his time-consuming download task in which he/she doesn't require any agent's assistance. Due to the `Max Simultaneous Chats per agent` limit, the agent is also not able to use this extra time to help other customer thus affecting his overall productivity. + + **So how does the On-Hold feature solve this problem?** + With the On-Hold feature, an agent is now able to place a chat on-hold. On-Hold chats **don’t count towards the maximum number of concurrent chats** an agent can have. So in our above example, the agent can simply now place the customer on-hold for 20-30 minutes until the customer downloads the software and within this time, the agent can serve other customers - hence increasing the productivity of an agent. + + ---------------------------------------- + ### Working of the new On-Hold feature + + #### How can you place a chat on Hold ? + + A chat can be placed on-hold via 2 means + 1. Automatically place Abandoned chats On-hold + ![image](https://user-images.githubusercontent.com/34130764/111537074-06431780-8791-11eb-8d23-99f5d9f8ec45.png) + Via this :top: option you can define a timer which will get started when a customer sends a message. If we don't receive any message from the customer within this timer, the timer will get expired and the chat will be considered as Abandoned. + ![image](https://user-images.githubusercontent.com/34130764/111537346-53bf8480-8791-11eb-8dc7-260633b4e98f.png) + The via this :top: setting you can choose to automatically place this abandoned chat On Hold + 2. Manually place a chat On Hold + As an admin, you can allow an agent to manually place a chat on-hold. To do so, you'll need to turn on this :point_down: setting + ![image](https://user-images.githubusercontent.com/34130764/111537545-97b28980-8791-11eb-86fd-db45b87e9cc1.png) + Now an agent will be able to see a new `On Hold` button within their `Visitor Info Panel` like this :point_down: , provided the agent has sent the last message + ![image](https://user-images.githubusercontent.com/34130764/111537853-f24be580-8791-11eb-9561-d77ba430c625.png) + + #### How can you resume a On Hold chat ? + An On Hold chat can be resumed via 2 means + + 1. If the Customer sends a message + If the Customer / Omnichannel End User sends a message to the On Hold chat, the On Hold chat will get automatically resumed. + 2. Manually by agent + An Agent can manually resume the On Hold chat via clicking the `Resume` button in the bottom of a chat room. + ![image](https://user-images.githubusercontent.com/34130764/111538666-f88e9180-8792-11eb-8d14-01453b8e3db0.png) + + #### What would happen if the agent already reached maximum chats, and a On-Hold chat gets resumed ? + Based on how the chat was resumed, there are multiple cases are each case is dealt differently + + - If an agent manually tries to resume the On Hold chat, he/she will get an error saying `Maximum Simultaneous chat limit reached` - If a customer replies back on an On Hold chat and the last serving agent has reached maximum capacity, then this customer will be placed on the queue again from where based on the Routing Algorithm selected, the chat will get transferred to any available agent - Ability to hide 'Room topic changed' system messages ([#21062](https://github.com/RocketChat/Rocket.Chat/pull/21062) by [@Tirieru](https://github.com/Tirieru)) @@ -8064,39 +12006,33 @@ - Teams ([#20966](https://github.com/RocketChat/Rocket.Chat/pull/20966) by [@g-thome](https://github.com/g-thome)) - ## Teams - - - - You can easily group your users as Teams on Rocket.Chat. The feature takes the hassle out of managing multiple users one by one and allows you to handle them at the same time efficiently. - - - - - Teams can be public or private and each team can have its own channels, which also can be public or private. - - - It's possible to add existing channels to a Team or create new ones inside a Team. - - - It's possible to invite people outside a Team to join Team's channels. - - - It's possible to convert channels to Teams - - - It's possible to add all team members to a channel at once - - - Team members have roles - - - ![image](https://user-images.githubusercontent.com/70927132/113421955-4f56b680-93a2-11eb-80dc-9b70a3f09b3e.png) - - - - **Quickly onboard new users with Autojoin channels** - - Teams can have Auto-join channels – channels to which the team members are automatically added, so you don’t need to go through the manual process of adding users repetitively - - ![image](https://user-images.githubusercontent.com/70927132/113419284-81194e80-939d-11eb-9fff-aeb05cbc8089.png) - - **Instantly mention multiple members at once** (available in EE) - + ## Teams + + + + You can easily group your users as Teams on Rocket.Chat. The feature takes the hassle out of managing multiple users one by one and allows you to handle them at the same time efficiently. + + + - Teams can be public or private and each team can have its own channels, which also can be public or private. + - It's possible to add existing channels to a Team or create new ones inside a Team. + - It's possible to invite people outside a Team to join Team's channels. + - It's possible to convert channels to Teams + - It's possible to add all team members to a channel at once + - Team members have roles + + + ![image](https://user-images.githubusercontent.com/70927132/113421955-4f56b680-93a2-11eb-80dc-9b70a3f09b3e.png) + + + + **Quickly onboard new users with Autojoin channels** + + Teams can have Auto-join channels – channels to which the team members are automatically added, so you don’t need to go through the manual process of adding users repetitively + + ![image](https://user-images.githubusercontent.com/70927132/113419284-81194e80-939d-11eb-9fff-aeb05cbc8089.png) + + **Instantly mention multiple members at once** (available in EE) + With Teams, you don’t need to remember everyone’s name to communicate with a team quickly. Just mention a Team — @engineers, for instance — and all members will be instantly notified. ### 🚀 Improvements @@ -8106,22 +12042,22 @@ - Added modal-box for preview after recording audio. ([#20370](https://github.com/RocketChat/Rocket.Chat/pull/20370) by [@Darshilp326](https://github.com/Darshilp326)) - A modal box will be displayed so that users can change the filename and add description. - - **Before** - - https://user-images.githubusercontent.com/55157259/105687301-4e2a8880-5f1e-11eb-873d-dc8a880a2fc8.mp4 - - **After** - + A modal box will be displayed so that users can change the filename and add description. + + **Before** + + https://user-images.githubusercontent.com/55157259/105687301-4e2a8880-5f1e-11eb-873d-dc8a880a2fc8.mp4 + + **After** + https://user-images.githubusercontent.com/55157259/105687342-597db400-5f1e-11eb-8b61-8f9d9ebad0c4.mp4 - Adds toast after follow/unfollow messages and following icon for followed messages without threads. ([#20025](https://github.com/RocketChat/Rocket.Chat/pull/20025) by [@RonLek](https://github.com/RonLek)) - There was no alert on following/unfollowing a message previously. Also, it was impossible to make out a followed message with no threads from an unfollowed one. - - This PR would show an alert on following/unfollowing a message and also display a small bell icon (similar to the ones for starred and pinned messages) when a message with no thread is followed. - + There was no alert on following/unfollowing a message previously. Also, it was impossible to make out a followed message with no threads from an unfollowed one. + + This PR would show an alert on following/unfollowing a message and also display a small bell icon (similar to the ones for starred and pinned messages) when a message with no thread is followed. + https://user-images.githubusercontent.com/28918901/103813540-43e73e00-5086-11eb-8592-2877eb650f3e.mp4 - Back to threads list button on threads contextual bar ([#20882](https://github.com/RocketChat/Rocket.Chat/pull/20882)) @@ -8134,12 +12070,12 @@ - Improve Apps permission modal ([#21193](https://github.com/RocketChat/Rocket.Chat/pull/21193) by [@lucassartor](https://github.com/lucassartor)) - Improve the UI of the Apps permission modal when installing an App that requires permissions. - - **New UI:** - ![after](https://user-images.githubusercontent.com/49413772/111685622-e817fe80-8806-11eb-998d-b56623560e74.PNG) - - **Old UI:** + Improve the UI of the Apps permission modal when installing an App that requires permissions. + + **New UI:** + ![after](https://user-images.githubusercontent.com/49413772/111685622-e817fe80-8806-11eb-998d-b56623560e74.PNG) + + **Old UI:** ![before](https://user-images.githubusercontent.com/49413772/111685897-375e2f00-8807-11eb-814e-cb8060dc1830.PNG) - Make debug logs of Apps configurable via Log_Level setting in the Admin panel ([#21000](https://github.com/RocketChat/Rocket.Chat/pull/21000) by [@cuonghuunguyen](https://github.com/cuonghuunguyen)) @@ -8150,15 +12086,15 @@ - Sort Users List In Case Insensitive Manner ([#20790](https://github.com/RocketChat/Rocket.Chat/pull/20790) by [@aditya-mitra](https://github.com/aditya-mitra)) - The users listed in the admin panel were sorted in a case-sensitive manner , where the capitals came first and then the small letters (like - *A B C a b c*). This Change fixes this by sorting the names in a caseinsensitive manner (now - *A a B b C c*). - - ### Before - - ![before](https://user-images.githubusercontent.com/55396651/108189880-3fa74980-7137-11eb-99da-6498707b4bf8.png) - - - ### With This Change - + The users listed in the admin panel were sorted in a case-sensitive manner , where the capitals came first and then the small letters (like - *A B C a b c*). This Change fixes this by sorting the names in a caseinsensitive manner (now - *A a B b C c*). + + ### Before + + ![before](https://user-images.githubusercontent.com/55396651/108189880-3fa74980-7137-11eb-99da-6498707b4bf8.png) + + + ### With This Change + ![after](https://user-images.githubusercontent.com/55396651/108190177-9dd42c80-7137-11eb-8b4e-b7cef4ba512f.png) ### 🐛 Bug fixes @@ -8172,12 +12108,12 @@ - **APPS:** Warn message while installing app in air-gapped environment ([#20992](https://github.com/RocketChat/Rocket.Chat/pull/20992) by [@lucassartor](https://github.com/lucassartor)) - Change **error** message to a **warn** message when uploading a `.zip` file app into a air-gapped environment. - - The **error** message was giving the impression for the user that the app wasn't properly being installed , which it wasn't the case: - ![error](https://user-images.githubusercontent.com/49413772/109855273-d3e4d680-7c36-11eb-824b-ad455d24710c.PNG) - - A more detailed **warn** message can fix that impression for the user: + Change **error** message to a **warn** message when uploading a `.zip` file app into a air-gapped environment. + + The **error** message was giving the impression for the user that the app wasn't properly being installed , which it wasn't the case: + ![error](https://user-images.githubusercontent.com/49413772/109855273-d3e4d680-7c36-11eb-824b-ad455d24710c.PNG) + + A more detailed **warn** message can fix that impression for the user: ![warn](https://user-images.githubusercontent.com/49413772/109855383-f2e36880-7c36-11eb-8d61-c442980bd8fd.PNG) - Add missing `unreads` field to `users.info` REST endpoint ([#20905](https://github.com/RocketChat/Rocket.Chat/pull/20905)) @@ -8192,10 +12128,10 @@ - Correct direction for admin mapview text ([#20897](https://github.com/RocketChat/Rocket.Chat/pull/20897) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - ![Screenshot from 2021-02-25 02-49-21](https://user-images.githubusercontent.com/38764067/109068512-f8602080-7715-11eb-8e22-d610f9d046d8.png) - ![Screenshot from 2021-02-25 02-49-46](https://user-images.githubusercontent.com/38764067/109068516-fa29e400-7715-11eb-9119-1c79abce278f.png) - ![Screenshot from 2021-02-25 02-49-57](https://user-images.githubusercontent.com/38764067/109068519-fbf3a780-7715-11eb-8b3d-0dc32f898725.png) - + ![Screenshot from 2021-02-25 02-49-21](https://user-images.githubusercontent.com/38764067/109068512-f8602080-7715-11eb-8e22-d610f9d046d8.png) + ![Screenshot from 2021-02-25 02-49-46](https://user-images.githubusercontent.com/38764067/109068516-fa29e400-7715-11eb-9119-1c79abce278f.png) + ![Screenshot from 2021-02-25 02-49-57](https://user-images.githubusercontent.com/38764067/109068519-fbf3a780-7715-11eb-8b3d-0dc32f898725.png) + The text says the share button will be on the left of the messagebox once enabled. However, it actually is on the right. - Correct ignored message CSS ([#20928](https://github.com/RocketChat/Rocket.Chat/pull/20928) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) @@ -8212,13 +12148,13 @@ - Custom emojis to override default ([#20359](https://github.com/RocketChat/Rocket.Chat/pull/20359) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - Due to the sequence of the imports and how the emojiRenderer prioritizes lists, the custom emojis could not override the emojione emojis. Making two small changes fixed the issue. - - With the custom emoji for `:facepalm:` added, you can check out the result below: - ### Before - ![Screenshot from 2021-01-25 02-20-04](https://user-images.githubusercontent.com/38764067/105643088-dfb0e080-5eb3-11eb-8a00-582c53fbe9a4.png) - - ### After + Due to the sequence of the imports and how the emojiRenderer prioritizes lists, the custom emojis could not override the emojione emojis. Making two small changes fixed the issue. + + With the custom emoji for `:facepalm:` added, you can check out the result below: + ### Before + ![Screenshot from 2021-01-25 02-20-04](https://user-images.githubusercontent.com/38764067/105643088-dfb0e080-5eb3-11eb-8a00-582c53fbe9a4.png) + + ### After ![Screenshot from 2021-01-25 02-18-58](https://user-images.githubusercontent.com/38764067/105643076-cdcf3d80-5eb3-11eb-84b8-5dbc4f1135df.png) - Empty URL in user avatar doesn't show error and enables save ([#20440](https://github.com/RocketChat/Rocket.Chat/pull/20440) by [@im-adithya](https://github.com/im-adithya)) @@ -8231,12 +12167,12 @@ - Fix the search list showing the last channel ([#21160](https://github.com/RocketChat/Rocket.Chat/pull/21160) by [@shrinish123](https://github.com/shrinish123)) - The search list now also properly shows the last channel - Before : - - ![searchlist](https://user-images.githubusercontent.com/56491104/111471487-f3a7ee80-874e-11eb-9c6e-19bbf0731d60.png) - - After : + The search list now also properly shows the last channel + Before : + + ![searchlist](https://user-images.githubusercontent.com/56491104/111471487-f3a7ee80-874e-11eb-9c6e-19bbf0731d60.png) + + After : ![search_final](https://user-images.githubusercontent.com/56491104/111471521-fe628380-874e-11eb-8fa3-d1edb57587e1.png) - Follow thread action on threads list ([#20881](https://github.com/RocketChat/Rocket.Chat/pull/20881)) @@ -8261,13 +12197,13 @@ - Multi Select isn't working in Export Messages ([#21236](https://github.com/RocketChat/Rocket.Chat/pull/21236) by [@PriyaBihani](https://github.com/PriyaBihani)) - While exporting messages, we were not able to select multiple Users like this: - - https://user-images.githubusercontent.com/69837339/111953057-169a2000-8b0c-11eb-94a4-0e1657683f96.mp4 - - Now we can select multiple users: - - + While exporting messages, we were not able to select multiple Users like this: + + https://user-images.githubusercontent.com/69837339/111953057-169a2000-8b0c-11eb-94a4-0e1657683f96.mp4 + + Now we can select multiple users: + + https://user-images.githubusercontent.com/69837339/111953097-274a9600-8b0c-11eb-9177-bec388b042bd.mp4 - New Channel popover not closing ([#21080](https://github.com/RocketChat/Rocket.Chat/pull/21080)) @@ -8276,31 +12212,31 @@ - OEmbedURLWidget - Show Full Embedded Text Description ([#20569](https://github.com/RocketChat/Rocket.Chat/pull/20569) by [@aditya-mitra](https://github.com/aditya-mitra)) - Embeds were cutoff when either _urls had a long description_. - This was handled by removing `overflow:hidden;text-overflow:ellipsis;` from the inline styles in [`oembedUrlWidget.html`](https://github.com/RocketChat/Rocket.Chat/blob/develop/app/oembed/client/oembedUrlWidget.html#L28). - - ### Earlier - - ![earlier](https://user-images.githubusercontent.com/55396651/107110825-00dcde00-6871-11eb-866e-13cabc5b0d05.png) - - ### Now - + Embeds were cutoff when either _urls had a long description_. + This was handled by removing `overflow:hidden;text-overflow:ellipsis;` from the inline styles in [`oembedUrlWidget.html`](https://github.com/RocketChat/Rocket.Chat/blob/develop/app/oembed/client/oembedUrlWidget.html#L28). + + ### Earlier + + ![earlier](https://user-images.githubusercontent.com/55396651/107110825-00dcde00-6871-11eb-866e-13cabc5b0d05.png) + + ### Now + ![now](https://user-images.githubusercontent.com/55396651/107110794-ca06c800-6870-11eb-9b3b-168679936612.png) - Reactions list showing users in reactions option of message action. ([#20753](https://github.com/RocketChat/Rocket.Chat/pull/20753) by [@Darshilp326](https://github.com/Darshilp326)) - Reactions list shows emojis with respected users who have reacted with that emoji. - + Reactions list shows emojis with respected users who have reacted with that emoji. + https://user-images.githubusercontent.com/55157259/107857609-5870e000-6e55-11eb-8137-494a9f71b171.mp4 - Removing truncation from profile ([#20352](https://github.com/RocketChat/Rocket.Chat/pull/20352) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - Truncating text in profile view was making some information completely inaccessible. Removed it from the user status and the custom fields where if the information is longer, the user would actually want to see all of it. - - ### Before - ![Screenshot from 2021-01-24 20-54-44](https://user-images.githubusercontent.com/38764067/105634935-7e264d00-5e86-11eb-8a6c-9f2a363e0f6c.png) - - ### After + Truncating text in profile view was making some information completely inaccessible. Removed it from the user status and the custom fields where if the information is longer, the user would actually want to see all of it. + + ### Before + ![Screenshot from 2021-01-24 20-54-44](https://user-images.githubusercontent.com/38764067/105634935-7e264d00-5e86-11eb-8a6c-9f2a363e0f6c.png) + + ### After ![Screenshot from 2021-01-24 20-54-06](https://user-images.githubusercontent.com/38764067/105634940-82eb0100-5e86-11eb-8b90-e97a43c5e938.png) - Replace wrong field description on Room Information panel ([#21395](https://github.com/RocketChat/Rocket.Chat/pull/21395) by [@rafaelblink](https://github.com/rafaelblink)) @@ -8311,8 +12247,8 @@ - Set establishing to false if OTR timeouts ([#21183](https://github.com/RocketChat/Rocket.Chat/pull/21183) by [@Darshilp326](https://github.com/Darshilp326)) - Set establishing false if OTR timeouts. - + Set establishing false if OTR timeouts. + https://user-images.githubusercontent.com/55157259/111617086-b30cab80-8808-11eb-8740-3b4ffacfc322.mp4 - Sidebar scroll missing full height ([#21071](https://github.com/RocketChat/Rocket.Chat/pull/21071)) @@ -8351,33 +12287,20 @@ - Chore: Add tests for Meteor methods ([#20901](https://github.com/RocketChat/Rocket.Chat/pull/20901)) - Add end-to-end tests for the following meteor methods - - - - [x] public-settings:get - - - [x] rooms:get - - - [x] subscriptions:get - - - [x] permissions:get - - - [x] loadMissedMessages - - - [x] loadHistory - - - [x] listCustomUserStatus - - - [x] getUserRoles - - - [x] getRoomRoles (called by the API, already covered) - - - [x] getMessages - - - [x] getUsersOfRoom - - - [x] loadNextMessages - + Add end-to-end tests for the following meteor methods + + - [x] public-settings:get + - [x] rooms:get + - [x] subscriptions:get + - [x] permissions:get + - [x] loadMissedMessages + - [x] loadHistory + - [x] listCustomUserStatus + - [x] getUserRoles + - [x] getRoomRoles (called by the API, already covered) + - [x] getMessages + - [x] getUsersOfRoom + - [x] loadNextMessages - [x] getThreadMessages - Chore: Meteor update 2.1 ([#21061](https://github.com/RocketChat/Rocket.Chat/pull/21061)) @@ -8390,10 +12313,8 @@ - Improve: Increase testing coverage ([#21015](https://github.com/RocketChat/Rocket.Chat/pull/21015)) - Add test for - - - settings/raw - + Add test for + - settings/raw - minimongo/comparisons - Improve: NPS survey fetch ([#21263](https://github.com/RocketChat/Rocket.Chat/pull/21263)) @@ -8412,19 +12333,17 @@ - Regression: Add scope to permission checks in Team's endpoints ([#21369](https://github.com/RocketChat/Rocket.Chat/pull/21369)) - - Include scope (team's main room ID) in the permission checks; + - Include scope (team's main room ID) in the permission checks; - Remove the `teamName` parameter from the `members`, `addMembers`, `updateMember` and `removeMembers` methods (since `teamId` will always be defined). - Regression: Add support to filter on `teams.listRooms` endpoint ([#21327](https://github.com/RocketChat/Rocket.Chat/pull/21327)) - - Add support for queries (within the `query` parameter); - + - Add support for queries (within the `query` parameter); - Add support to pagination (`offset` and `count`) when an user doesn't have the permission to get all rooms. - Regression: Add teams support to directory ([#21351](https://github.com/RocketChat/Rocket.Chat/pull/21351)) - - Change `directory.js` to reduce function complexity - + - Change `directory.js` to reduce function complexity - Add `teams` type of item. Directory will return all public teams & private teams the user is part of. - Regression: add view room action on Teams Channels ([#21295](https://github.com/RocketChat/Rocket.Chat/pull/21295)) @@ -8477,19 +12396,18 @@ - Regression: Quick action button missing for Omnichannel On-Hold queue ([#21285](https://github.com/RocketChat/Rocket.Chat/pull/21285)) - - Move the Manual On Hold button to the new Omnichannel Header - ![image](https://user-images.githubusercontent.com/34130764/112291749-6ae10380-8cb6-11eb-94cd-e05efc14b1bf.png) - ![image](https://user-images.githubusercontent.com/34130764/112304146-27d95d00-8cc3-11eb-85db-dde04a110dd1.png) - - + - Move the Manual On Hold button to the new Omnichannel Header + ![image](https://user-images.githubusercontent.com/34130764/112291749-6ae10380-8cb6-11eb-94cd-e05efc14b1bf.png) + ![image](https://user-images.githubusercontent.com/34130764/112304146-27d95d00-8cc3-11eb-85db-dde04a110dd1.png) + - Minor fixes - regression: Remove Breadcrumbs and update Tag component ([#21399](https://github.com/RocketChat/Rocket.Chat/pull/21399)) - Regression: Remove channel action on add channel's modal don't work ([#21356](https://github.com/RocketChat/Rocket.Chat/pull/21356)) - ![removechannel-on-add-existing-modal](https://user-images.githubusercontent.com/27704687/112911017-eda8fa80-90ca-11eb-9c24-47a70be0c314.gif) - + ![removechannel-on-add-existing-modal](https://user-images.githubusercontent.com/27704687/112911017-eda8fa80-90ca-11eb-9c24-47a70be0c314.gif) + ![image](https://user-images.githubusercontent.com/27704687/112911052-02858e00-90cb-11eb-85a2-0ef1f5f9ffd9.png) - Regression: Remove primary color from button in TeamChannels component ([#21293](https://github.com/RocketChat/Rocket.Chat/pull/21293)) @@ -8518,10 +12436,10 @@ - Regression: Unify Contact information displayed on the Room header and Room Info ([#21312](https://github.com/RocketChat/Rocket.Chat/pull/21312) by [@rafaelblink](https://github.com/rafaelblink)) - ![image](https://user-images.githubusercontent.com/34130764/112586659-35592900-8e22-11eb-94be-32bdff7ca883.png) - - ![image](https://user-images.githubusercontent.com/2493803/112913130-788bf400-90cf-11eb-84c6-782b203e100a.png) - + ![image](https://user-images.githubusercontent.com/34130764/112586659-35592900-8e22-11eb-94be-32bdff7ca883.png) + + ![image](https://user-images.githubusercontent.com/2493803/112913130-788bf400-90cf-11eb-84c6-782b203e100a.png) + ![image](https://user-images.githubusercontent.com/2493803/112913146-817cc580-90cf-11eb-87ad-ef79766be2b3.png) - Regression: Unify team actions to add a room to a team ([#21386](https://github.com/RocketChat/Rocket.Chat/pull/21386)) @@ -8530,10 +12448,8 @@ - Regression: Update .invite endpoints to support multiple users at once ([#21328](https://github.com/RocketChat/Rocket.Chat/pull/21328)) - - channels.invite now supports passing an array as a param (either with usernames or userIds) via `usernames` or `userIds` properties. - - - You can still use the endpoint to invite only one user via the old params `userId`, `username` or `user`. - + - channels.invite now supports passing an array as a param (either with usernames or userIds) via `usernames` or `userIds` properties. + - You can still use the endpoint to invite only one user via the old params `userId`, `username` or `user`. - Same changes apply to groups.invite - Regression: user actions in admin ([#21307](https://github.com/RocketChat/Rocket.Chat/pull/21307)) @@ -8668,7 +12584,7 @@ - Close Call contextual bar after starting jitsi call. ([#21004](https://github.com/RocketChat/Rocket.Chat/pull/21004)) - After jitsi call is started, if the call is started in a new window then we should close contextual tab bar. + After jitsi call is started, if the call is started in a new window then we should close contextual tab bar. So, when 'YES' is pressed on modal, we call handleClose function if openNewWindow is true, as call doesn't starts on tab bar, it starts on new window. ### 🐛 Bug fixes @@ -8678,7 +12594,7 @@ - Stopping Jitsi reload ([#20973](https://github.com/RocketChat/Rocket.Chat/pull/20973)) - The Function where Jitsi call is started gets called many times due to `room.usernames` dep of useMemo, this dep triggers reloading of this function many times. + The Function where Jitsi call is started gets called many times due to `room.usernames` dep of useMemo, this dep triggers reloading of this function many times. So removing this dep from useMemo dependencies ### 👩‍💻👨‍💻 Core Team 🤓 @@ -8703,10 +12619,10 @@ - Cloud Workspace bridge ([#20838](https://github.com/RocketChat/Rocket.Chat/pull/20838)) - Adds the new CloudWorkspace functionality. - - It allows apps to request the access token for the workspace it's installed on, so it can perform actions with other Rocket.Chat services, such as the Omni Gateway. - + Adds the new CloudWorkspace functionality. + + It allows apps to request the access token for the workspace it's installed on, so it can perform actions with other Rocket.Chat services, such as the Omni Gateway. + https://github.com/RocketChat/Rocket.Chat.Apps-engine/pull/382 - Header with Breadcrumbs ([#20609](https://github.com/RocketChat/Rocket.Chat/pull/20609)) @@ -8724,10 +12640,10 @@ - Add symbol to indicate apps' required settings in the UI ([#20447](https://github.com/RocketChat/Rocket.Chat/pull/20447)) - - Apps are able to define **required** settings. These settings should not be left blank by the user and an error will be thrown and shown in the interface if an user attempts to save changes in the app details page leaving any required fields blank; - ![prt_screen_required_app_settings_warning](https://user-images.githubusercontent.com/36537004/106032964-e73cd900-60af-11eb-8eab-c11fd651b593.png) - - - A sign (*) is added to the label of app settings' fields that are required so as to highlight the fields which must not be left blank. + - Apps are able to define **required** settings. These settings should not be left blank by the user and an error will be thrown and shown in the interface if an user attempts to save changes in the app details page leaving any required fields blank; + ![prt_screen_required_app_settings_warning](https://user-images.githubusercontent.com/36537004/106032964-e73cd900-60af-11eb-8eab-c11fd651b593.png) + + - A sign (*) is added to the label of app settings' fields that are required so as to highlight the fields which must not be left blank. ![prt_screen_required_app_settings](https://user-images.githubusercontent.com/36537004/106014879-ae473900-609c-11eb-9b9e-95de7bbf20a5.png) - Add visual validation on users admin forms ([#20308](https://github.com/RocketChat/Rocket.Chat/pull/20308)) @@ -8748,20 +12664,20 @@ - Adds tooltip for sidebar header icons ([#19934](https://github.com/RocketChat/Rocket.Chat/pull/19934) by [@RonLek](https://github.com/RonLek)) - Previously the header icons in the sidebar didn't show a tooltip when hovered over. This PR fixes that. - + Previously the header icons in the sidebar didn't show a tooltip when hovered over. This PR fixes that. + ![Screenshot from 2020-12-22 15-17-41](https://user-images.githubusercontent.com/28918901/102874804-f2756700-4468-11eb-8324-b7f3194e62fe.png) - Better Presentation of Blockquotes ([#20750](https://github.com/RocketChat/Rocket.Chat/pull/20750) by [@aditya-mitra](https://github.com/aditya-mitra)) - Changed the values of `margin-top` and `margin-bottom` for *first* and *last* childs in blockquotes to increase readability. - - ### Before - - ![before](https://user-images.githubusercontent.com/55396651/107858662-3e3a0080-6e5b-11eb-8274-9bd956807235.png) - - ### Now - + Changed the values of `margin-top` and `margin-bottom` for *first* and *last* childs in blockquotes to increase readability. + + ### Before + + ![before](https://user-images.githubusercontent.com/55396651/107858662-3e3a0080-6e5b-11eb-8274-9bd956807235.png) + + ### Now + ![now](https://user-images.githubusercontent.com/55396651/107858471-480f3400-6e5a-11eb-9ccb-3f1be2fed0a4.png) - Change header based on room type ([#20612](https://github.com/RocketChat/Rocket.Chat/pull/20612)) @@ -8782,18 +12698,13 @@ - Replace react-window for react-virtuoso package ([#20392](https://github.com/RocketChat/Rocket.Chat/pull/20392)) - Remove: - - - react-window - - - react-window-infinite-loader - - - simplebar-react - - Include: - - - react-virtuoso - + Remove: + - react-window + - react-window-infinite-loader + - simplebar-react + + Include: + - react-virtuoso - rc-scrollbars - Rewrite Call as React component ([#19778](https://github.com/RocketChat/Rocket.Chat/pull/19778)) @@ -8809,13 +12720,13 @@ - Add debouncing to add users search field. ([#20297](https://github.com/RocketChat/Rocket.Chat/pull/20297) by [@Darshilp326](https://github.com/Darshilp326)) - BEFORE - - https://user-images.githubusercontent.com/55157259/105350722-98a3c080-5c11-11eb-82f3-d9a62a4fa50b.mp4 - - - AFTER - + BEFORE + + https://user-images.githubusercontent.com/55157259/105350722-98a3c080-5c11-11eb-82f3-d9a62a4fa50b.mp4 + + + AFTER + https://user-images.githubusercontent.com/55157259/105350757-a2c5bf00-5c11-11eb-91db-25c0b9e01a28.mp4 - Add tooltips to Thread header buttons ([#20456](https://github.com/RocketChat/Rocket.Chat/pull/20456) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) @@ -8828,8 +12739,8 @@ - Added check for view admin permission page ([#20403](https://github.com/RocketChat/Rocket.Chat/pull/20403)) - Admin Permission page was visible to all, if you add admin/permissions after the base url. This should not be visible to all user, only people with certain permissions should be able to see this page. - I am also able to see permissions page for open workspace of Rocket chat. + Admin Permission page was visible to all, if you add admin/permissions after the base url. This should not be visible to all user, only people with certain permissions should be able to see this page. + I am also able to see permissions page for open workspace of Rocket chat. ![image](https://user-images.githubusercontent.com/58601732/105829728-bfd00880-5fea-11eb-9121-6c53a752f140.png) - Adding the accidentally deleted tag template, used by other templates ([#20772](https://github.com/RocketChat/Rocket.Chat/pull/20772)) @@ -8838,8 +12749,8 @@ - Admin cannot clear user details like bio or nickname ([#20785](https://github.com/RocketChat/Rocket.Chat/pull/20785)) - When the API users.update is called to update user data, it passes data to saveUser function. Here before saving data like bio or nickname we are checking if they are available or not. If data is available then we are saving it, but we are not doing anything when data isn't available. - + When the API users.update is called to update user data, it passes data to saveUser function. Here before saving data like bio or nickname we are checking if they are available or not. If data is available then we are saving it, but we are not doing anything when data isn't available. + So unsetting data if data isn't available to save. Will also fix bio and other fields. :) - Admin Panel pages not visible in Safari ([#20912](https://github.com/RocketChat/Rocket.Chat/pull/20912)) @@ -8856,24 +12767,24 @@ - Blank Personal Access Token Bug ([#20193](https://github.com/RocketChat/Rocket.Chat/pull/20193) by [@RonLek](https://github.com/RonLek)) - Adds error when personal access token is blank thereby disallowing the creation of one. - + Adds error when personal access token is blank thereby disallowing the creation of one. + https://user-images.githubusercontent.com/28918901/104483631-5adde100-55ee-11eb-9938-64146bce127e.mp4 - CAS login failing due to TOTP requirement ([#20840](https://github.com/RocketChat/Rocket.Chat/pull/20840)) - Changed password input field for password access in edit room info. ([#20356](https://github.com/RocketChat/Rocket.Chat/pull/20356) by [@Darshilp326](https://github.com/Darshilp326)) - Password field would be secured with asterisks in edit room info - - https://user-images.githubusercontent.com/55157259/105641758-cad04f00-5eab-11eb-90de-0c91263edd55.mp4 - + Password field would be secured with asterisks in edit room info + + https://user-images.githubusercontent.com/55157259/105641758-cad04f00-5eab-11eb-90de-0c91263edd55.mp4 + . - Channel mentions showing user subscribed channels twice ([#20484](https://github.com/RocketChat/Rocket.Chat/pull/20484) by [@Darshilp326](https://github.com/Darshilp326)) - Channel mention shows user subscribed channels twice. - + Channel mention shows user subscribed channels twice. + https://user-images.githubusercontent.com/55157259/106183033-b353d780-61c5-11eb-8aab-1dbb62b02ff8.mp4 - CORS config not accepting multiple origins ([#20696](https://github.com/RocketChat/Rocket.Chat/pull/20696) by [@g-thome](https://github.com/g-thome)) @@ -8884,26 +12795,26 @@ - Default Attachments - Remove Extra Margin in Field Attachments ([#20618](https://github.com/RocketChat/Rocket.Chat/pull/20618) by [@aditya-mitra](https://github.com/aditya-mitra)) - A large amount of unnecessary margin which existed in the **Field Attachments inside the `DefaultAttachments`** has been fixed. - - ### Earlier - - ![earlier](https://user-images.githubusercontent.com/55396651/107056792-ba4b9d00-67f8-11eb-9153-05281416cddb.png) - - ### Now - + A large amount of unnecessary margin which existed in the **Field Attachments inside the `DefaultAttachments`** has been fixed. + + ### Earlier + + ![earlier](https://user-images.githubusercontent.com/55396651/107056792-ba4b9d00-67f8-11eb-9153-05281416cddb.png) + + ### Now + ![now](https://user-images.githubusercontent.com/55396651/107057196-3219c780-67f9-11eb-84db-e4a0addfc168.png) - Default Attachments - Show Full Attachment.Text with Markdown ([#20606](https://github.com/RocketChat/Rocket.Chat/pull/20606) by [@aditya-mitra](https://github.com/aditya-mitra)) - Removed truncating of text in `Attachment.Text`. - Added `Attachment.Text` to be parsed to markdown by default. - - ### Earlier - ![earlier](https://user-images.githubusercontent.com/55396651/106910781-92d8cf80-6727-11eb-82ec-818df7544ff0.png) - - ### Now - + Removed truncating of text in `Attachment.Text`. + Added `Attachment.Text` to be parsed to markdown by default. + + ### Earlier + ![earlier](https://user-images.githubusercontent.com/55396651/106910781-92d8cf80-6727-11eb-82ec-818df7544ff0.png) + + ### Now + ![now](https://user-images.githubusercontent.com/55396651/106910840-a126eb80-6727-11eb-8bd6-d86383dd9181.png) - Don't ask again not rendering ([#20745](https://github.com/RocketChat/Rocket.Chat/pull/20745)) @@ -8924,21 +12835,21 @@ - Feedback on bulk invite ([#20339](https://github.com/RocketChat/Rocket.Chat/pull/20339) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - Resolved structure where no response was being received. Changed from callback to async/await. - Added error in case of empty submission, or if no valid emails were found. - + Resolved structure where no response was being received. Changed from callback to async/await. + Added error in case of empty submission, or if no valid emails were found. + https://user-images.githubusercontent.com/38764067/105613964-dfe5a900-5deb-11eb-80f2-21fc8dee57c0.mp4 - Filters are not being applied correctly in Omnichannel Current Chats list ([#20320](https://github.com/RocketChat/Rocket.Chat/pull/20320) by [@rafaelblink](https://github.com/rafaelblink)) - ### Before - ![image](https://user-images.githubusercontent.com/2493803/105537672-082cb500-5cd1-11eb-8f1b-1726ba60420a.png) - - ### After - ![image](https://user-images.githubusercontent.com/2493803/105537773-2d212800-5cd1-11eb-8746-048deb9502d9.png) - - ![image](https://user-images.githubusercontent.com/2493803/106494728-88090b00-6499-11eb-922e-5386107e2389.png) - + ### Before + ![image](https://user-images.githubusercontent.com/2493803/105537672-082cb500-5cd1-11eb-8f1b-1726ba60420a.png) + + ### After + ![image](https://user-images.githubusercontent.com/2493803/105537773-2d212800-5cd1-11eb-8746-048deb9502d9.png) + + ![image](https://user-images.githubusercontent.com/2493803/106494728-88090b00-6499-11eb-922e-5386107e2389.png) + ![image](https://user-images.githubusercontent.com/2493803/106494751-90f9dc80-6499-11eb-901b-5e4dbdc678ba.png) - Fix Empty highlighted words field ([#20329](https://github.com/RocketChat/Rocket.Chat/pull/20329)) @@ -8967,11 +12878,11 @@ - List of Omnichannel triggers is not listing data ([#20624](https://github.com/RocketChat/Rocket.Chat/pull/20624) by [@rafaelblink](https://github.com/rafaelblink)) - ### Before - ![image](https://user-images.githubusercontent.com/2493803/107095379-7308e080-67e7-11eb-8251-7e7ff891087a.png) - - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/2493803/107095379-7308e080-67e7-11eb-8251-7e7ff891087a.png) + + + ### After ![image](https://user-images.githubusercontent.com/2493803/107095261-3b019d80-67e7-11eb-8425-8612b03ac50a.png) - Livechat bridge permission checkers ([#20653](https://github.com/RocketChat/Rocket.Chat/pull/20653) by [@lolimay](https://github.com/lolimay)) @@ -8994,8 +12905,7 @@ - Missing setting to control when to send the ReplyTo field in email notifications ([#20744](https://github.com/RocketChat/Rocket.Chat/pull/20744)) - - Add a new setting ("Add Reply-To header") in the Email settings' page to control when the Reply-To header is used in e-mail notifications; - + - Add a new setting ("Add Reply-To header") in the Email settings' page to control when the Reply-To header is used in e-mail notifications; - The new setting is turned off (`false` value) by default. - New Integration page was not being displayed ([#20670](https://github.com/RocketChat/Rocket.Chat/pull/20670)) @@ -9028,15 +12938,15 @@ - Remove duplicate getCommonRoomEvents() event binding for starredMessages ([#20185](https://github.com/RocketChat/Rocket.Chat/pull/20185) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - The getCommonRoomEvents() returned functions were bound to the starredMessages template twice. This was causing some bugs, as detailed in the Issue mentioned below. + The getCommonRoomEvents() returned functions were bound to the starredMessages template twice. This was causing some bugs, as detailed in the Issue mentioned below. I removed the top events call that only bound the getCommonRoomEvents(). Therefore, only one call for the same is left, which is at the end of the file. Having the events bound just once removes the bugs mentioned. - Remove warning problems from console ([#20800](https://github.com/RocketChat/Rocket.Chat/pull/20800)) - Removed tooltip in kebab menu options. ([#20498](https://github.com/RocketChat/Rocket.Chat/pull/20498) by [@Darshilp326](https://github.com/Darshilp326)) - Removed tooltip as it was not needed. - + Removed tooltip as it was not needed. + https://user-images.githubusercontent.com/55157259/106246146-a53ca000-6233-11eb-9874-cbd1b4331bc0.mp4 - Retry icon comes out of the div ([#20390](https://github.com/RocketChat/Rocket.Chat/pull/20390) by [@im-adithya](https://github.com/im-adithya)) @@ -9051,8 +12961,8 @@ - Room's last message's update date format on IE ([#20680](https://github.com/RocketChat/Rocket.Chat/pull/20680)) - The proposed change fixes a bug when updates the cached records on Internet Explorer and it breaks the sidebar as shown on the screenshot below: - + The proposed change fixes a bug when updates the cached records on Internet Explorer and it breaks the sidebar as shown on the screenshot below: + ![image](https://user-images.githubusercontent.com/27704687/107578007-f2285b00-6bd1-11eb-9250-1e76ae67f9c9.png) - Save user password and email from My Account ([#20737](https://github.com/RocketChat/Rocket.Chat/pull/20737)) @@ -9061,8 +12971,8 @@ - Selected hide system messages would now be viewed in vertical bar. ([#20358](https://github.com/RocketChat/Rocket.Chat/pull/20358) by [@Darshilp326](https://github.com/Darshilp326)) - All selected hide system messages are now in vertical Bar. - + All selected hide system messages are now in vertical Bar. + https://user-images.githubusercontent.com/55157259/105642624-d5411780-5eb0-11eb-8848-93e4b02629cb.mp4 - Selected messages don't get unselected ([#20408](https://github.com/RocketChat/Rocket.Chat/pull/20408) by [@im-adithya](https://github.com/im-adithya)) @@ -9077,22 +12987,14 @@ - Several Slack Importer issues ([#20216](https://github.com/RocketChat/Rocket.Chat/pull/20216)) - - Fix: Slack Importer crashes when importing a large users.json file - - - Fix: Slack importer crashes when messages have invalid mentions - - - Skip listing all users on the preparation screen when the user count is too large. - - - Split avatar download into a separate process. - - - Update room's last message when the import is complete. - - - Prevent invalid or duplicated channel names - - - Improve message error handling. - - - Reduce max allowed BSON size to avoid possible issues in some servers. - + - Fix: Slack Importer crashes when importing a large users.json file + - Fix: Slack importer crashes when messages have invalid mentions + - Skip listing all users on the preparation screen when the user count is too large. + - Split avatar download into a separate process. + - Update room's last message when the import is complete. + - Prevent invalid or duplicated channel names + - Improve message error handling. + - Reduce max allowed BSON size to avoid possible issues in some servers. - Improve handling of very large channel files. - star icon was visible after unstarring a message ([#19645](https://github.com/RocketChat/Rocket.Chat/pull/19645) by [@bhavayAnand9](https://github.com/bhavayAnand9)) @@ -9111,15 +13013,15 @@ - User statuses in admin user info panel ([#20341](https://github.com/RocketChat/Rocket.Chat/pull/20341) by [@RonLek](https://github.com/RonLek)) - Modifies user statuses in admin info panel based on their actual status instead of their `statusConnection`. This enables correct and consistent change in user statuses. - Also, bot users having status as online were classified as offline, with this change they are now correctly classified based on their corresponding statuses. - + Modifies user statuses in admin info panel based on their actual status instead of their `statusConnection`. This enables correct and consistent change in user statuses. + Also, bot users having status as online were classified as offline, with this change they are now correctly classified based on their corresponding statuses. + https://user-images.githubusercontent.com/28918901/105624438-b8bcc500-5e47-11eb-8d1e-3a4180da1304.mp4 - Users autocomplete showing duplicated results ([#20481](https://github.com/RocketChat/Rocket.Chat/pull/20481) by [@Darshilp326](https://github.com/Darshilp326)) - Added new query for outside room users so that room members are not shown twice. - + Added new query for outside room users so that room members are not shown twice. + https://user-images.githubusercontent.com/55157259/106174582-33c10b00-61bb-11eb-9716-377ef7bba34e.mp4
@@ -9140,7 +13042,7 @@ - Chore: Disable Sessions Aggregates tests locally ([#20607](https://github.com/RocketChat/Rocket.Chat/pull/20607)) - Disable Session aggregates tests in local environments + Disable Session aggregates tests in local environments For context, refer to: #20161 - Chore: Improve performance of messages’ watcher ([#20519](https://github.com/RocketChat/Rocket.Chat/pull/20519)) @@ -9349,20 +13251,18 @@ - **ENTERPRISE:** Omnichannel Contact Manager as preferred agent for routing ([#20244](https://github.com/RocketChat/Rocket.Chat/pull/20244)) - If the `Contact-Manager` is assigned to a Visitor, the chat will automatically get transferred to the respective Contact-Manager, provided the Contact-Manager is online. In-case the Contact-Manager is offline, the chat will be transferred to any other online agent. - We have provided a setting to control this auto-assignment feature - ![image](https://user-images.githubusercontent.com/34130764/104880961-8104d780-5986-11eb-9d87-82b99814b028.png) - - Behavior based-on Routing method - - - 1. Auto-selection, Load-Balancing, or External Service (`autoAssignAgent = true`) - This is straightforward, - - if the Contact-manager is online, the chat will be transferred to the Contact-Manger only - - if the Contact-manager is offline, the chat will be transferred to any other online-agent based on the Routing system - - 2. Manual-selection (`autoAssignAgent = false`) - - If the Contact-Manager is online, the chat will appear in the Queue of Contact-Manager **ONLY** + If the `Contact-Manager` is assigned to a Visitor, the chat will automatically get transferred to the respective Contact-Manager, provided the Contact-Manager is online. In-case the Contact-Manager is offline, the chat will be transferred to any other online agent. + We have provided a setting to control this auto-assignment feature + ![image](https://user-images.githubusercontent.com/34130764/104880961-8104d780-5986-11eb-9d87-82b99814b028.png) + + Behavior based-on Routing method + + 1. Auto-selection, Load-Balancing, or External Service (`autoAssignAgent = true`) + This is straightforward, + - if the Contact-manager is online, the chat will be transferred to the Contact-Manger only + - if the Contact-manager is offline, the chat will be transferred to any other online-agent based on the Routing system + 2. Manual-selection (`autoAssignAgent = false`) + - If the Contact-Manager is online, the chat will appear in the Queue of Contact-Manager **ONLY** - If the Contact-Manager is offline, the chat will appear in the Queue of all related Agents/Manager ( like it's done right now ) - Banner system and NPS ([#20221](https://github.com/RocketChat/Rocket.Chat/pull/20221)) @@ -9371,34 +13271,34 @@ - Email Inboxes for Omnichannel ([#20101](https://github.com/RocketChat/Rocket.Chat/pull/20101) by [@rafaelblink](https://github.com/rafaelblink)) - With this new feature, email accounts will receive email messages(threads) which will be transformed into Omnichannel chats. It'll be possible to set up multiple email accounts, test the connection with email server(email provider) and define the behaviour of each account. - - https://user-images.githubusercontent.com/2493803/105430398-242d4980-5c32-11eb-835a-450c94837d23.mp4 - - ### New item on admin menu - - ![image](https://user-images.githubusercontent.com/2493803/105428723-bc293400-5c2e-11eb-8c02-e8d36ea82726.png) - - - ### Send test email tooltip - - ![image](https://user-images.githubusercontent.com/2493803/104366986-eaa16380-54f8-11eb-9ba7-831cfde2319c.png) - - - ### Inbox Info - - ![image](https://user-images.githubusercontent.com/2493803/104366796-ab731280-54f8-11eb-9941-a3cc8eb610e1.png) - - ### SMTP Info - - ![image](https://user-images.githubusercontent.com/2493803/104366868-c47bc380-54f8-11eb-969e-ccc29070957c.png) - - ### IMAP Info - - ![image](https://user-images.githubusercontent.com/2493803/104366897-cd6c9500-54f8-11eb-80c4-97d5b0c002d5.png) - - ### Messages - + With this new feature, email accounts will receive email messages(threads) which will be transformed into Omnichannel chats. It'll be possible to set up multiple email accounts, test the connection with email server(email provider) and define the behaviour of each account. + + https://user-images.githubusercontent.com/2493803/105430398-242d4980-5c32-11eb-835a-450c94837d23.mp4 + + ### New item on admin menu + + ![image](https://user-images.githubusercontent.com/2493803/105428723-bc293400-5c2e-11eb-8c02-e8d36ea82726.png) + + + ### Send test email tooltip + + ![image](https://user-images.githubusercontent.com/2493803/104366986-eaa16380-54f8-11eb-9ba7-831cfde2319c.png) + + + ### Inbox Info + + ![image](https://user-images.githubusercontent.com/2493803/104366796-ab731280-54f8-11eb-9941-a3cc8eb610e1.png) + + ### SMTP Info + + ![image](https://user-images.githubusercontent.com/2493803/104366868-c47bc380-54f8-11eb-969e-ccc29070957c.png) + + ### IMAP Info + + ![image](https://user-images.githubusercontent.com/2493803/104366897-cd6c9500-54f8-11eb-80c4-97d5b0c002d5.png) + + ### Messages + ![image](https://user-images.githubusercontent.com/2493803/105428971-45d90180-5c2f-11eb-992a-022a3df94471.png) - Encrypted Discussions and new Encryption Permissions ([#20201](https://github.com/RocketChat/Rocket.Chat/pull/20201)) @@ -9410,7 +13310,7 @@ - Add extra SAML settings to update room subs and add private room subs. ([#19489](https://github.com/RocketChat/Rocket.Chat/pull/19489) by [@tlskinneriv](https://github.com/tlskinneriv)) - Added a SAML setting to support updating room subscriptions each time a user logs in via SAML. + Added a SAML setting to support updating room subscriptions each time a user logs in via SAML. Added a SAML setting to support including private rooms in SAML updated subscriptions (whether initial or on each logon). - Autofocus on directory ([#20509](https://github.com/RocketChat/Rocket.Chat/pull/20509)) @@ -9437,7 +13337,7 @@ - Tooltip added for Kebab menu on chat header ([#20116](https://github.com/RocketChat/Rocket.Chat/pull/20116)) - Added the missing Tooltip for kebab menu on chat header. + Added the missing Tooltip for kebab menu on chat header. ![tooltip after](https://user-images.githubusercontent.com/58601732/104031406-b07f4b80-51f2-11eb-87a4-1e8da78a254f.gif) ### 🐛 Bug fixes @@ -9459,7 +13359,7 @@ - Added context check for closing active tabbar for member-list ([#20228](https://github.com/RocketChat/Rocket.Chat/pull/20228)) - When we click on a username and then click on see user's full profile, a tab gets active and shows us the user's profile, the problem occurs when the tab is still active and we try to see another user's profile. In this case, tabbar gets closed. + When we click on a username and then click on see user's full profile, a tab gets active and shows us the user's profile, the problem occurs when the tab is still active and we try to see another user's profile. In this case, tabbar gets closed. To resolve this, added context check for closing action of active tabbar. - Added Margin between status bullet and status label ([#20199](https://github.com/RocketChat/Rocket.Chat/pull/20199)) @@ -9468,8 +13368,8 @@ - Added success message on saving notification preference. ([#20220](https://github.com/RocketChat/Rocket.Chat/pull/20220) by [@Darshilp326](https://github.com/Darshilp326)) - Added success message after saving notification preferences. - + Added success message after saving notification preferences. + https://user-images.githubusercontent.com/55157259/104774617-03ca3e80-579d-11eb-8fa4-990b108dd8d9.mp4 - Admin User Info email verified status ([#20110](https://github.com/RocketChat/Rocket.Chat/pull/20110) by [@bdelwood](https://github.com/bdelwood)) @@ -9478,10 +13378,10 @@ - Change header's favorite icon to filled star ([#20174](https://github.com/RocketChat/Rocket.Chat/pull/20174)) - ### Before: - ![image](https://user-images.githubusercontent.com/27704687/104351819-a60bcd00-54e4-11eb-8b43-7d281a6e5dcb.png) - - ### After: + ### Before: + ![image](https://user-images.githubusercontent.com/27704687/104351819-a60bcd00-54e4-11eb-8b43-7d281a6e5dcb.png) + + ### After: ![image](https://user-images.githubusercontent.com/27704687/104351632-67761280-54e4-11eb-87ba-25b940494bb5.png) - Changed success message for adding custom sound. ([#20272](https://github.com/RocketChat/Rocket.Chat/pull/20272) by [@Darshilp326](https://github.com/Darshilp326)) @@ -9490,24 +13390,24 @@ - Changed success message for ignoring member. ([#19996](https://github.com/RocketChat/Rocket.Chat/pull/19996) by [@Darshilp326](https://github.com/Darshilp326)) - Different messages for ignoring/unignoring will be displayed. - + Different messages for ignoring/unignoring will be displayed. + https://user-images.githubusercontent.com/55157259/103310307-4241c880-4a3d-11eb-8c6c-4c9b99d023db.mp4 - Creation of Omnichannel rooms not working correctly through the Apps when the agent parameter is set ([#19997](https://github.com/RocketChat/Rocket.Chat/pull/19997)) - Engagement dashboard graphs labels superposing each other ([#20267](https://github.com/RocketChat/Rocket.Chat/pull/20267)) - Now after a certain breakpoint, the graphs should stack vertically, and overlapping text rotated. - + Now after a certain breakpoint, the graphs should stack vertically, and overlapping text rotated. + ![image](https://user-images.githubusercontent.com/40830821/105098926-93b40500-5a89-11eb-9a56-2fc3b1552914.png) - Fields overflowing page ([#20287](https://github.com/RocketChat/Rocket.Chat/pull/20287)) - ### Before - ![image](https://user-images.githubusercontent.com/40830821/105246952-c1b14c00-5b52-11eb-8671-cff88edf242d.png) - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/40830821/105246952-c1b14c00-5b52-11eb-8671-cff88edf242d.png) + + ### After ![image](https://user-images.githubusercontent.com/40830821/105247125-0a690500-5b53-11eb-9f3c-d6a68108e336.png) - Fix error that occurs on changing archive status of room ([#20098](https://github.com/RocketChat/Rocket.Chat/pull/20098) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) @@ -9524,7 +13424,7 @@ - Livechat.RegisterGuest method removing unset fields ([#20124](https://github.com/RocketChat/Rocket.Chat/pull/20124) by [@rafaelblink](https://github.com/rafaelblink)) - After changes made on https://github.com/RocketChat/Rocket.Chat/pull/19931, the `Livechat.RegisterGuest` method started removing properties from the visitor inappropriately. The properties that did not receive value were removed from the object. + After changes made on https://github.com/RocketChat/Rocket.Chat/pull/19931, the `Livechat.RegisterGuest` method started removing properties from the visitor inappropriately. The properties that did not receive value were removed from the object. Those changes were made to support the new Contact Form, but now the form has its own method to deal with Contact data so those changes are no longer necessary. - Markdown added for Header Room topic ([#20021](https://github.com/RocketChat/Rocket.Chat/pull/20021)) @@ -9545,18 +13445,18 @@ - Omnichannel - Contact Center form is not validating custom fields properly ([#20196](https://github.com/RocketChat/Rocket.Chat/pull/20196) by [@rafaelblink](https://github.com/rafaelblink)) - The contact form is accepting undefined values in required custom fields when creating or editing contacts, and, the errror message isn't following Rocket.chat design system. - - ### Before - ![image](https://user-images.githubusercontent.com/2493803/104522668-31688980-55dd-11eb-92c5-83f96073edc4.png) - - ### After - - #### New - ![image](https://user-images.githubusercontent.com/2493803/104770494-68f74300-574f-11eb-94a3-c8fd73365308.png) - - - #### Edit + The contact form is accepting undefined values in required custom fields when creating or editing contacts, and, the errror message isn't following Rocket.chat design system. + + ### Before + ![image](https://user-images.githubusercontent.com/2493803/104522668-31688980-55dd-11eb-92c5-83f96073edc4.png) + + ### After + + #### New + ![image](https://user-images.githubusercontent.com/2493803/104770494-68f74300-574f-11eb-94a3-c8fd73365308.png) + + + #### Edit ![image](https://user-images.githubusercontent.com/2493803/104770538-7b717c80-574f-11eb-829f-1ae304103369.png) - Omnichannel Agents unable to take new chats in the queue ([#20022](https://github.com/RocketChat/Rocket.Chat/pull/20022) by [@rafaelblink](https://github.com/rafaelblink)) @@ -9577,15 +13477,15 @@ - Room special name in prompts ([#20277](https://github.com/RocketChat/Rocket.Chat/pull/20277) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - The "Hide room" and "Leave Room" confirmation prompts use the "name" key from the room info. When the setting " - Allow Special Characters in Room Names" is enabled, the prompts show the normalized names instead of those that contain the special characters. - - Changed the value being used from name to fname, which always has the user-set name. - - Previous: - ![Screenshot from 2021-01-20 15-52-29](https://user-images.githubusercontent.com/38764067/105161642-9b31e780-5b37-11eb-8b0c-ec4b1414c948.png) - - Updated: + The "Hide room" and "Leave Room" confirmation prompts use the "name" key from the room info. When the setting " + Allow Special Characters in Room Names" is enabled, the prompts show the normalized names instead of those that contain the special characters. + + Changed the value being used from name to fname, which always has the user-set name. + + Previous: + ![Screenshot from 2021-01-20 15-52-29](https://user-images.githubusercontent.com/38764067/105161642-9b31e780-5b37-11eb-8b0c-ec4b1414c948.png) + + Updated: ![Screenshot from 2021-01-20 15-50-19](https://user-images.githubusercontent.com/38764067/105161627-966d3380-5b37-11eb-9812-3dd9352b4f95.png) - Room's list showing all rooms with same name ([#20176](https://github.com/RocketChat/Rocket.Chat/pull/20176)) @@ -9596,9 +13496,9 @@ - Saving with blank email in edit user ([#20259](https://github.com/RocketChat/Rocket.Chat/pull/20259) by [@RonLek](https://github.com/RonLek)) - Disallows showing a success popup when email field is made blank in Edit User and instead shows the relevant error popup. - - + Disallows showing a success popup when email field is made blank in Edit User and instead shows the relevant error popup. + + https://user-images.githubusercontent.com/28918901/104960749-dbd81680-59fa-11eb-9c7b-2b257936f894.mp4 - Search list filter ([#19937](https://github.com/RocketChat/Rocket.Chat/pull/19937)) @@ -9645,7 +13545,7 @@ - Add translation of Edit Status in all languages ([#19916](https://github.com/RocketChat/Rocket.Chat/pull/19916) by [@sushant52](https://github.com/sushant52)) - Closes [#19915](https://github.com/RocketChat/Rocket.Chat/issues/19915) + Closes [#19915](https://github.com/RocketChat/Rocket.Chat/issues/19915) The profile options menu is well translated in many languages. However, Edit Status is the only button which is not well translated. With this change, the whole profile options will be properly translated in a lot of languages. - Bump axios from 0.18.0 to 0.18.1 ([#20055](https://github.com/RocketChat/Rocket.Chat/pull/20055) by [@dependabot[bot]](https://github.com/dependabot[bot])) @@ -9680,10 +13580,10 @@ - Regression: Announcement bar not showing properly Markdown content ([#20290](https://github.com/RocketChat/Rocket.Chat/pull/20290)) - **Before**: - ![image](https://user-images.githubusercontent.com/27704687/105273746-a4907380-5b7a-11eb-8121-aff665251c44.png) - - **After**: + **Before**: + ![image](https://user-images.githubusercontent.com/27704687/105273746-a4907380-5b7a-11eb-8121-aff665251c44.png) + + **After**: ![image](https://user-images.githubusercontent.com/27704687/105274050-2e404100-5b7b-11eb-93b2-b6282a7bed95.png) - regression: Announcement link open in new tab ([#20435](https://github.com/RocketChat/Rocket.Chat/pull/20435)) @@ -9698,23 +13598,23 @@ - Regression: Change sort icon ([#20177](https://github.com/RocketChat/Rocket.Chat/pull/20177)) - ### Before - ![image](https://user-images.githubusercontent.com/40830821/104366414-1bcd6400-54f8-11eb-9fc7-c6f13f07a61e.png) - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/40830821/104366414-1bcd6400-54f8-11eb-9fc7-c6f13f07a61e.png) + + ### After ![image](https://user-images.githubusercontent.com/40830821/104366542-4cad9900-54f8-11eb-83ca-acb99899515a.png) - Regression: Custom field labels are not displayed properly on Omnichannel Contact Profile form ([#20393](https://github.com/RocketChat/Rocket.Chat/pull/20393) by [@rafaelblink](https://github.com/rafaelblink)) - ### Before - ![image](https://user-images.githubusercontent.com/2493803/105780399-20116c80-5f4f-11eb-9620-0901472e453b.png) - - ![image](https://user-images.githubusercontent.com/2493803/105780420-2e5f8880-5f4f-11eb-8e93-8115ebc685be.png) - - ### After - - ![image](https://user-images.githubusercontent.com/2493803/105780832-1ccab080-5f50-11eb-8042-188dd0c41904.png) - + ### Before + ![image](https://user-images.githubusercontent.com/2493803/105780399-20116c80-5f4f-11eb-9620-0901472e453b.png) + + ![image](https://user-images.githubusercontent.com/2493803/105780420-2e5f8880-5f4f-11eb-8e93-8115ebc685be.png) + + ### After + + ![image](https://user-images.githubusercontent.com/2493803/105780832-1ccab080-5f50-11eb-8042-188dd0c41904.png) + ![image](https://user-images.githubusercontent.com/2493803/105780911-500d3f80-5f50-11eb-96e0-7df3f179dbd5.png) - Regression: ESLint Warning - explicit-function-return-type ([#20434](https://github.com/RocketChat/Rocket.Chat/pull/20434) by [@aditya-mitra](https://github.com/aditya-mitra)) @@ -9731,8 +13631,8 @@ - Regression: Fixed update room avatar issue. ([#20433](https://github.com/RocketChat/Rocket.Chat/pull/20433) by [@Darshilp326](https://github.com/Darshilp326)) - Users can now update their room avatar without any error. - + Users can now update their room avatar without any error. + https://user-images.githubusercontent.com/55157259/105951602-560d3880-6096-11eb-97a5-b5eb9a28b58d.mp4 - Regression: Info Page Icon style and usage graph breaking ([#20180](https://github.com/RocketChat/Rocket.Chat/pull/20180)) @@ -9749,11 +13649,11 @@ - Regression: Unread superposing announcement. ([#20306](https://github.com/RocketChat/Rocket.Chat/pull/20306)) - ### Before - ![image](https://user-images.githubusercontent.com/40830821/105412619-c2f67d80-5c13-11eb-8204-5932ea880c8a.png) - - - ### After + ### Before + ![image](https://user-images.githubusercontent.com/40830821/105412619-c2f67d80-5c13-11eb-8204-5932ea880c8a.png) + + + ### After ![image](https://user-images.githubusercontent.com/40830821/105411176-d1439a00-5c11-11eb-8d1b-ea27c8485214.png) - Regression: User Dropdown margin ([#20222](https://github.com/RocketChat/Rocket.Chat/pull/20222)) @@ -10041,8 +13941,8 @@ - Hightlights validation on Account Preferences page ([#19902](https://github.com/RocketChat/Rocket.Chat/pull/19902) by [@aKn1ghtOut](https://github.com/aKn1ghtOut)) - This PR fixes two issues in the account settings "preferences" panel. - Once set, the "Highlighted Words" setting cannot be reset to an empty string. This was fixed by changing the string validation from checking the length to checking the type of variable. + This PR fixes two issues in the account settings "preferences" panel. + Once set, the "Highlighted Words" setting cannot be reset to an empty string. This was fixed by changing the string validation from checking the length to checking the type of variable. Secondly, it tracks the changes to correctly identify if changes after the last "save changes" action have been made, using an "updates" state variable, instead of just comparing against the initialValue that does not change on clicking "save changes". - Image preview for image URLs on messages ([#19734](https://github.com/RocketChat/Rocket.Chat/pull/19734) by [@g-thome](https://github.com/g-thome)) @@ -10101,14 +14001,10 @@ - Chore: Update Pull Request template ([#19768](https://github.com/RocketChat/Rocket.Chat/pull/19768)) - Improve the template of Pull Requests in order to make it clear reducing duplicated information and removing the visible checklists that were generating noise and misunderstanding with the PR progress. - - - Moved the checklists to inside comments - - - Merge the changelog and proposed changes sections to have a single source of description that goes to the changelog - - - Remove the screenshot section, they can be added inside the description - + Improve the template of Pull Requests in order to make it clear reducing duplicated information and removing the visible checklists that were generating noise and misunderstanding with the PR progress. + - Moved the checklists to inside comments + - Merge the changelog and proposed changes sections to have a single source of description that goes to the changelog + - Remove the screenshot section, they can be added inside the description - Changed the proposed changes title to incentivizing the usage of images and videos - Frontend folder structure ([#19631](https://github.com/RocketChat/Rocket.Chat/pull/19631)) @@ -10143,11 +14039,11 @@ - Regression: Double Scrollbars on tables ([#19980](https://github.com/RocketChat/Rocket.Chat/pull/19980)) - Before: - ![image](https://user-images.githubusercontent.com/40830821/103242719-0ec84680-4936-11eb-87a7-68b6eea8de7b.png) - - - After: + Before: + ![image](https://user-images.githubusercontent.com/40830821/103242719-0ec84680-4936-11eb-87a7-68b6eea8de7b.png) + + + After: ![image](https://user-images.githubusercontent.com/40830821/103242680-ee988780-4935-11eb-99e2-a95de99f78f1.png) - Regression: Failed autolinker and markdown rendering ([#19831](https://github.com/RocketChat/Rocket.Chat/pull/19831)) @@ -10166,7 +14062,7 @@ - Regression: Omnichannel Custom Fields Form no longer working after refactoring ([#19948](https://github.com/RocketChat/Rocket.Chat/pull/19948)) - The Omnichannel `Custom Fields` form is not working anymore after some refactorings on client-side. + The Omnichannel `Custom Fields` form is not working anymore after some refactorings on client-side. When the user clicks on `Custom Field` in the Omnichannel menu, a blank page appears. - Regression: polishing licenses endpoints ([#19981](https://github.com/RocketChat/Rocket.Chat/pull/19981) by [@g-thome](https://github.com/g-thome)) @@ -10365,8 +14261,8 @@ - Bundle Size Client ([#19533](https://github.com/RocketChat/Rocket.Chat/pull/19533)) - temporarily removes some codeblock languages - Moved some libraries to dynamic imports + temporarily removes some codeblock languages + Moved some libraries to dynamic imports Removed some shared code not used on the client side - Forward Omnichannel room to agent in another department ([#19576](https://github.com/RocketChat/Rocket.Chat/pull/19576) by [@mrfigueiredo](https://github.com/mrfigueiredo)) @@ -11447,10 +15343,8 @@ - **2FA:** Password enforcement setting and 2FA protection when saving settings or resetting E2E encryption ([#18640](https://github.com/RocketChat/Rocket.Chat/pull/18640)) - - Increase the 2FA remembering time from 5min to 30min - - - Add new setting to enforce 2FA password fallback (enabled only for new installations) - + - Increase the 2FA remembering time from 5min to 30min + - Add new setting to enforce 2FA password fallback (enabled only for new installations) - Require 2FA to save settings and reset E2E Encryption keys - **Omnichannel:** Allow set other agent status via method `livechat:changeLivechatStatus ` ([#18571](https://github.com/RocketChat/Rocket.Chat/pull/18571)) @@ -11468,7 +15362,7 @@ - 2FA by Email setting showing for the user even when disabled by the admin ([#18473](https://github.com/RocketChat/Rocket.Chat/pull/18473)) - The option to disable/enable the **Two-factor authentication via Email** at `Account > Security > Two Factor Authentication + The option to disable/enable the **Two-factor authentication via Email** at `Account > Security > Two Factor Authentication ` was visible even when the setting **Enable Two Factor Authentication via Email** at `Admin > Accounts > Two Factor Authentication` was disabled leading to misbehavior since the functionality was disabled. - Agents enabledDepartment attribute not set on collection ([#18614](https://github.com/RocketChat/Rocket.Chat/pull/18614) by [@paulobernardoaf](https://github.com/paulobernardoaf)) @@ -11818,16 +15712,13 @@ - Mention autocomplete UI and performance improvements ([#18309](https://github.com/RocketChat/Rocket.Chat/pull/18309)) - * New setting to configure the number of suggestions `Admin > Layout > User Interface > Number of users' autocomplete suggestions` (default 5) - - * The UI shows whenever the user is not a member of the room - - * The UI shows when the suggestion came from the last messages for quick selection/reply - - * The suggestions follow this order: - * The user with the exact username and member of the room - * The user with the exact username but not a member of the room (if allowed to list non-members) - * The users containing the text in username, name or nickname and member of the room + * New setting to configure the number of suggestions `Admin > Layout > User Interface > Number of users' autocomplete suggestions` (default 5) + * The UI shows whenever the user is not a member of the room + * The UI shows when the suggestion came from the last messages for quick selection/reply + * The suggestions follow this order: + * The user with the exact username and member of the room + * The user with the exact username but not a member of the room (if allowed to list non-members) + * The users containing the text in username, name or nickname and member of the room * The users containing the text in username, name or nickname and not a member of the room (if allowed to list non-members) - Message action styles ([#18190](https://github.com/RocketChat/Rocket.Chat/pull/18190)) @@ -12169,10 +16060,10 @@ - Split NOTIFICATIONS_SCHEDULE_DELAY into three separate variables ([#17669](https://github.com/RocketChat/Rocket.Chat/pull/17669) by [@jazztickets](https://github.com/jazztickets)) - Email notification delay can now be customized with the following environment variables: - NOTIFICATIONS_SCHEDULE_DELAY_ONLINE - NOTIFICATIONS_SCHEDULE_DELAY_AWAY - NOTIFICATIONS_SCHEDULE_DELAY_OFFLINE + Email notification delay can now be customized with the following environment variables: + NOTIFICATIONS_SCHEDULE_DELAY_ONLINE + NOTIFICATIONS_SCHEDULE_DELAY_AWAY + NOTIFICATIONS_SCHEDULE_DELAY_OFFLINE Setting the value to -1 disable notifications for that type. - Threads ([#17416](https://github.com/RocketChat/Rocket.Chat/pull/17416)) @@ -12572,11 +16463,11 @@ - **ENTERPRISE:** Omnichannel Last-Chatted Agent Preferred option ([#17666](https://github.com/RocketChat/Rocket.Chat/pull/17666)) - If activated, this feature will store the last agent that assisted each Omnichannel visitor when a conversation is taken. So, when a visitor returns(it works with any entry point, Livechat, Facebook, REST API, and so on) and starts a new chat, the routing system checks: - - 1 - The visitor object for any stored agent that the visitor has previously talked to; - 2 - If a previous agent is not found, the system will try to find a previous conversation of the same visitor. If a room is found, the system will get the previous agent from the room; - + If activated, this feature will store the last agent that assisted each Omnichannel visitor when a conversation is taken. So, when a visitor returns(it works with any entry point, Livechat, Facebook, REST API, and so on) and starts a new chat, the routing system checks: + + 1 - The visitor object for any stored agent that the visitor has previously talked to; + 2 - If a previous agent is not found, the system will try to find a previous conversation of the same visitor. If a room is found, the system will get the previous agent from the room; + After this process, if an agent has been found, the system will check the agent's availability to assist the new chat. If it's not available, then the routing system will get the next available agent in the queue. - **ENTERPRISE:** Support for custom Livechat registration form fields ([#17581](https://github.com/RocketChat/Rocket.Chat/pull/17581)) @@ -12681,12 +16572,9 @@ - Notification sounds ([#17616](https://github.com/RocketChat/Rocket.Chat/pull/17616)) - * Global CDN config was ignored when loading the sound files - - * Upload of custom sounds wasn't getting the file extension correctly - - * Some translations were missing - + * Global CDN config was ignored when loading the sound files + * Upload of custom sounds wasn't getting the file extension correctly + * Some translations were missing * Edit and delete of custom sounds were not working correctly - Omnichannel departments are not saved when the offline channel name is not defined ([#17553](https://github.com/RocketChat/Rocket.Chat/pull/17553)) @@ -12974,19 +16862,14 @@ - Better Push and Email Notification logic ([#17357](https://github.com/RocketChat/Rocket.Chat/pull/17357)) - We are still using the same logic to define which notifications every new message will generate, it takes some servers' settings, users's preferences and subscriptions' settings in consideration to determine who will receive each notification type (desktop, audio, email and mobile push), but now it doesn't check the user's status (online, away, offline) for email and mobile push notifications but send those notifications to a new queue with the following rules: - - - - When the user is online the notification is scheduled to be sent in 120 seconds - - - When the user is away the notification is scheduled to be sent in 120 seconds minus the amount of time he is away - - - When the user is offline the notification is scheduled to be sent right away - - - When the user reads a channel all the notifications for that user are removed (clear queue) - - - When a notification is processed to be sent to a user and there are other scheduled notifications: - - All the scheduled notifications for that user are rescheduled to now + We are still using the same logic to define which notifications every new message will generate, it takes some servers' settings, users's preferences and subscriptions' settings in consideration to determine who will receive each notification type (desktop, audio, email and mobile push), but now it doesn't check the user's status (online, away, offline) for email and mobile push notifications but send those notifications to a new queue with the following rules: + + - When the user is online the notification is scheduled to be sent in 120 seconds + - When the user is away the notification is scheduled to be sent in 120 seconds minus the amount of time he is away + - When the user is offline the notification is scheduled to be sent right away + - When the user reads a channel all the notifications for that user are removed (clear queue) + - When a notification is processed to be sent to a user and there are other scheduled notifications: + - All the scheduled notifications for that user are rescheduled to now - The current notification goes back to the queue to be processed ordered by creation date - Buttons to check/uncheck all users and channels on import ([#17207](https://github.com/RocketChat/Rocket.Chat/pull/17207)) @@ -13349,7 +17232,7 @@ - Translation via MS translate ([#16363](https://github.com/RocketChat/Rocket.Chat/pull/16363) by [@mrsimpson](https://github.com/mrsimpson)) - Adds Microsoft's translation service (https://translator.microsoft.com/) as a provider for translation of messages. + Adds Microsoft's translation service (https://translator.microsoft.com/) as a provider for translation of messages. In addition to implementing the interface (similar to google and DeepL), a small change has been done in order to display the translation provider on the UI. - Two Factor authentication via email ([#15949](https://github.com/RocketChat/Rocket.Chat/pull/15949)) @@ -24867,4 +28750,4 @@ - [@graywolf336](https://github.com/graywolf336) - [@marceloschmidt](https://github.com/marceloschmidt) - [@rodrigok](https://github.com/rodrigok) -- [@sampaiodiego](https://github.com/sampaiodiego) +- [@sampaiodiego](https://github.com/sampaiodiego) \ No newline at end of file diff --git a/_templates/package/new/.eslintrc.json.ejs.t b/_templates/package/new/.eslintrc.json.ejs.t new file mode 100644 index 000000000000..aa7fd8cbc2c0 --- /dev/null +++ b/_templates/package/new/.eslintrc.json.ejs.t @@ -0,0 +1,7 @@ +--- +to: packages/<%= name %>/.eslintrc.json +--- +{ + "extends": ["@rocket.chat/eslint-config"], + "ignorePatterns": ["**/dist"] +} diff --git a/_templates/package/new/package.json.ejs.t b/_templates/package/new/package.json.ejs.t index 948e2ded4be4..44fe58fea9b4 100644 --- a/_templates/package/new/package.json.ejs.t +++ b/_templates/package/new/package.json.ejs.t @@ -11,20 +11,18 @@ to: packages/<%= name %>/package.json "eslint": "^8.12.0", "jest": "^27.5.1", "ts-jest": "^27.1.4", - "typescript": "~4.3.4" + "typescript": "~4.5.5" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "jest": "jest", "build": "rm -rf dist && tsc -p tsconfig.json", - "dev": "tsc -p --watch --preserveWatchOutput tsconfig.json" + "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ "/dist" - ], - "dependencies": { - } + ] } diff --git a/apps/meteor/.docker-mongo/Dockerfile b/apps/meteor/.docker-mongo/Dockerfile index 2e92d78bcb0a..f8556d971d8b 100644 --- a/apps/meteor/.docker-mongo/Dockerfile +++ b/apps/meteor/.docker-mongo/Dockerfile @@ -1,4 +1,4 @@ -FROM node:14.18.3-bullseye-slim +FROM node:14.19.3-bullseye-slim LABEL maintainer="buildmaster@rocket.chat" diff --git a/apps/meteor/.docker/Dockerfile b/apps/meteor/.docker/Dockerfile index fb2cc092daa3..ce06aeb052eb 100644 --- a/apps/meteor/.docker/Dockerfile +++ b/apps/meteor/.docker/Dockerfile @@ -1,4 +1,4 @@ -FROM node:14.18.3-bullseye-slim +FROM node:14.19.3-bullseye-slim LABEL maintainer="buildmaster@rocket.chat" diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 0c7bd994e8f5..99aa4c2eb016 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -1,4 +1,4 @@ -FROM node:14.18.3-alpine3.15 +FROM node:14.19.3-alpine3.15 RUN apk add --no-cache ttf-dejavu @@ -12,7 +12,7 @@ RUN set -x \ && npm install --production \ # Start hack for sharp... && rm -rf npm/node_modules/sharp \ - && npm install sharp@0.29.3 \ + && npm install sharp@0.30.4 \ && mv node_modules/sharp npm/node_modules/sharp \ # End hack for sharp && cd npm \ diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index f4591110f530..c787a6a1c2b9 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 5.0.0-develop +ENV RC_VERSION 5.1.0-develop MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/.eslintignore b/apps/meteor/.eslintignore index cb0aed399c3b..158360ed08eb 100644 --- a/apps/meteor/.eslintignore +++ b/apps/meteor/.eslintignore @@ -3,9 +3,7 @@ data/ tests/e2e/test-failures/ packages/autoupdate/ packages/meteor-streams/ -packages/meteor-timesync/ app/emoji-emojione/generateEmojiIndex.js -app/favico/favico.js packages/rocketchat-livechat/assets/rocketchat-livechat.min.js packages/rocketchat-livechat/assets/rocket-livechat.js app/theme/client/vendor/ diff --git a/apps/meteor/.eslintrc b/apps/meteor/.eslintrc.json similarity index 100% rename from apps/meteor/.eslintrc rename to apps/meteor/.eslintrc.json diff --git a/apps/meteor/.gitignore b/apps/meteor/.gitignore index 3fc755f6bb5b..e8a861e58324 100644 --- a/apps/meteor/.gitignore +++ b/apps/meteor/.gitignore @@ -84,3 +84,4 @@ coverage /data tests/e2e/test-failures/ out.txt +dist diff --git a/apps/meteor/.meteor/packages b/apps/meteor/.meteor/packages index daa663bcce20..292ef0526ba7 100644 --- a/apps/meteor/.meteor/packages +++ b/apps/meteor/.meteor/packages @@ -1,6 +1,9 @@ # Meteor packages used by this project, one per line. + # + # 'meteor add' and 'meteor remove' will edit this file for you, + # but you can also edit it by hand. rocketchat:ddp @@ -10,22 +13,22 @@ accounts-facebook@1.3.3 accounts-github@1.5.0 accounts-google@1.4.0 accounts-meteor-developer@1.5.0 -accounts-password@2.2.0 +accounts-password@2.3.1 accounts-twitter@1.5.0 blaze-html-templates check@1.3.1 ddp-rate-limiter@1.1.0 ddp-common@1.4.0 dynamic-import@0.7.2 -ecmascript@0.16.1 -typescript@4.4.1 -ejson@1.1.1 -email@2.2.0 +ecmascript@0.16.2 +typescript@4.5.4 +ejson@1.1.2 +email@2.2.1 http@2.0.0 logging@1.3.1 meteor-base@1.5.1 mobile-experience@1.1.0 -mongo@1.13.0 +mongo@1.15.0 random@1.2.0 rate-limit@1.0.9 reactive-dict@1.3.0 @@ -46,11 +49,13 @@ konecty:multiple-instances-status konecty:user-presence dispatch:run-as-user -jalik:ufs@1.0.2 -jalik:ufs-gridfs@1.0.2 + +jalik:ufs@1.0.4 +jalik:ufs-gridfs@1.0.5 +jalik:ufs-local@1.0.4 + jparker:gravatar kadira:flow-router -mizzao:timesync mrt:reactive-store mystor:device-detection rocketchat:restivus @@ -64,27 +69,25 @@ rocketchat:tap-i18n@3.0.0 underscore@1.0.10 littledata:synced-cron -edgee:slingshot -jalik:ufs-local@1.0.2 -accounts-base@2.2.1 -accounts-oauth@1.4.0 +accounts-base@2.2.3 +accounts-oauth@1.4.1 autoupdate@1.8.0 -babel-compiler@7.8.0 -google-oauth@1.4.1 +babel-compiler@7.9.0 +google-oauth@1.4.2 htmljs less matb33:collection-hooks meteorhacks:inject-initial -oauth@2.1.1 +oauth@2.1.2 oauth2@1.3.1 routepolicy@1.1.1 sha@1.0.9 templating -webapp@1.13.0 +webapp@1.13.1 webapp-hashing@1.1.0 rocketchat:oauth2-server rocketchat:i18n -rocketchat:postcss dandv:caret-position facts-base@1.0.1 -url +url@1.3.2 +standard-minifier-css diff --git a/apps/meteor/.meteor/release b/apps/meteor/.meteor/release index 0150b11e9f5b..66dd7b664724 100644 --- a/apps/meteor/.meteor/release +++ b/apps/meteor/.meteor/release @@ -1 +1 @@ -METEOR@2.5.6 +METEOR@2.7.3 diff --git a/apps/meteor/.meteor/versions b/apps/meteor/.meteor/versions index 7ba89bd6e6f9..2cb41ac914aa 100644 --- a/apps/meteor/.meteor/versions +++ b/apps/meteor/.meteor/versions @@ -1,21 +1,21 @@ -accounts-base@2.2.1 +accounts-base@2.2.3 accounts-facebook@1.3.3 accounts-github@1.5.0 accounts-google@1.4.0 accounts-meteor-developer@1.5.0 -accounts-oauth@1.4.0 -accounts-password@2.2.0 +accounts-oauth@1.4.1 +accounts-password@2.3.1 accounts-twitter@1.5.0 aldeed:simple-schema@1.5.4 -allow-deny@1.1.0 +allow-deny@1.1.1 autoupdate@1.8.0 -babel-compiler@7.8.0 -babel-runtime@1.5.0 +babel-compiler@7.9.0 +babel-runtime@1.5.1 base64@1.0.12 binary-heap@1.0.11 -blaze@2.5.0 +blaze@2.6.0 blaze-html-templates@1.2.1 -blaze-tools@1.1.2 +blaze-tools@1.1.3 boilerplate-generator@1.7.1 caching-compiler@1.2.2 caching-html-compiler@1.2.1 @@ -33,29 +33,28 @@ deps@1.0.12 diff-sequence@1.1.1 dispatch:run-as-user@1.1.1 dynamic-import@0.7.2 -ecmascript@0.16.1 +ecmascript@0.16.2 ecmascript-runtime@0.8.0 ecmascript-runtime-client@0.12.1 ecmascript-runtime-server@0.11.0 -edgee:slingshot@0.7.1 -ejson@1.1.1 -email@2.2.0 +ejson@1.1.2 +email@2.2.1 es5-shim@4.8.0 -facebook-oauth@1.10.0 +facebook-oauth@1.11.0 facts-base@1.0.1 fetch@0.1.1 geojson-utils@1.0.10 -github-oauth@1.3.2 -google-oauth@1.4.1 +github-oauth@1.4.0 +google-oauth@1.4.2 hot-code-push@1.0.4 -html-tools@1.1.2 +html-tools@1.1.3 htmljs@1.1.1 http@2.0.0 id-map@1.1.1 inter-process-messaging@0.1.1 -jalik:ufs@1.0.2 -jalik:ufs-gridfs@1.0.2 -jalik:ufs-local@1.0.2 +jalik:ufs@1.0.4 +jalik:ufs-gridfs@1.0.5 +jalik:ufs-local@1.0.4 jparker:crypto-core@0.1.0 jparker:crypto-md5@0.1.1 jparker:gravatar@0.5.1 @@ -68,35 +67,34 @@ less@3.0.2 littledata:synced-cron@1.5.1 localstorage@1.2.0 logging@1.3.1 -matb33:collection-hooks@1.1.0 +matb33:collection-hooks@1.1.2 mdg:validation-error@0.5.1 meteor@1.10.0 meteor-base@1.5.1 meteor-developer-oauth@1.3.1 meteorhacks:inject-initial@1.0.5 minifier-css@1.6.0 -minifier-js@2.7.3 -minimongo@1.7.0 -mizzao:timesync@0.3.4 +minifier-js@2.7.4 +minimongo@1.8.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 -modern-browsers@0.1.7 +modern-browsers@0.1.8 modules@0.18.0 -modules-runtime@0.12.0 -mongo@1.13.0 -mongo-decimal@0.1.2 +modules-runtime@0.13.0 +mongo@1.15.0 +mongo-decimal@0.1.3 mongo-dev-server@1.1.0 mongo-id@1.0.8 mrt:reactive-store@0.0.1 mystor:device-detection@0.2.0 nooitaf:colors@1.2.0 -npm-mongo@3.9.1 -oauth@2.1.1 +npm-mongo@4.3.1 +oauth@2.1.2 oauth1@1.5.0 oauth2@1.3.1 -observe-sequence@1.0.19 +observe-sequence@1.0.20 ordered-dict@1.1.0 -ostrio:cookies@2.7.0 +ostrio:cookies@2.7.2 pauli:accounts-linkedin@6.0.0 pauli:linkedin-oauth@6.0.0 promise@0.12.0 @@ -105,7 +103,7 @@ raix:handlebar-helpers@0.2.5 raix:ui-dropped-event@0.0.7 random@1.2.0 rate-limit@1.0.9 -react-fast-refresh@0.2.2 +react-fast-refresh@0.2.3 reactive-dict@1.3.0 reactive-var@1.0.11 reload@1.3.1 @@ -115,7 +113,6 @@ rocketchat:i18n@0.0.1 rocketchat:livechat@0.0.1 rocketchat:mongo-config@0.0.1 rocketchat:oauth2-server@3.0.0 -rocketchat:postcss@1.0.0 rocketchat:restivus@1.0.0 rocketchat:streamer@1.1.0 rocketchat:tap-i18n@3.0.0 @@ -126,19 +123,20 @@ session@1.2.0 sha@1.0.9 shell-server@0.5.0 simple:json-routes@2.3.1 -socket-stream-client@0.4.0 -spacebars@1.2.0 -spacebars-compiler@1.3.0 +socket-stream-client@0.5.0 +spacebars@1.3.0 +spacebars-compiler@1.3.1 +standard-minifier-css@1.8.1 standard-minifier-js@2.8.0 -templating@1.4.1 +templating@1.4.2 templating-compiler@1.4.1 -templating-runtime@1.5.0 -templating-tools@1.2.1 +templating-runtime@1.6.0 +templating-tools@1.2.2 tracker@1.2.0 twitter-oauth@1.3.0 -typescript@4.4.1 +typescript@4.5.4 ui@1.0.13 underscore@1.0.10 url@1.3.2 -webapp@1.13.0 +webapp@1.13.1 webapp-hashing@1.1.0 diff --git a/apps/meteor/.meteorignore b/apps/meteor/.meteorignore index 9b77b349e3fd..7b2dc6e71c31 100644 --- a/apps/meteor/.meteorignore +++ b/apps/meteor/.meteorignore @@ -1,3 +1,4 @@ ee/server/services coverage data +dist diff --git a/apps/meteor/.mocharc.api.js b/apps/meteor/.mocharc.api.js index c7994105d68a..f6c09a541e00 100644 --- a/apps/meteor/.mocharc.api.js +++ b/apps/meteor/.mocharc.api.js @@ -11,8 +11,9 @@ module.exports = { file: 'tests/end-to-end/teardown.js', spec: [ 'tests/unit/app/api/server/v1/**/*.spec.ts', - 'tests/end-to-end/api/*.js', - 'tests/end-to-end/api/*.ts', + 'tests/end-to-end/api/**/*.js', + 'tests/end-to-end/api/**/*.ts', 'tests/end-to-end/apps/*.js', + 'tests/end-to-end/apps/*.ts', ], }; diff --git a/apps/meteor/.mocharc.js b/apps/meteor/.mocharc.js index 6a5010905612..f8118d74f7c9 100644 --- a/apps/meteor/.mocharc.js +++ b/apps/meteor/.mocharc.js @@ -25,6 +25,7 @@ module.exports = { exit: true, spec: [ 'ee/tests/**/*.tests.ts', + 'ee/tests/**/*.spec.ts', 'tests/unit/app/**/*.spec.ts', 'tests/unit/app/**/*.tests.js', 'tests/unit/app/**/*.tests.ts', diff --git a/apps/meteor/.scripts/start.js b/apps/meteor/.scripts/start.js deleted file mode 100644 index a29e7bb1dbf1..000000000000 --- a/apps/meteor/.scripts/start.js +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env node - -const path = require('path'); -const fs = require('fs'); -const { spawn } = require('child_process'); -const net = require('net'); - -const processes = []; - -const baseDir = path.resolve(__dirname, '..'); -const srcDir = path.resolve(baseDir); - -const isPortTaken = (port) => - new Promise((resolve, reject) => { - const tester = net - .createServer() - .once('error', (err) => (err.code === 'EADDRINUSE' ? resolve(true) : reject(err))) - .once('listening', () => tester.once('close', () => resolve(false)).close()) - .listen(port); - }); - -const waitPortRelease = (port, count = 0) => - new Promise((resolve, reject) => { - isPortTaken(port).then((taken) => { - if (!taken) { - return resolve(); - } - if (count > 60) { - return reject(); - } - console.log('Port', port, 'not released, waiting 1s...'); - setTimeout(() => { - waitPortRelease(port, ++count) - .then(resolve) - .catch(reject); - }, 1000); - }); - }); - -const appOptions = { - env: { - PORT: 3000, - ROOT_URL: 'http://localhost:3000', - }, -}; - -let killingAllProcess = false; -function killAllProcesses(mainExitCode) { - if (killingAllProcess) { - return; - } - killingAllProcess = true; - - processes.forEach((p) => { - console.log('Killing process', p.pid); - p.kill(); - }); - - waitPortRelease(appOptions.env.PORT) - .then(() => { - console.log(`Port ${appOptions.env.PORT} was released, exiting with code ${mainExitCode}`); - process.exit(mainExitCode); - }) - .catch((error) => { - console.error(`Error waiting port ${appOptions.env.PORT} to be released, exiting with code ${mainExitCode}`); - console.error(error); - process.exit(mainExitCode); - }); -} - -function startProcess(opts) { - console.log('Starting process', opts.name, opts.command, opts.params, opts.options.cwd); - const proc = spawn(opts.command, opts.params, opts.options); - processes.push(proc); - - if (opts.onData) { - proc.stdout.on('data', opts.onData); - } - - if (!opts.silent) { - proc.stdout.pipe(process.stdout); - proc.stderr.pipe(process.stderr); - } - - if (opts.logFile) { - const logStream = fs.createWriteStream(opts.logFile, { flags: 'a' }); - proc.stdout.pipe(logStream); - proc.stderr.pipe(logStream); - } - - proc.on('exit', function (code, signal) { - processes.splice(processes.indexOf(proc), 1); - - if (code != null) { - console.log(opts.name, `exited with code ${code}`); - } else { - console.log(opts.name, `exited with signal ${signal}`); - } - - killAllProcesses(code); - }); -} - -function startRocketChat() { - return new Promise((resolve) => { - const waitServerRunning = (message) => { - if (message.toString().match('SERVER RUNNING')) { - return resolve(); - } - }; - - startProcess({ - name: 'Meteor App', - command: 'node', - params: ['/tmp/build-test/bundle/main.js'], - onData: waitServerRunning, - options: { - cwd: srcDir, - env: { - ...appOptions.env, - ...process.env, - }, - }, - }); - }); -} - -async function startMicroservices() { - const waitStart = (resolve) => (message) => { - if (message.toString().match('NetworkBroker started successfully')) { - return resolve(); - } - }; - const startService = (name) => { - return new Promise((resolve) => { - const cwd = - name === 'ddp-streamer' - ? path.resolve(srcDir, '..', '..', 'ee', 'apps', name, 'dist', 'ee', 'apps', name) - : path.resolve(srcDir, 'ee', 'server', 'services', 'dist', 'ee', 'server', 'services', name); - - startProcess({ - name: `${name} service`, - command: 'node', - params: [name === 'ddp-streamer' ? 'src/service.js' : 'service.js'], - onData: waitStart(resolve), - options: { - cwd, - env: { - ...appOptions.env, - ...process.env, - PORT: 4000, - }, - }, - }); - }); - }; - - await Promise.all([ - startService('account'), - startService('authorization'), - startService('ddp-streamer'), - startService('presence'), - startService('stream-hub'), - ]); -} - -function startTests(options = []) { - const testOption = options.find((i) => i.startsWith('--test=')); - const testParam = testOption ? testOption.replace('--test=', '') : 'test'; - - console.log(`Running test "npm run ${testParam}"`); - - startProcess({ - name: 'Tests', - command: 'npm', - params: ['run', testParam], - options: { - env: { - ...process.env, - NODE_PATH: `${process.env.NODE_PATH + path.delimiter + srcDir + path.delimiter + srcDir}/node_modules`, - }, - }, - }); -} - -(async () => { - const [, , ...options] = process.argv; - - await startRocketChat(); - - if (options.includes('--enterprise')) { - await startMicroservices(); - } - - startTests(options); -})(); diff --git a/apps/meteor/.storybook/.eslintrc.js b/apps/meteor/.storybook/.eslintrc.js deleted file mode 120000 index 8589dc8c5324..000000000000 --- a/apps/meteor/.storybook/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -../client/.eslintrc.js \ No newline at end of file diff --git a/apps/meteor/.storybook/.eslintrc.json b/apps/meteor/.storybook/.eslintrc.json new file mode 100644 index 000000000000..0f030e2e46f2 --- /dev/null +++ b/apps/meteor/.storybook/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../client/.eslintrc.json" +} diff --git a/apps/meteor/app/2fa/server/functions/resetTOTP.ts b/apps/meteor/app/2fa/server/functions/resetTOTP.ts index 25da60ca2b1d..decce3432be5 100644 --- a/apps/meteor/app/2fa/server/functions/resetTOTP.ts +++ b/apps/meteor/app/2fa/server/functions/resetTOTP.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import * as Mailer from '../../../mailer'; -import { Users } from '../../../models/server/raw/index'; const sendResetNotification = async function (uid: string): Promise { const user = await Users.findOneById>(uid, { @@ -45,9 +45,10 @@ const sendResetNotification = async function (uid: string): Promise { html, } as any); } catch (error) { - throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${error.message}`, { + const message = error instanceof Error ? error.message : String(error); + throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { function: 'resetUserTOTP', - message: error.message, + message, }); } }); diff --git a/apps/meteor/app/2fa/server/lib/totp.ts b/apps/meteor/app/2fa/server/lib/totp.ts index ad831ce37029..c06a1ffc9b17 100644 --- a/apps/meteor/app/2fa/server/lib/totp.ts +++ b/apps/meteor/app/2fa/server/lib/totp.ts @@ -2,8 +2,7 @@ import { SHA256 } from 'meteor/sha'; import { Random } from 'meteor/random'; import speakeasy from 'speakeasy'; -// @ts-expect-error -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { settings } from '../../../settings/server'; export const TOTP = { diff --git a/apps/meteor/app/2fa/server/loginHandler.ts b/apps/meteor/app/2fa/server/loginHandler.ts index cf17953cd94a..8535dfa4f3d2 100644 --- a/apps/meteor/app/2fa/server/loginHandler.ts +++ b/apps/meteor/app/2fa/server/loginHandler.ts @@ -29,10 +29,8 @@ callbacks.add( if (login.type === 'resume' || login.type === 'proxy' || login.methodName === 'verifyEmail') { return login; } - - const [loginArgs] = login.methodArguments; // CAS login doesn't yet support 2FA. - if (loginArgs.cas) { + if (login.type === 'cas') { return login; } @@ -40,6 +38,7 @@ callbacks.add( return login; } + const [loginArgs] = login.methodArguments; const { totp } = loginArgs; checkCodeForUser({ diff --git a/apps/meteor/app/action-links/server/actionLinkHandler.ts b/apps/meteor/app/action-links/server/actionLinkHandler.ts index 331756a8aafe..51c196ffb3b7 100644 --- a/apps/meteor/app/action-links/server/actionLinkHandler.ts +++ b/apps/meteor/app/action-links/server/actionLinkHandler.ts @@ -1,10 +1,14 @@ import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; import { actionLinks } from './lib/actionLinks'; // Action Links Handler. This method will be called off the client. Meteor.methods({ actionLinkHandler(name, messageId) { + check(messageId, String); + check(name, String); + if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'actionLinkHandler' }); } diff --git a/apps/meteor/app/action-links/server/lib/actionLinks.ts b/apps/meteor/app/action-links/server/lib/actionLinks.ts index 634e5a54de9f..4b441df64fcd 100644 --- a/apps/meteor/app/action-links/server/lib/actionLinks.ts +++ b/apps/meteor/app/action-links/server/lib/actionLinks.ts @@ -11,7 +11,7 @@ function getMessageById(messageId: IMessage['_id']): IMessage | undefined { } return Promise.await(getMessageForUser(messageId, user)); } catch (e) { - throw new Meteor.Error(e.message, 'Invalid message', { + throw new Meteor.Error(e instanceof Error ? e.message : String(e), 'Invalid message', { function: 'actionLinks.getMessage', }); } diff --git a/apps/meteor/app/analytics/client/trackEvents.js b/apps/meteor/app/analytics/client/trackEvents.js index 28c3987dc1b9..86cd69ea95a4 100644 --- a/apps/meteor/app/analytics/client/trackEvents.js +++ b/apps/meteor/app/analytics/client/trackEvents.js @@ -4,7 +4,7 @@ import { Tracker } from 'meteor/tracker'; import { settings } from '../../settings'; import { callbacks } from '../../../lib/callbacks'; -import { ChatRoom } from '../../models'; +import { ChatRoom } from '../../models/client'; function trackEvent(category, action, label) { if (window._paq) { diff --git a/apps/meteor/app/api/server/api.d.ts b/apps/meteor/app/api/server/api.d.ts index ebc4871610e3..594f956edaea 100644 --- a/apps/meteor/app/api/server/api.d.ts +++ b/apps/meteor/app/api/server/api.d.ts @@ -69,6 +69,7 @@ type Options = ( } ) & { validateParams?: ValidateFunction; + authOrAnonRequired?: true; }; type PartialThis = { @@ -76,6 +77,15 @@ type PartialThis = { readonly response: Response; }; +type UserInfo = IUser & { + email?: string; + settings: { + profile: {}; + preferences: unknown; + }; + avatarUrl: string; +}; + type ActionThis = { readonly requestIp: string; urlParams: UrlParams; @@ -113,17 +123,9 @@ type ActionThis({ object, userId }: { object: { [key: string]: unknown }; userId: string }): { [key: string]: unknown } & T; + getUserInfo( + me: IUser, + ): TOptions extends { authRequired: true } ? UserInfo : TOptions extends { authOrAnonRequired: true } ? UserInfo | undefined : undefined; composeRoomWithLastMessage(room: IRoom, userId: string): IRoom; } & (TOptions extends { authRequired: true } ? { @@ -131,6 +133,12 @@ type ActionThis })[]> { + const uids = files.map(({ userId }) => userId).filter(Boolean); + + const users = await Users.findByIds(uids, { projection: { name: 1, username: 1 } }).toArray(); + + return files.map((file) => { + const user = users.find(({ _id: userId }) => file.userId && userId === file.userId); + if (!user) { + return file; + } + return { + ...file, + user, + }; + }); +} diff --git a/apps/meteor/app/api/server/helpers/insertUserObject.ts b/apps/meteor/app/api/server/helpers/insertUserObject.ts deleted file mode 100644 index f3ea765534bf..000000000000 --- a/apps/meteor/app/api/server/helpers/insertUserObject.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Users } from '../../../models/server'; -import { API } from '../api'; - -API.helperMethods.set( - 'insertUserObject', - Meteor.bindEnvironment(function _addUserToObject({ object, userId }: { object: { [key: string]: unknown }; userId: string }) { - // Maybe `object: { [key: string]: Meteor.User }`? - const user = Users.findOneById(userId); - if (user) { - object.user = { - _id: userId, - username: user.username, - name: user.name, - }; - } - - return object; - }), -); diff --git a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts index 4710aac5bc25..25371d232e19 100644 --- a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts +++ b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts @@ -11,6 +11,13 @@ const pathAllowConf = { 'def': ['$or', '$and', '$regex'], }; +const warnFields = + process.env.NODE_ENV !== 'production' || process.env.SHOW_WARNINGS === 'true' + ? (...rest: any): void => { + console.warn(...rest, new Error().stack); + } + : new Function(); + API.helperMethods.set( 'parseJsonQuery', function _parseJsonQuery(this: { @@ -48,6 +55,7 @@ API.helperMethods.set( let fields: Record | undefined; if (this.queryParams.fields) { + warnFields('attribute fields is deprecated'); try { fields = JSON.parse(this.queryParams.fields) as Record; @@ -98,7 +106,8 @@ API.helperMethods.set( let query: Record = {}; if (this.queryParams.query) { - this.logger.warn('attribute query is deprecated'); + warnFields('attribute query is deprecated'); + try { query = EJSON.parse(this.queryParams.query); query = clean(query, pathAllowConf.def); diff --git a/apps/meteor/app/api/server/index.ts b/apps/meteor/app/api/server/index.ts index 3e2a60d115cb..53d0bbbad0cc 100644 --- a/apps/meteor/app/api/server/index.ts +++ b/apps/meteor/app/api/server/index.ts @@ -5,7 +5,6 @@ import './helpers/getLoggedInUser'; import './helpers/getPaginationItems'; import './helpers/getUserFromParams'; import './helpers/getUserInfo'; -import './helpers/insertUserObject'; import './helpers/isUserFromParams'; import './helpers/parseJsonQuery'; import './helpers/requestParams'; diff --git a/apps/meteor/app/api/server/lib/emailInbox.ts b/apps/meteor/app/api/server/lib/emailInbox.ts index c679c3d714ee..006dfb1b0d73 100644 --- a/apps/meteor/app/api/server/lib/emailInbox.ts +++ b/apps/meteor/app/api/server/lib/emailInbox.ts @@ -1,7 +1,7 @@ import { IEmailInbox } from '@rocket.chat/core-typings'; -import { InsertOneWriteOpResult, UpdateWriteOpResult, WithId } from 'mongodb'; +import { InsertOneResult, UpdateResult, WithId } from 'mongodb'; +import { EmailInbox } from '@rocket.chat/models'; -import { EmailInbox } from '../../../models/server/raw'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Users } from '../../../models/server'; @@ -26,15 +26,13 @@ export const findEmailInboxes = async ({ if (!(await hasPermissionAsync(userId, 'manage-email-inbox'))) { throw new Error('error-not-allowed'); } - const cursor = EmailInbox.find(query, { + const { cursor, totalCount } = EmailInbox.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const emailInboxes = await cursor.toArray(); + const [emailInboxes, total] = await Promise.all([cursor.toArray(), totalCount]); return { emailInboxes, @@ -53,7 +51,7 @@ export const findOneEmailInbox = async ({ userId, _id }: { userId: string; _id: export const insertOneEmailInbox = async ( userId: string, emailInboxParams: Pick, -): Promise>> => { +): Promise>> => { const obj = { ...emailInboxParams, _createdAt: new Date(), @@ -66,7 +64,7 @@ export const insertOneEmailInbox = async ( export const updateEmailInbox = async ( userId: string, emailInboxParams: Pick, -): Promise> | UpdateWriteOpResult> => { +): Promise> | UpdateResult> => { const { _id, active, name, email, description, senderInfo, department, smtp, imap } = emailInboxParams; const emailInbox = await findOneEmailInbox({ userId, _id }); diff --git a/apps/meteor/app/api/server/lib/emoji-custom.ts b/apps/meteor/app/api/server/lib/emoji-custom.ts index 2c3f660c67f1..b9450338503e 100644 --- a/apps/meteor/app/api/server/lib/emoji-custom.ts +++ b/apps/meteor/app/api/server/lib/emoji-custom.ts @@ -1,29 +1,26 @@ -import { IEmojiCustom, ILivechatDepartmentRecord } from '@rocket.chat/core-typings'; -import { FilterQuery, SortOptionObject } from 'mongodb'; - -import { EmojiCustom } from '../../../models/server/raw'; +import { IEmojiCustom } from '@rocket.chat/core-typings'; +import { Filter, FindOptions } from 'mongodb'; +import { EmojiCustom } from '@rocket.chat/models'; export async function findEmojisCustom({ query = {}, pagination: { offset, count, sort }, }: { - query: FilterQuery; - pagination: { offset: number; count: number; sort: SortOptionObject }; + query: Filter; + pagination: { offset: number; count: number; sort: FindOptions['sort'] }; }): Promise<{ emojis: IEmojiCustom[]; count: number; offset: any; total: number; }> { - const cursor = EmojiCustom.find(query, { + const { cursor, totalCount } = EmojiCustom.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const emojis = await cursor.toArray(); + const [emojis, total] = await Promise.all([cursor.toArray(), totalCount]); return { emojis, diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index c9cf0263c5d8..f20075823bb5 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -13,26 +13,20 @@ type UploadResult = { fileBuffer: Buffer; }; -export const getUploadFormData = async >( +export const getUploadFormData = async < + T extends string, + K extends Record = Record, + V extends ValidateFunction = ValidateFunction, +>( { request }: { request: Request }, options: { field?: T; validate?: V; } = {}, -): Promise< - [ - UploadResult, - K extends unknown - ? { - [k: string]: string; - } - : K, - T, - ] -> => +): Promise<[UploadResult, K, T]> => new Promise((resolve, reject) => { const bb = busboy({ headers: request.headers, defParamCharset: 'utf8' }); - const fields: { [K: string]: string } = Object.create(null); + const fields = Object.create(null) as K; let uploadedFile: UploadResult | undefined; @@ -69,7 +63,7 @@ export const getUploadFormData = async { + bb.on('field', (fieldname: keyof K, value: K[keyof K]) => { fields[fieldname] = value; }); diff --git a/apps/meteor/app/api/server/lib/integrations.ts b/apps/meteor/app/api/server/lib/integrations.ts index c785984380e5..517b64ad3440 100644 --- a/apps/meteor/app/api/server/lib/integrations.ts +++ b/apps/meteor/app/api/server/lib/integrations.ts @@ -1,6 +1,6 @@ import type { IIntegration, IUser } from '@rocket.chat/core-typings'; +import { Integrations } from '@rocket.chat/models'; -import { Integrations } from '../../../models/server/raw'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; const hasIntegrationsPermission = async (userId: string, integration: IIntegration): Promise => { diff --git a/apps/meteor/app/api/server/lib/messages.ts b/apps/meteor/app/api/server/lib/messages.ts index 194bdabdd274..d6955ce7b933 100644 --- a/apps/meteor/app/api/server/lib/messages.ts +++ b/apps/meteor/app/api/server/lib/messages.ts @@ -1,7 +1,8 @@ -import { IMessage, IUser } from '@rocket.chat/core-typings'; +import type { FindOptions } from 'mongodb'; +import type { IMessage, IUser } from '@rocket.chat/core-typings'; +import { Rooms, Messages, Users } from '@rocket.chat/models'; import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; -import { Rooms, Messages, Users } from '../../../models/server/raw'; import { getValue } from '../../../settings/server/raw'; export async function findMentionedMessages({ @@ -11,7 +12,7 @@ export async function findMentionedMessages({ }: { uid: string; roomId: string; - pagination: { offset: number; count: number; sort: [string, number][] }; + pagination: { offset: number; count: number; sort: FindOptions['sort'] }; }): Promise<{ messages: IMessage[]; count: number; @@ -19,23 +20,21 @@ export async function findMentionedMessages({ total: number; }> { const room = await Rooms.findOneById(roomId); - if (!(await canAccessRoomAsync(room, { _id: uid }))) { + if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) { throw new Error('error-not-allowed'); } - const user: IUser | null = await Users.findOneById(uid, { fields: { username: 1 } }); + const user = await Users.findOneById>(uid, { projection: { username: 1 } }); if (!user) { throw new Error('invalid-user'); } - const cursor = await Messages.findVisibleByMentionAndRoomId(user.username, roomId, { + const { cursor, totalCount } = Messages.findVisibleByMentionAndRoomId(user.username, roomId, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const messages = await cursor.toArray(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return { messages, @@ -52,7 +51,7 @@ export async function findStarredMessages({ }: { uid: string; roomId: string; - pagination: { offset: number; count: number; sort: [string, number][] }; + pagination: { offset: number; count: number; sort: FindOptions['sort'] }; }): Promise<{ messages: IMessage[]; count: number; @@ -60,23 +59,21 @@ export async function findStarredMessages({ total: number; }> { const room = await Rooms.findOneById(roomId); - if (!(await canAccessRoomAsync(room, { _id: uid }))) { + if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) { throw new Error('error-not-allowed'); } - const user = await Users.findOneById(uid, { fields: { username: 1 } }); + const user = await Users.findOneById>(uid, { projection: { username: 1 } }); if (!user) { throw new Error('invalid-user'); } - const cursor = await Messages.findStarredByUserAtRoom(uid, roomId, { + const { cursor, totalCount } = Messages.findStarredByUserAtRoom(uid, roomId, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const messages = await cursor.toArray(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return { messages, @@ -121,7 +118,7 @@ export async function findSnippetedMessages({ }: { uid: string; roomId: string; - pagination: { offset: number; count: number; sort: [string, number][] }; + pagination: { offset: number; count: number; sort: FindOptions['sort'] }; }): Promise<{ messages: IMessage[]; count: number; @@ -133,19 +130,17 @@ export async function findSnippetedMessages({ } const room = await Rooms.findOneById(roomId); - if (!(await canAccessRoomAsync(room, { _id: uid }))) { + if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) { throw new Error('error-not-allowed'); } - const cursor = await Messages.findSnippetedByRoom(roomId, { + const { cursor, totalCount } = Messages.findSnippetedByRoom(roomId, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const messages = await cursor.toArray(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return { messages, @@ -164,7 +159,7 @@ export async function findDiscussionsFromRoom({ uid: string; roomId: string; text: string; - pagination: { offset: number; count: number; sort: [string, number][] }; + pagination: { offset: number; count: number; sort: FindOptions['sort'] }; }): Promise<{ messages: IMessage[]; count: number; @@ -173,19 +168,17 @@ export async function findDiscussionsFromRoom({ }> { const room = await Rooms.findOneById(roomId); - if (!(await canAccessRoomAsync(room, { _id: uid }))) { + if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) { throw new Error('error-not-allowed'); } - const cursor = Messages.findDiscussionsByRoomAndText(roomId, text, { + const { cursor, totalCount } = await Messages.findDiscussionsByRoomAndText(roomId, text, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const messages = await cursor.toArray(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return { messages, diff --git a/apps/meteor/app/api/server/lib/rooms.ts b/apps/meteor/app/api/server/lib/rooms.ts index 5ffd4798bb78..fa69e6af4be2 100644 --- a/apps/meteor/app/api/server/lib/rooms.ts +++ b/apps/meteor/app/api/server/lib/rooms.ts @@ -1,7 +1,7 @@ import { IRoom, ISubscription } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; import { hasPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Rooms } from '../../../models/server/raw'; import { Subscriptions } from '../../../models/server'; import { adminFields } from '../../../../lib/rooms/adminFields'; @@ -31,24 +31,24 @@ export async function findAdminRooms({ const typesToRemove = ['discussions', 'teams']; const showTypes = Array.isArray(types) ? types.filter((type) => !typesToRemove.includes(type)) : []; const options = { - fields: adminFields, + projection: adminFields, sort: sort || { default: -1, name: 1 }, skip: offset, limit: count, }; - let cursor; + let result; if (name && showTypes.length) { - cursor = Rooms.findByNameContainingAndTypes(name, showTypes, discussion, includeTeams, showOnlyTeams, options); + result = Rooms.findByNameContainingAndTypes(name, showTypes, discussion, includeTeams, showOnlyTeams, options); } else if (showTypes.length) { - cursor = Rooms.findByTypes(showTypes, discussion, includeTeams, showOnlyTeams, options); + result = Rooms.findByTypes(showTypes, discussion, includeTeams, showOnlyTeams, options); } else { - cursor = Rooms.findByNameContaining(name, discussion, includeTeams, showOnlyTeams, options); + result = Rooms.findByNameContaining(name, discussion, includeTeams, showOnlyTeams, options); } - const total = await cursor.count(); + const { cursor, totalCount } = result; - const rooms = await cursor.toArray(); + const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]); return { rooms, @@ -63,14 +63,14 @@ export async function findAdminRoom({ uid, rid }: { uid: string; rid: string }): throw new Error('error-not-authorized'); } - return Rooms.findOneById(rid, { fields: adminFields }); + return Rooms.findOneById(rid, { projection: adminFields }); } export async function findChannelAndPrivateAutocomplete({ uid, selector }: { uid: string; selector: { name: string } }): Promise<{ items: IRoom[]; }> { const options = { - fields: { + projection: { _id: 1, fname: 1, name: 1, @@ -101,7 +101,7 @@ export async function findAdminRoomsAutocomplete({ uid, selector }: { uid: strin throw new Error('error-not-authorized'); } const options = { - fields: { + projection: { _id: 1, fname: 1, name: 1, @@ -138,7 +138,7 @@ export async function findChannelAndPrivateAutocompleteWithPagination({ .map((item: Pick) => item.rid); const options = { - fields: { + projection: { _id: 1, fname: 1, name: 1, @@ -150,10 +150,9 @@ export async function findChannelAndPrivateAutocompleteWithPagination({ limit: count, }; - const cursor = await Rooms.findRoomsWithoutDiscussionsByRoomIds(selector.name, userRoomsIds, options); + const { cursor, totalCount } = Rooms.findPaginatedRoomsWithoutDiscussionsByRoomIds(selector.name, userRoomsIds, options); - const total = await cursor.count(); - const rooms = await cursor.toArray(); + const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]); return { items: rooms, @@ -165,7 +164,7 @@ export async function findRoomsAvailableForTeams({ uid, name }: { uid: string; n items: IRoom[]; }> { const options = { - fields: { + projection: { _id: 1, fname: 1, name: 1, diff --git a/apps/meteor/app/api/server/lib/users.ts b/apps/meteor/app/api/server/lib/users.ts index 7762b9b20a18..d683df20f192 100644 --- a/apps/meteor/app/api/server/lib/users.ts +++ b/apps/meteor/app/api/server/lib/users.ts @@ -1,10 +1,12 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { ILivechatDepartmentRecord, IUser } from '@rocket.chat/core-typings'; -import { FilterQuery } from 'mongodb'; +import { IUser } from '@rocket.chat/core-typings'; +import { Filter } from 'mongodb'; +import { Users } from '@rocket.chat/models'; +import type { Mongo } from 'meteor/mongo'; -import { Users } from '../../../models/server/raw'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +type UserAutoComplete = Required>; export async function findUsersToAutocomplete({ uid, selector, @@ -12,11 +14,11 @@ export async function findUsersToAutocomplete({ uid: string; selector: { exceptions: string[]; - conditions: FilterQuery; + conditions: Filter; term: string; }; }): Promise<{ - items: Required>[]; + items: UserAutoComplete[]; }> { if (!(await hasPermissionAsync(uid, 'view-outside-room'))) { return { items: [] }; @@ -37,7 +39,7 @@ export async function findUsersToAutocomplete({ limit: 10, }; - const users = await Users.findActiveByUsernameOrNameRegexWithExceptionsAndConditions( + const users = await Users.findActiveByUsernameOrNameRegexWithExceptionsAndConditions( new RegExp(escapeRegExp(selector.term), 'i'), exceptions, conditions, @@ -79,6 +81,7 @@ export function getNonEmptyFields(fields: { [k: string]: 1 | 0 }): { [k: string] active: 1, avatarETag: 1, lastLogin: 1, + type: 1, } as const; if (!fields || Object.keys(fields).length === 0) { @@ -88,29 +91,19 @@ export function getNonEmptyFields(fields: { [k: string]: 1 | 0 }): { [k: string] return { ...defaultFields, ...fields }; } -const _defaultQuery = { - $or: [ - { 'emails.address': { $regex: '', $options: 'i' } }, - { username: { $regex: '', $options: 'i' } }, - { name: { $regex: '', $options: 'i' } }, - ], -}; - /** * get the default query if **query** is empty (`{}`) or `undefined`/`null` * @param {Object|null|undefined} query the query from parsed jsonQuery */ - -type Query = { [k: string]: unknown }; -export function getNonEmptyQuery(query: Query): typeof _defaultQuery | (typeof _defaultQuery & Query) { - const defaultQuery = { - $or: [ - { 'emails.address': { $regex: '', $options: 'i' } }, - { username: { $regex: '', $options: 'i' } }, - { name: { $regex: '', $options: 'i' } }, - ], +export function getNonEmptyQuery(query: Mongo.Query | undefined | null, canSeeAllUserInfo?: boolean): Mongo.Query { + const defaultQuery: Mongo.Query = { + $or: [{ username: { $regex: '', $options: 'i' } }, { name: { $regex: '', $options: 'i' } }], }; + if (canSeeAllUserInfo) { + defaultQuery.$or?.push({ 'emails.address': { $regex: '', $options: 'i' } }); + } + if (!query || Object.keys(query).length === 0) { return defaultQuery; } diff --git a/apps/meteor/app/api/server/lib/webdav.ts b/apps/meteor/app/api/server/lib/webdav.ts index 7f3701a7d695..db0c47a64e2d 100644 --- a/apps/meteor/app/api/server/lib/webdav.ts +++ b/apps/meteor/app/api/server/lib/webdav.ts @@ -1,6 +1,5 @@ import type { IWebdavAccount } from '@rocket.chat/core-typings'; - -import { WebdavAccounts } from '../../../models/server/raw'; +import { WebdavAccounts } from '@rocket.chat/models'; export async function findWebdavAccountsByUserId({ uid }: { uid: string }): Promise { return WebdavAccounts.findWithUserId(uid, { diff --git a/apps/meteor/app/api/server/middlewares/authentication.ts b/apps/meteor/app/api/server/middlewares/authentication.ts new file mode 100644 index 000000000000..268000ef4cb8 --- /dev/null +++ b/apps/meteor/app/api/server/middlewares/authentication.ts @@ -0,0 +1,33 @@ +import { Request, Response, NextFunction } from 'express'; + +import { Users } from '../../../models/server'; +import { oAuth2ServerAuth } from '../../../oauth2-server-config/server/oauth/oauth2-server'; + +export type AuthenticationMiddlewareConfig = { + rejectUnauthorized: boolean; +}; + +export const defaultAuthenticationMiddlewareConfig = { + rejectUnauthorized: true, +}; + +export function authenticationMiddleware(config: AuthenticationMiddlewareConfig = defaultAuthenticationMiddlewareConfig) { + return (req: Request, res: Response, next: NextFunction): void => { + const { 'x-user-id': userId, 'x-auth-token': authToken } = req.headers; + + if (userId && authToken) { + req.user = Users.findOneByIdAndLoginToken(userId, authToken); + } else { + req.user = oAuth2ServerAuth(req)?.user; + } + + if (config.rejectUnauthorized && !req.user) { + res.status(401).send('Unauthorized'); + return; + } + + req.userId = req?.user?._id; + + next(); + }; +} diff --git a/apps/meteor/app/api/server/v1/autotranslate.js b/apps/meteor/app/api/server/v1/autotranslate.js deleted file mode 100644 index bb05ff15bfb6..000000000000 --- a/apps/meteor/app/api/server/v1/autotranslate.js +++ /dev/null @@ -1,80 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { API } from '../api'; -import { settings } from '../../../settings/server'; -import { Messages } from '../../../models/server'; - -API.v1.addRoute( - 'autotranslate.getSupportedLanguages', - { authRequired: true }, - { - get() { - if (!settings.get('AutoTranslate_Enabled')) { - return API.v1.failure('AutoTranslate is disabled.'); - } - const { targetLanguage } = this.queryParams; - const languages = Meteor.runAsUser(this.userId, () => Meteor.call('autoTranslate.getSupportedLanguages', targetLanguage)); - - return API.v1.success({ languages: languages || [] }); - }, - }, -); - -API.v1.addRoute( - 'autotranslate.saveSettings', - { authRequired: true }, - { - post() { - const { roomId, field, value, defaultLanguage } = this.bodyParams; - if (!settings.get('AutoTranslate_Enabled')) { - return API.v1.failure('AutoTranslate is disabled.'); - } - - if (!roomId) { - return API.v1.failure('The bodyParam "roomId" is required.'); - } - if (!field) { - return API.v1.failure('The bodyParam "field" is required.'); - } - if (value === undefined) { - return API.v1.failure('The bodyParam "value" is required.'); - } - if (field === 'autoTranslate' && typeof value !== 'boolean') { - return API.v1.failure('The bodyParam "autoTranslate" must be a boolean.'); - } - if (field === 'autoTranslateLanguage' && typeof value !== 'string') { - return API.v1.failure('The bodyParam "autoTranslateLanguage" must be a string.'); - } - - Meteor.runAsUser(this.userId, () => - Meteor.call('autoTranslate.saveSettings', roomId, field, value === true ? '1' : String(value).valueOf(), { defaultLanguage }), - ); - - return API.v1.success(); - }, - }, -); - -API.v1.addRoute( - 'autotranslate.translateMessage', - { authRequired: true }, - { - post() { - const { messageId, targetLanguage } = this.bodyParams; - if (!settings.get('AutoTranslate_Enabled')) { - return API.v1.failure('AutoTranslate is disabled.'); - } - if (!messageId) { - return API.v1.failure('The bodyParam "messageId" is required.'); - } - const message = Messages.findOneById(messageId); - if (!message) { - return API.v1.failure('Message not found.'); - } - - const translatedMessage = Meteor.runAsUser(this.userId, () => Meteor.call('autoTranslate.translateMessage', message, targetLanguage)); - - return API.v1.success({ message: translatedMessage }); - }, - }, -); diff --git a/apps/meteor/app/api/server/v1/autotranslate.ts b/apps/meteor/app/api/server/v1/autotranslate.ts new file mode 100644 index 000000000000..826ed2ee2968 --- /dev/null +++ b/apps/meteor/app/api/server/v1/autotranslate.ts @@ -0,0 +1,93 @@ +import { Meteor } from 'meteor/meteor'; +import { + isAutotranslateSaveSettingsParamsPOST, + isAutotranslateTranslateMessageParamsPOST, + isAutotranslateGetSupportedLanguagesParamsGET, +} from '@rocket.chat/rest-typings'; + +import { API } from '../api'; +import { settings } from '../../../settings/server'; +import { Messages } from '../../../models/server'; + +API.v1.addRoute( + 'autotranslate.getSupportedLanguages', + { + authRequired: true, + validateParams: isAutotranslateGetSupportedLanguagesParamsGET, + }, + { + get() { + if (!settings.get('AutoTranslate_Enabled')) { + return API.v1.failure('AutoTranslate is disabled.'); + } + const { targetLanguage } = this.queryParams; + const languages = Meteor.call('autoTranslate.getSupportedLanguages', targetLanguage); + + return API.v1.success({ languages: languages || [] }); + }, + }, +); + +API.v1.addRoute( + 'autotranslate.saveSettings', + { + authRequired: true, + validateParams: isAutotranslateSaveSettingsParamsPOST, + }, + { + post() { + const { roomId, field, value, defaultLanguage } = this.bodyParams; + if (!settings.get('AutoTranslate_Enabled')) { + return API.v1.failure('AutoTranslate is disabled.'); + } + + if (!roomId) { + return API.v1.failure('The bodyParam "roomId" is required.'); + } + if (!field) { + return API.v1.failure('The bodyParam "field" is required.'); + } + if (value === undefined) { + return API.v1.failure('The bodyParam "value" is required.'); + } + if (field === 'autoTranslate' && typeof value !== 'boolean') { + return API.v1.failure('The bodyParam "autoTranslate" must be a boolean.'); + } + + if (field === 'autoTranslateLanguage' && (typeof value !== 'string' || !Number.isNaN(Number.parseInt(value)))) { + return API.v1.failure('The bodyParam "autoTranslateLanguage" must be a string.'); + } + + Meteor.call('autoTranslate.saveSettings', roomId, field, value === true ? '1' : String(value).valueOf(), { defaultLanguage }); + + return API.v1.success(); + }, + }, +); + +API.v1.addRoute( + 'autotranslate.translateMessage', + { + authRequired: true, + validateParams: isAutotranslateTranslateMessageParamsPOST, + }, + { + post() { + const { messageId, targetLanguage } = this.bodyParams; + if (!settings.get('AutoTranslate_Enabled')) { + return API.v1.failure('AutoTranslate is disabled.'); + } + if (!messageId) { + return API.v1.failure('The bodyParam "messageId" is required.'); + } + const message = Messages.findOneById(messageId); + if (!message) { + return API.v1.failure('Message not found.'); + } + + const translatedMessage = Meteor.call('autoTranslate.translateMessage', message, targetLanguage); + + return API.v1.success({ message: translatedMessage }); + }, + }, +); diff --git a/apps/meteor/app/api/server/v1/channels.js b/apps/meteor/app/api/server/v1/channels.js index b5137b487223..0ba1e876094a 100644 --- a/apps/meteor/app/api/server/v1/channels.js +++ b/apps/meteor/app/api/server/v1/channels.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import _ from 'underscore'; +import { Integrations, Uploads, Messages as MessagesRaw, Rooms as RoomsRaw, Subscriptions as SubscriptionsRaw } from '@rocket.chat/models'; import { Rooms, Subscriptions, Messages, Users } from '../../../models/server'; -import { Integrations, Uploads } from '../../../models/server/raw'; import { canAccessRoom, hasPermission, hasAtLeastOnePermission } from '../../../authorization/server'; import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; @@ -11,6 +11,7 @@ import { API } from '../api'; import { settings } from '../../../settings/server'; import { Team } from '../../../../server/sdk'; import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; +import { addUserToFileObj } from '../helpers/addUserToFileObj'; // Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property function findChannelByIdOrName({ params, checkedArchived = true, userId }) { @@ -271,17 +272,11 @@ API.v1.addRoute( 'channels.files', { authRequired: true }, { - get() { + async get() { const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false, }); - const addUserObjectToEveryObject = (file) => { - if (file.userId) { - file = this.insertUserObject({ object: file, userId: file.userId }); - } - return file; - }; if (!canAccessRoom(findResult, { _id: this.userId })) { return API.v1.unauthorized(); @@ -292,20 +287,20 @@ API.v1.addRoute( const ourQuery = Object.assign({}, query, { rid: findResult._id }); - const files = Promise.await( - Uploads.find(ourQuery, { - sort: sort || { name: 1 }, - skip: offset, - limit: count, - fields, - }).toArray(), - ); + const { cursor, totalCount } = Uploads.findPaginated(ourQuery, { + sort: sort || { name: 1 }, + skip: offset, + limit: count, + projection: fields, + }); + + const [files, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ - files: files.map(addUserObjectToEveryObject), + files: await addUserToFileObj(files), count: files.length, offset, - total: Promise.await(Uploads.find(ourQuery).count()), + total, }); }, }, @@ -315,7 +310,7 @@ API.v1.addRoute( 'channels.getIntegrations', { authRequired: true }, { - get() { + async get() { if ( !hasAtLeastOnePermission(this.userId, [ 'manage-outgoing-integrations', @@ -351,15 +346,15 @@ API.v1.addRoute( const { sort, fields: projection, query } = this.parseJsonQuery(); ourQuery = Object.assign(mountIntegrationQueryBasedOnPermissions(this.userId), query, ourQuery); - const cursor = Integrations.find(ourQuery, { + + const { cursor, totalCount } = Integrations.findPaginated(ourQuery, { sort: sort || { _createdAt: 1 }, skip: offset, limit: count, projection, }); - const integrations = Promise.await(cursor.toArray()); - const total = Promise.await(cursor.count()); + const [integrations, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ integrations, @@ -413,66 +408,64 @@ API.v1.addRoute( 'channels.list', { authRequired: true }, { - get: { - // This is defined as such only to provide an example of how the routes can be defined :X - action() { - const { offset, count } = this.getPaginationItems(); - const { sort, fields, query } = this.parseJsonQuery(); - const hasPermissionToSeeAllPublicChannels = hasPermission(this.userId, 'view-c-room'); - - const ourQuery = { ...query, t: 'c' }; - - if (!hasPermissionToSeeAllPublicChannels) { - if (!hasPermission(this.userId, 'view-joined-room')) { - return API.v1.unauthorized(); - } - const roomIds = Subscriptions.findByUserIdAndType(this.userId, 'c', { - fields: { rid: 1 }, - }) - .fetch() - .map((s) => s.rid); - ourQuery._id = { $in: roomIds }; - } + async get() { + const { offset, count } = this.getPaginationItems(); + const { sort, fields, query } = this.parseJsonQuery(); + const hasPermissionToSeeAllPublicChannels = hasPermission(this.userId, 'view-c-room'); - // teams filter - I would love to have a way to apply this filter @ db level :( - const ids = Subscriptions.cachedFindByUserId(this.userId, { fields: { rid: 1 } }) + const ourQuery = { ...query, t: 'c' }; + + if (!hasPermissionToSeeAllPublicChannels) { + if (!hasPermission(this.userId, 'view-joined-room')) { + return API.v1.unauthorized(); + } + const roomIds = Subscriptions.findByUserIdAndType(this.userId, 'c', { + fields: { rid: 1 }, + }) .fetch() - .map((item) => item.rid); + .map((s) => s.rid); + ourQuery._id = { $in: roomIds }; + } + + // teams filter - I would love to have a way to apply this filter @ db level :( + const ids = Subscriptions.cachedFindByUserId(this.userId, { fields: { rid: 1 } }) + .fetch() + .map((item) => item.rid); - ourQuery.$or = [ - { - teamId: { - $exists: false, - }, + ourQuery.$or = [ + { + teamId: { + $exists: false, }, - { - teamId: { - $exists: true, - }, - _id: { - $in: ids, - }, + }, + { + teamId: { + $exists: true, }, - ]; - - const cursor = Rooms.find(ourQuery, { - sort: sort || { name: 1 }, - skip: offset, - limit: count, - fields, - }); + _id: { + $in: ids, + }, + }, + ]; - const total = cursor.count(); + const { cursor, totalCount } = RoomsRaw.findPaginated(ourQuery, { + sort: sort || { name: 1 }, + skip: offset, + limit: count, + projection: fields, + }); - const rooms = cursor.fetch(); + const [channels, total] = await Promise.all([ + cursor.map((room) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); - return API.v1.success({ - channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), - count: rooms.length, - offset, - total, - }); - }, + return API.v1.success({ + channels, + count: channels.length, + offset, + total, + }); }, }, ); @@ -481,26 +474,34 @@ API.v1.addRoute( 'channels.list.joined', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort, fields } = this.parseJsonQuery(); - // TODO: CACHE: Add Breacking notice since we removed the query param - const cursor = Rooms.findBySubscriptionTypeAndUserId('c', this.userId, { + const subs = await SubscriptionsRaw.findByUserIdAndTypes(this.userId, ['c'], { projection: { rid: 1 } }).toArray(); + const rids = subs.map(({ rid }) => rid).filter(Boolean); + + if (rids.length === 0) { + return API.v1.notFound(); + } + + const { cursor, totalCount } = RoomsRaw.findPaginatedByTypeAndIds('c', rids, { sort: sort || { name: 1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const totalCount = cursor.count(); - const rooms = cursor.fetch(); + const [channels, total] = await Promise.all([ + cursor.map((room) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); return API.v1.success({ - channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + channels, offset, - count: rooms.length, - total: totalCount, + count: channels.length, + total, }); }, }, @@ -510,7 +511,7 @@ API.v1.addRoute( 'channels.members', { authRequired: true }, { - get() { + async get() { const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false, @@ -532,7 +533,7 @@ API.v1.addRoute( ); const { status, filter } = this.queryParams; - const cursor = findUsersOfRoom({ + const { cursor, totalCount } = findUsersOfRoom({ rid: findResult._id, ...(status && { status: { $in: status } }), skip, @@ -541,8 +542,7 @@ API.v1.addRoute( ...(sort?.username && { sort: { username: sort.username } }), }); - const total = cursor.count(); - const members = cursor.fetch(); + const [members, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ members, @@ -917,7 +917,7 @@ API.v1.addRoute( 'channels.anonymousread', { authRequired: false }, { - get() { + async get() { const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false, @@ -933,15 +933,14 @@ API.v1.addRoute( }); } - const cursor = Messages.find(ourQuery, { + const { cursor, totalCount } = MessagesRaw.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const total = cursor.count(); - const messages = cursor.fetch(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages: normalizeMessagesForUser(messages, this.userId), diff --git a/apps/meteor/app/api/server/v1/channels.ts b/apps/meteor/app/api/server/v1/channels.ts index 484c2a077081..fab7c8f5e362 100644 --- a/apps/meteor/app/api/server/v1/channels.ts +++ b/apps/meteor/app/api/server/v1/channels.ts @@ -18,9 +18,10 @@ import { isChannelsSetReadOnlyProps, isChannelsDeleteProps, } from '@rocket.chat/rest-typings'; +import { Messages } from '@rocket.chat/models'; -import { Rooms, Subscriptions, Messages } from '../../../models/server'; -import { hasPermission, hasAllPermission } from '../../../authorization/server'; +import { Rooms, Subscriptions } from '../../../models/server'; +import { hasPermission } from '../../../authorization/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { Team } from '../../../../server/sdk'; @@ -247,7 +248,7 @@ API.v1.addRoute( validateParams: isChannelsMessagesProps, }, { - get() { + async get() { const { roomId } = this.queryParams; const findResult = findChannelByIdOrName({ params: { roomId }, @@ -269,15 +270,15 @@ API.v1.addRoute( return API.v1.unauthorized(); } - const cursor = Messages.find(ourQuery, { + // @ts-expect-error recursive types are causing issues here + const { cursor, totalCount } = Messages.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const total = cursor.count(); - const messages = cursor.fetch(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages: normalizeMessagesForUser(messages, this.userId), @@ -454,7 +455,7 @@ API.v1.addRoute( }, { async post() { - if (!hasAllPermission(this.userId, ['create-team', 'edit-room'])) { + if (!hasPermission(this.userId, 'create-team')) { return API.v1.unauthorized(); } @@ -464,6 +465,10 @@ API.v1.addRoute( return API.v1.failure('The parameter "channelId" or "channelName" is required'); } + if (!hasPermission(this.userId, 'edit-room', channelId)) { + return API.v1.unauthorized(); + } + const room = findChannelByIdOrName({ params: { roomId: channelId, diff --git a/apps/meteor/app/api/server/v1/chat.js b/apps/meteor/app/api/server/v1/chat.js index 424568cf2ced..73f4b6931ddc 100644 --- a/apps/meteor/app/api/server/v1/chat.js +++ b/apps/meteor/app/api/server/v1/chat.js @@ -1,8 +1,9 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Messages as MessagesRaw } from '@rocket.chat/models'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/server'; import { canAccessRoom, canAccessRoomId, roomAccessAttributes, hasPermission } from '../../../authorization/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { processWebhookMessage } from '../../../lib/server'; @@ -446,7 +447,7 @@ API.v1.addRoute( 'chat.getDeletedMessages', { authRequired: true }, { - get() { + async get() { const { roomId, since } = this.queryParams; const { offset, count } = this.getPaginationItems(); @@ -459,19 +460,18 @@ API.v1.addRoute( } else if (isNaN(Date.parse(since))) { throw new Meteor.Error('The "since" query parameter must be a valid date.'); } - const cursor = Messages.trashFindDeletedAfter( + + const { cursor, totalCount } = MessagesRaw.trashFindPaginatedDeletedAfter( new Date(since), { rid: roomId }, { skip: offset, limit: count, - fields: { _id: 1 }, + projection: { _id: 1 }, }, ); - const total = cursor.count(); - - const messages = cursor.fetch(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages, @@ -487,7 +487,7 @@ API.v1.addRoute( 'chat.getPinnedMessages', { authRequired: true }, { - get() { + async get() { const { roomId } = this.queryParams; const { offset, count } = this.getPaginationItems(); @@ -499,17 +499,15 @@ API.v1.addRoute( throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - const cursor = Messages.findPinnedByRoom(roomId, { + const { cursor, totalCount } = MessagesRaw.findPaginatedPinnedByRoom(roomId, { skip: offset, limit: count, }); - const total = cursor.count(); - - const messages = cursor.fetch(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ - messages, + messages: normalizeMessagesForUser(messages, this.userId), count: messages.length, offset, total, @@ -522,7 +520,7 @@ API.v1.addRoute( 'chat.getThreadsList', { authRequired: true }, { - get() { + async get() { const { rid, type, text } = this.queryParams; check(rid, String); check(type, Match.Maybe(String)); @@ -549,19 +547,17 @@ API.v1.addRoute( }; const threadQuery = { ...query, ...typeThread, rid: room._id, tcount: { $exists: true } }; - const cursor = Messages.find(threadQuery, { + const { cursor, totalCount } = MessagesRaw.findPaginated(threadQuery, { sort: sort || { tlm: -1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const total = cursor.count(); - - const threads = cursor.fetch(); + const [threads, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ - threads, + threads: normalizeMessagesForUser(threads, this.userId), count: threads.length, offset, total, @@ -614,7 +610,7 @@ API.v1.addRoute( 'chat.getThreadMessages', { authRequired: true }, { - get() { + async get() { const { tmid } = this.queryParams; const { query, fields, sort } = this.parseJsonQuery(); const { offset, count } = this.getPaginationItems(); @@ -635,19 +631,17 @@ API.v1.addRoute( if (!canAccessRoom(room, user)) { throw new Meteor.Error('error-not-allowed', 'Not Allowed'); } - const cursor = Messages.find( + const { cursor, totalCount } = MessagesRaw.findPaginated( { ...query, tmid }, { sort: sort || { ts: 1 }, skip: offset, limit: count, - fields, + projection: fields, }, ); - const total = cursor.count(); - - const messages = cursor.fetch(); + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages, diff --git a/apps/meteor/app/api/server/v1/commands.ts b/apps/meteor/app/api/server/v1/commands.ts index 75423636a1bf..e2659e17ac3d 100644 --- a/apps/meteor/app/api/server/v1/commands.ts +++ b/apps/meteor/app/api/server/v1/commands.ts @@ -141,7 +141,7 @@ API.v1.addRoute( { get() { const { offset, count } = this.getPaginationItems(); - const { sort, fields, query } = this.parseJsonQuery(); + const { sort, query } = this.parseJsonQuery(); let commands = Object.values(slashCommands.commands); @@ -151,16 +151,11 @@ API.v1.addRoute( const totalCount = commands.length; - if (fields) { - console.warn('commands.list -> fields is deprecated and will be removed in 5.0.0'); - } - return API.v1.success({ commands: processQueryOptionsOnResult(commands, { sort: sort || { name: 1 }, skip: offset, limit: count, - fields, }), offset, count: commands.length, diff --git a/apps/meteor/app/api/server/v1/custom-sounds.ts b/apps/meteor/app/api/server/v1/custom-sounds.ts index f6294c8a5ec1..d68a1fa5b24f 100644 --- a/apps/meteor/app/api/server/v1/custom-sounds.ts +++ b/apps/meteor/app/api/server/v1/custom-sounds.ts @@ -1,4 +1,5 @@ -import { CustomSounds } from '../../../models/server/raw'; +import { CustomSounds } from '@rocket.chat/models'; + import { API } from '../api'; API.v1.addRoute( @@ -8,15 +9,13 @@ API.v1.addRoute( async get() { const { offset, count } = this.getPaginationItems(); const { sort, query } = this.parseJsonQuery(); - const cursor = await CustomSounds.find(query, { + const { cursor, totalCount } = CustomSounds.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const sounds = await cursor.toArray(); + const [sounds, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ sounds, diff --git a/apps/meteor/app/api/server/v1/custom-user-status.ts b/apps/meteor/app/api/server/v1/custom-user-status.ts index e86df11a7380..0a2b086f5e2b 100644 --- a/apps/meteor/app/api/server/v1/custom-user-status.ts +++ b/apps/meteor/app/api/server/v1/custom-user-status.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { CustomUserStatus } from '@rocket.chat/models'; -import { CustomUserStatus } from '../../../models/server/raw'; import { API } from '../api'; API.v1.addRoute( @@ -12,15 +12,13 @@ API.v1.addRoute( const { offset, count } = this.getPaginationItems(); const { sort, query } = this.parseJsonQuery(); - const cursor = await CustomUserStatus.find(query, { + const { cursor, totalCount } = CustomUserStatus.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const statuses = await cursor.toArray(); + const [statuses, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ statuses, diff --git a/apps/meteor/app/api/server/v1/e2e.js b/apps/meteor/app/api/server/v1/e2e.js deleted file mode 100644 index f079e75b8119..000000000000 --- a/apps/meteor/app/api/server/v1/e2e.js +++ /dev/null @@ -1,183 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { API } from '../api'; - -API.v1.addRoute( - 'e2e.fetchMyKeys', - { authRequired: true }, - { - get() { - let result; - Meteor.runAsUser(this.userId, () => { - result = Meteor.call('e2e.fetchMyKeys'); - }); - - return API.v1.success(result); - }, - }, -); - -API.v1.addRoute( - 'e2e.getUsersOfRoomWithoutKey', - { authRequired: true }, - { - get() { - const { rid } = this.queryParams; - - let result; - Meteor.runAsUser(this.userId, () => { - result = Meteor.call('e2e.getUsersOfRoomWithoutKey', rid); - }); - - return API.v1.success(result); - }, - }, -); - -/** - * @openapi - * /api/v1/e2e.setRoomKeyID: - * post: - * description: Sets the end-to-end encryption key ID for a room - * security: - * - autenticated: {} - * requestBody: - * description: A tuple containing the room ID and the key ID - * content: - * application/json: - * schema: - * type: object - * properties: - * rid: - * type: string - * keyID: - * type: string - * responses: - * 200: - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiSuccessV1' - * default: - * description: Unexpected error - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiFailureV1' - */ -API.v1.addRoute( - 'e2e.setRoomKeyID', - { authRequired: true }, - { - post() { - const { rid, keyID } = this.bodyParams; - - Meteor.runAsUser(this.userId, () => { - API.v1.success(Meteor.call('e2e.setRoomKeyID', rid, keyID)); - }); - - return API.v1.success(); - }, - }, -); - -/** - * @openapi - * /api/v1/e2e.setUserPublicAndPrivateKeys: - * post: - * description: Sets the end-to-end encryption keys for the authenticated user - * security: - * - autenticated: {} - * requestBody: - * description: A tuple containing the public and the private keys - * content: - * application/json: - * schema: - * type: object - * properties: - * public_key: - * type: string - * private_key: - * type: string - * responses: - * 200: - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiSuccessV1' - * default: - * description: Unexpected error - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiFailureV1' - */ -API.v1.addRoute( - 'e2e.setUserPublicAndPrivateKeys', - { authRequired: true }, - { - post() { - const { public_key, private_key } = this.bodyParams; - - Meteor.runAsUser(this.userId, () => { - API.v1.success( - Meteor.call('e2e.setUserPublicAndPrivateKeys', { - public_key, - private_key, - }), - ); - }); - - return API.v1.success(); - }, - }, -); - -/** - * @openapi - * /api/v1/e2e.updateGroupKey: - * post: - * description: Updates the end-to-end encryption key for a user on a room - * security: - * - autenticated: {} - * requestBody: - * description: A tuple containing the user ID, the room ID, and the key - * content: - * application/json: - * schema: - * type: object - * properties: - * uid: - * type: string - * rid: - * type: string - * key: - * type: string - * responses: - * 200: - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiSuccessV1' - * default: - * description: Unexpected error - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ApiFailureV1' - */ -API.v1.addRoute( - 'e2e.updateGroupKey', - { authRequired: true }, - { - post() { - const { uid, rid, key } = this.bodyParams; - - Meteor.runAsUser(this.userId, () => { - API.v1.success(Meteor.call('e2e.updateGroupKey', rid, uid, key)); - }); - - return API.v1.success(); - }, - }, -); diff --git a/apps/meteor/app/api/server/v1/e2e.ts b/apps/meteor/app/api/server/v1/e2e.ts new file mode 100644 index 000000000000..34e963cd9f29 --- /dev/null +++ b/apps/meteor/app/api/server/v1/e2e.ts @@ -0,0 +1,197 @@ +import { Meteor } from 'meteor/meteor'; +import { + ise2eGetUsersOfRoomWithoutKeyParamsGET, + ise2eSetRoomKeyIDParamsPOST, + ise2eSetUserPublicAndPrivateKeysParamsPOST, + ise2eUpdateGroupKeyParamsPOST, +} from '@rocket.chat/rest-typings'; +import { IUser } from '@rocket.chat/core-typings'; + +import { API } from '../api'; + +API.v1.addRoute( + 'e2e.fetchMyKeys', + { + authRequired: true, + }, + { + get() { + const result: { + public_key: string; + private_key: string; + } = Meteor.call('e2e.fetchMyKeys'); + + return API.v1.success(result); + }, + }, +); + +API.v1.addRoute( + 'e2e.getUsersOfRoomWithoutKey', + { + authRequired: true, + validateParams: ise2eGetUsersOfRoomWithoutKeyParamsGET, + }, + { + get() { + const { rid } = this.queryParams; + + const result: { + users: IUser[]; + } = Meteor.call('e2e.getUsersOfRoomWithoutKey', rid); + + return API.v1.success(result); + }, + }, +); + +/** + * @openapi + * /api/v1/e2e.setRoomKeyID: + * post: + * description: Sets the end-to-end encryption key ID for a room + * security: + * - autenticated: {} + * requestBody: + * description: A tuple containing the room ID and the key ID + * content: + * application/json: + * schema: + * type: object + * properties: + * rid: + * type: string + * keyID: + * type: string + * responses: + * 200: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiSuccessV1' + * default: + * description: Unexpected error + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiFailureV1' + */ + +API.v1.addRoute( + 'e2e.setRoomKeyID', + { + authRequired: true, + validateParams: ise2eSetRoomKeyIDParamsPOST, + }, + { + post() { + const { rid, keyID } = this.bodyParams; + + Meteor.call('e2e.setRoomKeyID', rid, keyID); + + return API.v1.success(); + }, + }, +); + +/** + * @openapi + * /api/v1/e2e.setUserPublicAndPrivateKeys: + * post: + * description: Sets the end-to-end encryption keys for the authenticated user + * security: + * - autenticated: {} + * requestBody: + * description: A tuple containing the public and the private keys + * content: + * application/json: + * schema: + * type: object + * properties: + * public_key: + * type: string + * private_key: + * type: string + * responses: + * 200: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiSuccessV1' + * default: + * description: Unexpected error + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiFailureV1' + */ +API.v1.addRoute( + 'e2e.setUserPublicAndPrivateKeys', + { + authRequired: true, + validateParams: ise2eSetUserPublicAndPrivateKeysParamsPOST, + }, + { + post() { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { public_key, private_key } = Meteor.call('e2e.fetchMyKeys'); + + Meteor.call('e2e.setUserPublicAndPrivateKeys', { + public_key, + private_key, + }); + + return API.v1.success(); + }, + }, +); + +/** + * @openapi + * /api/v1/e2e.updateGroupKey: + * post: + * description: Updates the end-to-end encryption key for a user on a room + * security: + * - autenticated: {} + * requestBody: + * description: A tuple containing the user ID, the room ID, and the key + * content: + * application/json: + * schema: + * type: object + * properties: + * uid: + * type: string + * rid: + * type: string + * key: + * type: string + * responses: + * 200: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiSuccessV1' + * default: + * description: Unexpected error + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ApiFailureV1' + */ +API.v1.addRoute( + 'e2e.updateGroupKey', + { + authRequired: true, + validateParams: ise2eUpdateGroupKeyParamsPOST, + }, + { + post() { + const { uid, rid, key } = this.bodyParams; + + Meteor.call('e2e.updateGroupKey', rid, uid, key); + + return API.v1.success(); + }, + }, +); diff --git a/apps/meteor/app/api/server/v1/email-inbox.ts b/apps/meteor/app/api/server/v1/email-inbox.ts index 1e6392a8aad0..e94370acf3f3 100644 --- a/apps/meteor/app/api/server/v1/email-inbox.ts +++ b/apps/meteor/app/api/server/v1/email-inbox.ts @@ -1,9 +1,9 @@ import { check, Match } from 'meteor/check'; +import { EmailInbox } from '@rocket.chat/models'; import { API } from '../api'; import { insertOneEmailInbox, findEmailInboxes, findOneEmailInbox, updateEmailInbox } from '../lib/emailInbox'; import { hasPermission } from '../../../authorization/server/functions/hasPermission'; -import { EmailInbox } from '../../../models/server/raw'; import Users from '../../../models/server/models/Users'; import { sendTestEmailToInbox } from '../../../../server/features/EmailInbox/EmailInbox_Outgoing'; diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index df66f686edfd..e0027765352e 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -1,10 +1,11 @@ import { Meteor } from 'meteor/meteor'; +import { EmojiCustom } from '@rocket.chat/models'; -import { EmojiCustom } from '../../../models/server/raw'; import { API } from '../api'; import { getUploadFormData } from '../lib/getUploadFormData'; import { findEmojisCustom } from '../lib/emoji-custom'; import { Media } from '../../../../server/sdk'; +import { SystemLogger } from '../../../../server/lib/logger/system'; API.v1.addRoute( 'emoji-custom.list', @@ -82,16 +83,21 @@ API.v1.addRoute( const [, extension] = emoji.mimetype.split('/'); fields.extension = extension; - Meteor.call('insertOrUpdateEmoji', { - ...fields, - newFile: true, - aliases: fields.aliases || '', - }); - Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, { - ...fields, - newFile: true, - aliases: fields.aliases || '', - }); + try { + Meteor.call('insertOrUpdateEmoji', { + ...fields, + newFile: true, + aliases: fields.aliases || '', + }); + Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, { + ...fields, + newFile: true, + aliases: fields.aliases || '', + }); + } catch (e) { + SystemLogger.error(e); + return API.v1.failure(); + } return API.v1.success(); }, @@ -114,7 +120,7 @@ API.v1.addRoute( throw new Meteor.Error('The required "_id" query param is missing.'); } - const emojiToUpdate = Promise.await(EmojiCustom.findOneById(fields._id)); + const emojiToUpdate = await EmojiCustom.findOneById(fields._id); if (!emojiToUpdate) { throw new Meteor.Error('Emoji not found.'); } @@ -125,7 +131,7 @@ API.v1.addRoute( const newFile = Boolean(emoji?.fileBuffer.length); if (fields.newFile) { - const isUploadable = Promise.await(Media.isImage(emoji.fileBuffer)); + const isUploadable = await Media.isImage(emoji.fileBuffer); if (!isUploadable) { throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image"); } diff --git a/apps/meteor/app/api/server/v1/groups.js b/apps/meteor/app/api/server/v1/groups.js index 4a715ad65d2c..94a690be732a 100644 --- a/apps/meteor/app/api/server/v1/groups.js +++ b/apps/meteor/app/api/server/v1/groups.js @@ -1,10 +1,10 @@ import _ from 'underscore'; import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Integrations, Messages as MessagesRaw, Uploads, Rooms as RoomsRaw, Subscriptions as SubscriptionsRaw } from '@rocket.chat/models'; import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; import { Subscriptions, Rooms, Messages, Users } from '../../../models/server'; -import { Integrations, Uploads } from '../../../models/server/raw'; import { hasPermission, hasAtLeastOnePermission, @@ -16,6 +16,7 @@ import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMes import { API } from '../api'; import { Team } from '../../../../server/sdk'; import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; +import { addUserToFileObj } from '../helpers/addUserToFileObj'; // Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = true }) { @@ -333,38 +334,32 @@ API.v1.addRoute( 'groups.files', { authRequired: true }, { - get() { + async get() { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId, checkedArchived: false, }); - const addUserObjectToEveryObject = (file) => { - if (file.userId) { - file = this.insertUserObject({ object: file, userId: file.userId }); - } - return file; - }; const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); const ourQuery = Object.assign({}, query, { rid: findResult.rid }); - const files = Promise.await( - Uploads.find(ourQuery, { - sort: sort || { name: 1 }, - skip: offset, - limit: count, - fields, - }).toArray(), - ); + const { cursor, totalCount } = Uploads.findPaginated(ourQuery, { + sort: sort || { name: 1 }, + skip: offset, + limit: count, + projection: fields, + }); + + const [files, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ - files: files.map(addUserObjectToEveryObject), + files: await addUserToFileObj(files), count: files.length, offset, - total: Promise.await(Uploads.find(ourQuery).count()), + total, }); }, }, @@ -374,7 +369,7 @@ API.v1.addRoute( 'groups.getIntegrations', { authRequired: true }, { - get() { + async get() { if ( !hasAtLeastOnePermission(this.userId, [ 'manage-outgoing-integrations', @@ -408,15 +403,14 @@ API.v1.addRoute( const ourQuery = Object.assign(mountIntegrationQueryBasedOnPermissions(this.userId), query, { channel: { $in: channelsToSearch }, }); - const cursor = Integrations.find(ourQuery, { + const { cursor, totalCount } = Integrations.findPaginated(ourQuery, { sort: sort || { _createdAt: 1 }, skip: offset, limit: count, projection, }); - const integrations = Promise.await(cursor.toArray()); - const total = Promise.await(cursor.count()); + const [integrations, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ integrations, @@ -580,26 +574,34 @@ API.v1.addRoute( 'groups.list', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort, fields } = this.parseJsonQuery(); - // TODO: CACHE: Add Breacking notice since we removed the query param - const cursor = Rooms.findBySubscriptionTypeAndUserId('p', this.userId, { + const subs = await SubscriptionsRaw.findByUserIdAndTypes(this.userId, ['p'], { projection: { rid: 1 } }).toArray(); + const rids = subs.map(({ rid }) => rid).filter(Boolean); + + if (rids.length === 0) { + return API.v1.notFound(); + } + + const { cursor, totalCount } = RoomsRaw.findPaginatedByTypeAndIds('p', rids, { sort: sort || { name: 1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const totalCount = cursor.count(); - const rooms = cursor.fetch(); + const [groups, total] = await Promise.all([ + cursor.map((room) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); return API.v1.success({ - groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + groups, offset, - count: rooms.length, - total: totalCount, + count: groups.length, + total, }); }, }, @@ -609,7 +611,7 @@ API.v1.addRoute( 'groups.listAll', { authRequired: true }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-room-administration')) { return API.v1.unauthorized(); } @@ -617,21 +619,23 @@ API.v1.addRoute( const { sort, fields, query } = this.parseJsonQuery(); const ourQuery = Object.assign({}, query, { t: 'p' }); - const cursor = Rooms.find(ourQuery, { + const { cursor, totalCount } = RoomsRaw.findPaginated(ourQuery, { sort: sort || { name: 1 }, skip: offset, limit: count, - fields, + projection: fields, }); - const totalCount = cursor.count(); - const rooms = cursor.fetch(); + const [rooms, total] = await Promise.all([ + cursor.map((room) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); return API.v1.success({ - groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + groups: rooms, offset, count: rooms.length, - total: totalCount, + total, }); }, }, @@ -641,7 +645,7 @@ API.v1.addRoute( 'groups.members', { authRequired: true }, { - get() { + async get() { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId, @@ -663,7 +667,7 @@ API.v1.addRoute( ); const { status, filter } = this.queryParams; - const cursor = findUsersOfRoom({ + const { cursor, totalCount } = findUsersOfRoom({ rid: findResult.rid, ...(status && { status: { $in: status } }), skip, @@ -672,8 +676,7 @@ API.v1.addRoute( ...(sort?.username && { sort: { username: sort.username } }), }); - const total = cursor.count(); - const members = cursor.fetch(); + const [members, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ members, @@ -689,7 +692,7 @@ API.v1.addRoute( 'groups.messages', { authRequired: true }, { - get() { + async get() { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId, @@ -699,18 +702,20 @@ API.v1.addRoute( const ourQuery = Object.assign({}, query, { rid: findResult.rid }); - const messages = Messages.find(ourQuery, { + const { cursor, totalCount } = MessagesRaw.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, - fields, - }).fetch(); + projection: fields, + }); + + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages: normalizeMessagesForUser(messages, this.userId), count: messages.length, offset, - total: Messages.find(ourQuery).count(), + total, }); }, }, diff --git a/apps/meteor/app/api/server/v1/im.ts b/apps/meteor/app/api/server/v1/im.ts index 0236d350b212..a749a2da9b14 100644 --- a/apps/meteor/app/api/server/v1/im.ts +++ b/apps/meteor/app/api/server/v1/im.ts @@ -1,7 +1,7 @@ /** * Docs: https://github.com/RocketChat/developer-docs/blob/master/reference/api/rest-api/endpoints/team-collaboration-endpoints/im-endpoints */ -import type { IMessage, IRoom, ISetting, ISubscription, IUpload, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, IRoom, ISubscription, IUpload } from '@rocket.chat/core-typings'; import { isDmDeleteProps, isDmFileProps, @@ -12,15 +12,16 @@ import { } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Subscriptions, Uploads, Messages, Rooms, Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; -import { Subscriptions, Uploads, Messages, Rooms, Settings } from '../../../models/server/raw'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { hasPermission } from '../../../authorization/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { getRoomByNameOrIdWithOptionToJoin } from '../../../lib/server/functions/getRoomByNameOrIdWithOptionToJoin'; import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; +import { addUserToFileObj } from '../helpers/addUserToFileObj'; +import { settings } from '../../../settings/server'; interface IImFilesObject extends IUpload { userId: string; @@ -217,26 +218,17 @@ API.v1.addRoute( const ourQuery = query ? { rid: room._id, ...query } : { rid: room._id }; - const files = ( - await Uploads.find(ourQuery, { - sort: sort || { name: 1 }, - skip: offset, - limit: count, - projection: fields, - }).toArray() - ).map((file): IImFilesObject | (IImFilesObject & { user: Pick }) => { - if (file.userId) { - return this.insertUserObject }>({ - object: { ...file }, - userId: file.userId, - }); - } - return file; + const { cursor, totalCount } = Uploads.findPaginated(ourQuery, { + sort: sort || { name: 1 }, + skip: offset, + limit: count, + projection: fields, }); - const total = await Uploads.find(ourQuery).count(); + const [files, total] = await Promise.all([cursor.toArray(), totalCount]); + return API.v1.success({ - files, + files: await addUserToFileObj(files), count: files.length, offset, total, @@ -314,15 +306,16 @@ API.v1.addRoute( const options = { sort: { username: sort?.username ? sort.username : 1 }, - projection: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, + projection: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1, federated: 1 }, skip: offset, limit: count, }; - const cursor = Users.findByActiveUsersExcept(filter, [], options, null, [extraQuery]); + const searchFields = settings.get('Accounts_SearchFields').trim().split(','); + + const { cursor, totalCount } = Users.findPaginatedByActiveUsersExcept(filter, [], options, searchFields, [extraQuery]); - const members = cursor.fetch(); - const total = cursor.count(); + const [members, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ members, @@ -354,18 +347,21 @@ API.v1.addRoute( const ourQuery = { rid: room._id, ...query }; const sortObj = { ts: sort?.ts ?? -1 }; - const messages = await Messages.find(ourQuery, { + + const { cursor, totalCount } = Messages.findPaginated(ourQuery, { sort: sortObj, skip: offset, limit: count, ...(fields && { projection: fields }), - }).toArray(); + }); + + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages: normalizeMessagesForUser(messages, this.userId), count: messages.length, offset, - total: await Messages.find(ourQuery).count(), + total, }); }, }, @@ -376,13 +372,7 @@ API.v1.addRoute( { authRequired: true }, { async get() { - const settings = await Settings.findOne( - { _id: 'API_Enable_Direct_Message_History_EndPoint' }, - { - projection: { _id: 1, value: 1 }, - }, - ); - if (settings?.value !== true) { + if (settings.get('API_Enable_Direct_Message_History_EndPoint') !== true) { throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/im.messages.others', }); @@ -397,7 +387,7 @@ API.v1.addRoute( throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" is required'); } - const room = await Rooms.findOneById(roomId, { projection: { _id: 1, t: 1 } }); + const room = await Rooms.findOneById>(roomId, { projection: { _id: 1, t: 1 } }); if (!room || room?.t !== 'd') { throw new Meteor.Error('error-room-not-found', `No direct message room found by the id of: ${roomId}`); } @@ -406,17 +396,18 @@ API.v1.addRoute( const { sort, fields, query } = this.parseJsonQuery(); const ourQuery = Object.assign({}, query, { rid: room._id }); - const msgs = await Messages.find(ourQuery, { + const { cursor, totalCount } = Messages.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, projection: fields, - }).toArray(); + }); + + const [msgs, total] = await Promise.all([cursor.toArray(), totalCount]); if (!msgs) { throw new Meteor.Error('error-no-messages', 'No messages found'); } - const total = await Messages.find(ourQuery).count(); return API.v1.success({ messages: normalizeMessagesForUser(msgs, this.userId), @@ -442,7 +433,7 @@ API.v1.addRoute( .map((item) => item.rid) .toArray(); - const rooms = Rooms.find( + const { cursor, totalCount } = Rooms.findPaginated( { type: 'd', _id: { $in: subscriptions } }, { sort, @@ -450,10 +441,12 @@ API.v1.addRoute( limit: count, projection: fields, }, - ).map((room: IRoom) => this.composeRoomWithLastMessage(room, this.userId)); + ); - const total = await rooms.count(); - const ims = await rooms.toArray(); + const [ims, total] = await Promise.all([ + cursor.map((room: IRoom) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); return API.v1.success({ ims, @@ -477,22 +470,26 @@ API.v1.addRoute( const { offset, count }: { offset: number; count: number } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); - const ourQuery = { ...query, t: 'd' }; + const { cursor, totalCount } = Rooms.findPaginated( + { ...query, t: 'd' }, + { + sort: sort || { name: 1 }, + skip: offset, + limit: count, + projection: fields, + }, + ); - const rooms = await Rooms.find(ourQuery, { - sort: sort || { name: 1 }, - skip: offset, - limit: count, - projection: fields, - }) - .map((room: IRoom) => this.composeRoomWithLastMessage(room, this.userId)) - .toArray(); + const [rooms, total] = await Promise.all([ + cursor.map((room: IRoom) => this.composeRoomWithLastMessage(room, this.userId)).toArray(), + totalCount, + ]); return API.v1.success({ ims: rooms, offset, count: rooms.length, - total: await Rooms.find(ourQuery).count(), + total, }); }, }, diff --git a/apps/meteor/app/api/server/v1/import.js b/apps/meteor/app/api/server/v1/import.js deleted file mode 100644 index b681b480d9d5..000000000000 --- a/apps/meteor/app/api/server/v1/import.js +++ /dev/null @@ -1,185 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { API } from '../api'; -import { hasPermission } from '../../../authorization/server'; -import { Imports } from '../../../models/server'; -import { Importers } from '../../../importer/server'; - -API.v1.addRoute( - 'uploadImportFile', - { authRequired: true }, - { - post() { - const { binaryContent, contentType, fileName, importerKey } = this.bodyParams; - - return API.v1.success(Meteor.call('uploadImportFile', binaryContent, contentType, fileName, importerKey)); - }, - }, -); - -API.v1.addRoute( - 'downloadPublicImportFile', - { authRequired: true }, - { - post() { - const { fileUrl, importerKey } = this.bodyParams; - - Meteor.runAsUser(this.userId, () => { - API.v1.success(Meteor.call('downloadPublicImportFile', fileUrl, importerKey)); - }); - - return API.v1.success(); - }, - }, -); - -API.v1.addRoute( - 'startImport', - { authRequired: true }, - { - post() { - const { input } = this.bodyParams; - - Meteor.runAsUser(this.userId, () => { - API.v1.success(Meteor.call('startImport', input)); - }); - - return API.v1.success(); - }, - }, -); - -API.v1.addRoute( - 'getImportFileData', - { authRequired: true }, - { - get() { - let result; - Meteor.runAsUser(this.userId, () => { - result = Meteor.call('getImportFileData'); - }); - - return API.v1.success(result); - }, - }, -); - -API.v1.addRoute( - 'getImportProgress', - { authRequired: true }, - { - get() { - let result; - Meteor.runAsUser(this.userId, () => { - result = Meteor.call('getImportProgress'); - }); - - return API.v1.success(result); - }, - }, -); - -API.v1.addRoute( - 'getLatestImportOperations', - { authRequired: true }, - { - get() { - let result; - Meteor.runAsUser(this.userId, () => { - result = Meteor.call('getLatestImportOperations'); - }); - - return API.v1.success(result); - }, - }, -); - -API.v1.addRoute( - 'downloadPendingFiles', - { authRequired: true }, - { - post() { - if (!this.userId) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'downloadPendingFiles', - }); - } - - if (!hasPermission(this.userId, 'run-import')) { - throw new Meteor.Error('not_authorized'); - } - - const importer = Importers.get('pending-files'); - if (!importer) { - throw new Meteor.Error('error-importer-not-defined', 'The Pending File Importer was not found.', { - method: 'downloadPendingFiles', - }); - } - - importer.instance = new importer.importer(importer); // eslint-disable-line new-cap - const count = importer.instance.prepareFileCount(); - - return API.v1.success({ - success: true, - count, - }); - }, - }, -); - -API.v1.addRoute( - 'downloadPendingAvatars', - { authRequired: true }, - { - post() { - if (!this.userId) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'downloadPendingAvatars', - }); - } - - if (!hasPermission(this.userId, 'run-import')) { - throw new Meteor.Error('not_authorized'); - } - - const importer = Importers.get('pending-avatars'); - if (!importer) { - throw new Meteor.Error('error-importer-not-defined', 'The Pending File Importer was not found.', { - method: 'downloadPendingAvatars', - }); - } - - importer.instance = new importer.importer(importer); // eslint-disable-line new-cap - const count = importer.instance.prepareFileCount(); - - return API.v1.success({ - success: true, - count, - }); - }, - }, -); - -API.v1.addRoute( - 'getCurrentImportOperation', - { authRequired: true }, - { - get() { - if (!this.userId) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'getCurrentImportOperation', - }); - } - - if (!hasPermission(this.userId, 'run-import')) { - throw new Meteor.Error('not_authorized'); - } - - const operation = Imports.findLastImport(); - return API.v1.success({ - success: true, - operation, - }); - }, - }, -); diff --git a/apps/meteor/app/api/server/v1/import.ts b/apps/meteor/app/api/server/v1/import.ts new file mode 100644 index 000000000000..8245f5edfcb2 --- /dev/null +++ b/apps/meteor/app/api/server/v1/import.ts @@ -0,0 +1,178 @@ +import { Meteor } from 'meteor/meteor'; +import { + isUploadImportFileParamsPOST, + isDownloadPublicImportFileParamsPOST, + isStartImportParamsPOST, + isGetImportFileDataParamsGET, + isGetImportProgressParamsGET, + isGetLatestImportOperationsParamsGET, + isDownloadPendingFilesParamsPOST, + isDownloadPendingAvatarsParamsPOST, + isGetCurrentImportOperationParamsGET, +} from '@rocket.chat/rest-typings'; + +import { API } from '../api'; +import { Imports } from '../../../models/server'; +import { Importers } from '../../../importer/server'; +import { executeUploadImportFile } from '../../../importer/server/methods/uploadImportFile'; + +API.v1.addRoute( + 'uploadImportFile', + { + authRequired: true, + validateParams: isUploadImportFileParamsPOST, + }, + { + post() { + const { binaryContent, contentType, fileName, importerKey } = this.bodyParams; + + return API.v1.success(executeUploadImportFile(this.userId, binaryContent, contentType, fileName, importerKey)); + }, + }, +); + +API.v1.addRoute( + 'downloadPublicImportFile', + { + authRequired: true, + validateParams: isDownloadPublicImportFileParamsPOST, + }, + { + post() { + const { fileUrl, importerKey } = this.bodyParams; + + Meteor.call('downloadPublicImportFile', fileUrl, importerKey); + + return API.v1.success(); + }, + }, +); + +API.v1.addRoute( + 'startImport', + { + authRequired: true, + validateParams: isStartImportParamsPOST, + }, + { + post() { + const { input } = this.bodyParams; + + Meteor.call('startImport', input); + + return API.v1.success(); + }, + }, +); + +API.v1.addRoute( + 'getImportFileData', + { + authRequired: true, + validateParams: isGetImportFileDataParamsGET, + }, + { + get() { + const result = Meteor.call('getImportFileData'); + return API.v1.success(result); + }, + }, +); + +API.v1.addRoute( + 'getImportProgress', + { + authRequired: true, + validateParams: isGetImportProgressParamsGET, + }, + { + get() { + const result = Meteor.call('getImportProgress'); + return API.v1.success(result); + }, + }, +); + +API.v1.addRoute( + 'getLatestImportOperations', + { + authRequired: true, + validateParams: isGetLatestImportOperationsParamsGET, + }, + { + get() { + const result = Meteor.call('getLatestImportOperations'); + return API.v1.success(result); + }, + }, +); + +API.v1.addRoute( + 'downloadPendingFiles', + { + authRequired: true, + validateParams: isDownloadPendingFilesParamsPOST, + permissionsRequired: ['run-import'], + }, + { + post() { + const importer = Importers.get('pending-files'); + if (!importer) { + throw new Meteor.Error('error-importer-not-defined', 'The Pending File Importer was not found.', { + method: 'downloadPendingFiles', + }); + } + + importer.instance = new importer.importer(importer); // eslint-disable-line new-cap + const count = importer.instance.prepareFileCount(); + + return API.v1.success({ + count, + }); + }, + }, +); + +API.v1.addRoute( + 'downloadPendingAvatars', + { + authRequired: true, + validateParams: isDownloadPendingAvatarsParamsPOST, + permissionsRequired: ['run-import'], + }, + { + post() { + const importer = Importers.get('pending-avatars'); + if (!importer) { + throw new Meteor.Error('error-importer-not-defined', 'The Pending File Importer was not found.', { + method: 'downloadPendingAvatars', + }); + } + + importer.instance = new importer.importer(importer); // eslint-disable-line new-cap + const count = importer.instance.prepareFileCount(); + + return API.v1.success({ + count, + }); + }, + }, +); + +API.v1.addRoute( + 'getCurrentImportOperation', + { + authRequired: true, + validateParams: isGetCurrentImportOperationParamsGET, + permissionsRequired: ['run-import'], + }, + { + get() { + const operation = Imports.findLastImport(); + return API.v1.success({ + success: true, + operation, + }); + }, + }, +); diff --git a/apps/meteor/app/api/server/v1/instances.ts b/apps/meteor/app/api/server/v1/instances.ts index 6826d6733743..204f1ceb9440 100644 --- a/apps/meteor/app/api/server/v1/instances.ts +++ b/apps/meteor/app/api/server/v1/instances.ts @@ -1,9 +1,9 @@ import type { IInstanceStatus } from '@rocket.chat/core-typings'; +import { InstanceStatus } from '@rocket.chat/models'; import { getInstanceConnection } from '../../../../server/stream/streamBroadcast'; import { hasPermission } from '../../../authorization/server'; import { API } from '../api'; -import { InstanceStatus } from '../../../models/server/raw'; API.v1.addRoute( 'instances.get', diff --git a/apps/meteor/app/api/server/v1/integrations.ts b/apps/meteor/app/api/server/v1/integrations.ts index 61413e87af3a..4f3127e58fbb 100644 --- a/apps/meteor/app/api/server/v1/integrations.ts +++ b/apps/meteor/app/api/server/v1/integrations.ts @@ -8,9 +8,10 @@ import { isIntegrationsGetProps, isIntegrationsUpdateProps, } from '@rocket.chat/rest-typings'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; +import type { Filter } from 'mongodb'; import { hasAtLeastOnePermission } from '../../../authorization/server'; -import { Integrations, IntegrationHistory } from '../../../models/server/raw'; import { API } from '../api'; import { mountIntegrationHistoryQueryBasedOnPermissions, @@ -55,15 +56,14 @@ API.v1.addRoute( const { sort, fields: projection, query } = this.parseJsonQuery(); const ourQuery = Object.assign(mountIntegrationHistoryQueryBasedOnPermissions(userId, id), query); - const cursor = IntegrationHistory.find(ourQuery, { + const { cursor, totalCount } = IntegrationHistory.findPaginated(ourQuery, { sort: sort || { _updatedAt: -1 }, skip: offset, limit: count, projection, }); - const history = await cursor.toArray(); - const total = await cursor.count(); + const [history, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ history, @@ -80,7 +80,7 @@ API.v1.addRoute( 'integrations.list', { authRequired: true }, { - get() { + async get() { if ( !hasAtLeastOnePermission(this.userId, [ 'manage-outgoing-integrations', @@ -95,17 +95,16 @@ API.v1.addRoute( const { offset, count } = this.getPaginationItems(); const { sort, fields: projection, query } = this.parseJsonQuery(); - const ourQuery = Object.assign(mountIntegrationQueryBasedOnPermissions(this.userId), query); - const cursor = Integrations.find(ourQuery, { + const ourQuery = Object.assign(mountIntegrationQueryBasedOnPermissions(this.userId), query) as Filter; + + const { cursor, totalCount } = Integrations.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, projection, }); - const total = Promise.await(cursor.count()); - - const integrations = Promise.await(cursor.toArray()); + const [integrations, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ integrations, diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index 38e884bd6600..bc27049c92c1 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -433,7 +433,7 @@ API.v1.addRoute( ); declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/interface-name-prefix + // eslint-disable-next-line @typescript-eslint/naming-convention interface Endpoints { 'method.call/:method': { POST: (params: { method: string; args: any[] }) => any; diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index 50d9549dac67..a9d48884595d 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -1,7 +1,7 @@ import { isOauthAppsGetParams } from '@rocket.chat/rest-typings'; +import { OAuthApps } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { OAuthApps } from '../../../models/server/raw'; import { API } from '../api'; API.v1.addRoute( diff --git a/apps/meteor/app/api/server/v1/permissions.ts b/apps/meteor/app/api/server/v1/permissions.ts index f820c733c4b7..d03fab78e7fb 100644 --- a/apps/meteor/app/api/server/v1/permissions.ts +++ b/apps/meteor/app/api/server/v1/permissions.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import type { IPermission } from '@rocket.chat/core-typings'; import { isBodyParamsValidPermissionUpdate } from '@rocket.chat/rest-typings'; +import { Permissions, Roles } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; import { API } from '../api'; -import { Permissions, Roles } from '../../../models/server/raw'; API.v1.addRoute( 'permissions.listAll', diff --git a/apps/meteor/app/api/server/v1/push.ts b/apps/meteor/app/api/server/v1/push.ts index 1dc649cb5adf..9ed60260d422 100644 --- a/apps/meteor/app/api/server/v1/push.ts +++ b/apps/meteor/app/api/server/v1/push.ts @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { Match, check } from 'meteor/check'; +import { Messages } from '@rocket.chat/models'; import { appTokensCollection } from '../../../push/server'; import { API } from '../api'; import PushNotification from '../../../push-notifications/server/lib/PushNotification'; import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; import { Users, Rooms } from '../../../models/server'; -import { Messages } from '../../../models/server/raw'; API.v1.addRoute( 'push.token', @@ -38,6 +38,7 @@ API.v1.addRoute( Meteor.call('raix:push-update', { id: deviceId, token: { [type]: value }, + authToken: this.request.headers['x-auth-token'], appName, userId: this.userId, }), diff --git a/apps/meteor/app/api/server/v1/roles.ts b/apps/meteor/app/api/server/v1/roles.ts index 9785741326d7..252421d1a9b6 100644 --- a/apps/meteor/app/api/server/v1/roles.ts +++ b/apps/meteor/app/api/server/v1/roles.ts @@ -8,13 +8,14 @@ import { isRoleUpdateProps, } from '@rocket.chat/rest-typings'; import type { IRole } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; import { Users } from '../../../models/server'; import { API } from '../api'; -import { getUsersInRole, hasRole } from '../../../authorization/server'; +import { hasRole } from '../../../authorization/server'; +import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole'; import { settings } from '../../../settings/server/index'; import { api } from '../../../../server/sdk/api'; -import { Roles } from '../../../models/server/raw'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -175,14 +176,16 @@ API.v1.addRoute( apiDeprecationLogger.warn(`Querying roles by name is deprecated and will be removed on the next major release of Rocket.Chat`); } - const users = await getUsersInRole(roleData._id, roomId, { + const { cursor, totalCount } = await getUsersInRolePaginated(roleData._id, roomId, { limit: count as number, sort: { username: 1 }, skip: offset as number, projection, }); - return API.v1.success({ users: await users.toArray(), total: await users.count() }); + const [users, total] = await Promise.all([cursor.toArray(), totalCount]); + + return API.v1.success({ users, total }); }, }, ); diff --git a/apps/meteor/app/api/server/v1/rooms.js b/apps/meteor/app/api/server/v1/rooms.js index 3efbf9f00116..ee059908eda0 100644 --- a/apps/meteor/app/api/server/v1/rooms.js +++ b/apps/meteor/app/api/server/v1/rooms.js @@ -1,7 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { Rooms as RoomsRaw } from '@rocket.chat/models'; import { FileUpload } from '../../../file-upload'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; import { API } from '../api'; import { findAdminRooms, @@ -293,7 +294,7 @@ API.v1.addRoute( 'rooms.getDiscussions', { authRequired: true }, { - get() { + async get() { const room = findRoomByIdOrName({ params: this.requestParams() }); const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); @@ -304,18 +305,20 @@ API.v1.addRoute( const ourQuery = Object.assign(query, { prid: room._id }); - const discussions = Rooms.find(ourQuery, { + const { cursor, totalCount } = RoomsRaw.findPaginated(ourQuery, { sort: sort || { fname: 1 }, skip: offset, limit: count, - fields, - }).fetch(); + projection: fields, + }); + + const [discussions, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ discussions, count: discussions.length, offset, - total: Rooms.find(ourQuery).count(), + total, }); }, }, diff --git a/apps/meteor/app/api/server/v1/settings.ts b/apps/meteor/app/api/server/v1/settings.ts index f91c09d2166a..1a1880aa545d 100644 --- a/apps/meteor/app/api/server/v1/settings.ts +++ b/apps/meteor/app/api/server/v1/settings.ts @@ -8,30 +8,33 @@ import { isSettingsUpdatePropsActions, isSettingsUpdatePropsColor, } from '@rocket.chat/rest-typings'; +import { Settings } from '@rocket.chat/models'; +import type { FindOptions } from 'mongodb'; -import { Settings } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { API, ResultFor } from '../api'; import { SettingsEvents, settings } from '../../../settings/server'; import { setValue } from '../../../settings/server/raw'; -const fetchSettings = async ( +async function fetchSettings( query: Parameters[0], - sort: Parameters[1]['sort'], - offset: Parameters[1]['skip'], - count: Parameters[1]['limit'], - fields: Parameters[1]['projection'], -): Promise => { - const settings = (await Settings.find(query, { + sort: FindOptions['sort'], + offset: FindOptions['skip'], + count: FindOptions['limit'], + fields: FindOptions['projection'], +): Promise<{ settings: ISetting[]; totalCount: number }> { + const { cursor, totalCount } = Settings.findPaginated(query || {}, { sort: sort || { _id: 1 }, skip: offset, limit: count, projection: { _id: 1, value: 1, enterprise: 1, invalidValue: 1, modules: 1, ...fields }, - }).toArray()) as unknown as ISetting[]; + }); + + const [settings, total] = await Promise.all([cursor.toArray(), totalCount]); SettingsEvents.emit('fetch-settings', settings); - return settings; -}; + return { settings, totalCount: total }; +} // settings endpoints API.v1.addRoute( @@ -48,13 +51,13 @@ API.v1.addRoute( public: true, }; - const settings = await fetchSettings(ourQuery, sort, offset, count, fields); + const { settings, totalCount: total } = await fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, count: settings.length, offset, - total: await Settings.find(ourQuery).count(), + total, }); }, }, @@ -126,13 +129,13 @@ API.v1.addRoute( ourQuery = Object.assign({}, query, ourQuery); - const settings = await fetchSettings(ourQuery, sort, offset, count, fields); + const { settings, totalCount: total } = await fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, count: settings.length, offset, - total: Settings.find(ourQuery).count(), + total, }); }, }, diff --git a/apps/meteor/app/api/server/v1/subscriptions.ts b/apps/meteor/app/api/server/v1/subscriptions.ts index c042863eb1ec..08b186301f91 100644 --- a/apps/meteor/app/api/server/v1/subscriptions.ts +++ b/apps/meteor/app/api/server/v1/subscriptions.ts @@ -5,8 +5,8 @@ import { isSubscriptionsReadProps, isSubscriptionsUnreadProps, } from '@rocket.chat/rest-typings'; +import { Subscriptions } from '@rocket.chat/models'; -import { Subscriptions } from '../../../models/server/raw'; import { API } from '../api'; API.v1.addRoute( diff --git a/apps/meteor/app/api/server/v1/teams.ts b/apps/meteor/app/api/server/v1/teams.ts index 157f1ac6d894..4e905f51797e 100644 --- a/apps/meteor/app/api/server/v1/teams.ts +++ b/apps/meteor/app/api/server/v1/teams.ts @@ -1,8 +1,6 @@ -import type { FilterQuery } from 'mongodb'; import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import type { IUser } from '@rocket.chat/core-typings'; import { isTeamsConvertToChannelProps, isTeamsRemoveRoomProps, @@ -141,11 +139,9 @@ API.v1.addRoute( }); } - await Promise.all([ - Team.unsetTeamIdOfRooms(this.userId, team._id), - Team.removeAllMembersFromTeam(team._id), - Team.deleteById(team._id), - ]); + await Promise.all([Team.unsetTeamIdOfRooms(this.userId, team._id), Team.removeAllMembersFromTeam(team._id)]); + + await Team.deleteById(team._id); return API.v1.success(); }, @@ -413,7 +409,7 @@ API.v1.addRoute( username: username ? new RegExp(escapeRegExp(username), 'i') : undefined, name: name ? new RegExp(escapeRegExp(name), 'i') : undefined, status: status ? { $in: status } : undefined, - } as FilterQuery; + }; const { records, total } = await Team.members(this.userId, team._id, canSeeAllMembers, { offset, count }, query); diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index 8e6808fd34b4..944f33dce8d6 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -17,9 +17,9 @@ import { Accounts } from 'meteor/accounts-base'; import { Match, check } from 'meteor/check'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { IExportOperation, IPersonalAccessToken, IUser } from '@rocket.chat/core-typings'; +import { Users as UsersRaw } from '@rocket.chat/models'; import { Users, Subscriptions } from '../../../models/server'; -import { Users as UsersRaw } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { settings } from '../../../settings/server'; import { @@ -366,6 +366,7 @@ API.v1.addRoute( t: 1, roles: 1, unread: 1, + federated: 1, }, sort: { t: 1, @@ -390,7 +391,7 @@ API.v1.addRoute( queryOperations: ['$or', '$and'], }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-d-room')) { return API.v1.unauthorized(); } @@ -398,7 +399,7 @@ API.v1.addRoute( const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); - const nonEmptyQuery = getNonEmptyQuery(query); + const nonEmptyQuery = getNonEmptyQuery(query, hasPermission(this.userId, 'view-full-other-user-info')); const nonEmptyFields = getNonEmptyFields(fields); const inclusiveFields = getInclusiveFields(nonEmptyFields); @@ -413,6 +414,7 @@ API.v1.addRoute( inclusiveFieldsKeys.includes('emails') && 'emails.address.*', inclusiveFieldsKeys.includes('username') && 'username.*', inclusiveFieldsKeys.includes('name') && 'name.*', + inclusiveFieldsKeys.includes('type') && 'type.*', ].filter(Boolean) as string[], this.queryOperations, ) @@ -431,39 +433,37 @@ API.v1.addRoute( ] : []; - const result = Promise.await( - UsersRaw.col - .aggregate([ - { - $match: nonEmptyQuery, - }, - { - $project: inclusiveFields, - }, - { - $addFields: { - nameInsensitive: { - $toLower: '$name', - }, + const result = await UsersRaw.col + .aggregate<{ sortedResults: IUser[]; totalCount: { total: number }[] }>([ + { + $match: nonEmptyQuery, + }, + { + $project: inclusiveFields, + }, + { + $addFields: { + nameInsensitive: { + $toLower: '$name', }, }, - { - $facet: { - sortedResults: [ - { - $sort: actualSort, - }, - { - $skip: offset, - }, - ...limit, - ], - totalCount: [{ $group: { _id: null, total: { $sum: 1 } } }], - }, + }, + { + $facet: { + sortedResults: [ + { + $sort: actualSort, + }, + { + $skip: offset, + }, + ...limit, + ], + totalCount: [{ $group: { _id: null, total: { $sum: 1 } } }], }, - ]) - .toArray(), - ); + }, + ]) + .toArray(); const { sortedResults: users, @@ -485,7 +485,7 @@ API.v1.addRoute( { authRequired: false, rateLimiterOptions: { - numRequestsAllowed: settings.get('Rate_Limiter_Limit_RegisterUser'), + numRequestsAllowed: settings.get('Rate_Limiter_Limit_RegisterUser') ?? 1, intervalTimeInMS: settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), }, validateParams: isUserRegisterParamsPOST, diff --git a/apps/meteor/app/api/server/v1/videoConference.ts b/apps/meteor/app/api/server/v1/videoConference.ts index 34a78fdf09da..f471fcaebe40 100644 --- a/apps/meteor/app/api/server/v1/videoConference.ts +++ b/apps/meteor/app/api/server/v1/videoConference.ts @@ -1,25 +1,185 @@ -import { Meteor } from 'meteor/meteor'; +import type { VideoConference } from '@rocket.chat/core-typings'; +import { + isVideoConfStartProps, + isVideoConfJoinProps, + isVideoConfCancelProps, + isVideoConfInfoProps, + isVideoConfListProps, +} from '@rocket.chat/rest-typings'; -import { Rooms } from '../../../models/server'; import { API } from '../api'; +import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { VideoConf } from '../../../../server/sdk'; +import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; +import { availabilityErrors } from '../../../../lib/videoConference/constants'; API.v1.addRoute( - 'video-conference/jitsi.update-timeout', - { authRequired: true }, + 'video-conference.start', + { authRequired: true, validateParams: isVideoConfStartProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 60000 } }, { - post() { - const { roomId, joiningNow = true } = this.bodyParams; - if (!roomId) { - return API.v1.failure('The "roomId" parameter is required!'); + async post() { + const { roomId, title, allowRinging: requestRinging } = this.bodyParams; + const { userId } = this; + if (!userId || !(await canAccessRoomIdAsync(roomId, userId))) { + return API.v1.failure('invalid-params'); } - const room = Rooms.findOneById(roomId, { fields: { _id: 1 } }); - if (!room) { - return API.v1.failure('Room does not exist!'); + try { + const providerName = videoConfProviders.getActiveProvider(); + + if (!providerName) { + throw new Error(availabilityErrors.NOT_ACTIVE); + } + + const allowRinging = Boolean(requestRinging) && (await hasPermissionAsync(userId, 'videoconf-ring-users')); + + return API.v1.success({ + data: { + ...(await VideoConf.start(userId, roomId, { title, allowRinging })), + providerName, + }, + }); + } catch (e) { + return API.v1.failure(await VideoConf.diagnoseProvider(userId, roomId)); + } + }, + }, +); + +API.v1.addRoute( + 'video-conference.join', + { authOrAnonRequired: true, validateParams: isVideoConfJoinProps, rateLimiterOptions: { numRequestsAllowed: 2, intervalTimeInMS: 5000 } }, + { + async post() { + const { callId, state } = this.bodyParams; + const { userId } = this; + + const call = await VideoConf.get(callId); + if (!call) { + return API.v1.failure('invalid-params'); + } + + if (!(await canAccessRoomIdAsync(call.rid, userId))) { + return API.v1.failure('invalid-params'); + } + + let url: string | undefined; + + try { + url = await VideoConf.join(userId, callId, { + ...(state?.cam !== undefined ? { cam: state.cam } : {}), + ...(state?.mic !== undefined ? { mic: state.mic } : {}), + }); + } catch (e) { + if (userId) { + return API.v1.failure(await VideoConf.diagnoseProvider(userId, call.rid, call.providerName)); + } + } + + if (!url) { + return API.v1.failure('failed-to-get-url'); + } + + return API.v1.success({ + url, + providerName: call.providerName, + }); + }, + }, +); + +API.v1.addRoute( + 'video-conference.cancel', + { authRequired: true, validateParams: isVideoConfCancelProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 60000 } }, + { + async post() { + const { callId } = this.bodyParams; + const { userId } = this; + + const call = await VideoConf.get(callId); + if (!call) { + return API.v1.failure('invalid-params'); + } + + if (!userId || !(await canAccessRoomIdAsync(call.rid, userId))) { + return API.v1.failure('invalid-params'); + } + + await VideoConf.cancel(userId, callId); + return API.v1.success(); + }, + }, +); + +API.v1.addRoute( + 'video-conference.info', + { authRequired: true, validateParams: isVideoConfInfoProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const { callId } = this.queryParams; + const { userId } = this; + + const call = await VideoConf.get(callId); + if (!call) { + return API.v1.failure('invalid-params'); } - const jitsiTimeout = Meteor.runAsUser(this.userId, () => Meteor.call('jitsi:updateTimeout', roomId, Boolean(joiningNow))); - return API.v1.success({ jitsiTimeout }); + if (!userId || !(await canAccessRoomIdAsync(call.rid, userId))) { + return API.v1.failure('invalid-params'); + } + + const capabilities = await VideoConf.listProviderCapabilities(call.providerName); + + return API.v1.success({ + ...(call as VideoConference), + capabilities, + }); + }, + }, +); + +API.v1.addRoute( + 'video-conference.list', + { authRequired: true, validateParams: isVideoConfListProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const { roomId } = this.queryParams; + const { userId } = this; + + const { offset, count } = this.getPaginationItems(); + + if (!userId || !(await canAccessRoomIdAsync(roomId, userId))) { + return API.v1.failure('invalid-params'); + } + + const data = await VideoConf.list(roomId, { offset, count }); + + return API.v1.success(data); + }, + }, +); + +API.v1.addRoute( + 'video-conference.providers', + { authRequired: true, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const data = await VideoConf.listProviders(); + + return API.v1.success({ data }); + }, + }, +); + +API.v1.addRoute( + 'video-conference.capabilities', + { authRequired: true, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const data = await VideoConf.listCapabilities(); + + return API.v1.success(data); }, }, ); diff --git a/apps/meteor/app/api/server/v1/voip/events.ts b/apps/meteor/app/api/server/v1/voip/events.ts index 13648aff6e64..5b3d61a46f6d 100644 --- a/apps/meteor/app/api/server/v1/voip/events.ts +++ b/apps/meteor/app/api/server/v1/voip/events.ts @@ -1,10 +1,10 @@ import { Match, check } from 'meteor/check'; import { VoipClientEvents } from '@rocket.chat/core-typings'; +import { VoipRoom } from '@rocket.chat/models'; import { API } from '../../api'; import { LivechatVoip } from '../../../../../server/sdk'; import { canAccessRoom } from '../../../../authorization/server'; -import { VoipRoom } from '../../../../models/server/raw'; API.v1.addRoute( 'voip/events', diff --git a/apps/meteor/app/api/server/v1/voip/extensions.ts b/apps/meteor/app/api/server/v1/voip/extensions.ts index 7d23a991fefd..598a8bce662e 100644 --- a/apps/meteor/app/api/server/v1/voip/extensions.ts +++ b/apps/meteor/app/api/server/v1/voip/extensions.ts @@ -1,8 +1,8 @@ import { Match, check } from 'meteor/check'; import type { IVoipExtensionBase } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { API } from '../../api'; -import { Users } from '../../../../models/server/raw/index'; import { Voip } from '../../../../../server/sdk'; import { generateJWT } from '../../../../utils/server/lib/JWTHelper'; import { settings } from '../../../../settings/server'; diff --git a/apps/meteor/app/api/server/v1/voip/omnichannel.ts b/apps/meteor/app/api/server/v1/voip/omnichannel.ts index 521da77aa256..28ef3608be67 100644 --- a/apps/meteor/app/api/server/v1/voip/omnichannel.ts +++ b/apps/meteor/app/api/server/v1/voip/omnichannel.ts @@ -1,8 +1,8 @@ import { Match, check } from 'meteor/check'; import { IUser, IVoipExtensionWithAgentInfo } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { API } from '../../api'; -import { Users } from '../../../../models/server/raw/index'; import { hasPermission } from '../../../../authorization/server/index'; import { LivechatVoip } from '../../../../../server/sdk'; import { logger } from './logger'; @@ -33,38 +33,6 @@ API.v1.addRoute( 'omnichannel/agent/extension', { authRequired: true }, { - // Get the extensions associated with the agent passed as request params. - async get() { - if (!hasPermission(this.userId, 'view-agent-extension-association')) { - return API.v1.unauthorized(); - } - check( - this.requestParams(), - Match.ObjectIncluding({ - username: String, - }), - ); - const { username } = this.requestParams(); - const user = await Users.findOneByAgentUsername(username, { - projection: { _id: 1 }, - }); - if (!user) { - return API.v1.notFound('User not found'); - } - const extension = await Users.getVoipExtensionByUserId(user._id, { - projection: { - _id: 1, - username: 1, - extension: 1, - }, - }); - if (!extension) { - return API.v1.notFound('Extension not found'); - } - return API.v1.success({ extension }); - }, - - // Create agent-extension association. async post() { if (!hasPermission(this.userId, 'manage-agent-extension-association')) { return API.v1.unauthorized(); @@ -121,18 +89,55 @@ API.v1.addRoute( return API.v1.failure(`extension already in use ${extension}`); } }, + }, +); + +API.v1.addRoute( + 'omnichannel/agent/extension/:username', + { authRequired: true }, + { + // Get the extensions associated with the agent passed as request params. + async get() { + if (!hasPermission(this.userId, 'view-agent-extension-association')) { + return API.v1.unauthorized(); + } + check( + this.urlParams, + Match.ObjectIncluding({ + username: String, + }), + ); + const { username } = this.urlParams; + const user = await Users.findOneByAgentUsername(username, { + projection: { _id: 1 }, + }); + if (!user) { + return API.v1.notFound('User not found'); + } + const extension = await Users.getVoipExtensionByUserId(user._id, { + projection: { + _id: 1, + username: 1, + extension: 1, + }, + }); + if (!extension) { + return API.v1.notFound('Extension not found'); + } + return API.v1.success({ extension }); + }, async delete() { if (!hasPermission(this.userId, 'manage-agent-extension-association')) { return API.v1.unauthorized(); } check( - this.requestParams(), + this.urlParams, Match.ObjectIncluding({ username: String, }), ); - const { username } = this.requestParams(); + const { username } = this.urlParams; const user = await Users.findOneByAgentUsername(username, { projection: { _id: 1, diff --git a/apps/meteor/app/api/server/v1/voip/rooms.ts b/apps/meteor/app/api/server/v1/voip/rooms.ts index ee3a37fc1347..1798591f57db 100644 --- a/apps/meteor/app/api/server/v1/voip/rooms.ts +++ b/apps/meteor/app/api/server/v1/voip/rooms.ts @@ -1,10 +1,9 @@ -import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; -import type { ILivechatAgent } from '@rocket.chat/core-typings'; -import { isVoipRoomCloseProps } from '@rocket.chat/rest-typings/dist/v1/voip'; +import type { ILivechatAgent, IVoipRoom } from '@rocket.chat/core-typings'; +import { isVoipRoomProps, isVoipRoomsProps, isVoipRoomCloseProps } from '@rocket.chat/rest-typings'; +import { VoipRoom, LivechatVisitors, Users } from '@rocket.chat/models'; import { API } from '../../api'; -import { VoipRoom, LivechatVisitors, Users } from '../../../../models/server/raw'; import { LivechatVoip } from '../../../../../server/sdk'; import { hasPermission } from '../../../../authorization/server'; import { typedJsonParse } from '../../../../../lib/typedJSONParse'; @@ -25,6 +24,7 @@ const validateDateParams = (property: string, date: DateParam = {}): DateParam = const parseAndValidate = (property: string, date?: string): DateParam => { return validateDateParams(property, parseDateParams(date)); }; + /** * @openapi * /voip/server/api/v1/voip/room @@ -81,23 +81,38 @@ const parseAndValidate = (property: string, date?: string): DateParam => { * $ref: '#/components/schemas/ApiFailureV1' */ +const isRoomSearchProps = (props: any): props is { rid: string; token: string } => { + return 'rid' in props && 'token' in props; +}; + +const isRoomCreationProps = (props: any): props is { agentId: string; direction: IVoipRoom['direction'] } => { + return 'agentId' in props && 'direction' in props; +}; + API.v1.addRoute( 'voip/room', { authRequired: true, rateLimiterOptions: { numRequestsAllowed: 5, intervalTimeInMS: 60000 }, permissionsRequired: ['inbound-voip-calls'], + validateParams: isVoipRoomProps, }, { async get() { - const defaultCheckParams = { - token: String, - agentId: Match.Maybe(String), - rid: Match.Maybe(String), - }; - check(this.queryParams, defaultCheckParams); - - const { token, rid, agentId } = this.queryParams; + const { token } = this.queryParams; + let agentId: string | undefined = undefined; + let direction: IVoipRoom['direction'] = 'inbound'; + let rid: string | undefined = undefined; + + if (isRoomCreationProps(this.queryParams)) { + agentId = this.queryParams.agentId; + direction = this.queryParams.direction; + } + + if (isRoomSearchProps(this.queryParams)) { + rid = this.queryParams.rid; + } + const guest = await LivechatVisitors.getVisitorByToken(token, {}); if (!guest) { return API.v1.failure('invalid-token'); @@ -108,6 +123,9 @@ API.v1.addRoute( if (room) { return API.v1.success({ room, newRoom: false }); } + if (!agentId) { + return API.v1.failure('agent-not-found'); + } const agentObj: ILivechatAgent = await Users.findOneAgentById(agentId, { projection: { username: 1 }, @@ -120,7 +138,11 @@ API.v1.addRoute( const agent = { agentId: _id, username }; const rid = Random.id(); - return API.v1.success(await LivechatVoip.getNewRoom(guest, agent, rid, { projection: API.v1.defaultFieldsToExclude })); + return API.v1.success( + await LivechatVoip.getNewRoom(guest, agent, rid, direction, { + projection: API.v1.defaultFieldsToExclude, + }), + ); } const room = await VoipRoom.findOneByIdAndVisitorToken(rid, token, { projection: API.v1.defaultFieldsToExclude }); @@ -134,20 +156,15 @@ API.v1.addRoute( API.v1.addRoute( 'voip/rooms', - { authRequired: true }, + { authRequired: true, validateParams: isVoipRoomsProps }, { async get() { const { offset, count } = this.getPaginationItems(); + const { sort, fields } = this.parseJsonQuery(); - const { agents, open, tags, queue, visitorId } = this.requestParams(); + const { agents, open, tags, queue, visitorId, direction, roomName } = this.requestParams(); const { createdAt: createdAtParam, closedAt: closedAtParam } = this.requestParams(); - check(agents, Match.Maybe([String])); - check(open, Match.Maybe(String)); - check(tags, Match.Maybe([String])); - check(queue, Match.Maybe(String)); - check(visitorId, Match.Maybe(String)); - // Reusing same L room permissions for simplicity const hasAdminAccess = hasPermission(this.userId, 'view-livechat-rooms'); const hasAgentAccess = hasPermission(this.userId, 'view-l-room') && agents?.includes(this.userId) && agents?.length === 1; @@ -167,6 +184,8 @@ API.v1.addRoute( visitorId, createdAt, closedAt, + direction, + roomName, options: { sort, offset, count, fields }, }), ); diff --git a/apps/meteor/app/apps/client/communication/websockets.js b/apps/meteor/app/apps/client/communication/websockets.js index 6c2707af230d..67f03e0df1ee 100644 --- a/apps/meteor/app/apps/client/communication/websockets.js +++ b/apps/meteor/app/apps/client/communication/websockets.js @@ -51,7 +51,7 @@ export class AppWebsocketReceiver extends Emitter { onCommandAddedOrUpdated = (command) => { APIClient.get('/v1/commands.get', { command }).then((result) => { - slashCommands.commands[command] = result.command; + slashCommands.add(result.command); }); }; diff --git a/apps/meteor/app/apps/client/orchestrator.ts b/apps/meteor/app/apps/client/orchestrator.ts index 155027a31f94..d4052fa67308 100644 --- a/apps/meteor/app/apps/client/orchestrator.ts +++ b/apps/meteor/app/apps/client/orchestrator.ts @@ -89,7 +89,7 @@ class AppClientOrchestrator { } public async getApps(): Promise { - const result = await APIClient.get('/apps'); + const result = await APIClient.get<'/apps'>('/apps'); if ('apps' in result) { // TODO: chapter day: multiple results are returned, but we only need one @@ -134,7 +134,7 @@ class AppClientOrchestrator { return app; } - public async getAppFromMarketplace(appId: string, version: string): Promise { + public async getAppFromMarketplace(appId: string, version: string): Promise<{ app: App; success: boolean }> { const result = await APIClient.get( `/apps/${appId}` as any, { diff --git a/apps/meteor/app/apps/server/bridges/activation.ts b/apps/meteor/app/apps/server/bridges/activation.ts index ac0a2654cae8..a610200b896e 100644 --- a/apps/meteor/app/apps/server/bridges/activation.ts +++ b/apps/meteor/app/apps/server/bridges/activation.ts @@ -1,8 +1,8 @@ import { AppActivationBridge as ActivationBridge } from '@rocket.chat/apps-engine/server/bridges/AppActivationBridge'; import { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp'; import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server/raw'; import { AppServerOrchestrator } from '../orchestrator'; export class AppActivationBridge extends ActivationBridge { diff --git a/apps/meteor/app/apps/server/bridges/api.ts b/apps/meteor/app/apps/server/bridges/api.ts index ea2b8f05e689..fcca596a47b4 100644 --- a/apps/meteor/app/apps/server/bridges/api.ts +++ b/apps/meteor/app/apps/server/bridges/api.ts @@ -7,6 +7,7 @@ import { AppApi } from '@rocket.chat/apps-engine/server/managers/AppApi'; import { RequestMethod } from '@rocket.chat/apps-engine/definition/accessors'; import { AppServerOrchestrator } from '../orchestrator'; +import { authenticationMiddleware } from '../../../api/server/middlewares/authentication'; const apiServer = express(); @@ -14,10 +15,10 @@ apiServer.disable('x-powered-by'); WebApp.connectHandlers.use(apiServer); -type RequestWithPrivateHash = Request & { +interface IRequestWithPrivateHash extends Request { _privateHash?: string; content?: any; -}; +} export class AppApisBridge extends ApiBridge { appRouters: Map; @@ -27,7 +28,7 @@ export class AppApisBridge extends ApiBridge { super(); this.appRouters = new Map(); - apiServer.use('/api/apps/private/:appId/:hash', (req: RequestWithPrivateHash, res: Response) => { + apiServer.use('/api/apps/private/:appId/:hash', (req: IRequestWithPrivateHash, res: Response) => { const notFound = (): Response => res.sendStatus(404); const router = this.appRouters.get(req.params.appId); @@ -73,7 +74,7 @@ export class AppApisBridge extends ApiBridge { } if (router[method] instanceof Function) { - router[method](routePath, Meteor.bindEnvironment(this._appApiExecutor(endpoint, appId))); + router[method](routePath, this._authMiddleware(endpoint, appId), Meteor.bindEnvironment(this._appApiExecutor(endpoint, appId))); } } @@ -85,6 +86,11 @@ export class AppApisBridge extends ApiBridge { } } + private _authMiddleware(endpoint: IApiEndpoint, _appId: string): RequestHandler { + const authFunction = authenticationMiddleware({ rejectUnauthorized: !!endpoint.authRequired }); + return Meteor.bindEnvironment(authFunction); + } + private _verifyApi(api: IApi, endpoint: IApiEndpoint): void { if (typeof api !== 'object') { throw new Error('Invalid Api parameter provided, it must be a valid IApi object.'); @@ -96,7 +102,7 @@ export class AppApisBridge extends ApiBridge { } private _appApiExecutor(endpoint: IApiEndpoint, appId: string): RequestHandler { - return (req: RequestWithPrivateHash, res: Response): void => { + return (req: IRequestWithPrivateHash, res: Response): void => { const request: IApiRequest = { method: req.method.toLowerCase() as RequestMethod, headers: req.headers as { [key: string]: string }, @@ -104,6 +110,7 @@ export class AppApisBridge extends ApiBridge { params: req.params || {}, content: req.body, privateHash: req._privateHash, + user: req.user && this.orch.getConverters()?.get('users')?.convertToApp(req.user), }; this.orch diff --git a/apps/meteor/app/apps/server/bridges/bridges.js b/apps/meteor/app/apps/server/bridges/bridges.js index d03160e02d0f..828be594640d 100644 --- a/apps/meteor/app/apps/server/bridges/bridges.js +++ b/apps/meteor/app/apps/server/bridges/bridges.js @@ -18,6 +18,7 @@ import { AppLivechatBridge } from './livechat'; import { AppUploadBridge } from './uploads'; import { UiInteractionBridge } from './uiInteraction'; import { AppSchedulerBridge } from './scheduler'; +import { AppVideoConferenceBridge } from './videoConferences'; export class RealAppBridges extends AppBridges { constructor(orch) { @@ -41,6 +42,7 @@ export class RealAppBridges extends AppBridges { this._uiInteractionBridge = new UiInteractionBridge(orch); this._schedulerBridge = new AppSchedulerBridge(orch); this._cloudWorkspaceBridge = new AppCloudBridge(orch); + this._videoConfBridge = new AppVideoConferenceBridge(orch); } getCommandBridge() { @@ -114,4 +116,8 @@ export class RealAppBridges extends AppBridges { getCloudWorkspaceBridge() { return this._cloudWorkspaceBridge; } + + getVideoConferenceBridge() { + return this._videoConfBridge; + } } diff --git a/apps/meteor/app/apps/server/bridges/commands.ts b/apps/meteor/app/apps/server/bridges/commands.ts index 9f6448794e4d..8c9a429af170 100644 --- a/apps/meteor/app/apps/server/bridges/commands.ts +++ b/apps/meteor/app/apps/server/bridges/commands.ts @@ -6,6 +6,7 @@ import type { IMessage, RequiredField, SlashCommand } from '@rocket.chat/core-ty import { slashCommands } from '../../../utils/server'; import { Utilities } from '../../lib/misc/Utilities'; import { AppServerOrchestrator } from '../orchestrator'; +import { parseParameters } from '../../../../lib/utils/parseParameters'; export class AppCommandsBridge extends CommandBridge { disabledCommands: Map; @@ -169,9 +170,15 @@ export class AppCommandsBridge extends CommandBridge { const user = this.orch.getConverters()?.get('users').convertById(Meteor.userId()); const room = this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; - const params = parameters.length === 0 || parameters === ' ' ? [] : parameters.split(' '); + const params = parseParameters(parameters); - const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params), threadId, triggerId); + const context = new SlashCommandContext( + Object.freeze(user), + Object.freeze(room), + Object.freeze(params) as string[], + threadId, + triggerId, + ); Promise.await(this.orch.getManager()?.getCommandManager().executeCommand(command, context)); } @@ -180,9 +187,9 @@ export class AppCommandsBridge extends CommandBridge { const user = this.orch.getConverters()?.get('users').convertById(Meteor.userId()); const room = this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; - const params = parameters.length === 0 || parameters === ' ' ? [] : parameters.split(' '); + const params = parseParameters(parameters); - const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params), threadId); + const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params) as string[], threadId); return Promise.await(this.orch.getManager()?.getCommandManager().getPreviews(command, context)); } @@ -196,9 +203,15 @@ export class AppCommandsBridge extends CommandBridge { const user = this.orch.getConverters()?.get('users').convertById(Meteor.userId()); const room = this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; - const params = parameters.length === 0 || parameters === ' ' ? [] : parameters.split(' '); - - const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params), threadId, triggerId); + const params = parseParameters(parameters); + + const context = new SlashCommandContext( + Object.freeze(user), + Object.freeze(room), + Object.freeze(params) as string[], + threadId, + triggerId, + ); await this.orch.getManager()?.getCommandManager().executePreview(command, preview, context); } diff --git a/apps/meteor/app/apps/server/bridges/http.ts b/apps/meteor/app/apps/server/bridges/http.ts index f93ac8a42459..cd0cae9122fe 100644 --- a/apps/meteor/app/apps/server/bridges/http.ts +++ b/apps/meteor/app/apps/server/bridges/http.ts @@ -114,7 +114,7 @@ export class AppHttpBridge extends HttpBridge { } return result; - } catch (e) { + } catch (e: any) { return e.response; } } diff --git a/apps/meteor/app/apps/server/bridges/internal.ts b/apps/meteor/app/apps/server/bridges/internal.ts index 64cb2cc59ed0..7b21e61139ed 100644 --- a/apps/meteor/app/apps/server/bridges/internal.ts +++ b/apps/meteor/app/apps/server/bridges/internal.ts @@ -1,10 +1,10 @@ import { InternalBridge } from '@rocket.chat/apps-engine/server/bridges/InternalBridge'; import { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import type { ISubscription } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { AppServerOrchestrator } from '../orchestrator'; import { Subscriptions } from '../../../models/server'; -import { Settings } from '../../../models/server/raw'; export class AppInternalBridge extends InternalBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 23a0e91411da..28f28a9ae4c9 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -11,10 +11,11 @@ import { IUser } from '@rocket.chat/apps-engine/definition/users'; import { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { IExtraRoomParams } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { getRoom } from '../../../livechat/server/api/lib/livechat'; import { Livechat } from '../../../livechat/server/lib/Livechat'; -import { Users, LivechatDepartment, LivechatVisitors, LivechatRooms } from '../../../models/server'; +import { Users, LivechatDepartment, LivechatRooms } from '../../../models/server'; import { AppServerOrchestrator } from '../orchestrator'; export class AppLivechatBridge extends LivechatBridge { @@ -216,9 +217,9 @@ export class AppLivechatBridge extends LivechatBridge { console.warn('The method AppLivechatBridge.findVisitors is deprecated. Please consider using its alternatives'); } - return LivechatVisitors.find(query) - .fetch() - .map((visitor: IVisitor) => this.orch.getConverters()?.get('visitors').convertVisitor(visitor)); + return (await LivechatVisitors.find(query).toArray()).map( + (visitor) => visitor && this.orch.getConverters()?.get('visitors').convertVisitor(visitor), + ); } protected async findVisitorById(id: string, appId: string): Promise { @@ -230,19 +231,28 @@ export class AppLivechatBridge extends LivechatBridge { protected async findVisitorByEmail(email: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.findOneGuestByEmailAddress(email)); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.findOneGuestByEmailAddress(email)); } protected async findVisitorByToken(token: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.getVisitorByToken(token, {})); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.getVisitorByToken(token, {})); } protected async findVisitorByPhoneNumber(phoneNumber: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.findOneVisitorByPhone(phoneNumber)); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.findOneVisitorByPhone(phoneNumber)); } protected async findDepartmentByIdOrName(value: string, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index e2f4db549c07..ece43ca5d914 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -159,7 +159,7 @@ export class AppRoomBridge extends RoomBridge { const discussion = { prid: rcRoom.prid, - t_name: rcRoom.fname, // eslint-disable-line @typescript-eslint/camelcase + t_name: rcRoom.fname, pmid: rcMessage ? rcMessage._id : undefined, reply: reply && reply.trim() !== '' ? reply : undefined, users: members.length > 0 ? members : [], diff --git a/apps/meteor/app/apps/server/bridges/scheduler.ts b/apps/meteor/app/apps/server/bridges/scheduler.ts index 0bb388c21ee5..6daede78134d 100644 --- a/apps/meteor/app/apps/server/bridges/scheduler.ts +++ b/apps/meteor/app/apps/server/bridges/scheduler.ts @@ -1,4 +1,4 @@ -import Agenda from 'agenda'; +import { Agenda, Job } from '@rocket.chat/agenda'; import { ObjectID } from 'bson'; import { MongoInternals } from 'meteor/mongo'; import { StartupType, IProcessor, IOnetimeSchedule, IRecurringSchedule } from '@rocket.chat/apps-engine/definition/scheduler'; @@ -6,7 +6,7 @@ import { SchedulerBridge } from '@rocket.chat/apps-engine/server/bridges/Schedul import { AppServerOrchestrator } from '../orchestrator'; -function _callProcessor(processor: Function): (job: Agenda.Job) => void { +function _callProcessor(processor: Function): (job: Job) => void { return (job): void => { const data = job?.attrs?.data || {}; diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index 9262642b2d3c..9062eb923fd0 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -1,7 +1,7 @@ import { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { ServerSettingBridge } from '@rocket.chat/apps-engine/server/bridges/ServerSettingBridge'; +import { Settings } from '@rocket.chat/models'; -import { Settings } from '../../../models/server/raw'; import { AppServerOrchestrator } from '../orchestrator'; export class AppSettingBridge extends ServerSettingBridge { @@ -56,6 +56,16 @@ export class AppSettingBridge extends ServerSettingBridge { throw new Error(`The setting "${setting.id}" is not readable.`); } - throw new Error('Method not implemented.'); + await Settings.updateValueById(setting.id, setting.value); + } + + protected async incrementValue(id: string, value: number, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is incrementing the value of the setting ${id}.`); + + if (!(await this.isReadableById(id, appId))) { + throw new Error(`The setting "${id}" is not readable.`); + } + + await Settings.incrementValueById(id, value); } } diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index 68e5e54fc1f2..012ad09227c5 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -2,10 +2,10 @@ import { Random } from 'meteor/random'; import { UserPresence } from 'meteor/konecty:user-presence'; import { UserBridge } from '@rocket.chat/apps-engine/server/bridges/UserBridge'; import { IUserCreationOptions, IUser } from '@rocket.chat/apps-engine/definition/users'; +import { Subscriptions, Users as UsersRaw } from '@rocket.chat/models'; import { setUserAvatar, checkUsernameAvailability, deleteUser } from '../../../lib/server/functions'; import { Users } from '../../../models/server'; -import { Subscriptions, Users as UsersRaw } from '../../../models/server/raw'; import { AppServerOrchestrator } from '../orchestrator'; export class AppUserBridge extends UserBridge { @@ -98,7 +98,7 @@ export class AppUserBridge extends UserBridge { const { status } = fields; delete fields.status; - await UsersRaw.update({ _id: user.id }, { $set: fields }); + await UsersRaw.update({ _id: user.id }, { $set: fields as any }); if (status) { UserPresence.setDefaultStatus(user.id, status); diff --git a/apps/meteor/app/apps/server/bridges/videoConferences.ts b/apps/meteor/app/apps/server/bridges/videoConferences.ts new file mode 100644 index 000000000000..93c9e445f91c --- /dev/null +++ b/apps/meteor/app/apps/server/bridges/videoConferences.ts @@ -0,0 +1,71 @@ +import { VideoConferenceBridge } from '@rocket.chat/apps-engine/server/bridges/VideoConferenceBridge'; +import { AppVideoConference, VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; +import { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders'; + +import { VideoConf } from '../../../../server/sdk'; +import { AppServerOrchestrator } from '../orchestrator'; +import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; +import type { AppVideoConferencesConverter } from '../converters/videoConferences'; + +export class AppVideoConferenceBridge extends VideoConferenceBridge { + // eslint-disable-next-line no-empty-function + constructor(private readonly orch: AppServerOrchestrator) { + super(); + } + + protected async getById(callId: string, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is getting the video conference byId: "${callId}"`); + + return this.orch.getConverters()?.get('videoConferences').convertById(callId); + } + + protected async create(call: AppVideoConference, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is creating a video conference.`); + + return ( + await VideoConf.create({ + type: 'videoconference', + ...call, + }) + ).callId; + } + + protected async update(call: VideoConference, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is updating a video conference.`); + + const oldData = call._id && (await VideoConf.getUnfiltered(call._id)); + if (!oldData) { + throw new Error('A video conference must exist to update.'); + } + + const data = (this.orch.getConverters()?.get('videoConferences') as AppVideoConferencesConverter).convertAppVideoConference(call); + await VideoConf.setProviderData(call._id, data.providerData); + + for (const { _id, ts } of data.users) { + if (oldData.users.find((user) => user._id === _id)) { + continue; + } + + VideoConf.addUser(call._id, _id, ts); + } + + if (data.endedBy && data.endedBy._id !== oldData.endedBy?._id) { + await VideoConf.setEndedBy(call._id, data.endedBy._id); + } else if (data.endedAt) { + await VideoConf.setEndedAt(call._id, data.endedAt); + } + + if (data.status > oldData.status) { + await VideoConf.setStatus(call._id, data.status); + } + } + + protected async registerProvider(info: IVideoConfProvider, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is registering a video conference provider.`); + videoConfProviders.registerProvider(info.name, info.capabilities || {}, appId); + } + + protected async unRegisterProvider(info: IVideoConfProvider): Promise { + videoConfProviders.unRegisterProvider(info.name); + } +} diff --git a/apps/meteor/app/apps/server/communication/methods.ts b/apps/meteor/app/apps/server/communication/methods.ts index a469408146b2..5533651aecb3 100644 --- a/apps/meteor/app/apps/server/communication/methods.ts +++ b/apps/meteor/app/apps/server/communication/methods.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { SettingValue } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; -import { Settings } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; import { AppServerOrchestrator } from '../orchestrator'; diff --git a/apps/meteor/app/apps/server/communication/rest.js b/apps/meteor/app/apps/server/communication/rest.js index 0b7122e37521..632a57ea24f7 100644 --- a/apps/meteor/app/apps/server/communication/rest.js +++ b/apps/meteor/app/apps/server/communication/rest.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { HTTP } from 'meteor/http'; +import { Settings } from '@rocket.chat/models'; import { API } from '../../../api/server'; import { getUploadFormData } from '../../../api/server/lib/getUploadFormData'; @@ -7,7 +8,6 @@ import { getWorkspaceAccessToken, getUserCloudAccessToken } from '../../../cloud import { settings } from '../../../settings/server'; import { Info } from '../../../utils'; import { Users } from '../../../models/server'; -import { Settings } from '../../../models/server/raw'; import { Apps } from '../orchestrator'; import { formatAppInstanceForRest } from '../../lib/misc/formatAppInstanceForRest'; import { actionButtonsHandler } from './endpoints/actionButtonsHandler'; @@ -528,6 +528,38 @@ export class AppsRestApi { }, ); + this.api.addRoute( + ':id/versions', + { authRequired: true, permissionsRequired: ['manage-apps'] }, + { + get() { + const baseUrl = orchestrator.getMarketplaceUrl(); + + const headers = {}; // DO NOT ATTACH THE FRAMEWORK/ENGINE VERSION HERE. + const token = getWorkspaceAccessToken(); + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + let result; + try { + result = HTTP.get(`${baseUrl}/v1/apps/${this.urlParams.id}`, { + headers, + }); + } catch (e) { + return handleError('Unable to access Marketplace. Does the server has access to the internet?', e); + } + + if (!result || result.statusCode !== 200) { + orchestrator.getRocketChatLogger().error('Error getting the App versions from the Marketplace:', result.data); + return API.v1.failure(); + } + + return API.v1.success({ apps: result.data }); + }, + }, + ); + this.api.addRoute( ':id/sync', { authRequired: true, permissionsRequired: ['manage-apps'] }, diff --git a/apps/meteor/app/apps/server/communication/uikit.ts b/apps/meteor/app/apps/server/communication/uikit.ts index 266cc3f841a3..a9a8ddc4673b 100644 --- a/apps/meteor/app/apps/server/communication/uikit.ts +++ b/apps/meteor/app/apps/server/communication/uikit.ts @@ -6,10 +6,10 @@ import { WebApp } from 'meteor/webapp'; import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata'; -import { Users } from '../../../models/server'; import { settings } from '../../../settings/server'; import { Apps, AppServerOrchestrator } from '../orchestrator'; import { UiKitCoreApp } from '../../../../server/sdk'; +import { authenticationMiddleware } from '../../../api/server/middlewares/authentication'; const apiServer = express(); @@ -51,22 +51,20 @@ Meteor.startup(() => { settings.get('API_Enable_Rate_Limiter') !== true || (process.env.NODE_ENV === 'development' && settings.get('API_Enable_Rate_Limiter_Dev') !== true), }); + router.use(apiLimiter); }); -router.use((req, res, next) => { - const { 'x-user-id': userId, 'x-auth-token': authToken, 'x-visitor-token': visitorToken } = req.headers; +router.use(authenticationMiddleware({ rejectUnauthorized: false })); - if (userId && authToken) { - req.body.user = Users.findOneByIdAndLoginToken(userId, authToken); - req.body.userId = req.body.user._id; - } +router.use((req: Request, res, next) => { + const { 'x-visitor-token': visitorToken } = req.headers; if (visitorToken) { req.body.visitor = Apps.getConverters()?.get('visitors').convertByToken(visitorToken); } - if (!req.body.user && !req.body.visitor) { + if (!req.user && !req.body.visitor) { return unauthorized(res); } @@ -95,7 +93,9 @@ const getPayloadForType = (type: UIKitIncomingInteractionType, req: Request): {} if (type === UIKitIncomingInteractionType.BLOCK) { const { type, actionId, triggerId, mid, rid, payload, container } = req.body; - const { visitor, user } = req.body; + const { visitor } = req.body; + const { user } = req; + const room = rid; // orch.getConverters().get('rooms').convertById(rid); const message = mid; @@ -119,7 +119,7 @@ const getPayloadForType = (type: UIKitIncomingInteractionType, req: Request): {} payload: { view, isCleared }, } = req.body; - const { user } = req.body; + const { user } = req; return { type, @@ -135,7 +135,7 @@ const getPayloadForType = (type: UIKitIncomingInteractionType, req: Request): {} if (type === UIKitIncomingInteractionType.VIEW_SUBMIT) { const { type, actionId, triggerId, payload } = req.body; - const { user } = req.body; + const { user } = req; return { type, @@ -188,7 +188,7 @@ const appsRoutes = const { visitor } = req.body; const room = orch.getConverters()?.get('rooms').convertById(rid); - const user = orch.getConverters()?.get('users').convertToApp(req.body.user); + const user = orch.getConverters()?.get('users').convertToApp(req.user); const message = mid && orch.getConverters()?.get('messages').convertById(mid); const action = { @@ -223,7 +223,7 @@ const appsRoutes = payload: { view, isCleared }, } = req.body; - const user = orch.getConverters()?.get('users').convertToApp(req.body.user); + const user = orch.getConverters()?.get('users').convertToApp(req.user); const action = { type, @@ -237,9 +237,9 @@ const appsRoutes = }; try { - Promise.await(orch.triggerEvent('IUIKitInteractionHandler', action)); + const result = Promise.await(orch.triggerEvent('IUIKitInteractionHandler', action)); - res.sendStatus(200); + res.send(result); } catch (e) { res.status(500).send(e); // e.message } @@ -249,7 +249,7 @@ const appsRoutes = case UIKitIncomingInteractionType.VIEW_SUBMIT: { const { type, actionId, triggerId, payload } = req.body; - const user = orch.getConverters()?.get('users').convertToApp(req.body.user); + const user = orch.getConverters()?.get('users').convertToApp(req.user); const action = { type, @@ -281,7 +281,7 @@ const appsRoutes = } = req.body; const room = orch.getConverters()?.get('rooms').convertById(rid); - const user = orch.getConverters()?.get('users').convertToApp(req.body.user); + const user = orch.getConverters()?.get('users').convertToApp(req.user); const message = mid && orch.getConverters()?.get('messages').convertById(mid); const action = { diff --git a/apps/meteor/app/apps/server/converters/index.js b/apps/meteor/app/apps/server/converters/index.js index edb1bcf57cb1..d5fe67636dc0 100644 --- a/apps/meteor/app/apps/server/converters/index.js +++ b/apps/meteor/app/apps/server/converters/index.js @@ -2,5 +2,6 @@ import { AppMessagesConverter } from './messages'; import { AppRoomsConverter } from './rooms'; import { AppSettingsConverter } from './settings'; import { AppUsersConverter } from './users'; +import { AppVideoConferencesConverter } from './videoConferences'; -export { AppMessagesConverter, AppRoomsConverter, AppSettingsConverter, AppUsersConverter }; +export { AppMessagesConverter, AppRoomsConverter, AppSettingsConverter, AppUsersConverter, AppVideoConferencesConverter }; diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 35d6daa4b463..25931452edd0 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,6 +1,6 @@ import { Random } from 'meteor/random'; -import { Messages, Rooms, Users } from '../../../models'; +import { Messages, Rooms, Users } from '../../../models/server'; import { transformMappedData } from '../../lib/misc/transformMappedData'; export class AppMessagesConverter { diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 12857a94e117..4a9f6225af15 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -1,6 +1,7 @@ import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Rooms, Users, LivechatVisitors, LivechatDepartment } from '../../../models'; +import { Rooms, Users, LivechatDepartment } from '../../../models/server'; import { transformMappedData } from '../../lib/misc/transformMappedData'; export class AppRoomsConverter { @@ -36,7 +37,7 @@ export class AppRoomsConverter { let v; if (room.visitor) { - const visitor = LivechatVisitors.findOneById(room.visitor.id); + const visitor = Promise.await(LivechatVisitors.findOneById(room.visitor.id)); v = { _id: visitor._id, username: visitor.username, diff --git a/apps/meteor/app/apps/server/converters/settings.js b/apps/meteor/app/apps/server/converters/settings.js index bc5949bc7ccd..da3e075deb67 100644 --- a/apps/meteor/app/apps/server/converters/settings.js +++ b/apps/meteor/app/apps/server/converters/settings.js @@ -1,6 +1,5 @@ import { SettingType } from '@rocket.chat/apps-engine/definition/settings'; - -import { Settings } from '../../../models/server/raw'; +import { Settings } from '@rocket.chat/models'; export class AppSettingsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/uploads.js b/apps/meteor/app/apps/server/converters/uploads.js index efbda7ae5fd1..d386e52fdcac 100644 --- a/apps/meteor/app/apps/server/converters/uploads.js +++ b/apps/meteor/app/apps/server/converters/uploads.js @@ -1,5 +1,6 @@ +import { Uploads } from '@rocket.chat/models'; + import { transformMappedData } from '../../lib/misc/transformMappedData'; -import { Uploads } from '../../../models/server/raw'; export class AppUploadsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/users.js b/apps/meteor/app/apps/server/converters/users.js index e8891b9dd720..8c84f598934e 100644 --- a/apps/meteor/app/apps/server/converters/users.js +++ b/apps/meteor/app/apps/server/converters/users.js @@ -1,6 +1,6 @@ import { UserStatusConnection, UserType } from '@rocket.chat/apps-engine/definition/users'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; export class AppUsersConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/videoConferences.ts b/apps/meteor/app/apps/server/converters/videoConferences.ts new file mode 100644 index 000000000000..dd4e2c113b6f --- /dev/null +++ b/apps/meteor/app/apps/server/converters/videoConferences.ts @@ -0,0 +1,36 @@ +import type { VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; +import type { IVideoConference } from '@rocket.chat/core-typings'; + +import { VideoConf } from '../../../../server/sdk'; +import type { AppServerOrchestrator } from '../orchestrator'; + +export class AppVideoConferencesConverter { + // @ts-ignore + private orch: AppServerOrchestrator; + + constructor(orch: AppServerOrchestrator) { + this.orch = orch; + } + + async convertById(callId: string): Promise { + const call = await VideoConf.getUnfiltered(callId); + + return this.convertVideoConference(call); + } + + convertVideoConference(call: IVideoConference | null): VideoConference | undefined { + if (!call) { + return; + } + + return { + ...call, + } as VideoConference; + } + + convertAppVideoConference(call: VideoConference): IVideoConference { + return { + ...call, + } as IVideoConference; + } +} diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index 40c29e1c59a8..361aa3758c6a 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,19 +1,21 @@ -import LivechatVisitors from '../../../models/server/models/LivechatVisitors'; +import { LivechatVisitors } from '@rocket.chat/models'; + import { transformMappedData } from '../../lib/misc/transformMappedData'; +// TODO: check if functions from this converter can be async export class AppVisitorsConverter { constructor(orch) { this.orch = orch; } convertById(id) { - const visitor = LivechatVisitors.findOneById(id); + const visitor = Promise.await(LivechatVisitors.findOneById(id)); return this.convertVisitor(visitor); } convertByToken(token) { - const visitor = LivechatVisitors.getVisitorByToken(token); + const visitor = Promise.await(LivechatVisitors.getVisitorByToken(token)); return this.convertVisitor(visitor); } diff --git a/apps/meteor/app/apps/server/cron.js b/apps/meteor/app/apps/server/cron.js index d70ba36d1e70..86a0e74b937e 100644 --- a/apps/meteor/app/apps/server/cron.js +++ b/apps/meteor/app/apps/server/cron.js @@ -3,12 +3,12 @@ import { HTTP } from 'meteor/http'; import { SyncedCron } from 'meteor/littledata:synced-cron'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import { Settings } from '@rocket.chat/models'; import { Apps } from './orchestrator'; import { getWorkspaceAccessToken } from '../../cloud/server'; import { Users } from '../../models/server'; import { sendMessagesToAdmins } from '../../../server/lib/sendMessagesToAdmins'; -import { Settings } from '../../models/server/raw'; const notifyAdminsAboutInvalidApps = Meteor.bindEnvironment(function _notifyAdminsAboutInvalidApps(apps) { if (!apps) { diff --git a/apps/meteor/app/apps/server/orchestrator.js b/apps/meteor/app/apps/server/orchestrator.js index 85a46fdc4a16..9d3bdf7a35a9 100644 --- a/apps/meteor/app/apps/server/orchestrator.js +++ b/apps/meteor/app/apps/server/orchestrator.js @@ -8,7 +8,13 @@ import { AppsLogsModel, AppsModel, AppsPersistenceModel } from '../../models/ser import { settings, settingsRegistry } from '../../settings/server'; import { RealAppBridges } from './bridges'; import { AppMethods, AppServerNotifier, AppsRestApi, AppUIKitInteractionApi } from './communication'; -import { AppMessagesConverter, AppRoomsConverter, AppSettingsConverter, AppUsersConverter } from './converters'; +import { + AppMessagesConverter, + AppRoomsConverter, + AppSettingsConverter, + AppUsersConverter, + AppVideoConferencesConverter, +} from './converters'; import { AppDepartmentsConverter } from './converters/departments'; import { AppUploadsConverter } from './converters/uploads'; import { AppVisitorsConverter } from './converters/visitors'; @@ -54,6 +60,7 @@ export class AppServerOrchestrator { this._converters.set('visitors', new AppVisitorsConverter(this)); this._converters.set('departments', new AppDepartmentsConverter(this)); this._converters.set('uploads', new AppUploadsConverter(this)); + this._converters.set('videoConferences', new AppVideoConferencesConverter(this)); this._bridges = new RealAppBridges(this); diff --git a/apps/meteor/app/assets/server/assets.ts b/apps/meteor/app/assets/server/assets.ts index 84ed75f8480e..1262f85041df 100644 --- a/apps/meteor/app/assets/server/assets.ts +++ b/apps/meteor/app/assets/server/assets.ts @@ -40,7 +40,6 @@ const assets: IRocketChatAssets = { extensions: ['svg', 'png', 'jpg', 'jpeg'], }, }, - // eslint-disable-next-line @typescript-eslint/camelcase favicon_ico: { label: 'favicon (ico)', defaultUrl: 'favicon.ico', @@ -57,7 +56,6 @@ const assets: IRocketChatAssets = { extensions: ['svg'], }, }, - // eslint-disable-next-line @typescript-eslint/camelcase favicon_16: { label: 'favicon 16x16 (png)', defaultUrl: 'images/logo/favicon-16x16.png', @@ -68,7 +66,6 @@ const assets: IRocketChatAssets = { height: 16, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase favicon_32: { label: 'favicon 32x32 (png)', defaultUrl: 'images/logo/favicon-32x32.png', @@ -79,7 +76,6 @@ const assets: IRocketChatAssets = { height: 32, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase favicon_192: { label: 'android-chrome 192x192 (png)', defaultUrl: 'images/logo/android-chrome-192x192.png', @@ -90,7 +86,6 @@ const assets: IRocketChatAssets = { height: 192, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase favicon_512: { label: 'android-chrome 512x512 (png)', defaultUrl: 'images/logo/android-chrome-512x512.png', @@ -101,7 +96,6 @@ const assets: IRocketChatAssets = { height: 512, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase touchicon_180: { label: 'apple-touch-icon 180x180 (png)', defaultUrl: 'images/logo/apple-touch-icon.png', @@ -112,7 +106,6 @@ const assets: IRocketChatAssets = { height: 180, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase touchicon_180_pre: { label: 'apple-touch-icon-precomposed 180x180 (png)', defaultUrl: 'images/logo/apple-touch-icon-precomposed.png', @@ -123,7 +116,6 @@ const assets: IRocketChatAssets = { height: 180, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase tile_70: { label: 'mstile 70x70 (png)', defaultUrl: 'images/logo/mstile-70x70.png', @@ -134,7 +126,6 @@ const assets: IRocketChatAssets = { height: 70, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase tile_144: { label: 'mstile 144x144 (png)', defaultUrl: 'images/logo/mstile-144x144.png', @@ -145,7 +136,6 @@ const assets: IRocketChatAssets = { height: 144, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase tile_150: { label: 'mstile 150x150 (png)', defaultUrl: 'images/logo/mstile-150x150.png', @@ -156,7 +146,6 @@ const assets: IRocketChatAssets = { height: 150, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase tile_310_square: { label: 'mstile 310x310 (png)', defaultUrl: 'images/logo/mstile-310x310.png', @@ -167,7 +156,6 @@ const assets: IRocketChatAssets = { height: 310, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase tile_310_wide: { label: 'mstile 310x150 (png)', defaultUrl: 'images/logo/mstile-310x150.png', @@ -178,7 +166,6 @@ const assets: IRocketChatAssets = { height: 150, }, }, - // eslint-disable-next-line @typescript-eslint/camelcase safari_pinned: { label: 'safari pinned tab (svg)', defaultUrl: 'images/logo/safari-pinned-tab.svg', @@ -495,7 +482,7 @@ const listener = Meteor.bindEnvironment((req: IncomingMessage, res: ServerRespon const asset = getAssetByKey(params.asset); const file = asset?.cache; - const format = req.url.split('.').pop() || ''; + const format = req.url.replace(/.*\.([a-z]+)(?:$|\?.*)/i, '$1'); if (asset && Array.isArray(asset.constraints.extensions) && !asset.constraints.extensions.includes(format)) { res.writeHead(403); diff --git a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts index f9a5ff632e81..f49abd3ac68d 100644 --- a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts +++ b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts @@ -1,14 +1,14 @@ -import moment from 'moment'; import type { IServerEvent } from '@rocket.chat/core-typings'; import { ServerEventType } from '@rocket.chat/core-typings'; +import { Rooms, ServerEvents, Sessions, Users } from '@rocket.chat/models'; +import moment from 'moment'; -import { ILoginAttempt } from '../ILoginAttempt'; -import { ServerEvents, Users, Rooms, Sessions } from '../../../models/server/raw'; -import { settings } from '../../../settings/server'; import { addMinutesToADate } from '../../../../lib/utils/addMinutesToADate'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; import { sendMessage } from '../../../lib/server/functions'; import { Logger } from '../../../logger/server'; +import { settings } from '../../../settings/server'; +import { ILoginAttempt } from '../ILoginAttempt'; const logger = new Logger('LoginProtection'); @@ -94,7 +94,8 @@ export const isValidAttemptByUser = async (login: ILoginAttempt): Promise, ): Promise { @@ -51,6 +51,10 @@ export async function canSendMessageAsync( extraData: Record, ): Promise { const room = await Rooms.findOneById(rid); + if (!room) { + throw new Error('error-invalid-room'); + } + await validateRoomMessagePermissionsAsync(room, { uid, username, type }, extraData); return room; } diff --git a/apps/meteor/app/authorization/server/functions/getRoles.ts b/apps/meteor/app/authorization/server/functions/getRoles.ts index 5ace1e614527..657b546aaaae 100644 --- a/apps/meteor/app/authorization/server/functions/getRoles.ts +++ b/apps/meteor/app/authorization/server/functions/getRoles.ts @@ -1,5 +1,4 @@ import type { IRole } from '@rocket.chat/core-typings'; - -import { Roles } from '../../../models/server/raw'; +import { Roles } from '@rocket.chat/models'; export const getRoles = (): IRole[] => Promise.await(Roles.find().toArray()); diff --git a/apps/meteor/app/authorization/server/functions/getUsersInRole.ts b/apps/meteor/app/authorization/server/functions/getUsersInRole.ts index dee7e46ab2fa..b73bd133ba0f 100644 --- a/apps/meteor/app/authorization/server/functions/getUsersInRole.ts +++ b/apps/meteor/app/authorization/server/functions/getUsersInRole.ts @@ -1,26 +1,51 @@ -import { Cursor, FindOneOptions, WithoutProjection } from 'mongodb'; +import { FindCursor, FindOptions } from 'mongodb'; import type { IRole, IUser } from '@rocket.chat/core-typings'; +import { Roles, Subscriptions, Users } from '@rocket.chat/models'; +import { FindPaginated } from '@rocket.chat/model-typings'; +import { compact } from 'lodash'; -import { Roles } from '../../../models/server/raw'; +export function getUsersInRole(roleId: IRole['_id'], scope?: string): Promise>; -export function getUsersInRole(roleId: IRole['_id'], scope?: string): Promise>; +export function getUsersInRole(roleId: IRole['_id'], scope: string | undefined, options: FindOptions): Promise>; -export function getUsersInRole( +export function getUsersInRole

( roleId: IRole['_id'], scope: string | undefined, - options: WithoutProjection>, -): Promise>; + options: FindOptions

, +): Promise>; export function getUsersInRole

( roleId: IRole['_id'], scope: string | undefined, - options: FindOneOptions

, -): Promise>; + options?: any | undefined, +): Promise> { + // TODO move the code from Roles.findUsersInRole to here and change all places to use this function + return Roles.findUsersInRole(roleId, scope, options); +} -export function getUsersInRole

( +export async function getUsersInRolePaginated( roleId: IRole['_id'], scope: string | undefined, options?: any | undefined, -): Promise> { - return Roles.findUsersInRole(roleId, scope, options); +): Promise>> { + if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { + throw new Error('Roles.findUsersInRole method received a role scope instead of a scope value.'); + } + + const role = await Roles.findOneById>(roleId, { projection: { scope: 1 } }); + if (!role) { + throw new Error('role not found'); + } + + switch (role.scope) { + case 'Subscriptions': + const subscriptions = await Subscriptions.findByRolesAndRoomId({ roles: role._id, rid: scope }, { projection: { 'u._id': 1 } }) + .map((subscription) => subscription.u?._id) + .toArray(); + + return Users.findPaginated({ _id: { $in: compact(subscriptions) } }, options || {}); + case 'Users': + default: + return Users.findPaginatedUsersInRoles([role._id], options); + } } diff --git a/apps/meteor/app/authorization/server/functions/hasRole.ts b/apps/meteor/app/authorization/server/functions/hasRole.ts index 9057072a2787..040d28d8a905 100644 --- a/apps/meteor/app/authorization/server/functions/hasRole.ts +++ b/apps/meteor/app/authorization/server/functions/hasRole.ts @@ -1,6 +1,5 @@ import type { IRole, IUser, IRoom, ISubscription } from '@rocket.chat/core-typings'; - -import { Roles } from '../../../models/server/raw'; +import { Roles } from '@rocket.chat/models'; export const hasAnyRoleAsync = async ( userId: IUser['_id'], diff --git a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts index a058d02f3978..837020591c01 100644 --- a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts +++ b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts @@ -1,9 +1,9 @@ /* eslint no-multi-spaces: 0 */ import type { IPermission, ISetting } from '@rocket.chat/core-typings'; +import { Permissions, Settings } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { getSettingPermissionId, CONSTANTS } from '../../lib'; -import { Permissions, Settings } from '../../../models/server/raw'; import { createOrUpdateProtectedRoleAsync } from '../../../../server/lib/roles/createOrUpdateProtectedRole'; export const upsertPermissions = async (): Promise => { @@ -224,6 +224,7 @@ export const upsertPermissions = async (): Promise => { { _id: 'remove-slackbridge-links', roles: ['admin'] }, { _id: 'view-import-operations', roles: ['admin'] }, { _id: 'clear-oembed-cache', roles: ['admin'] }, + { _id: 'videoconf-ring-users', roles: ['admin', 'owner', 'moderator', 'user'] }, ]; for await (const permission of permissions) { @@ -293,17 +294,17 @@ export const upsertPermissions = async (): Promise => { _id: permissionId, ...permission, }, - { fields: { _id: 1 } }, + { projection: { _id: 1 } }, ); if (!existent) { try { - await Permissions.update({ _id: permissionId }, { $set: permission }, { upsert: true }); + await Permissions.updateOne({ _id: permissionId }, { $set: permission }, { upsert: true }); } catch (e) { if (!(e as Error).message.includes('E11000')) { // E11000 refers to a MongoDB error that can occur when using unique indexes for upserts // https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes - await Permissions.update({ _id: permissionId }, { $set: permission }, { upsert: true }); + await Permissions.updateOne({ _id: permissionId }, { $set: permission }, { upsert: true }); } } } diff --git a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts index 946a7813b226..8041cb8b7301 100644 --- a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { Permissions } from '@rocket.chat/models'; import { hasPermission } from '../functions/hasPermission'; import { CONSTANTS, AuthorizationUtils } from '../../lib'; -import { Permissions } from '../../../models/server/raw'; Meteor.methods({ async 'authorization:addPermissionToRole'(permissionId, role) { diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts index 072f5162fcbd..c33f950e0dbc 100644 --- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; import { Users } from '../../../models/server'; import { settings } from '../../../settings/server'; import { hasPermission } from '../functions/hasPermission'; import { api } from '../../../../server/sdk/api'; -import { Roles } from '../../../models/server/raw'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; Meteor.methods({ diff --git a/apps/meteor/app/authorization/server/methods/deleteRole.ts b/apps/meteor/app/authorization/server/methods/deleteRole.ts index 4d9fb5238fc9..71858b228705 100644 --- a/apps/meteor/app/authorization/server/methods/deleteRole.ts +++ b/apps/meteor/app/authorization/server/methods/deleteRole.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import type { IRole } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; -import { Roles } from '../../../models/server/raw'; import { hasPermission } from '../functions/hasPermission'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts index f690c29d5ea2..0d2ecf59333f 100644 --- a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts +++ b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { Permissions } from '@rocket.chat/models'; import { hasPermission } from '../functions/hasPermission'; import { CONSTANTS } from '../../lib'; -import { Permissions } from '../../../models/server/raw'; Meteor.methods({ async 'authorization:removeRoleFromPermission'(permissionId, role) { diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts index 996c17f8b910..3eeda9c2c84e 100644 --- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts +++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import type { IRole, IUser } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; import { Users } from '../../../models/server'; import { settings } from '../../../settings/server'; import { hasPermission } from '../functions/hasPermission'; import { api } from '../../../../server/sdk/api'; -import { Roles } from '../../../models/server/raw'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; Meteor.methods({ diff --git a/apps/meteor/app/authorization/server/methods/saveRole.ts b/apps/meteor/app/authorization/server/methods/saveRole.ts index 5a4b485a5e15..9eb19298f7f7 100644 --- a/apps/meteor/app/authorization/server/methods/saveRole.ts +++ b/apps/meteor/app/authorization/server/methods/saveRole.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { isRoleCreateProps } from '@rocket.chat/rest-typings'; +import { Roles } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { hasPermission } from '../functions/hasPermission'; -import { Roles } from '../../../models/server/raw'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { updateRoleAsync } from '../../../../server/lib/roles/updateRole'; import { insertRoleAsync } from '../../../../server/lib/roles/insertRole'; diff --git a/apps/meteor/app/authorization/server/streamer/permissions/index.ts b/apps/meteor/app/authorization/server/streamer/permissions/index.ts index 881f4f44e7b5..866bec5fb407 100644 --- a/apps/meteor/app/authorization/server/streamer/permissions/index.ts +++ b/apps/meteor/app/authorization/server/streamer/permissions/index.ts @@ -1,7 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { check, Match } from 'meteor/check'; - -import { Permissions } from '../../../../models/server/raw'; +import { Permissions } from '@rocket.chat/models'; Meteor.methods({ async 'permissions/get'(updatedAt: Date) { diff --git a/apps/meteor/app/autotranslate/client/lib/actionButton.ts b/apps/meteor/app/autotranslate/client/lib/actionButton.ts index 8144d46dae1e..0d742d68f9ab 100644 --- a/apps/meteor/app/autotranslate/client/lib/actionButton.ts +++ b/apps/meteor/app/autotranslate/client/lib/actionButton.ts @@ -32,7 +32,7 @@ Meteor.startup(() => { Messages.update({ _id: message._id }, { [action]: { autoTranslateShowInverse: true } }); }, condition({ message, user }) { - return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && !message.translations.original); + return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && message.autoTranslateShowInverse); }, order: 90, }); @@ -54,7 +54,7 @@ Meteor.startup(() => { Messages.update({ _id: message._id }, { [action]: { autoTranslateShowInverse: true } }); }, condition({ message, user }) { - return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && message.translations.original); + return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && !message.autoTranslateShowInverse); }, order: 90, }); diff --git a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts index d7f8338de98f..20ead8d84dea 100644 --- a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts +++ b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import _ from 'underscore'; import mem from 'mem'; import { IRoom, ISubscription, ISupportedLanguage, ITranslatedMessage, IUser, MessageAttachmentDefault } from '@rocket.chat/core-typings'; @@ -24,7 +23,7 @@ Meteor.startup(() => { export const AutoTranslate = { initialized: false, - providersMetadata: {}, + providersMetadata: {} as { [providerNamer: string]: { name: string; displayName: string } }, messageIdsToWait: {} as { [messageId: string]: string }, supportedLanguages: [] as ISupportedLanguage[], @@ -37,22 +36,38 @@ export const AutoTranslate = { } const language = (subscription?.autoTranslateLanguage || userLanguage || window.defaultUserLanguage?.()) as string; if (language.indexOf('-') !== -1) { - if (!_.findWhere(this.supportedLanguages, { language })) { - return language.substr(0, 2); + if (!this.supportedLanguages.some((supportedLanguage) => supportedLanguage.language === language)) { + return language.slice(0, 2); } } return language; }, - translateAttachments(attachments: MessageAttachmentDefault[], language: string): MessageAttachmentDefault[] { + translateAttachments( + attachments: MessageAttachmentDefault[], + language: string, + autoTranslateShowInverse: boolean, + ): MessageAttachmentDefault[] { for (const attachment of attachments) { if (attachment.author_name !== username) { if (attachment.text && attachment.translations && attachment.translations[language]) { - attachment.text = attachment.translations[language]; + attachment.translations.original = attachment.text; + + if (autoTranslateShowInverse) { + attachment.text = attachment.translations.original; + } else { + attachment.text = attachment.translations[language]; + } } if (attachment.description && attachment.translations && attachment.translations[language]) { - attachment.description = attachment.translations[language]; + attachment.translations.original = attachment.description; + + if (autoTranslateShowInverse) { + attachment.description = attachment.translations.original; + } else { + attachment.description = attachment.translations[language]; + } } // @ts-expect-error - not sure what to do with this @@ -107,17 +122,33 @@ export const createAutoTranslateMessageRenderer = (): ((message: ITranslatedMess message.translations = {}; } if (!!subscription?.autoTranslate !== !!message.autoTranslateShowInverse) { + const hasAttachmentsTranslate = + message.attachments?.some( + (attachment) => + 'translations' in attachment && + typeof attachment.translations === 'object' && + autoTranslateLanguage in attachment.translations, + ) ?? false; + message.translations.original = message.html; - if (message.translations[autoTranslateLanguage]) { + if (message.translations[autoTranslateLanguage] && !hasAttachmentsTranslate) { message.html = message.translations[autoTranslateLanguage]; } if (message.attachments && message.attachments.length > 0) { - message.attachments = AutoTranslate.translateAttachments(message.attachments, autoTranslateLanguage); + message.attachments = AutoTranslate.translateAttachments( + message.attachments, + autoTranslateLanguage, + !!message.autoTranslateShowInverse, + ); } } } else if (message.attachments && message.attachments.length > 0) { - message.attachments = AutoTranslate.translateAttachments(message.attachments, autoTranslateLanguage); + message.attachments = AutoTranslate.translateAttachments( + message.attachments, + autoTranslateLanguage, + !!message.autoTranslateShowInverse, + ); } return message; }; diff --git a/apps/meteor/app/autotranslate/server/autotranslate.ts b/apps/meteor/app/autotranslate/server/autotranslate.ts index a5f550d777d6..d50cb15a1457 100644 --- a/apps/meteor/app/autotranslate/server/autotranslate.ts +++ b/apps/meteor/app/autotranslate/server/autotranslate.ts @@ -308,6 +308,7 @@ export abstract class AutoTranslate { const translations = this._translateAttachmentDescriptions(attachment, targetLanguages); if (!_.isEmpty(translations)) { Messages.addAttachmentTranslations(message._id, index, translations); + Messages.addTranslations(message._id, translations, TranslationProviderRegistry[Provider]); } } } diff --git a/apps/meteor/app/autotranslate/server/deeplTranslate.ts b/apps/meteor/app/autotranslate/server/deeplTranslate.ts index 4da7b27f8f37..442d004d469e 100644 --- a/apps/meteor/app/autotranslate/server/deeplTranslate.ts +++ b/apps/meteor/app/autotranslate/server/deeplTranslate.ts @@ -209,9 +209,7 @@ class DeeplAutoTranslate extends AutoTranslate { try { const result = HTTP.get(this.apiEndPointUrl, { params: { - // eslint-disable-next-line @typescript-eslint/camelcase auth_key: this.apiKey, - // eslint-disable-next-line @typescript-eslint/camelcase target_lang: language, }, query, @@ -258,9 +256,7 @@ class DeeplAutoTranslate extends AutoTranslate { try { const result = HTTP.get(this.apiEndPointUrl, { params: { - // eslint-disable-next-line @typescript-eslint/camelcase auth_key: this.apiKey, - // eslint-disable-next-line @typescript-eslint/camelcase target_lang: language, }, query, diff --git a/apps/meteor/app/autotranslate/server/googleTranslate.ts b/apps/meteor/app/autotranslate/server/googleTranslate.ts index 13a861532e38..e519f56fdbb8 100644 --- a/apps/meteor/app/autotranslate/server/googleTranslate.ts +++ b/apps/meteor/app/autotranslate/server/googleTranslate.ts @@ -94,7 +94,7 @@ class GoogleAutoTranslate extends AutoTranslate { result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', { params, }); - } catch (e) { + } catch (e: any) { // Fallback: Get the English names of the target languages if ( e.response && diff --git a/apps/meteor/app/autotranslate/server/permissions.ts b/apps/meteor/app/autotranslate/server/permissions.ts index cf712cc6d552..f75edf705c24 100644 --- a/apps/meteor/app/autotranslate/server/permissions.ts +++ b/apps/meteor/app/autotranslate/server/permissions.ts @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; - -import { Permissions } from '../../models/server/raw'; +import { Permissions } from '@rocket.chat/models'; Meteor.startup(async () => { if (!(await Permissions.findOne({ _id: 'auto-translate' }))) { diff --git a/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js b/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js deleted file mode 100644 index 8cb3f4d447c4..000000000000 --- a/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js +++ /dev/null @@ -1,188 +0,0 @@ -/* eslint-disable */ -import crypto from 'crypto'; -import { SystemLogger } from '../../../server/lib/logger/system'; - -var BigBlueButtonApi, filterCustomParameters, include, noChecksumMethods, - __indexOf = [].indexOf || function (item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; - -BigBlueButtonApi = (function () { - function BigBlueButtonApi(url, salt, debug, opts) { - var _base; - if (opts == null) { - opts = {}; - } - this.url = url; - this.salt = salt; - this.opts = opts; - if ((_base = this.opts).shaType == null) { - _base.shaType = 'sha1'; - } - } - - BigBlueButtonApi.prototype.availableApiCalls = function () { - return ['/', 'create', 'join', 'isMeetingRunning', 'getMeetingInfo', 'end', 'getMeetings', 'getDefaultConfigXML', 'setConfigXML', 'enter', 'configXML', 'signOut', 'getRecordings', 'publishRecordings', 'deleteRecordings', 'updateRecordings', 'hooks/create']; - }; - - BigBlueButtonApi.prototype.urlParamsFor = function (param) { - switch (param) { - case "create": - return [["meetingID", true], ["name", true], ["attendeePW", false], ["moderatorPW", false], ["welcome", false], ["dialNumber", false], ["voiceBridge", false], ["webVoice", false], ["logoutURL", false], ["maxParticipants", false], ["record", false], ["duration", false], ["moderatorOnlyMessage", false], ["autoStartRecording", false], ["allowStartStopRecording", false], [/meta_\w+/, false]]; - case "join": - return [["fullName", true], ["meetingID", true], ["password", true], ["createTime", false], ["userID", false], ["webVoiceConf", false], ["configToken", false], ["avatarURL", false], ["redirect", false], ["clientURL", false]]; - case "isMeetingRunning": - return [["meetingID", true]]; - case "end": - return [["meetingID", true], ["password", true]]; - case "getMeetingInfo": - return [["meetingID", true], ["password", true]]; - case "getRecordings": - return [["meetingID", false], ["recordID", false], ["state", false], [/meta_\w+/, false]]; - case "publishRecordings": - return [["recordID", true], ["publish", true]]; - case "deleteRecordings": - return [["recordID", true]]; - case "updateRecordings": - return [["recordID", true], [/meta_\w+/, false]]; - case "hooks/create": - return [["callbackURL", false], ["meetingID", false]]; - } - }; - - BigBlueButtonApi.prototype.filterParams = function (params, method) { - var filters, r; - filters = this.urlParamsFor(method); - if ((filters == null) || filters.length === 0) { - ({}); - } else { - r = include(params, function (key, value) { - var filter, _i, _len; - for (_i = 0, _len = filters.length; _i < _len; _i++) { - filter = filters[_i]; - if (filter[0] instanceof RegExp) { - if (key.match(filter[0]) || key.match(/^custom_/)) { - return true; - } - } else { - if (key.match("^" + filter[0] + "$") || key.match(/^custom_/)) { - return true; - } - } - } - return false; - }); - } - return filterCustomParameters(r); - }; - - BigBlueButtonApi.prototype.urlFor = function (method, params, filter) { - var checksum, key, keys, param, paramList, property, query, sep, url, _i, _len; - if (filter == null) { - filter = true; - } - SystemLogger.debug("Generating URL for", method); - if (filter) { - params = this.filterParams(params, method); - } else { - params = filterCustomParameters(params); - } - url = this.url; - paramList = []; - if (params != null) { - keys = []; - for (property in params) { - keys.push(property); - } - keys = keys.sort(); - for (_i = 0, _len = keys.length; _i < _len; _i++) { - key = keys[_i]; - if (key != null) { - param = params[key]; - } - if (param != null) { - paramList.push("" + (this.encodeForUrl(key)) + "=" + (this.encodeForUrl(param))); - } - } - if (paramList.length > 0) { - query = paramList.join("&"); - } - } else { - query = ''; - } - checksum = this.checksum(method, query); - if (paramList.length > 0) { - query = "" + method + "?" + query; - sep = '&'; - } else { - if (method !== '/') { - query = method; - } - sep = '?'; - } - if (__indexOf.call(noChecksumMethods(), method) < 0) { - query = "" + query + sep + "checksum=" + checksum; - } - return "" + url + "/" + query; - }; - - BigBlueButtonApi.prototype.checksum = function (method, query) { - var c, shaObj, str; - query || (query = ""); - SystemLogger.debug("- Calculating the checksum using: '" + method + "', '" + query + "', '" + this.salt + "'"); - str = method + query + this.salt; - if (this.opts.shaType === 'sha256') { - shaObj = crypto.createHash('sha256', "TEXT") - } else { - shaObj = crypto.createHash('sha1', "TEXT") - } - shaObj.update(str); - c = shaObj.digest('hex'); - SystemLogger.debug("- Checksum calculated:", c); - return c; - }; - - BigBlueButtonApi.prototype.encodeForUrl = function (value) { - return encodeURIComponent(value).replace(/%20/g, '+').replace(/[!'()]/g, escape).replace(/\*/g, "%2A"); - }; - - BigBlueButtonApi.prototype.setMobileProtocol = function (url) { - return url.replace(/http[s]?\:\/\//, "bigbluebutton://"); - }; - - return BigBlueButtonApi; - -})(); - -include = function (input, _function) { - var key, value, _match, _obj; - _obj = new Object; - _match = null; - for (key in input) { - value = input[key]; - if (_function.call(input, key, value)) { - _obj[key] = value; - } - } - return _obj; -}; - -export default BigBlueButtonApi; - -filterCustomParameters = function (params) { - var key, v; - for (key in params) { - v = params[key]; - if (key.match(/^custom_/)) { - params[key.replace(/^custom_/, "")] = v; - } - } - for (key in params) { - if (key.match(/^custom_/)) { - delete params[key]; - } - } - return params; -}; - -noChecksumMethods = function () { - return ['setConfigXML', '/', 'enter', 'configXML', 'signOut']; -}; diff --git a/apps/meteor/app/bigbluebutton/server/index.js b/apps/meteor/app/bigbluebutton/server/index.js deleted file mode 100644 index b6be696a20bd..000000000000 --- a/apps/meteor/app/bigbluebutton/server/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './bigbluebutton-api'; diff --git a/apps/meteor/app/cas/server/cas_server.js b/apps/meteor/app/cas/server/cas_server.js index 53d64b1e34a9..cf0a273c4fcc 100644 --- a/apps/meteor/app/cas/server/cas_server.js +++ b/apps/meteor/app/cas/server/cas_server.js @@ -6,12 +6,12 @@ import { WebApp } from 'meteor/webapp'; import { RoutePolicy } from 'meteor/routepolicy'; import _ from 'underscore'; import fiber from 'fibers'; -import CAS from 'cas'; +import { CredentialTokens } from '@rocket.chat/models'; +import { validate } from '@rocket.chat/cas-validate'; import { logger } from './cas_rocketchat'; -import { settings } from '../../settings'; +import { settings } from '../../settings/server'; import { Rooms } from '../../models/server'; -import { CredentialTokens } from '../../models/server/raw'; import { _setRealName } from '../../lib'; import { createRoom } from '../../lib/server/functions/createRoom'; @@ -38,13 +38,12 @@ const casTicket = function (req, token, callback) { const appUrl = Meteor.absoluteUrl().replace(/\/$/, '') + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX; logger.debug(`Using CAS_base_url: ${baseUrl}`); - const cas = new CAS({ - base_url: baseUrl, - version: cas_version, - service: `${appUrl}/_cas/${token}`, - }); - - cas.validate( + validate( + { + base_url: baseUrl, + version: cas_version, + service: `${appUrl}/_cas/${token}`, + }, ticketId, Meteor.bindEnvironment(async function (err, status, username, details) { if (err) { @@ -113,7 +112,7 @@ WebApp.connectHandlers.use(function (req, res, next) { * It is call after Accounts.callLoginMethod() is call from client. * */ -Accounts.registerLoginHandler(function (options) { +Accounts.registerLoginHandler('cas', function (options) { if (!options.cas) { return undefined; } diff --git a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js index d4a6898678d6..8b118c51f01e 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js +++ b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; export const saveReactWhenReadOnly = function (rid, allowReact, user, sendMessage = true) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js index 38a5f56d612a..b3f66bf311a0 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; export const saveRoomAnnouncement = function (rid, roomAnnouncement, user, sendMessage = true) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js index 5691cd69b169..c246e01224ba 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Subscriptions } from '../../../models'; +import { Rooms, Subscriptions } from '../../../models/server'; export const saveRoomCustomFields = function (rid, roomCustomFields) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js index 0804175f8e2a..dd6e0fb3ee1b 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; export const saveRoomDescription = function (rid, roomDescription, user) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts index f0ccf1eb2164..7d6a906ab8f1 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts @@ -1,11 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import type { WriteOpResult } from 'mongodb'; +import type { UpdateResult } from 'mongodb'; import type { IUser } from '@rocket.chat/core-typings'; import { Rooms, Messages } from '../../../models/server'; -export const saveRoomEncrypted = function (rid: string, encrypted: boolean, user: IUser, sendMessage = true): Promise { +export const saveRoomEncrypted = function (rid: string, encrypted: boolean, user: IUser, sendMessage = true): Promise { if (!Match.test(rid, String)) { throw new Meteor.Error('invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomEncrypted', diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomName.js b/apps/meteor/app/channel-settings/server/functions/saveRoomName.js index 05615a865075..941f8e86fcfc 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomName.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomName.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations } from '@rocket.chat/models'; import { Rooms, Messages, Subscriptions } from '../../../models/server'; -import { Integrations } from '../../../models/server/raw'; import { getValidRoomName } from '../../../utils/server'; import { callbacks } from '../../../../lib/callbacks'; import { checkUsernameAvailability } from '../../../lib/server/functions'; diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js index 7b7ed7bd0404..7cdb614e0679 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; export const saveRoomReadOnly = function (rid, readOnly, user, sendMessage = true) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js index 200b2d4eea8d..f3e30630ce5c 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms } from '../../../models'; +import { Rooms } from '../../../models/server'; import { MessageTypesValues } from '../../../lib/lib/MessageTypes'; export const saveRoomSystemMessages = function (rid, systemMessages) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js index 51a5a05035e2..5edf3c215f31 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js @@ -1,7 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; +import { callbacks } from '../../../../lib/callbacks'; export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true) { if (!Match.test(rid, String)) { @@ -14,5 +15,6 @@ export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true) if (update && sendMessage) { Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', rid, roomTopic, user); } + callbacks.run('afterRoomTopicChange', { rid, topic: roomTopic }); return update; }; diff --git a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js index c5c2d2b8fb2b..b9b7cea7f5f7 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js +++ b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; -import { Rooms } from '../../../models'; +import { Rooms } from '../../../models/server'; export const saveStreamingOptions = function (rid, options) { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js index 0027ba085683..89dcd43fb8b8 100644 --- a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js +++ b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js @@ -4,7 +4,7 @@ import { TEAM_TYPE } from '@rocket.chat/core-typings'; import { setRoomAvatar } from '../../../lib/server/functions/setRoomAvatar'; import { hasPermission } from '../../../authorization'; -import { Rooms } from '../../../models'; +import { Rooms } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; import { saveRoomName } from '../functions/saveRoomName'; import { saveRoomTopic } from '../functions/saveRoomTopic'; diff --git a/apps/meteor/app/chatpal-search/client/template/result.js b/apps/meteor/app/chatpal-search/client/template/result.js index b96df425c3b4..17a91ae4f6b1 100644 --- a/apps/meteor/app/chatpal-search/client/template/result.js +++ b/apps/meteor/app/chatpal-search/client/template/result.js @@ -3,7 +3,7 @@ import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { getURL } from '../../../utils'; -import { Subscriptions } from '../../../models'; +import { Subscriptions } from '../../../models/client'; import { getUserAvatarURL as getAvatarUrl } from '../../../utils/lib/getUserAvatarURL'; import { formatTime } from '../../../../client/lib/utils/formatTime'; import { formatDate } from '../../../../client/lib/utils/formatDate'; diff --git a/apps/meteor/app/chatpal-search/server/provider/index.js b/apps/meteor/app/chatpal-search/server/provider/index.js index 77f88dcea5f1..77eaa2725596 100644 --- a/apps/meteor/app/chatpal-search/server/provider/index.js +++ b/apps/meteor/app/chatpal-search/server/provider/index.js @@ -3,7 +3,7 @@ import { HTTP } from 'meteor/http'; import { Random } from 'meteor/random'; import ChatpalLogger from '../utils/logger'; -import { Rooms, Messages } from '../../../models'; +import { Rooms, Messages } from '../../../models/server'; /** * Enables HTTP functions on Chatpal Backend diff --git a/apps/meteor/app/chatpal-search/server/provider/provider.js b/apps/meteor/app/chatpal-search/server/provider/provider.js index 51670a762c44..b8705cf29be3 100644 --- a/apps/meteor/app/chatpal-search/server/provider/provider.js +++ b/apps/meteor/app/chatpal-search/server/provider/provider.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { searchProviderService, SearchProvider } from '../../../search/server'; import ChatpalLogger from '../utils/logger'; -import { Subscriptions, Rooms } from '../../../models'; +import { Subscriptions, Rooms } from '../../../models/server'; import { baseUrl } from '../utils/settings'; import Index from './index'; diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts index 2b57931ec5cb..712795441ece 100644 --- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts @@ -1,8 +1,8 @@ import { SettingValue } from '@rocket.chat/core-typings'; +import { Statistics } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { Users } from '../../../models/server'; -import { Statistics } from '../../../models/server/raw'; import { statistics } from '../../../statistics/server'; import { LICENSE_VERSION } from '../license'; diff --git a/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js b/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js index 39743627152b..e68ff1570498 100644 --- a/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js +++ b/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js @@ -1,5 +1,5 @@ import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; export function checkUserHasCloudLogin(userId) { const { connectToCloud, workspaceRegistered } = retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/connectWorkspace.js b/apps/meteor/app/cloud/server/functions/connectWorkspace.js index 7ca7fcbb52f9..9c2a424c3221 100644 --- a/apps/meteor/app/cloud/server/functions/connectWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/connectWorkspace.js @@ -2,8 +2,8 @@ import { HTTP } from 'meteor/http'; import { getRedirectUri } from './getRedirectUri'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models'; -import { settings } from '../../../settings'; +import { Settings } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { saveRegistrationData } from './saveRegistrationData'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js index c1e2adda876e..c1ac36729aaa 100644 --- a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js @@ -1,5 +1,5 @@ import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models'; +import { Settings } from '../../../models/server'; export function disconnectWorkspace() { const { connectToCloud } = retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js index ec0601204795..e46bfda5cfd9 100644 --- a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js +++ b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js @@ -2,8 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { HTTP } from 'meteor/http'; import { getRedirectUri } from './getRedirectUri'; -import { settings } from '../../../settings'; -import { Users } from '../../../models'; +import { settings } from '../../../settings/server'; +import { Users } from '../../../models/server'; import { userScopes } from '../oauthScopes'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts index 9c4533d2e79d..f83dd650d833 100644 --- a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts +++ b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts @@ -10,7 +10,7 @@ export async function getConfirmationPoll(deviceCode: string): Promise('Register_Server'), + workspaceRegistered: !!settings.get('Cloud_Workspace_Client_Id'), + workspaceId: settings.get('Cloud_Workspace_Id'), + uniqueId: settings.get('uniqueID'), + token: '', + email: settings.get('Organization_Email'), + }; + + if (!info.email) { + const firstUser = Users.getOldest({ emails: 1 }); + info.email = firstUser?.emails?.[0]?.address; + } + + return info; +} diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.js b/apps/meteor/app/cloud/server/functions/saveRegistrationData.js index 1bec537f8952..f9f5e3c05a9d 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.js +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.js @@ -1,4 +1,5 @@ -import { Settings } from '../../../models/server/raw'; +import { Settings } from '@rocket.chat/models'; + import { callbacks } from '../../../../lib/callbacks'; export function saveRegistrationData({ diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js index e512469248a0..9633a3835159 100644 --- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js @@ -3,7 +3,7 @@ import { HTTP } from 'meteor/http'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { syncWorkspace } from './syncWorkspace'; import { settings } from '../../../settings/server'; -import { Settings } from '../../../models'; +import { Settings } from '../../../models/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts index d19ba9884916..75e2e3c9c51c 100644 --- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts +++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts @@ -14,7 +14,7 @@ export async function startRegisterWorkspaceSetupWizard(resend = false, email: s result = HTTP.post(`${cloudUrl}/api/v2/register/workspace/intent?resent=${resend}`, { data: regInfo, }); - } catch (e) { + } catch (e: any) { if (e.response && e.response.data && e.response.data.error) { SystemLogger.error(`Failed to register with Rocket.Chat Cloud. ErrorCode: ${e.response.data.error}`); } else { diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace.js b/apps/meteor/app/cloud/server/functions/syncWorkspace.js index 6424c5ea867e..ad8ec96faa49 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace.js @@ -4,8 +4,8 @@ import { buildWorkspaceRegistrationData } from './buildRegistrationData'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { getWorkspaceLicense } from './getWorkspaceLicense'; -import { Settings } from '../../../models'; -import { settings } from '../../../settings'; +import { Settings } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCreateNpsSurvey'; import { NPS, Banner } from '../../../../server/sdk'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js index f28a54a55153..37895e53e248 100644 --- a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js @@ -1,5 +1,5 @@ import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models'; +import { Settings } from '../../../models/server'; export function unregisterWorkspace() { const { workspaceRegistered } = retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/userLoggedOut.js b/apps/meteor/app/cloud/server/functions/userLoggedOut.js index 6e08b878eb6f..ce1e6c6ac17b 100644 --- a/apps/meteor/app/cloud/server/functions/userLoggedOut.js +++ b/apps/meteor/app/cloud/server/functions/userLoggedOut.js @@ -1,4 +1,4 @@ -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; export function userLoggedOut(userId) { if (!userId) { diff --git a/apps/meteor/app/cloud/server/functions/userLogout.js b/apps/meteor/app/cloud/server/functions/userLogout.js index cada35f809fc..6d2e917f347d 100644 --- a/apps/meteor/app/cloud/server/functions/userLogout.js +++ b/apps/meteor/app/cloud/server/functions/userLogout.js @@ -2,8 +2,8 @@ import { HTTP } from 'meteor/http'; import { userLoggedOut } from './userLoggedOut'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Users } from '../../../models'; -import { settings } from '../../../settings'; +import { Users } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; export function userLogout(userId) { diff --git a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js index cb0e1ee0d3f5..25c5ef82ed88 100644 --- a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js +++ b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js @@ -8,7 +8,7 @@ import _ from 'underscore'; import { normalizers, fromTemplate, renameInvalidProperties } from './transform_helpers'; import { Logger } from '../../logger'; -import { Users } from '../../models'; +import { Users } from '../../models/server'; import { isURL } from '../../../lib/utils/isURL'; import { registerAccessTokenService } from '../../lib/server/oauth/oauth'; import { callbacks } from '../../../lib/callbacks'; diff --git a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js index 07c83303cb32..5ab704946619 100644 --- a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js +++ b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js @@ -39,6 +39,20 @@ class CustomSoundsClass { extension: 'mp3', src: getURL('sounds/telephone.mp3'), }); + this.add({ + _id: 'outbound-call-ringing', + name: 'Outbound Call Ringing', + extension: 'mp3', + src: getURL('sounds/outbound-call-ringing.mp3'), + }); + this.add({ + _id: 'call-ended', + name: 'Call Ended', + extension: 'mp3', + src: getURL('sounds/call-ended.mp3'), + }); + this.add({ _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getURL('sounds/dialtone.mp3') }); + this.add({ _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getURL('sounds/ringtone.mp3') }); } add(sound) { @@ -108,6 +122,12 @@ class CustomSoundsClass { audio.currentTime = 0; } }; + + isPlaying = (sound) => { + const audio = document.querySelector(`#${getCustomSoundId(sound)}`); + + return audio && audio.duration > 0 && !audio.paused; + }; } export const CustomSounds = new CustomSoundsClass(); diff --git a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js index 9f935e1d3df4..1a51820ec6e8 100644 --- a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js +++ b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; +import { CustomSounds } from '@rocket.chat/models'; -import { CustomSounds } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { api } from '../../../../server/sdk/api'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; diff --git a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js index fa36d4165b2d..d5cf8ae377bc 100644 --- a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js +++ b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; import { check } from 'meteor/check'; +import { CustomSounds } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { CustomSounds } from '../../../models/server/raw'; import { api } from '../../../../server/sdk/api'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; diff --git a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js index 475da52286be..c22506e42ed1 100644 --- a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js +++ b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; - -import { CustomSounds } from '../../../models/server/raw'; +import { CustomSounds } from '@rocket.chat/models'; Meteor.methods({ async listCustomSounds() { diff --git a/apps/meteor/app/discussion/client/tabBar.ts b/apps/meteor/app/discussion/client/tabBar.ts index fc5f0934cfd5..a9bece626e54 100644 --- a/apps/meteor/app/discussion/client/tabBar.ts +++ b/apps/meteor/app/discussion/client/tabBar.ts @@ -1,16 +1,18 @@ import { useMemo, lazy } from 'react'; import { useSetting } from '@rocket.chat/ui-contexts'; +import { isRoomFederated } from '@rocket.chat/core-typings'; import { addAction } from '../../../client/views/room/lib/Toolbox'; const template = lazy(() => import('../../../client/views/room/contextualBar/Discussions')); -addAction('discussions', ({ room: { prid } }) => { +addAction('discussions', ({ room }) => { const discussionEnabled = useSetting('Discussion_enabled'); + const federated = isRoomFederated(room); return useMemo( () => - discussionEnabled && !prid + discussionEnabled && !room.prid ? { groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'], id: 'discussions', @@ -18,9 +20,13 @@ addAction('discussions', ({ room: { prid } }) => { icon: 'discussion', template, full: true, + ...(federated && { + 'disabled': true, + 'data-tooltip': 'Discussions_unavailable_for_federation', + }), order: 3, } : null, - [discussionEnabled, prid], + [discussionEnabled, room.prid, federated], ); }); diff --git a/apps/meteor/app/discussion/server/permissions.ts b/apps/meteor/app/discussion/server/permissions.ts index 260b2da71035..fcd2412a77ea 100644 --- a/apps/meteor/app/discussion/server/permissions.ts +++ b/apps/meteor/app/discussion/server/permissions.ts @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; - -import { Permissions } from '../../models/server/raw'; +import { Permissions } from '@rocket.chat/models'; Meteor.startup(() => { // Add permissions for discussion diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js index 223644f56796..fea71e381182 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js @@ -3,7 +3,6 @@ import { Base64 } from 'meteor/base64'; import { EJSON } from 'meteor/ejson'; import { Random } from 'meteor/random'; import { Session } from 'meteor/session'; -import { TimeSync } from 'meteor/mizzao:timesync'; import { Emitter } from '@rocket.chat/emitter'; import { e2e } from './rocketchat.e2e'; @@ -395,12 +394,7 @@ export class E2ERoom extends Emitter { // Helper function for encryption of messages encrypt(message) { - let ts; - if (isNaN(TimeSync.serverOffset())) { - ts = new Date(); - } else { - ts = new Date(Date.now() + TimeSync.serverOffset()); - } + const ts = new Date(); const data = new TextEncoder('UTF-8').encode( EJSON.stringify({ diff --git a/apps/meteor/app/e2e/server/methods/fetchMyKeys.js b/apps/meteor/app/e2e/server/methods/fetchMyKeys.js index f50e8578f815..744cb9cd9c62 100644 --- a/apps/meteor/app/e2e/server/methods/fetchMyKeys.js +++ b/apps/meteor/app/e2e/server/methods/fetchMyKeys.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; Meteor.methods({ 'e2e.fetchMyKeys'() { diff --git a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js index ee8727794f0b..91c6f6b0fc7f 100644 --- a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js +++ b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; Meteor.methods({ 'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }) { diff --git a/apps/meteor/app/e2e/server/methods/updateGroupKey.js b/apps/meteor/app/e2e/server/methods/updateGroupKey.js index e7276671c872..9aae29003ddf 100644 --- a/apps/meteor/app/e2e/server/methods/updateGroupKey.js +++ b/apps/meteor/app/e2e/server/methods/updateGroupKey.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Subscriptions } from '../../../models'; +import { Subscriptions } from '../../../models/server'; Meteor.methods({ 'e2e.updateGroupKey'(rid, uid, key) { diff --git a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js index eceb0d933c31..4d26d9ca065a 100644 --- a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js +++ b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { EmojiCustom } from '@rocket.chat/models'; import { api } from '../../../../server/sdk/api'; import { hasPermission } from '../../../authorization'; -import { EmojiCustom } from '../../../models/server/raw'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; Meteor.methods({ diff --git a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js index b26953b1d266..96b95f3fbc6f 100644 --- a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js +++ b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js @@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import s from 'underscore.string'; import limax from 'limax'; +import { EmojiCustom } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization'; -import { EmojiCustom } from '../../../models/server/raw'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; import { api } from '../../../../server/sdk/api'; diff --git a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js index d66aeee1a6ad..a4fd124abe91 100644 --- a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js +++ b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; - -import { EmojiCustom } from '../../../models/server/raw'; +import { EmojiCustom } from '@rocket.chat/models'; Meteor.methods({ async listEmojiCustom(options = {}) { diff --git a/apps/meteor/app/emoji/client/emojiPicker.js b/apps/meteor/app/emoji/client/emojiPicker.js index 5995ed8bd746..671698280cc1 100644 --- a/apps/meteor/app/emoji/client/emojiPicker.js +++ b/apps/meteor/app/emoji/client/emojiPicker.js @@ -151,7 +151,7 @@ Template.emojiPicker.events({ 'click .add-custom'(event) { event.stopPropagation(); event.preventDefault(); - FlowRouter.go('/admin/emoji-custom'); + FlowRouter.go('/admin/emoji-custom/new'); EmojiPicker.close(); }, 'click .category-link'(event) { diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js index ac0f31e29679..73b7b475f892 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { Users, Rooms } from '../../../models'; +import { Users, Rooms, Settings } from '../../../models/server'; import { sendMessage } from '../../../lib'; class ErrorHandler { @@ -33,6 +33,7 @@ class ErrorHandler { process.on( 'uncaughtException', Meteor.bindEnvironment((error) => { + Settings.incrementValueById('Uncaught_Exceptions_Count'); if (!this.reporting) { return; } diff --git a/apps/meteor/app/favico/client/favico.js b/apps/meteor/app/favico/client/favico.js deleted file mode 100644 index b85f9b4a4ee1..000000000000 --- a/apps/meteor/app/favico/client/favico.js +++ /dev/null @@ -1,844 +0,0 @@ -/** - * @license MIT - * @fileOverview Favico animations - * @author Miroslav Magda, http://blog.ejci.net - * @version 0.3.10 - */ - -/** - * Create new favico instance - * @param {Object} Options - * @return {Object} Favico object - * @example - * var favico = new Favico({ - * bgColor : '#d00', - * textColor : '#fff', - * fontFamily : 'sans-serif', - * fontStyle : 'bold', - * position : 'down', - * type : 'circle', - * animation : 'slide', - * dataUrl: function(url){}, - * win: top - * }); - */ -/* eslint-disable */ - - export const Favico = (function(opt) { - 'use strict'; - opt = (opt) ? opt : {}; - var _def = { - bgColor: '#d00', - textColor: '#fff', - fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,... - fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900 - type: 'circle', - position: 'down', // down, up, left, leftup (upleft) - animation: 'slide', - elementId: false, - dataUrl: false, - win: window - }; - var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc; - - _browser = {}; - _browser.ff = typeof InstallTrigger !== 'undefined'; - _browser.chrome = !!window.chrome; - _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0; - _browser.ie = /*@cc_on!@*/ false; - _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; - _browser.supported = (_browser.chrome || _browser.ff || _browser.opera); - - var _queue = []; - _readyCb = function() {}; - _ready = _stop = false; - /** - * Initialize favico - */ - var init = function() { - //merge initial options - _opt = merge(_def, opt); - _opt.bgColor = hexToRgb(_opt.bgColor); - _opt.textColor = hexToRgb(_opt.textColor); - _opt.position = _opt.position.toLowerCase(); - _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation; - - _doc = _opt.win.document; - - var isUp = _opt.position.indexOf('up') > -1; - var isLeft = _opt.position.indexOf('left') > -1; - - //transform the animations - if (isUp || isLeft) { - for (var a in animation.types) { - for (var i = 0; i < animation.types[a].length; i++) { - var step = animation.types[a][i]; - - if (isUp) { - if (step.y < 0.6) { - step.y = step.y - 0.4; - } else { - step.y = step.y - 2 * step.y + (1 - step.w); - } - } - - if (isLeft) { - if (step.x < 0.6) { - step.x = step.x - 0.4; - } else { - step.x = step.x - 2 * step.x + (1 - step.h); - } - } - - animation.types[a][i] = step; - } - } - } - _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type; - - _orig = link.getIcons(); - //create temp canvas - _canvas = document.createElement('canvas'); - //create temp image - _img = document.createElement('img'); - var lastIcon = _orig[_orig.length - 1]; - if (lastIcon.hasAttribute('href')) { - _img.setAttribute('crossOrigin', 'anonymous'); - //get width/height - _img.onload = function() { - _h = (_img.height > 0) ? _img.height : 32; - _w = (_img.width > 0) ? _img.width : 32; - _canvas.height = _h; - _canvas.width = _w; - _context = _canvas.getContext('2d'); - icon.ready(); - }; - _img.setAttribute('src', lastIcon.getAttribute('href')); - } else { - _img.onload = function() { - _h = 32; - _w = 32; - _img.height = _h; - _img.width = _w; - _canvas.height = _h; - _canvas.width = _w; - _context = _canvas.getContext('2d'); - icon.ready(); - }; - _img.setAttribute('src', ''); - } - - }; - /** - * Icon namespace - */ - var icon = {}; - /** - * Icon is ready (reset icon) and start animation (if ther is any) - */ - icon.ready = function() { - _ready = true; - icon.reset(); - _readyCb(); - }; - /** - * Reset icon to default state - */ - icon.reset = function() { - //reset - if (!_ready) { - return; - } - _queue = []; - _lastBadge = false; - _running = false; - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - //_stop=true; - link.setIcon(_canvas); - //webcam('stop'); - //video('stop'); - window.clearTimeout(_animTimeout); - window.clearTimeout(_drawTimeout); - }; - /** - * Start animation - */ - icon.start = function() { - if (!_ready || _running) { - return; - } - var finished = function() { - _lastBadge = _queue[0]; - _running = false; - if (_queue.length > 0) { - _queue.shift(); - icon.start(); - } - }; - if (_queue.length > 0) { - _running = true; - var run = function() { - // apply options for this animation - ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function(a) { - if (a in _queue[0].options) { - _opt[a] = _queue[0].options[a]; - } - }); - animation.run(_queue[0].options, function() { - finished(); - }, false); - }; - if (_lastBadge) { - animation.run(_lastBadge.options, function() { - run(); - }, true); - } else { - run(); - } - } - }; - - /** - * Badge types - */ - var type = {}; - var options = function(opt) { - opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n; - opt.x = _w * opt.x; - opt.y = _h * opt.y; - opt.w = _w * opt.w; - opt.h = _h * opt.h; - opt.len = ('' + opt.n).length; - return opt; - }; - /** - * Generate circle - * @param {Object} opt Badge options - */ - type.circle = function(opt) { - opt = options(opt); - var more = false; - if (opt.len === 2) { - opt.x = opt.x - opt.w * 0.4; - opt.w = opt.w * 1.4; - more = true; - } else if (opt.len >= 3) { - opt.x = opt.x - opt.w * 0.65; - opt.w = opt.w * 1.65; - more = true; - } - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - _context.beginPath(); - _context.font = _opt.fontStyle + ' ' + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + 'px ' + _opt.fontFamily; - _context.textAlign = 'center'; - if (more) { - _context.moveTo(opt.x + opt.w / 2, opt.y); - _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y); - _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2); - _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2); - _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h); - _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h); - _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2); - _context.lineTo(opt.x, opt.y + opt.h / 2); - _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y); - } else { - _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI); - } - _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; - _context.fill(); - _context.closePath(); - _context.beginPath(); - _context.stroke(); - _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; - //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - if ((typeof opt.n) === 'number' && opt.n > 999) { - _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); - } else { - _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - } - _context.closePath(); - }; - /** - * Generate rectangle - * @param {Object} opt Badge options - */ - type.rectangle = function(opt) { - opt = options(opt); - var more = false; - if (opt.len === 2) { - opt.x = opt.x - opt.w * 0.4; - opt.w = opt.w * 1.4; - more = true; - } else if (opt.len >= 3) { - opt.x = opt.x - opt.w * 0.65; - opt.w = opt.w * 1.65; - more = true; - } - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - _context.beginPath(); - _context.font = _opt.fontStyle + ' ' + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + 'px ' + _opt.fontFamily; - _context.textAlign = 'center'; - _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; - _context.fillRect(opt.x, opt.y, opt.w, opt.h); - _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; - //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - if ((typeof opt.n) === 'number' && opt.n > 999) { - _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); - } else { - _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - } - _context.closePath(); - }; - - /** - * Set badge - */ - var badge = function(number, opts) { - opts = ((typeof opts) === 'string' ? { - animation: opts - } : opts) || {}; - _readyCb = function() { - try { - if (typeof(number) === 'number' ? (number > 0) : (number !== '')) { - var q = { - type: 'badge', - options: { - n: number - } - }; - if ('animation' in opts && animation.types['' + opts.animation]) { - q.options.animation = '' + opts.animation; - } - if ('type' in opts && type['' + opts.type]) { - q.options.type = '' + opts.type; - } - ['bgColor', 'textColor'].forEach(function(o) { - if (o in opts) { - q.options[o] = hexToRgb(opts[o]); - } - }); - ['fontStyle', 'fontFamily'].forEach(function(o) { - if (o in opts) { - q.options[o] = opts[o]; - } - }); - _queue.push(q); - if (_queue.length > 100) { - throw new Error('Too many badges requests in queue.'); - } - icon.start(); - } else { - icon.reset(); - } - } catch (e) { - throw new Error('Error setting badge. Message: ' + e.message); - } - }; - if (_ready) { - _readyCb(); - } - }; - - /** - * Set image as icon - */ - var image = function(imageElement) { - _readyCb = function() { - try { - var w = imageElement.width; - var h = imageElement.height; - var newImg = document.createElement('img'); - var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); - newImg.setAttribute('crossOrigin', 'anonymous'); - newImg.onload = function() { - _context.clearRect(0, 0, _w, _h); - _context.drawImage(newImg, 0, 0, _w, _h); - link.setIcon(_canvas); - }; - newImg.setAttribute('src', imageElement.getAttribute('src')); - newImg.height = (h / ratio); - newImg.width = (w / ratio); - } catch (e) { - throw new Error('Error setting image. Message: ' + e.message); - } - }; - if (_ready) { - _readyCb(); - } - }; - /** - * Set video as icon - */ - var video = function(videoElement) { - _readyCb = function() { - try { - if (videoElement === 'stop') { - _stop = true; - icon.reset(); - _stop = false; - return; - } - //var w = videoElement.width; - //var h = videoElement.height; - //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); - videoElement.addEventListener('play', function() { - drawVideo(this); - }, false); - - } catch (e) { - throw new Error('Error setting video. Message: ' + e.message); - } - }; - if (_ready) { - _readyCb(); - } - }; - /** - * Set video as icon - */ - var webcam = function(action) { - //UR - if (!window.URL || !window.URL.createObjectURL) { - window.URL = window.URL || {}; - window.URL.createObjectURL = function(obj) { - return obj; - }; - } - if (_browser.supported) { - var newVideo = false; - navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; - _readyCb = function() { - try { - if (action === 'stop') { - _stop = true; - icon.reset(); - _stop = false; - return; - } - newVideo = document.createElement('video'); - newVideo.width = _w; - newVideo.height = _h; - navigator.getUserMedia({ - video: true, - audio: false - }, function(stream) { - newVideo.src = URL.createObjectURL(stream); - newVideo.play(); - drawVideo(newVideo); - }, function() {}); - } catch (e) { - throw new Error('Error setting webcam. Message: ' + e.message); - } - }; - if (_ready) { - _readyCb(); - } - } - - }; - - /** - * Draw video to context and repeat :) - */ - function drawVideo(video) { - if (video.paused || video.ended || _stop) { - return false; - } - //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl) - try { - _context.clearRect(0, 0, _w, _h); - _context.drawImage(video, 0, 0, _w, _h); - } catch (e) { - - } - _drawTimeout = setTimeout(function() { - drawVideo(video); - }, animation.duration); - link.setIcon(_canvas); - } - - var link = {}; - /** - * Get icons from HEAD tag or create a new element - */ - link.getIcons = function() { - var elms = []; - //get link element - var getLinks = function() { - var icons = []; - var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link'); - for (var i = 0; i < links.length; i++) { - if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) { - icons.push(links[i]); - } - } - return icons; - }; - if (_opt.element) { - elms = [_opt.element]; - } else if (_opt.elementId) { - //if img element identified by elementId - elms = [_doc.getElementById(_opt.elementId)]; - elms[0].setAttribute('href', elms[0].getAttribute('src')); - } else { - //if link element - elms = getLinks(); - if (elms.length === 0) { - elms = [_doc.createElement('link')]; - elms[0].setAttribute('rel', 'icon'); - _doc.getElementsByTagName('head')[0].appendChild(elms[0]); - } - } - elms.forEach(function(item) { - item.setAttribute('type', 'image/png'); - }); - return elms; - }; - link.setIcon = function(canvas) { - var url = canvas.toDataURL('image/png'); - if (_opt.dataUrl) { - //if using custom exporter - _opt.dataUrl(url); - } - if (_opt.element) { - _opt.element.setAttribute('href', url); - _opt.element.setAttribute('src', url); - } else if (_opt.elementId) { - //if is attached to element (image) - var elm = _doc.getElementById(_opt.elementId); - elm.setAttribute('href', url); - elm.setAttribute('src', url); - } else { - //if is attached to fav icon - if (_browser.ff || _browser.opera) { - //for FF we need to "recreate" element, atach to dom and remove old - //var originalType = _orig.getAttribute('rel'); - var old = _orig[_orig.length - 1]; - var newIcon = _doc.createElement('link'); - _orig = [newIcon]; - //_orig.setAttribute('rel', originalType); - if (_browser.opera) { - newIcon.setAttribute('rel', 'icon'); - } - newIcon.setAttribute('rel', 'icon'); - newIcon.setAttribute('type', 'image/png'); - _doc.getElementsByTagName('head')[0].appendChild(newIcon); - newIcon.setAttribute('href', url); - if (old.parentNode) { - old.parentNode.removeChild(old); - } - } else { - _orig.forEach(function(icon) { - icon.setAttribute('href', url); - }); - } - } - }; - - //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139 - //HEX to RGB convertor - function hexToRgb(hex) { - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function(m, r, g, b) { - return r + r + g + g + b + b; - }); - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : false; - } - - /** - * Merge options - */ - function merge(def, opt) { - var mergedOpt = {}; - var attrname; - for (attrname in def) { - mergedOpt[attrname] = def[attrname]; - } - for (attrname in opt) { - mergedOpt[attrname] = opt[attrname]; - } - return mergedOpt; - } - - /** - * Cross-browser page visibility shim - * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible - */ - function isPageHidden() { - return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden; - } - - /** - * @namespace animation - */ - var animation = {}; - /** - * Animation "frame" duration - */ - animation.duration = 40; - /** - * Animation types (none,fade,pop,slide) - */ - animation.types = {}; - animation.types.fade = [{ - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.0 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.1 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.2 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.3 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.4 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.5 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.6 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.7 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.8 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 0.9 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 1.0 - }]; - animation.types.none = [{ - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 1 - }]; - animation.types.pop = [{ - x: 1, - y: 1, - w: 0, - h: 0, - o: 1 - }, { - x: 0.9, - y: 0.9, - w: 0.1, - h: 0.1, - o: 1 - }, { - x: 0.8, - y: 0.8, - w: 0.2, - h: 0.2, - o: 1 - }, { - x: 0.7, - y: 0.7, - w: 0.3, - h: 0.3, - o: 1 - }, { - x: 0.6, - y: 0.6, - w: 0.4, - h: 0.4, - o: 1 - }, { - x: 0.5, - y: 0.5, - w: 0.5, - h: 0.5, - o: 1 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 1 - }]; - animation.types.popFade = [{ - x: 0.75, - y: 0.75, - w: 0, - h: 0, - o: 0 - }, { - x: 0.65, - y: 0.65, - w: 0.1, - h: 0.1, - o: 0.2 - }, { - x: 0.6, - y: 0.6, - w: 0.2, - h: 0.2, - o: 0.4 - }, { - x: 0.55, - y: 0.55, - w: 0.3, - h: 0.3, - o: 0.6 - }, { - x: 0.50, - y: 0.50, - w: 0.4, - h: 0.4, - o: 0.8 - }, { - x: 0.45, - y: 0.45, - w: 0.5, - h: 0.5, - o: 0.9 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 1 - }]; - animation.types.slide = [{ - x: 0.4, - y: 1, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.9, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.9, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.8, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.7, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.6, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.5, - w: 0.6, - h: 0.6, - o: 1 - }, { - x: 0.4, - y: 0.4, - w: 0.6, - h: 0.6, - o: 1 - }]; - /** - * Run animation - * @param {Object} opt Animation options - * @param {Object} cb Callabak after all steps are done - * @param {Object} revert Reverse order? true|false - * @param {Object} step Optional step number (frame bumber) - */ - animation.run = function(opt, cb, revert, step) { - var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation]; - if (revert === true) { - step = (typeof step !== 'undefined') ? step : animationType.length - 1; - } else { - step = (typeof step !== 'undefined') ? step : 0; - } - cb = (cb) ? cb : function() {}; - if ((step < animationType.length) && (step >= 0)) { - type[_opt.type](merge(opt, animationType[step])); - _animTimeout = setTimeout(function() { - if (revert) { - step = step - 1; - } else { - step = step + 1; - } - animation.run(opt, cb, revert, step); - }, animation.duration); - - link.setIcon(_canvas); - } else { - cb(); - return; - } - }; - //auto init - init(); - return { - badge: badge, - video: video, - image: image, - webcam: webcam, - reset: icon.reset, - browser: { - supported: _browser.supported - } - }; - }); diff --git a/apps/meteor/app/favico/client/index.js b/apps/meteor/app/favico/client/index.js deleted file mode 100644 index 239a252e455c..000000000000 --- a/apps/meteor/app/favico/client/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Favico } from './favico'; - -export { Favico }; diff --git a/apps/meteor/app/favico/index.js b/apps/meteor/app/favico/index.js deleted file mode 100644 index 40a7340d3887..000000000000 --- a/apps/meteor/app/favico/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './client/index'; diff --git a/apps/meteor/app/federation-v2/client/Federation.ts b/apps/meteor/app/federation-v2/client/Federation.ts new file mode 100644 index 000000000000..552c3a47623b --- /dev/null +++ b/apps/meteor/app/federation-v2/client/Federation.ts @@ -0,0 +1,24 @@ +import { IRoom, IUser, ValueOf } from '@rocket.chat/core-typings'; +import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; + +import { RoomMemberActions } from '../../../definition/IRoomTypeConfig'; + +const allowedActionsInFederatedRooms: ValueOf[] = [ + RoomMemberActions.REMOVE_USER, + RoomMemberActions.INVITE, + RoomMemberActions.JOIN, + RoomMemberActions.LEAVE, +]; + +export const actionAllowed = (room: Partial, action: ValueOf): boolean => { + return room.t === RoomType.DIRECT_MESSAGE && action === RoomMemberActions.REMOVE_USER + ? false + : allowedActionsInFederatedRooms.includes(action); +}; + +export const isEditableByTheUser = (user: IUser | undefined, room: IRoom | undefined): boolean => { + if (!user || !room) { + return false; + } + return user._id === room.u?._id; +}; diff --git a/apps/meteor/app/federation-v2/client/index.ts b/apps/meteor/app/federation-v2/client/index.ts new file mode 100644 index 000000000000..37ec361a570a --- /dev/null +++ b/apps/meteor/app/federation-v2/client/index.ts @@ -0,0 +1 @@ +import './slash-commands'; diff --git a/apps/meteor/app/federation-v2/client/slash-commands/index.ts b/apps/meteor/app/federation-v2/client/slash-commands/index.ts new file mode 100644 index 000000000000..7e32258f20c4 --- /dev/null +++ b/apps/meteor/app/federation-v2/client/slash-commands/index.ts @@ -0,0 +1,20 @@ +import { slashCommands } from '../../../utils/lib/slashCommand'; + +const callback = undefined; +const result = undefined; +const providesPreview = false; +const previewer = undefined; +const previewCallback = undefined; + +slashCommands.add({ + command: 'federation', + callback, + options: { + description: 'Federation_slash_commands', + params: '#command (dm) #user', + }, + result, + providesPreview, + previewer, + previewCallback, +}); diff --git a/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts b/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts index 49898c6ec2a7..4171426bb62b 100644 --- a/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts +++ b/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts @@ -18,11 +18,11 @@ import { export class FederationRoomServiceReceiver { constructor( - private rocketRoomAdapter: RocketChatRoomAdapter, - private rocketUserAdapter: RocketChatUserAdapter, - private rocketMessageAdapter: RocketChatMessageAdapter, - private rocketSettingsAdapter: RocketChatSettingsAdapter, - private bridge: IFederationBridge, + protected rocketRoomAdapter: RocketChatRoomAdapter, + protected rocketUserAdapter: RocketChatUserAdapter, + protected rocketMessageAdapter: RocketChatMessageAdapter, + protected rocketSettingsAdapter: RocketChatSettingsAdapter, + protected bridge: IFederationBridge, ) {} // eslint-disable-line no-empty-function public async createRoom(roomCreateInput: FederationRoomCreateInputDto): Promise { @@ -78,21 +78,22 @@ export class FederationRoomServiceReceiver { leave, } = roomChangeMembershipInput; const affectedFederatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId); + if (!affectedFederatedRoom && eventOrigin === EVENT_ORIGIN.LOCAL) { throw new Error(`Could not find room with external room id: ${externalRoomId}`); } - const isInviterFromTheSameHomeServer = await this.bridge.isUserIdFromTheSameHomeserver( + const isInviterFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver( externalInviterId, this.rocketSettingsAdapter.getHomeServerDomain(), ); - const isInviteeFromTheSameHomeServer = await this.bridge.isUserIdFromTheSameHomeserver( + const isInviteeFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver( externalInviteeId, this.rocketSettingsAdapter.getHomeServerDomain(), ); if (!(await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId))) { const externalUserProfileInformation = await this.bridge.getUserProfileInformation(externalInviterId); - const name = externalUserProfileInformation.displayname || normalizedInviterId; + const name = externalUserProfileInformation?.displayname || normalizedInviterId; const username = isInviterFromTheSameHomeServer ? inviterUsernameOnly : normalizedInviterId; const federatedInviterUser = FederatedUser.createInstance(externalInviterId, { name, @@ -105,7 +106,7 @@ export class FederationRoomServiceReceiver { if (!(await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviteeId))) { const externalUserProfileInformation = await this.bridge.getUserProfileInformation(externalInviteeId); - const name = externalUserProfileInformation.displayname || normalizedInviteeId; + const name = externalUserProfileInformation?.displayname || normalizedInviteeId; const username = isInviteeFromTheSameHomeServer ? inviteeUsernameOnly : normalizedInviteeId; const federatedInviteeUser = FederatedUser.createInstance(externalInviteeId, { name, @@ -118,9 +119,8 @@ export class FederationRoomServiceReceiver { const federatedInviteeUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviteeId); const federatedInviterUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId); - if (!affectedFederatedRoom && eventOrigin === EVENT_ORIGIN.REMOTE) { - const members = [federatedInviterUser, federatedInviteeUser] as any[]; + const members = [federatedInviterUser, federatedInviteeUser] as FederatedUser[]; const newFederatedRoom = FederatedRoom.createInstance( externalRoomId, normalizedRoomId, @@ -134,14 +134,42 @@ export class FederationRoomServiceReceiver { await this.bridge.joinRoom(externalRoomId, externalInviteeId); } const federatedRoom = affectedFederatedRoom || (await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId)); - if (leave) { + if ( + !(await this.rocketRoomAdapter.isUserAlreadyJoined( + federatedRoom?.internalReference?._id as string, + federatedInviteeUser?.internalReference._id as string, + )) + ) { + return; + } + return this.rocketRoomAdapter.removeUserFromRoom( federatedRoom as FederatedRoom, federatedInviteeUser as FederatedUser, federatedInviterUser as FederatedUser, ); } + if (affectedFederatedRoom?.isDirectMessage() && eventOrigin === EVENT_ORIGIN.REMOTE) { + const membersUsernames = [ + ...(affectedFederatedRoom.internalReference?.usernames || []), + federatedInviteeUser?.internalReference.username as string, + ]; + const newFederatedRoom = FederatedRoom.createInstance( + externalRoomId, + normalizedRoomId, + federatedInviterUser as FederatedUser, + RoomType.DIRECT_MESSAGE, + externalRoomName, + ); + if (affectedFederatedRoom.internalReference?.usernames?.includes(federatedInviteeUser?.internalReference.username || '')) { + return; + } + await this.rocketRoomAdapter.removeDirectMessageRoom(affectedFederatedRoom); + await this.rocketRoomAdapter.createFederatedRoomForDirectMessage(newFederatedRoom, membersUsernames); + return; + } + await this.rocketRoomAdapter.addUserToRoom( federatedRoom as FederatedRoom, federatedInviteeUser as FederatedUser, @@ -182,7 +210,7 @@ export class FederationRoomServiceReceiver { } public async changeRoomName(roomChangeNameInput: FederationRoomChangeNameDto): Promise { - const { externalRoomId, normalizedRoomName } = roomChangeNameInput; + const { externalRoomId, normalizedRoomName, externalSenderId } = roomChangeNameInput; const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId); if (!federatedRoom) { @@ -193,25 +221,43 @@ export class FederationRoomServiceReceiver { return; } + if (federatedRoom.internalReference?.name === normalizedRoomName) { + return; + } + + const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId); + if (!federatedUser) { + return; + } + federatedRoom.changeRoomName(normalizedRoomName); - await this.rocketRoomAdapter.updateRoomName(federatedRoom); + await this.rocketRoomAdapter.updateRoomName(federatedRoom, federatedUser); } public async changeRoomTopic(roomChangeTopicInput: FederationRoomChangeTopicDto): Promise { - const { externalRoomId, roomTopic } = roomChangeTopicInput; + const { externalRoomId, roomTopic, externalSenderId } = roomChangeTopicInput; const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId); if (!federatedRoom) { return; } + if (federatedRoom.internalReference?.topic === roomTopic) { + return; + } + if (federatedRoom.isDirectMessage()) { return; } + const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId); + if (!federatedUser) { + return; + } + federatedRoom.changeRoomTopic(roomTopic); - await this.rocketRoomAdapter.updateRoomTopic(federatedRoom); + await this.rocketRoomAdapter.updateRoomTopic(federatedRoom, federatedUser); } } diff --git a/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts b/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts index 2be2283ddd86..8d21f8241648 100644 --- a/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts +++ b/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts @@ -4,23 +4,25 @@ import { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { FederatedRoom } from '../domain/FederatedRoom'; import { FederatedUser } from '../domain/FederatedUser'; import { IFederationBridge } from '../domain/IFederationBridge'; -import { RocketChatNotificationAdapter } from '../infrastructure/rocket-chat/adapters/Notification'; import { RocketChatRoomAdapter } from '../infrastructure/rocket-chat/adapters/Room'; import { RocketChatSettingsAdapter } from '../infrastructure/rocket-chat/adapters/Settings'; import { RocketChatUserAdapter } from '../infrastructure/rocket-chat/adapters/User'; -import { FederationRoomInviteUserDto, FederationRoomSendExternalMessageDto } from './input/RoomSenderDto'; +import { + FederationAfterLeaveRoomDto, + FederationCreateDMAndInviteUserDto, + FederationRoomSendExternalMessageDto, +} from './input/RoomSenderDto'; export class FederationRoomServiceSender { constructor( - private rocketRoomAdapter: RocketChatRoomAdapter, - private rocketUserAdapter: RocketChatUserAdapter, - private rocketSettingsAdapter: RocketChatSettingsAdapter, - private rocketNotificationAdapter: RocketChatNotificationAdapter, - private bridge: IFederationBridge, + protected rocketRoomAdapter: RocketChatRoomAdapter, + protected rocketUserAdapter: RocketChatUserAdapter, + protected rocketSettingsAdapter: RocketChatSettingsAdapter, + protected bridge: IFederationBridge, ) {} // eslint-disable-line no-empty-function - public async inviteUserToAFederatedRoom(roomInviteUserInput: FederationRoomInviteUserDto): Promise { - const { normalizedInviteeId, rawInviteeId, internalInviterId, inviteeUsernameOnly, internalRoomId } = roomInviteUserInput; + public async createDirectMessageRoomAndInviteUser(roomCreateDMAndInviteUserInput: FederationCreateDMAndInviteUserDto): Promise { + const { normalizedInviteeId, rawInviteeId, internalInviterId, inviteeUsernameOnly } = roomCreateDMAndInviteUserInput; if (!(await this.rocketUserAdapter.getFederatedUserByInternalId(internalInviterId))) { const internalUser = (await this.rocketUserAdapter.getInternalUserById(internalInviterId)) as IUser; @@ -48,57 +50,62 @@ export class FederationRoomServiceSender { await this.rocketUserAdapter.createFederatedUser(federatedInviteeUser); } - const federatedInviterUser = (await this.rocketUserAdapter.getFederatedUserByInternalId(internalInviterId)) as FederatedUser; const federatedInviteeUser = (await this.rocketUserAdapter.getFederatedUserByInternalUsername(normalizedInviteeId)) as FederatedUser; - const isInviteeFromTheSameHomeServer = await this.bridge.isUserIdFromTheSameHomeserver( + const isInviteeFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver( rawInviteeId, this.rocketSettingsAdapter.getHomeServerDomain(), ); + const internalRoomId = FederatedRoom.buildRoomIdForDirectMessages(federatedInviterUser, federatedInviteeUser); if (!(await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId))) { - const internalRoom = (await this.rocketRoomAdapter.getInternalRoomById(internalRoomId)) as IRoom; - const roomName = (internalRoom.fname || internalRoom.name) as string; - const externalRoomId = await this.bridge.createRoom( - federatedInviterUser.externalId, - federatedInviteeUser.externalId, - internalRoom.t as RoomType, - roomName, - internalRoom.topic, - ); + const externalRoomId = await this.bridge.createDirectMessageRoom(federatedInviterUser.externalId, [federatedInviteeUser.externalId]); const newFederatedRoom = FederatedRoom.createInstance( externalRoomId, externalRoomId, federatedInviterUser, - internalRoom.t as RoomType, - roomName, + RoomType.DIRECT_MESSAGE, + '', + [federatedInviterUser, federatedInviteeUser] as any[], ); - await this.rocketRoomAdapter.updateFederatedRoomByInternalRoomId(internalRoom._id, newFederatedRoom); + await this.rocketRoomAdapter.createFederatedRoom(newFederatedRoom); } const federatedRoom = (await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId)) as FederatedRoom; - const wasInvitedWhenTheRoomWasCreated = federatedRoom.isDirectMessage(); if (isInviteeFromTheSameHomeServer) { await this.bridge.createUser( inviteeUsernameOnly, - federatedInviteeUser.internalReference.name as string, + federatedInviteeUser?.internalReference?.name || normalizedInviteeId, this.rocketSettingsAdapter.getHomeServerDomain(), ); await this.bridge.inviteToRoom(federatedRoom.externalId, federatedInviterUser.externalId, federatedInviteeUser.externalId); await this.bridge.joinRoom(federatedRoom.externalId, federatedInviteeUser.externalId); - } else if (!wasInvitedWhenTheRoomWasCreated) { - this.bridge.inviteToRoom(federatedRoom.externalId, federatedInviterUser.externalId, federatedInviteeUser.externalId).catch(() => { - this.rocketNotificationAdapter.notifyWithEphemeralMessage( - 'Federation_Matrix_only_owners_can_invite_users', - federatedInviterUser?.internalReference?._id, - internalRoomId, - federatedInviterUser?.internalReference?.language, - ); - }); } await this.rocketRoomAdapter.addUserToRoom(federatedRoom, federatedInviteeUser, federatedInviterUser); } + public async leaveRoom(afterLeaveRoomInput: FederationAfterLeaveRoomDto): Promise { + const { internalRoomId, internalUserId, whoRemovedInternalId } = afterLeaveRoomInput; + + const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId); + if (!federatedRoom) { + return; + } + + const federatedUser = await this.rocketUserAdapter.getFederatedUserByInternalId(internalUserId); + if (!federatedUser) { + return; + } + + if (whoRemovedInternalId) { + const who = await this.rocketUserAdapter.getFederatedUserByInternalId(whoRemovedInternalId); + await this.bridge.kickUserFromRoom(federatedRoom.externalId, federatedUser.externalId, who?.externalId as string); + return; + } + + await this.bridge.leaveRoom(federatedRoom.externalId, federatedUser.externalId); + } + public async sendMessageFromRocketChat(roomSendExternalMessageInput: FederationRoomSendExternalMessageDto): Promise { const { internalRoomId, internalSenderId, message } = roomSendExternalMessageInput; @@ -111,7 +118,6 @@ export class FederationRoomServiceSender { if (!federatedRoom) { throw new Error(`Could not find room id for ${internalRoomId}`); } - await this.bridge.sendMessage(federatedRoom.externalId, federatedSender.externalId, message.msg); return message; @@ -125,4 +131,68 @@ export class FederationRoomServiceSender { return Boolean(federatedRoom?.isFederated()); } + + public async canAddThisUserToTheRoom(internalUser: IUser | string, internalRoom: IRoom): Promise { + const newUserBeingAdded = typeof internalUser === 'string'; + if (newUserBeingAdded) { + return; + } + + if (internalRoom.federated) { + return; + } + + const user = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id); + if (user && !user.existsOnlyOnProxyServer) { + throw new Error('error-cant-add-federated-users'); + } + } + + public async canAddUsersToTheRoom(internalUser: IUser | string, internalInviter: IUser, internalRoom: IRoom): Promise { + if (!internalRoom.federated) { + return; + } + const tryingToAddNewFederatedUser = typeof internalUser === 'string'; + if (tryingToAddNewFederatedUser) { + throw new Error('error-this-is-an-ee-feature'); + } + + const invitee = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id); + const inviter = await this.rocketUserAdapter.getFederatedUserByInternalId((internalInviter as IUser)._id); + const externalRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoom._id); + if (!externalRoom || !inviter) { + return; + } + + const isARoomFromTheProxyServer = this.bridge.isRoomFromTheSameHomeserver( + externalRoom.externalId, + this.rocketSettingsAdapter.getHomeServerDomain(), + ); + const isInviterFromTheProxyServer = this.bridge.isUserIdFromTheSameHomeserver( + inviter.externalId, + this.rocketSettingsAdapter.getHomeServerDomain(), + ); + + if (!isARoomFromTheProxyServer && !isInviterFromTheProxyServer) { + return; + } + if (invitee && !invitee.existsOnlyOnProxyServer && internalRoom.t !== RoomType.DIRECT_MESSAGE) { + throw new Error('error-this-is-an-ee-feature'); + } + } + + public async beforeCreateDirectMessageFromUI(internalUsers: (IUser | string)[]): Promise { + const usernames = internalUsers.map((user) => { + if (typeof user === 'string') { + return user; + } + return user.username; + }); + const isThereAnyFederatedUser = + usernames.some((username) => username?.includes(':')) || + internalUsers.filter((user) => typeof user !== 'string').some((user) => (user as IUser).federated); + if (isThereAnyFederatedUser) { + throw new Error('error-this-is-an-ee-feature'); + } + } } diff --git a/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts b/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts index ed85b194d67d..ae0b2ace785e 100644 --- a/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts +++ b/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts @@ -56,8 +56,12 @@ export class FederationRoomChangeJoinRulesDto extends BaseRoom { export class FederationRoomChangeNameDto extends BaseRoom { normalizedRoomName: string; + + externalSenderId: string; } export class FederationRoomChangeTopicDto extends BaseRoom { roomTopic: string; + + externalSenderId: string; } diff --git a/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts b/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts index 700216105866..af4c9de94d0b 100644 --- a/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts +++ b/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts @@ -1,6 +1,6 @@ import { IMessage } from '@rocket.chat/core-typings'; -export class FederationRoomInviteUserDto { +export class FederationCreateDMAndInviteUserDto { internalInviterId: string; internalRoomId: string; @@ -19,3 +19,11 @@ export class FederationRoomSendExternalMessageDto { message: IMessage; } + +export class FederationAfterLeaveRoomDto { + internalRoomId: string; + + internalUserId: string; + + whoRemovedInternalId?: string; +} diff --git a/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts b/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts index c82b078c49e7..aec246f23788 100644 --- a/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts +++ b/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts @@ -11,9 +11,9 @@ export class FederatedRoom { public internalReference: IRoom; // eslint-disable-next-line - private constructor() {} + protected constructor() {} - private static generateTemporaryName(normalizedExternalId: string): string { + protected static generateTemporaryName(normalizedExternalId: string): string { return `Federation-${normalizedExternalId}`; } @@ -23,12 +23,12 @@ export class FederatedRoom { creator: FederatedUser, type: RoomType, name?: string, - members?: IUser[], + members?: FederatedUser[], ): FederatedRoom { const roomName = name || FederatedRoom.generateTemporaryName(normalizedExternalId); return Object.assign(new FederatedRoom(), { externalId, - ...(type === RoomType.DIRECT_MESSAGE ? { members } : {}), + members, internalReference: { t: type, name: roomName, @@ -42,10 +42,28 @@ export class FederatedRoom { return new FederatedRoom(); } + public getMembers(): IUser[] { + return this.members && this.members.length > 0 ? this.members.map((user) => user.internalReference) : []; + } + + public isFederated(): boolean { + return this.internalReference?.federated === true; + } + public isDirectMessage(): boolean { return this.internalReference?.t === RoomType.DIRECT_MESSAGE; } + public static buildRoomIdForDirectMessages(inviter: FederatedUser, invitee: FederatedUser): string { + if (!inviter.internalReference || !invitee.internalReference) { + throw new Error('Cannot create room Id without the user ids'); + } + return [inviter.internalReference, invitee.internalReference] + .map(({ _id }) => _id) + .sort() + .join(''); + } + public setRoomType(type: RoomType): void { if (this.isDirectMessage()) { throw new Error('Its not possible to change a direct message type'); @@ -65,14 +83,6 @@ export class FederatedRoom { if (this.isDirectMessage()) { throw new Error('Its not possible to change a direct message topic'); } - this.internalReference.description = topic; - } - - public getMembers(): IUser[] { - return this.isDirectMessage() && this.members && this.members.length > 0 ? this.members.map((user) => user.internalReference) : []; - } - - public isFederated(): boolean { - return this.internalReference?.federated === true; + this.internalReference.topic = topic; } } diff --git a/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts b/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts index 225b0fabc003..681f298038c9 100644 --- a/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts +++ b/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts @@ -14,7 +14,7 @@ export class FederatedUser { public existsOnlyOnProxyServer: boolean; // eslint-disable-next-line - private constructor() {} + protected constructor() {} public static createInstance(externalId: string, params: IFederatedUserCreationParams): FederatedUser { return Object.assign(new FederatedUser(), { diff --git a/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts b/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts index 86310ec30f17..66af6beabf01 100644 --- a/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts +++ b/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts @@ -1,22 +1,17 @@ -import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; - export interface IFederationBridge { start(): Promise; stop(): Promise; onFederationAvailabilityChanged(enabled: boolean): Promise; getUserProfileInformation(externalUserId: string): Promise; joinRoom(externalRoomId: string, externalUserId: string): Promise; - createRoom( - externalCreatorId: string, - externalInviteeId: string, - roomType: RoomType, - roomName: string, - roomTopic?: string, - ): Promise; + createDirectMessageRoom(externalCreatorId: string, externalInviteeIds: string[]): Promise; inviteToRoom(externalRoomId: string, externalInviterId: string, externalInviteeId: string): Promise; sendMessage(externalRoomId: string, externaSenderId: string, text: string): Promise; createUser(username: string, name: string, domain: string): Promise; isUserIdFromTheSameHomeserver(externalUserId: string, domain: string): boolean; + isRoomFromTheSameHomeserver(externalRoomId: string, domain: string): boolean; + leaveRoom(externalRoomId: string, externalUserId: string): Promise; + kickUserFromRoom(externalRoomId: string, externalUserId: string, externalOwnerId: string): Promise; } export enum EVENT_ORIGIN { diff --git a/apps/meteor/app/federation-v2/server/index.ts b/apps/meteor/app/federation-v2/server/index.ts index d2d90966d4d0..7ba7a631da0b 100644 --- a/apps/meteor/app/federation-v2/server/index.ts +++ b/apps/meteor/app/federation-v2/server/index.ts @@ -1,15 +1,14 @@ import { FederationFactory } from './infrastructure/Factory'; -const PROCESSING_CONCURRENCY = 1; +export const FEDERATION_PROCESSING_CONCURRENCY = 1; const rocketSettingsAdapter = FederationFactory.buildRocketSettingsAdapter(); rocketSettingsAdapter.initialize(); -const queueInstance = FederationFactory.buildQueue(); -const federation = FederationFactory.buildBridge(rocketSettingsAdapter, queueInstance); +export const federationQueueInstance = FederationFactory.buildQueue(); +const federation = FederationFactory.buildBridge(rocketSettingsAdapter, federationQueueInstance); const rocketRoomAdapter = FederationFactory.buildRocketRoomAdapter(); const rocketUserAdapter = FederationFactory.buildRocketUserAdapter(); const rocketMessageAdapter = FederationFactory.buildRocketMessageAdapter(); -const rocketNotificationAdapter = FederationFactory.buildRocketNotificationdapter(); const federationRoomServiceReceiver = FederationFactory.buildRoomServiceReceiver( rocketRoomAdapter, @@ -18,18 +17,37 @@ const federationRoomServiceReceiver = FederationFactory.buildRoomServiceReceiver rocketSettingsAdapter, federation, ); -const federationEventsHandler = FederationFactory.buildEventHandlers(federationRoomServiceReceiver); + +const federationEventsHandler = FederationFactory.buildEventHandlers(federationRoomServiceReceiver, rocketSettingsAdapter); export const federationRoomServiceSender = FederationFactory.buildRoomServiceSender( rocketRoomAdapter, rocketUserAdapter, rocketSettingsAdapter, - rocketNotificationAdapter, federation, ); -(async (): Promise => { - queueInstance.setHandler(federationEventsHandler.handleEvent.bind(federationEventsHandler), PROCESSING_CONCURRENCY); +FederationFactory.setupListeners(federationRoomServiceSender); +let cancelSettingsObserver: Function; + +export const runFederation = async (): Promise => { + federationQueueInstance.setHandler(federationEventsHandler.handleEvent.bind(federationEventsHandler), FEDERATION_PROCESSING_CONCURRENCY); + cancelSettingsObserver = rocketSettingsAdapter.onFederationEnabledStatusChanged( + federation.onFederationAvailabilityChanged.bind(federation), + ); + if (!rocketSettingsAdapter.isFederationEnabled()) { + return; + } await federation.start(); - await rocketSettingsAdapter.onFederationEnabledStatusChanged(federation.onFederationAvailabilityChanged.bind(federation)); + require('./infrastructure/rocket-chat/slash-commands'); +}; + +export const stopFederation = async (): Promise => { + FederationFactory.removeListeners(); + await federation.stop(); + cancelSettingsObserver(); +}; + +(async (): Promise => { + await runFederation(); })(); diff --git a/apps/meteor/app/federation-v2/server/infrastructure/Factory.ts b/apps/meteor/app/federation-v2/server/infrastructure/Factory.ts index 36a20f827c10..e5f6370814bf 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/Factory.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/Factory.ts @@ -1,3 +1,5 @@ +import { IRoom, IUser } from '@rocket.chat/core-typings'; + import { FederationRoomServiceReceiver } from '../application/RoomServiceReceiver'; import { FederationRoomServiceSender } from '../application/RoomServiceSender'; import { MatrixBridge } from './matrix/Bridge'; @@ -16,7 +18,8 @@ import { RocketChatRoomAdapter } from './rocket-chat/adapters/Room'; import { RocketChatSettingsAdapter } from './rocket-chat/adapters/Settings'; import { RocketChatUserAdapter } from './rocket-chat/adapters/User'; import { IFederationBridge } from '../domain/IFederationBridge'; -import { RocketChatNotificationAdapter } from './rocket-chat/adapters/Notification'; +import { FederationHooks } from './rocket-chat/hooks'; +import { FederationRoomSenderConverter } from './rocket-chat/converters/RoomSender'; export class FederationFactory { public static buildRocketSettingsAdapter(): RocketChatSettingsAdapter { @@ -35,10 +38,6 @@ export class FederationFactory { return new RocketChatMessageAdapter(); } - public static buildRocketNotificationdapter(): RocketChatNotificationAdapter { - return new RocketChatNotificationAdapter(); - } - public static buildQueue(): InMemoryQueue { return new InMemoryQueue(); } @@ -57,10 +56,9 @@ export class FederationFactory { rocketRoomAdapter: RocketChatRoomAdapter, rocketUserAdapter: RocketChatUserAdapter, rocketSettingsAdapter: RocketChatSettingsAdapter, - rocketNotificationAdapter: RocketChatNotificationAdapter, bridge: IFederationBridge, ): FederationRoomServiceSender { - return new FederationRoomServiceSender(rocketRoomAdapter, rocketUserAdapter, rocketSettingsAdapter, rocketNotificationAdapter, bridge); + return new FederationRoomServiceSender(rocketRoomAdapter, rocketUserAdapter, rocketSettingsAdapter, bridge); } public static buildBridge(rocketSettingsAdapter: RocketChatSettingsAdapter, queue: InMemoryQueue): IFederationBridge { @@ -75,16 +73,42 @@ export class FederationFactory { ); } - public static buildEventHandlers(roomServiceReceive: FederationRoomServiceReceiver): MatrixEventsHandler { - const EVENT_HANDLERS = [ - new MatrixRoomCreatedHandler(roomServiceReceive), - new MatrixRoomMembershipChangedHandler(roomServiceReceive), - new MatrixRoomJoinRulesChangedHandler(roomServiceReceive), - new MatrixRoomNameChangedHandler(roomServiceReceive), - new MatrixRoomTopicChangedHandler(roomServiceReceive), - new MatrixRoomMessageSentHandler(roomServiceReceive), + public static buildEventHandlers( + roomServiceReceive: FederationRoomServiceReceiver, + rocketSettingsAdapter: RocketChatSettingsAdapter, + ): MatrixEventsHandler { + return new MatrixEventsHandler(FederationFactory.getEventHandlers(roomServiceReceive, rocketSettingsAdapter)); + } + + public static getEventHandlers( + roomServiceReceiver: FederationRoomServiceReceiver, + rocketSettingsAdapter: RocketChatSettingsAdapter, + ): any[] { + return [ + new MatrixRoomCreatedHandler(roomServiceReceiver), + new MatrixRoomMembershipChangedHandler(roomServiceReceiver, rocketSettingsAdapter), + new MatrixRoomMessageSentHandler(roomServiceReceiver), + new MatrixRoomJoinRulesChangedHandler(roomServiceReceiver), + new MatrixRoomNameChangedHandler(roomServiceReceiver), + new MatrixRoomTopicChangedHandler(roomServiceReceiver), ]; + } + + public static setupListeners(roomServiceSender: FederationRoomServiceSender): void { + FederationHooks.afterLeaveRoom(async (user: IUser, room: IRoom) => + roomServiceSender.leaveRoom(FederationRoomSenderConverter.toAfterLeaveRoom(user._id, room._id)), + ); + FederationHooks.afterRemoveFromRoom(async (user: IUser, room: IRoom, userWhoRemoved: IUser) => + roomServiceSender.leaveRoom(FederationRoomSenderConverter.toAfterLeaveRoom(user._id, room._id, userWhoRemoved._id)), + ); + FederationHooks.canAddTheUserToTheRoom((user: IUser | string, room: IRoom) => roomServiceSender.canAddThisUserToTheRoom(user, room)); + FederationHooks.canAddUsersToTheRoom((user: IUser | string, inviter: IUser, room: IRoom) => + roomServiceSender.canAddUsersToTheRoom(user, inviter, room), + ); + FederationHooks.beforeCreateDirectMessage((members: (IUser | string)[]) => roomServiceSender.beforeCreateDirectMessageFromUI(members)); + } - return new MatrixEventsHandler(EVENT_HANDLERS); + public static removeListeners(): void { + FederationHooks.removeCEValidation(); } } diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/Bridge.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/Bridge.ts index acb2824f6903..6a27ff694b96 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/Bridge.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/Bridge.ts @@ -1,24 +1,29 @@ -import { AppServiceOutput, Bridge } from '@rocket.chat/forked-matrix-appservice-bridge'; -import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; +import type { AppServiceOutput, Bridge } from '@rocket.chat/forked-matrix-appservice-bridge'; import { IFederationBridge } from '../../domain/IFederationBridge'; import { bridgeLogger } from '../rocket-chat/adapters/logger'; import { IMatrixEvent } from './definitions/IMatrixEvent'; import { MatrixEventType } from './definitions/MatrixEventType'; +import { MatrixRoomType } from './definitions/MatrixRoomType'; +import { MatrixRoomVisibility } from './definitions/MatrixRoomVisibility'; + +let MatrixUserInstance: any; export class MatrixBridge implements IFederationBridge { - private bridgeInstance: Bridge; + protected bridgeInstance: Bridge; + + protected isRunning = false; - private isRunning = false; + protected isUpdatingBridgeStatus = false; constructor( - private appServiceId: string, - private homeServerUrl: string, - private homeServerDomain: string, - private bridgeUrl: string, - private bridgePort: number, - private homeServerRegistrationFile: Record, - private eventHandler: Function, + protected appServiceId: string, + protected homeServerUrl: string, + protected homeServerDomain: string, + protected bridgeUrl: string, + protected bridgePort: number, + protected homeServerRegistrationFile: Record, + protected eventHandler: Function, ) { this.logInfo(); } @@ -32,6 +37,10 @@ export class MatrixBridge implements IFederationBridge { } public async start(): Promise { + if (this.isUpdatingBridgeStatus) { + return; + } + this.isUpdatingBridgeStatus = true; try { await this.stop(); await this.createInstance(); @@ -43,8 +52,8 @@ export class MatrixBridge implements IFederationBridge { } catch (e) { bridgeLogger.error('Failed to initialize the matrix-appservice-bridge.', e); bridgeLogger.error('Disabling Matrix Bridge. Please resolve error and try again'); - - // await this.settingsAdapter.disableFederation(); + } finally { + this.isUpdatingBridgeStatus = false; } } @@ -52,14 +61,14 @@ export class MatrixBridge implements IFederationBridge { if (!this.isRunning) { return; } + this.isRunning = false; // the http server might take some minutes to shutdown, and this promise can take some time to be resolved await this.bridgeInstance?.close(); - this.isRunning = false; } public async getUserProfileInformation(externalUserId: string): Promise { try { - return this.bridgeInstance.getIntent(externalUserId).getProfileInfo(externalUserId); + return await this.bridgeInstance.getIntent(externalUserId).getProfileInfo(externalUserId); } catch (err) { // no-op } @@ -70,53 +79,50 @@ export class MatrixBridge implements IFederationBridge { } public async inviteToRoom(externalRoomId: string, externalInviterId: string, externalInviteeId: string): Promise { - await this.bridgeInstance.getIntent(externalInviterId).invite(externalRoomId, externalInviteeId); + try { + await this.bridgeInstance.getIntent(externalInviterId).invite(externalRoomId, externalInviteeId); + } catch (e) { + // no-op + } } public async createUser(username: string, name: string, domain: string): Promise { + if (!MatrixUserInstance) { + throw new Error('Error loading the Matrix User instance from the external library'); + } const matrixUserId = `@${username?.toLowerCase()}:${domain}`; - const intent = this.bridgeInstance.getIntent(matrixUserId); - - await intent.ensureProfile(name); - await intent.setDisplayName(`${username} (${name})`); + const newUser = new MatrixUserInstance(matrixUserId); + await this.bridgeInstance.provisionUser(newUser, { name: `${username} (${name})` }); return matrixUserId; } - public async createRoom( - externalCreatorId: string, - externalInviteeId: string, - roomType: RoomType, - roomName: string, - roomTopic?: string, - ): Promise { + public async createDirectMessageRoom(externalCreatorId: string, externalInviteeIds: string[]): Promise { const intent = this.bridgeInstance.getIntent(externalCreatorId); - const visibility = roomType === 'p' || roomType === 'd' ? 'invite' : 'public'; - const preset = roomType === 'p' || roomType === 'd' ? 'private_chat' : 'public_chat'; - - // Create the matrix room + const visibility = MatrixRoomVisibility.PRIVATE; + const preset = MatrixRoomType.PRIVATE; const matrixRoom = await intent.createRoom({ createAsClient: true, options: { - name: roomName, - topic: roomTopic, visibility, preset, - ...this.parametersForDirectMessagesIfNecessary(roomType, externalInviteeId), - // eslint-disable-next-line @typescript-eslint/camelcase + is_direct: true, + invite: externalInviteeIds, creation_content: { - // eslint-disable-next-line @typescript-eslint/camelcase was_internally_programatically_created: true, }, }, }); - return matrixRoom.room_id; } public async sendMessage(externalRoomId: string, externaSenderId: string, text: string): Promise { - await this.bridgeInstance.getIntent(externaSenderId).sendText(externalRoomId, text); + try { + await this.bridgeInstance.getIntent(externaSenderId).sendText(externalRoomId, text); + } catch (e) { + throw new Error('User is not part of the room.'); + } } public isUserIdFromTheSameHomeserver(externalUserId: string, domain: string): boolean { @@ -129,17 +135,7 @@ export class MatrixBridge implements IFederationBridge { return this; } - private parametersForDirectMessagesIfNecessary = (roomType: RoomType, invitedUserId: string): Record => { - return roomType === RoomType.DIRECT_MESSAGE - ? { - // eslint-disable-next-line @typescript-eslint/camelcase - is_direct: true, - invite: [invitedUserId], - } - : {}; - }; - - private logInfo(): void { + protected logInfo(): void { bridgeLogger.info(`Running Federation V2: id: ${this.appServiceId} bridgeUrl: ${this.bridgeUrl} @@ -148,11 +144,28 @@ export class MatrixBridge implements IFederationBridge { `); } - private async createInstance(): Promise { + public async leaveRoom(externalRoomId: string, externalUserId: string): Promise { + try { + await this.bridgeInstance.getIntent(externalUserId).leave(externalRoomId); + } catch (e) { + // no-op + } + } + + public async kickUserFromRoom(externalRoomId: string, externalUserId: string, externalOwnerId: string): Promise { + this.bridgeInstance.getIntent(externalOwnerId).kick(externalRoomId, externalUserId); + } + + public isRoomFromTheSameHomeserver(externalRoomId: string, domain: string): boolean { + return this.isUserIdFromTheSameHomeserver(externalRoomId, domain); + } + + protected async createInstance(): Promise { bridgeLogger.info('Performing Dynamic Import of matrix-appservice-bridge'); // Dynamic import to prevent Rocket.Chat from loading the module until needed and then handle if that fails - const { Bridge, AppServiceRegistration } = await import('@rocket.chat/forked-matrix-appservice-bridge'); + const { Bridge, AppServiceRegistration, MatrixUser } = await import('@rocket.chat/forked-matrix-appservice-bridge'); + MatrixUserInstance = MatrixUser; this.bridgeInstance = new Bridge({ homeserverUrl: this.homeServerUrl, @@ -160,9 +173,6 @@ export class MatrixBridge implements IFederationBridge { registration: AppServiceRegistration.fromObject(this.homeServerRegistrationFile as AppServiceOutput), disableStores: true, controller: { - onAliasQuery: (alias, matrixRoomId): void => { - console.log('onAliasQuery', alias, matrixRoomId); - }, onEvent: async (request /* , context*/): Promise => { // Get the event const event = request.getData() as unknown as IMatrixEvent; diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/converters/RoomReceiver.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/converters/RoomReceiver.ts index 9d845516cd75..e6e75c872f5a 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/converters/RoomReceiver.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/converters/RoomReceiver.ts @@ -29,6 +29,7 @@ export class MatrixRoomReceiverConverter { public static toChangeRoomMembershipDto( externalEvent: IMatrixEvent, + homeServerDomain: string, ): FederationRoomChangeMembershipDto { return Object.assign(new FederationRoomChangeMembershipDto(), { ...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id), @@ -42,7 +43,7 @@ export class MatrixRoomReceiverConverter { normalizedInviteeId: MatrixRoomReceiverConverter.convertMatrixUserIdFormatToRCFormat(externalEvent.state_key), inviteeUsernameOnly: MatrixRoomReceiverConverter.formatMatrixUserIdToRCUsernameFormat(externalEvent.state_key), inviterUsernameOnly: MatrixRoomReceiverConverter.formatMatrixUserIdToRCUsernameFormat(externalEvent.sender), - eventOrigin: MatrixRoomReceiverConverter.getEventOrigin(externalEvent.sender, externalEvent.state_key), + eventOrigin: MatrixRoomReceiverConverter.getEventOrigin(externalEvent.sender, homeServerDomain), leave: externalEvent.content?.membership === AddMemberToRoomMembership.LEAVE, }); } @@ -68,6 +69,7 @@ export class MatrixRoomReceiverConverter { public static toRoomChangeNameDto(externalEvent: IMatrixEvent): FederationRoomChangeNameDto { return Object.assign(new FederationRoomChangeNameDto(), { ...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id), + externalSenderId: externalEvent.sender, normalizedRoomName: MatrixRoomReceiverConverter.normalizeRoomNameToRCFormat(externalEvent.content?.name), }); } @@ -75,61 +77,62 @@ export class MatrixRoomReceiverConverter { public static toRoomChangeTopicDto(externalEvent: IMatrixEvent): FederationRoomChangeTopicDto { return Object.assign(new FederationRoomChangeTopicDto(), { ...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id), + externalSenderId: externalEvent.sender, roomTopic: externalEvent.content?.topic, }); } - private static convertMatrixUserIdFormatToRCFormat(matrixUserId = ''): string { + private static normalizeRoomNameToRCFormat(matrixRoomName = ''): string { + return matrixRoomName.replace('@', ''); + } + + protected static convertMatrixUserIdFormatToRCFormat(matrixUserId = ''): string { return matrixUserId.replace('@', ''); } - private static convertMatrixRoomIdFormatToRCFormat(matrixRoomId = ''): string { + protected static convertMatrixRoomIdFormatToRCFormat(matrixRoomId = ''): string { const prefixedRoomIdOnly = matrixRoomId.split(':')[0]; const prefix = '!'; return prefixedRoomIdOnly?.replace(prefix, ''); } - private static normalizeRoomNameToRCFormat(matrixRoomName = ''): string { - return matrixRoomName.replace('@', ''); - } - - private static formatMatrixUserIdToRCUsernameFormat(matrixUserId = ''): string { + protected static formatMatrixUserIdToRCUsernameFormat(matrixUserId = ''): string { return matrixUserId.split(':')[0]?.replace('@', ''); } - private static getEventOrigin(inviterId = '', inviteeId = ''): EVENT_ORIGIN { - const fromADifferentServer = - MatrixRoomReceiverConverter.extractServerNameFromMatrixUserId(inviterId) !== - MatrixRoomReceiverConverter.extractServerNameFromMatrixUserId(inviteeId); + protected static getEventOrigin(inviterId = '', homeServerDomain: string): EVENT_ORIGIN { + const fromADifferentServer = MatrixRoomReceiverConverter.extractServerNameFromMatrixUserId(inviterId) !== homeServerDomain; return fromADifferentServer ? EVENT_ORIGIN.REMOTE : EVENT_ORIGIN.LOCAL; } - private static extractServerNameFromMatrixUserId(matrixUserId = ''): string { + protected static extractServerNameFromMatrixUserId(matrixUserId = ''): string { const splitted = matrixUserId.split(':'); return splitted.length > 1 ? splitted[1] : ''; } - private static getBasicRoomsFields(externalRoomId: string): Record { + protected static getBasicRoomsFields(externalRoomId: string): Record { return { externalRoomId, normalizedRoomId: MatrixRoomReceiverConverter.convertMatrixRoomIdFormatToRCFormat(externalRoomId), }; } - private static convertMatrixJoinRuleToRCRoomType(matrixJoinRule: RoomJoinRules, matrixRoomIsDirect = false): RoomType { + protected static convertMatrixJoinRuleToRCRoomType(matrixJoinRule: RoomJoinRules, matrixRoomIsDirect = false): RoomType { + if (matrixRoomIsDirect) { + return RoomType.DIRECT_MESSAGE; + } const mapping: Record = { [RoomJoinRules.JOIN]: RoomType.CHANNEL, [RoomJoinRules.INVITE]: RoomType.PRIVATE_GROUP, }; - const roomType = mapping[matrixJoinRule] || RoomType.CHANNEL; - return roomType === RoomType.PRIVATE_GROUP && matrixRoomIsDirect ? RoomType.DIRECT_MESSAGE : roomType; + return mapping[matrixJoinRule] || RoomType.CHANNEL; } - private static tryToGetExternalInfoFromTheRoomState( + protected static tryToGetExternalInfoFromTheRoomState( roomState: Record[] = [], matrixRoomIsDirect = false, ): Record { diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/IMatrixEventContent/index.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/IMatrixEventContent/index.ts index 3c1d5b52f076..e260e62ac5eb 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/IMatrixEventContent/index.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/IMatrixEventContent/index.ts @@ -9,8 +9,8 @@ import { IMatrixEventContentSetRoomTopic } from './IMatrixEventContentSetRoomTop export type EventContent = { [MatrixEventType.ROOM_CREATED]: IMatrixEventContentCreateRoom; [MatrixEventType.ROOM_MEMBERSHIP_CHANGED]: IMatrixEventContentAddMemberToRoom; + [MatrixEventType.ROOM_MESSAGE_SENT]: IMatrixEventContentSendMessage; [MatrixEventType.ROOM_JOIN_RULES_CHANGED]: IMatrixEventContentSetRoomJoinRules; [MatrixEventType.ROOM_NAME_CHANGED]: IMatrixEventContentSetRoomName; [MatrixEventType.ROOM_TOPIC_CHANGED]: IMatrixEventContentSetRoomTopic; - [MatrixEventType.ROOM_MESSAGE_SENT]: IMatrixEventContentSendMessage; }; diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixEventType.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixEventType.ts index bb58a0d71825..fd0fa4b21924 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixEventType.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixEventType.ts @@ -1,12 +1,12 @@ export enum MatrixEventType { ROOM_CREATED = 'm.room.create', ROOM_MEMBERSHIP_CHANGED = 'm.room.member', + ROOM_MESSAGE_SENT = 'm.room.message', + ROOM_JOIN_RULES_CHANGED = 'm.room.join_rules', + ROOM_NAME_CHANGED = 'm.room.name', // SET_ROOM_POWER_LEVELS = 'm.room.power_levels', // SET_ROOM_CANONICAL_ALIAS = 'm.room.canonical_alias', - ROOM_JOIN_RULES_CHANGED = 'm.room.join_rules', // SET_ROOM_HISTORY_VISIBILITY = 'm.room.history_visibility', // SET_ROOM_GUEST_ACCESS = 'm.room.guest_access', - ROOM_NAME_CHANGED = 'm.room.name', ROOM_TOPIC_CHANGED = 'm.room.topic', - ROOM_MESSAGE_SENT = 'm.room.message', } diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomType.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomType.ts new file mode 100644 index 000000000000..7c591bbe590d --- /dev/null +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomType.ts @@ -0,0 +1,4 @@ +export enum MatrixRoomType { + PRIVATE = 'private_chat', + PUBLIC = 'public_chat', +} diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomVisibility.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomVisibility.ts new file mode 100644 index 000000000000..0256caac480c --- /dev/null +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/definitions/MatrixRoomVisibility.ts @@ -0,0 +1,4 @@ +export enum MatrixRoomVisibility { + PRIVATE = 'private', + PUBLIC = 'public ', +} diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/BaseEvent.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/BaseEvent.ts index 25e179bfae7e..5e6b916eef50 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/BaseEvent.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/BaseEvent.ts @@ -2,7 +2,7 @@ import { IMatrixEvent } from '../definitions/IMatrixEvent'; import { MatrixEventType } from '../definitions/MatrixEventType'; export abstract class MatrixBaseEventHandler { - private type: T; + protected type: T; public abstract handle(externalEvent: IMatrixEvent): Promise; diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/Room.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/Room.ts index 47d10be8fb73..0c7460d5a251 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/Room.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/Room.ts @@ -1,4 +1,5 @@ import { FederationRoomServiceReceiver } from '../../../application/RoomServiceReceiver'; +import { RocketChatSettingsAdapter } from '../../rocket-chat/adapters/Settings'; import { MatrixRoomReceiverConverter } from '../converters/RoomReceiver'; import { IMatrixEvent } from '../definitions/IMatrixEvent'; import { MatrixEventType } from '../definitions/MatrixEventType'; @@ -15,12 +16,24 @@ export class MatrixRoomCreatedHandler extends MatrixBaseEventHandler { - constructor(private roomService: FederationRoomServiceReceiver) { + constructor(private roomService: FederationRoomServiceReceiver, private rocketSettingsAdapter: RocketChatSettingsAdapter) { super(MatrixEventType.ROOM_MEMBERSHIP_CHANGED); } public async handle(externalEvent: IMatrixEvent): Promise { - await this.roomService.changeRoomMembership(MatrixRoomReceiverConverter.toChangeRoomMembershipDto(externalEvent)); + await this.roomService.changeRoomMembership( + MatrixRoomReceiverConverter.toChangeRoomMembershipDto(externalEvent, this.rocketSettingsAdapter.getHomeServerDomain()), + ); + } +} + +export class MatrixRoomMessageSentHandler extends MatrixBaseEventHandler { + constructor(private roomService: FederationRoomServiceReceiver) { + super(MatrixEventType.ROOM_MESSAGE_SENT); + } + + public async handle(externalEvent: IMatrixEvent): Promise { + await this.roomService.receiveExternalMessage(MatrixRoomReceiverConverter.toSendRoomMessageDto(externalEvent)); } } @@ -53,13 +66,3 @@ export class MatrixRoomTopicChangedHandler extends MatrixBaseEventHandler { - constructor(private roomService: FederationRoomServiceReceiver) { - super(MatrixEventType.ROOM_MESSAGE_SENT); - } - - public async handle(externalEvent: IMatrixEvent): Promise { - await this.roomService.receiveExternalMessage(MatrixRoomReceiverConverter.toSendRoomMessageDto(externalEvent)); - } -} diff --git a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/index.ts b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/index.ts index 67b361681492..5394f96e0282 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/index.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/matrix/handlers/index.ts @@ -4,7 +4,7 @@ import { MatrixBaseEventHandler } from './BaseEvent'; export class MatrixEventsHandler { // eslint-disable-next-line no-empty-function - constructor(private handlers: MatrixBaseEventHandler[]) {} + constructor(protected handlers: MatrixBaseEventHandler[]) {} public async handleEvent(event: IMatrixEvent): Promise { const handler = this.handlers.find((handler) => handler.equals(event.type)); diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/Federation.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/Federation.ts new file mode 100644 index 000000000000..a3b3b0b3bf13 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/Federation.ts @@ -0,0 +1,27 @@ +import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; +import { IRoom, ValueOf } from '@rocket.chat/core-typings'; + +import { RoomMemberActions } from '../../../../../definition/IRoomTypeConfig'; + +const allowedActionsInFederatedRooms: ValueOf[] = [ + RoomMemberActions.REMOVE_USER, + RoomMemberActions.INVITE, + RoomMemberActions.JOIN, + RoomMemberActions.LEAVE, +]; + +export class Federation { + public static isAFederatedRoom(room: IRoom): boolean { + return room.federated === true; + } + + public static actionAllowed(room: IRoom, action: ValueOf): boolean { + return room.t === RoomType.DIRECT_MESSAGE && action === RoomMemberActions.REMOVE_USER + ? false + : allowedActionsInFederatedRooms.includes(action); + } + + public static isAFederatedUsername(username: string): boolean { + return username.includes('@') && username.includes(':'); + } +} diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Room.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Room.ts index 44ec3ee5e5ad..53269fb090a8 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Room.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Room.ts @@ -1,10 +1,12 @@ import { ICreatedRoom, IRoom } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions as SubscriptionsRaw } from '@rocket.chat/models'; -import { MatrixBridgedRoom } from '../../../../../models/server'; +import { MatrixBridgedRoom, Subscriptions } from '../../../../../models/server'; import { FederatedRoom } from '../../../domain/FederatedRoom'; import { createRoom, addUserToRoom, removeUserFromRoom } from '../../../../../lib/server'; -import { Rooms, Subscriptions } from '../../../../../models/server/raw'; import { FederatedUser } from '../../../domain/FederatedUser'; +import { saveRoomName } from '../../../../../channel-settings/server/functions/saveRoomName'; +import { saveRoomTopic } from '../../../../../channel-settings/server/functions/saveRoomTopic'; export class RocketChatRoomAdapter { public async getFederatedRoomByExternalId(externalRoomId: string): Promise { @@ -13,8 +15,9 @@ export class RocketChatRoomAdapter { return; } const room = await Rooms.findOneById(internalBridgedRoomId); - - return this.createFederatedRoomInstance(externalRoomId, room); + if (room) { + return this.createFederatedRoomInstance(externalRoomId, room); + } } public async getFederatedRoomByInternalId(internalRoomId: string): Promise { @@ -24,10 +27,12 @@ export class RocketChatRoomAdapter { } const room = await Rooms.findOneById(internalRoomId); - return this.createFederatedRoomInstance(externalRoomId, room); + if (room) { + return this.createFederatedRoomInstance(externalRoomId, room); + } } - public async getInternalRoomById(internalRoomId: string): Promise { + public async getInternalRoomById(internalRoomId: string): Promise { return Rooms.findOneById(internalRoomId); } @@ -38,15 +43,35 @@ export class RocketChatRoomAdapter { federatedRoom.internalReference.name, federatedRoom.internalReference.u.username as string, members, + false, + undefined, + { creator: members[0]?._id as string }, ) as ICreatedRoom; const roomId = rid || _id; - MatrixBridgedRoom.insert({ rid: roomId, mri: federatedRoom.externalId }); + MatrixBridgedRoom.upsert({ rid: roomId }, { rid: roomId, mri: federatedRoom.externalId }); await Rooms.setAsFederated(roomId); } - public async updateFederatedRoomByInternalRoomId(internalRoomId: string, federatedRoom: FederatedRoom): Promise { - MatrixBridgedRoom.upsert({ rid: internalRoomId }, { rid: internalRoomId, mri: federatedRoom.externalId }); - await Rooms.setAsFederated(internalRoomId); + public async removeDirectMessageRoom(federatedRoom: FederatedRoom): Promise { + const roomId = federatedRoom.internalReference._id; + await Rooms.removeById(roomId); + await Subscriptions.removeByRoomId(roomId); + await MatrixBridgedRoom.remove({ rid: roomId }); + } + + public async createFederatedRoomForDirectMessage(federatedRoom: FederatedRoom, membersUsernames: string[]): Promise { + const { rid, _id } = createRoom( + federatedRoom.internalReference.t, + federatedRoom.internalReference.name, + federatedRoom.internalReference.u.username as string, + membersUsernames, + false, + undefined, + { creator: federatedRoom.internalReference.u._id }, + ) as ICreatedRoom; + const roomId = rid || _id; + MatrixBridgedRoom.upsert({ rid: roomId }, { rid: roomId, mri: federatedRoom.externalId }); + await Rooms.setAsFederated(roomId); } public async addUserToRoom(federatedRoom: FederatedRoom, inviteeUser: FederatedUser, inviterUser?: FederatedUser): Promise { @@ -65,39 +90,30 @@ export class RocketChatRoomAdapter { ); } - public async updateRoomType(federatedRoom: FederatedRoom): Promise { - await Rooms.update({ _id: federatedRoom.internalReference._id }, { $set: { t: federatedRoom.internalReference.t } }); - await Subscriptions.update( - { rid: federatedRoom.internalReference._id }, - { $set: { t: federatedRoom.internalReference.t } }, - { multi: true }, - ); + private createFederatedRoomInstance(externalRoomId: string, room: IRoom): FederatedRoom { + const federatedRoom = FederatedRoom.build(); + federatedRoom.externalId = externalRoomId; + federatedRoom.internalReference = room; + + return federatedRoom; } - public async updateRoomName(federatedRoom: FederatedRoom): Promise { - await Rooms.update( - { _id: federatedRoom.internalReference._id }, - { $set: { name: federatedRoom.internalReference.name, fname: federatedRoom.internalReference.fname } }, - ); - await Subscriptions.update( - { rid: federatedRoom.internalReference._id }, - { $set: { name: federatedRoom.internalReference.name, fname: federatedRoom.internalReference.fname } }, - { multi: true }, - ); + public async isUserAlreadyJoined(internalRoomId: string, internalUserId: string): Promise { + const subscription = await Subscriptions.findOneByRoomIdAndUserId(internalRoomId, internalUserId, { projection: { _id: 1 } }); + + return Boolean(subscription); } - public async updateRoomTopic(federatedRoom: FederatedRoom): Promise { - await Rooms.update( - { _id: federatedRoom.internalReference._id }, - { $set: { description: federatedRoom.internalReference.description } }, - ); + public async updateRoomType(federatedRoom: FederatedRoom): Promise { + await Rooms.setRoomTypeById(federatedRoom.internalReference._id, federatedRoom.internalReference.t); + await SubscriptionsRaw.updateAllRoomTypesByRoomId(federatedRoom.internalReference._id, federatedRoom.internalReference.t); } - private createFederatedRoomInstance(externalRoomId: string, room: IRoom): FederatedRoom { - const federatedRoom = FederatedRoom.build(); - federatedRoom.externalId = externalRoomId; - federatedRoom.internalReference = room; + public async updateRoomName(federatedRoom: FederatedRoom, federatedUser: FederatedUser): Promise { + await saveRoomName(federatedRoom.internalReference._id, federatedRoom.internalReference.name, federatedUser.internalReference); + } - return federatedRoom; + public async updateRoomTopic(federatedRoom: FederatedRoom, federatedUser: FederatedUser): Promise { + await saveRoomTopic(federatedRoom.internalReference._id, federatedRoom.internalReference.topic, federatedUser.internalReference); } } diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Settings.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Settings.ts index 71ff9af73461..d70e52407510 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Settings.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/Settings.ts @@ -1,7 +1,8 @@ import yaml from 'js-yaml'; import { SHA256 } from 'meteor/sha'; +import { v4 as uuidv4 } from 'uuid'; +import { Settings } from '@rocket.chat/models'; -import { Settings } from '../../../../../models/server/raw'; import { settings, settingsRegistry } from '../../../../../settings/server'; const EVERYTHING_REGEX = '.*'; @@ -32,7 +33,8 @@ export class RocketChatSettingsAdapter { public getBridgePort(): number { const [, , port] = this.getBridgeUrl().split(':'); - return parseInt(port); + // The port should be 3300 if none is specified on the URL + return parseInt(port || '3300'); } public getHomeServerUrl(): string { @@ -51,8 +53,12 @@ export class RocketChatSettingsAdapter { await Settings.updateValueById('Federation_Matrix_enabled', false); } - public onFederationEnabledStatusChanged(callback: Function): void { - settings.watchMultiple( + public isFederationEnabled(): boolean { + return settings.get('Federation_Matrix_enabled') === true; + } + + public onFederationEnabledStatusChanged(callback: Function): Function { + return settings.watchMultiple( [ 'Federation_Matrix_enabled', 'Federation_Matrix_id', @@ -68,7 +74,6 @@ export class RocketChatSettingsAdapter { } public generateRegistrationFileObject(): Record { - /* eslint-disable @typescript-eslint/camelcase */ return { id: this.getApplicationServiceId(), hs_token: this.getApplicationHomeServerToken(), @@ -96,7 +101,6 @@ export class RocketChatSettingsAdapter { ], }, }; - /* eslint-enable @typescript-eslint/camelcase */ } private async updateRegistrationFile(): Promise { @@ -127,9 +131,10 @@ export class RocketChatSettingsAdapter { i18nLabel: 'Federation_Matrix_enabled', i18nDescription: 'Federation_Matrix_enabled_desc', alert: 'Federation_Matrix_Enabled_Alert', + public: true, }); - const uniqueId = settings.get('uniqueID'); + const uniqueId = settings.get('uniqueID') || uuidv4().slice(0, 15).replace(new RegExp('-', 'g'), '_'); const hsToken = SHA256(`hs_${uniqueId}`); const asToken = SHA256(`as_${uniqueId}`); diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/User.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/User.ts index a2f2c26f8e6c..4dd7ada4bad0 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/User.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/adapters/User.ts @@ -1,6 +1,7 @@ import { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; -import { MatrixBridgedUser, Users } from '../../../../../models/server'; +import { MatrixBridgedUser } from '../../../../../models/server'; import { FederatedUser } from '../../../domain/FederatedUser'; export class RocketChatUserAdapter { @@ -12,7 +13,9 @@ export class RocketChatUserAdapter { const user = await Users.findOneById(internalBridgedUserId); - return this.createFederatedUserInstance(externalUserId, user); + if (user) { + return this.createFederatedUserInstance(externalUserId, user); + } } public async getFederatedUserByInternalId(internalUserId: string): Promise { @@ -20,10 +23,12 @@ export class RocketChatUserAdapter { if (!internalBridgedUserId) { return; } - const { uid: userId, mui: externalUserId } = internalBridgedUserId; + const { uid: userId, mui: externalUserId, remote } = internalBridgedUserId; const user = await Users.findOneById(userId); - return this.createFederatedUserInstance(externalUserId, user); + if (user) { + return this.createFederatedUserInstance(externalUserId, user, remote); + } } public async getFederatedUserByInternalUsername(username: string): Promise { @@ -35,17 +40,17 @@ export class RocketChatUserAdapter { if (!internalBridgedUserId) { return; } - const { mui: externalUserId } = internalBridgedUserId; + const { mui: externalUserId, remote } = internalBridgedUserId; - return this.createFederatedUserInstance(externalUserId, user); + return this.createFederatedUserInstance(externalUserId, user, remote); } - public async getInternalUserById(userId: string): Promise { + public async getInternalUserById(userId: string): Promise { return Users.findOneById(userId); } public async createFederatedUser(federatedUser: FederatedUser): Promise { - const existingLocalUser = await Users.findOneByUsername(federatedUser.internalReference.username); + const existingLocalUser = await Users.findOneByUsername(federatedUser.internalReference.username || ''); if (existingLocalUser) { return MatrixBridgedUser.upsert( { uid: existingLocalUser._id }, @@ -56,7 +61,7 @@ export class RocketChatUserAdapter { }, ); } - const newLocalUserId = await Users.create({ + const { insertedId } = await Users.insertOne({ username: federatedUser.internalReference.username, type: federatedUser.internalReference.type, status: federatedUser.internalReference.status, @@ -64,21 +69,24 @@ export class RocketChatUserAdapter { roles: federatedUser.internalReference.roles, name: federatedUser.internalReference.name, requirePasswordChange: federatedUser.internalReference.requirePasswordChange, + createdAt: new Date(), + federated: true, }); MatrixBridgedUser.upsert( - { uid: newLocalUserId }, + { uid: insertedId }, { - uid: newLocalUserId, + uid: insertedId, mui: federatedUser.externalId, remote: !federatedUser.existsOnlyOnProxyServer, }, ); } - private createFederatedUserInstance(externalUserId: string, user: IUser): FederatedUser { + private createFederatedUserInstance(externalUserId: string, user: IUser, remote = true): FederatedUser { const federatedUser = FederatedUser.build(); federatedUser.externalId = externalUserId; federatedUser.internalReference = user; + federatedUser.existsOnlyOnProxyServer = !remote; return federatedUser; } diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/converters/RoomSender.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/converters/RoomSender.ts index 76d6937f206b..e6a25fff9e1d 100644 --- a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/converters/RoomSender.ts +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/converters/RoomSender.ts @@ -1,17 +1,21 @@ import { IMessage } from '@rocket.chat/core-typings'; -import { FederationRoomInviteUserDto, FederationRoomSendExternalMessageDto } from '../../../application/input/RoomSenderDto'; +import { + FederationAfterLeaveRoomDto, + FederationCreateDMAndInviteUserDto, + FederationRoomSendExternalMessageDto, +} from '../../../application/input/RoomSenderDto'; export class FederationRoomSenderConverter { - public static toRoomInviteUserDto( + public static toCreateDirectMessageRoomDto( internalInviterId: string, internalRoomId: string, externalInviteeId: string, - ): FederationRoomInviteUserDto { + ): FederationCreateDMAndInviteUserDto { const normalizedInviteeId = externalInviteeId.replace('@', ''); const inviteeUsernameOnly = externalInviteeId.split(':')[0]?.replace('@', ''); - return Object.assign(new FederationRoomInviteUserDto(), { + return Object.assign(new FederationCreateDMAndInviteUserDto(), { internalInviterId, internalRoomId, rawInviteeId: externalInviteeId, @@ -31,4 +35,16 @@ export class FederationRoomSenderConverter { message, }); } + + public static toAfterLeaveRoom( + internalUserId: string, + internalRoomId: string, + whoRemovedInternalId?: string, + ): FederationAfterLeaveRoomDto { + return Object.assign(new FederationAfterLeaveRoomDto(), { + internalRoomId, + internalUserId, + whoRemovedInternalId, + }); + } } diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/hooks/index.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/hooks/index.ts new file mode 100644 index 000000000000..d7e75c817adc --- /dev/null +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/hooks/index.ts @@ -0,0 +1,71 @@ +import { IRoom, IUser } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../../../../lib/callbacks'; + +export class FederationHooks { + public static afterLeaveRoom(callback: Function): void { + callbacks.add( + 'afterLeaveRoom', + (user: IUser, room: IRoom | undefined): void => { + if (!room?.federated) { + return; + } + Promise.await(callback(user, room)); + }, + callbacks.priority.HIGH, + 'federation-v2-after-leave-room', + ); + } + + public static afterRemoveFromRoom(callback: Function): void { + callbacks.add( + 'afterRemoveFromRoom', + (params: { removedUser: IUser; userWhoRemoved: IUser }, room: IRoom | undefined): void => { + if (!room?.federated) { + return; + } + Promise.await(callback(params.removedUser, room, params.userWhoRemoved)); + }, + callbacks.priority.HIGH, + 'federation-v2-after-remove-from-room', + ); + } + + public static canAddTheUserToTheRoom(callback: Function): void { + callbacks.add( + 'federation.beforeAddUserAToRoom', + (params: { user: IUser | string }, room: IRoom): void => { + Promise.await(callback(params.user, room)); + }, + callbacks.priority.HIGH, + 'federation-v2-can-add-user-to-the-room', + ); + } + + public static canAddUsersToTheRoom(callback: Function): void { + callbacks.add( + 'federation.beforeAddUserAToRoom', + (params: { user: IUser | string; inviter: IUser }, room: IRoom): void => { + Promise.await(callback(params.user, params.inviter, room)); + }, + callbacks.priority.HIGH, + 'federation-v2-can-add-users-to-the-room', + ); + } + + public static beforeCreateDirectMessage(callback: Function): void { + callbacks.add( + 'federation.beforeCreateDirectMessage', + (members: IUser[]): void => { + Promise.await(callback(members)); + }, + callbacks.priority.HIGH, + 'federation-v2-before-create-direct-message-ce', + ); + } + + public static removeCEValidation(): void { + callbacks.remove('federation.beforeAddUserAToRoom', 'federation-v2-can-add-users-to-the-room'); + callbacks.remove('federation.beforeCreateDirectMessage', 'federation-v2-before-create-direct-message-ce'); + } +} diff --git a/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/slash-commands/index.ts b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/slash-commands/index.ts new file mode 100644 index 000000000000..2aa502c3ee88 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/infrastructure/rocket-chat/slash-commands/index.ts @@ -0,0 +1,62 @@ +import { Meteor } from 'meteor/meteor'; +import { Users } from '@rocket.chat/models'; + +import { federationRoomServiceSender } from '../../..'; +import { FederationRoomSenderConverter } from '../converters/RoomSender'; +import { slashCommands } from '../../../../../utils/lib/slashCommand'; + +const FEDERATION_COMMANDS: Record = { + dm: async (currentUserId: string, roomId: string, invitee: string) => + federationRoomServiceSender.createDirectMessageRoomAndInviteUser( + FederationRoomSenderConverter.toCreateDirectMessageRoomDto(currentUserId, roomId, invitee), + ), +}; + +export const normalizeUserId = (rawUserId: string): string => `@${rawUserId.replace('@', '')}`; + +const validateUserIdFormat = async (rawUserId: string, inviterId: string): Promise => { + const inviter = await Users.findOneById(inviterId); + const isInviterExternal = inviter?.federated === true || inviter?.username?.includes(':'); + if (!rawUserId.includes(':') && !isInviterExternal) { + throw new Error('Invalid userId format for federation command.'); + } +}; + +const executeSlashCommand = async ( + providedCommand: string, + stringParams: string | undefined, + item: Record, + commands: Record, +): Promise => { + if (providedCommand !== 'federation' || !stringParams) { + return; + } + + const [command, ...params] = stringParams.trim().split(' '); + const [rawUserId] = params; + const currentUserId = Meteor.userId(); + if (!currentUserId || !commands[command]) { + return; + } + + await validateUserIdFormat(rawUserId, currentUserId); + + const invitee = normalizeUserId(rawUserId); + + const { rid: roomId } = item; + + await commands[command](currentUserId, roomId, invitee); +}; + +function federation(providedCommand: string, stringParams: string | undefined, item: Record): void { + Promise.await(executeSlashCommand(providedCommand, stringParams, item, FEDERATION_COMMANDS)); +} + +slashCommands.add({ + command: 'federation', + callback: federation, + options: { + description: 'Federation_slash_commands', + params: '#command (dm) #user', + }, +}); diff --git a/apps/meteor/app/federation/server/endpoints/dispatch.js b/apps/meteor/app/federation/server/endpoints/dispatch.js index 3c841ca577a6..3e1f478ad39d 100644 --- a/apps/meteor/app/federation/server/endpoints/dispatch.js +++ b/apps/meteor/app/federation/server/endpoints/dispatch.js @@ -1,10 +1,10 @@ import { EJSON } from 'meteor/ejson'; +import { FederationServers } from '@rocket.chat/models'; import { API } from '../../../api/server'; import { serverLogger } from '../lib/logger'; import { contextDefinitions, eventTypes } from '../../../models/server/models/FederationEvents'; import { FederationRoomEvents, Messages, Rooms, Subscriptions, Users } from '../../../models/server'; -import { FederationServers } from '../../../models/server/raw'; import { normalizers } from '../normalizers'; import { deleteRoom } from '../../../lib/server/functions'; import { api } from '../../../../server/sdk/api'; diff --git a/apps/meteor/app/federation/server/endpoints/uploads.js b/apps/meteor/app/federation/server/endpoints/uploads.js index 146ac27517a4..2edbbbdb2e62 100644 --- a/apps/meteor/app/federation/server/endpoints/uploads.js +++ b/apps/meteor/app/federation/server/endpoints/uploads.js @@ -1,5 +1,6 @@ +import { Uploads } from '@rocket.chat/models'; + import { API } from '../../../api/server'; -import { Uploads } from '../../../models/server/raw'; import { FileUpload } from '../../../file-upload/server'; import { isFederationEnabled } from '../lib/isFederationEnabled'; diff --git a/apps/meteor/app/federation/server/functions/addUser.js b/apps/meteor/app/federation/server/functions/addUser.js index 314b7893fbc1..866185354d6a 100644 --- a/apps/meteor/app/federation/server/functions/addUser.js +++ b/apps/meteor/app/federation/server/functions/addUser.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { FederationServers } from '@rocket.chat/models'; import * as federationErrors from './errors'; import { Users } from '../../../models/server'; -import { FederationServers } from '../../../models/server/raw'; import { getUserByUsername } from '../handler'; export async function addUser(query) { diff --git a/apps/meteor/app/federation/server/functions/dashboard.js b/apps/meteor/app/federation/server/functions/dashboard.js index 3868d98d821b..149e2215f73f 100644 --- a/apps/meteor/app/federation/server/functions/dashboard.js +++ b/apps/meteor/app/federation/server/functions/dashboard.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { FederationServers } from '@rocket.chat/models'; import { FederationRoomEvents, Users } from '../../../models/server'; -import { FederationServers } from '../../../models/server/raw'; export async function getStatistics() { const numberOfEvents = FederationRoomEvents.find().count(); diff --git a/apps/meteor/app/federation/server/functions/helpers.ts b/apps/meteor/app/federation/server/functions/helpers.ts index 5d76fa4cd363..494934b6a18d 100644 --- a/apps/meteor/app/federation/server/functions/helpers.ts +++ b/apps/meteor/app/federation/server/functions/helpers.ts @@ -1,8 +1,8 @@ import { IRoom, isDirectMessageRoom } from '@rocket.chat/core-typings'; import type { ISubscription, IRegisterUser, IUser } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { Subscriptions, Users } from '../../../models/server'; -import { Settings } from '../../../models/server/raw'; import { STATUS_ENABLED, STATUS_REGISTERING } from '../constants'; export const getNameAndDomain = (fullyQualifiedName: string): string[] => fullyQualifiedName.split('@'); diff --git a/apps/meteor/app/federation/server/lib/crypt.js b/apps/meteor/app/federation/server/lib/crypt.js index 5a7685a2e9e0..4abe78248a8b 100644 --- a/apps/meteor/app/federation/server/lib/crypt.js +++ b/apps/meteor/app/federation/server/lib/crypt.js @@ -1,4 +1,5 @@ -import { FederationKeys } from '../../../models/server/raw'; +import { FederationKeys } from '@rocket.chat/models'; + import { getFederationDomain } from './getFederationDomain'; import { search } from './dns'; import { cryptLogger } from './logger'; diff --git a/apps/meteor/app/federation/server/startup/generateKeys.js b/apps/meteor/app/federation/server/startup/generateKeys.js index 5d5bbba64e00..da372335a662 100644 --- a/apps/meteor/app/federation/server/startup/generateKeys.js +++ b/apps/meteor/app/federation/server/startup/generateKeys.js @@ -1,4 +1,4 @@ -import { FederationKeys } from '../../../models/server/raw'; +import { FederationKeys } from '@rocket.chat/models'; // Create key pair if needed (async () => { diff --git a/apps/meteor/app/federation/server/startup/settings.ts b/apps/meteor/app/federation/server/startup/settings.ts index 04a9c6a49add..ee216054db04 100644 --- a/apps/meteor/app/federation/server/startup/settings.ts +++ b/apps/meteor/app/federation/server/startup/settings.ts @@ -1,3 +1,5 @@ +import { FederationKeys } from '@rocket.chat/models'; + import { settingsRegistry, settings } from '../../../settings/server'; import { updateStatus, updateEnabled, isRegisteringOrEnabled } from '../functions/helpers'; import { getFederationDomain } from '../lib/getFederationDomain'; @@ -5,7 +7,6 @@ import { getFederationDiscoveryMethod } from '../lib/getFederationDiscoveryMetho import { registerWithHub } from '../lib/dns'; import { enableCallbacks, disableCallbacks } from '../lib/callbacks'; import { setupLogger } from '../lib/logger'; -import { FederationKeys } from '../../../models/server/raw'; import { STATUS_ENABLED, STATUS_REGISTERING, STATUS_ERROR_REGISTERING, STATUS_DISABLED } from '../constants'; settingsRegistry.addGroup('Federation', function () { diff --git a/apps/meteor/app/file-upload/client/lib/fileUploadHandler.js b/apps/meteor/app/file-upload/client/lib/fileUploadHandler.js index 619cc70cb0aa..2764be6781dc 100644 --- a/apps/meteor/app/file-upload/client/lib/fileUploadHandler.js +++ b/apps/meteor/app/file-upload/client/lib/fileUploadHandler.js @@ -4,7 +4,7 @@ import { Tracker } from 'meteor/tracker'; import { UploadFS } from 'meteor/jalik:ufs'; import { FileUploadBase } from '../../lib/FileUploadBase'; -import { Uploads, Avatars } from '../../../models'; +import { Uploads, Avatars } from '../../../models/client'; new UploadFS.Store({ collection: Uploads.model, diff --git a/apps/meteor/app/file-upload/server/config/AmazonS3.js b/apps/meteor/app/file-upload/server/config/AmazonS3.js index 04f27c099da9..e265311786d6 100644 --- a/apps/meteor/app/file-upload/server/config/AmazonS3.js +++ b/apps/meteor/app/file-upload/server/config/AmazonS3.js @@ -3,7 +3,7 @@ import https from 'https'; import _ from 'underscore'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import '../../ufs/AmazonS3/server.js'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/file-upload/server/config/GoogleStorage.js b/apps/meteor/app/file-upload/server/config/GoogleStorage.js index f3f0de3e29d8..ce900d9811aa 100644 --- a/apps/meteor/app/file-upload/server/config/GoogleStorage.js +++ b/apps/meteor/app/file-upload/server/config/GoogleStorage.js @@ -4,7 +4,7 @@ import https from 'https'; import _ from 'underscore'; import { FileUploadClass, FileUpload } from '../lib/FileUpload'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import '../../ufs/GoogleStorage/server.js'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/file-upload/server/config/Webdav.js b/apps/meteor/app/file-upload/server/config/Webdav.js index f3fe904ccc56..7ca78cad5da6 100644 --- a/apps/meteor/app/file-upload/server/config/Webdav.js +++ b/apps/meteor/app/file-upload/server/config/Webdav.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import { FileUploadClass, FileUpload } from '../lib/FileUpload'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import '../../ufs/Webdav/server.js'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.js b/apps/meteor/app/file-upload/server/lib/FileUpload.js index 5bba041a6f2d..7d7afd4c8dc0 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.js +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.js @@ -12,9 +12,9 @@ import { Match } from 'meteor/check'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import filesize from 'filesize'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; +import { Avatars, UserDataFiles, Uploads } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { Avatars, UserDataFiles, Uploads } from '../../../models/server/raw'; import Users from '../../../models/server/models/Users'; import Rooms from '../../../models/server/models/Rooms'; import Settings from '../../../models/server/models/Settings'; diff --git a/apps/meteor/app/file-upload/server/lib/requests.js b/apps/meteor/app/file-upload/server/lib/requests.js index e4d8bb2e36b4..87f934f4a4f3 100644 --- a/apps/meteor/app/file-upload/server/lib/requests.js +++ b/apps/meteor/app/file-upload/server/lib/requests.js @@ -1,7 +1,7 @@ import { WebApp } from 'meteor/webapp'; +import { Uploads } from '@rocket.chat/models'; import { FileUpload } from './FileUpload'; -import { Uploads } from '../../../models/server/raw'; WebApp.connectHandlers.use(FileUpload.getPath(), async function (req, res, next) { const match = /^\/([^\/]+)\/(.*)/.exec(req.url); diff --git a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.js b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.js index c2a05725ec72..6a538caee41c 100644 --- a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.js +++ b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.js @@ -1,8 +1,10 @@ import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; import { UploadFS } from 'meteor/jalik:ufs'; +import { Uploads } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { Uploads } from '../../../models/server/raw'; +import { canAccessRoom } from '../../../authorization/server'; let protectedFiles; @@ -12,10 +14,14 @@ settings.watch('FileUpload_ProtectFiles', function (value) { Meteor.methods({ async getS3FileUrl(fileId) { + check(fileId, String); if (protectedFiles && !Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' }); } const file = await Uploads.findOneById(fileId); + if (!file.rid || !canAccessRoom({ _id: file.rid }, { _id: this.userId })) { + throw new Meteor.Error('error-not-allowed', 'Not allowed'); + } return UploadFS.getStore('AmazonS3:Uploads').getRedirectURL(file); }, diff --git a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts index 9bf8c0ea6129..766b598955d7 100644 --- a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts +++ b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts @@ -1,11 +1,10 @@ -/* eslint-disable @typescript-eslint/camelcase */ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import _ from 'underscore'; import { MessageAttachment, FileAttachmentProps } from '@rocket.chat/core-typings'; import type { IUser } from '@rocket.chat/core-typings'; +import { Rooms, Uploads } from '@rocket.chat/models'; -import { Rooms, Uploads } from '../../../models/server/raw'; import { callbacks } from '../../../../lib/callbacks'; import { FileUpload } from '../lib/FileUpload'; import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; @@ -21,6 +20,9 @@ Meteor.methods({ } const room = await Rooms.findOneById(roomId); + if (!room) { + return false; + } if (user?.type !== 'app' && !canAccessRoom(room, user)) { return false; @@ -126,12 +128,12 @@ Meteor.methods({ const msg = Meteor.call('sendMessage', { rid: roomId, ts: new Date(), - msg: '', file: files[0], files, - groupable: false, attachments, ...msgData, + msg: msgData.msg ?? '', + groupable: msgData.groupable ?? false, }); callbacks.runAsync('afterFileUpload', { user, room, message: msg }); diff --git a/apps/meteor/app/file/server/file.server.js b/apps/meteor/app/file/server/file.server.js index 8c3bb86723de..ae3c4a3c796a 100644 --- a/apps/meteor/app/file/server/file.server.js +++ b/apps/meteor/app/file/server/file.server.js @@ -4,13 +4,10 @@ import path from 'path'; import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; -import Grid from 'gridfs-stream'; import mkdirp from 'mkdirp'; -// Fix problem with usernames being converted to object id -Grid.prototype.tryParseObjectId = function () { - return false; -}; +const mongo = MongoInternals.NpmModule; +const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo; const RocketChatFile = {}; @@ -40,37 +37,30 @@ RocketChatFile.GridFS = class { this.name = name; this.transformWrite = transformWrite; - const mongo = MongoInternals.NpmModule; - const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo; - this.store = new Grid(db, mongo); - this.findOneSync = Meteor.wrapAsync(this.store.collection(this.name).findOne.bind(this.store.collection(this.name))); - this.removeSync = Meteor.wrapAsync(this.store.remove.bind(this.store)); - this.countSync = Meteor.wrapAsync(this.store._col.count.bind(this.store._col)); + + this.bucket = new mongo.GridFSBucket(db, { bucketName: this.name }); + this.getFileSync = Meteor.wrapAsync(this.getFile.bind(this)); } - findOne(fileName) { - return this.findOneSync({ - _id: fileName, - }); + findOne(filename) { + const file = Promise.await(this.bucket.find({ filename }).limit(1).toArray()); + if (!file) { + return; + } + return file[0]; } - remove(fileName) { - return this.removeSync({ - _id: fileName, - root: this.name, - }); + remove(fileId) { + Promise.await(this.bucket.delete(fileId)); } createWriteStream(fileName, contentType) { const self = this; - let ws = this.store.createWriteStream({ - _id: fileName, - filename: fileName, - mode: 'w', - root: this.name, - content_type: contentType, + let ws = this.bucket.openUploadStream(fileName, { + contentType, }); + if (self.transformWrite != null) { ws = RocketChatFile.addPassThrough(ws, function (rs, ws) { const file = { @@ -88,10 +78,7 @@ RocketChatFile.GridFS = class { } createReadStream(fileName) { - return this.store.createReadStream({ - _id: fileName, - root: this.name, - }); + return this.bucket.openDownloadStreamByName(fileName); } getFileWithReadStream(fileName) { @@ -138,7 +125,7 @@ RocketChatFile.GridFS = class { if (file == null) { return undefined; } - return this.remove(fileName); + return this.remove(file._id); } }; diff --git a/apps/meteor/app/importer-pending-avatars/server/importer.js b/apps/meteor/app/importer-pending-avatars/server/importer.js index 2726cb35401b..39fb6fd8b6ae 100644 --- a/apps/meteor/app/importer-pending-avatars/server/importer.js +++ b/apps/meteor/app/importer-pending-avatars/server/importer.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Base, ProgressStep, Selection } from '../../importer/server'; -import { Users } from '../../models'; +import { Users } from '../../models/server'; export class PendingAvatarImporter extends Base { prepareFileCount() { diff --git a/apps/meteor/app/importer-pending-files/server/importer.js b/apps/meteor/app/importer-pending-files/server/importer.js index a0f6899a3f9d..dd29caf8dc10 100644 --- a/apps/meteor/app/importer-pending-files/server/importer.js +++ b/apps/meteor/app/importer-pending-files/server/importer.js @@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { Base, ProgressStep, Selection } from '../../importer/server'; -import { Messages } from '../../models'; +import { Messages } from '../../models/server'; import { FileUpload } from '../../file-upload'; export class PendingFileImporter extends Base { diff --git a/apps/meteor/app/importer-slack-users/server/importer.js b/apps/meteor/app/importer-slack-users/server/importer.js index 9646ac79e7b3..da35a8d1bfc4 100644 --- a/apps/meteor/app/importer-slack-users/server/importer.js +++ b/apps/meteor/app/importer-slack-users/server/importer.js @@ -4,8 +4,7 @@ import { Random } from 'meteor/random'; import { RawImports, Base, ProgressStep, Selection, SelectionUser } from '../../importer/server'; import { RocketChatFile } from '../../file'; -import { Users } from '../../models'; -import { Settings as SettingsRaw } from '../../models/server'; +import { Users, Settings as SettingsRaw } from '../../models/server'; export class SlackUsersImporter extends Base { constructor(info, importRecord) { diff --git a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts index 8c58b7a61f8d..ff174cb26696 100644 --- a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts @@ -13,8 +13,8 @@ import type { IUser, IUserEmail, } from '@rocket.chat/core-typings'; +import { ImportData as ImportDataRaw } from '@rocket.chat/models'; -import { ImportData as ImportDataRaw } from '../../../models/server/raw'; import { IConversionCallbacks } from '../definitions/IConversionCallbacks'; import { Users, Rooms, Subscriptions, ImportData } from '../../../models/server'; import { generateUsernameSuggestion, insertMessage, saveUserIdentity, addUserToDefaultChannels } from '../../../lib/server'; @@ -367,7 +367,7 @@ export class ImportDataConverter { } } catch (e) { this._logger.error(e); - this.saveError(_id, e); + this.saveError(_id, e instanceof Error ? e : new Error(String(e))); } }); } @@ -622,7 +622,7 @@ export class ImportDataConverter { afterImportFn(data, 'message', true); } } catch (e) { - this.saveError(_id, e); + this.saveError(_id, e instanceof Error ? e : new Error(String(e))); } }); @@ -932,7 +932,7 @@ export class ImportDataConverter { afterImportFn(data, 'channel', !existingRoom); } } catch (e) { - this.saveError(_id, e); + this.saveError(_id, e instanceof Error ? e : new Error(String(e))); } }); } diff --git a/apps/meteor/app/importer/server/classes/ImporterBase.js b/apps/meteor/app/importer/server/classes/ImporterBase.js index fd4e7a9972c8..73ebfdd161a6 100644 --- a/apps/meteor/app/importer/server/classes/ImporterBase.js +++ b/apps/meteor/app/importer/server/classes/ImporterBase.js @@ -11,10 +11,9 @@ import { ImporterWebsocket } from './ImporterWebsocket'; import { ProgressStep } from '../../lib/ImporterProgressStep'; import { ImporterInfo } from '../../lib/ImporterInfo'; import { RawImports } from '../models/RawImports'; -import { Settings, Imports } from '../../../models'; +import { Settings, Imports, ImportData } from '../../../models/server'; import { Logger } from '../../../logger'; import { ImportDataConverter } from './ImportDataConverter'; -import { ImportData } from '../../../models/server'; import { t } from '../../../utils/server'; import { Selection, SelectionChannel, SelectionUser } from '..'; diff --git a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.js b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.js index 6424c633bdbe..6facd718f0e4 100644 --- a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.js +++ b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.js @@ -57,7 +57,7 @@ Meteor.methods({ importer.instance = new importer.importer(importer); // eslint-disable-line new-cap - const oldFileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1); + const oldFileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1).split('?')[0]; const date = new Date(); const dateStr = `${date.getUTCFullYear()}${date.getUTCMonth()}${date.getUTCDate()}${date.getUTCHours()}${date.getUTCMinutes()}${date.getUTCSeconds()}`; const newFileName = `${dateStr}_${userId}_${oldFileName}`; diff --git a/apps/meteor/app/importer/server/methods/getImportFileData.js b/apps/meteor/app/importer/server/methods/getImportFileData.js index 32c9aa6d0cd3..750729b39e76 100644 --- a/apps/meteor/app/importer/server/methods/getImportFileData.js +++ b/apps/meteor/app/importer/server/methods/getImportFileData.js @@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor'; import { RocketChatImportFileInstance } from '../startup/store'; import { hasPermission } from '../../../authorization'; -import { Imports } from '../../../models'; +import { Imports } from '../../../models/server'; import { ProgressStep } from '../../lib/ImporterProgressStep'; import { Importers } from '..'; diff --git a/apps/meteor/app/importer/server/methods/getImportProgress.js b/apps/meteor/app/importer/server/methods/getImportProgress.js index 31c9b2d4a5d5..b6a157ad37f0 100644 --- a/apps/meteor/app/importer/server/methods/getImportProgress.js +++ b/apps/meteor/app/importer/server/methods/getImportProgress.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { Imports } from '../../../models'; +import { Imports } from '../../../models/server'; import { Importers } from '..'; Meteor.methods({ diff --git a/apps/meteor/app/importer/server/methods/startImport.js b/apps/meteor/app/importer/server/methods/startImport.js index 45abfe5988f0..c9ef7e0a04c3 100644 --- a/apps/meteor/app/importer/server/methods/startImport.js +++ b/apps/meteor/app/importer/server/methods/startImport.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { Imports } from '../../../models'; +import { Imports } from '../../../models/server'; import { Importers, Selection, SelectionChannel, SelectionUser } from '..'; Meteor.methods({ diff --git a/apps/meteor/app/importer/server/methods/uploadImportFile.js b/apps/meteor/app/importer/server/methods/uploadImportFile.js index 115559664982..b23183210395 100644 --- a/apps/meteor/app/importer/server/methods/uploadImportFile.js +++ b/apps/meteor/app/importer/server/methods/uploadImportFile.js @@ -6,6 +6,38 @@ import { hasPermission } from '../../../authorization'; import { ProgressStep } from '../../lib/ImporterProgressStep'; import { Importers } from '..'; +export const executeUploadImportFile = (userId, binaryContent, contentType, fileName, importerKey) => { + const importer = Importers.get(importerKey); + if (!importer) { + throw new Meteor.Error('error-importer-not-defined', `The importer (${importerKey}) has no import class defined.`, { + method: 'uploadImportFile', + }); + } + + importer.instance = new importer.importer(importer); // eslint-disable-line new-cap + + const date = new Date(); + const dateStr = `${date.getUTCFullYear()}${date.getUTCMonth()}${date.getUTCDate()}${date.getUTCHours()}${date.getUTCMinutes()}${date.getUTCSeconds()}`; + const newFileName = `${dateStr}_${userId}_${fileName}`; + + // Store the file name and content type on the imports collection + importer.instance.startFileUpload(newFileName, contentType); + + // Save the file on the File Store + const file = Buffer.from(binaryContent, 'base64'); + const readStream = RocketChatFile.bufferToStream(file); + const writeStream = RocketChatImportFileInstance.createWriteStream(newFileName, contentType); + + writeStream.on( + 'end', + Meteor.bindEnvironment(() => { + importer.instance.updateProgress(ProgressStep.FILE_LOADED); + }), + ); + + readStream.pipe(writeStream); +}; + Meteor.methods({ uploadImportFile(binaryContent, contentType, fileName, importerKey) { const userId = Meteor.userId(); @@ -20,34 +52,6 @@ Meteor.methods({ }); } - const importer = Importers.get(importerKey); - if (!importer) { - throw new Meteor.Error('error-importer-not-defined', `The importer (${importerKey}) has no import class defined.`, { - method: 'uploadImportFile', - }); - } - - importer.instance = new importer.importer(importer); // eslint-disable-line new-cap - - const date = new Date(); - const dateStr = `${date.getUTCFullYear()}${date.getUTCMonth()}${date.getUTCDate()}${date.getUTCHours()}${date.getUTCMinutes()}${date.getUTCSeconds()}`; - const newFileName = `${dateStr}_${userId}_${fileName}`; - - // Store the file name and content type on the imports collection - importer.instance.startFileUpload(newFileName, contentType); - - // Save the file on the File Store - const file = Buffer.from(binaryContent, 'base64'); - const readStream = RocketChatFile.bufferToStream(file); - const writeStream = RocketChatImportFileInstance.createWriteStream(newFileName, contentType); - - writeStream.on( - 'end', - Meteor.bindEnvironment(() => { - importer.instance.updateProgress(ProgressStep.FILE_LOADED); - }), - ); - - readStream.pipe(writeStream); + executeUploadImportFile(userId, binaryContent, contentType, fileName, importerKey); }, }); diff --git a/apps/meteor/app/importer/server/models/RawImports.js b/apps/meteor/app/importer/server/models/RawImports.js index 168b73b3e07b..470c1a4ff7cc 100644 --- a/apps/meteor/app/importer/server/models/RawImports.js +++ b/apps/meteor/app/importer/server/models/RawImports.js @@ -1,4 +1,4 @@ -import { Base } from '../../../models'; +import { Base } from '../../../models/server'; class RawImportsModel extends Base { constructor() { diff --git a/apps/meteor/app/importer/server/startup/setImportsToInvalid.js b/apps/meteor/app/importer/server/startup/setImportsToInvalid.js index 691a42cd19b7..9a4ec9b728f3 100644 --- a/apps/meteor/app/importer/server/startup/setImportsToInvalid.js +++ b/apps/meteor/app/importer/server/startup/setImportsToInvalid.js @@ -29,11 +29,11 @@ Meteor.startup(function () { Imports.invalidateOperationsExceptId(idToKeep); // Clean up all the raw import data, except for the last operation - runDrop(() => RawImports.model.rawCollection().remove({ import: { $ne: idToKeep } })); + runDrop(() => RawImports.model.rawCollection().deleteMany({ import: { $ne: idToKeep } })); } else { Imports.invalidateAllOperations(); // Clean up all the raw import data - runDrop(() => RawImports.model.rawCollection().remove({})); + runDrop(() => RawImports.model.rawCollection().deleteMany({})); } }); diff --git a/apps/meteor/app/importer/server/startup/store.js b/apps/meteor/app/importer/server/startup/store.js index 2026945ba3f2..22ba5eadce34 100644 --- a/apps/meteor/app/importer/server/startup/store.js +++ b/apps/meteor/app/importer/server/startup/store.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { RocketChatFile } from '../../../file'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; export let RocketChatImportFileInstance; diff --git a/apps/meteor/app/integrations/server/api/api.js b/apps/meteor/app/integrations/server/api/api.js index 57a4c2b132bd..724bfea9ad80 100644 --- a/apps/meteor/app/integrations/server/api/api.js +++ b/apps/meteor/app/integrations/server/api/api.js @@ -8,12 +8,12 @@ import Future from 'fibers/future'; import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment'; +import { Integrations } from '@rocket.chat/models'; import { incomingLogger } from '../logger'; import { processWebhookMessage } from '../../../lib/server'; import { API, APIClass, defaultRateLimiterOptions } from '../../../api/server'; import * as Models from '../../../models/server'; -import { Integrations } from '../../../models/server/raw'; import { settings } from '../../../settings/server'; const compiledScripts = {}; @@ -67,7 +67,7 @@ function getIntegrationScript(integration) { const script = integration.scriptCompiled; const { sandbox, store } = buildSandbox(); try { - incomingLogger.info({ msg: 'Will evaluate script of Trigger', name: integration.name }); + incomingLogger.info({ msg: 'Will evaluate script of Trigger', integration: integration.name }); incomingLogger.debug(script); const vmScript = new VMScript(`${script}; Script;`, 'script.js'); @@ -89,20 +89,20 @@ function getIntegrationScript(integration) { } catch (err) { incomingLogger.error({ msg: 'Error evaluating Script in Trigger', - name: integration.name, + integration: integration.name, script, err, }); throw API.v1.failure('error-evaluating-script'); } - incomingLogger.error({ msg: 'Class "Script" not in Trigger', name: integration.name }); + incomingLogger.error({ msg: 'Class "Script" not in Trigger', integration: integration.name }); throw API.v1.failure('class-script-not-found'); } function createIntegration(options, user) { - incomingLogger.info({ msg: 'Add integration', name: options.name }); - incomingLogger.debug(options); + incomingLogger.info({ msg: 'Add integration', integration: options.name }); + incomingLogger.debug({ options }); Meteor.runAsUser(user._id, function () { switch (options.event) { @@ -139,7 +139,7 @@ function createIntegration(options, user) { function removeIntegration(options, user) { incomingLogger.info('Remove integration'); - incomingLogger.debug(options); + incomingLogger.debug({ options }); const integrationToRemove = Promise.await(Integrations.findOneByUrl(options.target_url)); if (!integrationToRemove) { @@ -152,7 +152,7 @@ function removeIntegration(options, user) { } function executeIntegrationRest() { - incomingLogger.info({ msg: 'Post integration:', name: this.integration.name }); + incomingLogger.info({ msg: 'Post integration:', integration: this.integration.name }); incomingLogger.debug({ urlParams: this.urlParams, bodyParams: this.bodyParams }); if (this.integration.enabled !== true) { @@ -230,7 +230,7 @@ function executeIntegrationRest() { if (!result) { incomingLogger.debug({ msg: 'Process Incoming Request result of Trigger has no data', - name: this.integration.name, + integration: this.integration.name, }); return API.v1.success(); } @@ -246,13 +246,13 @@ function executeIntegrationRest() { incomingLogger.debug({ msg: 'Process Incoming Request result of Trigger', - name: this.integration.name, + integration: this.integration.name, result: this.bodyParams, }); } catch (err) { incomingLogger.error({ msg: 'Error running Script in Trigger', - name: this.integration.name, + integration: this.integration.name, script: this.integration.scriptCompiled, err, }); diff --git a/apps/meteor/app/integrations/server/lib/triggerHandler.js b/apps/meteor/app/integrations/server/lib/triggerHandler.js index bc78c8595b83..a44deaa537d3 100644 --- a/apps/meteor/app/integrations/server/lib/triggerHandler.js +++ b/apps/meteor/app/integrations/server/lib/triggerHandler.js @@ -7,9 +7,9 @@ import s from 'underscore.string'; import moment from 'moment'; import Fiber from 'fibers'; import Future from 'fibers/future'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import * as Models from '../../../models/server'; -import { Integrations, IntegrationHistory } from '../../../models/server/raw'; import { settings } from '../../../settings/server'; import { getRoomByNameOrIdWithOptionToJoin, processWebhookMessage } from '../../../lib/server'; import { outgoingLogger } from '../logger'; @@ -274,7 +274,7 @@ export class RocketChatIntegrationHandler { const { store, sandbox } = this.buildSandbox(); try { - outgoingLogger.info({ msg: 'Will evaluate script of Trigger', name: integration.name }); + outgoingLogger.info({ msg: 'Will evaluate script of Trigger', integration: integration.name }); outgoingLogger.debug(script); const vmScript = new VMScript(`${script}; Script;`, 'script.js'); @@ -296,7 +296,7 @@ export class RocketChatIntegrationHandler { } catch (err) { outgoingLogger.error({ msg: 'Error evaluating Script in Trigger', - name: integration.name, + integration: integration.name, script, err, }); @@ -385,12 +385,12 @@ export class RocketChatIntegrationHandler { }); outgoingLogger.error({ msg: 'Error running Script in the Integration', - name: integration.name, + integration: integration.name, err, }); outgoingLogger.debug({ msg: 'Error running Script in the Integration', - name: integration.name, + integration: integration.name, script: integration.scriptCompiled, }); // Only output the compiled script if debugging is enabled, so the logs don't get spammed. } @@ -708,7 +708,7 @@ export class RocketChatIntegrationHandler { this.updateHistory({ historyId, step: 'mapped-args-to-data', data, triggerWord: word }); outgoingLogger.info(`Will be executing the Integration "${trigger.name}" to the url: ${url}`); - outgoingLogger.debug(data); + outgoingLogger.debug({ data }); let opts = { params: {}, diff --git a/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts b/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts index 44bc2594baf7..cf78e9ed79de 100644 --- a/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts @@ -182,7 +182,7 @@ export const validateOutgoingIntegration = function ( integrationData.scriptError = undefined; } catch (e) { integrationData.scriptCompiled = undefined; - integrationData.scriptError = _.pick(e, 'name', 'message', 'stack'); + integrationData.scriptError = e instanceof Error ? _.pick(e, 'name', 'message', 'stack') : undefined; } } diff --git a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts index 499c8b806ac3..0c9564ea5027 100644 --- a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts +++ b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { IntegrationHistory, Integrations } from '../../../models/server/raw'; import notifications from '../../../notifications/server/lib/Notifications'; Meteor.methods({ diff --git a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts index 43e063876a2e..1c40d62dcec8 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts @@ -5,10 +5,10 @@ import { Babel } from 'meteor/babel-compiler'; import _ from 'underscore'; import s from 'underscore.string'; import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings'; +import { Integrations, Roles } from '@rocket.chat/models'; import { hasPermission, hasAllPermission } from '../../../../authorization/server'; import { Users, Rooms, Subscriptions } from '../../../../models/server'; -import { Integrations, Roles } from '../../../../models/server/raw'; const validChannelChars = ['@', '#']; @@ -93,7 +93,7 @@ Meteor.methods({ integrationData.scriptError = undefined; } catch (e) { integrationData.scriptCompiled = undefined; - integrationData.scriptError = _.pick(e, 'name', 'message', 'stack'); + integrationData.scriptError = e instanceof Error ? _.pick(e, 'name', 'message', 'stack') : undefined; } } diff --git a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts index 81b1e719e40b..ef90340167be 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization/server'; -import { Integrations } from '../../../../models/server/raw'; Meteor.methods({ async deleteIncomingIntegration(integrationId) { diff --git a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.js b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.js index 49ea3faf54ce..f3d161f53976 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.js +++ b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.js @@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { Babel } from 'meteor/babel-compiler'; import _ from 'underscore'; import s from 'underscore.string'; +import { Integrations, Roles } from '@rocket.chat/models'; import { Rooms, Users, Subscriptions } from '../../../../models/server'; -import { Integrations, Roles } from '../../../../models/server/raw'; import { hasAllPermission, hasPermission } from '../../../../authorization/server'; const validChannelChars = ['@', '#']; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts index 8514793d9213..ee670807e3aa 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import type { INewOutgoingIntegration, IOutgoingIntegration } from '@rocket.chat/core-typings'; +import { Integrations } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization/server'; -import { Integrations } from '../../../../models/server/raw'; import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; Meteor.methods({ diff --git a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts index 6926e18b1c7c..7209ae909c74 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization/server'; -import { IntegrationHistory, Integrations } from '../../../../models/server/raw'; Meteor.methods({ async deleteOutgoingIntegration(integrationId) { diff --git a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts index 07e47bbf4ca5..00617c5d392b 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization/server'; -import { Integrations, IntegrationHistory } from '../../../../models/server/raw'; import { triggerHandler } from '../../lib/triggerHandler'; Meteor.methods({ diff --git a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js index b357063cac9a..8ddae4b48cab 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js +++ b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; +import { Integrations } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization/server'; import { Users } from '../../../../models/server'; -import { Integrations } from '../../../../models/server/raw'; import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; Meteor.methods({ diff --git a/apps/meteor/app/invites/server/functions/findOrCreateInvite.js b/apps/meteor/app/invites/server/functions/findOrCreateInvite.js index 3af45671cf7d..7d17dfae9c24 100644 --- a/apps/meteor/app/invites/server/functions/findOrCreateInvite.js +++ b/apps/meteor/app/invites/server/functions/findOrCreateInvite.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; +import { Invites } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; import { api } from '../../../../server/sdk/api'; import { Subscriptions, Rooms } from '../../../models/server'; -import { Invites } from '../../../models/server/raw'; import { settings } from '../../../settings/server'; import { getURL } from '../../../utils/lib/getURL'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/invites/server/functions/listInvites.js b/apps/meteor/app/invites/server/functions/listInvites.js index 10d67435237d..9ee3fb979204 100644 --- a/apps/meteor/app/invites/server/functions/listInvites.js +++ b/apps/meteor/app/invites/server/functions/listInvites.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Invites } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { Invites } from '../../../models/server/raw'; export const listInvites = async (userId) => { if (!userId) { diff --git a/apps/meteor/app/invites/server/functions/removeInvite.js b/apps/meteor/app/invites/server/functions/removeInvite.js index 3e51ded42e4b..d79f0c644bfa 100644 --- a/apps/meteor/app/invites/server/functions/removeInvite.js +++ b/apps/meteor/app/invites/server/functions/removeInvite.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Invites } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization'; -import { Invites } from '../../../models/server/raw'; export const removeInvite = async (userId, invite) => { if (!userId || !invite) { diff --git a/apps/meteor/app/invites/server/functions/useInviteToken.js b/apps/meteor/app/invites/server/functions/useInviteToken.js index ad020b9db251..6514ebe4e92f 100644 --- a/apps/meteor/app/invites/server/functions/useInviteToken.js +++ b/apps/meteor/app/invites/server/functions/useInviteToken.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { Invites } from '@rocket.chat/models'; import { Users, Subscriptions } from '../../../models/server'; -import { Invites } from '../../../models/server/raw'; import { validateInviteToken } from './validateInviteToken'; import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/invites/server/functions/validateInviteToken.js b/apps/meteor/app/invites/server/functions/validateInviteToken.js index 99603999f5ae..623761324b30 100644 --- a/apps/meteor/app/invites/server/functions/validateInviteToken.js +++ b/apps/meteor/app/invites/server/functions/validateInviteToken.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; - -import { Rooms, Invites } from '../../../models/server/raw'; +import { Invites, Rooms } from '@rocket.chat/models'; export const validateInviteToken = async (token) => { if (!token || typeof token !== 'string') { diff --git a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateRoom.js b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateRoom.js index 42e01bd78b9f..07caf87bd140 100644 --- a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateRoom.js +++ b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateRoom.js @@ -1,4 +1,4 @@ -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; export default function handleOnCreateRoom(user, room) { const users = Users.findByRoomId(room._id); diff --git a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateUser.js b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateUser.js index 851c16c628ad..5e1e9442b0e5 100644 --- a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateUser.js +++ b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onCreateUser.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users, Rooms } from '../../../../models'; +import { Users, Rooms } from '../../../../models/server'; export default function handleOnCreateUser(newUser) { if (!newUser) { diff --git a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onLogin.js b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onLogin.js index a10ae0950f04..7d8d350dd99d 100644 --- a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onLogin.js +++ b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onLogin.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users, Rooms } from '../../../../models'; +import { Users, Rooms } from '../../../../models/server'; export default function handleOnLogin(login) { if (login.user === null) { diff --git a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onSaveMessage.js b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onSaveMessage.js index e720091d630f..c9a1499a02d0 100644 --- a/apps/meteor/app/irc/server/irc-bridge/localHandlers/onSaveMessage.js +++ b/apps/meteor/app/irc/server/irc-bridge/localHandlers/onSaveMessage.js @@ -1,5 +1,5 @@ import { SystemLogger } from '../../../../../server/lib/logger/system'; -import { Subscriptions, Users } from '../../../../models'; +import { Subscriptions, Users } from '../../../../models/server'; export default function handleOnSaveMessage(message, to) { let toIdentification = ''; diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/disconnected.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/disconnected.js index e5ed2214f05d..fc432446b5fb 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/disconnected.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/disconnected.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; export default function handleQUIT(args) { const user = Users.findOne({ diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js index 6555807b5a0b..b17009e54eb5 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js @@ -1,4 +1,4 @@ -import { Users, Rooms } from '../../../../models'; +import { Users, Rooms } from '../../../../models/server'; import { createRoom, addUserToRoom } from '../../../../lib'; export default function handleJoinedChannel(args) { diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js index 13c29e828c46..e6b8f46f6301 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js @@ -1,4 +1,4 @@ -import { Users, Rooms } from '../../../../models'; +import { Users, Rooms } from '../../../../models/server'; import { removeUserFromRoom } from '../../../../lib'; export default function handleLeftChannel(args) { diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/nickChanged.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/nickChanged.js index 9ebc16057f3c..de2956e3302d 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/nickChanged.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/nickChanged.js @@ -1,4 +1,4 @@ -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; export default function handleNickChanged(args) { const user = Users.findOne({ diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js index d4f4bb8ae5d6..cfad046fd725 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js @@ -1,4 +1,4 @@ -import { Users, Rooms } from '../../../../models'; +import { Users, Rooms } from '../../../../models/server'; import { sendMessage, createDirectRoom } from '../../../../lib'; /* * diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/userRegistered.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/userRegistered.js index 2f42f790e8f5..29c0dad9019d 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/userRegistered.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/userRegistered.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; export default async function handleUserRegistered(args) { // Check if there is an user with the given username diff --git a/apps/meteor/app/irc/server/irc.js b/apps/meteor/app/irc/server/irc.js index dd6c4449ab01..de3638169607 100644 --- a/apps/meteor/app/irc/server/irc.js +++ b/apps/meteor/app/irc/server/irc.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import Bridge from './irc-bridge'; -import { settings } from '../../settings'; +import { settings } from '../../settings/server'; if (!!settings.get('IRC_Enabled') === true) { // Normalize the config values diff --git a/apps/meteor/app/irc/server/methods/resetIrcConnection.js b/apps/meteor/app/irc/server/methods/resetIrcConnection.js index cef9299c84d8..9c033c27017f 100644 --- a/apps/meteor/app/irc/server/methods/resetIrcConnection.js +++ b/apps/meteor/app/irc/server/methods/resetIrcConnection.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Settings } from '../../../models/server'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import Bridge from '../irc-bridge'; Meteor.methods({ diff --git a/apps/meteor/app/katex/client/index.ts b/apps/meteor/app/katex/client/index.ts index a2cee08f78e4..7baf7098d0c3 100644 --- a/apps/meteor/app/katex/client/index.ts +++ b/apps/meteor/app/katex/client/index.ts @@ -152,8 +152,10 @@ class Katex { '\\href': '\\@secondoftwo', }, }); - } catch ({ message }) { - return `

${escapeHTML(message)}
`; + } catch (e) { + return `
${escapeHTML( + e instanceof Error ? e.message : String(e), + )}
`; } }; diff --git a/apps/meteor/app/lib/client/methods/sendMessage.js b/apps/meteor/app/lib/client/methods/sendMessage.js index 8d3d9bd9532e..dad647d5be03 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.js +++ b/apps/meteor/app/lib/client/methods/sendMessage.js @@ -1,8 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { TimeSync } from 'meteor/mizzao:timesync'; import s from 'underscore.string'; -import { ChatMessage, Rooms } from '../../../models'; +import { ChatMessage, Rooms } from '../../../models/client'; import { settings } from '../../../settings'; import { callbacks } from '../../../../lib/callbacks'; import { t } from '../../../utils/client'; @@ -19,7 +18,7 @@ Meteor.methods({ return dispatchToastMessage({ type: 'error', message: t('Message_Already_Sent') }); } const user = Meteor.user(); - message.ts = isNaN(TimeSync.serverOffset()) ? new Date() : new Date(Date.now() + TimeSync.serverOffset()); + message.ts = new Date(); message.u = { _id: Meteor.userId(), username: user.username, diff --git a/apps/meteor/app/lib/lib/MessageTypes.ts b/apps/meteor/app/lib/lib/MessageTypes.ts index 282d2800c94d..ebed349dc548 100644 --- a/apps/meteor/app/lib/lib/MessageTypes.ts +++ b/apps/meteor/app/lib/lib/MessageTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ import { Meteor } from 'meteor/meteor'; import { MessageTypes } from '../../ui-utils/lib/MessageTypes'; diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 0bdaa33c8193..c219f32ad9a9 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -4,14 +4,14 @@ import type { IUser, IRoom } from '@rocket.chat/core-typings'; import { AppEvents, Apps } from '../../../apps/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Messages, Rooms, Subscriptions } from '../../../models/server'; +import { Messages, Rooms, Subscriptions, Users } from '../../../models/server'; import { Team } from '../../../../server/sdk'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; export const addUserToRoom = function ( rid: string, - user: Pick, + user: Pick | string, inviter?: Pick, silenced?: boolean, ): boolean | unknown { @@ -26,14 +26,22 @@ export const addUserToRoom = function ( return; } + try { + callbacks.run('federation.beforeAddUserAToRoom', { user, inviter }, room); + } catch (error) { + throw new Meteor.Error((error as any)?.message); + } + + const userToBeAdded = typeof user !== 'string' ? user : Users.findOneByUsername(user.replace('@', '')); + // Check if user is already in room - const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, user._id); + const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, userToBeAdded._id); if (subscription) { return; } try { - Promise.await(Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, user, inviter)); + Promise.await(Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter)); } catch (error) { if (error instanceof AppsEngineException) { throw new Meteor.Error('error-app-prevented', error.message); @@ -44,14 +52,14 @@ export const addUserToRoom = function ( if (room.t === 'c' || room.t === 'p' || room.t === 'l') { // Add a new event, with an optional inviter - callbacks.run('beforeAddedToRoom', { user, inviter }, room); + callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter }, room); // Keep the current event - callbacks.run('beforeJoinRoom', user, room); + callbacks.run('beforeJoinRoom', userToBeAdded, room); } Promise.await( - Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, user, inviter).catch((error) => { + Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter).catch((error) => { if (error instanceof AppsEngineException) { throw new Meteor.Error('error-app-prevented', error.message); } @@ -60,7 +68,7 @@ export const addUserToRoom = function ( }), ); - Subscriptions.createWithRoomAndUser(room, user, { + Subscriptions.createWithRoomAndUser(room, userToBeAdded, { ts: now, open: true, alert: true, @@ -79,34 +87,34 @@ export const addUserToRoom = function ( }, }; if (room.teamMain) { - Messages.createUserAddedToTeamWithRoomIdAndUser(rid, user, extraData); + Messages.createUserAddedToTeamWithRoomIdAndUser(rid, userToBeAdded, extraData); } else { - Messages.createUserAddedWithRoomIdAndUser(rid, user, extraData); + Messages.createUserAddedWithRoomIdAndUser(rid, userToBeAdded, extraData); } } else if (room.prid) { - Messages.createUserJoinWithRoomIdAndUserDiscussion(rid, user, { ts: now }); + Messages.createUserJoinWithRoomIdAndUserDiscussion(rid, userToBeAdded, { ts: now }); } else if (room.teamMain) { - Messages.createUserJoinTeamWithRoomIdAndUser(rid, user, { ts: now }); + Messages.createUserJoinTeamWithRoomIdAndUser(rid, userToBeAdded, { ts: now }); } else { - Messages.createUserJoinWithRoomIdAndUser(rid, user, { ts: now }); + Messages.createUserJoinWithRoomIdAndUser(rid, userToBeAdded, { ts: now }); } } if (room.t === 'c' || room.t === 'p') { Meteor.defer(function () { // Add a new event, with an optional inviter - callbacks.run('afterAddedToRoom', { user, inviter }, room); + callbacks.run('afterAddedToRoom', { user: userToBeAdded, inviter }, room); // Keep the current event - callbacks.run('afterJoinRoom', user, room); + callbacks.run('afterJoinRoom', userToBeAdded, room); - Apps.triggerEvent(AppEvents.IPostRoomUserJoined, room, user, inviter); + Apps.triggerEvent(AppEvents.IPostRoomUserJoined, room, userToBeAdded, inviter); }); } if (room.teamMain && room.teamId && inviter) { // if user is joining to main team channel, create a membership - Promise.await(Team.addMember(inviter, user._id, room.teamId)); + Promise.await(Team.addMember(inviter, userToBeAdded._id, room.teamId)); } return true; diff --git a/apps/meteor/app/lib/server/functions/attachMessage.ts b/apps/meteor/app/lib/server/functions/attachMessage.ts index 5c65684640e7..a24dea87b191 100644 --- a/apps/meteor/app/lib/server/functions/attachMessage.ts +++ b/apps/meteor/app/lib/server/functions/attachMessage.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { MessageAttachment } from '@rocket.chat/core-typings'; diff --git a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts index 6c61539a1ac5..90df732bf4cf 100644 --- a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts +++ b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts @@ -1,4 +1,4 @@ -import { Cursor } from 'mongodb'; +import { FindCursor } from 'mongodb'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import type { IMessage, IMessageDiscussion } from '@rocket.chat/core-typings'; @@ -48,7 +48,7 @@ export const cleanRoomHistory = function ({ Messages.findDiscussionByRoomIdPinnedTimestampAndUsers(rid, excludePinned, ts, fromUsers, { fields: { drid: 1 }, ...(limit && { limit }), - }) as Cursor + }) as FindCursor ).forEach(({ drid }) => deleteRoom(drid)), ); } diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index b5ad346eb9b6..4e01a3660270 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -1,14 +1,14 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; -import type { IUser } from '@rocket.chat/core-typings'; +import type { ICreatedRoom, IUser } from '@rocket.chat/core-typings'; +import { Subscriptions } from '@rocket.chat/models'; +import { Users, Rooms } from '../../../models/server'; import { Apps } from '../../../apps/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Rooms } from '../../../models/server'; import { settings } from '../../../settings/server'; import { getDefaultSubscriptionPref } from '../../../utils/server'; -import { Users, Subscriptions } from '../../../models/server/raw'; import { ICreateRoomParams } from '../../../../server/sdk/types/IRoomService'; const generateSubscription = (fname: string, name: string, user: IUser, extra: {}): any => ({ @@ -32,16 +32,31 @@ const generateSubscription = (fname: string, name: string, user: IUser, extra: { const getFname = (members: IUser[]): string => members.map(({ name, username }) => name || username).join(', '); const getName = (members: IUser[]): string => members.map(({ username }) => username).join(', '); -export const createDirectRoom = function (members: IUser[], roomExtraData = {}, options: ICreateRoomParams['options']): unknown { +export const createDirectRoom = function ( + members: IUser[] | string[], + roomExtraData = {}, + options: ICreateRoomParams['options'], +): ICreatedRoom { if (members.length > (settings.get('DirectMesssage_maxUsers') || 1)) { throw new Error('error-direct-message-max-user-exceeded'); } + callbacks.run('beforeCreateDirectRoom', members); + const membersUsernames = members.map((member) => { + if (typeof member === 'string') { + return member.replace('@', ''); + } + return member.username; + }); + + const roomMembers: IUser[] = Users.findUsersByUsernames(membersUsernames, { + fields: { _id: 1, name: 1, username: 1, settings: 1, customFields: 1 }, + }).fetch(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const sortedMembers = members.sort((u1, u2) => (u1.name! || u1.username!).localeCompare(u2.name! || u2.username!)); + const sortedMembers = roomMembers.sort((u1, u2) => (u1.name! || u1.username!).localeCompare(u2.name! || u2.username!)); - const usernames = sortedMembers.map(({ username }) => username); - const uids = members.map(({ _id }) => _id).sort(); + const usernames: string[] = sortedMembers.map(({ username }) => username as string).filter(Boolean); + const uids = roomMembers.map(({ _id }) => _id).sort(); // Deprecated: using users' _id to compose the room _id is deprecated const room = @@ -63,7 +78,7 @@ export const createDirectRoom = function (members: IUser[], roomExtraData = {}, }; if (isNewRoom) { - const tmpRoom = { + const tmpRoom: { _USERNAMES?: (string | undefined)[] } & typeof roomInfo = { ...roomInfo, _USERNAMES: usernames, }; @@ -88,26 +103,31 @@ export const createDirectRoom = function (members: IUser[], roomExtraData = {}, if (typeof result === 'object') { Object.assign(roomInfo, result); } + + delete tmpRoom._USERNAMES; } const rid = room?._id || Rooms.insert(roomInfo); - if (members.length === 1) { + if (roomMembers.length === 1) { // dm to yourself Subscriptions.updateOne( - { rid, 'u._id': members[0]._id }, + { rid, 'u._id': roomMembers[0]._id }, { $set: { open: true }, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - $setOnInsert: generateSubscription(members[0].name! || members[0].username!, members[0].username!, members[0], { + $setOnInsert: generateSubscription(roomMembers[0].name! || roomMembers[0].username!, roomMembers[0].username!, roomMembers[0], { ...options?.subscriptionExtra, }), }, { upsert: true }, ); } else { - const memberIds = members.map((member) => member._id); - const membersWithPreferences = Users.find({ _id: { $in: memberIds } }, { projection: { 'username': 1, 'settings.preferences': 1 } }); + const memberIds = roomMembers.map((member) => member._id); + const membersWithPreferences: IUser[] = Users.find( + { _id: { $in: memberIds } }, + { projection: { 'username': 1, 'settings.preferences': 1 } }, + ); membersWithPreferences.forEach((member) => { const otherMembers = sortedMembers.filter(({ _id }) => _id !== member._id); @@ -129,15 +149,16 @@ export const createDirectRoom = function (members: IUser[], roomExtraData = {}, if (isNewRoom) { const insertedRoom = Rooms.findOneById(rid); - callbacks.run('afterCreateDirectRoom', insertedRoom, { members }); + callbacks.run('afterCreateDirectRoom', insertedRoom, { members: roomMembers, creatorId: options?.creator }); Apps.triggerEvent('IPostRoomCreate', insertedRoom); } return { - _id: rid, + _id: String(rid), usernames, t: 'd', inserted: isNewRoom, + ...room, }; }; diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 903d942dba19..28fcf4b13c04 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -2,7 +2,7 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/excepti import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import s from 'underscore.string'; -import type { IUser } from '@rocket.chat/core-typings'; +import type { ICreatedRoom, IUser } from '@rocket.chat/core-typings'; import { IRoom, RoomType } from '@rocket.chat/core-typings'; import { Apps } from '../../../apps/server'; @@ -26,12 +26,12 @@ export const createRoom = function ( readOnly?: boolean, roomExtraData?: Partial, options?: ICreateRoomParams['options'], -): unknown { +): ICreatedRoom { const { teamId, ...extraData } = roomExtraData || ({} as IRoom); callbacks.run('beforeCreateRoom', { type, name, owner: ownerUsername, members, readOnly, extraData, options }); if (type === 'd') { - return createDirectRoom(members as IUser[], extraData, options); + return createDirectRoom(members as IUser[], extraData, { ...options, creator: options?.creator || ownerUsername }); } if (!isValidName(name)) { @@ -59,7 +59,7 @@ export const createRoom = function ( const now = new Date(); - const roomProps: Omit = { + const roomProps: Omit = { fname: name, ...extraData, name: getValidRoomName(name.trim(), undefined, { @@ -114,28 +114,46 @@ export const createRoom = function ( callbacks.run('beforeCreateChannel', owner, roomProps); } const room = Rooms.createWithFullRoomData(roomProps); - - for (const username of [...new Set(members as string[])]) { - const member = Users.findOneByUsername(username, { - fields: { 'username': 1, 'settings.preferences': 1 }, - }); - if (!member) { - continue; - } - + const shouldBeHandledByFederation = room.federated === true || ownerUsername.includes(':'); + if (shouldBeHandledByFederation) { const extra: Partial = options?.subscriptionExtra || {}; - extra.open = true; + extra.ls = now; if (room.prid) { extra.prid = room.prid; } - if (username === owner.username) { - extra.ls = now; - } + Subscriptions.createWithRoomAndUser(room, owner, extra); + } else { + for (const username of [...new Set(members as string[])]) { + const member = Users.findOneByUsername(username, { + fields: { 'username': 1, 'settings.preferences': 1, 'federated': 1 }, + }); + if (!member) { + continue; + } + + try { + callbacks.run('federation.beforeAddUserAToRoom', { user: member, inviter: owner }, room); + } catch (error) { + throw new Meteor.Error((error as any)?.message); + } - Subscriptions.createWithRoomAndUser(room, member, extra); + const extra: Partial = options?.subscriptionExtra || {}; + + extra.open = true; + + if (room.prid) { + extra.prid = room.prid; + } + + if (username === owner.username) { + extra.ls = now; + } + + Subscriptions.createWithRoomAndUser(room, member, extra); + } } addUserRoles(owner._id, ['owner'], room._id); @@ -150,6 +168,9 @@ export const createRoom = function ( callbacks.runAsync('afterCreatePrivateGroup', owner, room); } callbacks.runAsync('afterCreateRoom', owner, room); + if (shouldBeHandledByFederation) { + callbacks.run('federation.afterCreateFederatedRoom', room, { owner, originalMemberList: members as string[] }); + } Apps.triggerEvent('IPostRoomCreate', room); diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index dfb7f473a0ae..b80a513c59f6 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; +import { Uploads } from '@rocket.chat/models'; import { FileUpload } from '../../../file-upload/server'; import { settings } from '../../../settings/server'; import { Messages, Rooms } from '../../../models/server'; -import { Uploads } from '../../../models/server/raw'; import { api } from '../../../../server/sdk/api'; import { callbacks } from '../../../../lib/callbacks'; import { Apps } from '../../../apps/server'; @@ -37,7 +37,7 @@ export const deleteMessage = async function (message: IMessage, user: IUser): Pr } for await (const file of files) { - file?._id && (await Uploads.update({ _id: file._id }, { $set: { _hidden: true } })); + file?._id && (await Uploads.updateOne({ _id: file._id }, { $set: { _hidden: true } })); } } else { if (!showDeletedStatus) { diff --git a/apps/meteor/app/lib/server/functions/deleteRoom.ts b/apps/meteor/app/lib/server/functions/deleteRoom.ts index e74a54ebb9c7..46f247a4e797 100644 --- a/apps/meteor/app/lib/server/functions/deleteRoom.ts +++ b/apps/meteor/app/lib/server/functions/deleteRoom.ts @@ -1,15 +1,13 @@ -import { DeleteWriteOpResultObject } from 'mongodb'; - import { Messages, Subscriptions, Rooms } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; import { FileUpload } from '../../../file-upload/server'; -export const deleteRoom = function (rid: string): Promise { +export const deleteRoom = function (rid: string): void { FileUpload.removeFilesByRoomId(rid); Messages.removeByRoomId(rid); callbacks.run('beforeDeleteRoom', rid); Subscriptions.removeByRoomId(rid); FileUpload.getStore('Avatars').deleteByRoomId(rid); callbacks.run('afterDeleteRoom', rid); - return Rooms.removeById(rid); + Rooms.removeById(rid); }; diff --git a/apps/meteor/app/lib/server/functions/deleteUser.ts b/apps/meteor/app/lib/server/functions/deleteUser.ts index 056b3750a2d5..568cfd370924 100644 --- a/apps/meteor/app/lib/server/functions/deleteUser.ts +++ b/apps/meteor/app/lib/server/functions/deleteUser.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { FileProp } from '@rocket.chat/core-typings'; +import { Integrations, FederationServers, LivechatVisitors } from '@rocket.chat/models'; import { FileUpload } from '../../../file-upload/server'; import { Users, Subscriptions, Messages, Rooms, LivechatDepartmentAgents } from '../../../models/server'; -import { FederationServers, Integrations, LivechatVisitors } from '../../../models/server/raw'; import { settings } from '../../../settings/server'; import { updateGroupDMsName } from './updateGroupDMsName'; import { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; diff --git a/apps/meteor/app/lib/server/functions/getFullUserData.ts b/apps/meteor/app/lib/server/functions/getFullUserData.ts index 74bf410aee9b..c4db7e3f20cf 100644 --- a/apps/meteor/app/lib/server/functions/getFullUserData.ts +++ b/apps/meteor/app/lib/server/functions/getFullUserData.ts @@ -1,4 +1,4 @@ -import { IUser } from '@rocket.chat/core-typings'; +import type { IUser } from '@rocket.chat/core-typings'; import { Logger } from '../../../logger/server'; import { settings } from '../../../settings/server'; @@ -29,7 +29,6 @@ const fullFields = { bio: 1, createdAt: 1, lastLogin: 1, - services: 1, requirePasswordChange: 1, requirePasswordChangeReason: 1, roles: 1, @@ -38,16 +37,17 @@ const fullFields = { let publicCustomFields: Record = {}; let customFields: Record = {}; -settings.watch('Accounts_CustomFields', (value) => { +settings.watch('Accounts_CustomFields', (settingValue) => { publicCustomFields = {}; customFields = {}; - if (!value.trim()) { + const value = settingValue?.trim(); + if (!value) { return; } try { - const customFieldsOnServer = JSON.parse(value.trim()); + const customFieldsOnServer = JSON.parse(value); Object.keys(customFieldsOnServer).forEach((key) => { const element = customFieldsOnServer[key]; if (element.public) { @@ -68,11 +68,6 @@ const getFields = (canViewAllInfo: boolean): Record => ({ ...getCustomFields(canViewAllInfo), }); -const removePasswordInfo = (user: IUser): Omit => { - const { services, ...result } = user; - return result; -}; - export async function getFullUserDataByIdOrUsername( userId: string, { filterId, filterUsername }: { filterId: string; filterUsername?: undefined } | { filterId?: undefined; filterUsername: string }, @@ -85,8 +80,12 @@ export async function getFullUserDataByIdOrUsername( const fields = getFields(canViewAllInfo); const options = { - fields, + fields: { + ...fields, + ...(myself && { services: 1 }), + }, }; + const user = Users.findOneByIdOrUsername(targetUser, options); if (!user) { return null; @@ -94,5 +93,5 @@ export async function getFullUserDataByIdOrUsername( user.canViewAllInfo = canViewAllInfo; - return myself ? user : removePasswordInfo(user); + return user; } diff --git a/apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts b/apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts index ad74510c470c..b525ac05e695 100644 --- a/apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts +++ b/apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts @@ -1,4 +1,4 @@ -import { Cursor } from 'mongodb'; +import { FindCursor } from 'mongodb'; import type { IUser, ISubscription } from '@rocket.chat/core-typings'; import { subscriptionHasRole } from '../../../authorization/server'; @@ -24,7 +24,8 @@ export function getSubscribedRoomsForUserWithDetails( ): SubscribedRoomsForUserWithDetails[] { const subscribedRooms: SubscribedRoomsForUserWithDetails[] = []; - const cursor: Cursor = + // TODO this is not really a FindCursor since it is using Meteor Models -> migrate to raw models + const cursor: FindCursor = roomIds.length > 0 ? Subscriptions.findByUserIdAndRoomIds(userId, roomIds) : Subscriptions.findByUserIdExceptType(userId, 'd'); // Iterate through all the rooms the user is subscribed to, to check if he is the last owner of any of them. @@ -46,7 +47,7 @@ export function getSubscribedRoomsForUserWithDetails( if (numOwners === 1 && assignNewOwner) { // Let's check how many subscribers the room has. const options = { projection: { 'u._id': 1 }, sort: { ts: 1 } }; - const subscribersCursor: Cursor = Subscriptions.findByRoomId(subscription.rid, options); + const subscribersCursor: FindCursor = Subscriptions.findByRoomId(subscription.rid, options); subscribersCursor.forEach(({ u: { _id: uid } }) => { // If we already changed the owner or this subscription is for the user we are removing, then don't try to give it ownership diff --git a/apps/meteor/app/lib/server/functions/loadMessageHistory.ts b/apps/meteor/app/lib/server/functions/loadMessageHistory.ts index 6698b4da5221..ddfac9ba884f 100644 --- a/apps/meteor/app/lib/server/functions/loadMessageHistory.ts +++ b/apps/meteor/app/lib/server/functions/loadMessageHistory.ts @@ -4,7 +4,7 @@ import { Messages, Rooms } from '../../../models/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { getHiddenSystemMessages } from '../lib/getHiddenSystemMessages'; -export const loadMessageHistory = function loadMessageHistory({ +export function loadMessageHistory({ userId, rid, end, @@ -48,8 +48,7 @@ export const loadMessageHistory = function loadMessageHistory({ if (ls != null) { const firstMessage = messages[messages.length - 1]; - if ((firstMessage != null ? firstMessage.ts : undefined) > ls) { - // delete options.limit; + if (firstMessage && new Date(firstMessage.ts) > new Date(ls)) { const unreadMessages = Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes( rid, ls, @@ -64,8 +63,17 @@ export const loadMessageHistory = function loadMessageHistory({ showThreadMessages, ); + const totalCursor = Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes( + rid, + ls, + firstMessage.ts, + hiddenMessageTypes, + {}, + showThreadMessages, + ); + firstUnread = unreadMessages.fetch()[0]; - unreadNotLoaded = unreadMessages.count(); + unreadNotLoaded = totalCursor.count(); } } @@ -74,4 +82,4 @@ export const loadMessageHistory = function loadMessageHistory({ firstUnread, unreadNotLoaded, }; -}; +} diff --git a/apps/meteor/app/lib/server/functions/notifications/email.js b/apps/meteor/app/lib/server/functions/notifications/email.js index 8126497b28af..d9f9cf17d581 100644 --- a/apps/meteor/app/lib/server/functions/notifications/email.js +++ b/apps/meteor/app/lib/server/functions/notifications/email.js @@ -164,7 +164,7 @@ export function getEmailData({ message, receiver, sender, subscription, room, em // Reply-To header with format "username+messageId@domain" email.headers['Reply-To'] = `${replyto.split('@')[0].split(settings.get('Direct_Reply_Separator'))[0]}${settings.get( 'Direct_Reply_Separator', - )}${message._id}@${replyto.split('@')[1]}`; + )}${message.tmid || message._id}@${replyto.split('@')[1]}`; } metrics.notificationsSent.inc({ notification_type: 'email' }); diff --git a/apps/meteor/app/lib/server/functions/notifications/index.js b/apps/meteor/app/lib/server/functions/notifications/index.js index 16d45582614f..80ad6378df2d 100644 --- a/apps/meteor/app/lib/server/functions/notifications/index.js +++ b/apps/meteor/app/lib/server/functions/notifications/index.js @@ -3,7 +3,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { callbacks } from '../../../../../lib/callbacks'; -import { settings } from '../../../../settings'; +import { settings } from '../../../../settings/server'; /** * This function returns a string ready to be shown in the notification diff --git a/apps/meteor/app/lib/server/functions/notifications/mobile.js b/apps/meteor/app/lib/server/functions/notifications/mobile.js index 9c3b75cdfb28..88d125473639 100644 --- a/apps/meteor/app/lib/server/functions/notifications/mobile.js +++ b/apps/meteor/app/lib/server/functions/notifications/mobile.js @@ -1,7 +1,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Subscriptions } from '@rocket.chat/models'; -import { settings } from '../../../../settings'; -import { Subscriptions } from '../../../../models/server/raw'; +import { settings } from '../../../../settings/server'; import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; const CATEGORY_MESSAGE = 'MESSAGE'; diff --git a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts index 9c68ac5f9c5d..fd113c836563 100644 --- a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts +++ b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts @@ -1,6 +1,7 @@ +import { Roles } from '@rocket.chat/models'; + import { FileUpload } from '../../../file-upload/server'; import { Subscriptions, Messages, Rooms } from '../../../models/server'; -import { Roles } from '../../../models/server/raw'; import { SubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner'; const bulkRoomCleanUp = (rids: string[]): unknown => { diff --git a/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.js b/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.js index aaacb5062499..17fcbbb90f45 100644 --- a/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.js +++ b/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; -import { settings } from '../../../settings'; -import { Users, Subscriptions } from '../../../models'; +import { settings } from '../../../settings/server'; +import { Users, Subscriptions } from '../../../models/server'; export const saveCustomFieldsWithoutValidation = function (userId, formData) { if (s.trim(settings.get('Accounts_CustomFields')) !== '') { diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index 771f22d4d536..5b4ca2f43c2b 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -6,7 +6,7 @@ import { Gravatar } from 'meteor/jparker:gravatar'; import * as Mailer from '../../../mailer'; import { getRoles, hasPermission } from '../../../authorization'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { passwordPolicy } from '../lib/passwordPolicy'; import { validateEmailDomain } from '../lib'; import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; diff --git a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts index 7ba891c38a35..470b13b3d82e 100644 --- a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts +++ b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import type { IMessage } from '@rocket.chat/core-typings'; +import { VideoConference } from '@rocket.chat/models'; import { _setUsername } from './setUsername'; import { _setRealName } from './setRealName'; @@ -87,6 +88,9 @@ export function saveUserIdentity({ // update name and fname of group direct messages updateGroupDMsName(user); + + // update name and username of users on video conferences + Promise.await(VideoConference.updateUserReferences(user._id, username || previousUsername, name || previousName)); } } diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index 9c906439ab52..e80b527573f3 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -1,8 +1,8 @@ import { Match, check } from 'meteor/check'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/server'; import { Apps } from '../../../apps/server'; import { isURL } from '../../../../lib/utils/isURL'; import { FileUpload } from '../../../file-upload/server'; diff --git a/apps/meteor/app/lib/server/functions/setRoomAvatar.ts b/apps/meteor/app/lib/server/functions/setRoomAvatar.ts index 1cd2a2442b75..d67eafd1c51c 100644 --- a/apps/meteor/app/lib/server/functions/setRoomAvatar.ts +++ b/apps/meteor/app/lib/server/functions/setRoomAvatar.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import type { IUser } from '@rocket.chat/core-typings'; +import { Avatars } from '@rocket.chat/models'; import { RocketChatFile } from '../../../file'; import { FileUpload } from '../../../file-upload/server'; import { Rooms, Messages } from '../../../models/server'; -import { Avatars } from '../../../models/server/raw'; import { api } from '../../../../server/sdk/api'; export const setRoomAvatar = async function (rid: string, dataURI: string, user: IUser): Promise { diff --git a/apps/meteor/app/lib/server/functions/setStatusText.ts b/apps/meteor/app/lib/server/functions/setStatusText.ts index 23bda1463602..3077061f48b7 100644 --- a/apps/meteor/app/lib/server/functions/setStatusText.ts +++ b/apps/meteor/app/lib/server/functions/setStatusText.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users as UsersRaw } from '@rocket.chat/models'; import { Users } from '../../../models/server'; -import { Users as UsersRaw } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { RateLimiter } from '../lib'; import { api } from '../../../../server/sdk/api'; diff --git a/apps/meteor/app/lib/server/functions/setUsername.ts b/apps/meteor/app/lib/server/functions/setUsername.ts index 71cbc2afd557..6dc5eb4b0fb1 100644 --- a/apps/meteor/app/lib/server/functions/setUsername.ts +++ b/apps/meteor/app/lib/server/functions/setUsername.ts @@ -2,10 +2,10 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; import { Accounts } from 'meteor/accounts-base'; import type { IUser } from '@rocket.chat/core-typings'; +import { Invites } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { Users } from '../../../models/server'; -import { Invites } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; import { RateLimiter } from '../lib'; import { addUserToRoom } from './addUserToRoom'; diff --git a/apps/meteor/app/lib/server/functions/validateCustomFields.js b/apps/meteor/app/lib/server/functions/validateCustomFields.js index 37e953497134..b9580ce559d6 100644 --- a/apps/meteor/app/lib/server/functions/validateCustomFields.js +++ b/apps/meteor/app/lib/server/functions/validateCustomFields.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; export const validateCustomFields = function (fields) { // Special Case: diff --git a/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js b/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js index f00b0fc5762f..ba8783369de6 100644 --- a/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js +++ b/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js @@ -1,13 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import POP3Lib from 'poplib'; +import POP3Lib from '@rocket.chat/poplib'; import { simpleParser } from 'mailparser'; -import { settings } from '../../../settings/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings'; import { IMAPInterceptor } from '../../../../server/email/IMAPInterceptor'; import { processDirectEmail } from '.'; -export class IMAPIntercepter extends IMAPInterceptor { +export class DirectReplyIMAPInterceptor extends IMAPInterceptor { constructor(imapConfig, options = {}) { imapConfig = { user: settings.get('Direct_Reply_Username'), @@ -23,10 +21,7 @@ export class IMAPIntercepter extends IMAPInterceptor { super(imapConfig, options); - this.on( - 'email', - Meteor.bindEnvironment((email) => processDirectEmail(email)), - ); + this.on('email', (email) => processDirectEmail(email)); } } @@ -34,143 +29,120 @@ export class POP3Intercepter { constructor() { this.pop3 = new POP3Lib(settings.get('Direct_Reply_Port'), settings.get('Direct_Reply_Host'), { enabletls: !settings.get('Direct_Reply_IgnoreTLS'), - debug: settings.get('Direct_Reply_Debug') ? console.log : false, + // debug: settings.get('Direct_Reply_Debug') ? console.log : false, + debug: console.log, }); this.totalMsgCount = 0; this.currentMsgCount = 0; - this.pop3.on( - 'connect', - Meteor.bindEnvironment(() => { - this.pop3.login(settings.get('Direct_Reply_Username'), settings.get('Direct_Reply_Password')); - }), - ); - - this.pop3.on( - 'login', - Meteor.bindEnvironment((status) => { - if (status) { - // run on start - this.pop3.list(); - } else { - SystemLogger.info('Unable to Log-in ....'); - } - }), - ); + this.pop3.on('connect', () => { + console.log('Pop connect'); + this.pop3.login(settings.get('Direct_Reply_Username'), settings.get('Direct_Reply_Password')); + }); + + this.pop3.on('login', (status) => { + if (!status) { + return console.log('Unable to Log-in ....'); + } + console.log('Pop logged'); + // run on start + this.pop3.list(); + }); // on getting list of all emails - this.pop3.on( - 'list', - Meteor.bindEnvironment((status, msgcount) => { - if (status) { - if (msgcount > 0) { - this.totalMsgCount = msgcount; - this.currentMsgCount = 1; - // Retrieve email - this.pop3.retr(this.currentMsgCount); - } else { - this.pop3.quit(); - } - } else { - SystemLogger.info('Cannot Get Emails ....'); - } - }), - ); + this.pop3.on('list', (status, msgcount) => { + if (!status) { + console.log('Cannot Get Emails ....'); + } + if (msgcount === 0) { + return this.pop3.quit(); + } + + this.totalMsgCount = msgcount; + this.currentMsgCount = 1; + // Retrieve email + this.pop3.retr(this.currentMsgCount); + }); // on retrieved email - this.pop3.on( - 'retr', - Meteor.bindEnvironment((status, msgnumber, data) => { - if (status) { - // parse raw email data to JSON object - simpleParser( - data, - Meteor.bindEnvironment((err, mail) => { - this.initialProcess(mail); - }), - ); - - this.currentMsgCount += 1; - - // delete email - this.pop3.dele(msgnumber); - } else { - SystemLogger.info('Cannot Retrieve Message ....'); - } - }), - ); + this.pop3.on('retr', (status, msgnumber, data) => { + if (!status) { + return console.log('Cannot Retrieve Message ....'); + } + + // parse raw email data to JSON object + simpleParser(data, (err, mail) => { + processDirectEmail(mail); + }); + + this.currentMsgCount += 1; + + // delete email + this.pop3.dele(msgnumber); + }); // on email deleted - this.pop3.on( - 'dele', - Meteor.bindEnvironment((status) => { - if (status) { - // get next email - if (this.currentMsgCount <= this.totalMsgCount) { - this.pop3.retr(this.currentMsgCount); - } else { - // parsed all messages.. so quitting - this.pop3.quit(); - } - } else { - SystemLogger.info('Cannot Delete Message....'); - } - }), - ); + this.pop3.on('dele', (status) => { + if (!status) { + return console.log('Cannot Delete Message....'); + } + + // get next email + if (this.currentMsgCount <= this.totalMsgCount) { + return this.pop3.retr(this.currentMsgCount); + } + + // parsed all messages.. so quitting + this.pop3.quit(); + }); // invalid server state this.pop3.on('invalid-state', function (cmd) { - SystemLogger.info(`Invalid state. You tried calling ${cmd}`); + console.log(`Invalid state. You tried calling ${cmd}`); + }); + + this.pop3.on('error', function (cmd) { + console.log(`error state. You tried calling ${cmd}`); }); // locked => command already running, not finished yet this.pop3.on('locked', function (cmd) { - SystemLogger.info(`Current command has not finished yet. You tried calling ${cmd}`); + console.log(`Current command has not finished yet. You tried calling ${cmd}`); }); } - - initialProcess(mail) { - const email = { - headers: { - 'from': mail.from.text, - 'to': mail.to.text, - 'date': mail.date, - 'message-id': mail.messageId, - }, - body: mail.text, - }; - - processDirectEmail(email); - } } -export let POP3; export class POP3Helper { - constructor() { + constructor(frequency) { + this.frequency = frequency; this.running = false; - } - start() { - // run every x-minutes - if (settings.get('Direct_Reply_Frequency')) { - POP3 = new POP3Intercepter(); - - this.running = Meteor.setInterval(() => { - // get new emails and process - POP3 = new POP3Intercepter(); - }, Math.max(settings.get('Direct_Reply_Frequency') * 60 * 1000, 2 * 60 * 1000)); - } + this.POP3 = new POP3Intercepter(); } isActive() { return this.running; } + start() { + this.log('POP3 started'); + this.running = setInterval(() => { + // get new emails and process + this.POP3 = new POP3Intercepter(); + }, Math.max(this.frequency * 60 * 1000, 2 * 60 * 1000)); + } + + log(...args) { + console.log(...args); + } + stop(callback = new Function()) { + this.log('POP3 stop called'); if (this.isActive()) { - Meteor.clearInterval(this.running); + clearInterval(this.running); } callback(); + this.log('POP3 stopped'); } } diff --git a/apps/meteor/app/lib/server/lib/meteorFixes.js b/apps/meteor/app/lib/server/lib/meteorFixes.js index abc7a78855cb..0d7af6675f9a 100644 --- a/apps/meteor/app/lib/server/lib/meteorFixes.js +++ b/apps/meteor/app/lib/server/lib/meteorFixes.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; +import { Settings } from '@rocket.chat/models'; const timeoutQuery = parseInt(process.env.OBSERVERS_CHECK_TIMEOUT) || 2 * 60 * 1000; const interval = parseInt(process.env.OBSERVERS_CHECK_INTERVAL) || 60 * 1000; @@ -58,7 +59,9 @@ Meteor.setInterval(() => { * we will start respecting this and exit the process to prevent these kind of problems. */ -process.on('unhandledRejection', (error) => { +process.on('unhandledRejection', async (error) => { + await Settings.incrementValueById('Uncaught_Exceptions_Count'); + console.error('=== UnHandledPromiseRejection ==='); console.error(error); console.error('---------------------------------'); diff --git a/apps/meteor/app/lib/server/lib/processDirectEmail.js b/apps/meteor/app/lib/server/lib/processDirectEmail.js deleted file mode 100644 index 6bec346c8889..000000000000 --- a/apps/meteor/app/lib/server/lib/processDirectEmail.js +++ /dev/null @@ -1,128 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { EmailReplyParser as reply } from 'emailreplyparser'; -import moment from 'moment'; - -import { settings } from '../../../settings/server'; -import { Rooms, Messages, Users, Subscriptions } from '../../../models/server'; -import { metrics } from '../../../metrics/server'; -import { canAccessRoom, hasPermission } from '../../../authorization/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { sendMessage as _sendMessage } from '../functions'; - -export const processDirectEmail = function (email) { - function sendMessage(email) { - const message = { - ts: new Date(email.headers.date), - msg: email.body, - sentByEmail: true, - groupable: false, - }; - - if (message.ts) { - const tsDiff = Math.abs(moment(message.ts).diff()); - if (tsDiff > 10000) { - message.ts = new Date(); - } - } else { - message.ts = new Date(); - } - - if (message.msg && message.msg.length > settings.get('Message_MaxAllowedSize')) { - return false; - } - - // reduce new lines in multiline message - message.msg = message.msg.split('\n\n').join('\n'); - - const user = Users.findOneByEmailAddress(email.headers.from, { - fields: { - username: 1, - name: 1, - }, - }); - if (!user) { - // user not found - return false; - } - - const prevMessage = Messages.findOneById(email.headers.mid, { - rid: 1, - u: 1, - }); - if (!prevMessage) { - // message doesn't exist anymore - return false; - } - message.rid = prevMessage.rid; - - const room = Rooms.findOneById(message.rid); - - if (!canAccessRoom(room, user)) { - return false; - } - - // check mention - if (message.msg.indexOf(`@${prevMessage.u.username}`) === -1 && room.t !== 'd') { - message.msg = `@${prevMessage.u.username} ${message.msg}`; - } - - // reply message link - let prevMessageLink = `[ ](${Meteor.absoluteUrl().replace(/\/$/, '')}`; - if (room.t === 'c') { - prevMessageLink += `/channel/${room.name}?msg=${email.headers.mid}) `; - } else if (room.t === 'd') { - prevMessageLink += `/direct/${prevMessage.u.username}?msg=${email.headers.mid}) `; - } else if (room.t === 'p') { - prevMessageLink += `/group/${room.name}?msg=${email.headers.mid}) `; - } - // add reply message link - message.msg = prevMessageLink + message.msg; - - const subscription = Subscriptions.findOneByRoomIdAndUserId(message.rid, user._id); - if (subscription && (subscription.blocked || subscription.blocker)) { - // room is blocked - return false; - } - - if (room?.muted?.includes(user.username)) { - // user is muted - return false; - } - - // room is readonly - if (room.ro === true) { - if (!hasPermission(Meteor.userId(), 'post-readonly', room._id)) { - // Check if the user was manually unmuted - if (!(room.unmuted || []).includes(user.username)) { - return false; - } - } - } - - metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736 - - return _sendMessage(user, message, room); - } - - // Extract/parse reply from email body - email.body = reply.parse_reply(email.body); - - // if 'To' email format is "Name " - if (email.headers.to.indexOf('<') >= 0 && email.headers.to.indexOf('>') >= 0) { - email.headers.to = email.headers.to.split('<')[1].split('>')[0]; - } - - // if 'From' email format is "Name " - if (email.headers.from.indexOf('<') >= 0 && email.headers.from.indexOf('>') >= 0) { - email.headers.from = email.headers.from.split('<')[1].split('>')[0]; - } - - // 'To' email format "username+messageId@domain" - if (email.headers.to.indexOf('+') >= 0) { - // Valid 'To' format - email.headers.mid = email.headers.to.split('@')[0].split('+')[1]; - sendMessage(email); - } else { - SystemLogger.error('Invalid Email....If not. Please report it.'); - } -}; diff --git a/apps/meteor/app/lib/server/lib/processDirectEmail.ts b/apps/meteor/app/lib/server/lib/processDirectEmail.ts new file mode 100644 index 000000000000..0819f4475626 --- /dev/null +++ b/apps/meteor/app/lib/server/lib/processDirectEmail.ts @@ -0,0 +1,115 @@ +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; +import { ParsedMail } from 'mailparser'; +import { IMessage, IRoom } from '@rocket.chat/core-typings'; + +import { settings } from '../../../settings/server'; +import { Rooms, Messages, Users, Subscriptions } from '../../../models/server'; +import { metrics } from '../../../metrics'; +import { canAccessRoom, hasPermission } from '../../../authorization/server'; +import { sendMessage } from '../functions/sendMessage'; + +const isParsedEmail = (email: ParsedMail): email is Required => 'date' in email && 'html' in email; + +export const processDirectEmail = Meteor.bindEnvironment(function (email: ParsedMail): void { + if (!isParsedEmail(email)) { + return; + } + + const to = Array.isArray(email.to) ? email.to.find((email) => email.text) : email.to; + const mid = to?.text.split('@')[0].split('+')[1]; + + if (!mid) { + return; + } + + const ts = new Date(email.date); + + const tsDiff = Math.abs(moment(ts).diff(new Date())); + + let msg = email.text.split('\n\n').join('\n'); + + if (msg && msg.length > (settings.get('Message_MaxAllowedSize') as number)) { + return; + } + + const user = Users.findOneByEmailAddress(email.from.value[0].address, { + fields: { + username: 1, + name: 1, + }, + }); + + if (!user) { + // user not found + return; + } + + const prevMessage = Messages.findOneById(mid, { + rid: 1, + u: 1, + }); + + if (!prevMessage) { + // message doesn't exist anymore + return; + } + + const roomInfo: IRoom = Rooms.findOneById(prevMessage.rid); + + const room = canAccessRoom(roomInfo, user); + if (!room) { + return; + } + + // check mention + if (msg.indexOf(`@${prevMessage.u.username}`) === -1 && roomInfo.t !== 'd') { + msg = `@${prevMessage.u.username} ${msg}`; + } + + // reply message link WE HA THREADS KNOW DONT NEED TO QUOTE THE previous message + // let prevMessageLink = `[ ](${ Meteor.absoluteUrl().replace(/\/$/, '') }`; + // if (roomInfo.t === 'c') { + // prevMessageLink += `/channel/${ roomInfo.name }?msg=${ mid }) `; + // } else if (roomInfo.t === 'd') { + // prevMessageLink += `/direct/${ prevMessage.u.username }?msg=${ mid }) `; + // } else if (roomInfo.t === 'p') { + // prevMessageLink += `/group/${ roomInfo.name }?msg=${ mid }) `; + // } + // // add reply message link + // msg = prevMessageLink + msg; + + const subscription = Subscriptions.findOneByRoomIdAndUserId(prevMessage.rid, user._id); + if (subscription && (subscription.blocked || subscription.blocker)) { + // room is blocked + return; + } + + if ((roomInfo.muted || []).includes(user.username)) { + // user is muted + return; + } + + // room is readonly + if (roomInfo.ro === true) { + if (!hasPermission(user._id, 'post-readonly', roomInfo._id)) { + // Check if the user was manually unmuted + if (!(roomInfo.unmuted || []).includes(user.username)) { + return; + } + } + } + + metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736 + + const message: Pick = { + ts: tsDiff < 10000 ? ts : new Date(), + msg, + sentByEmail: true, + groupable: false, + tmid: mid, + rid: prevMessage.rid, + }; + + return sendMessage(user, message, roomInfo); +}); diff --git a/apps/meteor/app/lib/server/methods/addUsersToRoom.js b/apps/meteor/app/lib/server/methods/addUsersToRoom.js index bdec86a80db5..99c5db5d011b 100644 --- a/apps/meteor/app/lib/server/methods/addUsersToRoom.js +++ b/apps/meteor/app/lib/server/methods/addUsersToRoom.js @@ -2,10 +2,11 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { Rooms, Subscriptions, Users } from '../../../models'; +import { Rooms, Subscriptions, Users } from '../../../models/server'; import { hasPermission } from '../../../authorization'; import { addUserToRoom } from '../functions'; import { api } from '../../../../server/sdk/api'; +import { Federation } from '../../../federation-v2/server/infrastructure/rocket-chat/Federation'; Meteor.methods({ addUsersToRoom(data = {}) { @@ -65,14 +66,14 @@ Meteor.methods({ const user = Meteor.user(); data.users.forEach((username) => { const newUser = Users.findOneByUsernameIgnoringCase(username); - if (!newUser) { + if (!newUser && !Federation.isAFederatedUsername(username)) { throw new Meteor.Error('error-invalid-username', 'Invalid username', { method: 'addUsersToRoom', }); } - const subscription = Subscriptions.findOneByRoomIdAndUserId(data.rid, newUser._id); + const subscription = newUser && Subscriptions.findOneByRoomIdAndUserId(data.rid, newUser._id); if (!subscription) { - addUserToRoom(data.rid, newUser, user); + addUserToRoom(data.rid, newUser || username, user); } else { api.broadcast('notify.ephemeralMessage', userId, data.rid, { msg: TAPi18n.__( diff --git a/apps/meteor/app/lib/server/methods/filterATAllTag.js b/apps/meteor/app/lib/server/methods/filterATAllTag.js index 67ae6da78a2c..2c1d52a95093 100644 --- a/apps/meteor/app/lib/server/methods/filterATAllTag.js +++ b/apps/meteor/app/lib/server/methods/filterATAllTag.js @@ -5,7 +5,7 @@ import moment from 'moment'; import { hasPermission } from '../../../authorization'; import { callbacks } from '../../../../lib/callbacks'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { api } from '../../../../server/sdk/api'; callbacks.add( diff --git a/apps/meteor/app/lib/server/methods/filterATHereTag.js b/apps/meteor/app/lib/server/methods/filterATHereTag.js index 9691c416cb82..0978520e1cdd 100644 --- a/apps/meteor/app/lib/server/methods/filterATHereTag.js +++ b/apps/meteor/app/lib/server/methods/filterATHereTag.js @@ -5,7 +5,7 @@ import moment from 'moment'; import { hasPermission } from '../../../authorization'; import { callbacks } from '../../../../lib/callbacks'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { api } from '../../../../server/sdk/api'; callbacks.add( diff --git a/apps/meteor/app/lib/server/methods/getChannelHistory.ts b/apps/meteor/app/lib/server/methods/getChannelHistory.ts index 4557282191a1..948508dd5a3a 100644 --- a/apps/meteor/app/lib/server/methods/getChannelHistory.ts +++ b/apps/meteor/app/lib/server/methods/getChannelHistory.ts @@ -105,8 +105,17 @@ Meteor.methods({ showThreadMessages, ); + const totalCursor = Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes( + rid, + oldest, + firstMsg.ts, + hiddenMessageTypes, + {}, + showThreadMessages, + ); + firstUnread = unreadMessages.fetch()[0]; - unreadNotLoaded = unreadMessages.count(); + unreadNotLoaded = totalCursor.count(); } } diff --git a/apps/meteor/app/lib/server/methods/getRoomRoles.ts b/apps/meteor/app/lib/server/methods/getRoomRoles.ts index 879c53db5d0e..2e260e6519c8 100644 --- a/apps/meteor/app/lib/server/methods/getRoomRoles.ts +++ b/apps/meteor/app/lib/server/methods/getRoomRoles.ts @@ -3,16 +3,26 @@ import { check } from 'meteor/check'; import { settings } from '../../../settings/server'; import { getRoomRoles } from '../../../../server/lib/roles/getRoomRoles'; +import { canAccessRoom } from '../../../authorization/server'; +import { Rooms } from '../../../models/server'; Meteor.methods({ getRoomRoles(rid) { check(rid, String); + const fromUserId = Meteor.userId(); - if (!Meteor.userId() && settings.get('Accounts_AllowAnonymousRead') === false) { + if (!fromUserId && settings.get('Accounts_AllowAnonymousRead') === false) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getRoomRoles' }); } - check(rid, String); + const room = Rooms.findOneById(rid); + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getRoomRoles' }); + } + + if (fromUserId && !canAccessRoom(room, { _id: fromUserId })) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getRoomRoles' }); + } return getRoomRoles(rid); }, diff --git a/apps/meteor/app/lib/server/methods/leaveRoom.ts b/apps/meteor/app/lib/server/methods/leaveRoom.ts index 9db1d228e1d0..d9216f65df59 100644 --- a/apps/meteor/app/lib/server/methods/leaveRoom.ts +++ b/apps/meteor/app/lib/server/methods/leaveRoom.ts @@ -1,11 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import type { IUser } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; import { hasPermission, hasRole } from '../../../authorization/server'; import { Subscriptions, Rooms } from '../../../models/server'; import { removeUserFromRoom } from '../functions'; -import { Roles } from '../../../models/server/raw'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; @@ -40,7 +40,7 @@ Meteor.methods({ // If user is room owner, check if there are other owners. If there isn't anyone else, warn user to set a new owner. if (hasRole(user._id, 'owner', room._id)) { const cursor = await Roles.findUsersInRole('owner', room._id); - const numOwners = Promise.await(cursor.count()); + const numOwners = await cursor.count(); if (numOwners === 1) { throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', { method: 'leaveRoom', diff --git a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts index 466316f4ba76..b77b3e213c86 100644 --- a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; +import { Settings } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { Settings } from '../../../models/server/raw'; Meteor.methods({ async refreshOAuthService() { diff --git a/apps/meteor/app/lib/server/methods/removeOAuthService.ts b/apps/meteor/app/lib/server/methods/removeOAuthService.ts index 419992e2b0e5..9ef34bf59088 100644 --- a/apps/meteor/app/lib/server/methods/removeOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/removeOAuthService.ts @@ -1,9 +1,9 @@ import { capitalize } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { Settings } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { Settings } from '../../../models/server/raw'; Meteor.methods({ async removeOAuthService(name) { diff --git a/apps/meteor/app/lib/server/methods/restartServer.ts b/apps/meteor/app/lib/server/methods/restartServer.ts index f321461d6359..5de183a1019b 100644 --- a/apps/meteor/app/lib/server/methods/restartServer.ts +++ b/apps/meteor/app/lib/server/methods/restartServer.ts @@ -3,7 +3,6 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization/server'; Meteor.methods({ - // eslint-disable-next-line @typescript-eslint/camelcase restart_server() { const uid = Meteor.userId(); diff --git a/apps/meteor/app/lib/server/methods/saveSetting.js b/apps/meteor/app/lib/server/methods/saveSetting.js index 5c24bd377bdc..4e8829fb07bc 100644 --- a/apps/meteor/app/lib/server/methods/saveSetting.js +++ b/apps/meteor/app/lib/server/methods/saveSetting.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Settings } from '@rocket.chat/models'; import { hasPermission, hasAllPermission } from '../../../authorization/server'; import { getSettingPermissionId } from '../../../authorization/lib'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; -import { Settings } from '../../../models/server/raw'; Meteor.methods({ saveSetting: twoFactorRequired(async function (_id, value, editor) { diff --git a/apps/meteor/app/lib/server/methods/saveSettings.js b/apps/meteor/app/lib/server/methods/saveSettings.js index 137ca376a53e..5bbfd4d2cfea 100644 --- a/apps/meteor/app/lib/server/methods/saveSettings.js +++ b/apps/meteor/app/lib/server/methods/saveSettings.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Settings } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; import { getSettingPermissionId } from '../../../authorization/lib'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; -import { Settings } from '../../../models/server/raw'; Meteor.methods({ saveSettings: twoFactorRequired(async function (params = []) { diff --git a/apps/meteor/app/lib/server/methods/sendInvitationEmail.js b/apps/meteor/app/lib/server/methods/sendInvitationEmail.js index 89cbdce9eb97..fe33f73fe323 100644 --- a/apps/meteor/app/lib/server/methods/sendInvitationEmail.js +++ b/apps/meteor/app/lib/server/methods/sendInvitationEmail.js @@ -3,7 +3,7 @@ import { check } from 'meteor/check'; import * as Mailer from '../../../mailer'; import { hasPermission } from '../../../authorization'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { Settings as SettingsRaw } from '../../../models/server'; let html = ''; diff --git a/apps/meteor/app/lib/server/methods/sendMessage.js b/apps/meteor/app/lib/server/methods/sendMessage.js index 3cf002ba4e0b..e5c7f4e99f13 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.js +++ b/apps/meteor/app/lib/server/methods/sendMessage.js @@ -5,7 +5,7 @@ import moment from 'moment'; import { hasPermission } from '../../../authorization'; import { metrics } from '../../../metrics'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { messageProperties } from '../../../ui-utils'; import { Users, Messages } from '../../../models'; import { sendMessage } from '../functions'; diff --git a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.js b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.js index 56432497522d..da8648a2bce5 100644 --- a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.js +++ b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import * as Mailer from '../../../mailer'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; Meteor.methods({ sendSMTPTestEmail() { diff --git a/apps/meteor/app/lib/server/methods/updateMessage.js b/apps/meteor/app/lib/server/methods/updateMessage.js index 34fec260eefc..a615d679c52b 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.js +++ b/apps/meteor/app/lib/server/methods/updateMessage.js @@ -2,8 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import moment from 'moment'; -import { Messages } from '../../../models'; -import { settings } from '../../../settings'; +import { Messages } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { hasPermission, canSendMessage } from '../../../authorization/server'; import { updateMessage } from '../functions'; @@ -50,13 +50,13 @@ Meteor.methods({ let currentTsDiff; let msgTs; - if (Match.test(originalMessage.ts, Number)) { + if (originalMessage.ts instanceof Date || Match.test(originalMessage.ts, Number)) { msgTs = moment(originalMessage.ts); } if (msgTs) { currentTsDiff = moment().diff(msgTs, 'minutes'); } - if (currentTsDiff > blockEditInMinutes) { + if (currentTsDiff >= blockEditInMinutes) { throw new Meteor.Error('error-message-editing-blocked', 'Message editing is blocked', { method: 'updateMessage', }); diff --git a/apps/meteor/app/lib/server/oauth/proxy.js b/apps/meteor/app/lib/server/oauth/proxy.js index ef0b12c39301..1ea4db77f257 100644 --- a/apps/meteor/app/lib/server/oauth/proxy.js +++ b/apps/meteor/app/lib/server/oauth/proxy.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import { OAuth } from 'meteor/oauth'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; OAuth._redirectUri = _.wrap(OAuth._redirectUri, function (func, serviceName, ...args) { const proxy = settings.get('Accounts_OAuth_Proxy_services').replace(/\s/g, '').split(','); diff --git a/apps/meteor/app/lib/server/startup/email.ts b/apps/meteor/app/lib/server/startup/email.ts index 470599a41d52..bac864aa52b3 100644 --- a/apps/meteor/app/lib/server/startup/email.ts +++ b/apps/meteor/app/lib/server/startup/email.ts @@ -102,6 +102,9 @@ settingsRegistry.addGroup('Email', function () { } .social { font-size: 12px + } + .rc-color { + color: #F5455C; } `, { diff --git a/apps/meteor/app/lib/server/startup/robots.js b/apps/meteor/app/lib/server/startup/robots.js index 2ec4e3c19b4e..c7dbfb442462 100644 --- a/apps/meteor/app/lib/server/startup/robots.js +++ b/apps/meteor/app/lib/server/startup/robots.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; Meteor.startup(function () { return WebApp.connectHandlers.use( diff --git a/apps/meteor/app/lib/server/startup/settings.ts b/apps/meteor/app/lib/server/startup/settings.ts index 112241a71f69..e2d0500711d5 100644 --- a/apps/meteor/app/lib/server/startup/settings.ts +++ b/apps/meteor/app/lib/server/startup/settings.ts @@ -426,10 +426,23 @@ settingsRegistry.addGroup('Accounts', function () { i18nLabel: 'Sort_By', }); - this.add('Accounts_Default_User_Preferences_showMessageInMainThread', false, { - type: 'boolean', + this.add('Accounts_Default_User_Preferences_alsoSendThreadToChannel', 'default', { + type: 'select', + values: [ + { + key: 'default', + i18nLabel: 'Default', + }, + { + key: 'always', + i18nLabel: 'Always', + }, + { + key: 'never', + i18nLabel: 'Never', + }, + ], public: true, - i18nLabel: 'Show_Message_In_Main_Thread', }); this.add('Accounts_Default_User_Preferences_sidebarShowFavorites', true, { @@ -457,6 +470,7 @@ settingsRegistry.addGroup('Accounts', function () { public: true, i18nLabel: 'Enter_Behaviour', }); + this.add('Accounts_Default_User_Preferences_messageViewMode', 0, { type: 'select', values: [ @@ -534,11 +548,11 @@ settingsRegistry.addGroup('Accounts', function () { i18nLabel: 'Notifications_Sound_Volume', }); - this.add('Accounts_Default_User_Preferences_enableNewMessageTemplate', false, { + this.add('Accounts_Default_User_Preferences_useLegacyMessageTemplate', false, { type: 'boolean', public: true, - i18nLabel: 'Enable_New_Message_Template', - alert: 'Enable_New_Message_Template_alert', + i18nLabel: 'Use_Legacy_Message_Template', + alert: 'Use_Legacy_Message_Template_alert', }); }); @@ -829,7 +843,6 @@ settingsRegistry.addGroup('General', function () { ], }); - // eslint-disable-next-line @typescript-eslint/camelcase this.add( 'Site_Url', typeof (global as any).__meteor_runtime_config__ !== 'undefined' && (global as any).__meteor_runtime_config__ !== null @@ -1702,6 +1715,11 @@ settingsRegistry.addGroup('Logs', function () { }, }); + this.add('Uncaught_Exceptions_Count', 0, { + hidden: true, + type: 'int', + }); + this.section('Prometheus', function () { this.add('Prometheus_Enabled', false, { type: 'boolean', @@ -2911,6 +2929,10 @@ settingsRegistry.addGroup('Setup_Wizard', function () { this.add('Organization_Email', '', { type: 'string', }); + this.add('Triggered_Emails_Count', 0, { + type: 'int', + hidden: true, + }); }); this.section('Cloud_Info', function () { @@ -3187,7 +3209,6 @@ settingsRegistry.addGroup('Call_Center', function () { this.add('VoIP_Enabled', false, { type: 'boolean', public: true, - alert: 'Experimental_Feature_Alert', enableQuery: { _id: 'Livechat_enabled', value: true, @@ -3202,22 +3223,6 @@ settingsRegistry.addGroup('Call_Center', function () { }, }); this.section('Server_Configuration', function () { - this.add('VoIP_Server_Host', '', { - type: 'string', - public: true, - enableQuery: { - _id: 'VoIP_Enabled', - value: true, - }, - }); - this.add('VoIP_Server_Websocket_Port', 0, { - type: 'int', - public: true, - enableQuery: { - _id: 'VoIP_Enabled', - value: true, - }, - }); this.add('VoIP_Server_Name', '', { type: 'string', public: true, diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js deleted file mode 100644 index a58aacd884a9..000000000000 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js +++ /dev/null @@ -1,99 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Logger } from '../../../logger/server'; -import { settings } from '../../../settings/server'; -import { IMAPIntercepter, POP3Helper, POP3 } from '../lib/interceptDirectReplyEmails.js'; - -const logger = new Logger('Email Intercepter'); - -let IMAP; -let _POP3Helper; - -settings.watchMultiple( - [ - 'Direct_Reply_Enable', - 'Direct_Reply_Protocol', - 'Direct_Reply_Host', - 'Direct_Reply_Port', - 'Direct_Reply_Username', - 'Direct_Reply_Password', - 'Direct_Reply_Protocol', - 'Direct_Reply_Protocol', - ], - function () { - logger.debug('Starting Email Intercepter...'); - - if ( - settings.get('Direct_Reply_Enable') && - settings.get('Direct_Reply_Protocol') && - settings.get('Direct_Reply_Host') && - settings.get('Direct_Reply_Port') && - settings.get('Direct_Reply_Username') && - settings.get('Direct_Reply_Password') - ) { - if (settings.get('Direct_Reply_Protocol') === 'IMAP') { - // stop already running IMAP instance - if (IMAP && IMAP.isActive()) { - logger.debug('Disconnecting already running IMAP instance...'); - IMAP.stop( - Meteor.bindEnvironment(function () { - logger.debug('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - }), - ); - } else if (POP3 && _POP3Helper && _POP3Helper.isActive()) { - logger.debug('Disconnecting already running POP instance...'); - _POP3Helper.stop( - Meteor.bindEnvironment(function () { - logger.debug('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - }), - ); - } else { - logger.debug('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - } - } else if (settings.get('Direct_Reply_Protocol') === 'POP') { - // stop already running POP instance - if (POP3 && _POP3Helper && _POP3Helper.isActive()) { - logger.debug('Disconnecting already running POP instance...'); - _POP3Helper.stop( - Meteor.bindEnvironment(function () { - logger.debug('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - }), - ); - } else if (IMAP && IMAP.isActive()) { - logger.debug('Disconnecting already running IMAP instance...'); - IMAP.stop( - Meteor.bindEnvironment(function () { - logger.debug('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - }), - ); - } else { - logger.debug('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - } - } - } else if (IMAP && IMAP.isActive()) { - // stop IMAP instance - IMAP.stop(); - } else if (POP3 && _POP3Helper.isActive()) { - // stop POP3 instance - _POP3Helper.stop(); - } - }, -); diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts new file mode 100644 index 000000000000..0b4f55d9d831 --- /dev/null +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts @@ -0,0 +1,43 @@ +import _ from 'underscore'; + +import { settings } from '../../../settings/server'; +import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/interceptDirectReplyEmails.js'; +import { logger } from '../../../../server/features/EmailInbox/logger'; + +let client: DirectReplyIMAPInterceptor | POP3Helper | undefined; +const startEmailInterceptor = _.debounce(async function () { + logger.info('Email Interceptor...'); + const protocol = settings.get('Direct_Reply_Protocol'); + + const isEnabled = + settings.get('Direct_Reply_Enable') && + protocol && + settings.get('Direct_Reply_Host') && + settings.get('Direct_Reply_Port') && + settings.get('Direct_Reply_Username') && + settings.get('Direct_Reply_Password'); + + if (client) { + await client.stop(); + } + + if (!isEnabled) { + logger.info('Email Interceptor Stopped...'); + return; + } + logger.info('Starting Email Interceptor...'); + + if (protocol === 'IMAP') { + client = new DirectReplyIMAPInterceptor(); + client.start(); + } + + if (protocol === 'POP') { + client = new POP3Helper(settings.get('Direct_Reply_Frequency')); + client.start(); + } +}, 1000); + +settings.watchByRegex(/^Direct_Reply_.+/, startEmailInterceptor); + +startEmailInterceptor(); diff --git a/apps/meteor/app/livechat/client/index.js b/apps/meteor/app/livechat/client/index.js index 19a5118b809f..23da09e08a29 100644 --- a/apps/meteor/app/livechat/client/index.js +++ b/apps/meteor/app/livechat/client/index.js @@ -6,4 +6,3 @@ import './startup/notifyUnreadRooms'; import './views/app/dialog/closeRoom'; import './stylesheets/livechat.css'; import './externalFrame'; -import './lib/messageTypes'; diff --git a/apps/meteor/app/livechat/client/lib/chartHandler.js b/apps/meteor/app/livechat/client/lib/chartHandler.js deleted file mode 100644 index b49d09002fa9..000000000000 --- a/apps/meteor/app/livechat/client/lib/chartHandler.js +++ /dev/null @@ -1,221 +0,0 @@ -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -const lineChartConfiguration = ({ legends = false, anim = false, smallTicks = false, displayColors = false, tooltipCallbacks = {} }) => { - const config = { - layout: { - padding: { - top: 10, - bottom: 0, - }, - }, - legend: { - display: false, - }, - title: { - display: false, - }, - tooltips: { - enabled: true, - mode: 'point', - displayColors, - ...tooltipCallbacks, - }, - scales: { - xAxes: [ - { - scaleLabel: { - display: false, - }, - gridLines: { - display: true, - color: 'rgba(0, 0, 0, 0.03)', - }, - }, - ], - yAxes: [ - { - scaleLabel: { - display: false, - }, - gridLines: { - display: true, - color: 'rgba(0, 0, 0, 0.03)', - }, - ticks: { - beginAtZero: true, - }, - }, - ], - }, - hover: { - animationDuration: 0, // duration of animations when hovering an item - }, - responsive: true, - maintainAspectRatio: false, - responsiveAnimationDuration: 0, // animation duration after a resize - }; - - if (!anim) { - config.animation = { - duration: 0, // general animation time - }; - } - - if (legends) { - config.legend = { - display: true, - labels: { - boxWidth: 20, - fontSize: 8, - }, - }; - } - - if (smallTicks) { - config.scales.xAxes[0].ticks = { - fontSize: 8, - }; - - config.scales.yAxes[0].ticks = { - beginAtZero: true, - fontSize: 8, - }; - } - - return config; -}; - -const doughnutChartConfiguration = (title, tooltipCallbacks = {}) => ({ - layout: { - padding: { - top: 0, - bottom: 0, - }, - }, - legend: { - display: true, - position: 'right', - labels: { - boxWidth: 20, - fontSize: 8, - }, - }, - title: { - display: 'true', - text: title, - }, - tooltips: { - enabled: true, - mode: 'point', - displayColors: false, // hide color box - ...tooltipCallbacks, - }, - // animation: { - // duration: 0 // general animation time - // }, - hover: { - animationDuration: 0, // duration of animations when hovering an item - }, - responsive: true, - maintainAspectRatio: false, - responsiveAnimationDuration: 0, // animation duration after a resize -}); - -/** - * - * @param {Object} chart - chart element - * @param {Object} chartContext - Context of chart - * @param {Array(String)} chartLabel - * @param {Array(String)} dataLabels - * @param {Array(Array(Double))} dataPoints - */ -export const drawLineChart = async (chart, chartContext, chartLabels, dataLabels, dataSets, options = {}) => { - if (!chart) { - console.log('No chart element'); - return; - } - if (chartContext) { - chartContext.destroy(); - } - const colors = ['#2de0a5', '#ffd21f', '#f5455c', '#cbced1']; - - const datasets = []; - - chartLabels.forEach(function (chartLabel, index) { - datasets.push({ - label: TAPi18n.__(chartLabel), // chart label - data: dataSets[index], // data points corresponding to data labels, x-axis points - backgroundColor: [colors[index]], - borderColor: [colors[index]], - borderWidth: 3, - fill: false, - }); - }); - const chartImport = await import('chart.js'); - const Chart = chartImport.default; - return new Chart(chart, { - type: 'line', - data: { - labels: dataLabels, // data labels, y-axis points - datasets, - }, - options: lineChartConfiguration(options), - }); -}; - -/** - * - * @param {Object} chart - chart element - * @param {Object} chartContext - Context of chart - * @param {Array(String)} dataLabels - * @param {Array(Double)} dataPoints - */ -export const drawDoughnutChart = async (chart, title, chartContext, dataLabels, dataPoints) => { - if (!chart) { - return; - } - if (chartContext) { - chartContext.destroy(); - } - const chartImport = await import('chart.js'); - const Chart = chartImport.default; - return new Chart(chart, { - type: 'doughnut', - data: { - labels: dataLabels, // data labels, y-axis points - datasets: [ - { - data: dataPoints, // data points corresponding to data labels, x-axis points - backgroundColor: ['#2de0a5', '#cbced1', '#f5455c', '#ffd21f'], - borderWidth: 0, - }, - ], - }, - options: doughnutChartConfiguration(title), - }); -}; - -/** - * Update chart - * @param {Object} chart [Chart context] - * @param {String} label [chart label] - * @param {Array(Double)} data [updated data] - */ -export const updateChart = async (c, label, data) => { - const chart = await c; - if (chart.data.labels.indexOf(label) === -1) { - // insert data - chart.data.labels.push(label); - chart.data.datasets.forEach((dataset, idx) => { - dataset.data.push(data[idx]); - }); - } else { - // update data - const index = chart.data.labels.indexOf(label); - chart.data.datasets.forEach((dataset, idx) => { - dataset.data[index] = data[idx]; - }); - } - - chart.update(); -}; diff --git a/apps/meteor/app/livechat/client/lib/chartHandler.ts b/apps/meteor/app/livechat/client/lib/chartHandler.ts new file mode 100644 index 000000000000..7f9b6f1a6f4f --- /dev/null +++ b/apps/meteor/app/livechat/client/lib/chartHandler.ts @@ -0,0 +1,232 @@ +import type { ChartItem, Chart as ChartType, ChartConfiguration } from 'chart.js'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +type LineChartConfigOptions = Partial<{ + legends: boolean; + anim: boolean; + displayColors: boolean; + tooltipCallbacks: any; +}>; + +const lineChartConfiguration = ({ + legends = false, + anim = false, + tooltipCallbacks = {}, +}: LineChartConfigOptions): Partial['options']> => { + const config: ChartConfiguration<'line', number, string>['options'] = { + layout: { + padding: { + top: 10, + bottom: 0, + }, + }, + legend: { + display: false, + }, + plugins: { + tooltip: { + usePointStyle: true, + enabled: true, + mode: 'point', + yAlign: 'bottom', + displayColors: true, + ...tooltipCallbacks, + }, + }, + scales: { + xAxis: { + title: { + display: false, + }, + grid: { + display: true, + color: 'rgba(0, 0, 0, 0.03)', + }, + }, + yAxis: { + title: { + display: false, + }, + grid: { + display: true, + color: 'rgba(0, 0, 0, 0.03)', + }, + }, + }, + hover: { + intersect: false, // duration of animations when hovering an item + mode: 'index', + }, + responsive: true, + maintainAspectRatio: false, + ...(!anim ? { animation: { duration: 0 } } : {}), + ...(legends ? { legend: { display: true, labels: { boxWidth: 20, fontSize: 8 } } } : {}), + }; + + return config; +}; + +const doughnutChartConfiguration = ( + title: string, + tooltipCallbacks = {}, +): Partial['options']> => ({ + layout: { + padding: { + top: 0, + bottom: 0, + }, + }, + plugins: { + legend: { + display: true, + position: 'right', + labels: { + boxWidth: 20, + }, + }, + title: { + display: true, + text: title, + }, + tooltip: { + enabled: true, + mode: 'point', + displayColors: true, // hide color box + ...tooltipCallbacks, + }, + }, + // animation: { + // duration: 0 // general animation time + // }, + hover: { + intersect: true, // duration of animations when hovering an item + }, + responsive: true, + maintainAspectRatio: false, +}); + +type ChartDataSet = { + label: string; + data: number; + backgroundColor: string; + borderColor: string; + borderWidth: number; + fill: boolean; +}; + +/** + * + * @param {Object} chart - chart element + * @param {Object} chartContext - Context of chart + * @param {Array(String)} chartLabel + * @param {Array(String)} dataLabels + * @param {Array(Array(Double))} dataPoints + */ +export const drawLineChart = async ( + chart: HTMLCanvasElement, + chartContext: { destroy: () => void } | undefined, + chartLabels: string[], + dataLabels: string[], + dataSets: number[], + options: LineChartConfigOptions = {}, +): Promise | void> => { + if (!chart) { + console.error('No chart element'); + return; + } + if (chartContext) { + chartContext.destroy(); + } + const colors = ['#2de0a5', '#ffd21f', '#f5455c', '#cbced1']; + + const datasets: ChartDataSet[] = []; + + chartLabels.forEach(function (chartLabel: string, index: number) { + datasets.push({ + label: TAPi18n.__(chartLabel), // chart label + data: dataSets[index], // data points corresponding to data labels, x-axis points + backgroundColor: colors[index], + borderColor: colors[index], + borderWidth: 3, + fill: false, + }); + }); + const chartjs = await import('chart.js/auto'); + const Chart = chartjs.default; + return new Chart(chart, { + type: 'line', + data: { + labels: dataLabels, // data labels, y-axis points + datasets, + }, + options: lineChartConfiguration(options), + }); +}; + +/** + * + * @param {Object} chart - chart element + * @param {Object} chartContext - Context of chart + * @param {Array(String)} dataLabels + * @param {Array(Double)} dataPoints + */ +export const drawDoughnutChart = async ( + chart: ChartItem, + title: string, + chartContext: { destroy: () => void } | undefined, + dataLabels: string[], + dataPoints: number[], +): Promise | void> => { + if (!chart) { + console.error('No chart element'); + return; + } + if (chartContext) { + chartContext.destroy(); + } + const chartjs = await import('chart.js/auto'); + const Chart = chartjs.default; + return new Chart(chart, { + type: 'doughnut', + data: { + labels: dataLabels, // data labels, y-axis points + datasets: [ + { + data: dataPoints, // data points corresponding to data labels, x-axis points + backgroundColor: ['#2de0a5', '#cbced1', '#f5455c', '#ffd21f'], + borderWidth: 0, + }, + ], + }, + options: doughnutChartConfiguration(title), + }); +}; + +/** + * Update chart + * @param {Object} chart [Chart context] + * @param {String} label [chart label] + * @param {Array(Double)} data [updated data] + */ +export const updateChart = async (c: ChartType, label: string, data: { [x: string]: number }): Promise => { + const chart = await c; + if (chart.data?.labels?.indexOf(label) === -1) { + // insert data + chart.data.labels.push(label); + chart.data.datasets.forEach((dataset: { data: any[] }, idx: string | number) => { + dataset.data.push(data[idx]); + }); + } else { + // update data + const index = chart.data?.labels?.indexOf(label); + if (typeof index === 'undefined') { + return; + } + + chart.data.datasets.forEach((dataset: { data: { [x: string]: any } }, idx: string | number) => { + dataset.data[index] = data[idx]; + }); + } + + chart.update(); +}; diff --git a/apps/meteor/app/livechat/client/lib/messageTypes.js b/apps/meteor/app/livechat/client/lib/messageTypes.js deleted file mode 100644 index 9cf21362d9e1..000000000000 --- a/apps/meteor/app/livechat/client/lib/messageTypes.js +++ /dev/null @@ -1,5 +0,0 @@ -import { actionLinks } from '../../../action-links/client'; - -actionLinks.register('createLivechatCall', function (message, params, instance) { - instance.tabBar.open('video'); -}); diff --git a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js index 0449754bbd18..8fe8c82ee95c 100644 --- a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js +++ b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js @@ -3,7 +3,7 @@ import { Tracker } from 'meteor/tracker'; import { settings } from '../../../settings'; import { getUserPreference } from '../../../utils'; -import { Subscriptions, Users } from '../../../models'; +import { Subscriptions, Users } from '../../../models/client'; import { CustomSounds } from '../../../custom-sounds/client'; let audio = null; diff --git a/apps/meteor/app/livechat/client/views/app/livechatReadOnly.js b/apps/meteor/app/livechat/client/views/app/livechatReadOnly.js index e860e67869a6..aee6b90f7ef4 100644 --- a/apps/meteor/app/livechat/client/views/app/livechatReadOnly.js +++ b/apps/meteor/app/livechat/client/views/app/livechatReadOnly.js @@ -3,7 +3,7 @@ import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import { ChatRoom, CachedChatRoom } from '../../../../models'; +import { ChatRoom, CachedChatRoom } from '../../../../models/client'; import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import './livechatReadOnly.html'; import { APIClient } from '../../../../utils/client'; diff --git a/apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.js b/apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.js index 8e6c407bc03a..2c58cc34bf04 100644 --- a/apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.js +++ b/apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.js @@ -3,7 +3,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { ChatRoom } from '../../../../../models'; +import { ChatRoom } from '../../../../../models/client'; import { t } from '../../../../../utils'; import './visitorForward.html'; import { APIClient } from '../../../../../utils/client'; diff --git a/apps/meteor/app/livechat/client/views/app/tabbar/visitorInfo.js b/apps/meteor/app/livechat/client/views/app/tabbar/visitorInfo.js index 475987d158b2..c755006acff8 100644 --- a/apps/meteor/app/livechat/client/views/app/tabbar/visitorInfo.js +++ b/apps/meteor/app/livechat/client/views/app/tabbar/visitorInfo.js @@ -9,7 +9,7 @@ import moment from 'moment'; import UAParser from 'ua-parser-js'; import { modal } from '../../../../../ui-utils'; -import { Subscriptions } from '../../../../../models'; +import { Subscriptions } from '../../../../../models/client'; import { settings } from '../../../../../settings'; import { t } from '../../../../../utils'; import { hasRole, hasPermission, hasAtLeastOnePermission } from '../../../../../authorization'; diff --git a/apps/meteor/app/livechat/client/views/app/tabbar/visitorNavigation.js b/apps/meteor/app/livechat/client/views/app/tabbar/visitorNavigation.js index 3f5a9a77d55e..2c290f267aa1 100644 --- a/apps/meteor/app/livechat/client/views/app/tabbar/visitorNavigation.js +++ b/apps/meteor/app/livechat/client/views/app/tabbar/visitorNavigation.js @@ -3,7 +3,7 @@ import { Template } from 'meteor/templating'; import moment from 'moment'; import _ from 'underscore'; -import { ChatRoom } from '../../../../../models'; +import { ChatRoom } from '../../../../../models/client'; import { t } from '../../../../../utils'; import './visitorNavigation.html'; import { APIClient } from '../../../../../utils/client'; diff --git a/apps/meteor/app/livechat/imports/server/rest/dashboards.js b/apps/meteor/app/livechat/imports/server/rest/dashboards.js index f60ef581d42a..84ebf4f9dd04 100644 --- a/apps/meteor/app/livechat/imports/server/rest/dashboards.js +++ b/apps/meteor/app/livechat/imports/server/rest/dashboards.js @@ -13,7 +13,7 @@ import { getAgentsProductivityMetrics, getChatsMetrics, } from '../../../server/lib/analytics/dashboards'; -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; API.v1.addRoute( 'livechat/analytics/dashboards/conversation-totalizers', diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts index e501782744d4..ce0d9e83143e 100644 --- a/apps/meteor/app/livechat/imports/server/rest/departments.ts +++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts @@ -2,7 +2,7 @@ import { isLivechatDepartmentProps } from '@rocket.chat/rest-typings'; import { Match, check } from 'meteor/check'; import { API } from '../../../../api/server'; -import { hasPermission } from '../../../../authorization/server'; +import { hasPermission, hasAtLeastOnePermission } from '../../../../authorization/server'; import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models/server'; import { Livechat } from '../../../server/lib/Livechat'; import { @@ -18,6 +18,10 @@ API.v1.addRoute( { authRequired: true, validateParams: isLivechatDepartmentProps }, { async get() { + if (!hasAtLeastOnePermission(this.userId, ['view-livechat-departments', 'view-l-room'])) { + return API.v1.unauthorized(); + } + const { offset, count } = this.getPaginationItems(); const { sort } = this.parseJsonQuery(); @@ -76,6 +80,10 @@ API.v1.addRoute( { authRequired: true }, { async get() { + if (!hasAtLeastOnePermission(this.userId, ['view-livechat-departments', 'view-l-room'])) { + return API.v1.unauthorized(); + } + check(this.urlParams, { _id: String, }); diff --git a/apps/meteor/app/livechat/imports/server/rest/facebook.js b/apps/meteor/app/livechat/imports/server/rest/facebook.js index 342d99d96bf4..db8f74ea264c 100644 --- a/apps/meteor/app/livechat/imports/server/rest/facebook.js +++ b/apps/meteor/app/livechat/imports/server/rest/facebook.js @@ -1,10 +1,11 @@ import crypto from 'crypto'; import { Random } from 'meteor/random'; +import { LivechatVisitors } from '@rocket.chat/models'; import { API } from '../../../../api/server'; -import { LivechatRooms, LivechatVisitors } from '../../../../models'; -import { settings } from '../../../../settings'; +import { LivechatRooms } from '../../../../models/server'; +import { settings } from '../../../../settings/server'; import { Livechat } from '../../../server/lib/Livechat'; /** @@ -21,7 +22,7 @@ import { Livechat } from '../../../server/lib/Livechat'; * @apiParam {String} [attachments] Facebook message attachments */ API.v1.addRoute('livechat/facebook', { - post() { + async post() { if (!this.bodyParams.text && !this.bodyParams.attachments) { return { success: false, @@ -63,7 +64,7 @@ API.v1.addRoute('livechat/facebook', { }, }, }; - let visitor = LivechatVisitors.getVisitorByToken(this.bodyParams.token); + let visitor = await LivechatVisitors.getVisitorByToken(this.bodyParams.token); if (visitor) { const rooms = LivechatRooms.findOpenByVisitorToken(visitor.token).fetch(); if (rooms && rooms.length > 0) { @@ -76,12 +77,12 @@ API.v1.addRoute('livechat/facebook', { sendMessage.message.rid = Random.id(); sendMessage.message.token = this.bodyParams.token; - const userId = Livechat.registerGuest({ + const userId = await Livechat.registerGuest({ token: sendMessage.message.token, name: `${this.bodyParams.first_name} ${this.bodyParams.last_name}`, }); - visitor = LivechatVisitors.findOneById(userId); + visitor = await LivechatVisitors.findOneById(userId); } sendMessage.message.msg = this.bodyParams.text; diff --git a/apps/meteor/app/livechat/imports/server/rest/inquiries.js b/apps/meteor/app/livechat/imports/server/rest/inquiries.js index 72b077f556a3..7615bec8098b 100644 --- a/apps/meteor/app/livechat/imports/server/rest/inquiries.js +++ b/apps/meteor/app/livechat/imports/server/rest/inquiries.js @@ -1,17 +1,18 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { LivechatInquiryStatus } from '@rocket.chat/core-typings'; +import { LivechatInquiry } from '@rocket.chat/models'; import { API } from '../../../../api/server'; import { hasPermission } from '../../../../authorization'; -import { Users, LivechatDepartment, LivechatInquiry } from '../../../../models'; +import { Users, LivechatDepartment } from '../../../../models/server'; import { findInquiries, findOneInquiryByRoomId } from '../../../server/api/lib/inquiries'; API.v1.addRoute( 'livechat/inquiries.list', { authRequired: true }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-livechat-manager')) { return API.v1.unauthorized(); } @@ -25,11 +26,11 @@ API.v1.addRoute( ourQuery.department = departmentFromDB._id; } } - const cursor = LivechatInquiry.find(ourQuery, { + const { cursor, totalCount } = LivechatInquiry.findPaginated(ourQuery, { sort: sort || { ts: -1 }, skip: offset, limit: count, - fields: { + projection: { rid: 1, name: 1, ts: 1, @@ -37,14 +38,14 @@ API.v1.addRoute( department: 1, }, }); - const totalCount = cursor.count(); - const inquiries = cursor.fetch(); + + const [inquiries, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ inquiries, offset, count: inquiries.length, - total: totalCount, + total, }); }, }, diff --git a/apps/meteor/app/livechat/imports/server/rest/officeHour.js b/apps/meteor/app/livechat/imports/server/rest/officeHour.js deleted file mode 100644 index 1dcb2827f63b..000000000000 --- a/apps/meteor/app/livechat/imports/server/rest/officeHour.js +++ /dev/null @@ -1,21 +0,0 @@ -import { API } from '../../../../api/server'; -import { findLivechatOfficeHours } from '../../../server/api/lib/officeHour'; - -API.v1.addRoute( - 'livechat/office-hours', - { authRequired: true }, - { - get() { - const { officeHours } = Promise.await(findLivechatOfficeHours({ userId: this.userId })); - return API.v1.success( - this.deprecationWarning({ - endpoint: 'livechat/office-hours', - versionWillBeRemoved: '4.0.0', - response: { - officeHours, - }, - }), - ); - }, - }, -); diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.js b/apps/meteor/app/livechat/imports/server/rest/sms.js index 3447870082b8..1756325757df 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.js +++ b/apps/meteor/app/livechat/imports/server/rest/sms.js @@ -1,9 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { FileUpload } from '../../../../file-upload/server'; -import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../../models/server'; +import { LivechatRooms, LivechatDepartment } from '../../../../models/server'; import { API } from '../../../../api/server'; import { fetch } from '../../../../../server/lib/http/fetch'; import { SMS } from '../../../../sms'; @@ -34,7 +35,7 @@ const defineDepartment = (idOrName) => { return department && department._id; }; -const defineVisitor = (smsNumber, targetDepartment) => { +const defineVisitor = async (smsNumber, targetDepartment) => { const visitor = LivechatVisitors.findOneVisitorByPhone(smsNumber); let data = { token: (visitor && visitor.token) || Random.id(), @@ -53,7 +54,7 @@ const defineVisitor = (smsNumber, targetDepartment) => { data.department = targetDepartment; } - const id = Livechat.registerGuest(data); + const id = await Livechat.registerGuest(data); return LivechatVisitors.findOneById(id); }; @@ -79,7 +80,7 @@ API.v1.addRoute('livechat/sms-incoming/:service', { targetDepartment = defineDepartment(SMS.department); } - const visitor = defineVisitor(sms.from, targetDepartment); + const visitor = await defineVisitor(sms.from, targetDepartment); const { token } = visitor; const room = LivechatRooms.findOneOpenByVisitorTokenAndDepartmentId(token, targetDepartment); const roomExists = !!room; diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.js b/apps/meteor/app/livechat/imports/server/rest/upload.js index f6d4f1485a32..5057c5e7e006 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.js +++ b/apps/meteor/app/livechat/imports/server/rest/upload.js @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import filesize from 'filesize'; +import { LivechatVisitors } from '@rocket.chat/models'; import { settings } from '../../../../settings/server'; -import { Settings, LivechatRooms, LivechatVisitors } from '../../../../models'; +import { Settings, LivechatRooms } from '../../../../models/server'; import { fileUploadIsValidContentType } from '../../../../utils/server'; import { FileUpload } from '../../../../file-upload'; import { API } from '../../../../api/server'; @@ -19,13 +20,13 @@ settings.watch('FileUpload_MaxFileSize', function (value) { }); API.v1.addRoute('livechat/upload/:rid', { - post() { + async post() { if (!this.request.headers['x-visitor-token']) { return API.v1.unauthorized(); } const visitorToken = this.request.headers['x-visitor-token']; - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!visitor) { return API.v1.unauthorized(); @@ -36,13 +37,11 @@ API.v1.addRoute('livechat/upload/:rid', { return API.v1.unauthorized(); } - const [file, fields] = Promise.await( - getUploadFormData( - { - request: this.request, - }, - { field: 'file' }, - ), + const [file, fields] = await getUploadFormData( + { + request: this.request, + }, + { field: 'file' }, ); if (!fileUploadIsValidContentType(file.mimetype)) { diff --git a/apps/meteor/app/livechat/imports/server/rest/users.js b/apps/meteor/app/livechat/imports/server/rest/users.js index 0118d8fc6551..11e3b15ac6f8 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.js +++ b/apps/meteor/app/livechat/imports/server/rest/users.js @@ -3,7 +3,7 @@ import _ from 'underscore'; import { hasPermission } from '../../../../authorization'; import { API } from '../../../../api/server'; -import { Users } from '../../../../models'; +import { Users } from '../../../../models/server'; import { Livechat } from '../../../server/lib/Livechat'; import { findAgents, findManagers } from '../../../server/api/lib/users'; diff --git a/apps/meteor/app/livechat/imports/server/rest/visitors.js b/apps/meteor/app/livechat/imports/server/rest/visitors.js index ff1490cd5847..e9e6ad3eea45 100644 --- a/apps/meteor/app/livechat/imports/server/rest/visitors.js +++ b/apps/meteor/app/livechat/imports/server/rest/visitors.js @@ -1,3 +1,4 @@ +import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Match, check } from 'meteor/check'; import { API } from '../../../../api/server'; @@ -142,7 +143,7 @@ API.v1.addRoute( 'livechat/visitors.search', { authRequired: true }, { - get() { + async get() { const { term } = this.requestParams(); check(term, Match.Maybe(String)); @@ -150,18 +151,19 @@ API.v1.addRoute( const { offset, count } = this.getPaginationItems(); const { sort } = this.parseJsonQuery(); + const nameOrUsername = new RegExp(escapeRegExp(term), 'i'); + return API.v1.success( - Promise.await( - findVisitorsByEmailOrPhoneOrNameOrUsername({ - userId: this.userId, - term, - pagination: { - offset, - count, - sort, - }, - }), - ), + await findVisitorsByEmailOrPhoneOrNameOrUsername({ + userId: this.userId, + emailOrPhone: term, + nameOrUsername, + pagination: { + offset, + count, + sort, + }, + }), ); }, }, diff --git a/apps/meteor/app/livechat/imports/server/rest/visitors.ts b/apps/meteor/app/livechat/imports/server/rest/visitors.ts index 1c76ff876ac7..134286d700c4 100644 --- a/apps/meteor/app/livechat/imports/server/rest/visitors.ts +++ b/apps/meteor/app/livechat/imports/server/rest/visitors.ts @@ -1,9 +1,8 @@ import { check } from 'meteor/check'; -import type { IMessage } from '@rocket.chat/core-typings'; +import { Messages } from '@rocket.chat/models'; import { API } from '../../../../api/server'; import { LivechatRooms } from '../../../../models/server'; -import { Messages } from '../../../../models/server/raw'; import { normalizeMessagesForUser } from '../../../../utils/server/lib/normalizeMessagesForUser'; import { canAccessRoom } from '../../../../authorization/server'; @@ -29,15 +28,13 @@ API.v1.addRoute( throw new Error('not-allowed'); } - const cursor = Messages.findLivechatClosedMessages(this.urlParams.rid, { + const { cursor, totalCount } = Messages.findLivechatClosedMessages(this.urlParams.rid, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const messages = (await cursor.toArray()) as IMessage[]; + const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); return API.v1.success({ messages: normalizeMessagesForUser(messages, this.userId), diff --git a/apps/meteor/app/livechat/server/api.js b/apps/meteor/app/livechat/server/api.js index 9e2902ad30b3..55f2768a0941 100644 --- a/apps/meteor/app/livechat/server/api.js +++ b/apps/meteor/app/livechat/server/api.js @@ -13,5 +13,4 @@ import '../imports/server/rest/visitors.js'; import '../imports/server/rest/visitors.ts'; import '../imports/server/rest/dashboards.js'; import '../imports/server/rest/queue.js'; -import '../imports/server/rest/officeHour.js'; import '../imports/server/rest/businessHours.js'; diff --git a/apps/meteor/app/livechat/server/api/lib/agents.js b/apps/meteor/app/livechat/server/api/lib/agents.js index ded51c4f8eab..15d331552d16 100644 --- a/apps/meteor/app/livechat/server/api/lib/agents.js +++ b/apps/meteor/app/livechat/server/api/lib/agents.js @@ -1,5 +1,6 @@ +import { LivechatDepartmentAgents } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatDepartmentAgents } from '../../../../models/server/raw'; export async function findAgentDepartments({ userId, enabledDepartmentsOnly, agentId }) { if (!(await hasPermissionAsync(userId, 'view-l-room'))) { diff --git a/apps/meteor/app/livechat/server/api/lib/appearance.js b/apps/meteor/app/livechat/server/api/lib/appearance.js index 6af8612b32c1..94e6d36cef5d 100644 --- a/apps/meteor/app/livechat/server/api/lib/appearance.js +++ b/apps/meteor/app/livechat/server/api/lib/appearance.js @@ -1,5 +1,6 @@ +import { Settings } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { Settings } from '../../../../models/server/raw'; export async function findAppearance({ userId }) { if (!(await hasPermissionAsync(userId, 'view-livechat-manager'))) { diff --git a/apps/meteor/app/livechat/server/api/lib/customFields.js b/apps/meteor/app/livechat/server/api/lib/customFields.js index ee76b6c1a91d..20b91b5c5d0f 100644 --- a/apps/meteor/app/livechat/server/api/lib/customFields.js +++ b/apps/meteor/app/livechat/server/api/lib/customFields.js @@ -1,7 +1,7 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { LivechatCustomField } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatCustomField } from '../../../../models/server/raw'; export async function findLivechatCustomFields({ userId, text, pagination: { offset, count, sort } }) { if (!(await hasPermissionAsync(userId, 'view-l-room'))) { @@ -14,15 +14,13 @@ export async function findLivechatCustomFields({ userId, text, pagination: { off }), }; - const cursor = await LivechatCustomField.find(query, { + const { cursor, totalCount } = LivechatCustomField.findPaginated(query, { sort: sort || { label: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const customFields = await cursor.toArray(); + const [customFields, total] = await Promise.all([cursor.toArray(), totalCount]); return { customFields, diff --git a/apps/meteor/app/livechat/server/api/lib/departments.ts b/apps/meteor/app/livechat/server/api/lib/departments.ts index ffa00fe64d3b..423e4f4318dc 100644 --- a/apps/meteor/app/livechat/server/api/lib/departments.ts +++ b/apps/meteor/app/livechat/server/api/lib/departments.ts @@ -1,13 +1,13 @@ -import { FilterQuery, SortOptionObject } from 'mongodb'; +import type { Filter, FindOptions } from 'mongodb'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import type { PaginatedResult } from '@rocket.chat/rest-typings'; import type { ILivechatDepartmentRecord, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; +import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models/server/raw'; import { callbacks } from '../../../../../lib/callbacks'; -type Pagination = { pagination: { offset: number; count: number; sort: SortOptionObject } }; +type Pagination = { pagination: { offset: number; count: number; sort: FindOptions['sort'] } }; type FindDepartmentParams = { userId: string; onlyMyDepartments?: boolean; @@ -25,7 +25,7 @@ type FindDepartmentToAutocompleteParams = { uid: string; selector: { exceptions: string[]; - conditions: FilterQuery; + conditions: Filter; term: string; }; onlyMyDepartments?: boolean; @@ -58,15 +58,13 @@ export async function findDepartments({ query = callbacks.run('livechat.applyDepartmentRestrictions', query, { userId }); } - const cursor = LivechatDepartment.find(query, { + const { cursor, totalCount } = LivechatDepartment.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const departments = await cursor.toArray(); + const [departments, total] = await Promise.all([cursor.toArray(), totalCount]); return { departments, @@ -118,7 +116,11 @@ export async function findDepartmentsToAutocomplete({ const { exceptions = [] } = selector; let { conditions = {} } = selector; - const options = { + if (onlyMyDepartments) { + conditions = callbacks.run('livechat.applyDepartmentRestrictions', conditions, { userId: uid }); + } + + const items = await LivechatDepartment.findByNameRegexWithExceptionsAndConditions(selector.term, exceptions, conditions, { projection: { _id: 1, name: 1, @@ -126,18 +128,7 @@ export async function findDepartmentsToAutocomplete({ sort: { name: 1, }, - }; - - if (onlyMyDepartments) { - conditions = callbacks.run('livechat.applyDepartmentRestrictions', conditions, { userId: uid }); - } - - const items = await LivechatDepartment.findByNameRegexWithExceptionsAndConditions( - selector.term, - exceptions, - conditions, - options, - ).toArray(); + }).toArray(); return { items, }; @@ -152,15 +143,13 @@ export async function findDepartmentAgents({ throw new Error('error-not-authorized'); } - const cursor = LivechatDepartmentAgents.findAgentsByDepartmentId(departmentId, { + const { cursor, totalCount } = LivechatDepartmentAgents.findAgentsByDepartmentId(departmentId, { sort: sort || { username: 1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const agents = await cursor.toArray(); + const [agents, total] = await Promise.all([cursor.toArray(), totalCount]); return { agents, diff --git a/apps/meteor/app/livechat/server/api/lib/inquiries.js b/apps/meteor/app/livechat/server/api/lib/inquiries.js index 2b113433fe0c..88c3492f9c61 100644 --- a/apps/meteor/app/livechat/server/api/lib/inquiries.js +++ b/apps/meteor/app/livechat/server/api/lib/inquiries.js @@ -1,5 +1,6 @@ +import { LivechatDepartmentAgents, LivechatDepartment, LivechatInquiry } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatDepartmentAgents, LivechatDepartment, LivechatInquiry } from '../../../../models/server/raw'; const agentDepartments = async (userId) => { const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId).toArray()).map(({ departmentId }) => departmentId); @@ -48,9 +49,9 @@ export async function findInquiries({ userId, department: filterDepartment, stat ], }; - const cursor = LivechatInquiry.find(filter, options); - const total = await cursor.count(); - const inquiries = await cursor.toArray(); + const { cursor, totalCount } = LivechatInquiry.findPaginated(filter, options); + + const [inquiries, total] = await Promise.all([cursor.toArray(), totalCount]); return { inquiries, diff --git a/apps/meteor/app/livechat/server/api/lib/integrations.js b/apps/meteor/app/livechat/server/api/lib/integrations.js index 46bb09c3571c..8b90dad373e9 100644 --- a/apps/meteor/app/livechat/server/api/lib/integrations.js +++ b/apps/meteor/app/livechat/server/api/lib/integrations.js @@ -1,5 +1,6 @@ +import { Settings } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { Settings } from '../../../../models/server/raw'; export async function findIntegrationSettings({ userId }) { if (!(await hasPermissionAsync(userId, 'view-livechat-manager'))) { diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.js b/apps/meteor/app/livechat/server/api/lib/livechat.js index d324da67fe37..507f06d687ed 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.js +++ b/apps/meteor/app/livechat/server/api/lib/livechat.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { EmojiCustom, LivechatTrigger, LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../../models/server'; -import { EmojiCustom, LivechatTrigger } from '../../../../models/server/raw'; +import { LivechatRooms, LivechatDepartment } from '../../../../models/server'; import { Livechat } from '../../lib/Livechat'; import { callbacks } from '../../../../../lib/callbacks'; import { normalizeAgent } from '../../lib/Helper'; @@ -40,7 +40,7 @@ export function findDepartments(businessUnit) { export function findGuest(token) { return LivechatVisitors.getVisitorByToken(token, { - fields: { + projection: { name: 1, username: 1, token: 1, @@ -121,7 +121,7 @@ export async function settings({ businessUnit = '' } = {}) { nameFieldRegistrationForm: initSettings.Livechat_name_field_registration_form, emailFieldRegistrationForm: initSettings.Livechat_email_field_registration_form, displayOfflineForm: initSettings.Livechat_display_offline_form, - videoCall: initSettings.Omnichannel_call_provider === 'Jitsi' && initSettings.Jitsi_Enabled === true, + videoCall: initSettings.Omnichannel_call_provider === 'Jitsi', fileUpload: initSettings.Livechat_fileupload_enabled && initSettings.FileUpload_Enabled, language: initSettings.Language, transcript: initSettings.Livechat_enable_transcript, @@ -155,8 +155,8 @@ export async function settings({ businessUnit = '' } = {}) { }, ], jitsi: [ - { icon: 'icon-videocam', i18nLabel: 'Accept', method_id: 'createLivechatCall' }, - { icon: 'icon-cancel', i18nLabel: 'Decline', method_id: 'denyLivechatCall' }, + { icon: 'icon-videocam', i18nLabel: 'Accept' }, + { icon: 'icon-cancel', i18nLabel: 'Decline' }, ], }, }, diff --git a/apps/meteor/app/livechat/server/api/lib/officeHour.js b/apps/meteor/app/livechat/server/api/lib/officeHour.js deleted file mode 100644 index dbdadd48fe22..000000000000 --- a/apps/meteor/app/livechat/server/api/lib/officeHour.js +++ /dev/null @@ -1,12 +0,0 @@ -import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatBusinessHours } from '../../../../models/server/raw'; - -export async function findLivechatOfficeHours({ userId }) { - if (!(await hasPermissionAsync(userId, 'view-livechat-business-hours'))) { - throw new Error('error-not-authorized'); - } - - return { - officeHours: (await LivechatBusinessHours.findOneDefaultBusinessHour()).workHours, - }; -} diff --git a/apps/meteor/app/livechat/server/api/lib/queue.js b/apps/meteor/app/livechat/server/api/lib/queue.js index 730a8e99cc8c..8098c61ed723 100644 --- a/apps/meteor/app/livechat/server/api/lib/queue.js +++ b/apps/meteor/app/livechat/server/api/lib/queue.js @@ -1,5 +1,6 @@ +import { LivechatRooms } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatRooms } from '../../../../models/server/raw'; export async function findQueueMetrics({ userId, agentId, includeOfflineAgents, departmentId, pagination: { offset, count, sort } }) { if (!(await hasPermissionAsync(userId, 'view-l-room'))) { diff --git a/apps/meteor/app/livechat/server/api/lib/rooms.js b/apps/meteor/app/livechat/server/api/lib/rooms.js index 3ff91ba8b257..32862d65b4dd 100644 --- a/apps/meteor/app/livechat/server/api/lib/rooms.js +++ b/apps/meteor/app/livechat/server/api/lib/rooms.js @@ -1,4 +1,4 @@ -import { LivechatRooms, LivechatDepartment } from '../../../../models/server/raw'; +import { LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; export async function findRooms({ agents, @@ -12,7 +12,7 @@ export async function findRooms({ onhold, options: { offset, count, fields, sort }, }) { - const cursor = LivechatRooms.findRoomsWithCriteria({ + const { cursor, totalCount } = LivechatRooms.findRoomsWithCriteria({ agents, roomName, departmentId, @@ -30,9 +30,7 @@ export async function findRooms({ }, }); - const total = await cursor.count(); - - const rooms = await cursor.toArray(); + const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]); const departmentsIds = [...new Set(rooms.map((room) => room.departmentId).filter(Boolean))]; if (departmentsIds.length) { diff --git a/apps/meteor/app/livechat/server/api/lib/transfer.js b/apps/meteor/app/livechat/server/api/lib/transfer.js index f9f41a001b5d..68424216c16a 100644 --- a/apps/meteor/app/livechat/server/api/lib/transfer.js +++ b/apps/meteor/app/livechat/server/api/lib/transfer.js @@ -1,5 +1,6 @@ +import { Messages } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { Messages } from '../../../../models/server/raw'; const normalizeTransferHistory = ({ transferData }) => transferData; export async function findLivechatTransferHistory({ userId, rid, pagination: { offset, count, sort } }) { @@ -7,19 +8,17 @@ export async function findLivechatTransferHistory({ userId, rid, pagination: { o throw new Error('error-not-authorized'); } - const cursor = await Messages.find( + const { cursor, totalCount } = Messages.findPaginated( { rid, t: 'livechat_transfer_history' }, { - fields: { transferData: 1 }, + projection: { transferData: 1 }, sort: sort || { ts: 1 }, skip: offset, limit: count, }, ); - const total = await cursor.count(); - const messages = await cursor.toArray(); - const history = messages.map(normalizeTransferHistory); + const [history, total] = await Promise.all([cursor.map(normalizeTransferHistory).toArray(), totalCount]); return { history, diff --git a/apps/meteor/app/livechat/server/api/lib/triggers.js b/apps/meteor/app/livechat/server/api/lib/triggers.js index 06d46d578cd8..1cf65b944374 100644 --- a/apps/meteor/app/livechat/server/api/lib/triggers.js +++ b/apps/meteor/app/livechat/server/api/lib/triggers.js @@ -1,12 +1,13 @@ +import { LivechatTrigger } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatTrigger } from '../../../../models/server/raw'; export async function findTriggers({ userId, pagination: { offset, count, sort } }) { if (!(await hasPermissionAsync(userId, 'view-livechat-manager'))) { throw new Error('error-not-authorized'); } - const cursor = await LivechatTrigger.find( + const { cursor, totalCount } = LivechatTrigger.findPaginated( {}, { sort: sort || { name: 1 }, @@ -15,9 +16,7 @@ export async function findTriggers({ userId, pagination: { offset, count, sort } }, ); - const total = await cursor.count(); - - const triggers = await cursor.toArray(); + const [triggers, total] = await Promise.all([cursor.toArray(), totalCount]); return { triggers, diff --git a/apps/meteor/app/livechat/server/api/lib/users.js b/apps/meteor/app/livechat/server/api/lib/users.js index b7c5e1e11bd7..98f77cc144b0 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.js +++ b/apps/meteor/app/livechat/server/api/lib/users.js @@ -1,7 +1,7 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Users } from '@rocket.chat/models'; -import { hasAllPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { Users } from '../../../../models/server/raw'; +import { hasAllPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../../authorization/server/functions/hasPermission'; /** * @param {IRole['_id']} role the role id @@ -17,7 +17,7 @@ async function findUsers({ role, text, pagination: { offset, count, sort } }) { }); } - const cursor = await Users.findUsersInRolesWithQuery(role, query, { + const { cursor, totalCount } = Users.findPaginatedUsersInRolesWithQuery(role, query, { sort: sort || { name: 1 }, skip: offset, limit: count, @@ -31,9 +31,7 @@ async function findUsers({ role, text, pagination: { offset, count, sort } }) { }, }); - const total = await cursor.count(); - - const users = await cursor.toArray(); + const [users, total] = await Promise.all([cursor.toArray(), totalCount]); return { users, @@ -43,7 +41,7 @@ async function findUsers({ role, text, pagination: { offset, count, sort } }) { }; } export async function findAgents({ userId, text, pagination: { offset, count, sort } }) { - if (!(await hasAllPermissionAsync(userId, ['view-l-room', 'transfer-livechat-guest']))) { + if (!(await hasAtLeastOnePermissionAsync(userId, ['manage-livechat-agents', 'transfer-livechat-guest', 'edit-omnichannel-contact']))) { throw new Error('error-not-authorized'); } diff --git a/apps/meteor/app/livechat/server/api/lib/visitors.js b/apps/meteor/app/livechat/server/api/lib/visitors.js index 3fbda7c7705b..49486693f9b6 100644 --- a/apps/meteor/app/livechat/server/api/lib/visitors.js +++ b/apps/meteor/app/livechat/server/api/lib/visitors.js @@ -1,5 +1,6 @@ +import { LivechatVisitors, Messages, LivechatRooms } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { LivechatVisitors, Messages, LivechatRooms } from '../../../../models/server/raw'; import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom'; export async function findVisitorInfo({ userId, visitorId }) { @@ -25,15 +26,13 @@ export async function findVisitedPages({ userId, roomId, pagination: { offset, c if (!room) { throw new Error('invalid-room'); } - const cursor = Messages.findByRoomIdAndType(room._id, 'livechat_navigation_history', { + const { cursor, totalCount } = Messages.findPaginatedByRoomIdAndType(room._id, 'livechat_navigation_history', { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const pages = await cursor.toArray(); + const [pages, total] = await Promise.all([cursor.toArray(), totalCount]); return { pages, @@ -55,15 +54,13 @@ export async function findChatHistory({ userId, roomId, visitorId, pagination: { throw new Error('error-not-allowed'); } - const cursor = LivechatRooms.findByVisitorId(visitorId, { + const { cursor, totalCount } = LivechatRooms.findPaginatedByVisitorId(visitorId, { sort: sort || { ts: -1 }, skip: offset, limit: count, }); - const total = await cursor.count(); - - const history = await cursor.toArray(); + const [history, total] = await Promise.all([cursor.toArray(), totalCount]); return { history, @@ -149,17 +146,21 @@ export async function findVisitorsToAutocomplete({ userId, selector }) { }; } -export async function findVisitorsByEmailOrPhoneOrNameOrUsername({ userId, term, pagination: { offset, count, sort } }) { +export async function findVisitorsByEmailOrPhoneOrNameOrUsername({ + userId, + emailOrPhone, + nameOrUsername, + pagination: { offset, count, sort }, +}) { if (!(await hasPermissionAsync(userId, 'view-l-room'))) { throw new Error('error-not-authorized'); } - const cursor = LivechatVisitors.findVisitorsByEmailOrPhoneOrNameOrUsername(term, { + const { cursor, totalCount } = LivechatVisitors.findPaginatedVisitorsByEmailOrPhoneOrNameOrUsername(emailOrPhone, nameOrUsername, { sort: sort || { ts: -1 }, skip: offset, limit: count, - fields: { - _id: 1, + projection: { username: 1, name: 1, phone: 1, @@ -169,9 +170,7 @@ export async function findVisitorsByEmailOrPhoneOrNameOrUsername({ userId, term, }, }); - const total = await cursor.count(); - - const visitors = await cursor.toArray(); + const [visitors, total] = await Promise.all([cursor.toArray(), totalCount]); return { visitors, diff --git a/apps/meteor/app/livechat/server/api/rest.js b/apps/meteor/app/livechat/server/api/rest.js index 7273c565aac9..bfc6fe85465b 100644 --- a/apps/meteor/app/livechat/server/api/rest.js +++ b/apps/meteor/app/livechat/server/api/rest.js @@ -9,4 +9,4 @@ import './v1/customField.js'; import './v1/room.js'; import './v1/videoCall.js'; import './v1/transfer.js'; -import './v1/contact.js'; +import './v1/contact'; diff --git a/apps/meteor/app/livechat/server/api/v1/agent.js b/apps/meteor/app/livechat/server/api/v1/agent.js index 7e2329058542..104f0bc012e3 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.js +++ b/apps/meteor/app/livechat/server/api/v1/agent.js @@ -6,14 +6,14 @@ import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; API.v1.addRoute('livechat/agent.info/:rid/:token', { - get() { + async get() { try { check(this.urlParams, { rid: String, token: String, }); - const visitor = findGuest(this.urlParams.token); + const visitor = await findGuest(this.urlParams.token); if (!visitor) { throw new Meteor.Error('invalid-token'); } diff --git a/apps/meteor/app/livechat/server/api/v1/config.js b/apps/meteor/app/livechat/server/api/v1/config.js index c97a5b2a3b56..1d7bf36a08f9 100644 --- a/apps/meteor/app/livechat/server/api/v1/config.js +++ b/apps/meteor/app/livechat/server/api/v1/config.js @@ -26,7 +26,7 @@ API.v1.addRoute('livechat/config', { const config = await cachedSettings({ businessUnit }); const status = Livechat.online(department); - const guest = token && Livechat.findGuest(token); + const guest = token && (await Livechat.findGuest(token)); const room = guest && findOpenRoom(token); const agent = guest && room && room.servedBy && findAgent(room.servedBy._id); diff --git a/apps/meteor/app/livechat/server/api/v1/contact.js b/apps/meteor/app/livechat/server/api/v1/contact.js deleted file mode 100644 index 41a2ee9ec6d3..000000000000 --- a/apps/meteor/app/livechat/server/api/v1/contact.js +++ /dev/null @@ -1,77 +0,0 @@ -import { Match, check } from 'meteor/check'; -import { Meteor } from 'meteor/meteor'; - -import { API } from '../../../../api/server'; -import { Contacts } from '../../lib/Contacts'; -import { LivechatVisitors } from '../../../../models'; - -API.v1.addRoute( - 'omnichannel/contact', - { authRequired: true }, - { - post() { - try { - check(this.bodyParams, { - _id: Match.Maybe(String), - token: String, - name: String, - email: Match.Maybe(String), - phone: Match.Maybe(String), - customFields: Match.Maybe(Object), - contactManager: Match.Maybe({ - username: String, - }), - }); - - const contact = Contacts.registerContact(this.bodyParams); - - return API.v1.success({ contact }); - } catch (e) { - return API.v1.failure(e); - } - }, - get() { - check(this.queryParams, { - contactId: String, - }); - - const contact = Promise.await(LivechatVisitors.findOneById(this.queryParams.contactId)); - - return API.v1.success({ contact }); - }, - }, -); - -API.v1.addRoute( - 'omnichannel/contact.search', - { authRequired: true }, - { - get() { - try { - check(this.queryParams, { - email: Match.Maybe(String), - phone: Match.Maybe(String), - }); - - const { email, phone } = this.queryParams; - - if (!email && !phone) { - throw new Meteor.Error('error-invalid-params'); - } - - const query = Object.assign( - {}, - { - ...(email && { visitorEmails: { address: email } }), - ...(phone && { phone: { phoneNumber: phone } }), - }, - ); - - const contact = Promise.await(LivechatVisitors.findOne(query)); - return API.v1.success({ contact }); - } catch (e) { - return API.v1.failure(e); - } - }, - }, -); diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts new file mode 100644 index 000000000000..baf9eb6410e7 --- /dev/null +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -0,0 +1,70 @@ +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; + +import { API } from '../../../../api/server'; +import { Contacts } from '../../lib/Contacts'; + +API.v1.addRoute( + 'omnichannel/contact', + { authRequired: true }, + { + async post() { + check(this.bodyParams, { + _id: Match.Maybe(String), + token: String, + name: String, + email: Match.Maybe(String), + phone: Match.Maybe(String), + username: Match.Maybe(String), + customFields: Match.Maybe(Object), + contactManager: Match.Maybe({ + username: String, + }), + }); + + const contact = await Contacts.registerContact(this.bodyParams); + + return API.v1.success({ contact }); + }, + async get() { + check(this.queryParams, { + contactId: String, + }); + + const contact = await LivechatVisitors.findOneById(this.queryParams.contactId); + + return API.v1.success({ contact }); + }, + }, +); + +API.v1.addRoute( + 'omnichannel/contact.search', + { authRequired: true }, + { + async get() { + check(this.queryParams, { + email: Match.Maybe(String), + phone: Match.Maybe(String), + }); + + const { email, phone } = this.queryParams; + + if (!email && !phone) { + throw new Meteor.Error('error-invalid-params'); + } + + const query = Object.assign( + {}, + { + ...(email && { visitorEmails: { address: email } }), + ...(phone && { phone: { phoneNumber: phone } }), + }, + ); + + const contact = await LivechatVisitors.findOne(query); + return API.v1.success({ contact }); + }, + }, +); diff --git a/apps/meteor/app/livechat/server/api/v1/customField.js b/apps/meteor/app/livechat/server/api/v1/customField.js index b15ded04a5d0..5d242f3d00ad 100644 --- a/apps/meteor/app/livechat/server/api/v1/customField.js +++ b/apps/meteor/app/livechat/server/api/v1/customField.js @@ -7,7 +7,7 @@ import { Livechat } from '../../lib/Livechat'; import { findLivechatCustomFields, findCustomFieldById } from '../lib/customFields'; API.v1.addRoute('livechat/custom.field', { - post() { + async post() { try { check(this.bodyParams, { token: String, @@ -18,12 +18,12 @@ API.v1.addRoute('livechat/custom.field', { const { token, key, value, overwrite } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } - if (!Livechat.setCustomFields({ token, key, value, overwrite })) { + if (!(await Livechat.setCustomFields({ token, key, value, overwrite }))) { return API.v1.failure(); } @@ -35,7 +35,7 @@ API.v1.addRoute('livechat/custom.field', { }); API.v1.addRoute('livechat/custom.fields', { - post() { + async post() { check(this.bodyParams, { token: String, customFields: [ @@ -48,19 +48,21 @@ API.v1.addRoute('livechat/custom.fields', { }); const { token } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } - const fields = this.bodyParams.customFields.map((customField) => { - const data = Object.assign({ token }, customField); - if (!Livechat.setCustomFields(data)) { - return API.v1.failure(); - } + const fields = await Promise.all( + this.bodyParams.customFields.map(async (customField) => { + const data = Object.assign({ token }, customField); + if (!(await Livechat.setCustomFields(data))) { + return API.v1.failure(); + } - return { Key: customField.key, value: customField.value, overwrite: customField.overwrite }; - }); + return { Key: customField.key, value: customField.value, overwrite: customField.overwrite }; + }), + ); return API.v1.success({ fields }); }, diff --git a/apps/meteor/app/livechat/server/api/v1/message.js b/apps/meteor/app/livechat/server/api/v1/message.js index b0827d4748d7..2d57ca7f5724 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.js +++ b/apps/meteor/app/livechat/server/api/v1/message.js @@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Messages, LivechatRooms, LivechatVisitors } from '../../../../models'; +import { Messages, LivechatRooms } from '../../../../models/server'; import { hasPermission } from '../../../../authorization'; import { API } from '../../../../api/server'; import { loadMessageHistory } from '../../../../lib'; @@ -13,7 +14,7 @@ import { normalizeMessageFileUpload } from '../../../../utils/server/functions/n import { settings } from '../../../../settings/server'; API.v1.addRoute('livechat/message', { - post() { + async post() { try { check(this.bodyParams, { _id: Match.Maybe(String), @@ -28,7 +29,7 @@ API.v1.addRoute('livechat/message', { const { token, rid, agent, msg } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -67,7 +68,7 @@ API.v1.addRoute('livechat/message', { }, }; - const result = Promise.await(Livechat.sendMessage(sendMessage)); + const result = await Livechat.sendMessage(sendMessage); if (result) { const message = Messages.findOneById(_id); return API.v1.success({ message }); @@ -81,7 +82,7 @@ API.v1.addRoute('livechat/message', { }); API.v1.addRoute('livechat/message/:_id', { - get() { + async get() { try { check(this.urlParams, { _id: String, @@ -95,7 +96,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.queryParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -111,7 +112,7 @@ API.v1.addRoute('livechat/message/:_id', { } if (message.file) { - message = Promise.await(normalizeMessageFileUpload(message)); + message = await normalizeMessageFileUpload(message); } return API.v1.success({ message }); @@ -120,7 +121,7 @@ API.v1.addRoute('livechat/message/:_id', { } }, - put() { + async put() { try { check(this.urlParams, { _id: String, @@ -135,7 +136,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.bodyParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -157,7 +158,7 @@ API.v1.addRoute('livechat/message/:_id', { if (result) { let message = Messages.findOneById(_id); if (message.file) { - message = Promise.await(normalizeMessageFileUpload(message)); + message = await normalizeMessageFileUpload(message); } return API.v1.success({ message }); @@ -168,7 +169,7 @@ API.v1.addRoute('livechat/message/:_id', { return API.v1.failure(e); } }, - delete() { + async delete() { try { check(this.urlParams, { _id: String, @@ -182,7 +183,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.bodyParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -197,7 +198,7 @@ API.v1.addRoute('livechat/message/:_id', { throw new Meteor.Error('invalid-message'); } - const result = Promise.await(Livechat.deleteMessage({ guest, message })); + const result = await Livechat.deleteMessage({ guest, message }); if (result) { return API.v1.success({ message: { @@ -215,7 +216,7 @@ API.v1.addRoute('livechat/message/:_id', { }); API.v1.addRoute('livechat/messages.history/:rid', { - get() { + async get() { try { check(this.urlParams, { rid: String, @@ -230,7 +231,7 @@ API.v1.addRoute('livechat/messages.history/:rid', { throw new Meteor.Error('error-token-param-not-provided', 'The required "token" query param is missing.'); } - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -276,7 +277,7 @@ API.v1.addRoute( 'livechat/messages', { authRequired: true }, { - post() { + async post() { if (!hasPermission(this.userId, 'view-livechat-manager')) { return API.v1.unauthorized(); } @@ -299,7 +300,7 @@ API.v1.addRoute( const visitorToken = this.bodyParams.visitor.token; - let visitor = LivechatVisitors.getVisitorByToken(visitorToken); + let visitor = await LivechatVisitors.getVisitorByToken(visitorToken); let rid; if (visitor) { const rooms = LivechatRooms.findOpenByVisitorToken(visitorToken).fetch(); @@ -314,8 +315,8 @@ API.v1.addRoute( const guest = this.bodyParams.visitor; guest.connectionData = normalizeHttpHeaderData(this.request.headers); - const visitorId = Livechat.registerGuest(guest); - visitor = LivechatVisitors.findOneById(visitorId); + const visitorId = await Livechat.registerGuest(guest); + visitor = await LivechatVisitors.findOneById(visitorId); } const sentMessages = this.bodyParams.messages.map((message) => { diff --git a/apps/meteor/app/livechat/server/api/v1/room.js b/apps/meteor/app/livechat/server/api/v1/room.js index 5e3bf4636a28..6a0f991795ae 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.js +++ b/apps/meteor/app/livechat/server/api/v1/room.js @@ -4,8 +4,8 @@ import { Random } from 'meteor/random'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; -import { settings as rcSettings } from '../../../../settings'; -import { Messages, LivechatRooms } from '../../../../models'; +import { settings as rcSettings } from '../../../../settings/server'; +import { Messages, LivechatRooms } from '../../../../models/server'; import { API } from '../../../../api/server'; import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; @@ -15,7 +15,7 @@ import { canAccessRoom } from '../../../../authorization/server'; import { addUserToRoom } from '../../../../lib/server/functions'; API.v1.addRoute('livechat/room', { - get() { + async get() { const defaultCheckParams = { token: String, rid: Match.Maybe(String), @@ -28,7 +28,7 @@ API.v1.addRoute('livechat/room', { const { token, rid: roomId, agentId, ...extraParams } = this.queryParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -54,7 +54,7 @@ API.v1.addRoute('livechat/room', { }, }; - room = Promise.await(getRoom({ guest, rid, agent, roomInfo, extraParams })); + room = await getRoom({ guest, rid, agent, roomInfo, extraParams }); return API.v1.success(room); } @@ -68,7 +68,7 @@ API.v1.addRoute('livechat/room', { }); API.v1.addRoute('livechat/room.close', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -77,7 +77,7 @@ API.v1.addRoute('livechat/room.close', { const { rid, token } = this.bodyParams; - const visitor = findGuest(token); + const visitor = await findGuest(token); if (!visitor) { throw new Meteor.Error('invalid-token'); } @@ -106,7 +106,7 @@ API.v1.addRoute('livechat/room.close', { }); API.v1.addRoute('livechat/room.transfer', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -116,7 +116,7 @@ API.v1.addRoute('livechat/room.transfer', { const { rid, token, department } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -132,7 +132,7 @@ API.v1.addRoute('livechat/room.transfer', { const { _id, username, name } = guest; const transferredBy = normalizeTransferredByData({ _id, username, name, userType: 'visitor' }, room); - if (!Promise.await(Livechat.transfer(room, guest, { roomId: rid, departmentId: department, transferredBy }))) { + if (!(await Livechat.transfer(room, guest, { roomId: rid, departmentId: department, transferredBy }))) { return API.v1.failure(); } @@ -145,7 +145,7 @@ API.v1.addRoute('livechat/room.transfer', { }); API.v1.addRoute('livechat/room.survey', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -160,7 +160,7 @@ API.v1.addRoute('livechat/room.survey', { const { rid, token, data } = this.bodyParams; - const visitor = findGuest(token); + const visitor = await findGuest(token); if (!visitor) { throw new Meteor.Error('invalid-token'); } @@ -170,7 +170,7 @@ API.v1.addRoute('livechat/room.survey', { throw new Meteor.Error('invalid-room'); } - const config = Promise.await(settings()); + const config = await settings(); if (!config.survey || !config.survey.items || !config.survey.values) { throw new Meteor.Error('invalid-livechat-config'); } diff --git a/apps/meteor/app/livechat/server/api/v1/transcript.js b/apps/meteor/app/livechat/server/api/v1/transcript.js index f8f3c923d25e..040bb51a0f65 100644 --- a/apps/meteor/app/livechat/server/api/v1/transcript.js +++ b/apps/meteor/app/livechat/server/api/v1/transcript.js @@ -5,7 +5,7 @@ import { API } from '../../../../api/server'; import { Livechat } from '../../lib/Livechat'; API.v1.addRoute('livechat/transcript', { - post() { + async post() { try { check(this.bodyParams, { token: String, @@ -14,7 +14,7 @@ API.v1.addRoute('livechat/transcript', { }); const { token, rid, email } = this.bodyParams; - if (!Livechat.sendTranscript({ token, rid, email })) { + if (!(await Livechat.sendTranscript({ token, rid, email }))) { return API.v1.failure({ message: TAPi18n.__('Error_sending_livechat_transcript') }); } diff --git a/apps/meteor/app/livechat/server/api/v1/transfer.js b/apps/meteor/app/livechat/server/api/v1/transfer.js index 536884726a26..b1b65e0202e4 100644 --- a/apps/meteor/app/livechat/server/api/v1/transfer.js +++ b/apps/meteor/app/livechat/server/api/v1/transfer.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { LivechatRooms } from '../../../../models'; +import { LivechatRooms } from '../../../../models/server'; import { API } from '../../../../api/server'; import { findLivechatTransferHistory } from '../lib/transfer'; diff --git a/apps/meteor/app/livechat/server/api/v1/videoCall.js b/apps/meteor/app/livechat/server/api/v1/videoCall.js index b7f3e299a09d..ca13d3278bcd 100644 --- a/apps/meteor/app/livechat/server/api/v1/videoCall.js +++ b/apps/meteor/app/livechat/server/api/v1/videoCall.js @@ -1,76 +1,17 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; -import { Random } from 'meteor/random'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; -import { Messages, Rooms } from '../../../../models'; +import { Messages, Rooms, Settings } from '../../../../models'; import { settings as rcSettings } from '../../../../settings/server'; import { API } from '../../../../api/server'; -import { findGuest, getRoom, settings } from '../lib/livechat'; +import { settings } from '../lib/livechat'; import { hasPermission, canSendMessage } from '../../../../authorization'; import { Livechat } from '../../lib/Livechat'; import { Logger } from '../../../../logger'; const logger = new Logger('LivechatVideoCallApi'); -API.v1.addRoute('livechat/video.call/:token', { - get() { - try { - check(this.urlParams, { - token: String, - }); - - check(this.queryParams, { - rid: Match.Maybe(String), - }); - - const { token } = this.urlParams; - - const guest = findGuest(token); - if (!guest) { - throw new Meteor.Error('invalid-token'); - } - - const rid = this.queryParams.rid || Random.id(); - const roomInfo = { - jitsiTimeout: new Date(Date.now() + 3600 * 1000), - source: { - type: OmnichannelSourceType.API, - alias: 'video-call', - }, - }; - const { room } = getRoom({ guest, rid, roomInfo }); - const config = Promise.await(settings()); - if (!config.theme || !config.theme.actionLinks || !config.theme.actionLinks.jitsi) { - throw new Meteor.Error('invalid-livechat-config'); - } - - Messages.createWithTypeRoomIdMessageAndUser('livechat_video_call', room._id, '', guest, { - actionLinks: config.theme.actionLinks.jitsi, - }); - let rname; - if (rcSettings.get('Jitsi_URL_Room_Hash')) { - rname = rcSettings.get('uniqueID') + rid; - } else { - rname = encodeURIComponent(room.t === 'd' ? room.usernames.join(' x ') : room.name); - } - const videoCall = { - rid, - domain: rcSettings.get('Jitsi_Domain'), - provider: 'jitsi', - room: rcSettings.get('Jitsi_URL_Room_Prefix') + rname + rcSettings.get('Jitsi_URL_Room_Suffix'), - timeout: new Date(Date.now() + 3600 * 1000), - }; - - return API.v1.success(this.deprecationWarning({ videoCall })); - } catch (e) { - logger.error(e); - return API.v1.failure(e); - } - }, -}); - API.v1.addRoute( 'livechat/webrtc.call', { authRequired: true }, @@ -107,6 +48,7 @@ API.v1.addRoute( let { callStatus } = room; if (!callStatus || callStatus === 'ended' || callStatus === 'declined') { + Settings.incrementValueById('WebRTC_Calls_Count'); callStatus = 'ringing'; Promise.await(Rooms.setCallStatusAndCallStartTime(room._id, callStatus)); Promise.await( diff --git a/apps/meteor/app/livechat/server/api/v1/visitor.ts b/apps/meteor/app/livechat/server/api/v1/visitor.ts index 0c2a23cc2e21..9226926f94a9 100644 --- a/apps/meteor/app/livechat/server/api/v1/visitor.ts +++ b/apps/meteor/app/livechat/server/api/v1/visitor.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import type { ILivechatVisitorDTO, IRoom } from '@rocket.chat/core-typings'; +import { LivechatVisitors as VisitorsRaw } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatCustomField } from '../../../../models/server'; -import { LivechatVisitors as VisitorsRaw } from '../../../../models/server/raw'; +import { LivechatRooms, LivechatCustomField } from '../../../../models/server'; import { API } from '../../../../api/server'; import { findGuest, normalizeHttpHeaderData } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; @@ -37,7 +37,7 @@ API.v1.addRoute('livechat/visitor', { } guest.connectionData = normalizeHttpHeaderData(this.request.headers); - const visitorId = Livechat.registerGuest(guest as any); // TODO: Rewrite Livechat to TS + const visitorId = await Livechat.registerGuest(guest as any); // TODO: Rewrite Livechat to TS let visitor = await VisitorsRaw.findOneById(visitorId, {}); // If it's updating an existing visitor, it must also update the roomInfo @@ -55,7 +55,8 @@ API.v1.addRoute('livechat/visitor', { return; } const { key, value, overwrite } = field; - if (customField.scope === 'visitor' && !LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite)) { + // TODO: refactor this to use normal await + if (customField.scope === 'visitor' && !Promise.await(VisitorsRaw.updateLivechatDataByToken(token, key, value, overwrite))) { return API.v1.failure(); } }); @@ -112,7 +113,7 @@ API.v1.addRoute('livechat/visitor/:token', { } const { _id } = visitor; - const result = Livechat.removeGuest(_id); + const result = await Livechat.removeGuest(_id); if (!result) { throw new Meteor.Error('error-removing-visitor', 'An error ocurred while deleting visitor'); } @@ -156,7 +157,7 @@ API.v1.addRoute('livechat/visitor.callStatus', { }); const { token, callStatus, rid, callId } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -174,7 +175,7 @@ API.v1.addRoute('livechat/visitor.status', { const { token, status } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 66024e005ae2..205cd9e741fa 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -1,9 +1,9 @@ -import moment from 'moment'; +import moment from 'moment-timezone'; import type { ILivechatBusinessHour, ILivechatDepartment } from '@rocket.chat/core-typings'; +import type { ILivechatBusinessHoursModel, IUsersModel } from '@rocket.chat/model-typings'; +import { LivechatBusinessHours, Users } from '@rocket.chat/models'; -import { IWorkHoursCronJobsWrapper, LivechatBusinessHoursRaw } from '../../../models/server/raw/LivechatBusinessHours'; -import { UsersRaw } from '../../../models/server/raw/Users'; -import { LivechatBusinessHours, Users } from '../../../models/server/raw'; +import { IWorkHoursCronJobsWrapper } from '../../../../server/models/raw/LivechatBusinessHours'; export interface IBusinessHourBehavior { findHoursToCreateJobs(): Promise; @@ -27,9 +27,9 @@ export interface IBusinessHourType { } export abstract class AbstractBusinessHourBehavior { - protected BusinessHourRepository: LivechatBusinessHoursRaw = LivechatBusinessHours; + protected BusinessHourRepository: ILivechatBusinessHoursModel = LivechatBusinessHours; - protected UsersRepository: UsersRaw = Users; + protected UsersRepository: IUsersModel = Users; async findHoursToCreateJobs(): Promise { return this.BusinessHourRepository.findHoursToScheduleJobs(); @@ -54,9 +54,9 @@ export abstract class AbstractBusinessHourBehavior { } export abstract class AbstractBusinessHourType { - protected BusinessHourRepository: LivechatBusinessHoursRaw = LivechatBusinessHours; + protected BusinessHourRepository: ILivechatBusinessHoursModel = LivechatBusinessHours; - protected UsersRepository: UsersRaw = Users; + protected UsersRepository: IUsersModel = Users; protected async baseSaveBusinessHour(businessHourData: ILivechatBusinessHour): Promise { businessHourData.active = Boolean(businessHourData.active); diff --git a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts index 6ea2c8be5487..cf673dc0979e 100644 --- a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts +++ b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts @@ -1,11 +1,11 @@ import moment from 'moment'; import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import type { ICronJobs } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Users } from '../../../models/server/raw'; const cronJobDayDict: Record = { Sunday: 0, diff --git a/apps/meteor/app/livechat/server/business-hour/Default.ts b/apps/meteor/app/livechat/server/business-hour/Default.ts index 010950800946..a94f12baecfe 100644 --- a/apps/meteor/app/livechat/server/business-hour/Default.ts +++ b/apps/meteor/app/livechat/server/business-hour/Default.ts @@ -1,4 +1,4 @@ -import moment from 'moment'; +import moment from 'moment-timezone'; import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { AbstractBusinessHourType, IBusinessHourType } from './AbstractBusinessHour'; diff --git a/apps/meteor/app/livechat/server/business-hour/Helper.ts b/apps/meteor/app/livechat/server/business-hour/Helper.ts index 98369bf58d33..89c439606b0a 100644 --- a/apps/meteor/app/livechat/server/business-hour/Helper.ts +++ b/apps/meteor/app/livechat/server/business-hour/Helper.ts @@ -1,7 +1,7 @@ import moment from 'moment'; import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; +import { LivechatBusinessHours, Users } from '@rocket.chat/models'; -import { LivechatBusinessHours, Users } from '../../../models/server/raw'; import { createDefaultBusinessHourRow } from '../../../models/server/models/LivechatBusinessHours'; export const filterBusinessHoursThatMustBeOpened = async ( diff --git a/apps/meteor/app/livechat/server/hooks/beforeCloseRoom.js b/apps/meteor/app/livechat/server/hooks/beforeCloseRoom.js index 4604c7c0fca3..641b2bbb7b96 100644 --- a/apps/meteor/app/livechat/server/hooks/beforeCloseRoom.js +++ b/apps/meteor/app/livechat/server/hooks/beforeCloseRoom.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { LivechatDepartment } from '../../../models'; +import { LivechatDepartment } from '../../../models/server'; const concatUnique = (...arrays) => [...new Set([].concat(...arrays.filter(Array.isArray)))]; diff --git a/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js b/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js index e93c682ee175..9b13160afebe 100644 --- a/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js +++ b/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js @@ -1,6 +1,6 @@ import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings'; -import { Users, LivechatDepartmentAgents } from '../../../models'; +import { settings } from '../../../settings/server'; +import { Users, LivechatDepartmentAgents } from '../../../models/server'; callbacks.add( 'livechat.beforeDelegateAgent', @@ -14,10 +14,10 @@ callbacks.add( } if (department) { - return LivechatDepartmentAgents.getNextBotForDepartment(department); + return Promise.await(LivechatDepartmentAgents.getNextBotForDepartment(department)); } - return Users.getNextBotAgent(); + return Promise.await(Users.getNextBotAgent()); }, callbacks.priority.HIGH, 'livechat-before-delegate-agent', diff --git a/apps/meteor/app/livechat/server/hooks/leadCapture.js b/apps/meteor/app/livechat/server/hooks/leadCapture.js index 892bcf8aade0..6a1d3b5b67b9 100644 --- a/apps/meteor/app/livechat/server/hooks/leadCapture.js +++ b/apps/meteor/app/livechat/server/hooks/leadCapture.js @@ -1,6 +1,7 @@ +import { LivechatVisitors } from '@rocket.chat/models'; + import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings'; -import { LivechatVisitors } from '../../../models'; +import { settings } from '../../../settings/server'; function validateMessage(message, room) { // skips this callback if the message was edited @@ -40,7 +41,7 @@ callbacks.add( const msgEmails = message.msg.match(emailRegexp); if (msgEmails || msgPhones) { - LivechatVisitors.saveGuestEmailPhoneById(room.v._id, msgEmails, msgPhones); + Promise.await(LivechatVisitors.saveGuestEmailPhoneById(room.v._id, msgEmails, msgPhones)); callbacks.run('livechat.leadCapture', room); } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.js b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.js index 2503b894280c..78bf971d2889 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.js +++ b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; callbacks.add( 'afterSaveMessage', diff --git a/apps/meteor/app/livechat/server/hooks/markRoomResponded.js b/apps/meteor/app/livechat/server/hooks/markRoomResponded.js index 5b905cfb4fe7..78049d6d3ccd 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomResponded.js +++ b/apps/meteor/app/livechat/server/hooks/markRoomResponded.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; callbacks.add( 'afterSaveMessage', diff --git a/apps/meteor/app/livechat/server/hooks/offlineMessage.js b/apps/meteor/app/livechat/server/hooks/offlineMessage.js index 51898a917d45..4b12e9edf854 100644 --- a/apps/meteor/app/livechat/server/hooks/offlineMessage.js +++ b/apps/meteor/app/livechat/server/hooks/offlineMessage.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { Livechat } from '../lib/Livechat'; callbacks.add( diff --git a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.js b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.js index 5ea708c470d5..83a79c6b97af 100644 --- a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.js +++ b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.js @@ -1,9 +1,9 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { sendMessage } from '../../../lib'; -import { LivechatDepartment, Rooms, Users } from '../../../models'; +import { LivechatDepartment, Rooms, Users } from '../../../models/server'; callbacks.add( 'livechat.offlineMessage', diff --git a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.js b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.js index ebc4a018144c..e3453f6e1451 100644 --- a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.js +++ b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.js @@ -1,10 +1,10 @@ import moment from 'moment'; +import { LivechatBusinessHours, LivechatDepartment } from '@rocket.chat/models'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; import { LivechatRooms, Messages } from '../../../models/server'; import { businessHourManager } from '../business-hour'; -import { LivechatBusinessHours, LivechatDepartment } from '../../../models/server/raw'; const getSecondsWhenOfficeHoursIsDisabled = (room, agentLastMessage) => moment(new Date(room.closedAt)).diff(moment(new Date(agentLastMessage.ts)), 'seconds'); diff --git a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.js b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.js index f4f641a474ff..fbedddc12672 100644 --- a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.js +++ b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; callbacks.add( diff --git a/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js b/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js index 0d87723b7079..9745b09930e3 100644 --- a/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js +++ b/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js @@ -13,7 +13,7 @@ callbacks.add( _id, ts: new Date(), }; - Livechat.updateLastChat(guestId, lastChat); + Promise.await(Livechat.updateLastChat(guestId, lastChat)); }, callbacks.priority.MEDIUM, 'livechat-save-last-chat', diff --git a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts index a5c43e7397cb..fe726a73a410 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts @@ -1,7 +1,7 @@ import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { LivechatInquiry } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { LivechatInquiry } from '../../../models/server/raw'; import { settings } from '../../../settings/server'; import { RoutingManager } from '../lib/RoutingManager'; diff --git a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.js b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.js index 0748ae13e47e..40d6bd4c68d8 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.js +++ b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; callbacks.add( 'afterSaveMessage', diff --git a/apps/meteor/app/livechat/server/hooks/sendToCRM.js b/apps/meteor/app/livechat/server/hooks/sendToCRM.js index 0e63bb6c6393..c54b761717c9 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToCRM.js +++ b/apps/meteor/app/livechat/server/hooks/sendToCRM.js @@ -1,6 +1,6 @@ import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Messages, LivechatRooms } from '../../../models'; +import { Messages, LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; @@ -41,7 +41,7 @@ function sendToCRM(type, room, includeMessages = true) { return room; } - const postData = Livechat.getLivechatRoomGuestInfo(room); + const postData = Promise.await(Livechat.getLivechatRoomGuestInfo(room)); postData.type = type; diff --git a/apps/meteor/app/livechat/server/hooks/sendToFacebook.js b/apps/meteor/app/livechat/server/hooks/sendToFacebook.js index 334a68c8a22f..d45f56c2b366 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToFacebook.js +++ b/apps/meteor/app/livechat/server/hooks/sendToFacebook.js @@ -1,5 +1,5 @@ import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import OmniChannel from '../lib/OmniChannel'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; diff --git a/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js b/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js index 6744d4a71bb2..597d656b2f0e 100644 --- a/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js +++ b/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js @@ -1,6 +1,6 @@ import { callbacks } from '../../../../lib/callbacks'; import { Livechat } from '../lib/Livechat'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; const sendTranscriptOnClose = (room) => { const { _id: rid, transcriptRequest, v: { token } = {} } = room; @@ -9,7 +9,8 @@ const sendTranscriptOnClose = (room) => { } const { email, subject, requestedBy: user } = transcriptRequest; - Livechat.sendTranscript({ token, rid, email, subject, user }); + // TODO: refactor this to use normal await + Promise.await(Livechat.sendTranscript({ token, rid, email, subject, user })); LivechatRooms.removeTranscriptRequestByRoomId(rid); diff --git a/apps/meteor/app/livechat/server/index.js b/apps/meteor/app/livechat/server/index.js index 217db0c7a35b..c44390cd6de6 100644 --- a/apps/meteor/app/livechat/server/index.js +++ b/apps/meteor/app/livechat/server/index.js @@ -31,7 +31,6 @@ import './methods/getAgentData'; import './methods/getAgentOverviewData'; import './methods/getAnalyticsChartData'; import './methods/getAnalyticsOverviewData'; -import './methods/getInitialData'; import './methods/getNextAgent'; import './methods/getRoutingConfig'; import './methods/loadHistory'; @@ -60,8 +59,6 @@ import './methods/sendFileLivechatMessage'; import './methods/sendOfflineMessage'; import './methods/setCustomField'; import './methods/setDepartmentForVisitor'; -import './methods/startVideoCall'; -import './methods/startFileUploadRoom'; import './methods/transfer'; import './methods/webhookTest'; import './methods/setUpConnection'; @@ -83,7 +80,6 @@ import './sendMessageBySMS'; import './api'; import './api/rest'; import './externalFrame'; -import './lib/messageTypes'; import './methods/saveBusinessHour'; export { Livechat } from './lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/lib/Analytics.js b/apps/meteor/app/livechat/server/lib/Analytics.js index 1974a5be1985..7a18f19d9d94 100644 --- a/apps/meteor/app/livechat/server/lib/Analytics.js +++ b/apps/meteor/app/livechat/server/lib/Analytics.js @@ -1,8 +1,8 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import moment from 'moment'; +import moment from 'moment-timezone'; +import { LivechatRooms as LivechatRoomsRaw } from '@rocket.chat/models'; -import { LivechatRooms } from '../../../models'; -import { LivechatRooms as LivechatRoomsRaw } from '../../../models/server/raw'; +import { LivechatRooms } from '../../../models/server'; import { secondsToHHMMSS } from '../../../utils/server'; import { getTimezone } from '../../../utils/server/lib/getTimezone'; import { Logger } from '../../../logger'; diff --git a/apps/meteor/app/livechat/server/lib/Contacts.js b/apps/meteor/app/livechat/server/lib/Contacts.js deleted file mode 100644 index 24909685e6a9..000000000000 --- a/apps/meteor/app/livechat/server/lib/Contacts.js +++ /dev/null @@ -1,83 +0,0 @@ -import { check } from 'meteor/check'; -import { Meteor } from 'meteor/meteor'; -import s from 'underscore.string'; - -import { LivechatVisitors, LivechatCustomField, LivechatRooms, Rooms, LivechatInquiry, Subscriptions } from '../../../models'; -import { Users } from '../../../models/server/raw'; - -export const Contacts = { - registerContact({ token, name, email, phone, username, customFields = {}, contactManager = {} } = {}) { - check(token, String); - - const visitorEmail = s.trim(email).toLowerCase(); - - if (contactManager?.username) { - // verify if the user exists with this username and has a livechat-agent role - const user = Promise.await(Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } })); - if (!user) { - throw new Meteor.Error('error-contact-manager-not-found', `No user found with username ${contactManager.username}`); - } - if (!user.roles || !Array.isArray(user.roles) || !user.roles.includes('livechat-agent')) { - throw new Meteor.Error('error-invalid-contact-manager', 'The contact manager must have the role "livechat-agent"'); - } - } - - let contactId; - const updateUser = { - $set: { - token, - }, - }; - - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); - - if (user) { - contactId = user._id; - } else { - if (!username) { - username = LivechatVisitors.getNextVisitorUsername(); - } - - let existingUser = null; - - if (visitorEmail !== '' && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(visitorEmail))) { - contactId = existingUser._id; - } else { - const userData = { - username, - ts: new Date(), - }; - - contactId = LivechatVisitors.insert(userData); - } - } - - updateUser.$set.name = name; - updateUser.$set.phone = (phone && [{ phoneNumber: phone }]) || null; - updateUser.$set.visitorEmails = (visitorEmail && [{ address: visitorEmail }]) || null; - - const allowedCF = LivechatCustomField.find({ scope: 'visitor' }, { fields: { _id: 1 } }).map(({ _id }) => _id); - - const livechatData = Object.keys(customFields) - .filter((key) => allowedCF.includes(key) && customFields[key] !== '' && customFields[key] !== undefined) - .reduce((obj, key) => { - obj[key] = customFields[key]; - return obj; - }, {}); - - updateUser.$set.livechatData = livechatData; - updateUser.$set.contactManager = (contactManager?.username && { username: contactManager.username }) || null; - - LivechatVisitors.updateById(contactId, updateUser); - - const rooms = LivechatRooms.findByVisitorId(contactId).fetch(); - - rooms?.length && - rooms.forEach((room) => { - const { _id: rid } = room; - Rooms.setFnameById(rid, name) && LivechatInquiry.setNameByRoomId(rid, name) && Subscriptions.updateDisplayNameByRoomId(rid, name); - }); - - return contactId; - }, -}; diff --git a/apps/meteor/app/livechat/server/lib/Contacts.ts b/apps/meteor/app/livechat/server/lib/Contacts.ts new file mode 100644 index 000000000000..45fbf828fdb2 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/Contacts.ts @@ -0,0 +1,109 @@ +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import s from 'underscore.string'; +import { MatchKeysAndValues, OnlyFieldsOfType } from 'mongodb'; +import { LivechatVisitors, Users, LivechatRooms } from '@rocket.chat/models'; +import { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; + +import { LivechatCustomField, Rooms, LivechatInquiry, Subscriptions } from '../../../models/server'; + +type RegisterContactProps = { + _id?: string; + token: string; + name: string; + username?: string; + email?: string; + phone?: string; + customFields?: Record; + contactManager?: { + username: string; + }; +}; + +export const Contacts = { + async registerContact({ + token, + name, + email = '', + phone, + username, + customFields = {}, + contactManager, + }: RegisterContactProps): Promise { + check(token, String); + + const visitorEmail = s.trim(email).toLowerCase(); + + if (contactManager?.username) { + // verify if the user exists with this username and has a livechat-agent role + const user = await Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } }); + if (!user) { + throw new Meteor.Error('error-contact-manager-not-found', `No user found with username ${contactManager.username}`); + } + if (!user.roles || !Array.isArray(user.roles) || !user.roles.includes('livechat-agent')) { + throw new Meteor.Error('error-invalid-contact-manager', 'The contact manager must have the role "livechat-agent"'); + } + } + + let contactId; + + const user = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); + + if (user) { + contactId = user._id; + } else { + if (!username) { + username = await LivechatVisitors.getNextVisitorUsername(); + } + + let existingUser = null; + + if (visitorEmail !== '' && (existingUser = await LivechatVisitors.findOneGuestByEmailAddress(visitorEmail))) { + contactId = existingUser._id; + } else { + const userData = { + username, + ts: new Date(), + token, + }; + + contactId = (await LivechatVisitors.insertOne(userData)).insertedId; + } + } + + const allowedCF: ILivechatCustomField['_id'][] = LivechatCustomField.find({ scope: 'visitor' }, { fields: { _id: 1 } }).map( + ({ _id }: ILivechatCustomField) => _id, + ); + + const livechatData = Object.keys(customFields) + .filter((key) => allowedCF.includes(key) && customFields[key] !== '' && customFields[key] !== undefined) + .reduce((obj: Record, key) => { + obj[key] = customFields[key]; + return obj; + }, {}); + + const updateUser: { $set: MatchKeysAndValues; $unset?: OnlyFieldsOfType } = { + $set: { + token, + name, + livechatData, + ...(phone && { phone: [{ phoneNumber: phone }] }), + ...(visitorEmail && { visitorEmails: [{ address: visitorEmail }] }), + ...(contactManager?.username && { contactManager: { username: contactManager.username } }), + }, + ...(!contactManager?.username && { $unset: { contactManager: 1 } }), + }; + + await LivechatVisitors.updateOne({ _id: contactId }, updateUser); + + const rooms: IOmnichannelRoom[] = await LivechatRooms.findByVisitorId(contactId, {}).toArray(); + + rooms?.length && + rooms.forEach((room) => { + const { _id: rid } = room; + Rooms.setFnameById(rid, name) && LivechatInquiry.setNameByRoomId(rid, name) && Subscriptions.updateDisplayNameByRoomId(rid, name); + }); + + return contactId; + }, +}; diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.js index cf43982f9242..3a51f2343d1e 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.js @@ -19,7 +19,7 @@ import { Livechat } from './Livechat'; import { RoutingManager } from './RoutingManager'; import { callbacks } from '../../../../lib/callbacks'; import { Logger } from '../../../logger'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { Apps, AppEvents } from '../../../apps/server'; import notifications from '../../../notifications/server/lib/Notifications'; import { sendNotification } from '../../../lib/server'; @@ -531,7 +531,7 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { } const { token } = guest; - Livechat.setDepartmentForGuest({ token, department: departmentId }); + await Livechat.setDepartmentForGuest({ token, department: departmentId }); logger.debug(`Department for visitor with token ${token} was updated to ${departmentId}`); return true; diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index d71d12a3e606..218576effe4d 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -9,6 +9,7 @@ import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment-timezone'; import UAParser from 'ua-parser-js'; +import { Users as UsersRaw, LivechatVisitors } from '@rocket.chat/models'; import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; @@ -26,7 +27,6 @@ import { LivechatDepartmentAgents, LivechatDepartment, LivechatCustomField, - LivechatVisitors, LivechatInquiry, } from '../../../models/server'; import { Logger } from '../../../logger/server'; @@ -40,9 +40,9 @@ import { normalizeTransferredByData, parseAgentCustomFields, updateDepartmentAge import { Apps, AppEvents } from '../../../apps/server'; import { businessHourManager } from '../business-hour'; import notifications from '../../../notifications/server/lib/Notifications'; -import { Users as UsersRaw, LivechatVisitors as LivechatVisitorsRaw } from '../../../models/server/raw'; import { addUserRoles } from '../../../../server/lib/roles/addUserRoles'; import { removeUserFromRoles } from '../../../../server/lib/roles/removeUserFromRoles'; +import { VideoConf } from '../../../../server/sdk'; const logger = new Logger('Livechat'); @@ -172,8 +172,8 @@ export const Livechat = { } if (guest.department && !LivechatDepartment.findOneById(guest.department)) { - LivechatVisitors.removeDepartmentById(guest._id); - guest = LivechatVisitors.findOneById(guest._id); + await LivechatVisitors.removeDepartmentById(guest._id); + guest = await LivechatVisitors.findOneById(guest._id); } if (room == null) { @@ -272,7 +272,7 @@ export const Livechat = { return true; }, - registerGuest({ id, token, name, email, department, phone, username, connectionData, status = 'online' } = {}) { + async registerGuest({ id, token, name, email, department, phone, username, connectionData, status = 'online' } = {}) { check(token, String); check(id, Match.Maybe(String)); @@ -307,24 +307,24 @@ export const Livechat = { updateUser.$set.department = dep._id; } - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); let existingUser = null; if (user) { Livechat.logger.debug('Found matching user by token'); userId = user._id; - } else if (phone?.number && (existingUser = LivechatVisitors.findOneVisitorByPhone(phone.number))) { + } else if (phone?.number && (existingUser = await LivechatVisitors.findOneVisitorByPhone(phone.number))) { Livechat.logger.debug('Found matching user by phone number'); userId = existingUser._id; // Don't change token when matching by phone number, use current visitor token updateUser.$set.token = existingUser.token; - } else if (email && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(email))) { + } else if (email && (existingUser = await LivechatVisitors.findOneGuestByEmailAddress(email))) { Livechat.logger.debug('Found matching user by email'); userId = existingUser._id; } else { Livechat.logger.debug(`No matches found. Attempting to create new user with token ${token}`); if (!username) { - username = LivechatVisitors.getNextVisitorUsername(); + username = await LivechatVisitors.getNextVisitorUsername(); } const userData = { @@ -344,15 +344,15 @@ export const Livechat = { } } - userId = LivechatVisitors.insert(userData); + userId = (await LivechatVisitors.insertOne(userData)).insertedId; } - LivechatVisitors.updateById(userId, updateUser); + await LivechatVisitors.updateById(userId, updateUser); return userId; }, - setDepartmentForGuest({ token, department } = {}) { + async setDepartmentForGuest({ token, department } = {}) { check(token, String); check(department, String); @@ -371,14 +371,14 @@ export const Livechat = { }); } - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (user) { return LivechatVisitors.updateById(user._id, updateUser); } return false; }, - saveGuest({ _id, name, email, phone, livechatData = {} }, userId) { + async saveGuest({ _id, name, email, phone, livechatData = {} }, userId) { Livechat.logger.debug(`Saving data for visitor ${_id}`); const updateData = {}; @@ -411,7 +411,7 @@ export const Livechat = { }); updateData.livechatData = customFields; } - const ret = LivechatVisitors.saveGuestById(_id, updateData); + const ret = await LivechatVisitors.saveGuestById(_id, updateData); Meteor.defer(() => { Apps.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); @@ -506,7 +506,7 @@ export const Livechat = { return LivechatRooms.removeById(rid); }, - setCustomFields({ token, key, value, overwrite } = {}) { + async setCustomFields({ token, key, value, overwrite } = {}) { check(token, String); check(key, String); check(value, String); @@ -554,7 +554,6 @@ export const Livechat = { 'Livechat_offline_form_unavailable', 'Livechat_display_offline_form', 'Omnichannel_call_provider', - 'Jitsi_Enabled', 'Language', 'Livechat_enable_transcript', 'Livechat_transcript_message', @@ -636,7 +635,8 @@ export const Livechat = { forwardOpenChats(userId) { Livechat.logger.debug(`Transferring open chats for user ${userId}`); LivechatRooms.findOpenByAgent(userId).forEach((room) => { - const guest = LivechatVisitors.findOneById(room.v._id); + // TODO: refactor to use normal await + const guest = Promise.await(LivechatVisitors.findOneById(room.v._id)); const user = Users.findOneById(userId); const { _id, username, name } = user; const transferredBy = normalizeTransferredByData({ _id, username, name }, room); @@ -811,8 +811,8 @@ export const Livechat = { } }, - getLivechatRoomGuestInfo(room) { - const visitor = LivechatVisitors.findOneById(room.v._id); + async getLivechatRoomGuestInfo(room) { + const visitor = await LivechatVisitors.findOneById(room.v._id); const agent = Users.findOneById(room.servedBy && room.servedBy._id); const ua = new UAParser(); @@ -925,7 +925,7 @@ export const Livechat = { Users.removeLivechatData(_id); this.setUserStatusLivechat(_id, 'not-available'); LivechatDepartmentAgents.removeByAgentId(_id); - Promise.await(LivechatVisitorsRaw.removeContactManagerByUsername(username)); + Promise.await(LivechatVisitors.removeContactManagerByUsername(username)); return true; } @@ -946,16 +946,16 @@ export const Livechat = { return removeUserFromRoles(user._id, ['livechat-manager']); }, - removeGuest(_id) { + async removeGuest(_id) { check(_id, String); - const guest = LivechatVisitors.findOneById(_id); + const guest = await LivechatVisitors.findOneById(_id, { projection: { _id: 1 } }); if (!guest) { throw new Meteor.Error('error-invalid-guest', 'Invalid guest', { method: 'livechat:removeGuest', }); } - this.cleanGuestHistory(_id); + await this.cleanGuestHistory(_id); return LivechatVisitors.removeById(_id); }, @@ -971,8 +971,8 @@ export const Livechat = { return user; }, - cleanGuestHistory(_id) { - const guest = LivechatVisitors.findOneById(_id); + async cleanGuestHistory(_id) { + const guest = await LivechatVisitors.findOneById(_id); if (!guest) { throw new Meteor.Error('error-invalid-guest', 'Invalid guest', { method: 'livechat:cleanGuestHistory', @@ -1144,15 +1144,15 @@ export const Livechat = { }); }, - sendTranscript({ token, rid, email, subject, user }) { + async sendTranscript({ token, rid, email, subject, user }) { check(rid, String); check(email, String); Livechat.logger.debug(`Sending conversation transcript of room ${rid} to user with token ${token}`); const room = LivechatRooms.findOneById(rid); - const visitor = LivechatVisitors.getVisitorByToken(token, { - fields: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, + const visitor = await LivechatVisitors.getVisitorByToken(token, { + projection: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, }); const userLanguage = (visitor && visitor.language) || settings.get('Language') || 'en'; const timezone = getTimezone(user); @@ -1402,17 +1402,21 @@ export const Livechat = { return LivechatRooms.findOneById(roomId); }, - updateLastChat(contactId, lastChat) { + async updateLastChat(contactId, lastChat) { const updateUser = { $set: { lastChat, }, }; - LivechatVisitors.updateById(contactId, updateUser); + await LivechatVisitors.updateById(contactId, updateUser); }, updateCallStatus(callId, rid, status, user) { Rooms.setCallStatus(rid, status); if (status === 'ended' || status === 'declined') { + if (Promise.await(VideoConf.declineLivechatCall(callId))) { + return; + } + return updateMessage({ _id: callId, msg: status, actionLinks: [], webRtcCallEndTs: new Date() }, user); } }, diff --git a/apps/meteor/app/livechat/server/lib/OmniChannel.js b/apps/meteor/app/livechat/server/lib/OmniChannel.js index 9b320364b12c..0a4651114890 100644 --- a/apps/meteor/app/livechat/server/lib/OmniChannel.js +++ b/apps/meteor/app/livechat/server/lib/OmniChannel.js @@ -1,6 +1,6 @@ import { HTTP } from 'meteor/http'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; const gatewayURL = 'https://omni.rocket.chat'; diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.js b/apps/meteor/app/livechat/server/lib/QueueManager.js index b5faaa016903..5ffea4cb7881 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.js +++ b/apps/meteor/app/livechat/server/lib/QueueManager.js @@ -68,7 +68,7 @@ export const QueueManager = { ); logger.debug(`Generated inquiry for visitor ${guest._id} with id ${inquiry._id} [Not queued]`); - LivechatRooms.updateRoomCount(); + await LivechatRooms.updateRoomCount(); await queueInquiry(room, inquiry, agent); logger.debug(`Inquiry ${inquiry._id} queued`); diff --git a/apps/meteor/app/livechat/server/lib/analytics/agents.js b/apps/meteor/app/livechat/server/lib/analytics/agents.js index f23025cabeb6..1f1a39f85c5c 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/agents.js +++ b/apps/meteor/app/livechat/server/lib/analytics/agents.js @@ -1,4 +1,4 @@ -import { LivechatRooms, LivechatAgentActivity } from '../../../../models/server/raw'; +import { LivechatRooms, LivechatAgentActivity } from '@rocket.chat/models'; const findAllAverageServiceTimeAsync = async ({ start, end, options = {} }) => { if (!start || !end) { diff --git a/apps/meteor/app/livechat/server/lib/analytics/dashboards.js b/apps/meteor/app/livechat/server/lib/analytics/dashboards.js index 7819499ea9e9..71781bfa81c4 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/dashboards.js +++ b/apps/meteor/app/livechat/server/lib/analytics/dashboards.js @@ -1,7 +1,7 @@ import moment from 'moment'; +import { LivechatRooms, Users, LivechatVisitors, LivechatAgentActivity } from '@rocket.chat/models'; -import { LivechatRooms, Users, LivechatVisitors, LivechatAgentActivity } from '../../../../models/server/raw'; -import { settings } from '../../../../settings'; +import { settings } from '../../../../settings/server'; import { Livechat } from '../Livechat'; import { secondsToHHMMSS } from '../../../../utils/server'; import { diff --git a/apps/meteor/app/livechat/server/lib/analytics/departments.js b/apps/meteor/app/livechat/server/lib/analytics/departments.js index d1cd5904a869..bf7a01adf0b8 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/departments.js +++ b/apps/meteor/app/livechat/server/lib/analytics/departments.js @@ -1,4 +1,4 @@ -import { LivechatRooms, Messages } from '../../../../models/server/raw'; +import { LivechatRooms, Messages } from '@rocket.chat/models'; export const findAllRoomsAsync = async ({ start, end, answered, departmentId, options = {} }) => { if (!start || !end) { diff --git a/apps/meteor/app/livechat/server/lib/messageTypes.js b/apps/meteor/app/livechat/server/lib/messageTypes.js deleted file mode 100644 index dadb7e69594c..000000000000 --- a/apps/meteor/app/livechat/server/lib/messageTypes.js +++ /dev/null @@ -1,26 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { actionLinks } from '../../../action-links/server'; -import { api } from '../../../../server/sdk/api'; -import { Messages, LivechatRooms } from '../../../models/server'; -import { settings } from '../../../settings/server'; -import { Livechat } from './Livechat'; - -actionLinks.register('denyLivechatCall', function (message /* , params*/) { - const user = Meteor.user(); - - Messages.createWithTypeRoomIdMessageAndUser('command', message.rid, 'endCall', user); - api.broadcast('notify.deleteMessage', message.rid, { _id: message._id }); - - const language = user.language || settings.get('Language') || 'en'; - - Livechat.closeRoom({ - user, - room: LivechatRooms.findOneById(message.rid), - comment: TAPi18n.__('Videocall_declined', { lng: language }), - }); - Meteor.defer(() => { - Messages.setHiddenById(message._id); - }); -}); diff --git a/apps/meteor/app/livechat/server/methods/closeByVisitor.js b/apps/meteor/app/livechat/server/methods/closeByVisitor.js index 03c415dbb448..022e614bb7f0 100644 --- a/apps/meteor/app/livechat/server/methods/closeByVisitor.js +++ b/apps/meteor/app/livechat/server/methods/closeByVisitor.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { settings } from '../../../settings'; -import { LivechatRooms, LivechatVisitors } from '../../../models'; +import { settings } from '../../../settings/server'; +import { LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:closeByVisitor'({ roomId, token }) { - const visitor = LivechatVisitors.getVisitorByToken(token); + async 'livechat:closeByVisitor'({ roomId, token }) { + const visitor = await LivechatVisitors.getVisitorByToken(token); const language = (visitor && visitor.language) || settings.get('Language') || 'en'; diff --git a/apps/meteor/app/livechat/server/methods/closeRoom.js b/apps/meteor/app/livechat/server/methods/closeRoom.js index 773b22b5cdd4..9eeccfac638a 100644 --- a/apps/meteor/app/livechat/server/methods/closeRoom.js +++ b/apps/meteor/app/livechat/server/methods/closeRoom.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { Subscriptions, LivechatRooms } from '../../../models'; +import { Subscriptions, LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/discardTranscript.js b/apps/meteor/app/livechat/server/methods/discardTranscript.js index 5943ebb56ba3..099c8ad002d8 100644 --- a/apps/meteor/app/livechat/server/methods/discardTranscript.js +++ b/apps/meteor/app/livechat/server/methods/discardTranscript.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; Meteor.methods({ 'livechat:discardTranscript'(rid) { diff --git a/apps/meteor/app/livechat/server/methods/facebook.js b/apps/meteor/app/livechat/server/methods/facebook.js index 90d27222aa2e..86cd7132cc1a 100644 --- a/apps/meteor/app/livechat/server/methods/facebook.js +++ b/apps/meteor/app/livechat/server/methods/facebook.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; import { SystemLogger } from '../../../../server/lib/logger/system'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import OmniChannel from '../lib/OmniChannel'; import { Settings } from '../../../models/server'; diff --git a/apps/meteor/app/livechat/server/methods/getAgentData.js b/apps/meteor/app/livechat/server/methods/getAgentData.js index dd2d6a3b235d..1cc6993974c6 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentData.js +++ b/apps/meteor/app/livechat/server/methods/getAgentData.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Users, LivechatRooms, LivechatVisitors } from '../../../models'; +import { Users, LivechatRooms } from '../../../models/server'; Meteor.methods({ - 'livechat:getAgentData'({ roomId, token }) { + async 'livechat:getAgentData'({ roomId, token }) { check(roomId, String); check(token, String); const room = LivechatRooms.findOneById(roomId); - const visitor = LivechatVisitors.getVisitorByToken(token); + const visitor = await LivechatVisitors.getVisitorByToken(token); if (!room || room.t !== 'l' || !room.v || room.v.token !== visitor.token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); diff --git a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.js b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.js index d60d36957530..8df403e421e1 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.js +++ b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; import { Livechat } from '../lib/Livechat'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; Meteor.methods({ 'livechat:getAgentOverviewData'(options) { diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.js b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.js index caa86c650899..8f9706e93bf7 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.js +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.js b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.js index 1ff65fce8522..407be7c7596e 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.js +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { Users } from '../../../models'; -import { settings } from '../../../settings'; +import { Users } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/getCustomFields.js b/apps/meteor/app/livechat/server/methods/getCustomFields.js index df39364bccee..f18f94b6df2d 100644 --- a/apps/meteor/app/livechat/server/methods/getCustomFields.js +++ b/apps/meteor/app/livechat/server/methods/getCustomFields.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { LivechatCustomField } from '../../../models'; +import { LivechatCustomField } from '../../../models/server'; Meteor.methods({ 'livechat:getCustomFields'() { diff --git a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.js b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.js index 75665226b538..3c9cc0601577 100644 --- a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.js +++ b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { LivechatRooms, Messages } from '../../../models'; +import { LivechatRooms, Messages } from '../../../models/server'; import { hasPermission } from '../../../authorization'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/getInitialData.js b/apps/meteor/app/livechat/server/methods/getInitialData.js deleted file mode 100644 index e46c66e042f7..000000000000 --- a/apps/meteor/app/livechat/server/methods/getInitialData.js +++ /dev/null @@ -1,112 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; - -import { LivechatRooms, Users, LivechatDepartment, LivechatVisitors } from '../../../models/server'; -import { LivechatTrigger } from '../../../models/server/raw'; -import { Livechat } from '../lib/Livechat'; -import { deprecationWarning } from '../../../api/server/helpers/deprecationWarning'; - -Meteor.methods({ - async 'livechat:getInitialData'(visitorToken, departmentId) { - const info = { - enabled: null, - title: null, - color: null, - registrationForm: null, - room: null, - visitor: null, - triggers: [], - departments: [], - allowSwitchingDepartments: null, - online: true, - offlineColor: null, - offlineMessage: null, - offlineSuccessMessage: null, - offlineUnavailableMessage: null, - displayOfflineForm: null, - videoCall: null, - fileUpload: null, - conversationFinishedMessage: null, - conversationFinishedText: null, - nameFieldRegistrationForm: null, - emailFieldRegistrationForm: null, - registrationFormMessage: null, - showConnecting: false, - }; - - const options = { - fields: { - name: 1, - t: 1, - cl: 1, - u: 1, - usernames: 1, - v: 1, - servedBy: 1, - departmentId: 1, - }, - }; - const room = departmentId - ? LivechatRooms.findOpenByVisitorTokenAndDepartmentId(visitorToken, departmentId, options).fetch() - : LivechatRooms.findOpenByVisitorToken(visitorToken, options).fetch(); - if (room && room.length > 0) { - info.room = room[0]; - } - - const visitor = LivechatVisitors.getVisitorByToken(visitorToken, { - fields: { - name: 1, - username: 1, - visitorEmails: 1, - department: 1, - }, - }); - - if (room) { - info.visitor = visitor; - } - - const initSettings = Livechat.getInitSettings(); - - info.title = initSettings.Livechat_title; - info.color = initSettings.Livechat_title_color; - info.enabled = initSettings.Livechat_enabled; - info.registrationForm = initSettings.Livechat_registration_form; - info.offlineTitle = initSettings.Livechat_offline_title; - info.offlineColor = initSettings.Livechat_offline_title_color; - info.offlineMessage = initSettings.Livechat_offline_message; - info.offlineSuccessMessage = initSettings.Livechat_offline_success_message; - info.offlineUnavailableMessage = initSettings.Livechat_offline_form_unavailable; - info.displayOfflineForm = initSettings.Livechat_display_offline_form; - info.language = initSettings.Language; - info.videoCall = initSettings.Omnichannel_call_provider === 'Jitsi' && initSettings.Jitsi_Enabled === true; - info.fileUpload = initSettings.Livechat_fileupload_enabled && initSettings.FileUpload_Enabled; - info.transcript = initSettings.Livechat_enable_transcript; - info.transcriptMessage = initSettings.Livechat_transcript_message; - info.conversationFinishedMessage = initSettings.Livechat_conversation_finished_message; - info.conversationFinishedText = initSettings.Livechat_conversation_finished_text; - info.nameFieldRegistrationForm = initSettings.Livechat_name_field_registration_form; - info.emailFieldRegistrationForm = initSettings.Livechat_email_field_registration_form; - info.registrationFormMessage = initSettings.Livechat_registration_form_message; - info.showConnecting = initSettings.Livechat_Show_Connecting; - - info.agentData = room && room[0] && room[0].servedBy && Users.getAgentInfo(room[0].servedBy._id); - - await LivechatTrigger.findEnabled().forEach((trigger) => { - info.triggers.push(_.pick(trigger, '_id', 'actions', 'conditions', 'runOnce')); - }); - - LivechatDepartment.findEnabledWithAgents().forEach((department) => { - info.departments.push(department); - }); - info.allowSwitchingDepartments = initSettings.Livechat_allow_switching_departments; - - info.online = Users.findOnlineAgents().count() > 0; - - return deprecationWarning({ - endpoint: 'livechat:getInitialData', - versionWillBeRemoved: '5.0', - response: info, - }); - }, -}); diff --git a/apps/meteor/app/livechat/server/methods/getNextAgent.js b/apps/meteor/app/livechat/server/methods/getNextAgent.js index 8cfe404c0435..d2f0074e2d1c 100644 --- a/apps/meteor/app/livechat/server/methods/getNextAgent.js +++ b/apps/meteor/app/livechat/server/methods/getNextAgent.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { LivechatRooms, Users } from '../../../models'; +import { LivechatRooms, Users } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/loadHistory.js b/apps/meteor/app/livechat/server/methods/loadHistory.js index a9d59d42ccbd..7e2fd97d5329 100644 --- a/apps/meteor/app/livechat/server/methods/loadHistory.js +++ b/apps/meteor/app/livechat/server/methods/loadHistory.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; import { loadMessageHistory } from '../../../lib'; -import { LivechatVisitors, LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; Meteor.methods({ - 'livechat:loadHistory'({ token, rid, end, limit = 20, ls }) { + async 'livechat:loadHistory'({ token, rid, end, limit = 20, ls }) { if (!token || typeof token !== 'string') { return; } - const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (!visitor) { throw new Meteor.Error('invalid-visitor', 'Invalid Visitor', { diff --git a/apps/meteor/app/livechat/server/methods/loginByToken.js b/apps/meteor/app/livechat/server/methods/loginByToken.js index 9320b1f424d6..4c2c7c658365 100644 --- a/apps/meteor/app/livechat/server/methods/loginByToken.js +++ b/apps/meteor/app/livechat/server/methods/loginByToken.js @@ -1,10 +1,9 @@ import { Meteor } from 'meteor/meteor'; - -import { LivechatVisitors } from '../../../models'; +import { LivechatVisitors } from '@rocket.chat/models'; Meteor.methods({ - 'livechat:loginByToken'(token) { - const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + async 'livechat:loginByToken'(token) { + const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (!visitor) { return; diff --git a/apps/meteor/app/livechat/server/methods/registerGuest.js b/apps/meteor/app/livechat/server/methods/registerGuest.js index b2fd0c0e41d7..b6c7b7f14f32 100644 --- a/apps/meteor/app/livechat/server/methods/registerGuest.js +++ b/apps/meteor/app/livechat/server/methods/registerGuest.js @@ -1,11 +1,12 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Messages, LivechatRooms, LivechatVisitors } from '../../../models'; +import { Messages, LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:registerGuest'({ token, name, email, department, customFields } = {}) { - const userId = Livechat.registerGuest.call(this, { + async 'livechat:registerGuest'({ token, name, email, department, customFields } = {}) { + const userId = await Livechat.registerGuest.call(this, { token, name, email, @@ -15,8 +16,8 @@ Meteor.methods({ // update visited page history to not expire Messages.keepHistoryForToken(token); - const visitor = LivechatVisitors.getVisitorByToken(token, { - fields: { + const visitor = await LivechatVisitors.getVisitorByToken(token, { + projection: { token: 1, name: 1, username: 1, @@ -32,6 +33,7 @@ Meteor.methods({ }); if (customFields && customFields instanceof Array) { + // TODO: refactor to use normal await customFields.forEach((customField) => { if (typeof customField !== 'object') { return; @@ -39,7 +41,7 @@ Meteor.methods({ if (!customField.scope || customField.scope !== 'room') { const { key, value, overwrite } = customField; - LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite); + Promise.await(LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite)); } }); } diff --git a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.js b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.js index da93127f73aa..c70a82a853bd 100644 --- a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.js +++ b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/removeCustomField.js b/apps/meteor/app/livechat/server/methods/removeCustomField.js index 5b6fd869d6bf..d0364716194e 100644 --- a/apps/meteor/app/livechat/server/methods/removeCustomField.js +++ b/apps/meteor/app/livechat/server/methods/removeCustomField.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; -import { LivechatCustomField } from '../../../models'; +import { LivechatCustomField } from '../../../models/server'; Meteor.methods({ 'livechat:removeCustomField'(_id) { diff --git a/apps/meteor/app/livechat/server/methods/removeRoom.js b/apps/meteor/app/livechat/server/methods/removeRoom.js index f8c251e28a36..a39a0abb4f02 100644 --- a/apps/meteor/app/livechat/server/methods/removeRoom.js +++ b/apps/meteor/app/livechat/server/methods/removeRoom.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/removeTrigger.js b/apps/meteor/app/livechat/server/methods/removeTrigger.js index d20a9816a6bb..946b5f68be6f 100644 --- a/apps/meteor/app/livechat/server/methods/removeTrigger.js +++ b/apps/meteor/app/livechat/server/methods/removeTrigger.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { LivechatTrigger } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization/server'; -import { LivechatTrigger } from '../../../models/server/raw'; Meteor.methods({ async 'livechat:removeTrigger'(triggerId) { diff --git a/apps/meteor/app/livechat/server/methods/requestTranscript.js b/apps/meteor/app/livechat/server/methods/requestTranscript.js index 98f85bd66de1..8e3424c8d00f 100644 --- a/apps/meteor/app/livechat/server/methods/requestTranscript.js +++ b/apps/meteor/app/livechat/server/methods/requestTranscript.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/returnAsInquiry.js b/apps/meteor/app/livechat/server/methods/returnAsInquiry.js index 86203317d393..7cedc7ee7499 100644 --- a/apps/meteor/app/livechat/server/methods/returnAsInquiry.js +++ b/apps/meteor/app/livechat/server/methods/returnAsInquiry.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermission } from '../../../authorization'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts index bb0d585456b0..dcded190b982 100644 --- a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts +++ b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts @@ -8,7 +8,7 @@ Meteor.methods({ try { Promise.await(businessHourManager.saveBusinessHour(businessHourData)); } catch (e) { - throw new Meteor.Error(e.message); + throw new Meteor.Error(e instanceof Error ? e.message : String(e)); } }, }); diff --git a/apps/meteor/app/livechat/server/methods/saveCustomField.js b/apps/meteor/app/livechat/server/methods/saveCustomField.js index 1f45ed936fff..f50ac18231cb 100644 --- a/apps/meteor/app/livechat/server/methods/saveCustomField.js +++ b/apps/meteor/app/livechat/server/methods/saveCustomField.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; -import { LivechatCustomField } from '../../../models'; +import { LivechatCustomField } from '../../../models/server'; Meteor.methods({ 'livechat:saveCustomField'(_id, customFieldData) { diff --git a/apps/meteor/app/livechat/server/methods/saveInfo.js b/apps/meteor/app/livechat/server/methods/saveInfo.js index b00ffac58834..c5b4a164d6ca 100644 --- a/apps/meteor/app/livechat/server/methods/saveInfo.js +++ b/apps/meteor/app/livechat/server/methods/saveInfo.js @@ -2,12 +2,12 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; -import { LivechatRooms } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:saveInfo'(guestData, roomData) { + async 'livechat:saveInfo'(guestData, roomData) { const userId = Meteor.userId(); if (!userId || !hasPermission(userId, 'view-l-room')) { @@ -49,7 +49,7 @@ Meteor.methods({ delete guestData.phone; } - const ret = Livechat.saveGuest(guestData, userId) && Livechat.saveRoomInfo(roomData, guestData, userId); + const ret = (await Livechat.saveGuest(guestData, userId)) && Livechat.saveRoomInfo(roomData, guestData, userId); const user = Meteor.users.findOne({ _id: userId }, { fields: { _id: 1, username: 1 } }); diff --git a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js index 2f6b0b815167..7d908cdc7a90 100644 --- a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js +++ b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js @@ -1,17 +1,16 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import _ from 'underscore'; - -import { LivechatRooms, LivechatVisitors } from '../../../models'; +import { LivechatRooms, LivechatVisitors } from '@rocket.chat/models'; Meteor.methods({ - 'livechat:saveSurveyFeedback'(visitorToken, visitorRoom, formData) { + async 'livechat:saveSurveyFeedback'(visitorToken, visitorRoom, formData) { check(visitorToken, String); check(visitorRoom, String); check(formData, [Match.ObjectIncluding({ name: String, value: String })]); - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); - const room = LivechatRooms.findOneById(visitorRoom); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); + const room = await LivechatRooms.findOneById(visitorRoom); if (visitor !== undefined && room !== undefined && room.v !== undefined && room.v.token === visitor.token) { const updateData = {}; diff --git a/apps/meteor/app/livechat/server/methods/saveTrigger.js b/apps/meteor/app/livechat/server/methods/saveTrigger.js index 252d6c022397..66f1aa6be961 100644 --- a/apps/meteor/app/livechat/server/methods/saveTrigger.js +++ b/apps/meteor/app/livechat/server/methods/saveTrigger.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { LivechatTrigger } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization'; -import { LivechatTrigger } from '../../../models/server/raw'; Meteor.methods({ async 'livechat:saveTrigger'(trigger) { diff --git a/apps/meteor/app/livechat/server/methods/searchAgent.js b/apps/meteor/app/livechat/server/methods/searchAgent.js index bfd8d7b67584..600a28150bad 100644 --- a/apps/meteor/app/livechat/server/methods/searchAgent.js +++ b/apps/meteor/app/livechat/server/methods/searchAgent.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { hasPermission } from '../../../authorization'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; Meteor.methods({ 'livechat:searchAgent'(username) { diff --git a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js index 6f9a20d5d731..3f2a3e7c98bb 100644 --- a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js +++ b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors } from '../../../models'; +import { LivechatRooms } from '../../../models/server'; import { FileUpload } from '../../../file-upload/server'; Meteor.methods({ async sendFileLivechatMessage(roomId, visitorToken, file, msgData = {}) { - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!visitor) { return false; diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js index df6861f7af4a..7b6d2d0608b9 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatVisitors } from '../../../models'; import { Livechat } from '../lib/Livechat'; import { settings } from '../../../settings/server'; Meteor.methods({ - sendMessageLivechat({ token, _id, rid, msg, file, attachments }, agent) { + async sendMessageLivechat({ token, _id, rid, msg, file, attachments }, agent) { check(token, String); check(_id, String); check(rid, String); @@ -21,8 +21,8 @@ Meteor.methods({ }), ); - const guest = LivechatVisitors.getVisitorByToken(token, { - fields: { + const guest = await LivechatVisitors.getVisitorByToken(token, { + projection: { name: 1, username: 1, department: 1, diff --git a/apps/meteor/app/livechat/server/methods/sendTranscript.js b/apps/meteor/app/livechat/server/methods/sendTranscript.js index fb8fdc48863e..a29b73236533 100644 --- a/apps/meteor/app/livechat/server/methods/sendTranscript.js +++ b/apps/meteor/app/livechat/server/methods/sendTranscript.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; -import { Users } from '../../../models'; +import { Users } from '../../../models/server'; import { hasPermission } from '../../../authorization'; import { Livechat } from '../lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/methods/setCustomField.js b/apps/meteor/app/livechat/server/methods/setCustomField.js index 14601cbf8adf..cd8576bcd0aa 100644 --- a/apps/meteor/app/livechat/server/methods/setCustomField.js +++ b/apps/meteor/app/livechat/server/methods/setCustomField.js @@ -1,9 +1,10 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatCustomField } from '../../../models'; +import { LivechatRooms, LivechatCustomField } from '../../../models/server'; Meteor.methods({ - 'livechat:setCustomField'(token, key, value, overwrite = true) { + async 'livechat:setCustomField'(token, key, value, overwrite = true) { const customField = LivechatCustomField.findOneById(key); if (customField) { if (customField.scope === 'room') { diff --git a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js index 9d1f7abaccd9..ada1703b34c6 100644 --- a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js +++ b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js @@ -1,18 +1,19 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, Messages, LivechatVisitors } from '../../../models'; +import { LivechatRooms, Messages } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { normalizeTransferredByData } from '../lib/Helper'; Meteor.methods({ - 'livechat:setDepartmentForVisitor'({ roomId, visitorToken, departmentId } = {}) { + async 'livechat:setDepartmentForVisitor'({ roomId, visitorToken, departmentId } = {}) { check(roomId, String); check(visitorToken, String); check(departmentId, String); const room = LivechatRooms.findOneById(roomId); - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!room || room.t !== 'l' || !room.v || room.v.token !== visitor.token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); diff --git a/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js b/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js deleted file mode 100644 index f6a3691faeb1..000000000000 --- a/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js +++ /dev/null @@ -1,29 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; - -import { LivechatVisitors } from '../../../models'; -import { Livechat } from '../lib/Livechat'; -import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; - -Meteor.methods({ - 'livechat:startFileUploadRoom'(roomId, token) { - methodDeprecationLogger.warn('livechat:startFileUploadRoom will be deprecated in future versions of Rocket.Chat'); - const guest = LivechatVisitors.getVisitorByToken(token); - - const message = { - _id: Random.id(), - rid: roomId || Random.id(), - msg: '', - ts: new Date(), - token: guest.token, - }; - - const roomInfo = { - source: OmnichannelSourceType.API, - alias: 'file-upload', - }; - - return Livechat.getRoom(guest, message, roomInfo); - }, -}); diff --git a/apps/meteor/app/livechat/server/methods/startVideoCall.js b/apps/meteor/app/livechat/server/methods/startVideoCall.js deleted file mode 100644 index 3319ca905cf3..000000000000 --- a/apps/meteor/app/livechat/server/methods/startVideoCall.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; - -import { Messages } from '../../../models'; -import { settings } from '../../../settings'; -import { Livechat } from '../lib/Livechat'; -import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; - -Meteor.methods({ - async 'livechat:startVideoCall'(roomId) { - methodDeprecationLogger.warn('livechat:startVideoCall will be deprecated in future versions of Rocket.Chat'); - if (!Meteor.userId()) { - throw new Meteor.Error('error-not-authorized', 'Not authorized', { - method: 'livechat:closeByVisitor', - }); - } - - const guest = Meteor.user(); - - const message = { - _id: Random.id(), - rid: roomId || Random.id(), - msg: '', - ts: new Date(), - }; - - const roomInfo = { - jitsiTimeout: new Date(Date.now() + 3600 * 1000), - source: { - type: OmnichannelSourceType.API, - alias: 'video-call', - }, - }; - - const room = await Livechat.getRoom(guest, message, roomInfo); - message.rid = room._id; - - Messages.createWithTypeRoomIdMessageAndUser('livechat_video_call', room._id, '', guest, { - actionLinks: [ - { icon: 'icon-videocam', i18nLabel: 'Accept', method_id: 'createLivechatCall', params: '' }, - { icon: 'icon-cancel', i18nLabel: 'Decline', method_id: 'denyLivechatCall', params: '' }, - ], - }); - - let rname; - if (settings.get('Jitsi_URL_Room_Hash')) { - rname = settings.get('uniqueID') + roomId; - } else { - rname = encodeURIComponent(room.t === 'd' ? room.usernames.join(' x ') : room.name); - } - return { - roomId: room._id, - domain: settings.get('Jitsi_Domain'), - jitsiRoom: settings.get('Jitsi_URL_Room_Prefix') + rname + settings.get('Jitsi_URL_Room_Suffix'), - }; - }, -}); diff --git a/apps/meteor/app/livechat/server/methods/transfer.js b/apps/meteor/app/livechat/server/methods/transfer.js index 0e29de863aec..52a189c97e5c 100644 --- a/apps/meteor/app/livechat/server/methods/transfer.js +++ b/apps/meteor/app/livechat/server/methods/transfer.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { hasPermission } from '../../../authorization'; -import { LivechatRooms, Subscriptions, LivechatVisitors, Users } from '../../../models'; +import { hasPermission } from '../../../authorization/server'; +import { LivechatRooms, Subscriptions, Users } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { normalizeTransferredByData } from '../lib/Helper'; Meteor.methods({ - 'livechat:transfer'(transferData) { + async 'livechat:transfer'(transferData) { if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'view-l-room')) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:transfer' }); } @@ -38,7 +39,7 @@ Meteor.methods({ }); } - const guest = LivechatVisitors.findOneById(room.v && room.v._id); + const guest = await LivechatVisitors.findOneById(room.v && room.v._id); transferData.transferredBy = normalizeTransferredByData(Meteor.user() || {}, room); if (transferData.userId) { const userToTransfer = Users.findOneById(transferData.userId); diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts index d33c08f6e696..dc1a1a6cbc85 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts @@ -9,7 +9,7 @@ export class AuthorizationLivechat extends ServiceClassInternal implements IAuth protected internal = true; - async canAccessRoom(room: IOmnichannelRoom, user: Pick, extraData?: object): Promise { + async canAccessRoom(room: IOmnichannelRoom, user?: Pick, extraData?: object): Promise { for (const validator of validators) { if (validator(room, user, extraData)) { return true; diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.js b/apps/meteor/app/livechat/server/sendMessageBySMS.js index 72c36d158ce7..587dd2663a29 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.js +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.js @@ -1,7 +1,8 @@ +import { LivechatVisitors } from '@rocket.chat/models'; + import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings'; +import { settings } from '../../settings/server'; import { SMS } from '../../sms'; -import { LivechatVisitors } from '../../models'; import { normalizeMessageFileUpload } from '../../utils/server/functions/normalizeMessageFileUpload'; callbacks.add( @@ -49,7 +50,7 @@ callbacks.add( return message; } - const visitor = LivechatVisitors.getVisitorByToken(room.v.token); + const visitor = Promise.await(LivechatVisitors.getVisitorByToken(room.v.token)); if (!visitor || !visitor.phone || visitor.phone.length === 0) { return message; diff --git a/apps/meteor/app/livechat/server/startup.js b/apps/meteor/app/livechat/server/startup.js index 14229e94948b..f9dcf3201f81 100644 --- a/apps/meteor/app/livechat/server/startup.js +++ b/apps/meteor/app/livechat/server/startup.js @@ -3,7 +3,7 @@ import { Accounts } from 'meteor/accounts-base'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; -import { LivechatRooms } from '../../models'; +import { LivechatRooms } from '../../models/server'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; import { LivechatAgentActivityMonitor } from './statistics/LivechatAgentActivityMonitor'; diff --git a/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts b/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts index 5fbb8c9edd21..7379376a7a72 100644 --- a/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts +++ b/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts @@ -2,10 +2,10 @@ import moment from 'moment'; import { ISocketConnection } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { SyncedCron } from 'meteor/littledata:synced-cron'; +import { LivechatAgentActivity, Sessions } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { Users } from '../../../models/server'; -import { LivechatAgentActivity, Sessions } from '../../../models/server/raw'; const formatDate = (dateTime = new Date()): { date: number } => ({ date: parseInt(moment(dateTime).format('YYYYMMDD')), diff --git a/apps/meteor/app/livestream/client/tabBar.tsx b/apps/meteor/app/livestream/client/tabBar.tsx index d06e2ee90aa6..8d81cf78a915 100644 --- a/apps/meteor/app/livestream/client/tabBar.tsx +++ b/apps/meteor/app/livestream/client/tabBar.tsx @@ -1,6 +1,7 @@ import React, { ReactNode, useMemo } from 'react'; import { Option, Badge } from '@rocket.chat/fuselage'; import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { isRoomFederated } from '@rocket.chat/core-typings'; import { addAction } from '../../../client/views/room/lib/Toolbox'; import Header from '../../../client/components/Header'; @@ -8,7 +9,7 @@ import Header from '../../../client/components/Header'; addAction('livestream', ({ room }) => { const enabled = useSetting('Livestream_enabled'); const t = useTranslation(); - + const federated = isRoomFederated(room); const isLive = room?.streamingOptions?.id && room.streamingOptions.type === 'livestream'; return useMemo( @@ -21,6 +22,10 @@ addAction('livestream', ({ room }) => { icon: 'podcast', template: 'liveStreamTab', order: isLive ? -1 : 15, + ...(federated && { + 'data-tooltip': federated ? 'Livestream_unavailable_for_federation' : '', + 'disabled': true, + }), renderAction: (props): ReactNode => ( {isLive ? ( @@ -41,6 +46,6 @@ addAction('livestream', ({ room }) => { ), } : null, - [enabled, isLive, t], + [enabled, isLive, t, federated], ); }); diff --git a/apps/meteor/app/livestream/client/views/liveStreamTab.js b/apps/meteor/app/livestream/client/views/liveStreamTab.js index 52fe8519ddc3..d819278cb121 100644 --- a/apps/meteor/app/livestream/client/views/liveStreamTab.js +++ b/apps/meteor/app/livestream/client/views/liveStreamTab.js @@ -12,7 +12,7 @@ import { t } from '../../../utils'; import { settings } from '../../../settings'; import { callbacks } from '../../../../lib/callbacks'; import { hasAllPermission } from '../../../authorization'; -import { Users, Rooms } from '../../../models'; +import { Users, Rooms } from '../../../models/client'; import { handleError } from '../../../../client/lib/utils/handleError'; import { dispatchToastMessage } from '../../../../client/lib/toast'; diff --git a/apps/meteor/app/livestream/server/functions/livestream.js b/apps/meteor/app/livestream/server/functions/livestream.js index bba3833650d3..f17a5524c93d 100644 --- a/apps/meteor/app/livestream/server/functions/livestream.js +++ b/apps/meteor/app/livestream/server/functions/livestream.js @@ -1,4 +1,4 @@ -import google from 'googleapis'; +import { google } from 'googleapis'; const { OAuth2 } = google.auth; diff --git a/apps/meteor/app/livestream/server/methods.js b/apps/meteor/app/livestream/server/methods.js index ab8a91988ab2..f9942c3e7d8a 100644 --- a/apps/meteor/app/livestream/server/methods.js +++ b/apps/meteor/app/livestream/server/methods.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { createLiveStream, statusLiveStream, statusStreamLiveStream, getBroadcastStatus, setBroadcastStatus } from './functions/livestream'; -import { settings } from '../../settings'; -import { Rooms } from '../../models'; +import { settings } from '../../settings/server'; +import { Rooms } from '../../models/server'; const selectLivestreamSettings = (user) => user && user.settings && user.settings.livestream; diff --git a/apps/meteor/app/livestream/server/routes.js b/apps/meteor/app/livestream/server/routes.js index 3c63bac44c00..93f340ff5eeb 100644 --- a/apps/meteor/app/livestream/server/routes.js +++ b/apps/meteor/app/livestream/server/routes.js @@ -1,7 +1,7 @@ -import google from 'googleapis'; +import { google } from 'googleapis'; -import { settings } from '../../settings'; -import { Users } from '../../models'; +import { settings } from '../../settings/server'; +import { Users } from '../../models/server'; import { API } from '../../api/server'; const { OAuth2 } = google.auth; diff --git a/apps/meteor/app/mail-messages/server/functions/sendMail.ts b/apps/meteor/app/mail-messages/server/functions/sendMail.ts index dbce8cc9caf0..3f0fad8185c8 100644 --- a/apps/meteor/app/mail-messages/server/functions/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/functions/sendMail.ts @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { EJSON } from 'meteor/ejson'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { FilterQuery } from 'mongodb'; +import type { Filter } from 'mongodb'; import { IUser } from '@rocket.chat/core-typings'; import { placeholders } from '../../../utils/server'; @@ -18,7 +18,7 @@ export const sendMail = function (from: string, subject: string, body: string, d }); } - let userQuery: FilterQuery = { 'mailer.unsubscribed': { $exists: 0 } }; + let userQuery: Filter = { 'mailer.unsubscribed': { $exists: 0 } }; if (query) { userQuery = { $and: [userQuery, EJSON.parse(query)] }; } diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index ae4634fff0ae..75b5f7e11770 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -9,6 +9,7 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; import type { ISetting } from '@rocket.chat/core-typings'; import { settings } from '../../settings/server'; +import { Settings as SettingsRaw } from '../../models/server'; import { replaceVariables } from './replaceVariables'; import { Apps } from '../../apps/server'; import { validateEmail } from '../../../lib/emailValidator'; @@ -35,11 +36,8 @@ export const replace = (str: string, data: { [key: string]: unknown } = {}): str } const options = { - // eslint-disable-next-line @typescript-eslint/camelcase Site_Name: settings.get('Site_Name'), - // eslint-disable-next-line @typescript-eslint/camelcase Site_URL: settings.get('Site_Url'), - // eslint-disable-next-line @typescript-eslint/camelcase Site_URL_Slash: settings.get('Site_Url')?.replace(/\/?$/, '/'), ...(data.name ? { @@ -60,9 +58,7 @@ export const replaceEscaped = (str: string, data: { [key: string]: unknown } = { const siteUrl = settings.get('Site_Url'); return replace(str, { - // eslint-disable-next-line @typescript-eslint/camelcase Site_Name: siteName ? escapeHTML(siteName) : undefined, - // eslint-disable-next-line @typescript-eslint/camelcase Site_Url: siteUrl ? escapeHTML(siteUrl) : undefined, ...Object.entries(data).reduce<{ [key: string]: string }>((ret, [key, value]) => { if (value !== undefined && value !== null) { @@ -169,6 +165,8 @@ export const sendNoWrap = ({ html = undefined; } + SettingsRaw.incrementValueById('Triggered_Emails_Count'); + const email = { to, from, replyTo, subject, html, text, headers }; const eventResult = Promise.await(Apps.triggerEvent('IPreEmailSent', { email })); diff --git a/apps/meteor/app/markdown/lib/hljs.js b/apps/meteor/app/markdown/lib/hljs.js index 6a5232ba2f1c..58c486ac3b86 100644 --- a/apps/meteor/app/markdown/lib/hljs.js +++ b/apps/meteor/app/markdown/lib/hljs.js @@ -1,7 +1,7 @@ -import hljs from 'highlight.js/lib/highlight'; -import clean from 'highlight.js/lib/languages/clean'; -import markdown from 'highlight.js/lib/languages/markdown'; -import javascript from 'highlight.js/lib/languages/javascript'; +import hljs from 'hljs9/lib/highlight'; +import clean from 'hljs9/lib/languages/clean'; +import markdown from 'hljs9/lib/languages/markdown'; +import javascript from 'hljs9/lib/languages/javascript'; hljs.registerLanguage('markdown', markdown); hljs.registerLanguage('clean', clean); @@ -10,351 +10,351 @@ hljs.registerLanguage('javascript', javascript); export const register = async (lang) => { switch (lang) { case 'onec': - return hljs.registerLanguage('onec', (await import('highlight.js/lib/languages/1c')).default); + return hljs.registerLanguage('onec', (await import('hljs9/lib/languages/1c')).default); case 'abnf': - return hljs.registerLanguage('abnf', (await import('highlight.js/lib/languages/abnf')).default); + return hljs.registerLanguage('abnf', (await import('hljs9/lib/languages/abnf')).default); case 'accesslog': - return hljs.registerLanguage('accesslog', (await import('highlight.js/lib/languages/accesslog')).default); + return hljs.registerLanguage('accesslog', (await import('hljs9/lib/languages/accesslog')).default); case 'actionscript': - return hljs.registerLanguage('actionscript', (await import('highlight.js/lib/languages/actionscript')).default); + return hljs.registerLanguage('actionscript', (await import('hljs9/lib/languages/actionscript')).default); case 'ada': - return hljs.registerLanguage('ada', (await import('highlight.js/lib/languages/ada')).default); + return hljs.registerLanguage('ada', (await import('hljs9/lib/languages/ada')).default); case 'apache': - return hljs.registerLanguage('apache', (await import('highlight.js/lib/languages/apache')).default); + return hljs.registerLanguage('apache', (await import('hljs9/lib/languages/apache')).default); case 'applescript': - return hljs.registerLanguage('applescript', (await import('highlight.js/lib/languages/applescript')).default); + return hljs.registerLanguage('applescript', (await import('hljs9/lib/languages/applescript')).default); case 'arduino': - return hljs.registerLanguage('arduino', (await import('highlight.js/lib/languages/arduino')).default); + return hljs.registerLanguage('arduino', (await import('hljs9/lib/languages/arduino')).default); case 'armasm': - return hljs.registerLanguage('armasm', (await import('highlight.js/lib/languages/armasm')).default); + return hljs.registerLanguage('armasm', (await import('hljs9/lib/languages/armasm')).default); case 'asciidoc': - return hljs.registerLanguage('asciidoc', (await import('highlight.js/lib/languages/asciidoc')).default); + return hljs.registerLanguage('asciidoc', (await import('hljs9/lib/languages/asciidoc')).default); case 'aspectj': - return hljs.registerLanguage('aspectj', (await import('highlight.js/lib/languages/aspectj')).default); + return hljs.registerLanguage('aspectj', (await import('hljs9/lib/languages/aspectj')).default); case 'autohotkey': - return hljs.registerLanguage('autohotkey', (await import('highlight.js/lib/languages/autohotkey')).default); + return hljs.registerLanguage('autohotkey', (await import('hljs9/lib/languages/autohotkey')).default); case 'autoit': - return hljs.registerLanguage('autoit', (await import('highlight.js/lib/languages/autoit')).default); + return hljs.registerLanguage('autoit', (await import('hljs9/lib/languages/autoit')).default); case 'avrasm': - return hljs.registerLanguage('avrasm', (await import('highlight.js/lib/languages/avrasm')).default); + return hljs.registerLanguage('avrasm', (await import('hljs9/lib/languages/avrasm')).default); case 'awk': - return hljs.registerLanguage('awk', (await import('highlight.js/lib/languages/awk')).default); + return hljs.registerLanguage('awk', (await import('hljs9/lib/languages/awk')).default); case 'axapta': - return hljs.registerLanguage('axapta', (await import('highlight.js/lib/languages/axapta')).default); + return hljs.registerLanguage('axapta', (await import('hljs9/lib/languages/axapta')).default); case 'bash': - return hljs.registerLanguage('bash', (await import('highlight.js/lib/languages/bash')).default); + return hljs.registerLanguage('bash', (await import('hljs9/lib/languages/bash')).default); case 'basic': - return hljs.registerLanguage('basic', (await import('highlight.js/lib/languages/basic')).default); + return hljs.registerLanguage('basic', (await import('hljs9/lib/languages/basic')).default); case 'bnf': - return hljs.registerLanguage('bnf', (await import('highlight.js/lib/languages/bnf')).default); + return hljs.registerLanguage('bnf', (await import('hljs9/lib/languages/bnf')).default); case 'brainfuck': - return hljs.registerLanguage('brainfuck', (await import('highlight.js/lib/languages/brainfuck')).default); + return hljs.registerLanguage('brainfuck', (await import('hljs9/lib/languages/brainfuck')).default); case 'cal': - return hljs.registerLanguage('cal', (await import('highlight.js/lib/languages/cal')).default); + return hljs.registerLanguage('cal', (await import('hljs9/lib/languages/cal')).default); case 'capnproto': - return hljs.registerLanguage('capnproto', (await import('highlight.js/lib/languages/capnproto')).default); + return hljs.registerLanguage('capnproto', (await import('hljs9/lib/languages/capnproto')).default); case 'ceylon': - return hljs.registerLanguage('ceylon', (await import('highlight.js/lib/languages/ceylon')).default); + return hljs.registerLanguage('ceylon', (await import('hljs9/lib/languages/ceylon')).default); case 'clean': - return hljs.registerLanguage('clean', (await import('highlight.js/lib/languages/clean')).default); + return hljs.registerLanguage('clean', (await import('hljs9/lib/languages/clean')).default); case 'clojure': - return hljs.registerLanguage('clojure', (await import('highlight.js/lib/languages/clojure')).default); + return hljs.registerLanguage('clojure', (await import('hljs9/lib/languages/clojure')).default); case 'clojure-repl': - return hljs.registerLanguage('clojure-repl', (await import('highlight.js/lib/languages/clojure-repl')).default); + return hljs.registerLanguage('clojure-repl', (await import('hljs9/lib/languages/clojure-repl')).default); case 'cmake': - return hljs.registerLanguage('cmake', (await import('highlight.js/lib/languages/cmake')).default); + return hljs.registerLanguage('cmake', (await import('hljs9/lib/languages/cmake')).default); case 'coffeescript': - return hljs.registerLanguage('coffeescript', (await import('highlight.js/lib/languages/coffeescript')).default); + return hljs.registerLanguage('coffeescript', (await import('hljs9/lib/languages/coffeescript')).default); case 'coq': - return hljs.registerLanguage('coq', (await import('highlight.js/lib/languages/coq')).default); + return hljs.registerLanguage('coq', (await import('hljs9/lib/languages/coq')).default); case 'cos': - return hljs.registerLanguage('cos', (await import('highlight.js/lib/languages/cos')).default); + return hljs.registerLanguage('cos', (await import('hljs9/lib/languages/cos')).default); case 'cpp': - return hljs.registerLanguage('cpp', (await import('highlight.js/lib/languages/cpp')).default); + return hljs.registerLanguage('cpp', (await import('hljs9/lib/languages/cpp')).default); case 'crmsh': - return hljs.registerLanguage('crmsh', (await import('highlight.js/lib/languages/crmsh')).default); + return hljs.registerLanguage('crmsh', (await import('hljs9/lib/languages/crmsh')).default); case 'crystal': - return hljs.registerLanguage('crystal', (await import('highlight.js/lib/languages/crystal')).default); + return hljs.registerLanguage('crystal', (await import('hljs9/lib/languages/crystal')).default); case 'cs': - return hljs.registerLanguage('cs', (await import('highlight.js/lib/languages/cs')).default); + return hljs.registerLanguage('cs', (await import('hljs9/lib/languages/cs')).default); case 'csp': - return hljs.registerLanguage('csp', (await import('highlight.js/lib/languages/csp')).default); + return hljs.registerLanguage('csp', (await import('hljs9/lib/languages/csp')).default); case 'css': - return hljs.registerLanguage('css', (await import('highlight.js/lib/languages/css')).default); + return hljs.registerLanguage('css', (await import('hljs9/lib/languages/css')).default); case 'd': - return hljs.registerLanguage('d', (await import('highlight.js/lib/languages/d')).default); + return hljs.registerLanguage('d', (await import('hljs9/lib/languages/d')).default); case 'dart': - return hljs.registerLanguage('dart', (await import('highlight.js/lib/languages/dart')).default); + return hljs.registerLanguage('dart', (await import('hljs9/lib/languages/dart')).default); case 'delphi': - return hljs.registerLanguage('delphi', (await import('highlight.js/lib/languages/delphi')).default); + return hljs.registerLanguage('delphi', (await import('hljs9/lib/languages/delphi')).default); case 'diff': - return hljs.registerLanguage('diff', (await import('highlight.js/lib/languages/diff')).default); + return hljs.registerLanguage('diff', (await import('hljs9/lib/languages/diff')).default); case 'django': - return hljs.registerLanguage('django', (await import('highlight.js/lib/languages/django')).default); + return hljs.registerLanguage('django', (await import('hljs9/lib/languages/django')).default); case 'dns': - return hljs.registerLanguage('dns', (await import('highlight.js/lib/languages/dns')).default); + return hljs.registerLanguage('dns', (await import('hljs9/lib/languages/dns')).default); case 'dockerfile': - return hljs.registerLanguage('dockerfile', (await import('highlight.js/lib/languages/dockerfile')).default); + return hljs.registerLanguage('dockerfile', (await import('hljs9/lib/languages/dockerfile')).default); case 'dos': - return hljs.registerLanguage('dos', (await import('highlight.js/lib/languages/dos')).default); + return hljs.registerLanguage('dos', (await import('hljs9/lib/languages/dos')).default); case 'dsconfig': - return hljs.registerLanguage('dsconfig', (await import('highlight.js/lib/languages/dsconfig')).default); + return hljs.registerLanguage('dsconfig', (await import('hljs9/lib/languages/dsconfig')).default); case 'dts': - return hljs.registerLanguage('dts', (await import('highlight.js/lib/languages/dts')).default); + return hljs.registerLanguage('dts', (await import('hljs9/lib/languages/dts')).default); case 'dust': - return hljs.registerLanguage('dust', (await import('highlight.js/lib/languages/dust')).default); + return hljs.registerLanguage('dust', (await import('hljs9/lib/languages/dust')).default); case 'ebnf': - return hljs.registerLanguage('ebnf', (await import('highlight.js/lib/languages/ebnf')).default); + return hljs.registerLanguage('ebnf', (await import('hljs9/lib/languages/ebnf')).default); case 'elixir': - return hljs.registerLanguage('elixir', (await import('highlight.js/lib/languages/elixir')).default); + return hljs.registerLanguage('elixir', (await import('hljs9/lib/languages/elixir')).default); case 'elm': - return hljs.registerLanguage('elm', (await import('highlight.js/lib/languages/elm')).default); + return hljs.registerLanguage('elm', (await import('hljs9/lib/languages/elm')).default); case 'erb': - return hljs.registerLanguage('erb', (await import('highlight.js/lib/languages/erb')).default); + return hljs.registerLanguage('erb', (await import('hljs9/lib/languages/erb')).default); case 'erlang': - return hljs.registerLanguage('erlang', (await import('highlight.js/lib/languages/erlang')).default); + return hljs.registerLanguage('erlang', (await import('hljs9/lib/languages/erlang')).default); case 'excel': - return hljs.registerLanguage('excel', (await import('highlight.js/lib/languages/excel')).default); + return hljs.registerLanguage('excel', (await import('hljs9/lib/languages/excel')).default); case 'fix': - return hljs.registerLanguage('fix', (await import('highlight.js/lib/languages/fix')).default); + return hljs.registerLanguage('fix', (await import('hljs9/lib/languages/fix')).default); case 'flix': - return hljs.registerLanguage('flix', (await import('highlight.js/lib/languages/flix')).default); + return hljs.registerLanguage('flix', (await import('hljs9/lib/languages/flix')).default); case 'fortran': - return hljs.registerLanguage('fortran', (await import('highlight.js/lib/languages/fortran')).default); + return hljs.registerLanguage('fortran', (await import('hljs9/lib/languages/fortran')).default); case 'fsharp': - return hljs.registerLanguage('fsharp', (await import('highlight.js/lib/languages/fsharp')).default); + return hljs.registerLanguage('fsharp', (await import('hljs9/lib/languages/fsharp')).default); case 'gams': - return hljs.registerLanguage('gams', (await import('highlight.js/lib/languages/gams')).default); + return hljs.registerLanguage('gams', (await import('hljs9/lib/languages/gams')).default); case 'gauss': - return hljs.registerLanguage('gauss', (await import('highlight.js/lib/languages/gauss')).default); + return hljs.registerLanguage('gauss', (await import('hljs9/lib/languages/gauss')).default); case 'gcode': - return hljs.registerLanguage('gcode', (await import('highlight.js/lib/languages/gcode')).default); + return hljs.registerLanguage('gcode', (await import('hljs9/lib/languages/gcode')).default); case 'gherkin': - return hljs.registerLanguage('gherkin', (await import('highlight.js/lib/languages/gherkin')).default); + return hljs.registerLanguage('gherkin', (await import('hljs9/lib/languages/gherkin')).default); case 'glsl': - return hljs.registerLanguage('glsl', (await import('highlight.js/lib/languages/glsl')).default); + return hljs.registerLanguage('glsl', (await import('hljs9/lib/languages/glsl')).default); case 'go': - return hljs.registerLanguage('go', (await import('highlight.js/lib/languages/go')).default); + return hljs.registerLanguage('go', (await import('hljs9/lib/languages/go')).default); case 'golo': - return hljs.registerLanguage('golo', (await import('highlight.js/lib/languages/golo')).default); + return hljs.registerLanguage('golo', (await import('hljs9/lib/languages/golo')).default); case 'gradle': - return hljs.registerLanguage('gradle', (await import('highlight.js/lib/languages/gradle')).default); + return hljs.registerLanguage('gradle', (await import('hljs9/lib/languages/gradle')).default); case 'groovy': - return hljs.registerLanguage('groovy', (await import('highlight.js/lib/languages/groovy')).default); + return hljs.registerLanguage('groovy', (await import('hljs9/lib/languages/groovy')).default); case 'haml': - return hljs.registerLanguage('haml', (await import('highlight.js/lib/languages/haml')).default); + return hljs.registerLanguage('haml', (await import('hljs9/lib/languages/haml')).default); case 'handlebars': - return hljs.registerLanguage('handlebars', (await import('highlight.js/lib/languages/handlebars')).default); + return hljs.registerLanguage('handlebars', (await import('hljs9/lib/languages/handlebars')).default); case 'haskell': - return hljs.registerLanguage('haskell', (await import('highlight.js/lib/languages/haskell')).default); + return hljs.registerLanguage('haskell', (await import('hljs9/lib/languages/haskell')).default); case 'haxe': - return hljs.registerLanguage('haxe', (await import('highlight.js/lib/languages/haxe')).default); + return hljs.registerLanguage('haxe', (await import('hljs9/lib/languages/haxe')).default); case 'hsp': - return hljs.registerLanguage('hsp', (await import('highlight.js/lib/languages/hsp')).default); + return hljs.registerLanguage('hsp', (await import('hljs9/lib/languages/hsp')).default); case 'htmlbars': - return hljs.registerLanguage('htmlbars', (await import('highlight.js/lib/languages/htmlbars')).default); + return hljs.registerLanguage('htmlbars', (await import('hljs9/lib/languages/htmlbars')).default); case 'http': - return hljs.registerLanguage('http', (await import('highlight.js/lib/languages/http')).default); + return hljs.registerLanguage('http', (await import('hljs9/lib/languages/http')).default); case 'hy': - return hljs.registerLanguage('hy', (await import('highlight.js/lib/languages/hy')).default); + return hljs.registerLanguage('hy', (await import('hljs9/lib/languages/hy')).default); case 'inform7': - return hljs.registerLanguage('inform7', (await import('highlight.js/lib/languages/inform7')).default); + return hljs.registerLanguage('inform7', (await import('hljs9/lib/languages/inform7')).default); case 'ini': - return hljs.registerLanguage('ini', (await import('highlight.js/lib/languages/ini')).default); + return hljs.registerLanguage('ini', (await import('hljs9/lib/languages/ini')).default); case 'irpf90': - return hljs.registerLanguage('irpf90', (await import('highlight.js/lib/languages/irpf90')).default); + return hljs.registerLanguage('irpf90', (await import('hljs9/lib/languages/irpf90')).default); case 'java': - return hljs.registerLanguage('java', (await import('highlight.js/lib/languages/java')).default); + return hljs.registerLanguage('java', (await import('hljs9/lib/languages/java')).default); case 'javascript': - return hljs.registerLanguage('javascript', (await import('highlight.js/lib/languages/javascript')).default); + return hljs.registerLanguage('javascript', (await import('hljs9/lib/languages/javascript')).default); case 'jboss-cli': - return hljs.registerLanguage('jboss-cli', (await import('highlight.js/lib/languages/jboss-cli')).default); + return hljs.registerLanguage('jboss-cli', (await import('hljs9/lib/languages/jboss-cli')).default); case 'json': - return hljs.registerLanguage('json', (await import('highlight.js/lib/languages/json')).default); + return hljs.registerLanguage('json', (await import('hljs9/lib/languages/json')).default); case 'julia': - return hljs.registerLanguage('julia', (await import('highlight.js/lib/languages/julia')).default); + return hljs.registerLanguage('julia', (await import('hljs9/lib/languages/julia')).default); case 'julia-repl': - return hljs.registerLanguage('julia-repl', (await import('highlight.js/lib/languages/julia-repl')).default); + return hljs.registerLanguage('julia-repl', (await import('hljs9/lib/languages/julia-repl')).default); case 'kotlin': - return hljs.registerLanguage('kotlin', (await import('highlight.js/lib/languages/kotlin')).default); + return hljs.registerLanguage('kotlin', (await import('hljs9/lib/languages/kotlin')).default); case 'lasso': - return hljs.registerLanguage('lasso', (await import('highlight.js/lib/languages/lasso')).default); + return hljs.registerLanguage('lasso', (await import('hljs9/lib/languages/lasso')).default); case 'ldif': - return hljs.registerLanguage('ldif', (await import('highlight.js/lib/languages/ldif')).default); + return hljs.registerLanguage('ldif', (await import('hljs9/lib/languages/ldif')).default); case 'leaf': - return hljs.registerLanguage('leaf', (await import('highlight.js/lib/languages/leaf')).default); + return hljs.registerLanguage('leaf', (await import('hljs9/lib/languages/leaf')).default); case 'less': - return hljs.registerLanguage('less', (await import('highlight.js/lib/languages/less')).default); + return hljs.registerLanguage('less', (await import('hljs9/lib/languages/less')).default); case 'lisp': - return hljs.registerLanguage('lisp', (await import('highlight.js/lib/languages/lisp')).default); + return hljs.registerLanguage('lisp', (await import('hljs9/lib/languages/lisp')).default); case 'livecodeserver': - return hljs.registerLanguage('livecodeserver', (await import('highlight.js/lib/languages/livecodeserver')).default); + return hljs.registerLanguage('livecodeserver', (await import('hljs9/lib/languages/livecodeserver')).default); case 'livescript': - return hljs.registerLanguage('livescript', (await import('highlight.js/lib/languages/livescript')).default); + return hljs.registerLanguage('livescript', (await import('hljs9/lib/languages/livescript')).default); case 'llvm': - return hljs.registerLanguage('llvm', (await import('highlight.js/lib/languages/llvm')).default); + return hljs.registerLanguage('llvm', (await import('hljs9/lib/languages/llvm')).default); case 'lsl': - return hljs.registerLanguage('lsl', (await import('highlight.js/lib/languages/lsl')).default); + return hljs.registerLanguage('lsl', (await import('hljs9/lib/languages/lsl')).default); case 'lua': - return hljs.registerLanguage('lua', (await import('highlight.js/lib/languages/lua')).default); + return hljs.registerLanguage('lua', (await import('hljs9/lib/languages/lua')).default); case 'makefile': - return hljs.registerLanguage('makefile', (await import('highlight.js/lib/languages/makefile')).default); + return hljs.registerLanguage('makefile', (await import('hljs9/lib/languages/makefile')).default); case 'markdown': - return hljs.registerLanguage('markdown', (await import('highlight.js/lib/languages/markdown')).default); + return hljs.registerLanguage('markdown', (await import('hljs9/lib/languages/markdown')).default); case 'mathematica': - return hljs.registerLanguage('mathematica', (await import('highlight.js/lib/languages/mathematica')).default); + return hljs.registerLanguage('mathematica', (await import('hljs9/lib/languages/mathematica')).default); case 'matlab': - return hljs.registerLanguage('matlab', (await import('highlight.js/lib/languages/matlab')).default); + return hljs.registerLanguage('matlab', (await import('hljs9/lib/languages/matlab')).default); case 'maxima': - return hljs.registerLanguage('maxima', (await import('highlight.js/lib/languages/maxima')).default); + return hljs.registerLanguage('maxima', (await import('hljs9/lib/languages/maxima')).default); case 'mel': - return hljs.registerLanguage('mel', (await import('highlight.js/lib/languages/mel')).default); + return hljs.registerLanguage('mel', (await import('hljs9/lib/languages/mel')).default); case 'mercury': - return hljs.registerLanguage('mercury', (await import('highlight.js/lib/languages/mercury')).default); + return hljs.registerLanguage('mercury', (await import('hljs9/lib/languages/mercury')).default); case 'mipsasm': - return hljs.registerLanguage('mipsasm', (await import('highlight.js/lib/languages/mipsasm')).default); + return hljs.registerLanguage('mipsasm', (await import('hljs9/lib/languages/mipsasm')).default); case 'mizar': - return hljs.registerLanguage('mizar', (await import('highlight.js/lib/languages/mizar')).default); + return hljs.registerLanguage('mizar', (await import('hljs9/lib/languages/mizar')).default); case 'perl': - return hljs.registerLanguage('perl', (await import('highlight.js/lib/languages/perl')).default); + return hljs.registerLanguage('perl', (await import('hljs9/lib/languages/perl')).default); case 'mojolicious': - return hljs.registerLanguage('mojolicious', (await import('highlight.js/lib/languages/mojolicious')).default); + return hljs.registerLanguage('mojolicious', (await import('hljs9/lib/languages/mojolicious')).default); case 'monkey': - return hljs.registerLanguage('monkey', (await import('highlight.js/lib/languages/monkey')).default); + return hljs.registerLanguage('monkey', (await import('hljs9/lib/languages/monkey')).default); case 'moonscript': - return hljs.registerLanguage('moonscript', (await import('highlight.js/lib/languages/moonscript')).default); + return hljs.registerLanguage('moonscript', (await import('hljs9/lib/languages/moonscript')).default); case 'n1ql': - return hljs.registerLanguage('n1ql', (await import('highlight.js/lib/languages/n1ql')).default); + return hljs.registerLanguage('n1ql', (await import('hljs9/lib/languages/n1ql')).default); case 'nginx': - return hljs.registerLanguage('nginx', (await import('highlight.js/lib/languages/nginx')).default); + return hljs.registerLanguage('nginx', (await import('hljs9/lib/languages/nginx')).default); case 'nimrod': - return hljs.registerLanguage('nimrod', (await import('highlight.js/lib/languages/nimrod')).default); + return hljs.registerLanguage('nimrod', (await import('hljs9/lib/languages/nimrod')).default); case 'nix': - return hljs.registerLanguage('nix', (await import('highlight.js/lib/languages/nix')).default); + return hljs.registerLanguage('nix', (await import('hljs9/lib/languages/nix')).default); case 'nsis': - return hljs.registerLanguage('nsis', (await import('highlight.js/lib/languages/nsis')).default); + return hljs.registerLanguage('nsis', (await import('hljs9/lib/languages/nsis')).default); case 'objectivec': - return hljs.registerLanguage('objectivec', (await import('highlight.js/lib/languages/objectivec')).default); + return hljs.registerLanguage('objectivec', (await import('hljs9/lib/languages/objectivec')).default); case 'ocaml': - return hljs.registerLanguage('ocaml', (await import('highlight.js/lib/languages/ocaml')).default); + return hljs.registerLanguage('ocaml', (await import('hljs9/lib/languages/ocaml')).default); case 'openscad': - return hljs.registerLanguage('openscad', (await import('highlight.js/lib/languages/openscad')).default); + return hljs.registerLanguage('openscad', (await import('hljs9/lib/languages/openscad')).default); case 'oxygene': - return hljs.registerLanguage('oxygene', (await import('highlight.js/lib/languages/oxygene')).default); + return hljs.registerLanguage('oxygene', (await import('hljs9/lib/languages/oxygene')).default); case 'parser3': - return hljs.registerLanguage('parser3', (await import('highlight.js/lib/languages/parser3')).default); + return hljs.registerLanguage('parser3', (await import('hljs9/lib/languages/parser3')).default); case 'pf': - return hljs.registerLanguage('pf', (await import('highlight.js/lib/languages/pf')).default); + return hljs.registerLanguage('pf', (await import('hljs9/lib/languages/pf')).default); case 'php': - return hljs.registerLanguage('php', (await import('highlight.js/lib/languages/php')).default); + return hljs.registerLanguage('php', (await import('hljs9/lib/languages/php')).default); case 'pony': - return hljs.registerLanguage('pony', (await import('highlight.js/lib/languages/pony')).default); + return hljs.registerLanguage('pony', (await import('hljs9/lib/languages/pony')).default); case 'powershell': - return hljs.registerLanguage('powershell', (await import('highlight.js/lib/languages/powershell')).default); + return hljs.registerLanguage('powershell', (await import('hljs9/lib/languages/powershell')).default); case 'processing': - return hljs.registerLanguage('processing', (await import('highlight.js/lib/languages/processing')).default); + return hljs.registerLanguage('processing', (await import('hljs9/lib/languages/processing')).default); case 'profile': - return hljs.registerLanguage('profile', (await import('highlight.js/lib/languages/profile')).default); + return hljs.registerLanguage('profile', (await import('hljs9/lib/languages/profile')).default); case 'prolog': - return hljs.registerLanguage('prolog', (await import('highlight.js/lib/languages/prolog')).default); + return hljs.registerLanguage('prolog', (await import('hljs9/lib/languages/prolog')).default); case 'protobuf': - return hljs.registerLanguage('protobuf', (await import('highlight.js/lib/languages/protobuf')).default); + return hljs.registerLanguage('protobuf', (await import('hljs9/lib/languages/protobuf')).default); case 'puppet': - return hljs.registerLanguage('puppet', (await import('highlight.js/lib/languages/puppet')).default); + return hljs.registerLanguage('puppet', (await import('hljs9/lib/languages/puppet')).default); case 'purebasic': - return hljs.registerLanguage('purebasic', (await import('highlight.js/lib/languages/purebasic')).default); + return hljs.registerLanguage('purebasic', (await import('hljs9/lib/languages/purebasic')).default); case 'python': - return hljs.registerLanguage('python', (await import('highlight.js/lib/languages/python')).default); + return hljs.registerLanguage('python', (await import('hljs9/lib/languages/python')).default); case 'q': - return hljs.registerLanguage('q', (await import('highlight.js/lib/languages/q')).default); + return hljs.registerLanguage('q', (await import('hljs9/lib/languages/q')).default); case 'qml': - return hljs.registerLanguage('qml', (await import('highlight.js/lib/languages/qml')).default); + return hljs.registerLanguage('qml', (await import('hljs9/lib/languages/qml')).default); case 'r': - return hljs.registerLanguage('r', (await import('highlight.js/lib/languages/r')).default); + return hljs.registerLanguage('r', (await import('hljs9/lib/languages/r')).default); case 'rib': - return hljs.registerLanguage('rib', (await import('highlight.js/lib/languages/rib')).default); + return hljs.registerLanguage('rib', (await import('hljs9/lib/languages/rib')).default); case 'roboconf': - return hljs.registerLanguage('roboconf', (await import('highlight.js/lib/languages/roboconf')).default); + return hljs.registerLanguage('roboconf', (await import('hljs9/lib/languages/roboconf')).default); case 'rsl': - return hljs.registerLanguage('rsl', (await import('highlight.js/lib/languages/rsl')).default); + return hljs.registerLanguage('rsl', (await import('hljs9/lib/languages/rsl')).default); case 'ruleslanguage': - return hljs.registerLanguage('ruleslanguage', (await import('highlight.js/lib/languages/ruleslanguage')).default); + return hljs.registerLanguage('ruleslanguage', (await import('hljs9/lib/languages/ruleslanguage')).default); case 'rust': - return hljs.registerLanguage('rust', (await import('highlight.js/lib/languages/rust')).default); + return hljs.registerLanguage('rust', (await import('hljs9/lib/languages/rust')).default); case 'scala': - return hljs.registerLanguage('scala', (await import('highlight.js/lib/languages/scala')).default); + return hljs.registerLanguage('scala', (await import('hljs9/lib/languages/scala')).default); case 'scheme': - return hljs.registerLanguage('scheme', (await import('highlight.js/lib/languages/scheme')).default); + return hljs.registerLanguage('scheme', (await import('hljs9/lib/languages/scheme')).default); case 'scilab': - return hljs.registerLanguage('scilab', (await import('highlight.js/lib/languages/scilab')).default); + return hljs.registerLanguage('scilab', (await import('hljs9/lib/languages/scilab')).default); case 'scss': - return hljs.registerLanguage('scss', (await import('highlight.js/lib/languages/scss')).default); + return hljs.registerLanguage('scss', (await import('hljs9/lib/languages/scss')).default); case 'shell': - return hljs.registerLanguage('shell', (await import('highlight.js/lib/languages/shell')).default); + return hljs.registerLanguage('shell', (await import('hljs9/lib/languages/shell')).default); case 'smali': - return hljs.registerLanguage('smali', (await import('highlight.js/lib/languages/smali')).default); + return hljs.registerLanguage('smali', (await import('hljs9/lib/languages/smali')).default); case 'smalltalk': - return hljs.registerLanguage('smalltalk', (await import('highlight.js/lib/languages/smalltalk')).default); + return hljs.registerLanguage('smalltalk', (await import('hljs9/lib/languages/smalltalk')).default); case 'sml': - return hljs.registerLanguage('sml', (await import('highlight.js/lib/languages/sml')).default); + return hljs.registerLanguage('sml', (await import('hljs9/lib/languages/sml')).default); case 'sqf': - return hljs.registerLanguage('sqf', (await import('highlight.js/lib/languages/sqf')).default); + return hljs.registerLanguage('sqf', (await import('hljs9/lib/languages/sqf')).default); case 'sql': - return hljs.registerLanguage('sql', (await import('highlight.js/lib/languages/sql')).default); + return hljs.registerLanguage('sql', (await import('hljs9/lib/languages/sql')).default); case 'stan': - return hljs.registerLanguage('stan', (await import('highlight.js/lib/languages/stan')).default); + return hljs.registerLanguage('stan', (await import('hljs9/lib/languages/stan')).default); case 'stata': - return hljs.registerLanguage('stata', (await import('highlight.js/lib/languages/stata')).default); + return hljs.registerLanguage('stata', (await import('hljs9/lib/languages/stata')).default); case 'step21': - return hljs.registerLanguage('step21', (await import('highlight.js/lib/languages/step21')).default); + return hljs.registerLanguage('step21', (await import('hljs9/lib/languages/step21')).default); case 'stylus': - return hljs.registerLanguage('stylus', (await import('highlight.js/lib/languages/stylus')).default); + return hljs.registerLanguage('stylus', (await import('hljs9/lib/languages/stylus')).default); case 'subunit': - return hljs.registerLanguage('subunit', (await import('highlight.js/lib/languages/subunit')).default); + return hljs.registerLanguage('subunit', (await import('hljs9/lib/languages/subunit')).default); case 'swift': - return hljs.registerLanguage('swift', (await import('highlight.js/lib/languages/swift')).default); + return hljs.registerLanguage('swift', (await import('hljs9/lib/languages/swift')).default); case 'taggerscript': - return hljs.registerLanguage('taggerscript', (await import('highlight.js/lib/languages/taggerscript')).default); + return hljs.registerLanguage('taggerscript', (await import('hljs9/lib/languages/taggerscript')).default); case 'yaml': - return hljs.registerLanguage('yaml', (await import('highlight.js/lib/languages/yaml')).default); + return hljs.registerLanguage('yaml', (await import('hljs9/lib/languages/yaml')).default); case 'tap': - return hljs.registerLanguage('tap', (await import('highlight.js/lib/languages/tap')).default); + return hljs.registerLanguage('tap', (await import('hljs9/lib/languages/tap')).default); case 'tcl': - return hljs.registerLanguage('tcl', (await import('highlight.js/lib/languages/tcl')).default); + return hljs.registerLanguage('tcl', (await import('hljs9/lib/languages/tcl')).default); case 'tex': - return hljs.registerLanguage('tex', (await import('highlight.js/lib/languages/tex')).default); + return hljs.registerLanguage('tex', (await import('hljs9/lib/languages/tex')).default); case 'thrift': - return hljs.registerLanguage('thrift', (await import('highlight.js/lib/languages/thrift')).default); + return hljs.registerLanguage('thrift', (await import('hljs9/lib/languages/thrift')).default); case 'tp': - return hljs.registerLanguage('tp', (await import('highlight.js/lib/languages/tp')).default); + return hljs.registerLanguage('tp', (await import('hljs9/lib/languages/tp')).default); case 'twig': - return hljs.registerLanguage('twig', (await import('highlight.js/lib/languages/twig')).default); + return hljs.registerLanguage('twig', (await import('hljs9/lib/languages/twig')).default); case 'typescript': - return hljs.registerLanguage('typescript', (await import('highlight.js/lib/languages/typescript')).default); + return hljs.registerLanguage('typescript', (await import('hljs9/lib/languages/typescript')).default); case 'vala': - return hljs.registerLanguage('vala', (await import('highlight.js/lib/languages/vala')).default); + return hljs.registerLanguage('vala', (await import('hljs9/lib/languages/vala')).default); case 'vbnet': - return hljs.registerLanguage('vbnet', (await import('highlight.js/lib/languages/vbnet')).default); + return hljs.registerLanguage('vbnet', (await import('hljs9/lib/languages/vbnet')).default); case 'vbscript': - return hljs.registerLanguage('vbscript', (await import('highlight.js/lib/languages/vbscript')).default); + return hljs.registerLanguage('vbscript', (await import('hljs9/lib/languages/vbscript')).default); case 'vbscript-html': - return hljs.registerLanguage('vbscript-html(', (await import('highlight.js/lib/languages/vbscript-html')).default); + return hljs.registerLanguage('vbscript-html(', (await import('hljs9/lib/languages/vbscript-html')).default); case 'verilog': - return hljs.registerLanguage('verilog', (await import('highlight.js/lib/languages/verilog')).default); + return hljs.registerLanguage('verilog', (await import('hljs9/lib/languages/verilog')).default); case 'vhdl': - return hljs.registerLanguage('vhdl', (await import('highlight.js/lib/languages/vhdl')).default); + return hljs.registerLanguage('vhdl', (await import('hljs9/lib/languages/vhdl')).default); case 'vim': - return hljs.registerLanguage('vim', (await import('highlight.js/lib/languages/vim')).default); + return hljs.registerLanguage('vim', (await import('hljs9/lib/languages/vim')).default); case 'x86asm': - return hljs.registerLanguage('x86asm', (await import('highlight.js/lib/languages/x86asm')).default); + return hljs.registerLanguage('x86asm', (await import('hljs9/lib/languages/x86asm')).default); case 'xl': - return hljs.registerLanguage('xl', (await import('highlight.js/lib/languages/xl')).default); + return hljs.registerLanguage('xl', (await import('hljs9/lib/languages/xl')).default); case 'xquery': - return hljs.registerLanguage('xquery', (await import('highlight.js/lib/languages/xquery')).default); + return hljs.registerLanguage('xquery', (await import('hljs9/lib/languages/xquery')).default); case 'zephir': - return hljs.registerLanguage('zephir', (await import('highlight.js/lib/languages/zephir')).default); + return hljs.registerLanguage('zephir', (await import('hljs9/lib/languages/zephir')).default); default: - return hljs.registerLanguage('plaintext', (await import('highlight.js/lib/languages/plaintext')).default); + return hljs.registerLanguage('plaintext', (await import('hljs9/lib/languages/plaintext')).default); } }; diff --git a/apps/meteor/app/markdown/lib/parser/marked/marked.js b/apps/meteor/app/markdown/lib/parser/marked/marked.js index 22f7974fcace..a1aff30618c4 100644 --- a/apps/meteor/app/markdown/lib/parser/marked/marked.js +++ b/apps/meteor/app/markdown/lib/parser/marked/marked.js @@ -1,6 +1,6 @@ import { Random } from 'meteor/random'; import _ from 'underscore'; -import _marked from 'marked'; +import { marked as _marked } from 'marked'; import createDOMPurify from 'dompurify'; import { unescapeHTML, escapeHTML } from '@rocket.chat/string-helpers'; @@ -87,7 +87,7 @@ export const marked = (message, { marked: { gfm, tables, breaks, pedantic, smart message.tokens = []; } - message.html = _marked(unescapeHTML(message.html), { + message.html = _marked.parse(unescapeHTML(message.html), { gfm, tables, breaks, diff --git a/apps/meteor/app/markdown/server/index.js b/apps/meteor/app/markdown/server/index.js index 4a4a56a13f89..9b4f3084c58b 100644 --- a/apps/meteor/app/markdown/server/index.js +++ b/apps/meteor/app/markdown/server/index.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings'; +import { settings } from '../../settings/server'; import { createMarkdownMessageRenderer, createMarkdownNotificationRenderer } from '../lib/markdown'; import './settings'; diff --git a/apps/meteor/app/markdown/server/settings.ts b/apps/meteor/app/markdown/server/settings.ts index e25ed0a1872d..b2f231059097 100644 --- a/apps/meteor/app/markdown/server/settings.ts +++ b/apps/meteor/app/markdown/server/settings.ts @@ -19,6 +19,7 @@ settingsRegistry.add('Markdown_Parser', 'original', { group: 'Message', section: 'Markdown', public: true, + alert: 'Use_Legacy_Message_Template_alert', }); const enableQueryOriginal = { _id: 'Markdown_Parser', value: 'original' }; diff --git a/apps/meteor/app/mentions-flextab/client/tabBar.ts b/apps/meteor/app/mentions-flextab/client/tabBar.ts index fa88a0f2d96d..1699005c02f1 100644 --- a/apps/meteor/app/mentions-flextab/client/tabBar.ts +++ b/apps/meteor/app/mentions-flextab/client/tabBar.ts @@ -1,10 +1,20 @@ +import { isRoomFederated } from '@rocket.chat/core-typings'; + import { addAction } from '../../../client/views/room/lib/Toolbox'; -addAction('mentions', { - groups: ['channel', 'group', 'team'], - id: 'mentions', - title: 'Mentions', - icon: 'at', - template: 'mentionsFlexTab', - order: 9, +addAction('mentions', ({ room }) => { + const federated = isRoomFederated(room); + + return { + groups: ['channel', 'group', 'team'], + id: 'mentions', + title: 'Mentions', + icon: 'at', + template: 'mentionsFlexTab', + ...(federated && { + 'disabled': true, + 'data-tooltip': 'Mentions_unavailable_for_federation', + }), + order: 9, + }; }); diff --git a/apps/meteor/app/mentions/client/mentionLink.css b/apps/meteor/app/mentions/client/mentionLink.css index 4915ce792ab9..e338b20ea1f2 100644 --- a/apps/meteor/app/mentions/client/mentionLink.css +++ b/apps/meteor/app/mentions/client/mentionLink.css @@ -16,7 +16,6 @@ &:hover { opacity: 0.6; - color: var(--mention-link-text-color); } &--me { diff --git a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.js b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.js index 9c622e3d6581..8318c8a120aa 100644 --- a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.js +++ b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { Rooms, Users, Messages } from '../../../models'; +import { Rooms, Users, Messages } from '../../../models/server'; import { canAccessRoom } from '../../../authorization/server'; Meteor.methods({ diff --git a/apps/meteor/app/mentions/server/server.js b/apps/meteor/app/mentions/server/server.js index 280211c9c7ba..c82a7c3449e8 100644 --- a/apps/meteor/app/mentions/server/server.js +++ b/apps/meteor/app/mentions/server/server.js @@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import MentionsServer from './Mentions'; -import { settings } from '../../settings'; +import { settings } from '../../settings/server'; import { callbacks } from '../../../lib/callbacks'; -import { Users, Subscriptions, Rooms } from '../../models'; +import { Users, Subscriptions, Rooms } from '../../models/server'; import { api } from '../../../server/sdk/api'; export class MentionQueries { diff --git a/apps/meteor/app/message-pin/client/pinMessage.js b/apps/meteor/app/message-pin/client/pinMessage.js index 9e2ec2da0caa..b45bed7839c3 100644 --- a/apps/meteor/app/message-pin/client/pinMessage.js +++ b/apps/meteor/app/message-pin/client/pinMessage.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { settings } from '../../settings'; -import { ChatMessage, Subscriptions } from '../../models'; +import { ChatMessage, Subscriptions } from '../../models/client'; import { dispatchToastMessage } from '../../../client/lib/toast'; Meteor.methods({ diff --git a/apps/meteor/app/message-pin/client/tabBar.ts b/apps/meteor/app/message-pin/client/tabBar.ts index a5b5242d83b2..6065172f7cb6 100644 --- a/apps/meteor/app/message-pin/client/tabBar.ts +++ b/apps/meteor/app/message-pin/client/tabBar.ts @@ -1,10 +1,12 @@ import { useMemo } from 'react'; import { useSetting } from '@rocket.chat/ui-contexts'; +import { isRoomFederated } from '@rocket.chat/core-typings'; import { addAction } from '../../../client/views/room/lib/Toolbox'; -addAction('pinned-messages', () => { +addAction('pinned-messages', ({ room }) => { const pinningAllowed = useSetting('Message_AllowPinning'); + const federated = isRoomFederated(room); return useMemo( () => pinningAllowed @@ -14,9 +16,13 @@ addAction('pinned-messages', () => { title: 'Pinned_Messages', icon: 'pin', template: 'pinnedMessages', + ...(federated && { + 'data-tooltip': 'Pinned_messages_unavailable_for_federation', + 'disabled': true, + }), order: 11, } : null, - [pinningAllowed], + [pinningAllowed, federated], ); }); diff --git a/apps/meteor/app/message-pin/server/pinMessage.js b/apps/meteor/app/message-pin/server/pinMessage.js index 6f9c11d51589..c176d16acd45 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.js +++ b/apps/meteor/app/message-pin/server/pinMessage.js @@ -6,7 +6,7 @@ import { callbacks } from '../../../lib/callbacks'; import { isTheLastMessage } from '../../lib/server'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; import { canAccessRoom, hasPermission, roomAccessAttributes } from '../../authorization/server'; -import { Subscriptions, Messages, Users, Rooms } from '../../models'; +import { Subscriptions, Messages, Users, Rooms } from '../../models/server'; import { Apps, AppEvents } from '../../apps/server/orchestrator'; const recursiveRemove = (msg, deep = 1) => { diff --git a/apps/meteor/app/message-pin/server/startup/indexes.js b/apps/meteor/app/message-pin/server/startup/indexes.js index c73616e0d1a0..c6556421c38a 100644 --- a/apps/meteor/app/message-pin/server/startup/indexes.js +++ b/apps/meteor/app/message-pin/server/startup/indexes.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/server'; Meteor.startup(function () { return Meteor.defer(function () { diff --git a/apps/meteor/app/message-snippet/client/actionButton.js b/apps/meteor/app/message-snippet/client/actionButton.js deleted file mode 100644 index 342ff2b2854d..000000000000 --- a/apps/meteor/app/message-snippet/client/actionButton.js +++ /dev/null @@ -1,69 +0,0 @@ -// import { Meteor } from 'meteor/meteor'; -// import { MessageAction, modal } from '../../ui-utils'; -// import { messageArgs } from '../../ui-utils/client/lib/messageArgs'; -// import { t, handleError } from '../../utils'; -// import { settings } from '../../settings'; -// import { Subscriptions } from '../../models'; -// import { hasAtLeastOnePermission } from '../../authorization'; -// -// Meteor.startup(function() { -// MessageAction.addButton({ -// id: 'snippeted-message', -// icon: 'code', -// label: 'Snippet', -// context: [ -// 'snippeted', -// 'message', -// 'message-mobile', -// ], -// order: 10, -// group: 'menu', -// action() { -// const { msg: message } = messageArgs(this); -// -// modal.open({ -// title: 'Create a Snippet', -// text: 'The name of your snippet (with file extension):', -// type: 'input', -// showCancelButton: true, -// closeOnConfirm: false, -// inputPlaceholder: 'Snippet name', -// }, function(filename) { -// if (filename === false) { -// return false; -// } -// if (filename === '') { -// modal.showInputError('You need to write something!'); -// return false; -// } -// message.snippeted = true; -// Meteor.call('snippetMessage', message, filename, function(error) { -// if (error) { -// return handleError(error); -// } -// modal.open({ -// title: t('Nice'), -// text: `Snippet '${ filename }' created.`, -// type: 'success', -// timer: 2000, -// }); -// }); -// }); -// -// }, -// condition(message) { -// if (Subscriptions.findOne({ rid: message.rid, 'u._id': Meteor.userId() }) === undefined) { -// return false; -// } -// -// if (message.snippeted || ((settings.get('Message_AllowSnippeting') === undefined) || -// (settings.get('Message_AllowSnippeting') === null) || -// (settings.get('Message_AllowSnippeting')) === false)) { -// return false; -// } -// -// return hasAtLeastOnePermission('snippet-message', message.rid); -// }, -// }); -// -// }); diff --git a/apps/meteor/app/message-snippet/client/index.js b/apps/meteor/app/message-snippet/client/index.js index c3a4c091f880..d5b477773204 100644 --- a/apps/meteor/app/message-snippet/client/index.js +++ b/apps/meteor/app/message-snippet/client/index.js @@ -1,4 +1,3 @@ -import './actionButton'; import './messageType'; import './snippetMessage'; import './page/snippetPage.html'; diff --git a/apps/meteor/app/message-snippet/client/snippetMessage.js b/apps/meteor/app/message-snippet/client/snippetMessage.js index 5edefd30e423..3c8a35d6cfdc 100644 --- a/apps/meteor/app/message-snippet/client/snippetMessage.js +++ b/apps/meteor/app/message-snippet/client/snippetMessage.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { settings } from '../../settings'; -import { ChatMessage, Subscriptions } from '../../models'; +import { ChatMessage, Subscriptions } from '../../models/client'; Meteor.methods({ snippetMessage(message) { diff --git a/apps/meteor/app/message-snippet/server/methods/snippetMessage.js b/apps/meteor/app/message-snippet/server/methods/snippetMessage.js index b332e37b0fd3..987c3102c71d 100644 --- a/apps/meteor/app/message-snippet/server/methods/snippetMessage.js +++ b/apps/meteor/app/message-snippet/server/methods/snippetMessage.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { Subscriptions, Messages, Users, Rooms } from '../../../models'; -import { settings } from '../../../settings'; +import { Subscriptions, Messages, Users, Rooms } from '../../../models/server'; +import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; import { isTheLastMessage } from '../../../lib'; diff --git a/apps/meteor/app/message-snippet/server/requests.js b/apps/meteor/app/message-snippet/server/requests.js index d06fd06b075b..63f74bf59a00 100644 --- a/apps/meteor/app/message-snippet/server/requests.js +++ b/apps/meteor/app/message-snippet/server/requests.js @@ -1,7 +1,7 @@ import { WebApp } from 'meteor/webapp'; import { Cookies } from 'meteor/ostrio:cookies'; -import { Users, Rooms, Messages } from '../../models'; +import { Users, Rooms, Messages } from '../../models/server'; WebApp.connectHandlers.use('/snippet/download', function (req, res) { let rawCookies; diff --git a/apps/meteor/app/message-star/client/actionButton.ts b/apps/meteor/app/message-star/client/actionButton.ts index 51c21c4ee77c..981fce06dc53 100644 --- a/apps/meteor/app/message-star/client/actionButton.ts +++ b/apps/meteor/app/message-star/client/actionButton.ts @@ -16,7 +16,7 @@ Meteor.startup(function () { id: 'star-message', icon: 'star', label: 'Star', - context: ['starred', 'message', 'message-mobile', 'threads'], + context: ['starred', 'message', 'message-mobile', 'threads', 'federated'], action(_, props) { const { message = messageArgs(this).msg } = props; Meteor.call('starMessage', { ...message, starred: true }, function (error: any) { @@ -44,7 +44,7 @@ Meteor.startup(function () { id: 'unstar-message', icon: 'star', label: 'Unstar_Message', - context: ['starred', 'message', 'message-mobile', 'threads'], + context: ['starred', 'message', 'message-mobile', 'threads', 'federated'], action(_, props) { const { message = messageArgs(this).msg } = props; diff --git a/apps/meteor/app/message-star/client/starMessage.js b/apps/meteor/app/message-star/client/starMessage.js index 17178341b894..41e2c7ac7775 100644 --- a/apps/meteor/app/message-star/client/starMessage.js +++ b/apps/meteor/app/message-star/client/starMessage.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { settings } from '../../settings'; -import { ChatMessage, Subscriptions } from '../../models'; +import { ChatMessage, Subscriptions } from '../../models/client'; import { dispatchToastMessage } from '../../../client/lib/toast'; Meteor.methods({ diff --git a/apps/meteor/app/message-star/server/startup/indexes.js b/apps/meteor/app/message-star/server/startup/indexes.js index 12350e64a67a..a39f821a3155 100644 --- a/apps/meteor/app/message-star/server/startup/indexes.js +++ b/apps/meteor/app/message-star/server/startup/indexes.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/server'; Meteor.startup(function () { return Meteor.defer(function () { diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts index 0e9933aa68af..a9c208c9e462 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts @@ -7,10 +7,10 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import fiber from 'fibers'; import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import { IUser, IIncomingMessage } from '@rocket.chat/core-typings'; +import { CredentialTokens } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { Users, Rooms } from '../../../models/server'; -import { CredentialTokens } from '../../../models/server/raw'; import { saveUserIdentity, createRoom, generateUsernameSuggestion, addUserToRoom } from '../../../lib/server/functions'; import { SAMLServiceProvider } from './ServiceProvider'; import { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts index f1cd3d563052..a6adefb97cd0 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts @@ -95,7 +95,7 @@ export class SAMLServiceProvider { return callback(null, target); } catch (error) { - return callback(error); + return callback(error instanceof Error ? error : String(error)); } }); } @@ -154,7 +154,7 @@ export class SAMLServiceProvider { } callback(null, target); } catch (error) { - callback(error); + callback(error instanceof Error ? error : String(error)); } }); } diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts index 22f5d05b6c36..35eb9d9a92b4 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts @@ -1,4 +1,4 @@ -import xmldom from 'xmldom'; +import xmldom from '@xmldom/xmldom'; import { SAMLUtils } from '../Utils'; import { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; @@ -44,7 +44,7 @@ export class LogoutRequestParser { const msg = doc.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'StatusMessage'); SAMLUtils.log(`Unexpected msg from IDP. Does your session still exist at IDP? Idp returned: \n ${msg}`); - return callback(e, null); + return callback(e instanceof Error ? e : String(e), null); } } } diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts index 5922382e61c5..a34c2244876a 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts @@ -1,4 +1,4 @@ -import xmldom from 'xmldom'; +import xmldom from '@xmldom/xmldom'; import { SAMLUtils } from '../Utils'; import { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts index bf92043a5976..c554df00c9f5 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts @@ -1,4 +1,4 @@ -import xmldom from 'xmldom'; +import xmldom from '@xmldom/xmldom'; import xmlenc from 'xml-encryption'; import xmlCrypto from 'xml-crypto'; @@ -62,7 +62,7 @@ export class ResponseParser { this.verifySignatures(response, assertionData, xml); } catch (e) { - return callback(e, null, false); + return callback(e instanceof Error ? e : String(e), null, false); } const profile: Record = {}; @@ -74,7 +74,7 @@ export class ResponseParser { try { issuer = this.getIssuer(assertion); } catch (e) { - return callback(e, null, false); + return callback(e instanceof Error ? e : String(e), null, false); } if (issuer) { @@ -95,14 +95,14 @@ export class ResponseParser { try { this.validateSubjectConditions(subject); } catch (e) { - return callback(e, null, false); + return callback(e instanceof Error ? e : String(e), null, false); } } try { this.validateAssertionConditions(assertion); } catch (e) { - return callback(e, null, false); + return callback(e instanceof Error ? e : String(e), null, false); } const authnStatement = assertion.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'AuthnStatement')[0]; diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 61eacebba057..610954d074ef 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -6,11 +6,11 @@ import _ from 'underscore'; import gcStats from 'prometheus-gc-stats'; import { Meteor } from 'meteor/meteor'; import { Facts } from 'meteor/facts-base'; +import { Statistics } from '@rocket.chat/models'; import { Info, getOplogInfo } from '../../../utils/server'; import { getControl } from '../../../../server/lib/migrations'; import { settings } from '../../../settings/server'; -import { Statistics } from '../../../models/server/raw'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { metrics } from './metrics'; import { getAppsStatistics } from '../../../statistics/server/lib/getAppsStatistics'; @@ -23,9 +23,7 @@ const setPrometheusData = async (): Promise => { metrics.info.set( { version: Info.version, - // eslint-disable-next-line @typescript-eslint/camelcase unique_id: settings.get('uniqueID'), - // eslint-disable-next-line @typescript-eslint/camelcase site_url: settings.get('Site_Url'), }, 1, diff --git a/apps/meteor/app/models/index.js b/apps/meteor/app/models/index.ts similarity index 100% rename from apps/meteor/app/models/index.js rename to apps/meteor/app/models/index.ts diff --git a/apps/meteor/app/models/server/index.js b/apps/meteor/app/models/server/index.js index 24957884d25d..da1ab9c8549c 100644 --- a/apps/meteor/app/models/server/index.js +++ b/apps/meteor/app/models/server/index.js @@ -10,10 +10,9 @@ import LivechatCustomField from './models/LivechatCustomField'; import LivechatDepartment from './models/LivechatDepartment'; import LivechatDepartmentAgents from './models/LivechatDepartmentAgents'; import LivechatRooms from './models/LivechatRooms'; -import LivechatVisitors from './models/LivechatVisitors'; import LivechatInquiry from './models/LivechatInquiry'; -import OmnichannelQueue from './models/OmnichannelQueue'; import ImportData from './models/ImportData'; +import './lib/watchModels'; export { AppsLogsModel } from './models/apps-logs-model'; export { AppsPersistenceModel } from './models/apps-persistence-model'; @@ -22,7 +21,6 @@ export { FederationRoomEvents } from './models/FederationRoomEvents'; export { MatrixBridgedRoom } from './models/MatrixBridgedRoom'; export { MatrixBridgedUser } from './models/MatrixBridgedUser'; - export { Base, BaseDb, @@ -36,8 +34,6 @@ export { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms, - LivechatVisitors, LivechatInquiry, - OmnichannelQueue, ImportData, }; diff --git a/apps/meteor/app/models/server/lib/watchModels.ts b/apps/meteor/app/models/server/lib/watchModels.ts new file mode 100644 index 000000000000..0b9c50753126 --- /dev/null +++ b/apps/meteor/app/models/server/lib/watchModels.ts @@ -0,0 +1,73 @@ +import { + Messages, + Users, + Subscriptions, + Settings, + LivechatInquiry, + LivechatDepartmentAgents, + Rooms, + UsersSessions, + Roles, + LoginServiceConfiguration, + InstanceStatus, + IntegrationHistory, + Integrations, + EmailInbox, + PbxEvents, + Permissions, +} from '@rocket.chat/models'; + +import { api } from '../../../../server/sdk/api'; +import { BaseDbWatch } from '../models/_BaseDb'; +import { initWatchers } from '../../../../server/modules/watchers/watchers.module'; +import { isRunningMs } from '../../../../server/lib/isRunningMs'; +import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; +import MessagesModel from '../models/Messages'; +import UsersModel from '../models/Users'; +import SubscriptionsModel from '../models/Subscriptions'; +import SettingsModel from '../models/Settings'; +import LivechatInquiryModel from '../models/LivechatInquiry'; +import LivechatDepartmentAgentsModel from '../models/LivechatDepartmentAgents'; +import RoomsModel from '../models/Rooms'; + +const map = { + [Messages.col.collectionName]: MessagesModel, + [Users.col.collectionName]: UsersModel, + [Subscriptions.col.collectionName]: SubscriptionsModel, + [Settings.col.collectionName]: SettingsModel, + [LivechatInquiry.col.collectionName]: LivechatInquiryModel, + [LivechatDepartmentAgents.col.collectionName]: LivechatDepartmentAgentsModel, + [Rooms.col.collectionName]: RoomsModel, +}; + +if (!isRunningMs()) { + const models = { + Messages, + Users, + Subscriptions, + Settings, + LivechatInquiry, + LivechatDepartmentAgents, + UsersSessions, + Permissions, + Roles, + Rooms, + LoginServiceConfiguration, + InstanceStatus, + IntegrationHistory, + Integrations, + EmailInbox, + PbxEvents, + }; + + initWatchers(models, api.broadcastLocal.bind(api), (model, fn) => { + const { collectionName } = (model as BaseRaw).col; + + const meteorModel = map[collectionName] || new BaseDbWatch(collectionName); + if (!meteorModel) { + return; + } + + meteorModel.on('change', fn); + }); +} diff --git a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js b/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js index ea26943fe804..d3e9111b33f5 100644 --- a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js +++ b/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js @@ -1,4 +1,3 @@ -import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { Base } from './_Base'; @@ -14,9 +13,6 @@ export class LivechatDepartmentAgents extends Base { this.tryEnsureIndex({ departmentEnabled: 1 }); this.tryEnsureIndex({ agentId: 1 }); this.tryEnsureIndex({ username: 1 }); - - const collectionObj = this.model.rawCollection(); - this.findAndModify = Meteor.wrapAsync(collectionObj.findAndModify, collectionObj); } findByDepartmentId(departmentId) { @@ -96,7 +92,7 @@ export class LivechatDepartmentAgents extends Base { const collectionObj = this.model.rawCollection(); - const agent = Promise.await(collectionObj.findAndModify(query, sort, update)); + const agent = Promise.await(collectionObj.findOneAndUpdate(query, update, { sort, returnNewDocument: 'after' })); if (agent && agent.value) { return { agentId: agent.value.agentId, @@ -159,7 +155,7 @@ export class LivechatDepartmentAgents extends Base { return this.find(query); } - getNextBotForDepartment(departmentId, ignoreAgentId) { + async getNextBotForDepartment(departmentId, ignoreAgentId) { const agents = this.findByDepartmentId(departmentId).fetch(); if (agents.length === 0) { @@ -188,7 +184,7 @@ export class LivechatDepartmentAgents extends Base { }, }; - const bot = this.findAndModify(query, sort, update); + const bot = await this.model.rawCollection().findOneAndUpdate(query, update, { sort, returnNewDocument: 'after' }); if (bot && bot.value) { return { agentId: bot.value.agentId, diff --git a/apps/meteor/app/models/server/models/LivechatInquiry.js b/apps/meteor/app/models/server/models/LivechatInquiry.js deleted file mode 100644 index c85169cc15a7..000000000000 --- a/apps/meteor/app/models/server/models/LivechatInquiry.js +++ /dev/null @@ -1,304 +0,0 @@ -import { Base } from './_Base'; - -export class LivechatInquiry extends Base { - constructor() { - super('livechat_inquiry'); - - this.tryEnsureIndex({ rid: 1 }); // room id corresponding to this inquiry - this.tryEnsureIndex({ name: 1 }); // name of the inquiry (client name for now) - this.tryEnsureIndex({ message: 1 }); // message sent by the client - this.tryEnsureIndex({ ts: 1 }); // timestamp - this.tryEnsureIndex({ department: 1 }); - this.tryEnsureIndex({ status: 1 }); // 'ready', 'queued', 'taken' - this.tryEnsureIndex({ queueOrder: 1, estimatedWaitingTimeQueue: 1, estimatedServiceTimeAt: 1 }); - this.tryEnsureIndex({ 'v.token': 1, 'status': 1 }); // visitor token and status - } - - findOneById(inquiryId) { - return this.findOne({ _id: inquiryId }); - } - - findOneByRoomId(rid, options) { - return this.findOne({ rid }, options); - } - - getNextInquiryQueued(department) { - return this.findOne( - { - status: 'queued', - ...(department && { department }), - }, - { - sort: { - queueOrder: 1, - estimatedWaitingTimeQueue: 1, - estimatedServiceTimeAt: 1, - }, - }, - ); - } - - getQueuedInquiries(options) { - return this.find({ status: 'queued' }, options); - } - - /* - * mark the inquiry as taken - */ - takeInquiry(inquiryId) { - this.update( - { - _id: inquiryId, - }, - { - $set: { status: 'taken', takenAt: new Date() }, - $unset: { defaultAgent: 1, estimatedInactivityCloseTimeAt: 1 }, - }, - ); - } - - /* - * mark inquiry as open - */ - openInquiry(inquiryId) { - return this.update( - { - _id: inquiryId, - }, - { - $set: { status: 'open' }, - }, - ); - } - - /* - * mark inquiry as queued - */ - queueInquiry(inquiryId) { - return this.update( - { - _id: inquiryId, - }, - { - $set: { status: 'queued', queuedAt: new Date() }, - $unset: { takenAt: 1 }, - }, - ); - } - - queueInquiryAndRemoveDefaultAgent(inquiryId) { - return this.update( - { - _id: inquiryId, - }, - { - $set: { status: 'queued', queuedAt: new Date() }, - $unset: { takenAt: 1, defaultAgent: 1 }, - }, - ); - } - - /* - * mark inquiry as ready - */ - readyInquiry(inquiryId) { - return this.update( - { - _id: inquiryId, - }, - { - $set: { - status: 'ready', - }, - }, - ); - } - - changeDepartmentIdByRoomId(rid, department) { - const query = { - rid, - }; - const update = { - $set: { - department, - }, - }; - - this.update(query, update); - } - - /* - * return the status of the inquiry (open or taken) - */ - getStatus(inquiryId) { - return this.findOne({ _id: inquiryId }).status; - } - - updateVisitorStatus(token, status) { - const query = { - 'v.token': token, - 'status': 'queued', - }; - - const update = { - $set: { - 'v.status': status, - }, - }; - - return this.update(query, update); - } - - setDefaultAgentById(inquiryId, defaultAgent) { - return this.update( - { - _id: inquiryId, - }, - { - $set: { - defaultAgent, - }, - }, - ); - } - - setNameByRoomId(rid, name) { - const query = { rid }; - - const update = { - $set: { - name, - }, - }; - return this.update(query, update); - } - - findOneByToken(token) { - const query = { - 'v.token': token, - 'status': 'queued', - }; - return this.findOne(query); - } - - async getCurrentSortedQueueAsync({ _id, department }) { - const collectionObj = this.model.rawCollection(); - const aggregate = [ - { - $match: { - status: 'queued', - ...(department && { department }), - }, - }, - { - $sort: { - ts: 1, - }, - }, - { - $group: { - _id: 1, - inquiry: { - $push: { - _id: '$_id', - rid: '$rid', - name: '$name', - ts: '$ts', - status: '$status', - department: '$department', - }, - }, - }, - }, - { - $unwind: { - path: '$inquiry', - includeArrayIndex: 'position', - }, - }, - { - $project: { - _id: '$inquiry._id', - rid: '$inquiry.rid', - name: '$inquiry.name', - ts: '$inquiry.ts', - status: '$inquiry.status', - department: '$inquiry.department', - position: 1, - }, - }, - ]; - - // To get the current room position in the queue, we need to apply the next $match after the $project - if (_id) { - aggregate.push({ $match: { _id } }); - } - - return collectionObj.aggregate(aggregate).toArray(); - } - - removeDefaultAgentById(inquiryId) { - return this.update( - { - _id: inquiryId, - }, - { - $unset: { defaultAgent: 1 }, - }, - ); - } - - /* - * remove the inquiry by roomId - */ - removeByRoomId(rid) { - return this.remove({ rid }); - } - - removeByVisitorToken(token) { - const query = { - 'v.token': token, - }; - - this.remove(query); - } - - getUnnatendedQueueItems(date) { - const query = { - status: 'queued', - estimatedInactivityCloseTimeAt: { $lte: new Date(date) }, - }; - return this.find(query); - } - - setEstimatedInactivityCloseTime(_id, date) { - return this.update( - { _id }, - { - $set: { - estimatedInactivityCloseTimeAt: new Date(date), - }, - }, - ); - } - - // This is a better solution, but update pipelines are not supported until version 4.2 of mongo - // leaving this here for when the time comes - /* updateEstimatedInactivityCloseTime(milisecondsToAdd) { - return this.model.rawCollection().updateMany( - { status: 'queued' }, - [{ - // in case this field doesn't exists, set at the last time the item was modified (updatedAt) - $set: { estimatedInactivityCloseTimeAt: '$_updatedAt' }, - }, { - $set: { - estimatedInactivityCloseTimeAt: { - $add: ['$estimatedInactivityCloseTimeAt', milisecondsToAdd], - }, - }, - }], - ); - } */ -} - -export default new LivechatInquiry(); diff --git a/apps/meteor/app/models/server/models/LivechatInquiry.ts b/apps/meteor/app/models/server/models/LivechatInquiry.ts new file mode 100644 index 000000000000..49126d4fce00 --- /dev/null +++ b/apps/meteor/app/models/server/models/LivechatInquiry.ts @@ -0,0 +1,277 @@ +import { ILivechatInquiryRecord } from '@rocket.chat/core-typings'; +import { FindOptions, FindCursor, UpdateResult, DeleteResult } from 'mongodb'; + +import { Base } from './_Base'; + +export class LivechatInquiry extends Base { + constructor() { + super('livechat_inquiry'); + + this.tryEnsureIndex({ rid: 1 }); // room id corresponding to this inquiry + this.tryEnsureIndex({ name: 1 }); // name of the inquiry (client name for now) + this.tryEnsureIndex({ message: 1 }); // message sent by the client + this.tryEnsureIndex({ ts: 1 }); // timestamp + this.tryEnsureIndex({ department: 1 }); + this.tryEnsureIndex({ status: 1 }); // 'ready', 'queued', 'taken' + this.tryEnsureIndex({ queueOrder: 1, estimatedWaitingTimeQueue: 1, estimatedServiceTimeAt: 1 }); + this.tryEnsureIndex({ 'v.token': 1, 'status': 1 }); // visitor token and status + this.tryEnsureIndex({ locked: 1, lockedAt: 1 }, { sparse: true }); // locked and lockedAt + } + + findOneById(inquiryId: string): ILivechatInquiryRecord { + return this.findOne({ _id: inquiryId }); + } + + findOneByRoomId(rid: string, options?: FindOptions): ILivechatInquiryRecord { + return this.findOne({ rid }, options); + } + + getNextInquiryQueued(department?: string): ILivechatInquiryRecord { + return this.findOne( + { + status: 'queued', + ...(department && { department }), + }, + { + sort: { + queueOrder: 1, + estimatedWaitingTimeQueue: 1, + estimatedServiceTimeAt: 1, + }, + }, + ); + } + + getQueuedInquiries(options?: FindOptions): FindCursor { + return this.find({ status: 'queued' }, options); + } + + /* + * mark the inquiry as taken + */ + takeInquiry(inquiryId: string): void { + this.update( + { + _id: inquiryId, + }, + { + $set: { status: 'taken', takenAt: new Date() }, + $unset: { defaultAgent: 1, estimatedInactivityCloseTimeAt: 1 }, + }, + ); + } + + /* + * mark inquiry as open + */ + openInquiry(inquiryId: string): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $set: { status: 'open' }, + }, + ); + } + + /* + * mark inquiry as queued + */ + queueInquiry(inquiryId: string): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $set: { status: 'queued', queuedAt: new Date() }, + $unset: { takenAt: 1 }, + }, + ); + } + + queueInquiryAndRemoveDefaultAgent(inquiryId: string): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $set: { status: 'queued', queuedAt: new Date() }, + $unset: { takenAt: 1, defaultAgent: 1 }, + }, + ); + } + + /* + * mark inquiry as ready + */ + readyInquiry(inquiryId: string): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $set: { + status: 'ready', + }, + }, + ); + } + + changeDepartmentIdByRoomId(rid: string, department: string): void { + const query = { + rid, + }; + const updateObj = { + $set: { + department, + }, + }; + + this.update(query, updateObj); + } + + /* + * return the status of the inquiry (open or taken) + */ + getStatus(inquiryId: string): ILivechatInquiryRecord['status'] { + return this.findOne({ _id: inquiryId }).status; + } + + updateVisitorStatus(token: string, status: string): UpdateResult { + const query = { + 'v.token': token, + 'status': 'queued', + }; + + const update = { + $set: { + 'v.status': status, + }, + }; + + return this.update(query, update); + } + + setDefaultAgentById(inquiryId: string, defaultAgent: ILivechatInquiryRecord['defaultAgent']): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $set: { + defaultAgent, + }, + }, + ); + } + + setNameByRoomId(rid: string, name: string): UpdateResult { + const query = { rid }; + + const update = { + $set: { + name, + }, + }; + return this.update(query, update); + } + + findOneByToken(token: string): ILivechatInquiryRecord { + const query = { + 'v.token': token, + 'status': 'queued', + }; + return this.findOne(query); + } + + async getCurrentSortedQueueAsync({ + _id, + department, + }: { + _id: string; + department: string; + }): Promise & { position: number }> { + const collectionObj = this.model.rawCollection(); + const aggregate = [ + { + $match: { + status: 'queued', + ...(department && { department }), + }, + }, + { + $sort: { + ts: 1, + }, + }, + { + $group: { + _id: 1, + inquiry: { + $push: { + _id: '$_id', + rid: '$rid', + name: '$name', + ts: '$ts', + status: '$status', + department: '$department', + }, + }, + }, + }, + { + $unwind: { + path: '$inquiry', + includeArrayIndex: 'position', + }, + }, + { + $project: { + _id: '$inquiry._id', + rid: '$inquiry.rid', + name: '$inquiry.name', + ts: '$inquiry.ts', + status: '$inquiry.status', + department: '$inquiry.department', + position: 1, + }, + }, + ] as any[]; + + // To get the current room position in the queue, we need to apply the next $match after the $project + if (_id) { + aggregate.push({ $match: { _id } }); + } + + return collectionObj.aggregate(aggregate).toArray(); + } + + removeDefaultAgentById(inquiryId: string): UpdateResult { + return this.update( + { + _id: inquiryId, + }, + { + $unset: { defaultAgent: 1 }, + }, + ); + } + + /* + * remove the inquiry by roomId + */ + removeByRoomId(rid: string): DeleteResult { + return this.remove({ rid }); + } + + removeByVisitorToken(token: string): void { + const query = { + 'v.token': token, + }; + + this.remove(query); + } +} + +export default new LivechatInquiry(); diff --git a/apps/meteor/app/models/server/models/LivechatRooms.js b/apps/meteor/app/models/server/models/LivechatRooms.js index 4ce7aeb74ec2..25239366f589 100644 --- a/apps/meteor/app/models/server/models/LivechatRooms.js +++ b/apps/meteor/app/models/server/models/LivechatRooms.js @@ -1,9 +1,9 @@ import s from 'underscore.string'; import _ from 'underscore'; +import { Settings } from '@rocket.chat/models'; import { Base } from './_Base'; import Rooms from './Rooms'; -import Settings from './Settings'; export class LivechatRooms extends Base { constructor(...args) { @@ -23,11 +23,16 @@ export class LivechatRooms extends Base { this.tryEnsureIndex({ t: 1, departmentId: 1, closedAt: 1 }, { partialFilterExpression: { closedAt: { $exists: true } } }); this.tryEnsureIndex({ source: 1 }, { sparse: true }); this.tryEnsureIndex({ departmentAncestors: 1 }, { sparse: true }); - } - - findLivechat(filter = {}, offset = 0, limit = 20) { - const query = Object.assign(filter, { t: 'l' }); - return this.find(query, { sort: { ts: -1 }, offset, limit }); + this.tryEnsureIndex( + { 't': 1, 'open': 1, 'source.type': 1, 'v.status': 1 }, + { + partialFilterExpression: { + 't': { $eq: 'l' }, + 'open': { $eq: true }, + 'source.type': { $eq: 'widget' }, + }, + }, + ); } findOneByIdOrName(_idOrName, options) { @@ -248,7 +253,7 @@ export class LivechatRooms extends Base { return this.findOne(query, options); } - updateRoomCount = function () { + updateRoomCount = async function () { const query = { _id: 'Livechat_Room_Count', }; @@ -259,9 +264,8 @@ export class LivechatRooms extends Base { }, }; - const livechatCount = Settings.findAndModify(query, null, update); - - return livechatCount.value.value; + const livechatCount = await Settings.findOneAndUpdate(query, update, { returnDocument: 'after' }); + return livechatCount.value; }; findOpenByVisitorToken(visitorToken, options) { @@ -325,15 +329,6 @@ export class LivechatRooms extends Base { return this.find(query, options); } - findByVisitorId(visitorId) { - const query = { - 't': 'l', - 'v._id': visitorId, - }; - - return this.find(query); - } - findOneOpenByRoomIdAndVisitorToken(roomId, visitorToken, options) { const query = { 't': 'l', diff --git a/apps/meteor/app/models/server/models/LivechatVisitors.js b/apps/meteor/app/models/server/models/LivechatVisitors.js deleted file mode 100644 index ffffe1095fcd..000000000000 --- a/apps/meteor/app/models/server/models/LivechatVisitors.js +++ /dev/null @@ -1,253 +0,0 @@ -import _ from 'underscore'; -import s from 'underscore.string'; - -import { Base } from './_Base'; -import Settings from './Settings'; - -export class LivechatVisitors extends Base { - constructor() { - super('livechat_visitor'); - - this.tryEnsureIndex({ token: 1 }); - this.tryEnsureIndex({ 'phone.phoneNumber': 1 }, { sparse: true }); - this.tryEnsureIndex({ 'visitorEmails.address': 1 }, { sparse: true }); - this.tryEnsureIndex({ name: 1 }, { sparse: true }); - this.tryEnsureIndex({ username: 1 }); - this.tryEnsureIndex({ 'contactManager.username': 1 }, { sparse: true }); - } - - /** - * Gets visitor by token - * @param {string} token - Visitor token - */ - getVisitorByToken(token, options) { - const query = { - token, - }; - - return this.findOne(query, options); - } - - /** - * Find visitors by _id - * @param {string} token - Visitor token - */ - findById(_id, options) { - const query = { - _id, - }; - - return this.find(query, options); - } - - /** - * Find One visitor by _id - */ - findOneById(_id, options) { - const query = { - _id, - }; - - return this.findOne(query, options); - } - - /** - * Gets visitor by token - * @param {string} token - Visitor token - */ - findVisitorByToken(token) { - const query = { - token, - }; - - return this.find(query); - } - - updateLivechatDataByToken(token, key, value, overwrite = true) { - const query = { - token, - }; - - if (!overwrite) { - const user = this.findOne(query, { fields: { livechatData: 1 } }); - if (user.livechatData && typeof user.livechatData[key] !== 'undefined') { - return true; - } - } - - const update = { - $set: { - [`livechatData.${key}`]: value, - }, - }; - - return this.update(query, update); - } - - updateLastAgentByToken(token, lastAgent) { - const query = { - token, - }; - - const update = { - $set: { - lastAgent, - }, - }; - - return this.update(query, update); - } - - /** - * Find a visitor by their phone number - * @return {object} User from db - */ - findOneVisitorByPhone(phone) { - const query = { - 'phone.phoneNumber': phone, - }; - - return this.findOne(query); - } - - getVisitorsBetweenDate(date) { - const query = { - _updatedAt: { - $gte: date.gte, // ISO Date, ts >= date.gte - $lt: date.lt, // ISODate, ts < date.lt - }, - }; - - return this.find(query, { fields: { _id: 1 } }); - } - - /** - * Get the next visitor name - * @return {string} The next visitor name - */ - getNextVisitorUsername() { - const query = { - _id: 'Livechat_guest_count', - }; - - const update = { - $inc: { - value: 1, - }, - }; - - const livechatCount = Settings.findAndModify(query, null, update); - - return `guest-${livechatCount.value.value + 1}`; - } - - updateById(_id, update) { - return this.update({ _id }, update); - } - - saveGuestById(_id, data) { - const setData = {}; - const unsetData = {}; - - if (data.name) { - if (!_.isEmpty(s.trim(data.name))) { - setData.name = s.trim(data.name); - } else { - unsetData.name = 1; - } - } - - if (data.email) { - if (!_.isEmpty(s.trim(data.email))) { - setData.visitorEmails = [{ address: s.trim(data.email) }]; - } else { - unsetData.visitorEmails = 1; - } - } - - if (data.phone) { - if (!_.isEmpty(s.trim(data.phone))) { - setData.phone = [{ phoneNumber: s.trim(data.phone) }]; - } else { - unsetData.phone = 1; - } - } - - if (data.livechatData) { - Object.keys(data.livechatData).forEach((key) => { - const value = s.trim(data.livechatData[key]); - if (value) { - setData[`livechatData.${key}`] = value; - } else { - unsetData[`livechatData.${key}`] = 1; - } - }); - } - - const update = {}; - - if (!_.isEmpty(setData)) { - update.$set = setData; - } - - if (!_.isEmpty(unsetData)) { - update.$unset = unsetData; - } - - if (_.isEmpty(update)) { - return true; - } - - return this.update({ _id }, update); - } - - findOneGuestByEmailAddress(emailAddress) { - const query = { - 'visitorEmails.address': String(emailAddress).toLowerCase(), - }; - - return this.findOne(query); - } - - saveGuestEmailPhoneById(_id, emails, phones) { - const update = { - $addToSet: {}, - }; - - const saveEmail = [] - .concat(emails) - .filter((email) => email && email.trim()) - .map((email) => ({ address: email })); - - if (saveEmail.length > 0) { - update.$addToSet.visitorEmails = { $each: saveEmail }; - } - - const savePhone = [] - .concat(phones) - .filter((phone) => phone && phone.trim().replace(/[^\d]/g, '')) - .map((phone) => ({ phoneNumber: phone })); - - if (savePhone.length > 0) { - update.$addToSet.phone = { $each: savePhone }; - } - - if (!update.$addToSet.visitorEmails && !update.$addToSet.phone) { - return; - } - - return this.update({ _id }, update); - } - - // REMOVE - removeDepartmentById(_id) { - return this.update({ _id }, { $unset: { department: 1 } }); - } - - removeById(_id) { - const query = { _id }; - return this.remove(query); - } -} - -export default new LivechatVisitors(); diff --git a/apps/meteor/app/models/server/models/Messages.js b/apps/meteor/app/models/server/models/Messages.js index ebb91426337e..813568a6234f 100644 --- a/apps/meteor/app/models/server/models/Messages.js +++ b/apps/meteor/app/models/server/models/Messages.js @@ -173,7 +173,7 @@ export class Messages extends Base { ); } - countVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) { + countVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp) { const query = { _hidden: { $ne: true, @@ -185,7 +185,7 @@ export class Messages extends Base { }, }; - return this.find(query, options).count(); + return this.find(query).count(); } // FIND @@ -471,27 +471,6 @@ export class Messages extends Base { return this.find(query, options); } - findPinnedByRoom(roomId, options) { - const query = { - t: { $ne: 'rm' }, - _hidden: { $ne: true }, - pinned: true, - rid: roomId, - }; - - return this.find(query, options); - } - - findSnippetedByRoom(roomId, options) { - const query = { - _hidden: { $ne: true }, - snippeted: true, - rid: roomId, - }; - - return this.find(query, options); - } - getLastTimestamp(options = { fields: { _id: 0, ts: 1 } }) { options.sort = { ts: -1 }; options.limit = 1; @@ -534,19 +513,6 @@ export class Messages extends Base { return this.findOne(query, options); } - findByRoomIdAndType(roomId, type, options) { - const query = { - rid: roomId, - t: type, - }; - - if (options == null) { - options = {}; - } - - return this.find(query, options); - } - findByRoomId(roomId, options) { const query = { rid: roomId, diff --git a/apps/meteor/app/models/server/models/OmnichannelQueue.js b/apps/meteor/app/models/server/models/OmnichannelQueue.js deleted file mode 100644 index fb37a83af6d7..000000000000 --- a/apps/meteor/app/models/server/models/OmnichannelQueue.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Base } from './_Base'; - -export class OmnichannelQueue extends Base { - constructor() { - super('omnichannel_queue'); - } -} - -export default new OmnichannelQueue(); diff --git a/apps/meteor/app/models/server/models/Rooms.js b/apps/meteor/app/models/server/models/Rooms.js index e86f9fa054c0..d86fb37323c2 100644 --- a/apps/meteor/app/models/server/models/Rooms.js +++ b/apps/meteor/app/models/server/models/Rooms.js @@ -24,6 +24,8 @@ export class Rooms extends Base { this.tryEnsureIndex({ uids: 1 }, { sparse: true }); this.tryEnsureIndex({ createdOTR: 1 }, { sparse: true }); this.tryEnsureIndex({ encrypted: 1 }, { sparse: true }); // used on statistics + this.tryEnsureIndex({ broadcast: 1 }, { sparse: true }); // used on statistics + this.tryEnsureIndex({ 'streamingOptions.type': 1 }, { sparse: true }); // used on statistics this.tryEnsureIndex( { @@ -49,20 +51,6 @@ export class Rooms extends Base { return this.findOne(query, options); } - setJitsiTimeout(_id, time) { - const query = { - _id, - }; - - const update = { - $set: { - jitsiTimeout: time, - }, - }; - - return this.update(query, update); - } - setCallStatus(_id, status) { const query = { _id, @@ -329,7 +317,7 @@ export class Rooms extends Base { let channelName = s.trim(name); try { // TODO evaluate if this function call should be here - const { getValidRoomName } = Promise.await(import('../../../utils/lib/getValidRoomName')); + const { getValidRoomName } = Promise.await(import('../../../utils/server/lib/getValidRoomName')); channelName = getValidRoomName(channelName, null, { allowDuplicates: true }); } catch (e) { console.error(e); @@ -398,16 +386,6 @@ export class Rooms extends Base { return this.find(query, options); } - findByTypes(types, discussion = false, options = {}) { - const query = { - t: { - $in: types, - }, - prid: { $exists: discussion }, - }; - return this.find(query, options); - } - findByUserId(userId, options) { const query = { 'u._id': userId }; @@ -443,23 +421,6 @@ export class Rooms extends Base { return this.find(query, options); } - findBySubscriptionTypeAndUserId(type, userId, options) { - const data = Subscriptions.findByUserIdAndType(userId, type, { - fields: { rid: 1 }, - }) - .fetch() - .map((item) => item.rid); - - const query = { - t: type, - _id: { - $in: data, - }, - }; - - return this.find(query, options); - } - findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) { const ids = Subscriptions.findByUserId(userId, { fields: { rid: 1 } }) .fetch() @@ -492,41 +453,6 @@ export class Rooms extends Base { return this.find(query, options); } - findByNameContaining(name, discussion = false, options = {}) { - const nameRegex = new RegExp(s.trim(escapeRegExp(name)), 'i'); - - const query = { - prid: { $exists: discussion }, - $or: [ - { name: nameRegex }, - { - t: 'd', - usernames: nameRegex, - }, - ], - }; - return this.find(query, options); - } - - findByNameContainingAndTypes(name, types, discussion = false, options = {}) { - const nameRegex = new RegExp(s.trim(escapeRegExp(name)), 'i'); - - const query = { - t: { - $in: types, - }, - prid: { $exists: discussion }, - $or: [ - { name: nameRegex }, - { - t: 'd', - usernames: nameRegex, - }, - ], - }; - return this.find(query, options); - } - findByNameAndType(name, type, options) { const query = { t: type, @@ -557,92 +483,6 @@ export class Rooms extends Base { return this._db.find(query, options); } - findByNameOrFNameAndRoomIdsIncludingTeamRooms(text, teamIds, roomIds, options) { - const searchTerm = text && new RegExp(text, 'i'); - - const query = { - $and: [ - { teamMain: { $exists: false } }, - { prid: { $exists: false } }, - { - $or: [ - { - t: 'c', - teamId: { $exists: false }, - }, - { - t: 'c', - teamId: { $in: teamIds }, - }, - ...(roomIds?.length > 0 - ? [ - { - _id: { - $in: roomIds, - }, - }, - ] - : []), - ], - }, - ...(searchTerm - ? [ - { - $or: [ - { - name: searchTerm, - }, - { - fname: searchTerm, - }, - ], - }, - ] - : []), - ], - }; - - return this._db.find(query, options); - } - - findContainingNameOrFNameInIdsAsTeamMain(text, rids, options) { - const query = { - teamMain: true, - $and: [ - { - $or: [ - { - t: 'p', - _id: { - $in: rids, - }, - }, - { - t: 'c', - }, - ], - }, - ], - }; - - if (text) { - const regex = new RegExp(text, 'i'); - - query.$and.push({ - $or: [ - { - name: regex, - }, - { - fname: regex, - }, - ], - }); - } - - return this._db.find(query, options); - } - findByNameAndTypeNotDefault(name, type, options) { const query = { t: type, @@ -1002,6 +842,11 @@ export class Rooms extends Base { return this.update(query, update); } + /** + * @param {string} _id + * @param {string?} messageId + * @returns {Promise} + */ resetLastMessageById(_id, messageId = undefined) { const query = { _id }; const lastMessage = Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id, messageId); diff --git a/apps/meteor/app/models/server/models/Settings.js b/apps/meteor/app/models/server/models/Settings.js index c2ef98d35476..490b4b2822eb 100644 --- a/apps/meteor/app/models/server/models/Settings.js +++ b/apps/meteor/app/models/server/models/Settings.js @@ -1,5 +1,3 @@ -import { Meteor } from 'meteor/meteor'; - import { Base } from './_Base'; export class Settings extends Base { @@ -8,9 +6,6 @@ export class Settings extends Base { this.tryEnsureIndex({ blocked: 1 }, { sparse: 1 }); this.tryEnsureIndex({ hidden: 1 }, { sparse: 1 }); - - const collectionObj = this.model.rawCollection(); - this.findAndModify = Meteor.wrapAsync(collectionObj.findAndModify, collectionObj); } // FIND diff --git a/apps/meteor/app/models/server/models/Users.js b/apps/meteor/app/models/server/models/Users.js index dd42ac05b055..725f90a0be2f 100644 --- a/apps/meteor/app/models/server/models/Users.js +++ b/apps/meteor/app/models/server/models/Users.js @@ -59,12 +59,8 @@ export class Users extends Base { this.tryEnsureIndex({ statusLivechat: 1 }, { sparse: true }); this.tryEnsureIndex({ extension: 1 }, { sparse: true, unique: true }); this.tryEnsureIndex({ language: 1 }, { sparse: true }); - this.tryEnsureIndex({ 'active': 1, 'services.email2fa.enabled': 1 }, { sparse: true }); // used by statistics this.tryEnsureIndex({ 'active': 1, 'services.totp.enabled': 1 }, { sparse: true }); // used by statistics - - const collectionObj = this.model.rawCollection(); - this.findAndModify = Meteor.wrapAsync(collectionObj.findAndModify, collectionObj); } getLoginTokensByUserId(userId) { @@ -206,10 +202,10 @@ export class Users extends Base { return this.find(query); } - getNextAgent(ignoreAgentId, extraQuery) { + async getNextAgent(ignoreAgentId, extraQuery) { // TODO: Create class Agent // fetch all unavailable agents, and exclude them from the selection - const unavailableAgents = Promise.await(this.getUnavailableAgents(null, extraQuery)).map((u) => u.username); + const unavailableAgents = (await this.getUnavailableAgents(null, extraQuery)).map((u) => u.username); const extraFilters = { ...(ignoreAgentId && { _id: { $ne: ignoreAgentId } }), // limit query to remove booked agents @@ -229,7 +225,7 @@ export class Users extends Base { }, }; - const user = this.findAndModify(query, sort, update); + const user = await this.model.rawCollection().findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); if (user && user.value) { return { agentId: user.value._id, @@ -243,7 +239,7 @@ export class Users extends Base { return []; } - getNextBotAgent(ignoreAgentId) { + async getNextBotAgent(ignoreAgentId) { // TODO: Create class Agent const query = { roles: { @@ -263,7 +259,7 @@ export class Users extends Base { }, }; - const user = this.findAndModify(query, sort, update); + const user = await this.model.rawCollection().findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); if (user && user.value) { return { agentId: user.value._id, @@ -646,7 +642,7 @@ export class Users extends Base { return this.find(query, options); } - findOneByAppId(appId, options) { + findOneByAppId(appId, options = {}) { const query = { appId }; return this.findOne(query, options); @@ -876,81 +872,6 @@ export class Users extends Base { return this.find(query, options); } - findByActiveUsersExcept( - searchTerm, - exceptions, - options, - forcedSearchFields, - extraQuery = [], - { startsWith = false, endsWith = false } = {}, - ) { - if (exceptions == null) { - exceptions = []; - } - if (options == null) { - options = {}; - } - if (!_.isArray(exceptions)) { - exceptions = [exceptions]; - } - - // if the search term is empty, don't need to have the $or statement (because it would be an empty regex) - if (!searchTerm) { - const query = { - $and: [ - { - active: true, - username: { $exists: true, $nin: exceptions }, - }, - ...extraQuery, - ], - }; - - return this._db.find(query, options); - } - - const termRegex = new RegExp((startsWith ? '^' : '') + escapeRegExp(searchTerm) + (endsWith ? '$' : ''), 'i'); - - const searchFields = forcedSearchFields || settings.get('Accounts_SearchFields').trim().split(','); - - const orStmt = _.reduce( - searchFields, - function (acc, el) { - acc.push({ [el.trim()]: termRegex }); - return acc; - }, - [], - ); - - const query = { - $and: [ - { - active: true, - username: { $exists: true, $nin: exceptions }, - $or: orStmt, - }, - ...extraQuery, - ], - }; - - // do not use cache - return this._db.find(query, options); - } - - findByActiveLocalUsersExcept(searchTerm, exceptions, options, forcedSearchFields, localDomain) { - const extraQuery = [ - { - $or: [{ federation: { $exists: false } }, { 'federation.origin': localDomain }], - }, - ]; - return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); - } - - findByActiveExternalUsersExcept(searchTerm, exceptions, options, forcedSearchFields, localDomain) { - const extraQuery = [{ federation: { $exists: true } }, { 'federation.origin': { $ne: localDomain } }]; - return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); - } - findUsersByNameOrUsername(nameOrUsername, options) { const query = { username: { @@ -1044,7 +965,7 @@ export class Users extends Base { } /** - * @param {import('mongodb').FilterQuery} fields + * @param {import('mongodb').Filter} fields */ getOldest(fields = { _id: 1 }) { const query = { diff --git a/apps/meteor/app/models/server/models/_BaseDb.js b/apps/meteor/app/models/server/models/_BaseDb.js index 64bc40d081f8..b984ad02f2f9 100644 --- a/apps/meteor/app/models/server/models/_BaseDb.js +++ b/apps/meteor/app/models/server/models/_BaseDb.js @@ -7,21 +7,11 @@ import _ from 'underscore'; import { setUpdatedAt } from '../lib/setUpdatedAt'; import { metrics } from '../../../metrics/server/lib/metrics'; import { getOplogHandle } from './_oplogHandle'; -import { SystemLogger } from '../../../../server/lib/logger/system'; import { isRunningMs } from '../../../../server/lib/isRunningMs'; +import { trash } from '../../../../server/database/trash'; const baseName = 'rocketchat_'; -export const trash = new Mongo.Collection(`${baseName}_trash`); -try { - trash._ensureIndex({ __collection__: 1 }); - trash._ensureIndex({ _deletedAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 30 }); - - trash._ensureIndex({ rid: 1, __collection__: 1, _deletedAt: 1 }); -} catch (e) { - SystemLogger.error(e); -} - const actions = { i: 'insert', u: 'update', diff --git a/apps/meteor/app/models/server/models/_oplogHandle.ts b/apps/meteor/app/models/server/models/_oplogHandle.ts index 98f0e5608329..a3c83187d3c2 100644 --- a/apps/meteor/app/models/server/models/_oplogHandle.ts +++ b/apps/meteor/app/models/server/models/_oplogHandle.ts @@ -1,25 +1,29 @@ +import { Readable } from 'stream'; + import { Meteor } from 'meteor/meteor'; import { MongoInternals, OplogHandle } from 'meteor/mongo'; import semver from 'semver'; -import { MongoClient, Cursor, Timestamp, Db } from 'mongodb'; +import { MongoClient } from 'mongodb'; +import type { Timestamp, Db } from 'mongodb'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { urlParser } from './_oplogUrlParser'; import { isRunningMs } from '../../../../server/lib/isRunningMs'; +const ignoreChangeStream = ['yes', 'true'].includes(String(process.env.IGNORE_CHANGE_STREAM).toLowerCase()); + class CustomOplogHandle { dbName: string; client: MongoClient; - stream: Cursor; + stream: Readable; db: Db; usingChangeStream: boolean; async isChangeStreamAvailable(): Promise { - if (process.env.IGNORE_CHANGE_STREAM) { + if (ignoreChangeStream) { return false; } @@ -34,7 +38,7 @@ class CustomOplogHandle { await mongo.db.admin().command({ replSetGetStatus: 1 }); } catch (e) { - if (e.message.startsWith('not authorized')) { + if (e instanceof Error && e.message.startsWith('not authorized')) { console.info( 'Change Stream is available for your installation, give admin permissions to your database user to use this improved version.', ); @@ -47,37 +51,26 @@ class CustomOplogHandle { async start(): Promise { this.usingChangeStream = await this.isChangeStreamAvailable(); - const oplogUrl = this.usingChangeStream ? process.env.MONGO_URL : process.env.MONGO_OPLOG_URL; - let urlParsed; - try { - urlParsed = await urlParser(oplogUrl); - } catch (e) { - throw Error(`Error parsing database URL (${oplogUrl})`); - } - - if (!this.usingChangeStream && (!oplogUrl || urlParsed.defaultDatabase !== 'local')) { - throw Error("$MONGO_OPLOG_URL must be set to the 'local' database of a Mongo replica set"); - } - - if (!oplogUrl) { + const oplogUrl = this.usingChangeStream ? process.env.MONGO_URL : process.env.MONGO_OPLOG_URL; + if (!oplogUrl || !process.env.MONGO_URL) { throw Error('$MONGO_URL must be set'); } - if (process.env.MONGO_OPLOG_URL) { - const urlParsed = await urlParser(process.env.MONGO_URL); - this.dbName = urlParsed.defaultDatabase; - } - const mongoOptions = process.env.MONGO_OPTIONS ? JSON.parse(process.env.MONGO_OPTIONS) : null; this.client = new MongoClient(oplogUrl, { ...mongoOptions, - useUnifiedTopology: true, - useNewUrlParser: true, - poolSize: this.usingChangeStream ? 15 : 1, + minPoolSize: this.usingChangeStream ? 15 : 1, }); + if (!this.usingChangeStream && this.client.options.dbName !== 'local') { + throw Error("$MONGO_OPLOG_URL must be set to the 'local' database of a Mongo replica set"); + } + + const mongoClient = new MongoClient(process.env.MONGO_URL); + this.dbName = mongoClient.options.dbName; + await this.client.connect(); this.db = this.client.db(); @@ -129,7 +122,7 @@ class CustomOplogHandle { } _onOplogEntryOplog(query: { collection: string }, callback: Function): void { - this.stream.on( + this.stream?.on( 'data', Meteor.bindEnvironment((buffer) => { const doc = buffer as any; @@ -170,7 +163,7 @@ class CustomOplogHandle { // o: event.fullDocument, o: { $set: event.updateDescription.updatedFields, - $unset: event.updateDescription.removedFields.reduce((obj, field) => { + $unset: event.updateDescription.removedFields?.reduce((obj, field) => { obj[field as string] = true; return obj; }, {} as Record), @@ -214,7 +207,7 @@ if (!isRunningMs()) { try { oplogHandle = Promise.await(new CustomOplogHandle().start()); } catch (e) { - console.error(e.message); + console.error(e instanceof Error ? e.message : e); } } } diff --git a/apps/meteor/app/models/server/models/_oplogUrlParser.js b/apps/meteor/app/models/server/models/_oplogUrlParser.js deleted file mode 100644 index 986874cefccd..000000000000 --- a/apps/meteor/app/models/server/models/_oplogUrlParser.js +++ /dev/null @@ -1,5 +0,0 @@ -import { promisify } from 'util'; - -import { parseConnectionString } from 'mongodb/lib/core'; - -export const urlParser = promisify(parseConnectionString); diff --git a/apps/meteor/app/models/server/raw/Analytics.ts b/apps/meteor/app/models/server/raw/Analytics.ts deleted file mode 100644 index bde3461b020f..000000000000 --- a/apps/meteor/app/models/server/raw/Analytics.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { Random } from 'meteor/random'; -import { AggregationCursor, Cursor, SortOptionObject, UpdateWriteOpResult } from 'mongodb'; -import type { IAnalytic, IRoom } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = IAnalytic; - -export class AnalyticsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { date: 1 } }, { key: { 'room._id': 1, 'date': 1 }, unique: true }]; - } - - saveMessageSent({ room, date }: { room: IRoom; date: IAnalytic['date'] }): Promise { - return this.updateMany( - { date, 'room._id': room._id, 'type': 'messages' }, - { - $set: { - room: { - _id: room._id, - name: room.fname || room.name, - t: room.t, - usernames: room.usernames || [], - }, - }, - $setOnInsert: { - _id: Random.id(), - date, - type: 'messages', - }, - $inc: { messages: 1 }, - }, - { upsert: true }, - ); - } - - saveUserData({ date }: { date: IAnalytic['date'] }): Promise { - return this.updateMany( - { date, type: 'users' }, - { - $setOnInsert: { - _id: Random.id(), - date, - type: 'users', - }, - $inc: { users: 1 }, - }, - { upsert: true }, - ); - } - - saveMessageDeleted({ room, date }: { room: { _id: string }; date: IAnalytic['date'] }): Promise { - return this.updateMany( - { date, 'room._id': room._id }, - { - $inc: { messages: -1 }, - }, - ); - } - - getMessagesSentTotalByDate({ - start, - end, - options = {}, - }: { - start: IAnalytic['date']; - end: IAnalytic['date']; - options?: { sort?: SortOptionObject; count?: number }; - }): AggregationCursor<{ - _id: IAnalytic['date']; - messages: number; - }> { - return this.col.aggregate<{ - _id: IAnalytic['date']; - messages: number; - }>([ - { - $match: { - type: 'messages', - date: { $gte: start, $lte: end }, - }, - }, - { - $group: { - _id: '$date', - messages: { $sum: '$messages' }, - }, - }, - ...(options.sort ? [{ $sort: options.sort }] : []), - ...(options.count ? [{ $limit: options.count }] : []), - ]); - } - - getMessagesOrigin({ start, end }: { start: IAnalytic['date']; end: IAnalytic['date'] }): AggregationCursor<{ - t: IRoom['t']; - messages: number; - }> { - const params = [ - { - $match: { - type: 'messages', - date: { $gte: start, $lte: end }, - }, - }, - { - $group: { - _id: { t: '$room.t' }, - messages: { $sum: '$messages' }, - }, - }, - { - $project: { - _id: 0, - t: '$_id.t', - messages: 1, - }, - }, - ]; - return this.col.aggregate(params); - } - - getMostPopularChannelsByMessagesSentQuantity({ - start, - end, - options = {}, - }: { - start: IAnalytic['date']; - end: IAnalytic['date']; - options?: { sort?: SortOptionObject; count?: number }; - }): AggregationCursor<{ - t: IRoom['t']; - name: string; - messages: number; - usernames: string[]; - }> { - return this.col.aggregate([ - { - $match: { - type: 'messages', - date: { $gte: start, $lte: end }, - }, - }, - { - $group: { - _id: { t: '$room.t', name: '$room.name', usernames: '$room.usernames' }, - messages: { $sum: '$messages' }, - }, - }, - { - $project: { - _id: 0, - t: '$_id.t', - name: '$_id.name', - usernames: '$_id.usernames', - messages: 1, - }, - }, - ...(options.sort ? [{ $sort: options.sort }] : []), - ...(options.count ? [{ $limit: options.count }] : []), - ]); - } - - getTotalOfRegisteredUsersByDate({ - start, - end, - options = {}, - }: { - start: IAnalytic['date']; - end: IAnalytic['date']; - options?: { sort?: SortOptionObject; count?: number }; - }): AggregationCursor<{ - _id: IAnalytic['date']; - users: number; - }> { - return this.col.aggregate<{ - _id: IAnalytic['date']; - users: number; - }>([ - { - $match: { - type: 'users', - date: { $gte: start, $lte: end }, - }, - }, - { - $group: { - _id: '$date', - users: { $sum: '$users' }, - }, - }, - ...(options.sort ? [{ $sort: options.sort }] : []), - ...(options.count ? [{ $limit: options.count }] : []), - ]); - } - - findByTypeBeforeDate({ type, date }: { type: T['type']; date: T['date'] }): Cursor { - return this.find({ type, date: { $lte: date } }); - } -} diff --git a/apps/meteor/app/models/server/raw/Avatars.ts b/apps/meteor/app/models/server/raw/Avatars.ts deleted file mode 100644 index ba1af66f7ef7..000000000000 --- a/apps/meteor/app/models/server/raw/Avatars.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { DeleteWriteOpResultObject, UpdateWriteOpResult } from 'mongodb'; -import { IAvatar as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class AvatarsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [ - { key: { name: 1 }, sparse: true }, - { key: { rid: 1 }, sparse: true }, - ]; - } - - insertAvatarFileInit(name: string, userId: string, store: string, file: { name: string }, extra: object): Promise { - const fileData = { - name, - userId, - store, - complete: false, - uploading: true, - progress: 0, - extension: file.name.split('.').pop(), - uploadedAt: new Date(), - }; - - Object.assign(fileData, file, extra); - - return this.updateOne({ _id: name }, fileData, { upsert: true }); - } - - updateFileComplete(fileId: string, userId: string, file: object): Promise | undefined { - if (!fileId) { - return; - } - - const filter = { - _id: fileId, - userId, - }; - - const update = { - $set: { - complete: true, - uploading: false, - progress: 1, - }, - }; - - update.$set = Object.assign(file, update.$set); - - return this.updateOne(filter, update); - } - - async findOneByName(name: string): Promise { - return this.findOne({ name }); - } - - async findOneByRoomId(rid: string): Promise { - return this.findOne({ rid }); - } - - async updateFileNameById(fileId: string, name: string): Promise { - const filter = { _id: fileId }; - const update = { - $set: { - name, - }, - }; - return this.updateOne(filter, update); - } - - async deleteFile(fileId: string): Promise { - return this.deleteOne({ _id: fileId }); - } -} diff --git a/apps/meteor/app/models/server/raw/Banners.ts b/apps/meteor/app/models/server/raw/Banners.ts deleted file mode 100644 index 04a90fb8e2e6..000000000000 --- a/apps/meteor/app/models/server/raw/Banners.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Cursor, FindOneOptions, UpdateWriteOpResult, WithoutProjection, InsertOneWriteOpResult } from 'mongodb'; -import { BannerPlatform, IBanner } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = IBanner; -export class BannersRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { platform: 1, startAt: 1, expireAt: 1 } }, { key: { platform: 1, startAt: 1, expireAt: 1, active: 1 } }]; - } - - create(doc: IBanner): Promise> { - const invalidPlatform = doc.platform?.some((platform) => !Object.values(BannerPlatform).includes(platform)); - if (invalidPlatform) { - throw new Error('Invalid platform'); - } - - if (doc.startAt > doc.expireAt) { - throw new Error('Start date cannot be later than expire date'); - } - - if (doc.expireAt < new Date()) { - throw new Error('Cannot create banner already expired'); - } - - return this.insertOne({ - active: true, - ...doc, - }); - } - - findActiveByRoleOrId( - roles: string[], - platform: BannerPlatform, - bannerId?: string, - options?: WithoutProjection>, - ): Cursor { - const today = new Date(); - - const query = { - ...(bannerId && { _id: bannerId }), - platform, - startAt: { $lte: today }, - expireAt: { $gte: today }, - active: { $ne: false }, - $or: [{ roles: { $in: roles } }, { roles: { $exists: false } }], - }; - - return this.col.find(query, options); - } - - disable(bannerId: string): Promise { - return this.col.updateOne({ _id: bannerId, active: { $ne: false } }, { $set: { active: false, inactivedAt: new Date() } }); - } -} diff --git a/apps/meteor/app/models/server/raw/BannersDismiss.ts b/apps/meteor/app/models/server/raw/BannersDismiss.ts deleted file mode 100644 index 98ac83a76f3b..000000000000 --- a/apps/meteor/app/models/server/raw/BannersDismiss.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Cursor, FindOneOptions, WithoutProjection } from 'mongodb'; -import type { IBannerDismiss } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class BannersDismissRaw extends BaseRaw { - modelIndexes(): IndexSpecification[] { - return [{ key: { userId: 1, bannerId: 1 } }]; - } - - findByUserIdAndBannerId(userId: string, bannerIds: string[]): Cursor; - - findByUserIdAndBannerId( - userId: string, - bannerIds: string[], - options: WithoutProjection>, - ): Cursor; - - findByUserIdAndBannerId

( - userId: string, - bannerIds: string[], - options: FindOneOptions

, - ): Cursor

; - - findByUserIdAndBannerId

( - userId: string, - bannerIds: string[], - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - const query = { - userId, - bannerId: { $in: bannerIds }, - }; - - return options ? this.col.find(query, options) : this.col.find(query); - } -} diff --git a/apps/meteor/app/models/server/raw/BaseRaw.ts b/apps/meteor/app/models/server/raw/BaseRaw.ts deleted file mode 100644 index a1121f34cdb9..000000000000 --- a/apps/meteor/app/models/server/raw/BaseRaw.ts +++ /dev/null @@ -1,405 +0,0 @@ -import { - Collection, - CollectionInsertOneOptions, - CommonOptions, - Cursor, - DeleteWriteOpResultObject, - FilterQuery, - FindAndModifyWriteOpResultObject, - FindOneAndUpdateOption, - FindOneOptions, - IndexSpecification, - InsertOneWriteOpResult, - InsertWriteOpResult, - ObjectID, - ObjectId, - OptionalId, - UpdateManyOptions, - UpdateOneOptions, - UpdateQuery, - UpdateWriteOpResult, - WithId, - WithoutProjection, - WriteOpResult, -} from 'mongodb'; -import { IRocketChatRecord, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; - -import { setUpdatedAt } from '../lib/setUpdatedAt'; - -export { IndexSpecification } from 'mongodb'; - -// [extracted from @types/mongo] TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions -type EnhancedOmit = string | number extends keyof T - ? T // T has indexed type e.g. { _id: string; [k: string]: any; } or it is "any" - : T extends any - ? Pick> // discriminated unions - : never; - -// [extracted from @types/mongo] -type ExtractIdType = TSchema extends { _id: infer U } // user has defined a type for _id - ? {} extends U - ? Exclude - : unknown extends U - ? ObjectId - : U - : ObjectId; - -export type ModelOptionalId = EnhancedOmit & { _id?: ExtractIdType }; -// InsertionModel forces both _id and _updatedAt to be optional, regardless of how they are declared in T -export type InsertionModel = EnhancedOmit, '_updatedAt'> & { - _updatedAt?: Date; -}; - -export interface IBaseRaw { - col: Collection; -} - -const baseName = 'rocketchat_'; - -type DefaultFields = Record | Record | void; -type ResultFields = Defaults extends void - ? Base - : Defaults[keyof Defaults] extends 1 - ? Pick - : Omit; - -const warnFields = - process.env.NODE_ENV !== 'production' - ? (...rest: any): void => { - console.warn(...rest, new Error().stack); - } - : new Function(); - -export class BaseRaw = undefined> implements IBaseRaw { - public readonly defaultFields: C; - - protected name: string; - - private preventSetUpdatedAt: boolean; - - public readonly trash?: Collection>; - - constructor(public readonly col: Collection, trash?: Collection, options?: { preventSetUpdatedAt?: boolean }) { - this.name = this.col.collectionName.replace(baseName, ''); - this.trash = trash as unknown as Collection>; - - const indexes = this.modelIndexes(); - if (indexes?.length) { - this.col.createIndexes(indexes).catch((e) => { - console.warn(`Error creating indexes for ${this.name}`, e); - }); - } - - this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false; - } - - protected modelIndexes(): IndexSpecification[] | void { - // noop - } - - private doNotMixInclusionAndExclusionFields(options: FindOneOptions = {}): FindOneOptions { - const optionsDef = this.ensureDefaultFields(options); - if (optionsDef?.projection === undefined) { - return optionsDef; - } - - const projection: Record = optionsDef?.projection; - const keys = Object.keys(projection); - const removeKeys = keys.filter((key) => projection[key] === 0); - if (keys.length > removeKeys.length) { - removeKeys.forEach((key) => delete projection[key]); - } - - return { - ...optionsDef, - projection, - }; - } - - private ensureDefaultFields(options?: undefined): C extends void ? undefined : WithoutProjection>; - - private ensureDefaultFields(options: WithoutProjection>): WithoutProjection>; - - private ensureDefaultFields

(options: FindOneOptions

): FindOneOptions

; - - private ensureDefaultFields

(options?: any): FindOneOptions

| undefined | WithoutProjection> { - if (this.defaultFields === undefined) { - return options; - } - - const { fields: deprecatedFields, projection, ...rest } = options || {}; - - if (deprecatedFields) { - warnFields("Using 'fields' in models is deprecated.", options); - } - - const fields = { ...deprecatedFields, ...projection }; - - return { - projection: this.defaultFields, - ...(fields && Object.values(fields).length && { projection: fields }), - ...rest, - }; - } - - public findOneAndUpdate( - query: FilterQuery, - update: UpdateQuery | T, - options?: FindOneAndUpdateOption, - ): Promise> { - return this.col.findOneAndUpdate(query, update, options); - } - - async findOneById(_id: string, options?: WithoutProjection> | undefined): Promise; - - async findOneById

(_id: string, options: FindOneOptions

): Promise

; - - async findOneById

(_id: string, options?: any): Promise { - const query = { _id } as FilterQuery; - const optionsDef = this.doNotMixInclusionAndExclusionFields(options); - return this.col.findOne(query, optionsDef); - } - - async findOne(query?: FilterQuery | string, options?: undefined): Promise; - - async findOne(query: FilterQuery | string, options: WithoutProjection>): Promise; - - async findOne

(query: FilterQuery | string, options: FindOneOptions

): Promise

; - - async findOne

(query: FilterQuery | string = {}, options?: any): Promise { - const q = typeof query === 'string' ? ({ _id: query } as FilterQuery) : query; - - const optionsDef = this.doNotMixInclusionAndExclusionFields(options); - return this.col.findOne(q, optionsDef); - } - - // findUsersInRoles(): void { - // throw new Error('[overwrite-function] You must overwrite this function in the extended classes'); - // } - - find(query?: FilterQuery): Cursor>; - - find(query: FilterQuery, options: WithoutProjection>): Cursor>; - - find

(query: FilterQuery, options: FindOneOptions

): Cursor

; - - find

(query: FilterQuery | undefined = {}, options?: any): Cursor

| Cursor { - const optionsDef = this.doNotMixInclusionAndExclusionFields(options); - return this.col.find(query, optionsDef); - } - - update( - filter: FilterQuery, - update: UpdateQuery | Partial, - options?: UpdateOneOptions & { multi?: boolean }, - ): Promise { - this.setUpdatedAt(update); - return this.col.update(filter, update, options); - } - - updateOne( - filter: FilterQuery, - update: UpdateQuery | Partial, - options?: UpdateOneOptions & { multi?: boolean }, - ): Promise { - this.setUpdatedAt(update); - return this.col.updateOne(filter, update, options); - } - - updateMany(filter: FilterQuery, update: UpdateQuery | Partial, options?: UpdateManyOptions): Promise { - this.setUpdatedAt(update); - return this.col.updateMany(filter, update, options); - } - - insertMany(docs: Array>, options?: CollectionInsertOneOptions): Promise>> { - docs = docs.map((doc) => { - if (!doc._id || typeof doc._id !== 'string') { - const oid = new ObjectID(); - return { _id: oid.toHexString(), ...doc }; - } - this.setUpdatedAt(doc); - return doc; - }); - - // TODO reavaluate following type casting - return this.col.insertMany(docs as unknown as Array>, options); - } - - insertOne(doc: InsertionModel, options?: CollectionInsertOneOptions): Promise>> { - if (!doc._id || typeof doc._id !== 'string') { - const oid = new ObjectID(); - doc = { _id: oid.toHexString(), ...doc }; - } - - this.setUpdatedAt(doc); - - // TODO reavaluate following type casting - return this.col.insertOne(doc as unknown as OptionalId, options); - } - - removeById(_id: string): Promise { - return this.deleteOne({ _id } as FilterQuery); - } - - async deleteOne( - filter: FilterQuery, - options?: CommonOptions & { bypassDocumentValidation?: boolean }, - ): Promise { - if (!this.trash) { - return this.col.deleteOne(filter, options); - } - - const doc = (await this.findOne(filter)) as unknown as (IRocketChatRecord & T) | undefined; - - if (doc) { - const { _id, ...record } = doc; - - const trash = { - ...record, - - _deletedAt: new Date(), - __collection__: this.name, - } as RocketChatRecordDeleted; - - // since the operation is not atomic, we need to make sure that the record is not already deleted/inserted - await this.trash?.updateOne( - { _id } as FilterQuery>, - { $set: trash }, - { - upsert: true, - }, - ); - } - - return this.col.deleteOne(filter, options); - } - - async deleteMany(filter: FilterQuery, options?: CommonOptions): Promise { - if (!this.trash) { - return this.col.deleteMany(filter, options); - } - - const cursor = this.find(filter); - - const ids: string[] = []; - for await (const doc of cursor) { - const { _id, ...record } = doc as unknown as IRocketChatRecord & T; - - const trash = { - ...record, - - _deletedAt: new Date(), - __collection__: this.name, - } as RocketChatRecordDeleted; - - ids.push(_id); - - // since the operation is not atomic, we need to make sure that the record is not already deleted/inserted - await this.trash?.updateOne( - { _id } as FilterQuery>, - { $set: trash }, - { - upsert: true, - }, - ); - } - - return this.col.deleteMany({ _id: { $in: ids } } as unknown as FilterQuery, options); - } - - // Trash - trashFind

>( - query: FilterQuery>, - options: FindOneOptions

? RocketChatRecordDeleted : P>, - ): Cursor> | undefined { - if (!this.trash) { - return undefined; - } - const { trash } = this; - - return trash.find( - { - __collection__: this.name, - ...query, - }, - options, - ); - } - - trashFindOneById(_id: string): Promise | null>; - - trashFindOneById( - _id: string, - options: WithoutProjection>, - ): Promise> | null>; - - trashFindOneById

( - _id: string, - options: FindOneOptions

? RocketChatRecordDeleted : P>, - ): Promise

; - - async trashFindOneById

>( - _id: string, - options?: - | undefined - | WithoutProjection> - | FindOneOptions

? RocketChatRecordDeleted : P>, - ): Promise | null> { - const query = { - _id, - __collection__: this.name, - } as FilterQuery>; - - if (!this.trash) { - return null; - } - const { trash } = this; - - return trash.findOne(query, options); - } - - private setUpdatedAt(record: UpdateQuery | InsertionModel): void { - if (this.preventSetUpdatedAt) { - return; - } - setUpdatedAt(record); - } - - trashFindDeletedAfter(deletedAt: Date): Cursor>; - - trashFindDeletedAfter( - deletedAt: Date, - query: FilterQuery>, - options: WithoutProjection>, - ): Cursor>; - - trashFindDeletedAfter

>( - deletedAt: Date, - query: FilterQuery

, - options: FindOneOptions

? RocketChatRecordDeleted : P>, - ): Cursor>; - - trashFindDeletedAfter

>( - deletedAt: Date, - query?: FilterQuery>, - options?: - | WithoutProjection> - | FindOneOptions

? RocketChatRecordDeleted : P>, - ): Cursor> { - const q = { - __collection__: this.name, - _deletedAt: { - $gt: deletedAt, - }, - ...query, - } as FilterQuery>; - - const { trash } = this; - - if (!trash) { - throw new Error('Trash is not enabled for this collection'); - } - - return trash.find(q, options as any); - } -} diff --git a/apps/meteor/app/models/server/raw/CredentialTokens.ts b/apps/meteor/app/models/server/raw/CredentialTokens.ts deleted file mode 100644 index aad8ddd08d17..000000000000 --- a/apps/meteor/app/models/server/raw/CredentialTokens.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ICredentialToken as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class CredentialTokensRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { expireAt: 1 }, sparse: true, expireAfterSeconds: 0 }]; - } - - async create(_id: string, userInfo: T['userInfo']): Promise { - const validForMilliseconds = 60000; // Valid for 60 seconds - const token = { - _id, - userInfo, - expireAt: new Date(Date.now() + validForMilliseconds), - }; - - await this.insertOne(token); - return token; - } - - findOneNotExpiredById(_id: string): Promise { - const query = { - _id, - expireAt: { $gt: new Date() }, - }; - - return this.findOne(query); - } -} diff --git a/apps/meteor/app/models/server/raw/CustomSounds.ts b/apps/meteor/app/models/server/raw/CustomSounds.ts deleted file mode 100644 index f83948d6cfa9..000000000000 --- a/apps/meteor/app/models/server/raw/CustomSounds.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Cursor, FindOneOptions, InsertOneWriteOpResult, UpdateWriteOpResult, WithId, WithoutProjection } from 'mongodb'; -import { ICustomSound as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class CustomSoundsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { name: 1 } }]; - } - - // find - findByName(name: string, options: WithoutProjection>): Cursor { - const query = { - name, - }; - - return this.find(query, options); - } - - findByNameExceptId(name: string, except: string, options: WithoutProjection>): Cursor { - const query = { - _id: { $nin: [except] }, - name, - }; - - return this.find(query, options); - } - - // update - setName(_id: string, name: string): Promise { - const update = { - $set: { - name, - }, - }; - - return this.updateOne({ _id }, update); - } - - // INSERT - create(data: T): Promise>> { - return this.insertOne(data); - } -} diff --git a/apps/meteor/app/models/server/raw/CustomUserStatus.ts b/apps/meteor/app/models/server/raw/CustomUserStatus.ts deleted file mode 100644 index d23dc8fd5a13..000000000000 --- a/apps/meteor/app/models/server/raw/CustomUserStatus.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Cursor, FindOneOptions, InsertOneWriteOpResult, UpdateWriteOpResult, WithId, WithoutProjection } from 'mongodb'; -import { ICustomUserStatus as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class CustomUserStatusRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { name: 1 } }]; - } - - // find one by name - - async findOneByName(name: string, options?: undefined): Promise; - - async findOneByName(name: string, options?: WithoutProjection>): Promise { - return options ? this.findOne({ name }, options) : this.findOne({ name }); - } - - // find - findByName(name: string, options: WithoutProjection>): Cursor { - const query = { - name, - }; - - return this.find(query, options); - } - - findByNameExceptId(name: string, except: string, options: WithoutProjection>): Cursor { - const query = { - _id: { $nin: [except] }, - name, - }; - - return this.find(query, options); - } - - // update - setName(_id: string, name: string): Promise { - const update = { - $set: { - name, - }, - }; - - return this.updateOne({ _id }, update); - } - - setStatusType(_id: string, statusType: string): Promise { - const update = { - $set: { - statusType, - }, - }; - - return this.updateOne({ _id }, update); - } - - // INSERT - create(data: T): Promise>> { - return this.insertOne(data); - } -} diff --git a/apps/meteor/app/models/server/raw/EmailInbox.ts b/apps/meteor/app/models/server/raw/EmailInbox.ts deleted file mode 100644 index 73bd24cadfd2..000000000000 --- a/apps/meteor/app/models/server/raw/EmailInbox.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { IEmailInbox } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class EmailInboxRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { email: 1 }, unique: true }]; - } -} diff --git a/apps/meteor/app/models/server/raw/EmailMessageHistory.ts b/apps/meteor/app/models/server/raw/EmailMessageHistory.ts deleted file mode 100644 index f2eefedffe94..000000000000 --- a/apps/meteor/app/models/server/raw/EmailMessageHistory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { InsertOneWriteOpResult, WithId } from 'mongodb'; -import { IEmailMessageHistory as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class EmailMessageHistoryRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { createdAt: 1 }, expireAfterSeconds: 60 * 60 * 24 }]; - } - - async create({ _id, email }: T): Promise>> { - return this.insertOne({ - _id, - email, - createdAt: new Date(), - }); - } -} diff --git a/apps/meteor/app/models/server/raw/EmojiCustom.ts b/apps/meteor/app/models/server/raw/EmojiCustom.ts deleted file mode 100644 index 0a8005cb379f..000000000000 --- a/apps/meteor/app/models/server/raw/EmojiCustom.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Cursor, FindOneOptions, InsertOneWriteOpResult, UpdateWriteOpResult, WithId, WithoutProjection } from 'mongodb'; -import { IEmojiCustom as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class EmojiCustomRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { name: 1 } }, { key: { aliases: 1 } }, { key: { extension: 1 } }]; - } - - // find - findByNameOrAlias(emojiName: string, options: WithoutProjection>): Cursor { - let name = emojiName; - - if (typeof emojiName === 'string') { - name = emojiName.replace(/:/g, ''); - } - - const query = { - $or: [{ name }, { aliases: name }], - }; - - return this.find(query, options); - } - - findByNameOrAliasExceptID(name: string, except: string, options: WithoutProjection>): Cursor { - const query = { - _id: { $nin: [except] }, - $or: [{ name }, { aliases: name }], - }; - - return this.find(query, options); - } - - // update - setName(_id: string, name: string): Promise { - const update = { - $set: { - name, - }, - }; - - return this.updateOne({ _id }, update); - } - - setAliases(_id: string, aliases: string[]): Promise { - const update = { - $set: { - aliases, - }, - }; - - return this.updateOne({ _id }, update); - } - - setExtension(_id: string, extension: string): Promise { - const update = { - $set: { - extension, - }, - }; - - return this.updateOne({ _id }, update); - } - - // INSERT - create(data: T): Promise>> { - return this.insertOne(data); - } -} diff --git a/apps/meteor/app/models/server/raw/ExportOperations.ts b/apps/meteor/app/models/server/raw/ExportOperations.ts deleted file mode 100644 index 9ce76509fff3..000000000000 --- a/apps/meteor/app/models/server/raw/ExportOperations.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Cursor, UpdateWriteOpResult } from 'mongodb'; -import type { IExportOperation } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = IExportOperation; - -export class ExportOperationsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { userId: 1 } }, { key: { status: 1 } }]; - } - - findOnePending(): Promise { - const query = { - status: { $nin: ['completed', 'skipped'] }, - }; - - return this.findOne(query); - } - - async create(data: T): Promise { - const result = await this.insertOne({ - ...data, - createdAt: new Date(), - }); - - return result.insertedId; - } - - findLastOperationByUser(userId: string, fullExport = false): Promise { - const query = { - userId, - fullExport, - }; - - return this.findOne(query, { sort: { createdAt: -1 } }); - } - - findAllPendingBeforeMyRequest(requestDay: Date): Cursor { - const query = { - status: { $nin: ['completed', 'skipped'] }, - createdAt: { $lt: requestDay }, - }; - - return this.find(query); - } - - updateOperation(data: T): Promise { - const update = { - $set: { - roomList: data.roomList, - status: data.status, - fileList: data.fileList, - generatedFile: data.generatedFile, - fileId: data.fileId, - userNameTable: data.userNameTable, - userData: data.userData, - generatedUserFile: data.generatedUserFile, - generatedAvatar: data.generatedAvatar, - exportPath: data.exportPath, - assetsPath: data.assetsPath, - }, - }; - - return this.updateOne({ _id: data._id }, update); - } -} diff --git a/apps/meteor/app/models/server/raw/FederationKeys.ts b/apps/meteor/app/models/server/raw/FederationKeys.ts deleted file mode 100644 index 3b2c505b09aa..000000000000 --- a/apps/meteor/app/models/server/raw/FederationKeys.ts +++ /dev/null @@ -1,70 +0,0 @@ -import NodeRSA from 'node-rsa'; - -import { BaseRaw } from './BaseRaw'; - -type T = { - type: 'private' | 'public'; - key: string; -}; - -export class FederationKeysRaw extends BaseRaw { - async getKey(type: T['type']): Promise { - const keyResource = await this.findOne({ type }); - - if (!keyResource) { - return null; - } - - return keyResource.key; - } - - loadKey(keyData: NodeRSA.Key, type: T['type']): NodeRSA { - return new NodeRSA(keyData, `pkcs8-${type}-pem`); - } - - async generateKeys(): Promise<{ - privateKey: '' | NodeRSA | null; - publicKey: '' | NodeRSA | null; - }> { - const key = new NodeRSA({ b: 512 }); - - key.generateKeyPair(); - - await this.deleteMany({}); - - await this.insertOne({ - type: 'private', - key: key.exportKey('pkcs8-private-pem').replace(/\n|\r/g, ''), - }); - - await this.insertOne({ - type: 'public', - key: key.exportKey('pkcs8-public-pem').replace(/\n|\r/g, ''), - }); - - return { - privateKey: await this.getPrivateKey(), - publicKey: await this.getPublicKey(), - }; - } - - async getPrivateKey(): Promise<'' | NodeRSA | null> { - const keyData = await this.getKey('private'); - - return keyData && this.loadKey(keyData, 'private'); - } - - getPrivateKeyString(): Promise { - return this.getKey('private'); - } - - async getPublicKey(): Promise<'' | NodeRSA | null> { - const keyData = await this.getKey('public'); - - return keyData && this.loadKey(keyData, 'public'); - } - - getPublicKeyString(): Promise { - return this.getKey('public'); - } -} diff --git a/apps/meteor/app/models/server/raw/FederationServers.ts b/apps/meteor/app/models/server/raw/FederationServers.ts deleted file mode 100644 index 06af3db6e886..000000000000 --- a/apps/meteor/app/models/server/raw/FederationServers.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { UpdateWriteOpResult } from 'mongodb'; -import type { IFederationServer } from '@rocket.chat/core-typings'; - -import { Users } from './index'; -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class FederationServersRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { domain: 1 } }]; - } - - saveDomain(domain: string): Promise { - return this.updateOne( - { domain }, - { - $setOnInsert: { - domain, - }, - }, - { upsert: true }, - ); - } - - async refreshServers(): Promise { - const domains = await Users.getDistinctFederationDomains(); - - for await (const domain of domains) { - await this.saveDomain(domain); - } - - await this.deleteMany({ domain: { $nin: domains } }); - } -} diff --git a/apps/meteor/app/models/server/raw/ImportData.ts b/apps/meteor/app/models/server/raw/ImportData.ts deleted file mode 100644 index 0873627a6309..000000000000 --- a/apps/meteor/app/models/server/raw/ImportData.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Cursor } from 'mongodb'; -import type { IImportRecord, IImportUserRecord, IImportMessageRecord, IImportChannelRecord } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class ImportDataRaw extends BaseRaw { - getAllUsers(): Cursor { - return this.find({ dataType: 'user' }) as Cursor; - } - - getAllMessages(): Cursor { - return this.find({ dataType: 'message' }) as Cursor; - } - - getAllChannels(): Cursor { - return this.find({ dataType: 'channel' }) as Cursor; - } -} diff --git a/apps/meteor/app/models/server/raw/InstanceStatus.ts b/apps/meteor/app/models/server/raw/InstanceStatus.ts deleted file mode 100644 index 80974cf4c6d4..000000000000 --- a/apps/meteor/app/models/server/raw/InstanceStatus.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { IInstanceStatus } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class InstanceStatusRaw extends BaseRaw {} diff --git a/apps/meteor/app/models/server/raw/IntegrationHistory.ts b/apps/meteor/app/models/server/raw/IntegrationHistory.ts deleted file mode 100644 index 1fcf4230586a..000000000000 --- a/apps/meteor/app/models/server/raw/IntegrationHistory.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IIntegrationHistory } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class IntegrationHistoryRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [ - { key: { 'integration._id': 1, 'integration._createdBy._id': 1 } }, - { key: { _updatedAt: 1 }, expireAfterSeconds: 30 * 24 * 60 * 60 }, - ]; - } - - removeByIntegrationId(integrationId: string): ReturnType['deleteMany']> { - return this.deleteMany({ 'integration._id': integrationId }); - } - - findOneByIntegrationIdAndHistoryId(integrationId: string, historyId: string): Promise { - return this.findOne({ 'integration._id': integrationId, '_id': historyId }); - } -} diff --git a/apps/meteor/app/models/server/raw/Integrations.ts b/apps/meteor/app/models/server/raw/Integrations.ts deleted file mode 100644 index 844d046ed28f..000000000000 --- a/apps/meteor/app/models/server/raw/Integrations.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { IIntegration, IUser } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class IntegrationsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { type: 1 } }]; - } - - findOneByUrl(url: string): Promise { - return this.findOne({ url }); - } - - updateRoomName(oldRoomName: string, newRoomName: string): ReturnType['updateMany']> { - const hashedOldRoomName = `#${oldRoomName}`; - const hashedNewRoomName = `#${newRoomName}`; - - return this.updateMany( - { - channel: hashedOldRoomName, - }, - { - $set: { - 'channel.$': hashedNewRoomName, - }, - }, - ); - } - - findOneByIdAndCreatedByIfExists({ - _id, - createdBy, - }: { - _id: IIntegration['_id']; - createdBy?: IUser['_id']; - }): Promise { - return this.findOne({ - _id, - ...(createdBy && { '_createdBy._id': createdBy }), - }); - } - - disableByUserId(userId: string): ReturnType['updateMany']> { - return this.updateMany({ userId }, { $set: { enabled: false } }); - } -} diff --git a/apps/meteor/app/models/server/raw/Invites.ts b/apps/meteor/app/models/server/raw/Invites.ts deleted file mode 100644 index ef55e009ca8e..000000000000 --- a/apps/meteor/app/models/server/raw/Invites.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { UpdateWriteOpResult } from 'mongodb'; -import type { IInvite } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -type T = IInvite; - -export class InvitesRaw extends BaseRaw { - findOneByUserRoomMaxUsesAndExpiration(userId: string, rid: string, maxUses: number, daysToExpire: number): Promise { - return this.findOne({ - rid, - userId, - days: daysToExpire, - maxUses, - ...(daysToExpire > 0 ? { expires: { $gt: new Date() } } : {}), - ...(maxUses > 0 ? { uses: { $lt: maxUses } } : {}), - }); - } - - increaseUsageById(_id: string, uses = 1): Promise { - return this.updateOne( - { _id }, - { - $inc: { - uses, - }, - }, - ); - } - - async countUses(): Promise { - const [result] = await this.col - .aggregate<{ totalUses: number } | undefined>([{ $group: { _id: null, totalUses: { $sum: '$uses' } } }]) - .toArray(); - - return result?.totalUses || 0; - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatAgentActivity.ts b/apps/meteor/app/models/server/raw/LivechatAgentActivity.ts deleted file mode 100644 index 4e8aa305c281..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatAgentActivity.ts +++ /dev/null @@ -1,205 +0,0 @@ -import moment from 'moment'; -import type { ILivechatAgentActivity, IServiceHistory } from '@rocket.chat/core-typings'; -import type { AggregationCursor, Cursor, FindAndModifyWriteOpResultObject, IndexSpecification, UpdateWriteOpResult } from 'mongodb'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatAgentActivityRaw extends BaseRaw { - modelIndexes(): IndexSpecification[] { - return [{ key: { date: 1 } }, { key: { agentId: 1, date: 1 }, unique: true }]; - } - - findOneByAgendIdAndDate(agentId: string, date: ILivechatAgentActivity['date']): Promise { - return this.findOne({ agentId, date }); - } - - async createOrUpdate( - data: Partial> = {}, - ): Promise | undefined> { - const { date, agentId, lastStartedAt } = data; - - if (!date || !agentId) { - return; - } - - return this.findOneAndUpdate( - { agentId, date }, - { - $unset: { - lastStoppedAt: 1, - }, - $set: { - lastStartedAt: lastStartedAt || new Date(), - }, - $setOnInsert: { - date, - agentId, - }, - }, - ); - } - - updateLastStoppedAt({ - agentId, - date, - lastStoppedAt, - availableTime, - }: Pick): Promise { - const query = { - agentId, - date, - }; - const update = { - $inc: { availableTime }, - $set: { - lastStoppedAt, - }, - }; - return this.updateMany(query, update); - } - - updateServiceHistory({ - agentId, - date, - serviceHistory, - }: Pick & { serviceHistory: IServiceHistory }): Promise { - const query = { - agentId, - date, - }; - const update = { - $addToSet: { - serviceHistory, - }, - }; - return this.updateMany(query, update); - } - - findOpenSessions(): Cursor { - const query = { - lastStoppedAt: { $exists: false }, - }; - - return this.find(query); - } - - findAllAverageAvailableServiceTime({ date, departmentId }: { date: Date; departmentId: string }): Promise { - const match = { $match: { date } }; - const lookup = { - $lookup: { - from: 'rocketchat_livechat_department_agents', - localField: 'agentId', - foreignField: 'agentId', - as: 'departments', - }, - }; - const unwind = { - $unwind: { - path: '$departments', - preserveNullAndEmptyArrays: true, - }, - }; - const departmentsMatch = { - $match: { - 'departments.departmentId': departmentId, - }, - }; - const sumAvailableTimeWithCurrentTime = { - $sum: [{ $divide: [{ $subtract: [new Date(), '$lastStartedAt'] }, 1000] }, '$availableTime'], - }; - const group = { - $group: { - _id: null, - allAvailableTimeInSeconds: { - $sum: { - $cond: [{ $ifNull: ['$lastStoppedAt', false] }, '$availableTime', sumAvailableTimeWithCurrentTime], - }, - }, - rooms: { $sum: 1 }, - }, - }; - const project = { - $project: { - averageAvailableServiceTimeInSeconds: { - $trunc: { - $cond: [{ $eq: ['$rooms', 0] }, 0, { $divide: ['$allAvailableTimeInSeconds', '$rooms'] }], - }, - }, - }, - }; - const params = [match] as object[]; - if (departmentId && departmentId !== 'undefined') { - params.push(lookup); - params.push(unwind); - params.push(departmentsMatch); - } - params.push(group); - params.push(project); - return this.col.aggregate(params).toArray(); - } - - findAvailableServiceTimeHistory({ - start, - end, - fullReport, - onlyCount = false, - options = {}, - }: { - start: string; - end: string; - fullReport: boolean; - onlyCount: boolean; - options: any; - }): AggregationCursor { - const match = { - $match: { - date: { - $gte: parseInt(moment(start).format('YYYYMMDD')), - $lte: parseInt(moment(end).format('YYYYMMDD')), - }, - }, - }; - const lookup = { - $lookup: { - from: 'users', - localField: 'agentId', - foreignField: '_id', - as: 'user', - }, - }; - const unwind = { - $unwind: { - path: '$user', - }, - }; - const group = { - $group: { - _id: { _id: '$user._id', username: '$user.username' }, - serviceHistory: { $first: '$serviceHistory' }, - availableTimeInSeconds: { $sum: '$availableTime' }, - }, - }; - const project = { - $project: { - _id: 0, - username: '$_id.username', - availableTimeInSeconds: 1, - ...(fullReport && { serviceHistory: 1 }), - }, - }; - - const sort = { $sort: options.sort || { username: 1 } }; - const params = [match, lookup, unwind, group, project, sort] as object[]; - if (onlyCount) { - params.push({ $count: 'total' }); - return this.col.aggregate(params); - } - if (options.offset) { - params.push({ $skip: options.offset }); - } - if (options.count) { - params.push({ $limit: options.count }); - } - return this.col.aggregate(params, { allowDiskUse: true }); - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatBusinessHours.ts b/apps/meteor/app/models/server/raw/LivechatBusinessHours.ts deleted file mode 100644 index 161009c6bee2..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatBusinessHours.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { FindOneOptions, ObjectId, WithoutProjection } from 'mongodb'; -import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export interface IWorkHoursCronJobsItem { - day: string; - times: string[]; -} - -export interface IWorkHoursCronJobsWrapper { - start: IWorkHoursCronJobsItem[]; - finish: IWorkHoursCronJobsItem[]; -} - -export class LivechatBusinessHoursRaw extends BaseRaw { - async findOneDefaultBusinessHour(options?: undefined): Promise; - - async findOneDefaultBusinessHour( - options: WithoutProjection>, - ): Promise; - - async findOneDefaultBusinessHour

( - options: FindOneOptions

, - ): Promise

; - - findOneDefaultBusinessHour

(options?: any): Promise { - return this.findOne({ type: LivechatBusinessHourTypes.DEFAULT }, options); - } - - findActiveAndOpenBusinessHoursByDay(day: string, options?: any): Promise { - return this.find( - { - active: true, - workHours: { - $elemMatch: { - $or: [{ 'start.cron.dayOfWeek': day }, { 'finish.cron.dayOfWeek': day }], - open: true, - }, - }, - }, - options, - ).toArray(); - } - - findDefaultActiveAndOpenBusinessHoursByDay(day: string, options?: any): Promise { - return this.find( - { - type: LivechatBusinessHourTypes.DEFAULT, - active: true, - workHours: { - $elemMatch: { - $or: [{ 'start.cron.dayOfWeek': day, 'finish.cron.dayOfWeek': day }], - open: true, - }, - }, - }, - options, - ).toArray(); - } - - async insertOne(data: Omit): Promise { - return this.col.insertOne({ - _id: new ObjectId().toHexString(), - ...{ ts: new Date() }, - ...data, - }); - } - - findHoursToScheduleJobs(): Promise { - return this.col - .aggregate([ - { - $facet: { - start: [ - { $match: { active: true } }, - { $project: { _id: 0, workHours: 1 } }, - { $unwind: { path: '$workHours' } }, - { $match: { 'workHours.open': true } }, - { - $group: { - _id: { day: '$workHours.start.cron.dayOfWeek' }, - times: { $addToSet: '$workHours.start.cron.time' }, - }, - }, - { - $project: { - _id: 0, - day: '$_id.day', - times: 1, - }, - }, - ], - finish: [ - { $match: { active: true } }, - { $project: { _id: 0, workHours: 1 } }, - { $unwind: { path: '$workHours' } }, - { $match: { 'workHours.open': true } }, - { - $group: { - _id: { day: '$workHours.finish.cron.dayOfWeek' }, - times: { $addToSet: '$workHours.finish.cron.time' }, - }, - }, - { - $project: { - _id: 0, - day: '$_id.day', - times: 1, - }, - }, - ], - }, - }, - ]) - .toArray() as any; - } - - async findActiveBusinessHoursToOpen( - day: string, - start: string, - type?: LivechatBusinessHourTypes, - options?: any, - ): Promise { - const query: Record = { - active: true, - workHours: { - $elemMatch: { - 'start.cron.dayOfWeek': day, - 'start.cron.time': start, - 'open': true, - }, - }, - }; - if (type) { - query.type = type; - } - return this.col.find(query, options).toArray(); - } - - async findActiveBusinessHoursToClose( - day: string, - finish: string, - type?: LivechatBusinessHourTypes, - options?: any, - ): Promise { - const query: Record = { - active: true, - workHours: { - $elemMatch: { - 'finish.cron.dayOfWeek': day, - 'finish.cron.time': finish, - 'open': true, - }, - }, - }; - if (type) { - query.type = type; - } - return this.col.find(query, options).toArray(); - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatCustomField.ts b/apps/meteor/app/models/server/raw/LivechatCustomField.ts deleted file mode 100644 index 60593e573eb9..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatCustomField.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ILivechatCustomField } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatCustomFieldRaw extends BaseRaw {} diff --git a/apps/meteor/app/models/server/raw/LivechatDepartment.ts b/apps/meteor/app/models/server/raw/LivechatDepartment.ts deleted file mode 100644 index 8ef4fe5ac1c7..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatDepartment.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { FindOneOptions, Cursor, FilterQuery, WriteOpResult } from 'mongodb'; -import type { ILivechatDepartmentRecord } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatDepartmentRaw extends BaseRaw { - findInIds(departmentsIds: string[], options: FindOneOptions): Cursor { - const query = { _id: { $in: departmentsIds } }; - return this.find(query, options); - } - - findByNameRegexWithExceptionsAndConditions( - searchTerm: string, - exceptions: string[] = [], - conditions: FilterQuery = {}, - options: FindOneOptions = {}, - ): Cursor { - if (!Array.isArray(exceptions)) { - exceptions = [exceptions]; - } - - const nameRegex = new RegExp(`^${escapeRegExp(searchTerm).trim()}`, 'i'); - - const query = { - name: nameRegex, - _id: { - $nin: exceptions, - }, - ...conditions, - }; - - return this.find(query, options); - } - - findByBusinessHourId(businessHourId: string, options: FindOneOptions): Cursor { - const query = { businessHourId }; - return this.find(query, options); - } - - findEnabledByBusinessHourId( - businessHourId: string, - options: FindOneOptions, - ): Cursor { - const query = { businessHourId, enabled: true }; - return this.find(query, options); - } - - findEnabledByListOfBusinessHourIdsAndDepartmentIds( - businessHourIds: string[], - departmentIds: string[], - options: FindOneOptions, - ): Cursor { - const query: FilterQuery = { - enabled: true, - businessHourId: { - $in: businessHourIds, - }, - _id: { - $in: departmentIds, - }, - }; - return this.find(query, options); - } - - addBusinessHourToDepartmentsByIds(ids: string[] = [], businessHourId: string): Promise { - const query = { - _id: { $in: ids }, - }; - - const update = { - $set: { - businessHourId, - }, - }; - - return this.col.update(query, update, { multi: true }); - } - - removeBusinessHourFromDepartmentsByIdsAndBusinessHourId(ids: string[] = [], businessHourId: string): Promise { - const query = { - _id: { $in: ids }, - businessHourId, - }; - - const update = { - $unset: { - businessHourId: 1, - }, - }; - - return this.col.update(query, update, { multi: true }); - } - - removeBusinessHourFromDepartmentsByBusinessHourId(businessHourId: string): Promise { - const query = { - businessHourId, - }; - - const update = { - $unset: { - businessHourId: 1, - }, - }; - - return this.col.update(query, update, { multi: true }); - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatDepartmentAgents.ts b/apps/meteor/app/models/server/raw/LivechatDepartmentAgents.ts deleted file mode 100644 index 0a5c56f90a23..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatDepartmentAgents.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Cursor, FilterQuery, WithoutProjection, FindOneOptions } from 'mongodb'; -import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatDepartmentAgentsRaw extends BaseRaw { - findUsersInQueue(usersList: string[]): Cursor; - - findUsersInQueue( - usersList: string[], - options: WithoutProjection>, - ): Cursor; - - findUsersInQueue

( - usersList: string[], - options: FindOneOptions

, - ): Cursor

; - - findUsersInQueue

( - usersList: string[], - options?: - | undefined - | WithoutProjection> - | FindOneOptions

, - ): Cursor | Cursor

{ - const query: FilterQuery = {}; - - if (Array.isArray(usersList) && usersList.length) { - // TODO: Remove - query.username = { - $in: usersList, - }; - } - - if (options === undefined) { - return this.find(query); - } - - return this.find(query, options); - } - - findByAgentId(agentId: string): Cursor { - return this.find({ agentId }); - } - - findAgentsByDepartmentId(departmentId: string): Cursor; - - findAgentsByDepartmentId( - departmentId: string, - options: WithoutProjection>, - ): Cursor; - - findAgentsByDepartmentId

( - departmentId: string, - options: FindOneOptions

, - ): Cursor

; - - findAgentsByDepartmentId

( - departmentId: string, - options?: - | undefined - | WithoutProjection> - | FindOneOptions

, - ): Cursor | Cursor

{ - const query = { departmentId }; - - if (options === undefined) { - return this.find(query); - } - - return this.find(query, options); - } - - findActiveDepartmentsByAgentId(agentId: string): Cursor; - - findActiveDepartmentsByAgentId( - agentId: string, - options: WithoutProjection>, - ): Cursor; - - findActiveDepartmentsByAgentId

( - agentId: string, - options: FindOneOptions

, - ): Cursor

; - - findActiveDepartmentsByAgentId

( - agentId: string, - options?: - | undefined - | WithoutProjection> - | FindOneOptions

, - ): Cursor | Cursor

{ - const query = { - agentId, - departmentEnabled: true, - }; - - if (options === undefined) { - return this.find(query); - } - - return this.find(query, options); - } - - findByDepartmentIds(departmentIds: string[], options = {}): Cursor { - return this.find({ departmentId: { $in: departmentIds } }, options); - } - - findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): [] { - return []; - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatInquiry.ts b/apps/meteor/app/models/server/raw/LivechatInquiry.ts deleted file mode 100644 index 4a39b54673e3..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatInquiry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { FindOneOptions, MongoDistinctPreferences, UpdateWriteOpResult } from 'mongodb'; -import { IMessage, ILivechatInquiryRecord, LivechatInquiryStatus } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatInquiryRaw extends BaseRaw { - findOneQueuedByRoomId(rid: string): Promise<(ILivechatInquiryRecord & { status: LivechatInquiryStatus.QUEUED }) | null> { - const query = { - rid, - status: LivechatInquiryStatus.QUEUED, - }; - return this.findOne(query) as unknown as Promise<(ILivechatInquiryRecord & { status: LivechatInquiryStatus.QUEUED }) | null>; - } - - findOneByRoomId( - rid: string, - options: FindOneOptions, - ): Promise { - const query = { - rid, - }; - return this.findOne(query, options); - } - - getDistinctQueuedDepartments(options: MongoDistinctPreferences): Promise { - return this.col.distinct('department', { status: LivechatInquiryStatus.QUEUED }, options); - } - - async setDepartmentByInquiryId(inquiryId: string, department: string): Promise { - const updated = await this.findOneAndUpdate({ _id: inquiryId }, { $set: { department } }, { returnDocument: 'after' }); - return updated.value; - } - - async setLastMessageByRoomId(rid: string, message: IMessage): Promise { - return this.updateOne({ rid }, { $set: { lastMessage: message } }); - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatTrigger.ts b/apps/meteor/app/models/server/raw/LivechatTrigger.ts deleted file mode 100644 index 813d13d7ca95..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatTrigger.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Cursor, UpdateWriteOpResult } from 'mongodb'; -import type { ILivechatTrigger } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class LivechatTriggerRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { enabled: 1 } }]; - } - - findEnabled(): Cursor { - return this.find({ enabled: true }); - } - - updateById(_id: string, data: ILivechatTrigger): Promise { - return this.updateOne({ _id }, { $set: data }); - } -} diff --git a/apps/meteor/app/models/server/raw/LivechatVisitors.ts b/apps/meteor/app/models/server/raw/LivechatVisitors.ts deleted file mode 100644 index 222b44918617..000000000000 --- a/apps/meteor/app/models/server/raw/LivechatVisitors.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { AggregationCursor, Cursor, FilterQuery, FindOneOptions, UpdateWriteOpResult, WithoutProjection } from 'mongodb'; -import type { ILivechatVisitor } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LivechatVisitorsRaw extends BaseRaw { - findOneById(_id: string, options: WithoutProjection>): Promise { - const query = { - _id, - }; - - return this.findOne(query, options); - } - - getVisitorByToken(token: string, options: WithoutProjection>): Promise { - const query = { - token, - }; - - return this.findOne(query, options); - } - - getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department: string }): Cursor { - const query = { - _updatedAt: { - $gte: new Date(start), - $lt: new Date(end), - }, - ...(department && department !== 'undefined' && { department }), - }; - - return this.find(query, { projection: { _id: 1 } }); - } - - findByNameRegexWithExceptionsAndConditions

( - searchTerm: string, - exceptions: string[] = [], - conditions: FilterQuery = {}, - options: FindOneOptions

= {}, - ): AggregationCursor< - P & { - custom_name: string; - } - > { - if (!Array.isArray(exceptions)) { - exceptions = [exceptions]; - } - - const nameRegex = new RegExp(`^${escapeRegExp(searchTerm).trim()}`, 'i'); - - const match = { - $match: { - name: nameRegex, - _id: { - $nin: exceptions, - }, - ...conditions, - }, - }; - - const { projection, sort, skip, limit } = options; - const project = { - $project: { - // TODO: move this logic to client - // eslint-disable-next-line @typescript-eslint/camelcase - custom_name: { $concat: ['$username', ' - ', '$name'] }, - ...projection, - }, - }; - - const order = { $sort: sort || { name: 1 } }; - const params: Record[] = [match, order, skip && { $skip: skip }, limit && { $limit: limit }, project].filter( - Boolean, - ) as Record[]; - - return this.col.aggregate(params); - } - - /** - * Find visitors by their email or phone or username or name - * @return [{object}] List of Visitors from db - */ - findVisitorsByEmailOrPhoneOrNameOrUsername( - _emailOrPhoneOrNameOrUsername: string, - options: FindOneOptions, - ): Cursor { - const filter = new RegExp(_emailOrPhoneOrNameOrUsername, 'i'); - const query = { - $or: [ - { - 'visitorEmails.address': _emailOrPhoneOrNameOrUsername, - }, - { - 'phone.phoneNumber': _emailOrPhoneOrNameOrUsername, - }, - { - name: filter, - }, - { - username: filter, - }, - ], - }; - - return this.find(query, options); - } - - removeContactManagerByUsername(manager: string): Promise { - return this.updateMany( - { - contactManager: { - username: manager, - }, - }, - { - $unset: { - contactManager: true, - }, - }, - ); - } -} diff --git a/apps/meteor/app/models/server/raw/LoginServiceConfiguration.ts b/apps/meteor/app/models/server/raw/LoginServiceConfiguration.ts deleted file mode 100644 index edbe9b6e3681..000000000000 --- a/apps/meteor/app/models/server/raw/LoginServiceConfiguration.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ILoginServiceConfiguration } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class LoginServiceConfigurationRaw extends BaseRaw {} diff --git a/apps/meteor/app/models/server/raw/Messages.ts b/apps/meteor/app/models/server/raw/Messages.ts deleted file mode 100644 index ee81c791180a..000000000000 --- a/apps/meteor/app/models/server/raw/Messages.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import type { IMessage, IRoom, IUser, MessageTypesValues, ILivechatDepartment } from '@rocket.chat/core-typings'; -import type { PaginatedRequest } from '@rocket.chat/rest-typings'; -import type { - AggregationCursor, - Cursor, - FilterQuery, - FindOneOptions, - WithoutProjection, - Collection, - CollectionAggregationOptions, -} from 'mongodb'; - -import { BaseRaw } from './BaseRaw'; - -export class MessagesRaw extends BaseRaw { - findVisibleByMentionAndRoomId( - username: IUser['username'], - rid: IRoom['_id'], - options: WithoutProjection>, - ): Cursor { - const query: FilterQuery = { - '_hidden': { $ne: true }, - 'mentions.username': username, - rid, - }; - - return this.find(query, options); - } - - findStarredByUserAtRoom( - userId: IUser['_id'], - roomId: IRoom['_id'], - options: WithoutProjection>, - ): Cursor { - const query: FilterQuery = { - '_hidden': { $ne: true }, - 'starred._id': userId, - 'rid': roomId, - }; - - return this.find(query, options); - } - - findByRoomIdAndType( - roomId: IRoom['_id'], - type: IMessage['t'], - options: WithoutProjection> = {}, - ): Cursor { - const query: FilterQuery = { - rid: roomId, - t: type, - }; - - return this.find(query, options); - } - - findSnippetedByRoom(roomId: IRoom['_id'], options: WithoutProjection>): Cursor { - const query: FilterQuery = { - _hidden: { $ne: true }, - snippeted: true, - rid: roomId, - }; - - return this.find(query, options); - } - - // TODO: do we need this? currently not used anywhere - findDiscussionsByRoom(rid: IRoom['_id'], options: WithoutProjection>): Cursor { - const query: FilterQuery = { rid, drid: { $exists: true } }; - - return this.find(query, options); - } - - findDiscussionsByRoomAndText(rid: IRoom['_id'], text: string, options: WithoutProjection>): Cursor { - const query: FilterQuery = { - rid, - drid: { $exists: true }, - msg: new RegExp(escapeRegExp(text), 'i'), - }; - - return this.find(query, options); - } - - findAllNumberOfTransferredRooms({ - start, - end, - departmentId, - onlyCount = false, - options = {}, - }: { - start: string; - end: string; - departmentId: ILivechatDepartment['_id']; - onlyCount: boolean; - options: PaginatedRequest; - }): AggregationCursor { - // FIXME: aggregation type definitions - const match = { - $match: { - t: 'livechat_transfer_history', - ts: { $gte: new Date(start), $lte: new Date(end) }, - }, - }; - const lookup = { - $lookup: { - from: 'rocketchat_room', - localField: 'rid', - foreignField: '_id', - as: 'room', - }, - }; - const unwind = { - $unwind: { - path: '$room', - preserveNullAndEmptyArrays: true, - }, - }; - const group = { - $group: { - _id: { - _id: null, - departmentId: '$room.departmentId', - }, - numberOfTransferredRooms: { $sum: 1 }, - }, - }; - const project = { - $project: { - _id: { $ifNull: ['$_id.departmentId', null] }, - numberOfTransferredRooms: 1, - }, - }; - const firstParams: Exclude['aggregate']>[0], undefined> = [match, lookup, unwind]; - if (departmentId) { - firstParams.push({ - $match: { - 'room.departmentId': departmentId, - }, - }); - } - const sort = { $sort: options.sort || { name: 1 } }; - const params = [...firstParams, group, project, sort]; - if (onlyCount) { - params.push({ $count: 'total' }); - return this.col.aggregate(params); - } - if (options.offset) { - params.push({ $skip: options.offset }); - } - if (options.count) { - params.push({ $limit: options.count }); - } - return this.col.aggregate(params, { allowDiskUse: true }); - } - - getTotalOfMessagesSentByDate({ start, end, options = {} }: { start: Date; end: Date; options?: PaginatedRequest }): Promise { - const params: Exclude['aggregate']>[0], undefined> = [ - { $match: { t: { $exists: false }, ts: { $gte: start, $lte: end } } }, - { - $lookup: { - from: 'rocketchat_room', - localField: 'rid', - foreignField: '_id', - as: 'room', - }, - }, - { - $unwind: { - path: '$room', - }, - }, - { - $group: { - _id: { - _id: '$room._id', - name: { - $cond: [{ $ifNull: ['$room.fname', false] }, '$room.fname', '$room.name'], - }, - t: '$room.t', - usernames: { - $cond: [{ $ifNull: ['$room.usernames', false] }, '$room.usernames', []], - }, - date: { - $concat: [{ $substr: ['$ts', 0, 4] }, { $substr: ['$ts', 5, 2] }, { $substr: ['$ts', 8, 2] }], - }, - }, - messages: { $sum: 1 }, - }, - }, - { - $project: { - _id: 0, - date: '$_id.date', - room: { - _id: '$_id._id', - name: '$_id.name', - t: '$_id.t', - usernames: '$_id.usernames', - }, - type: 'messages', - messages: 1, - }, - }, - ]; - if (options.sort) { - params.push({ $sort: options.sort }); - } - if (options.count) { - params.push({ $limit: options.count }); - } - return this.col.aggregate(params).toArray(); - } - - findLivechatClosedMessages(rid: IRoom['_id'], options: WithoutProjection>): Cursor { - return this.find( - { - rid, - $or: [{ t: { $exists: false } }, { t: 'livechat-close' }], - }, - options, - ); - } - - async countRoomsWithStarredMessages(options: CollectionAggregationOptions): Promise { - const queryResult = await this.col - .aggregate<{ _id: null; total: number }>( - [ - { $match: { 'starred._id': { $exists: true } } }, - { $group: { _id: '$rid' } }, - { - $group: { - _id: null, - total: { $sum: 1 }, - }, - }, - ], - options, - ) - .next(); - - return queryResult?.total || 0; - } - - async countRoomsWithPinnedMessages(options: CollectionAggregationOptions): Promise { - const queryResult = await this.col - .aggregate<{ _id: null; total: number }>( - [ - { $match: { pinned: true } }, - { $group: { _id: '$rid' } }, - { - $group: { - _id: null, - total: { $sum: 1 }, - }, - }, - ], - options, - ) - .next(); - - return queryResult?.total || 0; - } - - async countE2EEMessages(options: WithoutProjection>): Promise { - return this.find({ t: 'e2e' }, options).count(); - } - - findPinned(options: WithoutProjection>): Cursor { - const query: FilterQuery = { - t: { $ne: 'rm' as MessageTypesValues }, - _hidden: { $ne: true }, - pinned: true, - }; - - return this.find(query, options); - } - - findStarred(options: WithoutProjection>): Cursor { - const query: FilterQuery = { - '_hidden': { $ne: true }, - 'starred._id': { $exists: true }, - }; - - return this.find(query, options); - } -} diff --git a/apps/meteor/app/models/server/raw/NotificationQueue.ts b/apps/meteor/app/models/server/raw/NotificationQueue.ts deleted file mode 100644 index 4297c1226566..000000000000 --- a/apps/meteor/app/models/server/raw/NotificationQueue.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { UpdateWriteOpResult } from 'mongodb'; -import type { INotification } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class NotificationQueueRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [ - { key: { uid: 1 } }, - { key: { ts: 1 }, expireAfterSeconds: 2 * 60 * 60 }, - { key: { schedule: 1 }, sparse: true }, - { key: { sending: 1 }, sparse: true }, - { key: { error: 1 }, sparse: true }, - ]; - } - - unsetSendingById(_id: string): Promise { - return this.updateOne( - { _id }, - { - $unset: { - sending: 1, - }, - }, - ); - } - - setErrorById(_id: string, error: any): Promise { - return this.updateOne( - { - _id, - }, - { - $set: { - error, - }, - $unset: { - sending: 1, - }, - }, - ); - } - - clearScheduleByUserId(uid: string): Promise { - return this.updateMany( - { - uid, - schedule: { $exists: true }, - }, - { - $unset: { - schedule: 1, - }, - }, - ); - } - - async clearQueueByUserId(uid: string): Promise { - const op = await this.deleteMany({ - uid, - }); - - return op.deletedCount; - } - - async findNextInQueueOrExpired(expired: Date): Promise { - const now = new Date(); - - const result = await this.col.findOneAndUpdate( - { - $and: [ - { - $or: [{ sending: { $exists: false } }, { sending: { $lte: expired } }], - }, - { - $or: [{ schedule: { $exists: false } }, { schedule: { $lte: now } }], - }, - { - error: { $exists: false }, - }, - ], - }, - { - $set: { - sending: now, - }, - }, - { - sort: { - ts: 1, - }, - }, - ); - - return result.value; - } -} diff --git a/apps/meteor/app/models/server/raw/Nps.ts b/apps/meteor/app/models/server/raw/Nps.ts deleted file mode 100644 index 0703da39640f..000000000000 --- a/apps/meteor/app/models/server/raw/Nps.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { UpdateWriteOpResult } from 'mongodb'; -import { INps, NPSStatus } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = INps; -export class NpsRaw extends BaseRaw { - modelIndexes(): IndexSpecification[] { - return [{ key: { status: 1, expireAt: 1 } }]; - } - - // get expired surveys still in progress - async getOpenExpiredAndStartSending(): Promise { - const today = new Date(); - - const query = { - status: NPSStatus.OPEN, - expireAt: { $lte: today }, - }; - const update = { - $set: { - status: NPSStatus.SENDING, - }, - }; - const { value } = await this.col.findOneAndUpdate(query, update, { sort: { expireAt: 1 } }); - - return value; - } - - // get expired surveys already sending results - async getOpenExpiredAlreadySending(): Promise { - const today = new Date(); - - const query = { - status: NPSStatus.SENDING, - expireAt: { $lte: today }, - }; - - return this.col.findOne(query); - } - - updateStatusById(_id: INps['_id'], status: INps['status']): Promise { - const update = { - $set: { - status, - }, - }; - return this.col.updateOne({ _id }, update); - } - - save({ - _id, - startAt, - expireAt, - createdBy, - status, - }: Pick): Promise { - return this.col.updateOne( - { - _id, - }, - { - $set: { - startAt, - _updatedAt: new Date(), - }, - $setOnInsert: { - expireAt, - createdBy, - createdAt: new Date(), - status, - }, - }, - { - upsert: true, - }, - ); - } - - closeAllByStatus(status: NPSStatus): Promise { - const query = { - status, - }; - - const update = { - $set: { - status: NPSStatus.CLOSED, - }, - }; - - return this.col.updateMany(query, update); - } -} diff --git a/apps/meteor/app/models/server/raw/NpsVote.ts b/apps/meteor/app/models/server/raw/NpsVote.ts deleted file mode 100644 index 999b96311191..000000000000 --- a/apps/meteor/app/models/server/raw/NpsVote.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { ObjectId, Cursor, FindOneOptions, UpdateWriteOpResult, WithoutProjection } from 'mongodb'; -import { INpsVote, INpsVoteStatus } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = INpsVote; -export class NpsVoteRaw extends BaseRaw { - modelIndexes(): IndexSpecification[] { - return [{ key: { npsId: 1, status: 1, sentAt: 1 } }, { key: { npsId: 1, identifier: 1 }, unique: true }]; - } - - findNotSentByNpsId(npsId: string, options?: WithoutProjection>): Cursor { - const query = { - npsId, - status: INpsVoteStatus.NEW, - }; - return this.col.find(query, options).sort({ ts: 1 }).limit(1000); - } - - findByNpsIdAndStatus(npsId: string, status: INpsVoteStatus, options?: WithoutProjection>): Cursor { - const query = { - npsId, - status, - }; - return this.col.find(query, options); - } - - findByNpsId(npsId: string, options?: WithoutProjection>): Cursor { - const query = { - npsId, - }; - return this.col.find(query, options); - } - - save(vote: Omit): Promise { - const { npsId, identifier } = vote; - - const query = { - npsId, - identifier, - }; - const update = { - $set: { - ...vote, - _updatedAt: new Date(), - }, - $setOnInsert: { - _id: new ObjectId().toHexString(), - }, - }; - - return this.col.updateOne(query, update, { upsert: true }); - } - - updateVotesToSent(voteIds: string[]): Promise { - const query = { - _id: { $in: voteIds }, - }; - const update = { - $set: { - status: INpsVoteStatus.SENT, - }, - }; - return this.col.updateMany(query, update); - } - - updateOldSendingToNewByNpsId(npsId: string): Promise { - const fiveMinutes = new Date(); - fiveMinutes.setMinutes(fiveMinutes.getMinutes() - 5); - - const query = { - npsId, - status: INpsVoteStatus.SENDING, - sentAt: { $lt: fiveMinutes }, - }; - const update = { - $set: { - status: INpsVoteStatus.NEW, - }, - $unset: { - sentAt: 1 as 1, // why do you do this to me TypeScript? - }, - }; - return this.col.updateMany(query, update); - } -} diff --git a/apps/meteor/app/models/server/raw/OAuthApps.ts b/apps/meteor/app/models/server/raw/OAuthApps.ts deleted file mode 100644 index 760aae203ac8..000000000000 --- a/apps/meteor/app/models/server/raw/OAuthApps.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IOAuthApps } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class OAuthAppsRaw extends BaseRaw { - findOneAuthAppByIdOrClientId(props: { clientId: string } | { appId: string }): Promise { - return this.findOne({ - ...('appId' in props && { _id: props.appId }), - ...('clientId' in props && { _id: props.clientId }), - }); - } -} diff --git a/apps/meteor/app/models/server/raw/OEmbedCache.ts b/apps/meteor/app/models/server/raw/OEmbedCache.ts deleted file mode 100644 index c43b38c979d7..000000000000 --- a/apps/meteor/app/models/server/raw/OEmbedCache.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DeleteWriteOpResultObject } from 'mongodb'; -import type { IOEmbedCache } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = IOEmbedCache; - -export class OEmbedCacheRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { updatedAt: 1 } }]; - } - - async createWithIdAndData(_id: string, data: any): Promise { - const record = { - _id, - data, - updatedAt: new Date(), - }; - record._id = (await this.insertOne(record)).insertedId; - return record; - } - - removeAfterDate(date: Date): Promise { - const query = { - updatedAt: { - $lte: date, - }, - }; - return this.deleteMany(query); - } -} diff --git a/apps/meteor/app/models/server/raw/OmnichannelQueue.ts b/apps/meteor/app/models/server/raw/OmnichannelQueue.ts deleted file mode 100644 index ef79f55baca0..000000000000 --- a/apps/meteor/app/models/server/raw/OmnichannelQueue.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -import type { IOmnichannelQueueStatus } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -const UNIQUE_QUEUE_ID = 'queue'; -export class OmnichannelQueueRaw extends BaseRaw { - initQueue() { - return this.col.updateOne( - { - _id: UNIQUE_QUEUE_ID, - }, - { - $unset: { - stoppedAt: 1, - }, - $set: { - startedAt: new Date(), - locked: false, - }, - }, - { - upsert: true, - }, - ); - } - - stopQueue() { - return this.col.updateOne( - { - _id: UNIQUE_QUEUE_ID, - }, - { - $set: { - stoppedAt: new Date(), - locked: false, - }, - }, - ); - } - - async lockQueue() { - const date = new Date(); - const result = await this.col.findOneAndUpdate( - { - _id: UNIQUE_QUEUE_ID, - $or: [ - { - locked: true, - lockedAt: { - $lte: new Date(date.getTime() - 5000), - }, - }, - { - locked: false, - }, - ], - }, - { - $set: { - locked: true, - // apply 5 secs lock lifetime - lockedAt: new Date(), - }, - }, - { - sort: { - _id: 1, - }, - }, - ); - - return result.value; - } - - async unlockQueue() { - const result = await this.col.findOneAndUpdate( - { - _id: UNIQUE_QUEUE_ID, - }, - { - $set: { - locked: false, - }, - $unset: { - lockedAt: 1, - }, - }, - { - sort: { - _id: 1, - }, - }, - ); - - return result.value; - } -} diff --git a/apps/meteor/app/models/server/raw/PbxEvents.ts b/apps/meteor/app/models/server/raw/PbxEvents.ts deleted file mode 100644 index 6b0c79a3e2a8..000000000000 --- a/apps/meteor/app/models/server/raw/PbxEvents.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Cursor } from 'mongodb'; -import type { IPbxEvent } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class PbxEventsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { uniqueId: 1 }, unique: true }]; - } - - findByEvents(callUniqueId: string, events: string[]): Cursor { - return this.find( - { - $or: [ - { - callUniqueId, - }, - { - callUniqueIdFallback: callUniqueId, - }, - ], - event: { - $in: events, - }, - }, - { - sort: { - ts: 1, - }, - }, - ); - } - - findOneByEvent(callUniqueId: string, event: string): Promise { - return this.findOne({ - $or: [ - { - callUniqueId, - }, - { - callUniqueIdFallback: callUniqueId, - }, - ], - event, - }); - } - - findOneByUniqueId(callUniqueId: string): Promise { - return this.findOne({ - $or: [ - { - callUniqueId, - }, - { - callUniqueIdFallback: callUniqueId, - }, - ], - }); - } -} diff --git a/apps/meteor/app/models/server/raw/Permissions.ts b/apps/meteor/app/models/server/raw/Permissions.ts deleted file mode 100644 index c7b1d772eb68..000000000000 --- a/apps/meteor/app/models/server/raw/Permissions.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { IPermission, IRole } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class PermissionsRaw extends BaseRaw { - async createOrUpdate(name: string, roles: IRole['_id'][]): Promise { - const exists = await this.findOne>( - { - _id: name, - roles, - }, - { fields: { _id: 1 } }, - ); - - if (exists) { - return exists._id; - } - - return this.update({ _id: name }, { $set: { roles } }, { upsert: true }).then((result) => result.result._id); - } - - async create(id: string, roles: IRole['_id'][]): Promise { - const exists = await this.findOneById>(id, { fields: { _id: 1 } }); - - if (exists) { - return exists._id; - } - - return this.update({ _id: id }, { $set: { roles } }, { upsert: true }).then((result) => result.result._id); - } - - async addRole(permission: string, role: IRole['_id']): Promise { - await this.update({ _id: permission, roles: { $ne: role } }, { $addToSet: { roles: role } }); - } - - async setRoles(permission: string, roles: IRole['_id'][]): Promise { - await this.update({ _id: permission }, { $set: { roles } }); - } - - async removeRole(permission: string, role: IRole['_id']): Promise { - await this.update({ _id: permission, roles: role }, { $pull: { roles: role } }); - } -} diff --git a/apps/meteor/app/models/server/raw/ReadReceipts.ts b/apps/meteor/app/models/server/raw/ReadReceipts.ts deleted file mode 100644 index 95b99d35f791..000000000000 --- a/apps/meteor/app/models/server/raw/ReadReceipts.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Cursor } from 'mongodb'; -import { ReadReceipt } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class ReadReceiptsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { roomId: 1, userId: 1, messageId: 1 }, unique: true }, { key: { messageId: 1 } }]; - } - - findByMessageId(messageId: string): Cursor { - return this.find({ messageId }); - } -} diff --git a/apps/meteor/app/models/server/raw/Reports.ts b/apps/meteor/app/models/server/raw/Reports.ts deleted file mode 100644 index 5b0e0b4a4ba6..000000000000 --- a/apps/meteor/app/models/server/raw/Reports.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { IReport, IMessage } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class ReportsRaw extends BaseRaw { - createWithMessageDescriptionAndUserId(message: IMessage, description: string, userId: string): ReturnType['insertOne']> { - const record: Pick = { - message, - description, - ts: new Date(), - userId, - }; - return this.insertOne(record); - } -} diff --git a/apps/meteor/app/models/server/raw/Roles.ts b/apps/meteor/app/models/server/raw/Roles.ts deleted file mode 100644 index 0d774be584fc..000000000000 --- a/apps/meteor/app/models/server/raw/Roles.ts +++ /dev/null @@ -1,262 +0,0 @@ -import type { - Collection, - Cursor, - FilterQuery, - FindOneOptions, - InsertOneWriteOpResult, - UpdateWriteOpResult, - WithId, - WithoutProjection, -} from 'mongodb'; -import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; -import { SubscriptionsRaw } from './Subscriptions'; -import { UsersRaw } from './Users'; - -type ScopedModelRoles = { - Subscriptions: SubscriptionsRaw; - Users: UsersRaw; -}; - -export class RolesRaw extends BaseRaw { - constructor(public readonly col: Collection, private readonly models: ScopedModelRoles, trash?: Collection) { - super(col, trash); - } - - findByUpdatedDate(updatedAfterDate: Date, options?: FindOneOptions): Cursor { - const query = { - _updatedAt: { $gte: new Date(updatedAfterDate) }, - }; - - return options ? this.find(query, options) : this.find(query); - } - - async addUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.addUserRoles method received a role scope instead of a scope value.'); - } - - if (!Array.isArray(roles)) { - roles = [roles]; - process.env.NODE_ENV === 'development' && console.warn('[WARN] RolesRaw.addUserRoles: roles should be an array'); - } - - for await (const roleId of roles) { - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - process.env.NODE_ENV === 'development' && console.warn(`[WARN] RolesRaw.addUserRoles: role: ${roleId} not found`); - continue; - } - switch (role.scope) { - case 'Subscriptions': - await this.models.Subscriptions.addRolesByUserId(userId, [role._id], scope); - break; - case 'Users': - default: - await this.models.Users.addRolesByUserId(userId, [role._id]); - } - } - return true; - } - - async isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.isUserInRoles method received a role scope instead of a scope value.'); - } - - for await (const roleId of roles) { - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - continue; - } - - switch (role.scope) { - case 'Subscriptions': - if (await this.models.Subscriptions.isUserInRole(userId, roleId, scope)) { - return true; - } - break; - case 'Users': - default: - if (await this.models.Users.isUserInRole(userId, roleId)) { - return true; - } - } - } - return false; - } - - async removeUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.removeUserRoles method received a role scope instead of a scope value.'); - } - - for await (const roleId of roles) { - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - continue; - } - - switch (role.scope) { - case 'Subscriptions': - scope && (await this.models.Subscriptions.removeRolesByUserId(userId, [roleId], scope)); - break; - case 'Users': - default: - await this.models.Users.removeRolesByUserId(userId, [roleId]); - } - } - return true; - } - - async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise; - - async findOneByIdOrName( - _idOrName: IRole['_id'] | IRole['name'], - options: WithoutProjection>, - ): Promise; - - async findOneByIdOrName

( - _idOrName: IRole['_id'] | IRole['name'], - options: FindOneOptions

, - ): Promise

; - - findOneByIdOrName

(_idOrName: IRole['_id'] | IRole['name'], options?: any): Promise { - const query: FilterQuery = { - $or: [ - { - _id: _idOrName, - }, - { - name: _idOrName, - }, - ], - }; - - return this.findOne(query, options); - } - - async findOneByName

(name: IRole['name'], options?: any): Promise { - const query: FilterQuery = { - name, - }; - - return this.findOne(query, options); - } - - findInIds

(ids: IRole['_id'][], options?: FindOneOptions): P extends Pick ? Cursor

: Cursor { - const query: FilterQuery = { - name: { - $in: ids, - }, - }; - - return this.find(query, options || {}) as P extends Pick ? Cursor

: Cursor; - } - - findAllExceptIds

(ids: IRole['_id'][], options?: FindOneOptions): P extends Pick ? Cursor

: Cursor { - const query: FilterQuery = { - _id: { - $nin: ids, - }, - }; - - return this.find(query, options || {}) as P extends Pick ? Cursor

: Cursor; - } - - updateById( - _id: IRole['_id'], - name: IRole['name'], - scope: IRole['scope'], - description: IRole['description'] = '', - mandatory2fa: IRole['mandatory2fa'] = false, - ): Promise { - const queryData = { - name, - scope, - description, - mandatory2fa, - }; - - return this.updateOne({ _id }, { $set: queryData }, { upsert: true }); - } - - findUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise>; - - findUsersInRole( - roleId: IRole['_id'], - scope: IRoom['_id'] | undefined, - options: WithoutProjection>, - ): Promise>; - - findUsersInRole

( - roleId: IRole['_id'], - scope: IRoom['_id'] | undefined, - options: FindOneOptions

, - ): Promise>; - - async findUsersInRole

( - roleId: IRole['_id'], - scope: IRoom['_id'] | undefined, - options?: any | undefined, - ): Promise | Cursor

> { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.findUsersInRole method received a role scope instead of a scope value.'); - } - - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - throw new Error('RolesRaw.findUsersInRole: role not found'); - } - - switch (role.scope) { - case 'Subscriptions': - return this.models.Subscriptions.findUsersInRoles([role._id], scope, options); - case 'Users': - default: - return this.models.Users.findUsersInRoles([role._id], null, options); - } - } - - createWithRandomId( - name: IRole['name'], - scope: IRole['scope'] = 'Users', - description = '', - protectedRole = true, - mandatory2fa = false, - ): Promise>> { - const role = { - name, - scope, - description, - protected: protectedRole, - mandatory2fa, - }; - - return this.insertOne(role); - } - - async canAddUserToRole(uid: IUser['_id'], roleId: IRole['_id'], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.canAddUserToRole method received a role scope instead of a scope value.'); - } - - const role = await this.findOne({ _id: roleId }, { fields: { scope: 1 } } as FindOneOptions); - if (!role) { - return false; - } - - switch (role.scope) { - case 'Subscriptions': - return this.models.Subscriptions.isUserInRoleScope(uid, scope); - case 'Users': - default: - return this.models.Users.isUserInRoleScope(uid); - } - } -} diff --git a/apps/meteor/app/models/server/raw/ServerEvents.ts b/apps/meteor/app/models/server/raw/ServerEvents.ts deleted file mode 100644 index e169524574d5..000000000000 --- a/apps/meteor/app/models/server/raw/ServerEvents.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { IServerEvent, ServerEventType } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class ServerEventsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { t: 1, ip: 1, ts: -1 } }, { key: { 't': 1, 'u.username': 1, 'ts': -1 } }]; - } - - async findLastFailedAttemptByIp(ip: string): Promise { - return this.findOne( - { - ip, - t: ServerEventType.FAILED_LOGIN_ATTEMPT, - }, - { sort: { ts: -1 } }, - ); - } - - async findLastFailedAttemptByUsername(username: string): Promise { - return this.findOne( - { - 'u.username': username, - 't': ServerEventType.FAILED_LOGIN_ATTEMPT, - }, - { sort: { ts: -1 } }, - ); - } - - async countFailedAttemptsByUsernameSince(username: string, since: Date): Promise { - return this.find({ - 'u.username': username, - 't': ServerEventType.FAILED_LOGIN_ATTEMPT, - 'ts': { - $gte: since, - }, - }).count(); - } - - countFailedAttemptsByIpSince(ip: string, since: Date): Promise { - return this.find({ - ip, - t: ServerEventType.FAILED_LOGIN_ATTEMPT, - ts: { - $gte: since, - }, - }).count(); - } - - countFailedAttemptsByIp(ip: string): Promise { - return this.find({ - ip, - t: ServerEventType.FAILED_LOGIN_ATTEMPT, - }).count(); - } - - countFailedAttemptsByUsername(username: string): Promise { - return this.find({ - 'u.username': username, - 't': ServerEventType.FAILED_LOGIN_ATTEMPT, - }).count(); - } -} diff --git a/apps/meteor/app/models/server/raw/Sessions.ts b/apps/meteor/app/models/server/raw/Sessions.ts deleted file mode 100644 index fa955bc06f1a..000000000000 --- a/apps/meteor/app/models/server/raw/Sessions.ts +++ /dev/null @@ -1,1317 +0,0 @@ -import { - AggregationCursor, - BulkWriteOperation, - BulkWriteOpResultObject, - Collection, - UpdateWriteOpResult, - FilterQuery, - Cursor, -} from 'mongodb'; -import type { - ISession, - UserSessionAggregation, - DeviceSessionAggregation, - OSSessionAggregation, - UserSessionAggregationResult, - DeviceSessionAggregationResult, - OSSessionAggregationResult, - IUser, -} from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification, ModelOptionalId } from './BaseRaw'; - -type DestructuredDate = { year: number; month: number; day: number }; -type DestructuredDateWithType = { - year: number; - month: number; - day: number; - type?: 'month' | 'week'; -}; -type DestructuredRange = { start: DestructuredDate; end: DestructuredDate }; -type DateRange = { start: Date; end: Date }; - -const matchBasedOnDate = (start: DestructuredDate, end: DestructuredDate): FilterQuery => { - if (start.year === end.year && start.month === end.month) { - return { - year: start.year, - month: start.month, - day: { $gte: start.day, $lte: end.day }, - }; - } - - if (start.year === end.year) { - return { - year: start.year, - $and: [ - { - $or: [ - { - month: { $gt: start.month }, - }, - { - month: start.month, - day: { $gte: start.day }, - }, - ], - }, - { - $or: [ - { - month: { $lt: end.month }, - }, - { - month: end.month, - day: { $lte: end.day }, - }, - ], - }, - ], - }; - } - - return { - $and: [ - { - $or: [ - { - year: { $gt: start.year }, - }, - { - year: start.year, - month: { $gt: start.month }, - }, - { - year: start.year, - month: start.month, - day: { $gte: start.day }, - }, - ], - }, - { - $or: [ - { - year: { $lt: end.year }, - }, - { - year: end.year, - month: { $lt: end.month }, - }, - { - year: end.year, - month: end.month, - day: { $lte: end.day }, - }, - ], - }, - ], - }; -}; - -const getGroupSessionsByHour = ( - _id: { range: string; day: string; month: string; year: string } | string, -): { listGroup: object; countGroup: object } => { - const isOpenSession = { $not: ['$session.closedAt'] }; - const isAfterLoginAt = { $gte: ['$range', { $hour: '$session.loginAt' }] }; - const isBeforeClosedAt = { $lte: ['$range', { $hour: '$session.closedAt' }] }; - - const listGroup = { - $group: { - _id, - usersList: { - $addToSet: { - $cond: [ - { - $or: [{ $and: [isOpenSession, isAfterLoginAt] }, { $and: [isAfterLoginAt, isBeforeClosedAt] }], - }, - '$session.userId', - '$$REMOVE', - ], - }, - }, - }, - }; - - const countGroup = { - $addFields: { - users: { $size: '$usersList' }, - }, - }; - - return { listGroup, countGroup }; -}; - -const getSortByFullDate = (): { year: number; month: number; day: number } => ({ - year: -1, - month: -1, - day: -1, -}); - -const getProjectionByFullDate = (): { day: string; month: string; year: string } => ({ - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', -}); - -export const aggregates = { - dailySessionsOfYesterday( - collection: Collection, - { year, month, day }: DestructuredDate, - ): AggregationCursor< - Pick & { - time: number; - sessions: number; - devices: ISession['device'][]; - _computedAt: string; - } - > { - return collection.aggregate< - Pick & { - time: number; - sessions: number; - devices: ISession['device'][]; - _computedAt: string; - } - >( - [ - { - $match: { - userId: { $exists: true }, - lastActivityAt: { $exists: true }, - device: { $exists: true }, - type: 'session', - $or: [ - { - year: { $lt: year }, - }, - { - year, - month: { $lt: month }, - }, - { - year, - month, - day: { $lte: day }, - }, - ], - }, - }, - { - $project: { - userId: 1, - device: 1, - day: 1, - month: 1, - year: 1, - mostImportantRole: 1, - time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } }, - }, - }, - { - $match: { - time: { $gt: 0 }, - }, - }, - { - $group: { - _id: { - userId: '$userId', - device: '$device', - day: '$day', - month: '$month', - year: '$year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: 1 }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $group: { - _id: { - userId: '$_id.userId', - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: '$sessions' }, - devices: { - $push: { - sessions: '$sessions', - time: '$time', - device: '$_id.device', - }, - }, - }, - }, - { - $sort: { - _id: 1, - }, - }, - { - $project: { - _id: 0, - type: { $literal: 'user_daily' }, - _computedAt: { $literal: new Date() }, - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', - userId: '$_id.userId', - mostImportantRole: 1, - time: 1, - sessions: 1, - devices: 1, - }, - }, - ], - { allowDiskUse: true }, - ); - }, - - async getUniqueUsersOfYesterday( - collection: Collection, - { year, month, day }: DestructuredDate, - ): Promise { - return collection - .aggregate([ - { - $match: { - year, - month, - day, - type: 'user_daily', - }, - }, - { - $group: { - _id: { - day: '$day', - month: '$month', - year: '$year', - mostImportantRole: '$mostImportantRole', - }, - count: { - $sum: 1, - }, - sessions: { - $sum: '$sessions', - }, - time: { - $sum: '$time', - }, - }, - }, - { - $group: { - _id: { - day: '$day', - month: '$month', - year: '$year', - }, - roles: { - $push: { - role: '$_id.mostImportantRole', - count: '$count', - sessions: '$sessions', - time: '$time', - }, - }, - count: { - $sum: '$count', - }, - sessions: { - $sum: '$sessions', - }, - time: { - $sum: '$time', - }, - }, - }, - { - $project: { - _id: 0, - count: 1, - sessions: 1, - time: 1, - roles: 1, - }, - }, - ]) - .toArray(); - }, - - async getUniqueUsersOfLastMonthOrWeek( - collection: Collection, - { year, month, day, type = 'month' }: DestructuredDateWithType, - ): Promise { - return collection - .aggregate( - [ - { - $match: { - type: 'user_daily', - ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }), - }, - }, - { - $group: { - _id: { - userId: '$userId', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - sessions: { - $sum: '$sessions', - }, - time: { - $sum: '$time', - }, - }, - }, - { - $group: { - _id: { - mostImportantRole: '$mostImportantRole', - }, - count: { - $sum: 1, - }, - sessions: { - $sum: '$sessions', - }, - time: { - $sum: '$time', - }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $group: { - _id: 1, - roles: { - $push: { - role: '$_id.mostImportantRole', - count: '$count', - sessions: '$sessions', - time: '$time', - }, - }, - count: { - $sum: '$count', - }, - sessions: { - $sum: '$sessions', - }, - time: { - $sum: '$time', - }, - }, - }, - { - $project: { - _id: 0, - count: 1, - roles: 1, - sessions: 1, - time: 1, - }, - }, - ], - { allowDiskUse: true }, - ) - .toArray(); - }, - - getMatchOfLastMonthOrWeek({ year, month, day, type = 'month' }: DestructuredDateWithType): FilterQuery { - let startOfPeriod; - - if (type === 'month') { - const pastMonthLastDay = new Date(year, month - 1, 0).getDate(); - const currMonthLastDay = new Date(year, month, 0).getDate(); - - startOfPeriod = new Date(year, month - 1, day); - startOfPeriod.setMonth( - startOfPeriod.getMonth() - 1, - (currMonthLastDay === day ? pastMonthLastDay : Math.min(pastMonthLastDay, day)) + 1, - ); - } else { - startOfPeriod = new Date(year, month - 1, day - 6); - } - - const startOfPeriodObject = { - year: startOfPeriod.getFullYear(), - month: startOfPeriod.getMonth() + 1, - day: startOfPeriod.getDate(), - }; - - if (year === startOfPeriodObject.year && month === startOfPeriodObject.month) { - return { - year, - month, - day: { $gte: startOfPeriodObject.day, $lte: day }, - }; - } - - if (year === startOfPeriodObject.year) { - return { - year, - $and: [ - { - $or: [ - { - month: { $gt: startOfPeriodObject.month }, - }, - { - month: startOfPeriodObject.month, - day: { $gte: startOfPeriodObject.day }, - }, - ], - }, - { - $or: [ - { - month: { $lt: month }, - }, - { - month, - day: { $lte: day }, - }, - ], - }, - ], - }; - } - - return { - $and: [ - { - $or: [ - { - year: { $gt: startOfPeriodObject.year }, - }, - { - year: startOfPeriodObject.year, - month: { $gt: startOfPeriodObject.month }, - }, - { - year: startOfPeriodObject.year, - month: startOfPeriodObject.month, - day: { $gte: startOfPeriodObject.day }, - }, - ], - }, - { - $or: [ - { - year: { $lt: year }, - }, - { - year, - month: { $lt: month }, - }, - { - year, - month, - day: { $lte: day }, - }, - ], - }, - ], - }; - }, - - async getUniqueDevicesOfLastMonthOrWeek( - collection: Collection, - { year, month, day, type = 'month' }: DestructuredDateWithType, - ): Promise { - return collection - .aggregate( - [ - { - $match: { - type: 'user_daily', - ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }), - }, - }, - { - $unwind: '$devices', - }, - { - $group: { - _id: { - type: '$devices.device.type', - name: '$devices.device.name', - version: '$devices.device.version', - }, - count: { - $sum: '$devices.sessions', - }, - time: { - $sum: '$devices.time', - }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $project: { - _id: 0, - type: '$_id.type', - name: '$_id.name', - version: '$_id.version', - count: 1, - time: 1, - }, - }, - ], - { allowDiskUse: true }, - ) - .toArray(); - }, - - getUniqueDevicesOfYesterday( - collection: Collection, - { year, month, day }: DestructuredDate, - ): Promise { - return collection - .aggregate([ - { - $match: { - year, - month, - day, - type: 'user_daily', - }, - }, - { - $unwind: '$devices', - }, - { - $group: { - _id: { - type: '$devices.device.type', - name: '$devices.device.name', - version: '$devices.device.version', - }, - count: { - $sum: '$devices.sessions', - }, - time: { - $sum: '$devices.time', - }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $project: { - _id: 0, - type: '$_id.type', - name: '$_id.name', - version: '$_id.version', - count: 1, - time: 1, - }, - }, - ]) - .toArray(); - }, - - getUniqueOSOfLastMonthOrWeek( - collection: Collection, - { year, month, day, type = 'month' }: DestructuredDateWithType, - ): Promise { - return collection - .aggregate( - [ - { - $match: { - 'type': 'user_daily', - 'devices.device.os.name': { - $exists: true, - }, - ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }), - }, - }, - { - $unwind: '$devices', - }, - { - $group: { - _id: { - name: '$devices.device.os.name', - version: '$devices.device.os.version', - }, - count: { - $sum: '$devices.sessions', - }, - time: { - $sum: '$devices.time', - }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $project: { - _id: 0, - name: '$_id.name', - version: '$_id.version', - count: 1, - time: 1, - }, - }, - ], - { allowDiskUse: true }, - ) - .toArray(); - }, - - getUniqueOSOfYesterday(collection: Collection, { year, month, day }: DestructuredDate): Promise { - return collection - .aggregate([ - { - $match: { - year, - month, - day, - 'type': 'user_daily', - 'devices.device.os.name': { - $exists: true, - }, - }, - }, - { - $unwind: '$devices', - }, - { - $group: { - _id: { - name: '$devices.device.os.name', - version: '$devices.device.os.version', - }, - count: { - $sum: '$devices.sessions', - }, - time: { - $sum: '$devices.time', - }, - }, - }, - { - $sort: { - time: -1, - }, - }, - { - $project: { - _id: 0, - name: '$_id.name', - version: '$_id.version', - count: 1, - time: 1, - }, - }, - ]) - .toArray(); - }, -}; - -export class SessionsRaw extends BaseRaw { - private secondaryCollection: Collection; - - constructor(public readonly col: Collection, public readonly colSecondary: Collection, trash?: Collection) { - super(col, trash); - - this.secondaryCollection = colSecondary; - } - - protected modelIndexes(): IndexSpecification[] { - return [ - { key: { instanceId: 1, sessionId: 1, year: 1, month: 1, day: 1 } }, - { key: { instanceId: 1, sessionId: 1, userId: 1 } }, - { key: { instanceId: 1, sessionId: 1 } }, - { key: { sessionId: 1 } }, - { key: { userId: 1 } }, - { key: { year: 1, month: 1, day: 1, type: 1 } }, - { key: { type: 1 } }, - { key: { ip: 1, loginAt: 1 } }, - { key: { _computedAt: 1 }, expireAfterSeconds: 60 * 60 * 24 * 45 }, - ]; - } - - async getActiveUsersBetweenDates({ start, end }: DestructuredRange): Promise { - return this.col - .aggregate([ - { - $match: { - ...matchBasedOnDate(start, end), - type: 'user_daily', - }, - }, - { - $group: { - _id: '$userId', - }, - }, - ]) - .toArray(); - } - - async findLastLoginByIp(ip: string): Promise { - return this.findOne( - { - ip, - }, - { - sort: { loginAt: -1 }, - limit: 1, - }, - ); - } - - findOneBySessionId(sessionId: string): Promise { - return this.findOne({ sessionId }); - } - - findSessionsNotClosedByDateWithoutLastActivity({ year, month, day }: DestructuredDate): Cursor { - const query = { - year, - month, - day, - type: 'session', - closedAt: { $exists: false }, - lastActivityAt: { $exists: false }, - }; - - return this.find(query); - } - - async getActiveUsersOfPeriodByDayBetweenDates({ start, end }: DestructuredRange): Promise< - { - day: number; - month: number; - year: number; - usersList: IUser['_id'][]; - users: number; - }[] - > { - return this.col - .aggregate<{ - day: number; - month: number; - year: number; - usersList: IUser['_id'][]; - users: number; - }>([ - { - $match: { - ...matchBasedOnDate(start, end), - type: 'user_daily', - mostImportantRole: { $ne: 'anonymous' }, - }, - }, - { - $group: { - _id: { - day: '$day', - month: '$month', - year: '$year', - userId: '$userId', - }, - }, - }, - { - $group: { - _id: { - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', - }, - usersList: { - $addToSet: '$_id.userId', - }, - users: { $sum: 1 }, - }, - }, - { - $project: { - _id: 0, - ...getProjectionByFullDate(), - usersList: 1, - users: 1, - }, - }, - { - $sort: { - ...getSortByFullDate(), - }, - }, - ]) - .toArray(); - } - - async getBusiestTimeWithinHoursPeriod({ start, end, groupSize }: DateRange & { groupSize: number }): Promise< - { - hour: number; - users: number; - }[] - > { - const match = { - $match: { - type: 'computed-session', - loginAt: { $gte: start, $lte: end }, - }, - }; - const rangeProject = { - $project: { - range: { - $range: [0, 24, groupSize], - }, - session: '$$ROOT', - }, - }; - const unwind = { - $unwind: '$range', - }; - const groups = getGroupSessionsByHour('$range'); - const presentationProject = { - $project: { - _id: 0, - hour: '$_id', - users: 1, - }, - }; - const sort = { - $sort: { - hour: -1, - }, - }; - return this.col - .aggregate<{ - hour: number; - users: number; - }>([match, rangeProject, unwind, groups.listGroup, groups.countGroup, presentationProject, sort]) - .toArray(); - } - - async getTotalOfSessionsByDayBetweenDates({ start, end }: DestructuredRange): Promise< - { - day: number; - month: number; - year: number; - users: number; - }[] - > { - return this.col - .aggregate<{ - day: number; - month: number; - year: number; - users: number; - }>([ - { - $match: { - ...matchBasedOnDate(start, end), - type: 'user_daily', - mostImportantRole: { $ne: 'anonymous' }, - }, - }, - { - $group: { - _id: { year: '$year', month: '$month', day: '$day' }, - users: { $sum: 1 }, - }, - }, - { - $project: { - _id: 0, - ...getProjectionByFullDate(), - users: 1, - }, - }, - { - $sort: { - ...getSortByFullDate(), - }, - }, - ]) - .toArray(); - } - - async getTotalOfSessionByHourAndDayBetweenDates({ start, end }: DateRange): Promise< - { - hour: number; - day: number; - month: number; - year: number; - users: number; - }[] - > { - const match = { - $match: { - type: 'computed-session', - loginAt: { $gte: start, $lte: end }, - }, - }; - const rangeProject = { - $project: { - range: { - $range: [{ $hour: '$loginAt' }, { $sum: [{ $ifNull: [{ $hour: '$closedAt' }, 23] }, 1] }], - }, - session: '$$ROOT', - }, - }; - const unwind = { - $unwind: '$range', - }; - const groups = getGroupSessionsByHour({ - range: '$range', - day: '$session.day', - month: '$session.month', - year: '$session.year', - }); - const presentationProject = { - $project: { - _id: 0, - hour: '$_id.range', - ...getProjectionByFullDate(), - users: 1, - }, - }; - const sort = { - $sort: { - ...getSortByFullDate(), - hour: -1, - }, - }; - - return this.col - .aggregate<{ - hour: number; - day: number; - month: number; - year: number; - users: number; - }>([match, rangeProject, unwind, groups.listGroup, groups.countGroup, presentationProject, sort]) - .toArray(); - } - - async getUniqueUsersOfYesterday(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueUsersOfYesterday(this.secondaryCollection, { - year, - month, - day, - }), - }; - } - - async getUniqueUsersOfLastMonth(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueUsersOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - }), - }; - } - - async getUniqueUsersOfLastWeek(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueUsersOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - type: 'week', - }), - }; - } - - async getUniqueDevicesOfYesterday(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueDevicesOfYesterday(this.secondaryCollection, { - year, - month, - day, - }), - }; - } - - async getUniqueDevicesOfLastMonth(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueDevicesOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - }), - }; - } - - async getUniqueDevicesOfLastWeek(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueDevicesOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - type: 'week', - }), - }; - } - - async getUniqueOSOfYesterday(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueOSOfYesterday(this.secondaryCollection, { year, month, day }), - }; - } - - async getUniqueOSOfLastMonth(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueOSOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - }), - }; - } - - async getUniqueOSOfLastWeek(): Promise { - const date = new Date(); - date.setDate(date.getDate() - 1); - - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - return { - year, - month, - day, - data: await aggregates.getUniqueOSOfLastMonthOrWeek(this.secondaryCollection, { - year, - month, - day, - type: 'week', - }), - }; - } - - async createOrUpdate(data: Omit): Promise { - const { year, month, day, sessionId, instanceId } = data; - - if (!year || !month || !day || !sessionId || !instanceId) { - return; - } - - const now = new Date(); - - return this.updateOne( - { instanceId, sessionId, year, month, day }, - { - $set: data, - $setOnInsert: { - createdAt: now, - }, - }, - { upsert: true }, - ); - } - - async closeByInstanceIdAndSessionId(instanceId: string, sessionId: string): Promise { - const query = { - instanceId, - sessionId, - closedAt: { $exists: false }, - }; - - const closeTime = new Date(); - const update = { - $set: { - closedAt: closeTime, - lastActivityAt: closeTime, - }, - }; - - return this.updateOne(query, update); - } - - async updateActiveSessionsByDateAndInstanceIdAndIds( - { year, month, day }: Partial = {}, - instanceId: string, - sessions: string[], - data = {}, - ): Promise { - const query = { - instanceId, - year, - month, - day, - sessionId: { $in: sessions }, - closedAt: { $exists: false }, - }; - - const update = { - $set: data, - }; - - return this.updateMany(query, update); - } - - async updateActiveSessionsByDate({ year, month, day }: DestructuredDate, data = {}): Promise { - const query = { - year, - month, - day, - type: 'session', - closedAt: { $exists: false }, - lastActivityAt: { $exists: false }, - }; - - const update = { - $set: data, - }; - - return this.updateMany(query, update); - } - - async logoutByInstanceIdAndSessionIdAndUserId(instanceId: string, sessionId: string, userId: string): Promise { - const query = { - instanceId, - sessionId, - userId, - logoutAt: { $exists: 0 }, - }; - - const logoutAt = new Date(); - const update = { - $set: { - logoutAt, - }, - }; - - return this.updateMany(query, update); - } - - async createBatch(sessions: ModelOptionalId[]): Promise { - if (!sessions || sessions.length === 0) { - return; - } - - const ops: BulkWriteOperation[] = []; - sessions.forEach((doc) => { - const { year, month, day, sessionId, instanceId } = doc; - delete doc._id; - - ops.push({ - updateOne: { - filter: { year, month, day, sessionId, instanceId }, - update: { - $set: doc, - }, - upsert: true, - }, - }); - }); - - return this.col.bulkWrite(ops, { ordered: false }); - } -} diff --git a/apps/meteor/app/models/server/raw/Settings.ts b/apps/meteor/app/models/server/raw/Settings.ts deleted file mode 100644 index 90b047798767..000000000000 --- a/apps/meteor/app/models/server/raw/Settings.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Cursor, FilterQuery, UpdateQuery, WriteOpResult } from 'mongodb'; -import type { ISetting, ISettingColor, ISettingSelectOption } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class SettingsRaw extends BaseRaw { - async getValueById(_id: string): Promise { - const setting = await this.findOne>({ _id }, { projection: { value: 1 } }); - - return setting?.value; - } - - findNotHidden({ updatedAfter }: { updatedAfter?: Date } = {}): Cursor { - const query: FilterQuery = { - hidden: { $ne: true }, - }; - - if (updatedAfter) { - query._updatedAt = { $gt: updatedAfter }; - } - - return this.find(query); - } - - findOneNotHiddenById(_id: string): Promise { - const query = { - _id, - hidden: { $ne: true }, - }; - - return this.findOne(query); - } - - findByIds(_id: string[] | string = []): Cursor { - if (typeof _id === 'string') { - _id = [_id]; - } - - const query = { - _id: { - $in: _id, - }, - }; - - return this.find(query); - } - - updateValueById(_id: string, value: T): Promise { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - updateOptionsById(_id: ISetting['_id'], options: UpdateQuery['$set']): Promise { - const query = { - blocked: { $ne: true }, - _id, - }; - - const update = { $set: options }; - - return this.update(query, update); - } - - updateValueNotHiddenById(_id: ISetting['_id'], value: T): Promise { - const query = { - _id, - hidden: { $ne: true }, - blocked: { $ne: true }, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - updateValueAndEditorById( - _id: ISetting['_id'], - value: T, - editor: ISettingColor['editor'], - ): Promise { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - editor, - }, - }; - - return this.update(query, update); - } - - findNotHiddenPublic( - ids: ISetting['_id'][] = [], - ): Cursor< - T extends ISettingColor - ? Pick - : Pick - > { - const filter: FilterQuery = { - hidden: { $ne: true }, - public: true, - }; - - if (ids.length > 0) { - filter._id = { $in: ids }; - } - - return this.find(filter, { - projection: { - _id: 1, - value: 1, - editor: 1, - enterprise: 1, - invalidValue: 1, - modules: 1, - requiredOnWizard: 1, - }, - }); - } - - findSetupWizardSettings(): Cursor { - return this.find({ wizard: { $exists: true } }); - } - - addOptionValueById(_id: ISetting['_id'], option: ISettingSelectOption): Promise { - const query = { - blocked: { $ne: true }, - _id, - }; - - const { key, i18nLabel } = option; - const update = { - $addToSet: { - values: { - key, - i18nLabel, - }, - }, - }; - - return this.update(query, update); - } - - findNotHiddenPublicUpdatedAfter(updatedAt: Date): Cursor { - const filter = { - hidden: { $ne: true }, - public: true, - _updatedAt: { - $gt: updatedAt, - }, - }; - - return this.find(filter, { - projection: { - _id: 1, - value: 1, - editor: 1, - enterprise: 1, - invalidValue: 1, - modules: 1, - requiredOnWizard: 1, - }, - }); - } -} diff --git a/apps/meteor/app/models/server/raw/SmarshHistory.ts b/apps/meteor/app/models/server/raw/SmarshHistory.ts deleted file mode 100644 index 8cb860993e96..000000000000 --- a/apps/meteor/app/models/server/raw/SmarshHistory.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ISmarshHistory } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -type T = ISmarshHistory; - -export class SmarshHistoryRaw extends BaseRaw {} diff --git a/apps/meteor/app/models/server/raw/Statistics.ts b/apps/meteor/app/models/server/raw/Statistics.ts deleted file mode 100644 index 70768a77ce46..000000000000 --- a/apps/meteor/app/models/server/raw/Statistics.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IStats } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class StatisticsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { createdAt: -1 } }]; - } - - async findLast(): Promise { - const options = { - sort: { - createdAt: -1, - }, - limit: 1, - }; - const records = await this.find({}, options).toArray(); - return records?.[0]; - } -} diff --git a/apps/meteor/app/models/server/raw/Subscriptions.ts b/apps/meteor/app/models/server/raw/Subscriptions.ts deleted file mode 100644 index be58d90b50dd..000000000000 --- a/apps/meteor/app/models/server/raw/Subscriptions.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { FindOneOptions, Cursor, UpdateQuery, FilterQuery, UpdateWriteOpResult, Collection, WithoutProjection } from 'mongodb'; -import { compact } from 'lodash'; -import type { ISubscription, IRole, IUser, IRoom } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; -import { UsersRaw } from './Users'; - -type T = ISubscription; -export class SubscriptionsRaw extends BaseRaw { - constructor(public readonly col: Collection, private readonly models: { Users: UsersRaw }, trash?: Collection) { - super(col, trash); - } - - async getBadgeCount(uid: string): Promise { - const [result] = await this.col - .aggregate<{ total: number } | undefined>([ - { $match: { 'u._id': uid, 'archived': { $ne: true } } }, - { - $group: { - _id: 'total', - total: { $sum: '$unread' }, - }, - }, - ]) - .toArray(); - - return result?.total || 0; - } - - findOneByRoomIdAndUserId(rid: string, uid: string, options: FindOneOptions = {}): Promise { - const query = { - rid, - 'u._id': uid, - }; - - return this.findOne(query, options); - } - - findByUserIdAndRoomIds(userId: string, roomIds: Array, options: FindOneOptions = {}): Cursor { - const query = { - 'u._id': userId, - 'rid': { - $in: roomIds, - }, - }; - - return this.find(query, options); - } - - findByRoomIdAndNotUserId(roomId: string, userId: string, options: FindOneOptions = {}): Cursor { - const query = { - 'rid': roomId, - 'u._id': { - $ne: userId, - }, - }; - - return this.find(query, options); - } - - findByLivechatRoomIdAndNotUserId(roomId: string, userId: string, options: FindOneOptions = {}): Cursor { - const query = { - 'rid': roomId, - 'servedBy._id': { - $ne: userId, - }, - }; - - return this.find(query, options); - } - - countByRoomIdAndUserId(rid: string, uid: string | undefined): Promise { - const query = { - rid, - 'u._id': uid, - }; - - const cursor = this.find(query, { projection: { _id: 0 } }); - - return cursor.count(); - } - - async isUserInRole(uid: IUser['_id'], roleId: IRole['_id'], rid?: IRoom['_id']): Promise { - if (rid == null) { - return null; - } - - const query = { - 'u._id': uid, - rid, - 'roles': roleId, - }; - - return this.findOne(query, { projection: { roles: 1 } }); - } - - setAsReadByRoomIdAndUserId(rid: string, uid: string, alert = false, options: FindOneOptions = {}): ReturnType['update']> { - const query: FilterQuery = { - rid, - 'u._id': uid, - }; - - const update: UpdateQuery = { - $set: { - open: true, - alert, - unread: 0, - userMentions: 0, - groupMentions: 0, - ls: new Date(), - }, - }; - - return this.update(query, update, options); - } - - removeRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][], rid: IRoom['_id']): Promise { - const query = { - 'u._id': uid, - rid, - }; - - const update = { - $pullAll: { - roles, - }, - }; - - return this.updateOne(query, update); - } - - findUsersInRoles(roles: IRole['_id'][], rid: string | undefined): Promise>; - - findUsersInRoles( - roles: IRole['_id'][], - rid: string | undefined, - options: WithoutProjection>, - ): Promise>; - - findUsersInRoles

( - roles: IRole['_id'][], - rid: string | undefined, - options: FindOneOptions

, - ): Promise>; - - async findUsersInRoles

( - roles: IRole['_id'][], - rid: IRoom['_id'] | undefined, - options?: FindOneOptions

, - ): Promise> { - const query = { - roles: { $in: roles }, - ...(rid && { rid }), - }; - - const subscriptions = await this.find(query).toArray(); - - const users = compact(subscriptions.map((subscription) => subscription.u?._id).filter(Boolean)); - - return !options - ? this.models.Users.find({ _id: { $in: users } }) - : this.models.Users.find({ _id: { $in: users } } as FilterQuery, options); - } - - addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][], rid?: IRoom['_id']): Promise { - if (!Array.isArray(roles)) { - roles = [roles]; - process.env.NODE_ENV === 'development' && console.warn('[WARN] Subscriptions.addRolesByUserId: roles should be an array'); - } - - const query = { - 'u._id': uid, - rid, - }; - - const update = { - $addToSet: { - roles: { $each: roles }, - }, - }; - - return this.updateOne(query, update); - } - - async isUserInRoleScope(uid: IUser['_id'], rid?: IRoom['_id']): Promise { - const query = { - 'u._id': uid, - rid, - }; - - if (!rid) { - return false; - } - const options = { - fields: { _id: 1 }, - }; - - const found = await this.findOne(query, options); - return !!found; - } -} diff --git a/apps/meteor/app/models/server/raw/Team.ts b/apps/meteor/app/models/server/raw/Team.ts deleted file mode 100644 index 385dcea5deb4..000000000000 --- a/apps/meteor/app/models/server/raw/Team.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { WithoutProjection, FindOneOptions, Cursor, UpdateWriteOpResult, DeleteWriteOpResultObject, FilterQuery } from 'mongodb'; -import { ITeam, TEAM_TYPE } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class TeamRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { name: 1 }, unique: true }]; - } - - findByNames(names: Array): Cursor; - - findByNames(names: Array, options: WithoutProjection>): Cursor; - - findByNames

(names: Array, options: FindOneOptions

): Cursor

; - - findByNames

( - names: Array, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - if (options === undefined) { - return this.col.find({ name: { $in: names } }); - } - return this.col.find({ name: { $in: names } }, options); - } - - findByIds(ids: Array, query?: FilterQuery): Cursor; - - findByIds(ids: Array, options: WithoutProjection>, query?: FilterQuery): Cursor; - - findByIds

(ids: Array, options: FindOneOptions

, query?: FilterQuery): Cursor

; - - findByIds

( - ids: Array, - options?: undefined | WithoutProjection> | FindOneOptions

, - query?: FilterQuery, - ): Cursor

| Cursor { - if (options === undefined) { - return this.col.find({ _id: { $in: ids }, ...query }); - } - - return this.col.find({ _id: { $in: ids }, ...query }, options); - } - - findByIdsAndType(ids: Array, type: TEAM_TYPE): Cursor; - - findByIdsAndType(ids: Array, type: TEAM_TYPE, options: WithoutProjection>): Cursor; - - findByIdsAndType

(ids: Array, type: TEAM_TYPE, options: FindOneOptions

): Cursor

; - - findByIdsAndType

( - ids: Array, - type: TEAM_TYPE, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - if (options === undefined) { - return this.col.find({ _id: { $in: ids }, type }); - } - return this.col.find({ _id: { $in: ids }, type }, options); - } - - findByType(type: number): Cursor; - - findByType(type: number, options: WithoutProjection>): Cursor; - - findByType

(type: number, options: FindOneOptions

): Cursor

; - - findByType

( - type: number, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor | Cursor

{ - if (options === undefined) { - return this.col.find({ type }, options); - } - return this.col.find({ type }, options); - } - - findByNameAndTeamIds(name: string | RegExp, teamIds: Array): Cursor; - - findByNameAndTeamIds(name: string | RegExp, teamIds: Array, options: WithoutProjection>): Cursor; - - findByNameAndTeamIds

(name: string | RegExp, teamIds: Array, options: FindOneOptions

): Cursor

; - - findByNameAndTeamIds

( - name: string | RegExp, - teamIds: Array, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - if (options === undefined) { - return this.col.find({ - name, - $or: [ - { - type: 0, - }, - { - _id: { - $in: teamIds, - }, - }, - ], - }); - } - return this.col.find( - { - name, - $or: [ - { - type: 0, - }, - { - _id: { - $in: teamIds, - }, - }, - ], - }, - options, - ); - } - - findOneByName(name: string | RegExp): Promise; - - findOneByName(name: string | RegExp, options: WithoutProjection>): Promise; - - findOneByName

(name: string | RegExp, options: FindOneOptions

): Promise

; - - findOneByName

( - name: string | RegExp, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Promise

| Promise { - if (options === undefined) { - return this.col.findOne({ name }); - } - return this.col.findOne({ name }, options); - } - - findOneByMainRoomId(roomId: string): Promise; - - findOneByMainRoomId(roomId: string, options: WithoutProjection>): Promise; - - findOneByMainRoomId

(roomId: string, options: FindOneOptions

): Promise

; - - findOneByMainRoomId

( - roomId: string, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Promise

| Promise { - return options ? this.col.findOne({ roomId }, options) : this.col.findOne({ roomId }); - } - - updateMainRoomForTeam(id: string, roomId: string): Promise { - return this.updateOne( - { - _id: id, - }, - { - $set: { - roomId, - }, - }, - ); - } - - deleteOneById(id: string): Promise { - return this.col.deleteOne({ - _id: id, - }); - } - - deleteOneByName(name: string): Promise { - return this.col.deleteOne({ name }); - } - - updateNameAndType(teamId: string, nameAndType: { name?: string; type?: TEAM_TYPE }): Promise { - const query = { - _id: teamId, - }; - - const update = { - $set: {}, - }; - - if (nameAndType.name) { - Object.assign(update.$set, { name: nameAndType.name }); - } - - if (typeof nameAndType.type !== 'undefined') { - Object.assign(update.$set, { type: nameAndType.type }); - } - - return this.updateOne(query, update); - } -} diff --git a/apps/meteor/app/models/server/raw/TeamMember.ts b/apps/meteor/app/models/server/raw/TeamMember.ts deleted file mode 100644 index d2ac14c03c7f..000000000000 --- a/apps/meteor/app/models/server/raw/TeamMember.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { - WithoutProjection, - FindOneOptions, - Cursor, - InsertOneWriteOpResult, - UpdateWriteOpResult, - DeleteWriteOpResultObject, - FilterQuery, -} from 'mongodb'; -import type { ITeamMember, IUser, IRole } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = ITeamMember; -export class TeamMemberRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [ - { - key: { teamId: 1 }, - }, - { - key: { teamId: 1, userId: 1 }, - unique: true, - }, - ]; - } - - findByUserId(userId: string): Cursor; - - findByUserId(userId: string, options: WithoutProjection>): Cursor; - - findByUserId

(userId: string, options: FindOneOptions

): Cursor

; - - findByUserId

( - userId: string, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - return options ? this.col.find({ userId }, options) : this.col.find({ userId }, options); - } - - findOneByUserIdAndTeamId(userId: string, teamId: string): Promise; - - findOneByUserIdAndTeamId( - userId: string, - teamId: string, - options: WithoutProjection>, - ): Promise; - - findOneByUserIdAndTeamId

(userId: string, teamId: string, options: FindOneOptions

): Promise

; - - findOneByUserIdAndTeamId

( - userId: string, - teamId: string, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Promise

{ - return options ? this.col.findOne({ userId, teamId }, options) : this.col.findOne({ userId, teamId }, options); - } - - findByTeamId(teamId: string): Cursor; - - findByTeamId(teamId: string, options: WithoutProjection>): Cursor; - - findByTeamId

(teamId: string, options: FindOneOptions

): Cursor

; - - findByTeamId

( - teamId: string, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - return options ? this.col.find({ teamId }, options) : this.col.find({ teamId }, options); - } - - findByTeamIds(teamIds: Array): Cursor; - - findByTeamIds(teamIds: Array, options: WithoutProjection>): Cursor; - - findByTeamIds

(teamIds: Array, options: FindOneOptions

): Cursor

; - - findByTeamIds

( - teamIds: Array, - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - return options ? this.col.find({ teamId: { $in: teamIds } }, options) : this.col.find({ teamId: { $in: teamIds } }, options); - } - - findByTeamIdAndRole(teamId: string, role: IRole['_id']): Cursor; - - findByTeamIdAndRole(teamId: string, role: IRole['_id'], options: WithoutProjection>): Cursor; - - findByTeamIdAndRole

(teamId: string, role: IRole['_id'], options: FindOneOptions

): Cursor

; - - findByTeamIdAndRole

( - teamId: string, - role: IRole['_id'], - options?: undefined | WithoutProjection> | FindOneOptions

, - ): Cursor

| Cursor { - return options ? this.col.find({ teamId, roles: role }, options) : this.col.find({ teamId, roles: role }); - } - - findByUserIdAndTeamIds(userId: string, teamIds: Array, options: FindOneOptions = {}): Cursor { - const query = { - userId, - teamId: { - $in: teamIds, - }, - }; - - return this.col.find(query, options); - } - - findMembersInfoByTeamId(teamId: string, limit: number, skip: number, query?: FilterQuery): Cursor { - return this.col.find({ ...query, teamId }, { - limit, - skip, - projection: { - userId: 1, - roles: 1, - createdBy: 1, - createdAt: 1, - }, - } as FindOneOptions); - } - - updateOneByUserIdAndTeamId(userId: string, teamId: string, update: Partial): Promise { - return this.updateOne({ userId, teamId }, { $set: update }); - } - - createOneByTeamIdAndUserId( - teamId: string, - userId: string, - createdBy: Pick, - ): Promise> { - return this.insertOne({ - teamId, - userId, - createdAt: new Date(), - createdBy, - }); - } - - updateRolesByTeamIdAndUserId(teamId: string, userId: string, roles: Array): Promise { - return this.updateOne( - { - teamId, - userId, - }, - { - $addToSet: { - roles: { $each: roles }, - }, - }, - ); - } - - removeRolesByTeamIdAndUserId(teamId: string, userId: string, roles: Array): Promise { - return this.updateOne( - { - teamId, - userId, - }, - { - $pull: { - roles: { $in: roles }, - }, - }, - ); - } - - deleteByUserIdAndTeamId(userId: string, teamId: string): Promise { - return this.col.deleteOne({ - teamId, - userId, - }); - } - - deleteByTeamId(teamId: string): Promise { - return this.col.deleteMany({ - teamId, - }); - } -} diff --git a/apps/meteor/app/models/server/raw/Uploads.ts b/apps/meteor/app/models/server/raw/Uploads.ts deleted file mode 100644 index f9f227277240..000000000000 --- a/apps/meteor/app/models/server/raw/Uploads.ts +++ /dev/null @@ -1,129 +0,0 @@ -// TODO: Lib imports should not exists inside the raw models -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { - CollectionInsertOneOptions, - Cursor, - DeleteWriteOpResultObject, - FilterQuery, - InsertOneWriteOpResult, - UpdateOneOptions, - UpdateQuery, - UpdateWriteOpResult, - WithId, - WriteOpResult, -} from 'mongodb'; -import { IUpload as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification, InsertionModel } from './BaseRaw'; - -const fillTypeGroup = (fileData: Partial): void => { - if (!fileData.type) { - return; - } - - fileData.typeGroup = fileData.type.split('/').shift(); -}; - -export class UploadsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { rid: 1 } }, { key: { uploadedAt: 1 } }, { key: { typeGroup: 1 } }]; - } - - findNotHiddenFilesOfRoom(roomId: string, searchText: string, fileType: string, limit: number): Cursor { - const fileQuery = { - rid: roomId, - complete: true, - uploading: false, - _hidden: { - $ne: true, - }, - - ...(searchText && { name: { $regex: new RegExp(escapeRegExp(searchText), 'i') } }), - ...(fileType && fileType !== 'all' && { typeGroup: fileType }), - }; - - const fileOptions = { - limit, - sort: { - uploadedAt: -1, - }, - projection: { - _id: 1, - userId: 1, - rid: 1, - name: 1, - description: 1, - type: 1, - url: 1, - uploadedAt: 1, - typeGroup: 1, - }, - }; - - return this.find(fileQuery, fileOptions); - } - - insert(fileData: InsertionModel, options?: CollectionInsertOneOptions): Promise>> { - fillTypeGroup(fileData); - return super.insertOne(fileData, options); - } - - update( - filter: FilterQuery, - update: UpdateQuery | Partial, - options?: UpdateOneOptions & { multi?: boolean }, - ): Promise { - if ('$set' in update && update.$set) { - fillTypeGroup(update.$set); - } else if ('type' in update && update.type) { - fillTypeGroup(update); - } - - return super.update(filter, update, options); - } - - async insertFileInit(userId: string, store: string, file: { name: string }, extra: object): Promise>> { - const fileData = { - userId, - store, - complete: false, - uploading: true, - progress: 0, - extension: file.name.split('.').pop(), - uploadedAt: new Date(), - ...file, - ...extra, - }; - - fillTypeGroup(fileData); - return this.insert(fileData); - } - - async updateFileComplete(fileId: string, userId: string, file: object): Promise { - if (!fileId) { - return; - } - - const filter = { - _id: fileId, - userId, - }; - - const update = { - $set: { - complete: true, - uploading: false, - progress: 1, - }, - }; - - update.$set = Object.assign(file, update.$set); - - fillTypeGroup(update.$set); - return this.updateOne(filter, update); - } - - async deleteFile(fileId: string): Promise { - return this.deleteOne({ _id: fileId }); - } -} diff --git a/apps/meteor/app/models/server/raw/UserDataFiles.ts b/apps/meteor/app/models/server/raw/UserDataFiles.ts deleted file mode 100644 index 3e441346ca0f..000000000000 --- a/apps/meteor/app/models/server/raw/UserDataFiles.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { FindOneOptions, InsertOneWriteOpResult, WithId, WithoutProjection } from 'mongodb'; -import { IUserDataFile as T } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -export class UserDataFilesRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { userId: 1 } }]; - } - - findLastFileByUser(userId: string, options: WithoutProjection> = {}): Promise { - const query = { - userId, - }; - - options.sort = { _updatedAt: -1 }; - return this.findOne(query, options); - } - - // INSERT - create(data: T): Promise>> { - const userDataFile = { - createdAt: new Date(), - ...data, - }; - - return this.insertOne(userDataFile); - } -} diff --git a/apps/meteor/app/models/server/raw/UsersSessions.ts b/apps/meteor/app/models/server/raw/UsersSessions.ts deleted file mode 100644 index f91616f83c9f..000000000000 --- a/apps/meteor/app/models/server/raw/UsersSessions.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IUserSession } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export class UsersSessionsRaw extends BaseRaw { - clearConnectionsFromInstanceId(instanceId: string[]): ReturnType['updateMany']> { - return this.col.updateMany( - {}, - { - $pull: { - connections: { - instanceId: { - $nin: instanceId, - }, - }, - }, - }, - ); - } -} diff --git a/apps/meteor/app/models/server/raw/VoipRooms.ts b/apps/meteor/app/models/server/raw/VoipRooms.ts deleted file mode 100644 index b552cae20c38..000000000000 --- a/apps/meteor/app/models/server/raw/VoipRooms.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { FilterQuery, WithoutProjection, FindOneOptions, WriteOpResult, Cursor } from 'mongodb'; -import type { IVoipRoom, IRoomClosingInfo } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; -import { Logger } from '../../../../server/lib/logger/Logger'; - -export class VoipRoomsRaw extends BaseRaw { - logger = new Logger('VoipRoomsRaw'); - - async findOneOpenByVisitorToken(visitorToken: string, options: FindOneOptions = {}): Promise { - const query: FilterQuery = { - 't': 'v', - 'open': true, - 'v.token': visitorToken, - }; - return this.findOne(query, options); - } - - findOpenByAgentId(agentId: string): Cursor { - return this.find({ - 't': 'v', - 'open': true, - 'servedBy._id': agentId, - }); - } - - async findOneByAgentId(agentId: string): Promise { - return this.findOne({ - 't': 'v', - 'open': true, - 'servedBy._id': agentId, - }); - } - - async findOneVoipRoomById(id: string, options: WithoutProjection> = {}): Promise { - const query: FilterQuery = { - t: 'v', - _id: id, - }; - return this.findOne(query, options); - } - - async findOneOpenByRoomIdAndVisitorToken( - roomId: string, - visitorToken: string, - options: FindOneOptions = {}, - ): Promise { - const query: FilterQuery = { - 't': 'v', - '_id': roomId, - 'open': true, - 'v.token': visitorToken, - }; - return this.findOne(query, options); - } - - async findOneByVisitorToken(visitorToken: string, options: FindOneOptions = {}): Promise { - const query: FilterQuery = { - 't': 'v', - 'v.token': visitorToken, - }; - return this.findOne(query, options); - } - - async findOneByIdAndVisitorToken( - _id: IVoipRoom['_id'], - visitorToken: string, - options: FindOneOptions = {}, - ): Promise { - const query: FilterQuery = { - 't': 'v', - _id, - 'v.token': visitorToken, - }; - return this.findOne(query, options); - } - - closeByRoomId(roomId: IVoipRoom['_id'], closeInfo: IRoomClosingInfo): Promise { - const { closer, closedBy, closedAt, callDuration, serviceTimeDuration, ...extraData } = closeInfo; - - return this.update( - { - _id: roomId, - t: 'v', - }, - { - $set: { - closer, - closedBy, - closedAt, - callDuration, - 'metrics.serviceTimeDuration': serviceTimeDuration, - 'v.status': 'offline', - ...extraData, - }, - $unset: { - open: 1, - }, - }, - ); - } - - findRoomsWithCriteria({ - agents, - open, - createdAt, - closedAt, - tags, - queue, - visitorId, - options = {}, - }: { - agents?: string[]; - open?: boolean; - createdAt?: { start?: string; end?: string }; - closedAt?: { start?: string; end?: string }; - tags?: string[]; - queue?: string; - visitorId?: string; - options?: { - sort?: Record; - count?: number; - fields?: Record; - offset?: number; - }; - }): Cursor { - const query: FilterQuery = { - t: 'v', - }; - - if (agents) { - query.$or = [{ 'servedBy._id': { $in: agents } }, { 'servedBy.username': { $in: agents } }]; - } - if (open !== undefined) { - query.open = { $exists: open }; - } - if (visitorId && visitorId !== 'undefined') { - query['v._id'] = visitorId; - } - if (createdAt && Object.keys(createdAt).length) { - query.ts = {}; - if (createdAt.start) { - query.ts.$gte = new Date(createdAt.start); - } - if (createdAt.end) { - query.ts.$lte = new Date(createdAt.end); - } - } - if (closedAt && Object.keys(closedAt).length) { - query.closedAt = {}; - if (closedAt.start) { - query.closedAt.$gte = new Date(closedAt.start); - } - if (closedAt.end) { - query.closedAt.$lte = new Date(closedAt.end); - } - } - if (tags) { - query.tags = { $in: tags }; - } - if (queue) { - query.queue = queue; - } - - return this.find(query, { - sort: options.sort || { name: 1 }, - skip: options.offset, - limit: options.count, - }); - } -} diff --git a/apps/meteor/app/models/server/raw/WebdavAccounts.ts b/apps/meteor/app/models/server/raw/WebdavAccounts.ts deleted file mode 100644 index 1e6d635711db..000000000000 --- a/apps/meteor/app/models/server/raw/WebdavAccounts.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Webdav Accounts model - */ -import type { FindOneOptions, Cursor, DeleteWriteOpResultObject } from 'mongodb'; -import type { IWebdavAccount } from '@rocket.chat/core-typings'; - -import { BaseRaw, IndexSpecification } from './BaseRaw'; - -type T = IWebdavAccount; - -export class WebdavAccountsRaw extends BaseRaw { - protected modelIndexes(): IndexSpecification[] { - return [{ key: { userId: 1 } }]; - } - - findOneByIdAndUserId(_id: string, userId: string, options: FindOneOptions): Promise { - return this.findOne({ _id, userId }, options); - } - - findOneByUserIdServerUrlAndUsername( - { - userId, - serverURL, - username, - }: { - userId: string; - serverURL: string; - username: string; - }, - options: FindOneOptions, - ): Promise { - return this.findOne({ userId, serverURL, username }, options); - } - - findWithUserId(userId: string, options: FindOneOptions): Cursor { - const query = { userId }; - return this.find(query, options); - } - - removeByUserAndId(_id: string, userId: string): Promise { - return this.deleteOne({ _id, userId }); - } -} diff --git a/apps/meteor/app/models/server/raw/_Users.d.ts b/apps/meteor/app/models/server/raw/_Users.d.ts deleted file mode 100644 index 573117e868fb..000000000000 --- a/apps/meteor/app/models/server/raw/_Users.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { UpdateWriteOpResult } from 'mongodb'; -import type { IRole, IUser } from '@rocket.chat/core-typings'; - -import { BaseRaw } from './BaseRaw'; - -export interface IUserRaw extends BaseRaw { - isUserInRole(uid: IUser['_id'], roleId: IRole['_id']): Promise; - removeRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][]): Promise; - findUsersInRoles(roles: IRole['_id'][]): Promise; - addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][]): Promise; - isUserInRoleScope(uid: IUser['_id']): Promise; - new (...args: any): IUser; -} -export const UsersRaw: IUserRaw; diff --git a/apps/meteor/app/models/server/raw/index.ts b/apps/meteor/app/models/server/raw/index.ts deleted file mode 100644 index 5213d78ffcf3..000000000000 --- a/apps/meteor/app/models/server/raw/index.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { MongoInternals } from 'meteor/mongo'; - -import { AvatarsRaw } from './Avatars'; -import { AnalyticsRaw } from './Analytics'; -import { api } from '../../../../server/sdk/api'; -import { BaseDbWatch, trash } from '../models/_BaseDb'; -import { CredentialTokensRaw } from './CredentialTokens'; -import { CustomSoundsRaw } from './CustomSounds'; -import { CustomUserStatusRaw } from './CustomUserStatus'; -import { EmailInboxRaw } from './EmailInbox'; -import { EmailMessageHistoryRaw } from './EmailMessageHistory'; -import { EmojiCustomRaw } from './EmojiCustom'; -import { ExportOperationsRaw } from './ExportOperations'; -import { FederationKeysRaw } from './FederationKeys'; -import { FederationServersRaw } from './FederationServers'; -import { ImportDataRaw } from './ImportData'; -import { initWatchers } from '../../../../server/modules/watchers/watchers.module'; -import { InstanceStatusRaw } from './InstanceStatus'; -import { IntegrationHistoryRaw } from './IntegrationHistory'; -import { IntegrationsRaw } from './Integrations'; -import { InvitesRaw } from './Invites'; -import { LivechatAgentActivityRaw } from './LivechatAgentActivity'; -import { LivechatBusinessHoursRaw } from './LivechatBusinessHours'; -import { LivechatCustomFieldRaw } from './LivechatCustomField'; -import { LivechatDepartmentAgentsRaw } from './LivechatDepartmentAgents'; -import { LivechatDepartmentRaw } from './LivechatDepartment'; -import { LivechatInquiryRaw } from './LivechatInquiry'; -import { LivechatRoomsRaw } from './LivechatRooms'; -import { LivechatTriggerRaw } from './LivechatTrigger'; -import { LivechatVisitorsRaw } from './LivechatVisitors'; -import { LoginServiceConfigurationRaw } from './LoginServiceConfiguration'; -import { MessagesRaw } from './Messages'; -import { NotificationQueueRaw } from './NotificationQueue'; -import { OAuthAppsRaw } from './OAuthApps'; -import { OEmbedCacheRaw } from './OEmbedCache'; -import { OmnichannelQueueRaw } from './OmnichannelQueue'; -import { PermissionsRaw } from './Permissions'; -import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; -import { ReadReceiptsRaw } from './ReadReceipts'; -import { ReportsRaw } from './Reports'; -import { RolesRaw } from './Roles'; -import { RoomsRaw } from './Rooms'; -import { ServerEventsRaw } from './ServerEvents'; -import { SessionsRaw } from './Sessions'; -import { SettingsRaw } from './Settings'; -import { SmarshHistoryRaw } from './SmarshHistory'; -import { StatisticsRaw } from './Statistics'; -import { SubscriptionsRaw } from './Subscriptions'; -import { UsersRaw } from './Users'; -import { UsersSessionsRaw } from './UsersSessions'; -import { UserDataFilesRaw } from './UserDataFiles'; -import { UploadsRaw } from './Uploads'; -import { WebdavAccountsRaw } from './WebdavAccounts'; -import { VoipRoomsRaw } from './VoipRooms'; -import ImportDataModel from '../models/ImportData'; -import LivechatBusinessHoursModel from '../models/LivechatBusinessHours'; -import LivechatCustomFieldModel from '../models/LivechatCustomField'; -import LivechatDepartmentAgentsModel from '../models/LivechatDepartmentAgents'; -import LivechatDepartmentModel from '../models/LivechatDepartment'; -import LivechatInquiryModel from '../models/LivechatInquiry'; -import LivechatRoomsModel from '../models/LivechatRooms'; -import LivechatVisitorsModel from '../models/LivechatVisitors'; -import MessagesModel from '../models/Messages'; -import OmnichannelQueueModel from '../models/OmnichannelQueue'; -import RoomsModel from '../models/Rooms'; -import SettingsModel from '../models/Settings'; -import SubscriptionsModel from '../models/Subscriptions'; -import UsersModel from '../models/Users'; -import { PbxEventsRaw } from './PbxEvents'; -import { isRunningMs } from '../../../../server/lib/isRunningMs'; - -const trashCollection = trash.rawCollection(); - -export const Users = new UsersRaw(UsersModel.model.rawCollection(), trashCollection); -export const Subscriptions = new SubscriptionsRaw(SubscriptionsModel.model.rawCollection(), { Users }, trashCollection); -export const Settings = new SettingsRaw(SettingsModel.model.rawCollection(), trashCollection); -export const Rooms = new RoomsRaw(RoomsModel.model.rawCollection(), trashCollection); -export const LivechatCustomField = new LivechatCustomFieldRaw(LivechatCustomFieldModel.model.rawCollection(), trashCollection); -export const LivechatDepartment = new LivechatDepartmentRaw(LivechatDepartmentModel.model.rawCollection(), trashCollection); -export const LivechatDepartmentAgents = new LivechatDepartmentAgentsRaw( - LivechatDepartmentAgentsModel.model.rawCollection(), - trashCollection, -); -export const LivechatRooms = new LivechatRoomsRaw(LivechatRoomsModel.model.rawCollection(), trashCollection); -export const Messages = new MessagesRaw(MessagesModel.model.rawCollection(), trashCollection); -export const LivechatVisitors = new LivechatVisitorsRaw(LivechatVisitorsModel.model.rawCollection(), trashCollection); -export const LivechatInquiry = new LivechatInquiryRaw(LivechatInquiryModel.model.rawCollection(), trashCollection); -export const LivechatBusinessHours = new LivechatBusinessHoursRaw(LivechatBusinessHoursModel.model.rawCollection(), trashCollection); -// export const Roles = new RolesRaw(RolesModel.model.rawCollection(), { Users, Subscriptions }, trashCollection); -export const OmnichannelQueue = new OmnichannelQueueRaw(OmnichannelQueueModel.model.rawCollection(), trashCollection); -export const ImportData = new ImportDataRaw(ImportDataModel.model.rawCollection(), trashCollection); - -const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo; -const prefix = 'rocketchat_'; - -export const Avatars = new AvatarsRaw(db.collection(`${prefix}avatars`), trashCollection); -export const Analytics = new AnalyticsRaw( - db.collection(`${prefix}analytics`, { readPreference: readSecondaryPreferred(db) }), - trashCollection, -); -export const CustomSounds = new CustomSoundsRaw(db.collection(`${prefix}custom_sounds`), trashCollection); -export const CustomUserStatus = new CustomUserStatusRaw(db.collection(`${prefix}custom_user_status`), trashCollection); -export const CredentialTokens = new CredentialTokensRaw(db.collection(`${prefix}credential_tokens`), trashCollection); -export const EmailInbox = new EmailInboxRaw(db.collection(`${prefix}email_inbox`), trashCollection); -export const EmailMessageHistory = new EmailMessageHistoryRaw(db.collection(`${prefix}email_message_history`), trashCollection); -export const EmojiCustom = new EmojiCustomRaw(db.collection(`${prefix}custom_emoji`), trashCollection); -export const ExportOperations = new ExportOperationsRaw(db.collection(`${prefix}export_operations`), trashCollection); -export const FederationKeys = new FederationKeysRaw(db.collection(`${prefix}federation_keys`), trashCollection); -export const FederationServers = new FederationServersRaw(db.collection(`${prefix}federation_servers`), trashCollection); -export const InstanceStatus = new InstanceStatusRaw(db.collection('instances'), trashCollection, { - preventSetUpdatedAt: true, -}); -export const Integrations = new IntegrationsRaw(db.collection(`${prefix}integrations`), trashCollection); -export const IntegrationHistory = new IntegrationHistoryRaw(db.collection(`${prefix}integration_history`), trashCollection); -export const Invites = new InvitesRaw(db.collection(`${prefix}invites`), trashCollection); -export const LivechatTrigger = new LivechatTriggerRaw(db.collection(`${prefix}livechat_trigger`), trashCollection); -export const LoginServiceConfiguration = new LoginServiceConfigurationRaw( - db.collection('meteor_accounts_loginServiceConfiguration'), - trashCollection, - { preventSetUpdatedAt: true }, -); - -export const NotificationQueue = new NotificationQueueRaw(db.collection(`${prefix}notification_queue`), trashCollection); -export const OAuthApps = new OAuthAppsRaw(db.collection(`${prefix}oauth_apps`), trashCollection); -export const OEmbedCache = new OEmbedCacheRaw(db.collection(`${prefix}oembed_cache`), trashCollection); -export const Permissions = new PermissionsRaw(db.collection(`${prefix}permissions`), trashCollection); -export const ReadReceipts = new ReadReceiptsRaw(db.collection(`${prefix}read_receipts`), trashCollection); -export const Reports = new ReportsRaw(db.collection(`${prefix}reports`), trashCollection); -export const ServerEvents = new ServerEventsRaw(db.collection(`${prefix}server_events`), trashCollection); -export const Sessions = new SessionsRaw( - db.collection(`${prefix}sessions`), - db.collection(`${prefix}sessions`, { readPreference: readSecondaryPreferred(db) }), - trashCollection, -); -export const Roles = new RolesRaw(db.collection(`${prefix}roles`), { Users, Subscriptions }, trashCollection); -export const SmarshHistory = new SmarshHistoryRaw(db.collection(`${prefix}smarsh_history`), trashCollection); -export const Statistics = new StatisticsRaw(db.collection(`${prefix}statistics`), trashCollection); -export const UsersSessions = new UsersSessionsRaw(db.collection('usersSessions'), trashCollection, { - preventSetUpdatedAt: true, -}); -export const UserDataFiles = new UserDataFilesRaw(db.collection(`${prefix}user_data_files`), trashCollection); -export const Uploads = new UploadsRaw(db.collection(`${prefix}uploads`), trashCollection); -export const WebdavAccounts = new WebdavAccountsRaw(db.collection(`${prefix}webdav_accounts`), trashCollection); -export const VoipRoom = new VoipRoomsRaw(db.collection(`${prefix}room`), trashCollection); -export const PbxEvent = new PbxEventsRaw(db.collection('pbx_events'), trashCollection); -export const LivechatAgentActivity = new LivechatAgentActivityRaw(db.collection(`${prefix}livechat_agent_activity`), trashCollection); - -const map = { - [Messages.col.collectionName]: MessagesModel, - [Users.col.collectionName]: UsersModel, - [Subscriptions.col.collectionName]: SubscriptionsModel, - [Settings.col.collectionName]: SettingsModel, - [LivechatInquiry.col.collectionName]: LivechatInquiryModel, - [LivechatDepartmentAgents.col.collectionName]: LivechatDepartmentAgentsModel, - [Rooms.col.collectionName]: RoomsModel, -}; - -if (!isRunningMs()) { - const models = { - Messages, - Users, - Subscriptions, - Settings, - LivechatInquiry, - LivechatDepartmentAgents, - UsersSessions, - Permissions, - Roles, - Rooms, - LoginServiceConfiguration, - InstanceStatus, - IntegrationHistory, - Integrations, - EmailInbox, - PbxEvent, - }; - - initWatchers(models, api.broadcastLocal.bind(api), (model, fn) => { - const meteorModel = map[model.col.collectionName] || new BaseDbWatch(model.col.collectionName); - if (!meteorModel) { - return; - } - - meteorModel.on('change', fn); - }); -} diff --git a/apps/meteor/app/notification-queue/server/NotificationQueue.ts b/apps/meteor/app/notification-queue/server/NotificationQueue.ts index b6c17a60c1ab..d46f261f0143 100644 --- a/apps/meteor/app/notification-queue/server/NotificationQueue.ts +++ b/apps/meteor/app/notification-queue/server/NotificationQueue.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { INotification, INotificationItemPush, INotificationItemEmail, NotificationItem } from '@rocket.chat/core-typings'; import type { IUser } from '@rocket.chat/core-typings'; +import { NotificationQueue, Users } from '@rocket.chat/models'; -import { NotificationQueue, Users } from '../../models/server/raw'; import { sendEmailFromData } from '../../lib/server/functions/notifications/email'; import { PushNotification } from '../../push-notifications/server'; import { SystemLogger } from '../../../server/lib/logger/system'; @@ -83,7 +83,7 @@ class NotificationClass { NotificationQueue.removeById(notification._id); } catch (e) { SystemLogger.error(e); - await NotificationQueue.setErrorById(notification._id, e.message); + await NotificationQueue.setErrorById(notification._id, e instanceof Error ? e.message : String(e)); } if (counter >= this.maxBatchSize) { @@ -92,7 +92,7 @@ class NotificationClass { this.worker(counter++); } - getNextNotification(): Promise { + getNextNotification(): Promise { const expired = new Date(); expired.setMinutes(expired.getMinutes() - 5); diff --git a/apps/meteor/app/notifications/server/lib/Notifications.ts b/apps/meteor/app/notifications/server/lib/Notifications.ts index f1f6d46ab13f..67260293cdbe 100644 --- a/apps/meteor/app/notifications/server/lib/Notifications.ts +++ b/apps/meteor/app/notifications/server/lib/Notifications.ts @@ -4,12 +4,6 @@ import { DDPCommon } from 'meteor/ddp-common'; import { NotificationsModule } from '../../../../server/modules/notifications/notifications.module'; import { Streamer } from '../../../../server/modules/streamer/streamer.module'; import { api } from '../../../../server/sdk/api'; -import { - Subscriptions as SubscriptionsRaw, - Rooms as RoomsRaw, - Users as UsersRaw, - Settings as SettingsRaw, -} from '../../../models/server/raw'; import './Presence'; export class Stream extends Streamer { @@ -35,12 +29,7 @@ export class Stream extends Streamer { const notifications = new NotificationsModule(Stream); -notifications.configure({ - Rooms: RoomsRaw, - Subscriptions: SubscriptionsRaw, - Users: UsersRaw, - Settings: SettingsRaw, -}); +notifications.configure(); notifications.streamLocal.on('broadcast', ({ eventName, args }) => { api.broadcastLocal(eventName, ...args); diff --git a/apps/meteor/app/notifications/server/lib/Presence.ts b/apps/meteor/app/notifications/server/lib/Presence.ts index a17f5cf9b206..f6746ccbbbcd 100644 --- a/apps/meteor/app/notifications/server/lib/Presence.ts +++ b/apps/meteor/app/notifications/server/lib/Presence.ts @@ -69,6 +69,7 @@ export class UserPresence { } export class StreamPresence { + // eslint-disable-next-line @typescript-eslint/naming-convention static getInstance(Streamer: IStreamerConstructor, name = 'user-presence'): IStreamer { return new (class StreamPresence extends Streamer { async _publish( diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.js b/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.js index fecb7b41b666..d330da92ae32 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.js +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import _ from 'underscore'; +import { OAuthApps } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization'; import { Users } from '../../../../models/server'; -import { OAuthApps } from '../../../../models/server/raw'; import { parseUriList } from '../functions/parseUriList'; Meteor.methods({ diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.js b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.js index 4bd24819b12f..7af1a14c3d59 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.js +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { OAuthApps } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization'; -import { OAuthApps } from '../../../../models/server/raw'; Meteor.methods({ async deleteOAuthApp(applicationId) { diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.js b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.js index 79838a227ab7..74742d07ad1c 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.js +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { OAuthApps } from '@rocket.chat/models'; import { hasPermission } from '../../../../authorization'; -import { OAuthApps } from '../../../../models/server/raw'; import { Users } from '../../../../models/server'; import { parseUriList } from '../functions/parseUriList'; diff --git a/apps/meteor/app/oauth2-server-config/server/oauth/default-services.ts b/apps/meteor/app/oauth2-server-config/server/oauth/default-services.ts index 77277b7ddc3b..68d05ef7fa72 100644 --- a/apps/meteor/app/oauth2-server-config/server/oauth/default-services.ts +++ b/apps/meteor/app/oauth2-server-config/server/oauth/default-services.ts @@ -1,4 +1,4 @@ -import { OAuthApps } from '../../../models/server/raw'; +import { OAuthApps } from '@rocket.chat/models'; async function run(): Promise { if (!(await OAuthApps.findOneById('zapier'))) { diff --git a/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts b/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts index e27805458f55..d9bc82b5007f 100644 --- a/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts +++ b/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts @@ -1,12 +1,12 @@ -/* eslint-disable @typescript-eslint/camelcase */ import { Meteor } from 'meteor/meteor'; import { Mongo } from 'meteor/mongo'; import { WebApp } from 'meteor/webapp'; import { OAuth2Server } from 'meteor/rocketchat:oauth2-server'; import { Request, Response } from 'express'; +import { IUser } from '@rocket.chat/core-typings'; +import { OAuthApps } from '@rocket.chat/models'; import { Users } from '../../../models/server'; -import { OAuthApps } from '../../../models/server/raw'; import { API } from '../../../api/server'; const oauth2server = new OAuth2Server({ @@ -15,7 +15,8 @@ const oauth2server = new OAuth2Server({ authCodesCollectionName: 'rocketchat_oauth_auth_codes', // TODO: Remove workaround. Used to pass meteor collection reference to a package clientsCollection: new Mongo.Collection(OAuthApps.col.collectionName), - debug: true, + // If you're developing something related to oauth servers, you should change this to true + debug: false, }); // https://github.com/RocketChat/rocketchat-oauth2-server/blob/e758fd7ef69348c7ceceabe241747a986c32d036/model.coffee#L27-L27 @@ -25,6 +26,29 @@ function getAccessToken(accessToken: string): any { }); } +export function oAuth2ServerAuth(partialRequest: { + headers: Record; + query: Record; +}): { user: IUser } | undefined { + const headerToken = partialRequest.headers.authorization?.replace('Bearer ', ''); + const queryToken = partialRequest.query.access_token; + + const accessToken = getAccessToken(headerToken || queryToken); + + // If there is no token available or the token has expired, return undefined + if (!accessToken || (accessToken.expires != null && accessToken.expires !== 0 && accessToken.expires < new Date())) { + return; + } + + const user = Users.findOne(accessToken.userId); + + if (user == null) { + return; + } + + return { user }; +} + oauth2server.app.disable('x-powered-by'); oauth2server.routes.disable('x-powered-by'); @@ -57,33 +81,5 @@ oauth2server.routes.get('/oauth/userinfo', function (req: Request, res: Response }); API.v1.addAuthMethod(function () { - const headerToken = this.request.headers.authorization; - const getToken = this.request.query.access_token; - - let token: string | undefined; - if (headerToken != null) { - const matches = headerToken.match(/Bearer\s(\S+)/); - if (matches) { - token = matches[1]; - } else { - token = undefined; - } - } - const bearerToken = token || getToken; - if (bearerToken == null) { - return; - } - - const accessToken = getAccessToken(bearerToken); - if (accessToken == null) { - return; - } - if (accessToken.expires != null && accessToken.expires !== 0 && accessToken.expires < new Date()) { - return; - } - const user = Users.findOne(accessToken.userId); - if (user == null) { - return; - } - return { user }; + return oAuth2ServerAuth(this.request); }); diff --git a/apps/meteor/app/oembed/client/index.js b/apps/meteor/app/oembed/client/index.ts similarity index 100% rename from apps/meteor/app/oembed/client/index.js rename to apps/meteor/app/oembed/client/index.ts diff --git a/apps/meteor/app/oembed/server/index.js b/apps/meteor/app/oembed/server/index.ts similarity index 100% rename from apps/meteor/app/oembed/server/index.js rename to apps/meteor/app/oembed/server/index.ts diff --git a/apps/meteor/app/oembed/server/jumpToMessage.js b/apps/meteor/app/oembed/server/jumpToMessage.js deleted file mode 100644 index 8bded0996ed9..000000000000 --- a/apps/meteor/app/oembed/server/jumpToMessage.js +++ /dev/null @@ -1,89 +0,0 @@ -import URL from 'url'; -import QueryString from 'querystring'; - -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; - -import { Messages, Rooms, Users } from '../../models/server'; -import { settings } from '../../settings/server'; -import { callbacks } from '../../../lib/callbacks'; -import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; -import { canAccessRoom } from '../../authorization/server/functions/canAccessRoom'; - -const recursiveRemove = (message, deep = 1) => { - if (message) { - if ('attachments' in message && message.attachments !== null && deep < settings.get('Message_QuoteChainLimit')) { - message.attachments.map((msg) => recursiveRemove(msg, deep + 1)); - } else { - delete message.attachments; - } - } - return message; -}; - -callbacks.add( - 'beforeSaveMessage', - (msg) => { - // if no message is present, or the message doesn't have any URL, skip - if (!msg || !msg.urls || !msg.urls.length) { - return msg; - } - - const currentUser = Users.findOneById(msg.u._id); - - msg.urls.forEach((item) => { - // if the URL is not internal, skip - if (!item.url.includes(Meteor.absoluteUrl())) { - return; - } - - const urlObj = URL.parse(item.url); - - // if the URL doesn't have query params (doesn't reference message) skip - if (!urlObj.query) { - return; - } - - const { msg: msgId } = QueryString.parse(urlObj.query); - - if (!_.isString(msgId)) { - return; - } - - const jumpToMessage = recursiveRemove(Messages.findOneById(msgId)); - if (!jumpToMessage) { - return; - } - - // validates if user can see the message - // user has to belong to the room the message was first wrote in - const room = Rooms.findOneById(jumpToMessage.rid); - const isLiveChatRoomVisitor = !!msg.token && !!room.v?.token && msg.token === room.v.token; - const canAccessRoomForUser = isLiveChatRoomVisitor || canAccessRoom(room, currentUser); - if (!canAccessRoomForUser) { - return; - } - - msg.attachments = msg.attachments || []; - const index = msg.attachments.findIndex((a) => a.message_link === item.url); - if (index > -1) { - msg.attachments.splice(index, 1); - } - - msg.attachments.push({ - text: jumpToMessage.msg, - translations: jumpToMessage.translations, - author_name: jumpToMessage.alias || jumpToMessage.u.username, - author_icon: getUserAvatarURL(jumpToMessage.u.username), - message_link: item.url, - attachments: jumpToMessage.attachments || [], - ts: jumpToMessage.ts, - }); - item.ignoreParse = true; - }); - - return msg; - }, - callbacks.priority.LOW, - 'jumpToMessage', -); diff --git a/apps/meteor/app/oembed/server/jumpToMessage.ts b/apps/meteor/app/oembed/server/jumpToMessage.ts new file mode 100644 index 000000000000..a1112939a67f --- /dev/null +++ b/apps/meteor/app/oembed/server/jumpToMessage.ts @@ -0,0 +1,103 @@ +import URL from 'url'; +import QueryString from 'querystring'; + +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; +import { ITranslatedMessage, MessageAttachment, isQuoteAttachment } from '@rocket.chat/core-typings'; + +import { Messages, Rooms, Users } from '../../models/server'; +import { settings } from '../../settings/server'; +import { callbacks } from '../../../lib/callbacks'; +import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; +import { canAccessRoom } from '../../authorization/server/functions/canAccessRoom'; + +const recursiveRemove = (attachments: MessageAttachment, deep = 1): MessageAttachment => { + if (attachments && isQuoteAttachment(attachments)) { + if (deep < settings.get('Message_QuoteChainLimit')) { + attachments.attachments?.map((msg) => recursiveRemove(msg, deep + 1)); + } else { + delete attachments.attachments; + } + } + + return attachments; +}; + +const validateAttachmentDeepness = (message: ITranslatedMessage): ITranslatedMessage => { + if (!message || !message.attachments) { + return message; + } + + message.attachments = message.attachments?.map((attachment) => recursiveRemove(attachment)); + + return message; +}; + +callbacks.add( + 'beforeSaveMessage', + (msg) => { + // if no message is present, or the message doesn't have any URL, skip + if (!msg || !msg.urls || !msg.urls.length) { + return msg; + } + + const currentUser = Users.findOneById(msg.u._id); + + msg.urls.forEach((item) => { + // if the URL is not internal, skip + if (!item.url.includes(Meteor.absoluteUrl())) { + return; + } + + const urlObj = URL.parse(item.url); + + // if the URL doesn't have query params (doesn't reference message) skip + if (!urlObj.query) { + return; + } + + const { msg: msgId } = QueryString.parse(urlObj.query); + + if (!_.isString(msgId)) { + return; + } + + const jumpToMessage = validateAttachmentDeepness(Messages.findOneById(msgId)); + if (!jumpToMessage) { + return; + } + + // validates if user can see the message + // user has to belong to the room the message was first wrote in + const room = Rooms.findOneById(jumpToMessage.rid); + const isLiveChatRoomVisitor = !!msg.token && !!room.v?.token && msg.token === room.v.token; + const canAccessRoomForUser = isLiveChatRoomVisitor || canAccessRoom(room, currentUser); + if (!canAccessRoomForUser) { + return; + } + + msg.attachments = msg.attachments || []; + // Only QuoteAttachments have "message_link" property + const index = msg.attachments.findIndex((a) => isQuoteAttachment(a) && a.message_link === item.url); + if (index > -1) { + msg.attachments.splice(index, 1); + } + + msg.attachments.push({ + text: jumpToMessage.msg, + translations: jumpToMessage.translations, + author_name: jumpToMessage.alias || jumpToMessage.u.username, + author_icon: getUserAvatarURL(jumpToMessage.u.username), + message_link: item.url, + // @ts-expect-error + attachments: jumpToMessage.attachments || [], + ts: jumpToMessage.ts, + }); + item.ignoreParse = true; + }); + + return msg; + }, + callbacks.priority.LOW, + 'jumpToMessage', +); diff --git a/apps/meteor/app/oembed/server/providers.js b/apps/meteor/app/oembed/server/providers.js deleted file mode 100644 index 1f89975e372e..000000000000 --- a/apps/meteor/app/oembed/server/providers.js +++ /dev/null @@ -1,167 +0,0 @@ -import URL from 'url'; -import QueryString from 'querystring'; - -import { camelCase } from 'change-case'; -import _ from 'underscore'; - -import { callbacks } from '../../../lib/callbacks'; -import { SystemLogger } from '../../../server/lib/logger/system'; - -class Providers { - constructor() { - this.providers = []; - } - - static getConsumerUrl(provider, url) { - const urlObj = URL.parse(provider.endPoint, true); - urlObj.query.url = url; - delete urlObj.search; - return URL.format(urlObj); - } - - registerProvider(provider) { - return this.providers.push(provider); - } - - getProviders() { - return this.providers; - } - - getProviderForUrl(url) { - return _.find(this.providers, function (provider) { - const candidate = _.find(provider.urls, function (re) { - return re.test(url); - }); - return candidate != null; - }); - } -} - -const providers = new Providers(); - -providers.registerProvider({ - urls: [new RegExp('https?://soundcloud\\.com/\\S+')], - endPoint: 'https://soundcloud.com/oembed?format=json&maxheight=150', -}); - -providers.registerProvider({ - urls: [ - new RegExp('https?://vimeo\\.com/[^/]+'), - new RegExp('https?://vimeo\\.com/channels/[^/]+/[^/]+'), - new RegExp('https://vimeo\\.com/groups/[^/]+/videos/[^/]+'), - ], - endPoint: 'https://vimeo.com/api/oembed.json?maxheight=200', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://www\\.youtube\\.com/\\S+'), new RegExp('https?://youtu\\.be/\\S+')], - endPoint: 'https://www.youtube.com/oembed?maxheight=200', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://www\\.rdio\\.com/\\S+'), new RegExp('https?://rd\\.io/\\S+')], - endPoint: 'https://www.rdio.com/api/oembed/?format=json&maxheight=150', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://www\\.slideshare\\.net/[^/]+/[^/]+')], - endPoint: 'https://www.slideshare.net/api/oembed/2?format=json&maxheight=200', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://www\\.dailymotion\\.com/video/\\S+')], - endPoint: 'https://www.dailymotion.com/services/oembed?maxheight=200', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://twitter\\.com/[^/]+/status/\\S+')], - endPoint: 'https://publish.twitter.com/oembed', -}); - -providers.registerProvider({ - urls: [new RegExp('https?://(play|open)\\.spotify\\.com/(track|album|playlist|show)/\\S+')], - endPoint: 'https://open.spotify.com/oembed', -}); - -export const oembed = {}; - -oembed.providers = providers; - -callbacks.add( - 'oembed:beforeGetUrlContent', - function (data) { - if (data.parsedUrl != null) { - const url = URL.format(data.parsedUrl); - const provider = providers.getProviderForUrl(url); - if (provider != null) { - let consumerUrl = Providers.getConsumerUrl(provider, url); - consumerUrl = URL.parse(consumerUrl, true); - _.extend(data.parsedUrl, consumerUrl); - data.urlObj.port = consumerUrl.port; - data.urlObj.hostname = consumerUrl.hostname; - data.urlObj.pathname = consumerUrl.pathname; - data.urlObj.query = consumerUrl.query; - delete data.urlObj.search; - delete data.urlObj.host; - } - } - return data; - }, - callbacks.priority.MEDIUM, - 'oembed-providers-before', -); - -const cleanupOembed = (data) => { - if (!data?.meta) { - return data; - } - - // remove oembedHtml key from original meta - const { oembedHtml, ...meta } = data.meta; - - return { - ...data, - meta, - }; -}; - -callbacks.add( - 'oembed:afterParseContent', - function (data) { - if (!data || !data.url || !data.content?.body || !data.parsedUrl?.query) { - return cleanupOembed(data); - } - - let queryString = data.parsedUrl.query; - if (_.isString(data.parsedUrl.query)) { - queryString = QueryString.parse(data.parsedUrl.query); - } - - if (!queryString.url) { - return cleanupOembed(data); - } - - const { url: originalUrl } = data; - const provider = providers.getProviderForUrl(originalUrl); - if (!provider) { - return cleanupOembed(data); - } - - const { url } = queryString; - data.meta.oembedUrl = url; - - try { - const metas = JSON.parse(data.content.body); - _.each(metas, function (value, key) { - if (_.isString(value)) { - data.meta[camelCase(`oembed_${key}`)] = value; - } - }); - } catch (error) { - SystemLogger.error(error); - } - return data; - }, - callbacks.priority.MEDIUM, - 'oembed-providers-after', -); diff --git a/apps/meteor/app/oembed/server/providers.ts b/apps/meteor/app/oembed/server/providers.ts new file mode 100644 index 000000000000..fae14bfa0a9e --- /dev/null +++ b/apps/meteor/app/oembed/server/providers.ts @@ -0,0 +1,189 @@ +import URL from 'url'; +import QueryString from 'querystring'; + +import { camelCase } from 'change-case'; +import _ from 'underscore'; +import { OEmbedMeta, OEmbedUrlContent, ParsedUrl, OEmbedProvider } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../lib/callbacks'; +import { SystemLogger } from '../../../server/lib/logger/system'; + +type OEmbedExecutor = { + providers: Providers; +}; + +class Providers { + private providers: OEmbedProvider[]; + + constructor() { + this.providers = []; + } + + static getConsumerUrl(provider: OEmbedProvider, url: string): string { + const urlObj = new URL.URL(provider.endPoint); + urlObj.searchParams.set('url', url); + + return URL.format(urlObj); + } + + registerProvider(provider: OEmbedProvider): number { + return this.providers.push(provider); + } + + getProviders(): OEmbedProvider[] { + return this.providers; + } + + getProviderForUrl(url: string): OEmbedProvider | undefined { + return _.find(this.providers, function (provider) { + const candidate = _.find(provider.urls, function (re) { + return re.test(url); + }); + return candidate != null; + }); + } +} + +const providers = new Providers(); + +providers.registerProvider({ + urls: [new RegExp('https?://soundcloud\\.com/\\S+')], + endPoint: 'https://soundcloud.com/oembed?format=json&maxheight=150', +}); + +providers.registerProvider({ + urls: [ + new RegExp('https?://vimeo\\.com/[^/]+'), + new RegExp('https?://vimeo\\.com/channels/[^/]+/[^/]+'), + new RegExp('https://vimeo\\.com/groups/[^/]+/videos/[^/]+'), + ], + endPoint: 'https://vimeo.com/api/oembed.json?maxheight=200', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://www\\.youtube\\.com/\\S+'), new RegExp('https?://youtu\\.be/\\S+')], + endPoint: 'https://www.youtube.com/oembed?maxheight=200', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://www\\.rdio\\.com/\\S+'), new RegExp('https?://rd\\.io/\\S+')], + endPoint: 'https://www.rdio.com/api/oembed/?format=json&maxheight=150', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://www\\.slideshare\\.net/[^/]+/[^/]+')], + endPoint: 'https://www.slideshare.net/api/oembed/2?format=json&maxheight=200', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://www\\.dailymotion\\.com/video/\\S+')], + endPoint: 'https://www.dailymotion.com/services/oembed?maxheight=200', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://twitter\\.com/[^/]+/status/\\S+')], + endPoint: 'https://publish.twitter.com/oembed', +}); + +providers.registerProvider({ + urls: [new RegExp('https?://(play|open)\\.spotify\\.com/(track|album|playlist|show)/\\S+')], + endPoint: 'https://open.spotify.com/oembed', +}); + +export const oembed: OEmbedExecutor = { + providers, +}; + +callbacks.add( + 'oembed:beforeGetUrlContent', + function (data) { + if (data.parsedUrl != null) { + const url = URL.format(data.parsedUrl); + const provider = providers.getProviderForUrl(url); + if (provider != null) { + const consumerUrl = Providers.getConsumerUrl(provider, url); + + const parsedConsumerUrl = URL.parse(consumerUrl, true); + _.extend(data.parsedUrl, parsedConsumerUrl); + + data.urlObj.port = parsedConsumerUrl.port; + data.urlObj.hostname = parsedConsumerUrl.hostname; + data.urlObj.pathname = parsedConsumerUrl.pathname; + data.urlObj.query = parsedConsumerUrl.query; + + delete data.urlObj.search; + delete data.urlObj.host; + } + } + return data; + }, + callbacks.priority.MEDIUM, + 'oembed-providers-before', +); + +const cleanupOembed = (data: { + url: string; + meta: OEmbedMeta; + headers: { [k: string]: string }; + parsedUrl: ParsedUrl; + content: OEmbedUrlContent; +}): { + url: string; + meta: Omit; + headers: { [k: string]: string }; + parsedUrl: ParsedUrl; + content: OEmbedUrlContent; +} => { + if (!data?.meta) { + return data; + } + + // remove oembedHtml key from original meta + const { oembedHtml, ...meta } = data.meta; + + return { + ...data, + meta, + }; +}; + +callbacks.add( + 'oembed:afterParseContent', + function (data) { + if (!data || !data.url || !data.content?.body || !data.parsedUrl?.query) { + return cleanupOembed(data); + } + + let queryString = data.parsedUrl.query; + if (_.isString(data.parsedUrl.query)) { + queryString = QueryString.parse(data.parsedUrl.query); + } + + if (!queryString.url) { + return cleanupOembed(data); + } + + const { url: originalUrl } = data; + const provider = providers.getProviderForUrl(originalUrl); + if (!provider) { + return cleanupOembed(data); + } + + const { url } = queryString; + data.meta.oembedUrl = url; + + try { + const metas = JSON.parse(data.content.body); + _.each(metas, function (value, key) { + if (_.isString(value)) { + data.meta[camelCase(`oembed_${key}`)] = value; + } + }); + } catch (error) { + SystemLogger.error(error); + } + return data; + }, + callbacks.priority.MEDIUM, + 'oembed-providers-after', +); diff --git a/apps/meteor/app/oembed/server/server.js b/apps/meteor/app/oembed/server/server.js deleted file mode 100644 index 84d5f3eef3e7..000000000000 --- a/apps/meteor/app/oembed/server/server.js +++ /dev/null @@ -1,310 +0,0 @@ -import URL from 'url'; -import querystring from 'querystring'; - -import { camelCase } from 'change-case'; -import _ from 'underscore'; -import iconv from 'iconv-lite'; -import ipRangeCheck from 'ip-range-check'; -import he from 'he'; -import jschardet from 'jschardet'; - -import { Messages } from '../../models/server'; -import { OEmbedCache } from '../../models/server/raw'; -import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings/server'; -import { isURL } from '../../../lib/utils/isURL'; -import { SystemLogger } from '../../../server/lib/logger/system'; -import { Info } from '../../utils/server'; -import { fetch } from '../../../server/lib/http/fetch'; - -const OEmbed = {}; - -// Detect encoding -// Priority: -// Detected == HTTP Header > Detected == HTML meta > HTTP Header > HTML meta > Detected > Default (utf-8) -// See also: https://www.w3.org/International/questions/qa-html-encoding-declarations.en#quickanswer -const getCharset = function (contentType, body) { - let detectedCharset; - let httpHeaderCharset; - let htmlMetaCharset; - let result; - - contentType = contentType || ''; - - const binary = body.toString('binary'); - const detected = jschardet.detect(binary); - if (detected.confidence > 0.8) { - detectedCharset = detected.encoding.toLowerCase(); - } - const m1 = contentType.match(/charset=([\w\-]+)/i); - if (m1) { - httpHeaderCharset = m1[1].toLowerCase(); - } - const m2 = binary.match(/]*charset=["']?([\w\-]+)/i); - if (m2) { - htmlMetaCharset = m2[1].toLowerCase(); - } - if (detectedCharset) { - if (detectedCharset === httpHeaderCharset) { - result = httpHeaderCharset; - } else if (detectedCharset === htmlMetaCharset) { - result = htmlMetaCharset; - } - } - if (!result) { - result = httpHeaderCharset || htmlMetaCharset || detectedCharset; - } - return result || 'utf-8'; -}; - -const toUtf8 = function (contentType, body) { - return iconv.decode(body, getCharset(contentType, body)); -}; - -const getUrlContent = async function (urlObj, redirectCount = 5) { - if (_.isString(urlObj)) { - urlObj = URL.parse(urlObj); - } - - const portsProtocol = { - 80: 'http:', - 8080: 'http:', - 443: 'https:', - }; - - const parsedUrl = _.pick(urlObj, ['host', 'hash', 'pathname', 'protocol', 'port', 'query', 'search', 'hostname']); - const ignoredHosts = settings.get('API_EmbedIgnoredHosts').replace(/\s/g, '').split(',') || []; - if (ignoredHosts.includes(parsedUrl.hostname) || ipRangeCheck(parsedUrl.hostname, ignoredHosts)) { - throw new Error('invalid host'); - } - - const safePorts = settings.get('API_EmbedSafePorts').replace(/\s/g, '').split(',') || []; - - if (safePorts.length > 0 && parsedUrl.port && !safePorts.includes(parsedUrl.port)) { - throw new Error('invalid/unsafe port'); - } - - if (safePorts.length > 0 && !parsedUrl.port && !safePorts.some((port) => portsProtocol[port] === parsedUrl.protocol)) { - throw new Error('invalid/unsafe port'); - } - - const data = callbacks.run('oembed:beforeGetUrlContent', { - urlObj, - parsedUrl, - }); - if (data.attachments != null) { - return data; - } - - const url = URL.format(data.urlObj); - - const sizeLimit = 250000; - - const response = await fetch( - url, - { - compress: true, - follow: redirectCount, - headers: { - 'User-Agent': `${settings.get('API_Embed_UserAgent')} Rocket.Chat/${Info.version}`, - 'Accept-Language': settings.get('Language') || 'en', - }, - size: sizeLimit, // max size of the response body, this was not working as expected so I'm also manually verifying that on the iterator - }, - settings.get('Allow_Invalid_SelfSigned_Certs'), - ); - - let totalSize = 0; - const chunks = []; - for await (const chunk of response.body) { - totalSize += chunk.length; - chunks.push(chunk); - - if (totalSize > sizeLimit) { - SystemLogger.info({ msg: 'OEmbed request size exceeded', url }); - break; - } - } - - const buffer = Buffer.concat(chunks); - - return { - headers: Object.fromEntries(response.headers), - body: toUtf8(response.headers.get('content-type'), buffer), - parsedUrl, - statusCode: response.status, - }; -}; - -OEmbed.getUrlMeta = function (url, withFragment) { - const urlObj = URL.parse(url); - if (withFragment != null) { - const queryStringObj = querystring.parse(urlObj.query); - queryStringObj._escaped_fragment_ = ''; - urlObj.query = querystring.stringify(queryStringObj); - let path = urlObj.pathname; - if (urlObj.query != null) { - path += `?${urlObj.query}`; - urlObj.search = `?${urlObj.query}`; - } - urlObj.path = path; - } - const content = Promise.await(getUrlContent(urlObj, 5)); - - if (!content) { - return; - } - - if (content.attachments != null) { - return content; - } - - let metas = undefined; - if (content && content.body) { - metas = {}; - const escapeMeta = (name, value) => { - metas[name] = metas[name] || he.unescape(value); - return metas[name]; - }; - content.body.replace(/]*>([^<]*)<\/title>/gim, function (meta, title) { - return escapeMeta('pageTitle', title); - }); - content.body.replace(/]*(?:name|property)=[']([^']*)['][^>]*\scontent=[']([^']*)['][^>]*>/gim, function (meta, name, value) { - return escapeMeta(camelCase(name), value); - }); - content.body.replace(/]*(?:name|property)=["]([^"]*)["][^>]*\scontent=["]([^"]*)["][^>]*>/gim, function (meta, name, value) { - return escapeMeta(camelCase(name), value); - }); - content.body.replace(/]*\scontent=[']([^']*)['][^>]*(?:name|property)=[']([^']*)['][^>]*>/gim, function (meta, value, name) { - return escapeMeta(camelCase(name), value); - }); - content.body.replace(/]*\scontent=["]([^"]*)["][^>]*(?:name|property)=["]([^"]*)["][^>]*>/gim, function (meta, value, name) { - return escapeMeta(camelCase(name), value); - }); - if (metas.fragment === '!' && withFragment == null) { - return OEmbed.getUrlMeta(url, true); - } - delete metas.oembedHtml; - } - let headers = undefined; - - if (content?.headers) { - headers = {}; - const headerObj = content.headers; - Object.keys(headerObj).forEach((header) => { - headers[camelCase(header)] = headerObj[header]; - }); - } - if (content && content.statusCode !== 200) { - return; - } - return callbacks.run('oembed:afterParseContent', { - url, - meta: metas, - headers, - parsedUrl: content.parsedUrl, - content, - }); -}; - -OEmbed.getUrlMetaWithCache = async function (url, withFragment) { - const cache = await OEmbedCache.findOneById(url); - - if (cache != null) { - return cache.data; - } - const data = OEmbed.getUrlMeta(url, withFragment); - if (data != null) { - try { - await OEmbedCache.createWithIdAndData(url, data); - } catch (_error) { - SystemLogger.error({ msg: 'OEmbed duplicated record', url }); - } - return data; - } -}; - -const getRelevantHeaders = function (headersObj) { - const headers = {}; - Object.keys(headersObj).forEach((key) => { - const value = headersObj[key]; - const lowerCaseKey = key.toLowerCase(); - if ((lowerCaseKey === 'contenttype' || lowerCaseKey === 'contentlength') && value && value.trim() !== '') { - headers[key] = value; - } - }); - - if (Object.keys(headers).length > 0) { - return headers; - } -}; - -const getRelevantMetaTags = function (metaObj) { - const tags = {}; - Object.keys(metaObj).forEach((key) => { - const value = metaObj[key]; - if (/^(og|fb|twitter|oembed|msapplication).+|description|title|pageTitle$/.test(key.toLowerCase()) && value && value.trim() !== '') { - tags[key] = value; - } - }); - - if (Object.keys(tags).length > 0) { - return tags; - } -}; - -const insertMaxWidthInOembedHtml = (oembedHtml) => oembedHtml?.replace('iframe', 'iframe style="max-width: 100%;width:400px;height:225px"'); - -OEmbed.rocketUrlParser = async function (message) { - if (Array.isArray(message.urls)) { - const attachments = []; - let changed = false; - for await (const item of message.urls) { - if (item.ignoreParse === true) { - return; - } - if (!isURL(item.url)) { - return; - } - const data = await OEmbed.getUrlMetaWithCache(item.url); - if (data != null) { - if (data.attachments) { - attachments.push(...data.attachments); - return; - } - if (data.meta != null) { - item.meta = getRelevantMetaTags(data.meta); - if (item.meta && item.meta.oembedHtml) { - item.meta.oembedHtml = insertMaxWidthInOembedHtml(item.meta.oembedHtml); - } - } - if (data.headers != null) { - item.headers = getRelevantHeaders(data.headers); - } - item.parsedUrl = data.parsedUrl; - changed = true; - } - } - if (attachments.length) { - Messages.setMessageAttachments(message._id, attachments); - } - if (changed === true) { - Messages.setUrlsById(message._id, message.urls); - } - } - return message; -}; - -settings.watch('API_Embed', function (value) { - if (value) { - return callbacks.add( - 'afterSaveMessage', - (message) => Promise.await(OEmbed.rocketUrlParser(message)), - callbacks.priority.LOW, - 'API_Embed', - ); - } - return callbacks.remove('afterSaveMessage', 'API_Embed'); -}); - -export { OEmbed }; diff --git a/apps/meteor/app/oembed/server/server.ts b/apps/meteor/app/oembed/server/server.ts new file mode 100644 index 000000000000..3b5b5427a4b1 --- /dev/null +++ b/apps/meteor/app/oembed/server/server.ts @@ -0,0 +1,365 @@ +import URL from 'url'; +import querystring from 'querystring'; + +import { camelCase } from 'change-case'; +import _ from 'underscore'; +import iconv from 'iconv-lite'; +import ipRangeCheck from 'ip-range-check'; +import he from 'he'; +import jschardet from 'jschardet'; +import { + OEmbedUrlContentResult, + OEmbedUrlWithMetadata, + IMessage, + MessageAttachment, + isOEmbedUrlContentResult, + isOEmbedUrlWithMetadata, + OEmbedMeta, +} from '@rocket.chat/core-typings'; +import { OEmbedCache } from '@rocket.chat/models'; + +import { Logger } from '../../logger/server'; +import { Messages } from '../../models/server'; +import { callbacks } from '../../../lib/callbacks'; +import { settings } from '../../settings/server'; +import { isURL } from '../../../lib/utils/isURL'; +import { Info } from '../../utils/server'; +import { fetch } from '../../../server/lib/http/fetch'; + +const log = new Logger('OEmbed'); +// Detect encoding +// Priority: +// Detected == HTTP Header > Detected == HTML meta > HTTP Header > HTML meta > Detected > Default (utf-8) +// See also: https://www.w3.org/International/questions/qa-html-encoding-declarations.en#quickanswer +const getCharset = function (contentType: string, body: Buffer): string { + let detectedCharset; + let httpHeaderCharset; + let htmlMetaCharset; + let result; + + contentType = contentType || ''; + + const binary = body.toString('binary'); + const detected = jschardet.detect(binary); + if (detected.confidence > 0.8) { + detectedCharset = detected.encoding.toLowerCase(); + } + const m1 = contentType.match(/charset=([\w\-]+)/i); + if (m1) { + httpHeaderCharset = m1[1].toLowerCase(); + } + const m2 = binary.match(/]*charset=["']?([\w\-]+)/i); + if (m2) { + htmlMetaCharset = m2[1].toLowerCase(); + } + if (detectedCharset) { + if (detectedCharset === httpHeaderCharset) { + result = httpHeaderCharset; + } else if (detectedCharset === htmlMetaCharset) { + result = htmlMetaCharset; + } + } + if (!result) { + result = httpHeaderCharset || htmlMetaCharset || detectedCharset; + } + return result || 'utf-8'; +}; + +const toUtf8 = function (contentType: string, body: Buffer): string { + return iconv.decode(body, getCharset(contentType, body)); +}; + +const getUrlContent = async function (urlObjStr: string | URL.UrlWithStringQuery, redirectCount = 5): Promise { + let urlObj: URL.UrlWithStringQuery; + if (typeof urlObjStr === 'string') { + urlObj = URL.parse(urlObjStr); + } else { + urlObj = urlObjStr; + } + + const portsProtocol = new Map( + Object.entries({ + 80: 'http:', + 8080: 'http:', + 443: 'https:', + }), + ); + + const parsedUrl = _.pick(urlObj, ['host', 'hash', 'pathname', 'protocol', 'port', 'query', 'search', 'hostname']); + const ignoredHosts = settings.get('API_EmbedIgnoredHosts').replace(/\s/g, '').split(',') || []; + if (parsedUrl.hostname && (ignoredHosts.includes(parsedUrl.hostname) || ipRangeCheck(parsedUrl.hostname, ignoredHosts))) { + throw new Error('invalid host'); + } + + const safePorts = settings.get('API_EmbedSafePorts').replace(/\s/g, '').split(',') || []; + + if (safePorts.length > 0 && parsedUrl.port && !safePorts.includes(parsedUrl.port)) { + throw new Error('invalid/unsafe port'); + } + + if (safePorts.length > 0 && !parsedUrl.port && !safePorts.some((port) => portsProtocol.get(port) === parsedUrl.protocol)) { + throw new Error('invalid/unsafe port'); + } + + const data = callbacks.run('oembed:beforeGetUrlContent', { + urlObj, + parsedUrl, + }); + + /* This prop is neither passed or returned by the callback, so I'll just comment it for now + if (data.attachments != null) { + return data; + } */ + + const url = URL.format(data.urlObj); + + const sizeLimit = 250000; + + log.debug(`Fetching ${url} following redirects ${redirectCount} times`); + const response = await fetch( + url, + { + compress: true, + follow: redirectCount, + headers: { + 'User-Agent': `${settings.get('API_Embed_UserAgent')} Rocket.Chat/${Info.version}`, + 'Accept-Language': settings.get('Language') || 'en', + }, + size: sizeLimit, // max size of the response body, this was not working as expected so I'm also manually verifying that on the iterator + }, + settings.get('Allow_Invalid_SelfSigned_Certs'), + ); + + let totalSize = 0; + const chunks = []; + // @ts-expect-error from https://github.com/microsoft/TypeScript/issues/39051 + for await (const chunk of response.body) { + totalSize += chunk.length; + chunks.push(chunk); + + if (totalSize > sizeLimit) { + log.warn({ msg: 'OEmbed request size exceeded', url }); + break; + } + } + + log.debug('Obtained response from server with length of', totalSize); + const buffer = Buffer.concat(chunks); + return { + // @ts-expect-error - fetch types are kinda weird + headers: Object.fromEntries(response.headers), + body: toUtf8(response.headers.get('content-type') || 'text/plain', buffer), + parsedUrl, + statusCode: response.status, + }; +}; + +const getUrlMeta = async function ( + url: string, + withFragment?: boolean, +): Promise { + log.debug('Obtaining metadata for URL', url); + const urlObj = URL.parse(url); + if (withFragment != null) { + const queryStringObj = querystring.parse(urlObj.query || ''); + queryStringObj._escaped_fragment_ = ''; + urlObj.query = querystring.stringify(queryStringObj); + let path = urlObj.pathname; + if (urlObj.query != null) { + path += `?${urlObj.query}`; + urlObj.search = `?${urlObj.query}`; + } + urlObj.path = path; + } + log.debug('Fetching url content', urlObj.path); + let content: OEmbedUrlContentResult | undefined; + try { + content = await getUrlContent(urlObj, 5); + } catch (e) { + log.error('Error fetching url content', e); + } + + if (!content) { + return; + } + + if (content.attachments != null) { + return content; + } + + log.debug('Parsing metadata for URL', url); + const metas: { [k: string]: string } = {}; + + if (content?.body) { + const escapeMeta = (name: string, value: string): string => { + metas[name] = metas[name] || he.unescape(value); + return metas[name]; + }; + content.body.replace(/]*>([^<]*)<\/title>/gim, function (_meta, title) { + return escapeMeta('pageTitle', title); + }); + content.body.replace(/]*(?:name|property)=[']([^']*)['][^>]*\scontent=[']([^']*)['][^>]*>/gim, function (_meta, name, value) { + return escapeMeta(camelCase(name), value); + }); + content.body.replace(/]*(?:name|property)=["]([^"]*)["][^>]*\scontent=["]([^"]*)["][^>]*>/gim, function (_meta, name, value) { + return escapeMeta(camelCase(name), value); + }); + content.body.replace(/]*\scontent=[']([^']*)['][^>]*(?:name|property)=[']([^']*)['][^>]*>/gim, function (_meta, value, name) { + return escapeMeta(camelCase(name), value); + }); + content.body.replace(/]*\scontent=["]([^"]*)["][^>]*(?:name|property)=["]([^"]*)["][^>]*>/gim, function (_meta, value, name) { + return escapeMeta(camelCase(name), value); + }); + if (metas.fragment === '!' && withFragment == null) { + return getUrlMeta(url, true); + } + delete metas.oembedHtml; + } + const headers: { [k: string]: string } = {}; + + if (content?.headers) { + const headerObj = content.headers; + Object.keys(headerObj).forEach((header) => { + headers[camelCase(header)] = headerObj[header]; + }); + } + if (content && content.statusCode !== 200) { + return; + } + return callbacks.run('oembed:afterParseContent', { + url, + meta: metas, + headers, + parsedUrl: content.parsedUrl, + content, + }); +}; + +const getUrlMetaWithCache = async function ( + url: string, + withFragment?: boolean, +): Promise { + log.debug('Getting oembed metadata for', url); + const cache = await OEmbedCache.findOneById(url); + + if (cache != null) { + log.debug('Found oembed metadata in cache for', url); + return cache.data; + } + const data = await getUrlMeta(url, withFragment); + if (data != null) { + try { + log.debug('Saving oembed metadata in cache for', url); + await OEmbedCache.createWithIdAndData(url, data); + } catch (_error) { + log.error({ msg: 'OEmbed duplicated record', url }); + } + return data; + } +}; + +const hasOnlyContentLength = (obj: any): obj is { contentLength: string } => 'contentLength' in obj && Object.keys(obj).length === 1; +const hasOnlyContentType = (obj: any): obj is { contentType: string } => 'contentType' in obj && Object.keys(obj).length === 1; +const hasContentLengthAndContentType = (obj: any): obj is { contentLength: string; contentType: string } => + 'contentLength' in obj && 'contentType' in obj && Object.keys(obj).length === 2; + +const getRelevantHeaders = function (headersObj: { + [key: string]: string; +}): { contentLength: string } | { contentType: string } | { contentLength: string; contentType: string } | void { + const headers = { + ...(headersObj.contentLength && { contentLength: headersObj.contentLength }), + ...(headersObj.contentType && { contentType: headersObj.contentType }), + }; + + if (hasOnlyContentLength(headers) || hasOnlyContentType(headers) || hasContentLengthAndContentType(headers)) { + return headers; + } +}; + +const getRelevantMetaTags = function (metaObj: OEmbedMeta): Record | void { + const tags: Record = {}; + Object.keys(metaObj).forEach((key) => { + const value = metaObj[key]; + if (/^(og|fb|twitter|oembed|msapplication).+|description|title|pageTitle$/.test(key.toLowerCase()) && value && value.trim() !== '') { + tags[key] = value; + } + }); + + if (Object.keys(tags).length > 0) { + return tags; + } +}; + +const insertMaxWidthInOembedHtml = (oembedHtml?: string): string | undefined => + oembedHtml?.replace('iframe', 'iframe style="max-width: 100%;width:400px;height:225px"'); + +const rocketUrlParser = async function (message: IMessage): Promise { + log.debug('Parsing message URLs'); + if (Array.isArray(message.urls)) { + log.debug('URLs found', message.urls.length); + const attachments: MessageAttachment[] = []; + + let changed = false; + for await (const item of message.urls) { + if (item.ignoreParse === true) { + log.debug('URL ignored', item.url); + break; + } + if (!isURL(item.url)) { + break; + } + const data = await getUrlMetaWithCache(item.url); + if (data != null) { + if (isOEmbedUrlContentResult(data) && data.attachments) { + attachments.push(...data.attachments); + break; + } + if (isOEmbedUrlWithMetadata(data) && data.meta != null) { + item.meta = getRelevantMetaTags(data.meta) || {}; + if (item.meta && item.meta.oembedHtml) { + item.meta.oembedHtml = insertMaxWidthInOembedHtml(item.meta.oembedHtml) || ''; + } + } + if (data.headers != null) { + const headers = getRelevantHeaders(data.headers); + if (headers) { + item.headers = headers; + } + } + item.parsedUrl = data.parsedUrl; + changed = true; + } + } + if (attachments.length) { + Messages.setMessageAttachments(message._id, attachments); + } + if (changed === true) { + Messages.setUrlsById(message._id, message.urls); + } + } + return message; +}; + +const OEmbed: { + getUrlMeta: (url: string, withFragment?: boolean) => Promise; + getUrlMetaWithCache: (url: string, withFragment?: boolean) => Promise; + rocketUrlParser: (message: IMessage) => Promise; +} = { + rocketUrlParser, + getUrlMetaWithCache, + getUrlMeta, +}; + +settings.watch('API_Embed', function (value) { + if (value) { + return callbacks.add( + 'afterSaveMessage', + (message) => Promise.await(OEmbed.rocketUrlParser(message)), + callbacks.priority.LOW, + 'API_Embed', + ); + } + return callbacks.remove('afterSaveMessage', 'API_Embed'); +}); + +export { OEmbed }; diff --git a/apps/meteor/app/otr/client/rocketchat.otr.js b/apps/meteor/app/otr/client/rocketchat.otr.js index f8a660d7fcfa..ebaefbe0d7db 100644 --- a/apps/meteor/app/otr/client/rocketchat.otr.js +++ b/apps/meteor/app/otr/client/rocketchat.otr.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import { Subscriptions } from '../../models'; +import { Subscriptions } from '../../models/client'; import { Notifications } from '../../notifications'; import { t } from '../../utils'; import { onClientMessageReceived } from '../../../client/lib/onClientMessageReceived'; diff --git a/apps/meteor/app/otr/client/rocketchat.otr.room.js b/apps/meteor/app/otr/client/rocketchat.otr.room.js index cf9dcd37308d..9354501a7fcc 100644 --- a/apps/meteor/app/otr/client/rocketchat.otr.room.js +++ b/apps/meteor/app/otr/client/rocketchat.otr.room.js @@ -4,7 +4,6 @@ import { Random } from 'meteor/random'; import { EJSON } from 'meteor/ejson'; import { Tracker } from 'meteor/tracker'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { TimeSync } from 'meteor/mizzao:timesync'; import _ from 'underscore'; import { OTR } from './rocketchat.otr'; @@ -219,12 +218,7 @@ OTR.Room = class { } encrypt(message) { - let ts; - if (isNaN(TimeSync.serverOffset())) { - ts = new Date(); - } else { - ts = new Date(Date.now() + TimeSync.serverOffset()); - } + const ts = new Date(); const data = new TextEncoder('UTF-8').encode( EJSON.stringify({ diff --git a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.js b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.js index c7b4dafd9900..3852734e4bb0 100644 --- a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.js +++ b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Subscriptions, Messages } from '../../../models'; +import { Subscriptions, Messages } from '../../../models/server'; Meteor.methods({ deleteOldOTRMessages(roomId) { diff --git a/apps/meteor/app/otr/server/methods/updateOTRAck.js b/apps/meteor/app/otr/server/methods/updateOTRAck.js index fc48adca642f..1c822a51d89c 100644 --- a/apps/meteor/app/otr/server/methods/updateOTRAck.js +++ b/apps/meteor/app/otr/server/methods/updateOTRAck.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/server'; Meteor.methods({ updateOTRAck(_id, ack) { diff --git a/apps/meteor/app/push-notifications/server/lib/PushNotification.ts b/apps/meteor/app/push-notifications/server/lib/PushNotification.ts index 50752d7b1d77..51c57666992a 100644 --- a/apps/meteor/app/push-notifications/server/lib/PushNotification.ts +++ b/apps/meteor/app/push-notifications/server/lib/PushNotification.ts @@ -110,7 +110,6 @@ export class PushNotification { idOnly, }); - // eslint-disable-next-line @typescript-eslint/camelcase metrics.notificationsSent.inc({ notification_type: 'mobile' }); Push.send(config); } diff --git a/apps/meteor/app/push/server/methods.js b/apps/meteor/app/push/server/methods.js index 5ab475ab2903..fe549db953d0 100644 --- a/apps/meteor/app/push/server/methods.js +++ b/apps/meteor/app/push/server/methods.js @@ -1,3 +1,4 @@ +import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; @@ -12,6 +13,7 @@ Meteor.methods({ check(options, { id: Match.Optional(String), token: _matchToken, + authToken: String, appName: String, userId: Match.OneOf(String, null), metadata: Match.Optional(Object), @@ -22,6 +24,9 @@ Meteor.methods({ throw new Meteor.Error(403, 'Forbidden access'); } + // we always store the hashed token to protect users + const hashedToken = Accounts._hashLoginToken(options.authToken); + let doc; // lookup app by id if one was included @@ -48,6 +53,7 @@ Meteor.methods({ // Rig default doc doc = { token: options.token, + authToken: hashedToken, appName: options.appName, userId: options.userId, enabled: true, @@ -71,6 +77,7 @@ Meteor.methods({ $set: { updatedAt: new Date(), token: options.token, + authToken: hashedToken, }, }, ); diff --git a/apps/meteor/app/push/server/push.js b/apps/meteor/app/push/server/push.js index 91781cd3a23d..29b30991155f 100644 --- a/apps/meteor/app/push/server/push.js +++ b/apps/meteor/app/push/server/push.js @@ -12,8 +12,6 @@ import { settings } from '../../settings/server'; export const _matchToken = Match.OneOf({ apn: String }, { gcm: String }); export const appTokensCollection = new Mongo.Collection('_raix_push_app_tokens'); -appTokensCollection._ensureIndex({ userId: 1 }); - export class PushClass { options = {}; diff --git a/apps/meteor/app/reactions/client/init.js b/apps/meteor/app/reactions/client/init.js index bc83ed83e05c..b20a36dee39d 100644 --- a/apps/meteor/app/reactions/client/init.js +++ b/apps/meteor/app/reactions/client/init.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Blaze } from 'meteor/blaze'; -import { Rooms, Subscriptions } from '../../models'; +import { Rooms, Subscriptions } from '../../models/client'; import { MessageAction } from '../../ui-utils'; import { messageArgs } from '../../../client/lib/utils/messageArgs'; import { EmojiPicker } from '../../emoji'; diff --git a/apps/meteor/app/reactions/client/methods/setReaction.js b/apps/meteor/app/reactions/client/methods/setReaction.js index 0da4565a49a0..26286e4e1130 100644 --- a/apps/meteor/app/reactions/client/methods/setReaction.js +++ b/apps/meteor/app/reactions/client/methods/setReaction.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { Messages, Rooms, Subscriptions } from '../../../models'; +import { Messages, Rooms, Subscriptions } from '../../../models/client'; import { callbacks } from '../../../../lib/callbacks'; import { emoji } from '../../../emoji'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/reactions/server/setReaction.js b/apps/meteor/app/reactions/server/setReaction.js index f477519e6d03..9cfbd264df88 100644 --- a/apps/meteor/app/reactions/server/setReaction.js +++ b/apps/meteor/app/reactions/server/setReaction.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import _ from 'underscore'; +import { EmojiCustom } from '@rocket.chat/models'; import { Messages, Rooms } from '../../models/server'; -import { EmojiCustom } from '../../models/server/raw'; import { callbacks } from '../../../lib/callbacks'; import { emoji } from '../../emoji/server'; import { isTheLastMessage, msgStream } from '../../lib/server'; diff --git a/apps/meteor/app/search/server/model/provider.js b/apps/meteor/app/search/server/model/provider.js index edd8ad805d15..5d236ad6d068 100644 --- a/apps/meteor/app/search/server/model/provider.js +++ b/apps/meteor/app/search/server/model/provider.js @@ -1,6 +1,6 @@ /* eslint no-unused-vars: [2, { "args": "none" }]*/ import SearchLogger from '../logger/logger'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; /** * Setting Object in order to manage settings loading for providers and admin ui display diff --git a/apps/meteor/app/settings/lib/settings.ts b/apps/meteor/app/settings/lib/settings.ts index 636d265acc26..bfa6ce2eabb4 100644 --- a/apps/meteor/app/settings/lib/settings.ts +++ b/apps/meteor/app/settings/lib/settings.ts @@ -16,9 +16,10 @@ export class SettingsBase { private regexCallbacks = new Map(); // private ts = new Date() - public get(_id: RegExp, callback: SettingCallback): void; - public get(_id: string, callback: SettingCallback): void; + public get(_id: RegExp, callback: SettingCallback): void; + + public get(_id: string, callback: SettingCallback): void; public get(_id: RegExp): SettingComposedValue[]; diff --git a/apps/meteor/app/settings/server/functions/validateSetting.ts b/apps/meteor/app/settings/server/functions/validateSetting.ts index 09db6d2cb74a..659bb12225c4 100644 --- a/apps/meteor/app/settings/server/functions/validateSetting.ts +++ b/apps/meteor/app/settings/server/functions/validateSetting.ts @@ -37,8 +37,9 @@ export const validateSetting = (_id: T['_id'], type: T['type } break; case 'select': + case 'lookup': if (typeof value !== 'string' && typeof value !== 'number') { - throw new Error(`Setting ${_id} is of type select but got ${typeof value}`); + throw new Error(`Setting ${_id} is of type ${type} but got ${typeof value}`); } break; case 'date': diff --git a/apps/meteor/app/settings/server/startup.ts b/apps/meteor/app/settings/server/startup.ts index 41d748578e60..9c43be76437a 100644 --- a/apps/meteor/app/settings/server/startup.ts +++ b/apps/meteor/app/settings/server/startup.ts @@ -3,6 +3,7 @@ import type { ISetting } from '@rocket.chat/core-typings'; import { Settings } from '../../models/server/models/Settings'; import { ICachedSettings } from './CachedSettings'; +// eslint-disable-next-line @typescript-eslint/naming-convention export function initializeSettings({ SettingsModel, settings }: { SettingsModel: Settings; settings: ICachedSettings }): void { SettingsModel.find().forEach((record: ISetting) => { settings.set(record); diff --git a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js index 1441701f92d6..abaf069b8e2f 100644 --- a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js +++ b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js @@ -3,8 +3,11 @@ import { slashCommands } from '../../utils'; settings.onload('SlackBridge_Enabled', (key, value) => { if (value) { - slashCommands.add('slackbridge-import', null, { - description: 'Import_old_messages_from_slackbridge', + slashCommands.add({ + command: 'slackbridge-import', + options: { + description: 'Import_old_messages_from_slackbridge', + }, }); } else { delete slashCommands.commands['slackbridge-import']; diff --git a/apps/meteor/app/slackbridge/server/RocketAdapter.js b/apps/meteor/app/slackbridge/server/RocketAdapter.js index 27f92b4fee45..302e35151ead 100644 --- a/apps/meteor/app/slackbridge/server/RocketAdapter.js +++ b/apps/meteor/app/slackbridge/server/RocketAdapter.js @@ -7,8 +7,8 @@ import { Random } from 'meteor/random'; import { rocketLogger } from './logger'; import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings'; -import { Messages, Rooms, Users } from '../../models'; +import { settings } from '../../settings/server'; +import { Messages, Rooms, Users } from '../../models/server'; import { createRoom, sendMessage, setUserAvatar } from '../../lib'; export default class RocketAdapter { diff --git a/apps/meteor/app/slackbridge/server/SlackAdapter.js b/apps/meteor/app/slackbridge/server/SlackAdapter.js index 4e597f43f1ef..efaa28e18e21 100644 --- a/apps/meteor/app/slackbridge/server/SlackAdapter.js +++ b/apps/meteor/app/slackbridge/server/SlackAdapter.js @@ -8,8 +8,8 @@ import { Meteor } from 'meteor/meteor'; import { slackLogger } from './logger'; import { SlackAPI } from './SlackAPI'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; -import { Messages, Rooms, Users } from '../../models'; -import { settings } from '../../settings'; +import { Messages, Rooms, Users } from '../../models/server'; +import { settings } from '../../settings/server'; import { deleteMessage, updateMessage, addUserToRoom, removeUserFromRoom, archiveRoom, unarchiveRoom, sendMessage } from '../../lib'; import { saveRoomName, saveRoomTopic } from '../../channel-settings'; import { FileUpload } from '../../file-upload'; diff --git a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js index e41cb15255cf..094c2124c241 100644 --- a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js +++ b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js @@ -4,7 +4,7 @@ import { Random } from 'meteor/random'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { SlackBridge } from './slackbridge'; -import { Rooms } from '../../models'; +import { Rooms } from '../../models/server'; import { msgStream } from '../../lib'; import { slashCommands } from '../../utils'; @@ -87,4 +87,4 @@ function SlackBridgeImport(command, params, item) { } } -slashCommands.add('slackbridge-import', SlackBridgeImport); +slashCommands.add({ command: 'slackbridge-import', callback: SlackBridgeImport }); diff --git a/apps/meteor/app/slashcommand-asciiarts/lib/gimme.ts b/apps/meteor/app/slashcommand-asciiarts/lib/gimme.ts index 2f7e92cd53c8..795c10aa3be7 100644 --- a/apps/meteor/app/slashcommand-asciiarts/lib/gimme.ts +++ b/apps/meteor/app/slashcommand-asciiarts/lib/gimme.ts @@ -13,7 +13,11 @@ function Gimme(_command: 'gimme', params: string, item: RequiredField { +slashCommands.add({ + command: 'shrug', + callback: (_command: 'shrug', params, item): void => { const msg = item; msg.msg = `${params} ¯\\_(ツ)_/¯`; Meteor.call('sendMessage', msg); }, - { + options: { description: 'Slash_Shrug_Description', params: 'your_message_optional', }, -); +}); diff --git a/apps/meteor/app/slashcommand-asciiarts/lib/tableflip.ts b/apps/meteor/app/slashcommand-asciiarts/lib/tableflip.ts index f71872060dc0..6bd3883a875e 100644 --- a/apps/meteor/app/slashcommand-asciiarts/lib/tableflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/lib/tableflip.ts @@ -6,15 +6,15 @@ import { slashCommands } from '../../utils/lib/slashCommand'; * @param {Object} message - The message object */ -slashCommands.add( - 'tableflip', - (_command, params, item): void => { +slashCommands.add({ + command: 'tableflip', + callback: (_command, params, item): void => { const msg = item; msg.msg = `${params} (╯°□°)╯︵ ┻━┻`; Meteor.call('sendMessage', msg); }, - { + options: { description: 'Slash_Tableflip_Description', params: 'your_message_optional', }, -); +}); diff --git a/apps/meteor/app/slashcommand-asciiarts/lib/unflip.ts b/apps/meteor/app/slashcommand-asciiarts/lib/unflip.ts index bc8525103450..2fbe8f75c2f5 100644 --- a/apps/meteor/app/slashcommand-asciiarts/lib/unflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/lib/unflip.ts @@ -6,15 +6,15 @@ import { slashCommands } from '../../utils/lib/slashCommand'; * @param {Object} message - The message object */ -slashCommands.add( - 'unflip', - (_command: 'unflip', params, item): void => { +slashCommands.add({ + command: 'unflip', + callback: (_command: 'unflip', params, item): void => { const msg = item; msg.msg = `${params} ┬─┬ ノ( ゜-゜ノ)`; Meteor.call('sendMessage', msg); }, - { + options: { description: 'Slash_TableUnflip_Description', params: 'your_message_optional', }, -); +}); diff --git a/apps/meteor/app/slashcommands-archiveroom/client/client.ts b/apps/meteor/app/slashcommands-archiveroom/client/client.ts index bee69247b2f2..c24763106684 100644 --- a/apps/meteor/app/slashcommands-archiveroom/client/client.ts +++ b/apps/meteor/app/slashcommands-archiveroom/client/client.ts @@ -1,15 +1,11 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'archive', - undefined, - { +slashCommands.add({ + command: 'archive', + options: { description: 'Archive', params: '#channel', permission: 'archive-room', }, - undefined, - false, - undefined, - undefined, -); + providesPreview: false, +}); diff --git a/apps/meteor/app/slashcommands-archiveroom/server/server.ts b/apps/meteor/app/slashcommands-archiveroom/server/server.ts index b6be536a2ce2..1fdfea6f5242 100644 --- a/apps/meteor/app/slashcommands-archiveroom/server/server.ts +++ b/apps/meteor/app/slashcommands-archiveroom/server/server.ts @@ -6,9 +6,9 @@ import { slashCommands } from '../../utils/lib/slashCommand'; import { api } from '../../../server/sdk/api'; import { settings } from '../../settings/server'; -slashCommands.add( - 'archive', - function Archive(_command, params, item): void { +slashCommands.add({ + command: 'archive', + callback: function Archive(_command, params, item): void { let channel = params.trim(); let room; @@ -64,9 +64,9 @@ slashCommands.add( }), }); }, - { + options: { description: 'Archive', params: '#channel', permission: 'archive-room', }, -); +}); diff --git a/apps/meteor/app/slashcommands-bridge/client/index.ts b/apps/meteor/app/slashcommands-bridge/client/index.ts deleted file mode 100644 index d7beb4be531e..000000000000 --- a/apps/meteor/app/slashcommands-bridge/client/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { slashCommands } from '../../utils/lib/slashCommand'; - -slashCommands.add( - 'bridge', - undefined, - { - description: 'Invites_an_user_to_a_bridged_room', - params: '#command #user', - }, - undefined, - false, - undefined, - undefined, -); diff --git a/apps/meteor/app/slashcommands-bridge/server/index.ts b/apps/meteor/app/slashcommands-bridge/server/index.ts deleted file mode 100644 index b9396a596312..000000000000 --- a/apps/meteor/app/slashcommands-bridge/server/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; - -import { slashCommands } from '../../utils/lib/slashCommand'; -import { federationRoomServiceSender } from '../../federation-v2/server'; -import { FederationRoomSenderConverter } from '../../federation-v2/server/infrastructure/rocket-chat/converters/RoomSender'; - -function Bridge(_command: 'bridge', stringParams: string | undefined, item: Record): void { - if (_command !== 'bridge' || !Match.test(stringParams, String)) { - return; - } - - const [command, ...params] = stringParams.split(' '); - - const { rid: roomId } = item; - - switch (command) { - case 'invite': - // Invite a user - // Example: /bridge invite rc_helena:b.rc.allskar.com - const [userId] = params; - - const currentUserId = Meteor.userId(); - - if (currentUserId) { - const invitee = `@${userId.replace('@', '')}`; - Promise.await( - federationRoomServiceSender.inviteUserToAFederatedRoom( - FederationRoomSenderConverter.toRoomInviteUserDto(currentUserId, roomId, invitee), - ), - ); - } - - break; - } -} - -slashCommands.add('bridge', Bridge, { - description: 'Invites_an_user_to_a_bridged_room', - params: '#command #user', -}); diff --git a/apps/meteor/app/slashcommands-create/client/client.ts b/apps/meteor/app/slashcommands-create/client/client.ts index f618d472f096..299db606db9c 100644 --- a/apps/meteor/app/slashcommands-create/client/client.ts +++ b/apps/meteor/app/slashcommands-create/client/client.ts @@ -1,15 +1,11 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'create', - undefined, - { +slashCommands.add({ + command: 'create', + options: { description: 'Create_A_New_Channel', params: '#channel', permission: ['create-c', 'create-p'], }, - undefined, - false, - undefined, - undefined, -); + providesPreview: false, +}); diff --git a/apps/meteor/app/slashcommands-create/server/server.ts b/apps/meteor/app/slashcommands-create/server/server.ts index d47b84da6787..0ff5d17e36b0 100644 --- a/apps/meteor/app/slashcommands-create/server/server.ts +++ b/apps/meteor/app/slashcommands-create/server/server.ts @@ -6,9 +6,9 @@ import { Rooms } from '../../models/server'; import { slashCommands } from '../../utils/lib/slashCommand'; import { api } from '../../../server/sdk/api'; -slashCommands.add( - 'create', - function Create(_command: 'create', params, item): void { +slashCommands.add({ + command: 'create', + callback: function Create(_command: 'create', params, item): void { function getParams(str: string): string[] { const regex = /(--(\w+))+/g; const result = []; @@ -54,9 +54,9 @@ slashCommands.add( Meteor.call('createChannel', channelStr, []); }, - { + options: { description: 'Create_A_New_Channel', params: '#channel', permission: ['create-c', 'create-p'], }, -); +}); diff --git a/apps/meteor/app/slashcommands-help/server/server.ts b/apps/meteor/app/slashcommands-help/server/server.ts index 00bd14934e9d..815f6df9323b 100644 --- a/apps/meteor/app/slashcommands-help/server/server.ts +++ b/apps/meteor/app/slashcommands-help/server/server.ts @@ -16,9 +16,9 @@ interface IHelpCommand { command: string; } -slashCommands.add( - 'help', - function Help(_command, _params, item): void { +slashCommands.add({ + command: 'help', + callback: function Help(_command, _params, item): void { const userId = Meteor.userId() as string; const user = Users.findOneById(userId); @@ -66,7 +66,7 @@ slashCommands.add( }); }); }, - { + options: { description: 'Show_the_keyboard_shortcut_list', }, -); +}); diff --git a/apps/meteor/app/slashcommands-hide/client/hide.ts b/apps/meteor/app/slashcommands-hide/client/hide.ts index 61612bf1ff8e..99c1eaea7049 100644 --- a/apps/meteor/app/slashcommands-hide/client/hide.ts +++ b/apps/meteor/app/slashcommands-hide/client/hide.ts @@ -1,6 +1,9 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add('hide', undefined, { - description: 'Hide_room', - params: '#room', +slashCommands.add({ + command: 'hide', + options: { + description: 'Hide_room', + params: '#room', + }, }); diff --git a/apps/meteor/app/slashcommands-hide/server/hide.ts b/apps/meteor/app/slashcommands-hide/server/hide.ts index 398344a8c70e..dbe94d0d7d1c 100644 --- a/apps/meteor/app/slashcommands-hide/server/hide.ts +++ b/apps/meteor/app/slashcommands-hide/server/hide.ts @@ -11,9 +11,9 @@ import { api } from '../../../server/sdk/api'; * @param {Object} message - The message object */ -slashCommands.add( - 'hide', - (_command: 'hide', param, item): void => { +slashCommands.add({ + command: 'hide', + callback: (_command: 'hide', param, item): void => { const room = param.trim(); const userId = Meteor.userId(); if (!userId) { @@ -71,5 +71,5 @@ slashCommands.add( } }); }, - { description: 'Hide_room', params: '#room' }, -); + options: { description: 'Hide_room', params: '#room' }, +}); diff --git a/apps/meteor/app/slashcommands-invite/client/client.ts b/apps/meteor/app/slashcommands-invite/client/client.ts index 4a494287d0ae..729073b785d8 100644 --- a/apps/meteor/app/slashcommands-invite/client/client.ts +++ b/apps/meteor/app/slashcommands-invite/client/client.ts @@ -1,15 +1,11 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'invite', - undefined, - { +slashCommands.add({ + command: 'invite', + options: { description: 'Invite_user_to_join_channel', params: '@username', permission: 'add-user-to-joined-room', }, - undefined, - false, - undefined, - undefined, -); + providesPreview: false, +}); diff --git a/apps/meteor/app/slashcommands-invite/server/server.ts b/apps/meteor/app/slashcommands-invite/server/server.ts index 1bc427536e83..23b60387df27 100644 --- a/apps/meteor/app/slashcommands-invite/server/server.ts +++ b/apps/meteor/app/slashcommands-invite/server/server.ts @@ -10,9 +10,9 @@ import { api } from '../../../server/sdk/api'; * Invite is a named function that will replace /invite commands * @param {Object} message - The message object */ -slashCommands.add( - 'invite', - (_command: 'invite', params, item): void => { +slashCommands.add({ + command: 'invite', + callback: (_command: 'invite', params, item): void => { const usernames = params .split(/[\s,]/) .map((username) => username.replace(/(^@)|( @)/, '')) @@ -76,9 +76,9 @@ slashCommands.add( } }); }, - { + options: { description: 'Invite_user_to_join_channel', params: '@username', permission: 'add-user-to-joined-room', }, -); +}); diff --git a/apps/meteor/app/slashcommands-inviteall/client/client.ts b/apps/meteor/app/slashcommands-inviteall/client/client.ts index d09c9d9e908d..f8ab40953d27 100644 --- a/apps/meteor/app/slashcommands-inviteall/client/client.ts +++ b/apps/meteor/app/slashcommands-inviteall/client/client.ts @@ -1,12 +1,18 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add('invite-all-to', undefined, { - description: 'Invite_user_to_join_channel_all_to', - params: '#room', - permission: ['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], +slashCommands.add({ + command: 'invite-all-to', + options: { + description: 'Invite_user_to_join_channel_all_to', + params: '#room', + permission: ['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], + }, }); -slashCommands.add('invite-all-from', undefined, { - description: 'Invite_user_to_join_channel_all_from', - params: '#room', - permission: 'add-user-to-joined-room', +slashCommands.add({ + command: 'invite-all-from', + options: { + description: 'Invite_user_to_join_channel_all_from', + params: '#room', + permission: 'add-user-to-joined-room', + }, }); diff --git a/apps/meteor/app/slashcommands-inviteall/server/server.ts b/apps/meteor/app/slashcommands-inviteall/server/server.ts index 4c05b3e1b19e..3f5225a4a6c3 100644 --- a/apps/meteor/app/slashcommands-inviteall/server/server.ts +++ b/apps/meteor/app/slashcommands-inviteall/server/server.ts @@ -93,14 +93,22 @@ function inviteAll(type: T): SlashCommand['callback'] { }; } -slashCommands.add('invite-all-to', inviteAll('to'), { - description: 'Invite_user_to_join_channel_all_to', - params: '#room', - permission: ['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], +slashCommands.add({ + command: 'invite-all-to', + callback: inviteAll('to'), + options: { + description: 'Invite_user_to_join_channel_all_to', + params: '#room', + permission: ['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], + }, }); -slashCommands.add('invite-all-from', inviteAll('from'), { - description: 'Invite_user_to_join_channel_all_from', - params: '#room', - permission: 'add-user-to-joined-room', +slashCommands.add({ + command: 'invite-all-from', + callback: inviteAll('from'), + options: { + description: 'Invite_user_to_join_channel_all_from', + params: '#room', + permission: 'add-user-to-joined-room', + }, }); module.exports = inviteAll; diff --git a/apps/meteor/app/slashcommands-join/client/client.ts b/apps/meteor/app/slashcommands-join/client/client.ts index fcf56d6e55a6..71374b69d6c3 100644 --- a/apps/meteor/app/slashcommands-join/client/client.ts +++ b/apps/meteor/app/slashcommands-join/client/client.ts @@ -2,19 +2,18 @@ import { Meteor } from 'meteor/meteor'; import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'join', - undefined, - { +slashCommands.add({ + command: 'join', + options: { description: 'Join_the_given_channel', params: '#channel', permission: 'view-c-room', }, - function (err, _result: unknown, params: Record) { + result(err, _result: unknown, params: Record) { if ((err as Meteor.Error).error === 'error-user-already-in-room') { params.cmd = 'open'; params.msg.msg = params.msg.msg.replace('join', 'open'); return slashCommands.run('open', params.params, params.msg, ''); } }, -); +}); diff --git a/apps/meteor/app/slashcommands-join/server/server.ts b/apps/meteor/app/slashcommands-join/server/server.ts index 1d65feb1f979..5ea5c5c0b9bb 100644 --- a/apps/meteor/app/slashcommands-join/server/server.ts +++ b/apps/meteor/app/slashcommands-join/server/server.ts @@ -6,9 +6,9 @@ import { settings } from '../../settings/server'; import { slashCommands } from '../../utils/lib/slashCommand'; import { api } from '../../../server/sdk/api'; -slashCommands.add( - 'join', - (_command: 'join', params, item): void => { +slashCommands.add({ + command: 'join', + callback: (_command: 'join', params, item): void => { let channel = params.trim(); if (channel === '') { return; @@ -46,9 +46,9 @@ slashCommands.add( Meteor.call('joinRoom', room._id); }, - { + options: { description: 'Join_the_given_channel', params: '#channel', permission: 'view-c-room', }, -); +}); diff --git a/apps/meteor/app/slashcommands-kick/client/client.ts b/apps/meteor/app/slashcommands-kick/client/client.ts index f3a2b50410aa..23dee27e5d2e 100644 --- a/apps/meteor/app/slashcommands-kick/client/client.ts +++ b/apps/meteor/app/slashcommands-kick/client/client.ts @@ -1,17 +1,17 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'kick', - function (_command: 'kick', params: string) { +slashCommands.add({ + command: 'kick', + callback(_command: 'kick', params: string) { const username = params.trim(); if (username === '') { return; } return username.replace('@', ''); }, - { + options: { description: 'Remove_someone_from_room', params: '@username', permission: 'remove-user', }, -); +}); diff --git a/apps/meteor/app/slashcommands-kick/server/server.ts b/apps/meteor/app/slashcommands-kick/server/server.ts index 70d7c44db543..f385269cc4d1 100644 --- a/apps/meteor/app/slashcommands-kick/server/server.ts +++ b/apps/meteor/app/slashcommands-kick/server/server.ts @@ -7,9 +7,9 @@ import { settings } from '../../settings/server'; import { slashCommands } from '../../utils/lib/slashCommand'; import { api } from '../../../server/sdk/api'; -slashCommands.add( - 'kick', - function (_command: 'kick', params, item): void { +slashCommands.add({ + command: 'kick', + callback: (_command: 'kick', params, item): void => { const username = params.trim().replace('@', ''); if (username === '') { return; @@ -47,9 +47,9 @@ slashCommands.add( const { rid } = item; Meteor.call('removeUserFromRoom', { rid, username }); }, - { + options: { description: 'Remove_someone_from_room', params: '@username', permission: 'remove-user', }, -); +}); diff --git a/apps/meteor/app/slashcommands-leave/server/leave.ts b/apps/meteor/app/slashcommands-leave/server/leave.ts index d99def1db777..566de8295b2e 100644 --- a/apps/meteor/app/slashcommands-leave/server/leave.ts +++ b/apps/meteor/app/slashcommands-leave/server/leave.ts @@ -26,11 +26,19 @@ const Leave: SlashCommand<'leave'>['callback'] = function Leave(_command, _param } }; -slashCommands.add('leave', Leave, { - description: 'Leave_the_current_channel', - permission: ['leave-c', 'leave-p'], +slashCommands.add({ + command: 'leave', + callback: Leave, + options: { + description: 'Leave_the_current_channel', + permission: ['leave-c', 'leave-p'], + }, }); -slashCommands.add('part', Leave, { - description: 'Leave_the_current_channel', - permission: ['leave-c', 'leave-p'], +slashCommands.add({ + command: 'part', + callback: Leave, + options: { + description: 'Leave_the_current_channel', + permission: ['leave-c', 'leave-p'], + }, }); diff --git a/apps/meteor/app/slashcommands-me/server/me.ts b/apps/meteor/app/slashcommands-me/server/me.ts index 9907474dbf15..945cd87620b7 100644 --- a/apps/meteor/app/slashcommands-me/server/me.ts +++ b/apps/meteor/app/slashcommands-me/server/me.ts @@ -7,17 +7,17 @@ import { slashCommands } from '../../utils/lib/slashCommand'; * Me is a named function that will replace /me commands * @param {Object} message - The message object */ -slashCommands.add( - 'me', - function Me(_command: 'me', params, item): void { +slashCommands.add({ + command: 'me', + callback: function Me(_command: 'me', params, item): void { if (s.trim(params)) { const msg = item; msg.msg = `_${params}_`; Meteor.call('sendMessage', msg); } }, - { + options: { description: 'Displays_action_text', params: 'your_message', }, -); +}); diff --git a/apps/meteor/app/slashcommands-msg/server/server.ts b/apps/meteor/app/slashcommands-msg/server/server.ts index d1d4cc60ede0..1dc5313bfa10 100644 --- a/apps/meteor/app/slashcommands-msg/server/server.ts +++ b/apps/meteor/app/slashcommands-msg/server/server.ts @@ -11,9 +11,9 @@ import { api } from '../../../server/sdk/api'; * Msg is a named function that will replace /msg commands */ -slashCommands.add( - 'msg', - function Msg(_command: 'msg', params, item): void { +slashCommands.add({ + command: 'msg', + callback: function Msg(_command: 'msg', params, item): void { const trimmedParams = params.trim(); const separator = trimmedParams.indexOf(' '); const userId = Meteor.userId() as string; @@ -46,9 +46,9 @@ slashCommands.add( }; Meteor.call('sendMessage', msgObject); }, - { + options: { description: 'Direct_message_someone', params: '@username ', permission: 'create-d', }, -); +}); diff --git a/apps/meteor/app/slashcommands-mute/server/mute.ts b/apps/meteor/app/slashcommands-mute/server/mute.ts index 0353337a162d..5f9ab47c6117 100644 --- a/apps/meteor/app/slashcommands-mute/server/mute.ts +++ b/apps/meteor/app/slashcommands-mute/server/mute.ts @@ -10,9 +10,9 @@ import { api } from '../../../server/sdk/api'; * Mute is a named function that will replace /mute commands */ -slashCommands.add( - 'mute', - function Mute(_command: 'mute', params, item): void { +slashCommands.add({ + command: 'mute', + callback: function Mute(_command: 'mute', params, item): void { const username = params.trim().replace('@', ''); if (username === '') { return; @@ -47,9 +47,9 @@ slashCommands.add( username, }); }, - { + options: { description: 'Mute_someone_in_room', params: '@username', permission: 'mute-user', }, -); +}); diff --git a/apps/meteor/app/slashcommands-mute/server/unmute.ts b/apps/meteor/app/slashcommands-mute/server/unmute.ts index 82d513302478..602268da65f4 100644 --- a/apps/meteor/app/slashcommands-mute/server/unmute.ts +++ b/apps/meteor/app/slashcommands-mute/server/unmute.ts @@ -10,9 +10,9 @@ import { api } from '../../../server/sdk/api'; * Unmute is a named function that will replace /unmute commands */ -slashCommands.add( - 'unmute', - function Unmute(_command, params, item): void | Promise { +slashCommands.add({ + command: 'unmute', + callback: function Unmute(_command, params, item): void | Promise { const username = params.trim().replace('@', ''); if (username === '') { return; @@ -46,9 +46,9 @@ slashCommands.add( username, }); }, - { + options: { description: 'Unmute_someone_in_room', params: '@username', permission: 'mute-user', }, -); +}); diff --git a/apps/meteor/app/slashcommands-open/client/client.ts b/apps/meteor/app/slashcommands-open/client/client.ts index 14dd0ff9fb09..c2622f00dfbe 100644 --- a/apps/meteor/app/slashcommands-open/client/client.ts +++ b/apps/meteor/app/slashcommands-open/client/client.ts @@ -5,9 +5,9 @@ import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; import { slashCommands } from '../../utils/lib/slashCommand'; import { Subscriptions, ChatSubscription } from '../../models/client'; -slashCommands.add( - 'open', - function Open(_command, params): void { +slashCommands.add({ + command: 'open', + callback: function Open(_command, params): void { const dict: Record = { '#': ['c', 'p'], '@': ['d'], @@ -38,10 +38,10 @@ slashCommands.add( roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); }); }, - { + options: { description: 'Opens_a_channel_group_or_direct_message', params: 'room_name', clientOnly: true, permission: ['view-c-room', 'view-c-room', 'view-d-room', 'view-joined-room', 'create-d'], }, -); +}); diff --git a/apps/meteor/app/slashcommands-status/client/status.ts b/apps/meteor/app/slashcommands-status/client/status.ts index 2a47886488af..188707a9c809 100644 --- a/apps/meteor/app/slashcommands-status/client/status.ts +++ b/apps/meteor/app/slashcommands-status/client/status.ts @@ -6,9 +6,9 @@ import { settings } from '../../settings/server'; import { api } from '../../../server/sdk/api'; import { handleError } from '../../../client/lib/utils/handleError'; -slashCommands.add( - 'status', - function Status(_command, params, item): void { +slashCommands.add({ + command: 'status', + callback: function Status(_command, params, item): void { const userId = Meteor.userId() as string; Meteor.call('setUserStatus', null, params, (err: Meteor.Error) => { @@ -20,8 +20,8 @@ slashCommands.add( }); }); }, - { + options: { description: 'Slash_Status_Description', params: 'Slash_Status_Params', }, -); +}); diff --git a/apps/meteor/app/slashcommands-status/server/status.ts b/apps/meteor/app/slashcommands-status/server/status.ts index 33ae7f77e1e8..d72687ceb333 100644 --- a/apps/meteor/app/slashcommands-status/server/status.ts +++ b/apps/meteor/app/slashcommands-status/server/status.ts @@ -6,9 +6,9 @@ import { settings } from '../../settings/server'; import { api } from '../../../server/sdk/api'; import { Users } from '../../models/server'; -slashCommands.add( - 'status', - function Status(_command: 'status', params, item): void { +slashCommands.add({ + command: 'status', + callback: function Status(_command: 'status', params, item): void { const userId = Meteor.userId() as string; Meteor.call('setUserStatus', null, params, (err: Meteor.Error) => { @@ -30,8 +30,8 @@ slashCommands.add( } }); }, - { + options: { description: 'Slash_Status_Description', params: 'Slash_Status_Params', }, -); +}); diff --git a/apps/meteor/app/slashcommands-topic/client/topic.ts b/apps/meteor/app/slashcommands-topic/client/topic.ts index 3c30011fb6ea..e7093e94e314 100644 --- a/apps/meteor/app/slashcommands-topic/client/topic.ts +++ b/apps/meteor/app/slashcommands-topic/client/topic.ts @@ -6,9 +6,9 @@ import { callbacks } from '../../../lib/callbacks'; import { hasPermission } from '../../authorization/client'; import { handleError } from '../../../client/lib/utils/handleError'; -slashCommands.add( - 'topic', - function Topic(_command: 'topic', params, item): void { +slashCommands.add({ + command: 'topic', + callback: function Topic(_command: 'topic', params, item): void { if (Meteor.isClient && hasPermission('edit-room', item.rid)) { Meteor.call('saveRoomSettings', item.rid, 'roomTopic', params, (err: Meteor.Error) => { if (err) { @@ -24,9 +24,9 @@ slashCommands.add( }); } }, - { + options: { description: 'Slash_Topic_Description', params: 'Slash_Topic_Params', permission: 'edit-room', }, -); +}); diff --git a/apps/meteor/app/slashcommands-topic/server/topic.ts b/apps/meteor/app/slashcommands-topic/server/topic.ts index 0256b6bb7fae..b4b2565ed54f 100644 --- a/apps/meteor/app/slashcommands-topic/server/topic.ts +++ b/apps/meteor/app/slashcommands-topic/server/topic.ts @@ -3,9 +3,9 @@ import { Meteor } from 'meteor/meteor'; import { slashCommands } from '../../utils/lib/slashCommand'; import { hasPermission } from '../../authorization/server/functions/hasPermission'; -slashCommands.add( - 'topic', - function Topic(_command: 'topic', params, item): void { +slashCommands.add({ + command: 'topic', + callback: function Topic(_command: 'topic', params, item): void { if (Meteor.isServer && hasPermission(Meteor.userId() as string, 'edit-room', item.rid)) { Meteor.call('saveRoomSettings', item.rid, 'roomTopic', params, (err: Meteor.Error) => { if (err) { @@ -14,9 +14,9 @@ slashCommands.add( }); } }, - { + options: { description: 'Slash_Topic_Description', params: 'Slash_Topic_Params', permission: 'edit-room', }, -); +}); diff --git a/apps/meteor/app/slashcommands-unarchiveroom/client/client.ts b/apps/meteor/app/slashcommands-unarchiveroom/client/client.ts index aab7690912a0..2fed1e1c7802 100644 --- a/apps/meteor/app/slashcommands-unarchiveroom/client/client.ts +++ b/apps/meteor/app/slashcommands-unarchiveroom/client/client.ts @@ -1,15 +1,11 @@ import { slashCommands } from '../../utils/lib/slashCommand'; -slashCommands.add( - 'unarchive', - undefined, - { +slashCommands.add({ + command: 'unarchive', + options: { description: 'Unarchive', params: '#channel', permission: 'unarchive-room', }, - undefined, - false, - undefined, - undefined, -); + providesPreview: false, +}); diff --git a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts index 2bc7318d75aa..fa74c5fd3502 100644 --- a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts +++ b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts @@ -8,9 +8,9 @@ import { api } from '../../../server/sdk/api'; import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../definition/IRoomTypeConfig'; -slashCommands.add( - 'unarchive', - function Unarchive(_command: 'unarchive', params, item): void { +slashCommands.add({ + command: 'unarchive', + callback: function Unarchive(_command: 'unarchive', params, item): void { let channel = params.trim(); let room; @@ -62,9 +62,9 @@ slashCommands.add( }), }); }, - { + options: { description: 'Unarchive', params: '#channel', permission: 'unarchive-room', }, -); +}); diff --git a/apps/meteor/app/smarsh-connector/server/functions/generateEml.js b/apps/meteor/app/smarsh-connector/server/functions/generateEml.js index a4244e3f9501..fea8bc9d4d23 100644 --- a/apps/meteor/app/smarsh-connector/server/functions/generateEml.js +++ b/apps/meteor/app/smarsh-connector/server/functions/generateEml.js @@ -2,10 +2,10 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import _ from 'underscore'; import moment from 'moment'; +import { SmarshHistory } from '@rocket.chat/models'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { Rooms, Messages, Users } from '../../../models/server'; -import { SmarshHistory } from '../../../models/server/raw'; import { MessageTypes } from '../../../ui-utils'; import { smarsh } from '../lib/rocketchat'; import 'moment-timezone'; diff --git a/apps/meteor/app/smarsh-connector/server/functions/sendEmail.js b/apps/meteor/app/smarsh-connector/server/functions/sendEmail.js index 67fcfde02e67..ed062693ad57 100644 --- a/apps/meteor/app/smarsh-connector/server/functions/sendEmail.js +++ b/apps/meteor/app/smarsh-connector/server/functions/sendEmail.js @@ -5,10 +5,10 @@ // files: ['i3nc9l3mn'] // } import { UploadFS } from 'meteor/jalik:ufs'; +import { Uploads } from '@rocket.chat/models'; import * as Mailer from '../../../mailer'; -import { Uploads } from '../../../models/server/raw'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { smarsh } from '../lib/rocketchat'; smarsh.sendEmail = async (data) => { diff --git a/apps/meteor/app/smarsh-connector/server/settings.js b/apps/meteor/app/smarsh-connector/server/settings.js index 77c8f4e3b3de..f5fe44cca1b5 100644 --- a/apps/meteor/app/smarsh-connector/server/settings.js +++ b/apps/meteor/app/smarsh-connector/server/settings.js @@ -1,7 +1,6 @@ -import moment from 'moment'; +import moment from 'moment-timezone'; import { settingsRegistry } from '../../settings/server'; -import 'moment-timezone'; settingsRegistry.addGroup('Smarsh', function addSettings() { this.add('Smarsh_Enabled', false, { diff --git a/apps/meteor/app/sms/server/services/mobex.js b/apps/meteor/app/sms/server/services/mobex.js index 5f92c2920305..ad0ceb7ffec0 100644 --- a/apps/meteor/app/sms/server/services/mobex.js +++ b/apps/meteor/app/sms/server/services/mobex.js @@ -1,7 +1,7 @@ import { HTTP } from 'meteor/http'; import { Base64 } from 'meteor/base64'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { SMS } from '../SMS'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/sms/server/services/twilio.js b/apps/meteor/app/sms/server/services/twilio.js index 3fab2b7520a8..c0213051b91b 100644 --- a/apps/meteor/app/sms/server/services/twilio.js +++ b/apps/meteor/app/sms/server/services/twilio.js @@ -3,7 +3,7 @@ import twilio from 'twilio'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import filesize from 'filesize'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { SMS } from '../SMS'; import { fileUploadIsValidContentType } from '../../../utils/lib/fileUploadRestrictions'; import { api } from '../../../../server/sdk/api'; diff --git a/apps/meteor/app/sms/server/services/voxtelesys.js b/apps/meteor/app/sms/server/services/voxtelesys.js index bc8347caa10b..c5b7a2e88a3e 100644 --- a/apps/meteor/app/sms/server/services/voxtelesys.js +++ b/apps/meteor/app/sms/server/services/voxtelesys.js @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import filesize from 'filesize'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { SMS } from '../SMS'; import { fileUploadIsValidContentType } from '../../../utils/lib/fileUploadRestrictions'; import { mime } from '../../../utils/lib/mimeTypes'; diff --git a/apps/meteor/app/statistics/server/functions/getLastStatistics.js b/apps/meteor/app/statistics/server/functions/getLastStatistics.js index c405109d6dfd..5fc6bbbb1620 100644 --- a/apps/meteor/app/statistics/server/functions/getLastStatistics.js +++ b/apps/meteor/app/statistics/server/functions/getLastStatistics.js @@ -1,6 +1,7 @@ +import { Statistics } from '@rocket.chat/models'; + import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { statistics } from '../lib/statistics'; -import { Statistics } from '../../../models/server/raw'; export async function getLastStatistics({ userId, refresh }) { if (!(await hasPermissionAsync(userId, 'view-statistics'))) { diff --git a/apps/meteor/app/statistics/server/functions/getStatistics.ts b/apps/meteor/app/statistics/server/functions/getStatistics.ts index da536355c494..4f4cefc64a9f 100644 --- a/apps/meteor/app/statistics/server/functions/getStatistics.ts +++ b/apps/meteor/app/statistics/server/functions/getStatistics.ts @@ -1,8 +1,8 @@ -import type { SortOptionObject, SchemaMember } from 'mongodb'; +import type { FindOptions, SchemaMember } from 'mongodb'; import type { IStats } from '@rocket.chat/core-typings'; +import { Statistics } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Statistics } from '../../../models/server/raw'; type GetStatisticsParams = { userId: string; @@ -10,7 +10,7 @@ type GetStatisticsParams = { pagination: { offset: number; count?: number; - sort?: SortOptionObject; + sort?: FindOptions['sort']; fields?: SchemaMember; }; }; @@ -26,16 +26,14 @@ export async function getStatistics({ throw new Error('error-not-allowed'); } - const cursor = Statistics.find(query, { + const { cursor, totalCount } = Statistics.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, limit: count, projection: fields, }); - const total = await cursor.count(); - - const statistics = await cursor.toArray(); + const [statistics, total] = await Promise.all([cursor.toArray(), totalCount]); return { statistics, diff --git a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts index f52bce1ec94b..dec524ca3705 100644 --- a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts +++ b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts @@ -2,14 +2,15 @@ import { Meteor } from 'meteor/meteor'; import { SyncedCron } from 'meteor/littledata:synced-cron'; import UAParser from 'ua-parser-js'; import mem from 'mem'; -import type { ISession, ISessionDevice, ISocketConnection, IUser } from '@rocket.chat/core-typings'; +import type { ISession, ISessionDevice, ISocketConnectionLogged, IUser } from '@rocket.chat/core-typings'; +import { Sessions, Users } from '@rocket.chat/models'; import { UAParserMobile, UAParserDesktop } from './UAParserCustom'; -import { Sessions, Users } from '../../../models/server/raw'; -import { aggregates } from '../../../models/server/raw/Sessions'; +import { aggregates } from '../../../../server/models/raw/Sessions'; import { Logger } from '../../../../server/lib/logger/Logger'; import { getMostImportantRole } from '../../../../lib/roles/getMostImportantRole'; import { sauEvents } from '../../../../server/services/sauMonitor/events'; +import { getClientAddress } from '../../../../server/lib/getClientAddress'; type DateObj = { day: number; month: number; year: number }; @@ -23,7 +24,7 @@ const logger = new Logger('SAUMonitor'); const getUserRoles = mem( async (userId: string): Promise => { - const user = await Users.findOneById(userId, { projection: { roles: 1 } }); + const user = await Users.findOneById>(userId, { projection: { roles: 1 } }); return user?.roles || []; }, @@ -121,20 +122,25 @@ export class SAUMonitorClass { if (!this.isRunning()) { return; } + const { id: sessionId } = connection; - await Sessions.logoutByInstanceIdAndSessionIdAndUserId(connection.instanceId, connection.id, userId); + await Sessions.logoutBySessionIdAndUserId({ sessionId, userId }); }); } private async _handleSession( - connection: ISocketConnection, + connection: ISocketConnectionLogged, params: Pick, ): Promise { const data = this._getConnectionInfo(connection, params); + if (!data) { return; } - await Sessions.createOrUpdate(data); + + const searchTerm = this._getSearchTerm(data); + + await Sessions.insertOne({ ...data, searchTerm, createdAt: new Date() }); } private async _finishSessionsFromDate(yesterday: Date, today: Date): Promise { @@ -181,30 +187,37 @@ export class SAUMonitorClass { // TODO missing an action to perform on dangling sessions (for example remove sessions not closed one month ago) } + private _getSearchTerm(session: Omit): string { + return [session.device?.name, session.device?.type, session.device?.os.name, session.sessionId, session.userId] + .filter(Boolean) + .join(''); + } + private _getConnectionInfo( - connection: ISocketConnection, + connection: ISocketConnectionLogged, params: Pick, - ): Omit | undefined { + ): Omit | undefined { if (!connection) { return; } - const ip = connection.clientAddress || connection.httpHeaders?.['x-real-ip'] || connection.httpHeaders?.['x-forwarded-for']; + const ip = getClientAddress(connection); - const host = connection.httpHeaders?.host || ''; + const host = connection.httpHeaders?.host ?? ''; return { type: 'session', sessionId: connection.id, instanceId: connection.instanceId, - ip: (Array.isArray(ip) ? ip[0] : ip) || '', + ...(connection.loginToken && { loginToken: connection.loginToken }), + ip, host, ...this._getUserAgentInfo(connection), ...params, }; } - private _getUserAgentInfo(connection: ISocketConnection): { device: ISessionDevice } | undefined { + private _getUserAgentInfo(connection: ISocketConnectionLogged): { device: ISessionDevice } | undefined { if (!connection?.httpHeaders?.['user-agent']) { return; } @@ -315,13 +328,6 @@ export class SAUMonitorClass { date.setDate(date.getDate() - 0); // yesterday const yesterday = getDateObj(date); - const match = { - type: 'session', - year: { $lte: yesterday.year }, - month: { $lte: yesterday.month }, - day: { $lte: yesterday.day }, - }; - for await (const record of aggregates.dailySessionsOfYesterday(Sessions.col, yesterday)) { await Sessions.updateOne( { _id: `${record.userId}-${record.year}-${record.month}-${record.day}` }, @@ -330,11 +336,19 @@ export class SAUMonitorClass { ); } - await Sessions.updateMany(match, { - $set: { - type: 'computed-session', - _computedAt: new Date(), + await Sessions.updateMany( + { + type: 'session', + year: { $lte: yesterday.year }, + month: { $lte: yesterday.month }, + day: { $lte: yesterday.day }, }, - }); + { + $set: { + type: 'computed-session', + _computedAt: new Date(), + }, + }, + ); } } diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 051456c55ae7..5f1bf083f87b 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -5,12 +5,6 @@ import _ from 'underscore'; import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; import type { IRoom, IStats } from '@rocket.chat/core-typings'; - -import { Settings, Users, Rooms, Subscriptions, Messages, LivechatVisitors } from '../../../models/server'; -import { settings } from '../../../settings/server'; -import { Info, getMongoInfo } from '../../../utils/server'; -import { getControl } from '../../../../server/lib/migrations'; -import { getStatistics as federationGetStatistics } from '../../../federation/server/functions/dashboard'; import { NotificationQueue, Users as UsersRaw, @@ -21,17 +15,25 @@ import { Invites, Uploads, LivechatDepartment, + LivechatVisitors, EmailInbox, LivechatBusinessHours, Messages as MessagesRaw, + Roles as RolesRaw, InstanceStatus, -} from '../../../models/server/raw'; +} from '@rocket.chat/models'; + +import { Settings, Users, Rooms, Subscriptions, Messages } from '../../../models/server'; +import { settings } from '../../../settings/server'; +import { Info, getMongoInfo } from '../../../utils/server'; +import { getControl } from '../../../../server/lib/migrations'; +import { getStatistics as federationGetStatistics } from '../../../federation/server/functions/dashboard'; import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; import { getAppsStatistics } from './getAppsStatistics'; import { getImporterStatistics } from './getImporterStatistics'; import { getServicesStatistics } from './getServicesStatistics'; import { getStatistics as getEnterpriseStatistics } from '../../../../ee/app/license/server'; -import { Analytics, Team } from '../../../../server/sdk'; +import { Analytics, Team, VideoConf } from '../../../../server/sdk'; import { getSettingsStatistics } from '../../../../server/lib/statistics/getSettingsStatistics'; const wizardFields = ['Organization_Type', 'Industry', 'Size', 'Country', 'Language', 'Server_Type', 'Register_Server']; @@ -112,7 +114,7 @@ export const statistics = { statistics.totalThreads = Messages.countThreads(); // livechat visitors - statistics.totalLivechatVisitors = LivechatVisitors.find().count(); + statistics.totalLivechatVisitors = await LivechatVisitors.find().count(); // livechat agents statistics.totalLivechatAgents = Users.findAgents().count(); @@ -182,8 +184,8 @@ export const statistics = { // Amount of chats placed on hold statsPms.push( - MessagesRaw.col.distinct('rid', { t: 'omnichannel_placed_chat_on_hold' }).then((msgs) => { - statistics.chatsOnHold = msgs.length; + MessagesRaw.countRoomsWithMessageType('omnichannel_placed_chat_on_hold', { readPreference }).then((total) => { + statistics.chatsOnHold = total; }), ); @@ -192,12 +194,9 @@ export const statistics = { // Amount of VoIP Calls statsPms.push( - RoomsRaw.col - .find({ t: 'v' }) - .count() - .then((count) => { - statistics.voipCalls = count; - }), + RoomsRaw.countByType('v').then((count) => { + statistics.voipCalls = count; + }), ); // Amount of VoIP Extensions connected @@ -212,27 +211,21 @@ export const statistics = { // Amount of Calls that ended properly statsPms.push( - MessagesRaw.col - .find({ t: 'voip-call-wrapup' }) - .count() - .then((count) => { - statistics.voipSuccessfulCalls = count; - }), + MessagesRaw.countByType('voip-call-wrapup', { readPreference }).then((count) => { + statistics.voipSuccessfulCalls = count; + }), ); // Amount of Calls that ended with an error statsPms.push( - MessagesRaw.col - .find({ t: 'voip-call-ended-unexpectedly' }) - .count() - .then((count) => { - statistics.voipErrorCalls = count; - }), + MessagesRaw.countByType('voip-call-ended-unexpectedly', { readPreference }).then((count) => { + statistics.voipErrorCalls = count; + }), ); // Amount of Calls that were put on hold statsPms.push( - MessagesRaw.col.distinct('rid', { t: 'voip-call-on-hold' }).then((msgs) => { - statistics.voipOnHoldCalls = msgs.length; + MessagesRaw.countRoomsWithMessageType('voip-call-on-hold', { readPreference }).then((count) => { + statistics.voipOnHoldCalls = count; }), ); @@ -398,6 +391,7 @@ export const statistics = { statistics.apps = getAppsStatistics(); statistics.services = getServicesStatistics(); statistics.importer = getImporterStatistics(); + statistics.videoConf = await VideoConf.getStatistics(); // If getSettingsStatistics() returns an error, save as empty object. statsPms.push( @@ -466,6 +460,9 @@ export const statistics = { statistics.slashCommandsJitsi = settings.get('Jitsi_Start_SlashCommands_Count'); statistics.totalOTRRooms = Rooms.findByCreatedOTR().count(); statistics.totalOTR = settings.get('OTR_Count'); + statistics.totalBroadcastRooms = await RoomsRaw.findByBroadcast().count(); + statistics.totalRoomsWithActiveLivestream = await RoomsRaw.findByActiveLivestream().count(); + statistics.totalTriggeredEmails = settings.get('Triggered_Emails_Count'); statistics.totalRoomsWithStarred = await MessagesRaw.countRoomsWithStarredMessages({ readPreference }); statistics.totalRoomsWithPinned = await MessagesRaw.countRoomsWithPinnedMessages({ readPreference }); statistics.totalUserTOTP = await UsersRaw.findActiveUsersTOTPEnable({ readPreference }).count(); @@ -477,10 +474,32 @@ export const statistics = { statistics.totalEmailInvitation = settings.get('Invitation_Email_Count'); statistics.totalE2ERooms = await RoomsRaw.findByE2E({ readPreference }).count(); statistics.logoChange = Object.keys(settings.get('Assets_logo')).includes('url'); - statistics.homeTitleChanged = settings.get('Layout_Home_Title') !== 'Home'; statistics.showHomeButton = settings.get('Layout_Show_Home_Button'); - statistics.totalEncryptedMessages = await MessagesRaw.countE2EEMessages({ readPreference }); + statistics.totalEncryptedMessages = await MessagesRaw.countByType('e2e', { readPreference }); statistics.totalManuallyAddedUsers = settings.get('Manual_Entry_User_Count'); + statistics.totalSubscriptionRoles = await RolesRaw.findByScope('Subscriptions').count(); + statistics.totalUserRoles = await RolesRaw.findByScope('Users').count(); + statistics.totalWebRTCCalls = settings.get('WebRTC_Calls_Count'); + statistics.matrixBridgeEnabled = settings.get('Federation_Matrix_enabled'); + statistics.uncaughtExceptionsCount = settings.get('Uncaught_Exceptions_Count'); + + const defaultHomeTitle = Settings.findOneById('Layout_Home_Title').packageValue; + statistics.homeTitleChanged = settings.get('Layout_Home_Title') !== defaultHomeTitle; + + const defaultHomeBody = Settings.findOneById('Layout_Home_Body').packageValue; + statistics.homeBodyChanged = settings.get('Layout_Home_Body') !== defaultHomeBody; + + const defaultCustomCSS = Settings.findOneById('theme-custom-css').packageValue; + statistics.customCSSChanged = settings.get('theme-custom-css') !== defaultCustomCSS; + + const defaultOnLogoutCustomScript = Settings.findOneById('Custom_Script_On_Logout').packageValue; + statistics.onLogoutCustomScriptChanged = settings.get('Custom_Script_On_Logout') !== defaultOnLogoutCustomScript; + + const defaultLoggedOutCustomScript = Settings.findOneById('Custom_Script_Logged_Out').packageValue; + statistics.loggedOutCustomScriptChanged = settings.get('Custom_Script_Logged_Out') !== defaultLoggedOutCustomScript; + + const defaultLoggedInCustomScript = Settings.findOneById('Custom_Script_Logged_In').packageValue; + statistics.loggedInCustomScriptChanged = settings.get('Custom_Script_Logged_In') !== defaultLoggedInCustomScript; await Promise.all(statsPms).catch(log); diff --git a/apps/meteor/app/theme/client/imports/components/message-box.css b/apps/meteor/app/theme/client/imports/components/message-box.css index 92c801d2be82..c775af9fa865 100644 --- a/apps/meteor/app/theme/client/imports/components/message-box.css +++ b/apps/meteor/app/theme/client/imports/components/message-box.css @@ -146,6 +146,10 @@ } } + &__federation_icon { + width: 20px; + } + &__action-menu { position: relative; diff --git a/apps/meteor/app/theme/client/imports/general/base_old.css b/apps/meteor/app/theme/client/imports/general/base_old.css index 5e198fed7f87..f6ed236827ea 100644 --- a/apps/meteor/app/theme/client/imports/general/base_old.css +++ b/apps/meteor/app/theme/client/imports/general/base_old.css @@ -36,7 +36,7 @@ padding: 0.05rem 0.2rem; - line-height: 1rem; + line-height: 1.25rem; } } @@ -120,7 +120,7 @@ } .copyonly { - display: inline-block; + display: none; width: 0; height: 0; @@ -2934,6 +2934,8 @@ .rc-old .highlight-text { padding: 2px; + color: inherit; + border-radius: 15px; background-color: var(--selection-background); } diff --git a/apps/meteor/app/threads/client/flextab/thread.js b/apps/meteor/app/threads/client/flextab/thread.js index c1dfa725b5e6..6c71447980be 100644 --- a/apps/meteor/app/threads/client/flextab/thread.js +++ b/apps/meteor/app/threads/client/flextab/thread.js @@ -11,7 +11,7 @@ import { chatMessages, ChatMessages } from '../../../ui'; import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManager'; -import { Messages } from '../../../models'; +import { Messages } from '../../../models/client'; import { fileUpload } from '../../../ui/client/lib/fileUpload'; import { dropzoneEvents, dropzoneHelpers } from '../../../ui/client/views/app/room'; import './thread.html'; @@ -77,6 +77,8 @@ Template.thread.helpers({ } = Template.currentData(); const showFormattingTips = settings.get('Message_ShowFormattingTips'); + const alsoSendPreferenceState = getUserPreference(Meteor.userId(), 'alsoSendThreadToChannel'); + return { showFormattingTips, tshow: instance.state.get('sendToChannel'), @@ -85,7 +87,9 @@ Template.thread.helpers({ tmid, onSend: (...args) => { instance.sendToBottom(); - instance.state.set('sendToChannel', false); + if (alsoSendPreferenceState === 'default') { + instance.state.set('sendToChannel', false); + } return instance.chatMessages && instance.chatMessages.send.apply(instance.chatMessages, args); }, onKeyUp: (...args) => instance.chatMessages && instance.chatMessages.keyup.apply(instance.chatMessages, args), @@ -243,8 +247,22 @@ Template.thread.onRendered(function () { Template.thread.onCreated(async function () { this.Threads = new Mongo.Collection(null); + const preferenceState = getUserPreference(Meteor.userId(), 'alsoSendThreadToChannel'); + + let sendToChannel; + switch (preferenceState) { + case 'always': + sendToChannel = true; + break; + case 'never': + sendToChannel = false; + break; + default: + sendToChannel = !this.data.mainMessage.tcount; + } + this.state = new ReactiveDict({ - sendToChannel: !this.data.mainMessage.tcount, + sendToChannel, }); this.loadMore = async () => { diff --git a/apps/meteor/app/threads/client/flextab/threadlist.tsx b/apps/meteor/app/threads/client/flextab/threadlist.tsx index 52e8930b159c..d5003a429aa7 100644 --- a/apps/meteor/app/threads/client/flextab/threadlist.tsx +++ b/apps/meteor/app/threads/client/flextab/threadlist.tsx @@ -1,6 +1,6 @@ import React, { useMemo, lazy, LazyExoticComponent, FC, ReactNode } from 'react'; import { BadgeProps } from '@rocket.chat/fuselage'; -import type { ISubscription } from '@rocket.chat/core-typings'; +import { IRoom, isRoomFederated, ISubscription } from '@rocket.chat/core-typings'; import { useSetting } from '@rocket.chat/ui-contexts'; import { addAction } from '../../../../client/views/room/lib/Toolbox'; @@ -19,7 +19,8 @@ const getVariant = (tunreadUser: number, tunreadGroup: number): BadgeProps['vari const template = lazy(() => import('../../../../client/views/room/contextualBar/Threads')) as LazyExoticComponent; addAction('thread', (options) => { - const room = options.room as unknown as ISubscription; + const room = options.room as unknown as ISubscription & IRoom; + const federated = isRoomFederated(room); const threadsEnabled = useSetting('Threads_enabled'); return useMemo( () => @@ -31,6 +32,10 @@ addAction('thread', (options) => { title: 'Threads', icon: 'thread', template, + ...(federated && { + 'data-tooltip': 'Threads_unavailable_for_federation', + 'disabled': true, + }), renderAction: (props): ReactNode => { const tunread = room.tunread?.length || 0; const tunreadUser = room.tunreadUser?.length || 0; @@ -46,6 +51,6 @@ addAction('thread', (options) => { order: 2, } : null, - [threadsEnabled, room.tunread?.length, room.tunreadUser?.length, room.tunreadGroup?.length], + [threadsEnabled, room.tunread?.length, room.tunreadUser?.length, room.tunreadGroup?.length, federated], ); }); diff --git a/apps/meteor/app/threads/client/messageAction/follow.ts b/apps/meteor/app/threads/client/messageAction/follow.ts index b6194f751afa..b1fe4da2eef4 100644 --- a/apps/meteor/app/threads/client/messageAction/follow.ts +++ b/apps/meteor/app/threads/client/messageAction/follow.ts @@ -18,7 +18,7 @@ Meteor.startup(function () { id: 'follow-message', icon: 'bell', label: 'Follow_message', - context: ['message', 'message-mobile', 'threads'], + context: ['message', 'message-mobile', 'threads', 'federated'], async action(_, { message }) { callWithErrorHandling('followMessage', { mid: message._id }).then(() => dispatchToastMessage({ diff --git a/apps/meteor/app/threads/client/messageAction/unfollow.ts b/apps/meteor/app/threads/client/messageAction/unfollow.ts index f476d3fab767..5b42a499b8e1 100644 --- a/apps/meteor/app/threads/client/messageAction/unfollow.ts +++ b/apps/meteor/app/threads/client/messageAction/unfollow.ts @@ -17,7 +17,7 @@ Meteor.startup(function () { id: 'unfollow-message', icon: 'bell-off', label: 'Unfollow_message', - context: ['message', 'message-mobile', 'threads'], + context: ['message', 'message-mobile', 'threads', 'federated'], async action(_, { message }) { callWithErrorHandling('unfollowMessage', { mid: message._id }).then(() => dispatchToastMessage({ diff --git a/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts b/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts index fdbe64940ff3..72fcbf22f598 100644 --- a/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts +++ b/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts @@ -1,5 +1,6 @@ import { useMemo, lazy } from 'react'; import { usePermission } from '@rocket.chat/ui-contexts'; +import { isRoomFederated } from '@rocket.chat/core-typings'; import { addAction } from '../../../../client/views/room/lib/Toolbox'; @@ -7,6 +8,8 @@ const template = lazy(() => import('../../../../client/views/room/contextualBar/ addAction('clean-history', ({ room }) => { const hasPermission = usePermission('clean-channel-history', room._id); + const federated = isRoomFederated(room); + return useMemo( () => hasPermission @@ -16,10 +19,14 @@ addAction('clean-history', ({ room }) => { full: true, title: 'Prune_Messages', icon: 'eraser', + ...(federated && { + 'data-tooltip': 'Clean_History_unavailable_for_federation', + 'disabled': true, + }), template, order: 250, } : null, - [hasPermission], + [hasPermission, federated], ); }); diff --git a/apps/meteor/app/ui-login/client/index.js b/apps/meteor/app/ui-login/client/index.js index 5772939cf607..f78815730e02 100644 --- a/apps/meteor/app/ui-login/client/index.js +++ b/apps/meteor/app/ui-login/client/index.js @@ -6,3 +6,4 @@ import './username/username.html'; import './login/form'; import './login/services'; import './username/username'; +import './login/startup'; diff --git a/apps/meteor/app/ui-login/client/login/layout.html b/apps/meteor/app/ui-login/client/login/layout.html index 4333479c42f9..42ad59b33a82 100644 --- a/apps/meteor/app/ui-login/client/login/layout.html +++ b/apps/meteor/app/ui-login/client/login/layout.html @@ -2,6 +2,9 @@

{{> loginLayoutHeader}} + {{#if showForcedLogoutBanner}} + {{> loggedOutBanner}} + {{/if}} {{> loginForm}} {{> loginLayoutFooter}}
diff --git a/apps/meteor/app/ui-login/client/login/layout.js b/apps/meteor/app/ui-login/client/login/layout.js index a0db1560bf84..d9386ae42a1a 100644 --- a/apps/meteor/app/ui-login/client/login/layout.js +++ b/apps/meteor/app/ui-login/client/login/layout.js @@ -1,3 +1,4 @@ +import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { settings } from '../../../settings'; @@ -10,4 +11,7 @@ Template.loginLayout.helpers({ return `${prefix}/${asset.url || asset.defaultUrl}`; } }, + showForcedLogoutBanner() { + return Session.get('force_logout'); + }, }); diff --git a/apps/meteor/app/ui-login/client/login/startup.ts b/apps/meteor/app/ui-login/client/login/startup.ts new file mode 100644 index 000000000000..60a2cb80cf7c --- /dev/null +++ b/apps/meteor/app/ui-login/client/login/startup.ts @@ -0,0 +1,19 @@ +import { Meteor } from 'meteor/meteor'; +import { Session } from 'meteor/session'; +import { Tracker } from 'meteor/tracker'; + +import { Notifications } from '../../../notifications/client'; + +Meteor.startup(() => { + Tracker.autorun(() => { + const userId = Meteor.userId(); + + if (!userId) { + return; + } + + Notifications.onUser('force_logout', () => { + Session.set('force_logout', true); + }); + }); +}); diff --git a/apps/meteor/app/ui-master/client/body.js b/apps/meteor/app/ui-master/client/body.js index 9fc2bbb4065d..af1bf3c5557a 100644 --- a/apps/meteor/app/ui-master/client/body.js +++ b/apps/meteor/app/ui-master/client/body.js @@ -9,7 +9,7 @@ import { t } from '../../utils/client'; import { chatMessages } from '../../ui'; import { popover, RoomManager } from '../../ui-utils'; import { settings } from '../../settings'; -import { ChatSubscription } from '../../models'; +import { ChatSubscription } from '../../models/client'; import './body.html'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import GenericModal from '../../../client/components/GenericModal'; diff --git a/apps/meteor/app/ui-master/server/index.js b/apps/meteor/app/ui-master/server/index.js index c93c8d73e3c2..cf837de93795 100644 --- a/apps/meteor/app/ui-master/server/index.js +++ b/apps/meteor/app/ui-master/server/index.js @@ -4,7 +4,7 @@ import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { Settings } from '../../models'; +import { Settings } from '../../models/server'; import { settings } from '../../settings/server'; import { applyHeadInjections, headInjections, injectIntoBody, injectIntoHead } from './inject'; import './scripts'; diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts index 69fc85aa679f..bb62cc79864b 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts @@ -14,7 +14,7 @@ export const onAdded = (button: IUIActionButton): void => applyButtonFilters(button, room) ? { id: button.actionId, - icon: '', // Apps won't provide icons for now + icon: undefined, // Apps won't provide icons for now order: 300, // Make sure the button only shows up inside the room toolbox title: t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)) as any, // Filters were applied in the applyButtonFilters function diff --git a/apps/meteor/app/ui-message/client/message.html b/apps/meteor/app/ui-message/client/message.html index 4837a2bb0df5..fb29dbfbd5dd 100644 --- a/apps/meteor/app/ui-message/client/message.html +++ b/apps/meteor/app/ui-message/client/message.html @@ -1,5 +1,5 @@