diff --git a/.circleci/config.yml b/.circleci/config.yml index 0bea79326b8c..dec1de4c14ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,6 +80,8 @@ jobs: - run: name: Build Rocket.Chat + environment: + TOOL_NODE_FLAGS: --max_old_space_size=4096 command: | if [[ $CIRCLE_TAG ]]; then meteor reset; fi set +e @@ -139,7 +141,7 @@ jobs: environment: TEST_MODE: "true" - MONGO_URL: mongodb://localhost:27017/testwithoplog + MONGO_URL: mongodb://localhost:27017/rocketchat MONGO_OPLOG_URL: mongodb://localhost:27017/local RETRY_TESTS: 5 @@ -200,9 +202,11 @@ jobs: name: Install dependencies command: | wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add - + sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 echo "deb [ arch=amd64 ] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google.list + echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list sudo apt-get update - sudo apt-get install -y google-chrome-stable + sudo apt-get install -y mongodb-org-shell google-chrome-stable - run: name: NPM install diff --git a/.docker/Dockerfile.rhel b/.docker/Dockerfile.rhel index 7abbbd4f9847..3b8967f78965 100644 --- a/.docker/Dockerfile.rhel +++ b/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/rhscl/nodejs-8-rhel7 -ENV RC_VERSION 0.70.4 +ENV RC_VERSION 0.71.1 MAINTAINER buildmaster@rocket.chat diff --git a/.eslintrc b/.eslintrc index 7a4aeb6d2fd6..75450d624d50 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "extends": ["@rocket.chat/eslint-config"], + "parser": "babel-eslint", "globals": { "__meteor_runtime_config__" : false, "AccountBox" : false, @@ -30,7 +31,7 @@ "LivechatDepartment" : false, "LivechatDepartmentAgents" : false, "livechatManagerRoutes" : true, - "LivechatMonitoring" : false, + "LivechatMonitoring" : false, "LivechatPageVisited" : false, "LivechatTrigger" : false, "Logger" : false, diff --git a/.github/history.json b/.github/history.json index 5113ce28f65f..6e93977d8328 100644 --- a/.github/history.json +++ b/.github/history.json @@ -19834,9 +19834,31 @@ ] }, "0.70.2": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [] + }, + "0.70.3": { "node_version": "8.11.3", "npm_version": "5.6.0", "pull_requests": [ + { + "pr": "12281", + "title": "Release 0.70.3", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12276", + "title": "Release 0.70.2", + "userLogin": "sampaiodiego", + "contributors": [ + "Hudell", + "sampaiodiego" + ] + }, { "pr": "12272", "title": "[FIX] E2E alert shows up when encryption is disabled", @@ -19848,16 +19870,334 @@ } ] }, - "0.70.3": { + "0.70.4": { "node_version": "8.11.3", "npm_version": "5.6.0", "pull_requests": [ { - "pr": "12276", - "title": "Release 0.70.2", + "pr": "12299", + "title": "Release 0.70.4", "userLogin": "sampaiodiego", "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12283", + "title": "[FIX] Modal confirm on enter", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12298", + "title": "Fix: Add wizard opt-in fields", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + } + ] + }, + "0.71.0-rc.0": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "11521", + "title": "[FIX] Add image dimensions to attachment even when no reorientation is required", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "12158", + "title": "[FIX] iframe login token not checked", + "userLogin": "nimetu", + "contributors": [ + "nimetu", + "web-flow" + ] + }, + { + "pr": "11431", + "title": "[FIX] REST `users.setAvatar` endpoint wasn't allowing update the avatar of other users even with correct permissions", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto", + "sampaiodiego" + ] + }, + { + "pr": "11875", + "title": "[FIX] Slack importer: image previews not showing", + "userLogin": "madguy02", + "milestone": "0.71.0", + "contributors": [ + "madguy02", "Hudell", + "web-flow", + "sampaiodiego" + ] + }, + { + "pr": "12235", + "title": "[FIX] Edit room name with uppercase letters", + "userLogin": "nikeee", + "milestone": "0.71.0", + "contributors": [ + "nikeee", + "web-flow" + ] + }, + { + "pr": "12256", + "title": "[FIX] Custom OAuth Configuration can't be removed", + "userLogin": "Hudell", + "milestone": "0.71.0", + "contributors": [ + "Hudell" + ] + }, + { + "pr": "12360", + "title": "[IMPROVE] Livechat room closure endpoints", + "userLogin": "renatobecker", + "milestone": "0.71.0", + "contributors": [ + "renatobecker" + ] + }, + { + "pr": "12266", + "title": "[BREAK] Update `lastMessage` rooms property and convert the \"starred\" property, to the same format", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "12384", + "title": "Fix: wrong saveUser permission validations", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "11860", + "title": "[NEW] Add delete channel mutation to GraphQL API", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "12344", + "title": "[FIX] Remove e2e from users endpoint responses", + "userLogin": "MarcosSpessatto", + "contributors": [ + "MarcosSpessatto", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "12372", + "title": "[NEW] sidenav size on large screens", + "userLogin": "ggazzo", + "milestone": "0.71.0", + "contributors": [ + "ggazzo", + "tassoevan", + "web-flow" + ] + }, + { + "pr": "12375", + "title": "Regression: do not render pdf preview on safari <= 12", + "userLogin": "ggazzo", + "milestone": "0.71.0", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "12373", + "title": "[FIX] email api TAPi18n is undefined", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "12338", + "title": "[FIX] Blockstack errors in IE 11", + "userLogin": "tassoevan", + "milestone": "0.71.0", + "contributors": [ + "tassoevan", + "sampaiodiego" + ] + }, + { + "pr": "12365", + "title": "[FIX] avatar?_dc=undefined", + "userLogin": "geekgonecrazy", + "contributors": [ + "geekgonecrazy", + "web-flow" + ] + }, + { + "pr": "12186", + "title": "[BREAK] Add expiration to API login tokens and fix duplicate login tokens created by LDAP", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "12358", + "title": "Improve: Drop database between running tests on CI", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12161", + "title": "[IMPROVE] Set Livechat department before register guest", + "userLogin": "renatobecker", + "milestone": "0.71.0", + "contributors": [ + "renatobecker" + ] + }, + { + "pr": "12330", + "title": "[IMPROVE] Add missing livechat i18n keys", + "userLogin": "MarcosEllys", + "contributors": [ + "MarcosEllys", + "web-flow" + ] + }, + { + "pr": "12297", + "title": "[FIX] users.register endpoint to not create an user if username already being used", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto" + ] + }, + { + "pr": "12345", + "title": "[FIX] Date range check on livechat analytics", + "userLogin": "teresy", + "contributors": [ + "teresy" + ] + }, + { + "pr": "12194", + "title": "[FIX] Cast env var setting to int based on option type", + "userLogin": "crazy-max", + "milestone": "0.71.0", + "contributors": [ + "crazy-max" + ] + }, + { + "pr": "12355", + "title": "[FIX] Links in home layout", + "userLogin": "upiksaleh", + "contributors": [ + "upiksaleh" + ] + }, + { + "pr": "11212", + "title": "[IMPROVE] Avoid unnecessary calls to Meteor.user() on client", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow", + "tassoevan" + ] + }, + { + "pr": "12346", + "title": "Fix: update check on err.details", + "userLogin": "teresy", + "milestone": "0.71.0", + "contributors": [ + "teresy" + ] + }, + { + "pr": "12350", + "title": "[FIX] Last message not updating after message delete if show deleted status is on", + "userLogin": "sampaiodiego", + "milestone": "0.71.0", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12353", + "title": "[NEW] Ability to disable user presence monitor", + "userLogin": "sampaiodiego", + "milestone": "0.71.0", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12354", + "title": "[FIX] Invalid destructuring on Livechat API endpoint", + "userLogin": "renatobecker", + "milestone": "0.71.0", + "contributors": [ + "renatobecker" + ] + }, + { + "pr": "10519", + "title": "[NEW] PDF message attachment preview (client side rendering)", + "userLogin": "kb0304", + "milestone": "0.71.0", + "contributors": [ + "kb0304", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "12298", + "title": "Fix: Add wizard opt-in fields", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12283", + "title": "[FIX] Modal confirm on enter", + "userLogin": "sampaiodiego", + "contributors": [ "sampaiodiego" ] }, @@ -19869,24 +20209,170 @@ "contributors": [ "Hudell" ] + }, + { + "pr": "12299", + "title": "Release 0.70.4", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12276", + "title": "Release 0.70.2", + "userLogin": "sampaiodiego", + "contributors": [ + "Hudell", + "sampaiodiego" + ] } ] }, - "0.70.4": { + "0.71.0-rc.1": { "node_version": "8.11.3", "npm_version": "5.6.0", "pull_requests": [ { - "pr": "12283", - "title": "[FIX] Modal confirm on enter", + "pr": "12398", + "title": "[FIX] E2E: Decrypting UTF-8 encoded messages", + "userLogin": "pmmaga", + "milestone": "0.71.0", + "contributors": [ + "pmmaga", + "web-flow" + ] + }, + { + "pr": "12442", + "title": "Update Apps Framework to version 1.2.1", + "userLogin": "rodrigok", + "milestone": "0.71.0", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "12424", + "title": "[FIX] Ignore errors when creating image preview for uploads", + "userLogin": "tassoevan", + "milestone": "0.71.0", + "contributors": [ + "tassoevan", + "web-flow" + ] + }, + { + "pr": "12405", + "title": "Regression: Change `starred` message property from object to array", + "userLogin": "MarcosSpessatto", + "milestone": "0.71.0", + "contributors": [ + "MarcosSpessatto", + "sampaiodiego" + ] + }, + { + "pr": "12425", + "title": "[IMPROVE] Allow the imports to accept any file type", + "userLogin": "graywolf336", + "milestone": "0.71.0", + "contributors": [ + "graywolf336" + ] + }, + { + "pr": "12409", + "title": "Apps: Room’s usernames was not working", + "userLogin": "rodrigok", + "milestone": "0.71.0", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "12436", + "title": "[FIX] Attachment actions not being collapsable", + "userLogin": "graywolf336", + "milestone": "0.71.0", + "contributors": [ + "graywolf336" + ] + }, + { + "pr": "12432", + "title": "[NEW] Add \"help wanted\" section to Readme", + "userLogin": "isabellarussell", + "contributors": [ + "isabellarussell", + "Sing-Li", + "web-flow" + ] + }, + { + "pr": "12392", + "title": "Regression: Fix email headers not being used", "userLogin": "sampaiodiego", "contributors": [ "sampaiodiego" ] + } + ] + }, + "0.71.0-rc.2": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "12445", + "title": "[FIX] Attachment timestamp from and to Apps system not working", + "userLogin": "graywolf336", + "milestone": "0.71.0", + "contributors": [ + "graywolf336" + ] }, { - "pr": "12298", - "title": "Fix: Add wizard opt-in fields", + "pr": "12391", + "title": "[FIX] Apps not being able to state how the action buttons are aligned", + "userLogin": "graywolf336", + "milestone": "0.71.0", + "contributors": [ + "graywolf336" + ] + } + ] + }, + "0.71.0": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "12299", + "title": "Release 0.70.4", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "12276", + "title": "Release 0.70.2", + "userLogin": "sampaiodiego", + "contributors": [ + "Hudell", + "sampaiodiego" + ] + } + ] + }, + "0.71.1": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "12487", + "title": "[FIX] Email sending with GDPR user data", "userLogin": "sampaiodiego", "contributors": [ "sampaiodiego" diff --git a/.meteor/packages b/.meteor/packages index 702f4f075fc8..46ae82c82196 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -196,7 +196,6 @@ rocketchat:e2e rocketchat:blockstack rocketchat:version-check - rocketchat:search chatpal:search rocketchat:lazy-load diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index a4bef3751335..83bb35b7294a 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -19,9 +19,9 @@ const pkgdef :Spk.PackageDefinition = ( appTitle = (defaultText = "Rocket.Chat"), - appVersion = 110, # Increment this for every release. + appVersion = 112, # Increment this for every release. - appMarketingVersion = (defaultText = "0.70.4"), + appMarketingVersion = (defaultText = "0.71.1"), # Human-readable representation of appVersion. Should match the way you # identify versions of your app in documentation and marketing. diff --git a/.travis/snap.sh b/.travis/snap.sh index 80507f5b2a76..2005bc709434 100755 --- a/.travis/snap.sh +++ b/.travis/snap.sh @@ -17,7 +17,7 @@ elif [[ $TRAVIS_TAG ]]; then RC_VERSION=$TRAVIS_TAG else CHANNEL=edge - RC_VERSION=0.70.4 + RC_VERSION=0.71.1 fi echo "Preparing to trigger a snap release for $CHANNEL channel" diff --git a/HISTORY.md b/HISTORY.md index ae634bf5b099..ef4fd3be4538 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -211,8 +211,8 @@ Assistify.Chat is now based on Rocket.Chat 0.68.5! - [@tassoevan](https://github.com/tassoevan) - [@timkinnane](https://github.com/timkinnane) -# 0.70.4 -`2018-10-09 · 1 🐛 · 1 🔍 · 1 👩‍💻👨‍💻` +# 0.71.1 +`2018-10-31 · 1 🐛 · 1 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.3` @@ -220,40 +220,129 @@ Assistify.Chat is now based on Rocket.Chat 0.68.5! ### 🐛 Bug fixes -- Modal confirm on enter ([#12283](https://github.com/RocketChat/Rocket.Chat/pull/12283)) +- Email sending with GDPR user data ([#12487](https://github.com/RocketChat/Rocket.Chat/pull/12487)) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@sampaiodiego](https://github.com/sampaiodiego) + +# 0.71.0 +`2018-10-27 · 2 ️️️⚠️ · 5 🎉 · 5 🚀 · 21 🐛 · 8 🔍 · 20 👩‍💻👨‍💻` + +### Engine versions +- Node: `8.11.3` +- NPM: `5.6.0` + +### ⚠️ BREAKING CHANGES + +- Update `lastMessage` rooms property and convert the "starred" property, to the same format ([#12266](https://github.com/RocketChat/Rocket.Chat/pull/12266)) +- Add expiration to API login tokens and fix duplicate login tokens created by LDAP ([#12186](https://github.com/RocketChat/Rocket.Chat/pull/12186)) + +### 🎉 New features + +- Add delete channel mutation to GraphQL API ([#11860](https://github.com/RocketChat/Rocket.Chat/pull/11860)) +- sidenav size on large screens ([#12372](https://github.com/RocketChat/Rocket.Chat/pull/12372)) +- Ability to disable user presence monitor ([#12353](https://github.com/RocketChat/Rocket.Chat/pull/12353)) +- PDF message attachment preview (client side rendering) ([#10519](https://github.com/RocketChat/Rocket.Chat/pull/10519) by [@kb0304](https://github.com/kb0304)) +- Add "help wanted" section to Readme ([#12432](https://github.com/RocketChat/Rocket.Chat/pull/12432)) + +### 🚀 Improvements + +- Livechat room closure endpoints ([#12360](https://github.com/RocketChat/Rocket.Chat/pull/12360)) +- Set Livechat department before register guest ([#12161](https://github.com/RocketChat/Rocket.Chat/pull/12161)) +- Add missing livechat i18n keys ([#12330](https://github.com/RocketChat/Rocket.Chat/pull/12330) by [@MarcosEllys](https://github.com/MarcosEllys)) +- Avoid unnecessary calls to Meteor.user() on client ([#11212](https://github.com/RocketChat/Rocket.Chat/pull/11212)) +- Allow the imports to accept any file type ([#12425](https://github.com/RocketChat/Rocket.Chat/pull/12425)) + +### 🐛 Bug fixes + +- Add image dimensions to attachment even when no reorientation is required ([#11521](https://github.com/RocketChat/Rocket.Chat/pull/11521)) +- iframe login token not checked ([#12158](https://github.com/RocketChat/Rocket.Chat/pull/12158) by [@nimetu](https://github.com/nimetu)) +- REST `users.setAvatar` endpoint wasn't allowing update the avatar of other users even with correct permissions ([#11431](https://github.com/RocketChat/Rocket.Chat/pull/11431)) +- Slack importer: image previews not showing ([#11875](https://github.com/RocketChat/Rocket.Chat/pull/11875) by [@madguy02](https://github.com/madguy02)) +- Edit room name with uppercase letters ([#12235](https://github.com/RocketChat/Rocket.Chat/pull/12235) by [@nikeee](https://github.com/nikeee)) +- Custom OAuth Configuration can't be removed ([#12256](https://github.com/RocketChat/Rocket.Chat/pull/12256)) +- Remove e2e from users endpoint responses ([#12344](https://github.com/RocketChat/Rocket.Chat/pull/12344)) +- email api TAPi18n is undefined ([#12373](https://github.com/RocketChat/Rocket.Chat/pull/12373)) +- Blockstack errors in IE 11 ([#12338](https://github.com/RocketChat/Rocket.Chat/pull/12338)) +- avatar?_dc=undefined ([#12365](https://github.com/RocketChat/Rocket.Chat/pull/12365)) +- users.register endpoint to not create an user if username already being used ([#12297](https://github.com/RocketChat/Rocket.Chat/pull/12297)) +- Date range check on livechat analytics ([#12345](https://github.com/RocketChat/Rocket.Chat/pull/12345) by [@teresy](https://github.com/teresy)) +- Cast env var setting to int based on option type ([#12194](https://github.com/RocketChat/Rocket.Chat/pull/12194) by [@crazy-max](https://github.com/crazy-max)) +- Links in home layout ([#12355](https://github.com/RocketChat/Rocket.Chat/pull/12355) by [@upiksaleh](https://github.com/upiksaleh)) +- Last message not updating after message delete if show deleted status is on ([#12350](https://github.com/RocketChat/Rocket.Chat/pull/12350)) +- Invalid destructuring on Livechat API endpoint ([#12354](https://github.com/RocketChat/Rocket.Chat/pull/12354)) +- E2E: Decrypting UTF-8 encoded messages ([#12398](https://github.com/RocketChat/Rocket.Chat/pull/12398) by [@pmmaga](https://github.com/pmmaga)) +- Ignore errors when creating image preview for uploads ([#12424](https://github.com/RocketChat/Rocket.Chat/pull/12424)) +- Attachment actions not being collapsable ([#12436](https://github.com/RocketChat/Rocket.Chat/pull/12436)) +- Attachment timestamp from and to Apps system not working ([#12445](https://github.com/RocketChat/Rocket.Chat/pull/12445)) +- Apps not being able to state how the action buttons are aligned ([#12391](https://github.com/RocketChat/Rocket.Chat/pull/12391))
🔍 Minor changes -- Fix: Add wizard opt-in fields ([#12298](https://github.com/RocketChat/Rocket.Chat/pull/12298)) +- Fix: wrong saveUser permission validations ([#12384](https://github.com/RocketChat/Rocket.Chat/pull/12384)) +- Regression: do not render pdf preview on safari <= 12 ([#12375](https://github.com/RocketChat/Rocket.Chat/pull/12375)) +- Improve: Drop database between running tests on CI ([#12358](https://github.com/RocketChat/Rocket.Chat/pull/12358)) +- Fix: update check on err.details ([#12346](https://github.com/RocketChat/Rocket.Chat/pull/12346) by [@teresy](https://github.com/teresy)) +- Update Apps Framework to version 1.2.1 ([#12442](https://github.com/RocketChat/Rocket.Chat/pull/12442)) +- Regression: Change `starred` message property from object to array ([#12405](https://github.com/RocketChat/Rocket.Chat/pull/12405)) +- Apps: Room’s usernames was not working ([#12409](https://github.com/RocketChat/Rocket.Chat/pull/12409)) +- Regression: Fix email headers not being used ([#12392](https://github.com/RocketChat/Rocket.Chat/pull/12392))
+### 👩‍💻👨‍💻 Contributors 😍 + +- [@MarcosEllys](https://github.com/MarcosEllys) +- [@crazy-max](https://github.com/crazy-max) +- [@kb0304](https://github.com/kb0304) +- [@madguy02](https://github.com/madguy02) +- [@nikeee](https://github.com/nikeee) +- [@nimetu](https://github.com/nimetu) +- [@pmmaga](https://github.com/pmmaga) +- [@teresy](https://github.com/teresy) +- [@upiksaleh](https://github.com/upiksaleh) + ### 👩‍💻👨‍💻 Core Team 🤓 +- [@Hudell](https://github.com/Hudell) +- [@MarcosSpessatto](https://github.com/MarcosSpessatto) +- [@Sing-Li](https://github.com/Sing-Li) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@ggazzo](https://github.com/ggazzo) +- [@graywolf336](https://github.com/graywolf336) +- [@isabellarussell](https://github.com/isabellarussell) +- [@renatobecker](https://github.com/renatobecker) +- [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) -# 0.70.3 -`2018-10-08 · 1 🔍 · 2 👩‍💻👨‍💻` +# 0.70.4 +`2018-10-09 · 1 🐛 · 2 🔍 · 1 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.3` - NPM: `5.6.0` +### 🐛 Bug fixes + +- Modal confirm on enter ([#12283](https://github.com/RocketChat/Rocket.Chat/pull/12283)) +
🔍 Minor changes -- Release 0.70.2 ([#12276](https://github.com/RocketChat/Rocket.Chat/pull/12276)) +- Release 0.70.4 ([#12299](https://github.com/RocketChat/Rocket.Chat/pull/12299)) +- Fix: Add wizard opt-in fields ([#12298](https://github.com/RocketChat/Rocket.Chat/pull/12298))
### 👩‍💻👨‍💻 Core Team 🤓 -- [@Hudell](https://github.com/Hudell) - [@sampaiodiego](https://github.com/sampaiodiego) -# 0.70.2 -`2018-10-05 · 1 🐛 · 1 👩‍💻👨‍💻` +# 0.70.3 +`2018-10-08 · 1 🐛 · 2 🔍 · 2 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.3` @@ -263,9 +352,18 @@ Assistify.Chat is now based on Rocket.Chat 0.68.5! - E2E alert shows up when encryption is disabled ([#12272](https://github.com/RocketChat/Rocket.Chat/pull/12272)) +
+🔍 Minor changes + +- Release 0.70.3 ([#12281](https://github.com/RocketChat/Rocket.Chat/pull/12281)) +- Release 0.70.2 ([#12276](https://github.com/RocketChat/Rocket.Chat/pull/12276)) + +
+ ### 👩‍💻👨‍💻 Core Team 🤓 - [@Hudell](https://github.com/Hudell) +- [@sampaiodiego](https://github.com/sampaiodiego) # 0.70.1 `2018-10-05 · 8 🐛 · 5 🔍 · 11 👩‍💻👨‍💻` diff --git a/README.md b/README.md index babf76c7a512..01964b8724da 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/RocketChat/Rocket.Chat/raw/master/LICENSE) +* [**NEW!** Help Wanted](#help-wanted) * [Community](#community) * [Mobile Apps](#mobile-apps) * [Desktop Apps](#desktop-apps) @@ -55,6 +56,21 @@ * [Credits](#credits) * [Donate](#donate) +# Help Wanted + +At Rocket.Chat, our community drives *everything* we do. The Rocket.Chat team is expanding, and we know no better place to find qualified new team members than *right here* - in our Github community. + +If you are passionate about our project, want to work with a world-leading open source team, and enjoy working remotely at a location of your choice, then we want to talk to you! + +Explore current openings below: + +- [Lead Security Researcher and Developer](https://rocketchat.recruitee.com/o/lead-security-researcher-and-developer) + +- [Sales Engineer](https://rocketchat.recruitee.com/o/sales-engineer) + +- [Business Developer/Sales/Channel](https://rocketchat.recruitee.com/o/business-developer-sales-channel) + +- [Front-End Developer](https://rocketchat.recruitee.com/o/frontend-developer) # Community Join thousands of members worldwide 24/7 in our [community server](https://open.rocket.chat). @@ -351,7 +367,7 @@ Read about [how it all started](https://blog.blackducksoftware.com/rocket-chat-e ## Feature Requests -[Feature Request Forums](https://forums.rocket.chat/c/feature-requests) are used to suggest, discuss and upvote feature suggestions. +[Feature Request Forums](https://forums.rocket.chat/c/feature-requests) are used to suggest, discuss and upvote feature suggestions. ### Stack Overflow @@ -413,7 +429,7 @@ It is based on [Gitflow Workflow](http://nvie.com/posts/a-successful-git-branchi See also this [Git Workflows Comparison](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) for more details. ## Translations -We are experimenting [Lingohub](https://translate.lingohub.com/engelgabriel/rocket-dot-chat/dashboard). +We are experimenting [Lingohub](https://translate.lingohub.com/rocketchat/dashboard). If you want to help, send an email to support at rocket.chat to be invited to the translation project. ## How to Contribute diff --git a/client/notifications/notification.js b/client/notifications/notification.js index 031b3bd963c9..6aff57a1cf63 100644 --- a/client/notifications/notification.js +++ b/client/notifications/notification.js @@ -29,7 +29,7 @@ Meteor.startup(function() { // This logic is duplicated in /client/startup/unread.coffee. const hasFocus = readMessage.isEnable(); const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - const muteFocusedConversations = RocketChat.getUserPreference(Meteor.user(), 'muteFocusedConversations'); + const muteFocusedConversations = RocketChat.getUserPreference(Meteor.userId(), 'muteFocusedConversations'); fireGlobalEvent('notification', { notification, @@ -60,7 +60,7 @@ Meteor.startup(function() { // This logic is duplicated in /client/startup/unread.coffee. const hasFocus = readMessage.isEnable(); const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - const muteFocusedConversations = RocketChat.getUserPreference(Meteor.user(), 'muteFocusedConversations'); + const muteFocusedConversations = RocketChat.getUserPreference(Meteor.userId(), 'muteFocusedConversations'); if (RocketChat.Layout.isEmbedded()) { if (!hasFocus && messageIsInOpenedRoom) { diff --git a/client/startup/unread.js b/client/startup/unread.js index 53307c824426..82c123e495cb 100644 --- a/client/startup/unread.js +++ b/client/startup/unread.js @@ -32,7 +32,7 @@ Meteor.startup(function() { // Increment the total unread count. unreadCount += subscription.unread; if (subscription.alert === true && subscription.unreadAlert !== 'nothing') { - const userUnreadAlert = RocketChat.getUserPreference(Meteor.user(), 'unreadAlert'); + const userUnreadAlert = RocketChat.getUserPreference(Meteor.userId(), 'unreadAlert'); if (subscription.unreadAlert === 'all' || userUnreadAlert !== false) { unreadAlert = '•'; } diff --git a/package-lock.json b/package-lock.json index b4072cdfba7c..b2684bf332e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,128 @@ { "name": "Rocket.Chat", - "version": "0.70.4", + "version": "0.71.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "7.0.0" + } + }, + "@babel/generator": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.3.tgz", + "integrity": "sha512-ZoCZGcfIJFJuZBqxcY9OjC1KW2lWK64qrX1o4UYL3yshVhwKFYgzpWZ0vvtGMNJdTlvkw0W+HR1VnYN8q3QPFQ==", + "dev": true, + "requires": { + "@babel/types": "7.1.3", + "jsesc": "2.5.1", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "7.0.0", + "@babel/template": "7.1.2", + "@babel/types": "7.1.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "7.1.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "7.1.3" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.3.tgz", + "integrity": "sha512-gqmspPZOMW3MIRb9HlrnbZHXI1/KHTOroBwN1NcLL6pWxzqzEKGvRTq0W/PxS45OtQGbaFikSQpkS5zbnsQm2w==", + "dev": true + }, "@babel/runtime": { "version": "7.0.0-beta.49", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.49.tgz", @@ -13,6 +132,76 @@ "regenerator-runtime": "0.11.1" } }, + "@babel/template": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0", + "@babel/parser": "7.1.3", + "@babel/types": "7.1.3" + } + }, + "@babel/traverse": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.4.tgz", + "integrity": "sha512-my9mdrAIGdDiSVBuMjpn/oXYpva0/EZwWL3sm3Wcy/AVWO2eXnsoZruOT9jOGNRXU8KbCIu5zsKnXcAJ6PcV6Q==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0", + "@babel/generator": "7.1.3", + "@babel/helper-function-name": "7.1.0", + "@babel/helper-split-export-declaration": "7.0.0", + "@babel/parser": "7.1.3", + "@babel/types": "7.1.3", + "debug": "3.2.6", + "globals": "11.8.0", + "lodash": "4.17.10" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "2.1.1" + } + }, + "globals": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz", + "integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==", + "dev": true, + "requires": { + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@google-cloud/common": { "version": "0.17.0", "resolved": "http://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", @@ -1004,6 +1193,20 @@ } } }, + "babel-eslint": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", + "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0", + "@babel/parser": "7.1.3", + "@babel/traverse": "7.1.4", + "@babel/types": "7.1.3", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0" + } + }, "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -2035,22 +2238,22 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "requires": { "delegates": "1.0.0", @@ -2059,12 +2262,12 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "1.0.0", @@ -2073,32 +2276,32 @@ }, "chownr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" @@ -2106,22 +2309,22 @@ }, "deep-extend": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "resolved": false, "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "requires": { "minipass": "2.2.4" @@ -2129,12 +2332,12 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { "aproba": "1.2.0", @@ -2149,7 +2352,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", @@ -2162,12 +2365,12 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "iconv-lite": { "version": "0.4.21", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "resolved": false, "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "requires": { "safer-buffer": "2.1.2" @@ -2175,7 +2378,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "requires": { "minimatch": "3.0.4" @@ -2183,7 +2386,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "1.4.0", @@ -2192,17 +2395,17 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "1.0.1" @@ -2210,12 +2413,12 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "1.1.11" @@ -2223,12 +2426,12 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "requires": { "safe-buffer": "5.1.1", @@ -2237,14 +2440,14 @@ "dependencies": { "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "minizlib": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "resolved": false, "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "requires": { "minipass": "2.2.4" @@ -2252,7 +2455,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -2260,12 +2463,12 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "needle": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "resolved": false, "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "requires": { "debug": "2.6.9", @@ -2275,7 +2478,7 @@ }, "node-pre-gyp": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz", + "resolved": false, "integrity": "sha1-8RwHUW3ZL4cZnbx+GDjqt81WyeA=", "requires": { "detect-libc": "1.0.3", @@ -2292,7 +2495,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "requires": { "abbrev": "1.1.1", @@ -2301,12 +2504,12 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "resolved": false, "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==" }, "npm-packlist": { "version": "1.1.10", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "resolved": false, "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "requires": { "ignore-walk": "3.0.1", @@ -2315,7 +2518,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "1.1.4", @@ -2326,17 +2529,17 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1.0.2" @@ -2344,17 +2547,17 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "1.0.2", @@ -2363,17 +2566,17 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "rc": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", + "resolved": false, "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", "requires": { "deep-extend": "0.4.2", @@ -2384,14 +2587,14 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, "readable-stream": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "resolved": false, "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "requires": { "core-util-is": "1.0.2", @@ -2405,7 +2608,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { "glob": "7.1.2" @@ -2413,37 +2616,37 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "1.1.0", @@ -2453,7 +2656,7 @@ }, "string_decoder": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "resolved": false, "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { "safe-buffer": "5.1.1" @@ -2461,7 +2664,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "2.1.1" @@ -2469,12 +2672,12 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "tar": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "resolved": false, "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "requires": { "chownr": "1.0.1", @@ -2488,19 +2691,19 @@ "dependencies": { "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "wide-align": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "requires": { "string-width": "1.0.2" @@ -2508,7 +2711,7 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } @@ -2533,6 +2736,11 @@ "integrity": "sha512-VOMDtYPwLbIncTxNoSzRyvaMxtXmLWLUqr8k5AfC1BzLk34HvBXaQX8snOwQZ4c0aX8aSERqtJSiI9/m2u5kuA==", "dev": true }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + }, "bigi": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", @@ -3419,7 +3627,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -3464,7 +3672,7 @@ "mocha": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -3482,13 +3690,13 @@ "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -3516,7 +3724,7 @@ "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { "has-flag": "2.0.0" @@ -3834,7 +4042,7 @@ }, "compression": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/compression/-/compression-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.5.2.tgz", "integrity": "sha1-sDuNhub4rSloPLqN+R3cb/x3s5U=", "requires": { "accepts": "1.2.13", @@ -5046,6 +5254,11 @@ "resolved": "https://registry.npmjs.org/emailreplyparser/-/emailreplyparser-0.0.5.tgz", "integrity": "sha1-BpYswKRCFnWbo7mOOyV3wM4w/Aw=" }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, "empower": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/empower/-/empower-1.2.3.tgz", @@ -5091,6 +5304,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "optional": true, "requires": { "prr": "1.0.1" } @@ -6502,7 +6716,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, @@ -6512,7 +6726,7 @@ }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true @@ -7022,7 +7236,7 @@ "dependencies": { "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { @@ -7036,17 +7250,17 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", @@ -7059,7 +7273,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "1.4.0", @@ -7068,12 +7282,12 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "1.1.8" @@ -7119,7 +7333,7 @@ }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1.0.2" @@ -7127,7 +7341,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "protobufjs": { @@ -7143,7 +7357,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { "glob": "7.1.2" @@ -7151,12 +7365,12 @@ }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } @@ -7309,8 +7523,7 @@ "requires": { "boom": "2.7.2", "hoek": "2.14.0", - "joi": "6.4.1", - "wreck": "6.0.0" + "joi": "6.4.1" } }, "heavy": { @@ -7320,8 +7533,7 @@ "dev": true, "requires": { "boom": "2.7.2", - "hoek": "2.14.0", - "joi": "6.4.1" + "hoek": "2.14.0" } }, "hoek": { @@ -9041,14 +9253,14 @@ "version": "https://github.com/meteor/less.js/tarball/8130849eb3d7f0ecf0ca8d0af7c4207b0442e3f6", "integrity": "sha512-514pMpzrABW11ploVaRcbHLf3MhollYYCJj9SyJmNT1O8ZeLCiIeqZ2SjZUM1uudohPlHgTRwmCZouzIJFJ7Jw==", "requires": { - "errno": "^0.1.1", - "graceful-fs": "^3.0.5", - "image-size": "~0.3.5", - "mime": "^1.2.11", - "mkdirp": "^0.5.0", - "promise": "^6.0.1", - "request": "^2.51.0", - "source-map": "^0.4.2" + "errno": "0.1.7", + "graceful-fs": "3.0.11", + "image-size": "0.3.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "6.1.0", + "request": "2.87.0", + "source-map": "0.4.4" }, "dependencies": { "asap": { @@ -9063,7 +9275,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "optional": true, "requires": { - "natives": "^1.1.0" + "natives": "1.1.4" } }, "image-size": { @@ -9084,7 +9296,7 @@ "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", "optional": true, "requires": { - "asap": "~1.0.0" + "asap": "1.0.0" } }, "source-map": { @@ -9093,7 +9305,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "optional": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -9186,6 +9398,23 @@ "strip-bom": "3.0.0" } }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + }, + "dependencies": { + "json5": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + } + } + }, "localforage": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.1.tgz", @@ -10784,7 +11013,8 @@ "natives": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", - "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==" + "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==", + "optional": true }, "natural-compare": { "version": "1.4.0", @@ -10837,6 +11067,11 @@ "resolved": "https://registry.npmjs.org/node-dogstatsd/-/node-dogstatsd-0.0.7.tgz", "integrity": "sha1-VE0nTJGkE0JqLzxA/3uOiBPP2TA=" }, + "node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=" + }, "node-fetch": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", @@ -10916,7 +11151,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", "requires": { "are-we-there-yet": "1.1.5", "console-control-strings": "1.1.0", @@ -11342,7 +11577,7 @@ "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "requires": { "pify": "3.0.0" } @@ -11368,6 +11603,15 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.1.0.tgz", "integrity": "sha1-68ikqGGf8LioGsFRPDQ0/0af23Q=" }, + "pdfjs-dist": { + "version": "2.0.489", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.0.489.tgz", + "integrity": "sha1-Y+VLKSqGeQpFRpfrRNQ0e4+/rSc=", + "requires": { + "node-ensure": "0.0.0", + "worker-loader": "1.1.1" + } + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -11709,7 +11953,7 @@ "postcss-import": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha1-Vck2LJGSmU7GiGXSJEGd8dspgfA=", + "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", "dev": true, "requires": { "postcss": "6.0.22", @@ -11758,7 +12002,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "supports-color": { @@ -12595,7 +12839,8 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true }, "pseudomap": { "version": "1.0.2", @@ -13382,6 +13627,43 @@ } } }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "requires": { + "ajv": "6.5.4", + "ajv-keywords": "3.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "requires": { + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } + } + }, "scmp": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/scmp/-/scmp-0.0.3.tgz", @@ -14488,7 +14770,7 @@ "stylelint-order": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-0.8.1.tgz", - "integrity": "sha1-Nfca86FZVBVODpnlZGuj1vvjT40=", + "integrity": "sha512-8mp1P2wnI9XShYXVXDsxVigE2eXnc0C2O4ktbwUvTBwjCP4xZskIbUVxp1evSG3OK4R7hXVNl/2BnJCZkrcc/w==", "dev": true, "requires": { "lodash": "4.17.10", @@ -14536,7 +14818,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "supports-color": { @@ -15416,6 +15698,21 @@ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", "dev": true }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, "urijs": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz", @@ -15989,6 +16286,15 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, + "worker-loader": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-1.1.1.tgz", + "integrity": "sha512-qJZLVS/jMCBITDzPo/RuweYSIG8VJP5P67mP/71alGyTZRe1LYJFdwLjLalY3T5ifx0bMDRD3OB6P2p1escvlg==", + "requires": { + "loader-utils": "1.1.0", + "schema-utils": "0.4.7" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index 2b9e1d52d6fc..d001f70ab85e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Rocket.Chat", "description": "The Ultimate Open Source WebChat Platform", - "version": "0.70.4", + "version": "0.71.1", "author": { "name": "Rocket.Chat", "url": "https://rocket.chat/" @@ -94,6 +94,7 @@ "@octokit/rest": "^15.8.1", "@rocket.chat/eslint-config": "^0.1.2", "autoprefixer": "^8.6.0", + "babel-eslint": "^10.0.1", "babel-mocha-es6-compiler": "^0.1.0", "babel-plugin-array-includes": "^2.0.3", "chimp": "^0.51.1", @@ -119,7 +120,7 @@ "@google-cloud/language": "^1.2.0", "@google-cloud/storage": "^1.7.0", "@google-cloud/vision": "^0.19.0", - "@rocket.chat/apps-engine": "1.1.1", + "@rocket.chat/apps-engine": "1.2.1", "@slack/client": "^4.2.2", "adm-zip": "^0.4.11", "archiver": "^2.1.1", @@ -174,6 +175,7 @@ "moment-timezone": "^0.5.17", "node-dogstatsd": "^0.0.7", "object-path": "^0.11.4", + "pdfjs-dist": "^2.0.489", "photoswipe": "^4.1.2", "poplib": "^0.1.7", "prom-client": "^11.0.0", diff --git a/packages/rocketchat-api/package.js b/packages/rocketchat-api/package.js index f0aef96e9b5d..7101ecc7bf10 100644 --- a/packages/rocketchat-api/package.js +++ b/packages/rocketchat-api/package.js @@ -16,6 +16,7 @@ Package.onUse(function(api) { api.addFiles('server/settings.js', 'server'); // Register helpers + api.addFiles('server/helpers/composeRoomWithLastMessage.js', 'server'); api.addFiles('server/helpers/requestParams.js', 'server'); api.addFiles('server/helpers/getPaginationItems.js', 'server'); api.addFiles('server/helpers/getUserFromParams.js', 'server'); @@ -47,4 +48,5 @@ Package.onUse(function(api) { api.addFiles('server/v1/stats.js', 'server'); api.addFiles('server/v1/users.js', 'server'); api.addFiles('server/v1/assets.js', 'server'); + api.addFiles('server/v1/e2e.js', 'server'); }); diff --git a/packages/rocketchat-api/server/api.js b/packages/rocketchat-api/server/api.js index 401099edcbf5..9a47a947d0b6 100644 --- a/packages/rocketchat-api/server/api.js +++ b/packages/rocketchat-api/server/api.js @@ -11,6 +11,7 @@ class API extends Restivus { joinCode: 0, members: 0, importIds: 0, + e2e: 0, }; this.limitedUserFieldsToExclude = { avatarOrigin: 0, @@ -261,16 +262,6 @@ class API extends Restivus { this.userId = this.user._id; - // Remove tokenExpires to keep the old behavior - Meteor.users.update({ - _id: this.user._id, - 'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(auth.token), - }, { - $unset: { - 'services.resume.loginTokens.$.when': 1, - }, - }); - const response = { status: 'success', data: { diff --git a/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js b/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js new file mode 100644 index 000000000000..34f38bb675a5 --- /dev/null +++ b/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js @@ -0,0 +1,6 @@ +RocketChat.API.helperMethods.set('composeRoomWithLastMessage', function _composeRoomWithLastMessage(room, userId) { + if (room.lastMessage) { + room.lastMessage = RocketChat.composeMessageObjectWithUser(room.lastMessage, userId); + } + return room; +}); diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js index 784a763bfb3f..2a6416650b8c 100644 --- a/packages/rocketchat-api/server/v1/channels.js +++ b/packages/rocketchat-api/server/v1/channels.js @@ -1,7 +1,7 @@ import _ from 'underscore'; // Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property -function findChannelByIdOrName({ params, checkedArchived = true }) { +function findChannelByIdOrName({ params, checkedArchived = true, userId }) { if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) { throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" or "roomName" is required'); } @@ -22,6 +22,9 @@ function findChannelByIdOrName({ params, checkedArchived = true }) { if (checkedArchived && room.archived) { throw new Meteor.Error('error-room-archived', `The channel, ${ room.name }, is archived`); } + if (userId && room.lastMessage) { + room.lastMessage = RocketChat.composeMessageObjectWithUser(room.lastMessage, userId); + } return room; } @@ -35,7 +38,7 @@ RocketChat.API.v1.addRoute('channels.addAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -175,14 +178,10 @@ function createChannelValidator(params) { function createChannel(userId, params) { const readOnly = typeof params.readOnly !== 'undefined' ? params.readOnly : false; - - let id; - Meteor.runAsUser(userId, () => { - id = Meteor.call('createChannel', params.name, params.members ? params.members : [], readOnly, params.customFields); - }); + const id = Meteor.runAsUser(userId, () => Meteor.call('createChannel', params.name, params.members ? params.members : [], readOnly, params.customFields)); return { - channel: RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: { roomId: id.rid }, userId: this.userId }), }; } @@ -236,9 +235,7 @@ RocketChat.API.v1.addRoute('channels.delete', { authRequired: true }, { Meteor.call('eraseRoom', findResult._id); }); - return RocketChat.API.v1.success({ - channel: findResult, - }); + return RocketChat.API.v1.success(); }, }); @@ -367,10 +364,12 @@ RocketChat.API.v1.addRoute('channels.history', { authRequired: true }, { RocketChat.API.v1.addRoute('channels.info', { authRequired: true }, { get() { - const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false }); - return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ + params: this.requestParams(), + checkedArchived: false, + userId: this.userId, + }), }); }, }); @@ -386,7 +385,7 @@ RocketChat.API.v1.addRoute('channels.invite', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -400,7 +399,7 @@ RocketChat.API.v1.addRoute('channels.join', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -416,7 +415,7 @@ RocketChat.API.v1.addRoute('channels.kick', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -430,7 +429,7 @@ RocketChat.API.v1.addRoute('channels.leave', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -465,7 +464,7 @@ RocketChat.API.v1.addRoute('channels.list', { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - channels: rooms, + channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), count: rooms.length, offset, total, @@ -491,7 +490,7 @@ RocketChat.API.v1.addRoute('channels.list.joined', { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - channels: rooms, + channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -526,7 +525,7 @@ RocketChat.API.v1.addRoute('channels.members', { authRequired: true }, { const users = RocketChat.models.Users.find({ _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 }, - sort: { username: sort.username != null ? sort.username : 1 }, + sort: { username: sort.username != null ? sort.username : 1 }, }).fetch(); return RocketChat.API.v1.success({ @@ -568,7 +567,7 @@ RocketChat.API.v1.addRoute('channels.messages', { authRequired: true }, { const messages = cursor.fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((record) => RocketChat.composeMessageObjectWithUser(record, this.userId)), count: messages.length, offset, total, @@ -703,7 +702,7 @@ RocketChat.API.v1.addRoute('channels.rename', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: { roomId: this.bodyParams.roomId }, userId: this.userId }), }); }, }); @@ -721,7 +720,7 @@ RocketChat.API.v1.addRoute('channels.setCustomFields', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -743,7 +742,7 @@ RocketChat.API.v1.addRoute('channels.setDefault', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -783,7 +782,7 @@ RocketChat.API.v1.addRoute('channels.setJoinCode', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -827,7 +826,7 @@ RocketChat.API.v1.addRoute('channels.setReadOnly', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -889,7 +888,7 @@ RocketChat.API.v1.addRoute('channels.setType', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/chat.js b/packages/rocketchat-api/server/v1/chat.js index 059d6058eca9..1d3e8621cad5 100644 --- a/packages/rocketchat-api/server/v1/chat.js +++ b/packages/rocketchat-api/server/v1/chat.js @@ -58,7 +58,10 @@ RocketChat.API.v1.addRoute('chat.syncMessages', { authRequired: true }, { } return RocketChat.API.v1.success({ - result, + result: { + updated: result.updated.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), + deleted: result.deleted.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), + }, }); }, }); @@ -79,7 +82,7 @@ RocketChat.API.v1.addRoute('chat.getMessage', { authRequired: true }, { } return RocketChat.API.v1.success({ - message: msg, + message: RocketChat.composeMessageObjectWithUser(msg, this.userId), }); }, }); @@ -100,7 +103,7 @@ RocketChat.API.v1.addRoute('chat.pinMessage', { authRequired: true }, { Meteor.runAsUser(this.userId, () => pinnedMessage = Meteor.call('pinMessage', msg)); return RocketChat.API.v1.success({ - message: pinnedMessage, + message: RocketChat.composeMessageObjectWithUser(pinnedMessage, this.userId), }); }, }); @@ -116,7 +119,7 @@ RocketChat.API.v1.addRoute('chat.postMessage', { authRequired: true }, { return RocketChat.API.v1.success({ ts: Date.now(), channel: messageReturn.channel, - message: messageReturn.message, + message: RocketChat.composeMessageObjectWithUser(messageReturn.message, this.userId), }); }, }); @@ -138,7 +141,7 @@ RocketChat.API.v1.addRoute('chat.search', { authRequired: true }, { Meteor.runAsUser(this.userId, () => result = Meteor.call('messageSearch', searchText, roomId, count).message.docs); return RocketChat.API.v1.success({ - messages: result, + messages: result.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), }); }, }); @@ -156,7 +159,7 @@ RocketChat.API.v1.addRoute('chat.sendMessage', { authRequired: true }, { Meteor.runAsUser(this.userId, () => message = Meteor.call('sendMessage', this.bodyParams.message)); return RocketChat.API.v1.success({ - message, + message: RocketChat.composeMessageObjectWithUser(message, this.userId), }); }, }); @@ -248,7 +251,7 @@ RocketChat.API.v1.addRoute('chat.update', { authRequired: true }, { }); return RocketChat.API.v1.success({ - message: RocketChat.models.Messages.findOneById(msg._id), + message: RocketChat.composeMessageObjectWithUser(RocketChat.models.Messages.findOneById(msg._id), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/groups.js b/packages/rocketchat-api/server/v1/groups.js index 2944e8f1c1f6..bfb6c67fc96b 100644 --- a/packages/rocketchat-api/server/v1/groups.js +++ b/packages/rocketchat-api/server/v1/groups.js @@ -33,7 +33,7 @@ RocketChat.API.v1.addRoute('groups.addAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -202,7 +202,7 @@ RocketChat.API.v1.addRoute('groups.create', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -215,9 +215,7 @@ RocketChat.API.v1.addRoute('groups.delete', { authRequired: true }, { Meteor.call('eraseRoom', findResult.rid); }); - return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), - }); + return RocketChat.API.v1.success(); }, }); @@ -331,7 +329,7 @@ RocketChat.API.v1.addRoute('groups.info', { authRequired: true }, { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId, checkedArchived: false }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -355,7 +353,7 @@ RocketChat.API.v1.addRoute('groups.invite', { authRequired: true }, { Meteor.runAsUser(this.userId, () => Meteor.call('addUserToRoom', { rid, username })); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -405,7 +403,7 @@ RocketChat.API.v1.addRoute('groups.list', { authRequired: true }, { return RocketChat.API.v1.success({ - groups: rooms, + groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -434,7 +432,7 @@ RocketChat.API.v1.addRoute('groups.listAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - groups: rooms, + groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -495,7 +493,7 @@ RocketChat.API.v1.addRoute('groups.messages', { authRequired: true }, { }).fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), count: messages.length, offset, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -608,7 +606,7 @@ RocketChat.API.v1.addRoute('groups.rename', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -626,7 +624,7 @@ RocketChat.API.v1.addRoute('groups.setCustomFields', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -684,7 +682,7 @@ RocketChat.API.v1.addRoute('groups.setReadOnly', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -724,7 +722,7 @@ RocketChat.API.v1.addRoute('groups.setType', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/im.js b/packages/rocketchat-api/server/v1/im.js index ff728e6e63dc..35ad3849d9db 100644 --- a/packages/rocketchat-api/server/v1/im.js +++ b/packages/rocketchat-api/server/v1/im.js @@ -219,7 +219,7 @@ RocketChat.API.v1.addRoute(['dm.messages', 'im.messages'], { authRequired: true }).fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), count: messages.length, offset, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -259,7 +259,7 @@ RocketChat.API.v1.addRoute(['dm.messages.others', 'im.messages.others'], { authR }).fetch(); return RocketChat.API.v1.success({ - messages: msgs, + messages: msgs.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), offset, count: msgs.length, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -285,7 +285,7 @@ RocketChat.API.v1.addRoute(['dm.list', 'im.list'], { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - ims: rooms, + ims: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total, @@ -312,7 +312,7 @@ RocketChat.API.v1.addRoute(['dm.list.everyone', 'im.list.everyone'], { authRequi }).fetch(); return RocketChat.API.v1.success({ - ims: rooms, + ims: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: RocketChat.models.Rooms.find(ourQuery).count(), diff --git a/packages/rocketchat-api/server/v1/rooms.js b/packages/rocketchat-api/server/v1/rooms.js index 0071953d414b..b758901385a3 100644 --- a/packages/rocketchat-api/server/v1/rooms.js +++ b/packages/rocketchat-api/server/v1/rooms.js @@ -46,7 +46,10 @@ RocketChat.API.v1.addRoute('rooms.get', { authRequired: true }, { }; } - return RocketChat.API.v1.success(result); + return RocketChat.API.v1.success({ + update: result.update.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + remove: result.remove.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + }); }, }); diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index c231830a6c2b..35a11b8e4f0d 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -166,6 +166,10 @@ RocketChat.API.v1.addRoute('users.register', { authRequired: false }, { username: String, })); + if (!RocketChat.checkUsernameAvailability(this.bodyParams.username)) { + return RocketChat.API.v1.failure('Username is already in use'); + } + // Register the user const userId = Meteor.call('registerUser', this.bodyParams); @@ -220,24 +224,44 @@ RocketChat.API.v1.addRoute('users.setAvatar', { authRequired: true }, { RocketChat.setUserAvatar(user, this.bodyParams.avatarUrl, '', 'url'); } else { const busboy = new Busboy({ headers: this.request.headers }); + const fields = {}; + const getUserFromFormData = (fields) => { + if (fields.userId) { + return RocketChat.models.Users.findOneById(fields.userId, { _id: 1 }); + } + if (fields.username) { + return RocketChat.models.Users.findOneByUsername(fields.username, { _id: 1 }); + } + }; Meteor.wrapAsync((callback) => { busboy.on('file', Meteor.bindEnvironment((fieldname, file, filename, encoding, mimetype) => { if (fieldname !== 'image') { return callback(new Meteor.Error('invalid-field')); } - const imageData = []; file.on('data', Meteor.bindEnvironment((data) => { imageData.push(data); })); file.on('end', Meteor.bindEnvironment(() => { + const sentTheUserByFormData = fields.userId || fields.username; + if (sentTheUserByFormData) { + user = getUserFromFormData(fields); + if (!user) { + return callback(new Meteor.Error('error-invalid-user', 'The optional "userId" or "username" param provided does not match any users')); + } + if (!RocketChat.authz.hasPermission(this.userId, 'edit-other-user-info')) { + return callback(new Meteor.Error('error-not-allowed', 'Not allowed')); + } + } RocketChat.setUserAvatar(user, Buffer.concat(imageData), mimetype, 'rest'); callback(); })); - })); + busboy.on('field', (fieldname, val) => { + fields[fieldname] = val; + }); this.request.pipe(busboy); })(); } diff --git a/packages/rocketchat-apps/server/bridges/api.js b/packages/rocketchat-apps/server/bridges/api.js index 55d5f1d6729b..2d275cd4a9d1 100644 --- a/packages/rocketchat-apps/server/bridges/api.js +++ b/packages/rocketchat-apps/server/bridges/api.js @@ -2,6 +2,9 @@ import express from 'express'; import { WebApp } from 'meteor/webapp'; const apiServer = express(); + +apiServer.set('x-powered-by', false); + WebApp.connectHandlers.use(apiServer); export class AppApisBridge { diff --git a/packages/rocketchat-apps/server/bridges/bridges.js b/packages/rocketchat-apps/server/bridges/bridges.js index 57dc5a93a4a4..f79a3b54cb21 100644 --- a/packages/rocketchat-apps/server/bridges/bridges.js +++ b/packages/rocketchat-apps/server/bridges/bridges.js @@ -10,6 +10,7 @@ import { AppListenerBridge } from './listeners'; import { AppMessageBridge } from './messages'; import { AppPersistenceBridge } from './persistence'; import { AppRoomBridge } from './rooms'; +import { AppInternalBridge } from './internal'; import { AppSettingBridge } from './settings'; import { AppUserBridge } from './users'; @@ -27,6 +28,7 @@ export class RealAppBridges extends AppBridges { this._msgBridge = new AppMessageBridge(orch); this._persistBridge = new AppPersistenceBridge(orch); this._roomBridge = new AppRoomBridge(orch); + this._internalBridge = new AppInternalBridge(orch); this._setsBridge = new AppSettingBridge(orch); this._userBridge = new AppUserBridge(orch); } @@ -71,6 +73,10 @@ export class RealAppBridges extends AppBridges { return this._roomBridge; } + getInternalBridge() { + return this._internalBridge; + } + getServerSettingBridge() { return this._setsBridge; } diff --git a/packages/rocketchat-apps/server/bridges/index.js b/packages/rocketchat-apps/server/bridges/index.js index 32dfc2ff6d05..638c03c142c5 100644 --- a/packages/rocketchat-apps/server/bridges/index.js +++ b/packages/rocketchat-apps/server/bridges/index.js @@ -7,6 +7,7 @@ import { AppListenerBridge } from './listeners'; import { AppMessageBridge } from './messages'; import { AppPersistenceBridge } from './persistence'; import { AppRoomBridge } from './rooms'; +import { AppInternalBridge } from './internal'; import { AppSettingBridge } from './settings'; import { AppUserBridge } from './users'; @@ -22,4 +23,5 @@ export { AppRoomBridge, AppSettingBridge, AppUserBridge, + AppInternalBridge, }; diff --git a/packages/rocketchat-apps/server/bridges/internal.js b/packages/rocketchat-apps/server/bridges/internal.js new file mode 100644 index 000000000000..d475d7d2e328 --- /dev/null +++ b/packages/rocketchat-apps/server/bridges/internal.js @@ -0,0 +1,19 @@ +export class AppInternalBridge { + constructor(orch) { + this.orch = orch; + } + + getUsernamesOfRoomById(roomId) { + const records = RocketChat.models.Subscriptions.findByRoomIdWhenUsernameExists(roomId, { + fields: { + 'u.username': 1, + }, + }).fetch(); + + if (!records || records.length === 0) { + return []; + } + + return records.map((s) => s.u.username); + } +} diff --git a/packages/rocketchat-apps/server/bridges/rooms.js b/packages/rocketchat-apps/server/bridges/rooms.js index 699bb5daac11..873e279dc5ba 100644 --- a/packages/rocketchat-apps/server/bridges/rooms.js +++ b/packages/rocketchat-apps/server/bridges/rooms.js @@ -5,7 +5,7 @@ export class AppRoomBridge { this.orch = orch; } - async create(room, appId) { + async create(room, members, appId) { console.log(`The App ${ appId } is creating a new room.`, room); const rcRoom = this.orch.getConverters().get('rooms').convertAppRoom(room); @@ -24,7 +24,12 @@ export class AppRoomBridge { let rid; Meteor.runAsUser(room.creator.id, () => { - const info = Meteor.call(method, rcRoom.members); + const extraData = Object.assign({}, rcRoom); + delete extraData.name; + delete extraData.t; + delete extraData.ro; + delete extraData.customFields; + const info = Meteor.call(method, rcRoom.name, members, rcRoom.ro, rcRoom.customFields, extraData); rid = info.rid; }); @@ -67,15 +72,25 @@ export class AppRoomBridge { return this.orch.getConverters().get('users').convertById(room.u._id); } - async update(room, appId) { + async update(room, members = [], appId) { console.log(`The App ${ appId } is updating a room.`); - if (!room.id || RocketChat.models.Rooms.findOneById(room.id)) { + if (!room.id || !RocketChat.models.Rooms.findOneById(room.id)) { throw new Error('A room must exist to update.'); } const rm = this.orch.getConverters().get('rooms').convertAppRoom(room); RocketChat.models.Rooms.update(rm._id, rm); + + for (const username of members) { + const member = RocketChat.models.Users.findOneByUsername(username); + + if (!member) { + continue; + } + + RocketChat.addUserToRoom(rm._id, member); + } } } diff --git a/packages/rocketchat-apps/server/converters/messages.js b/packages/rocketchat-apps/server/converters/messages.js index 34188ed2cdea..e1bbc5a9b88c 100644 --- a/packages/rocketchat-apps/server/converters/messages.js +++ b/packages/rocketchat-apps/server/converters/messages.js @@ -120,7 +120,7 @@ export class AppMessagesConverter { collapsed: attachment.collapsed, color: attachment.color, text: attachment.text, - ts: attachment.timestamp, + ts: attachment.timestamp ? attachment.timestamp.toJSON() : attachment.timestamp, message_link: attachment.timestampLink, thumb_url: attachment.thumbnailUrl, author_name: attachment.author ? attachment.author.name : undefined, @@ -133,6 +133,7 @@ export class AppMessagesConverter { audio_url: attachment.audioUrl, video_url: attachment.videoUrl, fields: attachment.fields, + button_alignment: attachment.actionButtonsAlignment, actions: attachment.actions, type: attachment.type, description: attachment.description, @@ -175,7 +176,7 @@ export class AppMessagesConverter { collapsed: attachment.collapsed, color: attachment.color, text: attachment.text, - timestamp: attachment.ts, + timestamp: new Date(attachment.ts), timestampLink: attachment.message_link, thumbnailUrl: attachment.thumb_url, author, @@ -184,6 +185,7 @@ export class AppMessagesConverter { audioUrl: attachment.audio_url, videoUrl: attachment.video_url, fields: attachment.fields, + actionButtonsAlignment: attachment.button_alignment, actions: attachment.actions, type: attachment.type, description: attachment.description, diff --git a/packages/rocketchat-blockstack/client/main.js b/packages/rocketchat-blockstack/client/main.js index 0d75d369e801..cc52d4d8da97 100644 --- a/packages/rocketchat-blockstack/client/main.js +++ b/packages/rocketchat-blockstack/client/main.js @@ -2,9 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import { check, Match } from 'meteor/check'; import { Session } from 'meteor/session'; - import './routes'; -import { redirectToSignIn, signUserOut } from 'blockstack'; const handleError = (error) => error && Session.set('errorMessage', error.reason || 'Unknown error'); @@ -29,7 +27,8 @@ Meteor.loginWithBlockstack = (options, callback = handleError) => { manifestURI: String, })); - return redirectToSignIn(options.redirectURI, options.manifestURI, options.scopes); + import('blockstack/dist/blockstack').then(({ redirectToSignIn }) => + redirectToSignIn(options.redirectURI, options.manifestURI, options.scopes)); } catch (err) { callback.call(Meteor, err); } @@ -45,7 +44,8 @@ Meteor.logout = (...args) => { if (serviceConfig && blockstackAuth) { Session.delete('blockstack_auth'); - signUserOut(window.location.href); + import('blockstack/dist/blockstack').then(({ signUserOut }) => + signUserOut(window.location.href)); } return meteorLogout(...args); diff --git a/packages/rocketchat-blockstack/client/routes.js b/packages/rocketchat-blockstack/client/routes.js index c724548150a3..4791dce0c7d1 100644 --- a/packages/rocketchat-blockstack/client/routes.js +++ b/packages/rocketchat-blockstack/client/routes.js @@ -1,7 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import blockstack from 'blockstack'; const blockstackLogin = (authResponse, userData = {}) => { Accounts.callLoginMethod({ @@ -19,6 +18,8 @@ const blockstackLogin = (authResponse, userData = {}) => { FlowRouter.route('/_blockstack/validate', { name: 'blockstackValidate', async action(params, queryParams) { + const blockstack = await import('blockstack/dist/blockstack'); + if (Meteor.userId()) { console.log('Blockstack Auth requested when already logged in. Reloading.'); return FlowRouter.go('home'); diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index b12fb98e067d..57f28e3df8bb 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -118,7 +118,20 @@ function roomMaxAge(room) { return roomMaxAgeDefault(room.t); } +const fixRoomName = (old) => { + if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { + return old; + } + const reg = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`); + return [...old.replace(' ', '').toLocaleLowerCase()].filter((f) => reg.test(f)).join(''); +}; + Template.channelSettingsEditing.events({ + 'input [name="name"]'(e) { + const input = e.currentTarget; + const modified = fixRoomName(input.value); + input.value = modified; + }, 'input .js-input'(e) { this.value.set(e.currentTarget.value); }, diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.room.js b/packages/rocketchat-e2e/client/rocketchat.e2e.room.js index 0c8c8b2c7c32..ad0ac0a2aa1e 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.room.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.room.js @@ -285,7 +285,7 @@ export class E2ERoom { try { const result = await decryptAES(vector, this.groupSessionKey, cipherText); - return EJSON.parse(toString(result)); + return EJSON.parse(new TextDecoder('UTF-8').decode(new Uint8Array(result))); } catch (error) { return console.error('E2E -> Error decrypting message: ', error, message); } diff --git a/packages/rocketchat-emoji-emojione/rocketchat.js b/packages/rocketchat-emoji-emojione/rocketchat.js index 2a00688ba57c..caaf654b2495 100644 --- a/packages/rocketchat-emoji-emojione/rocketchat.js +++ b/packages/rocketchat-emoji-emojione/rocketchat.js @@ -35,8 +35,8 @@ for (const key in emojione.emojioneList) { Meteor.startup(function() { Tracker.autorun(function() { if (isSetNotNull(() => RocketChat.emoji.packages.emojione)) { - if (isSetNotNull(() => RocketChat.getUserPreference(Meteor.user(), 'convertAsciiEmoji'))) { - RocketChat.emoji.packages.emojione.ascii = RocketChat.getUserPreference(Meteor.user(), 'convertAsciiEmoji'); + if (isSetNotNull(() => RocketChat.getUserPreference(Meteor.userId(), 'convertAsciiEmoji'))) { + RocketChat.emoji.packages.emojione.ascii = RocketChat.getUserPreference(Meteor.userId(), 'convertAsciiEmoji'); } else { RocketChat.emoji.packages.emojione.ascii = true; } diff --git a/packages/rocketchat-emoji/client/emojiButton.js b/packages/rocketchat-emoji/client/emojiButton.js index 584d4b498a5a..7114155ea456 100644 --- a/packages/rocketchat-emoji/client/emojiButton.js +++ b/packages/rocketchat-emoji/client/emojiButton.js @@ -4,7 +4,7 @@ Template.messageBox.events({ event.stopPropagation(); event.preventDefault(); - if (!RocketChat.getUserPreference(Meteor.user(), 'useEmojis')) { + if (!RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')) { return false; } diff --git a/packages/rocketchat-emoji/client/emojiParser.js b/packages/rocketchat-emoji/client/emojiParser.js index 7411a440ca06..f3271ab14825 100644 --- a/packages/rocketchat-emoji/client/emojiParser.js +++ b/packages/rocketchat-emoji/client/emojiParser.js @@ -6,8 +6,8 @@ import s from 'underscore.string'; * @param {Object} message - The message object */ RocketChat.callbacks.add('renderMessage', (message) => { - if (isSetNotNull(() => RocketChat.getUserPreference(Meteor.user(), 'useEmojis')) && - !RocketChat.getUserPreference(Meteor.user(), 'useEmojis')) { + if (isSetNotNull(() => RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')) && + !RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')) { return message; } diff --git a/packages/rocketchat-file-upload/server/lib/FileUpload.js b/packages/rocketchat-file-upload/server/lib/FileUpload.js index a5e0ce2d3609..b8ac908f52eb 100644 --- a/packages/rocketchat-file-upload/server/lib/FileUpload.js +++ b/packages/rocketchat-file-upload/server/lib/FileUpload.js @@ -161,29 +161,34 @@ Object.assign(FileUpload, { }, }; - if (metadata.orientation == null) { - return fut.return(); - } - - s.rotate() - .toFile(`${ tmpFile }.tmp`) - .then(Meteor.bindEnvironment(() => { - fs.unlink(tmpFile, Meteor.bindEnvironment(() => { - fs.rename(`${ tmpFile }.tmp`, tmpFile, Meteor.bindEnvironment(() => { - const { size } = fs.lstatSync(tmpFile); - this.getCollection().direct.update({ _id: file._id }, { - $set: { - size, - identify, - }, - }); - fut.return(); + const reorientation = (cb) => { + if (!metadata.orientation) { + return cb(); + } + s.rotate() + .toFile(`${ tmpFile }.tmp`) + .then(Meteor.bindEnvironment(() => { + fs.unlink(tmpFile, Meteor.bindEnvironment(() => { + fs.rename(`${ tmpFile }.tmp`, tmpFile, Meteor.bindEnvironment(() => { + cb(); + })); })); - })); - })).catch((err) => { - console.error(err); - fut.return(); + })).catch((err) => { + console.error(err); + fut.return(); + }); + + return; + }; + + reorientation(() => { + const { size } = fs.lstatSync(tmpFile); + this.getCollection().direct.update({ _id: file._id }, { + $set: { size, identify }, }); + + fut.return(); + }); })); return fut.wait(); diff --git a/packages/rocketchat-file-upload/server/methods/sendFileMessage.js b/packages/rocketchat-file-upload/server/methods/sendFileMessage.js index 6074f001817a..ca565ff94cb3 100644 --- a/packages/rocketchat-file-upload/server/methods/sendFileMessage.js +++ b/packages/rocketchat-file-upload/server/methods/sendFileMessage.js @@ -1,7 +1,7 @@ import _ from 'underscore'; Meteor.methods({ - async 'sendFileMessage'(roomId, store, file, msgData = {}) { + async sendFileMessage(roomId, store, file, msgData = {}) { if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' }); } @@ -39,7 +39,14 @@ Meteor.methods({ if (file.identify && file.identify.size) { attachment.image_dimensions = file.identify.size; } - attachment.image_preview = await FileUpload.resizeImagePreview(file); + try { + attachment.image_preview = await FileUpload.resizeImagePreview(file); + } catch (e) { + delete attachment.image_url; + delete attachment.image_type; + delete attachment.image_size; + delete attachment.image_dimensions; + } } else if (/^audio\/.+/.test(file.type)) { attachment.audio_url = fileUrl; attachment.audio_type = file.type; diff --git a/packages/rocketchat-grant/server/authenticate.js b/packages/rocketchat-grant/server/authenticate.js index 5832c6496064..2e1a8c957f3e 100644 --- a/packages/rocketchat-grant/server/authenticate.js +++ b/packages/rocketchat-grant/server/authenticate.js @@ -10,7 +10,7 @@ const setAvatarFromUrl = (userId, url) => new Promise((resolve, reject) => { Meteor.runAsUser(userId, () => { Meteor.call('setAvatarFromService', url, '', 'url', (err) => { if (err) { - if (err.details.timeToReset && err.details.timeToReset) { + if (err.details && err.details.timeToReset) { reject((t('error-too-many-requests', { seconds: parseInt(err.details.timeToReset / 1000), }))); diff --git a/packages/rocketchat-graphql/server/resolvers/channels/deleteChannel.js b/packages/rocketchat-graphql/server/resolvers/channels/deleteChannel.js new file mode 100644 index 000000000000..88dca7097998 --- /dev/null +++ b/packages/rocketchat-graphql/server/resolvers/channels/deleteChannel.js @@ -0,0 +1,41 @@ +import { Meteor } from 'meteor/meteor'; +import { RocketChat } from 'meteor/rocketchat:lib'; + +import { authenticated } from '../../helpers/authenticated'; +import schema from '../../schemas/channels/deleteChannel.graphqls'; + +const resolver = { + Mutation: { + deleteChannel: authenticated((root, { channelId }, { user }) => { + const channel = RocketChat.models.Rooms.findOne({ + _id: channelId, + t: 'c', + }); + + if (!channel) { + throw new Error('error-room-not-found', 'The required "channelId" param provided does not match any channel'); + } + + const sub = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(channel._id, user._id); + + if (!sub) { + throw new Error(`The user/callee is not in the channel "${ channel.name }.`); + } + + if (!sub.open) { + throw new Error(`The channel, ${ channel.name }, is already closed to the sender`); + } + + Meteor.runAsUser(user._id, () => { + Meteor.call('eraseRoom', channel._id); + }); + + return true; + }), + }, +}; + +export { + schema, + resolver, +}; diff --git a/packages/rocketchat-graphql/server/resolvers/channels/index.js b/packages/rocketchat-graphql/server/resolvers/channels/index.js index cf3d61a85f19..f463dccabea4 100644 --- a/packages/rocketchat-graphql/server/resolvers/channels/index.js +++ b/packages/rocketchat-graphql/server/resolvers/channels/index.js @@ -9,6 +9,7 @@ import * as channelsByUser from './channelsByUser'; import * as createChannel from './createChannel'; import * as leaveChannel from './leaveChannel'; import * as hideChannel from './hideChannel'; +import * as deleteChannel from './deleteChannel'; // types import * as ChannelType from './Channel-type'; import * as ChannelSort from './ChannelSort-enum'; @@ -26,6 +27,7 @@ export const schema = mergeTypes([ createChannel.schema, leaveChannel.schema, hideChannel.schema, + deleteChannel.schema, // types ChannelType.schema, ChannelSort.schema, @@ -44,6 +46,7 @@ export const resolvers = mergeResolvers([ createChannel.resolver, leaveChannel.resolver, hideChannel.resolver, + deleteChannel.resolver, // types ChannelType.resolver, ]); diff --git a/packages/rocketchat-graphql/server/schemas/channels/deleteChannel.graphqls b/packages/rocketchat-graphql/server/schemas/channels/deleteChannel.graphqls new file mode 100644 index 000000000000..bf11cc4fbe47 --- /dev/null +++ b/packages/rocketchat-graphql/server/schemas/channels/deleteChannel.graphqls @@ -0,0 +1,3 @@ +type Mutation { + deleteChannel(channelId: String!): Boolean +} diff --git a/packages/rocketchat-highlight-words/client/client.js b/packages/rocketchat-highlight-words/client/client.js index f2174eb38ca5..6afd8938c453 100644 --- a/packages/rocketchat-highlight-words/client/client.js +++ b/packages/rocketchat-highlight-words/client/client.js @@ -15,7 +15,7 @@ function HighlightWordsClient(message) { } } - const to_highlight = RocketChat.getUserPreference(Meteor.user(), 'highlights'); + const to_highlight = RocketChat.getUserPreference(Meteor.userId(), 'highlights'); if (Array.isArray(to_highlight)) { to_highlight.forEach((highlight) => { if (!s.isBlank(highlight)) { diff --git a/packages/rocketchat-i18n/i18n/de.i18n.json b/packages/rocketchat-i18n/i18n/de.i18n.json index 7c602513af35..181b91da9a39 100644 --- a/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/packages/rocketchat-i18n/i18n/de.i18n.json @@ -1,4 +1,6 @@ { + "__username__is_no_longer__role__defined_by__user_by_": "__username__ ist nicht länger __role__, geändert durch __user_by__", + "__username__was_set__role__by__user_by_": "__username__ ist jetzt __role__, geändert durch __user_by__", "@username_message": "@Benutzername ", "@username": "@Benutzername", "#channel": "#Kanal", @@ -7,8 +9,6 @@ "2_Erros_Information_and_Debug": "2 - Fehler, Informationen und Debug-Meldungen", "403": "Verboten", "500": "Interner Serverfehler", - "__username__is_no_longer__role__defined_by__user_by_": "__username__ ist nicht länger __role__, geändert durch __user_by__", - "__username__was_set__role__by__user_by_": "__username__ ist jetzt __role__, geändert durch __user_by__", "a_direct_message": "einer Direktnachricht", "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Akzeptiere eingehende Livechat-Anfragen auch wenn kein Agent online ist", "Accept_with_no_online_agents": "Auch annehmen, wenn keine Agenten online sind", @@ -2559,16 +2559,6 @@ "This_is_a_push_test_messsage": "Dies ist eine Test-Push-Nachricht", "This_room_has_been_archived_by__username_": "Dieser Raum wurde von __username__ archiviert", "This_room_has_been_unarchived_by__username_": "Dieser Raum wurde von __username__ aus dem Archiv geholt", - "Threading_title": "Einen neuen Thread anlegen", - "Thread_start": "Thread starten", - "thread-created": "Ich habe einen neuen __channelLink__ angelegt", - "thread-welcome": "Danke __username__, dass Du einen neuen Thread angelegt hast! Ich habe für Dich Mitglieder aus __parentChannel__ eingeladen. Tipp: mit \"@all\" kannst Du sie anstupsen, wenn sich länger niemand melden sollte ;)", - "Threading_description": "Erstelle einen Thread, um wichtigen Dingen mehr Raum zu geben. Dort kannst Du mit allen verfügbaren Mitgliedern schreiben, ohne andere zu stören. So sorgst Du für etwas mehr Ordnung in Eurem Chat.", - "Thread_from_context_menu": "Threads im Kontext-Menü", - "Thread_invitations_threshold": "Max. Anzahl automatisch einzuladender Benutzer", - "Thread_invitations_threshold_description": "Max. Anzahl der Benutzer, die automatisch zu einem öffentlichen Thread hinzugezogen werden", - "Threading_context_menu_button": "Separater Button", - "Threading_context_menu_none": "Unsichtbar", "Thread_creation_on_home": "Threads von der Home-Seite aus anlegen", "Thread_default_parent_Channel": "Standard-Kanal für neue Threads", "Thread_from_context_menu": "Threads im Kontext-Menü", diff --git a/packages/rocketchat-iframe-login/iframe_server.js b/packages/rocketchat-iframe-login/iframe_server.js index 1b1c49008ff6..9db26b6e7237 100644 --- a/packages/rocketchat-iframe-login/iframe_server.js +++ b/packages/rocketchat-iframe-login/iframe_server.js @@ -5,6 +5,8 @@ Accounts.registerLoginHandler('iframe', function(result) { return; } + check(result.token, String); + console.log('[Method] registerLoginHandler'); const user = Meteor.users.findOne({ diff --git a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js index f019f10fc3de..a19af074fa29 100644 --- a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js +++ b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js @@ -495,6 +495,7 @@ export class HipChatEnterpriseImporter extends Base { super.updateProgress(ProgressStep.FINISHING); super.updateProgress(ProgressStep.DONE); } catch (e) { + super.updateRecord({ 'error-record': JSON.stringify(e, Object.getOwnPropertyNames(e)) }); this.logger.error(e); super.updateProgress(ProgressStep.ERROR); } diff --git a/packages/rocketchat-importer-slack/server/importer.js b/packages/rocketchat-importer-slack/server/importer.js index f76647eb00d5..11dd06814e01 100644 --- a/packages/rocketchat-importer-slack/server/importer.js +++ b/packages/rocketchat-importer-slack/server/importer.js @@ -304,6 +304,13 @@ export class SlackImporter extends Base { } if (message.type === 'message') { + if (message.files && message.files[0].url_private_download !== undefined) { + const msgObj = { + ...msgDataDefaults, + msg: this.convertSlackMessageToRocketChat(message.files[0].url_private_download), + }; + RocketChat.sendMessage(this.getRocketUser(message.user), msgObj, room, true); + } if (message.subtype) { if (message.subtype === 'channel_join') { if (this.getRocketUser(message.user)) { diff --git a/packages/rocketchat-importer/server/classes/ImporterBase.js b/packages/rocketchat-importer/server/classes/ImporterBase.js index bdd83c48ff18..7d9ec7c33e46 100644 --- a/packages/rocketchat-importer/server/classes/ImporterBase.js +++ b/packages/rocketchat-importer/server/classes/ImporterBase.js @@ -192,12 +192,16 @@ export class Base { this.oldSettings.FileUpload_MaxFileSize = RocketChat.models.Settings.findOneById('FileUpload_MaxFileSize').value; RocketChat.models.Settings.updateValueById('FileUpload_MaxFileSize', -1); + + this.oldSettings.FileUpload_MediaTypeWhiteList = RocketChat.models.Settings.findOneById('FileUpload_MediaTypeWhiteList').value; + RocketChat.models.Settings.updateValueById('FileUpload_MediaTypeWhiteList', '*'); break; case ProgressStep.DONE: case ProgressStep.ERROR: RocketChat.models.Settings.updateValueById('Accounts_AllowedDomainsList', this.oldSettings.Accounts_AllowedDomainsList); RocketChat.models.Settings.updateValueById('Accounts_AllowUsernameChange', this.oldSettings.Accounts_AllowUsernameChange); RocketChat.models.Settings.updateValueById('FileUpload_MaxFileSize', this.oldSettings.FileUpload_MaxFileSize); + RocketChat.models.Settings.updateValueById('FileUpload_MediaTypeWhiteList', this.oldSettings.FileUpload_MediaTypeWhiteList); break; } diff --git a/packages/rocketchat-ldap/server/loginHandler.js b/packages/rocketchat-ldap/server/loginHandler.js index e05d23a55841..2f9cdefe4ce1 100644 --- a/packages/rocketchat-ldap/server/loginHandler.js +++ b/packages/rocketchat-ldap/server/loginHandler.js @@ -116,23 +116,14 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) { logger.info('Logging user'); - const stampedToken = Accounts._generateStampedLoginToken(); - - Meteor.users.update(user._id, { - $push: { - 'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken), - }, - }); - syncUserData(user, ldapUser); if (RocketChat.settings.get('LDAP_Login_Fallback') === true && typeof loginRequest.ldapPass === 'string' && loginRequest.ldapPass.trim() !== '') { Accounts.setPassword(user._id, loginRequest.ldapPass, { logout: false }); } - + RocketChat.callbacks.run('afterLDAPLogin', { user, ldapUser, ldap }); return { userId: user._id, - token: stampedToken.token, }; } @@ -152,6 +143,7 @@ Accounts.registerLoginHandler('ldap', function(loginRequest) { if (result instanceof Error) { throw result; } + RocketChat.callbacks.run('afterLDAPLogin', { user: result, ldapUser, ldap }); return result; }); diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index a31e5db8fa94..7f3e15c5fd0c 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -82,11 +82,13 @@ Package.onUse(function(api) { // SERVER FUNCTIONS api.addFiles('server/functions/isDocker.js', 'server'); + api.addFiles('server/functions/isTheLastMessage.js', 'server'); api.addFiles('server/functions/addUserToDefaultChannels.js', 'server'); api.addFiles('server/functions/addUserToRoom.js', 'server'); api.addFiles('server/functions/archiveRoom.js', 'server'); api.addFiles('server/functions/checkUsernameAvailability.js', 'server'); api.addFiles('server/functions/checkEmailAvailability.js', 'server'); + api.addFiles('server/functions/composeMessageObjectWithUser.js', 'server'); api.addFiles('server/functions/createRoom.js', 'server'); api.addFiles('server/functions/cleanRoomHistory.js', 'server'); api.addFiles('server/functions/deleteMessage.js', 'server'); diff --git a/packages/rocketchat-lib/rocketchat.info b/packages/rocketchat-lib/rocketchat.info index a3b1fcd45772..347509546c18 100644 --- a/packages/rocketchat-lib/rocketchat.info +++ b/packages/rocketchat-lib/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "0.70.4-0.9.5" + "version": "0.71.1-0.9.5" } diff --git a/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js b/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js new file mode 100644 index 000000000000..bb67bf6a9d9c --- /dev/null +++ b/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js @@ -0,0 +1,20 @@ +const getUser = (userId) => RocketChat.models.Users.findOneById(userId); + +RocketChat.composeMessageObjectWithUser = function(message, userId) { + if (message) { + if (message.starred && Array.isArray(message.starred)) { + message.starred = message.starred.filter((star) => star._id === userId); + } + if (message.u && message.u._id && RocketChat.settings.get('UI_Use_Real_Name')) { + const user = getUser(message.u._id); + message.u.name = user && user.name; + } + if (message.mentions && message.mentions.length && RocketChat.settings.get('UI_Use_Real_Name')) { + message.mentions.forEach((mention) => { + const user = getUser(mention._id); + mention.name = user && user.name; + }); + } + } + return message; +}; diff --git a/packages/rocketchat-lib/server/functions/deleteMessage.js b/packages/rocketchat-lib/server/functions/deleteMessage.js index f8ddf3887498..eb0d6956a499 100644 --- a/packages/rocketchat-lib/server/functions/deleteMessage.js +++ b/packages/rocketchat-lib/server/functions/deleteMessage.js @@ -39,7 +39,7 @@ RocketChat.deleteMessage = function(message, user) { if (RocketChat.settings.get('Store_Last_Message')) { const room = RocketChat.models.Rooms.findOneById(message.rid, { fields: { lastMessage: 1 } }); if (!room.lastMessage || room.lastMessage._id === message._id) { - RocketChat.models.Rooms.resetLastMessageById(message.rid); + RocketChat.models.Rooms.resetLastMessageById(message.rid, message._id); } } diff --git a/packages/rocketchat-lib/server/functions/isTheLastMessage.js b/packages/rocketchat-lib/server/functions/isTheLastMessage.js new file mode 100644 index 000000000000..c2bc6d0717f2 --- /dev/null +++ b/packages/rocketchat-lib/server/functions/isTheLastMessage.js @@ -0,0 +1 @@ +RocketChat.isTheLastMessage = (room, message) => RocketChat.settings.get('Store_Last_Message') && (!room.lastMessage || room.lastMessage._id === message._id); diff --git a/packages/rocketchat-lib/server/functions/loadMessageHistory.js b/packages/rocketchat-lib/server/functions/loadMessageHistory.js index a8eb95cf7b6f..3110114b2f60 100644 --- a/packages/rocketchat-lib/server/functions/loadMessageHistory.js +++ b/packages/rocketchat-lib/server/functions/loadMessageHistory.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - const hideMessagesOfType = []; RocketChat.settings.get(/Message_HideType_.+/, function(key, value) { @@ -39,26 +37,7 @@ RocketChat.loadMessageHistory = function loadMessageHistory({ userId, rid, end, } else { records = RocketChat.models.Messages.findVisibleByRoomIdNotContainingTypes(rid, hideMessagesOfType, options).fetch(); } - - const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; - - const messages = records.map((message) => { - message.starred = _.findWhere(message.starred, { - _id: userId, - }); - if (message.u && message.u._id && UI_Use_Real_Name) { - const user = RocketChat.models.Users.findOneById(message.u._id); - message.u.name = user && user.name; - } - if (message.mentions && message.mentions.length && UI_Use_Real_Name) { - message.mentions.forEach((mention) => { - const user = RocketChat.models.Users.findOneById(mention._id); - mention.name = user && user.name; - }); - } - return message; - }); - + const messages = records.map((record) => RocketChat.composeMessageObjectWithUser(record, userId)); let unreadNotLoaded = 0; let firstUnread; diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index 02dc6f02aea1..3844b0677d5e 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -94,6 +94,48 @@ function validateUserData(userId, userData) { } } +function validateUserEditing(userId, userData) { + const editingMyself = userData._id && userId === userData._id; + + const canEditOtherUserInfo = RocketChat.authz.hasPermission(userId, 'edit-other-user-info'); + const canEditOtherUserPassword = RocketChat.authz.hasPermission(userId, 'edit-other-user-password'); + + if (!RocketChat.settings.get('Accounts_AllowUserProfileChange') && !canEditOtherUserInfo && !canEditOtherUserPassword) { + throw new Meteor.Error('error-action-not-allowed', 'Edit user profile is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } + + if (userData.username && !RocketChat.settings.get('Accounts_AllowUsernameChange') && (!canEditOtherUserInfo || editingMyself)) { + throw new Meteor.Error('error-action-not-allowed', 'Edit username is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } + + if (userData.name && !RocketChat.settings.get('Accounts_AllowRealNameChange') && (!canEditOtherUserInfo || editingMyself)) { + throw new Meteor.Error('error-action-not-allowed', 'Edit user real name is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } + + if (userData.email && !RocketChat.settings.get('Accounts_AllowEmailChange') && (!canEditOtherUserInfo || editingMyself)) { + throw new Meteor.Error('error-action-not-allowed', 'Edit user email is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } + + if (userData.password && !RocketChat.settings.get('Accounts_AllowPasswordChange') && (!canEditOtherUserPassword || editingMyself)) { + throw new Meteor.Error('error-action-not-allowed', 'Edit user password is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } +} + RocketChat.saveUser = function(userId, userData) { validateUserData(userId, userData); @@ -169,40 +211,8 @@ RocketChat.saveUser = function(userId, userData) { return _id; } - if (!RocketChat.settings.get('Accounts_AllowUserProfileChange') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-password')) { - throw new Meteor.Error('error-action-not-allowed', 'Edit user profile is not allowed', { - method: 'insertOrUpdateUser', - action: 'Update_user', - }); - } - - if (userData.username && !RocketChat.settings.get('Accounts_AllowUsernameChange') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info')) { - throw new Meteor.Error('error-action-not-allowed', 'Edit username is not allowed', { - method: 'insertOrUpdateUser', - action: 'Update_user', - }); - } - - if (userData.name && !RocketChat.settings.get('Accounts_AllowRealNameChange') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info')) { - throw new Meteor.Error('error-action-not-allowed', 'Edit user real name is not allowed', { - method: 'insertOrUpdateUser', - action: 'Update_user', - }); - } - if (userData.email && !RocketChat.settings.get('Accounts_AllowEmailChange') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info')) { - throw new Meteor.Error('error-action-not-allowed', 'Edit user email is not allowed', { - method: 'insertOrUpdateUser', - action: 'Update_user', - }); - } - - if (userData.password && !RocketChat.settings.get('Accounts_AllowPasswordChange') && !RocketChat.authz.hasPermission(userId, 'edit-other-user-password')) { - throw new Meteor.Error('error-action-not-allowed', 'Edit user password is not allowed', { - method: 'insertOrUpdateUser', - action: 'Update_user', - }); - } + validateUserEditing(userId, userData); // update user if (userData.username) { diff --git a/packages/rocketchat-lib/server/functions/settings.js b/packages/rocketchat-lib/server/functions/settings.js index 804d37997260..8933d91912f7 100644 --- a/packages/rocketchat-lib/server/functions/settings.js +++ b/packages/rocketchat-lib/server/functions/settings.js @@ -50,6 +50,8 @@ RocketChat.settings.add = function(_id, value, options = {}) { value = true; } else if (value.toLowerCase() === 'false') { value = false; + } else if (options.type === 'int') { + value = parseInt(value); } options.processEnvValue = value; options.valueSource = 'processEnvValue'; @@ -83,6 +85,8 @@ RocketChat.settings.add = function(_id, value, options = {}) { value = true; } else if (value.toLowerCase() === 'false') { value = false; + } else if (options.type === 'int') { + value = parseInt(value); } options.value = value; options.processEnvValue = value; diff --git a/packages/rocketchat-lib/server/methods/getChannelHistory.js b/packages/rocketchat-lib/server/methods/getChannelHistory.js index 440fd1e63a24..72108ae707b0 100644 --- a/packages/rocketchat-lib/server/methods/getChannelHistory.js +++ b/packages/rocketchat-lib/server/methods/getChannelHistory.js @@ -51,22 +51,7 @@ Meteor.methods({ records = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestamps(rid, oldest, latest, options).fetch(); } - const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; - - const messages = _.map(records, (message) => { - message.starred = _.findWhere(message.starred, { _id: fromUserId }); - if (message.u && message.u._id && UI_Use_Real_Name) { - const user = RocketChat.models.Users.findOneById(message.u._id); - message.u.name = user && user.name; - } - if (message.mentions && message.mentions.length && UI_Use_Real_Name) { - message.mentions.forEach((mention) => { - const user = RocketChat.models.Users.findOneById(mention._id); - mention.name = user && user.name; - }); - } - return message; - }); + const messages = records.map((record) => RocketChat.composeMessageObjectWithUser(record, fromUserId)); if (unreads) { let unreadNotLoaded = 0; diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js index 8367a30cc1eb..53f2399542d2 100644 --- a/packages/rocketchat-lib/server/models/Rooms.js +++ b/packages/rocketchat-lib/server/models/Rooms.js @@ -436,9 +436,9 @@ class ModelRooms extends RocketChat.models._Base { return this.update(query, update); } - resetLastMessageById(_id) { + resetLastMessageById(_id, messageId) { const query = { _id }; - const lastMessage = RocketChat.models.Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id); + const lastMessage = RocketChat.models.Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id, messageId); const update = lastMessage ? { $set: { diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js index f22d11a968fd..749431adfd17 100644 --- a/packages/rocketchat-lib/server/startup/settings.js +++ b/packages/rocketchat-lib/server/startup/settings.js @@ -1194,7 +1194,7 @@ RocketChat.settings.addGroup('Layout', function() { type: 'string', public: true, }); - this.add('Layout_Home_Body', '

Welcome to Rocket.Chat!

\n

The Rocket.Chat desktops apps for Windows, macOS and Linux are available to download here.

The native mobile app, Rocket.Chat+,\n for Android and iOS is available from Google Play and the App Store.

\n

For further help, please consult the documentation.

\n

If you\'re an admin, feel free to change this content via Administration -> Layout -> Home Body. Or clicking here.

', { + this.add('Layout_Home_Body', '

Welcome to Rocket.Chat!

\n

The Rocket.Chat desktops apps for Windows, macOS and Linux are available to download here.

The native mobile app, Rocket.Chat+,\n for Android and iOS is available from Google Play and the App Store.

\n

For further help, please consult the documentation.

\n

If you\'re an admin, feel free to change this content via Administration -> Layout -> Home Body. Or clicking here.

', { type: 'code', code: 'text/html', multiline: true, diff --git a/packages/rocketchat-livechat/.app/client/lib/_livechat.js b/packages/rocketchat-livechat/.app/client/lib/_livechat.js index eb3a93f19911..cfb5c0af076d 100644 --- a/packages/rocketchat-livechat/.app/client/lib/_livechat.js +++ b/packages/rocketchat-livechat/.app/client/lib/_livechat.js @@ -209,10 +209,7 @@ this.Livechat = new (class Livechat { } set department(departmentId) { const dept = Department.findOne({ _id: departmentId }) || Department.findOne({ name: departmentId }); - - if (dept) { - this._department.set(dept._id); - } + this._department.set(dept && dept._id); } set agent(agentData) { this._agent.set(agentData); diff --git a/packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json b/packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json index 78f9bed1c4e5..b8725250ec7a 100644 --- a/packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json @@ -17,6 +17,7 @@ "How_satisfied_were_you_with_this_chat": "Você ficou satisfeito com este bate-papo?", "Installation": "Instalação", "New_messages": "Novas mensagens", + "New_livechat_in_queue": "Novo chat na roleta", "No": "Não", "Options": "Opções", "Please_answer_survey": "Por favor, tire um momento para responder uma rápida pesquisa sobre este chat", @@ -38,6 +39,7 @@ "Type_your_email": "Digite seu e-mail", "Type_your_message": "Digite sua mensagem", "Type_your_name": "Digite seu nome", + "Upload_file_question": "Enviar arquivo?", "User_joined": "Usuário entrou", "User_left": "Usuário saiu", "We_are_not_online_right_now_please_leave_a_message": "Nós não estamos online agora. Por favor, deixe uma mensagem.", @@ -45,4 +47,4 @@ "Yes": "Sim", "You": "Você", "You_must_complete_all_fields": "Você deve preencher todos os campos" -} \ No newline at end of file +} diff --git a/packages/rocketchat-livechat/client/lib/dateHandler.js b/packages/rocketchat-livechat/client/lib/dateHandler.js index 9a0d0b6de1c2..01bd25246eb1 100644 --- a/packages/rocketchat-livechat/client/lib/dateHandler.js +++ b/packages/rocketchat-livechat/client/lib/dateHandler.js @@ -13,7 +13,7 @@ export const checkDaterangeValue = (value, from, to) => { if (moment().startOf('day').isSame(from) && moment().startOf('day').isSame(to)) { return 'today'; } - if (moment().startOf('day').subtract(1, 'days').isSame(from) && moment().startOf('day').subtract(1, 'days').isSame(from)) { + if (moment().startOf('day').subtract(1, 'days').isSame(from) && moment().startOf('day').subtract(1, 'days').isSame(to)) { return 'yesterday'; } if (moment().startOf('week').isSame(from) && moment().endOf('week').isSame(to)) { diff --git a/packages/rocketchat-livechat/server/api/v1/config.js b/packages/rocketchat-livechat/server/api/v1/config.js index c6ca840fac26..2dfd7746eddd 100644 --- a/packages/rocketchat-livechat/server/api/v1/config.js +++ b/packages/rocketchat-livechat/server/api/v1/config.js @@ -12,7 +12,7 @@ RocketChat.API.v1.addRoute('livechat/config', { return RocketChat.API.v1.success({ config: { enabled: false } }); } - const { status } = online(); + const status = online(); let guest; let room; diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 5bfeb1d4f7ea..2acfdf0c6e35 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -221,7 +221,8 @@ RocketChat.Livechat = { } if (department) { - updateUser.$set.department = department; + const dep = RocketChat.models.LivechatDepartment.findOneByIdOrName(department); + updateUser.$set.department = dep && dep._id; } LivechatVisitors.updateById(userId, updateUser); @@ -267,6 +268,10 @@ RocketChat.Livechat = { }, closeRoom({ user, visitor, room, comment }) { + if (!room || room.t !== 'l' || !room.open) { + return false; + } + const now = new Date(); const closeData = { diff --git a/packages/rocketchat-livechat/server/methods/closeByVisitor.js b/packages/rocketchat-livechat/server/methods/closeByVisitor.js index 85614b011bd1..3eb4a8adf21d 100644 --- a/packages/rocketchat-livechat/server/methods/closeByVisitor.js +++ b/packages/rocketchat-livechat/server/methods/closeByVisitor.js @@ -2,19 +2,13 @@ import LivechatVisitors from '../models/LivechatVisitors'; Meteor.methods({ 'livechat:closeByVisitor'({ roomId, token }) { - const room = RocketChat.models.Rooms.findOneOpenByRoomIdAndVisitorToken(roomId, token); - - if (!room || !room.open) { - return false; - } - const visitor = LivechatVisitors.getVisitorByToken(token); const language = (visitor && visitor.language) || RocketChat.settings.get('language') || 'en'; return RocketChat.Livechat.closeRoom({ visitor, - room, + room: RocketChat.models.Rooms.findOneOpenByRoomIdAndVisitorToken(roomId, token), comment: TAPi18n.__('Closed_by_visitor', { lng: language }), }); }, diff --git a/packages/rocketchat-livechat/server/methods/closeRoom.js b/packages/rocketchat-livechat/server/methods/closeRoom.js index 7ea442915d25..fb4896b3f37b 100644 --- a/packages/rocketchat-livechat/server/methods/closeRoom.js +++ b/packages/rocketchat-livechat/server/methods/closeRoom.js @@ -5,12 +5,6 @@ Meteor.methods({ throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeRoom' }); } - const room = RocketChat.models.Rooms.findOneById(roomId); - - if (!room || room.t !== 'l') { - throw new Meteor.Error('room-not-found', 'Room not found', { method: 'livechat:closeRoom' }); - } - const user = Meteor.user(); const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, user._id, { _id: 1 }); @@ -20,7 +14,7 @@ Meteor.methods({ return RocketChat.Livechat.closeRoom({ user, - room, + room: RocketChat.models.Rooms.findOneById(roomId), comment, }); }, diff --git a/packages/rocketchat-livechat/server/models/LivechatDepartment.js b/packages/rocketchat-livechat/server/models/LivechatDepartment.js index c9c8de1a5f44..f122f7612a32 100644 --- a/packages/rocketchat-livechat/server/models/LivechatDepartment.js +++ b/packages/rocketchat-livechat/server/models/LivechatDepartment.js @@ -78,6 +78,18 @@ class LivechatDepartment extends RocketChat.models._Base { }; return this.find(query); } + + findOneByIdOrName(_idOrName, options) { + const query = { + $or: [{ + _id: _idOrName, + }, { + name: _idOrName, + }], + }; + + return this.findOne(query, options); + } } RocketChat.models.LivechatDepartment = new LivechatDepartment(); diff --git a/packages/rocketchat-mailer/package.js b/packages/rocketchat-mailer/package.js index f450e3839283..0d3d26d051ac 100644 --- a/packages/rocketchat-mailer/package.js +++ b/packages/rocketchat-mailer/package.js @@ -8,6 +8,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'ddp-rate-limiter', + 'rocketchat:i18n', ]); api.mainModule('server/api.js', 'server'); diff --git a/packages/rocketchat-mailer/server/api.js b/packages/rocketchat-mailer/server/api.js index 1a3ef0669619..d357132d58e1 100644 --- a/packages/rocketchat-mailer/server/api.js +++ b/packages/rocketchat-mailer/server/api.js @@ -81,14 +81,14 @@ export const rfcMailPatternWithName = /^(?:.*<)?([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~- export const checkAddressFormat = (from) => rfcMailPatternWithName.test(from); -export const sendNoWrap = ({ to, from, subject, html }) => { +export const sendNoWrap = ({ to, from, subject, html, headers }) => { if (!checkAddressFormat(to)) { return; } - Meteor.defer(() => Email.send({ to, from, subject, html })); + Meteor.defer(() => Email.send({ to, from, subject, html, headers })); }; -export const send = ({ to, from, subject, html, data }) => sendNoWrap({ to, from, subject: replace(subject, data), html: wrap(html, data) }); +export const send = ({ to, from, subject, html, data, headers }) => sendNoWrap({ to, from, subject: replace(subject, data), html: wrap(html, data), headers }); export const checkAddressFormatAndThrow = (from, func) => { if (checkAddressFormat(from)) { diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.html b/packages/rocketchat-message-attachments/client/messageAttachment.html index 75036c32b57e..99a8a5633055 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.html +++ b/packages/rocketchat-message-attachments/client/messageAttachment.html @@ -130,6 +130,14 @@ {{/unless}} {{/if}} + {{#if isPDF}} + {{#unless collapsed}} +
+ {{> icon block="rc-input__icon-svg" icon="loading"}} +
+ + {{/unless}} + {{/if}} {{#if fields}} {{#unless collapsed}}
@@ -148,13 +156,15 @@
{{description}}
{{/if}} {{/unless}} - + {{#if actions}} -
- {{#each actions}} - {{> messageAction}} - {{/each}} -
+ {{#unless collapsed}} +
+ {{#each actions}} + {{> messageAction}} + {{/each}} +
+ {{/unless}} {{/if}} {{#each attachments}} diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.js b/packages/rocketchat-message-attachments/client/messageAttachment.js index 73438fdd78e2..49081a449acf 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.js +++ b/packages/rocketchat-message-attachments/client/messageAttachment.js @@ -50,8 +50,7 @@ Template.messageAttachment.helpers({ if (this.collapsed != null) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, time() { @@ -69,4 +68,11 @@ Template.messageAttachment.helpers({ isFile() { return this.type === 'file'; }, + isPDF() { + if (this.type === 'file' && this.title_link.endsWith('.pdf') && Template.parentData().file) { + this.fileId = Template.parentData().file._id; + return true; + } + return false; + }, }); diff --git a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css index 6377e7c943df..e4e39ed64cfc 100644 --- a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css +++ b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css @@ -145,6 +145,16 @@ html.rtl .attachment { border-radius: 5px; } + & .attachment-canvas { + display: none; + } + + & .attachment-pdf-loading { + display: none; + + font-size: 1.5rem; + } + & .actions-container { margin-top: 6px; } diff --git a/packages/rocketchat-message-pin/package.js b/packages/rocketchat-message-pin/package.js index 5fa6da757927..a31b1efc4851 100644 --- a/packages/rocketchat-message-pin/package.js +++ b/packages/rocketchat-message-pin/package.js @@ -25,6 +25,7 @@ Package.onUse(function(api) { ], 'client'); api.addFiles([ + 'server/models/Rooms.js', 'server/settings.js', 'server/pinMessage.js', 'server/publications/pinnedMessages.js', diff --git a/packages/rocketchat-message-pin/server/models/Rooms.js b/packages/rocketchat-message-pin/server/models/Rooms.js new file mode 100644 index 000000000000..6cdf5e14c8be --- /dev/null +++ b/packages/rocketchat-message-pin/server/models/Rooms.js @@ -0,0 +1,13 @@ +RocketChat.models.Rooms.setLastMessagePinned = function(roomId, pinnedBy, pinned, pinnedAt) { + const query = { _id: roomId }; + + const update = { + $set: { + 'lastMessage.pinned': pinned, + 'lastMessage.pinnedAt': pinnedAt || new Date, + 'lastMessage.pinnedBy': pinnedBy, + }, + }; + + return this.update(query, update); +}; diff --git a/packages/rocketchat-message-pin/server/pinMessage.js b/packages/rocketchat-message-pin/server/pinMessage.js index 497662b5f0cf..2abd64bda757 100644 --- a/packages/rocketchat-message-pin/server/pinMessage.js +++ b/packages/rocketchat-message-pin/server/pinMessage.js @@ -50,7 +50,7 @@ Meteor.methods({ if (RocketChat.settings.get('Message_KeepHistory')) { RocketChat.models.Messages.cloneAndSaveAsHistoryById(message._id); } - + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); const me = RocketChat.models.Users.findOneById(userId); originalMessage.pinned = true; @@ -63,6 +63,9 @@ Meteor.methods({ originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage); RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); + } const attachments = []; @@ -134,6 +137,10 @@ Meteor.methods({ username: me.username, }; originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage); + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); + } return RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); }, diff --git a/packages/rocketchat-message-snippet/package.js b/packages/rocketchat-message-snippet/package.js index e342ecca1c3a..5292dba032cb 100644 --- a/packages/rocketchat-message-snippet/package.js +++ b/packages/rocketchat-message-snippet/package.js @@ -28,6 +28,7 @@ Package.onUse(function(api) { // Server api.addFiles([ 'server/startup/settings.js', + 'server/models/Rooms.js', 'server/methods/snippetMessage.js', 'server/requests.js', 'server/publications/snippetedMessagesByRoom.js', diff --git a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js index bf4cf3156f43..ff7a6e0b476f 100644 --- a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js +++ b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js @@ -36,6 +36,10 @@ Meteor.methods({ // Create the SnippetMessage RocketChat.models.Messages.setSnippetedByIdAndUserId(message, filename, message.snippetedBy, message.snippeted, Date.now, filename); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessageSnippeted(room._id, message, filename, message.snippetedBy, + message.snippeted, Date.now, filename); + } RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser( 'message_snippeted', message.rid, '', me, { snippetId: message._id, snippetName: filename }); diff --git a/packages/rocketchat-message-snippet/server/models/Rooms.js b/packages/rocketchat-message-snippet/server/models/Rooms.js new file mode 100644 index 000000000000..907c911531df --- /dev/null +++ b/packages/rocketchat-message-snippet/server/models/Rooms.js @@ -0,0 +1,17 @@ +RocketChat.models.Rooms.setLastMessageSnippeted = function(roomId, message, snippetName, snippetedBy, snippeted, snippetedAt) { + const query = { _id: roomId }; + + const msg = `\`\`\`${ message.msg }\`\`\``; + + const update = { + $set: { + 'lastMessage.msg': msg, + 'lastMessage.snippeted': snippeted, + 'lastMessage.snippetedAt': snippetedAt || new Date, + 'lastMessage.snippetedBy': snippetedBy, + 'lastMessage.snippetName': snippetName, + }, + }; + + return this.update(query, update); +}; diff --git a/packages/rocketchat-message-star/client/actionButton.js b/packages/rocketchat-message-star/client/actionButton.js index 5ed9dcb4ab34..52b2fd201492 100644 --- a/packages/rocketchat-message-star/client/actionButton.js +++ b/packages/rocketchat-message-star/client/actionButton.js @@ -1,4 +1,3 @@ -import _ from 'underscore'; import toastr from 'toastr'; Meteor.startup(function() { @@ -21,7 +20,7 @@ Meteor.startup(function() { return false; } - return !_.findWhere(message.starred, { _id: Meteor.userId() }); + return !message.starred || !message.starred.find((star) => star._id === Meteor.userId()); }, order: 10, group: 'menu', @@ -46,7 +45,7 @@ Meteor.startup(function() { return false; } - return Boolean(_.findWhere(message.starred, { _id: Meteor.userId() })); + return message.starred && message.starred.find((star) => star._id === Meteor.userId()); }, order: 10, group: 'menu', diff --git a/packages/rocketchat-message-star/package.js b/packages/rocketchat-message-star/package.js index edcc39d9d13f..1a8f1bbe5b78 100644 --- a/packages/rocketchat-message-star/package.js +++ b/packages/rocketchat-message-star/package.js @@ -25,6 +25,7 @@ Package.onUse(function(api) { ], 'client'); api.addFiles([ + 'server/models/Rooms.js', 'server/settings.js', 'server/starMessage.js', 'server/publications/starredMessages.js', diff --git a/packages/rocketchat-message-star/server/models/Rooms.js b/packages/rocketchat-message-star/server/models/Rooms.js new file mode 100644 index 000000000000..bd457526d6f7 --- /dev/null +++ b/packages/rocketchat-message-star/server/models/Rooms.js @@ -0,0 +1,21 @@ +RocketChat.models.Rooms.updateLastMessageStar = function(roomId, userId, starred) { + let update; + const query = { _id: roomId }; + + if (starred) { + update = { + $addToSet: { + 'lastMessage.starred': { _id: userId }, + }, + }; + } else { + update = { + $pull: { + 'lastMessage.starred': { _id: userId }, + }, + }; + } + + return this.update(query, update); +}; + diff --git a/packages/rocketchat-message-star/server/starMessage.js b/packages/rocketchat-message-star/server/starMessage.js index 1bacf3673cb9..1b64fadb9b5c 100644 --- a/packages/rocketchat-message-star/server/starMessage.js +++ b/packages/rocketchat-message-star/server/starMessage.js @@ -17,6 +17,10 @@ Meteor.methods({ if (!subscription) { return false; } + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.updateLastMessageStar(room._id, Meteor.userId(), message.starred); + } return RocketChat.models.Messages.updateUserStarById(message._id, Meteor.userId(), message.starred); }, diff --git a/packages/rocketchat-oembed/client/oembedAudioWidget.js b/packages/rocketchat-oembed/client/oembedAudioWidget.js index 9ac57ba09fa6..9b4b997cba09 100644 --- a/packages/rocketchat-oembed/client/oembedAudioWidget.js +++ b/packages/rocketchat-oembed/client/oembedAudioWidget.js @@ -3,8 +3,7 @@ Template.oembedAudioWidget.helpers({ if (this.collapsed) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, }); diff --git a/packages/rocketchat-oembed/client/oembedFrameWidget.js b/packages/rocketchat-oembed/client/oembedFrameWidget.js index a1c1b5535bfb..59eabfe5d26e 100644 --- a/packages/rocketchat-oembed/client/oembedFrameWidget.js +++ b/packages/rocketchat-oembed/client/oembedFrameWidget.js @@ -3,8 +3,7 @@ Template.oembedFrameWidget.helpers({ if (this.collapsed) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, }); diff --git a/packages/rocketchat-oembed/client/oembedImageWidget.js b/packages/rocketchat-oembed/client/oembedImageWidget.js index 7b2046b51529..a9e6f994c6a1 100644 --- a/packages/rocketchat-oembed/client/oembedImageWidget.js +++ b/packages/rocketchat-oembed/client/oembedImageWidget.js @@ -1,11 +1,9 @@ Template.oembedImageWidget.helpers({ loadImage() { - const user = Meteor.user(); - - if (RocketChat.getUserPreference(user, 'autoImageLoad') === false && this.downloadImages == null) { + if (RocketChat.getUserPreference(Meteor.userId(), 'autoImageLoad') === false && this.downloadImages == null) { return false; } - if (Meteor.Device.isPhone() && RocketChat.getUserPreference(user, 'saveMobileBandwidth') && this.downloadImages == null) { + if (Meteor.Device.isPhone() && RocketChat.getUserPreference(Meteor.userId(), 'saveMobileBandwidth') && this.downloadImages == null) { return false; } return true; @@ -14,8 +12,7 @@ Template.oembedImageWidget.helpers({ if (this.collapsed != null) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, }); diff --git a/packages/rocketchat-oembed/client/oembedUrlWidget.js b/packages/rocketchat-oembed/client/oembedUrlWidget.js index a8d2180c8042..c8abef9ef4c3 100644 --- a/packages/rocketchat-oembed/client/oembedUrlWidget.js +++ b/packages/rocketchat-oembed/client/oembedUrlWidget.js @@ -62,8 +62,7 @@ Template.oembedUrlWidget.helpers({ if (this.collapsed != null) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, }); diff --git a/packages/rocketchat-oembed/client/oembedVideoWidget.js b/packages/rocketchat-oembed/client/oembedVideoWidget.js index c54b6a6671c2..456b99b0dd05 100644 --- a/packages/rocketchat-oembed/client/oembedVideoWidget.js +++ b/packages/rocketchat-oembed/client/oembedVideoWidget.js @@ -27,8 +27,7 @@ Template.oembedVideoWidget.helpers({ if (this.collapsed) { return this.collapsed; } else { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'collapseMediaByDefault') === true; + return RocketChat.getUserPreference(Meteor.userId(), 'collapseMediaByDefault') === true; } }, diff --git a/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js b/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js index d27db69d362b..a91b18f5db3b 100644 --- a/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js +++ b/packages/rocketchat-push-notifications/client/views/pushNotificationsFlexTab.js @@ -73,21 +73,21 @@ Template.pushNotificationsFlexTab.helpers({ } }, defaultAudioNotification() { - let preference = RocketChat.getUserPreference(Meteor.user(), 'audioNotifications'); + let preference = RocketChat.getUserPreference(Meteor.userId(), 'audioNotifications'); if (preference === 'default') { preference = RocketChat.settings.get('Accounts_Default_User_Preferences_audioNotifications'); } return notificationLabels[preference]; }, defaultDesktopNotification() { - let preference = RocketChat.getUserPreference(Meteor.user(), 'desktopNotifications'); + let preference = RocketChat.getUserPreference(Meteor.userId(), 'desktopNotifications'); if (preference === 'default') { preference = RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications'); } return notificationLabels[preference]; }, defaultMobileNotification() { - let preference = RocketChat.getUserPreference(Meteor.user(), 'mobileNotifications'); + let preference = RocketChat.getUserPreference(Meteor.userId(), 'mobileNotifications'); if (preference === 'default') { preference = RocketChat.settings.get('Accounts_Default_User_Preferences_mobileNotifications'); } @@ -190,7 +190,7 @@ Template.pushNotificationsFlexTab.events({ 'click [data-play]'(e) { e.preventDefault(); - const user = Meteor.user(); + const user = Meteor.userId(); let value = Template.instance().form.audioNotificationValue.get(); if (value === '0') { @@ -340,7 +340,7 @@ Template.pushNotificationsPopover.helpers({ return Template.instance().data.options; }, defaultDesktopNotification() { - let preference = RocketChat.getUserPreference(Meteor.user(), 'desktopNotifications'); + let preference = RocketChat.getUserPreference(Meteor.userId(), 'desktopNotifications'); if (preference === 'default') { preference = RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications'); } diff --git a/packages/rocketchat-reactions/package.js b/packages/rocketchat-reactions/package.js index 78e23d96093b..03ae0e50d4dd 100644 --- a/packages/rocketchat-reactions/package.js +++ b/packages/rocketchat-reactions/package.js @@ -16,6 +16,7 @@ Package.onUse(function(api) { api.addFiles('client/init.js', 'client'); api.addFiles('server/models/Messages.js'); + api.addFiles('server/models/Rooms.js'); api.addFiles('client/methods/setReaction.js', 'client'); api.addFiles('setReaction.js', 'server'); diff --git a/packages/rocketchat-reactions/server/models/Rooms.js b/packages/rocketchat-reactions/server/models/Rooms.js new file mode 100644 index 000000000000..26e16f8b7628 --- /dev/null +++ b/packages/rocketchat-reactions/server/models/Rooms.js @@ -0,0 +1,7 @@ +RocketChat.models.Rooms.setReactionsInLastMessage = function(roomId, lastMessage) { + return this.update({ _id: roomId }, { $set: { lastMessage } }); +}; + +RocketChat.models.Rooms.unsetReactionsInLastMessage = function(roomId) { + return this.update({ _id: roomId }, { $unset: { lastMessage: { reactions: 1 } } }); +}; diff --git a/packages/rocketchat-reactions/setReaction.js b/packages/rocketchat-reactions/setReaction.js index 807ecdcb336a..f51303b7d572 100644 --- a/packages/rocketchat-reactions/setReaction.js +++ b/packages/rocketchat-reactions/setReaction.js @@ -61,9 +61,15 @@ Meteor.methods({ if (_.isEmpty(message.reactions)) { delete message.reactions; + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.unsetReactionsInLastMessage(room._id); + } RocketChat.models.Messages.unsetReactions(messageId); RocketChat.callbacks.run('unsetReaction', messageId, reaction); } else { + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setReactionsInLastMessage(room._id, message); + } RocketChat.models.Messages.setReactions(messageId, message.reactions); RocketChat.callbacks.run('setReaction', messageId, reaction); } @@ -77,7 +83,9 @@ Meteor.methods({ }; } message.reactions[reaction].usernames.push(user.username); - + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setReactionsInLastMessage(room._id, message); + } RocketChat.models.Messages.setReactions(messageId, message.reactions); RocketChat.callbacks.run('setReaction', messageId, reaction); } diff --git a/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-header.css b/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-header.css index 0e0981fe07d9..9b6776363d9c 100644 --- a/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-header.css +++ b/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-header.css @@ -129,3 +129,20 @@ } } } + +@media (min-width: 1372px) { /* 1440px -68px (eletron menu) */ + .sidebar { + flex: 0 0 20%; + + width: 20%; + max-width: 20%; + + &__toolbar { + justify-content: flex-end; + + &-button { + margin: 0 6px; + } + } + } +} diff --git a/packages/rocketchat-ui-account/client/accountPreferences.js b/packages/rocketchat-ui-account/client/accountPreferences.js index 56f9b169cb2b..b25787debce0 100644 --- a/packages/rocketchat-ui-account/client/accountPreferences.js +++ b/packages/rocketchat-ui-account/client/accountPreferences.js @@ -18,7 +18,7 @@ function checkedSelected(property, value, defaultValue = undefined) { if (defaultValue && defaultValue.hash) { defaultValue = undefined; } - return RocketChat.getUserPreference(Meteor.user(), property, defaultValue) === value; + return RocketChat.getUserPreference(Meteor.userId(), property, defaultValue) === value; } Template.accountPreferences.helpers({ @@ -26,13 +26,13 @@ Template.accountPreferences.helpers({ return (RocketChat.CustomSounds && RocketChat.CustomSounds.getList && RocketChat.CustomSounds.getList()) || []; }, newMessageNotification() { - return RocketChat.getUserPreference(Meteor.user(), 'newMessageNotification'); + return RocketChat.getUserPreference(Meteor.userId(), 'newMessageNotification'); }, newRoomNotification() { - return RocketChat.getUserPreference(Meteor.user(), 'newRoomNotification'); + return RocketChat.getUserPreference(Meteor.userId(), 'newRoomNotification'); }, muteFocusedConversations() { - return RocketChat.getUserPreference(Meteor.user(), 'muteFocusedConversations'); + return RocketChat.getUserPreference(Meteor.userId(), 'muteFocusedConversations'); }, languages() { const languages = TAPi18n.getLanguages(); @@ -60,7 +60,7 @@ Template.accountPreferences.helpers({ return checkedSelected(property, value, defaultValue); }, highlights() { - const userHighlights = RocketChat.getUserPreference(Meteor.user(), 'highlights'); + const userHighlights = RocketChat.getUserPreference(Meteor.userId(), 'highlights'); return userHighlights ? userHighlights.join(',\n') : undefined; }, desktopNotificationEnabled() { @@ -70,14 +70,14 @@ Template.accountPreferences.helpers({ return KonchatNotification.notificationStatus.get() === 'denied' || (window.Notification && Notification.permission === 'denied'); }, desktopNotificationDuration() { - const userPref = RocketChat.getUserPreference(Meteor.user(), 'desktopNotificationDuration', 'undefined'); + const userPref = RocketChat.getUserPreference(Meteor.userId(), 'desktopNotificationDuration', 'undefined'); return userPref !== 'undefined' ? userPref : undefined; }, defaultDesktopNotificationDuration() { return RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotificationDuration'); }, idleTimeLimit() { - return RocketChat.getUserPreference(Meteor.user(), 'idleTimeLimit'); + return RocketChat.getUserPreference(Meteor.userId(), 'idleTimeLimit'); }, defaultIdleTimeLimit() { return RocketChat.settings.get('Accounts_Default_User_Preferences_idleTimeLimit'); @@ -98,15 +98,14 @@ Template.accountPreferences.helpers({ return RocketChat.settings.get('UserData_EnableDownload') !== false; }, notificationsSoundVolume() { - return RocketChat.getUserPreference(Meteor.user(), 'notificationsSoundVolume'); + return RocketChat.getUserPreference(Meteor.userId(), 'notificationsSoundVolume'); }, dontAskAgainList() { - return RocketChat.getUserPreference(Meteor.user(), 'dontAskAgainList'); + return RocketChat.getUserPreference(Meteor.userId(), 'dontAskAgainList'); }, }); Template.accountPreferences.onCreated(function() { - const user = Meteor.user(); const settingsTemplate = this.parentTemplate(3); if (settingsTemplate.child == null) { @@ -115,7 +114,7 @@ Template.accountPreferences.onCreated(function() { settingsTemplate.child.push(this); - this.useEmojis = new ReactiveVar(RocketChat.getUserPreference(user, 'useEmojis')); + this.useEmojis = new ReactiveVar(RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')); let instance = this; @@ -173,7 +172,7 @@ Template.accountPreferences.onCreated(function() { } // if highlights changed we need page reload - const highlights = RocketChat.getUserPreference(Meteor.user(), 'highlights'); + const highlights = RocketChat.getUserPreference(Meteor.userId(), 'highlights'); if (highlights && highlights.join('\n') !== data.highlights.join('\n')) { reload = true; } diff --git a/packages/rocketchat-ui-account/client/avatar/prompt.js b/packages/rocketchat-ui-account/client/avatar/prompt.js index 108a56bf74f9..5722aa7d18f3 100644 --- a/packages/rocketchat-ui-account/client/avatar/prompt.js +++ b/packages/rocketchat-ui-account/client/avatar/prompt.js @@ -65,7 +65,7 @@ Template.avatarPrompt.events({ if (s.trim($('#avatarurl').val())) { Meteor.call('setAvatarFromService', $('#avatarurl').val(), '', this.service, function(err) { if (err) { - if (err.details.timeToReset && err.details.timeToReset) { + if (err.details && err.details.timeToReset) { toastr.error(t('error-too-many-requests', { seconds: parseInt(err.details.timeToReset / 1000), })); diff --git a/packages/rocketchat-ui-admin/client/admin.js b/packages/rocketchat-ui-admin/client/admin.js index e274d97375dd..7a2ceeb60d1a 100644 --- a/packages/rocketchat-ui-admin/client/admin.js +++ b/packages/rocketchat-ui-admin/client/admin.js @@ -479,7 +479,7 @@ Template.admin.events({ } }); }, - 'click .rc-header__section-button .remove-custom-oauth'() { + 'click .remove-custom-oauth'() { const name = this.section.replace('Custom OAuth: ', ''); const config = { title: TAPi18n.__('Are_you_sure'), diff --git a/packages/rocketchat-ui-message/client/message.js b/packages/rocketchat-ui-message/client/message.js index 2c553832df29..8503d934ca0e 100644 --- a/packages/rocketchat-ui-message/client/message.js +++ b/packages/rocketchat-ui-message/client/message.js @@ -3,6 +3,39 @@ import _ from 'underscore'; import moment from 'moment'; import { DateFormat } from 'meteor/rocketchat:lib'; +async function renderPdfToCanvas(canvasId, pdfLink) { + + if (navigator.userAgent.toLowerCase().indexOf('safari/') > -1) { + const [, version] = /Version\/([0-9]+)/.exec(navigator.userAgent) || [null, 0]; + if (version <= 12) { + return; + } + } + + if (!pdfLink || /\.pdf$/i.test(pdfLink)) { return; } + const canvas = document.getElementById(canvasId); + if (!canvas) { return; } + const pdfjsLib = await import('pdfjs-dist'); + pdfjsLib.GlobalWorkerOptions.workerSrc = `${ Meteor.absoluteUrl() }node_modules/pdfjs-dist/build/pdf.worker.js`; + const loader = document.getElementById('js-loading-${canvasId}'); + if (loader) { loader.style.display = 'block'; } + const pdf = await pdfjsLib.getDocument(pdfLink); + const page = await pdf.getPage(1); + const scale = 0.5; + const viewport = page.getViewport(scale); + const context = canvas.getContext('2d'); + canvas.height = viewport.height; + canvas.width = viewport.width; + page.render({ + canvasContext: context, + viewport, + }); + if (loader) { loader.style.display = 'none'; } + canvas.style.maxWidth = '-webkit-fill-available'; + canvas.style.maxWidth = '-moz-available'; + canvas.style.display = 'block'; +} + Template.message.helpers({ encodeURI(text) { return encodeURI(text); @@ -26,8 +59,7 @@ Template.message.helpers({ } }, roleTags() { - const user = Meteor.user(); - if (!RocketChat.settings.get('UI_DisplayRoles') || RocketChat.getUserPreference(user, 'hideRoles')) { + if (!RocketChat.settings.get('UI_DisplayRoles') || RocketChat.getUserPreference(Meteor.userId(), 'hideRoles')) { return []; } @@ -364,7 +396,10 @@ Template.message.onCreated(function() { }); Template.message.onViewRendered = function(context) { - return this._domrange.onAttached(function(domRange) { + return this._domrange.onAttached((domRange) => { + if (context.file && context.file.type === 'application/pdf') { + Meteor.defer(() => { renderPdfToCanvas(context.file._id, context.attachments[0].title_link); }); + } const currentNode = domRange.lastNode(); const currentDataset = currentNode.dataset; const getPreviousSentMessage = (currentNode) => { diff --git a/packages/rocketchat-ui-message/client/messageBox.js b/packages/rocketchat-ui-message/client/messageBox.js index 92ea0e42864f..5c482873265e 100644 --- a/packages/rocketchat-ui-message/client/messageBox.js +++ b/packages/rocketchat-ui-message/client/messageBox.js @@ -267,7 +267,7 @@ Template.messageBox.helpers({ return RocketChat.Layout.isEmbedded(); }, isEmojiEnable() { - return RocketChat.getUserPreference(Meteor.user(), 'useEmojis'); + return RocketChat.getUserPreference(Meteor.userId(), 'useEmojis'); }, dataReply() { return Template.instance().dataReply.get(); diff --git a/packages/rocketchat-ui-message/package.js b/packages/rocketchat-ui-message/package.js index 805eda964bad..e2288d4e04e8 100644 --- a/packages/rocketchat-ui-message/package.js +++ b/packages/rocketchat-ui-message/package.js @@ -44,5 +44,7 @@ Package.onUse(function(api) { api.addFiles('startup/messageBoxActions.js', 'client'); + api.addAssets('../../node_modules/pdfjs-dist/build/pdf.worker.js', 'client'); + api.export('renderMessageBody'); }); diff --git a/packages/rocketchat-ui-sidenav/client/roomList.js b/packages/rocketchat-ui-sidenav/client/roomList.js index e6bcbd9c3780..5802b49d6a57 100644 --- a/packages/rocketchat-ui-sidenav/client/roomList.js +++ b/packages/rocketchat-ui-sidenav/client/roomList.js @@ -13,7 +13,16 @@ Template.roomList.helpers({ if (this.anonymous) { return RocketChat.models.Rooms.find({ t: 'c' }, { sort: { name: 1 } }); } - const user = Meteor.userId(); + + const user = RocketChat.models.Users.findOne(Meteor.userId(), { + fields: { + 'settings.preferences.sidebarSortby': 1, + 'settings.preferences.sidebarShowFavorites': 1, + 'settings.preferences.sidebarShowUnread': 1, + 'services.tokenpass': 1, + }, + }); + const sortBy = RocketChat.getUserPreference(user, 'sidebarSortby') || 'alphabetical'; const query = { open: true, diff --git a/packages/rocketchat-ui-sidenav/client/sideNav.js b/packages/rocketchat-ui-sidenav/client/sideNav.js index 27b5a8c7a96a..62387ab0019f 100644 --- a/packages/rocketchat-ui-sidenav/client/sideNav.js +++ b/packages/rocketchat-ui-sidenav/client/sideNav.js @@ -42,7 +42,7 @@ Template.sideNav.helpers({ }, sidebarHideAvatar() { - return RocketChat.getUserPreference(Meteor.user(), 'sidebarHideAvatar'); + return RocketChat.getUserPreference(Meteor.userId(), 'sidebarHideAvatar'); }, }); diff --git a/packages/rocketchat-ui-sidenav/client/sortlist.js b/packages/rocketchat-ui-sidenav/client/sortlist.js index 45aae8366058..2d0d9c7ede83 100644 --- a/packages/rocketchat-ui-sidenav/client/sortlist.js +++ b/packages/rocketchat-ui-sidenav/client/sortlist.js @@ -3,18 +3,18 @@ import { hideOldSubscriptions } from 'meteor/rocketchat:lib'; const checked = function(prop, field) { - const user = Meteor.userId(); + const userId = Meteor.userId(); if (prop === 'sidebarShowFavorites') { - return RocketChat.getUserPreference(user, 'sidebarShowFavorites'); + return RocketChat.getUserPreference(userId, 'sidebarShowFavorites'); } if (prop === 'sidebarGroupByType') { - return RocketChat.getUserPreference(user, 'sidebarGroupByType'); + return RocketChat.getUserPreference(userId, 'sidebarGroupByType'); } if (prop === 'sidebarShowUnread') { - return RocketChat.getUserPreference(user, 'sidebarShowUnread'); + return RocketChat.getUserPreference(userId, 'sidebarShowUnread'); } if (prop === 'sidebarSortby') { - return (RocketChat.getUserPreference(user, 'sidebarSortby') || 'alphabetical') === field; + return (RocketChat.getUserPreference(userId, 'sidebarSortby') || 'alphabetical') === field; } }; diff --git a/packages/rocketchat-ui/client/lib/fileUpload.js b/packages/rocketchat-ui/client/lib/fileUpload.js index ce9ed295eda5..3a3177a803aa 100644 --- a/packages/rocketchat-ui/client/lib/fileUpload.js +++ b/packages/rocketchat-ui/client/lib/fileUpload.js @@ -30,7 +30,7 @@ const showUploadPreview = (file, callback) => { const getAudioUploadPreview = (file, preview) => `\
-
`; -const getUploadPreview = (file, preview) => { +const getUploadPreview = async(file, preview) => { if (file.type === 'audio') { return getAudioUploadPreview(file, preview); } @@ -117,14 +117,21 @@ const getUploadPreview = (file, preview) => { return getVideoUploadPreview(file, preview); } - if (file.type === 'image') { + const isImageFormatSupported = () => new Promise((resolve) => { + const element = document.createElement('img'); + element.onload = () => resolve(true); + element.onerror = () => resolve(false); + element.src = preview; + }); + + if (file.type === 'image' && await isImageFormatSupported()) { return getImageUploadPreview(file, preview); } return getGenericUploadPreview(file, preview); }; -fileUpload = (files) => { +fileUpload = async(files) => { files = [].concat(files); const roomId = Session.get('openedRoom'); @@ -155,9 +162,9 @@ fileUpload = (files) => { return; } - showUploadPreview(file, (file, preview) => modal.open({ + showUploadPreview(file, async(file, preview) => modal.open({ title: t('Upload_file_question'), - text: getUploadPreview(file, preview), + text: await getUploadPreview(file, preview), showCancelButton: true, closeOnConfirm: false, closeOnCancel: false, diff --git a/packages/rocketchat-ui/client/lib/notification.js b/packages/rocketchat-ui/client/lib/notification.js index 3686fd9b083a..3d92f6d5218d 100644 --- a/packages/rocketchat-ui/client/lib/notification.js +++ b/packages/rocketchat-ui/client/lib/notification.js @@ -30,8 +30,7 @@ const KonchatNotification = { canReply: true, }); - const user = Meteor.user(); - const notificationDuration = notification.duration - 0 || RocketChat.getUserPreference(user, 'desktopNotificationDuration') - 0; + const notificationDuration = notification.duration - 0 || RocketChat.getUserPreference(Meteor.userId(), 'desktopNotificationDuration') - 0; if (notificationDuration > 0) { setTimeout((() => n.close()), notificationDuration * 1000); } @@ -89,9 +88,9 @@ const KonchatNotification = { newMessage(rid) { if (!Session.equals(`user_${ Meteor.user().username }_status`, 'busy')) { - const user = Meteor.user(); - const newMessageNotification = RocketChat.getUserPreference(user, 'newMessageNotification'); - const audioVolume = RocketChat.getUserPreference(user, 'notificationsSoundVolume'); + const userId = Meteor.userId(); + const newMessageNotification = RocketChat.getUserPreference(userId, 'newMessageNotification'); + const audioVolume = RocketChat.getUserPreference(userId, 'notificationsSoundVolume'); const sub = ChatSubscription.findOne({ rid }, { fields: { audioNotificationValue: 1 } }); diff --git a/packages/rocketchat-ui/client/views/app/modal.js b/packages/rocketchat-ui/client/views/app/modal.js index 22630fef4e3e..f2157cffb9d8 100644 --- a/packages/rocketchat-ui/client/views/app/modal.js +++ b/packages/rocketchat-ui/client/views/app/modal.js @@ -132,7 +132,7 @@ Template.rc_modal.events({ label: dontAskAgain.label, }; - let dontAskAgainList = RocketChat.getUserPreference(Meteor.user(), 'dontAskAgainList'); + let dontAskAgainList = RocketChat.getUserPreference(Meteor.userId(), 'dontAskAgainList'); if (dontAskAgainList) { dontAskAgainList.push(dontAskAgainObject); } else { diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index ac282e9f1c61..706bc14e4eb1 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -400,8 +400,7 @@ Template.room.helpers({ }, messageViewMode() { - const user = Meteor.user(); - const viewMode = RocketChat.getUserPreference(user, 'messageViewMode'); + const viewMode = RocketChat.getUserPreference(Meteor.userId(), 'messageViewMode'); const modes = ['', 'cozy', 'compact']; return modes[viewMode] || modes[0]; }, @@ -411,13 +410,11 @@ Template.room.helpers({ }, hideUsername() { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'hideUsernames') ? 'hide-usernames' : undefined; + return RocketChat.getUserPreference(Meteor.userId(), 'hideUsernames') ? 'hide-usernames' : undefined; }, hideAvatar() { - const user = Meteor.user(); - return RocketChat.getUserPreference(user, 'hideAvatars') ? 'hide-avatars' : undefined; + return RocketChat.getUserPreference(Meteor.userId(), 'hideAvatars') ? 'hide-avatars' : undefined; }, userCanDrop() { @@ -495,9 +492,7 @@ Template.room.events({ }, 'click .messages-container-main'() { - const user = Meteor.user(); - - if ((Template.instance().tabBar.getState() === 'opened') && RocketChat.getUserPreference(user, 'hideFlexTab')) { + if (Template.instance().tabBar.getState() === 'opened' && RocketChat.getUserPreference(Meteor.userId(), 'hideFlexTab')) { Template.instance().tabBar.close(); } }, @@ -641,7 +636,6 @@ Template.room.events({ }, 'scroll .wrapper': _.throttle(function(e, t) { - lazyloadtick(); const $roomLeader = $('.room-leader'); @@ -654,10 +648,14 @@ Template.room.events({ } lastScrollTop = e.target.scrollTop; - if ((RoomHistoryManager.isLoading(this._id) === false && RoomHistoryManager.hasMore(this._id) === true) || RoomHistoryManager.hasMoreNext(this._id) === true) { - if (RoomHistoryManager.hasMore(this._id) === true && e.target.scrollTop === 0) { + const isLoading = RoomHistoryManager.isLoading(this._id); + const hasMore = RoomHistoryManager.hasMore(this._id); + const hasMoreNext = RoomHistoryManager.hasMoreNext(this._id); + + if ((isLoading === false && hasMore === true) || hasMoreNext === true) { + if (hasMore === true && e.target.scrollTop === 0) { RoomHistoryManager.getMore(this._id); - } else if (RoomHistoryManager.hasMoreNext(this._id) === true && e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight) { + } else if (hasMoreNext === true && Math.ceil(e.target.scrollTop) >= e.target.scrollHeight - e.target.clientHeight) { RoomHistoryManager.getMoreNext(this._id); } } diff --git a/packages/rocketchat-ui/getAvatarUrlFromUsername.js b/packages/rocketchat-ui/getAvatarUrlFromUsername.js index d9e17c396539..66b6db3983ea 100644 --- a/packages/rocketchat-ui/getAvatarUrlFromUsername.js +++ b/packages/rocketchat-ui/getAvatarUrlFromUsername.js @@ -1,7 +1,7 @@ // TODO: remove global this.getAvatarUrlFromUsername = function(username) { const key = `avatar_random_${ username }`; - const random = typeof Session !== 'undefined' ? Session.keys[key] : 0; + const random = typeof Session !== 'undefined' && typeof Session.keys[key] !== 'undefined' ? Session.keys[key] : 0; if (username == null) { return; } diff --git a/packages/rocketchat-user-data-download/server/cronProcessDownloads.js b/packages/rocketchat-user-data-download/server/cronProcessDownloads.js index 59af82a29b82..c275d807f1ae 100644 --- a/packages/rocketchat-user-data-download/server/cronProcessDownloads.js +++ b/packages/rocketchat-user-data-download/server/cronProcessDownloads.js @@ -291,7 +291,7 @@ const sendEmail = function(userId) { } const userData = RocketChat.models.Users.findOneById(userId); - if (!userData || userData.emails || userData.emails[0] || userData.emails[0].address) { + if (!userData || !userData.emails || !userData.emails[0] || !userData.emails[0].address) { return; } const emailAddress = `${ userData.name } <${ userData.emails[0].address }>`; diff --git a/server/methods/loadNextMessages.js b/server/methods/loadNextMessages.js index 431a6b399be7..73ae17e105bd 100644 --- a/server/methods/loadNextMessages.js +++ b/server/methods/loadNextMessages.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - Meteor.methods({ loadNextMessages(rid, end, limit = 20) { check(rid, String); @@ -37,15 +35,8 @@ Meteor.methods({ records = RocketChat.models.Messages.findVisibleByRoomId(rid, options).fetch(); } - const messages = records.map((message) => { - message.starred = _.findWhere(message.starred, { - _id: fromId, - }); - return message; - }); - return { - messages, + messages: records.map((message) => RocketChat.composeMessageObjectWithUser(message, fromId)), }; }, }); diff --git a/server/methods/loadSurroundingMessages.js b/server/methods/loadSurroundingMessages.js index 97f6a39efd37..74df083c1937 100644 --- a/server/methods/loadSurroundingMessages.js +++ b/server/methods/loadSurroundingMessages.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - Meteor.methods({ loadSurroundingMessages(message, limit = 50) { check(message, Object); @@ -42,14 +40,7 @@ Meteor.methods({ }; } - const recordsBefore = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestamp(message.rid, message.ts, options).fetch(); - - const messages = recordsBefore.map((message) => { - message.starred = _.findWhere(message.starred, { - _id: fromId, - }); - return message; - }); + const messages = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestamp(message.rid, message.ts, options).fetch(); const moreBefore = messages.length === options.limit; @@ -61,18 +52,13 @@ Meteor.methods({ options.limit = Math.floor(limit / 2); - const recordsAfter = RocketChat.models.Messages.findVisibleByRoomIdAfterTimestamp(message.rid, message.ts, options).fetch(); - const afterMessages = recordsAfter.map((message) => { - message.starred = _.findWhere(message.starred, { - _id: fromId, - }); - return message; - }); + const afterMessages = RocketChat.models.Messages.findVisibleByRoomIdAfterTimestamp(message.rid, message.ts, options).fetch(); const moreAfter = afterMessages.length === options.limit; return { - messages: messages.concat(afterMessages), + messages: messages.concat(afterMessages) + .map((message) => RocketChat.composeMessageObjectWithUser(message, fromId)), moreBefore, moreAfter, }; diff --git a/server/publications/messages.js b/server/publications/messages.js index 6efcea8fda2c..2e4f733d8f71 100644 --- a/server/publications/messages.js +++ b/server/publications/messages.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - Meteor.publish('messages', function(rid/* , start*/) { if (!this.userId) { return this.ready(); @@ -24,16 +22,10 @@ Meteor.publish('messages', function(rid/* , start*/) { const cursorHandle = cursor.observeChanges({ added(_id, record) { - record.starred = _.findWhere(record.starred, { - _id: publication.userId, - }); - return publication.added('rocketchat_message', _id, record); + return publication.added('rocketchat_message', _id, RocketChat.composeMessageObjectWithUser(record, publication.userId)); }, changed(_id, record) { - record.starred = _.findWhere(record.starred, { - _id: publication.userId, - }); - return publication.changed('rocketchat_message', _id, record); + return publication.changed('rocketchat_message', _id, RocketChat.composeMessageObjectWithUser(record, publication.userId)); }, }); diff --git a/server/startup/presence.js b/server/startup/presence.js index 4a1b9cf46af3..6bc6029168cc 100644 --- a/server/startup/presence.js +++ b/server/startup/presence.js @@ -14,5 +14,9 @@ Meteor.startup(function() { UserPresence.start(); - return UserPresenceMonitor.start(); + const startMonitor = typeof process.env.DISABLE_PRESENCE_MONITOR === 'undefined' || + !['true', 'yes'].includes(String(process.env.DISABLE_PRESENCE_MONITOR).toLowerCase()); + if (startMonitor) { + UserPresenceMonitor.start(); + } }); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index 10b72f616769..56cd313a6134 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -14,15 +14,44 @@ import { targetUser, log, } from '../../data/api-data.js'; -import { adminEmail, preferences, password } from '../../data/user.js'; +import { adminEmail, preferences, password, adminUsername } from '../../data/user.js'; import { imgURL } from '../../data/interactions.js'; import { customFieldText, clearCustomFields, setCustomFields } from '../../data/custom-fields.js'; +const updateSetting = (setting, value) => new Promise((resolve) => { + request.post(`/api/v1/settings/${ setting }`) + .set(credentials) + .send({ value }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(resolve); +}); + describe('[Users]', function() { this.retries(0); before((done) => getCredentials(done)); + it('enabling E2E in server and generating keys to user...', (done) => { + updateSetting('E2E_Enable', true).then(() => { + request.post(api('e2e.setUserPublicAndPivateKeys')) + .set(credentials) + .send({ + private_key: 'test', + public_key: 'test', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + }); + describe('[/users.create]', () => { before((done) => clearCustomFields(done)); after((done) => clearCustomFields(done)); @@ -48,10 +77,12 @@ describe('[Users]', function() { expect(res.body).to.have.nested.property('user.emails[0].address', apiEmail); expect(res.body).to.have.nested.property('user.active', true); expect(res.body).to.have.nested.property('user.name', apiUsername); + expect(res.body).to.not.have.nested.property('user.e2e'); expect(res.body).to.not.have.nested.property('user.customFields'); targetUser._id = res.body.user._id; + targetUser.username = res.body.user.username; }) .end(done); }); @@ -88,6 +119,7 @@ describe('[Users]', function() { expect(res.body).to.have.nested.property('user.active', true); expect(res.body).to.have.nested.property('user.name', username); expect(res.body).to.have.nested.property('user.customFields.customFieldText', 'success'); + expect(res.body).to.not.have.nested.property('user.e2e'); }) .end(done); }); @@ -136,6 +168,45 @@ describe('[Users]', function() { }); }); + describe('[/users.register]', () => { + const email = `email@email${ Date.now() }.com`; + const username = `myusername${ Date.now() }`; + it('should register new user', (done) => { + request.post(api('users.register')) + .send({ + email, + name: 'name', + username, + pass: 'test', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('user.username', username); + expect(res.body).to.have.nested.property('user.emails[0].address', email); + expect(res.body).to.have.nested.property('user.active', true); + expect(res.body).to.have.nested.property('user.name', 'name'); + }) + .end(done); + }); + it('should return an error when trying register new user with an existing username', (done) => { + request.post(api('users.register')) + .send({ + email, + name: 'name', + username, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error').and.to.be.equal('Username is already in use'); + }) + .end(done); + }); + }); + describe('[/users.info]', () => { it('should query information about a user by userId', (done) => { request.get(api('users.info')) @@ -151,6 +222,7 @@ describe('[Users]', function() { expect(res.body).to.have.nested.property('user.emails[0].address', apiEmail); expect(res.body).to.have.nested.property('user.active', true); expect(res.body).to.have.nested.property('user.name', apiUsername); + expect(res.body).to.not.have.nested.property('user.e2e'); }) .end(done); }); @@ -183,6 +255,8 @@ describe('[Users]', function() { expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('count'); expect(res.body).to.have.property('total'); + const myself = res.body.users.find((user) => user.username === adminUsername); + expect(myself).to.not.have.property('e2e'); }) .end(done); }); @@ -209,10 +283,70 @@ describe('[Users]', function() { }); describe('[/users.setAvatar]', () => { - it.skip('should set the avatar of the auth user', (done) => { + let user; + before((done) => { + const username = `user.test.${ Date.now() }`; + const email = `${ username }@rocket.chat`; + request.post(api('users.create')) + .set(credentials) + .send({ email, name: username, username, password }) + .end((err, res) => { + user = res.body.user; + done(); + }); + }); + + let userCredentials; + before((done) => { + request.post(api('login')) + .send({ + user: user.username, + password, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + userCredentials = {}; + userCredentials['X-Auth-Token'] = res.body.data.authToken; + userCredentials['X-User-Id'] = res.body.data.userId; + }) + .end(done); + }); + after((done) => { + request.post(api('users.delete')).set(credentials).send({ + userId: user._id, + }).end(done); + user = undefined; + }); + + it('should set the avatar of the auth user by a local image', (done) => { + request.post(api('users.setAvatar')) + .set(userCredentials) + .attach('image', imgURL) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should prevent from updating someone else\'s avatar', (done) => { + request.post(api('users.setAvatar')) + .set(userCredentials) + .attach('image', imgURL) + .field({ userId: targetUser._id }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + }) + .end(done); + }); + it('should set the avatar of another user by username and local image', (done) => { request.post(api('users.setAvatar')) .set(credentials) - .attach('avatarUrl', imgURL) + .attach('image', imgURL) + .field({ username: targetUser.username }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { @@ -220,20 +354,21 @@ describe('[Users]', function() { }) .end(done); }); - }); - - describe('[/users.update]', () => { - const updateSetting = (setting, value) => new Promise((resolve) => { - request.post(`/api/v1/settings/${ setting }`) + it('should set the avatar of another user by userId and local image', (done) => { + request.post(api('users.setAvatar')) .set(credentials) - .send({ value }) + .attach('image', imgURL) + .field({ userId: targetUser._id }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { expect(res.body).to.have.property('success', true); }) - .end(resolve); + .end(done); }); + }); + + describe('[/users.update]', () => { const updatePermission = (permission, roles) => new Promise((resolve) => { request.post(api('permissions.update')) .set(credentials) @@ -284,6 +419,7 @@ describe('[Users]', function() { expect(res.body).to.have.nested.property('user.emails[0].address', apiEmail); expect(res.body).to.have.nested.property('user.active', true); expect(res.body).to.have.nested.property('user.name', `edited${ apiUsername }`); + expect(res.body).to.not.have.nested.property('user.e2e'); }) .end(done); }); @@ -303,6 +439,7 @@ describe('[Users]', function() { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('user.emails[0].address', `edited${ apiEmail }`); expect(res.body).to.have.nested.property('user.emails[0].verified', false); + expect(res.body).to.not.have.nested.property('user.e2e'); }) .end(done); }); @@ -321,6 +458,7 @@ describe('[Users]', function() { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('user.emails[0].verified', true); + expect(res.body).to.not.have.nested.property('user.e2e'); }) .end(done); }); @@ -588,6 +726,23 @@ describe('[Users]', function() { const editedName = `basic-info-test-name${ +new Date() }`; const editedEmail = `test${ +new Date() }@mail.com`; + it('enabling E2E in server and generating keys to user...', (done) => { + updateSetting('E2E_Enable', true).then(() => { + request.post(api('e2e.setUserPublicAndPivateKeys')) + .set(userCredentials) + .send({ + private_key: 'test', + public_key: 'test', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + }); + it('should update the user own basic information', (done) => { request.post(api('users.updateOwnBasicInfo')) .set(userCredentials) @@ -606,6 +761,7 @@ describe('[Users]', function() { expect(res.body).to.have.property('success', true); expect(user.username).to.be.equal(editedUsername); expect(user.name).to.be.equal(editedName); + expect(user).to.not.have.property('e2e'); }) .end(done); }); @@ -624,6 +780,7 @@ describe('[Users]', function() { const { user } = res.body; expect(res.body).to.have.property('success', true); expect(user.username).to.be.equal(editedUsername); + expect(user).to.not.have.property('e2e'); }) .end(done); }); @@ -686,6 +843,7 @@ describe('[Users]', function() { expect(res.body).to.have.property('success', true); expect(user.emails[0].address).to.be.equal(editedEmail); expect(user.emails[0].verified).to.be.false; + expect(user).to.not.have.property('e2e'); }) .end(done); }); @@ -998,7 +1156,7 @@ describe('[Users]', function() { }) .end(resolve); }); - const testUsername = `testuser${ +new Date() }`; + const testUsername = `testuserdelete${ +new Date() }`; let targetUser; it('register a new user...', (done) => { request.post(api('users.register')) diff --git a/tests/end-to-end/api/02-channels.js b/tests/end-to-end/api/02-channels.js index 4cbd2cddb5f9..30fe33e61ff2 100644 --- a/tests/end-to-end/api/02-channels.js +++ b/tests/end-to-end/api/02-channels.js @@ -15,7 +15,7 @@ import { apiPublicChannelName, channel, } from '../../data/api-data.js'; -import { adminEmail, password } from '../../data/user.js'; +import { adminEmail, password, adminUsername } from '../../data/user.js'; import supertest from 'supertest'; function getRoomInfo(roomId) { @@ -55,22 +55,135 @@ describe('[Channels]', function() { .end(done); }); - it('/channels.info', (done) => { - request.get(api('channels.info')) - .set(credentials) - .query({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', 0); - }) - .end(done); + describe('[/channels.info]', () => { + let testChannel = {}; + let channelMessage = {}; + it('creating new channel...', (done) => { + request.post(api('channels.create')) + .set(credentials) + .send({ + name: apiPublicChannelName, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testChannel = res.body.channel; + }) + .end(done); + }); + it('should return channel basic structure', (done) => { + request.get(api('channels.info')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', 0); + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testChannel._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + channelMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return channel structure with "lastMessage" object including pin, reaction and star(should be an array) infos', (done) => { + request.get(api('channels.info')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('channel').and.to.be.an('object'); + const { channel } = res.body; + expect(channel).to.have.property('lastMessage').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('reactions').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('pinned').and.to.be.a('boolean'); + expect(channel.lastMessage).to.have.property('pinnedAt').and.to.be.a('string'); + expect(channel.lastMessage).to.have.property('pinnedBy').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('starred').and.to.be.an('array'); + }) + .end(done); + }); + it('should return all channels messages where the last message of array should have the "star" array with USERS star ONLY', (done) => { + request.get(api('channels.messages')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === channelMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('array'); + expect(lastMessage.starred[0]._id).to.be.equal(adminUsername); + }) + .end(done); + }); }); it('/channels.invite', async(done) => { diff --git a/tests/end-to-end/api/03-groups.js b/tests/end-to-end/api/03-groups.js index 9594ca5390c6..b03ab6a638e1 100644 --- a/tests/end-to-end/api/03-groups.js +++ b/tests/end-to-end/api/03-groups.js @@ -5,6 +5,7 @@ import { getCredentials, api, login, request, credentials, group, log, apiPrivateChannelName } from '../../data/api-data.js'; import { adminEmail, password } from '../../data/user.js'; import supertest from 'supertest'; +import { adminUsername } from '../../data/user'; function getRoomInfo(roomId) { return new Promise((resolve/* , reject*/) => { @@ -43,22 +44,135 @@ describe('[Groups]', function() { .end(done); }); - it('/groups.info', (done) => { - request.get(api('groups.info')) - .set(credentials) - .query({ - roomId: group._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('group._id'); - expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName); - expect(res.body).to.have.nested.property('group.t', 'p'); - expect(res.body).to.have.nested.property('group.msgs', 0); - }) - .end(done); + describe('[/groups.info]', () => { + let testGroup = {}; + let groupMessage = {}; + it('creating new group...', (done) => { + request.post(api('groups.create')) + .set(credentials) + .send({ + name: apiPrivateChannelName, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testGroup = res.body.group; + }) + .end(done); + }); + it('should return group basic structure', (done) => { + request.get(api('groups.info')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('group._id'); + expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName); + expect(res.body).to.have.nested.property('group.t', 'p'); + expect(res.body).to.have.nested.property('group.msgs', 0); + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testGroup._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + groupMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return group structure with "lastMessage" object including pin, reaction and star(should be an array) infos', (done) => { + request.get(api('groups.info')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('group').and.to.be.an('object'); + const { group } = res.body; + expect(group).to.have.property('lastMessage').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('reactions').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('pinned').and.to.be.a('boolean'); + expect(group.lastMessage).to.have.property('pinnedAt').and.to.be.a('string'); + expect(group.lastMessage).to.have.property('pinnedBy').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('starred').and.to.be.an('array'); + }) + .end(done); + }); + it('should return all groups messages where the last message of array should have the "star" array with USERS star ONLY', (done) => { + request.get(api('groups.messages')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === groupMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('array'); + expect(lastMessage.starred[0]._id).to.be.equal(adminUsername); + }) + .end(done); + }); }); it('/groups.invite', async(done) => { diff --git a/tests/end-to-end/api/04-direct-message.js b/tests/end-to-end/api/04-direct-message.js index 4b4a0ea8d215..f22b7f290215 100644 --- a/tests/end-to-end/api/04-direct-message.js +++ b/tests/end-to-end/api/04-direct-message.js @@ -2,9 +2,20 @@ /* globals expect */ /* eslint no-unused-vars: 0 */ -import { getCredentials, api, login, request, credentials, directMessage, log, apiUsername, apiEmail } from '../../data/api-data.js'; +import { + getCredentials, + api, + login, + request, + credentials, + directMessage, + log, + apiUsername, + apiEmail, +} from '../../data/api-data.js'; import { adminEmail, password } from '../../data/user.js'; import supertest from 'supertest'; +import { adminUsername } from '../../data/user'; describe('[Direct Messages]', function() { this.retries(0); @@ -44,6 +55,99 @@ describe('[Direct Messages]', function() { .end(done); }); + describe('Testing DM info', () => { + let testDM = {}; + let dmMessage = {}; + it('creating new DM...', (done) => { + request.post(api('im.create')) + .set(credentials) + .send({ + username: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testDM = res.body.room; + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testDM._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + dmMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return all DM messages where the last message of array should have the "star" array with USERS star ONLY', (done) => { + request.get(api('im.messages')) + .set(credentials) + .query({ + roomId: testDM._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === dmMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('array'); + expect(lastMessage.starred[0]._id).to.be.equal(adminUsername); + }) + .end(done); + }); + }); + it('/im.history', (done) => { request.get(api('im.history')) .set(credentials)