Merge pull request #16078 from RocketChat/release-2.4.0

pull/16107/head 2.4.0
Guilherme Gazzo 5 years ago committed by GitHub
commit 097c7c04f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      .circleci/config.yml.old
  2. 13
      .circleci/docker.sh
  3. 4
      .circleci/snap.sh
  4. 4
      .docker/Dockerfile
  5. 2
      .docker/Dockerfile.rhel
  6. 1
      .eslintignore
  7. 103
      .eslintrc
  8. 797
      .github/history.json
  9. 443
      .github/workflows/build_and_test.yml
  10. 1
      .gitignore
  11. 1
      .meteor/.finished-upgraders
  12. 19
      .meteor/packages
  13. 2
      .meteor/release
  14. 44
      .meteor/versions
  15. 51
      .scripts/houstonMetadata.js
  16. 5
      .scripts/set-version.js
  17. 6
      .scripts/start-xvfb.sh
  18. 2
      .scripts/start.js
  19. 2
      .scripts/version.js
  20. 2
      .snapcraft/resources/preparenode
  21. 35
      .storybook/config.js
  22. 31
      .storybook/mocks/decorators.js
  23. 0
      .storybook/mocks/empty.js
  24. 4
      .storybook/mocks/meteor.js
  25. 67
      .storybook/mocks/providers.js
  26. 4
      .storybook/webpack.config.js
  27. 2
      .stylelintignore
  28. 104
      .travis.yml
  29. 15
      .travis/docker.sh
  30. 9
      .travis/namefiles.sh
  31. 6
      .travis/setartname.sh
  32. 2
      .travis/setdeploydir.sh
  33. 9
      .travis/setupsig.sh
  34. BIN
      .travis/sign.key.gpg
  35. 54
      .travis/snap.sh
  36. 8
      .travis/update-releases.sh
  37. 132
      HISTORY.md
  38. 18
      README.md
  39. 25
      app/api/server/api.js
  40. 5
      app/api/server/index.js
  41. 20
      app/api/server/lib/custom-sounds.js
  42. 20
      app/api/server/lib/custom-user-status.js
  43. 20
      app/api/server/lib/emoji-custom.js
  44. 27
      app/api/server/lib/integrations.js
  45. 25
      app/api/server/lib/messages.js
  46. 13
      app/api/server/lib/oauthApps.js
  47. 77
      app/api/server/lib/rooms.js
  48. 27
      app/api/server/lib/users.js
  49. 14
      app/api/server/lib/webdav.js
  50. 24
      app/api/server/v1/chat.js
  51. 18
      app/api/server/v1/custom-sounds.js
  52. 18
      app/api/server/v1/custom-user-status.js
  53. 17
      app/api/server/v1/emoji-custom.js
  54. 18
      app/api/server/v1/integrations.js
  55. 61
      app/api/server/v1/invites.js
  56. 20
      app/api/server/v1/misc.js
  57. 23
      app/api/server/v1/oauthapps.js
  58. 38
      app/api/server/v1/rooms.js
  59. 4
      app/api/server/v1/subscriptions.js
  60. 29
      app/api/server/v1/users.js
  61. 8
      app/api/server/v1/webdav.js
  62. 4
      app/apps/server/bridges/commands.js
  63. 10
      app/apps/server/bridges/listeners.js
  64. 60
      app/apps/server/bridges/livechat.js
  65. 2
      app/apps/server/bridges/messages.js
  66. 1
      app/apps/server/converters/messages.js
  67. 3
      app/apps/server/converters/visitors.js
  68. 4
      app/apps/server/cron.js
  69. 3
      app/authorization/client/lib/streamer.js
  70. 16
      app/authorization/client/startup.js
  71. 9
      app/authorization/client/views/permissions.js
  72. 6
      app/authorization/client/views/permissionsRole.js
  73. 4
      app/authorization/server/functions/canAccessRoom.js
  74. 6
      app/authorization/server/functions/hasRole.js
  75. 5
      app/authorization/server/lib/streamer.js
  76. 11
      app/authorization/server/methods/deleteRole.js
  77. 6
      app/authorization/server/methods/saveRole.js
  78. 4
      app/authorization/server/publications/permissions/emitter.js
  79. 2
      app/authorization/server/publications/permissions/index.js
  80. 1
      app/authorization/server/publications/roles.js
  81. 1
      app/authorization/server/startup.js
  82. 4
      app/callbacks/lib/callbacks.js
  83. 2
      app/cas/client/cas_client.js
  84. 6
      app/channel-settings-mail-messages/client/views/mailMessagesInstructions.js
  85. 24
      app/channel-settings/client/views/channelSettings.js
  86. 4
      app/chatpal-search/server/provider/provider.js
  87. 2
      app/cloud/server/functions/syncWorkspace.js
  88. 1
      app/crowd/server/crowd.js
  89. 24
      app/custom-sounds/client/admin/adminSounds.html
  90. 99
      app/custom-sounds/client/admin/adminSounds.js
  91. 4
      app/custom-sounds/client/admin/route.js
  92. 5
      app/custom-sounds/client/admin/soundEdit.js
  93. 8
      app/custom-sounds/client/admin/soundInfo.js
  94. 4
      app/custom-sounds/client/lib/CustomSounds.js
  95. 4
      app/custom-sounds/client/notifications/deleteCustomSound.js
  96. 4
      app/custom-sounds/client/notifications/updateCustomSound.js
  97. 2
      app/custom-sounds/server/methods/uploadCustomSound.js
  98. 1
      app/custom-sounds/server/publications/customSounds.js
  99. 1
      app/discussion/client/index.js
  100. 3
      app/discussion/client/lib/discussionsOfRoom.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -43,7 +43,7 @@ test-save-npm-cache: &test-save-npm-cache
- ./node_modules
test-docker-image: &test-docker-image
circleci/node:8.15-stretch-browsers
circleci/node:8.16-stretch-browsers
test-with-oplog: &test-with-oplog
<<: *defaults
@ -68,7 +68,7 @@ jobs:
build:
<<: *defaults
docker:
- image: circleci/node:8.15-stretch
- image: circleci/node:8.16-stretch
- image: mongo:3.4
steps:
@ -146,7 +146,7 @@ jobs:
- run:
name: Build Rocket.Chat
environment:
TOOL_NODE_FLAGS: --max_old_space_size=3072
TOOL_NODE_FLAGS: --max_old_space_size=4096
command: |
if [[ $CIRCLE_TAG ]] || [[ $CIRCLE_BRANCH == 'develop' ]]; then
meteor reset;
@ -218,7 +218,7 @@ jobs:
deploy:
<<: *defaults
docker:
- image: circleci/node:8.15-stretch
- image: circleci/node:8.16-stretch
steps:
- attach_workspace:
@ -332,7 +332,7 @@ jobs:
pr-build:
<<: *defaults
docker:
- image: circleci/node:8.15-stretch
- image: circleci/node:8.16-stretch
steps:
- checkout
@ -445,7 +445,16 @@ workflows:
version: 2
build-and-test:
jobs:
- hold-all:
type: approval
filters:
branches:
only: develop
tags:
only: /^[0-9]+\.[0-9]+\.[0-9]+(?:-(?:rc|beta)\.[0-9]+)?$/
- build:
requires:
- hold-all
filters:
tags:
only: /^[0-9]+\.[0-9]+\.[0-9]+(?:-(?:rc|beta)\.[0-9]+)?$/

@ -1,13 +0,0 @@
#!/bin/bash
set -euvo pipefail
IFS=$'\n\t'
CURL_URL="https://registry.hub.docker.com/u/rocketchat/rocket.chat/trigger/$DOCKER_TRIGGER_TOKEN/"
if [[ $CIRCLE_TAG ]]; then
CURL_DATA='{"source_type":"Tag","source_name":"'"$CIRCLE_TAG"'"}';
else
CURL_DATA='{"source_type":"Branch","source_name":"'"$CIRCLE_BRANCH"'"}';
fi
curl -H "Content-Type: application/json" --data "$CURL_DATA" -X POST "$CURL_URL"

@ -3,7 +3,9 @@ set -euvo pipefail
IFS=$'\n\t'
# Add launchpad to known hosts
ssh-keyscan -t rsa -H git.launchpad.net > ~/.ssh/known_hosts
mkdir -p $HOME/.ssh
ssh-keyscan -t rsa -H git.launchpad.net >> $HOME/.ssh/known_hosts
echo "Preparing to trigger a snap release for $SNAP_CHANNEL channel"

@ -1,8 +1,8 @@
FROM rocketchat/base:8
FROM rocketchat/base:8.17.0
ADD . /app
MAINTAINER buildmaster@rocket.chat
LABEL maintainer="buildmaster@rocket.chat"
RUN set -x \
&& cd /app/bundle/programs/server \

@ -1,6 +1,6 @@
FROM registry.access.redhat.com/rhscl/nodejs-8-rhel7
ENV RC_VERSION 2.3.2
ENV RC_VERSION 2.4.0
MAINTAINER buildmaster@rocket.chat

@ -19,3 +19,4 @@ public/livechat/
!.scripts
public/pdf.worker.min.js
imports/client/
!/.storybook/

@ -1,23 +1,104 @@
{
"extends": ["@rocket.chat/eslint-config"],
"parser": "babel-eslint",
"globals": {
"__meteor_bootstrap__" : false,
"__meteor_runtime_config__" : false,
"Assets" : false,
"chrome" : false
"extends": [
"@rocket.chat/eslint-config"
],
"parser": "babel-eslint",
"globals": {
"__meteor_bootstrap__": false,
"__meteor_runtime_config__": false,
"Assets": false,
"chrome": false,
"jscolor": false
},
"plugins": ["react"],
"rules": {
"jsx-quotes": ["error", "prefer-single"],
"plugins": [
"react"
],
"rules": {
"jsx-quotes": [
"error",
"prefer-single"
],
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/jsx-no-undef": "error",
"react/jsx-fragments": ["error", "syntax"],
"react/jsx-fragments": [
"error",
"syntax"
],
},
"settings": {
"react": {
"version": "detect",
},
},
"overrides": [
{
"files": [
"**/*.ts",
"**/*.tsx"
],
"extends": [
"@rocket.chat/eslint-config",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/eslint-recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2018,
"warnOnUnsupportedTypeScriptVersion": false,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"legacyDecorators": true
}
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
"jsx-quotes": [
"error",
"prefer-single"
],
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/jsx-no-undef": "error",
"react/jsx-fragments": [
"error",
"syntax"
],
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/interface-name-prefix": [
"error",
"always"
]
},
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true
},
"settings": {
"import/resolver": {
"node": {
"extensions": [
".js",
".ts",
".tsx"
]
}
},
"react": {
"version": "detect"
}
}
}
]
}

@ -37299,6 +37299,803 @@
]
}
]
},
"2.4.0-rc.0": {
"node_version": "8.17.0",
"npm_version": "6.13.4",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": [
{
"pr": "16043",
"title": "Update NodeJS to 8.17.0",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15933",
"title": "[NEW] Invite links: share a link to invite users",
"userLogin": "pierre-lehnen-rc",
"milestone": "2.4.0",
"contributors": [
"pierre-lehnen-rc",
"web-flow",
"rodrigok"
]
},
{
"pr": "15998",
"title": "Fix typo in Italian translation",
"userLogin": "iannuzzelli",
"milestone": "2.4.0",
"contributors": [
"iannuzzelli"
]
},
{
"pr": "16037",
"title": "Update Meteor to 1.8.3",
"userLogin": "sampaiodiego",
"milestone": "2.4.0",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "16010",
"title": "[FIX] Importer: Variable name appearing instead of it's value",
"userLogin": "ashwaniYDV",
"milestone": "2.4.0",
"contributors": [
"ashwaniYDV"
]
},
{
"pr": "15977",
"title": "[IMPROVE] Replace livechat:inquiry publication by REST and Streamer",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"renatobecker"
]
},
{
"pr": "16021",
"title": "[IMPROVE] Sorting on livechat analytics queries were wrong",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15650",
"title": "[IMPROVE] Replace fullUserData publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "15885",
"title": "[IMPROVE] Replace integrations and integrationHistory publications by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "15886",
"title": "Some performance improvements",
"userLogin": "sampaiodiego",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "15930",
"title": "[FIX]Add time format for latest message on the sidebar",
"userLogin": "ritwizsinha",
"contributors": [
"ritwizsinha",
"ggazzo"
]
},
{
"pr": "15994",
"title": "[FIX] Admin Setting descriptions and Storybook",
"userLogin": "tassoevan",
"milestone": "2.4.0",
"contributors": [
"tassoevan"
]
},
{
"pr": "16033",
"title": "[IMPROVE] Notify logged agents when their departments change",
"userLogin": "renatobecker",
"milestone": "2.4.0",
"contributors": [
"renatobecker"
]
},
{
"pr": "15901",
"title": "[IMPROVE] Replace fullEmojiData publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "15948",
"title": "[IMPROVE] Replace adminRooms publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "15926",
"title": "[IMPROVE] Replace webdavAccounts publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "15878",
"title": "[IMPROVE] Replace oauth publications by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "15956",
"title": "[IMPROVE] Replace userAutocomplete publication by REST",
"userLogin": "MarcosSpessatto",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "15908",
"title": "[IMPROVE] Replace discussionsOfARoom publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "16023",
"title": "[FIX] width of upload-progress-text",
"userLogin": "mariaeduardacunha",
"milestone": "2.4.0",
"contributors": [
"mariaeduardacunha"
]
},
{
"pr": "15685",
"title": "[IMPROVE] Move 'Reply in Thread' button from menu to message actions",
"userLogin": "antkaz",
"milestone": "2.4.0",
"contributors": [
"antkaz",
"ggazzo"
]
},
{
"pr": "15570",
"title": "Fixed Grammatical Mistakes.",
"userLogin": "breaking-let",
"milestone": "2.4.0",
"contributors": [
"breaking-let"
]
},
{
"pr": "16020",
"title": "Upgrade limax to 2.0.0",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15907",
"title": "[IMPROVE] Replace customSounds publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "16018",
"title": "[FIX] Message list scrolling to bottom on reactions",
"userLogin": "ggazzo",
"milestone": "2.4.0",
"contributors": [
"ggazzo",
"mariaeduardacunha"
]
},
{
"pr": "16004",
"title": "[IMPROVE] Replace stdout publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "15978",
"title": "[FIX] SAML logout error",
"userLogin": "sampaiodiego",
"milestone": "2.4.0",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "16016",
"title": "[FIX]Added Join button to Read Only rooms.",
"userLogin": "gabriellsh",
"milestone": "2.4.0",
"contributors": [
"gabriellsh",
"MartinSchoeler"
]
},
{
"pr": "15942",
"title": "[IMPROVE] Replace fullUserStatusData publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow"
]
},
{
"pr": "15916",
"title": "[IMPROVE] Replace userData subscriptions by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego"
]
},
{
"pr": "16013",
"title": "[FIX] z-index of new message button",
"userLogin": "mariaeduardacunha",
"contributors": [
"mariaeduardacunha"
]
},
{
"pr": "16017",
"title": "[FIX] new message popup",
"userLogin": "mariaeduardacunha",
"milestone": "2.4.0",
"contributors": [
"mariaeduardacunha"
]
},
{
"pr": "16012",
"title": "[FIX] Changed renderMessage priority, fixed Katex on/off setting",
"userLogin": "gabriellsh",
"contributors": [
"gabriellsh"
]
},
{
"pr": "16009",
"title": "[FIX] Empty security section when 2fa is disabled",
"userLogin": "MartinSchoeler",
"milestone": "2.4.0",
"contributors": [
"MartinSchoeler"
]
},
{
"pr": "16006",
"title": "[FIX] Dropzone being stuck when dragging to thread",
"userLogin": "MartinSchoeler",
"contributors": [
"MartinSchoeler"
]
},
{
"pr": "15910",
"title": "[IMPROVE] Replace roles publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15792",
"title": "[IMPROVE] Livechat realtime dashboard",
"userLogin": "MarcosSpessatto",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "16001",
"title": "[FIX] Fix sort livechat rooms",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15927",
"title": "[NEW] Logout other clients when changing password",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15991",
"title": "[FIX] Guest's name field missing when forwarding livechat rooms",
"userLogin": "renatobecker",
"milestone": "2.4.0",
"contributors": [
"renatobecker"
]
},
{
"pr": "15968",
"title": "[IMPROVE] Replace livechat:rooms publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15989",
"title": "Remove unnecessary cron starts",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15979",
"title": "Enable typescript lint",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15988",
"title": "LingoHub based on develop",
"userLogin": "engelgabriel",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "15928",
"title": "[NEW] Do not print emails in console on production mode",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15985",
"title": "[FIX] Error of bind environment on user data export",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
},
{
"pr": "15503",
"title": "[IMPROVE] Replace livechat:officeHour publication to REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15975",
"title": "[FIX] Incorrect translation key on Livechat Appearance template",
"userLogin": "ritwizsinha",
"milestone": "2.4.0",
"contributors": [
"ritwizsinha"
]
},
{
"pr": "15970",
"title": "[IMPROVE] Replace forgotten livechat:departmentAgents subscriptions",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"renatobecker"
]
},
{
"pr": "15966",
"title": "[FIX] Livechat Widget version 1.3.0",
"userLogin": "renatobecker",
"milestone": "2.3.2",
"contributors": [
"renatobecker"
]
},
{
"pr": "15962",
"title": "Fix 'How it all started' link on README",
"userLogin": "zdumitru",
"contributors": [
"zdumitru",
"web-flow"
]
},
{
"pr": "15957",
"title": "[FIX] Invalid Redirect URI on Custom OAuth",
"userLogin": "pierre-lehnen-rc",
"milestone": "2.3.2",
"contributors": [
"pierre-lehnen-rc"
]
},
{
"pr": "15961",
"title": "Check package-lock consistency with package.json on CI",
"userLogin": "rodrigok",
"contributors": [
"rodrigok"
]
},
{
"pr": "15873",
"title": "Meteor update to 1.8.2",
"userLogin": "sampaiodiego",
"contributors": [
"sampaiodiego",
"rodrigok"
]
},
{
"pr": "15918",
"title": "GitHub CI",
"userLogin": "rodrigok",
"contributors": [
"rodrigok",
"web-flow"
]
},
{
"pr": "15944",
"title": "[IMPROVE] Replace livechat:managers publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15943",
"title": "[IMPROVE] Replace livechat:visitorHistory publication by REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"renatobecker"
]
},
{
"pr": "15903",
"title": "[FIX] Livechat build without NodeJS installed",
"userLogin": "localguru",
"contributors": [
"localguru"
]
},
{
"pr": "15940",
"title": "Change migration number 169 <-> 170",
"userLogin": "sampaiodiego",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "15939",
"title": "LingoHub based on develop",
"userLogin": "engelgabriel",
"contributors": [
"sampaiodiego"
]
},
{
"pr": "15612",
"title": "[IMPROVE] Replace livechat:queue subscription",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"renatobecker",
"web-flow"
]
},
{
"pr": "15935",
"title": "[IMPROVE] Add deprecate warning in some unused publications",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto"
]
},
{
"pr": "15937",
"title": "[FIX] Admin menu not showing after renamed integration permissions",
"userLogin": "n-se",
"milestone": "2.3.1",
"contributors": [
"n-se"
]
},
{
"pr": "15934",
"title": "[FIX] Administration UI issues",
"userLogin": "tassoevan",
"milestone": "2.3.1",
"contributors": [
"tassoevan"
]
},
{
"pr": "15496",
"title": "[IMPROVE] Replace livechat:customFields to REST",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"sampaiodiego",
"web-flow",
"renatobecker"
]
},
{
"pr": "15919",
"title": "[FIX] Server crash on sync with no response",
"userLogin": "geekgonecrazy",
"contributors": [
"geekgonecrazy",
"web-flow"
]
},
{
"pr": "15915",
"title": "[FIX] Livechat permissions being overwrite on server restart",
"userLogin": "renatobecker",
"milestone": "2.3.1",
"contributors": [
"renatobecker"
]
},
{
"pr": "15897",
"title": "[FIX] Livechat triggers not firing",
"userLogin": "renatobecker",
"milestone": "2.3.1",
"contributors": [
"renatobecker"
]
},
{
"pr": "15895",
"title": "[FIX] Auto load image user preference",
"userLogin": "ggazzo",
"milestone": "2.3.1",
"contributors": [
"ggazzo",
"web-flow"
]
},
{
"pr": "15887",
"title": "[IMPROVE] Validate user identity on send message process",
"userLogin": "MarcosSpessatto",
"milestone": "2.4.0",
"contributors": [
"MarcosSpessatto",
"web-flow"
]
},
{
"pr": "15850",
"title": "[FIX] Don't throw an error when a message is prevented from apps engine",
"userLogin": "wreiske",
"contributors": [
"wreiske",
"web-flow"
]
},
{
"pr": "15898",
"title": "[FIX] Default value of the Livechat WebhookUrl setting",
"userLogin": "renatobecker",
"milestone": "2.3.1",
"contributors": [
"renatobecker"
]
},
{
"pr": "15888",
"title": "[IMPROVE] Update ui for Roles field",
"userLogin": "antkaz",
"milestone": "2.4.0",
"contributors": [
"antkaz"
]
},
{
"pr": "15837",
"title": "[NEW] Apps-Engine event for when a livechat room is closed",
"userLogin": "lolimay",
"milestone": "2.4.0",
"contributors": [
"lolimay",
"renatobecker",
"d-gubert"
]
},
{
"pr": "15894",
"title": "[CHORE] Replace findOne with findOneById methods (Omnichannel)",
"userLogin": "renatobecker",
"milestone": "2.4.0",
"contributors": [
"renatobecker"
]
},
{
"pr": "15841",
"title": "[FIX] Thread Replies in Search",
"userLogin": "MartinSchoeler",
"contributors": [
"MartinSchoeler"
]
},
{
"pr": "15872",
"title": "Merge master into develop & Set version to 3.0.0-develop",
"userLogin": "sampaiodiego",
"contributors": [
"rodrigok",
"web-flow",
"sampaiodiego"
]
}
]
},
"2.4.0-rc.1": {
"node_version": "8.17.0",
"npm_version": "6.13.4",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": [
{
"pr": "16053",
"title": "Regression: Update components",
"userLogin": "tassoevan",
"milestone": "2.4.0",
"contributors": [
"tassoevan"
]
}
]
},
"2.4.0-rc.2": {
"node_version": "8.17.0",
"npm_version": "6.13.4",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": [
{
"pr": "16084",
"title": "Regression: Missing button to copy Invite links",
"userLogin": "ggazzo",
"milestone": "2.4.0",
"contributors": [
"ggazzo"
]
},
{
"pr": "16062",
"title": "[FIX] Registration form was hidden when login form was disabled",
"userLogin": "rodrigok",
"milestone": "2.4.0",
"contributors": [
"rodrigok"
]
}
]
},
"2.4.0-rc.3": {
"node_version": "8.17.0",
"npm_version": "6.13.4",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": []
},
"2.4.0": {
"node_version": "8.17.0",
"npm_version": "6.13.4",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": []
}
}
}

@ -0,0 +1,443 @@
name: Build and Test
on:
release:
types: [published]
pull_request:
branches: '**'
push:
branches:
- develop
env:
CI: true
MONGO_URL: mongodb://localhost:27017
TOOL_NODE_FLAGS: --max_old_space_size=4096
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Github Info
run: |
echo "GITHUB_ACTION: $GITHUB_ACTION"
echo "GITHUB_ACTOR: $GITHUB_ACTOR"
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "github.event_name: ${{ github.event_name }}"
cat $GITHUB_EVENT_PATH
- name: Use Node.js 8.17.0
uses: actions/setup-node@v1
with:
node-version: "8.17.0"
- uses: actions/checkout@v1
- name: check package-lock
run: |
npx package-lock-check
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.OS }}-node_modules-${{ hashFiles('**/package-lock.json') }}
- name: Cache meteor local
uses: actions/cache@v1
with:
path: ./.meteor/local
key: ${{ runner.OS }}-meteor_cache-${{ hashFiles('.meteor/versions') }}
- name: Cache meteor
uses: actions/cache@v1
with:
path: ~/.meteor
key: ${{ runner.OS }}-meteor-${{ hashFiles('.meteor/release') }}
- name: Install Meteor
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
- name: Versions
run: |
npm --versions
node -v
meteor --version
meteor npm --versions
meteor node -v
git version
- name: npm install
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
run: |
meteor npm install
- run: npm run lint
- name: Launch MongoDB
uses: wbari/start-mongoDB@v0.2
with:
mongoDBVersion: "4.0"
- run: npm run testunit
# 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.js
# echo "" > client/main.js
# sed -i.backup 's/rocketchat:livechat/#rocketchat:livechat/' .meteor/packages
# meteor build --server-only --debug --directory /tmp/build-temp
# git checkout -- server/main.js client/main.js .meteor/packages
- name: Reset Meteor
if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop'
run: |
meteor reset
- name: Build Rocket.Chat From Pull Request
if: startsWith(github.ref, 'refs/pull/') == true
env:
METEOR_PROFILE: 1000
run: |
meteor build --server-only --directory --debug /tmp/build-test
- name: Build Rocket.Chat
if: startsWith(github.ref, 'refs/pull/') != true
run: |
meteor build --server-only --directory /tmp/build-test
- 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
cd /tmp
tar czf Rocket.Chat.test.tar.gz ./build-test
- name: Store build for tests
uses: actions/upload-artifact@v1
with:
name: build-test
path: /tmp/Rocket.Chat.test.tar.gz
- name: Store build
uses: actions/upload-artifact@v1
with:
name: build
path: /tmp/build
test:
runs-on: ubuntu-16.04
needs: build
strategy:
matrix:
node-version: ["8.17.0"]
mongodb-version: ["3.4", "3.6", "4.0"]
steps:
- name: Launch MongoDB
uses: wbari/start-mongoDB@v0.2
with:
mongoDBVersion: ${{ matrix.mongodb-version }} --noprealloc --smallfiles --replSet=rs0
- name: Restore build for tests
uses: actions/download-artifact@v1
with:
name: build-test
path: /tmp
- name: Decompress build
run: |
cd /tmp
tar xzf Rocket.Chat.test.tar.gz
cd -
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- 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@v1
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
- name: NPM install
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
run: |
npm install
- name: Test
env:
TEST_MODE: "true"
MONGO_URL: mongodb://localhost:27017/rocketchat
MONGO_OPLOG_URL: mongodb://localhost:27017/local
run: |
for i in $(seq 1 5); do (docker exec mongo mongo rocketchat --eval 'db.dropDatabase()') && xvfb-run --auto-servernum npm test && s=0 && break || s=$? && sleep 1; done; (exit $s)
# notification:
# runs-on: ubuntu-latest
# 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-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v1
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.OS }}-node_modules-${{ hashFiles('**/package-lock.json') }}
- name: Cache meteor local
uses: actions/cache@v1
with:
path: ./.meteor/local
key: ${{ runner.OS }}-meteor_cache-${{ hashFiles('.meteor/versions') }}
- name: Cache meteor
uses: actions/cache@v1
with:
path: ~/.meteor
key: ${{ runner.OS }}-meteor-${{ hashFiles('.meteor/release') }}
- name: Use Node.js 8.17.0
uses: actions/setup-node@v1
with:
node-version: "8.17.0"
- name: Install Meteor
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
- name: Versions
run: |
npm --versions
node -v
meteor --version
meteor npm --versions
meteor node -v
git version
echo $GITHUB_REF
- name: npm install
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
run: |
meteor npm install
# 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.js
# echo "" > client/main.js
# sed -i.backup 's/rocketchat:livechat/#rocketchat:livechat/' .meteor/packages
# meteor build --server-only --debug --directory /tmp/build-temp
# git checkout -- server/main.js client/main.js .meteor/packages
- name: Build Rocket.Chat
run: |
meteor build --server-only --directory /tmp/build-pr
- name: Build Docker image for PRs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: pr-${{ github.event.number }}
run: |
cd /tmp/build-pr
export OWNER="${GITHUB_REPOSITORY%/*}"
docker login docker.pkg.github.com -u "${OWNER}" -p "${GITHUB_TOKEN}"
cp $GITHUB_WORKSPACE/.docker/Dockerfile .
export LOWERCASE_REPOSITORY=$(echo "$GITHUB_REPOSITORY" | tr "[:upper:]" "[:lower:]")
export IMAGE_NAME="docker.pkg.github.com/${LOWERCASE_REPOSITORY}/rocket.chat:${VERSION}"
echo "Build official Docker image ${IMAGE_NAME}"
docker build -t $IMAGE_NAME .
docker push $IMAGE_NAME
deploy:
runs-on: ubuntu-latest
if: github.event_name == 'release' || github.ref == 'refs/heads/develop'
needs: test
steps:
- uses: actions/checkout@v1
- name: Restore build
uses: actions/download-artifact@v1
with:
name: build
path: /tmp/build
- name: Publish assets
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}
REDHAT_REGISTRY_PID: ${{ secrets.REDHAT_REGISTRY_PID }}
REDHAT_REGISTRY_KEY: ${{ secrets.REDHAT_REGISTRY_KEY }}
UPDATE_TOKEN: ${{ secrets.UPDATE_TOKEN }}
run: |
if [[ '${{ github.event_name }}' = 'release' ]]; then
export CIRCLE_TAG="${GITHUB_REF#*tags/}"
else
export CIRCLE_BRANCH="${GITHUB_REF#*heads/}"
fi;
export CIRCLE_TAG=${CIRCLE_TAG:=}
export CIRCLE_SHA1=$GITHUB_SHA
export CIRCLE_BUILD_NUM=$GITHUB_SHA
aws s3 cp s3://rocketchat/sign.key.gpg .circleci/sign.key.gpg
source .circleci/setartname.sh
source .circleci/setdeploydir.sh
bash .circleci/setupsig.sh
bash .circleci/namefiles.sh
aws s3 cp $ROCKET_DEPLOY_DIR/ s3://download.rocket.chat/build/ --recursive
bash .circleci/update-releases.sh
# bash .circleci/snap.sh
bash .circleci/redhat-registry.sh
image-build:
runs-on: ubuntu-latest
needs: deploy
strategy:
matrix:
release: ["official", "preview"]
env:
IMAGE: "rocketchat/rocket.chat"
steps:
- uses: actions/checkout@v1
- name: Restore build
uses: actions/download-artifact@v1
with:
name: build
path: /tmp/build
- name: Unpack build
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
run: |
cd /tmp/build
tar xzf Rocket.Chat.tar.gz
rm Rocket.Chat.tar.gz
export DOCKER_PATH="${GITHUB_WORKSPACE}/.docker"
if [[ '${{ matrix.release }}' = 'preview' ]]; then
export IMAGE="${IMAGE}.preview"
export DOCKER_PATH="${DOCKER_PATH}-mongo"
fi;
echo "Build ${{ matrix.release }} Docker image"
cp ${DOCKER_PATH}/Dockerfile .
if [ -e ${DOCKER_PATH}/entrypoint.sh ]; then
cp ${DOCKER_PATH}/entrypoint.sh .
fi;
docker login -u $DOCKER_USER -p $DOCKER_PASS
- name: Build Docker image for tag
if: github.event_name == 'release'
run: |
cd /tmp/build
export CIRCLE_TAG="${GITHUB_REF#*tags/}"
docker build -t ${IMAGE}:$CIRCLE_TAG .
docker push ${IMAGE}:$CIRCLE_TAG
if echo "$CIRCLE_TAG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' ; then
export RELEASE="latest"
elif echo "$CIRCLE_TAG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' ; then
export RELEASE="release-candidate"
fi
docker tag ${IMAGE}:$CIRCLE_TAG ${IMAGE}:${RELEASE}
docker push ${IMAGE}:${RELEASE}
- name: Build Docker image for develop
if: github.ref == 'refs/heads/develop'
run: |
cd /tmp/build
docker build -t ${IMAGE}:develop .
docker push ${IMAGE}:develop

1
.gitignore vendored

@ -78,3 +78,4 @@ packages/rocketchat-i18n/i18n/livechat.*
tests/end-to-end/temporary_staged_test
.screenshots
/private/livechat
/storybook-static

@ -17,3 +17,4 @@ notices-for-facebook-graph-api-2
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze

@ -7,16 +7,17 @@ rocketchat:mongo-config
accounts-facebook@1.3.2
accounts-github@1.4.2
accounts-google@1.3.2
accounts-google@1.3.3
accounts-meteor-developer@1.4.2
accounts-password@1.5.1
accounts-password@1.5.2
accounts-twitter@1.4.2
blaze-html-templates
check@1.3.1
ddp-rate-limiter@1.0.7
ddp-common@1.4.0
dynamic-import@0.5.1
ecmascript@0.12.4
ecmascript@0.13.2
typescript@3.7.1
ejson@1.1.0
email@1.2.3
fastclick@1.0.13
@ -25,7 +26,7 @@ jquery@1.11.10
logging@1.1.20
meteor-base@1.4.0
mobile-experience@1.0.5
mongo@1.6.2
mongo@1.7.0
random@1.1.0
rate-limit@1.0.9
reactive-dict@1.3.0
@ -35,7 +36,7 @@ service-configuration@1.0.11
session@1.2.0
shell-server@0.4.0
spacebars
standard-minifier-js@2.4.1
standard-minifier-js@2.5.2
tracker@1.2.0
#rocketchat:google-natural-language
@ -57,7 +58,6 @@ jparker:gravatar
kadira:blaze-layout
kadira:flow-router
keepnox:perfect-scrollbar
mizzao:autocomplete
mizzao:timesync
mrt:reactive-store
mystor:device-detection
@ -77,10 +77,10 @@ littledata:synced-cron
edgee:slingshot
jalik:ufs-local@0.2.5
accounts-base@1.4.3
accounts-base@1.4.5
accounts-oauth@1.1.16
autoupdate@1.6.0
babel-compiler@7.3.4
babel-compiler@7.4.2
google-oauth@1.2.6
htmljs
less
@ -92,7 +92,8 @@ raix:eventemitter
routepolicy@1.1.0
sha@1.0.9
templating
webapp@1.7.3
webapp@1.7.5
webapp-hashing@1.0.9
rocketchat:oauth2-server
rocketchat:i18n
dandv:caret-position

@ -1 +1 @@
METEOR@1.8.1
METEOR@1.8.3

@ -1,25 +1,25 @@
accounts-base@1.4.4
accounts-base@1.4.5
accounts-facebook@1.3.2
accounts-github@1.4.2
accounts-google@1.3.3
accounts-meteor-developer@1.4.2
accounts-oauth@1.1.16
accounts-password@1.5.1
accounts-password@1.5.2
accounts-twitter@1.4.2
aldeed:simple-schema@1.5.4
allow-deny@1.1.0
autoupdate@1.6.0
babel-compiler@7.3.4
babel-runtime@1.3.0
babel-compiler@7.4.2
babel-runtime@1.4.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.3
blaze@2.3.4
blaze-html-templates@1.1.2
blaze-tools@1.0.10
boilerplate-generator@1.6.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.1.0
callback-hook@1.2.0
cfs:http-methods@0.0.32
check@1.3.1
coffeescript@1.0.17
@ -34,12 +34,12 @@ deps@1.0.12
diff-sequence@1.1.1
dispatch:run-as-user@1.1.1
dynamic-import@0.5.1
ecmascript@0.12.7
ecmascript@0.13.2
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.8.0
ecmascript-runtime-server@0.7.1
ecmascript-runtime-client@0.9.0
ecmascript-runtime-server@0.8.0
edgee:slingshot@0.7.1
ejson@1.1.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
facebook-oauth@1.6.0
@ -76,24 +76,23 @@ littledata:synced-cron@1.5.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
matb33:collection-hooks@0.8.4
matb33:collection-hooks@0.9.1
mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
meteor-developer-oauth@1.2.1
meteorhacks:inject-initial@1.0.4
meteorspark:util@0.2.0
minifier-css@1.4.2
minifier-js@2.4.1
minifier-css@1.4.3
minifier-js@2.5.1
minimongo@1.4.5
mizzao:autocomplete@0.5.1
mizzao:timesync@0.3.4
mobile-experience@1.0.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.13.0
modules-runtime@0.10.3
mongo@1.6.3
modules@0.14.0
modules-runtime@0.11.0
mongo@1.7.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
@ -103,13 +102,13 @@ mystor:device-detection@0.2.0
nimble:restivus@0.8.12
nooitaf:colors@1.1.2_1
npm-bcrypt@0.9.3
npm-mongo@3.1.2
npm-mongo@3.2.0
oauth@1.2.8
oauth1@1.2.2
oauth2@1.2.1
observe-sequence@1.0.16
ordered-dict@1.1.0
ostrio:cookies@2.4.0
ostrio:cookies@2.5.0
pauli:accounts-linkedin@5.0.0
pauli:linkedin-oauth@5.0.0
promise@0.11.2
@ -128,7 +127,7 @@ rocketchat:livechat@0.0.1
rocketchat:mongo-config@0.0.1
rocketchat:oauth2-server@2.1.0
rocketchat:push@3.3.1
rocketchat:streamer@1.0.2
rocketchat:streamer@1.1.0
rocketchat:tap-i18n@1.9.1
rocketchat:version@1.0.0
routepolicy@1.1.0
@ -141,7 +140,7 @@ socket-stream-client@0.2.2
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-js@2.4.1
standard-minifier-js@2.5.2
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
@ -150,8 +149,9 @@ tmeasday:check-npm-versions@0.3.2
todda00:friendly-slugs@0.6.0
tracker@1.2.0
twitter-oauth@1.2.0
typescript@3.7.1
ui@1.0.13
underscore@1.0.10
url@1.2.0
webapp@1.7.4
webapp@1.7.5
webapp-hashing@1.0.9

@ -0,0 +1,51 @@
const getMongoVersion = async function({ version, git }) {
try {
const workflows = await git.show([`${ version }:.github/workflows/build_and_test.yml`]);
const mongoMatch = workflows.match(/mongodb\-version: \[([^\]]+)\]/);
if (!mongoMatch) {
return [];
}
return mongoMatch[1].replace(/"/g, '').replace(/ /g, '').split(',');
} catch (e) {
console.error(e);
}
return [];
};
const getNodeNpmVersions = async function({ version, git, request }) {
try {
const meteorRelease = await git.show([`${ version }:.meteor/release`]);
if (!/^METEOR@(\d+\.){1,2}\d/.test(meteorRelease)) {
return {};
}
const meteorVersion = meteorRelease.replace(/\n|\s/g, '');
const requestResult = await request(`https://raw.githubusercontent.com/meteor/meteor/release/${ meteorVersion }/scripts/build-dev-bundle-common.sh`);
return {
node_version: requestResult.match(/NODE_VERSION=((?:\d+\.){2}\d)/m)[1],
npm_version: requestResult.match(/NPM_VERSION=((?:\d+\.){2}\d)/m)[1],
};
} catch (e) {
console.error(e);
}
return {};
};
module.exports = async function({ version, git, request }) {
const mongo_versions = await getMongoVersion({ version, git });
const {
node_version,
npm_version,
} = await getNodeNpmVersions({ version, git, request });
return {
node_version,
npm_version,
mongo_versions,
};
};

@ -13,7 +13,7 @@ let pkgJson = {};
try {
pkgJson = require(path.resolve( // eslint-disable-line import/no-dynamic-require
process.cwd(),
'./package.json'
'./package.json',
));
} catch (err) {
console.error('no root package.json found');
@ -21,7 +21,6 @@ try {
const files = [
'./package.json',
'./.travis/snap.sh',
'./.circleci/snap.sh',
'./.circleci/update-releases.sh',
'./.docker/Dockerfile',
@ -88,7 +87,7 @@ git.status()
type: 'confirm',
message: 'Commit files?',
name: 'commit',
}])
}]),
)
.then((answers) => {
if (!answers.commit) {

@ -1,6 +0,0 @@
#!/usr/bin/env bash
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
sh -e /etc/init.d/xvfb start
sleep 3
fi

@ -43,7 +43,7 @@ function startProcess(opts, callback) {
const proc = spawn(
opts.command,
opts.params,
opts.options
opts.options,
);
if (opts.waitForMessage) {

@ -5,7 +5,7 @@ let pkgJson = {};
try {
pkgJson = require(path.resolve( // eslint-disable-line import/no-dynamic-require
process.cwd(),
'./package.json'
'./package.json',
));
} catch (err) {
console.error('no root package.json found');

@ -1,6 +1,6 @@
#!/bin/bash
node_version="v8.15.1"
node_version="v8.17.0"
unamem="$(uname -m)"
if [[ $unamem == *aarch64* ]]; then

@ -1,11 +1,8 @@
import { action } from '@storybook/addon-actions';
import { withKnobs }from '@storybook/addon-knobs';
import { withKnobs } from '@storybook/addon-knobs';
import { MINIMAL_VIEWPORTS, INITIAL_VIEWPORTS } from '@storybook/addon-viewport/dist/defaults';
import { addDecorator, addParameters, configure } from '@storybook/react';
import React from 'react';
import { ConnectionStatusProvider } from '../client/components/providers/ConnectionStatusProvider.mock';
import { TranslationProvider } from '../client/components/providers/TranslationProvider.mock';
import { rocketChatDecorator } from './mocks/decorators';
addParameters({
viewport: {
@ -15,35 +12,9 @@ addParameters({
},
defaultViewport: 'responsive',
},
})
addDecorator(function RocketChatDecorator(fn) {
const linkElement = document.getElementById('theme-styles') || document.createElement('link');
if (linkElement.id !== 'theme-styles') {
require('../app/theme/client/main.css');
require('../app/theme/client/vendor/fontello/css/fontello.css');
require('../client/RocketChat.font.css');
linkElement.setAttribute('id', 'theme-styles');
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('href', 'https://open.rocket.chat/theme.css');
document.head.appendChild(linkElement);
}
return <ConnectionStatusProvider connected status='connected' reconnect={action('reconnect')}>
<TranslationProvider>
<style>{`
body {
background-color: white;
}
`}</style>
<div dangerouslySetInnerHTML={{ __html: require('!!raw-loader!../private/public/icons.svg').default }} />
<div className='global-font-family color-primary-font-color'>
{fn()}
</div>
</TranslationProvider>
</ConnectionStatusProvider>;
});
addDecorator(rocketChatDecorator);
addDecorator(withKnobs);
configure(require.context('../client', true, /\.stories\.js$/), module);

@ -0,0 +1,31 @@
import React from 'react';
import { MeteorProviderMock } from './providers';
export const rocketChatDecorator = (fn) => {
const linkElement = document.getElementById('theme-styles') || document.createElement('link');
if (linkElement.id !== 'theme-styles') {
require('../../app/theme/client/main.css');
require('../../app/theme/client/vendor/fontello/css/fontello.css');
require('../../client/rocketchat.font.css');
linkElement.setAttribute('id', 'theme-styles');
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('href', 'https://open.rocket.chat/theme.css');
document.head.appendChild(linkElement);
}
// eslint-disable-next-line import/no-unresolved
const { default: icons } = require('!!raw-loader!../../private/public/icons.svg');
return <MeteorProviderMock>
<style>{`
body {
background-color: white;
}
`}</style>
<div dangerouslySetInnerHTML={{ __html: icons }} />
<div className='global-font-family color-primary-font-color'>
{fn()}
</div>
</MeteorProviderMock>;
};

@ -27,7 +27,7 @@ export const Mongo = {
find: () => ({
observe: () => {},
fetch: () => [],
})
}),
}),
};
@ -63,7 +63,7 @@ window.Blaze = Blaze;
export const check = () => {};
export const FlowRouter = {
route: () => {}
route: () => {},
};
export const BlazeLayout = {};

@ -0,0 +1,67 @@
import i18next from 'i18next';
import React from 'react';
import { TranslationContext } from '../../client/contexts/TranslationContext';
let contextValue;
const getContextValue = () => {
if (contextValue) {
return contextValue;
}
i18next.init({
fallbackLng: 'en',
defaultNS: 'project',
resources: {
en: {
project: require('../../packages/rocketchat-i18n/i18n/en.i18n.json'),
},
},
interpolation: {
prefix: '__',
suffix: '__',
},
initImmediate: false,
});
const translate = (key, ...replaces) => {
if (typeof replaces[0] === 'object') {
const [options] = replaces;
return i18next.t(key, options);
}
if (replaces.length === 0) {
return i18next.t(key);
}
return i18next.t(key, {
postProcess: 'sprintf',
sprintf: replaces,
});
};
translate.has = (key) => key && i18next.exists(key);
contextValue = {
languages: [{
name: 'English',
en: 'English',
key: 'en',
}],
language: 'en',
translate,
};
return contextValue;
};
function TranslationProviderMock({ children }) {
return <TranslationContext.Provider children={children} value={getContextValue()} />;
}
export function MeteorProviderMock({ children }) {
return <TranslationProviderMock>
{children}
</TranslationProviderMock>;
}

@ -33,12 +33,12 @@ module.exports = async ({ config }) => {
config.plugins.push(new webpack.NormalModuleReplacementPlugin(
/^meteor/,
require.resolve('./meteor.js'),
require.resolve('./mocks/meteor.js'),
));
config.plugins.push(new webpack.NormalModuleReplacementPlugin(
/\.\/server\/index.js/,
require.resolve('./empty.js'),
require.resolve('./mocks/empty.js'),
));
config.mode = 'development';

@ -1,4 +1,4 @@
app/theme/client/vendor/fontello/css/fontello.css
packages/meteor-autocomplete/client/autocomplete.css
app/meteor-autocomplete/client/autocomplete.css
app/katex/katex.min.css
app/emoji-emojione/client/*.css

@ -1,104 +0,0 @@
language: node_js
services:
- docker
- mongodb
branches:
only:
- develop
- "/^\\d+\\.\\d+\\.\\d+(-rc\\.\\d+)?$/"
git:
depth: 1
node_js:
- '8'
addons:
apt:
sources:
- google-chrome
- ubuntu-toolchain-r-test
packages:
- google-chrome-stable
- g++-4.8
firefox: "latest"
before_cache:
- rm -rf $HOME/build/RocketChat/Rocket.Chat/.meteor/local/log
- rm -rf $HOME/build/RocketChat/Rocket.Chat/.meteor/local/run
- rm -rf $HOME/build/RocketChat/Rocket.Chat/.meteor/local/db
cache:
directories:
- "$HOME/node_modules"
- "$HOME/.meteor"
- "$HOME/.npm"
- "$HOME/.node-gyp"
- "$HOME/build/RocketChat/Rocket.Chat/node_modules"
- "$HOME/build/RocketChat/Rocket.Chat/.meteor/local"
- "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.npm"
- "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.app/node_modules"
- "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.app/.meteor/local"
before_install:
- if [ ! -e "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh; fi
# Start X Virtual Frame Buffer for headless testing with real browsers
- .scripts/start-xvfb.sh
install:
- export PATH="$HOME/.meteor:$PATH"
before_script:
- if [[ $TRAVIS_TAG ]]; then meteor reset; fi
- echo "replication:" | sudo tee -a /etc/mongod.conf
- |-
echo " replSetName: \"rs0\"" | sudo tee -a /etc/mongod.conf
- sudo service mongod restart
- mkdir /tmp/build
- meteor --version
- travis_retry meteor npm install
- |-
mongo --eval 'rs.initiate({_id:"rs0", members: [{"_id":1, "host":"localhost:27017"}]})'
- meteor npm run lint
- meteor npm run testunit
- travis_retry meteor build --headless /tmp/build
- mkdir /tmp/build-test
- tar -xf /tmp/build/Rocket.Chat.tar.gz -C /tmp/build-test/
- cd /tmp/build-test/bundle/programs/server
- npm install
- cd -
- mongo --eval 'rs.status()'
- mongo meteor --eval 'db.getCollectionNames()'
script:
- travis_retry npm test
- mongo meteor --eval 'db.dropDatabase()'
- unset MONGO_OPLOG_URL
- travis_retry npm test
before_deploy:
- source ".travis/setartname.sh"
- source ".travis/setdeploydir.sh"
- ".travis/setupsig.sh"
- ".travis/namefiles.sh"
deploy:
- provider: s3
access_key_id: AKIAIKIA7H7D47KUHYCA
secret_access_key: "$ACCESSKEY"
bucket: download.rocket.chat
skip_cleanup: true
upload_dir: build
local_dir: "$ROCKET_DEPLOY_DIR"
on:
condition: "$TRAVIS_PULL_REQUEST=false"
all_branches: true
# - provider: releases
# api-key: "$GITHUB_TOKEN"
# file_glob: true
# file: build/*
# skip_cleanup: true
# on:
# tags: true
after_deploy:
- ".travis/docker.sh"
- ".travis/update-releases.sh"
- ".travis/snap.sh"
env:
global:
- DISPLAY=:99.0
- CXX=g++-4.8
- secure: HrPOM5sBibYkMcf9aeQThYPCDiXeLkg0Xgv0HvH88/ku/gphDpNEjHNReHZM3cyfm9y3RhHpVdD+Zzy38S2goKyewRzpXJsuyerOYkjND0v3tivhs9CAX8PAUxj1U5zllTyH4bgW2ZwRtNnwnmtIM/JJlnySMpKVDqIZBpbhn3ph9bJ2J+BW3D3Jw8meQ1vCX8szIibyJK/5QX6HG2RBFXJGYoQ8DmR8jQv0aJQvT1Az5DO4yImk8tX4NP95qOc19Jywr1DsbaSBZeJ8lFJAmBpIGx7KAmUVCcxSxfbXGRhs2K4iEYb3rJ/dU6KiyPsKGUG4aYNGgbvcX0ZxX/BZ6ZU9ff0E4IIf43IxoN3ElrOqOFk5msJAXbrJEreINSzDqKOy8NFYtCQ49E2gwzfage4ZXkhFyx3wMPa5bzpr3ncsTceMjMVz03uL781X6NLuCkUmXv+n8K2MNhJU9Xinpdx1GRJm+0lXJspNNJ1ruHeJtls4epj4bmCwKmmZbFKPXqa5e8xVcMIkwt1LMiHduhE+WgKNHdOMhXrCcTxF62ybLlsHXmyLLJeNjTeKS8QG2XSoonClDAz/1R41I1DsMPblcgz9uvYCf7UtyftbhJ83bnJeEmOYQiwijLG0+QMq+B2+mmZan3Z7Hl7O53dnwuLxz7EO7EhQhY+CqHVgc6s=
- MONGO_OPLOG_URL: "mongodb://localhost:27017/local"
- MONGO_URL: "mongodb://localhost:27017/meteor"
- TEST_MODE: "true"

@ -1,15 +0,0 @@
#!/bin/bash
set -x
set -euvo pipefail
IFS=$'\n\t'
CURL_URL="https://registry.hub.docker.com/u/rocketchat/rocket.chat/trigger/$PUSHTOKEN/"
if [[ $TRAVIS_TAG ]]
then
CURL_DATA='{"source_type":"Tag","source_name":"'"$TRAVIS_TAG"'"}';
else
CURL_DATA='{"source_type":"Branch","source_name":"'"$TRAVIS_BRANCH"'"}';
fi
curl -H "Content-Type: application/json" --data "$CURL_DATA" -X POST "$CURL_URL"

@ -1,9 +0,0 @@
#!/bin/bash
set -x
set -euvo pipefail
IFS=$'\n\t'
FILENAME="$ROCKET_DEPLOY_DIR/rocket.chat-$ARTIFACT_NAME.tgz";
ln -s /tmp/build/Rocket.Chat.tar.gz "$FILENAME"
gpg --armor --detach-sign "$FILENAME"

@ -1,6 +0,0 @@
if [[ $TRAVIS_TAG ]]
then
export ARTIFACT_NAME="$(meteor npm run version --silent)"
else
export ARTIFACT_NAME="$(meteor npm run version --silent).$TRAVIS_BUILD_NUMBER"
fi

@ -1,2 +0,0 @@
export ROCKET_DEPLOY_DIR="/tmp/deploy"
mkdir -p $ROCKET_DEPLOY_DIR

@ -1,9 +0,0 @@
#!/bin/bash
set -x
set -euvo pipefail
IFS=$'\n\t'
cp .travis/sign.key.gpg /tmp
gpg --yes --batch --passphrase=$mypass /tmp/sign.key.gpg
gpg --allow-secret-key-import --import /tmp/sign.key
rm /tmp/sign.key

Binary file not shown.

@ -1,54 +0,0 @@
#!/bin/bash
set -euvo pipefail
IFS=$'\n\t'
# Add launchpad to known hosts
ssh-keyscan -t rsa -H git.launchpad.net > ~/.ssh/known_hosts
git config user.name "CI Bot"
git config user.email "rocketchat.buildmaster@git.launchpad.net"
# Determine the channel to push snap to.
if [[ $TRAVIS_TAG =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+ ]]; then
CHANNEL=candidate
RC_VERSION=$TRAVIS_TAG
elif [[ $TRAVIS_TAG ]]; then
CHANNEL=stable
RC_VERSION=$TRAVIS_TAG
else
CHANNEL=edge
RC_VERSION=2.3.2
fi
echo "Preparing to trigger a snap release for $CHANNEL channel"
cd $PWD/.snapcraft
# Decrypt key
openssl aes-256-cbc -K $encrypted_f5c8ae370556_key -iv $encrypted_f5c8ae370556_iv -in launchpadkey.enc -out launchpadkey -d
# Change permissions
chmod 0600 launchpadkey
# We need some meta data so it'll actually commit. This could be useful to have for debugging later.
echo "Tag: $TRAVIS_TAG \r\nBranch: $TRAVIS_BRANCH\r\nBuild: $TRAVIS_BUILD_NUMBER\r\nCommit: $TRAVIS_COMMIT" > buildinfo
# Clone launchpad repo for the channel down.
GIT_SSH_COMMAND="ssh -i launchpadkey" git clone -b $CHANNEL git+ssh://rocket.chat.buildmaster@git.launchpad.net/rocket.chat launchpad
# Rarely will change, but just incase we copy it all
cp -r resources buildinfo launchpad/
sed s/#{RC_VERSION}/$RC_VERSION/ snapcraft.yaml > launchpad/snapcraft.yaml
cd launchpad
git add resources snapcraft.yaml buildinfo
# Another place where basic meta data will live for at a glance info
git commit -m "Travis Build: $TRAVIS_BUILD_NUMBER Travis Commit: $TRAVIS_COMMIT"
# Push up up to the branch of choice.
GIT_SSH_COMMAND="ssh -i ../launchpadkey" git push origin $CHANNEL
# Clean up
cd ..
rm -rf launchpadkey launchpad

@ -1,8 +0,0 @@
#!/bin/bash
set -x
set -euvo pipefail
IFS=$'\n\t'
CURL_URL="https://rocket.chat/releases/update"
curl -X POST "$CURL_URL"

@ -1,4 +1,126 @@
# 2.4.0
`2019-12-27 · 4 🎉 · 28 🚀 · 21 🐛 · 19 🔍 · 21 👩💻👨💻`
### Engine versions
- Node: `8.17.0`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🎉 New features
- Invite links: share a link to invite users ([#15933](https://github.com/RocketChat/Rocket.Chat/pull/15933))
- Logout other clients when changing password ([#15927](https://github.com/RocketChat/Rocket.Chat/pull/15927))
- Do not print emails in console on production mode ([#15928](https://github.com/RocketChat/Rocket.Chat/pull/15928))
- Apps-Engine event for when a livechat room is closed ([#15837](https://github.com/RocketChat/Rocket.Chat/pull/15837))
### 🚀 Improvements
- Replace livechat:inquiry publication by REST and Streamer ([#15977](https://github.com/RocketChat/Rocket.Chat/pull/15977))
- Sorting on livechat analytics queries were wrong ([#16021](https://github.com/RocketChat/Rocket.Chat/pull/16021))
- Replace fullUserData publication by REST ([#15650](https://github.com/RocketChat/Rocket.Chat/pull/15650))
- Replace integrations and integrationHistory publications by REST ([#15885](https://github.com/RocketChat/Rocket.Chat/pull/15885))
- Notify logged agents when their departments change ([#16033](https://github.com/RocketChat/Rocket.Chat/pull/16033))
- Replace fullEmojiData publication by REST ([#15901](https://github.com/RocketChat/Rocket.Chat/pull/15901))
- Replace adminRooms publication by REST ([#15948](https://github.com/RocketChat/Rocket.Chat/pull/15948))
- Replace webdavAccounts publication by REST ([#15926](https://github.com/RocketChat/Rocket.Chat/pull/15926))
- Replace oauth publications by REST ([#15878](https://github.com/RocketChat/Rocket.Chat/pull/15878))
- Replace userAutocomplete publication by REST ([#15956](https://github.com/RocketChat/Rocket.Chat/pull/15956))
- Replace discussionsOfARoom publication by REST ([#15908](https://github.com/RocketChat/Rocket.Chat/pull/15908))
- Move 'Reply in Thread' button from menu to message actions ([#15685](https://github.com/RocketChat/Rocket.Chat/pull/15685) by [@antkaz](https://github.com/antkaz))
- Replace customSounds publication by REST ([#15907](https://github.com/RocketChat/Rocket.Chat/pull/15907))
- Replace stdout publication by REST ([#16004](https://github.com/RocketChat/Rocket.Chat/pull/16004))
- Replace fullUserStatusData publication by REST ([#15942](https://github.com/RocketChat/Rocket.Chat/pull/15942))
- Replace userData subscriptions by REST ([#15916](https://github.com/RocketChat/Rocket.Chat/pull/15916))
- Replace roles publication by REST ([#15910](https://github.com/RocketChat/Rocket.Chat/pull/15910))
- Livechat realtime dashboard ([#15792](https://github.com/RocketChat/Rocket.Chat/pull/15792))
- Replace livechat:rooms publication by REST ([#15968](https://github.com/RocketChat/Rocket.Chat/pull/15968))
- Replace livechat:officeHour publication to REST ([#15503](https://github.com/RocketChat/Rocket.Chat/pull/15503))
- Replace forgotten livechat:departmentAgents subscriptions ([#15970](https://github.com/RocketChat/Rocket.Chat/pull/15970))
- Replace livechat:managers publication by REST ([#15944](https://github.com/RocketChat/Rocket.Chat/pull/15944))
- Replace livechat:visitorHistory publication by REST ([#15943](https://github.com/RocketChat/Rocket.Chat/pull/15943))
- Replace livechat:queue subscription ([#15612](https://github.com/RocketChat/Rocket.Chat/pull/15612))
- Add deprecate warning in some unused publications ([#15935](https://github.com/RocketChat/Rocket.Chat/pull/15935))
- Replace livechat:customFields to REST ([#15496](https://github.com/RocketChat/Rocket.Chat/pull/15496))
- Validate user identity on send message process ([#15887](https://github.com/RocketChat/Rocket.Chat/pull/15887))
- Update ui for Roles field ([#15888](https://github.com/RocketChat/Rocket.Chat/pull/15888) by [@antkaz](https://github.com/antkaz))
### 🐛 Bug fixes
- Importer: Variable name appearing instead of it's value ([#16010](https://github.com/RocketChat/Rocket.Chat/pull/16010) by [@ashwaniYDV](https://github.com/ashwaniYDV))
- Add time format for latest message on the sidebar ([#15930](https://github.com/RocketChat/Rocket.Chat/pull/15930) by [@ritwizsinha](https://github.com/ritwizsinha))
- Admin Setting descriptions and Storybook ([#15994](https://github.com/RocketChat/Rocket.Chat/pull/15994))
- width of upload-progress-text ([#16023](https://github.com/RocketChat/Rocket.Chat/pull/16023))
- Message list scrolling to bottom on reactions ([#16018](https://github.com/RocketChat/Rocket.Chat/pull/16018))
- SAML logout error ([#15978](https://github.com/RocketChat/Rocket.Chat/pull/15978))
- Added Join button to Read Only rooms. ([#16016](https://github.com/RocketChat/Rocket.Chat/pull/16016))
- z-index of new message button ([#16013](https://github.com/RocketChat/Rocket.Chat/pull/16013))
- new message popup ([#16017](https://github.com/RocketChat/Rocket.Chat/pull/16017))
- Changed renderMessage priority, fixed Katex on/off setting ([#16012](https://github.com/RocketChat/Rocket.Chat/pull/16012))
- Empty security section when 2fa is disabled ([#16009](https://github.com/RocketChat/Rocket.Chat/pull/16009))
- Dropzone being stuck when dragging to thread ([#16006](https://github.com/RocketChat/Rocket.Chat/pull/16006))
- Fix sort livechat rooms ([#16001](https://github.com/RocketChat/Rocket.Chat/pull/16001))
- Guest's name field missing when forwarding livechat rooms ([#15991](https://github.com/RocketChat/Rocket.Chat/pull/15991))
- Error of bind environment on user data export ([#15985](https://github.com/RocketChat/Rocket.Chat/pull/15985))
- Incorrect translation key on Livechat Appearance template ([#15975](https://github.com/RocketChat/Rocket.Chat/pull/15975) by [@ritwizsinha](https://github.com/ritwizsinha))
- Livechat build without NodeJS installed ([#15903](https://github.com/RocketChat/Rocket.Chat/pull/15903) by [@localguru](https://github.com/localguru))
- Server crash on sync with no response ([#15919](https://github.com/RocketChat/Rocket.Chat/pull/15919))
- Don't throw an error when a message is prevented from apps engine ([#15850](https://github.com/RocketChat/Rocket.Chat/pull/15850) by [@wreiske](https://github.com/wreiske))
- Thread Replies in Search ([#15841](https://github.com/RocketChat/Rocket.Chat/pull/15841))
- Registration form was hidden when login form was disabled ([#16062](https://github.com/RocketChat/Rocket.Chat/pull/16062))
<details>
<summary>🔍 Minor changes</summary>
- Update NodeJS to 8.17.0 ([#16043](https://github.com/RocketChat/Rocket.Chat/pull/16043))
- Fix typo in Italian translation ([#15998](https://github.com/RocketChat/Rocket.Chat/pull/15998) by [@iannuzzelli](https://github.com/iannuzzelli))
- Update Meteor to 1.8.3 ([#16037](https://github.com/RocketChat/Rocket.Chat/pull/16037))
- Some performance improvements ([#15886](https://github.com/RocketChat/Rocket.Chat/pull/15886))
- Fixed Grammatical Mistakes. ([#15570](https://github.com/RocketChat/Rocket.Chat/pull/15570) by [@breaking-let](https://github.com/breaking-let))
- Upgrade limax to 2.0.0 ([#16020](https://github.com/RocketChat/Rocket.Chat/pull/16020))
- Remove unnecessary cron starts ([#15989](https://github.com/RocketChat/Rocket.Chat/pull/15989))
- Enable typescript lint ([#15979](https://github.com/RocketChat/Rocket.Chat/pull/15979))
- LingoHub based on develop ([#15988](https://github.com/RocketChat/Rocket.Chat/pull/15988))
- Fix 'How it all started' link on README ([#15962](https://github.com/RocketChat/Rocket.Chat/pull/15962) by [@zdumitru](https://github.com/zdumitru))
- Check package-lock consistency with package.json on CI ([#15961](https://github.com/RocketChat/Rocket.Chat/pull/15961))
- Meteor update to 1.8.2 ([#15873](https://github.com/RocketChat/Rocket.Chat/pull/15873))
- GitHub CI ([#15918](https://github.com/RocketChat/Rocket.Chat/pull/15918))
- Change migration number 169 <-> 170 ([#15940](https://github.com/RocketChat/Rocket.Chat/pull/15940))
- LingoHub based on develop ([#15939](https://github.com/RocketChat/Rocket.Chat/pull/15939))
- [CHORE] Replace findOne with findOneById methods (Omnichannel) ([#15894](https://github.com/RocketChat/Rocket.Chat/pull/15894))
- Merge master into develop & Set version to 3.0.0-develop ([#15872](https://github.com/RocketChat/Rocket.Chat/pull/15872))
- Regression: Update components ([#16053](https://github.com/RocketChat/Rocket.Chat/pull/16053))
- Regression: Missing button to copy Invite links ([#16084](https://github.com/RocketChat/Rocket.Chat/pull/16084))
</details>
### 👩💻👨💻 Contributors 😍
- [@antkaz](https://github.com/antkaz)
- [@ashwaniYDV](https://github.com/ashwaniYDV)
- [@breaking-let](https://github.com/breaking-let)
- [@iannuzzelli](https://github.com/iannuzzelli)
- [@localguru](https://github.com/localguru)
- [@ritwizsinha](https://github.com/ritwizsinha)
- [@wreiske](https://github.com/wreiske)
- [@zdumitru](https://github.com/zdumitru)
### 👩💻👨💻 Core Team 🤓
- [@MarcosSpessatto](https://github.com/MarcosSpessatto)
- [@MartinSchoeler](https://github.com/MartinSchoeler)
- [@d-gubert](https://github.com/d-gubert)
- [@gabriellsh](https://github.com/gabriellsh)
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@lolimay](https://github.com/lolimay)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@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)
# 2.3.2
`2019-12-12 · 2 🐛 · 2 👩💻👨💻`
@ -100,9 +222,9 @@
- Livechat transfer history messages ([#15780](https://github.com/RocketChat/Rocket.Chat/pull/15780))
- Add button to reset.css ([#15773](https://github.com/RocketChat/Rocket.Chat/pull/15773))
- Mentions before blockquote ([#15774](https://github.com/RocketChat/Rocket.Chat/pull/15774))
- Sidebar font color was not respecting theming ([#15745](https://github.com/RocketChat/Rocket.Chat/pull/15745) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Sidebar font color was not respecting theming ([#15745](https://github.com/RocketChat/Rocket.Chat/pull/15745))
- Add livechat agents into departments ([#15732](https://github.com/RocketChat/Rocket.Chat/pull/15732))
- Changed cmsPage Style ([#15632](https://github.com/RocketChat/Rocket.Chat/pull/15632) by [@gabriellsh](https://github.com/gabriellsh))
- Changed cmsPage Style ([#15632](https://github.com/RocketChat/Rocket.Chat/pull/15632))
- Forward Livechat UI and the related permissions ([#15718](https://github.com/RocketChat/Rocket.Chat/pull/15718))
- line-height to show entire letters ([#15581](https://github.com/RocketChat/Rocket.Chat/pull/15581) by [@nstseek](https://github.com/nstseek))
- Apply server side filters on Livechat lists ([#15717](https://github.com/RocketChat/Rocket.Chat/pull/15717))
@ -110,7 +232,7 @@
- Livechat webhook broken when sending an image ([#15699](https://github.com/RocketChat/Rocket.Chat/pull/15699) by [@tatosjb](https://github.com/tatosjb))
- Sending messages to livechat rooms without a subscription ([#15707](https://github.com/RocketChat/Rocket.Chat/pull/15707))
- Duplicate label 'Hide Avatars' in accounts ([#15694](https://github.com/RocketChat/Rocket.Chat/pull/15694) by [@rajvaibhavdubey](https://github.com/rajvaibhavdubey))
- Block Show_Setup_Wizard Option ([#15623](https://github.com/RocketChat/Rocket.Chat/pull/15623) by [@gabriellsh](https://github.com/gabriellsh))
- Block Show_Setup_Wizard Option ([#15623](https://github.com/RocketChat/Rocket.Chat/pull/15623))
- Use Media Devices API to guess if a microphone is not available ([#15636](https://github.com/RocketChat/Rocket.Chat/pull/15636))
- Ignore file uploads from message box if text/plain content is being pasted ([#15631](https://github.com/RocketChat/Rocket.Chat/pull/15631))
- typo on PT-BR translation ([#15645](https://github.com/RocketChat/Rocket.Chat/pull/15645))
@ -147,9 +269,7 @@
- [@Exordian](https://github.com/Exordian)
- [@Montel](https://github.com/Montel)
- [@antkaz](https://github.com/antkaz)
- [@gabriellsh](https://github.com/gabriellsh)
- [@hmagarotto](https://github.com/hmagarotto)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@mpdbl](https://github.com/mpdbl)
- [@nstseek](https://github.com/nstseek)
- [@oguhpereira](https://github.com/oguhpereira)
@ -164,9 +284,11 @@
- [@MartinSchoeler](https://github.com/MartinSchoeler)
- [@d-gubert](https://github.com/d-gubert)
- [@engelgabriel](https://github.com/engelgabriel)
- [@gabriellsh](https://github.com/gabriellsh)
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@mar-v](https://github.com/mar-v)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@renatobecker](https://github.com/renatobecker)
- [@rodrigok](https://github.com/rodrigok)

@ -83,7 +83,7 @@ Join thousands of members worldwide 24/7 in our [community server](https://open.
[![Rocket.Chat](https://open.rocket.chat/api/v1/shield.svg?type=channel&name=Rocket.Chat&channel=dev)](https://open.rocket.chat/channel/dev) for developers needing help from the community to developing new features.
You can also join the conversation at [Twitter](https://twitter.com/RocketChat) and [Facebook](https://www.facebook.com/RocketChatApp).
You can also join the conversation on [Twitter](https://twitter.com/RocketChat) and [Facebook](https://www.facebook.com/RocketChatApp).
# Desktop Apps
Download the Native Cross-Platform Desktop Application at [Rocket.Chat.Electron](https://github.com/RocketChat/Rocket.Chat.Electron/releases)
@ -138,7 +138,7 @@ The easiest way to install a ready-to-run Rocket.Chat server on a Linux machine,
[![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/images/logo.png)](https://dfabric.github.io/DPlatform-ShellCore)
## IndieHosters
Get your Rocket.Chat instance hosted in a "as a Service" style. You register and we manage it for you! (updates, backup...).
Get your Rocket.Chat instance hosted in an "as a Service" style. You register and we manage it for you! (updates, backup...).
[![Rocket.Chat on IndieHosters](https://indie.host/signup.png)](https://indiehosters.net/shop/product/rocket-chat-21)
@ -236,7 +236,7 @@ Run Rocket.Chat on your easy to use personal device.
# About Rocket.Chat
Rocket.Chat is a Web Chat Server, developed in JavaScript, using the [Meteor](https://www.meteor.com/install) fullstack framework.
Rocket.Chat is a Web Chat Server, developed in JavaScript, using the [Meteor](https://www.meteor.com/install) full stack framework.
It is a great solution for communities and companies wanting to privately host their own chat service or for developers looking forward to build and evolve their own chat platforms.
@ -321,12 +321,12 @@ It is a great solution for communities and companies wanting to privately host t
## Roadmap
To see an up to date view of what we have planned view our [milestones](https://github.com/RocketChat/Rocket.Chat/milestones).
To see an up to date view of what we have planned, view our [milestones](https://github.com/RocketChat/Rocket.Chat/milestones).
## How it all started
Read about [how it all started](https://blog.blackducksoftware.com/rocket-chat-enabling-privately-hosted-chat-services).
Read about [how it all started](https://www.synopsys.com/blogs/software-security/rocket-chat-privately-hosted-chat-services/).
## Awards
[![InfoWorld Bossie Awards 2016 - Best Open Source Applications](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/bossie.png)](http://www.infoworld.com/article/3122000/open-source-tools/bossie-awards-2016-the-best-open-source-applications.html#slide4)
@ -352,7 +352,7 @@ Please use the [Stack Overflow TAG](http://stackoverflow.com/questions/tagged/ro
#### Hubot
The docker image is ready.
Everyone can start hacking the adapter code, or launch his/her own bot within a few minutes now.
Everyone can start hacking the adapter code or launch his/her own bot within a few minutes now.
Please head over to the [Hubot Integration Project](https://github.com/RocketChat/hubot-rocketchat) for more information.
@ -393,7 +393,7 @@ meteor npm install
meteor npm start
```
In order to debug the server part use [meteor debugging](https://docs.meteor.com/commandline.html#meteordebug). You should use Chrome for best debugging experience:
To debug the server part, use [meteor debugging](https://docs.meteor.com/commandline.html#meteordebug). You should use Chrome for best debugging experience:
```sh
meteor debug
@ -416,7 +416,7 @@ If you want to help, send an email to support at rocket.chat to be invited to th
## How to Contribute
Already a JavaScript developer? Familiar with Meteor? [Pick an issue](https://github.com/RocketChat/Rocket.Chat/labels/contrib%3A%20easy), push a PR and instantly become a member of Rocket.Chat's international contributors community. For more information, check out our [Contributing Guide](.github/CONTRIBUTING.md) and our [Official Documentation for Contributors](https://rocket.chat/docs/contributing/).
Already a JavaScript developer? Familiar with Meteor? [Pick an issue](https://github.com/RocketChat/Rocket.Chat/labels/contrib%3A%20easy), push a PR and instantly become a member of Rocket.Chat's international contributors' community. For more information, check out our [Contributing Guide](.github/CONTRIBUTING.md) and our [Official Documentation for Contributors](https://rocket.chat/docs/contributing/).
A lot of work has already gone into Rocket.Chat, but we have much bigger plans for it!
@ -434,7 +434,7 @@ Thanks to our core team
[Marcelo Schmidt](https://github.com/marceloschmidt),
[Rodrigo Nascimento](https://github.com/rodrigok),
[Sing Li](https://github.com/Sing-Li),
and to hundreds of awesome [contributors](https://github.com/RocketChat/Rocket.Chat/graphs/contributors).
and hundreds of awesome [contributors](https://github.com/RocketChat/Rocket.Chat/graphs/contributors).
![JoyPixels](https://i.imgur.com/OrhYvLe.png)

@ -1,4 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { DDPCommon } from 'meteor/ddp-common';
import { DDP } from 'meteor/ddp';
import { Accounts } from 'meteor/accounts-base';
@ -312,6 +313,13 @@ export class APIClass extends Restivus {
route: `${ this.request.route }${ this.request.method.toLowerCase() }`,
};
let result;
const connection = {
id: Random.id(),
close() {},
token: this.token,
};
try {
api.enforceRateLimit(objectForRateLimitMatch, this.request, this.response);
@ -321,7 +329,18 @@ export class APIClass extends Restivus {
});
}
result = originalAction.apply(this);
const invocation = new DDPCommon.MethodInvocation({
connection,
isSimulation: false,
userId: this.userId,
});
Accounts._accountData[connection.id] = {
connection,
};
Accounts._setAccountData(connection.id, 'loginToken', this.token);
result = DDP._CurrentInvocation.withValue(invocation, () => originalAction.apply(this));
} catch (e) {
logger.debug(`${ method } ${ route } threw an error:`, e.stack);
@ -331,6 +350,8 @@ export class APIClass extends Restivus {
}[e.error] || 'failure';
result = API.v1[apiMethod](e.message, e.error);
} finally {
delete Accounts._accountData[connection.id];
}
result = result || API.v1.success();
@ -545,6 +566,8 @@ const getUserAuth = function _getUserAuth(...args) {
token = Accounts._hashLoginToken(this.request.headers['x-auth-token']);
}
this.token = token;
return {
userId: this.request.headers['x-user-id'],
token,

@ -20,6 +20,7 @@ import './v1/emoji-custom';
import './v1/groups';
import './v1/im';
import './v1/integrations';
import './v1/invites';
import './v1/import';
import './v1/misc';
import './v1/permissions';
@ -32,5 +33,9 @@ import './v1/subscriptions';
import './v1/users';
import './v1/video-conference';
import './v1/autotranslate';
import './v1/webdav';
import './v1/oauthapps';
import './v1/custom-sounds';
import './v1/custom-user-status';
export { API, APIClass, defaultRateLimiterOptions } from './api';

@ -0,0 +1,20 @@
import { CustomSounds } from '../../../models/server/raw';
export async function findCustomSounds({ query = {}, pagination: { offset, count, sort } }) {
const cursor = await CustomSounds.find(query, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const sounds = await cursor.toArray();
return {
sounds,
count: sounds.length,
offset,
total,
};
}

@ -0,0 +1,20 @@
import { CustomUserStatus } from '../../../models/server/raw';
export async function findCustomUserStatus({ query = {}, pagination: { offset, count, sort } }) {
const cursor = await CustomUserStatus.find(query, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const statuses = await cursor.toArray();
return {
statuses,
count: statuses.length,
offset,
total,
};
}

@ -0,0 +1,20 @@
import { EmojiCustom } from '../../../models/server/raw';
export async function findEmojisCustom({ query = {}, pagination: { offset, count, sort } }) {
const cursor = EmojiCustom.find(query, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const emojis = await cursor.toArray();
return {
emojis,
count: emojis.length,
offset,
total,
};
}

@ -0,0 +1,27 @@
import { Integrations } from '../../../models/server/raw';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
const hasIntegrationsPermission = async (userId, integration) => {
const type = integration.type === 'webhook-incoming' ? 'incoming' : 'outgoing';
if (await hasPermissionAsync(userId, `manage-${ type }-integrations`)) {
return true;
}
if (userId === integration._createdBy._id) {
return hasPermissionAsync(userId, `manage-own-${ type }-integrations`);
}
return false;
};
export const findOneIntegration = async ({ userId, integrationId, createdBy }) => {
const integration = await Integrations.findOneByIdAndCreatedByIfExists({ _id: integrationId, createdBy });
if (!integration) {
throw new Error('The integration does not exists.');
}
if (!await hasIntegrationsPermission(userId, integration)) {
throw new Error('not-authorized');
}
return integration;
};

@ -115,3 +115,28 @@ export async function findSnippetedMessages({ uid, roomId, pagination: { offset,
total,
};
}
export async function findDiscussionsFromRoom({ uid, roomId, pagination: { offset, count, sort } }) {
const room = await Rooms.findOneById(roomId);
if (!await canAccessRoomAsync(room, { _id: uid })) {
throw new Error('error-not-allowed');
}
const cursor = Messages.findDiscussionsByRoom(roomId, {
sort: sort || { ts: -1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const messages = await cursor.toArray();
return {
messages,
count: messages.length,
offset,
total,
};
}

@ -0,0 +1,13 @@
import { OAuthApps } from '../../../models/server/raw';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
export async function findOAuthApps({ uid }) {
if (!await hasPermissionAsync(uid, 'manage-oauth-apps')) {
throw new Error('error-not-allowed');
}
return OAuthApps.find().toArray();
}
export async function findOneAuthApp({ clientId, appId }) {
return OAuthApps.findOneAuthAppByIdOrClientId({ clientId, appId });
}

@ -0,0 +1,77 @@
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { Rooms } from '../../../models/server/raw';
export async function findAdminRooms({ uid, filter, types = [], pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(uid, 'view-room-administration')) {
throw new Error('error-not-authorized');
}
const fields = {
prid: 1,
fname: 1,
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
usersCount: 1,
muted: 1,
unmuted: 1,
ro: 1,
default: 1,
topic: 1,
msgs: 1,
archived: 1,
tokenpass: 1,
};
const name = filter && filter.trim();
const discussion = types && types.includes('discussions');
const showTypes = Array.isArray(types) ? types.filter((type) => type !== 'discussions') : [];
const options = {
fields,
sort: sort || { default: -1, name: 1 },
skip: offset,
limit: count,
};
let cursor = Rooms.findByNameContaining(name, discussion, options);
if (name && showTypes.length) {
cursor = Rooms.findByNameContainingAndTypes(name, showTypes, discussion, options);
} else if (showTypes.length) {
cursor = Rooms.findByTypes(showTypes, discussion, options);
}
const total = await cursor.count();
const rooms = await cursor.toArray();
return {
rooms,
count: rooms.length,
offset,
total,
};
}
export async function findChannelAndPrivateAutocomplete({ uid, selector }) {
if (!await hasPermissionAsync(uid, 'view-other-user-channels')) {
return { items: [] };
}
const options = {
fields: {
_id: 1,
name: 1,
},
limit: 10,
sort: {
name: 1,
},
};
const rooms = await Rooms.findChannelAndPrivateByNameStarting(selector.name, options).toArray();
return {
items: rooms,
};
}

@ -0,0 +1,27 @@
import { Users } from '../../../models/server/raw';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
export async function findUsersToAutocomplete({ uid, selector }) {
if (!await hasPermissionAsync(uid, 'view-outside-room')) {
return { items: [] };
}
const exceptions = selector.exceptions || [];
const conditions = selector.conditions || {};
const options = {
fields: {
name: 1,
username: 1,
status: 1,
},
sort: {
username: 1,
},
limit: 10,
};
const users = await Users.findActiveByUsernameOrNameRegexWithExceptionsAndConditions(selector.term, exceptions, conditions, options).toArray();
return {
items: users,
};
}

@ -0,0 +1,14 @@
import { WebdavAccounts } from '../../../models/server/raw';
export async function findWebdavAccountsByUserId({ uid }) {
return {
accounts: await WebdavAccounts.findWithUserId(uid, {
fields: {
_id: 1,
username: 1,
server_url: 1,
name: 1,
},
}).toArray(),
};
}

@ -9,7 +9,7 @@ import { API } from '../api';
import Rooms from '../../../models/server/models/Rooms';
import Users from '../../../models/server/models/Users';
import { settings } from '../../../settings';
import { findMentionedMessages, findStarredMessages, findSnippetedMessageById, findSnippetedMessages } from '../lib/messages';
import { findMentionedMessages, findStarredMessages, findSnippetedMessageById, findSnippetedMessages, findDiscussionsFromRoom } from '../lib/messages';
API.v1.addRoute('chat.delete', { authRequired: true }, {
post() {
@ -680,3 +680,25 @@ API.v1.addRoute('chat.getSnippetedMessages', { authRequired: true }, {
return API.v1.success(messages);
},
});
API.v1.addRoute('chat.getDiscussions', { authRequired: true }, {
get() {
const { roomId } = this.queryParams;
const { sort } = this.parseJsonQuery();
const { offset, count } = this.getPaginationItems();
if (!roomId) {
throw new Meteor.Error('error-invalid-params', 'The required "roomId" query param is missing.');
}
const messages = Promise.await(findDiscussionsFromRoom({
uid: this.userId,
roomId,
pagination: {
offset,
count,
sort,
},
}));
return API.v1.success(messages);
},
});

@ -0,0 +1,18 @@
import { API } from '../api';
import { findCustomSounds } from '../lib/custom-sounds';
API.v1.addRoute('custom-sounds.list', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();
return API.v1.success(Promise.await(findCustomSounds({
query,
pagination: {
offset,
count,
sort,
},
})));
},
});

@ -0,0 +1,18 @@
import { API } from '../api';
import { findCustomUserStatus } from '../lib/custom-user-status';
API.v1.addRoute('custom-user-status.list', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();
return API.v1.success(Promise.await(findCustomUserStatus({
query,
pagination: {
offset,
count,
sort,
},
})));
},
});

@ -3,6 +3,7 @@ import Busboy from 'busboy';
import { EmojiCustom } from '../../../models';
import { API } from '../api';
import { findEmojisCustom } from '../lib/emoji-custom';
// DEPRECATED
// Will be removed after v3.0.0
@ -51,6 +52,22 @@ API.v1.addRoute('emoji-custom.list', { authRequired: true }, {
},
});
API.v1.addRoute('emoji-custom.all', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();
return API.v1.success(Promise.await(findEmojisCustom({
query,
pagination: {
offset,
count,
sort,
},
})));
},
});
API.v1.addRoute('emoji-custom.create', { authRequired: true }, {
post() {
Meteor.runAsUser(this.userId, () => {

@ -5,6 +5,7 @@ import { hasAtLeastOnePermission } from '../../../authorization/server';
import { IntegrationHistory, Integrations } from '../../../models';
import { API } from '../api';
import { mountIntegrationHistoryQueryBasedOnPermissions, mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission';
import { findOneIntegration } from '../lib/integrations';
API.v1.addRoute('integrations.create', { authRequired: true }, {
post() {
@ -172,3 +173,20 @@ API.v1.addRoute('integrations.remove', { authRequired: true }, {
}
},
});
API.v1.addRoute('integrations.get', { authRequired: true }, {
get() {
const { integrationId, createdBy } = this.queryParams;
if (!integrationId) {
return API.v1.failure('The query parameter "integrationId" is required.');
}
return API.v1.success({
integration: Promise.await(findOneIntegration({
userId: this.userId,
integrationId,
createdBy,
})),
});
},
});

@ -0,0 +1,61 @@
import { Meteor } from 'meteor/meteor';
import { API } from '../api';
import { findOrCreateInvite } from '../../../invites/server/functions/findOrCreateInvite';
import { removeInvite } from '../../../invites/server/functions/removeInvite';
import { listInvites } from '../../../invites/server/functions/listInvites';
import { useInviteToken } from '../../../invites/server/functions/useInviteToken';
import { validateInviteToken } from '../../../invites/server/functions/validateInviteToken';
API.v1.addRoute('listInvites', { authRequired: true }, {
get() {
const result = listInvites(this.userId);
return API.v1.success(result);
},
});
API.v1.addRoute('findOrCreateInvite', { authRequired: true }, {
post() {
const { rid, days, maxUses } = this.bodyParams;
const result = findOrCreateInvite(this.userId, { rid, days, maxUses });
return API.v1.success(result);
},
});
API.v1.addRoute('removeInvite/:_id', { authRequired: true }, {
delete() {
const { _id } = this.urlParams;
const result = removeInvite(this.userId, { _id });
return API.v1.success(result);
},
});
API.v1.addRoute('useInviteToken', { authRequired: true }, {
post() {
const { token } = this.bodyParams;
const result = useInviteToken(this.userId, token);
return API.v1.success(result);
},
});
API.v1.addRoute('validateInviteToken', { authRequired: false }, {
post() {
const { token } = this.bodyParams;
if (!token) {
throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', { method: 'validateInviteToken', field: 'token' });
}
let valid = true;
try {
validateInviteToken(token);
} catch (e) {
valid = false;
}
return API.v1.success({ valid });
},
});

@ -3,13 +3,14 @@ import { check } from 'meteor/check';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import s from 'underscore.string';
import { hasRole } from '../../../authorization';
import { Info } from '../../../utils';
import { Users } from '../../../models';
import { settings } from '../../../settings';
import { hasRole, hasPermission } from '../../../authorization/server';
import { Info } from '../../../utils/server';
import { Users } from '../../../models/server';
import { settings } from '../../../settings/server';
import { API } from '../api';
import { getDefaultUserFields } from '../../../utils/server/functions/getDefaultUserFields';
import { getURL } from '../../../utils/lib/getURL';
import { StdOut } from '../../../logger/server/publish';
// DEPRECATED
@ -162,7 +163,7 @@ API.v1.addRoute('spotlight', { authRequired: true }, {
const { query } = this.queryParams;
const result = Meteor.runAsUser(this.userId, () =>
Meteor.call('spotlight', query)
Meteor.call('spotlight', query),
);
return API.v1.success(result);
@ -202,3 +203,12 @@ API.v1.addRoute('directory', { authRequired: true }, {
});
},
});
API.v1.addRoute('stdout.queue', { authRequired: true }, {
get() {
if (!hasPermission(this.userId, 'view-logs')) {
return API.v1.unauthorized();
}
return API.v1.success({ queue: StdOut.queue });
},
});

@ -0,0 +1,23 @@
import { API } from '../api';
import { findOAuthApps, findOneAuthApp } from '../lib/oauthApps';
API.v1.addRoute('oauth-apps.list', { authRequired: true }, {
get() {
return API.v1.success({
oauthApps: Promise.await(findOAuthApps({ uid: this.userId })),
});
},
});
API.v1.addRoute('oauth-apps.get', { authRequired: true }, {
get() {
const { clientId, appId } = this.queryParams;
if (!clientId && !appId) {
return API.v1.failure('At least one of the query parameters "clientId" or "appId" is required.');
}
return API.v1.success({
oauthApp: Promise.await(findOneAuthApp({ clientId, appId })),
});
},
});

@ -4,6 +4,7 @@ import Busboy from 'busboy';
import { FileUpload } from '../../../file-upload';
import { Rooms, Messages } from '../../../models';
import { API } from '../api';
import { findAdminRooms, findChannelAndPrivateAutocomplete } from '../lib/rooms';
function findRoomByIdOrName({ params, checkedArchived = true }) {
if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) {
@ -134,8 +135,8 @@ API.v1.addRoute('rooms.saveNotification', { authRequired: true }, {
const saveNotifications = (notifications, roomId) => {
Object.keys(notifications).forEach((notificationKey) =>
Meteor.runAsUser(this.userId, () =>
Meteor.call('saveNotificationSettings', roomId, notificationKey, notifications[notificationKey])
)
Meteor.call('saveNotificationSettings', roomId, notificationKey, notifications[notificationKey]),
),
);
};
const { roomId, notifications } = this.bodyParams;
@ -274,3 +275,36 @@ API.v1.addRoute('rooms.getDiscussions', { authRequired: true }, {
});
},
});
API.v1.addRoute('rooms.adminRooms', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();
const { types, filter } = this.requestParams();
return API.v1.success(Promise.await(findAdminRooms({
uid: this.userId,
filter,
types,
pagination: {
offset,
count,
sort,
},
})));
},
});
API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true }, {
get() {
const { selector } = this.queryParams;
if (!selector) {
return API.v1.failure('The \'selector\' param is required');
}
return API.v1.success(Promise.await(findChannelAndPrivateAutocomplete({
uid: this.userId,
selector: JSON.parse(selector),
})));
},
});

@ -62,7 +62,7 @@ API.v1.addRoute('subscriptions.read', { authRequired: true }, {
});
Meteor.runAsUser(this.userId, () =>
Meteor.call('readMessages', this.bodyParams.rid)
Meteor.call('readMessages', this.bodyParams.rid),
);
return API.v1.success();
@ -77,7 +77,7 @@ API.v1.addRoute('subscriptions.unread', { authRequired: true }, {
}
Meteor.runAsUser(this.userId, () =>
Meteor.call('unreadMessages', firstUnreadMessage, roomId)
Meteor.call('unreadMessages', firstUnreadMessage, roomId),
);
return API.v1.success();

@ -16,9 +16,10 @@ import {
setUserAvatar,
saveCustomFields,
} from '../../../lib';
import { getFullUserData } from '../../../lib/server/functions/getFullUserData';
import { getFullUserData, getFullUserDataById } from '../../../lib/server/functions/getFullUserData';
import { API } from '../api';
import { setStatusText } from '../../../lib/server';
import { findUsersToAutocomplete } from '../lib/users';
API.v1.addRoute('users.create', { authRequired: true }, {
post() {
@ -152,14 +153,18 @@ API.v1.addRoute('users.getPresence', { authRequired: true }, {
API.v1.addRoute('users.info', { authRequired: true }, {
get() {
const { username } = this.getUserFromParams();
const { username, userId } = this.requestParams();
const { fields } = this.parseJsonQuery();
const result = getFullUserData({
const params = {
userId: this.userId,
filter: username,
limit: 1,
});
};
const result = userId
? getFullUserDataById({ userId: this.userId, filterId: userId })
: getFullUserData(params);
if (!result || result.count() !== 1) {
return API.v1.failure(`Failed to get the user data for the userId of "${ this.userId }".`);
}
@ -685,3 +690,17 @@ API.v1.addRoute('users.requestDataDownload', { authRequired: true }, {
});
},
});
API.v1.addRoute('users.autocomplete', { authRequired: true }, {
get() {
const { selector } = this.queryParams;
if (!selector) {
return API.v1.failure('The \'selector\' param is required');
}
return API.v1.success(Promise.await(findUsersToAutocomplete({
uid: this.userId,
selector: JSON.parse(selector),
})));
},
});

@ -0,0 +1,8 @@
import { API } from '../api';
import { findWebdavAccountsByUserId } from '../lib/webdav';
API.v1.addRoute('webdav.getMyAccounts', { authRequired: true }, {
get() {
return API.v1.success(Promise.await(findWebdavAccountsByUserId({ uid: this.userId })));
},
});

@ -170,7 +170,7 @@ export class AppCommandsBridge {
Object.freeze(user),
Object.freeze(room),
Object.freeze(params),
threadId
threadId,
);
return Promise.await(this.orch.getManager().getCommandManager().getPreviews(command, context));
}
@ -185,7 +185,7 @@ export class AppCommandsBridge {
Object.freeze(user),
Object.freeze(room),
Object.freeze(params),
threadId
threadId,
);
Promise.await(this.orch.getManager().getCommandManager().executePreview(command, preview, context));
}

@ -36,4 +36,14 @@ export class AppListenerBridge {
// this.orch.debugLog(e.stack);
// }
}
async livechatEvent(inte, room) {
const rm = this.orch.getConverters().get('rooms').convertRoom(room);
const result = await this.orch.getManager().getListenerManager().executeListener(inte, rm);
if (typeof result === 'boolean') {
return result;
}
return this.orch.getConverters().get('rooms').convertAppRoom(result);
}
}

@ -2,8 +2,9 @@ import { Random } from 'meteor/random';
import { getRoom } from '../../../livechat/server/api/lib/livechat';
import { Livechat } from '../../../livechat/server/lib/Livechat';
import Rooms from '../../../models/server/models/Rooms';
import LivechatRooms from '../../../models/server/models/LivechatRooms';
import LivechatVisitors from '../../../models/server/models/LivechatVisitors';
import LivechatDepartment from '../../../models/server/models/LivechatDepartment';
import Users from '../../../models/server/models/Users';
export class AppLivechatBridge {
@ -11,6 +12,10 @@ export class AppLivechatBridge {
this.orch = orch;
}
isOnline() {
return Livechat.online();
}
async createMessage(message, appId) {
this.orch.debugLog(`The App ${ appId } is creating a new message.`);
@ -48,14 +53,19 @@ export class AppLivechatBridge {
async createRoom(visitor, agent, appId) {
this.orch.debugLog(`The App ${ appId } is creating a livechat room.`);
const agentUser = Users.findOneById(agent.id);
agentUser.agentId = agentUser._id;
let agentRoom;
if (agent && agent.id) {
const user = Users.getAgentInfo(agent.id);
agentRoom = Object.assign({}, { agentId: user._id });
}
return this.orch.getConverters().get('rooms').convertRoom(getRoom({
const result = await getRoom({
guest: this.orch.getConverters().get('visitors').convertAppVisitor(visitor),
agent: agentUser,
agent: agentRoom,
rid: Random.id(),
}).room);
});
return this.orch.getConverters().get('rooms').convertRoom(result.room);
}
async closeRoom(room, comment, appId) {
@ -78,9 +88,9 @@ export class AppLivechatBridge {
let result;
if (departmentId) {
result = Rooms.findOpenByVisitorTokenAndDepartmentId(visitor.token, departmentId).fetch();
result = LivechatRooms.findOpenByVisitorTokenAndDepartmentId(visitor.token, departmentId).fetch();
} else {
result = Rooms.findOpenByVisitorToken(visitor.token).fetch();
result = LivechatRooms.findOpenByVisitorToken(visitor.token).fetch();
}
return result.map((room) => this.orch.getConverters().get('rooms').convertRoom(room));
@ -130,6 +140,40 @@ export class AppLivechatBridge {
async findVisitors(query, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat visitors.`);
if (this.orch.isDebugging()) {
console.warn('The method AppLivechatBridge.findVisitors is deprecated. Please consider using its alternatives');
}
return LivechatVisitors.find(query).fetch().map((visitor) => this.orch.getConverters().get('visitors').convertVisitor(visitor));
}
async findVisitorById(id, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat visitors.`);
return this.orch.getConverters().get('visitors').convertById(id);
}
async findVisitorByEmail(email, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat visitors.`);
return this.orch.getConverters().get('visitors').convertVisitor(LivechatVisitors.findOneGuestByEmailAddress(email));
}
async findVisitorByToken(token, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat visitors.`);
return this.orch.getConverters().get('visitors').convertVisitor(LivechatVisitors.getVisitorByToken(token));
}
async findVisitorByPhoneNumber(phoneNumber, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat visitors.`);
return this.orch.getConverters().get('visitors').convertVisitor(LivechatVisitors.findOneVisitorByPhone(phoneNumber));
}
async findDepartmentByIdOrName(value, appId) {
this.orch.debugLog(`The App ${ appId } is looking for livechat departments.`);
return this.orch.getConverters().get('departments').convertDepartment(LivechatDepartment.findOneByIdOrName(value));
}
}

@ -77,7 +77,7 @@ export class AppMessageBridge {
Users.findByIds(users, { fields: { _id: 1 } })
.fetch()
.forEach(({ _id }) =>
Notifications.notifyUser(_id, 'message', rmsg)
Notifications.notifyUser(_id, 'message', rmsg),
);
}
}

@ -135,6 +135,7 @@ export class AppMessagesConverter {
attachments,
reactions: message.reactions,
parseUrls: message.parseUrls,
token: message.token,
};
return Object.assign(newMessage, message._unmappedProperties_);

@ -48,7 +48,8 @@ export class AppVisitorsConverter {
name: visitor.name,
token: visitor.token,
phone: visitor.phone,
visitorEmails: visitor.visitorEmails,
...visitor.visitorEmails && { visitorEmails: visitor.visitorEmails },
...visitor.department && { department: visitor.department },
};
return Object.assign(newVisitor, visitor._unmappedProperties_);

@ -93,7 +93,7 @@ export const appsUpdateMarketplaceInfo = Meteor.bindEnvironment(function _appsUp
Promise.await(
Apps.updateAppsMarketplaceInfo(data)
.then(notifyAdminsAboutInvalidApps)
.then(notifyAdminsAboutRenewedApps)
.then(notifyAdminsAboutRenewedApps),
);
});
@ -104,5 +104,3 @@ SyncedCron.add({
appsUpdateMarketplaceInfo();
},
});
SyncedCron.start();

@ -0,0 +1,3 @@
import { Meteor } from 'meteor/meteor';
export const rolesStreamer = new Meteor.Streamer('roles');

@ -3,9 +3,15 @@ import { Meteor } from 'meteor/meteor';
import { hasAtLeastOnePermission } from './hasPermission';
import { CachedCollectionManager } from '../../ui-cached-collection';
import { AdminBox } from '../../ui-utils/client/lib/AdminBox';
import { APIClient } from '../../utils/client';
import { Roles } from '../../models/client';
import { rolesStreamer } from './lib/streamer';
Meteor.startup(() => {
CachedCollectionManager.onLogin(() => Meteor.subscribe('roles'));
CachedCollectionManager.onLogin(async () => {
const { roles } = await APIClient.v1.get('roles.list');
roles.forEach((role) => Roles.insert(role));
});
AdminBox.addOption({
href: 'admin-permissions',
@ -15,4 +21,12 @@ Meteor.startup(() => {
return hasAtLeastOnePermission(['access-permissions', 'access-setting-permissions']);
},
});
const events = {
changed: (role) => {
delete role.type;
Roles.upsert({ _id: role.name }, role);
},
removed: (role) => Roles.remove({ _id: role.name }),
};
rolesStreamer.on('roles', (role) => events[role.type](role));
});

@ -8,13 +8,12 @@ import { Template } from 'meteor/templating';
import { Roles } from '../../../models';
import { ChatPermissions } from '../lib/ChatPermissions';
import { hasAllPermission } from '../hasPermission';
import { hasAtLeastOnePermission } from '..';
import { t } from '../../../utils/client';
import { SideNav } from '../../../ui-utils/client/lib/SideNav';
import { CONSTANTS } from '../../lib';
import { hasAtLeastOnePermission } from '..';
Template.permissions.helpers({
tabsData() {
const {
@ -85,7 +84,7 @@ Template.permissions.helpers({
_id: 1,
},
limit,
}
},
);
},
@ -105,7 +104,7 @@ Template.permissions.helpers({
group: 1,
section: 1,
},
}
},
);
},

@ -95,7 +95,7 @@ Template.permissionsRole.helpers({
rules: [
{
collection: 'CachedChannelList',
subscription: 'channelAndPrivateAutocomplete',
endpoint: 'rooms.autocomplete.channelAndPrivate',
field: 'name',
template: Template.roomSearch,
noMatchTemplate: Template.roomSearchEmpty,
@ -118,7 +118,7 @@ Template.permissionsRole.helpers({
rules: [
{
collection: 'CachedUserList',
subscription: 'userAutocomplete',
endpoint: 'users.autocomplete',
field: 'username',
template: Template.userSearch,
noMatchTemplate: Template.userSearchEmpty,
@ -256,8 +256,6 @@ Template.permissionsRole.onCreated(async function() {
this.searchRoom = new ReactiveVar();
this.searchUsername = new ReactiveVar();
this.usersInRole = new ReactiveVar([]);
this.subscription = this.subscribe('roles', FlowRouter.getParam('name'));
});
Template.permissionsRole.onRendered(function() {

@ -18,8 +18,8 @@ export const roomAccessValidators = [
return;
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id);
if (subscription) {
const exists = await Subscriptions.countByRoomIdAndUserId(room._id, user._id);
if (exists) {
return true;
}
},

@ -1,6 +1,8 @@
import { Roles } from '../../../models';
import { Roles } from '../../../models/server/raw';
export const hasRole = (userId, roleNames, scope) => {
export const hasRoleAsync = async (userId, roleNames, scope) => {
roleNames = [].concat(roleNames);
return Roles.isUserInRoles(userId, roleNames, scope);
};
export const hasRole = (userId, roleNames, scope) => Promise.await(hasRoleAsync(userId, roleNames, scope));

@ -0,0 +1,5 @@
import { Meteor } from 'meteor/meteor';
export const rolesStreamer = new Meteor.Streamer('roles');
rolesStreamer.allowWrite('none');
rolesStreamer.allowRead('logged');

@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import * as Models from '../../../models/server';
import { hasPermission } from '../functions/hasPermission';
import { rolesStreamer } from '../lib/streamer';
Meteor.methods({
'authorization:deleteRole'(roleName) {
@ -34,7 +35,13 @@ Meteor.methods({
method: 'authorization:deleteRole',
});
}
return Models.Roles.remove(role.name);
const removed = Models.Roles.remove(role.name);
if (removed) {
rolesStreamer.emit('roles', {
type: 'removed',
name: roleName,
});
}
return removed;
},
});

@ -4,6 +4,7 @@ import { Roles } from '../../../models/server';
import { settings } from '../../../settings/server';
import { Notifications } from '../../../notifications/server';
import { hasPermission } from '../functions/hasPermission';
import { rolesStreamer } from '../lib/streamer';
Meteor.methods({
'authorization:saveRole'(roleData) {
@ -31,7 +32,10 @@ Meteor.methods({
_id: roleData.name,
});
}
rolesStreamer.emit('roles', {
type: 'changed',
...roleData,
});
return update;
},
});

@ -25,7 +25,7 @@ Permissions.on('change', ({ clientAction, id, data, diff }) => {
Notifications.notifyLoggedInThisInstance(
'permissions-changed',
clientAction,
data
data,
);
if (data.level && data.level === CONSTANTS.SETTINGS_LEVEL) {
@ -36,7 +36,7 @@ Permissions.on('change', ({ clientAction, id, data, diff }) => {
Notifications.notifyLoggedInThisInstance(
'private-settings-changed',
'updated',
setting
setting,
);
}
});

@ -16,7 +16,7 @@ Meteor.methods({
remove: Permissions.trashFindDeletedAfter(
updatedAt,
{},
{ fields: { _id: 1, _deletedAt: 1 } }
{ fields: { _id: 1, _deletedAt: 1 } },
).fetch(),
};
}

@ -4,6 +4,7 @@ import { Roles } from '../../../models';
import { clearCache } from '../functions/hasPermission';
Meteor.publish('roles', function() {
console.warn('The publication "roles" is deprecated and will be removed after version v3.0.0');
if (!this.userId) {
return this.ready();
}

@ -85,6 +85,7 @@ Meteor.startup(function() {
{ _id: 'view-outside-room', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'view-broadcast-member-list', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'call-management', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'view-l-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] },
{ _id: 'view-livechat-manager', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-rooms', roles: ['livechat-manager', 'admin'] },

@ -50,7 +50,7 @@ const createCallbackTimed = (hook, callbacks) =>
callbacks
.map(wrapCallback)
.map(handleResult)
.reduce(pipe, identity)
.reduce(pipe, identity),
);
const create = (hook, cbs) =>
@ -79,7 +79,7 @@ callbacks.add = function(
hook,
callback,
priority = callbacks.priority.MEDIUM,
id = Random.id()
id = Random.id(),
) {
callbacks[hook] = getHooks(hook);
if (callbacks[hook].find((cb) => cb.id === id)) {

@ -45,7 +45,7 @@ Meteor.loginWithCas = function(options, callback) {
const popup = openCenteredPopup(
loginUrl,
popup_width || 800,
popup_height || 600
popup_height || 600,
);

@ -3,7 +3,6 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { Blaze } from 'meteor/blaze';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';
import { AutoComplete } from 'meteor/mizzao:autocomplete';
import { Deps } from 'meteor/deps';
import toastr from 'toastr';
@ -11,6 +10,7 @@ import { ChatRoom } from '../../../models';
import { t, isEmail, handleError, roomTypes } from '../../../utils';
import { settings } from '../../../settings';
import resetSelection from '../resetSelection';
import { AutoComplete } from '../../../meteor-autocomplete/client';
const filterNames = (old) => {
const reg = new RegExp(`^${ settings.get('UTF8_Names_Validation') }$`);
@ -39,7 +39,7 @@ Template.mailMessagesInstructions.helpers({
rules: [
{
collection: 'CachedChannelList',
subscription: 'userAutocomplete',
endpoint: 'users.autocomplete',
field: 'username',
template: Template.userSearch,
noMatchTemplate: Template.userSearchEmpty,
@ -253,7 +253,7 @@ Template.mailMessagesInstructions.onCreated(function() {
rules: [
{
collection: 'UserAndRoom',
subscription: 'userAutocomplete',
endpoint: 'users.autocomplete',
field: 'username',
matchAll: true,
filter,

@ -412,7 +412,7 @@ Template.channelSettingsEditing.onCreated(function() {
canView() {
return roomTypes.roomTypes[room.t].allowRoomSettingChange(
room,
RoomSettingsEnum.SYSTEM_MESSAGES
RoomSettingsEnum.SYSTEM_MESSAGES,
);
},
getValue() {
@ -425,9 +425,9 @@ Template.channelSettingsEditing.onCreated(function() {
return call('saveRoomSettings', room._id, 'systemMessages', value).then(
() => {
toastr.success(
t('System_messages_setting_changed_successfully')
t('System_messages_setting_changed_successfully'),
);
}
},
);
},
},
@ -572,9 +572,9 @@ Template.channelSettingsEditing.onCreated(function() {
return call('saveRoomSettings', room._id, 'retentionOverrideGlobal', value).then(
() => {
toastr.success(
t('Retention_setting_changed_successfully')
t('Retention_setting_changed_successfully'),
);
}
},
);
},
},
@ -596,9 +596,9 @@ Template.channelSettingsEditing.onCreated(function() {
return call('saveRoomSettings', room._id, 'retentionMaxAge', value).then(
() => {
toastr.success(
t('Retention_setting_changed_successfully')
t('Retention_setting_changed_successfully'),
);
}
},
);
},
},
@ -620,9 +620,9 @@ Template.channelSettingsEditing.onCreated(function() {
return call('saveRoomSettings', room._id, 'retentionExcludePinned', value).then(
() => {
toastr.success(
t('Retention_setting_changed_successfully')
t('Retention_setting_changed_successfully'),
);
}
},
);
},
},
@ -644,9 +644,9 @@ Template.channelSettingsEditing.onCreated(function() {
return call('saveRoomSettings', room._id, 'retentionFilesOnly', value).then(
() => {
toastr.success(
t('Retention_setting_changed_successfully')
t('Retention_setting_changed_successfully'),
);
}
},
);
},
},
@ -664,7 +664,7 @@ Template.channelSettingsEditing.onCreated(function() {
save(value) {
return call('saveRoomSettings', room._id, 'encrypted', value).then(() => {
toastr.success(
t('Encrypted_setting_changed_successfully')
t('Encrypted_setting_changed_successfully'),
);
});
},

@ -315,7 +315,7 @@ class ChatpalProvider extends SearchProvider {
payload.start || 0,
payload.rows || this._settings.get('PageSize'),
callback,
params
params,
);
}
@ -332,7 +332,7 @@ class ChatpalProvider extends SearchProvider {
this._settings.get('Main_Language'),
this._getAcl(context),
type,
callback
callback,
);
}
}

@ -72,7 +72,7 @@ export function syncWorkspace(reconnectCheck = false) {
const { data } = result;
if (data.publicKey) {
if (data && data.publicKey) {
Settings.updateValueById('Cloud_Workspace_PublicKey', data.publicKey);
}

@ -322,7 +322,6 @@ const addCronJob = _.debounce(Meteor.bindEnvironment(function addCronJobDebounce
crowd.sync();
},
});
SyncedCron.start();
}
}), 500);

@ -7,10 +7,10 @@
<form class="search-form" role="form">
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{#if isReady}}
{{> icon block="rc-input__icon-svg" icon="magnifier" }}
{{else}}
{{#if isLoading}}
{{> loading }}
{{else}}
{{> icon block="rc-input__icon-svg" icon="magnifier" }}
{{/if}}
</div>
<input id="sound-filter" type="text" class="rc-input__element"
@ -43,13 +43,13 @@
</div>
</div>
</td>
<td width="20%">
<div class="rc-table-wrapper">
{{>icon _id=_id icon="play" block="icon-play-circled"}}
{{>icon _id=_id icon="pause" block="icon-pause-circled"}}
{{>icon _id=_id icon="ban" block="icon-reset-circled"}}
</div>
</td>
<td width="20%">
<div class="rc-table-wrapper">
{{>icon _id=_id icon="play" block="icon-play-circled"}}
{{>icon _id=_id icon="pause" block="icon-pause-circled"}}
{{>icon _id=_id icon="ban" block="icon-reset-circled"}}
</div>
</td>
</tr>
{{else}}
{{# with searchText}}
@ -58,11 +58,11 @@
</tr>
{{/with}}
{{/each}}
{{#unless isReady}}
{{#if isLoading}}
<tr class="table-no-click">
<td class="table-loading-td">{{> loading}}</td>
</tr>
{{/unless}}
{{/if}}
</tbody>
{{/table}}
{{/requiresPermission}}

@ -2,40 +2,25 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import s from 'underscore.string';
import _ from 'underscore';
import { CustomSounds as CustomSoundsModel } from '../../../models';
import { RocketChatTabBar, SideNav, TabBar } from '../../../ui-utils';
import { CustomSounds } from '../lib/CustomSounds';
import { APIClient } from '../../../utils/client';
const LIST_SIZE = 50;
const DEBOUNCE_TIME_TO_SEARCH_IN_MS = 500;
Template.adminSounds.helpers({
searchText() {
const instance = Template.instance();
return instance.filter && instance.filter.get();
},
isReady() {
if (Template.instance().ready != null) {
return Template.instance().ready.get();
}
return undefined;
},
customsounds() {
return Template.instance().customsounds();
return Template.instance().sounds.get();
},
isLoading() {
if (Template.instance().ready != null) {
if (!Template.instance().ready.get()) {
return 'btn-loading';
}
}
},
hasMore() {
if (Template.instance().limit != null) {
if (typeof Template.instance().customsounds === 'function') {
return Template.instance().limit.get() === Template.instance().customsounds().length;
}
}
return false;
return Template.instance().isLoading.get();
},
flexData() {
return {
@ -47,18 +32,22 @@ Template.adminSounds.helpers({
onTableScroll() {
const instance = Template.instance();
return function(currentTarget) {
if (
currentTarget.offsetHeight + currentTarget.scrollTop
>= currentTarget.scrollHeight - 100
) {
return instance.limit.set(instance.limit.get() + 50);
if (currentTarget.offsetHeight + currentTarget.scrollTop < currentTarget.scrollHeight - 100) {
return;
}
const sounds = instance.sounds.get();
if (instance.total.get() > sounds.length) {
instance.offset.set(instance.offset.get() + LIST_SIZE);
}
};
},
onTableItemClick() {
const instance = Template.instance();
return function(item) {
instance.tabBarData.set(CustomSoundsModel.findOne({ _id: item._id }));
instance.tabBarData.set({
sound: instance.sounds.get().find((sound) => sound._id === item._id),
onSuccess: instance.onSuccessCallback,
});
instance.tabBar.showGroup('custom-sounds-selected');
instance.tabBar.open('admin-sound-info');
};
@ -67,9 +56,12 @@ Template.adminSounds.helpers({
Template.adminSounds.onCreated(function() {
const instance = this;
this.limit = new ReactiveVar(50);
this.sounds = new ReactiveVar([]);
this.offset = new ReactiveVar(0);
this.total = new ReactiveVar(0);
this.query = new ReactiveVar({});
this.isLoading = new ReactiveVar(false);
this.filter = new ReactiveVar('');
this.ready = new ReactiveVar(false);
this.tabBar = new RocketChatTabBar();
this.tabBar.showGroup(FlowRouter.current().route.name);
@ -81,10 +73,6 @@ Template.adminSounds.onCreated(function() {
i18nTitle: 'Custom_Sound_Add',
icon: 'plus',
template: 'adminSoundEdit',
openClick(/* e, t*/) {
instance.tabBarData.set();
return true;
},
order: 1,
});
@ -97,33 +85,43 @@ Template.adminSounds.onCreated(function() {
order: 2,
});
this.autorun(function() {
const limit = instance.limit != null ? instance.limit.get() : 0;
const subscription = instance.subscribe('customSounds', '', limit);
instance.ready.set(subscription.ready());
});
this.onSuccessCallback = () => {
this.offset.set(0);
return this.loadSounds(this.query.get(), this.offset.get());
};
this.customsounds = function() {
const filter = instance.filter != null ? s.trim(instance.filter.get()) : '';
this.tabBarData.set({
onSuccess: instance.onSuccessCallback,
});
let query = {};
this.loadSounds = _.debounce(async (query, offset) => {
this.isLoading.set(true);
const { sounds, total } = await APIClient.v1.get(`custom-sounds.list?count=${ LIST_SIZE }&offset=${ offset }&query=${ JSON.stringify(query) }`);
this.total.set(total);
if (offset === 0) {
this.sounds.set(sounds);
} else {
this.sounds.set(this.sounds.get().concat(sounds));
}
this.isLoading.set(false);
}, DEBOUNCE_TIME_TO_SEARCH_IN_MS);
this.autorun(() => {
const filter = this.filter.get() && this.filter.get().trim();
const offset = this.offset.get();
if (filter) {
const filterReg = new RegExp(s.escapeRegExp(filter), 'i');
query = { name: filterReg };
const regex = { $regex: filter, $options: 'i' };
return this.loadSounds({ name: regex }, offset);
}
const limit = instance.limit != null ? instance.limit.get() : 0;
return CustomSoundsModel.find(query, { limit, sort: { name: 1 } }).fetch();
};
return this.loadSounds({}, offset);
});
});
Template.adminSounds.onRendered(() =>
Tracker.afterFlush(function() {
SideNav.setFlex('adminFlex');
SideNav.openFlex();
})
}),
);
Template.adminSounds.events({
@ -138,6 +136,7 @@ Template.adminSounds.events({
e.stopPropagation();
e.preventDefault();
t.filter.set(e.currentTarget.value);
t.offset.set(0);
},
'click .icon-play-circled'(e) {
e.preventDefault();

@ -1,12 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
FlowRouter.route('/admin/custom-sounds', {
name: 'custom-sounds',
subscriptions(/* params, queryParams*/) {
this.register('customSounds', Meteor.subscribe('customSounds'));
},
async action(/* params*/) {
await import('./views');
BlazeLayout.render('main', { center: 'adminSounds' });

@ -57,7 +57,7 @@ Template.soundEdit.onCreated(function() {
this.sound = undefined;
this.data.tabBar.showGroup('custom-sounds');
}
this.onSuccess = Template.currentData().onSuccess;
this.cancel = (form, name) => {
form.reset();
this.data.tabBar.close();
@ -133,7 +133,7 @@ Template.soundEdit.onCreated(function() {
handleError(uploadError);
console.log(uploadError);
}
}
},
);
delete this.soundFile;
toastr.success(TAPi18n.__('File_uploaded'));
@ -141,6 +141,7 @@ Template.soundEdit.onCreated(function() {
}
toastr.success(t('Custom_Sound_Saved_Successfully'));
this.onSuccess();
this.cancel(form, soundData.name);
}

@ -25,6 +25,7 @@ Template.soundInfo.helpers({
tabBar: instance.data.tabBar,
data: instance.data.data,
sound: instance.sound.get(),
onSuccess: instance.onSuccess,
back(name) {
instance.editingSound.set();
@ -68,7 +69,7 @@ Template.soundInfo.events({
timer: 2000,
showConfirmButton: false,
});
instance.onSuccess();
instance.data.tabBar.showGroup('custom-sounds');
instance.data.tabBar.close();
}
@ -91,6 +92,7 @@ Template.soundInfo.onCreated(function() {
this.editingSound = new ReactiveVar();
this.loadedName = new ReactiveVar();
this.onSuccess = Template.currentData().onSuccess;
this.autorun(() => {
const data = Template.currentData();
@ -100,7 +102,7 @@ Template.soundInfo.onCreated(function() {
});
this.autorun(() => {
const data = Template.currentData();
const data = Template.currentData().sound;
const sound = this.sound.get();
if (sound && sound.name != null) {
this.loadedName.set(sound.name);
@ -110,7 +112,7 @@ Template.soundInfo.onCreated(function() {
});
this.autorun(() => {
const data = Template.currentData();
const data = Template.currentData().sound;
this.sound.set(data);
});
});

@ -20,7 +20,7 @@ class CustomSoundsClass {
sound.src = this.getURL(sound);
}
const audio = $('<audio />', { id: sound._id, preload: true }).append(
$('<source />', { src: sound.src })
$('<source />', { src: sound.src }),
);
const list = this.list.get();
list[sound._id] = sound;
@ -81,5 +81,5 @@ Meteor.startup(() =>
CustomSounds.add(sound);
}
});
})
}),
);

@ -6,6 +6,6 @@ import { CustomSounds } from '../lib/CustomSounds';
Meteor.startup(() =>
CachedCollectionManager.onLogin(() =>
Notifications.onAll('deleteCustomSound', (data) => CustomSounds.remove(data.soundData))
)
Notifications.onAll('deleteCustomSound', (data) => CustomSounds.remove(data.soundData)),
),
);

@ -6,6 +6,6 @@ import { CustomSounds } from '../lib/CustomSounds';
Meteor.startup(() =>
CachedCollectionManager.onLogin(() =>
Notifications.onAll('updateCustomSound', (data) => CustomSounds.update(data.soundData))
)
Notifications.onAll('updateCustomSound', (data) => CustomSounds.update(data.soundData)),
),
);

@ -17,7 +17,7 @@ Meteor.methods({
RocketChatFileCustomSoundsInstance.deleteFile(`${ soundData._id }.${ soundData.extension }`);
const ws = RocketChatFileCustomSoundsInstance.createWriteStream(`${ soundData._id }.${ soundData.extension }`, contentType);
ws.on('end', Meteor.bindEnvironment(() =>
Meteor.setTimeout(() => Notifications.notifyAll('updateCustomSound', { soundData }), 500)
Meteor.setTimeout(() => Notifications.notifyAll('updateCustomSound', { soundData }), 500),
));
rs.pipe(ws);

@ -4,6 +4,7 @@ import s from 'underscore.string';
import { CustomSounds } from '../../../models';
Meteor.publish('customSounds', function(filter, limit) {
console.warn('The publication "customSounds" is deprecated and will be removed after version v3.0.0');
if (!this.userId) {
return this.ready();
}

@ -5,7 +5,6 @@ import './views/DiscussionTabbar';
// Other UI extensions
import './lib/messageTypes/discussionMessage';
import './lib/discussionsOfRoom';
import './createDiscussionMessageAction';
import './discussionFromMessageBox';
import './tabBar';

@ -1,3 +0,0 @@
import { Mongo } from 'meteor/mongo';
export const DiscussionOfRoom = new Mongo.Collection('rocketchat_discussions_of_room');

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save