diff --git a/.docker/Dockerfile.rhel b/.docker/Dockerfile.rhel index 29b6cd066957..a7002cc1f4d9 100644 --- a/.docker/Dockerfile.rhel +++ b/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/rhscl/nodejs-8-rhel7 -ENV RC_VERSION 0.66.3 +ENV RC_VERSION 0.67.0 MAINTAINER buildmaster@rocket.chat diff --git a/.github/history.json b/.github/history.json index 71ee92de4bf7..493924bf99a6 100644 --- a/.github/history.json +++ b/.github/history.json @@ -16859,6 +16859,270 @@ ] } ] + }, + "0.67.0-rc.0": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "10749", + "title": "[BREAK] Remove cache layer and internal calculated property `room.usernames`", + "userLogin": "rodrigok", + "milestone": "0.66.0", + "contributors": [ + "rodrigok", + "engelgabriel", + "web-flow" + ] + }, + { + "pr": "11417", + "title": "Merge master into develop & Set version to 0.67.0-develop", + "userLogin": "sampaiodiego", + "contributors": [ + "renatobecker", + "sampaiodiego", + "rodrigok", + "web-flow" + ] + }, + { + "pr": "11406", + "title": "[FIX] Livechat taking inquiry leading to 404 page", + "userLogin": "renatobecker", + "milestone": "0.66.3", + "contributors": [ + "renatobecker" + ] + }, + { + "pr": "11398", + "title": "[FIX] All messages notifications via email were sent as mention alert", + "userLogin": "rodrigok", + "milestone": "0.66.3", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "11358", + "title": "[FIX] sort fname sidenav", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo", + "web-flow" + ] + }, + { + "pr": "11399", + "title": "Merge master into develop & Set version to 0.67.0-develop", + "userLogin": "rodrigok", + "contributors": [ + "sampaiodiego", + "geekgonecrazy", + "renatobecker", + "rodrigok", + "web-flow" + ] + }, + { + "pr": "10918", + "title": "[NEW] Additional Livechat iFrame API's", + "userLogin": "renatobecker", + "milestone": "0.66.0", + "contributors": [ + "renatobecker", + "sampaiodiego", + "web-flow" + ] + }, + { + "pr": "11266", + "title": "[FIX] Livechat not sending desktop notifications", + "userLogin": "renatobecker", + "milestone": "0.66.2", + "contributors": [ + "renatobecker", + "sampaiodiego" + ] + }, + { + "pr": "11319", + "title": "[FIX] SVG icons code", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "11365", + "title": "[FIX] Remove file snap store doesn't like", + "userLogin": "geekgonecrazy", + "milestone": "0.66.2", + "contributors": [ + "geekgonecrazy", + "web-flow" + ] + }, + { + "pr": "11364", + "title": "Regression: Fix migration 125 checking for settings field", + "userLogin": "sampaiodiego", + "milestone": "0.66.2", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "11359", + "title": "Send setting Allow_Marketing_Emails to statistics collector", + "userLogin": "rodrigok", + "milestone": "0.66.2", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "11313", + "title": "[FIX] Message popup responsiveness in slash commands", + "userLogin": "tassoevan", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "9991", + "title": "[FIX] web app manifest errors as reported by Chrome DevTools", + "userLogin": "justinribeiro", + "milestone": "0.68.0", + "contributors": [ + "justinribeiro", + "web-flow" + ] + }, + { + "pr": "11342", + "title": "[FIX] Message attachment's fields with different sizes", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "11330", + "title": "[IMPROVE] Stop sort callbacks on run", + "userLogin": "ggazzo", + "contributors": [ + "ggazzo" + ] + }, + { + "pr": "9754", + "title": "[FIX] Parse inline code without space before initial backtick", + "userLogin": "c0dzilla", + "milestone": "0.68.0", + "contributors": [ + "c0dzilla", + "gdelavald", + "web-flow", + "ggazzo" + ] + }, + { + "pr": "11348", + "title": "Merge master into develop & Set version to 0.67.0-develop", + "userLogin": "rodrigok", + "contributors": [ + "sampaiodiego", + "rodrigok", + "gdelavald", + "tassoevan", + "Hudell", + "web-flow" + ] + }, + { + "pr": "11335", + "title": "[FIX] Some updates were returning errors when based on queries with position operators", + "userLogin": "rodrigok", + "milestone": "0.66.1", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "11315", + "title": "[FIX] SAML attributes with periods are not properly read.", + "userLogin": "Hudell", + "milestone": "0.66.1", + "contributors": [ + "Hudell", + "web-flow" + ] + }, + { + "pr": "11333", + "title": "[FIX] Outgoing integrations were stopping the oplog tailing sometimes", + "userLogin": "rodrigok", + "milestone": "0.66.1", + "contributors": [ + "rodrigok" + ] + }, + { + "pr": "11254", + "title": "[IMPROVE] Setup Wizard username validation, step progress and optin/optout", + "userLogin": "tassoevan", + "milestone": "0.66.1", + "contributors": [ + "tassoevan", + "ggazzo", + "web-flow" + ] + }, + { + "pr": "11267", + "title": "[FIX] Livestream muted when audio only option was enabled", + "userLogin": "gdelavald", + "milestone": "0.66.1", + "contributors": [ + "gdelavald", + "web-flow" + ] + }, + { + "pr": "11295", + "title": "[FIX] Notification preferences being lost when switching view mode", + "userLogin": "sampaiodiego", + "milestone": "0.66.1", + "contributors": [ + "sampaiodiego" + ] + }, + { + "pr": "11290", + "title": "Merge master into develop & Set version to 0.67.0-develop", + "userLogin": "rodrigok", + "contributors": [ + "rodrigok", + "web-flow" + ] + } + ] + }, + "0.67.0": { + "node_version": "8.11.3", + "npm_version": "5.6.0", + "pull_requests": [ + { + "pr": "11497", + "title": "Fix dependency issue in redhat image", + "userLogin": "geekgonecrazy", + "contributors": [ + "geekgonecrazy" + ] + } + ] } } } \ No newline at end of file diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 3a1f14b671a2..e3a94c300b53 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -19,9 +19,9 @@ const pkgdef :Spk.PackageDefinition = ( appTitle = (defaultText = "Rocket.Chat"), - appVersion = 80, # Increment this for every release. + appVersion = 83, # Increment this for every release. - appMarketingVersion = (defaultText = "0.66.3"), + appMarketingVersion = (defaultText = "0.67.0"), # 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 7c09e063df5f..a8e2b3c3e19a 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.66.3 + RC_VERSION=0.67.0 fi echo "Preparing to trigger a snap release for $CHANNEL channel" diff --git a/HISTORY.md b/HISTORY.md index 4f174f2cd8e4..c1d1c8b16a3a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,60 @@ +# 0.67.0 +`2018-07-20 · 1 ️️️⚠️ · 1 🎉 · 1 🚀 · 6 🐛 · 5 🔍 · 11 👩‍💻👨‍💻` + +### Engine versions +- Node: `8.11.3` +- NPM: `5.6.0` + +### ⚠️ BREAKING CHANGES + +- Remove cache layer and internal calculated property `room.usernames` ([#10749](https://github.com/RocketChat/Rocket.Chat/pull/10749)) + +### 🎉 New features + +- Additional Livechat iFrame API's ([#10918](https://github.com/RocketChat/Rocket.Chat/pull/10918)) + +### 🚀 Improvements + +- Stop sort callbacks on run ([#11330](https://github.com/RocketChat/Rocket.Chat/pull/11330)) + +### 🐛 Bug fixes + +- sort fname sidenav ([#11358](https://github.com/RocketChat/Rocket.Chat/pull/11358)) +- SVG icons code ([#11319](https://github.com/RocketChat/Rocket.Chat/pull/11319)) +- Message popup responsiveness in slash commands ([#11313](https://github.com/RocketChat/Rocket.Chat/pull/11313)) +- web app manifest errors as reported by Chrome DevTools ([#9991](https://github.com/RocketChat/Rocket.Chat/pull/9991) by [@justinribeiro](https://github.com/justinribeiro)) +- Message attachment's fields with different sizes ([#11342](https://github.com/RocketChat/Rocket.Chat/pull/11342)) +- Parse inline code without space before initial backtick ([#9754](https://github.com/RocketChat/Rocket.Chat/pull/9754) by [@c0dzilla](https://github.com/c0dzilla)) + +
+🔍 Minor changes + +- Fix dependency issue in redhat image ([#11497](https://github.com/RocketChat/Rocket.Chat/pull/11497)) +- Merge master into develop & Set version to 0.67.0-develop ([#11417](https://github.com/RocketChat/Rocket.Chat/pull/11417)) +- Merge master into develop & Set version to 0.67.0-develop ([#11399](https://github.com/RocketChat/Rocket.Chat/pull/11399)) +- Merge master into develop & Set version to 0.67.0-develop ([#11348](https://github.com/RocketChat/Rocket.Chat/pull/11348)) +- Merge master into develop & Set version to 0.67.0-develop ([#11290](https://github.com/RocketChat/Rocket.Chat/pull/11290)) + +
+ +### 👩‍💻👨‍💻 Contributors 😍 + +- [@c0dzilla](https://github.com/c0dzilla) +- [@justinribeiro](https://github.com/justinribeiro) + +### 👩‍💻👨‍💻 Core Team 🤓 + +- [@Hudell](https://github.com/Hudell) +- [@engelgabriel](https://github.com/engelgabriel) +- [@gdelavald](https://github.com/gdelavald) +- [@geekgonecrazy](https://github.com/geekgonecrazy) +- [@ggazzo](https://github.com/ggazzo) +- [@renatobecker](https://github.com/renatobecker) +- [@rodrigok](https://github.com/rodrigok) +- [@sampaiodiego](https://github.com/sampaiodiego) +- [@tassoevan](https://github.com/tassoevan) + # 0.66.3 `2018-07-09 · 2 🐛 · 2 👩‍💻👨‍💻` @@ -44,12 +100,16 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.66.1 -`2018-07-04 · 5 🐛 · 1 🔍 · 6 👩‍💻👨‍💻` +`2018-07-04 · 1 🚀 · 5 🐛 · 6 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.3` - NPM: `5.6.0` +### 🚀 Improvements + +- Setup Wizard username validation, step progress and optin/optout ([#11254](https://github.com/RocketChat/Rocket.Chat/pull/11254)) + ### 🐛 Bug fixes - Some updates were returning errors when based on queries with position operators ([#11335](https://github.com/RocketChat/Rocket.Chat/pull/11335)) @@ -58,13 +118,6 @@ - Livestream muted when audio only option was enabled ([#11267](https://github.com/RocketChat/Rocket.Chat/pull/11267)) - Notification preferences being lost when switching view mode ([#11295](https://github.com/RocketChat/Rocket.Chat/pull/11295)) -
-🔍 Minor changes - -- [IMPROVE] Setup Wizard username validation, step progress and optin/optout ([#11254](https://github.com/RocketChat/Rocket.Chat/pull/11254)) - -
- ### 👩‍💻👨‍💻 Core Team 🤓 - [@Hudell](https://github.com/Hudell) @@ -75,7 +128,7 @@ - [@tassoevan](https://github.com/tassoevan) # 0.66.0 -`2018-06-27 · 1 ️️️⚠️ · 23 🎉 · 61 🐛 · 50 🔍 · 45 👩‍💻👨‍💻` +`2018-06-27 · 1 ️️️⚠️ · 23 🎉 · 3 🚀 · 55 🐛 · 47 🔍 · 45 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.3` @@ -111,9 +164,14 @@ - Changes all 'mergeChannels' to 'groupByType'. ([#10055](https://github.com/RocketChat/Rocket.Chat/pull/10055) by [@mikaelmello](https://github.com/mikaelmello)) - Update WeDeploy deployment ([#10841](https://github.com/RocketChat/Rocket.Chat/pull/10841) by [@jonnilundy](https://github.com/jonnilundy)) +### 🚀 Improvements + +- Listing of apps in the admin page ([#11166](https://github.com/RocketChat/Rocket.Chat/pull/11166)) +- UI design for Tables and tabs component on Directory ([#11026](https://github.com/RocketChat/Rocket.Chat/pull/11026)) +- User mentions ([#11001](https://github.com/RocketChat/Rocket.Chat/pull/11001) by [@vynmera](https://github.com/vynmera)) + ### 🐛 Bug fixes -- i18n - add semantic markup ([#9534](https://github.com/RocketChat/Rocket.Chat/pull/9534) by [@brylie](https://github.com/brylie)) - Wordpress oauth configuration not loading properly ([#11187](https://github.com/RocketChat/Rocket.Chat/pull/11187)) - REST API: Add more test cases for `/login` ([#10999](https://github.com/RocketChat/Rocket.Chat/pull/10999)) - Wrong font-family order ([#11191](https://github.com/RocketChat/Rocket.Chat/pull/11191) by [@myfonj](https://github.com/myfonj)) @@ -159,13 +217,8 @@ - Allow inviting livechat managers to the same LiveChat room ([#10956](https://github.com/RocketChat/Rocket.Chat/pull/10956)) - Cannot read property 'debug' of undefined when trying to use REST API ([#10805](https://github.com/RocketChat/Rocket.Chat/pull/10805) by [@haffla](https://github.com/haffla)) - Icons svg xml structure ([#10771](https://github.com/RocketChat/Rocket.Chat/pull/10771)) -- Leave room wasn't working as expected ([#10851](https://github.com/RocketChat/Rocket.Chat/pull/10851)) -- Application crashing on startup when trying to log errors to `exceptions` channel ([#10934](https://github.com/RocketChat/Rocket.Chat/pull/10934)) -- Image lazy load was breaking attachments ([#10904](https://github.com/RocketChat/Rocket.Chat/pull/10904)) -- Incomplete email notification link ([#10928](https://github.com/RocketChat/Rocket.Chat/pull/10928)) - Remove outdated 2FA warning for mobile clients ([#10916](https://github.com/RocketChat/Rocket.Chat/pull/10916)) - Update Sandstorm build config ([#10867](https://github.com/RocketChat/Rocket.Chat/pull/10867) by [@ocdtrekkie](https://github.com/ocdtrekkie)) -- i18n - add semantic markup ([#9534](https://github.com/RocketChat/Rocket.Chat/pull/9534) by [@brylie](https://github.com/brylie)) - "blank messages" on iOS < 11 ([#11221](https://github.com/RocketChat/Rocket.Chat/pull/11221)) - "blank" screen on iOS < 11 ([#11199](https://github.com/RocketChat/Rocket.Chat/pull/11199)) - The process was freezing in some cases when HTTP calls exceeds timeout on integrations ([#11253](https://github.com/RocketChat/Rocket.Chat/pull/11253)) @@ -179,16 +232,13 @@ 🔍 Minor changes - Merge master into develop & Set version to 0.66.0-develop ([#11277](https://github.com/RocketChat/Rocket.Chat/pull/11277) by [@brylie](https://github.com/brylie) & [@stuartpb](https://github.com/stuartpb)) -- [IMPROVE] Listing of apps in the admin page ([#11166](https://github.com/RocketChat/Rocket.Chat/pull/11166)) - Regression: Directory css ([#11206](https://github.com/RocketChat/Rocket.Chat/pull/11206)) - LingoHub based on develop ([#11208](https://github.com/RocketChat/Rocket.Chat/pull/11208)) - IRC Federation: RFC2813 implementation (ngIRCd) ([#10113](https://github.com/RocketChat/Rocket.Chat/pull/10113) by [@cpitman](https://github.com/cpitman) & [@lindoelio](https://github.com/lindoelio)) - Add verification to make sure the user exists in REST insert object helper ([#11008](https://github.com/RocketChat/Rocket.Chat/pull/11008)) - Regression: Directory user table infinite scroll doesn't working ([#11200](https://github.com/RocketChat/Rocket.Chat/pull/11200)) -- [IMPROVE] UI design for Tables and tabs component on Directory ([#11026](https://github.com/RocketChat/Rocket.Chat/pull/11026)) - [FIX Readme] Nodejs + Python version spicifications ([#11181](https://github.com/RocketChat/Rocket.Chat/pull/11181) by [@mahdiyari](https://github.com/mahdiyari)) - Regression: sorting direct message by asc on favorites group ([#11090](https://github.com/RocketChat/Rocket.Chat/pull/11090)) -- [IMPROVE] User mentions ([#11001](https://github.com/RocketChat/Rocket.Chat/pull/11001) by [@vynmera](https://github.com/vynmera)) - Fix PR Docker image creation by splitting in two build jobs ([#11107](https://github.com/RocketChat/Rocket.Chat/pull/11107)) - Update v126.js ([#11103](https://github.com/RocketChat/Rocket.Chat/pull/11103)) - Speed up the build time by removing JSON Minify from i18n package ([#11097](https://github.com/RocketChat/Rocket.Chat/pull/11097)) @@ -332,7 +382,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.65.0 -`2018-05-28 · 17 🎉 · 24 🐛 · 42 🔍 · 29 👩‍💻👨‍💻` +`2018-05-28 · 13 🎉 · 16 🐛 · 15 🔍 · 25 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.1` @@ -352,10 +402,6 @@ - Now is possible to access files using header authorization (`x-user-id` and `x-auth-token`) ([#10741](https://github.com/RocketChat/Rocket.Chat/pull/10741)) - Add REST API endpoints `channels.counters`, `groups.counters and `im.counters` ([#9679](https://github.com/RocketChat/Rocket.Chat/pull/9679) by [@xbolshe](https://github.com/xbolshe)) - Add REST API endpoints `channels.setCustomFields` and `groups.setCustomFields` ([#9733](https://github.com/RocketChat/Rocket.Chat/pull/9733) by [@xbolshe](https://github.com/xbolshe)) -- Add REST endpoints `channels.roles` & `groups.roles` ([#10607](https://github.com/RocketChat/Rocket.Chat/pull/10607)) -- Add more options for Wordpress OAuth configuration ([#10724](https://github.com/RocketChat/Rocket.Chat/pull/10724)) -- Setup Wizard ([#10523](https://github.com/RocketChat/Rocket.Chat/pull/10523)) -- Improvements to notifications logic ([#10686](https://github.com/RocketChat/Rocket.Chat/pull/10686)) - Add permission `view-broadcast-member-list` ([#10753](https://github.com/RocketChat/Rocket.Chat/pull/10753)) ### 🐛 Bug fixes @@ -367,14 +413,6 @@ - Remove outdated translations of Internal Hubot's description of Scripts to Load that were pointing to a non existent address ([#10448](https://github.com/RocketChat/Rocket.Chat/pull/10448)) - UI was not disabling the actions when users has had no permissions to create channels or add users to rooms ([#10564](https://github.com/RocketChat/Rocket.Chat/pull/10564) by [@cfunkles](https://github.com/cfunkles) & [@chuckAtCataworx](https://github.com/chuckAtCataworx)) - Private settings were not being cleared from client cache in some cases ([#10625](https://github.com/RocketChat/Rocket.Chat/pull/10625)) -- Not escaping special chars on mentions ([#10793](https://github.com/RocketChat/Rocket.Chat/pull/10793) by [@erhan-](https://github.com/erhan-)) -- Send a message when muted returns inconsistent result in chat.sendMessage ([#10720](https://github.com/RocketChat/Rocket.Chat/pull/10720)) -- Regression: Empty content on announcement modal ([#10733](https://github.com/RocketChat/Rocket.Chat/pull/10733)) -- Missing attachment description when Rocket.Chat Apps were enabled ([#10705](https://github.com/RocketChat/Rocket.Chat/pull/10705)) -- Improve desktop notification formatting ([#10445](https://github.com/RocketChat/Rocket.Chat/pull/10445) by [@Sameesunkaria](https://github.com/Sameesunkaria)) -- Message box emoji icon was flickering when typing a text ([#10678](https://github.com/RocketChat/Rocket.Chat/pull/10678)) -- Channel owner was being set as muted when creating a read-only channel ([#10665](https://github.com/RocketChat/Rocket.Chat/pull/10665)) -- SAML wasn't working correctly when running multiple instances ([#10681](https://github.com/RocketChat/Rocket.Chat/pull/10681)) - Internal Error when requesting user data download ([#10837](https://github.com/RocketChat/Rocket.Chat/pull/10837)) - Broadcast channels were showing reply button for deleted messages and generating wrong reply links some times ([#10835](https://github.com/RocketChat/Rocket.Chat/pull/10835)) - User's preference `Unread on Top` wasn't working for LiveChat rooms ([#10734](https://github.com/RocketChat/Rocket.Chat/pull/10734)) @@ -389,37 +427,10 @@ 🔍 Minor changes - Release 0.65.0 ([#10893](https://github.com/RocketChat/Rocket.Chat/pull/10893) by [@Sameesunkaria](https://github.com/Sameesunkaria) & [@erhan-](https://github.com/erhan-) & [@peccu](https://github.com/peccu) & [@winterstefan](https://github.com/winterstefan)) -- Release 0.64.2 ([#10812](https://github.com/RocketChat/Rocket.Chat/pull/10812) by [@Sameesunkaria](https://github.com/Sameesunkaria) & [@erhan-](https://github.com/erhan-) & [@peccu](https://github.com/peccu) & [@winterstefan](https://github.com/winterstefan)) -- Release 0.64.1 ([#10660](https://github.com/RocketChat/Rocket.Chat/pull/10660) by [@saplla](https://github.com/saplla)) -- Release 0.64.0 ([#10613](https://github.com/RocketChat/Rocket.Chat/pull/10613) by [@christianh814](https://github.com/christianh814) & [@tttt-conan](https://github.com/tttt-conan)) -- Release 0.63.3 ([#10504](https://github.com/RocketChat/Rocket.Chat/pull/10504)) -- Release 0.63.2 ([#10476](https://github.com/RocketChat/Rocket.Chat/pull/10476)) -- add redhat dockerfile to master ([#10408](https://github.com/RocketChat/Rocket.Chat/pull/10408)) - Apps: Command Previews, Message and Room Removal Events ([#10822](https://github.com/RocketChat/Rocket.Chat/pull/10822)) - Develop sync ([#10815](https://github.com/RocketChat/Rocket.Chat/pull/10815) by [@nsuchy](https://github.com/nsuchy)) - Major dependencies update ([#10661](https://github.com/RocketChat/Rocket.Chat/pull/10661)) - Prevent setup wizard redirects ([#10811](https://github.com/RocketChat/Rocket.Chat/pull/10811)) -- Prometheus: Add metric to track hooks time ([#10798](https://github.com/RocketChat/Rocket.Chat/pull/10798)) -- Regression: Autorun of wizard was not destroyed after completion ([#10802](https://github.com/RocketChat/Rocket.Chat/pull/10802)) -- Prometheus: Fix notification metric ([#10803](https://github.com/RocketChat/Rocket.Chat/pull/10803)) -- Regression: Fix wrong wizard field name ([#10804](https://github.com/RocketChat/Rocket.Chat/pull/10804)) -- Prometheus: Improve metric names ([#10789](https://github.com/RocketChat/Rocket.Chat/pull/10789)) -- Improvement to push notifications on direct messages ([#10788](https://github.com/RocketChat/Rocket.Chat/pull/10788)) -- Better metric for notifications ([#10786](https://github.com/RocketChat/Rocket.Chat/pull/10786)) -- Add badge back to push notifications ([#10779](https://github.com/RocketChat/Rocket.Chat/pull/10779)) -- Wizard improvements ([#10776](https://github.com/RocketChat/Rocket.Chat/pull/10776)) -- Add setting and expose prometheus on port 9100 ([#10766](https://github.com/RocketChat/Rocket.Chat/pull/10766)) -- Regression: Fix notifications for direct messages ([#10760](https://github.com/RocketChat/Rocket.Chat/pull/10760)) -- More improvements on send notifications logic ([#10736](https://github.com/RocketChat/Rocket.Chat/pull/10736)) -- LingoHub based on develop ([#10691](https://github.com/RocketChat/Rocket.Chat/pull/10691)) -- Add `npm run postinstall` into example build script ([#10524](https://github.com/RocketChat/Rocket.Chat/pull/10524) by [@peccu](https://github.com/peccu)) -- Correct links in README file ([#10674](https://github.com/RocketChat/Rocket.Chat/pull/10674) by [@winterstefan](https://github.com/winterstefan)) -- Release 0.64.2 ([#10812](https://github.com/RocketChat/Rocket.Chat/pull/10812) by [@Sameesunkaria](https://github.com/Sameesunkaria) & [@erhan-](https://github.com/erhan-) & [@peccu](https://github.com/peccu) & [@winterstefan](https://github.com/winterstefan)) -- Release 0.64.1 ([#10660](https://github.com/RocketChat/Rocket.Chat/pull/10660) by [@saplla](https://github.com/saplla)) -- Release 0.64.0 ([#10613](https://github.com/RocketChat/Rocket.Chat/pull/10613) by [@christianh814](https://github.com/christianh814) & [@tttt-conan](https://github.com/tttt-conan)) -- Release 0.63.3 ([#10504](https://github.com/RocketChat/Rocket.Chat/pull/10504)) -- Release 0.63.2 ([#10476](https://github.com/RocketChat/Rocket.Chat/pull/10476)) -- add redhat dockerfile to master ([#10408](https://github.com/RocketChat/Rocket.Chat/pull/10408)) - Fix: Regression in REST API endpoint `/me` ([#10833](https://github.com/RocketChat/Rocket.Chat/pull/10833)) - Regression: Fix email notification preference not showing correct selected value ([#10847](https://github.com/RocketChat/Rocket.Chat/pull/10847)) - Apps: Command previews are clickable & Apps Framework is controlled via a setting ([#10853](https://github.com/RocketChat/Rocket.Chat/pull/10853)) @@ -440,14 +451,11 @@ - [@ThomasRoehl](https://github.com/ThomasRoehl) - [@c0dzilla](https://github.com/c0dzilla) - [@cfunkles](https://github.com/cfunkles) -- [@christianh814](https://github.com/christianh814) - [@chuckAtCataworx](https://github.com/chuckAtCataworx) - [@erhan-](https://github.com/erhan-) - [@kos4live](https://github.com/kos4live) - [@nsuchy](https://github.com/nsuchy) - [@peccu](https://github.com/peccu) -- [@saplla](https://github.com/saplla) -- [@tttt-conan](https://github.com/tttt-conan) - [@winterstefan](https://github.com/winterstefan) - [@xbolshe](https://github.com/xbolshe) @@ -455,7 +463,6 @@ - [@Hudell](https://github.com/Hudell) - [@MarcosSpessatto](https://github.com/MarcosSpessatto) -- [@TwizzyDizzy](https://github.com/TwizzyDizzy) - [@cardoso](https://github.com/cardoso) - [@engelgabriel](https://github.com/engelgabriel) - [@gdelavald](https://github.com/gdelavald) @@ -599,7 +606,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.64.0 -`2018-04-28 · 2 ️️️⚠️ · 18 🎉 · 44 🐛 · 37 🔍 · 30 👩‍💻👨‍💻` +`2018-04-28 · 2 ️️️⚠️ · 18 🎉 · 44 🐛 · 31 🔍 · 30 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.1` @@ -682,9 +689,6 @@ 🔍 Minor changes - Release 0.64.0 ([#10613](https://github.com/RocketChat/Rocket.Chat/pull/10613) by [@christianh814](https://github.com/christianh814) & [@tttt-conan](https://github.com/tttt-conan)) -- Release 0.63.3 ([#10504](https://github.com/RocketChat/Rocket.Chat/pull/10504)) -- Release 0.63.2 ([#10476](https://github.com/RocketChat/Rocket.Chat/pull/10476)) -- add redhat dockerfile to master ([#10408](https://github.com/RocketChat/Rocket.Chat/pull/10408)) - Regression: Various search provider fixes ([#10591](https://github.com/RocketChat/Rocket.Chat/pull/10591) by [@tkurz](https://github.com/tkurz)) - Regression: /api/v1/settings.oauth not sending needed info for SAML & CAS ([#10596](https://github.com/RocketChat/Rocket.Chat/pull/10596)) - Regression: Apps and Livechats not getting along well with each other ([#10598](https://github.com/RocketChat/Rocket.Chat/pull/10598)) @@ -703,9 +707,6 @@ - Remove @core team mention from Pull Request template ([#10384](https://github.com/RocketChat/Rocket.Chat/pull/10384)) - New issue template for *Release Process* ([#10234](https://github.com/RocketChat/Rocket.Chat/pull/10234)) - Master into Develop Branch Sync ([#10376](https://github.com/RocketChat/Rocket.Chat/pull/10376)) -- Release 0.63.3 ([#10504](https://github.com/RocketChat/Rocket.Chat/pull/10504)) -- Release 0.63.2 ([#10476](https://github.com/RocketChat/Rocket.Chat/pull/10476)) -- add redhat dockerfile to master ([#10408](https://github.com/RocketChat/Rocket.Chat/pull/10408)) - LingoHub based on develop ([#10545](https://github.com/RocketChat/Rocket.Chat/pull/10545)) - Regression: Revert announcement structure ([#10544](https://github.com/RocketChat/Rocket.Chat/pull/10544)) - Regression: Upload was not working ([#10543](https://github.com/RocketChat/Rocket.Chat/pull/10543)) @@ -797,7 +798,7 @@ - [@graywolf336](https://github.com/graywolf336) # 0.63.1 -`2018-04-07 · 3 🔍 · 18 👩‍💻👨‍💻` +`2018-04-07 · 1 🔍 · 7 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.1` @@ -807,37 +808,24 @@ 🔍 Minor changes - Release 0.63.1 ([#10374](https://github.com/RocketChat/Rocket.Chat/pull/10374) by [@TechyPeople](https://github.com/TechyPeople) & [@kaiiiiiiiii](https://github.com/kaiiiiiiiii) & [@tttt-conan](https://github.com/tttt-conan)) -- Release 0.63.0 ([#10324](https://github.com/RocketChat/Rocket.Chat/pull/10324) by [@Joe-mcgee](https://github.com/Joe-mcgee) & [@TopHattedCat](https://github.com/TopHattedCat) & [@hmagarotto](https://github.com/hmagarotto) & [@kaiiiiiiiii](https://github.com/kaiiiiiiiii) & [@kb0304](https://github.com/kb0304) & [@lunaticmonk](https://github.com/lunaticmonk) & [@ramrami](https://github.com/ramrami)) -- Release 0.63.0 ([#10324](https://github.com/RocketChat/Rocket.Chat/pull/10324) by [@Joe-mcgee](https://github.com/Joe-mcgee) & [@TopHattedCat](https://github.com/TopHattedCat) & [@hmagarotto](https://github.com/hmagarotto) & [@kaiiiiiiiii](https://github.com/kaiiiiiiiii) & [@kb0304](https://github.com/kb0304) & [@lunaticmonk](https://github.com/lunaticmonk) & [@ramrami](https://github.com/ramrami)) ### 👩‍💻👨‍💻 Contributors 😍 -- [@Joe-mcgee](https://github.com/Joe-mcgee) - [@TechyPeople](https://github.com/TechyPeople) -- [@TopHattedCat](https://github.com/TopHattedCat) -- [@hmagarotto](https://github.com/hmagarotto) - [@kaiiiiiiiii](https://github.com/kaiiiiiiiii) -- [@kb0304](https://github.com/kb0304) -- [@lunaticmonk](https://github.com/lunaticmonk) -- [@ramrami](https://github.com/ramrami) - [@tttt-conan](https://github.com/tttt-conan) ### 👩‍💻👨‍💻 Core Team 🤓 -- [@Hudell](https://github.com/Hudell) -- [@MarcosSpessatto](https://github.com/MarcosSpessatto) -- [@engelgabriel](https://github.com/engelgabriel) - [@geekgonecrazy](https://github.com/geekgonecrazy) -- [@ggazzo](https://github.com/ggazzo) - [@graywolf336](https://github.com/graywolf336) -- [@karlprieb](https://github.com/karlprieb) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) # 0.63.0 -`2018-04-04 · 1 ️️️⚠️ · 18 🎉 · 48 🐛 · 22 🔍 · 25 👩‍💻👨‍💻` +`2018-04-04 · 1 ️️️⚠️ · 18 🎉 · 30 🐛 · 20 🔍 · 24 👩‍💻👨‍💻` ### Engine versions - Node: `8.11.1` @@ -870,10 +858,6 @@ ### 🐛 Bug fixes -- Delete user without username was removing direct rooms of all users ([#9986](https://github.com/RocketChat/Rocket.Chat/pull/9986)) -- New channel page on medium size screens ([#9988](https://github.com/RocketChat/Rocket.Chat/pull/9988)) -- Empty sidenav when sorting by activity and there is a subscription without room ([#9960](https://github.com/RocketChat/Rocket.Chat/pull/9960)) -- Two factor authentication modal was not showing ([#9982](https://github.com/RocketChat/Rocket.Chat/pull/9982)) - Audio Message UI fixes ([#10303](https://github.com/RocketChat/Rocket.Chat/pull/10303) by [@kb0304](https://github.com/kb0304)) - "View All Members" button inside channel's "User Info" is over sized ([#10012](https://github.com/RocketChat/Rocket.Chat/pull/10012)) - Apostrophe-containing URL misparsed" ([#10242](https://github.com/RocketChat/Rocket.Chat/pull/10242)) @@ -892,24 +876,10 @@ - Nextcloud as custom oauth provider wasn't mapping data correctly ([#10090](https://github.com/RocketChat/Rocket.Chat/pull/10090)) - Missing sidebar default options on admin ([#10016](https://github.com/RocketChat/Rocket.Chat/pull/10016)) - Able to react with invalid emoji ([#8667](https://github.com/RocketChat/Rocket.Chat/pull/8667) by [@mutdmour](https://github.com/mutdmour)) -- Slack Import reports `invalid import file type` due to a call to BSON.native() which is now doesn't exist ([#10071](https://github.com/RocketChat/Rocket.Chat/pull/10071) by [@trongthanh](https://github.com/trongthanh)) -- Verified property of user is always set to false if not supplied ([#9719](https://github.com/RocketChat/Rocket.Chat/pull/9719)) -- Update preferences of users with settings: null was crashing the server ([#10076](https://github.com/RocketChat/Rocket.Chat/pull/10076)) -- REST API: Can't list all public channels when user has permission `view-joined-room` ([#10009](https://github.com/RocketChat/Rocket.Chat/pull/10009)) -- Message editing is crashing the server when read receipts are enabled ([#10061](https://github.com/RocketChat/Rocket.Chat/pull/10061)) -- Download links was duplicating Sub Paths ([#10029](https://github.com/RocketChat/Rocket.Chat/pull/10029)) - User preferences can't be saved when roles are hidden in admin settings ([#10051](https://github.com/RocketChat/Rocket.Chat/pull/10051)) - Browser was auto-filling values when editing another user profile ([#9932](https://github.com/RocketChat/Rocket.Chat/pull/9932) by [@kaiiiiiiiii](https://github.com/kaiiiiiiiii)) - Avatar input was accepting not supported image types ([#10011](https://github.com/RocketChat/Rocket.Chat/pull/10011)) - Initial loading feedback was missing ([#10028](https://github.com/RocketChat/Rocket.Chat/pull/10028)) -- Delete user without username was removing direct rooms of all users ([#9986](https://github.com/RocketChat/Rocket.Chat/pull/9986)) -- Two factor authentication modal was not showing ([#9982](https://github.com/RocketChat/Rocket.Chat/pull/9982)) -- Empty sidenav when sorting by activity and there is a subscription without room ([#9960](https://github.com/RocketChat/Rocket.Chat/pull/9960)) -- New channel page on medium size screens ([#9988](https://github.com/RocketChat/Rocket.Chat/pull/9988)) -- Delete user without username was removing direct rooms of all users ([#9986](https://github.com/RocketChat/Rocket.Chat/pull/9986)) -- New channel page on medium size screens ([#9988](https://github.com/RocketChat/Rocket.Chat/pull/9988)) -- Empty sidenav when sorting by activity and there is a subscription without room ([#9960](https://github.com/RocketChat/Rocket.Chat/pull/9960)) -- Two factor authentication modal was not showing ([#9982](https://github.com/RocketChat/Rocket.Chat/pull/9982)) - File had redirect delay when using external storage services and no option to proxy only avatars ([#10272](https://github.com/RocketChat/Rocket.Chat/pull/10272)) - Missing pt-BR translations ([#10262](https://github.com/RocketChat/Rocket.Chat/pull/10262)) - /me REST endpoint was missing user roles and preferences ([#10240](https://github.com/RocketChat/Rocket.Chat/pull/10240)) @@ -923,7 +893,6 @@ 🔍 Minor changes - Release 0.63.0 ([#10324](https://github.com/RocketChat/Rocket.Chat/pull/10324) by [@Joe-mcgee](https://github.com/Joe-mcgee) & [@TopHattedCat](https://github.com/TopHattedCat) & [@hmagarotto](https://github.com/hmagarotto) & [@kaiiiiiiiii](https://github.com/kaiiiiiiiii) & [@kb0304](https://github.com/kb0304) & [@lunaticmonk](https://github.com/lunaticmonk) & [@ramrami](https://github.com/ramrami)) -- Release 0.62.2 ([#10087](https://github.com/RocketChat/Rocket.Chat/pull/10087)) - Fix: Reaction endpoint/api only working with regular emojis ([#10323](https://github.com/RocketChat/Rocket.Chat/pull/10323)) - Bump snap version to include security fix ([#10313](https://github.com/RocketChat/Rocket.Chat/pull/10313)) - Update Meteor to 1.6.1.1 ([#10314](https://github.com/RocketChat/Rocket.Chat/pull/10314)) @@ -936,7 +905,6 @@ - [OTHER] Reactivate all tests ([#10036](https://github.com/RocketChat/Rocket.Chat/pull/10036)) - [OTHER] Reactivate API tests ([#9844](https://github.com/RocketChat/Rocket.Chat/pull/9844)) - Start 0.63.0-develop / develop sync from master ([#9985](https://github.com/RocketChat/Rocket.Chat/pull/9985)) -- Release 0.62.2 ([#10087](https://github.com/RocketChat/Rocket.Chat/pull/10087)) - Fix: Renaming channels.notifications Get/Post endpoints ([#10257](https://github.com/RocketChat/Rocket.Chat/pull/10257)) - Fix caddy download link to pull from github ([#10260](https://github.com/RocketChat/Rocket.Chat/pull/10260)) - Fix: possible errors on rocket.chat side of the apps ([#10252](https://github.com/RocketChat/Rocket.Chat/pull/10252)) @@ -961,7 +929,6 @@ - [@lunaticmonk](https://github.com/lunaticmonk) - [@mutdmour](https://github.com/mutdmour) - [@ramrami](https://github.com/ramrami) -- [@trongthanh](https://github.com/trongthanh) - [@ubarsaiyan](https://github.com/ubarsaiyan) ### 👩‍💻👨‍💻 Core Team 🤓 @@ -1040,7 +1007,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.62.0 -`2018-02-27 · 1 ️️️⚠️ · 24 🎉 · 32 🐛 · 26 🔍 · 39 👩‍💻👨‍💻` +`2018-02-27 · 1 ️️️⚠️ · 24 🎉 · 29 🐛 · 26 🔍 · 39 👩‍💻👨‍💻` ### Engine versions - Node: `8.9.4` @@ -1082,14 +1049,11 @@ - Typo on french translation for "Open" ([#9934](https://github.com/RocketChat/Rocket.Chat/pull/9934) by [@sizrar](https://github.com/sizrar)) - Wrong behavior of rooms info's *Read Only* and *Collaborative* buttons ([#9665](https://github.com/RocketChat/Rocket.Chat/pull/9665)) - Close button on file upload bar was not working ([#9662](https://github.com/RocketChat/Rocket.Chat/pull/9662)) -- Livechat conversation not receiving messages when start without form ([#9772](https://github.com/RocketChat/Rocket.Chat/pull/9772)) -- Emoji rendering on last message ([#9776](https://github.com/RocketChat/Rocket.Chat/pull/9776)) - Chrome 64 breaks jitsi-meet iframe ([#9560](https://github.com/RocketChat/Rocket.Chat/pull/9560) by [@speedy01](https://github.com/speedy01)) - Harmonize channel-related actions ([#9697](https://github.com/RocketChat/Rocket.Chat/pull/9697)) - Custom emoji was cropping sometimes ([#9676](https://github.com/RocketChat/Rocket.Chat/pull/9676) by [@anu-007](https://github.com/anu-007)) - Show custom room types icon in channel header ([#9696](https://github.com/RocketChat/Rocket.Chat/pull/9696)) - 'Query' support for channels.list.joined, groups.list, groups.listAll, im.list ([#9424](https://github.com/RocketChat/Rocket.Chat/pull/9424) by [@xbolshe](https://github.com/xbolshe)) -- Livechat issues on external queue and lead capture ([#9750](https://github.com/RocketChat/Rocket.Chat/pull/9750)) - DeprecationWarning: prom-client ... when starting Rocket Chat server ([#9747](https://github.com/RocketChat/Rocket.Chat/pull/9747) by [@jgtoriginal](https://github.com/jgtoriginal)) - API to retrive rooms was returning empty objects ([#9737](https://github.com/RocketChat/Rocket.Chat/pull/9737)) - Chat Message Reactions REST API End Point ([#9487](https://github.com/RocketChat/Rocket.Chat/pull/9487) by [@jgtoriginal](https://github.com/jgtoriginal)) @@ -1234,7 +1198,7 @@ - [@rodrigok](https://github.com/rodrigok) # 0.61.0 -`2018-01-27 · 1 ️️️⚠️ · 12 🎉 · 55 🐛 · 43 🔍 · 23 👩‍💻👨‍💻` +`2018-01-27 · 1 ️️️⚠️ · 11 🎉 · 13 🐛 · 8 🔍 · 15 👩‍💻👨‍💻` ### Engine versions - Node: `8.9.3` @@ -1256,15 +1220,10 @@ - Livechat extract lead data from message ([#9135](https://github.com/RocketChat/Rocket.Chat/pull/9135)) - Add impersonate option for livechat triggers ([#9107](https://github.com/RocketChat/Rocket.Chat/pull/9107)) - Add support to external livechat queue service provider ([#9053](https://github.com/RocketChat/Rocket.Chat/pull/9053)) -- Make Custom oauth accept nested usernameField ([#9066](https://github.com/RocketChat/Rocket.Chat/pull/9066)) - Contextual bar mail messages ([#9510](https://github.com/RocketChat/Rocket.Chat/pull/9510)) ### 🐛 Bug fixes -- Restore translations from other languages ([#9277](https://github.com/RocketChat/Rocket.Chat/pull/9277)) -- Remove sweetalert from livechat facebook integration page ([#9274](https://github.com/RocketChat/Rocket.Chat/pull/9274)) -- Missing translations ([#9272](https://github.com/RocketChat/Rocket.Chat/pull/9272)) -- File access not working when passing credentials via querystring ([#9262](https://github.com/RocketChat/Rocket.Chat/pull/9262)) - [i18n] add room type translation support for room-changed-privacy message ([#9369](https://github.com/RocketChat/Rocket.Chat/pull/9369) by [@cyclops24](https://github.com/cyclops24)) - Fix livechat register form ([#9452](https://github.com/RocketChat/Rocket.Chat/pull/9452)) - Fix livechat build ([#9451](https://github.com/RocketChat/Rocket.Chat/pull/9451)) @@ -1273,45 +1232,7 @@ - Slash command 'archive' throws exception if the channel does not exist ([#9428](https://github.com/RocketChat/Rocket.Chat/pull/9428) by [@ramrami](https://github.com/ramrami)) - Subscriptions not removed when removing user ([#9432](https://github.com/RocketChat/Rocket.Chat/pull/9432)) - Highlight setting not working correctly ([#9364](https://github.com/RocketChat/Rocket.Chat/pull/9364) by [@cyclops24](https://github.com/cyclops24)) -- announcement hyperlink color ([#9330](https://github.com/RocketChat/Rocket.Chat/pull/9330)) -- popover on safari for iOS ([#9328](https://github.com/RocketChat/Rocket.Chat/pull/9328)) -- last message cutting on bottom ([#9345](https://github.com/RocketChat/Rocket.Chat/pull/9345)) -- Deleting message with store last message not removing ([#9335](https://github.com/RocketChat/Rocket.Chat/pull/9335)) -- custom emoji size on sidebar item ([#9314](https://github.com/RocketChat/Rocket.Chat/pull/9314)) -- svg render on firefox ([#9311](https://github.com/RocketChat/Rocket.Chat/pull/9311)) -- sidebar footer padding ([#9249](https://github.com/RocketChat/Rocket.Chat/pull/9249)) -- LDAP/AD is not importing all users ([#9309](https://github.com/RocketChat/Rocket.Chat/pull/9309)) -- Wrong position of notifications alert in accounts preference page ([#9289](https://github.com/RocketChat/Rocket.Chat/pull/9289) by [@HammyHavoc](https://github.com/HammyHavoc)) -- English Typos ([#9285](https://github.com/RocketChat/Rocket.Chat/pull/9285) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Restore translations from other languages ([#9277](https://github.com/RocketChat/Rocket.Chat/pull/9277)) -- Remove sweetalert from livechat facebook integration page ([#9274](https://github.com/RocketChat/Rocket.Chat/pull/9274)) -- Missing translations ([#9272](https://github.com/RocketChat/Rocket.Chat/pull/9272)) - File access not working when passing credentials via querystring ([#9264](https://github.com/RocketChat/Rocket.Chat/pull/9264)) -- Move emojipicker css to theme package ([#9243](https://github.com/RocketChat/Rocket.Chat/pull/9243)) -- Show modal with announcement ([#9241](https://github.com/RocketChat/Rocket.Chat/pull/9241)) -- "Enter usernames" placeholder is cutting in "create channel" view ([#9194](https://github.com/RocketChat/Rocket.Chat/pull/9194) by [@TheReal1604](https://github.com/TheReal1604)) -- File upload not working on IE and weird on Chrome ([#9206](https://github.com/RocketChat/Rocket.Chat/pull/9206)) -- make the cross icon on user selection at channel creation page work ([#9176](https://github.com/RocketChat/Rocket.Chat/pull/9176) by [@vitor-nagao](https://github.com/vitor-nagao)) -- Made welcome emails more readable ([#9193](https://github.com/RocketChat/Rocket.Chat/pull/9193) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Cursor position when reply on safari ([#9185](https://github.com/RocketChat/Rocket.Chat/pull/9185)) -- Emoji size on last message preview ([#9186](https://github.com/RocketChat/Rocket.Chat/pull/9186)) -- Unread bar position when room have announcement ([#9188](https://github.com/RocketChat/Rocket.Chat/pull/9188)) -- Error when user roles is missing or is invalid ([#9040](https://github.com/RocketChat/Rocket.Chat/pull/9040) by [@paulovitin](https://github.com/paulovitin)) -- Make mentions and menu icons color darker ([#8922](https://github.com/RocketChat/Rocket.Chat/pull/8922)) -- "Use Emoji" preference not working ([#9182](https://github.com/RocketChat/Rocket.Chat/pull/9182)) -- channel create scroll on small screens ([#9168](https://github.com/RocketChat/Rocket.Chat/pull/9168)) -- go to replied message ([#9172](https://github.com/RocketChat/Rocket.Chat/pull/9172)) -- modal data on enter and modal style for file preview ([#9171](https://github.com/RocketChat/Rocket.Chat/pull/9171)) -- show oauth logins when adblock is used ([#9170](https://github.com/RocketChat/Rocket.Chat/pull/9170)) -- Last sent message reoccurs in textbox ([#9169](https://github.com/RocketChat/Rocket.Chat/pull/9169)) -- Update Rocket.Chat for sandstorm ([#9062](https://github.com/RocketChat/Rocket.Chat/pull/9062) by [@peterlee0127](https://github.com/peterlee0127)) -- Importers not recovering when an error occurs ([#9134](https://github.com/RocketChat/Rocket.Chat/pull/9134)) -- Do not block room while loading history ([#9121](https://github.com/RocketChat/Rocket.Chat/pull/9121)) -- Channel page error ([#9091](https://github.com/RocketChat/Rocket.Chat/pull/9091) by [@ggrish](https://github.com/ggrish)) -- Restore translations from other languages ([#9277](https://github.com/RocketChat/Rocket.Chat/pull/9277)) -- Remove sweetalert from livechat facebook integration page ([#9274](https://github.com/RocketChat/Rocket.Chat/pull/9274)) -- Missing translations ([#9272](https://github.com/RocketChat/Rocket.Chat/pull/9272)) -- File access not working when passing credentials via querystring ([#9262](https://github.com/RocketChat/Rocket.Chat/pull/9262)) - Contextual bar redesign ([#9481](https://github.com/RocketChat/Rocket.Chat/pull/9481)) - mention-here is missing i18n text #9455 ([#9456](https://github.com/RocketChat/Rocket.Chat/pull/9456) by [@ryjones](https://github.com/ryjones)) - Fix livechat visitor edit ([#9506](https://github.com/RocketChat/Rocket.Chat/pull/9506)) @@ -1321,45 +1242,10 @@ 🔍 Minor changes - Release 0.61.0 ([#9533](https://github.com/RocketChat/Rocket.Chat/pull/9533) by [@ryjones](https://github.com/ryjones)) -- Release 0.60.4 ([#9377](https://github.com/RocketChat/Rocket.Chat/pull/9377)) -- Release 0.60.3 ([#9320](https://github.com/RocketChat/Rocket.Chat/pull/9320) by [@HammyHavoc](https://github.com/HammyHavoc)) - Add community bot ([#9439](https://github.com/RocketChat/Rocket.Chat/pull/9439)) - Use correct version of Mailparser module ([#9356](https://github.com/RocketChat/Rocket.Chat/pull/9356)) -- Update Marked dependecy to 0.3.9 ([#9346](https://github.com/RocketChat/Rocket.Chat/pull/9346)) -- Fix: English language improvements ([#9299](https://github.com/RocketChat/Rocket.Chat/pull/9299) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Fix: Change 'Wordpress' to 'WordPress ([#9291](https://github.com/RocketChat/Rocket.Chat/pull/9291) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Fix: Improved README.md ([#9290](https://github.com/RocketChat/Rocket.Chat/pull/9290) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Fix: README typo ([#9286](https://github.com/RocketChat/Rocket.Chat/pull/9286) by [@HammyHavoc](https://github.com/HammyHavoc)) - Develop sync - Bump version to 0.61.0-develop ([#9260](https://github.com/RocketChat/Rocket.Chat/pull/9260) by [@cpitman](https://github.com/cpitman)) -- Do not change room icon color when room is unread ([#9257](https://github.com/RocketChat/Rocket.Chat/pull/9257)) -- LingoHub based on develop ([#9256](https://github.com/RocketChat/Rocket.Chat/pull/9256)) -- Fix: Sidebar item on rtl and small devices ([#9247](https://github.com/RocketChat/Rocket.Chat/pull/9247)) -- Add curl, its missing on worker nodes so has to be explicitly added ([#9248](https://github.com/RocketChat/Rocket.Chat/pull/9248)) -- Fix: Unneeded warning in payload of REST API calls ([#9240](https://github.com/RocketChat/Rocket.Chat/pull/9240)) -- Fix: Missing option to set user's avatar from a url ([#9229](https://github.com/RocketChat/Rocket.Chat/pull/9229)) -- Fix: Upload access control too distributed ([#9215](https://github.com/RocketChat/Rocket.Chat/pull/9215)) -- Fix: Username find is matching partially ([#9217](https://github.com/RocketChat/Rocket.Chat/pull/9217)) -- Fix: updating last message on message edit or delete ([#9227](https://github.com/RocketChat/Rocket.Chat/pull/9227)) -- Fix: Rooms and users are using different avatar style ([#9196](https://github.com/RocketChat/Rocket.Chat/pull/9196)) -- Replace postcss-nesting with postcss-nested ([#9200](https://github.com/RocketChat/Rocket.Chat/pull/9200)) -- Dependencies Update ([#9197](https://github.com/RocketChat/Rocket.Chat/pull/9197)) -- Typo: German language file ([#9190](https://github.com/RocketChat/Rocket.Chat/pull/9190) by [@TheReal1604](https://github.com/TheReal1604)) -- Fix: Snippet name to not showing in snippet list ([#9184](https://github.com/RocketChat/Rocket.Chat/pull/9184)) -- Fix/api me only return verified ([#9183](https://github.com/RocketChat/Rocket.Chat/pull/9183)) -- Fix: UI: Descenders of glyphs are cut off ([#9181](https://github.com/RocketChat/Rocket.Chat/pull/9181)) - [Fix] oauth not working because of email array ([#9173](https://github.com/RocketChat/Rocket.Chat/pull/9173)) -- Fix: Click on channel name - hover area bigger than link area ([#9165](https://github.com/RocketChat/Rocket.Chat/pull/9165)) -- Fix: UI: Descenders of glyphs are cut off ([#9166](https://github.com/RocketChat/Rocket.Chat/pull/9166)) -- Fix: Can’t login using LDAP via REST ([#9162](https://github.com/RocketChat/Rocket.Chat/pull/9162)) -- Fix: Unread line ([#9149](https://github.com/RocketChat/Rocket.Chat/pull/9149)) -- Fix test without oplog by waiting a successful login on changing users ([#9146](https://github.com/RocketChat/Rocket.Chat/pull/9146)) -- Fix: Messages being displayed in reverse order ([#9144](https://github.com/RocketChat/Rocket.Chat/pull/9144)) -- Fix: Clear all unreads modal not closing after confirming ([#9137](https://github.com/RocketChat/Rocket.Chat/pull/9137)) -- Fix: Message action quick buttons drops if "new message" divider is being shown ([#9138](https://github.com/RocketChat/Rocket.Chat/pull/9138)) -- Fix: Confirmation modals showing `Send` button ([#9136](https://github.com/RocketChat/Rocket.Chat/pull/9136)) -- Fix: Multiple unread indicators ([#9120](https://github.com/RocketChat/Rocket.Chat/pull/9120)) -- Release 0.60.4 ([#9377](https://github.com/RocketChat/Rocket.Chat/pull/9377)) -- Release 0.60.3 ([#9320](https://github.com/RocketChat/Rocket.Chat/pull/9320) by [@HammyHavoc](https://github.com/HammyHavoc)) - [DOCS] Update the links of our Mobile Apps in Features topic ([#9469](https://github.com/RocketChat/Rocket.Chat/pull/9469)) - Update license ([#9490](https://github.com/RocketChat/Rocket.Chat/pull/9490)) - Prevent NPM package-lock inside livechat ([#9504](https://github.com/RocketChat/Rocket.Chat/pull/9504)) @@ -1368,17 +1254,11 @@ ### 👩‍💻👨‍💻 Contributors 😍 -- [@HammyHavoc](https://github.com/HammyHavoc) -- [@TheReal1604](https://github.com/TheReal1604) - [@cpitman](https://github.com/cpitman) - [@cyclops24](https://github.com/cyclops24) -- [@ggrish](https://github.com/ggrish) -- [@paulovitin](https://github.com/paulovitin) -- [@peterlee0127](https://github.com/peterlee0127) - [@ramrami](https://github.com/ramrami) - [@rndmh3ro](https://github.com/rndmh3ro) - [@ryjones](https://github.com/ryjones) -- [@vitor-nagao](https://github.com/vitor-nagao) ### 👩‍💻👨‍💻 Core Team 🤓 @@ -1388,15 +1268,13 @@ - [@gdelavald](https://github.com/gdelavald) - [@geekgonecrazy](https://github.com/geekgonecrazy) - [@ggazzo](https://github.com/ggazzo) -- [@graywolf336](https://github.com/graywolf336) - [@karlprieb](https://github.com/karlprieb) -- [@pierreozoux](https://github.com/pierreozoux) - [@rafaelks](https://github.com/rafaelks) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) # 0.60.4 -`2018-01-10 · 5 🐛 · 4 🔍 · 4 👩‍💻👨‍💻` +`2018-01-10 · 5 🐛 · 2 🔍 · 3 👩‍💻👨‍💻` ### Engine versions - Node: `8.9.3` @@ -1414,16 +1292,10 @@ 🔍 Minor changes - Release 0.60.4 ([#9377](https://github.com/RocketChat/Rocket.Chat/pull/9377)) -- Release 0.60.3 ([#9320](https://github.com/RocketChat/Rocket.Chat/pull/9320) by [@HammyHavoc](https://github.com/HammyHavoc)) -- Release 0.60.3 ([#9320](https://github.com/RocketChat/Rocket.Chat/pull/9320) by [@HammyHavoc](https://github.com/HammyHavoc)) - Update Marked dependecy to 0.3.9 ([#9346](https://github.com/RocketChat/Rocket.Chat/pull/9346)) -### 👩‍💻👨‍💻 Contributors 😍 - -- [@HammyHavoc](https://github.com/HammyHavoc) - ### 👩‍💻👨‍💻 Core Team 🤓 - [@karlprieb](https://github.com/karlprieb) @@ -1507,7 +1379,7 @@ - [@rodrigok](https://github.com/rodrigok) # 0.60.0 -`2017-12-27 · 33 🎉 · 174 🐛 · 108 🔍 · 71 👩‍💻👨‍💻` +`2017-12-27 · 27 🎉 · 74 🐛 · 72 🔍 · 59 👩‍💻👨‍💻` ### Engine versions - Node: `8.9.3` @@ -1536,22 +1408,15 @@ - code to get the updated messages ([#8857](https://github.com/RocketChat/Rocket.Chat/pull/8857)) - Rest API endpoints to list, get, and run commands ([#8531](https://github.com/RocketChat/Rocket.Chat/pull/8531)) - Upgrade Meteor to 1.6 ([#8715](https://github.com/RocketChat/Rocket.Chat/pull/8715)) -- Setting to disable MarkDown and enable AutoLinker ([#8459](https://github.com/RocketChat/Rocket.Chat/pull/8459)) - Add settings for allow user direct messages to yourself ([#8066](https://github.com/RocketChat/Rocket.Chat/pull/8066) by [@lindoelio](https://github.com/lindoelio)) - Add sweet alert to video call tab ([#8108](https://github.com/RocketChat/Rocket.Chat/pull/8108)) - Displays QR code for manually entering when enabling 2fa ([#8143](https://github.com/RocketChat/Rocket.Chat/pull/8143)) -- Unify unread and mentions badge ([#8361](https://github.com/RocketChat/Rocket.Chat/pull/8361)) -- make sidebar item width 100% ([#8362](https://github.com/RocketChat/Rocket.Chat/pull/8362)) -- Smaller accountBox ([#8360](https://github.com/RocketChat/Rocket.Chat/pull/8360)) -- Add RD Station integration to livechat ([#8304](https://github.com/RocketChat/Rocket.Chat/pull/8304)) -- Upgrade to meteor 1.5.2 ([#8073](https://github.com/RocketChat/Rocket.Chat/pull/8073)) - Add yunohost.org installation method to Readme.md ([#8037](https://github.com/RocketChat/Rocket.Chat/pull/8037) by [@selamanse](https://github.com/selamanse)) - Modal ([#9092](https://github.com/RocketChat/Rocket.Chat/pull/9092)) - Make Custom oauth accept nested usernameField ([#9066](https://github.com/RocketChat/Rocket.Chat/pull/9066)) ### 🐛 Bug fixes -- Channel settings buttons ([#8753](https://github.com/RocketChat/Rocket.Chat/pull/8753)) - Can't react on Read Only rooms even when enabled ([#8925](https://github.com/RocketChat/Rocket.Chat/pull/8925)) - CAS does not share secrets when operating multiple server instances ([#8654](https://github.com/RocketChat/Rocket.Chat/pull/8654) by [@AmShaegar13](https://github.com/AmShaegar13)) - Snippetted messages not working ([#8937](https://github.com/RocketChat/Rocket.Chat/pull/8937)) @@ -1587,7 +1452,6 @@ - Xenforo [BD]API for 'user.user_id; instead of 'id' ([#8968](https://github.com/RocketChat/Rocket.Chat/pull/8968) by [@wesnspace](https://github.com/wesnspace)) - flextab height on smaller screens ([#8994](https://github.com/RocketChat/Rocket.Chat/pull/8994)) - Check for mention-all permission in room scope ([#8931](https://github.com/RocketChat/Rocket.Chat/pull/8931)) -- Channel settings buttons ([#8753](https://github.com/RocketChat/Rocket.Chat/pull/8753)) - fix emoji package path so they show up correctly in browser ([#8822](https://github.com/RocketChat/Rocket.Chat/pull/8822) by [@ryoshimizu](https://github.com/ryoshimizu)) - Set correct Twitter link ([#8830](https://github.com/RocketChat/Rocket.Chat/pull/8830) by [@jotafeldmann](https://github.com/jotafeldmann)) - User email settings on DM ([#8810](https://github.com/RocketChat/Rocket.Chat/pull/8810)) @@ -1600,106 +1464,8 @@ - Sort direct messages by full name if show real names setting enabled ([#8717](https://github.com/RocketChat/Rocket.Chat/pull/8717)) - Improving consistency of UX ([#8796](https://github.com/RocketChat/Rocket.Chat/pull/8796) by [@HammyHavoc](https://github.com/HammyHavoc)) - fixed some typos ([#8787](https://github.com/RocketChat/Rocket.Chat/pull/8787) by [@TheReal1604](https://github.com/TheReal1604)) -- Fix e-mail message forward ([#8645](https://github.com/RocketChat/Rocket.Chat/pull/8645)) -- Audio message icon ([#8648](https://github.com/RocketChat/Rocket.Chat/pull/8648)) -- Highlighted color height issue ([#8431](https://github.com/RocketChat/Rocket.Chat/pull/8431) by [@cyclops24](https://github.com/cyclops24)) -- AmazonS3: Quote file.name for ContentDisposition for files with commas ([#8593](https://github.com/RocketChat/Rocket.Chat/pull/8593)) -- Update pt-BR translation ([#8655](https://github.com/RocketChat/Rocket.Chat/pull/8655) by [@rodorgas](https://github.com/rodorgas)) -- Fix typos ([#8679](https://github.com/RocketChat/Rocket.Chat/pull/8679)) -- LDAP not respecting UTF8 characters & Sync Interval not working ([#8691](https://github.com/RocketChat/Rocket.Chat/pull/8691)) -- Missing scroll at create channel page ([#8637](https://github.com/RocketChat/Rocket.Chat/pull/8637)) -- Message popup menu on mobile/cordova ([#8634](https://github.com/RocketChat/Rocket.Chat/pull/8634)) -- API channel/group.members not sorting ([#8635](https://github.com/RocketChat/Rocket.Chat/pull/8635)) -- LDAP not merging existent users && Wrong id link generation ([#8613](https://github.com/RocketChat/Rocket.Chat/pull/8613)) -- encode filename in url to prevent links breaking ([#8551](https://github.com/RocketChat/Rocket.Chat/pull/8551) by [@joesitton](https://github.com/joesitton)) -- Fix guest pool inquiry taking ([#8577](https://github.com/RocketChat/Rocket.Chat/pull/8577)) - Changed all rocket.chat/docs/ to docs.rocket.chat/ ([#8588](https://github.com/RocketChat/Rocket.Chat/pull/8588) by [@RekkyRek](https://github.com/RekkyRek)) -- Color reset when default value editor is different ([#8543](https://github.com/RocketChat/Rocket.Chat/pull/8543)) -- Wrong colors after migration 103 ([#8547](https://github.com/RocketChat/Rocket.Chat/pull/8547)) -- LDAP login error regression at 0.59.0 ([#8541](https://github.com/RocketChat/Rocket.Chat/pull/8541)) -- Migration 103 wrong converting primrary colors ([#8544](https://github.com/RocketChat/Rocket.Chat/pull/8544)) -- Do not send joinCode field to clients ([#8527](https://github.com/RocketChat/Rocket.Chat/pull/8527)) -- Uncessary route reload break some routes ([#8514](https://github.com/RocketChat/Rocket.Chat/pull/8514)) -- Invalid Code message for password protected channel ([#8491](https://github.com/RocketChat/Rocket.Chat/pull/8491)) -- Wrong message when reseting password and 2FA is enabled ([#8489](https://github.com/RocketChat/Rocket.Chat/pull/8489)) -- LDAP memory issues when pagination is not available ([#8457](https://github.com/RocketChat/Rocket.Chat/pull/8457)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) -- Sidebar item menu position in RTL ([#8397](https://github.com/RocketChat/Rocket.Chat/pull/8397) by [@cyclops24](https://github.com/cyclops24)) -- disabled katex tooltip on messageBox ([#8386](https://github.com/RocketChat/Rocket.Chat/pull/8386)) -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) -- Various LDAP issues & Missing pagination ([#8372](https://github.com/RocketChat/Rocket.Chat/pull/8372)) -- remove accountBox from admin menu ([#8358](https://github.com/RocketChat/Rocket.Chat/pull/8358)) -- Missing i18n translations ([#8357](https://github.com/RocketChat/Rocket.Chat/pull/8357)) -- After deleting the room, cache is not synchronizing ([#8314](https://github.com/RocketChat/Rocket.Chat/pull/8314) by [@szluohua](https://github.com/szluohua)) -- Remove sidebar header on admin embedded version ([#8334](https://github.com/RocketChat/Rocket.Chat/pull/8334)) -- Email Subjects not being sent ([#8317](https://github.com/RocketChat/Rocket.Chat/pull/8317)) -- Put delete action on another popover group ([#8315](https://github.com/RocketChat/Rocket.Chat/pull/8315)) -- Mention unread indicator was removed ([#8316](https://github.com/RocketChat/Rocket.Chat/pull/8316)) -- Execute meteor reset on TRAVIS_TAG builds ([#8310](https://github.com/RocketChat/Rocket.Chat/pull/8310)) -- Wrong file name when upload to AWS S3 ([#8296](https://github.com/RocketChat/Rocket.Chat/pull/8296)) -- TypeError: Cannot read property 't' of undefined ([#8298](https://github.com/RocketChat/Rocket.Chat/pull/8298)) -- Check attachments is defined before accessing first element ([#8295](https://github.com/RocketChat/Rocket.Chat/pull/8295) by [@Darkneon](https://github.com/Darkneon)) -- Amin menu not showing all items & File list breaking line ([#8299](https://github.com/RocketChat/Rocket.Chat/pull/8299)) -- Call buttons with wrong margin on RTL ([#8307](https://github.com/RocketChat/Rocket.Chat/pull/8307)) -- Emoji Picker hidden for reactions in RTL ([#8300](https://github.com/RocketChat/Rocket.Chat/pull/8300)) -- fix color on unread messages ([#8282](https://github.com/RocketChat/Rocket.Chat/pull/8282)) -- Missing placeholder translations ([#8286](https://github.com/RocketChat/Rocket.Chat/pull/8286)) -- "Cancel button" on modal in RTL in Firefox 55 ([#8278](https://github.com/RocketChat/Rocket.Chat/pull/8278) by [@cyclops24](https://github.com/cyclops24)) -- Attachment icons alignment in LTR and RTL ([#8271](https://github.com/RocketChat/Rocket.Chat/pull/8271) by [@cyclops24](https://github.com/cyclops24)) -- [i18n] My Profile & README.md links ([#8270](https://github.com/RocketChat/Rocket.Chat/pull/8270) by [@Rzeszow](https://github.com/Rzeszow)) -- Incorrect URL for login terms when using prefix ([#8211](https://github.com/RocketChat/Rocket.Chat/pull/8211) by [@Darkneon](https://github.com/Darkneon)) -- Scrollbar not using new style ([#8190](https://github.com/RocketChat/Rocket.Chat/pull/8190)) -- User avatar in DM list. ([#8210](https://github.com/RocketChat/Rocket.Chat/pull/8210)) -- Fix iframe login API response (issue #8145) ([#8146](https://github.com/RocketChat/Rocket.Chat/pull/8146) by [@astax-t](https://github.com/astax-t)) -- Issue #8166 where empty analytics setting breaks to load Piwik script ([#8167](https://github.com/RocketChat/Rocket.Chat/pull/8167) by [@ruKurz](https://github.com/ruKurz)) -- Sidebar and RTL alignments ([#8154](https://github.com/RocketChat/Rocket.Chat/pull/8154)) -- "*.members" rest api being useless and only returning usernames ([#8147](https://github.com/RocketChat/Rocket.Chat/pull/8147)) -- Text area lost text when page reloads ([#8159](https://github.com/RocketChat/Rocket.Chat/pull/8159)) -- Add admin audio preferences translations ([#8094](https://github.com/RocketChat/Rocket.Chat/pull/8094)) -- RTL ([#8112](https://github.com/RocketChat/Rocket.Chat/pull/8112)) -- Settings description not showing ([#8122](https://github.com/RocketChat/Rocket.Chat/pull/8122)) -- Not sending email to mentioned users with unchanged preference ([#8059](https://github.com/RocketChat/Rocket.Chat/pull/8059)) -- Dynamic popover ([#8101](https://github.com/RocketChat/Rocket.Chat/pull/8101)) -- Fix setting user avatar on LDAP login ([#8099](https://github.com/RocketChat/Rocket.Chat/pull/8099)) -- Scroll on messagebox ([#8047](https://github.com/RocketChat/Rocket.Chat/pull/8047)) -- Invisible leader bar on hover ([#8048](https://github.com/RocketChat/Rocket.Chat/pull/8048)) -- Fix email on mention ([#7754](https://github.com/RocketChat/Rocket.Chat/pull/7754)) -- Prevent autotranslate tokens race condition ([#8046](https://github.com/RocketChat/Rocket.Chat/pull/8046)) -- Vertical menu on flex-tab ([#7988](https://github.com/RocketChat/Rocket.Chat/pull/7988)) -- message-box autogrow ([#8019](https://github.com/RocketChat/Rocket.Chat/pull/8019)) -- copy to clipboard and update clipboard.js library ([#8039](https://github.com/RocketChat/Rocket.Chat/pull/8039)) -- Recent emojis not updated when adding via text ([#7998](https://github.com/RocketChat/Rocket.Chat/pull/7998)) -- [PL] Polish translation ([#7989](https://github.com/RocketChat/Rocket.Chat/pull/7989) by [@Rzeszow](https://github.com/Rzeszow)) -- Chat box no longer auto-focuses when typing ([#7984](https://github.com/RocketChat/Rocket.Chat/pull/7984)) -- Fix the status on the members list ([#7963](https://github.com/RocketChat/Rocket.Chat/pull/7963)) -- Markdown being rendered in code tags ([#7965](https://github.com/RocketChat/Rocket.Chat/pull/7965)) - Email verification indicator added ([#7923](https://github.com/RocketChat/Rocket.Chat/pull/7923) by [@aditya19496](https://github.com/aditya19496)) -- Show leader on first load ([#7712](https://github.com/RocketChat/Rocket.Chat/pull/7712) by [@danischreiber](https://github.com/danischreiber)) -- Add padding on messages to allow space to the action buttons ([#7971](https://github.com/RocketChat/Rocket.Chat/pull/7971)) -- Small alignment fixes ([#7970](https://github.com/RocketChat/Rocket.Chat/pull/7970)) -- username ellipsis on firefox ([#7953](https://github.com/RocketChat/Rocket.Chat/pull/7953)) -- Document README.md. Drupal repo out of date ([#7948](https://github.com/RocketChat/Rocket.Chat/pull/7948) by [@Lawri-van-Buel](https://github.com/Lawri-van-Buel)) -- Double scroll on 'keyboard shortcuts' menu in sidepanel ([#7927](https://github.com/RocketChat/Rocket.Chat/pull/7927) by [@aditya19496](https://github.com/aditya19496)) -- Broken emoji picker on firefox ([#7943](https://github.com/RocketChat/Rocket.Chat/pull/7943)) -- Broken embedded view layout ([#7944](https://github.com/RocketChat/Rocket.Chat/pull/7944)) -- Fix placeholders in account profile ([#7945](https://github.com/RocketChat/Rocket.Chat/pull/7945) by [@josiasds](https://github.com/josiasds)) -- OTR buttons padding ([#7954](https://github.com/RocketChat/Rocket.Chat/pull/7954)) -- status and active room colors on sidebar ([#7960](https://github.com/RocketChat/Rocket.Chat/pull/7960)) -- Fix google play logo on repo README ([#7912](https://github.com/RocketChat/Rocket.Chat/pull/7912) by [@luizbills](https://github.com/luizbills)) -- Fix livechat toggle UI issue ([#7904](https://github.com/RocketChat/Rocket.Chat/pull/7904)) -- Remove break change in Realtime API ([#7895](https://github.com/RocketChat/Rocket.Chat/pull/7895)) -- Window exception when parsing Markdown on server ([#7893](https://github.com/RocketChat/Rocket.Chat/pull/7893)) -- sidebar buttons and badge paddings ([#7888](https://github.com/RocketChat/Rocket.Chat/pull/7888)) -- hyperlink style on sidebar footer ([#7882](https://github.com/RocketChat/Rocket.Chat/pull/7882)) -- livechat icon ([#7886](https://github.com/RocketChat/Rocket.Chat/pull/7886)) -- Makes text action menu width based on content size ([#7887](https://github.com/RocketChat/Rocket.Chat/pull/7887)) -- message actions over unread bar ([#7885](https://github.com/RocketChat/Rocket.Chat/pull/7885)) -- popover position on mobile ([#7883](https://github.com/RocketChat/Rocket.Chat/pull/7883)) -- search results position on sidebar ([#7881](https://github.com/RocketChat/Rocket.Chat/pull/7881)) -- sidebar paddings ([#7880](https://github.com/RocketChat/Rocket.Chat/pull/7880)) -- Adds default search text padding for emoji search ([#7878](https://github.com/RocketChat/Rocket.Chat/pull/7878)) -- Channel settings buttons ([#8753](https://github.com/RocketChat/Rocket.Chat/pull/8753)) - REST API file upload not respecting size limit ([#9108](https://github.com/RocketChat/Rocket.Chat/pull/9108)) - Creating channels on Firefox ([#9109](https://github.com/RocketChat/Rocket.Chat/pull/9109)) - Some UI problems on 0.60 ([#9095](https://github.com/RocketChat/Rocket.Chat/pull/9095)) @@ -1730,10 +1496,6 @@ 🔍 Minor changes - Release 0.60.0 ([#9259](https://github.com/RocketChat/Rocket.Chat/pull/9259)) -- Fix tag build ([#8973](https://github.com/RocketChat/Rocket.Chat/pull/8973)) -- Fix CircleCI deploy filter ([#8972](https://github.com/RocketChat/Rocket.Chat/pull/8972)) -- Release/0.59.4 ([#8967](https://github.com/RocketChat/Rocket.Chat/pull/8967) by [@cpitman](https://github.com/cpitman)) -- Add CircleCI ([#8685](https://github.com/RocketChat/Rocket.Chat/pull/8685)) - Fix tag build ([#9084](https://github.com/RocketChat/Rocket.Chat/pull/9084)) - Turn off prettyJson if the node environment isn't development ([#9068](https://github.com/RocketChat/Rocket.Chat/pull/9068)) - Fix api regression (exception when deleting user) ([#9049](https://github.com/RocketChat/Rocket.Chat/pull/9049)) @@ -1756,19 +1518,9 @@ - Fix typo ([#8705](https://github.com/RocketChat/Rocket.Chat/pull/8705) by [@rmetzler](https://github.com/rmetzler)) - [Fix] Store Outgoing Integration Result as String in Mongo ([#8413](https://github.com/RocketChat/Rocket.Chat/pull/8413) by [@cpitman](https://github.com/cpitman)) - Update DEMO to OPEN links ([#8793](https://github.com/RocketChat/Rocket.Chat/pull/8793)) -- Add CircleCI ([#8685](https://github.com/RocketChat/Rocket.Chat/pull/8685)) - Fix Travis CI build ([#8750](https://github.com/RocketChat/Rocket.Chat/pull/8750)) - Updated comments. ([#8719](https://github.com/RocketChat/Rocket.Chat/pull/8719) by [@jasonjyu](https://github.com/jasonjyu)) -- removing a duplicate line ([#8434](https://github.com/RocketChat/Rocket.Chat/pull/8434) by [@vikaskedia](https://github.com/vikaskedia)) -- install grpc package manually to fix snap armhf build ([#8653](https://github.com/RocketChat/Rocket.Chat/pull/8653)) - Fix community links in readme ([#8589](https://github.com/RocketChat/Rocket.Chat/pull/8589)) -- Improve room sync speed ([#8529](https://github.com/RocketChat/Rocket.Chat/pull/8529)) -- Fix high CPU load when sending messages on large rooms (regression) ([#8520](https://github.com/RocketChat/Rocket.Chat/pull/8520)) -- Change artifact path ([#8515](https://github.com/RocketChat/Rocket.Chat/pull/8515)) -- Color variables migration ([#8463](https://github.com/RocketChat/Rocket.Chat/pull/8463)) -- Fix: Change password not working in new UI ([#8516](https://github.com/RocketChat/Rocket.Chat/pull/8516)) -- Enable AutoLinker back ([#8490](https://github.com/RocketChat/Rocket.Chat/pull/8490)) -- Improve markdown parser code ([#8451](https://github.com/RocketChat/Rocket.Chat/pull/8451)) - [MOVE] Move favico to client folder ([#8077](https://github.com/RocketChat/Rocket.Chat/pull/8077) by [@vcapretz](https://github.com/vcapretz)) - [MOVE] Move files from emojione to client/server folders ([#8078](https://github.com/RocketChat/Rocket.Chat/pull/8078) by [@vcapretz](https://github.com/vcapretz)) - [MOVE] Move files from slashcommands-unarchive to client/server folders ([#8084](https://github.com/RocketChat/Rocket.Chat/pull/8084) by [@vcapretz](https://github.com/vcapretz)) @@ -1782,33 +1534,11 @@ - [MOVE] Move slackbridge to client/server folders ([#8141](https://github.com/RocketChat/Rocket.Chat/pull/8141) by [@vcapretz](https://github.com/vcapretz)) - [MOVE] Move logger files to client/server folders ([#8150](https://github.com/RocketChat/Rocket.Chat/pull/8150) by [@vcapretz](https://github.com/vcapretz)) - [MOVE] Move timesync files to client/server folders ([#8152](https://github.com/RocketChat/Rocket.Chat/pull/8152) by [@vcapretz](https://github.com/vcapretz)) -- Fix: Account menu position on RTL ([#8416](https://github.com/RocketChat/Rocket.Chat/pull/8416)) -- Fix: Missing LDAP option to show internal logs ([#8417](https://github.com/RocketChat/Rocket.Chat/pull/8417)) -- Fix: Missing LDAP reconnect setting ([#8414](https://github.com/RocketChat/Rocket.Chat/pull/8414)) -- Add i18n Title to snippet messages ([#8394](https://github.com/RocketChat/Rocket.Chat/pull/8394)) -- Fix: Missing settings to configure LDAP size and page limits ([#8398](https://github.com/RocketChat/Rocket.Chat/pull/8398)) -- LingoHub based on develop ([#8375](https://github.com/RocketChat/Rocket.Chat/pull/8375)) -- Update Meteor to 1.5.2.2 ([#8364](https://github.com/RocketChat/Rocket.Chat/pull/8364)) -- Sync translations from LingoHub ([#8363](https://github.com/RocketChat/Rocket.Chat/pull/8363)) -- Remove field `lastActivity` from subscription data ([#8345](https://github.com/RocketChat/Rocket.Chat/pull/8345)) -- Update meteor to 1.5.2.2-rc.0 ([#8355](https://github.com/RocketChat/Rocket.Chat/pull/8355)) -- [FIX-RC] Mobile file upload not working ([#8331](https://github.com/RocketChat/Rocket.Chat/pull/8331)) -- Deps update ([#8273](https://github.com/RocketChat/Rocket.Chat/pull/8273)) -- Fix more rtl issues ([#8194](https://github.com/RocketChat/Rocket.Chat/pull/8194)) -- npm deps update ([#8197](https://github.com/RocketChat/Rocket.Chat/pull/8197)) -- Remove unnecessary returns in cors common ([#8054](https://github.com/RocketChat/Rocket.Chat/pull/8054) by [@Kiran-Rao](https://github.com/Kiran-Rao)) - Adding: How to Install in WeDeploy ([#8036](https://github.com/RocketChat/Rocket.Chat/pull/8036) by [@thompsonemerson](https://github.com/thompsonemerson)) - Revert "npm deps update" ([#7983](https://github.com/RocketChat/Rocket.Chat/pull/7983)) -- [DOCS] Add native mobile app links into README and update button images ([#7909](https://github.com/RocketChat/Rocket.Chat/pull/7909)) - npm deps update ([#7969](https://github.com/RocketChat/Rocket.Chat/pull/7969)) -- Update BlackDuck URL ([#7941](https://github.com/RocketChat/Rocket.Chat/pull/7941)) -- Hide flex-tab close button ([#7894](https://github.com/RocketChat/Rocket.Chat/pull/7894)) - Added RocketChatLauncher (SaaS) ([#6606](https://github.com/RocketChat/Rocket.Chat/pull/6606) by [@designgurudotorg](https://github.com/designgurudotorg)) - Develop sync ([#7866](https://github.com/RocketChat/Rocket.Chat/pull/7866)) -- Fix tag build ([#8973](https://github.com/RocketChat/Rocket.Chat/pull/8973)) -- Fix CircleCI deploy filter ([#8972](https://github.com/RocketChat/Rocket.Chat/pull/8972)) -- Release/0.59.4 ([#8967](https://github.com/RocketChat/Rocket.Chat/pull/8967) by [@cpitman](https://github.com/cpitman)) -- Add CircleCI ([#8685](https://github.com/RocketChat/Rocket.Chat/pull/8685)) - Fix: users listed as online after API login ([#9111](https://github.com/RocketChat/Rocket.Chat/pull/9111)) - Fix regression in api channels.members ([#9110](https://github.com/RocketChat/Rocket.Chat/pull/9110)) - Fix: Clear all unreads modal not closing after confirming ([#9137](https://github.com/RocketChat/Rocket.Chat/pull/9137)) @@ -1845,17 +1575,13 @@ - [@AmShaegar13](https://github.com/AmShaegar13) - [@Darkneon](https://github.com/Darkneon) - [@HammyHavoc](https://github.com/HammyHavoc) -- [@Kiran-Rao](https://github.com/Kiran-Rao) -- [@Lawri-van-Buel](https://github.com/Lawri-van-Buel) - [@Pharserror](https://github.com/Pharserror) - [@RekkyRek](https://github.com/RekkyRek) -- [@Rzeszow](https://github.com/Rzeszow) - [@TheReal1604](https://github.com/TheReal1604) - [@aditya19496](https://github.com/aditya19496) - [@armand1m](https://github.com/armand1m) - [@arungalva](https://github.com/arungalva) - [@ashward](https://github.com/ashward) -- [@astax-t](https://github.com/astax-t) - [@ccfang](https://github.com/ccfang) - [@cpitman](https://github.com/cpitman) - [@cyclops24](https://github.com/cyclops24) @@ -1868,11 +1594,9 @@ - [@icosamuel](https://github.com/icosamuel) - [@jasonjyu](https://github.com/jasonjyu) - [@joesitton](https://github.com/joesitton) -- [@josiasds](https://github.com/josiasds) - [@jotafeldmann](https://github.com/jotafeldmann) - [@jwilkins](https://github.com/jwilkins) - [@lindoelio](https://github.com/lindoelio) -- [@luizbills](https://github.com/luizbills) - [@mastappl](https://github.com/mastappl) - [@mritunjaygoutam12](https://github.com/mritunjaygoutam12) - [@paulovitin](https://github.com/paulovitin) @@ -1881,8 +1605,6 @@ - [@ramrami](https://github.com/ramrami) - [@rmetzler](https://github.com/rmetzler) - [@robbyoconnor](https://github.com/robbyoconnor) -- [@rodorgas](https://github.com/rodorgas) -- [@ruKurz](https://github.com/ruKurz) - [@ryoshimizu](https://github.com/ryoshimizu) - [@sarbasamuel](https://github.com/sarbasamuel) - [@satyapramodh](https://github.com/satyapramodh) @@ -1890,11 +1612,9 @@ - [@selamanse](https://github.com/selamanse) - [@stalley](https://github.com/stalley) - [@stefanoverducci](https://github.com/stefanoverducci) -- [@szluohua](https://github.com/szluohua) - [@thompsonemerson](https://github.com/thompsonemerson) - [@torgeirl](https://github.com/torgeirl) - [@vcapretz](https://github.com/vcapretz) -- [@vikaskedia](https://github.com/vikaskedia) - [@vitor-nagao](https://github.com/vitor-nagao) - [@wesnspace](https://github.com/wesnspace) - [@wferris722](https://github.com/wferris722) @@ -1912,10 +1632,8 @@ - [@marceloschmidt](https://github.com/marceloschmidt) - [@mrsimpson](https://github.com/mrsimpson) - [@pierreozoux](https://github.com/pierreozoux) -- [@rafaelks](https://github.com/rafaelks) - [@rodrigok](https://github.com/rodrigok) - [@sampaiodiego](https://github.com/sampaiodiego) -- [@xenithorb](https://github.com/xenithorb) # 0.59.6 `2017-11-29 · 1 🔍 · 1 👩‍💻👨‍💻` @@ -2068,7 +1786,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.59.0 -`2017-10-18 · 25 🎉 · 131 🐛 · 51 🔍 · 46 👩‍💻👨‍💻` +`2017-10-18 · 24 🎉 · 116 🐛 · 50 🔍 · 44 👩‍💻👨‍💻` ### Engine versions - Node: `4.8.4` @@ -2093,7 +1811,6 @@ - Package to render issue numbers into links to an issue tracker. ([#6700](https://github.com/RocketChat/Rocket.Chat/pull/6700) by [@TAdeJong](https://github.com/TAdeJong) & [@TobiasKappe](https://github.com/TobiasKappe)) - Automatically select the first channel ([#7350](https://github.com/RocketChat/Rocket.Chat/pull/7350) by [@antaryami-sahoo](https://github.com/antaryami-sahoo)) - Rocket.Chat UI Redesign ([#7643](https://github.com/RocketChat/Rocket.Chat/pull/7643)) -- Add unread options for direct messages ([#7658](https://github.com/RocketChat/Rocket.Chat/pull/7658)) - Upgrade to meteor 1.5.2 ([#8073](https://github.com/RocketChat/Rocket.Chat/pull/8073)) - Enable read only channel creation ([#8260](https://github.com/RocketChat/Rocket.Chat/pull/8260)) - Add RD Station integration to livechat ([#8304](https://github.com/RocketChat/Rocket.Chat/pull/8304)) @@ -2104,12 +1821,6 @@ ### 🐛 Bug fixes -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) - File upload on multi-instances using a path prefix ([#7855](https://github.com/RocketChat/Rocket.Chat/pull/7855) by [@Darkneon](https://github.com/Darkneon)) - Fix migration 100 ([#7863](https://github.com/RocketChat/Rocket.Chat/pull/7863)) - Email message forward error ([#7846](https://github.com/RocketChat/Rocket.Chat/pull/7846)) @@ -2125,16 +1836,10 @@ - Update Snap links ([#7778](https://github.com/RocketChat/Rocket.Chat/pull/7778) by [@MichaelGooden](https://github.com/MichaelGooden)) - Remove redundant "do" in "Are you sure ...?" messages. ([#7809](https://github.com/RocketChat/Rocket.Chat/pull/7809) by [@xurizaemon](https://github.com/xurizaemon)) - Fixed function closure syntax allowing validation emails to be sent. ([#7758](https://github.com/RocketChat/Rocket.Chat/pull/7758) by [@snoozan](https://github.com/snoozan)) -- Csv importer: work with more problematic data ([#7456](https://github.com/RocketChat/Rocket.Chat/pull/7456) by [@reist](https://github.com/reist)) - Fix avatar upload fail on Cordova app ([#7656](https://github.com/RocketChat/Rocket.Chat/pull/7656) by [@ccfang](https://github.com/ccfang)) - Make link inside YouTube preview open in new tab ([#7679](https://github.com/RocketChat/Rocket.Chat/pull/7679) by [@1lann](https://github.com/1lann)) - Remove references to non-existent tests ([#7672](https://github.com/RocketChat/Rocket.Chat/pull/7672) by [@Kiran-Rao](https://github.com/Kiran-Rao)) - Example usage of unsubscribe.js ([#7673](https://github.com/RocketChat/Rocket.Chat/pull/7673) by [@Kiran-Rao](https://github.com/Kiran-Rao)) -- Wrong email subject when "All Messages" setting enabled ([#7639](https://github.com/RocketChat/Rocket.Chat/pull/7639)) -- Markdown noopener/noreferrer: use correct HTML attribute ([#7644](https://github.com/RocketChat/Rocket.Chat/pull/7644) by [@jangmarker](https://github.com/jangmarker)) -- Fix room load on first hit ([#7687](https://github.com/RocketChat/Rocket.Chat/pull/7687)) -- Wrong render of snippet’s name ([#7630](https://github.com/RocketChat/Rocket.Chat/pull/7630)) -- Fix messagebox growth ([#7629](https://github.com/RocketChat/Rocket.Chat/pull/7629)) - sidebar paddings ([#7880](https://github.com/RocketChat/Rocket.Chat/pull/7880)) - Adds default search text padding for emoji search ([#7878](https://github.com/RocketChat/Rocket.Chat/pull/7878)) - search results position on sidebar ([#7881](https://github.com/RocketChat/Rocket.Chat/pull/7881)) @@ -2225,11 +1930,8 @@ - Various LDAP issues & Missing pagination ([#8372](https://github.com/RocketChat/Rocket.Chat/pull/8372)) - remove accountBox from admin menu ([#8358](https://github.com/RocketChat/Rocket.Chat/pull/8358)) - Missing i18n translations ([#8357](https://github.com/RocketChat/Rocket.Chat/pull/8357)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) - Sidebar item menu position in RTL ([#8397](https://github.com/RocketChat/Rocket.Chat/pull/8397) by [@cyclops24](https://github.com/cyclops24)) - disabled katex tooltip on messageBox ([#8386](https://github.com/RocketChat/Rocket.Chat/pull/8386)) -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) - LDAP memory issues when pagination is not available ([#8457](https://github.com/RocketChat/Rocket.Chat/pull/8457)) - Uncessary route reload break some routes ([#8514](https://github.com/RocketChat/Rocket.Chat/pull/8514)) - Invalid Code message for password protected channel ([#8491](https://github.com/RocketChat/Rocket.Chat/pull/8491)) @@ -2257,7 +1959,6 @@ - [MOVE] Client folder rocketchat-highlight-words ([#7669](https://github.com/RocketChat/Rocket.Chat/pull/7669) by [@Kiran-Rao](https://github.com/Kiran-Rao)) - [MOVE] Client folder rocketchat-custom-sounds ([#7670](https://github.com/RocketChat/Rocket.Chat/pull/7670) by [@Kiran-Rao](https://github.com/Kiran-Rao)) - [MOVE] Client folder rocketchat-emoji ([#7671](https://github.com/RocketChat/Rocket.Chat/pull/7671) by [@Kiran-Rao](https://github.com/Kiran-Rao)) -- Only use "File Uploaded" prefix on files ([#7652](https://github.com/RocketChat/Rocket.Chat/pull/7652)) - Fix typo in generated URI ([#7661](https://github.com/RocketChat/Rocket.Chat/pull/7661) by [@Rohlik](https://github.com/Rohlik)) - Bump version to 0.59.0-develop ([#7625](https://github.com/RocketChat/Rocket.Chat/pull/7625)) - implemented new page-loader animated icon ([#2](https://github.com/RocketChat/Rocket.Chat/pull/2)) @@ -2314,7 +2015,6 @@ - [@cyclops24](https://github.com/cyclops24) - [@danischreiber](https://github.com/danischreiber) - [@goiaba](https://github.com/goiaba) -- [@jangmarker](https://github.com/jangmarker) - [@josiasds](https://github.com/josiasds) - [@luizbills](https://github.com/luizbills) - [@maarten-v](https://github.com/maarten-v) @@ -2323,7 +2023,6 @@ - [@nishimaki10](https://github.com/nishimaki10) - [@pkgodara](https://github.com/pkgodara) - [@rdebeasi](https://github.com/rdebeasi) -- [@reist](https://github.com/reist) - [@ruKurz](https://github.com/ruKurz) - [@snoozan](https://github.com/snoozan) - [@szluohua](https://github.com/szluohua) @@ -2346,23 +2045,12 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.58.4 -`2017-10-05 · 3 🐛 · 2 👩‍💻👨‍💻` +`2017-10-05` ### Engine versions - Node: `4.8.4` - NPM: `4.6.1` -### 🐛 Bug fixes - -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) - -### 👩‍💻👨‍💻 Core Team 🤓 - -- [@geekgonecrazy](https://github.com/geekgonecrazy) -- [@graywolf336](https://github.com/graywolf336) - # 0.58.2 `2017-08-22 · 1 🔍 · 2 👩‍💻👨‍💻` @@ -2409,7 +2097,7 @@ - [@rodrigok](https://github.com/rodrigok) # 0.58.0 -`2017-08-16 · 1 ️️️⚠️ · 27 🎉 · 72 🐛 · 22 🔍 · 33 👩‍💻👨‍💻` +`2017-08-16 · 1 ️️️⚠️ · 26 🎉 · 33 🐛 · 18 🔍 · 32 👩‍💻👨‍💻` ### Engine versions - Node: `4.8.4` @@ -2445,31 +2133,11 @@ - Update meteor to 1.5.1 ([#7496](https://github.com/RocketChat/Rocket.Chat/pull/7496)) - flex-tab now is side by side with message list ([#7448](https://github.com/RocketChat/Rocket.Chat/pull/7448)) - Option to select unread count behavior ([#7477](https://github.com/RocketChat/Rocket.Chat/pull/7477)) -- Force use of MongoDB for spotlight queries ([#7311](https://github.com/RocketChat/Rocket.Chat/pull/7311)) - Add healthchecks in OpenShift templates ([#7184](https://github.com/RocketChat/Rocket.Chat/pull/7184) by [@jfchevrette](https://github.com/jfchevrette)) - Add unread options for direct messages ([#7658](https://github.com/RocketChat/Rocket.Chat/pull/7658)) ### 🐛 Bug fixes -- Modernize rate limiting of sendMessage ([#7325](https://github.com/RocketChat/Rocket.Chat/pull/7325) by [@jangmarker](https://github.com/jangmarker)) -- custom soundEdit.html ([#7390](https://github.com/RocketChat/Rocket.Chat/pull/7390) by [@rasos](https://github.com/rasos)) -- Use UTF8 setting for /create command ([#7394](https://github.com/RocketChat/Rocket.Chat/pull/7394)) -- file upload broken when running in subdirectory https://github.com… ([#7395](https://github.com/RocketChat/Rocket.Chat/pull/7395) by [@ryoshimizu](https://github.com/ryoshimizu)) -- Fix Anonymous User ([#7444](https://github.com/RocketChat/Rocket.Chat/pull/7444)) -- Missing eventName in unUser ([#7533](https://github.com/RocketChat/Rocket.Chat/pull/7533) by [@Darkneon](https://github.com/Darkneon)) -- Fix Join Channel Without Preview Room Permission ([#7535](https://github.com/RocketChat/Rocket.Chat/pull/7535)) -- Improve build script example ([#7555](https://github.com/RocketChat/Rocket.Chat/pull/7555)) -- Slack import failing and not being able to be restarted ([#8390](https://github.com/RocketChat/Rocket.Chat/pull/8390)) -- Duplicate code in rest api letting in a few bugs with the rest api ([#8408](https://github.com/RocketChat/Rocket.Chat/pull/8408)) -- Add needed dependency for snaps ([#8389](https://github.com/RocketChat/Rocket.Chat/pull/8389)) -- Modernize rate limiting of sendMessage ([#7325](https://github.com/RocketChat/Rocket.Chat/pull/7325) by [@jangmarker](https://github.com/jangmarker)) -- custom soundEdit.html ([#7390](https://github.com/RocketChat/Rocket.Chat/pull/7390) by [@rasos](https://github.com/rasos)) -- Use UTF8 setting for /create command ([#7394](https://github.com/RocketChat/Rocket.Chat/pull/7394)) -- file upload broken when running in subdirectory https://github.com… ([#7395](https://github.com/RocketChat/Rocket.Chat/pull/7395) by [@ryoshimizu](https://github.com/ryoshimizu)) -- Fix Anonymous User ([#7444](https://github.com/RocketChat/Rocket.Chat/pull/7444)) -- Missing eventName in unUser ([#7533](https://github.com/RocketChat/Rocket.Chat/pull/7533) by [@Darkneon](https://github.com/Darkneon)) -- Fix Join Channel Without Preview Room Permission ([#7535](https://github.com/RocketChat/Rocket.Chat/pull/7535)) -- Improve build script example ([#7555](https://github.com/RocketChat/Rocket.Chat/pull/7555)) - Error when updating message with an empty attachment array ([#7624](https://github.com/RocketChat/Rocket.Chat/pull/7624)) - Uploading an unknown file type erroring out ([#7623](https://github.com/RocketChat/Rocket.Chat/pull/7623)) - Error when acessing settings before ready ([#7622](https://github.com/RocketChat/Rocket.Chat/pull/7622)) @@ -2477,44 +2145,24 @@ - The username not being allowed to be passed into the user.setAvatar ([#7620](https://github.com/RocketChat/Rocket.Chat/pull/7620)) - Fix Custom Fields Crashing on Register ([#7617](https://github.com/RocketChat/Rocket.Chat/pull/7617)) - Fix admin room list show the correct i18n type ([#7582](https://github.com/RocketChat/Rocket.Chat/pull/7582) by [@ccfang](https://github.com/ccfang)) -- Missing eventName in unUser ([#7533](https://github.com/RocketChat/Rocket.Chat/pull/7533) by [@Darkneon](https://github.com/Darkneon)) - URL parse error fix for issue #7169 ([#7538](https://github.com/RocketChat/Rocket.Chat/pull/7538) by [@satyapramodh](https://github.com/satyapramodh)) - User avatar image background ([#7572](https://github.com/RocketChat/Rocket.Chat/pull/7572)) -- Improve build script example ([#7555](https://github.com/RocketChat/Rocket.Chat/pull/7555)) -- Fix Join Channel Without Preview Room Permission ([#7535](https://github.com/RocketChat/Rocket.Chat/pull/7535)) - Look for livechat visitor IP address on X-Forwarded-For header ([#7554](https://github.com/RocketChat/Rocket.Chat/pull/7554)) - Revert emojione package version upgrade ([#7557](https://github.com/RocketChat/Rocket.Chat/pull/7557)) - Stop logging mentions object to console ([#7562](https://github.com/RocketChat/Rocket.Chat/pull/7562)) - Fix hiding flex-tab on embedded view ([#7486](https://github.com/RocketChat/Rocket.Chat/pull/7486)) - Fix emoji picker translations ([#7195](https://github.com/RocketChat/Rocket.Chat/pull/7195)) -- Modernize rate limiting of sendMessage ([#7325](https://github.com/RocketChat/Rocket.Chat/pull/7325) by [@jangmarker](https://github.com/jangmarker)) -- custom soundEdit.html ([#7390](https://github.com/RocketChat/Rocket.Chat/pull/7390) by [@rasos](https://github.com/rasos)) -- Use UTF8 setting for /create command ([#7394](https://github.com/RocketChat/Rocket.Chat/pull/7394)) -- file upload broken when running in subdirectory https://github.com… ([#7395](https://github.com/RocketChat/Rocket.Chat/pull/7395) by [@ryoshimizu](https://github.com/ryoshimizu)) -- Fix Anonymous User ([#7444](https://github.com/RocketChat/Rocket.Chat/pull/7444)) - Issue #7365: added check for the existence of a parameter in the CAS URL ([#7471](https://github.com/RocketChat/Rocket.Chat/pull/7471) by [@wsw70](https://github.com/wsw70)) - Fix Word Placement Anywhere on WebHooks ([#7392](https://github.com/RocketChat/Rocket.Chat/pull/7392)) - Prevent new room status from playing when user status changes ([#7487](https://github.com/RocketChat/Rocket.Chat/pull/7487)) -- S3 uploads not working for custom URLs ([#7443](https://github.com/RocketChat/Rocket.Chat/pull/7443)) -- Fix Private Channel List Submit ([#7432](https://github.com/RocketChat/Rocket.Chat/pull/7432)) -- Fix file upload on Slack import ([#7469](https://github.com/RocketChat/Rocket.Chat/pull/7469)) -- Fix Unread Bar Disappearing ([#7403](https://github.com/RocketChat/Rocket.Chat/pull/7403)) -- Always set LDAP properties on login ([#7472](https://github.com/RocketChat/Rocket.Chat/pull/7472)) - url click events in the cordova app open in external browser or not at all ([#7205](https://github.com/RocketChat/Rocket.Chat/pull/7205) by [@flaviogrossi](https://github.com/flaviogrossi)) -- Fix Emails in User Admin View ([#7431](https://github.com/RocketChat/Rocket.Chat/pull/7431)) -- Fix migration of avatars from version 0.57.0 ([#7428](https://github.com/RocketChat/Rocket.Chat/pull/7428)) - sweetalert alignment on mobile ([#7404](https://github.com/RocketChat/Rocket.Chat/pull/7404)) - Sweet-Alert modal popup position on mobile devices ([#7376](https://github.com/RocketChat/Rocket.Chat/pull/7376) by [@Oliver84](https://github.com/Oliver84)) - Update node-engine in Snap to latest v4 LTS relase: 4.8.3 ([#7355](https://github.com/RocketChat/Rocket.Chat/pull/7355) by [@al3x](https://github.com/al3x)) - Remove warning about 2FA support being unavailable in mobile apps ([#7354](https://github.com/RocketChat/Rocket.Chat/pull/7354) by [@al3x](https://github.com/al3x)) - Fix geolocation button ([#7322](https://github.com/RocketChat/Rocket.Chat/pull/7322)) - Fix Block Delete Message After (n) Minutes ([#7207](https://github.com/RocketChat/Rocket.Chat/pull/7207)) -- Fix jump to unread button ([#7320](https://github.com/RocketChat/Rocket.Chat/pull/7320)) -- Fix Secret Url ([#7321](https://github.com/RocketChat/Rocket.Chat/pull/7321)) - Use I18n on "File Uploaded" ([#7199](https://github.com/RocketChat/Rocket.Chat/pull/7199)) -- "requirePasswordChange" property not being saved when set to false ([#7209](https://github.com/RocketChat/Rocket.Chat/pull/7209)) -- Fix oembed previews not being shown ([#7208](https://github.com/RocketChat/Rocket.Chat/pull/7208)) -- Fix editing others messages ([#7200](https://github.com/RocketChat/Rocket.Chat/pull/7200)) - Fix error on image preview due to undefined description|title ([#7187](https://github.com/RocketChat/Rocket.Chat/pull/7187)) - Fix messagebox growth ([#7629](https://github.com/RocketChat/Rocket.Chat/pull/7629)) - Wrong render of snippet’s name ([#7630](https://github.com/RocketChat/Rocket.Chat/pull/7630)) @@ -2529,8 +2177,6 @@ - Release 0.58.0 ([#7752](https://github.com/RocketChat/Rocket.Chat/pull/7752) by [@flaviogrossi](https://github.com/flaviogrossi) & [@jangmarker](https://github.com/jangmarker) & [@ryoshimizu](https://github.com/ryoshimizu)) - Sync Master with 0.57.3 ([#7690](https://github.com/RocketChat/Rocket.Chat/pull/7690)) -- [Fix] Users and Channels list not respecting permissions ([#7212](https://github.com/RocketChat/Rocket.Chat/pull/7212)) -- [Fix] Users and Channels list not respecting permissions ([#7212](https://github.com/RocketChat/Rocket.Chat/pull/7212)) - Add missing parts of `one click to direct message` ([#7608](https://github.com/RocketChat/Rocket.Chat/pull/7608)) - LingoHub based on develop ([#7613](https://github.com/RocketChat/Rocket.Chat/pull/7613)) - Improve link parser using tokens ([#7615](https://github.com/RocketChat/Rocket.Chat/pull/7615)) @@ -2539,14 +2185,12 @@ - Improve room leader ([#7578](https://github.com/RocketChat/Rocket.Chat/pull/7578)) - Develop sync ([#7590](https://github.com/RocketChat/Rocket.Chat/pull/7590)) - [Fix] Don't save user to DB when a custom field is invalid ([#7513](https://github.com/RocketChat/Rocket.Chat/pull/7513) by [@Darkneon](https://github.com/Darkneon)) -- [Fix] Users and Channels list not respecting permissions ([#7212](https://github.com/RocketChat/Rocket.Chat/pull/7212)) - Develop sync ([#7500](https://github.com/RocketChat/Rocket.Chat/pull/7500) by [@thinkeridea](https://github.com/thinkeridea)) - Better Issue Template ([#7492](https://github.com/RocketChat/Rocket.Chat/pull/7492)) - Add helm chart kubernetes deployment ([#6340](https://github.com/RocketChat/Rocket.Chat/pull/6340)) - Develop sync ([#7363](https://github.com/RocketChat/Rocket.Chat/pull/7363)) - Escape error messages ([#7308](https://github.com/RocketChat/Rocket.Chat/pull/7308)) - update meteor to 1.5.0 ([#7287](https://github.com/RocketChat/Rocket.Chat/pull/7287)) -- Fix the Zapier oAuth return url to the new one ([#7215](https://github.com/RocketChat/Rocket.Chat/pull/7215)) - [New] Add instance id to response headers ([#7211](https://github.com/RocketChat/Rocket.Chat/pull/7211)) - Only use "File Uploaded" prefix on files ([#7652](https://github.com/RocketChat/Rocket.Chat/pull/7652)) @@ -2566,7 +2210,6 @@ - [@jangmarker](https://github.com/jangmarker) - [@jfchevrette](https://github.com/jfchevrette) - [@lindoelio](https://github.com/lindoelio) -- [@rasos](https://github.com/rasos) - [@reist](https://github.com/reist) - [@ruKurz](https://github.com/ruKurz) - [@ryoshimizu](https://github.com/ryoshimizu) @@ -2686,7 +2329,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.57.0 -`2017-07-03 · 1 ️️️⚠️ · 12 🎉 · 45 🐛 · 31 🔍 · 25 👩‍💻👨‍💻` +`2017-07-03 · 1 ️️️⚠️ · 10 🎉 · 44 🐛 · 30 🔍 · 25 👩‍💻👨‍💻` ### Engine versions - Node: `4.8.2` @@ -2706,8 +2349,6 @@ - postcss parser and cssnext implementation ([#6982](https://github.com/RocketChat/Rocket.Chat/pull/6982)) - Start running unit tests ([#6605](https://github.com/RocketChat/Rocket.Chat/pull/6605)) - Make channel/group delete call answer to roomName ([#6857](https://github.com/RocketChat/Rocket.Chat/pull/6857) by [@reist](https://github.com/reist)) -- Show info about multiple instances at admin page ([#6953](https://github.com/RocketChat/Rocket.Chat/pull/6953)) -- Improve CI/Docker build/release ([#6938](https://github.com/RocketChat/Rocket.Chat/pull/6938)) - Feature/delete any message permission ([#6919](https://github.com/RocketChat/Rocket.Chat/pull/6919) by [@phutchins](https://github.com/phutchins)) - Force use of MongoDB for spotlight queries ([#7311](https://github.com/RocketChat/Rocket.Chat/pull/7311)) @@ -2743,7 +2384,6 @@ - Fix login with Meteor saving an object as email address ([#6974](https://github.com/RocketChat/Rocket.Chat/pull/6974)) - Check that username is not in the room when being muted / unmuted ([#6840](https://github.com/RocketChat/Rocket.Chat/pull/6840) by [@matthewshirley](https://github.com/matthewshirley)) - Use AWS Signature Version 4 signed URLs for uploads ([#6947](https://github.com/RocketChat/Rocket.Chat/pull/6947)) -- make channels.create API check for create-c ([#6968](https://github.com/RocketChat/Rocket.Chat/pull/6968) by [@reist](https://github.com/reist)) - Bugs in `isUserFromParams` helper ([#6904](https://github.com/RocketChat/Rocket.Chat/pull/6904) by [@abrom](https://github.com/abrom)) - Allow image insert from slack through slackbridge ([#6910](https://github.com/RocketChat/Rocket.Chat/pull/6910)) - Slackbridge text replacements ([#6913](https://github.com/RocketChat/Rocket.Chat/pull/6913)) @@ -2785,7 +2425,6 @@ - LingoHub based on develop ([#6978](https://github.com/RocketChat/Rocket.Chat/pull/6978)) - fix the crashing tests ([#6976](https://github.com/RocketChat/Rocket.Chat/pull/6976)) - Convert WebRTC Package to Js ([#6775](https://github.com/RocketChat/Rocket.Chat/pull/6775)) -- [Fix] Error when trying to show preview of undefined filetype ([#6935](https://github.com/RocketChat/Rocket.Chat/pull/6935)) - Remove missing CoffeeScript dependencies ([#7154](https://github.com/RocketChat/Rocket.Chat/pull/7154)) - Switch logic of artifact name ([#7158](https://github.com/RocketChat/Rocket.Chat/pull/7158)) - Fix the Zapier oAuth return url to the new one ([#7215](https://github.com/RocketChat/Rocket.Chat/pull/7215)) @@ -2828,7 +2467,7 @@ - [@sampaiodiego](https://github.com/sampaiodiego) # 0.56.0 -`2017-05-15 · 11 🎉 · 21 🐛 · 22 🔍 · 19 👩‍💻👨‍💻` +`2017-05-15 · 11 🎉 · 19 🐛 · 19 🔍 · 19 👩‍💻👨‍💻` ### Engine versions - Node: `4.8.2` @@ -2864,8 +2503,6 @@ - CSV importer: require that there is some data in the zip, not ALL data ([#6768](https://github.com/RocketChat/Rocket.Chat/pull/6768) by [@reist](https://github.com/reist)) - Archiving Direct Messages ([#6737](https://github.com/RocketChat/Rocket.Chat/pull/6737)) - Fix Caddy by forcing go 1.7 as needed by one of caddy's dependencies ([#6721](https://github.com/RocketChat/Rocket.Chat/pull/6721)) -- emoji picker exception ([#6709](https://github.com/RocketChat/Rocket.Chat/pull/6709)) -- Fix message types ([#6704](https://github.com/RocketChat/Rocket.Chat/pull/6704)) - Users status on main menu always offline ([#6896](https://github.com/RocketChat/Rocket.Chat/pull/6896)) - Not showing unread count on electron app’s icon ([#6923](https://github.com/RocketChat/Rocket.Chat/pull/6923)) - Compile CSS color variables ([#6939](https://github.com/RocketChat/Rocket.Chat/pull/6939)) @@ -2875,7 +2512,6 @@
🔍 Minor changes -- [Fix] Bug with incoming integration (0.55.1) ([#6734](https://github.com/RocketChat/Rocket.Chat/pull/6734)) - [New] Snap arm support ([#6842](https://github.com/RocketChat/Rocket.Chat/pull/6842)) - Meteor update ([#6858](https://github.com/RocketChat/Rocket.Chat/pull/6858)) - Converted rocketchat-lib 3 ([#6672](https://github.com/RocketChat/Rocket.Chat/pull/6672)) @@ -2889,13 +2525,11 @@ - Anonymous use ([#5986](https://github.com/RocketChat/Rocket.Chat/pull/5986)) - Breaking long URLS to prevent overflow ([#6368](https://github.com/RocketChat/Rocket.Chat/pull/6368) by [@robertdown](https://github.com/robertdown)) - Rocketchat lib2 ([#6593](https://github.com/RocketChat/Rocket.Chat/pull/6593)) -- [Fix] Bug with incoming integration (0.55.1) ([#6734](https://github.com/RocketChat/Rocket.Chat/pull/6734)) - disable proxy configuration ([#6654](https://github.com/RocketChat/Rocket.Chat/pull/6654) by [@glehmann](https://github.com/glehmann)) - Convert markdown to js ([#6694](https://github.com/RocketChat/Rocket.Chat/pull/6694) by [@ehkasper](https://github.com/ehkasper)) - LingoHub based on develop ([#6715](https://github.com/RocketChat/Rocket.Chat/pull/6715)) - meteor update to 1.4.4 ([#6706](https://github.com/RocketChat/Rocket.Chat/pull/6706)) - LingoHub based on develop ([#6703](https://github.com/RocketChat/Rocket.Chat/pull/6703)) -- [Fix] Bug with incoming integration (0.55.1) ([#6734](https://github.com/RocketChat/Rocket.Chat/pull/6734)) - [Fix] Error when trying to show preview of undefined filetype ([#6935](https://github.com/RocketChat/Rocket.Chat/pull/6935))
diff --git a/package-lock.json b/package-lock.json index 1587fdbdda34..bcecc3b632dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4481,6 +4481,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "optional": true, "requires": { "prr": "1.0.1" } @@ -9490,7 +9491,8 @@ "natives": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", - "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==" + "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==", + "optional": true }, "natural-compare": { "version": "1.4.0", @@ -11222,7 +11224,8 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true }, "pseudomap": { "version": "1.0.2", diff --git a/package.json b/package.json index 278978390c16..512a6fc1962b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Rocket.Chat", "description": "The Ultimate Open Source WebChat Platform", - "version": "0.66.3", + "version": "0.67.0", "author": { "name": "Rocket.Chat", "url": "https://rocket.chat/" diff --git a/packages/rocketchat-action-links/both/lib/actionLinks.js b/packages/rocketchat-action-links/both/lib/actionLinks.js index edf0d0e4b257..b055555600c8 100644 --- a/packages/rocketchat-action-links/both/lib/actionLinks.js +++ b/packages/rocketchat-action-links/both/lib/actionLinks.js @@ -5,7 +5,8 @@ RocketChat.actionLinks = { RocketChat.actionLinks.actions[name] = funct; }, getMessage(name, messageId) { - if (!Meteor.userId()) { + const userId = Meteor.userId(); + if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'actionLinks.getMessage' }); } @@ -14,8 +15,11 @@ RocketChat.actionLinks = { throw new Meteor.Error('error-invalid-message', 'Invalid message', { function: 'actionLinks.getMessage' }); } - const room = RocketChat.models.Rooms.findOne({ _id: message.rid }); - if (Array.isArray(room.usernames) && room.usernames.indexOf(Meteor.user().username) === -1) { + const subscription = RocketChat.models.Subscriptions.findOne({ + rid: message.rid, + 'u._id': userId + }); + if (!subscription) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { function: 'actionLinks.getMessage' }); } diff --git a/packages/rocketchat-api/server/api.js b/packages/rocketchat-api/server/api.js index c27b4d664226..fd0f09a1d3e2 100644 --- a/packages/rocketchat-api/server/api.js +++ b/packages/rocketchat-api/server/api.js @@ -9,10 +9,7 @@ class API extends Restivus { this.fieldSeparator = '.'; this.defaultFieldsToExclude = { joinCode: 0, - $loki: 0, - meta: 0, members: 0, - usernames: 0, // Please use the `channel/dm/group.members` endpoint. This is disabled for performance reasons importIds: 0 }; this.limitedUserFieldsToExclude = { @@ -85,13 +82,14 @@ class API extends Restivus { return result; } - failure(result, errorType) { + failure(result, errorType, stack) { if (_.isObject(result)) { result.success = false; } else { result = { success: false, - error: result + error: result, + stack }; if (errorType) { diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js index be7de10766be..3c13e3e94a3f 100644 --- a/packages/rocketchat-api/server/v1/channels.js +++ b/packages/rocketchat-api/server/v1/channels.js @@ -1,15 +1,12 @@ import _ from 'underscore'; //Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property -function findChannelByIdOrName({ params, checkedArchived = true, returnUsernames = false }) { +function findChannelByIdOrName({ params, checkedArchived = true }) { if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) { throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" or "roomName" is required'); } const fields = { ...RocketChat.API.v1.defaultFieldsToExclude }; - if (returnUsernames) { - delete fields.usernames; - } let room; if (params.roomId) { @@ -143,7 +140,7 @@ RocketChat.API.v1.addRoute('channels.close', { authRequired: true }, { RocketChat.API.v1.addRoute('channels.counters', { authRequired: true }, { get() { const access = RocketChat.authz.hasPermission(this.userId, 'view-room-administration'); - const ruserId = this.requestParams().userId; + const userId = this.requestParams().userId; let user = this.userId; let unreads = null; let userMentions = null; @@ -152,34 +149,33 @@ RocketChat.API.v1.addRoute('channels.counters', { authRequired: true }, { let msgs = null; let latest = null; let members = null; - let lm = null; - if (ruserId) { + if (userId) { if (!access) { return RocketChat.API.v1.unauthorized(); } - user = ruserId; + user = userId; } const room = findChannelByIdOrName({ params: this.requestParams(), returnUsernames: true }); - const channel = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user); - lm = channel._room.lm ? channel._room.lm : channel._room._updatedAt; + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user); + const lm = room.lm ? room.lm : room._updatedAt; - if (typeof channel !== 'undefined' && channel.open) { - if (channel.ls) { - unreads = RocketChat.models.Messages.countVisibleByRoomIdBetweenTimestampsInclusive(channel.rid, channel.ls, lm); - unreadsFrom = channel.ls; + if (typeof subscription !== 'undefined' && subscription.open) { + if (subscription.ls) { + unreads = RocketChat.models.Messages.countVisibleByRoomIdBetweenTimestampsInclusive(subscription.rid, subscription.ls, lm); + unreadsFrom = subscription.ls; } - userMentions = channel.userMentions; + userMentions = subscription.userMentions; joined = true; } if (access || joined) { msgs = room.msgs; latest = lm; - members = room.usernames.length; + members = room.usersCount; } return RocketChat.API.v1.success({ @@ -494,28 +490,32 @@ RocketChat.API.v1.addRoute('channels.list', { authRequired: true }, { const { sort, fields, query } = this.parseJsonQuery(); const hasPermissionToSeeAllPublicChannels = RocketChat.authz.hasPermission(this.userId, 'view-c-room'); - const ourQuery = Object.assign({}, query, { t: 'c' }); + const ourQuery = { ...query, t: 'c' }; - if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room') && !hasPermissionToSeeAllPublicChannels) { - ourQuery.usernames = { - $in: [this.user.username] - }; - } else if (!hasPermissionToSeeAllPublicChannels) { - return RocketChat.API.v1.unauthorized(); + if (!hasPermissionToSeeAllPublicChannels) { + if (!RocketChat.authz.hasPermission(this.userId, 'view-joined-room')) { + return RocketChat.API.v1.unauthorized(); + } + const roomIds = RocketChat.models.Subscriptions.findByUserIdAndType(this.userId, 'c', { fields: { rid: 1 } }).fetch().map(s => s.rid); + ourQuery._id = { $in: roomIds }; } - const rooms = RocketChat.models.Rooms.find(ourQuery, { + const cursor = RocketChat.models.Rooms.find(ourQuery, { sort: sort ? sort : { name: 1 }, skip: offset, limit: count, fields - }).fetch(); + }); + + const total = cursor.count(); + + const rooms = cursor.fetch(); return RocketChat.API.v1.success({ channels: rooms, count: rooms.length, offset, - total: RocketChat.models.Rooms.find(ourQuery).count() + total }); } } @@ -524,22 +524,19 @@ RocketChat.API.v1.addRoute('channels.list', { authRequired: true }, { RocketChat.API.v1.addRoute('channels.list.joined', { authRequired: true }, { get() { const { offset, count } = this.getPaginationItems(); - const { sort, fields, query } = this.parseJsonQuery(); - const ourQuery = Object.assign({}, query, { - t: 'c', - 'u._id': this.userId - }); - - let rooms = _.pluck(RocketChat.models.Subscriptions.find(ourQuery).fetch(), '_room'); - const totalCount = rooms.length; + const { sort, fields } = this.parseJsonQuery(); - rooms = RocketChat.models.Rooms.processQueryOptionsOnResult(rooms, { + // TODO: CACHE: Add Breacking notice since we removed the query param + const cursor = RocketChat.models.Rooms.findBySubscriptionTypeAndUserId('c', this.userId, { sort: sort ? sort : { name: 1 }, skip: offset, limit: count, fields }); + const totalCount = cursor.count(); + const rooms = cursor.fetch(); + return RocketChat.API.v1.success({ channels: rooms, offset, @@ -553,8 +550,7 @@ RocketChat.API.v1.addRoute('channels.members', { authRequired: true }, { get() { const findResult = findChannelByIdOrName({ params: this.requestParams(), - checkedArchived: false, - returnUsernames: true + checkedArchived: false }); if (findResult.broadcast && !RocketChat.authz.hasPermission(this.userId, 'view-broadcast-member-list')) { @@ -562,29 +558,29 @@ RocketChat.API.v1.addRoute('channels.members', { authRequired: true }, { } const { offset, count } = this.getPaginationItems(); - const { sort } = this.parseJsonQuery(); - - const shouldBeOrderedDesc = Match.test(sort, Object) && Match.test(sort.username, Number) && sort.username === -1; + const { sort = {} } = this.parseJsonQuery(); - let members = RocketChat.models.Rooms.processQueryOptionsOnResult(Array.from(findResult.usernames).sort(), { + const subscriptions = RocketChat.models.Subscriptions.findByRoomId(findResult._id, { + fields: { 'u._id': 1 }, + sort: { 'u.username': sort.username != null ? sort.username : 1 }, skip: offset, limit: count }); - if (shouldBeOrderedDesc) { - members = members.reverse(); - } + const total = subscriptions.count(); + + const members = subscriptions.fetch().map(s => s.u && s.u._id); - const users = RocketChat.models.Users.find({ username: { $in: members } }, { + const users = RocketChat.models.Users.find({ _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 }, - sort: sort ? sort : { username: 1 } + sort: { username: sort.username != null ? sort.username : 1 } }).fetch(); return RocketChat.API.v1.success({ members: users, count: users.length, offset, - total: findResult.usernames.length + total }); } }); @@ -593,8 +589,7 @@ RocketChat.API.v1.addRoute('channels.messages', { authRequired: true }, { get() { const findResult = findChannelByIdOrName({ params: this.requestParams(), - checkedArchived: false, - returnUsernames: true + checkedArchived: false }); const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); @@ -602,27 +597,59 @@ RocketChat.API.v1.addRoute('channels.messages', { authRequired: true }, { const ourQuery = Object.assign({}, query, { rid: findResult._id }); //Special check for the permissions - if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room') && !findResult.usernames.includes(this.user.username)) { + if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(findResult._id, this.userId, { fields: { _id: 1 } })) { return RocketChat.API.v1.unauthorized(); - } else if (!RocketChat.authz.hasPermission(this.userId, 'view-c-room')) { + } + if (!RocketChat.authz.hasPermission(this.userId, 'view-c-room')) { return RocketChat.API.v1.unauthorized(); } - const messages = RocketChat.models.Messages.find(ourQuery, { + const cursor = RocketChat.models.Messages.find(ourQuery, { sort: sort ? sort : { ts: -1 }, skip: offset, limit: count, fields - }).fetch(); + }); + + const total = cursor.count(); + const messages = cursor.fetch(); return RocketChat.API.v1.success({ messages, count: messages.length, offset, - total: RocketChat.models.Messages.find(ourQuery).count() + total }); } }); +// TODO: CACHE: I dont like this method( functionality and how we implemented ) its very expensive +// TODO check if this code is better or not +// RocketChat.API.v1.addRoute('channels.online', { authRequired: true }, { +// get() { +// const { query } = this.parseJsonQuery(); +// const ourQuery = Object.assign({}, query, { t: 'c' }); + +// const room = RocketChat.models.Rooms.findOne(ourQuery); + +// if (room == null) { +// return RocketChat.API.v1.failure('Channel does not exists'); +// } + +// const ids = RocketChat.models.Subscriptions.find({ rid: room._id }, { fields: { 'u._id': 1 } }).fetch().map(sub => sub.u._id); + +// const online = RocketChat.models.Users.find({ +// username: { $exists: 1 }, +// _id: { $in: ids }, +// status: { $in: ['online', 'away', 'busy'] } +// }, { +// fields: { username: 1 } +// }).fetch(); + +// return RocketChat.API.v1.success({ +// online +// }); +// } +// }); RocketChat.API.v1.addRoute('channels.online', { authRequired: true }, { get() { @@ -636,14 +663,13 @@ RocketChat.API.v1.addRoute('channels.online', { authRequired: true }, { } const online = RocketChat.models.Users.findUsersNotOffline({ - fields: { - username: 1 - } + fields: { username: 1 } }).fetch(); const onlineInRoom = []; online.forEach(user => { - if (room.usernames.indexOf(user.username) !== -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(root._id, user._id, { fields: { _id: 1 } }); + if (subscription) { onlineInRoom.push({ _id: user._id, username: user.username diff --git a/packages/rocketchat-api/server/v1/groups.js b/packages/rocketchat-api/server/v1/groups.js index ffeb3cf4434a..c5835e0f4099 100644 --- a/packages/rocketchat-api/server/v1/groups.js +++ b/packages/rocketchat-api/server/v1/groups.js @@ -120,7 +120,6 @@ RocketChat.API.v1.addRoute('groups.counters', { authRequired: true }, { let msgs = null; let latest = null; let members = null; - let lm = null; if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) { throw new Meteor.Error('error-room-param-not-provided', 'The parameter "roomId" or "roomName" is required'); @@ -146,22 +145,22 @@ RocketChat.API.v1.addRoute('groups.counters', { authRequired: true }, { } user = params.userId; } - const group = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user); - lm = group._room.lm ? group._room.lm : group._room._updatedAt; + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user); + const lm = room.lm ? room.lm : room._updatedAt; - if (typeof group !== 'undefined' && group.open) { - if (group.ls) { - unreads = RocketChat.models.Messages.countVisibleByRoomIdBetweenTimestampsInclusive(group.rid, group.ls, lm); - unreadsFrom = group.ls; + if (typeof subscription !== 'undefined' && subscription.open) { + if (subscription.ls) { + unreads = RocketChat.models.Messages.countVisibleByRoomIdBetweenTimestampsInclusive(subscription.rid, subscription.ls, lm); + unreadsFrom = subscription.ls; } - userMentions = group.userMentions; + userMentions = subscription.userMentions; joined = true; } if (access || joined) { msgs = room.msgs; latest = lm; - members = room.usernames.length; + members = room.usersCount; } return RocketChat.API.v1.success({ @@ -220,7 +219,7 @@ RocketChat.API.v1.addRoute('groups.delete', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.processQueryOptionsOnResult([findResult._room], { fields: RocketChat.API.v1.defaultFieldsToExclude })[0] + group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }) }); } }); @@ -400,22 +399,20 @@ RocketChat.API.v1.addRoute('groups.leave', { authRequired: true }, { RocketChat.API.v1.addRoute('groups.list', { authRequired: true }, { get() { const { offset, count } = this.getPaginationItems(); - const { sort, fields, query } = this.parseJsonQuery(); - const ourQuery = Object.assign({}, query, { - t: 'p', - 'u._id': this.userId - }); + const { sort, fields} = this.parseJsonQuery(); - let rooms = _.pluck(RocketChat.models.Subscriptions.find(ourQuery).fetch(), '_room'); - const totalCount = rooms.length; - - rooms = RocketChat.models.Rooms.processQueryOptionsOnResult(rooms, { + // TODO: CACHE: Add Breacking notice since we removed the query param + const cursor = RocketChat.models.Rooms.findBySubscriptionTypeAndUserId('p', this.userId, { sort: sort ? sort : { name: 1 }, skip: offset, limit: count, fields }); + const totalCount = cursor.count(); + const rooms = cursor.fetch(); + + return RocketChat.API.v1.success({ groups: rooms, offset, @@ -457,34 +454,36 @@ RocketChat.API.v1.addRoute('groups.listAll', { authRequired: true }, { RocketChat.API.v1.addRoute('groups.members', { authRequired: true }, { get() { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId }); + const room = RocketChat.models.Rooms.findOneById(findResult.rid, { fields: { broadcast: 1 } }); - if (findResult._room.broadcast && !RocketChat.authz.hasPermission(this.userId, 'view-broadcast-member-list')) { + if (room.broadcast && !RocketChat.authz.hasPermission(this.userId, 'view-broadcast-member-list')) { return RocketChat.API.v1.unauthorized(); } const { offset, count } = this.getPaginationItems(); - const { sort } = this.parseJsonQuery(); - - let sortFn = (a, b) => a > b; - if (Match.test(sort, Object) && Match.test(sort.username, Number) && sort.username === -1) { - sortFn = (a, b) => b < a; - } + const { sort = {} } = this.parseJsonQuery(); - const members = RocketChat.models.Rooms.processQueryOptionsOnResult(Array.from(findResult._room.usernames).sort(sortFn), { + const subscriptions = RocketChat.models.Subscriptions.findByRoomId(findResult.rid, { + fields: { 'u._id': 1 }, + sort: { 'u.username': sort.username != null ? sort.username : 1 }, skip: offset, limit: count }); - const users = RocketChat.models.Users.find({ username: { $in: members } }, { + const total = subscriptions.count(); + + const members = subscriptions.fetch().map(s => s.u && s.u._id); + + const users = RocketChat.models.Users.find({ _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 }, - sort: sort ? sort : { username: 1 } + sort: { username: sort.username != null ? sort.username : 1 } }).fetch(); return RocketChat.API.v1.success({ members: users, - count: members.length, + count: users.length, offset, - total: findResult._room.usernames.length + total }); } }); @@ -512,7 +511,7 @@ RocketChat.API.v1.addRoute('groups.messages', { authRequired: true }, { }); } }); - +// TODO: CACHE: same as channels.online RocketChat.API.v1.addRoute('groups.online', { authRequired: true }, { get() { const { query } = this.parseJsonQuery(); @@ -532,7 +531,8 @@ RocketChat.API.v1.addRoute('groups.online', { authRequired: true }, { const onlineInRoom = []; online.forEach(user => { - if (room.usernames.indexOf(user.username) !== -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(root._id, user._id, { fields: { _id: 1 } }); + if (subscription) { onlineInRoom.push({ _id: user._id, username: user.username diff --git a/packages/rocketchat-api/server/v1/im.js b/packages/rocketchat-api/server/v1/im.js index c2ad7d8da8e0..865b6ab3bc8a 100644 --- a/packages/rocketchat-api/server/v1/im.js +++ b/packages/rocketchat-api/server/v1/im.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - function findDirectMessageRoom(params, user) { if ((!params.roomId || !params.roomId.trim()) && (!params.username || !params.username.trim())) { throw new Meteor.Error('error-room-param-not-provided', 'Body param "roomId" or "username" is required'); @@ -86,7 +84,7 @@ RocketChat.API.v1.addRoute(['dm.counters', 'im.counters'], { authRequired: true if (access || joined) { msgs = room.msgs; latest = lm; - members = room.usernames.length; + members = room.usersCount; } return RocketChat.API.v1.success({ @@ -187,21 +185,26 @@ RocketChat.API.v1.addRoute(['dm.members', 'im.members'], { authRequired: true }, const { offset, count } = this.getPaginationItems(); const { sort } = this.parseJsonQuery(); - - const members = RocketChat.models.Rooms.processQueryOptionsOnResult(Array.from(findResult.room.usernames), { - sort: sort ? sort : -1, + const cursor = RocketChat.models.Subscriptions.findByRoomId(findResult._id, { + sort: { 'u.username': sort.username != null ? sort.username : 1 }, skip: offset, limit: count }); - const users = RocketChat.models.Users.find({ username: { $in: members } }, - { fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 } }).fetch(); + const total = cursor.count(); + + const members = cursor.fetch().map(s => s.u && s.u.username); + + const users = RocketChat.models.Users.find({ username: { $in: members } }, { + fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 }, + sort: { username: sort.username != null ? sort.username : 1 } + }).fetch(); return RocketChat.API.v1.success({ members: users, count: members.length, offset, - total: findResult.room.usernames.length + total }); } }); @@ -275,27 +278,25 @@ RocketChat.API.v1.addRoute(['dm.messages.others', 'im.messages.others'], { authR RocketChat.API.v1.addRoute(['dm.list', 'im.list'], { authRequired: true }, { get() { const { offset, count } = this.getPaginationItems(); - const { sort, fields, query } = this.parseJsonQuery(); - const ourQuery = Object.assign({}, query, { - t: 'd', - 'u._id': this.userId - }); + const { sort = { name: 1 }, fields } = this.parseJsonQuery(); - let rooms = _.pluck(RocketChat.models.Subscriptions.find(ourQuery).fetch(), '_room'); - const totalCount = rooms.length; + // TODO: CACHE: Add Breacking notice since we removed the query param - rooms = RocketChat.models.Rooms.processQueryOptionsOnResult(rooms, { - sort: sort ? sort : { name: 1 }, + const cursor = RocketChat.models.Rooms.findBySubscriptionTypeAndUserId('d', this.userId, { + sort, skip: offset, limit: count, fields }); + const total = cursor.count(); + const rooms = cursor.fetch(); + return RocketChat.API.v1.success({ ims: rooms, offset, count: rooms.length, - total: totalCount + total }); } }); diff --git a/packages/rocketchat-api/server/v1/subscriptions.js b/packages/rocketchat-api/server/v1/subscriptions.js index e58fe4094a26..8bc1c57e4fdf 100644 --- a/packages/rocketchat-api/server/v1/subscriptions.js +++ b/packages/rocketchat-api/server/v1/subscriptions.js @@ -33,13 +33,7 @@ RocketChat.API.v1.addRoute('subscriptions.getOne', { authRequired: true }, { 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 - } - }); + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId); return RocketChat.API.v1.success({ subscription diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index 496833312557..d64b5680aaa5 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -352,26 +352,29 @@ RocketChat.API.v1.addRoute('users.setPreferences', { authRequired: true }, { }) }); - let preferences; const userId = this.bodyParams.userId ? this.bodyParams.userId : this.userId; + const userData = { + _id: userId, + settings: { + preferences: this.bodyParams.data + } + }; + if (this.bodyParams.data.language) { const language = this.bodyParams.data.language; delete this.bodyParams.data.language; - preferences = _.extend({ _id: userId, settings: { preferences: this.bodyParams.data }, language }); - } else { - preferences = _.extend({ _id: userId, settings: { preferences: this.bodyParams.data } }); + userData.language = language; } - // Keep compatibility with old values - if (preferences.emailNotificationMode === 'all') { - preferences.emailNotificationMode = 'mentions'; - } else if (preferences.emailNotificationMode === 'disabled') { - preferences.emailNotificationMode = 'nothing'; - } - - Meteor.runAsUser(this.userId, () => RocketChat.saveUser(this.userId, preferences)); + Meteor.runAsUser(this.userId, () => RocketChat.saveUser(this.userId, userData)); - return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.bodyParams.userId, { fields: preferences }) }); + return RocketChat.API.v1.success({ + user: RocketChat.models.Users.findOneById(userId, { + fields: { + 'settings.preferences': 1 + } + }) + }); } }); diff --git a/packages/rocketchat-apps/server/bridges/messages.js b/packages/rocketchat-apps/server/bridges/messages.js index 22d391e5c581..126b1f1b668b 100644 --- a/packages/rocketchat-apps/server/bridges/messages.js +++ b/packages/rocketchat-apps/server/bridges/messages.js @@ -54,7 +54,7 @@ export class AppMessageBridge { async notifyRoom(room, message, appId) { console.log(`The App ${ appId } is notifying a room's users.`); - if (room && room.usernames && Array.isArray(room.usernames)) { + if (room) { const msg = this.orch.getConverters().get('messages').convertAppMessage(message); const rmsg = Object.assign(msg, { _id: Random.id(), @@ -64,12 +64,14 @@ export class AppMessageBridge { editor: undefined }); - room.usernames.forEach((u) => { - const user = RocketChat.models.Users.findOneByUsername(u); - if (user) { - RocketChat.Notifications.notifyUser(user._id, 'message', rmsg); - } - }); + const users = RocketChat.models.Subscriptions.findByRoomIdWhenUserIdExists(room._id, { fields: { 'u._id': 1 } }) + .fetch() + .map(s => s.u._id); + RocketChat.models.Users.findByIds(users, { fields: { _id: 1 } }) + .fetch() + .forEach(({ _id }) => + RocketChat.Notifications.notifyUser(_id, 'message', rmsg) + ); } } } diff --git a/packages/rocketchat-apps/server/bridges/rooms.js b/packages/rocketchat-apps/server/bridges/rooms.js index d0bece2d7e8e..2db168165233 100644 --- a/packages/rocketchat-apps/server/bridges/rooms.js +++ b/packages/rocketchat-apps/server/bridges/rooms.js @@ -24,7 +24,7 @@ export class AppRoomBridge { let rid; Meteor.runAsUser(room.creator.id, () => { - const info = Meteor.call(method, rcRoom.usernames); + const info = Meteor.call(method, rcRoom.members); rid = info.rid; }); diff --git a/packages/rocketchat-apps/server/converters/rooms.js b/packages/rocketchat-apps/server/converters/rooms.js index a12e9bcf0499..9fd875755331 100644 --- a/packages/rocketchat-apps/server/converters/rooms.js +++ b/packages/rocketchat-apps/server/converters/rooms.js @@ -37,7 +37,7 @@ export class AppRoomsConverter { name: room.slugifiedName, t: room.type, u, - usernames: room.usernames, + members: room.members, default: typeof room.isDefault === 'undefined' ? false : room.isDefault, ro: typeof room.isReadOnly === 'undefined' ? false : room.isReadOnly, sysMes: typeof room.displaySystemMessages === 'undefined' ? true : room.displaySystemMessages, @@ -64,7 +64,7 @@ export class AppRoomsConverter { slugifiedName: room.name, type: this._convertTypeToApp(room.t), creator, - usernames: room.usernames, + members: room.members, isDefault: typeof room.default === 'undefined' ? false : room.default, isReadOnly: typeof room.ro === 'undefined' ? false : room.ro, displaySystemMessages: typeof room.sysMes === 'undefined' ? true : room.sysMes, diff --git a/packages/rocketchat-authorization/server/functions/canAccessRoom.js b/packages/rocketchat-authorization/server/functions/canAccessRoom.js index 31a6e17674de..1084bb8ecf04 100644 --- a/packages/rocketchat-authorization/server/functions/canAccessRoom.js +++ b/packages/rocketchat-authorization/server/functions/canAccessRoom.js @@ -12,7 +12,7 @@ RocketChat.authz.roomAccessValidators = [ function(room, user = {}) { const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id); if (subscription) { - return subscription._room; + return RocketChat.models.Rooms.findOneById(subscription.rid); } } ]; diff --git a/packages/rocketchat-authorization/server/models/Permissions.js b/packages/rocketchat-authorization/server/models/Permissions.js index a82e0411fca0..220e8d94a827 100644 --- a/packages/rocketchat-authorization/server/models/Permissions.js +++ b/packages/rocketchat-authorization/server/models/Permissions.js @@ -29,5 +29,4 @@ class ModelPermissions extends RocketChat.models._Base { } } -RocketChat.models.Permissions = new ModelPermissions('permissions', true); -RocketChat.models.Permissions.cache.load(); +RocketChat.models.Permissions = new ModelPermissions('permissions'); diff --git a/packages/rocketchat-authorization/server/models/Roles.js b/packages/rocketchat-authorization/server/models/Roles.js index 8c5f1fed85fa..f93c95883c82 100644 --- a/packages/rocketchat-authorization/server/models/Roles.js +++ b/packages/rocketchat-authorization/server/models/Roles.js @@ -65,5 +65,4 @@ class ModelRoles extends RocketChat.models._Base { } } -RocketChat.models.Roles = new ModelRoles('roles', true); -RocketChat.models.Roles.cache.load(); +RocketChat.models.Roles = new ModelRoles('roles'); diff --git a/packages/rocketchat-authorization/server/publications/permissions.js b/packages/rocketchat-authorization/server/publications/permissions.js index 029109db025c..1477c91940d7 100644 --- a/packages/rocketchat-authorization/server/publications/permissions.js +++ b/packages/rocketchat-authorization/server/publications/permissions.js @@ -1,6 +1,8 @@ Meteor.methods({ 'permissions/get'(updatedAt) { this.unblock(); + // TODO: should we return this for non logged users? + // TODO: we could cache this collection const records = RocketChat.models.Permissions.find().fetch(); @@ -17,7 +19,17 @@ Meteor.methods({ } }); +RocketChat.models.Permissions.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + data = data || RocketChat.models.Permissions.findOneById(id); + break; -RocketChat.models.Permissions.on('changed', (type, permission) => { - RocketChat.Notifications.notifyLoggedInThisInstance('permissions-changed', type, permission); + case 'removed': + data = { _id: id }; + break; + } + + RocketChat.Notifications.notifyLoggedInThisInstance('permissions-changed', clientAction, data); }); diff --git a/packages/rocketchat-cas/server/cas_server.js b/packages/rocketchat-cas/server/cas_server.js index 492700a134f3..c5e689b444f9 100644 --- a/packages/rocketchat-cas/server/cas_server.js +++ b/packages/rocketchat-cas/server/cas_server.js @@ -233,7 +233,6 @@ Accounts.registerLoginHandler(function(options) { } if (!RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, userId)) { - RocketChat.models.Rooms.addUsernameByName(room_name, result.username); RocketChat.models.Subscriptions.createWithRoomAndUser(room, user, { ts: new Date(), open: true, diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index 922a44c9d75c..b84796ea3491 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -503,9 +503,6 @@ Template.channelSettingsInfo.helpers({ topic() { return Template.instance().room.topic; }, - users() { - return Template.instance().room.usernames; - }, channelIcon() { const roomType = Template.instance().room.t; diff --git a/packages/rocketchat-channel-settings/server/models/Rooms.js b/packages/rocketchat-channel-settings/server/models/Rooms.js index 07e81e5ff17f..de14f8c365c8 100644 --- a/packages/rocketchat-channel-settings/server/models/Rooms.js +++ b/packages/rocketchat-channel-settings/server/models/Rooms.js @@ -16,27 +16,27 @@ RocketChat.models.Rooms.setReadOnlyById = function(_id, readOnly) { }; const update = { $set: { - ro: readOnly + ro: readOnly, + muted: [] } }; if (readOnly) { - RocketChat.models.Subscriptions.findByRoomId(_id).forEach(function(subscription) { - if (subscription._user == null) { + RocketChat.models.Subscriptions.findByRoomIdWhenUsernameExists(_id, { fields: { 'u._id': 1, 'u.username': 1 } }).forEach(function({ u: user }) { + if (RocketChat.authz.hasPermission(user._id, 'post-readonly')) { return; } - const user = subscription._user; - if (RocketChat.authz.hasPermission(user._id, 'post-readonly') === false) { - if (!update.$set.muted) { - update.$set.muted = []; - } - return update.$set.muted.push(user.username); - } + return update.$set.muted.push(user.username); }); } else { update.$unset = { muted: '' }; } + + if (update.$set.muted.length === 0) { + delete update.$set.muted; + } + return this.update(query, update); }; diff --git a/packages/rocketchat-graphql/server/resolvers/channels/Channel-type.js b/packages/rocketchat-graphql/server/resolvers/channels/Channel-type.js index e16dffa8ab83..423ac8e7813e 100644 --- a/packages/rocketchat-graphql/server/resolvers/channels/Channel-type.js +++ b/packages/rocketchat-graphql/server/resolvers/channels/Channel-type.js @@ -14,9 +14,10 @@ const resolver = { return root.name; }, members: (root) => { - return root.usernames.map( - username => RocketChat.models.Users.findOneByUsername(username) - ); + const ids = RocketChat.models.Subscriptions.findByRoomIdWhenUserIdExists(root._id, { fields: { 'u._id': 1 } }) + .fetch() + .map(sub => sub.u._id); + return RocketChat.models.Users.findByIds(ids).fetch(); }, owners: (root) => { // there might be no owner @@ -26,7 +27,9 @@ const resolver = { return [RocketChat.models.Users.findOneByUsername(root.u.username)]; }, - numberOfMembers: (root) => (root.usernames || []).length, + numberOfMembers: (root) => { + return RocketChat.models.Subscriptions.findByRoomId(root._id).count(); + }, numberOfMessages: property('msgs'), readOnly: (root) => root.ro === true, direct: (root) => root.t === 'd', diff --git a/packages/rocketchat-graphql/server/resolvers/channels/channelsByUser.js b/packages/rocketchat-graphql/server/resolvers/channels/channelsByUser.js index 4fdc6e15a309..2b46164bf620 100644 --- a/packages/rocketchat-graphql/server/resolvers/channels/channelsByUser.js +++ b/packages/rocketchat-graphql/server/resolvers/channels/channelsByUser.js @@ -13,7 +13,8 @@ const resolver = { throw new Error('No user'); } - const rooms = RocketChat.models.Rooms.findByContainingUsername(user.username, { + const roomIds = RocketChat.models.Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch().map(s => s.rid); + const rooms = RocketChat.models.Rooms.findByIds(roomIds, { sort: { name: 1 }, diff --git a/packages/rocketchat-graphql/server/resolvers/users/User-type.js b/packages/rocketchat-graphql/server/resolvers/users/User-type.js index d768b78ab7f8..ee4df2393346 100644 --- a/packages/rocketchat-graphql/server/resolvers/users/User-type.js +++ b/packages/rocketchat-graphql/server/resolvers/users/User-type.js @@ -21,7 +21,7 @@ const resolver = { return await RocketChat.models.Rooms.findBySubscriptionUserId(_id).fetch(); }), directMessages: ({ username }) => { - return RocketChat.models.Rooms.findByTypeContainingUsername('d', username).fetch(); + return RocketChat.models.Rooms.findDirectRoomContainingUsername(username).fetch(); } } }; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 96b4e6c56eee..e21cf7492964 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -943,13 +943,13 @@ "edit-other-user-active-status": "Edit Other User Active Status", "edit-other-user-active-status_description": "Permission to enable or disable other accounts", "edit-other-user-info": "Edit Other User Information", - "edit-other-user-info_description": "Permission to change other user’s name, username or email address.", + "edit-other-user-info_description": "Permission to change other user's name, username or email address.", "edit-other-user-password": "Edit Other User Password", - "edit-other-user-password_description": "Permission to modify other user’s passwords. Requires edit-other-user-info permission.", + "edit-other-user-password_description": "Permission to modify other user's passwords. Requires edit-other-user-info permission.", "edit-privileged-setting": "Edit privileged Setting", "edit-privileged-setting_description": "Permission to edit settings", "edit-room": "Edit Room", - "edit-room_description": "Permission to edit a room’s name, topic, type (private or public status) and status (active or archived)", + "edit-room_description": "Permission to edit a room's name, topic, type (private or public status) and status (active or archived)", "Edit_Custom_Field": "Edit Custom Field", "Edit_Department": "Edit Department", "Edit_previous_message": "`%s` - Edit previous message", @@ -2223,7 +2223,7 @@ "Settings": "Settings", "Settings_updated": "Settings updated", "Setup_Wizard": "Setup Wizard", - "Setup_Wizard_Info": "We’ll guide you through setting up your first admin user, configuring your organisation and registering your server to receive free push notifications and more.", + "Setup_Wizard_Info": "We'll guide you through setting up your first admin user, configuring your organisation and registering your server to receive free push notifications and more.", "Share_Location_Title": "Share Location?", "Shared_Location": "Shared Location", "Should_be_a_URL_of_an_image": "Should be a URL of an image.", diff --git a/packages/rocketchat-i18n/i18n/fr.i18n.json b/packages/rocketchat-i18n/i18n/fr.i18n.json index 58469f085c14..0f28bc445da7 100644 --- a/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -336,7 +336,7 @@ "Auth_Token": "Jeton d'Auth", "Author": "Auteur", "Author_Information": "Informations sur l'auteur", - "Authorization_URL": "URL d’autorisation", + "Authorization_URL": "URL d'autorisation", "Authorize": "Autoriser", "auto-translate": "Traduction automatique", "auto-translate_description": "Permission d'utiliser l'outil de traduction automatique", @@ -1296,7 +1296,7 @@ "Importer_importing_users": "Importation des utilisateurs.", "Importer_not_in_progress": "L'import n'est pas en cours d'exécution.", "Importer_not_setup": "L'importateur n'est pas configuré correctement, car il n'a renvoyé aucune donnée.", - "Importer_Prepare_Restart_Import": "Recommencer l’Importation", + "Importer_Prepare_Restart_Import": "Recommencer l'Importation", "Importer_Prepare_Start_Import": "Commencer l'Importation", "Importer_Prepare_Uncheck_Archived_Channels": "Désélectionner les canaux archivés", "Importer_Prepare_Uncheck_Deleted_Users": "Désélectionner les utilisateurs supprimés", @@ -1810,7 +1810,7 @@ "New_visitor_navigation": "Nouvelle navigation: __history__", "No_available_agents_to_transfer": "Aucun agent disponible pour le transfert", "No_channel_with_name_%s_was_found": "Aucun canal nommé \"%s\" n'a été trouvé !", - "No_channels_yet": "Vous ne faites partie d’aucun canal pour le moment.", + "No_channels_yet": "Vous ne faites partie d'aucun canal pour le moment.", "No_direct_messages_yet": "Vous n'avez pris part à aucune discussion pour le moment.", "No_Encryption": "Pas de Chiffrement", "No_group_with_name_%s_was_found": "Aucun groupe privé nommé \"%s\" n'a été trouvé !", diff --git a/packages/rocketchat-i18n/i18n/uk.i18n.json b/packages/rocketchat-i18n/i18n/uk.i18n.json index 4a69d5220f2c..78af4414f549 100644 --- a/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -1372,7 +1372,7 @@ "Invalid_email": "Невірний email", "Invalid_Export_File": "Файл завантажений не є коректним %s файл експорту.", "Invalid_Import_File_Type": "Невірний тип файлу імпорту.", - "Invalid_name": "Ім’я має бути заповненим", + "Invalid_name": "Ім'я має бути заповненим", "Invalid_notification_setting_s": "Неприпустима настройка сповіщення: %s", "Invalid_pass": "Пароль не має бути пустим", "Invalid_reason": "Причина для приєднання не може бути порожньою", @@ -1784,7 +1784,7 @@ "My_location": "Моє місцезнаходження", "n_messages": "%s повідомлень", "N_new_messages": " %s нових повідомлень", - "Name": "Ім’я", + "Name": "Ім'я", "Name_cant_be_empty": "Ім'я не може бути порожнім", "Name_of_agent": "ім'я агента", "Name_optional": "Ім'я (не обов'язково)", @@ -2586,10 +2586,10 @@ "UserDataDownload_Requested": "Завантажте потрібний файл", "UserDataDownload_Requested_Text": "Ваш файл даних буде згенерований. Посилання на його завантаження буде надіслано на вашу електронну адресу, коли вона буде готова.", "UserDataDownload_RequestExisted_Text": "Ваш файл даних вже створено. Посилання на його завантаження буде надіслано на вашу електронну адресу, коли вона буде готова.", - "Username": "Ім’я користувача", + "Username": "Ім'я користувача", "Username_already_exist": "Ім'я користувача вже існує. Будь ласка, спробуйте інше ім'я користувача.", "Username_and_message_must_not_be_empty": "Ім'я користувача і повідомлення не повинно бути порожнім.", - "Username_cant_be_empty": "Ім’я має бути заповненим", + "Username_cant_be_empty": "Ім'я має бути заповненим", "Username_Change_Disabled": "Ваш Rocket.Chat адміністратор відключив зміна імен користувачів", "Username_denied_the_OTR_session": "__username__ заперечував сесію OTR", "Username_description": "Ім'я користувача використовується, щоб дозволити іншим про вас в повідомленнях.", diff --git a/packages/rocketchat-integrations/server/lib/triggerHandler.js b/packages/rocketchat-integrations/server/lib/triggerHandler.js index 36a9b4777634..0894f3fb1ea3 100644 --- a/packages/rocketchat-integrations/server/lib/triggerHandler.js +++ b/packages/rocketchat-integrations/server/lib/triggerHandler.js @@ -89,12 +89,11 @@ RocketChat.integrations.triggerHandler = new class RocketChatIntegrationHandler history.data = { ...data }; if (data.user) { - history.data.user = _.omit(data.user, ['meta', '$loki', 'services']); + history.data.user = _.omit(data.user, ['services']); } if (data.room) { - history.data.room = _.omit(data.room, ['meta', '$loki', 'usernames']); - history.data.room.usernames = ['this_will_be_filled_in_with_usernames_when_replayed']; + history.data.room = data.room; } } diff --git a/packages/rocketchat-integrations/server/lib/validation.js b/packages/rocketchat-integrations/server/lib/validation.js index 22f83b80a780..22c06a8ce6dd 100644 --- a/packages/rocketchat-integrations/server/lib/validation.js +++ b/packages/rocketchat-integrations/server/lib/validation.js @@ -70,7 +70,7 @@ function _verifyUserHasPermissionForChannels(integration, userId, channels) { throw new Meteor.Error('error-invalid-room', 'Invalid room', { function: 'validateOutgoing._verifyUserHasPermissionForChannels' }); } - if (record.usernames && !RocketChat.authz.hasPermission(userId, 'manage-integrations') && RocketChat.authz.hasPermission(userId, 'manage-own-integrations') && !record.usernames.includes(Meteor.user().username)) { + if (!RocketChat.authz.hasAllPermission(userId, 'manage-integrations', 'manage-own-integrations') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(record._id, userId, { fields: { _id: 1 } })) { throw new Meteor.Error('error-invalid-channel', 'Invalid Channel', { function: 'validateOutgoing._verifyUserHasPermissionForChannels' }); } } diff --git a/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.js b/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.js index ffababb1d137..c933d9d4de87 100644 --- a/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.js +++ b/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.js @@ -70,7 +70,7 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'addIncomingIntegration' }); } - if (record.usernames && !RocketChat.authz.hasPermission(this.userId, 'manage-integrations') && RocketChat.authz.hasPermission(this.userId, 'manage-own-integrations') && !record.usernames.includes(Meteor.user().username)) { + if (!RocketChat.authz.hasAllPermission(this.userId, 'manage-integrations', 'manage-own-integrations') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(record._id, this.userId, { fields: { _id: 1 } })) { throw new Meteor.Error('error-invalid-channel', 'Invalid Channel', { method: 'addIncomingIntegration' }); } } diff --git a/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.js b/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.js index bb789b816ad8..789c12e3bdaf 100644 --- a/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.js +++ b/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.js @@ -72,7 +72,7 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'updateIncomingIntegration' }); } - if (record.usernames && !RocketChat.authz.hasPermission(this.userId, 'manage-integrations') && RocketChat.authz.hasPermission(this.userId, 'manage-own-integrations') && !record.usernames.includes(Meteor.user().username)) { + if (!RocketChat.authz.hasAllPermission(this.userId, 'manage-integrations', 'manage-own-integrations') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(record._id, this.userId, { fields: { _id: 1 } })) { throw new Meteor.Error('error-invalid-channel', 'Invalid Channel', { method: 'updateIncomingIntegration' }); } } diff --git a/packages/rocketchat-integrations/server/processWebhookMessage.js b/packages/rocketchat-integrations/server/processWebhookMessage.js index 91b3a85f55ab..76aa1e73a672 100644 --- a/packages/rocketchat-integrations/server/processWebhookMessage.js +++ b/packages/rocketchat-integrations/server/processWebhookMessage.js @@ -37,7 +37,7 @@ this.processWebhookMessage = function(messageObj, user, defaultValues = { channe throw new Meteor.Error('invalid-channel'); } - if (mustBeJoined && !room.usernames.includes(user.username)) { + if (mustBeJoined && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { fields: { _id: 1 } })) { // throw new Meteor.Error('invalid-room', 'Invalid room provided to send a message to, must be joined.'); throw new Meteor.Error('invalid-channel'); // Throwing the generic one so people can't "brute force" find rooms } diff --git a/packages/rocketchat-lib/client/lib/cachedCollection.js b/packages/rocketchat-lib/client/lib/cachedCollection.js index d1f6b3484f96..4daa5d35467f 100644 --- a/packages/rocketchat-lib/client/lib/cachedCollection.js +++ b/packages/rocketchat-lib/client/lib/cachedCollection.js @@ -213,7 +213,6 @@ class CachedCollection { Meteor.call(this.methodName, (error, data) => { this.log(`${ data.length } records loaded from server`); data.forEach((record) => { - delete record.$loki; RocketChat.callbacks.run(`cachedCollection-loadFromServer-${ this.name }`, record, 'changed'); this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); @@ -276,7 +275,6 @@ class CachedCollection { }); for (const record of changes) { - delete record.$loki; RocketChat.callbacks.run(`cachedCollection-sync-${ this.name }`, record, record._deletedAt? 'removed' : 'changed'); if (record._deletedAt) { this.collection.remove({ _id: record._id }); @@ -342,7 +340,6 @@ class CachedCollection { this.collection.remove(record._id); RoomManager.close(record.t+record.name); } else { - delete record.$loki; this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); } diff --git a/packages/rocketchat-lib/client/lib/openRoom.js b/packages/rocketchat-lib/client/lib/openRoom.js index 3ca566a57cc4..09dac262d5ea 100644 --- a/packages/rocketchat-lib/client/lib/openRoom.js +++ b/packages/rocketchat-lib/client/lib/openRoom.js @@ -42,7 +42,6 @@ function openRoom(type, name) { Session.set('roomNotFound', {type, name}); return BlazeLayout.render('main', {center: 'roomNotFound'}); } else { - delete record.$loki; RocketChat.models.Rooms.upsert({ _id: record._id }, _.omit(record, '_id')); RoomManager.close(type + name); return openRoom(type, name); diff --git a/packages/rocketchat-lib/lib/callbacks.js b/packages/rocketchat-lib/lib/callbacks.js index a1a2fee70878..d9b644074106 100644 --- a/packages/rocketchat-lib/lib/callbacks.js +++ b/packages/rocketchat-lib/lib/callbacks.js @@ -26,6 +26,7 @@ RocketChat.callbacks.priority = { LOW: 1000 }; +const getHooks = hookName => RocketChat.callbacks[hookName] || []; /* * Add a callback function to a hook @@ -33,24 +34,26 @@ RocketChat.callbacks.priority = { * @param {Function} callback - The callback function */ -RocketChat.callbacks.add = function(hook, callback, priority, id) { - if (priority == null) { - priority = RocketChat.callbacks.priority.MEDIUM; - } +RocketChat.callbacks.add = function(hook, callback, priority, id = Random.id()) { if (!_.isNumber(priority)) { priority = RocketChat.callbacks.priority.MEDIUM; } callback.priority = priority; - callback.id = id || Random.id(); - RocketChat.callbacks[hook] = RocketChat.callbacks[hook] || []; + callback.id = id; + RocketChat.callbacks[hook] = getHooks(hook); + if (RocketChat.callbacks.showTime === true) { const err = new Error; callback.stack = err.stack; } + if (RocketChat.callbacks[hook].find((cb) => cb.id === callback.id)) { return; } RocketChat.callbacks[hook].push(callback); + RocketChat.callbacks[hook] = _.sortBy(RocketChat.callbacks[hook], function(callback) { + return callback.priority || RocketChat.callbacks.priority.MEDIUM; + }); }; @@ -60,8 +63,8 @@ RocketChat.callbacks.add = function(hook, callback, priority, id) { * @param {string} id - The callback's id */ -RocketChat.callbacks.remove = function(hookName, id) { - RocketChat.callbacks[hookName] = _.reject(RocketChat.callbacks[hookName], (callback) => callback.id === id); +RocketChat.callbacks.remove = function(hook, id) { + RocketChat.callbacks[hook] = getHooks(hook).filter(callback => callback.id !== id); }; @@ -75,59 +78,56 @@ RocketChat.callbacks.remove = function(hookName, id) { RocketChat.callbacks.run = function(hook, item, constant) { const callbacks = RocketChat.callbacks[hook]; - if (callbacks && callbacks.length) { + if (!callbacks || !callbacks.length) { + return item; + } + + let rocketchatHooksEnd; + if (Meteor.isServer) { + rocketchatHooksEnd = RocketChat.metrics.rocketchatHooks.startTimer({hook, callbacks_length: callbacks.length}); + } - let rocketchatHooksEnd; + let totalTime = 0; + const result = callbacks.reduce(function(result, callback) { + let rocketchatCallbacksEnd; if (Meteor.isServer) { - rocketchatHooksEnd = RocketChat.metrics.rocketchatHooks.startTimer({hook, callbacks_length: callbacks.length}); + rocketchatCallbacksEnd = RocketChat.metrics.rocketchatCallbacks.startTimer({hook, callback: callback.id}); } - - let totalTime = 0; - const result = _.sortBy(callbacks, function(callback) { - return callback.priority || RocketChat.callbacks.priority.MEDIUM; - }).reduce(function(result, callback) { - let rocketchatCallbacksEnd; - if (Meteor.isServer) { - rocketchatCallbacksEnd = RocketChat.metrics.rocketchatCallbacks.startTimer({hook, callback: callback.id}); - } - let time = 0; - if (RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true) { - time = Date.now(); - } - const callbackResult = callback(result, constant); - if (RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true) { - const currentTime = Date.now() - time; - totalTime += currentTime; - if (RocketChat.callbacks.showTime === true) { - if (Meteor.isServer) { - rocketchatCallbacksEnd(); - RocketChat.statsTracker.timing('callbacks.time', currentTime, [`hook:${ hook }`, `callback:${ callback.id }`]); - } else { - let stack = callback.stack && typeof callback.stack.split === 'function' && callback.stack.split('\n'); - stack = stack && stack[2] && (stack[2].match(/\(.+\)/)||[])[0]; - console.log(String(currentTime), hook, callback.id, stack); - } + const time = RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true ? Date.now() : 0; + + const callbackResult = callback(result, constant); + + if (RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true) { + const currentTime = Date.now() - time; + totalTime += currentTime; + if (RocketChat.callbacks.showTime === true) { + if (Meteor.isServer) { + rocketchatCallbacksEnd(); + RocketChat.statsTracker.timing('callbacks.time', currentTime, [`hook:${ hook }`, `callback:${ callback.id }`]); + } else { + let stack = callback.stack && typeof callback.stack.split === 'function' && callback.stack.split('\n'); + stack = stack && stack[2] && (stack[2].match(/\(.+\)/)||[])[0]; + console.log(String(currentTime), hook, callback.id, stack); } } - return (typeof callbackResult === 'undefined') ? result : callbackResult; - }, item); + } + return (typeof callbackResult === 'undefined') ? result : callbackResult; + }, item); + if (Meteor.isServer) { + rocketchatHooksEnd(); + } + + if (RocketChat.callbacks.showTotalTime === true) { if (Meteor.isServer) { - rocketchatHooksEnd(); + RocketChat.statsTracker.timing('callbacks.totalTime', totalTime, [`hook:${ hook }`]); + } else { + console.log(`${ hook }:`, totalTime); } + } - if (RocketChat.callbacks.showTotalTime === true) { - if (Meteor.isServer) { - RocketChat.statsTracker.timing('callbacks.totalTime', totalTime, [`hook:${ hook }`]); - } else { - console.log(`${ hook }:`, totalTime); - } - } + return result; - return result; - } else { - return item; - } }; @@ -142,9 +142,8 @@ RocketChat.callbacks.runAsync = function(hook, item, constant) { const callbacks = RocketChat.callbacks[hook]; if (Meteor.isServer && callbacks && callbacks.length) { Meteor.defer(function() { - _.sortBy(callbacks, (callback) => callback.priority || RocketChat.callbacks.priority.MEDIUM).forEach((callback) => callback(item, constant)); + callbacks.forEach(callback => callback(item, constant)); }); - } else { - return item; } + return item; }; diff --git a/packages/rocketchat-lib/lib/promises.js b/packages/rocketchat-lib/lib/promises.js index 6a52e562ae91..2b0244629c15 100644 --- a/packages/rocketchat-lib/lib/promises.js +++ b/packages/rocketchat-lib/lib/promises.js @@ -18,6 +18,7 @@ RocketChat.promises.priority = { LOW: 1000 }; +const getHook = hookName => RocketChat.promises[hookName] || []; /* * Add a callback function to a hook @@ -26,14 +27,14 @@ RocketChat.promises.priority = { */ RocketChat.promises.add = function(hook, callback, p = RocketChat.promises.priority.MEDIUM, id) { - const priority = !_.isNumber(p) ? RocketChat.promises.priority.MEDIUM : p; - callback.priority = priority; + callback.priority = _.isNumber(p) ? p : RocketChat.promises.priority.MEDIUM; callback.id = id || Random.id(); - RocketChat.promises[hook] = RocketChat.promises[hook] || []; + RocketChat.promises[hook] = getHook(hook); if (RocketChat.promises[hook].find(cb => cb.id === callback.id)) { return; } RocketChat.promises[hook].push(callback); + RocketChat.promises[hook] = _.sortBy(RocketChat.promises[hook], callback => callback.priority || RocketChat.promises.priority.MEDIUM); }; @@ -43,8 +44,8 @@ RocketChat.promises.add = function(hook, callback, p = RocketChat.promises.prior * @param {string} id - The callback's id */ -RocketChat.promises.remove = function(hookName, id) { - RocketChat.promises[hookName] = _.reject(RocketChat.promises[hookName], (callback) => callback.id === id); +RocketChat.promises.remove = function(hook, id) { + RocketChat.promises[hook] = getHook(hook).filter(callback => callback.id !== id); }; @@ -57,16 +58,11 @@ RocketChat.promises.remove = function(hookName, id) { */ RocketChat.promises.run = function(hook, item, constant) { - let callbacks = RocketChat.promises[hook]; + const callbacks = RocketChat.promises[hook]; if (callbacks == null || callbacks.length === 0) { return Promise.resolve(item); } - callbacks = _.sortBy(callbacks, (callback) => callback.priority || RocketChat.promises.priority.MEDIUM); - return callbacks.reduce(function(previousPromise, callback) { - return new Promise(function(resolve, reject) { - return previousPromise.then((result) => callback(result, constant).then(resolve, reject)); - }); - }, Promise.resolve(item)); + return callbacks.reduce((previousPromise, callback) => previousPromise.then(result => callback(result, constant)), Promise.resolve(item)); }; @@ -82,9 +78,5 @@ RocketChat.promises.runAsync = function(hook, item, constant) { if (!Meteor.isServer || callbacks == null || callbacks.length === 0) { return item; } - Meteor.defer(() => { - _.sortBy(callbacks, (callback) => callback.priority || RocketChat.promises.priority.MEDIUM).forEach(function(callback) { - callback(item, constant); - }); - }); + Meteor.defer(() => callbacks.forEach(callback => callback(item, constant))); }; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 4592252e5633..e1d0cec3e599 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -141,9 +141,6 @@ Package.onUse(function(api) { api.addFiles('server/startup/statsTracker.js', 'server'); - // CACHE - api.addFiles('server/startup/cache/CacheLoad.js', 'server'); - // SERVER PUBLICATIONS api.addFiles('server/publications/settings.js', 'server'); diff --git a/packages/rocketchat-lib/rocketchat.info b/packages/rocketchat-lib/rocketchat.info index def1769a6785..010f28401586 100644 --- a/packages/rocketchat-lib/rocketchat.info +++ b/packages/rocketchat-lib/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "0.66.3" + "version": "0.67.0" } diff --git a/packages/rocketchat-lib/server/functions/Notifications.js b/packages/rocketchat-lib/server/functions/Notifications.js index 808d0b8bb902..7e0e09f4b7ad 100644 --- a/packages/rocketchat-lib/server/functions/Notifications.js +++ b/packages/rocketchat-lib/server/functions/Notifications.js @@ -27,11 +27,6 @@ RocketChat.Notifications = new class { this.streamLogged.allowRead('logged'); this.streamRoom.allowRead(function(eventName, extraData) { const [roomId] = eventName.split('/'); - const user = Meteor.users.findOne(this.userId, { - fields: { - username: 1 - } - }); const room = RocketChat.models.Rooms.findOneById(roomId); if (!room) { console.warn(`Invalid streamRoom eventName: "${ eventName }"`); @@ -43,7 +38,8 @@ RocketChat.Notifications = new class { if (this.userId == null) { return false; } - return room.usernames.indexOf(user.username) > -1; + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, { fields: { _id: 1 } }); + return subscription != null; }); this.streamRoomUsers.allowRead('none'); this.streamUser.allowRead(function(eventName) { diff --git a/packages/rocketchat-lib/server/functions/addUserToDefaultChannels.js b/packages/rocketchat-lib/server/functions/addUserToDefaultChannels.js index 414e5e3b7588..71718be0b84c 100644 --- a/packages/rocketchat-lib/server/functions/addUserToDefaultChannels.js +++ b/packages/rocketchat-lib/server/functions/addUserToDefaultChannels.js @@ -5,7 +5,9 @@ RocketChat.addUserToDefaultChannels = function(user, silenced) { // put user in default rooms const muted = room.ro && !RocketChat.authz.hasPermission(user._id, 'post-readonly'); - RocketChat.models.Rooms.addUsernameById(room._id, user.username, muted); + if (muted) { + RocketChat.models.Rooms.muteUsernameByRoomId(room._id, user.username); + } if (!RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id)) { diff --git a/packages/rocketchat-lib/server/functions/addUserToRoom.js b/packages/rocketchat-lib/server/functions/addUserToRoom.js index 527b68fab3f8..42a8823c6edc 100644 --- a/packages/rocketchat-lib/server/functions/addUserToRoom.js +++ b/packages/rocketchat-lib/server/functions/addUserToRoom.js @@ -13,7 +13,10 @@ RocketChat.addUserToRoom = function(rid, user, inviter, silenced) { } const muted = room.ro && !RocketChat.authz.hasPermission(user._id, 'post-readonly'); - RocketChat.models.Rooms.addUsernameById(rid, user.username, muted); + if (muted) { + RocketChat.models.Rooms.muteUsernameByRoomId(rid, user.username); + } + RocketChat.models.Subscriptions.createWithRoomAndUser(room, user, { ts: now, open: true, diff --git a/packages/rocketchat-lib/server/functions/createRoom.js b/packages/rocketchat-lib/server/functions/createRoom.js index 0aa21c9e96dd..385b4ef43a8e 100644 --- a/packages/rocketchat-lib/server/functions/createRoom.js +++ b/packages/rocketchat-lib/server/functions/createRoom.js @@ -31,7 +31,7 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData fname: name, t: type, msgs: 0, - usernames: members, + usersCount: 0, u: { _id: owner._id, username: owner.username @@ -42,6 +42,10 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData sysMes: readOnly !== true }); + if (type === 'd') { + room.usernames = members; + } + if (Apps && Apps.isLoaded()) { const prevent = Promise.await(Apps.getBridges().getListenerBridge().roomEvent('IPreRoomCreatePrevent', room)); if (prevent) { diff --git a/packages/rocketchat-lib/server/functions/deleteUser.js b/packages/rocketchat-lib/server/functions/deleteUser.js index 362dc83a5014..5a65b0fae5fb 100644 --- a/packages/rocketchat-lib/server/functions/deleteUser.js +++ b/packages/rocketchat-lib/server/functions/deleteUser.js @@ -19,7 +19,7 @@ RocketChat.deleteUser = function(userId) { 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) { + if (room.t !== 'c' && RocketChat.models.Subscriptions.findByRoomId(room._id).count() === 1) { RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted) } if (room.t === 'd') { @@ -30,8 +30,7 @@ RocketChat.deleteUser = function(userId) { }); 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.Rooms.removeDirectRoomContainingUsername(user.username); // Remove direct rooms with the user // removes user's avatar if (user.avatarOrigin === 'upload' || user.avatarOrigin === 'url') { diff --git a/packages/rocketchat-lib/server/functions/removeUserFromRoom.js b/packages/rocketchat-lib/server/functions/removeUserFromRoom.js index 87b9faf5d086..dc910a0a7ab1 100644 --- a/packages/rocketchat-lib/server/functions/removeUserFromRoom.js +++ b/packages/rocketchat-lib/server/functions/removeUserFromRoom.js @@ -3,9 +3,10 @@ RocketChat.removeUserFromRoom = function(rid, user) { if (room) { RocketChat.callbacks.run('beforeLeaveRoom', user, room); - RocketChat.models.Rooms.removeUsernameById(rid, user.username); - if (room.usernames.indexOf(user.username) !== -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, user._id, { fields: { _id: 1 } }); + + if (subscription) { const removedUser = user; RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser(rid, removedUser); } @@ -17,6 +18,7 @@ RocketChat.removeUserFromRoom = function(rid, user) { RocketChat.models.Subscriptions.removeByRoomIdAndUserId(rid, user._id); Meteor.defer(function() { + // TODO: CACHE: maybe a queue? RocketChat.callbacks.run('afterLeaveRoom', user, room); }); } diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js index 321d085641f0..ff3bc8bef8ae 100644 --- a/packages/rocketchat-lib/server/functions/sendMessage.js +++ b/packages/rocketchat-lib/server/functions/sendMessage.js @@ -91,15 +91,6 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) { message.ts = new Date(); } - if (!room.usernames || room.usernames.length === 0) { - const updated_room = RocketChat.models.Rooms.findOneById(room._id); - if (updated_room) { - room = updated_room; - } else { - room.usernames = []; - } - } - if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { message.unread = true; } diff --git a/packages/rocketchat-lib/server/functions/setRealName.js b/packages/rocketchat-lib/server/functions/setRealName.js index da7be9783ca0..8781bfb15658 100644 --- a/packages/rocketchat-lib/server/functions/setRealName.js +++ b/packages/rocketchat-lib/server/functions/setRealName.js @@ -17,6 +17,8 @@ RocketChat._setRealName = function(userId, name) { RocketChat.models.Users.setName(user._id, name); user.name = name; + RocketChat.models.Subscriptions.updateDirectFNameByName(user.username, name); + if (RocketChat.settings.get('UI_Use_Real_Name') === true) { RocketChat.Notifications.notifyLogged('Users:NameChanged', { _id: user._id, diff --git a/packages/rocketchat-lib/server/lib/debug.js b/packages/rocketchat-lib/server/lib/debug.js index 7b0631970320..faf36b1e4a92 100644 --- a/packages/rocketchat-lib/server/lib/debug.js +++ b/packages/rocketchat-lib/server/lib/debug.js @@ -46,7 +46,11 @@ const traceConnection = (enable, filter, prefix, name, connection, userId) => { const wrapMethods = function(name, originalHandler, methodsMap) { methodsMap[name] = function() { traceConnection(Log_Trace_Methods, Log_Trace_Methods_Filter, 'method', name, this.connection, this.userId); - const end = RocketChat.metrics.meteorMethods.startTimer({method: name}); + const end = RocketChat.metrics.meteorMethods.startTimer({ + method: name, + has_connection: this.connection != null, + has_user: this.userId != null + }); const args = name === 'ufsWrite' ? Array.prototype.slice.call(arguments, 1) : arguments; logger.method(name, '-> userId:', Meteor.userId(), ', arguments: ', args); diff --git a/packages/rocketchat-lib/server/lib/metrics.js b/packages/rocketchat-lib/server/lib/metrics.js index 61ac0298bd2b..af838ba23d18 100644 --- a/packages/rocketchat-lib/server/lib/metrics.js +++ b/packages/rocketchat-lib/server/lib/metrics.js @@ -13,7 +13,7 @@ RocketChat.metrics = {}; RocketChat.metrics.meteorMethods = new client.Summary({ name: 'rocketchat_meteor_methods', help: 'summary of meteor methods count and time', - labelNames: ['method'] + labelNames: ['method', 'has_connection', 'has_user'] }); RocketChat.metrics.rocketchatCallbacks = new client.Summary({ diff --git a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js index 675f0f6bf50f..2769f4c91fab 100644 --- a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js +++ b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js @@ -174,7 +174,8 @@ function sendAllNotifications(message, room) { // Don't fetch all users if room exceeds max members const maxMembersForNotification = RocketChat.settings.get('Notifications_Max_Room_Members'); - const disableAllMessageNotifications = room.usernames && room.usernames.length > maxMembersForNotification && maxMembersForNotification !== 0; + const roomMembersCount = RocketChat.models.Subscriptions.findByRoomId(room._id).count(); + const disableAllMessageNotifications = roomMembersCount > maxMembersForNotification && maxMembersForNotification !== 0; const query = { rid: room._id, @@ -236,29 +237,23 @@ function sendAllNotifications(message, room) { // on public channels, if a mentioned user is not member of the channel yet, he will first join the channel and then be notified based on his preferences. if (room.t === 'c') { - Promise.all(message.mentions - .filter(({ _id, username }) => _id !== 'here' && _id !== 'all' && !room.usernames.includes(username)) - .map(async(user) => { - await callJoinRoom(user, room._id); - - return user._id; - }) - ).then((users) => { - users.forEach((userId) => { - const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, userId); - - sendNotification({ - subscription, - sender, - hasMentionToAll, - hasMentionToHere, - message, - notificationMessage, - room, - mentionIds - }); - }); - }); + const mentions = message.mentions.filter(({ _id }) => _id !== 'here' && _id !== 'all').map(({ _id }) => _id); + Promise.all(RocketChat.models.Subscriptions.findByRoomIdAndUserIds(room._id, mentions) + .fetch() + .map(async subscription => { + await callJoinRoom(subscription.u, room._id); + return subscription; + })).then(subscriptions => subscriptions.forEach(subscription => + sendNotification({ + subscription, + sender, + hasMentionToAll, + hasMentionToHere, + message, + notificationMessage, + room, + mentionIds + }))); } return message; diff --git a/packages/rocketchat-lib/server/methods/addUsersToRoom.js b/packages/rocketchat-lib/server/methods/addUsersToRoom.js index b00c42350d31..5a85068bdc06 100644 --- a/packages/rocketchat-lib/server/methods/addUsersToRoom.js +++ b/packages/rocketchat-lib/server/methods/addUsersToRoom.js @@ -16,8 +16,8 @@ Meteor.methods({ // Get user and room details const room = RocketChat.models.Rooms.findOneById(data.rid); const userId = Meteor.userId(); - const user = Meteor.user(); - const userInRoom = Array.isArray(room.usernames) && room.usernames.includes(user.username); + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(data.rid, userId, { fields: { _id: 1 } }); + const userInRoom = subscription != null; // Can't add to direct room ever if (room.t === 'd') { @@ -51,6 +51,7 @@ Meteor.methods({ } // Validate each user, then add to room + const user = Meteor.user(); data.users.forEach((username) => { const newUser = RocketChat.models.Users.findOneByUsername(username); if (!newUser) { @@ -58,7 +59,6 @@ Meteor.methods({ method: 'addUsersToRoom' }); } - RocketChat.addUserToRoom(data.rid, newUser, user); }); diff --git a/packages/rocketchat-lib/server/methods/getChannelHistory.js b/packages/rocketchat-lib/server/methods/getChannelHistory.js index b3583cb253ea..a8039a7594f6 100644 --- a/packages/rocketchat-lib/server/methods/getChannelHistory.js +++ b/packages/rocketchat-lib/server/methods/getChannelHistory.js @@ -15,7 +15,7 @@ Meteor.methods({ } //Make sure they can access the room - if (room.t === 'c' && !RocketChat.authz.hasPermission(fromUserId, 'preview-c-room') && room.usernames.indexOf(room.username) === -1) { + if (room.t === 'c' && !RocketChat.authz.hasPermission(fromUserId, 'preview-c-room') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, fromUserId, { fields: { _id: 1 } })) { return false; } diff --git a/packages/rocketchat-lib/server/methods/leaveRoom.js b/packages/rocketchat-lib/server/methods/leaveRoom.js index dd13f2e67ba0..c85a44e1fefb 100644 --- a/packages/rocketchat-lib/server/methods/leaveRoom.js +++ b/packages/rocketchat-lib/server/methods/leaveRoom.js @@ -16,18 +16,19 @@ Meteor.methods({ throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'leaveRoom' }); } - if (!Array.from(room.usernames || []).includes(user.username)) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, user._id, { fields: { _id: 1 } }); + if (!subscription) { throw new Meteor.Error('error-user-not-in-room', 'You are not in this room', { method: 'leaveRoom' }); } // If user is room owner, check if there are other owners. If there isn't anyone else, warn user to set a new owner. if (RocketChat.authz.hasRole(user._id, 'owner', room._id)) { - const numOwners = RocketChat.authz.getUsersInRole('owner', room._id).fetch().length; + const numOwners = RocketChat.authz.getUsersInRole('owner', room._id).count(); if (numOwners === 1) { throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', { method: 'leaveRoom' }); } } - return RocketChat.removeUserFromRoom(rid, Meteor.user()); + return RocketChat.removeUserFromRoom(rid, user); } }); diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js index 09b04176d229..b714069c78fc 100644 --- a/packages/rocketchat-lib/server/models/Rooms.js +++ b/packages/rocketchat-lib/server/models/Rooms.js @@ -7,13 +7,8 @@ class ModelRooms extends RocketChat.models._Base { this.tryEnsureIndex({ 'name': 1 }, { unique: 1, sparse: 1 }); this.tryEnsureIndex({ 'default': 1 }); - this.tryEnsureIndex({ 'usernames': 1 }); this.tryEnsureIndex({ 't': 1 }); this.tryEnsureIndex({ 'u._id': 1 }); - - this.cache.ignoreUpdatedFields = ['msgs', 'lm']; - this.cache.ensureIndex(['t', 'name'], 'unique'); - this.cache.options = {fields: {usernames: 0}}; } findOneByIdOrName(_idOrName, options) { @@ -64,28 +59,6 @@ class ModelRooms extends RocketChat.models._Base { return this.findOne(query, options); } - findOneByIdContainingUsername(_id, username, options) { - const query = { - _id, - usernames: username - }; - - return this.findOne(query, options); - } - - findOneByNameAndTypeNotContainingUsername(name, type, username, options) { - const query = { - name, - t: type, - usernames: { - $ne: username - } - }; - - return this.findOne(query, options); - } - - // FIND findWithUsername(username, options) { @@ -106,6 +79,17 @@ class ModelRooms extends RocketChat.models._Base { return this.find(query, options); } + findByTypeInIds(type, ids, options) { + const query = { + _id: { + $in: ids + }, + t: type + }; + + return this.find(query, options); + } + findByTypes(types, options) { const query = { t: { @@ -123,23 +107,24 @@ class ModelRooms extends RocketChat.models._Base { } findBySubscriptionUserId(userId, options) { - let data; - if (this.useCache) { - data = RocketChat.models.Subscriptions.findByUserId(userId).fetch(); - data = data.map(function(item) { - if (item._room) { - return item._room; - } - console.log('Empty Room for Subscription', item); - }); - data = data.filter(item => item); - return this.arrayToCursor(this.processQueryOptionsOnResult(data, options)); - } + const data = RocketChat.models.Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() + .map(item => item.rid); + + const query = { + _id: { + $in: data + } + }; + + return this.find(query, options); + } - data = RocketChat.models.Subscriptions.findByUserId(userId, {fields: {rid: 1}}).fetch(); - data = data.map(item => item.rid); + findBySubscriptionTypeAndUserId(type, userId, options) { + const data = RocketChat.models.Subscriptions.findByUserIdAndType(userId, type, { fields: { rid: 1 } }).fetch() + .map(item => item.rid); const query = { + t: type, _id: { $in: data } @@ -149,20 +134,8 @@ class ModelRooms extends RocketChat.models._Base { } findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) { - if (this.useCache) { - let data = RocketChat.models.Subscriptions.findByUserId(userId).fetch(); - data = data.map(function(item) { - if (item._room) { - return item._room; - } - console.log('Empty Room for Subscription', item); - }); - data = data.filter(item => item && item._updatedAt > _updatedAt); - return this.arrayToCursor(this.processQueryOptionsOnResult(data, options)); - } - - let ids = RocketChat.models.Subscriptions.findByUserId(userId, {fields: {rid: 1}}).fetch(); - ids = ids.map(item => item.rid); + const ids = RocketChat.models.Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() + .map(item => item.rid); const query = { _id: { @@ -192,45 +165,6 @@ class ModelRooms extends RocketChat.models._Base { return this.find(query, options); } - findByNameContainingTypesWithUsername(name, types, options) { - const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); - - const $or = []; - for (const type of Array.from(types)) { - const obj = {name: nameRegex, t: type.type}; - if (type.username != null) { - obj.usernames = type.username; - } - if (type.ids != null) { - obj._id = {$in: type.ids}; - } - $or.push(obj); - } - - const query = {$or}; - - return this.find(query, options); - } - - findContainingTypesWithUsername(types, options) { - - const $or = []; - for (const type of Array.from(types)) { - const obj = {t: type.type}; - if (type.username != null) { - obj.usernames = type.username; - } - if (type.ids != null) { - obj._id = {$in: type.ids}; - } - $or.push(obj); - } - - const query = {$or}; - - return this.find(query, options); - } - findByNameContainingAndTypes(name, types, options) { const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); @@ -273,35 +207,29 @@ class ModelRooms extends RocketChat.models._Base { return this._db.find(query, options); } - findByNameAndTypesNotContainingUsername(name, types, username, options) { + findByNameAndTypesNotInIds(name, types, ids, options) { const query = { + _id: { + $ne: ids + }, t: { $in: types }, - name, - usernames: { - $ne: username - } + name }; // do not use cache return this._db.find(query, options); } - findByNameStartingAndTypes(name, types, options) { + findChannelAndPrivateByNameStarting(name, options) { const nameRegex = new RegExp(`^${ s.trim(s.escapeRegExp(name)) }`, 'i'); const query = { t: { - $in: types + $in: ['c', 'p'] }, - $or: [ - {name: nameRegex}, - { - t: 'd', - usernames: nameRegex - } - ] + name: nameRegex }; return this.find(query, options); @@ -318,62 +246,44 @@ class ModelRooms extends RocketChat.models._Base { return this.find(query, options); } - findByTypeContainingUsername(type, username, options) { + findDirectRoomContainingUsername(username, options) { const query = { - t: type, + t: 'd', usernames: username }; return this.find(query, options); } - findByTypeContainingUsernames(type, username, options) { - const query = { - t: type, - usernames: { $all: [].concat(username) } - }; - - return this.find(query, options); - } - - findByTypesAndNotUserIdContainingUsername(types, userId, username, options) { + findByTypeAndName(type, name, options) { const query = { - t: { - $in: types - }, - uid: { - $ne: userId - }, - usernames: username + name, + t: type }; return this.find(query, options); } - findByContainingUsername(username, options) { - const query = {usernames: username}; - - return this.find(query, options); - } - - findByTypeAndName(type, name, options) { - if (this.useCache) { - return this.cache.findByIndex('t,name', [type, name], options); - } + findByTypeAndNameContaining(type, name, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); const query = { - name, + name: nameRegex, t: type }; return this.find(query, options); } - findByTypeAndNameContainingUsername(type, name, username, options) { + findByTypeInIdsAndNameContaining(type, ids, name, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); + const query = { - name, - t: type, - usernames: username + _id: { + $in: ids + }, + name: nameRegex, + t: type }; return this.find(query, options); @@ -431,98 +341,6 @@ class ModelRooms extends RocketChat.models._Base { return this.update(query, update); } - addUsernameById(_id, username, muted) { - const query = {_id}; - - const update = { - $addToSet: { - usernames: username - } - }; - - if (muted) { - update.$addToSet.muted = username; - } - - return this.update(query, update); - } - - addUsernamesById(_id, usernames) { - const query = {_id}; - - const update = { - $addToSet: { - usernames: { - $each: usernames - } - } - }; - - return this.update(query, update); - } - - addUsernameByName(name, username) { - const query = {name}; - - const update = { - $addToSet: { - usernames: username - } - }; - - return this.update(query, update); - } - - removeUsernameById(_id, username) { - const query = {_id}; - - const update = { - $pull: { - usernames: username - } - }; - - return this.update(query, update); - } - - removeUsernamesById(_id, usernames) { - const query = {_id}; - - const update = { - $pull: { - usernames: { - $in: usernames - } - } - }; - - return this.update(query, update); - } - - removeUsernameFromAll(username) { - const query = {usernames: username}; - - const update = { - $pull: { - usernames: username - } - }; - - return this.update(query, update, { multi: true }); - } - - removeUsernameByName(name, username) { - const query = {name}; - - const update = { - $pull: { - usernames: username - } - }; - - return this.update(query, update); - } - setNameById(_id, name, fname) { const query = {_id}; @@ -581,6 +399,34 @@ class ModelRooms extends RocketChat.models._Base { return this.update(query, update); } + incUsersCountById(_id, inc = 1) { + const query = { _id }; + + const update = { + $inc: { + usersCount: inc + } + }; + + return this.update(query, update); + } + + incUsersCountByIds(ids, inc = 1) { + const query = { + _id: { + $in: ids + } + }; + + const update = { + $inc: { + usersCount: inc + } + }; + + return this.update(query, update, {multi: true}); + } + setLastMessageById(_id, lastMessage) { const query = {_id}; @@ -801,6 +647,7 @@ class ModelRooms extends RocketChat.models._Base { t: type, usernames, msgs: 0, + usersCount: 0, u: { _id: user._id, username: user.username @@ -820,7 +667,8 @@ class ModelRooms extends RocketChat.models._Base { t: type, name, usernames: [], - msgs: 0 + msgs: 0, + usersCount: 0 }; _.extend(room, extraData); @@ -844,9 +692,9 @@ class ModelRooms extends RocketChat.models._Base { return this.remove(query); } - removeByTypeContainingUsername(type, username) { + removeDirectRoomContainingUsername(username) { const query = { - t: type, + t: 'd', usernames: username }; diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js index 12d40fedee78..d3bfa893c3dc 100644 --- a/packages/rocketchat-lib/server/models/Subscriptions.js +++ b/packages/rocketchat-lib/server/models/Subscriptions.js @@ -3,6 +3,7 @@ class ModelSubscriptions extends RocketChat.models._Base { super(...arguments); this.tryEnsureIndex({ 'rid': 1, 'u._id': 1 }, { unique: 1 }); + this.tryEnsureIndex({ 'rid': 1, 'u.username': 1 }); this.tryEnsureIndex({ 'rid': 1, 'alert': 1, 'u._id': 1 }); this.tryEnsureIndex({ 'rid': 1, 'roles': 1 }); this.tryEnsureIndex({ 'u._id': 1, 'name': 1, 't': 1 }); @@ -20,20 +21,11 @@ class ModelSubscriptions extends RocketChat.models._Base { this.tryEnsureIndex({ 'autoTranslate': 1 }, { sparse: 1 }); this.tryEnsureIndex({ 'autoTranslateLanguage': 1 }, { sparse: 1 }); this.tryEnsureIndex({ 'userHighlights.0': 1 }, { sparse: 1 }); - - this.cache.ensureIndex('rid', 'array'); - this.cache.ensureIndex('u._id', 'array'); - this.cache.ensureIndex('name', 'array'); - this.cache.ensureIndex(['rid', 'u._id'], 'unique'); - this.cache.ensureIndex(['name', 'u._id'], 'unique'); } // FIND ONE findOneByRoomIdAndUserId(roomId, userId, options) { - if (this.useCache) { - return this.cache.findByIndex('rid,u._id', [roomId, userId], options).fetch(); - } const query = { rid: roomId, 'u._id': userId @@ -42,10 +34,16 @@ class ModelSubscriptions extends RocketChat.models._Base { return this.findOne(query, options); } + findOneByRoomIdAndUsername(roomId, username, options) { + const query = { + rid: roomId, + 'u.username': username + }; + + return this.findOne(query, options); + } + findOneByRoomNameAndUserId(roomName, userId) { - if (this.useCache) { - return this.cache.findByIndex('name,u._id', [roomName, userId]).fetch(); - } const query = { name: roomName, 'u._id': userId @@ -56,16 +54,32 @@ class ModelSubscriptions extends RocketChat.models._Base { // FIND findByUserId(userId, options) { - if (this.useCache) { - return this.cache.findByIndex('u._id', userId, options); - } - const query = { 'u._id': userId }; return this.find(query, options); } + findByUserIdAndType(userId, type, options) { + const query = { + 'u._id': userId, + t: type + }; + + return this.find(query, options); + } + + findByUserIdAndTypes(userId, types, options) { + const query = { + 'u._id': userId, + t: { + $in: types + } + }; + + return this.find(query, options); + } + findByUserIdUpdatedAfter(userId, updatedAt, options) { const query = { 'u._id': userId, @@ -106,21 +120,7 @@ class ModelSubscriptions extends RocketChat.models._Base { return this.find(query, options); } - findByTypeNameAndUserId(type, name, userId, options) { - const query = { - t: type, - name, - 'u._id': userId - }; - - return this.find(query, options); - } - findByRoomId(roomId, options) { - if (this.useCache) { - return this.cache.findByIndex('rid', roomId, options); - } - const query = { rid: roomId }; @@ -181,6 +181,18 @@ class ModelSubscriptions extends RocketChat.models._Base { return this.find(query); } + findByRoomIdWhenUserIdExists(rid, options) { + const query = { rid, 'u._id': { $exists: 1 } }; + + return this.find(query, options); + } + + findByRoomIdWhenUsernameExists(rid, options) { + const query = { rid, 'u.username': { $exists: 1 } }; + + return this.find(query, options); + } + findUnreadByUserId(userId) { const query = { 'u._id': userId, @@ -745,6 +757,21 @@ class ModelSubscriptions extends RocketChat.models._Base { return this.update(query, update, { multi: true }); } + updateDirectFNameByName(name, fname) { + const query = { + t: 'd', + name + }; + + const update = { + $set: { + fname + } + }; + + return this.update(query, update, { multi: true }); + } + // INSERT createWithRoomAndUser(room, user, extraData) { const subscription = { @@ -768,23 +795,43 @@ class ModelSubscriptions extends RocketChat.models._Base { ...extraData }; - return this.insert(subscription); + const result = this.insert(subscription); + + RocketChat.models.Rooms.incUsersCountById(room._id); + + return result; } // REMOVE removeByUserId(userId) { - const query = - { 'u._id': userId }; + const query = { + 'u._id': userId + }; + + const roomIds = this.findByUserId(userId).map(s => s.rid); + + const result = this.remove(query); + + if (Match.test(result, Number) && result > 0) { + RocketChat.models.Rooms.incUsersCountByIds(roomIds, -1); + } - return this.remove(query); + return result; } removeByRoomId(roomId) { - const query = - { rid: roomId }; + const query = { + rid: roomId + }; + + const result = this.remove(query); - return this.remove(query); + if (Match.test(result, Number) && result > 0) { + RocketChat.models.Rooms.incUsersCountById(roomId, - result); + } + + return result; } removeByRoomIdAndUserId(roomId, userId) { @@ -793,7 +840,13 @@ class ModelSubscriptions extends RocketChat.models._Base { 'u._id': userId }; - return this.remove(query); + const result = this.remove(query); + + if (Match.test(result, Number) && result > 0) { + RocketChat.models.Rooms.incUsersCountById(roomId, - result); + } + + return result; } } diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js index 5424fb97982d..36ca22f57c1a 100644 --- a/packages/rocketchat-lib/server/models/Users.js +++ b/packages/rocketchat-lib/server/models/Users.js @@ -12,8 +12,6 @@ class ModelUsers extends RocketChat.models._Base { this.tryEnsureIndex({ 'active': 1 }, { sparse: 1 }); this.tryEnsureIndex({ 'statusConnection': 1 }, { sparse: 1 }); this.tryEnsureIndex({ 'type': 1 }); - - this.cache.ensureIndex('username', 'unique'); } findOneByImportId(_id, options) { @@ -31,13 +29,13 @@ class ModelUsers extends RocketChat.models._Base { } findOneByEmailAddress(emailAddress, options) { - const query = {'emails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i')}; + const query = {'emails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i')}; return this.findOne(query, options); } findOneAdmin(admin, options) { - const query = {admin}; + const query = {admin}; return this.findOne(query, options); } @@ -52,18 +50,23 @@ class ModelUsers extends RocketChat.models._Base { } findOneById(userId, options) { - const query = {_id: userId}; + const query = { _id: userId }; return this.findOne(query, options); } // FIND findById(userId) { - const query = {_id: userId}; + const query = { _id: userId }; return this.find(query); } + findByIds(users, options) { + const query = { _id: { $in: users } }; + return this.find(query, options); + } + findUsersNotOffline(options) { const query = { username: { @@ -79,33 +82,7 @@ class ModelUsers extends RocketChat.models._Base { findByUsername(username, options) { - const query = {username}; - - return this.find(query, options); - } - - findUsersByUsernamesWithHighlights(usernames, options) { - if (this.useCache) { - const result = { - fetch() { - return RocketChat.models.Users.getDynamicView('highlights').data().filter(record => usernames.indexOf(record.username) > -1); - }, - count() { - return result.fetch().length; - }, - forEach(fn) { - return result.fetch().forEach(fn); - } - }; - return result; - } - - const query = { - username: { $in: usernames }, - 'settings.preferences.highlights.0': { - $exists: true - } - }; + const query = { username }; return this.find(query, options); } @@ -206,13 +183,13 @@ class ModelUsers extends RocketChat.models._Base { } findLDAPUsers(options) { - const query = {ldap: true}; + const query = {ldap: true}; return this.find(query, options); } findCrowdUsers(options) { - const query = {crowd: true}; + const query = {crowd: true}; return this.find(query, options); } @@ -245,11 +222,40 @@ class ModelUsers extends RocketChat.models._Base { return this.find(query, options); } + findUsersWithUsernameByIds(ids, options) { + const query = { + _id: { + $in: ids + }, + username: { + $exists: 1 + } + }; + + return this.find(query, options); + } + + findUsersWithUsernameByIdsNotOffline(ids, options) { + const query = { + _id: { + $in: ids + }, + username: { + $exists: 1 + }, + status: { + $in: ['online', 'away', 'busy'] + } + }; + + return this.find(query, options); + } + // UPDATE addImportIds(_id, importIds) { importIds = [].concat(importIds); - const query = {_id}; + const query = {_id}; const update = { $addToSet: { diff --git a/packages/rocketchat-lib/server/models/_Base.js b/packages/rocketchat-lib/server/models/_Base.js index 6431305c01df..e124e4ca387d 100644 --- a/packages/rocketchat-lib/server/models/_Base.js +++ b/packages/rocketchat-lib/server/models/_Base.js @@ -1,42 +1,22 @@ import ModelsBaseDb from './_BaseDb'; -import ModelsBaseCache from './_BaseCache'; - -RocketChat.models._CacheControl = new Meteor.EnvironmentVariable(); +import objectPath from 'object-path'; +import _ from 'underscore'; class ModelsBase { - constructor(nameOrModel, useCache) { + constructor(nameOrModel) { this._db = new ModelsBaseDb(nameOrModel, this); this.model = this._db.model; this.collectionName = this._db.collectionName; this.name = this._db.name; - this._useCache = useCache === true; - - this.cache = new ModelsBaseCache(this); - // TODO_CACHE: remove - this.on = this.cache.on.bind(this.cache); - this.emit = this.cache.emit.bind(this.cache); - this.getDynamicView = this.cache.getDynamicView.bind(this.cache); - this.processQueryOptionsOnResult = this.cache.processQueryOptionsOnResult.bind(this.cache); - // END_TODO_CACHE + this.on = this._db.on.bind(this._db); + this.emit = this._db.emit.bind(this._db); this.db = this; - - if (this._useCache) { - this.db = new this.constructor(this.model, false); - } - } - - get useCache() { - if (RocketChat.models._CacheControl.get() === false) { - return false; - } - - return this._useCache; } get origin() { - return this.useCache === true ? 'cache' : '_db'; + return '_db'; } arrayToCursor(data) { @@ -139,10 +119,121 @@ class ModelsBase { return this._db.trashFind(...arguments); } + trashFindOneById(/*_id, options*/) { + return this._db.trashFindOneById(...arguments); + } + trashFindDeletedAfter(/*deletedAt, query, options*/) { return this._db.trashFindDeletedAfter(...arguments); } + processQueryOptionsOnResult(result, options={}) { + if (result === undefined || result === null) { + return undefined; + } + + if (Array.isArray(result)) { + if (options.sort) { + result = result.sort((a, b) => { + let r = 0; + for (const field in options.sort) { + if (options.sort.hasOwnProperty(field)) { + const direction = options.sort[field]; + let valueA; + let valueB; + if (field.indexOf('.') > -1) { + valueA = objectPath.get(a, field); + valueB = objectPath.get(b, field); + } else { + valueA = a[field]; + valueB = b[field]; + } + if (valueA > valueB) { + r = direction; + break; + } + if (valueA < valueB) { + r = -direction; + break; + } + } + } + return r; + }); + } + + if (typeof options.skip === 'number') { + result.splice(0, options.skip); + } + + if (typeof options.limit === 'number' && options.limit !== 0) { + result.splice(options.limit); + } + } + + if (!options.fields) { + options.fields = {}; + } + + const fieldsToRemove = []; + const fieldsToGet = []; + + for (const field in options.fields) { + if (options.fields.hasOwnProperty(field)) { + if (options.fields[field] === 0) { + fieldsToRemove.push(field); + } else if (options.fields[field] === 1) { + fieldsToGet.push(field); + } + } + } + + if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) { + console.warn('Can\'t mix remove and get fields'); + fieldsToRemove.splice(0, fieldsToRemove.length); + } + + if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id') === -1) { + fieldsToGet.push('_id'); + } + + const pickFields = (obj, fields) => { + const picked = {}; + fields.forEach((field) => { + if (field.indexOf('.') !== -1) { + objectPath.set(picked, field, objectPath.get(obj, field)); + } else { + picked[field] = obj[field]; + } + }); + return picked; + }; + + if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) { + if (Array.isArray(result)) { + result = result.map((record) => { + if (fieldsToRemove.length > 0) { + return _.omit(record, ...fieldsToRemove); + } + + if (fieldsToGet.length > 0) { + return pickFields(record, fieldsToGet); + } + }); + } else { + if (fieldsToRemove.length > 0) { + return _.omit(result, ...fieldsToRemove); + } + + if (fieldsToGet.length > 0) { + return pickFields(result, fieldsToGet); + } + } + } + + return result; + } + // dinamicTrashFindAfter(method, deletedAt, ...args) { // const scope = { // find: (query={}) => { diff --git a/packages/rocketchat-lib/server/models/_BaseCache.js b/packages/rocketchat-lib/server/models/_BaseCache.js deleted file mode 100644 index 1e01cabb8661..000000000000 --- a/packages/rocketchat-lib/server/models/_BaseCache.js +++ /dev/null @@ -1,950 +0,0 @@ -/* eslint new-cap: 0 */ -import _ from 'underscore'; -import loki from 'lokijs'; -import {EventEmitter} from 'events'; -import objectPath from 'object-path'; - -const logger = new Logger('BaseCache'); - -const lokiEq = loki.LokiOps.$eq; -const lokiNe = loki.LokiOps.$ne; - -loki.LokiOps.$eq = function(a, b) { - if (Array.isArray(a)) { - return a.indexOf(b) !== -1; - } - return lokiEq(a, b); -}; - -loki.LokiOps.$ne = function(a, b) { - if (Array.isArray(a)) { - return a.indexOf(b) === -1; - } - return lokiNe(a, b); -}; - -const lokiIn = loki.LokiOps.$in; -loki.LokiOps.$in = function(a, b) { - if (Array.isArray(a)) { - return a.some(subA => lokiIn(subA, b)); - } - return lokiIn(a, b); -}; - -loki.LokiOps.$nin = function(a, b) { - return !loki.LokiOps.$in(a, b); -}; - -loki.LokiOps.$all = function(a, b) { - return b.every(subB => a.includes(subB)); -}; - -loki.LokiOps.$exists = function(a, b) { - if (b) { - return loki.LokiOps.$ne(a, undefined); - } - - return loki.LokiOps.$eq(a, undefined); -}; - -loki.LokiOps.$elemMatch = function(a, b) { - return _.findWhere(a, b); -}; - -const ignore = [ - 'emit', - 'load', - 'on', - 'addToAllIndexes' -]; - -function traceMethodCalls(target) { - target._stats = {}; - - for (const property in target) { - if (typeof target[property] === 'function' && ignore.indexOf(property) === -1) { - target._stats[property] = { - calls: 0, - time: 0, - avg: 0 - }; - const origMethod = target[property]; - target[property] = function(...args) { - - if (target.loaded !== true) { - return origMethod.apply(target, args); - } - - const startTime = RocketChat.statsTracker.now(); - const result = origMethod.apply(target, args); - const time = Math.round(RocketChat.statsTracker.now() - startTime) / 1000; - target._stats[property].time += time; - target._stats[property].calls++; - target._stats[property].avg = target._stats[property].time / target._stats[property].calls; - - return result; - }; - } - } - - setInterval(function() { - for (const property in target._stats) { - if (target._stats.hasOwnProperty(property) && target._stats[property].time > 0) { - const tags = [`property:${ property }`, `collection:${ target.collectionName }`]; - RocketChat.statsTracker.timing('cache.methods.time', target._stats[property].avg, tags); - RocketChat.statsTracker.increment('cache.methods.totalTime', target._stats[property].time, tags); - RocketChat.statsTracker.increment('cache.methods.count', target._stats[property].calls, tags); - target._stats[property].avg = 0; - target._stats[property].time = 0; - target._stats[property].calls = 0; - } - } - }, 10000); - - target._getStatsAvg = function() { - const stats = []; - for (const property in target._stats) { - if (target._stats.hasOwnProperty(property)) { - stats.push([Math.round(target._stats[property].avg*100)/100, property]); - } - } - return _.sortBy(stats, function(record) { - return record[0]; - }); - }; -} - -class Adapter { - loadDatabase(/*dbname, callback*/) {} - saveDatabase(/*dbname, dbstring, callback*/) {} - deleteDatabase(/*dbname, callback*/) {} -} - -const db = new loki('rocket.chat.json', {adapter: Adapter}); - -class ModelsBaseCache extends EventEmitter { - constructor(model) { - super(); - - traceMethodCalls(this); - - this.indexes = {}; - this.ignoreUpdatedFields = ['_updatedAt']; - - this.query = {}; - this.options = {}; - - this.ensureIndex('_id', 'unique'); - - this.joins = {}; - - this.on('inserted', (...args) => { this.emit('changed', 'inserted', ...args); }); - this.on('removed', (...args) => { this.emit('changed', 'removed', ...args); }); - this.on('updated', (...args) => { this.emit('changed', 'updated', ...args); }); - - this.on('beforeinsert', (...args) => { this.emit('beforechange', 'inserted', ...args); }); - this.on('beforeremove', (...args) => { this.emit('beforechange', 'removed', ...args); }); - this.on('beforeupdate', (...args) => { this.emit('beforechange', 'updated', ...args); }); - - this.on('inserted', (...args) => { this.emit('sync', 'inserted', ...args); }); - this.on('updated', (...args) => { this.emit('sync', 'updated', ...args); }); - this.on('beforeremove', (...args) => { this.emit('sync', 'removed', ...args); }); - - this.db = db; - - this.model = model; - - this.collectionName = this.model._db.collectionName; - this.collection = this.db.addCollection(this.collectionName); - } - - hasOne(join, {field, link}) { - this.join({join, field, link, multi: false}); - } - - hasMany(join, {field, link}) { - this.join({join, field, link, multi: true}); - } - - join({join, field, link, multi}) { - if (!RocketChat.models[join]) { - console.log(`Invalid cache model ${ join }`); - return; - } - - RocketChat.models[join].cache.on('inserted', (record) => { - this.processRemoteJoinInserted({join, field, link, multi, record}); - }); - - RocketChat.models[join].cache.on('beforeupdate', (record, diff) => { - if (diff[link.remote]) { - this.processRemoteJoinRemoved({join, field, link, multi, record}); - } - }); - - RocketChat.models[join].cache.on('updated', (record, diff) => { - if (diff[link.remote]) { - this.processRemoteJoinInserted({join, field, link, multi, record}); - } - }); - - RocketChat.models[join].cache.on('removed', (record) => { - this.processRemoteJoinRemoved({join, field, link, multi, record}); - }); - - this.on('inserted', (localRecord) => { - this.processLocalJoinInserted({join, field, link, multi, localRecord}); - }); - - this.on('beforeupdate', (localRecord, diff) => { - if (diff[link.local]) { - if (multi === true) { - localRecord[field] = []; - } else { - localRecord[field] = undefined; - } - } - }); - - this.on('updated', (localRecord, diff) => { - if (diff[link.local]) { - this.processLocalJoinInserted({join, field, link, multi, localRecord}); - } - }); - } - - processRemoteJoinInserted({field, link, multi, record}) { - let localRecords = this._findByIndex(link.local, objectPath.get(record, link.remote)); - - if (!localRecords) { - return; - } - - if (!Array.isArray(localRecords)) { - localRecords = [localRecords]; - } - - for (let i = 0; i < localRecords.length; i++) { - const localRecord = localRecords[i]; - if (multi === true && !localRecord[field]) { - localRecord[field] = []; - } - - if (typeof link.where === 'function' && link.where(localRecord, record) === false) { - continue; - } - - let mutableRecord = record; - - if (typeof link.transform === 'function') { - mutableRecord = link.transform(localRecord, mutableRecord); - } - - if (multi === true) { - localRecord[field].push(mutableRecord); - } else { - localRecord[field] = mutableRecord; - } - - this.emit(`join:${ field }:inserted`, localRecord, mutableRecord); - this.emit(`join:${ field }:changed`, 'inserted', localRecord, mutableRecord); - } - } - - processLocalJoinInserted({join, field, link, multi, localRecord}) { - let records = RocketChat.models[join].cache._findByIndex(link.remote, objectPath.get(localRecord, link.local)); - - if (!Array.isArray(records)) { - records = [records]; - } - - for (let i = 0; i < records.length; i++) { - let record = records[i]; - - if (typeof link.where === 'function' && link.where(localRecord, record) === false) { - continue; - } - - if (typeof link.transform === 'function') { - record = link.transform(localRecord, record); - } - - if (multi === true) { - localRecord[field].push(record); - } else { - localRecord[field] = record; - } - - this.emit(`join:${ field }:inserted`, localRecord, record); - this.emit(`join:${ field }:changed`, 'inserted', localRecord, record); - } - } - - processRemoteJoinRemoved({field, link, multi, record}) { - let localRecords = this._findByIndex(link.local, objectPath.get(record, link.remote)); - - if (!localRecords) { - return; - } - - if (!Array.isArray(localRecords)) { - localRecords = [localRecords]; - } - - for (let i = 0; i < localRecords.length; i++) { - const localRecord = localRecords[i]; - - if (multi === true) { - if (Array.isArray(localRecord[field])) { - if (typeof link.remove === 'function') { - link.remove(localRecord[field], record); - } else if (localRecord[field].indexOf(record) > -1) { - localRecord[field].splice(localRecord[field].indexOf(record), 1); - } - } - } else { - localRecord[field] = undefined; - } - - this.emit(`join:${ field }:removed`, localRecord, record); - this.emit(`join:${ field }:changed`, 'removed', localRecord, record); - } - } - - ensureIndex(fields, type='array') { - if (!Array.isArray(fields)) { - fields = [fields]; - } - - this.indexes[fields.join(',')] = { - type, - fields, - data: {} - }; - } - - addToAllIndexes(record) { - for (const indexName in this.indexes) { - if (this.indexes.hasOwnProperty(indexName)) { - this.addToIndex(indexName, record); - } - } - } - - addToIndex(indexName, record) { - const index = this.indexes[indexName]; - if (!index) { - console.error(`Index not defined ${ indexName }`); - return; - } - - const keys = []; - for (const field of index.fields) { - keys.push(objectPath.get(record, field)); - } - const key = keys.join('|'); - - if (index.type === 'unique') { - index.data[key] = record; - return; - } - - if (index.type === 'array') { - if (!index.data[key]) { - index.data[key] = []; - } - index.data[key].push(record); - return; - } - } - - removeFromAllIndexes(record) { - for (const indexName in this.indexes) { - if (this.indexes.hasOwnProperty(indexName)) { - this.removeFromIndex(indexName, record); - } - } - } - - removeFromIndex(indexName, record) { - const index = this.indexes[indexName]; - if (!this.indexes[indexName]) { - console.error(`Index not defined ${ indexName }`); - return; - } - - if (!index.data) { - return; - } - - let key = []; - for (const field of index.fields) { - key.push(objectPath.get(record, field)); - } - key = key.join('|'); - - if (index.type === 'unique') { - index.data[key] = undefined; - return; - } - - if (index.type === 'array') { - if (!index.data[key]) { - return; - } - const i = index.data[key].indexOf(record); - if (i > -1) { - index.data[key].splice(i, 1); - } - return; - } - } - - _findByIndex(index, keys) { - const key = [].concat(keys).join('|'); - if (!this.indexes[index]) { - return; - } - - if (this.indexes[index].data) { - const result = this.indexes[index].data[key]; - if (result) { - return result; - } - } - - if (this.indexes[index].type === 'array') { - return []; - } - } - - findByIndex(index, keys, options={}) { - return { - fetch: () => { - return this.processQueryOptionsOnResult(this._findByIndex(index, keys), options); - }, - - count: () => { - const records = this.findByIndex(index, keys, options).fetch(); - if (Array.isArray(records)) { - return records.length; - } - return !records ? 0 : 1; - }, - - forEach: (fn) => { - const records = this.findByIndex(index, keys, options).fetch(); - if (Array.isArray(records)) { - return records.forEach(fn); - } - if (records) { - return fn(records); - } - } - }; - } - - load() { - if (this.model._useCache === false) { - return; - } - - console.log('Will load cache for', this.collectionName); - this.emit('beforeload'); - this.loaded = false; - const time = RocketChat.statsTracker.now(); - const data = this.model.db.find(this.query, this.options).fetch(); - for (let i=0; i < data.length; i++) { - this.insert(data[i]); - } - console.log(String(data.length), 'records load from', this.collectionName); - RocketChat.statsTracker.timing('cache.load', RocketChat.statsTracker.now() - time, [`collection:${ this.collectionName }`]); - - this.startSync(); - this.loaded = true; - this.emit('afterload'); - } - - startSync() { - if (this.model._useCache === false) { - return; - } - - this.model._db.on('change', ({action, id, data/*, oplog*/}) => { - switch (action) { - case 'insert': - data._id = id; - this.insert(data); - break; - - case 'remove': - this.removeById(id); - break; - - case 'update:record': - this.updateDiffById(id, data); - break; - - case 'update:diff': - this.updateDiffById(id, data); - break; - - case 'update:query': - this.update(data.query, data.update, data.options); - break; - } - }); - } - - processQueryOptionsOnResult(result, options={}) { - if (result === undefined || result === null) { - return undefined; - } - - if (Array.isArray(result)) { - if (options.sort) { - result = result.sort((a, b) => { - let r = 0; - for (const field in options.sort) { - if (options.sort.hasOwnProperty(field)) { - const direction = options.sort[field]; - let valueA; - let valueB; - if (field.indexOf('.') > -1) { - valueA = objectPath.get(a, field); - valueB = objectPath.get(b, field); - } else { - valueA = a[field]; - valueB = b[field]; - } - if (valueA > valueB) { - r = direction; - break; - } - if (valueA < valueB) { - r = -direction; - break; - } - } - } - return r; - }); - } - - if (typeof options.skip === 'number') { - result.splice(0, options.skip); - } - - if (typeof options.limit === 'number' && options.limit !== 0) { - result.splice(options.limit); - } - } - - if (!options.fields) { - options.fields = {}; - } - - const fieldsToRemove = []; - const fieldsToGet = []; - - for (const field in options.fields) { - if (options.fields.hasOwnProperty(field)) { - if (options.fields[field] === 0) { - fieldsToRemove.push(field); - } else if (options.fields[field] === 1) { - fieldsToGet.push(field); - } - } - } - - if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) { - console.warn('Can\'t mix remove and get fields'); - fieldsToRemove.splice(0, fieldsToRemove.length); - } - - if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id') === -1) { - fieldsToGet.push('_id'); - } - - const pickFields = (obj, fields) => { - const picked = {}; - fields.forEach((field) => { - if (field.indexOf('.') !== -1) { - objectPath.set(picked, field, objectPath.get(obj, field)); - } else { - picked[field] = obj[field]; - } - }); - return picked; - }; - - if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) { - if (Array.isArray(result)) { - result = result.map((record) => { - if (fieldsToRemove.length > 0) { - return _.omit(record, ...fieldsToRemove); - } - - if (fieldsToGet.length > 0) { - return pickFields(record, fieldsToGet); - } - }); - } else { - if (fieldsToRemove.length > 0) { - return _.omit(result, ...fieldsToRemove); - } - - if (fieldsToGet.length > 0) { - return pickFields(result, fieldsToGet); - } - } - } - - return result; - } - - processQuery(query, parentField) { - if (!query) { - return query; - } - - if (Match.test(query, String)) { - return { - _id: query - }; - } - - if (Object.keys(query).length > 1 && parentField !== '$elemMatch') { - const and = []; - for (const field in query) { - if (query.hasOwnProperty(field)) { - and.push({ - [field]: query[field] - }); - } - } - query = {$and: and}; - } - - for (const field in query) { - if (query.hasOwnProperty(field)) { - const value = query[field]; - if (value instanceof RegExp && field !== '$regex') { - query[field] = { - $regex: value - }; - } - - if (field === '$and' || field === '$or') { - query[field] = value.map((subValue) => { - return this.processQuery(subValue, field); - }); - } - - if (Match.test(value, Object) && Object.keys(value).length > 0) { - query[field] = this.processQuery(value, field); - } - } - } - - return query; - } - - find(query, options={}) { - return { - fetch: () => { - try { - query = this.processQuery(query); - return this.processQueryOptionsOnResult(this.collection.find(query), options); - } catch (e) { - console.error('Exception on cache find for', this.collectionName); - console.error('Query:', JSON.stringify(query, null, 2)); - console.error('Options:', JSON.stringify(options, null, 2)); - console.error(e.stack); - throw e; - } - }, - - count: () => { - try { - query = this.processQuery(query); - const { limit, skip } = options; - return this.processQueryOptionsOnResult(this.collection.find(query), { limit, skip }).length; - } catch (e) { - console.error('Exception on cache find for', this.collectionName); - console.error('Query:', JSON.stringify(query, null, 2)); - console.error('Options:', JSON.stringify(options, null, 2)); - console.error(e.stack); - throw e; - } - }, - - forEach: (fn) => { - return this.find(query, options).fetch().forEach(fn); - }, - - observe: (obj) => { - logger.debug(this.collectionName, 'Falling back observe to model with query:', query); - return this.model.db.find(...arguments).observe(obj); - }, - - observeChanges: (obj) => { - logger.debug(this.collectionName, 'Falling back observeChanges to model with query:', query); - return this.model.db.find(...arguments).observeChanges(obj); - }, - - _publishCursor: (cursor, sub, collection) => { - logger.debug(this.collectionName, 'Falling back _publishCursor to model with query:', query); - return this.model.db.find(...arguments)._publishCursor(cursor, sub, collection); - } - }; - } - - findOne(query, options) { - try { - query = this.processQuery(query); - return this.processQueryOptionsOnResult(this.collection.findOne(query), options); - } catch (e) { - console.error('Exception on cache findOne for', this.collectionName); - console.error('Query:', JSON.stringify(query, null, 2)); - console.error('Options:', JSON.stringify(options, null, 2)); - console.error(e.stack); - throw e; - } - } - - findOneById(_id, options) { - return this.findByIndex('_id', _id, options).fetch(); - } - - findOneByIds(ids, options) { - const query = this.processQuery({ _id: { $in: ids }}); - return this.processQueryOptionsOnResult(this.collection.findOne(query), options); - } - - findWhere(query, options) { - query = this.processQuery(query); - return this.processQueryOptionsOnResult(this.collection.findWhere(query), options); - } - - addDynamicView() { - return this.collection.addDynamicView(...arguments); - } - - getDynamicView() { - return this.collection.getDynamicView(...arguments); - } - - insert(record) { - if (Array.isArray(record)) { - for (const item of record) { - this.insert(item); - } - } else { - // TODO remove - ignore updates in room.usernames - if (this.collectionName === 'rocketchat_room' && record.usernames) { - delete record.usernames; - } - this.emit('beforeinsert', record); - this.addToAllIndexes(record); - this.collection.insert(record); - this.emit('inserted', record); - } - } - - updateDiffById(id, diff) { - // TODO remove - ignore updates in room.usernames - if (this.collectionName === 'rocketchat_room' && diff.usernames) { - delete diff.usernames; - } - - const record = this._findByIndex('_id', id); - if (!record) { - console.error('Cache.updateDiffById: No record', this.collectionName, id, diff); - return; - } - this.removeFromAllIndexes(record); - - const updatedFields = _.without(Object.keys(diff), ...this.ignoreUpdatedFields); - - if (updatedFields.length > 0) { - this.emit('beforeupdate', record, diff); - } - - for (const key in diff) { - if (diff.hasOwnProperty(key)) { - objectPath.set(record, key, diff[key]); - } - } - - this.collection.update(record); - this.addToAllIndexes(record); - - if (updatedFields.length > 0) { - this.emit('updated', record, diff); - } - } - - updateRecord(record, update) { - // TODO remove - ignore updates in room.usernames - if (this.collectionName === 'rocketchat_room' && (record.usernames || (record.$set && record.$set.usernames))) { - delete record.usernames; - if (record.$set && record.$set.usernames) { - delete record.$set.usernames; - } - } - - this.removeFromAllIndexes(record); - - const topLevelFields = Object.keys(update).map(field => field.split('.')[0]); - const updatedFields = _.without(topLevelFields, ...this.ignoreUpdatedFields); - - if (updatedFields.length > 0) { - this.emit('beforeupdate', record, record); - } - - if (update.$set) { - _.each(update.$set, (value, field) => { - objectPath.set(record, field, value); - }); - } - - if (update.$unset) { - _.each(update.$unset, (value, field) => { - objectPath.del(record, field); - }); - } - - if (update.$min) { - _.each(update.$min, (value, field) => { - const curValue = objectPath.get(record, field); - if (curValue === undefined || value < curValue) { - objectPath.set(record, field, value); - } - }); - } - - if (update.$max) { - _.each(update.$max, (value, field) => { - const curValue = objectPath.get(record, field); - if (curValue === undefined || value > curValue) { - objectPath.set(record, field, value); - } - }); - } - - if (update.$inc) { - _.each(update.$inc, (value, field) => { - let curValue = objectPath.get(record, field); - if (curValue === undefined) { - curValue = value; - } else { - curValue += value; - } - objectPath.set(record, field, curValue); - }); - } - - if (update.$mul) { - _.each(update.$mul, (value, field) => { - let curValue = objectPath.get(record, field); - if (curValue === undefined) { - curValue = 0; - } else { - curValue *= value; - } - objectPath.set(record, field, curValue); - }); - } - - if (update.$rename) { - _.each(update.$rename, (value, field) => { - const curValue = objectPath.get(record, field); - if (curValue !== undefined) { - objectPath.set(record, value, curValue); - objectPath.del(record, field); - } - }); - } - - if (update.$pullAll) { - _.each(update.$pullAll, (value, field) => { - let curValue = objectPath.get(record, field); - if (Array.isArray(curValue)) { - curValue = _.difference(curValue, value); - objectPath.set(record, field, curValue); - } - }); - } - - if (update.$pop) { - _.each(update.$pop, (value, field) => { - const curValue = objectPath.get(record, field); - if (Array.isArray(curValue)) { - if (value === -1) { - curValue.shift(); - } else { - curValue.pop(); - } - objectPath.set(record, field, curValue); - } - }); - } - - if (update.$addToSet) { - _.each(update.$addToSet, (value, field) => { - let curValue = objectPath.get(record, field); - if (curValue === undefined) { - curValue = []; - } - if (Array.isArray(curValue)) { - const length = curValue.length; - - if (value && value.$each && Array.isArray(value.$each)) { - for (const valueItem of value.$each) { - if (curValue.indexOf(valueItem) === -1) { - curValue.push(valueItem); - } - } - } else if (curValue.indexOf(value) === -1) { - curValue.push(value); - } - - if (curValue.length > length) { - objectPath.set(record, field, curValue); - } - } - }); - } - - this.collection.update(record); - this.addToAllIndexes(record); - - if (updatedFields.length > 0) { - this.emit('updated', record, record); - } - } - - update(query, update, options = {}) { - let records = options.multi ? this.find(query).fetch() : this.findOne(query) || []; - if (!Array.isArray(records)) { - records = [records]; - } - - for (const record of records) { - this.updateRecord(record, update); - } - } - - removeById(id) { - const record = this._findByIndex('_id', id); - if (record) { - this.emit('beforeremove', record); - this.collection.removeWhere({_id: id}); - this.removeFromAllIndexes(record); - this.emit('removed', record); - } - } -} - -export default ModelsBaseCache; diff --git a/packages/rocketchat-lib/server/models/_BaseDb.js b/packages/rocketchat-lib/server/models/_BaseDb.js index b93f3e917701..a39f0827ca7d 100644 --- a/packages/rocketchat-lib/server/models/_BaseDb.js +++ b/packages/rocketchat-lib/server/models/_BaseDb.js @@ -36,9 +36,11 @@ class ModelsBaseDb extends EventEmitter { this.wrapModel(); + let alreadyListeningToOplog = false; // When someone start listening for changes we start oplog if available - this.once('newListener', (event/*, listener*/) => { - if (event === 'change') { + this.on('newListener', (event/*, listener*/) => { + if (event === 'change' && alreadyListeningToOplog === false) { + alreadyListeningToOplog = true; if (isOplogEnabled) { const query = { collection: this.collectionName @@ -98,65 +100,32 @@ class ModelsBaseDb extends EventEmitter { }; } + _doNotMixInclusionAndExclusionFields(options) { + if (options && options.fields) { + const keys = Object.keys(options.fields); + const removeKeys = keys.filter(key => options.fields[key] === 0); + if (keys.length > removeKeys.length) { + removeKeys.forEach(key => delete options.fields[key]); + } + } + } + find() { + this._doNotMixInclusionAndExclusionFields(arguments[1]); return this.model.find(...arguments); } findOne() { + this._doNotMixInclusionAndExclusionFields(arguments[1]); return this.model.findOne(...arguments); } findOneById(_id, options) { - return this.model.findOne({ _id }, options); + return this.findOne({ _id }, options); } findOneByIds(ids, options) { - return this.model.findOne({ _id: { $in: ids }}, options); - } - - defineSyncStrategy(query, modifier, options) { - if (this.baseModel.useCache === false) { - return 'db'; - } - - if (options.upsert === true) { - return 'db'; - } - - // const dbModifiers = [ - // '$currentDate', - // '$bit', - // '$pull', - // '$pushAll', - // '$push', - // '$setOnInsert' - // ]; - - const cacheAllowedModifiers = [ - '$set', - '$unset', - '$min', - '$max', - '$inc', - '$mul', - '$rename', - '$pullAll', - '$pop', - '$addToSet' - ]; - - const notAllowedModifiers = Object.keys(modifier).filter(i => i.startsWith('$') && cacheAllowedModifiers.includes(i) === false); - - if (notAllowedModifiers.length > 0) { - return 'db'; - } - - const placeholderFields = Object.keys(query).filter(item => item.indexOf('$') > -1); - if (placeholderFields.length > 0) { - return 'db'; - } - - return 'cache'; + return this.findOne({ _id: { $in: ids }}, options); } updateHasPositionalOperator(update) { @@ -171,6 +140,7 @@ class ModelsBaseDb extends EventEmitter { if (action.op.op === 'i') { this.emit('change', { action: 'insert', + clientAction: 'inserted', id: action.op.o._id, data: action.op.o, oplog: true @@ -181,7 +151,8 @@ class ModelsBaseDb extends EventEmitter { if (action.op.op === 'u') { if (!action.op.o.$set && !action.op.o.$unset) { this.emit('change', { - action: 'update:record', + action: 'update', + clientAction: 'updated', id: action.id, data: action.op.o, oplog: true @@ -207,9 +178,10 @@ class ModelsBaseDb extends EventEmitter { } this.emit('change', { - action: 'update:diff', + action: 'update', + clientAction: 'updated', id: action.id, - data: diff, + diff, oplog: true }); return; @@ -218,6 +190,7 @@ class ModelsBaseDb extends EventEmitter { if (action.op.op === 'd') { this.emit('change', { action: 'remove', + clientAction: 'removed', id: action.id, oplog: true }); @@ -229,27 +202,28 @@ class ModelsBaseDb extends EventEmitter { this.setUpdatedAt(record); const result = this.originals.insert(...arguments); + + record._id = result; + if (!isOplogEnabled && this.listenerCount('change') > 0) { this.emit('change', { action: 'insert', + clientAction: 'inserted', id: result, data: _.extend({}, record), oplog: false }); } - record._id = result; - return result; } update(query, update, options = {}) { this.setUpdatedAt(update, true, query); - const strategy = this.defineSyncStrategy(query, update, options); let ids = []; - if (!isOplogEnabled && this.listenerCount('change') > 0 && strategy === 'db') { - const findOptions = {fields: {_id: 1}}; + if (!isOplogEnabled && this.listenerCount('change') > 0) { + const findOptions = { fields: { _id: 1 } }; let records = options.multi ? this.find(query, findOptions).fetch() : this.findOne(query, findOptions) || []; if (!Array.isArray(records)) { records = [records]; @@ -265,53 +239,31 @@ class ModelsBaseDb extends EventEmitter { } } + // TODO: CACHE: Can we use findAndModify here when oplog is disabled? const result = this.originals.update(query, update, options); if (!isOplogEnabled && this.listenerCount('change') > 0) { - if (strategy === 'db') { - if (options.upsert === true) { - if (result.insertedId) { - this.emit('change', { - action: 'insert', - id: result.insertedId, - data: this.findOne({_id: result.insertedId}), - oplog: false - }); - return; - } + if (options.upsert === true && result.insertedId) { + this.emit('change', { + action: 'insert', + clientAction: 'inserted', + id: result.insertedId, + oplog: false + }); - query = { - _id: { - $in: ids - } - }; - } + return result; + } - let records = options.multi ? this.find(query).fetch() : this.findOne(query) || []; - if (!Array.isArray(records)) { - records = [records]; - } - for (const record of records) { - this.emit('change', { - action: 'update:record', - id: record._id, - data: record, - oplog: false - }); - } - } else { + for (const id of ids) { this.emit('change', { - action: 'update:query', - id: undefined, - data: { - query, - update, - options - }, + action: 'update', + clientAction: 'updated', + id, oplog: false }); } } + return result; } @@ -342,6 +294,7 @@ class ModelsBaseDb extends EventEmitter { for (const record of records) { this.emit('change', { action: 'remove', + clientAction: 'removed', id: record._id, data: _.extend({}, record), oplog: false @@ -405,6 +358,15 @@ class ModelsBaseDb extends EventEmitter { return trash.find(query, options); } + trashFindOneById(_id, options) { + const query = { + _id, + __collection__: this.name + }; + + return trash.findOne(query, options); + } + trashFindDeletedAfter(deletedAt, query = {}, options) { query.__collection__ = this.name; query._deletedAt = { diff --git a/packages/rocketchat-lib/server/publications/settings.js b/packages/rocketchat-lib/server/publications/settings.js index 5707468422bf..175ccea3cae7 100644 --- a/packages/rocketchat-lib/server/publications/settings.js +++ b/packages/rocketchat-lib/server/publications/settings.js @@ -1,11 +1,8 @@ -import _ from 'underscore'; - Meteor.methods({ 'public-settings/get'(updatedAt) { this.unblock(); - const records = RocketChat.models.Settings.find().fetch().filter(function(record) { - return record.hidden !== true && record['public'] === true; - }); + const records = RocketChat.models.Settings.findNotHiddenPublic().fetch(); + if (updatedAt instanceof Date) { return { update: records.filter(function(record) { @@ -34,9 +31,7 @@ Meteor.methods({ if (!RocketChat.authz.hasPermission(Meteor.userId(), 'view-privileged-setting')) { return []; } - const records = RocketChat.models.Settings.find().fetch().filter(function(record) { - return record.hidden !== true; - }); + const records = RocketChat.models.Settings.findNotHidden().fetch(); if (updatedAt instanceof Date) { return { update: records.filter(function(record) { @@ -58,11 +53,30 @@ Meteor.methods({ } }); -RocketChat.models.Settings.cache.on('changed', function(type, setting) { - if (setting['public'] === true) { - RocketChat.Notifications.notifyAllInThisInstance('public-settings-changed', type, _.pick(setting, '_id', 'value', 'editor', 'properties')); +RocketChat.models.Settings.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + const setting = data || RocketChat.models.Settings.findOneById(id); + const value = { + _id: setting._id, + value: setting.value, + editor: setting.editor, + properties: setting.properties + }; + + if (setting['public'] === true) { + RocketChat.Notifications.notifyAllInThisInstance('public-settings-changed', clientAction, value); + } else { + RocketChat.Notifications.notifyLoggedInThisInstance('private-settings-changed', clientAction, setting); + } + break; + + case 'removed': + RocketChat.Notifications.notifyLoggedInThisInstance('private-settings-changed', clientAction, { _id: id }); + RocketChat.Notifications.notifyAllInThisInstance('public-settings-changed', clientAction, { _id: id }); + break; } - return RocketChat.Notifications.notifyLoggedInThisInstance('private-settings-changed', type, setting); }); RocketChat.Notifications.streamAll.allowRead('private-settings-changed', function() { diff --git a/packages/rocketchat-lib/server/startup/cache/CacheLoad.js b/packages/rocketchat-lib/server/startup/cache/CacheLoad.js deleted file mode 100644 index 8f555fc454c7..000000000000 --- a/packages/rocketchat-lib/server/startup/cache/CacheLoad.js +++ /dev/null @@ -1,73 +0,0 @@ -RocketChat.models.Rooms.cache.hasMany('Subscriptions', { - field: 'usernames', - link: { - local: '_id', - remote: 'rid', - transform(room, subscription) { - return subscription.u.username; - }, - remove(arr, subscription) { - if (arr.indexOf(subscription.u.username) > -1) { - arr.splice(arr.indexOf(subscription.u.username), 1); - } - } - } -}); - - -RocketChat.models.Subscriptions.cache.hasOne('Rooms', { - field: '_room', - link: { - local: 'rid', - remote: '_id' - } -}); - - -RocketChat.models.Subscriptions.cache.hasOne('Users', { - field: '_user', - link: { - local: 'u._id', - remote: '_id' - } -}); - -RocketChat.models.Subscriptions.cache.hasOne('Users', { - field: 'fname', - link: { - local: 'name', - remote: 'username', - where(subscription/*, user*/) { - return subscription.t === 'd'; - }, - transform(subscription, user) { - if (user == null || subscription == null) { - return undefined; - } - // Prevent client cache for old subscriptions with new names - // Cuz when a user change his name, the subscription's _updateAt - // will not change - if (subscription._updatedAt < user._updatedAt) { - subscription._updatedAt = user._updatedAt; - } - return user.name; - } - } -}); - -RocketChat.models.Users.cache.load(); -RocketChat.models.Rooms.cache.load(); -RocketChat.models.Subscriptions.cache.load(); -RocketChat.models.Settings.cache.load(); - - -RocketChat.models.Users.cache.addDynamicView('highlights').applyFind({ - 'settings.preferences.highlights': {$size: {$gt: 0}} -}); - -RocketChat.models.Subscriptions.cache.addDynamicView('notifications').applyFind({ - $or: [ - {desktopNotifications: {$in: ['all', 'nothing']}}, - {mobilePushNotifications: {$in: ['all', 'nothing']}} - ] -}); diff --git a/packages/rocketchat-livechat/.app/client/lib/_livechat.js b/packages/rocketchat-livechat/.app/client/lib/_livechat.js index 541af4c2a826..13aae330ea16 100644 --- a/packages/rocketchat-livechat/.app/client/lib/_livechat.js +++ b/packages/rocketchat-livechat/.app/client/lib/_livechat.js @@ -34,6 +34,9 @@ this.Livechat = new (class Livechat { this.stream = new Meteor.Streamer('livechat-room'); + this._guestName = new ReactiveVar(); + this._guestEmail = new ReactiveVar(); + Tracker.autorun(() => { if (this._room.get() && visitor.getId()) { RoomHistoryManager.getMoreIfIsEmpty(this._room.get()); @@ -121,6 +124,12 @@ this.Livechat = new (class Livechat { get agent() { return this._agent.get(); } + get guestName() { + return this._guestName.get(); + } + get guestEmail() { + return this._guestEmail.get(); + } set online(value) { this._online.set(value); @@ -198,6 +207,12 @@ this.Livechat = new (class Livechat { set agent(agentData) { this._agent.set(agentData); } + set guestName(name) { + return this._guestName.set(name); + } + set guestEmail(email) { + return this._guestEmail.set(email); + } ready() { this._ready.set(true); diff --git a/packages/rocketchat-livechat/.app/client/lib/chatMessages.js b/packages/rocketchat-livechat/.app/client/lib/chatMessages.js index 245e998e5e98..6797cace30df 100644 --- a/packages/rocketchat-livechat/.app/client/lib/chatMessages.js +++ b/packages/rocketchat-livechat/.app/client/lib/chatMessages.js @@ -148,6 +148,14 @@ this.ChatMessages = class ChatMessages { guest.department = Livechat.department; } + if (Livechat.guestName) { + guest.name = Livechat.guestName; + } + + if (Livechat.guestEmail) { + guest.email = Livechat.guestEmail; + } + Meteor.call('livechat:registerGuest', guest, (error, result) => { if (error) { return showError(error.reason); diff --git a/packages/rocketchat-livechat/.app/client/lib/hooks.js b/packages/rocketchat-livechat/.app/client/lib/hooks.js index 89436156dc19..93ba1a6f11c6 100644 --- a/packages/rocketchat-livechat/.app/client/lib/hooks.js +++ b/packages/rocketchat-livechat/.app/client/lib/hooks.js @@ -37,6 +37,34 @@ const api = { widgetClosed() { Livechat.setWidgetClosed(); + }, + + setGuestToken(token) { + visitor.setToken(token); + }, + + setGuestName(name) { + visitor.setName(name); + }, + + setGuestEmail(email) { + visitor.setEmail(email); + }, + + registerGuest(data) { + if (typeof data !== 'object') { + return; + } + + if (!data.token) { + data.token = Random.id(); + } + + Meteor.call('livechat:registerGuest', data, function(error, result) { + if (!error && result.visitor && result.visitor.token) { + visitor.setToken(result.visitor.token); + } + }); } }; diff --git a/packages/rocketchat-livechat/.app/client/lib/msgTyping.js b/packages/rocketchat-livechat/.app/client/lib/msgTyping.js index 378af82a34aa..addcfa608ef4 100644 --- a/packages/rocketchat-livechat/.app/client/lib/msgTyping.js +++ b/packages/rocketchat-livechat/.app/client/lib/msgTyping.js @@ -10,6 +10,7 @@ export const MsgTyping = (function() { const selfTyping = new ReactiveVar(false); const usersTyping = {}; const dep = new Tracker.Dependency; + let oldRoom; const addStream = function(room) { if (!_.isEmpty(usersTyping[room] && usersTyping[room].users)) { @@ -38,7 +39,11 @@ export const MsgTyping = (function() { Tracker.autorun(() => { if (visitor.getRoom() && visitor.getId()) { + if (oldRoom) { + Notifications.unRoom(oldRoom, 'typing'); + } addStream(visitor.getRoom()); + oldRoom = visitor.getRoom(); } }); diff --git a/packages/rocketchat-livechat/.app/client/views/livechatWindow.js b/packages/rocketchat-livechat/.app/client/views/livechatWindow.js index 414bd2cdf9a1..07aaa0912f9c 100644 --- a/packages/rocketchat-livechat/.app/client/views/livechatWindow.js +++ b/packages/rocketchat-livechat/.app/client/views/livechatWindow.js @@ -4,7 +4,7 @@ import visitor from '../../imports/client/visitor'; function showDepartments() { return Department.find({ showOnRegistration: true }).count() > 1; }; - + Template.livechatWindow.helpers({ title() { return Livechat.title; @@ -90,11 +90,20 @@ Template.livechatWindow.onCreated(function() { return lng; }; - // get all needed live chat info for the user - Meteor.call('livechat:getInitialData', visitor.getToken(), (err, result) => { - if (err) { - console.error(err); - } else { + const loadDepartments = departments => { + Department.remove({}); + departments.forEach((department) => { + Department.insert(department); + }); + }; + + this.autorun(() => { + // get all needed live chat info for the user + Meteor.call('livechat:getInitialData', visitor.getToken(), (err, result) => { + if (err) { + return console.error(err); + } + if (!result.enabled) { Triggers.setDisabled(); return parentCall('removeWidget'); @@ -129,6 +138,14 @@ Template.livechatWindow.onCreated(function() { if (result.visitor) { visitor.setData(result.visitor); + + if (visitor.name) { + Livechat.guestName = visitor.name; + } + + if (visitor.visitorEmails && visitor.visitorEmails.length > 0) { + Livechat.guestEmail = visitor.visitorEmails[0].address; + } } if (result.agentData) { @@ -146,13 +163,12 @@ Template.livechatWindow.onCreated(function() { Triggers.setTriggers(result.triggers); Triggers.init(); - result.departments.forEach((department) => { - Department.insert(department); - }); + loadDepartments(result.departments); + Livechat.allowSwitchingDepartments = result.allowSwitchingDepartments; Livechat.ready(); - } + }); }); $(window).on('focus', () => { diff --git a/packages/rocketchat-livechat/.app/client/views/register.html b/packages/rocketchat-livechat/.app/client/views/register.html index 142ca7637593..1e3e533b7e34 100644 --- a/packages/rocketchat-livechat/.app/client/views/register.html +++ b/packages/rocketchat-livechat/.app/client/views/register.html @@ -9,11 +9,11 @@

{{#if showNameFieldRegisterForm}} - - {{/if}} + + {{/if}} {{#if showEmailFieldRegisterForm}} - - {{/if}} + + {{/if}} {{#if showDepartments}} - -
- -
+
+
+ {{> avatar username=initialsUsername }} +
+
+ + +
+
+ +
+ {{#if suggestions.ready}} {{#each service in services}} {{ > avatarService service}} {{/each}} -
- - + {{else}} +
+ {{_ "Loading_suggestion"}}
+ {{/if}} +
+ +
- {{else}} -
- {{_ "Loading_suggestion"}} -
- {{/if}} +
{{/if}}
diff --git a/packages/rocketchat-ui-master/public/icons.svg b/packages/rocketchat-ui-master/public/icons.svg index f0299fa6c5ff..eed97022f676 100644 --- a/packages/rocketchat-ui-master/public/icons.svg +++ b/packages/rocketchat-ui-master/public/icons.svg @@ -1,456 +1,475 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/rocketchat-ui-message/client/popup/messagePopup.js b/packages/rocketchat-ui-message/client/popup/messagePopup.js index db9ed11f1d3a..e04b317e8806 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopup.js +++ b/packages/rocketchat-ui-message/client/popup/messagePopup.js @@ -81,6 +81,7 @@ Template.messagePopup.onCreated(function() { if (previous != null) { current.className = current.className.replace(/\sselected/, '').replace('sidebar-item__popup-active', ''); previous.className += ' selected sidebar-item__popup-active'; + previous.scrollIntoView(false); return template.value.set(previous.getAttribute('data-id')); } }; @@ -90,6 +91,7 @@ Template.messagePopup.onCreated(function() { if (next && next.classList.contains('popup-item')) { current.className = current.className.replace(/\sselected/, '').replace('sidebar-item__popup-active', ''); next.className += ' selected sidebar-item__popup-active'; + next.scrollIntoView(false); return template.value.set(next.getAttribute('data-id')); } }; diff --git a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html index fc2eae0c225d..52ff78339509 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html +++ b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html @@ -1,4 +1,4 @@ diff --git a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js index a3836b1fa177..8686c9e6b65e 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js +++ b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js @@ -1,5 +1,5 @@ Template.messagePopupChannel.helpers({ - icon() { + channelIcon() { return RocketChat.roomTypes.getIcon(this.t); } }); diff --git a/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html b/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html index c1f1ec4dd813..f23ab768ea61 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html +++ b/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html @@ -1,4 +1,6 @@ \ No newline at end of file + + diff --git a/packages/rocketchat-ui-sidenav/client/roomList.js b/packages/rocketchat-ui-sidenav/client/roomList.js index 8088f8d67376..928d6683aca0 100644 --- a/packages/rocketchat-ui-sidenav/client/roomList.js +++ b/packages/rocketchat-ui-sidenav/client/roomList.js @@ -25,7 +25,6 @@ Template.roomList.helpers({ sort.lm = -1; } else { // alphabetical sort[this.identifier === 'd' && RocketChat.settings.get('UI_Use_Real_Name') ? 'lowerCaseFName' : 'lowerCaseName'] = /descending/.test(sortBy) ? -1 : 1; - sort['name'] = /descending/.test(sortBy) ? -1 : 1; } if (this.identifier === 'unread') { @@ -100,9 +99,9 @@ Template.roomList.helpers({ } }); -const getLowerCaseNames = (room, nameDefault = '') => { +const getLowerCaseNames = (room, nameDefault = '', fnameDefault= '') => { const name = room.name || nameDefault; - const fname = room.fname || name; + const fname = room.fname || fnameDefault || name; return { lowerCaseName: name.toLowerCase(), lowerCaseFName: fname.toLowerCase() @@ -128,7 +127,7 @@ const mergeRoomSub = room => { $set: { lastMessage: room.lastMessage, lm: room._updatedAt, - ...getLowerCaseNames(room, sub.name) + ...getLowerCaseNames(room, sub.name, sub.fname) } }); diff --git a/packages/rocketchat-ui/client/views/app/directory.html b/packages/rocketchat-ui/client/views/app/directory.html index 765a2867110e..e8450d88a69a 100644 --- a/packages/rocketchat-ui/client/views/app/directory.html +++ b/packages/rocketchat-ui/client/views/app/directory.html @@ -18,8 +18,8 @@
{{_ "Name"}} {{> icon icon=(sortIcon 'name')}}
- -
{{_ "Users"}}
+ +
{{_ "Users"}} {{> icon icon=(sortIcon 'usersCount')}}
{{#if showLastMessage}} diff --git a/packages/rocketchat-ui/client/views/app/directory.js b/packages/rocketchat-ui/client/views/app/directory.js index 70465f6a6930..3c8a3e68cc9f 100644 --- a/packages/rocketchat-ui/client/views/app/directory.js +++ b/packages/rocketchat-ui/client/views/app/directory.js @@ -2,6 +2,10 @@ import moment from 'moment'; import _ from 'underscore'; function timeAgo(time) { + if (!time) { + return; + } + const now = new Date(); const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); @@ -14,7 +18,7 @@ function directorySearch(config, cb) { if (config.type === 'channels') { return { name: result.name, - users: (result.usernames ? result.usernames.length : result.usersCount) || 0, + users: result.usersCount || 0, createdAt: timeAgo(result.ts), lastMessage: result.lastMessage && timeAgo(result.lastMessage.ts), description: result.description, @@ -198,8 +202,8 @@ Template.directory.onRendered(function() { Template.directory.onCreated(function() { this.searchText = new ReactiveVar(''); this.searchType = new ReactiveVar('channels'); - this.searchSortBy = new ReactiveVar('name'); - this.sortDirection = new ReactiveVar('asc'); + this.searchSortBy = new ReactiveVar('usersCount'); + this.sortDirection = new ReactiveVar('desc'); this.limit = new ReactiveVar(0); this.page = new ReactiveVar(0); this.end = new ReactiveVar(false); diff --git a/packages/rocketchat-user-data-download/server/cronProcessDownloads.js b/packages/rocketchat-user-data-download/server/cronProcessDownloads.js index 985171edac32..d1f56f6e8dcf 100644 --- a/packages/rocketchat-user-data-download/server/cronProcessDownloads.js +++ b/packages/rocketchat-user-data-download/server/cronProcessDownloads.js @@ -37,7 +37,7 @@ const loadUserSubscriptions = function(exportOperation) { const cursor = RocketChat.models.Subscriptions.findByUserId(exportUserId); cursor.forEach((subscription) => { const roomId = subscription.rid; - const roomData = subscription._room; + const roomData = RocketChat.models.Rooms.findOneById(roomId); let roomName = roomData.name ? roomData.name : roomId; let userId = null; diff --git a/public/images/manifest.json b/public/images/manifest.json index 36be779fed28..b846cad037e9 100644 --- a/public/images/manifest.json +++ b/public/images/manifest.json @@ -1,17 +1,19 @@ { "name": "Rocket.Chat", + "short_name": "Rocket.Chat", + "background_color": "#fff", "icons": [ { - "src": "assets\/favicon_192.png", + "src": "/assets/favicon_192.png", "sizes": "192x192", - "type": "image\/png" + "type": "image/png" }, { - "src": "assets\/favicon_512.png", + "src": "/assets/favicon_512.png", "sizes": "512x512", - "type": "image\/png" + "type": "image/png" } ], - "start_url": "https:\/\/rocket.chat\/home", + "start_url": "/home?homescreen", "display": "standalone" } diff --git a/server/methods/addAllUserToRoom.js b/server/methods/addAllUserToRoom.js index 14a6d1a770f0..bf8978303065 100644 --- a/server/methods/addAllUserToRoom.js +++ b/server/methods/addAllUserToRoom.js @@ -32,7 +32,6 @@ Meteor.methods({ return; } RocketChat.callbacks.run('beforeJoinRoom', user, room); - RocketChat.models.Rooms.addUsernameById(rid, user.username); RocketChat.models.Subscriptions.createWithRoomAndUser(room, user, { ts: now, open: true, diff --git a/server/methods/browseChannels.js b/server/methods/browseChannels.js index 6f8be8edf325..f92dad8d7523 100644 --- a/server/methods/browseChannels.js +++ b/server/methods/browseChannels.js @@ -35,7 +35,7 @@ Meteor.methods({ return; } - if (!['name', 'createdAt', ...type === 'channels' ? ['usernames'] : [], ...type === 'users' ? ['username'] : []].includes(sortBy)) { + if (!['name', 'createdAt', ...type === 'channels' ? ['usersCount'] : [], ...type === 'users' ? ['username'] : []].includes(sortBy)) { return; } @@ -56,23 +56,19 @@ Meteor.methods({ return; } return { - results: RocketChat.models.Rooms.findByNameAndType( - regex, - 'c', - { - ...options, - sort, - fields: { - description: 1, - topic: 1, - name: 1, - lastMessage: 1, - ts: 1, - archived: 1, - usernames: 1, - usersCount: 1 - } - }).fetch(), + results: RocketChat.models.Rooms.findByNameAndType(regex, 'c', { + ...options, + sort, + fields: { + description: 1, + topic: 1, + name: 1, + lastMessage: 1, + ts: 1, + archived: 1, + usersCount: 1 + } + }).fetch(), total: RocketChat.models.Rooms.findByNameAndType(regex, 'c').count() }; } diff --git a/server/methods/channelsList.js b/server/methods/channelsList.js index b8dcea5077e3..9a8d03c57f97 100644 --- a/server/methods/channelsList.js +++ b/server/methods/channelsList.js @@ -44,56 +44,50 @@ Meteor.methods({ } } - const roomTypes = []; + let channels = []; + + const userId = Meteor.userId(); if (channelType !== 'private') { - if (RocketChat.authz.hasPermission(Meteor.userId(), 'view-c-room')) { - roomTypes.push({ - type: 'c' - }); - } else if (RocketChat.authz.hasPermission(Meteor.userId(), 'view-joined-room')) { - const roomIds = _.pluck(RocketChat.models.Subscriptions.findByTypeAndUserId('c', Meteor.userId()).fetch(), 'rid'); - roomTypes.push({ - type: 'c', - ids: roomIds - }); + if (RocketChat.authz.hasPermission(userId, 'view-c-room')) { + if (filter) { + channels = channels.concat(RocketChat.models.Rooms.findByType('c', options).fetch()); + } else { + channels = channels.concat(RocketChat.models.Rooms.findByTypeAndNameContaining('c', filter, options).fetch()); + } + } else if (RocketChat.authz.hasPermission(userId, 'view-joined-room')) { + const roomIds = RocketChat.models.Subscriptions.findByTypeAndUserId('c', userId, {fields: {rid: 1}}).fetch().map(s => s.rid); + if (filter) { + channels = channels.concat(RocketChat.models.Rooms.findByTypeInIds('c', roomIds, options).fetch()); + } else { + channels = channels.concat(RocketChat.models.Rooms.findByTypeInIdsAndNameContaining('c', roomIds, filter, options).fetch()); + } } } - if (channelType !== 'public' && RocketChat.authz.hasPermission(Meteor.userId(), 'view-p-room')) { - const user = RocketChat.models.Users.findOne(Meteor.userId(), { + if (channelType !== 'public' && RocketChat.authz.hasPermission(userId, 'view-p-room')) { + const user = RocketChat.models.Users.findOne(userId, { fields: { username: 1, 'settings.preferences.sidebarGroupByType': 1 } }); const userPref = RocketChat.getUserPreference(user, 'sidebarGroupByType'); - const globalPref = RocketChat.settings.get('UI_Group_Channels_By_Type'); // needs to negate globalPref because userPref represents its opposite - const groupByType = userPref !== undefined ? userPref : globalPref; + const groupByType = userPref !== undefined ? userPref : RocketChat.settings.get('UI_Group_Channels_By_Type'); if (!groupByType) { - roomTypes.push({ - type: 'p', - username: user.username - }); - } - } - - if (roomTypes.length) { - if (filter) { - return { - channels: RocketChat.models.Rooms.findByNameContainingTypesWithUsername(filter, roomTypes, options).fetch() - }; + const roomIds = RocketChat.models.Subscriptions.findByTypeAndUserId('p', userId, {fields: {rid: 1}}).fetch().map(s => s.rid); + if (filter) { + channels = channels.concat(RocketChat.models.Rooms.findByTypeInIds('p', roomIds, options).fetch()); + } else { + channels = channels.concat(RocketChat.models.Rooms.findByTypeInIdsAndNameContaining('p', roomIds, filter, options).fetch()); + } } - - return { - channels: RocketChat.models.Rooms.findContainingTypesWithUsername(roomTypes, options).fetch() - }; } return { - channels: [] + channels }; } }); diff --git a/server/methods/createDirectMessage.js b/server/methods/createDirectMessage.js index c481160caa4d..8c21d855d031 100644 --- a/server/methods/createDirectMessage.js +++ b/server/methods/createDirectMessage.js @@ -50,7 +50,8 @@ Meteor.methods({ $setOnInsert: { t: 'd', msgs: 0, - ts: now + ts: now, + usersCount: 2 } }); @@ -64,6 +65,7 @@ Meteor.methods({ open: true }, $setOnInsert: { + fname: to.name, name: to.username, t: 'd', alert: false, @@ -95,6 +97,7 @@ Meteor.methods({ $and: [{'u._id': to._id}] // work around to solve problems with upsert and dot }, { $setOnInsert: { + fname: me.username, name: me.username, t: 'd', open: false, diff --git a/server/methods/getRoomIdByNameOrId.js b/server/methods/getRoomIdByNameOrId.js index 116e7cfe8d97..67f6baff54c3 100644 --- a/server/methods/getRoomIdByNameOrId.js +++ b/server/methods/getRoomIdByNameOrId.js @@ -1,3 +1,4 @@ +// DEPRECATE Meteor.methods({ getRoomIdByNameOrId(rid) { check(rid, String); @@ -16,11 +17,6 @@ Meteor.methods({ }); } - const user = Meteor.user(); - if (user && user.username && room.usernames.indexOf(user.username) !== -1) { - return room._id; - } - if (room.t !== 'c' || RocketChat.authz.hasPermission(Meteor.userId(), 'view-c-room') !== true) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getRoomIdByNameOrId' diff --git a/server/methods/getRoomNameById.js b/server/methods/getRoomNameById.js index 1228eda83278..bff4f28cda25 100644 --- a/server/methods/getRoomNameById.js +++ b/server/methods/getRoomNameById.js @@ -1,8 +1,8 @@ Meteor.methods({ getRoomNameById(rid) { check(rid, String); - - if (!Meteor.userId()) { + const userId = Meteor.userId(); + if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getRoomNameById' }); @@ -16,12 +16,12 @@ Meteor.methods({ }); } - const user = Meteor.user(); - if (user && user.username && room.usernames.indexOf(user.username) !== -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, userId, { fields: { _id: 1 } }); + if (subscription) { return room.name; } - if (room.t !== 'c' || RocketChat.authz.hasPermission(Meteor.userId(), 'view-c-room') !== true) { + if (room.t !== 'c' || RocketChat.authz.hasPermission(userId, 'view-c-room') !== true) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getRoomNameById' }); diff --git a/server/methods/getUsersOfRoom.js b/server/methods/getUsersOfRoom.js index 3a6bc5c2e66a..e65391cc2112 100644 --- a/server/methods/getUsersOfRoom.js +++ b/server/methods/getUsersOfRoom.js @@ -1,44 +1,30 @@ Meteor.methods({ - getUsersOfRoom(roomId, showAll) { - if (!Meteor.userId()) { + getUsersOfRoom(rid, showAll) { + const userId = Meteor.userId(); + if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getUsersOfRoom' }); } - const room = Meteor.call('canAccessRoom', roomId, Meteor.userId()); + const room = Meteor.call('canAccessRoom', rid, userId); if (!room) { throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' }); } - if (room.broadcast && !RocketChat.authz.hasPermission(Meteor.userId(), 'view-broadcast-member-list', roomId)) { + if (room.broadcast && !RocketChat.authz.hasPermission(userId, 'view-broadcast-member-list', rid)) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getUsersOfRoom' }); } - const filter = (record) => { - if (!record._user) { - console.log('Subscription without user', record._id); - return false; - } + const subscriptions = RocketChat.models.Subscriptions.findByRoomIdWhenUsernameExists(rid, { fields: { 'u._id': 1 } }).fetch(); + const userIds = subscriptions.map(s => s.u._id); // TODO: CACHE: expensive + const options = { fields: { username: 1, name: 1 } }; - if (showAll === true) { - return true; - } - - return record._user.status !== 'offline'; - }; - - const map = (record) => { - return { - _id: record._user._id, - username: record._user.username, - name: record._user.name - }; - }; - - const records = RocketChat.models.Subscriptions.findByRoomId(roomId).fetch(); + const users = showAll === true + ? RocketChat.models.Users.findUsersWithUsernameByIds(userIds, options).fetch() + : RocketChat.models.Users.findUsersWithUsernameByIdsNotOffline(userIds, options).fetch(); return { - total: records.length, - records: records.filter(filter).map(map) + total: userIds.length, + records: users }; } }); diff --git a/server/methods/groupsList.js b/server/methods/groupsList.js deleted file mode 100644 index a235e9bf44cd..000000000000 --- a/server/methods/groupsList.js +++ /dev/null @@ -1,45 +0,0 @@ -import _ from 'underscore'; -import s from 'underscore.string'; - -Meteor.methods({ - groupsList(nameFilter, limit, sort) { - - check(nameFilter, Match.Optional(String)); - check(limit, Match.Optional(Number)); - check(sort, Match.Optional(String)); - - if (!Meteor.userId()) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'groupsList' }); - } - - const options = { - fields: { name: 1 }, - sort: { name: 1 } - }; - - //Verify the limit param is a number - if (_.isNumber(limit)) { - options.limit = limit; - } - - //Verify there is a sort option and it's a string - if (s.trim(sort)) { - switch (sort) { - case 'name': - options.sort = { name: 1 }; - break; - case 'msgs': - options.sort = { msgs: -1 }; - break; - } - } - - //Determine if they are searching or not, base it upon the name field - if (nameFilter) { - return { groups: RocketChat.models.Rooms.findByTypeAndNameContainingUsername('p', new RegExp(s.trim(s.escapeRegExp(nameFilter)), 'i'), Meteor.user().username, options).fetch() }; - } else { - const roomIds = _.pluck(RocketChat.models.Subscriptions.findByTypeAndUserId('p', Meteor.userId()).fetch(), 'rid'); - return { groups: RocketChat.models.Rooms.findByIds(roomIds, options).fetch() }; - } - } -}); diff --git a/server/methods/loadHistory.js b/server/methods/loadHistory.js index 76ef737b2eef..cd7c0cde20f9 100644 --- a/server/methods/loadHistory.js +++ b/server/methods/loadHistory.js @@ -37,7 +37,8 @@ Meteor.methods({ const canAnonymous = RocketChat.settings.get('Accounts_AllowAnonymousRead'); const canPreview = RocketChat.authz.hasPermission(fromId, 'preview-c-room'); - if (room.t === 'c' && !canAnonymous && !canPreview && room.usernames.indexOf(room.username) === -1) { + + if (room.t === 'c' && !canAnonymous && !canPreview && RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, fromId, { fields: { _id: 1 } })) { return false; } diff --git a/server/methods/muteUserInRoom.js b/server/methods/muteUserInRoom.js index 862ee9a9511d..e6d2b42dba7c 100644 --- a/server/methods/muteUserInRoom.js +++ b/server/methods/muteUserInRoom.js @@ -34,7 +34,8 @@ Meteor.methods({ }); } - if (Array.isArray(room.usernames) === false || room.usernames.includes(data.username) === false) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUsername(data.rid, data.username, { fields: { _id: 1 } }); + if (!subscription) { throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', { method: 'muteUserInRoom' }); diff --git a/server/methods/removeUserFromRoom.js b/server/methods/removeUserFromRoom.js index 4a2984d4ebf6..47c7f6a18b9f 100644 --- a/server/methods/removeUserFromRoom.js +++ b/server/methods/removeUserFromRoom.js @@ -27,14 +27,15 @@ Meteor.methods({ }); } - if (Array.isArray(room.usernames) === false || room.usernames.includes(data.username) === false) { + const removedUser = RocketChat.models.Users.findOneByUsername(data.username); + + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(data.rid, removedUser._id, { fields: { _id: 1 } }); + if (!subscription) { throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', { method: 'removeUserFromRoom' }); } - const removedUser = RocketChat.models.Users.findOneByUsername(data.username); - if (RocketChat.authz.hasRole(removedUser._id, 'owner', room._id)) { const numOwners = RocketChat.authz.getUsersInRole('owner', room._id).fetch().length; @@ -45,8 +46,6 @@ Meteor.methods({ } } - RocketChat.models.Rooms.removeUsernameById(data.rid, data.username); - RocketChat.models.Subscriptions.removeByRoomIdAndUserId(data.rid, removedUser._id); if (['c', 'p'].includes(room.t) === true) { diff --git a/server/methods/unmuteUserInRoom.js b/server/methods/unmuteUserInRoom.js index 3c75b44921f3..dce8f868c909 100644 --- a/server/methods/unmuteUserInRoom.js +++ b/server/methods/unmuteUserInRoom.js @@ -28,7 +28,8 @@ Meteor.methods({ }); } - if (Array.isArray(room.usernames) === false || room.usernames.includes(data.username) === false) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUsername(data.rid, data.username, { fields: { _id: 1 } }); + if (!subscription) { throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', { method: 'unmuteUserInRoom' }); diff --git a/server/publications/channelAndPrivateAutocomplete.js b/server/publications/channelAndPrivateAutocomplete.js index fec41bbeafdc..32f3a8fe717d 100644 --- a/server/publications/channelAndPrivateAutocomplete.js +++ b/server/publications/channelAndPrivateAutocomplete.js @@ -19,7 +19,7 @@ Meteor.publish('channelAndPrivateAutocomplete', function(selector) { } }; - const cursorHandle = RocketChat.models.Rooms.findByNameStartingAndTypes(selector.name, ['c', 'p'], options).observeChanges({ + const cursorHandle = RocketChat.models.Rooms.findChannelAndPrivateByNameStarting(selector.name, options).observeChanges({ added(_id, record) { return pub.added('autocompleteRecords', _id, record); }, diff --git a/server/publications/room.js b/server/publications/room.js index a2bfa46da5e8..7d97405597d0 100644 --- a/server/publications/room.js +++ b/server/publications/room.js @@ -40,10 +40,9 @@ const fields = { }; const roomMap = (record) => { - if (record._room) { - return _.pick(record._room, ...Object.keys(fields)); + if (record) { + return _.pick(record, ...Object.keys(fields)); } - console.log('Empty Room for Subscription', record); return {}; }; @@ -75,7 +74,9 @@ Meteor.methods({ }, getRoomByTypeAndName(type, name) { - if (!Meteor.userId() && RocketChat.settings.get('Accounts_AllowAnonymousRead') === false) { + const userId = Meteor.userId(); + + if (!userId && RocketChat.settings.get('Accounts_AllowAnonymousRead') === false) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getRoomByTypeAndName' }); } @@ -89,36 +90,40 @@ Meteor.methods({ room = RocketChat.models.Rooms.findByTypeAndName(type, name).fetch(); } - if (!room) { + if (!room || room.length === 0) { throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getRoomByTypeAndName' }); } - if (!Meteor.call('canAccessRoom', room._id, Meteor.userId())) { + room = room[0]; + + if (!Meteor.call('canAccessRoom', room._id, userId)) { throw new Meteor.Error('error-no-permission', 'No permission', { method: 'getRoomByTypeAndName' }); } - if (RocketChat.settings.get('Store_Last_Message') && !RocketChat.authz.hasPermission(Meteor.userId(), 'preview-c-room')) { + if (RocketChat.settings.get('Store_Last_Message') && !RocketChat.authz.hasPermission(userId, 'preview-c-room')) { delete room.lastMessage; } - return roomMap({ _room: room }); + return roomMap(room); } }); -RocketChat.models.Rooms.cache.on('sync', (type, room/*, diff*/) => { - const records = RocketChat.models.Subscriptions.findByRoomId(room._id).fetch(); - - const _room = roomMap({_room: room}); - for (const record of records) { - RocketChat.Notifications.notifyUserInThisInstance(record.u._id, 'rooms-changed', type, _room); +RocketChat.models.Rooms.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + // Override data cuz we do not publish all fields + data = RocketChat.models.Rooms.findOneById(id, { fields }); + break; + + case 'removed': + data = { _id: id }; + break; } -}); -RocketChat.models.Subscriptions.on('changed', (type, subscription/*, diff*/) => { - if (type === 'inserted' || type === 'removed') { - const room = RocketChat.models.Rooms.findOneById(subscription.rid); - if (room) { - RocketChat.Notifications.notifyUserInThisInstance(subscription.u._id, 'rooms-changed', type, roomMap({_room: room})); - } + if (data) { + RocketChat.models.Subscriptions.findByRoomId(id, {fields: {'u._id': 1}}).forEach(({u}) => { + RocketChat.Notifications.notifyUserInThisInstance(u._id, 'rooms-changed', clientAction, data); + }); } }); diff --git a/server/publications/spotlight.js b/server/publications/spotlight.js index 85708e9f7369..57e6c447b89e 100644 --- a/server/publications/spotlight.js +++ b/server/publications/spotlight.js @@ -67,15 +67,12 @@ Meteor.methods({ } if (type.rooms === true && RocketChat.authz.hasPermission(userId, 'view-c-room')) { - const username = RocketChat.models.Users.findOneById(userId, { - username: 1 - }).username; - const searchableRoomTypes = Object.entries(RocketChat.roomTypes.roomTypes) .filter((roomType)=>roomType[1].includeInRoomSearch()) .map((roomType)=>roomType[0]); - result.rooms = fetchRooms(userId, RocketChat.models.Rooms.findByNameAndTypesNotContainingUsername(regex, searchableRoomTypes, username, roomOptions).fetch()); + const roomIds = RocketChat.models.Subscriptions.findByUserIdAndTypes(userId, searchableRoomTypes, {fields: {rid: 1}}).fetch().map(s => s.rid); + result.rooms = fetchRooms(userId, RocketChat.models.Rooms.findByNameAndTypesNotInIds(regex, searchableRoomTypes, roomIds, roomOptions).fetch()); } } else if (type.users === true && rid) { const subscriptions = RocketChat.models.Subscriptions.find({ diff --git a/server/publications/subscription.js b/server/publications/subscription.js index 88758879c124..2ab526a6775b 100644 --- a/server/publications/subscription.js +++ b/server/publications/subscription.js @@ -65,17 +65,18 @@ Meteor.methods({ } }); -RocketChat.models.Subscriptions.on('changed', function(type, subscription) { - RocketChat.Notifications.notifyUserInThisInstance(subscription.u._id, 'subscriptions-changed', type, RocketChat.models.Subscriptions.processQueryOptionsOnResult(subscription, { - fields - })); -}); +RocketChat.models.Subscriptions.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + // Override data cuz we do not publish all fields + data = RocketChat.models.Subscriptions.findOneById(id, { fields }); + break; + + case 'removed': + data = RocketChat.models.Subscriptions.trashFindOneById(id, { fields: { u: 1 } }); + break; + } -// TODO needs improvement -// We are sending the record again cuz any update on subscription will send the record without the fname (join) -// Then we need to sent it again listening to the join event. -RocketChat.models.Subscriptions.on('join:fname:inserted', function(subscription/*, user*/) { - RocketChat.Notifications.notifyUserInThisInstance(subscription.u._id, 'subscriptions-changed', 'changed', RocketChat.models.Subscriptions.processQueryOptionsOnResult(subscription, { - fields - })); + RocketChat.Notifications.notifyUserInThisInstance(data.u._id, 'subscriptions-changed', clientAction, data); }); diff --git a/server/startup/initialData.js b/server/startup/initialData.js index bada6f920803..97facd5389b8 100644 --- a/server/startup/initialData.js +++ b/server/startup/initialData.js @@ -1,7 +1,7 @@ import _ from 'underscore'; Meteor.startup(function() { - Meteor.defer(() => RocketChat.models._CacheControl.withValue(false, function() { + Meteor.defer(() => { if (!RocketChat.models.Rooms.findOneById('GENERAL')) { RocketChat.models.Rooms.createWithIdTypeAndName('GENERAL', 'c', 'general', { 'default': true @@ -201,5 +201,5 @@ Meteor.startup(function() { return RocketChat.addUserToDefaultChannels(adminUser, true); } - })); + }); }); diff --git a/server/startup/migrations/v130.js b/server/startup/migrations/v130.js new file mode 100644 index 000000000000..80b3f63a52a7 --- /dev/null +++ b/server/startup/migrations/v130.js @@ -0,0 +1,120 @@ +import Future from 'fibers/future'; + +RocketChat.Migrations.add({ + version: 129, + up() { + RocketChat.models.Rooms._db.originals.update( + { + t: { $ne: 'd' } + }, + { + $unset: { usernames: 1 } + }, + { + multi: true + } + ); + + RocketChat.models.Rooms.find( + { + usersCount: { $exists: false } + }, + { + fields: { + _id: 1 + } + } + ).forEach(({ _id }) => { + const usersCount = RocketChat.models.Subscriptions.findByRoomId( + _id + ).count(); + + RocketChat.models.Rooms._db.originals.update( + { + _id + }, + { + $set: { + usersCount + } + } + ); + }); + + // Getting all subscriptions and users to memory allow us to process in batches, + // all other solutions takes hundreds or thousands times more to process. + const subscriptions = RocketChat.models.Subscriptions.find( + { + t: 'd', + name: { $exists: true }, + fname: { $exists: false } + }, + { + fields: { + name: 1 + } + } + ).fetch(); + + const users = RocketChat.models.Users.find( + { username: { $exists: true }, name: { $exists: true } }, + { fields: { username: 1, name: 1 } } + ).fetch(); + const usersByUsername = users.reduce((obj, user) => { + obj[user.username] = user.name; + return obj; + }, {}); + + const updateSubscription = subscription => { + return new Promise(resolve => { + Meteor.defer(() => { + const name = usersByUsername[subscription.name]; + + if (!name) { + return resolve(); + } + + RocketChat.models.Subscriptions._db.originals.update( + { + _id: subscription._id + }, + { + $set: { + fname: name + } + } + ); + + resolve(); + }); + }); + }; + + // Use FUTURE to process itens in batchs and wait the final one + const fut = new Future(); + + const processBatch = () => { + const itens = subscriptions.splice(0, 1000); + + console.log( + 'Migrating', + itens.length, + 'of', + subscriptions.length, + 'subscriptions' + ); + + if (itens.length) { + Promise.all(itens.map(s => updateSubscription(s))).then(() => { + processBatch(); + }); + } else { + fut.return(); + } + }; + + processBatch(); + + fut.wait(); + } +}); diff --git a/server/startup/roomPublishes.js b/server/startup/roomPublishes.js deleted file mode 100644 index 20fb57c259e0..000000000000 --- a/server/startup/roomPublishes.js +++ /dev/null @@ -1,92 +0,0 @@ -Meteor.startup(function() { - RocketChat.roomTypes.setPublish('c', function(identifier) { - const options = { - fields: { - name: 1, - t: 1, - cl: 1, - u: 1, - usernames: 1, - topic: 1, - announcement: 1, - muted: 1, - archived: 1, - ro: 1, - reactWhenReadOnly: 1, - jitsiTimeout: 1, - description: 1, - sysMes: 1, - joinCodeRequired: 1, - streamingOptions: 1 - } - }; - - if (RocketChat.authz.hasPermission(this.userId, 'view-c-room')) { - return RocketChat.models.Rooms.findByTypeAndName('c', identifier, options); - } else if (RocketChat.authz.hasPermission(this.userId, 'view-joined-room')) { - const roomId = RocketChat.models.Subscriptions.findByTypeNameAndUserId('c', identifier, this.userId).fetch(); - if (roomId.length > 0) { - return RocketChat.models.Rooms.findById(roomId[0].rid, options); - } - } - - return this.ready(); - }); - - RocketChat.roomTypes.setPublish('p', function(identifier) { - const options = { - fields: { - name: 1, - t: 1, - cl: 1, - u: 1, - usernames: 1, - topic: 1, - announcement: 1, - muted: 1, - archived: 1, - ro: 1, - reactWhenReadOnly: 1, - jitsiTimeout: 1, - description: 1, - sysMes: 1, - tokenpass: 1, - streamingOptions: 1 - } - }; - - const user = RocketChat.models.Users.findOneById(this.userId, { - fields: { - username: 1 - } - }); - - return RocketChat.models.Rooms.findByTypeAndNameContainingUsername('p', identifier, user.username, options); - }); - - return RocketChat.roomTypes.setPublish('d', function(identifier) { - const options = { - fields: { - name: 1, - t: 1, - cl: 1, - u: 1, - usernames: 1, - topic: 1, - jitsiTimeout: 1 - } - }; - - const user = RocketChat.models.Users.findOneById(this.userId, { - fields: { - username: 1 - } - }); - - if (RocketChat.authz.hasAtLeastOnePermission(this.userId, ['view-d-room', 'view-joined-room'])) { - return RocketChat.models.Rooms.findByTypeContainingUsernames('d', [user.username, identifier], options); - } - - return this.ready(); - }); -}); diff --git a/server/stream/messages.js b/server/stream/messages.js index 71c68bcdfe0d..4f5537cde49b 100644 --- a/server/stream/messages.js +++ b/server/stream/messages.js @@ -11,7 +11,7 @@ msgStream.allowRead(function(eventName, args) { return false; } - if (room.t === 'c' && !RocketChat.authz.hasPermission(this.userId, 'preview-c-room') && room.usernames.indexOf(room.username) === -1) { + if (room.t === 'c' && !RocketChat.authz.hasPermission(this.userId, 'preview-c-room') && !RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, this.userId, { fields: { _id: 1 } })) { return false; } @@ -32,7 +32,7 @@ msgStream.allowEmit('__my_messages__', function(eventName, msg, options) { return false; } - options.roomParticipant = room.usernames.indexOf(room.username) > -1; + options.roomParticipant = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, this.userId, { fields: { _id: 1 } }) != null; options.roomType = room.t; return true; @@ -63,19 +63,12 @@ Meteor.startup(function() { } } - return RocketChat.models.Messages._db.on('change', function({action, id, data/*, oplog*/}) { - switch (action) { - case 'insert': - data._id = id; - publishMessage('inserted', data); - break; - case 'update:record': - publishMessage('updated', data); - break; - case 'update:diff': - publishMessage('updated', RocketChat.models.Messages.findOne({ - _id: id - })); + return RocketChat.models.Messages.on('change', function({ clientAction, id, data/*, oplog*/ }) { + switch (clientAction) { + case 'inserted': + case 'updated': + const message = data || RocketChat.models.Messages.findOne({ _id: id }); + publishMessage(clientAction, message); break; } }); diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js index 871ad79bcf7c..26ac0426d051 100644 --- a/tests/end-to-end/api/00-miscellaneous.js +++ b/tests/end-to-end/api/00-miscellaneous.js @@ -205,7 +205,7 @@ describe('miscellaneous', function() { expect(res.body).to.have.property('result').and.to.be.an('array'); expect(res.body.result[0]).to.have.property('_id'); expect(res.body.result[0]).to.have.property('name'); - expect(res.body.result[0]).to.have.property('usernames').and.to.be.an('array'); + expect(res.body.result[0]).to.have.property('usersCount').and.to.be.an('number'); expect(res.body.result[0]).to.have.property('ts'); }) .end(done); @@ -232,7 +232,7 @@ describe('miscellaneous', function() { expect(res.body).to.have.property('result').and.to.be.an('array'); expect(res.body.result[0]).to.have.property('_id'); expect(res.body.result[0]).to.have.property('name'); - expect(res.body.result[0]).to.have.property('usernames').and.to.be.an('array'); + expect(res.body.result[0]).to.have.property('usersCount').and.to.be.an('number'); expect(res.body.result[0]).to.have.property('ts'); }) .end(done); diff --git a/tests/end-to-end/api/04-direct-message.js b/tests/end-to-end/api/04-direct-message.js index 84ee72527554..c17cf64684a0 100644 --- a/tests/end-to-end/api/04-direct-message.js +++ b/tests/end-to-end/api/04-direct-message.js @@ -2,7 +2,7 @@ /* globals expect */ /* eslint no-unused-vars: 0 */ -import {getCredentials, api, login, request, credentials, directMessage, log } from '../../data/api-data.js'; +import {getCredentials, api, login, request, credentials, directMessage, log, apiUsername, apiEmail } from '../../data/api-data.js'; import {adminEmail, password} from '../../data/user.js'; import supertest from 'supertest'; @@ -136,4 +136,97 @@ describe('[Direct Messages]', function() { }) .end(done); }); + + describe('fname property', () => { + const username = `fname_${ apiUsername }`; + const name = `Name fname_${ apiUsername }`; + const updatedName = `Updated Name fname_${ apiUsername }`; + const email = `fname_${ apiEmail }`; + let userId; + let directMessageId; + + before((done) => { + request.post(api('users.create')) + .set(credentials) + .send({ + email, + name, + username, + password, + active: true, + roles: ['user'], + joinDefaultChannels: true, + verified: true + }) + .expect((res) => { + userId = res.body.user._id; + }) + .end(done); + }); + + before((done) => { + request.post(api('chat.postMessage')) + .set(credentials) + .send({ + channel: `@${ username }`, + text: 'This message was sent using the API' + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('message.msg', 'This message was sent using the API'); + expect(res.body).to.have.nested.property('message.rid'); + directMessageId = res.body.message.rid; + }) + .end(done); + }); + + it('should have fname property', (done) => { + request.get(api('subscriptions.getOne')) + .set(credentials) + .query({ + roomId: directMessageId + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body.subscription).to.have.property('name', username); + expect(res.body.subscription).to.have.property('fname', name); + }) + .end(done); + }); + + it('should update user\'s name', (done) => { + request.post(api('users.update')) + .set(credentials) + .send({ + userId, + data: { + name: updatedName + } + }) + .expect((res) => { + expect(res.body.user).to.have.property('name', updatedName); + }) + .end(done); + }); + + it('should have fname property updated', (done) => { + request.get(api('subscriptions.getOne')) + .set(credentials) + .query({ + roomId: directMessageId + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body.subscription).to.have.property('name', username); + expect(res.body.subscription).to.have.property('fname', updatedName); + }) + .end(done); + }); + }); }); diff --git a/tests/end-to-end/api/11-permissions.js b/tests/end-to-end/api/11-permissions.js index c7bb2e890355..734aecdd2c2a 100644 --- a/tests/end-to-end/api/11-permissions.js +++ b/tests/end-to-end/api/11-permissions.js @@ -23,11 +23,6 @@ describe('[Permissions]', function() { expect(firstElement).to.have.property('_id'); expect(firstElement).to.have.property('roles').and.to.be.a('array'); expect(firstElement).to.have.property('_updatedAt'); - expect(firstElement).to.have.property('meta'); - expect(firstElement.meta).to.have.property('revision'); - expect(firstElement.meta).to.have.property('created'); - expect(firstElement.meta).to.have.property('version'); - expect(firstElement).to.have.property('$loki'); }) .end(done); }); @@ -47,11 +42,6 @@ describe('[Permissions]', function() { expect(firstElement).to.have.property('_id'); expect(firstElement).to.have.property('roles').and.to.be.a('array'); expect(firstElement).to.have.property('_updatedAt'); - expect(firstElement).to.have.property('meta'); - expect(firstElement.meta).to.have.property('revision'); - expect(firstElement.meta).to.have.property('created'); - expect(firstElement.meta).to.have.property('version'); - expect(firstElement).to.have.property('$loki'); }) .end(done); }); @@ -78,11 +68,6 @@ describe('[Permissions]', function() { expect(firstElement).to.have.property('_id'); expect(firstElement).to.have.property('roles').and.to.be.a('array'); expect(firstElement).to.have.property('_updatedAt'); - expect(firstElement).to.have.property('meta'); - expect(firstElement.meta).to.have.property('revision'); - expect(firstElement.meta).to.have.property('created'); - expect(firstElement.meta).to.have.property('version'); - expect(firstElement).to.have.property('$loki'); }) .end(done); });