diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index e7bb1da9be9..681f04429a2 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -1,6 +1,6 @@
FROM rocketchat/base:8
-ENV RC_VERSION 0.62.0-develop
+ENV RC_VERSION 0.63.0-develop
MAINTAINER buildmaster@rocket.chat
diff --git a/.eslintignore b/.eslintignore
index 984ba1d5dc3..4c21ff058f7 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -18,4 +18,5 @@ packages/rocketchat-videobridge/client/public/external_api.js
packages/rocketchat-theme/client/vendor/
private/moment-locales/
public/livechat/
-public/recorderWorker.js
+public/mp3-realtime-worker.js
+public/lame.min.js
diff --git a/.openshift/rocket-chat-ephemeral.json b/.openshift/rocket-chat-ephemeral.json
index 7b924089f56..2c5cd26e68f 100644
--- a/.openshift/rocket-chat-ephemeral.json
+++ b/.openshift/rocket-chat-ephemeral.json
@@ -151,7 +151,7 @@
"name": "rocketchat"
},
"spec": {
- "dockerImageRepository": "registry.connect.redhat.com/rocketchat",
+ "dockerImageRepository": "registry.connect.redhat.com/rocketchat/rocketchat",
"tags": [
{
"name": "latest",
@@ -208,7 +208,6 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "${NAMESPACE}",
"name": "rocketchat:latest"
}
}
@@ -395,12 +394,6 @@
"description": "Maximum amount of memory the container can use.",
"value": "512Mi"
},
- {
- "name": "NAMESPACE",
- "displayName": "Namespace",
- "description": "The OpenShift Namespace where the ImageStream resides.",
- "value": "openshift"
- },
{
"name": "DATABASE_SERVICE_NAME",
"displayName": "Database Service Name",
@@ -428,7 +421,7 @@
"name": "MONGODB_DATABASE",
"displayName": "MongoDB Database Name",
"description": "Name of the MongoDB database accessed.",
- "value": "sampledb",
+ "value": "rocketchatdb",
"required": true
},
{
diff --git a/.openshift/rocket-chat-persistent.json b/.openshift/rocket-chat-persistent.json
index dabd4d81c0d..150347e5165 100644
--- a/.openshift/rocket-chat-persistent.json
+++ b/.openshift/rocket-chat-persistent.json
@@ -228,7 +228,6 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "${NAMESPACE}",
"name": "rocketchat:latest"
}
}
@@ -415,12 +414,6 @@
"description": "Maximum amount of memory the container can use.",
"value": "512Mi"
},
- {
- "name": "NAMESPACE",
- "displayName": "Namespace",
- "description": "The OpenShift Namespace where the ImageStream resides.",
- "value": "openshift"
- },
{
"name": "DATABASE_SERVICE_NAME",
"displayName": "Database Service Name",
@@ -448,7 +441,7 @@
"name": "MONGODB_DATABASE",
"displayName": "MongoDB Database Name",
"description": "Name of the MongoDB database accessed.",
- "value": "sampledb",
+ "value": "rocketchatdb",
"required": true
},
{
diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp
index b720f3c27dd..1f51bd69210 100644
--- a/.sandstorm/sandstorm-pkgdef.capnp
+++ b/.sandstorm/sandstorm-pkgdef.capnp
@@ -21,7 +21,7 @@ const pkgdef :Spk.PackageDefinition = (
appVersion = 62, # Increment this for every release.
- appMarketingVersion = (defaultText = "0.62.0-develop"),
+ appMarketingVersion = (defaultText = "0.63.0-develop"),
# 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 38e016c2fae..e663baeb15d 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.62.0-develop
+ RC_VERSION=0.63.0-develop
fi
echo "Preparing to trigger a snap release for $CHANNEL channel"
diff --git a/HISTORY.md b/HISTORY.md
index b8fb45f406f..1e6c26b34e9 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,3 +1,265 @@
+
+# 0.62.0 (2018-02-28)
+
+
+### BREAKING CHANGES
+
+- [#9711](https://github.com/RocketChat/Rocket.Chat/pull/9711) Remove Graphics/Image Magick support
+
+
+### New Features
+
+- [#9549](https://github.com/RocketChat/Rocket.Chat/pull/9549) Add route to get user shield/badge
+- [#9457](https://github.com/RocketChat/Rocket.Chat/pull/9457) Add user settings / preferences API endpoint
+- [#7098](https://github.com/RocketChat/Rocket.Chat/pull/7098) Alert admins when user requires approval & alert users when the account is approved/activated/deactivated
+- [#9527](https://github.com/RocketChat/Rocket.Chat/pull/9527) Allow configuration of SAML logout behavior
+- [#8193](https://github.com/RocketChat/Rocket.Chat/pull/8193) Allow request avatar placeholders as PNG or JPG instead of SVG
+- [#9312](https://github.com/RocketChat/Rocket.Chat/pull/9312) Allow sounds when conversation is focused
+- [#9519](https://github.com/RocketChat/Rocket.Chat/pull/9519) API to fetch permissions & user roles
+- [#9642](https://github.com/RocketChat/Rocket.Chat/pull/9642) Browse more channels / Directory
+- [#9778](https://github.com/RocketChat/Rocket.Chat/pull/9778) General alert banner
+- [#9687](https://github.com/RocketChat/Rocket.Chat/pull/9687) Global message search (beta: disabled by default)
+- [#8158](https://github.com/RocketChat/Rocket.Chat/pull/8158) GraphQL API
+- [#9298](https://github.com/RocketChat/Rocket.Chat/pull/9298) Improved default welcome message
+- [#8933](https://github.com/RocketChat/Rocket.Chat/pull/8933) Internal hubot support for Direct Messages and Private Groups
+- [#9255](https://github.com/RocketChat/Rocket.Chat/pull/9255) Livestream tab
+- [#9746](https://github.com/RocketChat/Rocket.Chat/pull/9746) Makes shield icon configurable
+- [#9717](https://github.com/RocketChat/Rocket.Chat/pull/9717) Message read receipts
+- [#9507](https://github.com/RocketChat/Rocket.Chat/pull/9507) New REST API to mark channel as read
+- [#9608](https://github.com/RocketChat/Rocket.Chat/pull/9608) New sidebar layout
+- [#9699](https://github.com/RocketChat/Rocket.Chat/pull/9699) Option to proxy files and avatars through the server
+- [#9509](https://github.com/RocketChat/Rocket.Chat/pull/9509) REST API to use Spotlight
+- [#9793](https://github.com/RocketChat/Rocket.Chat/pull/9793) Version update check
+- [#9934](https://github.com/RocketChat/Rocket.Chat/pull/9934) Typo on french translation for "Open"
+
+
+### Bug Fixes
+
+- [#9424](https://github.com/RocketChat/Rocket.Chat/pull/9424) 'Query' support for channels.list.joined, groups.list, groups.listAll, im.list
+- [#9737](https://github.com/RocketChat/Rocket.Chat/pull/9737) API to retrive rooms was returning empty objects
+- [#9487](https://github.com/RocketChat/Rocket.Chat/pull/9487) Chat Message Reactions REST API End Point
+- [#9560](https://github.com/RocketChat/Rocket.Chat/pull/9560) Chrome 64 breaks jitsi-meet iframe
+- [#9662](https://github.com/RocketChat/Rocket.Chat/pull/9662) Close button on file upload bar was not working
+- [#9714](https://github.com/RocketChat/Rocket.Chat/pull/9714) Close Livechat conversation by visitor not working in version 0.61.0
+- [#9676](https://github.com/RocketChat/Rocket.Chat/pull/9676) Custom emoji was cropping sometimes
+- [#9747](https://github.com/RocketChat/Rocket.Chat/pull/9747) DeprecationWarning: prom-client ... when starting Rocket Chat server
+- [#9639](https://github.com/RocketChat/Rocket.Chat/pull/9639) Desktop notification not showing when avatar came from external storage service
+- [#9776](https://github.com/RocketChat/Rocket.Chat/pull/9776) Emoji rendering on last message
+- [#9640](https://github.com/RocketChat/Rocket.Chat/pull/9640) Facebook integration in livechat not working on version 0.61.0
+- [#9067](https://github.com/RocketChat/Rocket.Chat/pull/9067) Formal pronouns and some small mistakes in German texts
+- [#9716](https://github.com/RocketChat/Rocket.Chat/pull/9716) GitLab OAuth does not work when GitLab’s URL ends with slash
+- [#9697](https://github.com/RocketChat/Rocket.Chat/pull/9697) Harmonize channel-related actions
+- [#9772](https://github.com/RocketChat/Rocket.Chat/pull/9772) Livechat conversation not receiving messages when start without form
+- [#9599](https://github.com/RocketChat/Rocket.Chat/pull/9599) Livechat is not working when running in a sub path
+- [#9750](https://github.com/RocketChat/Rocket.Chat/pull/9750) Livechat issues on external queue and lead capture
+- [#9720](https://github.com/RocketChat/Rocket.Chat/pull/9720) Messages can't be quoted sometimes
+- [#9454](https://github.com/RocketChat/Rocket.Chat/pull/9454) Missing link Site URLs in enrollment e-mails
+- [#9610](https://github.com/RocketChat/Rocket.Chat/pull/9610) Missing string 'Username_already_exist' on the accountProfile page
+- [#9520](https://github.com/RocketChat/Rocket.Chat/pull/9520) Rest API helpers only applying to v1
+- [#9696](https://github.com/RocketChat/Rocket.Chat/pull/9696) Show custom room types icon in channel header
+- [#9570](https://github.com/RocketChat/Rocket.Chat/pull/9570) SVG avatars are not been displayed correctly when load in non HTML containers
+- [#9623](https://github.com/RocketChat/Rocket.Chat/pull/9623) Weird rendering of emojis at sidebar when `last message` is activated
+- [#9665](https://github.com/RocketChat/Rocket.Chat/pull/9665) Wrong behavior of rooms info's *Read Only* and *Collaborative* buttons
+- [#9802](https://github.com/RocketChat/Rocket.Chat/pull/9802) Not receiving sound notifications in rooms created by new LiveChats
+- [#9858](https://github.com/RocketChat/Rocket.Chat/pull/9858) Silence the update check error message
+- [#9850](https://github.com/RocketChat/Rocket.Chat/pull/9850) Importers no longer working due to the FileUpload changes
+- [#9888](https://github.com/RocketChat/Rocket.Chat/pull/9888) Misplaced "Save Changes" button in user account panel
+- [#9877](https://github.com/RocketChat/Rocket.Chat/pull/9877) Not Translated Phrases
+- [#9884](https://github.com/RocketChat/Rocket.Chat/pull/9884) Parsing messages with multiple markdown matches ignore some tokens
+- [#9879](https://github.com/RocketChat/Rocket.Chat/pull/9879) Snap build was failing
+
+
+
+Others
+
+- [#9218](https://github.com/RocketChat/Rocket.Chat/pull/9218) [NEW] Image preview as 32x32 base64 jpeg
+- [#9753](https://github.com/RocketChat/Rocket.Chat/pull/9753) Move NRR package to inside the project and convert from CoffeeScript
+- [#9666](https://github.com/RocketChat/Rocket.Chat/pull/9666) Rocket.Chat Apps
+- [#9796](https://github.com/RocketChat/Rocket.Chat/pull/9796) Sync from Master
+- [#9546](https://github.com/RocketChat/Rocket.Chat/pull/9546) Update to meteor 1.6.1
+- [#9811](https://github.com/RocketChat/Rocket.Chat/pull/9811) Dependencies update
+- [#9797](https://github.com/RocketChat/Rocket.Chat/pull/9797) Develop fix sync from master
+- [#9821](https://github.com/RocketChat/Rocket.Chat/pull/9821) Fix: Custom fields not showing on user info panel
+- [#9843](https://github.com/RocketChat/Rocket.Chat/pull/9843) Regression: Avatar now open account related options
+- [#9837](https://github.com/RocketChat/Rocket.Chat/pull/9837) Regression: Open search using ctrl/cmd + p and ctrl/cmd + k
+- [#9804](https://github.com/RocketChat/Rocket.Chat/pull/9804) Regression: Page was not respecting the window height on Firefox
+- [#9839](https://github.com/RocketChat/Rocket.Chat/pull/9839) Regression: Search bar is now full width
+- [#9851](https://github.com/RocketChat/Rocket.Chat/pull/9851) Regression: Change create channel icon
+- [#9845](https://github.com/RocketChat/Rocket.Chat/pull/9845) Regression: Fix admin/user settings item text
+- [#9852](https://github.com/RocketChat/Rocket.Chat/pull/9852) Regression: Fix channel icons on safari
+- [#9902](https://github.com/RocketChat/Rocket.Chat/pull/9902) Fix Apps not working on multi-instance deployments
+- [#9905](https://github.com/RocketChat/Rocket.Chat/pull/9905) Regression: Improve sidebar filter
+- [#9889](https://github.com/RocketChat/Rocket.Chat/pull/9889) Regression: Overlapping header in user profile panel
+- [#9897](https://github.com/RocketChat/Rocket.Chat/pull/9897) Regression: sort on room's list not working correctly
+- [#9908](https://github.com/RocketChat/Rocket.Chat/pull/9908) Improve link handling for attachments
+- [#9931](https://github.com/RocketChat/Rocket.Chat/pull/9931) Regression: Directory now list default channel
+- [#9928](https://github.com/RocketChat/Rocket.Chat/pull/9928) Regression: Fix livechat queue link
+- [#9883](https://github.com/RocketChat/Rocket.Chat/pull/9883) Regression: Misplaced language dropdown in user preferences panel
+
+
+
+
+
+
+Detils
+
+## 0.62.0 (2018-02-28)
+
+
+### Bug Fixes
+
+- [#9934](https://github.com/RocketChat/Rocket.Chat/pull/9934) Typo on french translation for "Open"
+
+
+
+Others
+
+- [#9908](https://github.com/RocketChat/Rocket.Chat/pull/9908) Improve link handling for attachments
+- [#9931](https://github.com/RocketChat/Rocket.Chat/pull/9931) Regression: Directory now list default channel
+- [#9928](https://github.com/RocketChat/Rocket.Chat/pull/9928) Regression: Fix livechat queue link
+- [#9883](https://github.com/RocketChat/Rocket.Chat/pull/9883) Regression: Misplaced language dropdown in user preferences panel
+
+
+
+
+## 0.62.0-rc.3 (2018-02-27)
+
+
+### Bug Fixes
+
+- [#9850](https://github.com/RocketChat/Rocket.Chat/pull/9850) Importers no longer working due to the FileUpload changes
+- [#9888](https://github.com/RocketChat/Rocket.Chat/pull/9888) Misplaced "Save Changes" button in user account panel
+- [#9877](https://github.com/RocketChat/Rocket.Chat/pull/9877) Not Translated Phrases
+- [#9884](https://github.com/RocketChat/Rocket.Chat/pull/9884) Parsing messages with multiple markdown matches ignore some tokens
+- [#9879](https://github.com/RocketChat/Rocket.Chat/pull/9879) Snap build was failing
+
+
+
+Others
+
+- [#9902](https://github.com/RocketChat/Rocket.Chat/pull/9902) Fix Apps not working on multi-instance deployments
+- [#9905](https://github.com/RocketChat/Rocket.Chat/pull/9905) Regression: Improve sidebar filter
+- [#9889](https://github.com/RocketChat/Rocket.Chat/pull/9889) Regression: Overlapping header in user profile panel
+- [#9897](https://github.com/RocketChat/Rocket.Chat/pull/9897) Regression: sort on room's list not working correctly
+
+
+
+
+## 0.62.0-rc.2 (2018-02-23)
+
+
+### Bug Fixes
+
+- [#9858](https://github.com/RocketChat/Rocket.Chat/pull/9858) Silence the update check error message
+
+
+
+Others
+
+- [#9851](https://github.com/RocketChat/Rocket.Chat/pull/9851) Regression: Change create channel icon
+- [#9845](https://github.com/RocketChat/Rocket.Chat/pull/9845) Regression: Fix admin/user settings item text
+- [#9852](https://github.com/RocketChat/Rocket.Chat/pull/9852) Regression: Fix channel icons on safari
+
+
+
+
+## 0.62.0-rc.1 (2018-02-22)
+
+
+### Bug Fixes
+
+- [#9802](https://github.com/RocketChat/Rocket.Chat/pull/9802) Not receiving sound notifications in rooms created by new LiveChats
+
+
+
+Others
+
+- [#9811](https://github.com/RocketChat/Rocket.Chat/pull/9811) Dependencies update
+- [#9797](https://github.com/RocketChat/Rocket.Chat/pull/9797) Develop fix sync from master
+- [#9821](https://github.com/RocketChat/Rocket.Chat/pull/9821) Fix: Custom fields not showing on user info panel
+- [#9843](https://github.com/RocketChat/Rocket.Chat/pull/9843) Regression: Avatar now open account related options
+- [#9837](https://github.com/RocketChat/Rocket.Chat/pull/9837) Regression: Open search using ctrl/cmd + p and ctrl/cmd + k
+- [#9804](https://github.com/RocketChat/Rocket.Chat/pull/9804) Regression: Page was not respecting the window height on Firefox
+- [#9839](https://github.com/RocketChat/Rocket.Chat/pull/9839) Regression: Search bar is now full width
+
+
+
+
+## 0.62.0-rc.0 (2018-02-21)
+
+
+### BREAKING CHANGES
+
+- [#9711](https://github.com/RocketChat/Rocket.Chat/pull/9711) Remove Graphics/Image Magick support
+
+
+### New Features
+
+- [#9549](https://github.com/RocketChat/Rocket.Chat/pull/9549) Add route to get user shield/badge
+- [#9457](https://github.com/RocketChat/Rocket.Chat/pull/9457) Add user settings / preferences API endpoint
+- [#7098](https://github.com/RocketChat/Rocket.Chat/pull/7098) Alert admins when user requires approval & alert users when the account is approved/activated/deactivated
+- [#9527](https://github.com/RocketChat/Rocket.Chat/pull/9527) Allow configuration of SAML logout behavior
+- [#8193](https://github.com/RocketChat/Rocket.Chat/pull/8193) Allow request avatar placeholders as PNG or JPG instead of SVG
+- [#9312](https://github.com/RocketChat/Rocket.Chat/pull/9312) Allow sounds when conversation is focused
+- [#9519](https://github.com/RocketChat/Rocket.Chat/pull/9519) API to fetch permissions & user roles
+- [#9642](https://github.com/RocketChat/Rocket.Chat/pull/9642) Browse more channels / Directory
+- [#9778](https://github.com/RocketChat/Rocket.Chat/pull/9778) General alert banner
+- [#9687](https://github.com/RocketChat/Rocket.Chat/pull/9687) Global message search (beta: disabled by default)
+- [#8158](https://github.com/RocketChat/Rocket.Chat/pull/8158) GraphQL API
+- [#9298](https://github.com/RocketChat/Rocket.Chat/pull/9298) Improved default welcome message
+- [#8933](https://github.com/RocketChat/Rocket.Chat/pull/8933) Internal hubot support for Direct Messages and Private Groups
+- [#9255](https://github.com/RocketChat/Rocket.Chat/pull/9255) Livestream tab
+- [#9746](https://github.com/RocketChat/Rocket.Chat/pull/9746) Makes shield icon configurable
+- [#9717](https://github.com/RocketChat/Rocket.Chat/pull/9717) Message read receipts
+- [#9507](https://github.com/RocketChat/Rocket.Chat/pull/9507) New REST API to mark channel as read
+- [#9608](https://github.com/RocketChat/Rocket.Chat/pull/9608) New sidebar layout
+- [#9699](https://github.com/RocketChat/Rocket.Chat/pull/9699) Option to proxy files and avatars through the server
+- [#9509](https://github.com/RocketChat/Rocket.Chat/pull/9509) REST API to use Spotlight
+- [#9793](https://github.com/RocketChat/Rocket.Chat/pull/9793) Version update check
+
+
+### Bug Fixes
+
+- [#9424](https://github.com/RocketChat/Rocket.Chat/pull/9424) 'Query' support for channels.list.joined, groups.list, groups.listAll, im.list
+- [#9737](https://github.com/RocketChat/Rocket.Chat/pull/9737) API to retrive rooms was returning empty objects
+- [#9487](https://github.com/RocketChat/Rocket.Chat/pull/9487) Chat Message Reactions REST API End Point
+- [#9560](https://github.com/RocketChat/Rocket.Chat/pull/9560) Chrome 64 breaks jitsi-meet iframe
+- [#9662](https://github.com/RocketChat/Rocket.Chat/pull/9662) Close button on file upload bar was not working
+- [#9714](https://github.com/RocketChat/Rocket.Chat/pull/9714) Close Livechat conversation by visitor not working in version 0.61.0
+- [#9676](https://github.com/RocketChat/Rocket.Chat/pull/9676) Custom emoji was cropping sometimes
+- [#9747](https://github.com/RocketChat/Rocket.Chat/pull/9747) DeprecationWarning: prom-client ... when starting Rocket Chat server
+- [#9639](https://github.com/RocketChat/Rocket.Chat/pull/9639) Desktop notification not showing when avatar came from external storage service
+- [#9776](https://github.com/RocketChat/Rocket.Chat/pull/9776) Emoji rendering on last message
+- [#9640](https://github.com/RocketChat/Rocket.Chat/pull/9640) Facebook integration in livechat not working on version 0.61.0
+- [#9067](https://github.com/RocketChat/Rocket.Chat/pull/9067) Formal pronouns and some small mistakes in German texts
+- [#9716](https://github.com/RocketChat/Rocket.Chat/pull/9716) GitLab OAuth does not work when GitLab’s URL ends with slash
+- [#9697](https://github.com/RocketChat/Rocket.Chat/pull/9697) Harmonize channel-related actions
+- [#9772](https://github.com/RocketChat/Rocket.Chat/pull/9772) Livechat conversation not receiving messages when start without form
+- [#9599](https://github.com/RocketChat/Rocket.Chat/pull/9599) Livechat is not working when running in a sub path
+- [#9750](https://github.com/RocketChat/Rocket.Chat/pull/9750) Livechat issues on external queue and lead capture
+- [#9720](https://github.com/RocketChat/Rocket.Chat/pull/9720) Messages can't be quoted sometimes
+- [#9454](https://github.com/RocketChat/Rocket.Chat/pull/9454) Missing link Site URLs in enrollment e-mails
+- [#9610](https://github.com/RocketChat/Rocket.Chat/pull/9610) Missing string 'Username_already_exist' on the accountProfile page
+- [#9520](https://github.com/RocketChat/Rocket.Chat/pull/9520) Rest API helpers only applying to v1
+- [#9696](https://github.com/RocketChat/Rocket.Chat/pull/9696) Show custom room types icon in channel header
+- [#9570](https://github.com/RocketChat/Rocket.Chat/pull/9570) SVG avatars are not been displayed correctly when load in non HTML containers
+- [#9623](https://github.com/RocketChat/Rocket.Chat/pull/9623) Weird rendering of emojis at sidebar when `last message` is activated
+- [#9665](https://github.com/RocketChat/Rocket.Chat/pull/9665) Wrong behavior of rooms info's *Read Only* and *Collaborative* buttons
+
+
+
+Others
+
+- [#9218](https://github.com/RocketChat/Rocket.Chat/pull/9218) [NEW] Image preview as 32x32 base64 jpeg
+- [#9753](https://github.com/RocketChat/Rocket.Chat/pull/9753) Move NRR package to inside the project and convert from CoffeeScript
+- [#9666](https://github.com/RocketChat/Rocket.Chat/pull/9666) Rocket.Chat Apps
+- [#9796](https://github.com/RocketChat/Rocket.Chat/pull/9796) Sync from Master
+- [#9546](https://github.com/RocketChat/Rocket.Chat/pull/9546) Update to meteor 1.6.1
+
+
+
+
+
## 0.61.2 (2018-02-20)
diff --git a/README.md b/README.md
index 1dc6c976996..7478c27dc04 100644
--- a/README.md
+++ b/README.md
@@ -363,7 +363,11 @@ Read about [how it all started](https://blog.blackducksoftware.com/rocket-chat-e
## Issues
-[Github Issues](https://github.com/RocketChat/Rocket.Chat/issues) are used to track todos, bugs, feature requests, and more.
+[Github Issues](https://github.com/RocketChat/Rocket.Chat/issues) are used to track bugs and tasks on the roadmap.
+
+## Feature Requests
+
+[Feature Request Forums](https://forums.rocket.chat/c/feature-requests) are used to suggest, discuss and upvote feature suggestions.
### Stack Overflow
diff --git a/client/routes/router.js b/client/routes/router.js
index d75f07595f0..67d0f4870e5 100644
--- a/client/routes/router.js
+++ b/client/routes/router.js
@@ -95,19 +95,6 @@ FlowRouter.route('/account/:group?', {
}]
});
-FlowRouter.route('/history/private', {
- name: 'privateHistory',
-
- subscriptions(/*params, queryParams*/) {
- this.register('privateHistory', Meteor.subscribe('privateHistory'));
- },
-
- action() {
- Session.setDefault('historyFilter', '');
- BlazeLayout.render('main', {center: 'privateHistory'});
- }
-});
-
FlowRouter.route('/terms-of-service', {
name: 'terms-of-service',
diff --git a/imports/message-read-receipt/server/hooks.js b/imports/message-read-receipt/server/hooks.js
index 560ebf449ac..4039271580e 100644
--- a/imports/message-read-receipt/server/hooks.js
+++ b/imports/message-read-receipt/server/hooks.js
@@ -2,6 +2,11 @@ import { ReadReceipt } from './lib/ReadReceipt';
RocketChat.callbacks.add('afterSaveMessage', (message, room) => {
+ // skips this callback if the message was edited
+ if (message.editedAt) {
+ return message;
+ }
+
// set subscription as read right after message was sent
RocketChat.models.Subscriptions.setAsReadByRoomIdAndUserId(room._id, message.u._id);
diff --git a/package-lock.json b/package-lock.json
index 0baedf23ed5..e0d1bf19c4a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12521,6 +12521,16 @@
}
}
},
+ "twit": {
+ "version": "2.2.9",
+ "resolved": "https://registry.npmjs.org/twit/-/twit-2.2.9.tgz",
+ "integrity": "sha1-ZxBXT4FkHaoDeWobS457eNPXVnY=",
+ "requires": {
+ "bluebird": "3.5.1",
+ "mime": "1.6.0",
+ "request": "2.83.0"
+ }
+ },
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/package.json b/package.json
index 33e6ce72f89..df3226fdaf6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "Rocket.Chat",
"description": "The Ultimate Open Source WebChat Platform",
- "version": "0.62.0-develop",
+ "version": "0.63.0-develop",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
@@ -166,6 +166,7 @@
"tar-stream": "^1.5.5",
"toastr": "^2.1.4",
"twilio": "^2.9.1",
+ "twit": "^2.2.9",
"ua-parser-js": "^0.7.17",
"underscore": "^1.8.3",
"underscore.string": "^3.3.4",
diff --git a/packages/rocketchat-2fa/server/lib/totp.js b/packages/rocketchat-2fa/server/lib/totp.js
index bf81dd043cd..54b4f579f2d 100644
--- a/packages/rocketchat-2fa/server/lib/totp.js
+++ b/packages/rocketchat-2fa/server/lib/totp.js
@@ -13,30 +13,39 @@ RocketChat.TOTP = {
},
verify({ secret, token, backupTokens, userId }) {
- let verified;
-
// validates a backup code
if (token.length === 8 && backupTokens) {
const hashedCode = SHA256(token);
const usedCode = backupTokens.indexOf(hashedCode);
if (usedCode !== -1) {
- verified = true;
-
backupTokens.splice(usedCode, 1);
// mark the code as used (remove it from the list)
RocketChat.models.Users.update2FABackupCodesByUserId(userId, backupTokens);
+ return true;
}
- } else {
- verified = speakeasy.totp.verify({
+
+ return false;
+ }
+
+ const maxDelta = RocketChat.settings.get('Accounts_TwoFactorAuthentication_MaxDelta');
+ if (maxDelta) {
+ const verifiedDelta = speakeasy.totp.verifyDelta({
secret,
encoding: 'base32',
- token
+ token,
+ window: maxDelta
});
+
+ return verifiedDelta !== undefined;
}
- return verified;
+ return speakeasy.totp.verify({
+ secret,
+ encoding: 'base32',
+ token
+ });
},
generateCodes() {
diff --git a/packages/rocketchat-api/package.js b/packages/rocketchat-api/package.js
index df6d1b9b3ab..5b0d5ffa53c 100644
--- a/packages/rocketchat-api/package.js
+++ b/packages/rocketchat-api/package.js
@@ -33,6 +33,7 @@ Package.onUse(function(api) {
api.addFiles('server/v1/subscriptions.js', 'server');
api.addFiles('server/v1/chat.js', 'server');
api.addFiles('server/v1/commands.js', 'server');
+ api.addFiles('server/v1/emoji-custom.js', 'server');
api.addFiles('server/v1/groups.js', 'server');
api.addFiles('server/v1/im.js', 'server');
api.addFiles('server/v1/integrations.js', 'server');
diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js
index c8dd2905385..ef07da8d1df 100644
--- a/packages/rocketchat-api/server/v1/channels.js
+++ b/packages/rocketchat-api/server/v1/channels.js
@@ -330,7 +330,14 @@ RocketChat.API.v1.addRoute('channels.history', { authRequired: true }, {
let result;
Meteor.runAsUser(this.userId, () => {
- result = Meteor.call('getChannelHistory', { rid: findResult._id, latest: latestDate, oldest: oldestDate, inclusive, count, unreads });
+ result = Meteor.call('getChannelHistory', {
+ rid: findResult._id,
+ latest: latestDate,
+ oldest: oldestDate,
+ inclusive,
+ count,
+ unreads
+ });
});
if (!result) {
@@ -417,15 +424,15 @@ RocketChat.API.v1.addRoute('channels.list', { authRequired: true }, {
action() {
const { offset, count } = this.getPaginationItems();
const { sort, fields, query } = this.parseJsonQuery();
+ const hasPermissionToSeeAllPublicChannels = RocketChat.authz.hasPermission(this.userId, 'view-c-room');
const ourQuery = Object.assign({}, query, { t: 'c' });
- //Special check for the permissions
- if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room')) {
+ if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room') && !hasPermissionToSeeAllPublicChannels) {
ourQuery.usernames = {
- $in: [ this.user.username ]
+ $in: [this.user.username]
};
- } else if (!RocketChat.authz.hasPermission(this.userId, 'view-c-room')) {
+ } else if (!hasPermissionToSeeAllPublicChannels) {
return RocketChat.API.v1.unauthorized();
}
@@ -476,7 +483,11 @@ RocketChat.API.v1.addRoute('channels.list.joined', { authRequired: true }, {
RocketChat.API.v1.addRoute('channels.members', { authRequired: true }, {
get() {
- const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false, returnUsernames: true });
+ const findResult = findChannelByIdOrName({
+ params: this.requestParams(),
+ checkedArchived: false,
+ returnUsernames: true
+ });
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();
@@ -625,7 +636,7 @@ RocketChat.API.v1.addRoute('channels.rename', { authRequired: true }, {
return RocketChat.API.v1.failure('The bodyParam "name" is required');
}
- const findResult = findChannelByIdOrName({ params: { roomId: this.bodyParams.roomId} });
+ const findResult = findChannelByIdOrName({ params: { roomId: this.bodyParams.roomId } });
if (findResult.name === this.bodyParams.name) {
return RocketChat.API.v1.failure('The channel name is the same as what it would be renamed to.');
@@ -747,6 +758,24 @@ RocketChat.API.v1.addRoute('channels.setTopic', { authRequired: true }, {
}
});
+RocketChat.API.v1.addRoute('channels.setAnnouncement', { authRequired: true }, {
+ post() {
+ if (!this.bodyParams.announcement || !this.bodyParams.announcement.trim()) {
+ return RocketChat.API.v1.failure('The bodyParam "announcement" is required');
+ }
+
+ const findResult = findChannelByIdOrName({ params: this.requestParams() });
+
+ Meteor.runAsUser(this.userId, () => {
+ Meteor.call('saveRoomSettings', findResult._id, 'roomAnnouncement', this.bodyParams.announcement);
+ });
+
+ return RocketChat.API.v1.success({
+ announcement: this.bodyParams.announcement
+ });
+ }
+});
+
RocketChat.API.v1.addRoute('channels.setType', { authRequired: true }, {
post() {
if (!this.bodyParams.type || !this.bodyParams.type.trim()) {
@@ -784,3 +813,76 @@ RocketChat.API.v1.addRoute('channels.unarchive', { authRequired: true }, {
return RocketChat.API.v1.success();
}
});
+
+RocketChat.API.v1.addRoute('channels.getAllUserMentionsByChannel', { authRequired: true }, {
+ get() {
+ const { roomId } = this.requestParams();
+ const { offset, count } = this.getPaginationItems();
+ const { sort } = this.parseJsonQuery();
+
+ if (!roomId) {
+ return RocketChat.API.v1.failure('The request param "roomId" is required');
+ }
+
+ const mentions = Meteor.runAsUser(this.userId, () => Meteor.call('getUserMentionsByChannel', {
+ roomId,
+ options: {
+ sort: sort ? sort : { ts: 1 },
+ skip: offset,
+ limit: count
+ }
+ }));
+
+ const allMentions = Meteor.runAsUser(this.userId, () => Meteor.call('getUserMentionsByChannel', {
+ roomId,
+ options: {}
+ }));
+
+ return RocketChat.API.v1.success({
+ mentions,
+ count: mentions.length,
+ offset,
+ total: allMentions.length
+ });
+ }
+});
+
+RocketChat.API.v1.addRoute('channels.notifications', { authRequired: true }, {
+ get() {
+ const { roomId } = this.requestParams();
+
+ if (!roomId) {
+ return RocketChat.API.v1.failure('The \'roomId\' param is required');
+ }
+
+ const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, {
+ fields: {
+ _room: 0,
+ _user: 0,
+ $loki: 0
+ }
+ });
+
+ return RocketChat.API.v1.success({
+ subscription
+ });
+ },
+ post() {
+ const saveNotifications = (notifications, roomId) => {
+ Object.keys(notifications).map((notificationKey) => {
+ Meteor.runAsUser(this.userId, () => Meteor.call('saveNotificationSettings', roomId, notificationKey, notifications[notificationKey]));
+ });
+ };
+ const { roomId, notifications } = this.bodyParams;
+
+ if (!roomId) {
+ return RocketChat.API.v1.failure('The \'roomId\' param is required');
+ }
+
+ if (!notifications || Object.keys(notifications).length === 0) {
+ return RocketChat.API.v1.failure('The \'notifications\' param is required');
+ }
+
+ saveNotifications(notifications, roomId);
+ }
+});
diff --git a/packages/rocketchat-api/server/v1/emoji-custom.js b/packages/rocketchat-api/server/v1/emoji-custom.js
new file mode 100644
index 00000000000..f1ad75e9dd9
--- /dev/null
+++ b/packages/rocketchat-api/server/v1/emoji-custom.js
@@ -0,0 +1,7 @@
+RocketChat.API.v1.addRoute('emoji-custom', { authRequired: true }, {
+ get() {
+ const emojis = Meteor.call('listEmojiCustom');
+
+ return RocketChat.API.v1.success({ emojis });
+ }
+});
diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js
index c1bf99ef3e9..4fad6919a43 100644
--- a/packages/rocketchat-api/server/v1/misc.js
+++ b/packages/rocketchat-api/server/v1/misc.js
@@ -18,6 +18,29 @@ RocketChat.API.v1.addRoute('info', { authRequired: false }, {
}
});
+RocketChat.API.v1.addRoute('settings.oauth', { authRequired: false }, {
+ get() {
+ const mountOAuthServices = () => {
+ const oAuthServicesEnabled = ServiceConfiguration.configurations.find({}).fetch();
+
+ return oAuthServicesEnabled.map((service) => {
+ return {
+ id: service._id,
+ name: service.service,
+ appId: service.appId || service.clientId,
+ buttonLabelText: service.buttonLabelText || '',
+ buttonColor: service.buttonColor || '',
+ buttonLabelColor: service.buttonLabelColor || ''
+ };
+ });
+ };
+
+ return RocketChat.API.v1.success({
+ services: mountOAuthServices()
+ });
+ }
+});
+
RocketChat.API.v1.addRoute('me', { authRequired: true }, {
get() {
const me = _.pick(this.user, [
diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js
index ab92925e3c3..a820a059cc6 100644
--- a/packages/rocketchat-api/server/v1/users.js
+++ b/packages/rocketchat-api/server/v1/users.js
@@ -260,6 +260,33 @@ RocketChat.API.v1.addRoute('users.update', { authRequired: true }, {
}
});
+RocketChat.API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, {
+ post() {
+ check(this.bodyParams, {
+ data: Match.ObjectIncluding({
+ email: Match.Maybe(String),
+ name: Match.Maybe(String),
+ username: Match.Maybe(String),
+ currentPassword: Match.Maybe(String),
+ newPassword: Match.Maybe(String)
+ }),
+ customFields: Match.Maybe(Object)
+ });
+
+ const userData = {
+ email: this.bodyParams.data.email,
+ realname: this.bodyParams.data.name,
+ username: this.bodyParams.data.username,
+ newPassword: this.bodyParams.data.newPassword,
+ typedPassword: this.bodyParams.data.currentPassword
+ };
+
+ Meteor.runAsUser(this.userId, () => Meteor.call('saveUserProfile', userData, this.bodyParams.customFields));
+
+ return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.userId, { fields: RocketChat.API.v1.defaultFieldsToExclude }) });
+ }
+});
+
RocketChat.API.v1.addRoute('users.createToken', { authRequired: true }, {
post() {
const user = this.getUserFromParams();
@@ -267,7 +294,7 @@ RocketChat.API.v1.addRoute('users.createToken', { authRequired: true }, {
Meteor.runAsUser(this.userId, () => {
data = Meteor.call('createToken', user._id);
});
- return data ? RocketChat.API.v1.success({data}) : RocketChat.API.v1.unauthorized();
+ return data ? RocketChat.API.v1.success({ data }) : RocketChat.API.v1.unauthorized();
}
});
@@ -308,7 +335,6 @@ RocketChat.API.v1.addRoute('users.setPreferences', { authRequired: true }, {
enableAutoAway: Match.Maybe(Boolean),
highlights: Match.Maybe(Array),
desktopNotificationDuration: Match.Maybe(Number),
- viewMode: Match.Maybe(Number),
hideUsernames: Match.Maybe(Boolean),
hideRoles: Match.Maybe(Boolean),
hideAvatars: Match.Maybe(Boolean),
diff --git a/packages/rocketchat-apps/client/admin/appInstall.js b/packages/rocketchat-apps/client/admin/appInstall.js
index 57fa44dd729..e32ec62a694 100644
--- a/packages/rocketchat-apps/client/admin/appInstall.js
+++ b/packages/rocketchat-apps/client/admin/appInstall.js
@@ -24,7 +24,6 @@ Template.appInstall.onCreated(function() {
// Allow passing in a url as a query param to show installation of
if (FlowRouter.getQueryParam('url')) {
- console.log('Url:', FlowRouter.getQueryParam('url'));
instance.appUrl.set(FlowRouter.getQueryParam('url'));
FlowRouter.setQueryParams({ url: null });
}
@@ -36,11 +35,8 @@ Template.appInstall.events({
// Handle url installations
if (url) {
- console.log('Installing via url.');
t.isInstalling.set(true);
RocketChat.API.post('apps', { url }).then((result) => {
- console.log('result', result);
-
FlowRouter.go(`/admin/apps/${ result.app.id }`);
}).catch((err) => {
console.warn('err', err);
diff --git a/packages/rocketchat-apps/client/admin/appManage.js b/packages/rocketchat-apps/client/admin/appManage.js
index 284a070853b..8341928043b 100644
--- a/packages/rocketchat-apps/client/admin/appManage.js
+++ b/packages/rocketchat-apps/client/admin/appManage.js
@@ -1,6 +1,8 @@
import _ from 'underscore';
import s from 'underscore.string';
+import { AppEvents } from '../communication';
+
Template.appManage.onCreated(function() {
const instance = this;
this.id = new ReactiveVar(FlowRouter.getParam('appId'));
@@ -13,24 +15,58 @@ Template.appManage.onCreated(function() {
const id = this.id.get();
+ function _morphSettings(settings) {
+ Object.keys(settings).forEach((k) => {
+ settings[k].i18nPlaceholder = settings[k].i18nPlaceholder || ' ';
+ settings[k].value = settings[k].value || settings[k].packageValue;
+ settings[k].oldValue = settings[k].value;
+ });
+
+ instance.settings.set(settings);
+ }
+
Promise.all([
RocketChat.API.get(`apps/${ id }`),
RocketChat.API.get(`apps/${ id }/settings`)
]).then((results) => {
instance.app.set(results[0].app);
+ _morphSettings(results[1].settings);
- Object.keys(results[1].settings).forEach((k) => {
- results[1].settings[k].i18nPlaceholder = results[1].settings[k].i18nPlaceholder || ' ';
- results[1].settings[k].value = results[1].settings[k].value || results[1].settings[k].packageValue;
- results[1].settings[k].oldValue = results[1].settings[k].value;
- });
-
- instance.settings.set(results[1].settings);
this.ready.set(true);
}).catch((e) => {
instance.hasError.set(true);
instance.theError.set(e.message);
});
+
+ instance.onStatusChanged = function _onStatusChanged({ appId, status }) {
+ if (appId !== id) {
+ return;
+ }
+
+ const app = instance.app.get();
+ app.status = status;
+ instance.app.set(app);
+ };
+
+ instance.onSettingUpdated = function _onSettingUpdated({ appId }) {
+ if (appId !== id) {
+ return;
+ }
+
+ RocketChat.API.get(`apps/${ id }/settings`).then((result) => {
+ _morphSettings(result.settings);
+ });
+ };
+
+ window.Apps.getWsListener().registerListener(AppEvents.APP_STATUS_CHANGE, instance.onStatusChanged);
+ window.Apps.getWsListener().registerListener(AppEvents.APP_SETTING_UPDATED, instance.onSettingUpdated);
+});
+
+Template.apps.onDestroyed(function() {
+ const instance = this;
+
+ window.Apps.getWsListener().unregisterListener(AppEvents.APP_STATUS_CHANGE, instance.onStatusChanged);
+ window.Apps.getWsListener().unregisterListener(AppEvents.APP_SETTING_UPDATED, instance.onSettingUpdated);
});
Template.appManage.helpers({
@@ -173,7 +209,6 @@ Template.appManage.events({
'change .input-monitor, keyup .input-monitor': _.throttle(function(e, t) {
let value = s.trim($(e.target).val());
- console.log(value);
switch (this.type) {
case 'int':
value = parseInt(value);
diff --git a/packages/rocketchat-apps/client/admin/apps.js b/packages/rocketchat-apps/client/admin/apps.js
index b8726a53cf5..9582e1e494a 100644
--- a/packages/rocketchat-apps/client/admin/apps.js
+++ b/packages/rocketchat-apps/client/admin/apps.js
@@ -1,3 +1,5 @@
+import { AppEvents } from '../communication';
+
Template.apps.onCreated(function() {
const instance = this;
this.ready = new ReactiveVar(false);
@@ -7,6 +9,39 @@ Template.apps.onCreated(function() {
instance.apps.set(result.apps);
instance.ready.set(true);
});
+
+ instance.onAppAdded = function _appOnAppAdded(appId) {
+ RocketChat.API.get(`apps/${ appId }`).then((result) => {
+ const apps = instance.apps.get();
+ apps.push(result.app);
+ instance.apps.set(apps);
+ });
+ };
+
+ instance.onAppRemoved = function _appOnAppRemoved(appId) {
+ const apps = instance.apps.get();
+
+ let index = -1;
+ apps.find((item, i) => {
+ if (item.id === appId) {
+ index = i;
+ return true;
+ }
+ });
+
+ apps.splice(index, 1);
+ instance.apps.set(apps);
+ };
+
+ window.Apps.getWsListener().registerListener(AppEvents.APP_ADDED, instance.onAppAdded);
+ window.Apps.getWsListener().registerListener(AppEvents.APP_REMOVED, instance.onAppAdded);
+});
+
+Template.apps.onDestroyed(function() {
+ const instance = this;
+
+ window.Apps.getWsListener().unregisterListener(AppEvents.APP_ADDED, instance.onAppAdded);
+ window.Apps.getWsListener().unregisterListener(AppEvents.APP_REMOVED, instance.onAppAdded);
});
Template.apps.helpers({
diff --git a/packages/rocketchat-apps/client/communication/index.js b/packages/rocketchat-apps/client/communication/index.js
index 54c32e3cedf..8878b65fcf2 100644
--- a/packages/rocketchat-apps/client/communication/index.js
+++ b/packages/rocketchat-apps/client/communication/index.js
@@ -1,3 +1,3 @@
-import { AppWebsocketReceiver } from './websockets';
+import { AppWebsocketReceiver, AppEvents } from './websockets';
-export { AppWebsocketReceiver };
+export { AppWebsocketReceiver, AppEvents };
diff --git a/packages/rocketchat-apps/client/communication/websockets.js b/packages/rocketchat-apps/client/communication/websockets.js
index 290e23ef74c..2f2ccc9aa7e 100644
--- a/packages/rocketchat-apps/client/communication/websockets.js
+++ b/packages/rocketchat-apps/client/communication/websockets.js
@@ -1,19 +1,67 @@
+export const AppEvents = Object.freeze({
+ APP_ADDED: 'app/added',
+ APP_REMOVED: 'app/removed',
+ APP_UPDATED: 'app/updated',
+ APP_STATUS_CHANGE: 'app/statusUpdate',
+ APP_SETTING_UPDATED: 'app/settingUpdated',
+ COMMAND_ADDED: 'command/added',
+ COMMAND_DISABLED: 'command/disabled',
+ COMMAND_UPDATED: 'command/updated',
+ COMMAND_REMOVED: 'command/removed'
+});
+
export class AppWebsocketReceiver {
constructor(orch) {
this.orch = orch;
this.streamer = new Meteor.Streamer('apps');
- this.streamer.on('app/added', this.onAppAdded.bind(this));
- this.streamer.on('command/added', this.onCommandAdded.bind(this));
- this.streamer.on('command/disabled', this.onCommandDisabled.bind(this));
- this.streamer.on('command/updated', this.onCommandUpdated.bind(this));
- this.streamer.on('command/removed', this.onCommandDisabled.bind(this));
+ this.streamer.on(AppEvents.APP_ADDED, this.onAppAdded.bind(this));
+ this.streamer.on(AppEvents.APP_REMOVED, this.onAppRemoved.bind(this));
+ this.streamer.on(AppEvents.APP_UPDATED, this.onAppUpdated.bind(this));
+ this.streamer.on(AppEvents.APP_STATUS_CHANGE, this.onAppStatusUpdated.bind(this));
+ this.streamer.on(AppEvents.APP_SETTING_UPDATED, this.onAppSettingUpdated.bind(this));
+ this.streamer.on(AppEvents.COMMAND_ADDED, this.onCommandAdded.bind(this));
+ this.streamer.on(AppEvents.COMMAND_DISABLED, this.onCommandDisabled.bind(this));
+ this.streamer.on(AppEvents.COMMAND_UPDATED, this.onCommandUpdated.bind(this));
+ this.streamer.on(AppEvents.COMMAND_REMOVED, this.onCommandDisabled.bind(this));
+
+ this.listeners = {};
+
+ Object.keys(AppEvents).forEach((v) => {
+ this.listeners[AppEvents[v]] = [];
+ });
+ }
+
+ registerListener(event, listener) {
+ this.listeners[event].push(listener);
+ }
+
+ unregisterListener(event, listener) {
+ this.listeners[event].splice(this.listeners[event].indexOf(listener), 1);
}
onAppAdded(appId) {
RocketChat.API.get(`apps/${ appId }/languages`).then((result) => {
this.orch.parseAndLoadLanguages(result.languages);
});
+
+ this.listeners[AppEvents.APP_ADDED].forEach((listener) => listener(appId));
+ }
+
+ onAppRemoved(appId) {
+ this.listeners[AppEvents.APP_REMOVED].forEach((listener) => listener(appId));
+ }
+
+ onAppUpdated(appId) {
+ this.listeners[AppEvents.APP_UPDATED].forEach((listener) => listener(appId));
+ }
+
+ onAppStatusUpdated({ appId, status }) {
+ this.listeners[AppEvents.APP_STATUS_CHANGE].forEach((listener) => listener({ appId, status }));
+ }
+
+ onAppSettingUpdated({ appId }) {
+ this.listeners[AppEvents.APP_SETTING_UPDATED].forEach((listener) => listener({ appId }));
}
onCommandAdded(command) {
diff --git a/packages/rocketchat-apps/package.js b/packages/rocketchat-apps/package.js
index 0619a74e435..588f97d1faa 100644
--- a/packages/rocketchat-apps/package.js
+++ b/packages/rocketchat-apps/package.js
@@ -87,6 +87,6 @@ Package.onUse(function(api) {
Npm.depends({
'busboy': '0.2.13',
- '@rocket.chat/apps-engine': '0.3.5',
- '@rocket.chat/apps-ts-definition': '0.7.6'
+ '@rocket.chat/apps-engine': '0.4.8',
+ '@rocket.chat/apps-ts-definition': '0.7.15'
});
diff --git a/packages/rocketchat-apps/server/bridges/activation.js b/packages/rocketchat-apps/server/bridges/activation.js
index a67165bf465..b15bcc0c4e2 100644
--- a/packages/rocketchat-apps/server/bridges/activation.js
+++ b/packages/rocketchat-apps/server/bridges/activation.js
@@ -3,33 +3,19 @@ export class AppActivationBridge {
this.orch = orch;
}
- appEnabled(app) {
- console.log(`The App ${ app.getName() } (${ app.getID() }) has been enabled.`);
+ appAdded(app) {
+ this.orch.getNotifier().appAdded(app.getID());
}
- appDisabled(app) {
- console.log(`The App ${ app.getName() } (${ app.getID() }) has been disabled.`);
- }
-
- appLoaded(app, enabled) {
- console.log(`The App ${ app.getName() } (${ app.getID() }) has been loaded and enabled? ${ enabled }`);
-
- if (enabled) {
- this.orch.getNotifier().appAdded(app.getID());
- }
- }
-
- appUpdated(app, enabled) {
- console.log(`The App ${ app.getName() } (${ app.getID() }) has been updated and enabled? ${ enabled }`);
-
- if (enabled) {
- this.orch.getNotifier().appUpdated(app.getID());
- }
+ appUpdated(app) {
+ this.orch.getNotifier().appUpdated(app.getID());
}
appRemoved(app) {
- console.log(`The App ${ app.getName() } (${ app.getID() }) has been removed.`);
-
this.orch.getNotifier().appRemoved(app.getID());
}
+
+ appStatusChanged(app, status) {
+ this.orch.getNotifier().appStatusUpdated(app.getID(), status);
+ }
}
diff --git a/packages/rocketchat-apps/server/bridges/bridges.js b/packages/rocketchat-apps/server/bridges/bridges.js
index 1294e16e1de..83ccc7cd4b1 100644
--- a/packages/rocketchat-apps/server/bridges/bridges.js
+++ b/packages/rocketchat-apps/server/bridges/bridges.js
@@ -1,9 +1,11 @@
import { AppBridges } from '@rocket.chat/apps-engine/server/bridges';
import { AppActivationBridge } from './activation';
+import { AppDetailChangesBridge } from './details';
import { AppCommandsBridge } from './commands';
import { AppEnvironmentalVariableBridge } from './environmental';
import { AppHttpBridge } from './http';
+import { AppListenerBridge } from './listeners';
import { AppMessageBridge } from './messages';
import { AppPersistenceBridge } from './persistence';
import { AppRoomBridge } from './rooms';
@@ -16,8 +18,10 @@ export class RealAppBridges extends AppBridges {
this._actBridge = new AppActivationBridge(orch);
this._cmdBridge = new AppCommandsBridge(orch);
+ this._detBridge = new AppDetailChangesBridge(orch);
this._envBridge = new AppEnvironmentalVariableBridge(orch);
this._httpBridge = new AppHttpBridge();
+ this._lisnBridge = new AppListenerBridge(orch);
this._msgBridge = new AppMessageBridge(orch);
this._persistBridge = new AppPersistenceBridge(orch);
this._roomBridge = new AppRoomBridge(orch);
@@ -37,6 +41,10 @@ export class RealAppBridges extends AppBridges {
return this._httpBridge;
}
+ getListenerBridge() {
+ return this._lisnBridge;
+ }
+
getMessageBridge() {
return this._msgBridge;
}
@@ -49,6 +57,10 @@ export class RealAppBridges extends AppBridges {
return this._actBridge;
}
+ getAppDetailChangesBridge() {
+ return this._detBridge;
+ }
+
getRoomBridge() {
return this._roomBridge;
}
diff --git a/packages/rocketchat-apps/server/bridges/details.js b/packages/rocketchat-apps/server/bridges/details.js
new file mode 100644
index 00000000000..dea680abe07
--- /dev/null
+++ b/packages/rocketchat-apps/server/bridges/details.js
@@ -0,0 +1,13 @@
+export class AppDetailChangesBridge {
+ constructor(orch) {
+ this.orch = orch;
+ }
+
+ onAppSettingsChange(appId, setting) {
+ try {
+ this.orch.getNotifier().appSettingsChange(appId, setting);
+ } catch (e) {
+ console.warn('failed to notify about the setting change.', appId);
+ }
+ }
+}
diff --git a/packages/rocketchat-apps/server/bridges/index.js b/packages/rocketchat-apps/server/bridges/index.js
index 5eaae0b938a..d1d9a78a4aa 100644
--- a/packages/rocketchat-apps/server/bridges/index.js
+++ b/packages/rocketchat-apps/server/bridges/index.js
@@ -3,6 +3,7 @@ import { AppActivationBridge } from './activation';
import { AppCommandsBridge } from './commands';
import { AppEnvironmentalVariableBridge } from './environmental';
import { AppHttpBridge } from './http';
+import { AppListenerBridge } from './listeners';
import { AppMessageBridge } from './messages';
import { AppPersistenceBridge } from './persistence';
import { AppRoomBridge } from './rooms';
@@ -15,6 +16,7 @@ export {
AppCommandsBridge,
AppEnvironmentalVariableBridge,
AppHttpBridge,
+ AppListenerBridge,
AppMessageBridge,
AppPersistenceBridge,
AppRoomBridge,
diff --git a/packages/rocketchat-apps/server/bridges/listeners.js b/packages/rocketchat-apps/server/bridges/listeners.js
new file mode 100644
index 00000000000..8577310c71f
--- /dev/null
+++ b/packages/rocketchat-apps/server/bridges/listeners.js
@@ -0,0 +1,15 @@
+export class AppListenerBridge {
+ constructor(orch) {
+ this.orch = orch;
+ }
+
+ messageEvent(inte, message) {
+ const msg = this.orch.getConverters().get('messages').convertMessage(message);
+ return this.orch.getManager().getListenerManager().executeListener(inte, msg);
+ }
+
+ roomEvent(inte, room) {
+ const rm = this.orch.getConverters().get('rooms').convertRoom(room);
+ return this.orch.getManager().getListenerManager().executeListener(inte, rm);
+ }
+}
diff --git a/packages/rocketchat-apps/server/bridges/messages.js b/packages/rocketchat-apps/server/bridges/messages.js
index 33b77a0d031..4e4c37f3813 100644
--- a/packages/rocketchat-apps/server/bridges/messages.js
+++ b/packages/rocketchat-apps/server/bridges/messages.js
@@ -37,4 +37,30 @@ export class AppMessageBridge {
RocketChat.updateMessage(msg, editor);
}
+
+ notifyUser(user, message, appId) {
+ console.log(`The App ${ appId } is notifying a user.`);
+
+ const msg = this.orch.getConverters().get('messages').convertAppMessage(message);
+
+ RocketChat.Notifications.notifyUser(user.id, 'message', Object.assign(msg, {
+ _id: Random.id(),
+ ts: new Date(),
+ u: undefined,
+ editor: undefined
+ }));
+ }
+
+ notifyRoom(room, message, appId) {
+ console.log(`The App ${ appId } is notifying a room's users.`);
+
+ const msg = this.orch.getConverters().get('messages').convertAppMessage(message);
+
+ RocketChat.Notifications.notifyUsersOfRoom(room.id, 'message', Object.assign(msg, {
+ _id: Random.id(),
+ ts: new Date(),
+ u: undefined,
+ editor: undefined
+ }));
+ }
}
diff --git a/packages/rocketchat-apps/server/bridges/persistence.js b/packages/rocketchat-apps/server/bridges/persistence.js
index 03ff53e9bc1..adc591788f4 100644
--- a/packages/rocketchat-apps/server/bridges/persistence.js
+++ b/packages/rocketchat-apps/server/bridges/persistence.js
@@ -40,19 +40,24 @@ export class AppPersistenceBridge {
readByAssociations(associations, appId) {
console.log(`The App ${ appId } is searching for records that are associated with the following:`, associations);
- throw new Error('Not implemented.');
+ const records = this.orch.getPersistenceModel().find({
+ appId,
+ associations: { $all: associations }
+ }).fetch();
+
+ return Array.isArray(records) ? records.map((r) => r.data) : [];
}
remove(id, appId) {
console.log(`The App ${ appId } is removing one of their records by the id: "${ id }"`);
- const record = this.orch.getPersistenceModel().findOneById(id);
+ const record = this.orch.getPersistenceModel().findOne({ _id: id, appId });
if (!record) {
return undefined;
}
- this.orch.getPersistenceModel().remove({ _id: id });
+ this.orch.getPersistenceModel().remove({ _id: id, appId });
return record.data;
}
@@ -60,7 +65,22 @@ export class AppPersistenceBridge {
removeByAssociations(associations, appId) {
console.log(`The App ${ appId } is removing records with the following associations:`, associations);
- throw new Error('Not implemented.');
+ const query = {
+ appId,
+ associations: {
+ $all: associations
+ }
+ };
+
+ const records = this.orch.getPersistenceModel().find(query).fetch();
+
+ if (!records) {
+ return undefined;
+ }
+
+ this.orch.getPersistenceModel().remove(query);
+
+ return Array.isArray(records) ? records.map((r) => r.data) : [];
}
update(id, data, upsert, appId) {
diff --git a/packages/rocketchat-apps/server/communication/index.js b/packages/rocketchat-apps/server/communication/index.js
index 0e3c11484f0..ad9090a71b4 100644
--- a/packages/rocketchat-apps/server/communication/index.js
+++ b/packages/rocketchat-apps/server/communication/index.js
@@ -1,9 +1,11 @@
import { AppMethods} from './methods';
import { AppsRestApi } from './rest';
-import { AppWebsocketNotifier } from './websockets';
+import { AppEvents, AppServerNotifier, AppServerListener } from './websockets';
export {
AppMethods,
AppsRestApi,
- AppWebsocketNotifier
+ AppEvents,
+ AppServerNotifier,
+ AppServerListener
};
diff --git a/packages/rocketchat-apps/server/communication/methods.js b/packages/rocketchat-apps/server/communication/methods.js
index 1875856dad7..5b21809e6ba 100644
--- a/packages/rocketchat-apps/server/communication/methods.js
+++ b/packages/rocketchat-apps/server/communication/methods.js
@@ -5,16 +5,24 @@ export class AppMethods {
this._addMethods();
}
+ isEnabled() {
+ return typeof this._manager !== 'undefined';
+ }
+
+ isLoaded() {
+ return typeof this._manager !== 'undefined' && this.manager.areAppsLoaded();
+ }
+
_addMethods() {
- const manager = this._manager;
+ const instance = this;
Meteor.methods({
'apps/is-enabled'() {
- return typeof manager !== 'undefined';
+ return instance.isEnabled();
},
'apps/is-loaded'() {
- return typeof manager !== 'undefined' || manager.areAppsLoaded();
+ return instance.isLoaded();
}
});
}
diff --git a/packages/rocketchat-apps/server/communication/rest.js b/packages/rocketchat-apps/server/communication/rest.js
index a10df7c1084..972466d32a4 100644
--- a/packages/rocketchat-apps/server/communication/rest.js
+++ b/packages/rocketchat-apps/server/communication/rest.js
@@ -158,7 +158,7 @@ export class AppsRestApi {
}
});
- this.api.addRoute(':id/languages', { authRequired: true }, {
+ this.api.addRoute(':id/languages', { authRequired: false }, {
get() {
console.log(`Getting ${ this.urlParams.id }'s languages..`);
const prl = manager.getOneById(this.urlParams.id);
diff --git a/packages/rocketchat-apps/server/communication/websockets.js b/packages/rocketchat-apps/server/communication/websockets.js
index 80d17e02872..f9cd49f4a1b 100644
--- a/packages/rocketchat-apps/server/communication/websockets.js
+++ b/packages/rocketchat-apps/server/communication/websockets.js
@@ -1,36 +1,152 @@
-export class AppWebsocketNotifier {
- constructor() {
- this.streamer = new Meteor.Streamer('apps', { retransmit: false });
- this.streamer.allowRead('all');
- this.streamer.allowEmit('all');
- this.streamer.allowWrite('none');
+import { AppStatus, AppStatusUtils } from '@rocket.chat/apps-ts-definition/AppStatus';
+
+export const AppEvents = Object.freeze({
+ APP_ADDED: 'app/added',
+ APP_REMOVED: 'app/removed',
+ APP_UPDATED: 'app/updated',
+ APP_STATUS_CHANGE: 'app/statusUpdate',
+ APP_SETTING_UPDATED: 'app/settingUpdated',
+ COMMAND_ADDED: 'command/added',
+ COMMAND_DISABLED: 'command/disabled',
+ COMMAND_UPDATED: 'command/updated',
+ COMMAND_REMOVED: 'command/removed'
+});
+
+export class AppServerListener {
+ constructor(orch, engineStreamer, clientStreamer, recieved) {
+ this.orch = orch;
+ this.engineStreamer = engineStreamer;
+ this.clientStreamer = clientStreamer;
+ this.recieved = recieved;
+
+ this.engineStreamer.on(AppEvents.APP_ADDED, this.onAppAdded.bind(this));
+ this.engineStreamer.on(AppEvents.APP_STATUS_CHANGE, this.onAppStatusUpdated.bind(this));
+ this.engineStreamer.on(AppEvents.APP_SETTING_UPDATED, this.onAppSettingUpdated.bind(this));
+ this.engineStreamer.on(AppEvents.APP_REMOVED, this.onAppRemoved.bind(this));
+ this.engineStreamer.on(AppEvents.COMMAND_ADDED, this.onCommandAdded.bind(this));
+ this.engineStreamer.on(AppEvents.COMMAND_DISABLED, this.onCommandDisabled.bind(this));
+ this.engineStreamer.on(AppEvents.COMMAND_UPDATED, this.onCommandUpdated.bind(this));
+ this.engineStreamer.on(AppEvents.COMMAND_REMOVED, this.onCommandRemoved.bind(this));
+ }
+
+ onAppAdded(appId) {
+ this.orch.getManager().loadOne(appId).then(() => this.clientStreamer.emit(AppEvents.APP_ADDED, appId));
+ }
+
+ onAppStatusUpdated({ appId, status }) {
+ this.recieved.set(`${ AppEvents.APP_STATUS_CHANGE }_${ appId }`, { appId, status, when: new Date() });
+
+ if (AppStatusUtils.isEnabled(status)) {
+ this.orch.getManager().enable(appId)
+ .then(() => this.clientStreamer.emit(AppEvents.APP_STATUS_CHANGE, { appId, status }));
+ } else if (AppStatusUtils.isDisabled(status)) {
+ this.orch.getManager().disable(appId, AppStatus.MANUALLY_DISABLED === status)
+ .then(() => this.clientStreamer.emit(AppEvents.APP_STATUS_CHANGE, { appId, status }));
+ }
+ }
+
+ onAppSettingUpdated({ appId, setting }) {
+ this.recieved.set(`${ AppEvents.APP_SETTING_UPDATED }_${ appId }_${ setting.id }`, { appId, setting, when: new Date() });
+
+ this.orch.getManager().getSettingsManager().updateAppSetting(appId, setting)
+ .then(() => this.clientStreamer.emit(AppEvents.APP_SETTING_UPDATED, { appId }));
+ }
+
+ onAppRemoved(appId) {
+ this.orch.getManager().remove(appId).then(() => this.clientStreamer.emit(AppEvents.APP_REMOVED, appId));
+ }
+
+ onCommandAdded(command) {
+ this.clientStreamer.emit(AppEvents.COMMAND_ADDED, command);
+ }
+
+ onCommandDisabled(command) {
+ this.clientStreamer.emit(AppEvents.COMMAND_DISABLED, command);
+ }
+
+ onCommandUpdated(command) {
+ this.clientStreamer.emit(AppEvents.COMMAND_UPDATED, command);
+ }
+
+ onCommandRemoved(command) {
+ this.clientStreamer.emit(AppEvents.COMMAND_REMOVED, command);
+ }
+}
+
+export class AppServerNotifier {
+ constructor(orch) {
+ this.engineStreamer = new Meteor.Streamer('apps-engine', { retransmit: false });
+ this.engineStreamer.serverOnly = true;
+ this.engineStreamer.allowRead('none');
+ this.engineStreamer.allowEmit('all');
+ this.engineStreamer.allowWrite('none');
+
+ // This is used to broadcast to the web clients
+ this.clientStreamer = new Meteor.Streamer('apps', { retransmit: false });
+ this.clientStreamer.serverOnly = true;
+ this.clientStreamer.allowRead('all');
+ this.clientStreamer.allowEmit('all');
+ this.clientStreamer.allowWrite('none');
+
+ this.recieved = new Map();
+ this.listener = new AppServerListener(orch, this.engineStreamer, this.clientStreamer, this.recieved);
}
appAdded(appId) {
- this.streamer.emit('app/added', appId);
+ this.engineStreamer.emit(AppEvents.APP_ADDED, appId);
+ this.clientStreamer.emit(AppEvents.APP_ADDED, appId);
}
appRemoved(appId) {
- this.streamer.emit('app/removed', appId);
+ this.engineStreamer.emit(AppEvents.APP_REMOVED, appId);
+ this.clientStreamer.emit(AppEvents.APP_REMOVED, appId);
}
appUpdated(appId) {
- this.streamer.emit('app/updated', appId);
+ this.engineStreamer.emit(AppEvents.APP_UPDATED, appId);
+ this.clientStreamer.emit(AppEvents.APP_UPDATED, appId);
+ }
+
+ appStatusUpdated(appId, status) {
+ if (this.recieved.has(`${ AppEvents.APP_STATUS_CHANGE }_${ appId }`)) {
+ const details = this.recieved.get(`${ AppEvents.APP_STATUS_CHANGE }_${ appId }`);
+ if (details.status === status) {
+ this.recieved.delete(`${ AppEvents.APP_STATUS_CHANGE }_${ appId }`);
+ return;
+ }
+ }
+
+ this.engineStreamer.emit(AppEvents.APP_STATUS_CHANGE, { appId, status });
+ this.clientStreamer.emit(AppEvents.APP_STATUS_CHANGE, { appId, status });
+ }
+
+ appSettingsChange(appId, setting) {
+ if (this.recieved.has(`${ AppEvents.APP_SETTING_UPDATED }_${ appId }_${ setting.id }`)) {
+ this.recieved.delete(`${ AppEvents.APP_SETTING_UPDATED }_${ appId }_${ setting.id }`);
+ return;
+ }
+
+ this.engineStreamer.emit(AppEvents.APP_SETTING_UPDATED, { appId, setting });
+ this.clientStreamer.emit(AppEvents.APP_SETTING_UPDATED, { appId });
}
commandAdded(command) {
- this.streamer.emit('command/added', command);
+ this.engineStreamer.emit(AppEvents.COMMAND_ADDED, command);
+ this.clientStreamer.emit(AppEvents.COMMAND_ADDED, command);
}
commandDisabled(command) {
- this.streamer.emit('command/disabled', command);
+ this.engineStreamer.emit(AppEvents.COMMAND_DISABLED, command);
+ this.clientStreamer.emit(AppEvents.COMMAND_DISABLED, command);
}
commandUpdated(command) {
- this.streamer.emit('command/updated', command);
+ this.engineStreamer.emit(AppEvents.COMMAND_UPDATED, command);
+ this.clientStreamer.emit(AppEvents.COMMAND_UPDATED, command);
}
commandRemoved(command) {
- this.streamer.emit('command/removed', command);
+ this.engineStreamer.emit(AppEvents.COMMAND_REMOVED, command);
+ this.clientStreamer.emit(AppEvents.COMMAND_REMOVED, command);
}
}
diff --git a/packages/rocketchat-apps/server/orchestrator.js b/packages/rocketchat-apps/server/orchestrator.js
index a5178ee07e0..fbd29441e61 100644
--- a/packages/rocketchat-apps/server/orchestrator.js
+++ b/packages/rocketchat-apps/server/orchestrator.js
@@ -1,5 +1,5 @@
import { RealAppBridges } from './bridges';
-import { AppMethods, AppsRestApi, AppWebsocketNotifier } from './communication';
+import { AppMethods, AppsRestApi, AppServerNotifier } from './communication';
import { AppMessagesConverter, AppRoomsConverter, AppSettingsConverter, AppUsersConverter } from './converters';
import { AppsLogsModel, AppsModel, AppsPersistenceModel, AppRealStorage, AppRealLogsStorage } from './storage';
@@ -15,7 +15,7 @@ class AppServerOrchestrator {
this._logModel = new AppsLogsModel();
this._persistModel = new AppsPersistenceModel();
this._storage = new AppRealStorage(this._model);
- this._logStorage = new AppRealLogsStorage(this._persistModel);
+ this._logStorage = new AppRealLogsStorage(this._logModel);
this._converters = new Map();
this._converters.set('messages', new AppMessagesConverter(this));
@@ -29,7 +29,7 @@ class AppServerOrchestrator {
this._communicators = new Map();
this._communicators.set('methods', new AppMethods(this._manager));
- this._communicators.set('notifier', new AppWebsocketNotifier());
+ this._communicators.set('notifier', new AppServerNotifier(this));
this._communicators.set('restapi', new AppsRestApi(this, this._manager));
}
@@ -64,18 +64,27 @@ class AppServerOrchestrator {
getManager() {
return this._manager;
}
+
+ isEnabled() {
+ return true;
+ }
+
+ isLoaded() {
+ return this.getManager().areAppsLoaded();
+ }
}
Meteor.startup(function _appServerOrchestrator() {
// Ensure that everything is setup
if (process.env[AppManager.ENV_VAR_NAME_FOR_ENABLING] !== 'true' && process.env[AppManager.SUPER_FUN_ENV_ENABLEMENT_NAME] !== 'true') {
- return new AppMethods();
+ global.Apps = new AppMethods();
+ return;
}
console.log('Orchestrating the app piece...');
global.Apps = new AppServerOrchestrator();
global.Apps.getManager().load()
- .then(() => console.log('...done! ;)'))
+ .then(() => console.log('...done! :)'))
.catch((err) => console.warn('...failed!', err));
});
diff --git a/packages/rocketchat-apps/server/storage/logs-storage.js b/packages/rocketchat-apps/server/storage/logs-storage.js
index baaa817331f..a184526883b 100644
--- a/packages/rocketchat-apps/server/storage/logs-storage.js
+++ b/packages/rocketchat-apps/server/storage/logs-storage.js
@@ -22,14 +22,11 @@ export class AppRealLogsStorage extends AppLogStorage {
}
storeEntries(appId, logger) {
- console.log(appId);
return new Promise((resolve, reject) => {
const item = AppConsole.toStorageEntry(appId, logger);
try {
- console.log(item);
const id = this.db.insert(item);
- console.log(id);
resolve(this.db.findOneById(id));
} catch (e) {
diff --git a/packages/rocketchat-authorization/server/startup.js b/packages/rocketchat-authorization/server/startup.js
index 10b8846b01e..82bb3ab1a8e 100644
--- a/packages/rocketchat-authorization/server/startup.js
+++ b/packages/rocketchat-authorization/server/startup.js
@@ -34,6 +34,8 @@ Meteor.startup(function() {
{ _id: 'edit-room', roles : ['admin', 'owner', 'moderator'] },
{ _id: 'force-delete-message', roles : ['admin', 'owner'] },
{ _id: 'join-without-join-code', roles : ['admin', 'bot'] },
+ { _id: 'leave-c', roles : ['admin', 'user', 'bot', 'anonymous'] },
+ { _id: 'leave-p', roles : ['admin', 'user', 'bot', 'anonymous'] },
{ _id: 'manage-assets', roles : ['admin'] },
{ _id: 'manage-emoji', roles : ['admin'] },
{ _id: 'manage-integrations', roles : ['admin'] },
diff --git a/packages/rocketchat-autolinker/client/client.js b/packages/rocketchat-autolinker/client/client.js
index 60c18123d00..4846ff16e25 100644
--- a/packages/rocketchat-autolinker/client/client.js
+++ b/packages/rocketchat-autolinker/client/client.js
@@ -7,7 +7,14 @@ import s from 'underscore.string';
import Autolinker from 'autolinker';
+function htmlDecode(input) {
+ const e = document.createElement('div');
+ e.innerHTML = input;
+ return e.childNodes.length === 0 ? '' : e.childNodes[0].nodeValue;
+}
+
function AutoLinker(message) {
+ message.html = htmlDecode(message.html);
if (RocketChat.settings.get('AutoLinker') !== true) {
return message;
}
diff --git a/packages/rocketchat-cors/cors.js b/packages/rocketchat-cors/cors.js
index a41a833d159..5e45911acc9 100644
--- a/packages/rocketchat-cors/cors.js
+++ b/packages/rocketchat-cors/cors.js
@@ -3,11 +3,18 @@ import _ from 'underscore';
import url from 'url';
+import { Mongo } from 'meteor/mongo';
import tls from 'tls';
// FIX For TLS error see more here https://github.com/RocketChat/Rocket.Chat/issues/9316
// TODO: Remove after NodeJS fix it, more information https://github.com/nodejs/node/issues/16196 https://github.com/nodejs/node/pull/16853
tls.DEFAULT_ECDH_CURVE = 'auto';
+// Revert change from Meteor 1.6.1 who set ignoreUndefined: true
+// more information https://github.com/meteor/meteor/pull/9444
+Mongo.setConnectionOptions({
+ ignoreUndefined: false
+});
+
WebApp.rawConnectHandlers.use(Meteor.bindEnvironment(function(req, res, next) {
if (req._body) {
return next();
diff --git a/packages/rocketchat-cors/package.js b/packages/rocketchat-cors/package.js
index 9959f7e1539..3863a9acd73 100644
--- a/packages/rocketchat-cors/package.js
+++ b/packages/rocketchat-cors/package.js
@@ -8,7 +8,8 @@ Package.describe({
Package.onUse(function(api) {
api.use([
'ecmascript',
- 'webapp'
+ 'webapp',
+ 'mongo'
]);
api.addFiles('cors.js', 'server');
diff --git a/packages/rocketchat-custom-oauth/server/custom_oauth_server.js b/packages/rocketchat-custom-oauth/server/custom_oauth_server.js
index f78b9e57edb..0e2b812f742 100644
--- a/packages/rocketchat-custom-oauth/server/custom_oauth_server.js
+++ b/packages/rocketchat-custom-oauth/server/custom_oauth_server.js
@@ -223,6 +223,13 @@ export class CustomOAuth {
identity.id = identity.userid;
}
+ // Fix Nextcloud provider
+ if (!identity.id && identity.ocs && identity.ocs.data && identity.ocs.data.id) {
+ identity.id = identity.ocs.data.id;
+ identity.name = identity.ocs.data.display-name;
+ identity.email = identity.ocs.data.email;
+ }
+
// Fix when authenticating from a meteor app with 'emails' field
if (!identity.email && (identity.emails && Array.isArray(identity.emails) && identity.emails.length >= 1)) {
identity.email = identity.emails[0].address ? identity.emails[0].address : undefined;
diff --git a/packages/rocketchat-emoji-custom/client/models/EmojiCustom.js b/packages/rocketchat-emoji-custom/client/models/EmojiCustom.js
index 369ab9754dc..78cae29a32b 100644
--- a/packages/rocketchat-emoji-custom/client/models/EmojiCustom.js
+++ b/packages/rocketchat-emoji-custom/client/models/EmojiCustom.js
@@ -3,6 +3,18 @@ class EmojiCustom extends RocketChat.models._Base {
super();
this._initModel('custom_emoji');
}
+
+ //find
+ findByNameOrAlias(name, options) {
+ const query = {
+ $or: [
+ {name},
+ {aliases: name}
+ ]
+ };
+
+ return this.find(query, options);
+ }
}
RocketChat.models.EmojiCustom = new EmojiCustom();
diff --git a/packages/rocketchat-file-upload/server/lib/requests.js b/packages/rocketchat-file-upload/server/lib/requests.js
index 7a47c0496e8..7962e095351 100644
--- a/packages/rocketchat-file-upload/server/lib/requests.js
+++ b/packages/rocketchat-file-upload/server/lib/requests.js
@@ -1,6 +1,6 @@
/* globals FileUpload, WebApp */
-WebApp.connectHandlers.use(`${ __meteor_runtime_config__.ROOT_URL_PATH_PREFIX }/file-upload/`, function(req, res, next) {
+WebApp.connectHandlers.use('/file-upload/', function(req, res, next) {
const match = /^\/([^\/]+)\/(.*)/.exec(req.url);
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index 69b83e34f0e..0f9f6d10160 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -147,6 +147,8 @@
"Accounts_SetDefaultAvatar": "Set Default Avatar",
"Accounts_SetDefaultAvatar_Description": "Tries to determine default avatar based on OAuth Account or Gravatar",
"Accounts_ShowFormLogin": "Show Form-Based Login",
+ "Accounts_TwoFactorAuthentication_MaxDelta": "Maximum Delta",
+ "Accounts_TwoFactorAuthentication_MaxDelta_Description": "The Maximum Delta determines how many tokens are valid at any given time. Tokens are generated every 30 seconds, and are valid for (30 * Maximum Delta) seconds.
Example: With a Maximum Delta set to 10, each token can be used up to 300 seconds before or after it's timestamp. This is useful when the client's clock is not properly synced with the server.",
"Accounts_UseDefaultBlockedDomainsList": "Use Default Blocked Domains List",
"Accounts_UseDNSDomainCheck": "Use DNS Domain Check",
"Accounts_UserAddedEmail_Default": "
Welcome to [Site_Name]
Go to [Site_URL] and try the best open source chat solution available today!
You may login using your email: [email] and password: [password]. You may be required to change it after your first login.",
@@ -510,6 +512,9 @@
"Delete_my_account": "Delete my account",
"Delete_Room_Warning": "Deleting a room will delete all messages posted within the room. This cannot be undone.",
"Delete_User_Warning": "Deleting a user will delete all messages from that user as well. This cannot be undone.",
+ "Delete_User_Warning_Keep": "The user will be deleted, but their messages will remain visible. This cannot be undone.",
+ "Delete_User_Warning_Delete": "Deleting a user will delete all messages from that user as well. This cannot be undone.",
+ "Delete_User_Warning_Unlink": "Deleting a user will remove the user name from all their messages. This cannot be undone.",
"Deleted": "Deleted!",
"Department": "Department",
"Department_removed": "Department removed",
@@ -551,6 +556,7 @@
"Display_offline_form": "Display Offline Form",
"Displays_action_text": "Displays action text",
"Do_not_display_unread_counter": "Do not display any counter of this channel",
+ "Do_you_want_to_accept": "Do you want to accept?",
"Do_you_want_to_change_to_s_question": "Do you want to change to %s?",
"Domain": "Domain",
"Domain_added": "domain Added",
@@ -716,7 +722,6 @@
"Facebook_Page": "Facebook Page",
"False": "False",
"Favorite_Rooms": "Enable Favorite Rooms",
- "Favorite": "Favorite",
"Favorites": "Favorites",
"Features_Enabled": "Features Enabled",
"Field": "Field",
@@ -816,6 +821,7 @@
"GoogleVision_Type_SafeSearch": "SafeSearch Detection",
"GoogleVision_Type_Similar": "Search Similar Images",
"Group_by_Type": "Group by Type",
+ "Group_favorites": "Group favorites",
"Group_mentions_only": "Group mentions only",
"Guest_Pool": "Guest Pool",
"Hash": "Hash",
@@ -1132,6 +1138,8 @@
"Lead_capture_email_regex": "Lead capture email regex",
"Lead_capture_phone_regex": "Lead capture phone regex",
"Least_Amount": "Least Amount",
+ "leave-c": "Leave Channels",
+ "leave-p": "Leave Private Groups",
"Leave_Group_Warning": "Are you sure you want to leave the group \"%s\"?",
"Leave_Livechat_Warning": "Are you sure you want to leave the livechat with \"%s\"?",
"Leave_Private_Warning": "Are you sure you want to leave the discussion with \"%s\"?",
@@ -1263,14 +1271,21 @@
"Message_Attachments": "Message Attachments",
"Message_Attachments_GroupAttach": "Group Attachment Buttons",
"Message_Attachments_GroupAttachDescription": "This groups the icons under an expandable menu. Takes up less screen space.",
+ "Message_Audio": "Audio Message",
+ "Message_Audio_bitRate": "Audio Message Bit Rate",
"Message_AudioRecorderEnabled": "Audio Recorder Enabled",
- "Message_AudioRecorderEnabledDescription": "Requires 'audio/wav' files to be an accepted media type within 'File Upload' settings.",
+ "Message_AudioRecorderEnabled_Description": "Requires 'audio/mp3' files to be an accepted media type within 'File Upload' settings.",
"Message_BadWordsFilterList": "Add Bad Words to the Blacklist",
"Message_BadWordsFilterListDescription": "Add List of Comma-separated list of bad words to filter",
"Message_DateFormat": "Date Format",
"Message_DateFormat_Description": "See also: Moment.js",
"Message_deleting_blocked": "This message cannot be deleted anymore",
"Message_editing": "Message editing",
+ "Message_ErasureType" : "Message Erasure Type",
+ "Message_ErasureType_Description" : "Determine what to do with messages of users who remove their account.",
+ "Message_ErasureType_Keep" : "Keep Messages and User Name",
+ "Message_ErasureType_Delete" : "Delete All Messages",
+ "Message_ErasureType_Unlink" : "Remove Link Between User and Messages",
"Message_GlobalSearch": "Global Search",
"Message_GroupingPeriod": "Grouping Period (in seconds)",
"Message_GroupingPeriodDescription": "Messages will be grouped with previous message if both are from the same user and the elapsed time was less than the informed time in seconds.",
@@ -1425,6 +1440,7 @@
"Offline_unavailable": "Offline unavailable",
"On": "On",
"Online": "Online",
+ "online": "online",
"Only_authorized_users_can_write_new_messages": "Only authorized users can write new messages",
"Only_On_Desktop": "Desktop mode (only sends with enter on desktop)",
"Only_you_can_see_this_message": "Only you can see this message",
@@ -1588,6 +1604,7 @@
"Remove_last_admin": "Removing last admin",
"Remove_someone_from_room": "Remove someone from the room",
"Removed": "Removed",
+ "Removed_User": "Removed User",
"Reply": "Reply",
"Report_Abuse": "Report Abuse",
"Report_exclamation_mark": "Report!",
@@ -1709,6 +1726,8 @@
"Send_request_on_chat_close": "Send Request on Chat Close",
"Send_request_on_lead_capture": "Send request on lead capture",
"Send_request_on_offline_messages": "Send Request on Offline Messages",
+ "Send_request_on_visitor_message": "Send Request on Visitor Messages",
+ "Send_request_on_agent_message": "Send Request on Agent Messages",
"Send_Test": "Send Test",
"Send_welcome_email": "Send welcome email",
"Send_your_JSON_payloads_to_this_URL": "Send your JSON payloads to this URL.",
@@ -1992,7 +2011,7 @@
"Unmute_user": "Unmute user",
"Unnamed": "Unnamed",
"Unpin_Message": "Unpin Message",
- "Unread" : "Unread",
+ "Unread_on_top" : "Unread on top",
"Unread_Count": "Unread Count",
"Unread_Count_DM": "Unread Count for Direct Messages",
"Unread_Messages": "Unread Messages",
@@ -2161,6 +2180,11 @@
"We_have_sent_registration_email": "We have sent you an email to confirm your registration. If you do not receive an email shortly, please come back and try again.",
"Webhook_URL": "Webhook URL",
"Webhooks": "Webhooks",
+ "WebRTC_direct_audio_call_from_%s": "Direct audio call from %s",
+ "WebRTC_direct_video_call_from_%s": "Direct video call from %s",
+ "WebRTC_group_audio_call_from_%s": "Group audio call from %s",
+ "WebRTC_group_video_call_from_%s": "Group video call from %s",
+ "WebRTC_monitor_call_from_%s": "Monitor call from %s",
"WebRTC_Enable_Channel": "Enable for Public Channels",
"WebRTC_Enable_Direct": "Enable for Direct Messages",
"WebRTC_Enable_Private": "Enable for Private Channels",
@@ -2218,4 +2242,4 @@
"your_message_optional": "your message (optional)",
"Your_password_is_wrong": "Your password is wrong!",
"Your_push_was_sent_to_s_devices": "Your push was sent to %s devices"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/fr.i18n.json b/packages/rocketchat-i18n/i18n/fr.i18n.json
index ac9df9359e9..eb28f22ecb8 100644
--- a/packages/rocketchat-i18n/i18n/fr.i18n.json
+++ b/packages/rocketchat-i18n/i18n/fr.i18n.json
@@ -1042,7 +1042,7 @@
"Only_On_Desktop": "Mode Bureau (envoyé seulement quand Entrée sur le bureau)",
"Only_you_can_see_this_message": "Vous seul pouvez voir ce message",
"Oops!": "Oups",
- "Open": "Ouvrerture",
+ "Open": "Ouverture",
"Open_days_of_the_week": "Jours d'ouverture",
"Open_Livechats": "Ouvrir les chats en direct",
"Opened": "Ouvert",
diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
index d869357e085..ca1915becd4 100644
--- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
@@ -1033,6 +1033,8 @@
"Send_invitation_email_success": "Você enviou com sucesso um convite por e-mail para os seguintes endereços:",
"Send_request_on_chat_close": "Enviar requisição ao fechar conversa",
"Send_request_on_offline_messages": "Enviar requisição para mensagens off-line",
+ "Send_request_on_visitor_message": "Enviar requisição para mensagens do Visitante",
+ "Send_request_on_agent_message": "Enviar requisição para mensagens do Agente",
"Send_Test": "Enviar teste",
"Send_welcome_email": "Enviar e-mail de boas-vindas",
"Send_your_JSON_payloads_to_this_URL": "Envie seu payload JSON para esta URL.",
diff --git a/packages/rocketchat-importer/server/classes/ImporterBase.js b/packages/rocketchat-importer/server/classes/ImporterBase.js
index 09ef4063052..74de695acb8 100644
--- a/packages/rocketchat-importer/server/classes/ImporterBase.js
+++ b/packages/rocketchat-importer/server/classes/ImporterBase.js
@@ -26,7 +26,7 @@ export class Base {
* @static
*/
static getBSONSize(item) {
- const { BSON } = require('bson').native();
+ const { BSON } = require('bson');
const bson = new BSON();
return bson.calculateObjectSize(item);
}
diff --git a/packages/rocketchat-lib/client/MessageAction.js b/packages/rocketchat-lib/client/MessageAction.js
index c473d2de438..79747da1631 100644
--- a/packages/rocketchat-lib/client/MessageAction.js
+++ b/packages/rocketchat-lib/client/MessageAction.js
@@ -103,25 +103,10 @@ Meteor.startup(function() {
action() {
const message = this._arguments[1];
const {input} = chatMessages[message.rid];
- const url = RocketChat.MessageAction.getPermaLink(message._id);
- const roomInfo = RocketChat.models.Rooms.findOne(message.rid, { fields: { t: 1 } });
- let text = `[ ](${ url }) `;
- let inputValue = '';
-
- if (roomInfo.t !== 'd' && message.u.username !== Meteor.user().username) {
- text += `@${ message.u.username } `;
- }
-
- if (input.value && !input.value.endsWith(' ')) {
- inputValue += ' ';
- }
- inputValue += text;
-
$(input)
.focus()
- .val(inputValue)
- .trigger('change')
- .trigger('input');
+ .data('reply', message)
+ .trigger('dataChange');
},
condition(message) {
if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
diff --git a/packages/rocketchat-lib/client/lib/RestApiClient.js b/packages/rocketchat-lib/client/lib/RestApiClient.js
index 273652a5cfb..cadcbaed02e 100644
--- a/packages/rocketchat-lib/client/lib/RestApiClient.js
+++ b/packages/rocketchat-lib/client/lib/RestApiClient.js
@@ -44,7 +44,7 @@ RocketChat.API = {
return new Promise(function _rlRestApiGet(resolve, reject) {
jQuery.ajax({
method,
- url: `${ Meteor.absoluteUrl() }api/${ endpoint }${ query }`,
+ url: `${ window.location.origin }/api/${ endpoint }${ query }`,
headers: {
'Content-Type': 'application/json',
'X-User-Id': localStorage['Meteor.userId'],
diff --git a/packages/rocketchat-lib/client/lib/cachedCollection.js b/packages/rocketchat-lib/client/lib/cachedCollection.js
index 08ed0c357dd..ddeaa288808 100644
--- a/packages/rocketchat-lib/client/lib/cachedCollection.js
+++ b/packages/rocketchat-lib/client/lib/cachedCollection.js
@@ -99,7 +99,7 @@ class CachedCollection {
useSync = true,
useCache = true,
debug = false,
- version = 6,
+ version = 7,
maxCacheTime = 60*60*24*30,
onSyncData = (/* action, record */) => {}
}) {
diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js
index 6cdea174f1f..79c939d2120 100644
--- a/packages/rocketchat-lib/package.js
+++ b/packages/rocketchat-lib/package.js
@@ -128,6 +128,8 @@ Package.onUse(function(api) {
api.addFiles('server/models/Users.js', 'server');
api.addFiles('server/oauth/oauth.js', 'server');
+ api.addFiles('server/oauth/facebook.js', 'server');
+ api.addFiles('server/oauth/twitter.js', 'server');
api.addFiles('server/oauth/google.js', 'server');
api.addFiles('server/oauth/proxy.js', 'server');
diff --git a/packages/rocketchat-lib/rocketchat.info b/packages/rocketchat-lib/rocketchat.info
index 46273c8211e..5db7177fdf2 100644
--- a/packages/rocketchat-lib/rocketchat.info
+++ b/packages/rocketchat-lib/rocketchat.info
@@ -1,3 +1,3 @@
{
- "version": "0.62.0-develop"
+ "version": "0.63.0-develop"
}
diff --git a/packages/rocketchat-lib/server/functions/deleteUser.js b/packages/rocketchat-lib/server/functions/deleteUser.js
index 035e40ef944..362dc83a501 100644
--- a/packages/rocketchat-lib/server/functions/deleteUser.js
+++ b/packages/rocketchat-lib/server/functions/deleteUser.js
@@ -1,30 +1,45 @@
RocketChat.deleteUser = function(userId) {
const user = RocketChat.models.Users.findOneById(userId);
- RocketChat.models.Messages.removeByUserId(userId); // Remove user messages
- RocketChat.models.Subscriptions.db.findByUserId(userId).forEach((subscription) => {
- const room = RocketChat.models.Rooms.findOneById(subscription.rid);
- if (room) {
- if (room.t !== 'c' && room.usernames.length === 1) {
- RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted)
- }
- if (room.t === 'd') {
- RocketChat.models.Subscriptions.removeByRoomId(subscription.rid);
- RocketChat.models.Messages.removeByRoomId(subscription.rid);
- }
+ // Users without username can't do anything, so there is nothing to remove
+ if (user.username != null) {
+ const messageErasureType = RocketChat.settings.get('Message_ErasureType');
+
+ switch (messageErasureType) {
+ case 'Delete' :
+ RocketChat.models.Messages.removeByUserId(userId);
+ break;
+ case 'Unlink' :
+ const rocketCat = RocketChat.models.Users.findById('rocket.cat').fetch()[0];
+ const nameAlias = TAPi18n.__('Removed_User');
+ RocketChat.models.Messages.unlinkUserId(userId, rocketCat._id, rocketCat.username, nameAlias);
+ break;
}
- });
- RocketChat.models.Subscriptions.removeByUserId(userId); // Remove user subscriptions
- RocketChat.models.Rooms.removeByTypeContainingUsername('d', user.username); // Remove direct rooms with the user
- RocketChat.models.Rooms.removeUsernameFromAll(user.username); // Remove user from all other rooms
+ RocketChat.models.Subscriptions.db.findByUserId(userId).forEach((subscription) => {
+ const room = RocketChat.models.Rooms.findOneById(subscription.rid);
+ if (room) {
+ if (room.t !== 'c' && room.usernames.length === 1) {
+ RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted)
+ }
+ if (room.t === 'd') {
+ RocketChat.models.Subscriptions.removeByRoomId(subscription.rid);
+ RocketChat.models.Messages.removeByRoomId(subscription.rid);
+ }
+ }
+ });
+
+ RocketChat.models.Subscriptions.removeByUserId(userId); // Remove user subscriptions
+ RocketChat.models.Rooms.removeByTypeContainingUsername('d', user.username); // Remove direct rooms with the user
+ RocketChat.models.Rooms.removeUsernameFromAll(user.username); // Remove user from all other rooms
- // removes user's avatar
- if (user.avatarOrigin === 'upload' || user.avatarOrigin === 'url') {
- FileUpload.getStore('Avatars').deleteByName(user.username);
- }
+ // removes user's avatar
+ if (user.avatarOrigin === 'upload' || user.avatarOrigin === 'url') {
+ FileUpload.getStore('Avatars').deleteByName(user.username);
+ }
- RocketChat.models.Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted.
+ RocketChat.models.Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted.
+ }
RocketChat.models.Users.removeById(userId); // Remove user from users database
};
diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js
index 9f45ed155c3..c4d1b4089b7 100644
--- a/packages/rocketchat-lib/server/functions/saveUser.js
+++ b/packages/rocketchat-lib/server/functions/saveUser.js
@@ -7,27 +7,45 @@ RocketChat.saveUser = function(userId, userData) {
const existingRoles = _.pluck(RocketChat.authz.getRoles(), '_id');
if (userData._id && userId !== userData._id && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info')) {
- throw new Meteor.Error('error-action-not-allowed', 'Editing user is not allowed', { method: 'insertOrUpdateUser', action: 'Editing_user' });
+ throw new Meteor.Error('error-action-not-allowed', 'Editing user is not allowed', {
+ method: 'insertOrUpdateUser',
+ action: 'Editing_user'
+ });
}
if (!userData._id && !RocketChat.authz.hasPermission(userId, 'create-user')) {
- throw new Meteor.Error('error-action-not-allowed', 'Adding user is not allowed', { method: 'insertOrUpdateUser', action: 'Adding_user' });
+ throw new Meteor.Error('error-action-not-allowed', 'Adding user is not allowed', {
+ method: 'insertOrUpdateUser',
+ action: 'Adding_user'
+ });
}
if (userData.roles && _.difference(userData.roles, existingRoles).length > 0) {
- throw new Meteor.Error('error-action-not-allowed', 'The field Roles consist invalid role name', { method: 'insertOrUpdateUser', action: 'Assign_role' });
+ throw new Meteor.Error('error-action-not-allowed', 'The field Roles consist invalid role name', {
+ method: 'insertOrUpdateUser',
+ action: 'Assign_role'
+ });
}
if (userData.roles && _.indexOf(userData.roles, 'admin') >= 0 && !RocketChat.authz.hasPermission(userId, 'assign-admin-role')) {
- throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { method: 'insertOrUpdateUser', action: 'Assign_admin' });
+ throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', {
+ method: 'insertOrUpdateUser',
+ action: 'Assign_admin'
+ });
}
if (!userData._id && !s.trim(userData.name)) {
- throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { method: 'insertOrUpdateUser', field: 'Name' });
+ throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', {
+ method: 'insertOrUpdateUser',
+ field: 'Name'
+ });
}
if (!userData._id && !s.trim(userData.username)) {
- throw new Meteor.Error('error-the-field-is-required', 'The field Username is required', { method: 'insertOrUpdateUser', field: 'Username' });
+ throw new Meteor.Error('error-the-field-is-required', 'The field Username is required', {
+ method: 'insertOrUpdateUser',
+ field: 'Username'
+ });
}
let nameValidation;
@@ -39,20 +57,33 @@ RocketChat.saveUser = function(userId, userData) {
}
if (userData.username && !nameValidation.test(userData.username)) {
- throw new Meteor.Error('error-input-is-not-a-valid-field', `${ _.escape(userData.username) } is not a valid username`, { method: 'insertOrUpdateUser', input: userData.username, field: 'Username' });
+ throw new Meteor.Error('error-input-is-not-a-valid-field', `${ _.escape(userData.username) } is not a valid username`, {
+ method: 'insertOrUpdateUser',
+ input: userData.username,
+ field: 'Username'
+ });
}
if (!userData._id && !userData.password) {
- throw new Meteor.Error('error-the-field-is-required', 'The field Password is required', { method: 'insertOrUpdateUser', field: 'Password' });
+ throw new Meteor.Error('error-the-field-is-required', 'The field Password is required', {
+ method: 'insertOrUpdateUser',
+ field: 'Password'
+ });
}
if (!userData._id) {
if (!RocketChat.checkUsernameAvailability(userData.username)) {
- throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, { method: 'insertOrUpdateUser', field: userData.username });
+ throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, {
+ method: 'insertOrUpdateUser',
+ field: userData.username
+ });
}
if (userData.email && !RocketChat.checkEmailAvailability(userData.email)) {
- throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.email) } is already in use :(`, { method: 'insertOrUpdateUser', field: userData.email });
+ throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.email) } is already in use :(`, {
+ method: 'insertOrUpdateUser',
+ field: userData.email
+ });
}
RocketChat.validateEmailDomain(userData.email);
@@ -73,7 +104,7 @@ RocketChat.saveUser = function(userId, userData) {
$set: {
name: userData.name,
roles: userData.roles || ['user'],
- settings: userData.settings
+ settings: userData.settings || {}
}
};
@@ -81,8 +112,8 @@ RocketChat.saveUser = function(userId, userData) {
updateUser.$set.requirePasswordChange = userData.requirePasswordChange;
}
- if (userData.verified) {
- updateUser.$set['emails.0.verified'] = true;
+ if (typeof userData.verified === 'boolean') {
+ updateUser.$set['emails.0.verified'] = userData.verified;
}
Meteor.users.update({ _id }, updateUser);
@@ -120,7 +151,10 @@ RocketChat.saveUser = function(userId, userData) {
try {
Email.send(email);
} catch (error) {
- throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, { function: 'RocketChat.saveUser', message: error.message });
+ throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, {
+ function: 'RocketChat.saveUser',
+ message: error.message
+ });
}
});
}
@@ -128,7 +162,7 @@ RocketChat.saveUser = function(userId, userData) {
userData._id = _id;
if (RocketChat.settings.get('Accounts_SetDefaultAvatar') === true && userData.email) {
- const gravatarUrl = Gravatar.imageUrl(userData.email, {default: '404', size: 200, secure: true});
+ const gravatarUrl = Gravatar.imageUrl(userData.email, { default: '404', size: 200, secure: true });
try {
RocketChat.setUserAvatar(userData, gravatarUrl, '', 'url');
@@ -149,7 +183,8 @@ RocketChat.saveUser = function(userId, userData) {
}
if (userData.email) {
- RocketChat.setEmail(userData._id, userData.email);
+ const shouldSendVerificationEmailToUser = userData.verified !== true;
+ RocketChat.setEmail(userData._id, userData.email, shouldSendVerificationEmailToUser);
}
if (userData.password && userData.password.trim() && RocketChat.authz.hasPermission(userId, 'edit-other-user-password')) {
@@ -172,7 +207,9 @@ RocketChat.saveUser = function(userId, userData) {
updateUser.$set.requirePasswordChange = userData.requirePasswordChange;
}
- updateUser.$set['emails.0.verified'] = !!userData.verified;
+ if (typeof userData.verified === 'boolean') {
+ updateUser.$set['emails.0.verified'] = userData.verified;
+ }
Meteor.users.update({ _id: userData._id }, updateUser);
diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js
index 197ca28729f..baf979687e5 100644
--- a/packages/rocketchat-lib/server/functions/sendMessage.js
+++ b/packages/rocketchat-lib/server/functions/sendMessage.js
@@ -4,22 +4,27 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) {
if (!user || !message || !room._id) {
return false;
}
- if (message.ts == null) {
- message.ts = new Date();
- }
- message.u = _.pick(user, ['_id', 'username', 'name']);
+
if (!Match.test(message.msg, String)) {
message.msg = '';
}
+
+ if (message.ts == null) {
+ message.ts = new Date();
+ }
+
message.rid = room._id;
+ message.u = _.pick(user, ['_id', 'username', 'name']);
+
if (!room.usernames || room.usernames.length === 0) {
const updated_room = RocketChat.models.Rooms.findOneById(room._id);
- if (updated_room != null) {
+ if (updated_room) {
room = updated_room;
} else {
room.usernames = [];
}
}
+
if (message.parseUrls !== false) {
const urls = message.msg.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\(\)\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g);
@@ -44,6 +49,17 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) {
sandstormSessionId = message.sandstormSessionId;
delete message.sandstormSessionId;
}
+
+ // For the Rocket.Chat Apps :)
+ if (Apps && Apps.isLoaded()) {
+ const prevent = Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentPrevent', message);
+ if (prevent) {
+ return false;
+ }
+
+ // TODO: The rest of the IPreMessageSent events
+ }
+
if (message._id && upsert) {
const _id = message._id;
delete message._id;
@@ -56,6 +72,10 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) {
message._id = RocketChat.models.Messages.insert(message);
}
+ if (Apps && Apps.isLoaded()) {
+ Apps.getBridges().getListenerBridge().messageEvent('IPostMessageSent', message);
+ }
+
/*
Defer other updates as their return is not interesting to the user
*/
diff --git a/packages/rocketchat-lib/server/functions/setEmail.js b/packages/rocketchat-lib/server/functions/setEmail.js
index b63f39cb832..9d1819cfd1f 100644
--- a/packages/rocketchat-lib/server/functions/setEmail.js
+++ b/packages/rocketchat-lib/server/functions/setEmail.js
@@ -1,6 +1,6 @@
import s from 'underscore.string';
-RocketChat._setEmail = function(userId, email) {
+RocketChat._setEmail = function(userId, email, shouldSendVerificationEmail = true) {
email = s.trim(email);
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: '_setEmail' });
@@ -27,6 +27,9 @@ RocketChat._setEmail = function(userId, email) {
// Set new email
RocketChat.models.Users.setEmail(user._id, email);
user.email = email;
+ if (shouldSendVerificationEmail === true) {
+ Meteor.call('sendConfirmationEmail', user.email);
+ }
return user;
};
diff --git a/packages/rocketchat-lib/server/methods/leaveRoom.js b/packages/rocketchat-lib/server/methods/leaveRoom.js
index 603e785acdb..dd13f2e67ba 100644
--- a/packages/rocketchat-lib/server/methods/leaveRoom.js
+++ b/packages/rocketchat-lib/server/methods/leaveRoom.js
@@ -12,7 +12,7 @@ Meteor.methods({
const room = RocketChat.models.Rooms.findOneById(rid);
const user = Meteor.user();
- if (room.t === 'd') {
+ if (room.t === 'd' || (room.t === 'c' && !RocketChat.authz.hasPermission(user._id, 'leave-c')) || (room.t === 'p' && !RocketChat.authz.hasPermission(user._id, 'leave-p'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'leaveRoom' });
}
diff --git a/packages/rocketchat-lib/server/methods/sendMessage.js b/packages/rocketchat-lib/server/methods/sendMessage.js
index 03e4272efaf..40b7e55f219 100644
--- a/packages/rocketchat-lib/server/methods/sendMessage.js
+++ b/packages/rocketchat-lib/server/methods/sendMessage.js
@@ -44,7 +44,7 @@ Meteor.methods({
}
const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId());
- if (subscription && subscription.blocked || subscription.blocker) {
+ if (subscription && (subscription.blocked || subscription.blocker)) {
RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', {
_id: Random.id(),
rid: room._id,
diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js
index a52fcdaa184..2bb631520dd 100644
--- a/packages/rocketchat-lib/server/models/Messages.js
+++ b/packages/rocketchat-lib/server/models/Messages.js
@@ -500,6 +500,22 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base {
return this.update(query, update);
}
+ unlinkUserId(userId, newUserId, newUsername, newNameAlias) {
+ const query = {
+ 'u._id': userId
+ };
+
+ const update = {
+ $set: {
+ 'alias': newNameAlias,
+ 'u._id': newUserId,
+ 'u.username' : newUsername,
+ 'u.name' : undefined
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
// INSERT
createWithTypeRoomIdMessageAndUser(type, roomId, message, user, extraData) {
diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js
index ced650e5436..cc7806c8e15 100644
--- a/packages/rocketchat-lib/server/models/Rooms.js
+++ b/packages/rocketchat-lib/server/models/Rooms.js
@@ -246,6 +246,16 @@ class ModelRooms extends RocketChat.models._Base {
return this.find(query, options);
}
+ findByNameAndType(name, type, options) {
+ const query = {
+ t: type,
+ name
+ };
+
+ // do not use cache
+ return this._db.find(query, options);
+ }
+
findByNameAndTypeNotDefault(name, type, options) {
const query = {
t: type,
diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js
index 726a0b4299c..9e585150bf0 100644
--- a/packages/rocketchat-lib/server/models/Subscriptions.js
+++ b/packages/rocketchat-lib/server/models/Subscriptions.js
@@ -30,16 +30,16 @@ class ModelSubscriptions extends RocketChat.models._Base {
// FIND ONE
- findOneByRoomIdAndUserId(roomId, userId) {
+ findOneByRoomIdAndUserId(roomId, userId, options) {
if (this.useCache) {
- return this.cache.findByIndex('rid,u._id', [roomId, userId]).fetch();
+ return this.cache.findByIndex('rid,u._id', [roomId, userId], options).fetch();
}
const query = {
rid: roomId,
'u._id': userId
};
- return this.findOne(query);
+ return this.findOne(query, options);
}
findOneByRoomNameAndUserId(roomName, userId) {
@@ -61,7 +61,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
const query =
- {'u._id': userId};
+ { 'u._id': userId };
return this.find(query, options);
}
@@ -123,7 +123,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
const query =
- {rid: roomId};
+ { rid: roomId };
return this.find(query, options);
}
@@ -140,7 +140,9 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
getLastSeen(options) {
- if (options == null) { options = {}; }
+ if (options == null) {
+ options = {};
+ }
const query = { ls: { $exists: 1 } };
options.sort = { ls: -1 };
options.limit = 1;
@@ -198,7 +200,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
// UPDATE
archiveByRoomId(roomId) {
const query =
- {rid: roomId};
+ { rid: roomId };
const update = {
$set: {
@@ -213,7 +215,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
unarchiveByRoomId(roomId) {
const query =
- {rid: roomId};
+ { rid: roomId };
const update = {
$set: {
@@ -311,7 +313,9 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
setFavoriteByRoomIdAndUserId(roomId, userId, favorite) {
- if (favorite == null) { favorite = true; }
+ if (favorite == null) {
+ favorite = true;
+ }
const query = {
rid: roomId,
'u._id': userId
@@ -328,7 +332,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
updateNameAndAlertByRoomId(roomId, name, fname) {
const query =
- {rid: roomId};
+ { rid: roomId };
const update = {
$set: {
@@ -343,7 +347,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
updateNameByRoomId(roomId, name) {
const query =
- {rid: roomId};
+ { rid: roomId };
const update = {
$set: {
@@ -356,7 +360,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
setUserUsernameByUserId(userId, username) {
const query =
- {'u._id': userId};
+ { 'u._id': userId };
const update = {
$set: {
@@ -383,7 +387,9 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
incUnreadForRoomIdExcludingUserId(roomId, userId, inc) {
- if (inc == null) { inc = 1; }
+ if (inc == null) {
+ inc = 1;
+ }
const query = {
rid: roomId,
'u._id': {
@@ -447,6 +453,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
return this.update(query, update, { multi: true });
}
+
setAlertForRoomIdExcludingUserId(roomId, userId) {
const query = {
rid: roomId,
@@ -522,7 +529,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
updateTypeByRoomId(roomId, type) {
const query =
- {rid: roomId};
+ { rid: roomId };
const update = {
$set: {
@@ -535,7 +542,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
addRoleById(_id, role) {
const query =
- {_id};
+ { _id };
const update = {
$addToSet: {
@@ -548,7 +555,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
removeRoleById(_id, role) {
const query =
- {_id};
+ { _id };
const update = {
$pull: {
@@ -604,14 +611,14 @@ class ModelSubscriptions extends RocketChat.models._Base {
// REMOVE
removeByUserId(userId) {
const query =
- {'u._id': userId};
+ { 'u._id': userId };
return this.remove(query);
}
removeByRoomId(roomId) {
const query =
- {rid: roomId};
+ { rid: roomId };
return this.remove(query);
}
diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js
index 41ed1fe2926..9217dad7dc8 100644
--- a/packages/rocketchat-lib/server/models/Users.js
+++ b/packages/rocketchat-lib/server/models/Users.js
@@ -435,6 +435,16 @@ class ModelUsers extends RocketChat.models._Base {
return this.update(_id, update);
}
+ clearSettings(_id) {
+ const update = {
+ $set: {
+ settings: {}
+ }
+ };
+
+ return this.update(_id, update);
+ }
+
setPreferences(_id, preferences) {
const settings = Object.assign(
{},
diff --git a/packages/rocketchat-lib/server/oauth/facebook.js b/packages/rocketchat-lib/server/oauth/facebook.js
new file mode 100644
index 00000000000..20e47b9cfa3
--- /dev/null
+++ b/packages/rocketchat-lib/server/oauth/facebook.js
@@ -0,0 +1,63 @@
+import _ from 'underscore';
+import { OAuth } from 'meteor/oauth';
+const crypto = Npm.require('crypto');
+const whitelisted = [
+ 'id',
+ 'email',
+ 'name',
+ 'first_name',
+ 'last_name',
+ 'link',
+ 'gender',
+ 'locale',
+ 'age_range'];
+
+const FB_API_VERSION = 'v2.9';
+const FB_URL = 'https://graph.facebook.com';
+
+const getIdentity = function(accessToken, fields, secret) {
+ const hmac = crypto.createHmac('sha256', OAuth.openSecret(secret));
+ hmac.update(accessToken);
+
+ try {
+ return HTTP.get(`${ FB_URL }/${ FB_API_VERSION }/me`, {
+ params: {
+ access_token: accessToken,
+ appsecret_proof: hmac.digest('hex'),
+ fields: fields.join(',')
+ }
+ }).data;
+ } catch (err) {
+ throw _.extend(new Error(`Failed to fetch identity from Facebook. ${ err.message }`),
+ {response: err.response});
+ }
+};
+
+RocketChat.registerAccessTokenService('facebook', function(options) {
+ check(options, Match.ObjectIncluding({
+ accessToken: String,
+ secret: String,
+ expiresIn: Match.Integer,
+ identity: Match.Maybe(Object)
+ }));
+
+ const identity = options.identity || getIdentity(options.accessToken, whitelisted, options.secret);
+
+ const serviceData = {
+ accessToken: options.accessToken,
+ expiresAt: (+new Date) + (1000 * parseInt(options.expiresIn, 10))
+ };
+
+ const fields = _.pick(identity, whitelisted);
+ _.extend(serviceData, fields);
+
+ return {
+ serviceData,
+ options: {
+ profile: {
+ name: identity.name
+ }
+ }
+ };
+});
+
diff --git a/packages/rocketchat-lib/server/oauth/twitter.js b/packages/rocketchat-lib/server/oauth/twitter.js
new file mode 100644
index 00000000000..bb0def48395
--- /dev/null
+++ b/packages/rocketchat-lib/server/oauth/twitter.js
@@ -0,0 +1,58 @@
+import Twit from 'twit';
+import _ from 'underscore';
+
+const whitelistedFields = [
+ 'id',
+ 'name',
+ 'description',
+ 'profile_image_url',
+ 'profile_image_url_https',
+ 'lang',
+ 'email'
+];
+
+const getIdentity = function(accessToken, appId, appSecret, accessTokenSecret) {
+ const Twitter = new Twit({
+ consumer_key: appId,
+ consumer_secret: appSecret,
+ access_token: accessToken,
+ access_token_secret: accessTokenSecret
+ });
+ const syncTwitter = Meteor.wrapAsync(Twitter.get, Twitter);
+ try {
+ return syncTwitter('account/verify_credentials.json?include_email=true');
+ } catch (err) {
+ throw _.extend(new Error(`Failed to fetch identity from Twwiter. ${ err.message }`),
+ {response: err.response});
+ }
+};
+
+RocketChat.registerAccessTokenService('twitter', function(options) {
+ check(options, Match.ObjectIncluding({
+ accessToken: String,
+ appSecret: String,
+ appId: String,
+ accessTokenSecret: String,
+ expiresIn: Match.Integer,
+ identity: Match.Maybe(Object)
+ }));
+
+ const identity = options.identity || getIdentity(options.accessToken, options.appId, options.appSecret, options.accessTokenSecret);
+
+ const serviceData = {
+ accessToken: options.accessToken,
+ expiresAt: (+new Date) + (1000 * parseInt(options.expiresIn, 10))
+ };
+
+ const fields = _.pick(identity, whitelistedFields);
+ _.extend(serviceData, fields);
+
+ return {
+ serviceData,
+ options: {
+ profile: {
+ name: identity.name
+ }
+ }
+ };
+});
diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js
index 42f4bb23f84..9b0904c01a3 100644
--- a/packages/rocketchat-lib/server/startup/settings.js
+++ b/packages/rocketchat-lib/server/startup/settings.js
@@ -83,6 +83,14 @@ RocketChat.settings.addGroup('Accounts', function() {
public: true
});
+ this.section('Two Factor Authentication', function() {
+ this.add('Accounts_TwoFactorAuthentication_MaxDelta', 1, {
+ type: 'int',
+ public: true,
+ i18nLabel: 'Accounts_TwoFactorAuthentication_MaxDelta'
+ });
+ });
+
this.section('Registration', function() {
this.add('Accounts_DefaultUsernamePrefixSuggestion', 'user', {
type: 'string'
@@ -310,48 +318,58 @@ RocketChat.settings.addGroup('Accounts', function() {
'public': true,
i18nLabel: 'Sidebar_list_mode'
});
- this.add('Accounts_Default_User_Preferences_mergeChannels', false, {
- type: 'boolean',
- 'public': true,
- i18nLabel: 'UI_Merge_Channels_Groups'
- });
- this.add('Accounts_Default_User_Preferences_sendOnEnter', 'normal', {
+ this.add('Accounts_Default_User_Preferences_sidebarViewMode', 'medium', {
type: 'select',
values: [
{
- key: 'normal',
- i18nLabel: 'Enter_Normal'
+ key: 'extended',
+ i18nLabel: 'Extended'
},
{
- key: 'alternative',
- i18nLabel: 'Enter_Alternative'
+ key: 'medium',
+ i18nLabel: 'Medium'
},
{
- key: 'desktop',
- i18nLabel: 'Only_On_Desktop'
+ key: 'condensed',
+ i18nLabel: 'Condensed'
}
],
'public': true,
- i18nLabel: 'Enter_Behaviour'
+ i18nLabel: 'Sidebar_list_mode'
+ });
+ this.add('Accounts_Default_User_Preferences_sidebarHideAvatar', false, {
+ type: 'boolean',
+ 'public': true,
+ i18nLabel: 'Hide_Avatars'
+ });
+ this.add('Accounts_Default_User_Preferences_sidebarShowUnread', false, {
+ type: 'boolean',
+ 'public': true,
+ i18nLabel: 'Unread_on_top'
});
- this.add('Accounts_Default_User_Preferences_viewMode', 0, {
+ this.add('Accounts_Default_User_Preferences_sidebarShowFavorites', true, {
+ type: 'boolean',
+ 'public': true,
+ i18nLabel: 'Group_favorites'
+ });
+ this.add('Accounts_Default_User_Preferences_sendOnEnter', 'normal', {
type: 'select',
values: [
{
- key: 0,
- i18nLabel: 'Normal'
+ key: 'normal',
+ i18nLabel: 'Enter_Normal'
},
{
- key: 1,
- i18nLabel: 'Cozy'
+ key: 'alternative',
+ i18nLabel: 'Enter_Alternative'
},
{
- key: 2,
- i18nLabel: 'Compact'
+ key: 'desktop',
+ i18nLabel: 'Only_On_Desktop'
}
],
'public': true,
- i18nLabel: 'View_mode'
+ i18nLabel: 'Enter_Behaviour'
});
this.add('Accounts_Default_User_Preferences_emailNotificationMode', 'all', {
type: 'select',
@@ -1177,11 +1195,17 @@ RocketChat.settings.addGroup('Message', function() {
'public': true,
i18nDescription: 'Message_Attachments_GroupAttachDescription'
});
+ });
+ this.section('Message_Audio', function() {
this.add('Message_AudioRecorderEnabled', true, {
type: 'boolean',
'public': true,
i18nDescription: 'Message_AudioRecorderEnabledDescription'
});
+ this.add('Message_Audio_bitRate', 32, {
+ type: 'int',
+ 'public': true
+ });
});
this.add('Message_AllowEditing', true, {
type: 'boolean',
@@ -1329,6 +1353,23 @@ RocketChat.settings.addGroup('Message', function() {
'public': true,
alert: 'This feature is currently in beta and could decrease the application performance! Please report bugs to github.com/RocketChat/Rocket.Chat/issues'
});
+
+ this.add('Message_ErasureType', 'Delete', {
+ type: 'select',
+ 'public': true,
+ values: [
+ {
+ key: 'Keep',
+ i18nLabel: 'Message_ErasureType_Keep'
+ }, {
+ key: 'Delete',
+ i18nLabel: 'Message_ErasureType_Delete'
+ }, {
+ key: 'Unlink',
+ i18nLabel: 'Message_ErasureType_Unlink'
+ }
+ ]
+ });
});
RocketChat.settings.addGroup('Meta', function() {
diff --git a/packages/rocketchat-livechat/.app/client/views/loading.html b/packages/rocketchat-livechat/.app/client/views/loading.html
index e13a3f77ba1..77310699f18 100644
--- a/packages/rocketchat-livechat/.app/client/views/loading.html
+++ b/packages/rocketchat-livechat/.app/client/views/loading.html
@@ -1,8 +1,8 @@
{{_ "Connecting to an Agent"}}
-
-
-
+
+
+
-
\ No newline at end of file
+
diff --git a/packages/rocketchat-livechat/.app/client/views/messages.js b/packages/rocketchat-livechat/.app/client/views/messages.js
index 9d298fa207c..f27e7d00f2d 100644
--- a/packages/rocketchat-livechat/.app/client/views/messages.js
+++ b/packages/rocketchat-livechat/.app/client/views/messages.js
@@ -79,7 +79,9 @@ Template.messages.helpers({
agentData.email = agent.emails[0].address;
}
- if (agent.customFields && agent.customFields.phone) {
+ if (agent.phone && agent.phone.length > 0) {
+ agentData.phone = agent.phone[0].phoneNumber;
+ } else if (agent.customFields && agent.customFields.phone) {
agentData.phone = agent.customFields.phone;
}
diff --git a/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.html b/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.html
index 04bb8832d1f..119d0f7859d 100644
--- a/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.html
+++ b/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.html
@@ -32,6 +32,18 @@
{{_ "Send_request_on_offline_messages"}}
+
+
+
+
+
+
diff --git a/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.js b/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.js
index 2a560232836..cc373fb2f44 100644
--- a/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.js
+++ b/packages/rocketchat-livechat/client/views/app/integrations/livechatIntegrationWebhook.js
@@ -22,6 +22,14 @@ Template.livechatIntegrationWebhook.helpers({
sendOnOfflineChecked() {
const setting = LivechatIntegration.findOne('Livechat_webhook_on_offline_msg');
return setting && setting.value;
+ },
+ sendOnVisitorMessageChecked() {
+ const setting = LivechatIntegration.findOne('Livechat_webhook_on_visitor_message');
+ return setting && setting.value;
+ },
+ sendOnAgentMessageChecked() {
+ const setting = LivechatIntegration.findOne('Livechat_webhook_on_agent_message');
+ return setting && setting.value;
}
});
@@ -62,11 +70,15 @@ Template.livechatIntegrationWebhook.events({
const secretToken = LivechatIntegration.findOne('Livechat_secret_token');
const webhookOnClose = LivechatIntegration.findOne('Livechat_webhook_on_close');
const webhookOnOfflineMsg = LivechatIntegration.findOne('Livechat_webhook_on_offline_msg');
+ const webhookOnVisitorMessage = LivechatIntegration.findOne('Livechat_webhook_on_visitor_message');
+ const webhookOnAgentMessage = LivechatIntegration.findOne('Livechat_webhook_on_agent_message');
instance.$('#webhookUrl').val(webhookUrl && webhookUrl.value);
instance.$('#secretToken').val(secretToken && secretToken.value);
instance.$('#sendOnClose').get(0).checked = webhookOnClose && webhookOnClose.value;
instance.$('#sendOnOffline').get(0).checked = webhookOnOfflineMsg && webhookOnOfflineMsg.value;
+ instance.$('#sendOnVisitorMessage').get(0).checked = webhookOnVisitorMessage && webhookOnVisitorMessage.value;
+ instance.$('#sendOnAgentMessage').get(0).checked = webhookOnAgentMessage && webhookOnAgentMessage.value;
instance.disableTest.set(!webhookUrl || _.isEmpty(webhookUrl.value));
},
@@ -77,7 +89,9 @@ Template.livechatIntegrationWebhook.events({
'Livechat_webhookUrl': s.trim(instance.$('#webhookUrl').val()),
'Livechat_secret_token': s.trim(instance.$('#secretToken').val()),
'Livechat_webhook_on_close': instance.$('#sendOnClose').get(0).checked,
- 'Livechat_webhook_on_offline_msg': instance.$('#sendOnOffline').get(0).checked
+ 'Livechat_webhook_on_offline_msg': instance.$('#sendOnOffline').get(0).checked,
+ 'Livechat_webhook_on_visitor_message': instance.$('#sendOnVisitorMessage').get(0).checked,
+ 'Livechat_webhook_on_agent_message': instance.$('#sendOnAgentMessage').get(0).checked
};
Meteor.call('livechat:saveIntegration', settings, (err) => {
if (err) {
diff --git a/packages/rocketchat-livechat/client/views/app/livechatIntegrations.html b/packages/rocketchat-livechat/client/views/app/livechatIntegrations.html
index 61670e4cad5..3fdc6955038 100644
--- a/packages/rocketchat-livechat/client/views/app/livechatIntegrations.html
+++ b/packages/rocketchat-livechat/client/views/app/livechatIntegrations.html
@@ -32,6 +32,18 @@
{{_ "Send_request_on_offline_messages"}}
+
+
+
+
+
+
diff --git a/packages/rocketchat-livechat/config.js b/packages/rocketchat-livechat/config.js
index 3a7815a4d6e..c69df7adcea 100644
--- a/packages/rocketchat-livechat/config.js
+++ b/packages/rocketchat-livechat/config.js
@@ -131,6 +131,20 @@ Meteor.startup(function() {
i18nLabel: 'Send_request_on_offline_messages'
});
+ RocketChat.settings.add('Livechat_webhook_on_visitor_message', false, {
+ type: 'boolean',
+ group: 'Livechat',
+ section: 'CRM_Integration',
+ i18nLabel: 'Send_request_on_visitor_message'
+ });
+
+ RocketChat.settings.add('Livechat_webhook_on_agent_message', false, {
+ type: 'boolean',
+ group: 'Livechat',
+ section: 'CRM_Integration',
+ i18nLabel: 'Send_request_on_agent_message'
+ });
+
RocketChat.settings.add('Livechat_webhook_on_capture', false, {
type: 'boolean',
group: 'Livechat',
diff --git a/packages/rocketchat-livechat/roomType.js b/packages/rocketchat-livechat/roomType.js
index 5a012f7edf5..27eeb36c958 100644
--- a/packages/rocketchat-livechat/roomType.js
+++ b/packages/rocketchat-livechat/roomType.js
@@ -25,7 +25,7 @@ class LivechatRoomType extends RoomTypeConfig {
super({
identifier: 'l',
order: 5,
- icon: 'livechat',
+ // icon: 'livechat',
label: 'Livechat',
route: new LivechatRoomRoute()
});
diff --git a/packages/rocketchat-livechat/server/hooks/sendToCRM.js b/packages/rocketchat-livechat/server/hooks/sendToCRM.js
index 8ae0073ff27..46e48dcc70f 100644
--- a/packages/rocketchat-livechat/server/hooks/sendToCRM.js
+++ b/packages/rocketchat-livechat/server/hooks/sendToCRM.js
@@ -5,15 +5,24 @@ function sendToCRM(type, room, includeMessages = true) {
postData.messages = [];
- if (includeMessages) {
- RocketChat.models.Messages.findVisibleByRoomId(room._id, { sort: { ts: 1 } }).forEach((message) => {
+ let messages;
+ if (typeof includeMessages === 'boolean' && includeMessages) {
+ messages = RocketChat.models.Messages.findVisibleByRoomId(room._id, { sort: { ts: 1 } });
+ } else if (includeMessages instanceof Array) {
+ messages = includeMessages;
+ }
+
+ if (messages) {
+ messages.forEach((message) => {
if (message.t) {
return;
}
const msg = {
+ _id: message._id,
username: message.u.username,
msg: message.msg,
- ts: message.ts
+ ts: message.ts,
+ editedAt: message.editedAt
};
if (message.u.username !== postData.visitor.username) {
@@ -49,6 +58,31 @@ RocketChat.callbacks.add('livechat.saveInfo', (room) => {
return sendToCRM('LivechatEdit', room);
}, RocketChat.callbacks.priority.MEDIUM, 'livechat-send-crm-save-info');
+RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
+ // only call webhook if it is a livechat room
+ if (room.t !== 'l' || room.v == null || room.v.token == null) {
+ return message;
+ }
+
+ // if the message has a token, it was sent from the visitor
+ // if not, it was sent from the agent
+ if (message.token) {
+ if (!RocketChat.settings.get('Livechat_webhook_on_visitor_message')) {
+ return message;
+ }
+ } else if (!RocketChat.settings.get('Livechat_webhook_on_agent_message')) {
+ return message;
+ }
+
+ // if the message has a type means it is a special message (like the closing comment), so skips
+ if (message.t) {
+ return message;
+ }
+
+ sendToCRM('Message', room, [message]);
+ return message;
+}, RocketChat.callbacks.priority.MEDIUM, 'livechat-send-crm-message');
+
RocketChat.callbacks.add('livechat.leadCapture', (room) => {
if (!RocketChat.settings.get('Livechat_webhook_on_capture')) {
return room;
diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js
index 99079cb7f6b..b614a7096bb 100644
--- a/packages/rocketchat-livechat/server/lib/Livechat.js
+++ b/packages/rocketchat-livechat/server/lib/Livechat.js
@@ -415,6 +415,7 @@ RocketChat.Livechat = {
customFields: room.livechatData,
visitor: {
_id: visitor._id,
+ token: visitor.token,
name: visitor.name,
username: visitor.username,
email: null,
diff --git a/packages/rocketchat-livechat/server/methods/saveIntegration.js b/packages/rocketchat-livechat/server/methods/saveIntegration.js
index f839f8adbe6..023d4ce95c1 100644
--- a/packages/rocketchat-livechat/server/methods/saveIntegration.js
+++ b/packages/rocketchat-livechat/server/methods/saveIntegration.js
@@ -22,6 +22,14 @@ Meteor.methods({
RocketChat.settings.updateById('Livechat_webhook_on_offline_msg', !!values['Livechat_webhook_on_offline_msg']);
}
+ if (typeof values['Livechat_webhook_on_visitor_message'] !== 'undefined') {
+ RocketChat.settings.updateById('Livechat_webhook_on_visitor_message', !!values['Livechat_webhook_on_visitor_message']);
+ }
+
+ if (typeof values['Livechat_webhook_on_agent_message'] !== 'undefined') {
+ RocketChat.settings.updateById('Livechat_webhook_on_agent_message', !!values['Livechat_webhook_on_agent_message']);
+ }
+
return;
}
});
diff --git a/packages/rocketchat-livechat/server/models/Users.js b/packages/rocketchat-livechat/server/models/Users.js
index 65a8142a1b7..55f7c8799c0 100644
--- a/packages/rocketchat-livechat/server/models/Users.js
+++ b/packages/rocketchat-livechat/server/models/Users.js
@@ -167,6 +167,7 @@ RocketChat.models.Users.getAgentInfo = function(agentId) {
fields: {
name: 1,
username: 1,
+ phone: 1,
customFields: 1
}
};
diff --git a/packages/rocketchat-livechat/server/publications/livechatIntegration.js b/packages/rocketchat-livechat/server/publications/livechatIntegration.js
index 656ea19712d..455c5388c71 100644
--- a/packages/rocketchat-livechat/server/publications/livechatIntegration.js
+++ b/packages/rocketchat-livechat/server/publications/livechatIntegration.js
@@ -9,7 +9,7 @@ Meteor.publish('livechat:integration', function() {
const self = this;
- const handle = RocketChat.models.Settings.findByIds(['Livechat_webhookUrl', 'Livechat_secret_token', 'Livechat_webhook_on_close', 'Livechat_webhook_on_offline_msg']).observeChanges({
+ const handle = RocketChat.models.Settings.findByIds(['Livechat_webhookUrl', 'Livechat_secret_token', 'Livechat_webhook_on_close', 'Livechat_webhook_on_offline_msg', 'Livechat_webhook_on_visitor_message', 'Livechat_webhook_on_agent_message']).observeChanges({
added(id, fields) {
self.added('livechatIntegration', id, fields);
},
diff --git a/packages/rocketchat-mentions/package.js b/packages/rocketchat-mentions/package.js
index 55e35d9efff..8ea9346b873 100644
--- a/packages/rocketchat-mentions/package.js
+++ b/packages/rocketchat-mentions/package.js
@@ -12,5 +12,6 @@ Package.onUse(function(api) {
]);
api.addFiles('server/server.js', 'server');
+ api.addFiles('server/methods/getUserMentionsByChannel.js', 'server');
api.addFiles('client/client.js', 'client');
});
diff --git a/packages/rocketchat-mentions/server/methods/getUserMentionsByChannel.js b/packages/rocketchat-mentions/server/methods/getUserMentionsByChannel.js
new file mode 100644
index 00000000000..6c2bba4ce3e
--- /dev/null
+++ b/packages/rocketchat-mentions/server/methods/getUserMentionsByChannel.js
@@ -0,0 +1,19 @@
+Meteor.methods({
+ getUserMentionsByChannel({ roomId, options }) {
+ check(roomId, String);
+
+ if (!Meteor.userId()) {
+ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getUserMentionsByChannel' });
+ }
+
+ const room = RocketChat.models.Rooms.findOneById(roomId);
+
+ if (!room) {
+ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUserMentionsByChannel' });
+ }
+
+ const user = RocketChat.models.Users.findOneById(Meteor.userId());
+
+ return RocketChat.models.Messages.findVisibleByMentionAndRoomId(user.username, roomId, options).fetch();
+ }
+});
diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.html b/packages/rocketchat-message-attachments/client/messageAttachment.html
index 53dfa169a67..35b9b5ec47f 100644
--- a/packages/rocketchat-message-attachments/client/messageAttachment.html
+++ b/packages/rocketchat-message-attachments/client/messageAttachment.html
@@ -10,9 +10,9 @@
{{#if author_icon}}

{{/if}}
-
{{author_name}}
+
{{author_name}}
{{#if ts}}
-
{{#if message_link}}{{time}}{{else}}{{time}}{{/if}}
+
{{#if message_link}}{{time}}{{else}}{{time}}{{/if}}
{{/if}}
{{else}}
@@ -22,7 +22,7 @@
{{/if}}
{{author_name}}
{{#if ts}}
- {{#if message_link}}{{time}}{{else}}{{time}}{{/if}}
+ {{#if message_link}}{{time}}{{else}}{{time}}{{/if}}
{{/if}}
{{/if}}
@@ -30,9 +30,9 @@
{{#if title}}
{{#if title_link}}
-
{{#if isFile}} {{_ "Attachment_File_Uploaded"}}: {{/if}}{{title}}
+
{{#if isFile}} {{_ "Attachment_File_Uploaded"}}: {{/if}}{{title}}
{{#if title_link_download}}
-
+
{{/if}}
{{else}}
{{title}}
diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.js b/packages/rocketchat-message-attachments/client/messageAttachment.js
index 8da6606bca3..d0df4d8f3b5 100644
--- a/packages/rocketchat-message-attachments/client/messageAttachment.js
+++ b/packages/rocketchat-message-attachments/client/messageAttachment.js
@@ -22,7 +22,7 @@ const fixCordova = function(url) {
} else if (navigator.userAgent.indexOf('Electron') > -1) {
return __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + url;
} else {
- return Meteor.absoluteUrl().replace(/\/$/, '') + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + url;
+ return Meteor.absoluteUrl().replace(/\/$/, '') + url;
}
};
/*globals renderMessageBody*/
diff --git a/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js b/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
index 365d0b3f6c5..1bf4ec2ac84 100644
--- a/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
+++ b/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
@@ -1,49 +1,59 @@
Meteor.methods({
- saveNotificationSettings(rid, field, value) {
+ saveNotificationSettings(roomId, field, value) {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'saveNotificationSettings' });
}
-
- check(rid, String);
+ check(roomId, String);
check(field, String);
check(value, String);
- if (['audioNotifications', 'desktopNotifications', 'mobilePushNotifications', 'emailNotifications', 'unreadAlert', 'disableNotifications', 'hideUnreadStatus'].indexOf(field) === -1) {
+ const notifications = {
+ 'audioNotifications': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateAudioNotificationsById(subscription._id, value)
+ },
+ 'desktopNotifications': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateDesktopNotificationsById(subscription._id, value)
+ },
+ 'mobilePushNotifications': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateMobilePushNotificationsById(subscription._id, value)
+ },
+ 'emailNotifications': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateEmailNotificationsById(subscription._id, value)
+ },
+ 'unreadAlert': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateUnreadAlertById(subscription._id, value)
+ },
+ 'disableNotifications': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateDisableNotificationsById(subscription._id, value === '1')
+ },
+ 'hideUnreadStatus': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateHideUnreadStatusById(subscription._id, value === '1')
+ },
+ 'desktopNotificationDuration': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateDesktopNotificationDurationById(subscription._id, value)
+ },
+ 'audioNotificationValue': {
+ updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateAudioNotificationValueById(subscription._id, value)
+ }
+ };
+ const isInvalidNotification = !Object.keys(notifications).includes(field);
+ const basicValuesForNotifications = ['all', 'mentions', 'nothing', 'default'];
+ const fieldsMustHaveBasicValues = ['emailNotifications', 'audioNotifications', 'mobilePushNotifications', 'desktopNotifications'];
+
+ if (isInvalidNotification) {
throw new Meteor.Error('error-invalid-settings', 'Invalid settings field', { method: 'saveNotificationSettings' });
}
- if (field !== 'hideUnreadStatus' && field !== 'disableNotifications' && ['all', 'mentions', 'nothing', 'default'].indexOf(value) === -1) {
+ if (fieldsMustHaveBasicValues.includes(field) && !basicValuesForNotifications.includes(value)) {
throw new Meteor.Error('error-invalid-settings', 'Invalid settings value', { method: 'saveNotificationSettings' });
}
- const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId());
+ const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, Meteor.userId());
if (!subscription) {
throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { method: 'saveNotificationSettings' });
}
- switch (field) {
- case 'audioNotifications':
- RocketChat.models.Subscriptions.updateAudioNotificationsById(subscription._id, value);
- break;
- case 'desktopNotifications':
- RocketChat.models.Subscriptions.updateDesktopNotificationsById(subscription._id, value);
- break;
- case 'mobilePushNotifications':
- RocketChat.models.Subscriptions.updateMobilePushNotificationsById(subscription._id, value);
- break;
- case 'emailNotifications':
- RocketChat.models.Subscriptions.updateEmailNotificationsById(subscription._id, value);
- break;
- case 'unreadAlert':
- RocketChat.models.Subscriptions.updateUnreadAlertById(subscription._id, value);
- break;
- case 'disableNotifications':
- RocketChat.models.Subscriptions.updateDisableNotificationsById(subscription._id, value === '1' ? true : false);
- break;
- case 'hideUnreadStatus':
- RocketChat.models.Subscriptions.updateHideUnreadStatusById(subscription._id, value === '1' ? true : false);
- break;
- }
+ notifications[field].updateMethod(subscription, value);
return true;
},
diff --git a/packages/rocketchat-reactions/client/init.js b/packages/rocketchat-reactions/client/init.js
index 040caa413e9..115cb80b826 100644
--- a/packages/rocketchat-reactions/client/init.js
+++ b/packages/rocketchat-reactions/client/init.js
@@ -45,13 +45,8 @@ Meteor.startup(function() {
'message-mobile'
],
action(event) {
- const data = Blaze.getData(event.currentTarget);
-
event.stopPropagation();
-
- RocketChat.EmojiPicker.open(event.currentTarget, (emoji) => {
- Meteor.call('setReaction', `:${ emoji }:`, data._arguments[1]._id);
- });
+ RocketChat.EmojiPicker.open(event.currentTarget, emoji => Meteor.call('setReaction', `:${ emoji }:`, this._arguments[1]._id));
},
condition(message) {
const room = RocketChat.models.Rooms.findOne({ _id: message.rid });
diff --git a/packages/rocketchat-reactions/client/methods/setReaction.js b/packages/rocketchat-reactions/client/methods/setReaction.js
index 59d05ae71dc..ec1188f5e40 100644
--- a/packages/rocketchat-reactions/client/methods/setReaction.js
+++ b/packages/rocketchat-reactions/client/methods/setReaction.js
@@ -17,6 +17,8 @@ Meteor.methods({
return false;
} else if (message.private) {
return false;
+ } else if (!RocketChat.emoji.list[reaction] && RocketChat.models.EmojiCustom.findByNameOrAlias(reaction).count() === 0) {
+ return false;
}
if (message.reactions && message.reactions[reaction] && message.reactions[reaction].usernames.indexOf(user.username) !== -1) {
diff --git a/packages/rocketchat-reactions/setReaction.js b/packages/rocketchat-reactions/setReaction.js
index 4027ffcf3bd..9d1b5949405 100644
--- a/packages/rocketchat-reactions/setReaction.js
+++ b/packages/rocketchat-reactions/setReaction.js
@@ -19,6 +19,10 @@ Meteor.methods({
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}
+ if (!RocketChat.emoji.list[reaction] && RocketChat.models.EmojiCustom.findByNameOrAlias(reaction).count() === 0) {
+ throw new Meteor.Error('error-not-allowed', 'Invalid emoji provided.', { method: 'setReaction' });
+ }
+
const user = Meteor.user();
if (Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1 && !room.reactWhenReadOnly) {
diff --git a/packages/rocketchat-theme/client/imports/components/file-list.css b/packages/rocketchat-theme/client/imports/components/file-list.css
new file mode 100644
index 00000000000..b2a7178a7d7
--- /dev/null
+++ b/packages/rocketchat-theme/client/imports/components/file-list.css
@@ -0,0 +1,3 @@
+.attachments__content .attachments__name {
+ overflow: unset;
+}
diff --git a/packages/rocketchat-theme/client/imports/components/main-content.css b/packages/rocketchat-theme/client/imports/components/main-content.css
index 87ba3157fcf..ede6f5a121f 100644
--- a/packages/rocketchat-theme/client/imports/components/main-content.css
+++ b/packages/rocketchat-theme/client/imports/components/main-content.css
@@ -4,6 +4,8 @@
width: 1vw;
height: 100%;
+
+ position: relative;
}
.messages-container .room-icon {
diff --git a/packages/rocketchat-theme/client/imports/components/message-box.css b/packages/rocketchat-theme/client/imports/components/message-box.css
index 5cb83c9ac6c..df01615e11d 100644
--- a/packages/rocketchat-theme/client/imports/components/message-box.css
+++ b/packages/rocketchat-theme/client/imports/components/message-box.css
@@ -138,6 +138,26 @@
& .rc-input__icon-svg--plus {
transition: transform 0.1s linear;
}
+
+ &.cross {
+ color: red;
+ }
+
+ &.loading {
+ cursor: pointer;
+ display: none;
+ &.active {
+ display: flex;
+ }
+ }
+
+ &.mic {
+ display: none;
+ &.active {
+ display: flex;
+ }
+ }
+
}
&__action-menu {
@@ -151,6 +171,33 @@
& [data-small] {
display: none;
}
+
+ &__audio-recording {
+ display: none;
+ position: relative;
+ z-index: -1;
+ &.active{
+ display: flex;
+ z-index: 2;
+ }
+ }
+
+ &__audio-message{
+ &.hidden{
+ z-index: -1;
+ }
+ }
+
+ &__timer-box{
+ display: flex;
+ width: 50px;
+ }
+
+ &__timer-dot{
+ font-size: 1.5em;
+ color: #d60000;
+ line-height: 0.8em;
+ }
}
@media (width <= 500px) {
diff --git a/packages/rocketchat-theme/client/imports/components/popover.css b/packages/rocketchat-theme/client/imports/components/popover.css
index 3353ee43fb1..dc0c207624a 100644
--- a/packages/rocketchat-theme/client/imports/components/popover.css
+++ b/packages/rocketchat-theme/client/imports/components/popover.css
@@ -65,6 +65,13 @@
}
}
+ &__content-scroll {
+
+ .rc-popover__item {
+ display:block;
+ }
+ }
+
&__title {
flex: 1;
@@ -113,6 +120,13 @@
}
}
+ &__label {
+ display: flex;
+
+ cursor: pointer;
+ align-items: center;
+ }
+
&__icon {
display: flex;
diff --git a/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-item.css b/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-item.css
index 98f645f84ee..f68d4a39217 100644
--- a/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-item.css
+++ b/packages/rocketchat-theme/client/imports/components/sidebar/sidebar-item.css
@@ -18,22 +18,6 @@
display: none;
}
-.sidebar--medium .sidebar-item {
- height: var(--sidebar-item-height-medium);
-
- &__picture {
- flex: 0 0 var(--sidebar-item-thumb-size-medium);
-
- width: var(--sidebar-item-thumb-size-medium);
- height: var(--sidebar-item-thumb-size-medium);
- }
-
- &__user-thumb {
- width: var(--sidebar-item-thumb-size-medium);
- height: var(--sidebar-item-thumb-size-medium);
- }
-}
-
.sidebar--extended .sidebar-item {
height: var(--sidebar-item-height-extended);
@@ -44,10 +28,6 @@
height: var(--sidebar-item-thumb-size-extended);
}
- &__icon {
- margin: 0 auto;
- }
-
&__user-thumb {
width: var(--sidebar-item-thumb-size-extended);
height: var(--sidebar-item-thumb-size-extended);
@@ -68,6 +48,22 @@
}
}
+.sidebar--medium .sidebar-item {
+ height: var(--sidebar-item-height-medium);
+
+ &__picture {
+ flex: 0 0 var(--sidebar-item-thumb-size-medium);
+
+ width: var(--sidebar-item-thumb-size-medium);
+ height: var(--sidebar-item-thumb-size-medium);
+ }
+
+ &__user-thumb {
+ width: var(--sidebar-item-thumb-size-medium);
+ height: var(--sidebar-item-thumb-size-medium);
+ }
+}
+
.sidebar-item {
position: relative;
@@ -196,16 +192,11 @@
&__message {
display: flex;
overflow: hidden;
- flex-direction: row;
flex: 1;
margin: 0 -3px;
align-items: center;
justify-content: space-between;
-
- &-top {
- overflow: hidden;
- }
}
&__name {
@@ -299,6 +290,10 @@
}
}
+.flex-nav .sidebar-item__message {
+ flex-direction: row;
+}
+
.rtl .sidebar-item {
&__menu {
right: auto;
diff --git a/packages/rocketchat-theme/client/imports/forms/switch.css b/packages/rocketchat-theme/client/imports/forms/switch.css
index bbed3cf5390..503f7cceb0c 100644
--- a/packages/rocketchat-theme/client/imports/forms/switch.css
+++ b/packages/rocketchat-theme/client/imports/forms/switch.css
@@ -18,7 +18,7 @@
& .rc-switch__input {
&:checked {
& + .rc-switch__button {
- border-color: #1757B7;
+ border-color: var(--button-primary-background);
background-color: var(--button-primary-background);
}
}
diff --git a/packages/rocketchat-theme/client/imports/general/base.css b/packages/rocketchat-theme/client/imports/general/base.css
index 28a4e734276..f4a7f3adcda 100644
--- a/packages/rocketchat-theme/client/imports/general/base.css
+++ b/packages/rocketchat-theme/client/imports/general/base.css
@@ -30,6 +30,8 @@ body {
font-size: var(--text-small-size);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
+ background-color: var(--rc-color-primary)
}
:focus {
@@ -205,3 +207,52 @@ button {
.hidden {
display: none;
}
+
+.loading-animation {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+
+ display: flex;
+
+ text-align: center;
+ align-items: center;
+ justify-content: center;
+}
+
+.loading-animation > .bounce {
+ display: inline-block;
+
+ width: 10px;
+ height: 10px;
+ margin: 2px;
+
+ animation: loading-bouncedelay 1.4s infinite ease-in-out both;
+
+ border-radius: 100%;
+ background-color: rgba(255, 255, 255, 0.6);
+}
+
+.loading-animation .bounce1 {
+ -webkit-animation-delay: -0.32s;
+ animation-delay: -0.32s;
+}
+
+.loading-animation .bounce2 {
+ -webkit-animation-delay: -0.16s;
+ animation-delay: -0.16s;
+}
+
+@keyframes loading-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ transform: scale(0);
+ }
+
+ 40% {
+ transform: scale(1);
+ }
+}
diff --git a/packages/rocketchat-theme/client/imports/general/base_old.css b/packages/rocketchat-theme/client/imports/general/base_old.css
index c6bf0c91c3e..6ec7acebce0 100644
--- a/packages/rocketchat-theme/client/imports/general/base_old.css
+++ b/packages/rocketchat-theme/client/imports/general/base_old.css
@@ -1702,8 +1702,7 @@
white-space: nowrap;
text-overflow: ellipsis;
- color: white;
- background-color: #04436a;
+ color: var(--rc-color-content);
font-size: 1.2em;
line-height: 40px;
@@ -2609,6 +2608,44 @@
position: relative;
}
+.rc-old .rc-message-box .reply-preview {
+ display: flex;
+ position: relative;
+ background-color:#fff;
+ padding-left: 15px;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.rc-old .rc-message-box .reply-preview-with-popup {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+ box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.16);;
+}
+
+.rc-old .reply-preview .cancel-reply {
+ padding: 10px;
+}
+
+.rc-old .reply-preview .mention-link.mention-link-all {
+ color: #fff;
+}
+
+.rc-old .reply-preview .mention-link.mention-link-me {
+ color: #fff;
+}
+
+.rc-old .message-popup.popup-with-reply-preview {
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
.rc-old .message-popup {
position: absolute;
z-index: 101;
@@ -4558,8 +4595,6 @@ body:not(.is-cordova) {
}
.rc-old.full-page {
- z-index: 101;
-
display: flex;
width: 100%;
diff --git a/packages/rocketchat-theme/client/main.css b/packages/rocketchat-theme/client/main.css
index ad0867b2a50..95577ddd1e4 100644
--- a/packages/rocketchat-theme/client/main.css
+++ b/packages/rocketchat-theme/client/main.css
@@ -42,6 +42,7 @@
@import 'imports/components/contextual-bar.css';
@import 'imports/components/emojiPicker.css';
@import 'imports/components/table.css';
+@import 'imports/components/file-list.css';
/* Modal */
@import 'imports/components/modal/full-modal.css';
diff --git a/packages/rocketchat-theme/server/colors.less b/packages/rocketchat-theme/server/colors.less
index 89bfaa2391a..1f7df2e7b36 100755
--- a/packages/rocketchat-theme/server/colors.less
+++ b/packages/rocketchat-theme/server/colors.less
@@ -947,3 +947,7 @@ label.required::after {
.range-slider-range::-moz-range-track {
background-color: @tertiary-background-color;
}
+
+.announcement {
+ background-color: @primary-background-color;
+}
diff --git a/packages/rocketchat-ui-account/client/accountPreferences.html b/packages/rocketchat-ui-account/client/accountPreferences.html
index 4ea1c69f251..8b493c562a4 100644
--- a/packages/rocketchat-ui-account/client/accountPreferences.html
+++ b/packages/rocketchat-ui-account/client/accountPreferences.html
@@ -14,8 +14,8 @@