diff --git a/.docker/Dockerfile.rhel b/.docker/Dockerfile.rhel index 29b6cd066957c0b08f3d2cd97c44ea8b7b2ede88..392806ab63628076597f3a742bc82dc10af0413f 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-rc.0 MAINTAINER buildmaster@rocket.chat diff --git a/.github/history.json b/.github/history.json index 71ee92de4bf710058b3f03720f6d6057adcaeb4f..5ece26294f305c3b220050e4bfac6498b873341f 100644 --- a/.github/history.json +++ b/.github/history.json @@ -16859,6 +16859,259 @@ ] } ] + }, + "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", + "milestone": "0.67.0", + "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", + "milestone": "0.67.0", + "contributors": [ + "tassoevan" + ] + }, + { + "pr": "9991", + "title": "[FIX] web app manifest errors as reported by Chrome DevTools", + "userLogin": "justinribeiro", + "milestone": "0.67.0", + "contributors": [ + "justinribeiro", + "web-flow" + ] + }, + { + "pr": "11342", + "title": "[FIX] Message attachment's fields with different sizes", + "userLogin": "sampaiodiego", + "milestone": "0.67.0", + "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.67.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" + ] + } + ] } } } \ No newline at end of file diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 3a1f14b671a296343e7b87f99d7ef60324573b0a..5706e2fade13c4d7c1ee4331d7fe439221c859f3 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 = 82, # Increment this for every release. - appMarketingVersion = (defaultText = "0.66.3"), + appMarketingVersion = (defaultText = "0.67.0-rc.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 7c09e063df5f74f2c5c1abf703d6b9c2657ec4de..e035853870a97d0eb51e0325dc3f2955a2659619 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-rc.0 fi echo "Preparing to trigger a snap release for $CHANNEL channel" diff --git a/HISTORY.md b/HISTORY.md index 4f174f2cd8e4ff407f96aded27be471a293a9cf6..7a46a946e4a7a11a55ff0c34b558b4a31b86a440 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,57 @@ +# 0.67.0 (Under Release Candidate Process) + +## 0.67.0-rc.0 +`2018-07-10 · 1 ï¸ï¸ï¸âš ï¸ Â· 1 🎉 · 1 🚀 · 6 🛠· 4 🔠· 11 👩â€ðŸ’»ðŸ‘¨â€ðŸ’»` + +### âš ï¸ 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)) + +<details> +<summary>🔠Minor changes</summary> + +- 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)) + +</details> + +### 👩â€ðŸ’»ðŸ‘¨â€ðŸ’» 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 +97,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 +115,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)) -<details> -<summary>🔠Minor changes</summary> - -- [IMPROVE] Setup Wizard username validation, step progress and optin/optout ([#11254](https://github.com/RocketChat/Rocket.Chat/pull/11254)) - -</details> - ### 👩â€ðŸ’»ðŸ‘¨â€ðŸ’» Core Team 🤓 - [@Hudell](https://github.com/Hudell) @@ -75,7 +125,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 +161,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 +214,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 +229,13 @@ <summary>🔠Minor changes</summary> - 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 +379,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 +399,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 +410,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 +424,10 @@ <summary>🔠Minor changes</summary> - 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 +448,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 +460,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 +603,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 +686,6 @@ <summary>🔠Minor changes</summary> - 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 +704,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 +795,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 +805,24 @@ <summary>🔠Minor changes</summary> - 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)) </details> ### 👩â€ðŸ’»ðŸ‘¨â€ðŸ’» 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 +855,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 +873,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 +890,6 @@ <summary>🔠Minor changes</summary> - 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 +902,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 +926,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 +1004,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 +1046,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 +1195,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 +1217,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 +1229,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 +1239,10 @@ <summary>🔠Minor changes</summary> - 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 +1251,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 +1265,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 +1289,10 @@ <summary>🔠Minor changes</summary> - 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)) </details> -### 👩â€ðŸ’»ðŸ‘¨â€ðŸ’» Contributors 😠- -- [@HammyHavoc](https://github.com/HammyHavoc) - ### 👩â€ðŸ’»ðŸ‘¨â€ðŸ’» Core Team 🤓 - [@karlprieb](https://github.com/karlprieb) @@ -1507,7 +1376,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 +1405,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 +1449,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 +1461,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 +1493,6 @@ <summary>🔠Minor changes</summary> - 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 +1515,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 +1531,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 +1572,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 +1591,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 +1602,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 +1609,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 +1629,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 +1783,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 +1808,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 +1818,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 +1833,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 +1927,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 +1956,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 +2012,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 +2020,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 +2042,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 +2094,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 +2130,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 +2142,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 +2174,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 +2182,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 +2207,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 +2326,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 +2346,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 +2381,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 +2422,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 +2464,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 +2500,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 +2509,6 @@ <details> <summary>🔠Minor changes</summary> -- [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 +2522,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)) </details> diff --git a/package-lock.json b/package-lock.json index 1587fdbdda343bdddfaf04a13363cbf3bac4b5f6..bcecc3b632dcb8bb31c19a67e550371d8a87f573 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 278978390c1669764bc03b88f3804c0bdb850fe2..31bc481fef09f5e4431d519eddfe0b5c7029ca80 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-rc.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 edf0d0e4b2570de38960d80a568d3d9110bcce8c..b055555600c83092dad7a99d99981a88097a6f11 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 c27b4d66422622fe06cbf7569085a1098583cfdb..fd0f09a1d3e2ca30bb769a016a3ec9cb1c5977ee 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 be7de10766bec5b7eddce606d100ddaecaa74593..3c13e3e94a3fe4bdd6fd07e9b426a26602350221 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 ffeb3cf4434a33a721653a948cc15a37986bae91..c5835e0f4099198a309608264b0693d019663105 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 c2ad7d8da8e047e029d80b38f2fd09d5a6bb1270..865b6ab3bc8a645b7738285c8c6975bb63cdc870 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 e58fe4094a26cc329db78b7b200dbdc867c4ce88..8bc1c57e4fdf349e15b79e06b185d4d9f8ff773a 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 4968333125577e3b481321aed8a2fe0dbf3ea05e..d64b5680aaa573da81c62e3cd70ad484aca2826c 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 22d391e5c581a11cee071834a3e4c69fb4cfe796..126b1f1b668b5a8ff4a8758b236864d42fb9bfdb 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 d0bece2d7e8ee93c2be117a37891d63612e98f8c..2db16816523345e323d6569c76d7a436359e579e 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 a12e9bcf04996ca6ddf8aab91b09bce054c01d4b..9fd875755331af4ad204606c4971e62a8ea47d64 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 31a6e17674deef6378f30a610c5c0f4392929ba1..1084bb8ecf0431aa26691c45ab23e1029118cd98 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 a82e0411fca0d34ab178649f061f839f4d102655..220e8d94a827f55e4194a6ec17b96424dbb87eea 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 8c5f1fed85fac2a50e15bacc1ee3313dae64753e..f93c95883c82d394936d3d826b57d2b26cef6d7c 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 029109db025c675700cfa70a9facbb11da0d758b..1477c91940d7cd36ce0972f243ad051da2a3b6a4 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 492700a134f36259696be6436d0856577c2f3225..c5e689b444f9c8818f731b22e65c30507f48166c 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 922a44c9d75cb01272fbae7c14a8ea60c6fc16fc..b84796ea3491b6dbaf4c587d9c98ae003697466e 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 07e81e5ff17fe385e41783b67da60b831034d5ea..de14f8c365c8b87ee82b921d9e382635f23a9a43 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 e16dffa8ab83e8b4af5dddc8ca9bf79990edecfa..423ac8e7813eee69f6361b6be5328f529f764a18 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 4fdc6e15a3094454886ed0307d7263918eb78e07..2b46164bf620005a6310e584c3df2051c983b34e 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 d768b78ab7f8b996d738a555ef0de1b3e72bc4ea..ee4df2393346b6ef23837bf29b26ea44458bf0cb 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 96b4e6c56eeee814db97f98fb99771ac4e4652a3..e21cf74929645bf672f398a9fd8ddf3cb3b9b924 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 58469f085c144b3dd904429a31c6707729306f15..0f28bc445da7990e311f81f2558c752487e0375a 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é <strong>\"%s\"</strong> 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é <strong>\"%s\"</strong> n'a été trouvé !", diff --git a/packages/rocketchat-i18n/i18n/uk.i18n.json b/packages/rocketchat-i18n/i18n/uk.i18n.json index 4a69d5220f2cc100ac8543295e5d83cac86cdf5a..78af4414f549d1b14bb53bcb1167509c6dbee8aa 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 36a9b47776348237b0ef78b19e349d0ae7b1fb9c..0894f3fb1ea3c19d452c175bba903dc16df9b96c 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 22f83b80a78032a998993ef98f67371de92d7ea1..22c06a8ce6dd7a1afc7c2121a33e475dc717d843 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 ffababb1d1377e73b48df02004482ef72c6fed78..c933d9d4de8777240f1c7fef62b4ede34bc2c550 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 bb789b816ad854fd5fc7d716f44cadf48296c2c3..789c12e3bdaf467464e7a7381479b6db1c28ba78 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 91b3a85f55aba312df60689132b68e045d1e6ca3..76aa1e73a672b09057ee91482b3845ba9ae39dc4 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 d1f6b3484f967fccd29a9b082ca7edcfbe779451..4daa5d35467f07e2e5eece739ee3b4034188a7c6 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 3ca566a57cc45cc5377fcd27f1aeb900833947b9..09dac262d5eaa97e9e818f9efcf9bfa016c58c14 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 a1a2fee708781ef5e528f9657a864587f0c39d37..d9b644074106dc3daa5fca6030cb9be6070cb822 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 6a52e562ae91287797120fc7658ee95935a0955a..2b0244629c15ad2f6a9bbd4e400dde0dcebc782e 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 4592252e56336a1cee3671e61118c9baaed381c7..e1d0cec3e599665fb01d7edb74e8a2006a12c1de 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 def1769a67854a844f183fac245677d74b3b2ca3..fc3b8e95a94bd0385a05235be4361c4a4cee5cbe 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-rc.0" } diff --git a/packages/rocketchat-lib/server/functions/Notifications.js b/packages/rocketchat-lib/server/functions/Notifications.js index 808d0b8bb9028e64dbeac51208d620e945922f88..7e0e09f4b7adf30249153cfbad5258dbf68a9bb8 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 414e5e3b7588bb16fd0b88f3b03418ba801f55af..71718be0b84ce816a1d93764d1a0930c56fda312 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 527b68fab3f88e6682057348829c0df07cebf6f2..42a8823c6edc6ac378d784251e9c134f341bfb2d 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 0aa21c9e96dda9ac13f92ee2aa4ecc9b8a256dfe..385b4ef43a8e8422e7fa45082317cbfff9f9a559 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 362dc83a5014b037ede6cf8bc87d191e564c5469..5a65b0fae5fbd4f489ffce9b0fa6267737dc2215 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 87b9faf5d0861e9cceb47269540e52f13c37b708..dc910a0a7ab1c64a2736d0516f386f20439079ab 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 321d085641f02491f452a122b27d528553f4862a..ff3bc8bef8ae620bd1ac48fa32f0ba9516378a47 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 da7be9783ca0873f1d9d0108ce082171d1a78a7f..8781bfb156583a75d9576aaf0d24397e31d56d7c 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 7b0631970320cdaba79ef9c5b4b24303c4e02b15..faf36b1e4a92d01e153731fdea9f59e6ed63e8a8 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 61ac0298bd2bb23f063af332cfc36a1f10fe5768..af838ba23d18865b038cd7a51040c6888fb2cba9 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 675f0f6bf50fec3fe54eba350488a18130891770..2769f4c91faba731b2cd8b43a0ce30bd8fc7cca0 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 b00c42350d314e8040a76043a0d4d3fbbc04d0be..5a85068bdc060ce753dc820e7d2663a6d85a88ee 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 b3583cb253eae826ceb304d673c8b8a791d4b4da..a8039a7594f66d8adc5bae34977c6457c5a94f46 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 dd13f2e67ba0477e0ba623eca821bba0fc9b6e0c..c85a44e1fefb3cd2c58e145f3f7c5a47e1d3b1f1 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 09b04176d229dd037789c1ab7619da39ff89964a..b714069c78fc80e3ed6ee41360779e859ea1c598 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 12d40fedee7844415185799f8b41b72b8d1b5a78..d3bfa893c3dcd2265b4e5338d98d0856dae96242 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 5424fb97982dca5dce4884b03a4b81dcd49b8caf..36ca22f57c1a053c3ee986a3396f5d7eae343c19 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 6431305c01df663c2c3411ff5cc9b6ace11be8f6..e124e4ca387d3bf69d8da8982491c6e6d0697723 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 1e01cabb86619edc46655bce5c0f78579f31f1b0..0000000000000000000000000000000000000000 --- 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 b93f3e91770151d1920f4b638e6daa47455976f6..a39f0827ca7d782ffedf002193bc8edc42628d51 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 5707468422bf54f78e8364926984cf2baf00190e..175ccea3cae7d7d1d68943b40ade89edb8fa1e08 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 8f555fc454c7dce1db26429f276a893d65d3eea1..0000000000000000000000000000000000000000 --- 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 541af4c2a8268458e42884647c62cb45daf6804a..13aae330ea163b5dbb9ea034cf9c8d16c007241e 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 245e998e5e98efd69678aba8017f1882d1877a1a..6797cace30df9d6a04efda91e122647e00a24b85 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 89436156dc19e13d2af3a1d9a790a2ffcbf871f7..93ba1a6f11c6b57693864a78c0c0c40304113358 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 378af82a34aa50bddf820a3e7288f138c6d42b4f..addcfa608ef409c030f7ac42381f18ca007d4330 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 414bd2cdf9a19ad3d60dabc7579a5ff1054a11d3..07aaa0912f9cc27fad01d42771fd45062d095f81 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 142ca76375938e179fe8cf55ab1ea0d0d3f70d3e..1e3e533b7e349ef700c56bf7539df660a79bb083 100644 --- a/packages/rocketchat-livechat/.app/client/views/register.html +++ b/packages/rocketchat-livechat/.app/client/views/register.html @@ -9,11 +9,11 @@ </p> <form id="livechat-registration"> {{#if showNameFieldRegisterForm}} - <input type="text" name="name" id="guestName" placeholder="{{_ "Name"}}"> - {{/if}} + <input type="text" name="name" id="guestName" placeholder="{{_ "Name"}}" value={{getName}}> + {{/if}} {{#if showEmailFieldRegisterForm}} - <input type="email" name="email" id="guestEmail" placeholder="{{_ "Email"}}"> - {{/if}} + <input type="email" name="email" id="guestEmail" placeholder="{{_ "Email"}}" value={{getEmail}}> + {{/if}} {{#if showDepartments}} <select name="department"> <option value="">{{_ "Select_a_department"}}</option> diff --git a/packages/rocketchat-livechat/.app/client/views/register.js b/packages/rocketchat-livechat/.app/client/views/register.js index df4d27ace9a8b139808bc8c04dd3fc954f321cd1..4139fb1c5c631df72ad9709b913f93d17743e677 100644 --- a/packages/rocketchat-livechat/.app/client/views/register.js +++ b/packages/rocketchat-livechat/.app/client/views/register.js @@ -27,7 +27,13 @@ Template.register.helpers({ }, showEmailFieldRegisterForm() { return Livechat.emailFieldRegistrationForm; - } + }, + getName() { + return Livechat.guestName; + }, + getEmail() { + return Livechat.guestEmail; + } }); Template.register.events({ @@ -41,10 +47,10 @@ Template.register.events({ } }; const form = e.currentTarget; - + const fields = []; - let name; - let email; + let name = Livechat.guestName; + let email = Livechat.guestEmail; if (Livechat.nameFieldRegistrationForm) { fields.push('name'); @@ -106,7 +112,7 @@ Template.register.onCreated(function() { return valid; }; - + this.showError = (msg) => { $('.error').addClass('show'); this.error.set(msg); diff --git a/packages/rocketchat-livechat/.app/imports/client/visitor.js b/packages/rocketchat-livechat/.app/imports/client/visitor.js index 76bd78956f180a93de747606f3bc38fa9977711a..6839df3b35f9f4e9199c86e577b9344cb7a708b9 100644 --- a/packages/rocketchat-livechat/.app/imports/client/visitor.js +++ b/packages/rocketchat-livechat/.app/imports/client/visitor.js @@ -1,4 +1,4 @@ -/* globals Commands */ +/* globals Commands, Livechat */ const msgStream = new Meteor.Streamer('room-messages'); export default { @@ -18,6 +18,23 @@ export default { this.token.set(localStorage.getItem('visitorToken')); }, + reset() { + msgStream.unsubscribe(this.roomSubscribed); + + this.id.set(null); + this.token.set(null); + this.room.set(null); + this.data.set(null); + this.roomToSubscribe.set(null); + this.roomSubscribed = null; + + Livechat.room = null; + Livechat.department = null; + Livechat.agent = null; + Livechat.guestName = null; + Livechat.guestEmail = null; + }, + getId() { return this.id.get(); }, @@ -38,6 +55,59 @@ export default { return this.token.get(); }, + setToken(token) { + if (!token || token == this.token.get()) { + return; + } + + this.reset(); + + localStorage.setItem('visitorToken', token); + this.token.set(token); + + Meteor.call('livechat:loginByToken', token, (err, result) => { + + if (!result) { + return; + } + + if (result._id) { + this.setId(result._id); + return result._id; + } + }); + }, + + setName(name) { + Livechat.guestName = name; + + if (!this.getId()) { + return; + } + + const data = { + token: this.getToken(), + name + }; + + Meteor.call('livechat:registerGuest', data); + }, + + setEmail(email) { + Livechat.guestEmail = email; + + if (!this.getId()) { + return; + } + + const data = { + token: this.getToken(), + email + }; + + Meteor.call('livechat:registerGuest', data); + }, + setRoom(rid) { this.room.set(rid); }, diff --git a/packages/rocketchat-livechat/assets/rocket-livechat.js b/packages/rocketchat-livechat/assets/rocket-livechat.js index d81d0ae304c6cbaf5ff173e9b5a2eaca64afa329..fb4c58b3ed25bcfa6e651b6a7581b6c412e7bd9e 100644 --- a/packages/rocketchat-livechat/assets/rocket-livechat.js +++ b/packages/rocketchat-livechat/assets/rocket-livechat.js @@ -620,6 +620,22 @@ callHook('setDepartment', department); }; + var setGuestToken = function(token) { + callHook('setGuestToken', token); + }; + + var setGuestName = function(name) { + callHook('setGuestName', name); + }; + + var setGuestEmail = function(email) { + callHook('setGuestEmail', email); + }; + + var registerGuest = function(guest) { + callHook('registerGuest', guest); + }; + var clearDepartment = function() { callHook('clearDepartment'); }; @@ -722,6 +738,10 @@ setTheme: setTheme, setDepartment: setDepartment, clearDepartment: clearDepartment, + setGuestToken: setGuestToken, + setGuestName: setGuestName, + setGuestEmail: setGuestEmail, + registerGuest: registerGuest, // callbacks onChatMaximized: function(fn) { registerCallback('chat-maximized', fn); }, diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 60fb7d4ec52bbb749a596ddad593565c518870d1..9c4efb5620c995245d85add2ed6bdc7198c998c5 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -365,8 +365,6 @@ RocketChat.Livechat = { const servedBy = room.servedBy; if (agent && agent.agentId !== servedBy._id) { - room.usernames = _.without(room.usernames, servedBy.username).concat(agent.username); - RocketChat.models.Rooms.changeAgentByRoomId(room._id, agent); const subscriptionData = { @@ -389,6 +387,7 @@ RocketChat.Livechat = { RocketChat.models.Subscriptions.removeByRoomIdAndUserId(room._id, servedBy._id); RocketChat.models.Subscriptions.insert(subscriptionData); + RocketChat.models.Rooms.incUsersCountById(room._id); RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser(room._id, { _id: servedBy._id, username: servedBy.username }); RocketChat.models.Messages.createUserJoinWithRoomIdAndUser(room._id, { _id: agent.agentId, username: agent.username }); diff --git a/packages/rocketchat-livechat/server/lib/QueueMethods.js b/packages/rocketchat-livechat/server/lib/QueueMethods.js index 0523b98e355bbb3937f299cb96b2e6108ef4804e..d167f18df62aea9e7e4e00992d68da1fadda8594 100644 --- a/packages/rocketchat-livechat/server/lib/QueueMethods.js +++ b/packages/rocketchat-livechat/server/lib/QueueMethods.js @@ -19,6 +19,7 @@ RocketChat.QueueMethods = { const room = _.extend({ _id: message.rid, msgs: 1, + usersCount: 1, lm: new Date(), fname: (roomInfo && roomInfo.fname) || guest.name || guest.username, // usernames: [agent.username, guest.username], @@ -38,6 +39,7 @@ RocketChat.QueueMethods = { open: true, waitingResponse: true }, roomInfo); + const subscriptionData = { rid: message.rid, fname: guest.name || guest.username, @@ -57,6 +59,7 @@ RocketChat.QueueMethods = { }; RocketChat.models.Rooms.insert(room); + RocketChat.models.Subscriptions.insert(subscriptionData); RocketChat.Livechat.stream.emit(room._id, { @@ -114,9 +117,11 @@ RocketChat.QueueMethods = { }, t: 'l' }; + const room = _.extend({ _id: message.rid, msgs: 1, + usersCount: 0, lm: new Date(), fname: guest.name || guest.username, // usernames: [guest.username], @@ -132,6 +137,7 @@ RocketChat.QueueMethods = { open: true, waitingResponse: true }, roomInfo); + RocketChat.models.LivechatInquiry.insert(inquiry); RocketChat.models.Rooms.insert(room); diff --git a/packages/rocketchat-livechat/server/methods/closeRoom.js b/packages/rocketchat-livechat/server/methods/closeRoom.js index b61350ec9ff94929a98a32aeec0168d0745fff24..6013a517a66402e2e6669a3a47140c30d8e9f9b1 100644 --- a/packages/rocketchat-livechat/server/methods/closeRoom.js +++ b/packages/rocketchat-livechat/server/methods/closeRoom.js @@ -1,6 +1,7 @@ Meteor.methods({ 'livechat:closeRoom'(roomId, comment) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'close-livechat-room')) { + const userId = Meteor.userId(); + if (!userId || !RocketChat.authz.hasPermission(userId, 'close-livechat-room')) { throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeRoom' }); } @@ -12,7 +13,8 @@ Meteor.methods({ const user = Meteor.user(); - if ((!room.usernames || room.usernames.indexOf(user.username) === -1) && !RocketChat.authz.hasPermission(Meteor.userId(), 'close-others-livechat-room')) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, user._id, { _id: 1 }); + if (!subscription && !RocketChat.authz.hasPermission(userId, 'close-others-livechat-room')) { throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeRoom' }); } diff --git a/packages/rocketchat-livechat/server/methods/loginByToken.js b/packages/rocketchat-livechat/server/methods/loginByToken.js index 577bceb6c9f7850c943a5fdf4340fec7c3f218d7..e2a0f852ee6160f2f7c2ff2de0245054324ebf18 100644 --- a/packages/rocketchat-livechat/server/methods/loginByToken.js +++ b/packages/rocketchat-livechat/server/methods/loginByToken.js @@ -2,14 +2,14 @@ import LivechatVisitors from '../models/LivechatVisitors'; Meteor.methods({ 'livechat:loginByToken'(token) { - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); - if (!user) { + if (!visitor) { return; } return { - _id: user._id + _id: visitor._id }; } }); diff --git a/packages/rocketchat-livechat/server/methods/registerGuest.js b/packages/rocketchat-livechat/server/methods/registerGuest.js index ebad101c5d9b21fa3ff9c37fd15a094d5d103073..c41a0f489a28e8c2f094c454918e20ecd58d6f55 100644 --- a/packages/rocketchat-livechat/server/methods/registerGuest.js +++ b/packages/rocketchat-livechat/server/methods/registerGuest.js @@ -14,12 +14,19 @@ Meteor.methods({ const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { + token: 1, name: 1, username: 1, visitorEmails: 1 } }); + //If it's updating an existing visitor, it must also update the roomInfo + const cursor = RocketChat.models.Rooms.findOpenByVisitorToken(token); + cursor.forEach((room) => { + RocketChat.Livechat.saveRoomInfo(room, visitor); + }); + return { userId, visitor diff --git a/packages/rocketchat-livechat/server/methods/returnAsInquiry.js b/packages/rocketchat-livechat/server/methods/returnAsInquiry.js index 91abafb0444a73a4f1899c99dca1f5c76b9ddfa8..059f2687956a360565dfd1cc2e9b7eaea44bf05c 100644 --- a/packages/rocketchat-livechat/server/methods/returnAsInquiry.js +++ b/packages/rocketchat-livechat/server/methods/returnAsInquiry.js @@ -7,11 +7,6 @@ Meteor.methods({ // //delete agent and room subscription RocketChat.models.Subscriptions.removeByRoomId(rid); - // remove user from room - const username = Meteor.user().username; - - RocketChat.models.Rooms.removeUsernameById(rid, username); - // find inquiry corresponding to room const inquiry = RocketChat.models.LivechatInquiry.findOne({rid}); diff --git a/packages/rocketchat-livechat/server/methods/takeInquiry.js b/packages/rocketchat-livechat/server/methods/takeInquiry.js index da199cb8474096497029b0fd0277b8968a90e1ac..14d73a4393a27c9fb98495b609f0affc32f385a8 100644 --- a/packages/rocketchat-livechat/server/methods/takeInquiry.js +++ b/packages/rocketchat-livechat/server/methods/takeInquiry.js @@ -35,7 +35,9 @@ Meteor.methods({ mobilePushNotifications: 'all', emailNotifications: 'all' }; + RocketChat.models.Subscriptions.insert(subscriptionData); + RocketChat.models.Rooms.incUsersCountById(inquiry.rid); // update room const room = RocketChat.models.Rooms.findOneById(inquiry.rid); diff --git a/packages/rocketchat-livechat/server/methods/transfer.js b/packages/rocketchat-livechat/server/methods/transfer.js index e2af0627348884dd17f9ba5d2a2490b402fb6720..aac6d632a5ddaf60446fff7737085468888a856c 100644 --- a/packages/rocketchat-livechat/server/methods/transfer.js +++ b/packages/rocketchat-livechat/server/methods/transfer.js @@ -18,9 +18,8 @@ Meteor.methods({ const guest = LivechatVisitors.findOneById(room.v._id); - const user = Meteor.user(); - - if (room.usernames.indexOf(user.username) === -1 && !RocketChat.authz.hasRole(Meteor.userId(), 'livechat-manager')) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, Meteor.userId(), { fields: { _id: 1 } }); + if (!subscription && !RocketChat.authz.hasRole(Meteor.userId(), 'livechat-manager')) { throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:transfer' }); } diff --git a/packages/rocketchat-livechat/server/publications/visitorHistory.js b/packages/rocketchat-livechat/server/publications/visitorHistory.js index e90e34fc1470682e150dc47075b62b9dde7c71d2..f46711de734753876817231b001e970f1f071616 100644 --- a/packages/rocketchat-livechat/server/publications/visitorHistory.js +++ b/packages/rocketchat-livechat/server/publications/visitorHistory.js @@ -9,9 +9,8 @@ Meteor.publish('livechat:visitorHistory', function({ rid: roomId }) { const room = RocketChat.models.Rooms.findOneById(roomId); - const user = RocketChat.models.Users.findOneById(this.userId); - - if (room.usernames.indexOf(user.username) === -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, this.userId, { fields: { _id: 1 } }); + if (!subscription) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorHistory' })); } diff --git a/packages/rocketchat-markdown/parser/original/code.js b/packages/rocketchat-markdown/parser/original/code.js index dc69dcc9f5900b6376d482f8da2ddac56a138c8e..fadc54ac1dd16aa2e021abf60888a6717b03456f 100644 --- a/packages/rocketchat-markdown/parser/original/code.js +++ b/packages/rocketchat-markdown/parser/original/code.js @@ -8,12 +8,12 @@ import hljs from 'highlight.js'; const inlinecode = (message) => { // Support `text` - return message.html = message.html.replace(/(^|>|[ >_*~])\`([^`\r\n]+)\`([<_*~]|\B|\b|$)/gm, (match, p1, p2, p3) => { + return message.html = message.html.replace(/\`([^`\r\n]+)\`([<_*~]|\B|\b|$)/gm, (match, p1, p2) => { const token = ` =!=${ Random.id() }=!=`; message.tokens.push({ token, - text: `${ p1 }<span class=\"copyonly\">\`</span><span><code class=\"code-colors inline\">${ p2 }</code></span><span class=\"copyonly\">\`</span>${ p3 }`, + text: `<span class=\"copyonly\">\`</span><span><code class=\"code-colors inline\">${ p1 }</code></span><span class=\"copyonly\">\`</span>${ p2 }`, noHtml: match }); diff --git a/packages/rocketchat-markdown/tests/client.tests.js b/packages/rocketchat-markdown/tests/client.tests.js index fa376d6bb03dbafe71a8ef63c69679ea63d95ca0..ef30feab98549b2c1f96246fa2c9e76a4ad1adde 100644 --- a/packages/rocketchat-markdown/tests/client.tests.js +++ b/packages/rocketchat-markdown/tests/client.tests.js @@ -200,8 +200,8 @@ const inlinecode = { 'End `code`': `End ${ inlinecodeWrapper('code') }`, 'Middle `code` middle': `Middle ${ inlinecodeWrapper('code') } middle`, '`code`begin': `${ inlinecodeWrapper('code') }begin`, - 'End`code`': 'End`code`', - 'Middle`code`middle': 'Middle`code`middle' + 'End`code`': `End${ inlinecodeWrapper('code') }`, + 'Middle`code`middle': `Middle${ inlinecodeWrapper('code') }middle` }; const code = { diff --git a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css index 705008a24cf0ca312e24e0ec0c75629323cadccf..446b8b41b6924d89d9a7a33291e8855b9f444042 100644 --- a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css +++ b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css @@ -87,6 +87,7 @@ html.rtl .attachment { } & .attachment-fields { + display: flex; margin-top: 4px; & .attachment-field { @@ -103,10 +104,6 @@ html.rtl .attachment { } } - & .attachment-field ~ .attachment-field { - margin-top: 8px; - } - & .attachment-thumb { padding-top: 5px; padding-right: 10px; diff --git a/packages/rocketchat-message-pin/client/actionButton.js b/packages/rocketchat-message-pin/client/actionButton.js index 407904b6dfe2bf4bd2957f6ea548d72b935e5815..02c16203be3f1c3fdb7d0bdee66e7d46433f6a43 100644 --- a/packages/rocketchat-message-pin/client/actionButton.js +++ b/packages/rocketchat-message-pin/client/actionButton.js @@ -16,7 +16,7 @@ Meteor.startup(function() { }); }, condition(message) { - if (!RocketChat.settings.get('Message_AllowPinning') || message.pinned || !RocketChat.models.Subscriptions.findOne({ rid: message.rid }, {fields: {_id: 1}})) { + if (!RocketChat.settings.get('Message_AllowPinning') || message.pinned || !RocketChat.models.Subscriptions.findOne({ rid: message.rid }, { fields: { _id: 1 } })) { return false; } @@ -41,7 +41,7 @@ Meteor.startup(function() { }); }, condition(message) { - if (!RocketChat.settings.get('Message_AllowPinning') || !message.pinned || !RocketChat.models.Subscriptions.findOne({ rid: message.rid }, {fields: {_id: 1}})) { + if (!RocketChat.settings.get('Message_AllowPinning') || !message.pinned || !RocketChat.models.Subscriptions.findOne({ rid: message.rid }, { fields: { _id: 1 } })) { return false; } @@ -64,7 +64,7 @@ Meteor.startup(function() { return RoomHistoryManager.getSurroundingMessages(message, 50); }, condition(message) { - if (!RocketChat.models.Subscriptions.findOne({ rid: message.rid }, {fields: {_id: 1}})) { + if (!RocketChat.models.Subscriptions.findOne({ rid: message.rid }, { fields: { _id: 1 } })) { return false; } return true; @@ -85,7 +85,7 @@ Meteor.startup(function() { toastr.success(TAPi18n.__('Copied')); }, condition(message) { - if (!RocketChat.models.Subscriptions.findOne({ rid: message.rid }, {fields: {_id: 1}})) { + if (!RocketChat.models.Subscriptions.findOne({ rid: message.rid }, { fields: { _id: 1 } })) { return false; } return true; diff --git a/packages/rocketchat-message-pin/server/pinMessage.js b/packages/rocketchat-message-pin/server/pinMessage.js index 97cd6cdd5b1e850ab20da2f63f2e0c587a7a8ed7..5be88399d572b0e32bcb3f44a486b8512aa10806 100644 --- a/packages/rocketchat-message-pin/server/pinMessage.js +++ b/packages/rocketchat-message-pin/server/pinMessage.js @@ -33,8 +33,8 @@ Meteor.methods({ }); } - const room = RocketChat.models.Rooms.findOneById(message.rid); - if (Array.isArray(room.usernames) && room.usernames.indexOf(Meteor.user().username) === -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } }); + if (!subscription) { return false; } @@ -108,9 +108,8 @@ Meteor.methods({ }); } - const room = RocketChat.models.Rooms.findOneById(message.rid); - - if (Array.isArray(room.usernames) && room.usernames.indexOf(Meteor.user().username) === -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } }); + if (!subscription) { return false; } diff --git a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js index 6299c81d0b991496e31a3ce9d7047e2d760db86d..d4f590c1c65a0df2efc8f7f65b381265dcbcbb7c 100644 --- a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js +++ b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js @@ -1,6 +1,6 @@ Meteor.methods({ snippetMessage(message, filename) { - if ((typeof Meteor.userId() === 'undefined') || (Meteor.userId() === null)) { + if (Meteor.userId() == null) { //noinspection JSUnresolvedFunction throw new Meteor.Error('error-invalid-user', 'Invalid user', {method: 'snippetMessage'}); @@ -12,7 +12,8 @@ Meteor.methods({ return false; } - if (Array.isArray(room.usernames) && (room.usernames.indexOf(Meteor.user().username) === -1)) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } }); + if (!subscription) { return false; } diff --git a/packages/rocketchat-message-star/server/starMessage.js b/packages/rocketchat-message-star/server/starMessage.js index cb8b99ad9eef65903408c61622380ab1ab466d22..230c5af85e2d5bda110f5980759b3715272902d8 100644 --- a/packages/rocketchat-message-star/server/starMessage.js +++ b/packages/rocketchat-message-star/server/starMessage.js @@ -13,12 +13,11 @@ Meteor.methods({ }); } - const room = RocketChat.models.Rooms.findOneById(message.rid); - if (Array.isArray(room.usernames) && room.usernames.indexOf(Meteor.user().username) === -1) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } }); + if (!subscription) { return false; } return RocketChat.models.Messages.updateUserStarById(message._id, Meteor.userId(), message.starred); } }); - diff --git a/packages/rocketchat-migrations/migrations.js b/packages/rocketchat-migrations/migrations.js index bc72ae3b83003674634a8b293b9f9c20eaca6170..ab31856b9233accbf43beccae81aade08f57ae0f 100644 --- a/packages/rocketchat-migrations/migrations.js +++ b/packages/rocketchat-migrations/migrations.js @@ -272,9 +272,7 @@ Migrations._migrateTo = function(version, rerun) { log.info(`Running ${ direction }() on version ${ migration.version }${ maybeName() }`); try { - RocketChat.models._CacheControl.withValue(false, function() { - migration[direction](migration); - }); + migration[direction](migration); } catch (e) { console.log(makeABox([ 'ERROR! SERVER STOPPED', diff --git a/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.js b/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.js index ad7e3be83f72457f32c39313bb1d4b2f75c0e7a4..9e4c29460b902bb113b68b2de4b7b85d95ba330c 100644 --- a/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.js +++ b/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.js @@ -1,5 +1,4 @@ /*global OAuth2Server */ -import _ from 'underscore'; const oauth2server = new OAuth2Server({ accessTokensCollectionName: 'rocketchat_oauth_access_tokens', @@ -80,5 +79,5 @@ RocketChat.API.v1.addAuthMethod(function() { if (user == null) { return; } - return { user: _.omit(user, '$loki') }; + return { user }; }); diff --git a/packages/rocketchat-search/server/events/events.js b/packages/rocketchat-search/server/events/events.js index cb562bf4008b453b965b7b63332e0967e23e923c..723764d317a8032b98e9c9987e4b56104f9ffcce 100644 --- a/packages/rocketchat-search/server/events/events.js +++ b/packages/rocketchat-search/server/events/events.js @@ -33,20 +33,31 @@ RocketChat.callbacks.add('afterDeleteMessage', function(m) { * Listen to user and room changes via cursor */ -RocketChat.models.Users.on('changed', (type, user)=>{ - if (type === 'inserted' || type === 'updated') { - eventService.promoteEvent('user.save', user._id, user); - } - if (type === 'removed') { - eventService.promoteEvent('user.delete', user._id); + +RocketChat.models.Users.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + const user = data || RocketChat.models.Users.findOneById(id); + eventService.promoteEvent('user.save', id, user); + break; + + case 'removed': + eventService.promoteEvent('user.delete', id); + break; } }); -RocketChat.models.Rooms.on('changed', (type, room)=>{ - if (type === 'inserted' || type === 'updated') { - eventService.promoteEvent('room.save', room._id, room); - } - if (type === 'removed') { - eventService.promoteEvent('room.delete', room._id); +RocketChat.models.Rooms.on('change', ({clientAction, id, data}) => { + switch (clientAction) { + case 'updated': + case 'inserted': + const room = data || RocketChat.models.Rooms.findOneById(id); + eventService.promoteEvent('room.save', id, room); + break; + + case 'removed': + eventService.promoteEvent('room.delete', id); + break; } }); diff --git a/packages/rocketchat-slashcommands-hide/server/hide.js b/packages/rocketchat-slashcommands-hide/server/hide.js index 4ba92586b561c2b51cfa8273416d4dbc9c812c23..fade32a23d54d63059f53e1daa2d651f3ac41e45 100644 --- a/packages/rocketchat-slashcommands-hide/server/hide.js +++ b/packages/rocketchat-slashcommands-hide/server/hide.js @@ -32,7 +32,7 @@ function Hide(command, param, item) { }); } - if (!roomObject.usernames.includes(user.username)) { + if (!RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { fields: { _id: 1 } })) { return RocketChat.Notifications.notifyUser(user._id, 'message', { _id: Random.id(), rid: item.rid, diff --git a/packages/rocketchat-slashcommands-invite/server/server.js b/packages/rocketchat-slashcommands-invite/server/server.js index 2dcd6923c5d2fc326c4776e3871d767fe3e2308a..9e5f10c95de30a2f61c58ed381088cfe7f75896c 100644 --- a/packages/rocketchat-slashcommands-invite/server/server.js +++ b/packages/rocketchat-slashcommands-invite/server/server.js @@ -10,18 +10,19 @@ function Invite(command, params, item) { if (command !== 'invite' || !Match.test(params, String)) { return; } - let usernames = params.replace(/@/g, '').split(/[\s,]/).filter((a) => a !== ''); + const usernames = params.replace(/@/g, '').split(/[\s,]/).filter((a) => a !== ''); if (usernames.length === 0) { return; } - const users = Meteor.users.find({ + let users = Meteor.users.find({ username: { $in: usernames } }); - const currentUser = Meteor.users.findOne(Meteor.userId()); + const userId = Meteor.userId(); + const currentUser = Meteor.users.findOne(userId); if (users.count() === 0) { - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, @@ -32,24 +33,23 @@ function Invite(command, params, item) { }); return; } - usernames = usernames.filter(function(username) { - if (RocketChat.models.Rooms.findOneByIdContainingUsername(item.rid, username) == null) { + users = users.fetch().filter(function(user) { + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(item.rid, user._id, { fields: { _id: 1 } }); + if (subscription == null) { return true; } - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, msg: TAPi18n.__('Username_is_already_in_here', { postProcess: 'sprintf', - sprintf: [username] + sprintf: [user.username] }, currentUser.language) }); return false; }); - if (usernames.length === 0) { - return; - } + users.forEach(function(user) { try { @@ -59,14 +59,14 @@ function Invite(command, params, item) { }); } catch ({error}) { if (error === 'cant-invite-for-direct-room') { - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, msg: TAPi18n.__('Cannot_invite_users_to_direct_rooms', null, currentUser.language) }); } else { - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, diff --git a/packages/rocketchat-slashcommands-inviteall/server/server.js b/packages/rocketchat-slashcommands-inviteall/server/server.js index 133c8200ef18335022a47756c6312135a85085e7..400b955b5319b56b625889463aefef1d7a5e083e 100644 --- a/packages/rocketchat-slashcommands-inviteall/server/server.js +++ b/packages/rocketchat-slashcommands-inviteall/server/server.js @@ -16,13 +16,13 @@ function inviteAll(type) { if (!channel) { return; } - - const currentUser = Meteor.users.findOne(Meteor.userId()); + const userId = Meteor.userId(); + const currentUser = Meteor.users.findOne(userId); const baseChannel = type === 'to' ? RocketChat.models.Rooms.findOneById(item.rid) : RocketChat.models.Rooms.findOneByName(channel); const targetChannel = type === 'from' ? RocketChat.models.Rooms.findOneById(item.rid) : RocketChat.models.Rooms.findOneByName(channel); if (!baseChannel) { - return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + return RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date(), @@ -32,18 +32,19 @@ function inviteAll(type) { }, currentUser.language) }); } - const users = baseChannel.usernames || []; + const cursor = RocketChat.models.Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, { fields: { 'u.username': 1 } }); try { - if (users.length > RocketChat.settings.get('API_User_Limit')) { + if (cursor.count() > RocketChat.settings.get('API_User_Limit')) { throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', { method: 'addAllToRoom' }); } + const users = cursor.fetch().map(s => s.u.username); if (!targetChannel && ['c', 'p'].indexOf(baseChannel.t) > -1) { Meteor.call(baseChannel.t === 'c' ? 'createChannel' : 'createPrivateGroup', channel, users); - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date(), @@ -58,7 +59,7 @@ function inviteAll(type) { users }); } - return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + return RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date(), @@ -66,7 +67,7 @@ function inviteAll(type) { }); } catch (e) { const msg = e.error === 'cant-invite-for-direct-room' ? 'Cannot_invite_users_to_direct_rooms' : e.error; - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date(), diff --git a/packages/rocketchat-slashcommands-join/server/server.js b/packages/rocketchat-slashcommands-join/server/server.js index 1cabe1e1a307f8ad97e72b17a69d7392fe620f44..4289ec16bc048a8d4eef2a8aca06269bae7cd7a1 100644 --- a/packages/rocketchat-slashcommands-join/server/server.js +++ b/packages/rocketchat-slashcommands-join/server/server.js @@ -28,7 +28,9 @@ RocketChat.slashCommands.add('join', function Join(command, params, item) { }, user.language) }); } - if (room.usernames.includes(user.username)) { + + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { fields: { _id: 1 } }); + if (subscription) { throw new Meteor.Error('error-user-already-in-room', 'You are already in the channel', { method: 'slashCommands' }); diff --git a/packages/rocketchat-slashcommands-kick/server/server.js b/packages/rocketchat-slashcommands-kick/server/server.js index 8b6a298838cf6e9626fb2710d53c3ba10ebb4b52..4fffdc7af62d3cf3cdf5b01f670ef25be77c846c 100644 --- a/packages/rocketchat-slashcommands-kick/server/server.js +++ b/packages/rocketchat-slashcommands-kick/server/server.js @@ -9,11 +9,12 @@ const Kick = function(command, params, {rid}) { if (username === '') { return; } - const user = Meteor.users.findOne(Meteor.userId()); + const userId = Meteor.userId(); + const user = Meteor.users.findOne(userId); const kickedUser = RocketChat.models.Users.findOneByUsername(username); - const room = RocketChat.models.Rooms.findOneById(rid); + if (kickedUser == null) { - return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + return RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid, ts: new Date, @@ -23,8 +24,10 @@ const Kick = function(command, params, {rid}) { }, user.language) }); } - if ((room.usernames || []).includes(username) === false) { - return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, user._id, { fields: { _id: 1 } }); + if (!subscription) { + return RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid, ts: new Date, diff --git a/packages/rocketchat-slashcommands-mute/server/mute.js b/packages/rocketchat-slashcommands-mute/server/mute.js index 2c477609d7979d3eeaef87c3fca25440163f8a87..357aab64c09084b2923b73b33db2fcf5675fbfce 100644 --- a/packages/rocketchat-slashcommands-mute/server/mute.js +++ b/packages/rocketchat-slashcommands-mute/server/mute.js @@ -11,11 +11,11 @@ RocketChat.slashCommands.add('mute', function Mute(command, params, item) { if (username === '') { return; } - const user = Meteor.users.findOne(Meteor.userId()); + const userId = Meteor.userId(); + const user = Meteor.users.findOne(userId); const mutedUser = RocketChat.models.Users.findOneByUsername(username); - const room = RocketChat.models.Rooms.findOneById(item.rid); if (mutedUser == null) { - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, @@ -26,8 +26,10 @@ RocketChat.slashCommands.add('mute', function Mute(command, params, item) { }); return; } - if ((room.usernames || []).includes(username) === false) { - RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { + + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(item.rid, mutedUser._id, { fields: { _id: 1 } }); + if (!subscription) { + RocketChat.Notifications.notifyUser(userId, 'message', { _id: Random.id(), rid: item.rid, ts: new Date, diff --git a/packages/rocketchat-slashcommands-mute/server/unmute.js b/packages/rocketchat-slashcommands-mute/server/unmute.js index 1a864ab63f89279597cdc9745b92c7aad4322e94..8a471bef50ae97c50e848c3d42ddf148548cd671 100644 --- a/packages/rocketchat-slashcommands-mute/server/unmute.js +++ b/packages/rocketchat-slashcommands-mute/server/unmute.js @@ -14,7 +14,6 @@ RocketChat.slashCommands.add('unmute', function Unmute(command, params, item) { } const user = Meteor.users.findOne(Meteor.userId()); const unmutedUser = RocketChat.models.Users.findOneByUsername(username); - const room = RocketChat.models.Rooms.findOneById(item.rid); if (unmutedUser == null) { return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { _id: Random.id(), @@ -26,7 +25,9 @@ RocketChat.slashCommands.add('unmute', function Unmute(command, params, item) { }, user.language) }); } - if ((room.usernames || []).includes(username) === false) { + + const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(item.rid, unmutedUser._id, { fields: { _id: 1 } }); + if (!subscription) { return RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', { _id: Random.id(), rid: item.rid, diff --git a/packages/rocketchat-theme/client/imports/general/base_old.css b/packages/rocketchat-theme/client/imports/general/base_old.css index 5bf0d8970a5a9e2235895ca582794de7ef83fdcd..4747c4590d1bb60cef71bfa25eac139958d73f4e 100644 --- a/packages/rocketchat-theme/client/imports/general/base_old.css +++ b/packages/rocketchat-theme/client/imports/general/base_old.css @@ -2614,6 +2614,33 @@ .rc-old .message-popup-position { position: relative; + + .message-popup { + display: flex; + flex-direction: column; + align-items: stretch; + max-height: 20rem; + + .message-popup-items { + overflow-y: auto; + + .popup-slash-command { + display: flex; + flex-direction: row; + flex-wrap: wrap; + + &-format { + flex-grow: 1; + } + + &-description { + flex-grow: 2; + font-style: italic; + text-align: right; + } + } + } + } } .rc-old .message-popup-items.preview-items { @@ -2773,10 +2800,6 @@ border-radius: 10px; } -.rc-old .popup-slash-command-description { - float: right; -} - .messages-box { position: relative; diff --git a/packages/rocketchat-ui-account/client/accountProfile.html b/packages/rocketchat-ui-account/client/accountProfile.html index 32761147d9c1dfc0aa055dd711138d911bda888f..7e027d4e3910dea171e3a5124a6f46fde7bca86d 100644 --- a/packages/rocketchat-ui-account/client/accountProfile.html +++ b/packages/rocketchat-ui-account/client/accountProfile.html @@ -33,40 +33,40 @@ {{/unless}} </div> {{#if allowAvatarChange}} - {{#if suggestions.ready}} - <div class="rc-select-avatar__list"> - <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-initials" aria-label="{{_ "initials_avatar" }}"> - {{> avatar username=initialsUsername }} - </div> - <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-upload" aria-label="{{_ "Upload_user_avatar" }}"> - <label class="rc-select-avatar__upload-label avatar" for="upload-avatar"> - {{> icon block="rc-select-avatar__upload-icon" icon="upload"}} - </label> - <input type="file" name="" value="" id="upload-avatar" style="display:none;" accept="image/x-png,image/gif,image/jpeg"> - </div> - <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-url {{selectUrl}}" aria-label="{{_ "Use_url_for_avatar" }}"> - <label class="rc-select-avatar__upload-label avatar"> - {{> icon block="rc-select-avatar__upload-icon" icon="permalink"}} - </label> - </div> + <div class="rc-select-avatar__list"> + <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-initials" aria-label="{{_ "initials_avatar" }}"> + {{> avatar username=initialsUsername }} + </div> + <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-upload" aria-label="{{_ "Upload_user_avatar" }}"> + <label class="rc-select-avatar__upload-label avatar" for="upload-avatar"> + {{> icon block="rc-select-avatar__upload-icon" icon="upload"}} + </label> + <input type="file" name="" value="" id="upload-avatar" style="display:none;" accept="image/x-png,image/gif,image/jpeg"> + </div> + <div class="rc-select-avatar__list-item rc-tooltip js-select-avatar-url {{selectUrl}}" aria-label="{{_ "Use_url_for_avatar" }}"> + <label class="rc-select-avatar__upload-label avatar"> + {{> icon block="rc-select-avatar__upload-icon" icon="permalink"}} + </label> + </div> + {{#if suggestions.ready}} {{#each service in services}} {{ > avatarService service}} {{/each}} - <div class="rc-input"> - - <label class="rc-input__label"> - <div class="rc-input__title">{{_ "Use_url_for_avatar"}}</div> - <div class="rc-input__wrapper"> - <input name="avatar_url" class="rc-input__element js-avatar-url-input" placeholder="{{_ "Use_url_for_avatar"}}"> - </div> - </label> + {{else}} + <div class="rc-select-avatar__loading"> + {{_ "Loading_suggestion"}} </div> + {{/if}} + <div class="rc-input"> + + <label class="rc-input__label"> + <div class="rc-input__title">{{_ "Use_url_for_avatar"}}</div> + <div class="rc-input__wrapper"> + <input name="avatar_url" class="rc-input__element js-avatar-url-input" placeholder="{{_ "Use_url_for_avatar"}}"> + </div> + </label> </div> - {{else}} - <div class="rc-select-avatar__loading"> - {{_ "Loading_suggestion"}} - </div> - {{/if}} + </div> {{/if}} </div> </div> diff --git a/packages/rocketchat-ui-master/public/icons.svg b/packages/rocketchat-ui-master/public/icons.svg index f0299fa6c5ff540e465a18fa49724ba71b625d09..eed97022f6762c62815cd6525a0529f21dfec12a 100644 --- a/packages/rocketchat-ui-master/public/icons.svg +++ b/packages/rocketchat-ui-master/public/icons.svg @@ -1,456 +1,475 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none"> - <symbol viewBox="0 0 20 20" id="icon-add-reaction"> - <g fill="none" fill-rule="evenodd"> - <g transform="translate(3 3)"> - <circle fill="currentColor" cx="9" cy="5" r="1" /> - <circle fill="currentColor" cx="5" cy="5" r="1" /> - <path d="M7 0a7 7 0 1 0 0 14 7 7 0 0 0 7-7M4.172 9.328a4 4 0 0 0 5.656 0" stroke="currentColor" stroke-width="1.5" /> - </g> - <path d="M16.2 1.2v5.2m-2.6-2.6h5.2" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-at"> - <path d="M256 8C118.941 8 8 118.919 8 256c0 137.058 110.919 248 248 248 52.925 0 104.68-17.078 147.092-48.319 5.501-4.052 6.423-11.924 2.095-17.211l-5.074-6.198c-4.018-4.909-11.193-5.883-16.307-2.129C346.93 457.208 301.974 472 256 472c-119.373 0-216-96.607-216-216 0-119.375 96.607-216 216-216 118.445 0 216 80.024 216 200 0 72.873-52.819 108.241-116.065 108.241-19.734 0-23.695-10.816-19.503-33.868l32.07-164.071c1.449-7.411-4.226-14.302-11.777-14.302h-12.421a12 12 0 0 0-11.781 9.718c-2.294 11.846-2.86 13.464-3.861 25.647-11.729-27.078-38.639-43.023-73.375-43.023-68.044 0-133.176 62.95-133.176 157.027 0 61.587 33.915 98.354 90.723 98.354 39.729 0 70.601-24.278 86.633-46.982-1.211 27.786 17.455 42.213 45.975 42.213C453.089 378.954 504 321.729 504 240 504 103.814 393.863 8 256 8zm-37.92 342.627c-36.681 0-58.58-25.108-58.58-67.166 0-74.69 50.765-121.545 97.217-121.545 38.857 0 58.102 27.79 58.102 65.735 0 58.133-38.369 122.976-96.739 122.976z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-back"> - <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> - <path d="M7.5 15.06L2.44 10 7.5 4.94" /> - <path d="M3.333 10h11.67c.918 0 1.664.74 1.664 1.667v2.916" /> - </g> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-bell"> - <path d="M433.884 366.059C411.634 343.809 384 316.118 384 208c0-79.394-57.831-145.269-133.663-157.83A31.845 31.845 0 0 0 256 32c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 6.75 2.095 13.008 5.663 18.17C121.831 62.731 64 128.606 64 208c0 108.118-27.643 135.809-49.893 158.059C-16.042 396.208 5.325 448 48.048 448H160c0 35.29 28.71 64 64 64s64-28.71 64-64h111.943c42.638 0 64.151-51.731 33.941-81.941zM224 480c-17.645 0-32-14.355-32-32h64c0 17.645-14.355 32-32 32zm175.943-64H48.048c-14.223 0-21.331-17.296-11.314-27.314C71.585 353.836 96 314.825 96 208c0-70.741 57.249-128 128-128 70.74 0 128 57.249 128 128 0 106.419 24.206 145.635 59.257 180.686C421.314 398.744 414.11 416 399.943 416z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-bold"> - <path d="M10.627 16.25H6.16V4.8h4.38c1.052 0 1.88.257 2.483.77.603.513.905 1.209.905 2.087a2.49 2.49 0 0 1-.576 1.603c-.383.476-.85.764-1.4.865v.126c.783.101 1.404.401 1.865.901.46.5.69 1.128.69 1.884 0 1.006-.343 1.792-1.028 2.361-.685.569-1.636.853-2.852.853zM7.587 6.062v3.674h2.286c.868 0 1.52-.154 1.956-.46.436-.307.655-.765.655-1.373 0-.582-.198-1.034-.592-1.357-.394-.323-.945-.484-1.654-.484h-2.65zm0 8.926h2.826c.862 0 1.515-.172 1.96-.515.444-.344.666-.847.666-1.508s-.231-1.16-.694-1.496c-.463-.335-1.152-.503-2.067-.503h-2.69v4.022z" fill-rule="evenodd" /> - </symbol> - <symbol viewBox="0 0 28 28" id="icon-chat"> - <path d="M11 6c-4.875 0-9 2.75-9 6 0 1.719 1.156 3.375 3.156 4.531l1.516 0.875-0.547 1.313c0.328-0.187 0.656-0.391 0.969-0.609l0.688-0.484 0.828 0.156c0.781 0.141 1.578 0.219 2.391 0.219 4.875 0 9-2.75 9-6s-4.125-6-9-6zM11 4c6.078 0 11 3.578 11 8s-4.922 8-11 8c-0.953 0-1.875-0.094-2.75-0.25-1.297 0.922-2.766 1.594-4.344 2-0.422 0.109-0.875 0.187-1.344 0.25h-0.047c-0.234 0-0.453-0.187-0.5-0.453v0c-0.063-0.297 0.141-0.484 0.313-0.688 0.609-0.688 1.297-1.297 1.828-2.594-2.531-1.469-4.156-3.734-4.156-6.266 0-4.422 4.922-8 11-8zM23.844 22.266c0.531 1.297 1.219 1.906 1.828 2.594 0.172 0.203 0.375 0.391 0.313 0.688v0c-0.063 0.281-0.297 0.484-0.547 0.453-0.469-0.063-0.922-0.141-1.344-0.25-1.578-0.406-3.047-1.078-4.344-2-0.875 0.156-1.797 0.25-2.75 0.25-2.828 0-5.422-0.781-7.375-2.063 0.453 0.031 0.922 0.063 1.375 0.063 3.359 0 6.531-0.969 8.953-2.719 2.609-1.906 4.047-4.484 4.047-7.281 0-0.812-0.125-1.609-0.359-2.375 2.641 1.453 4.359 3.766 4.359 6.375 0 2.547-1.625 4.797-4.156 6.266z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-clip"> - <path d="M149.106 512c-33.076 0-66.153-12.59-91.333-37.771-50.364-50.361-50.364-132.305-.002-182.665L319.842 29.498c39.331-39.331 103.328-39.331 142.66 0 39.331 39.332 39.331 103.327 0 142.657l-222.63 222.626c-28.297 28.301-74.347 28.303-102.65 0-28.3-28.301-28.3-74.349 0-102.649l170.301-170.298c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971l-170.3 170.297c-15.821 15.821-15.821 41.563.001 57.385 15.821 15.82 41.564 15.82 57.385 0l222.63-222.626c26.851-26.851 26.851-70.541 0-97.394-26.855-26.851-70.544-26.849-97.395 0L80.404 314.196c-37.882 37.882-37.882 99.519 0 137.401 37.884 37.881 99.523 37.882 137.404.001l217.743-217.739c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971L240.44 474.229C215.26 499.41 182.183 512 149.106 512z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-code"> - <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> - <path d="M5.833 13.88L1.953 10l3.88-3.88" /> - <path d="M11.661 3.8l-3.37 12.576L11.662 3.8z" stroke-linecap="square" /> - <path d="M14.167 6.12l3.88 3.88-3.88 3.88" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-cog"> - <g transform="translate(1.667 2.5)" stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> - <circle cx="8.333" cy="7.5" r="2.083" /> - <path d="M6.47 14.883l1.863-1.966 1.864 1.966a7.568 7.568 0 0 0 2.04-.845l-.074-2.708 2.708.073a7.568 7.568 0 0 0 .846-2.04L13.75 7.5l1.967-1.864a7.568 7.568 0 0 0-.846-2.04l-2.708.074.073-2.708a7.568 7.568 0 0 0-2.04-.845L8.334 2.083 6.47.117a7.568 7.568 0 0 0-2.04.845l.073 2.708-2.707-.073a7.568 7.568 0 0 0-.846 2.04L2.917 7.5.95 9.364a7.56 7.56 0 0 0 .846 2.04l2.707-.074-.073 2.708a7.568 7.568 0 0 0 2.04.845z" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-computer"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M3 4h14v9H3z" /> - <path d="M7.5 16h5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-copy"> - <g fill="none" fill-rule="evenodd"> - <path d="M0 0h20v20H0z" /> - <path stroke="currentColor" stroke-width="1.5" d="M7 5h5l5 5v8H7z" /> - <path stroke="currentColor" stroke-width="1.5" d="M6.959 15H3V2h5l3.043 3.043M17 10h-5V5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-customize"> - <g transform="translate(3 3)" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M0 7h8m4 0h2M0 2h2m3.996 0H14M0 12h3m4 0h7" stroke-linecap="square" /> - <circle cx="4" cy="2" r="2" /> - <circle cx="10" cy="7" r="2" /> - <circle cx="5" cy="12" r="2" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-edit"> - <g stroke-width="1.2" stroke="currentColor" fill="none" fill-rule="evenodd"> - <path d="M12.73 3.412c.78-.78 2.044-.78 2.83.006l.7.7c.783.783.788 2.047.005 2.83l-8.901 8.901-4.596 1.06 1.06-4.595 8.902-8.902z" /> - <path d="M11.24 5.609l2.829 2.828" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-emoji"> - <g fill="none" fill-rule="evenodd"> - <path d="M0 0h20v20H0z" /> - <circle fill="currentColor" cx="12" cy="8" r="1" /> - <circle fill="currentColor" cx="8" cy="8" r="1" /> - <circle stroke="currentColor" stroke-width="1.5" cx="10" cy="10" r="7" /> - <path d="M7.172 12.328a4 4 0 0 0 5.656 0" stroke="currentColor" stroke-width="1.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-facebook"> - <path d="M19 1.992v16.012c0 .55-.446.992-.992.992h-4.589v-6.967h2.339l.35-2.716h-2.693V7.577c0-.787.217-1.322 1.346-1.322H16.2v-2.43a19.082 19.082 0 0 0-2.098-.109c-2.073 0-3.495 1.266-3.495 3.592v2.005H8.26v2.716h2.347V19H1.992A.995.995 0 0 1 1 18.008V1.992C1 1.446 1.446 1 1.992 1h16.012c.55 0 .996.446.996.992z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-github"> - <path d="M7.019 15.128c0 .072-.083.13-.189.13-.12.011-.203-.047-.203-.13 0-.073.084-.13.189-.13.109-.012.203.046.203.13zm-1.128-.163c-.026.072.047.156.156.177.094.037.203 0 .225-.072.021-.073-.048-.156-.156-.189-.095-.025-.2.011-.225.084zm1.603-.062c-.105.025-.177.094-.167.178.011.072.106.12.214.094.106-.025.178-.094.167-.167-.01-.069-.108-.116-.214-.105zM9.882 1C4.849 1 1 4.82 1 9.853c0 4.023 2.532 7.466 6.15 8.678.464.083.627-.203.627-.439 0-.225-.01-1.466-.01-2.228 0 0-2.54.545-3.074-1.08 0 0-.413-1.057-1.008-1.329 0 0-.831-.57.058-.558 0 0 .903.072 1.4.936.795 1.4 2.126.997 2.645.758.084-.58.32-.983.58-1.223-2.027-.225-4.074-.519-4.074-4.009 0-.998.276-1.498.857-2.137-.095-.236-.403-1.208.094-2.463.758-.236 2.503.98 2.503.98a8.524 8.524 0 0 1 2.279-.31 8.49 8.49 0 0 1 2.278.31s1.745-1.22 2.504-.98c.497 1.259.188 2.227.094 2.463.58.642.936 1.143.936 2.137 0 3.501-2.137 3.78-4.165 4.01.334.286.617.83.617 1.683 0 1.222-.011 2.735-.011 3.033 0 .236.167.522.627.439 3.629-1.205 6.088-4.648 6.088-8.671C18.995 4.82 14.914 1 9.882 1zM4.527 13.513c-.048.037-.037.12.025.189.058.058.141.083.189.036.047-.036.036-.12-.026-.188-.058-.058-.141-.084-.188-.037zm-.392-.294c-.026.048.01.106.083.142.058.036.13.025.156-.025.026-.048-.01-.106-.083-.142-.073-.022-.13-.01-.156.025zm1.175 1.292c-.058.047-.036.156.047.225.084.083.189.094.236.036.047-.047.026-.156-.047-.225-.08-.083-.189-.094-.236-.036zm-.413-.533c-.058.036-.058.13 0 .214.058.083.156.12.203.083.058-.047.058-.141 0-.225-.051-.083-.145-.12-.203-.072z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-gitlab"> - <path d="M2.047 8.387l7.95 10.333-8.713-6.468a.701.701 0 0 1-.251-.773l1.014-3.092zm2.65-6.144a.352.352 0 0 0-.663 0L2.047 8.387h4.638L4.697 2.243zm1.988 6.144L9.998 18.72 13.31 8.387H6.685zm12.278 3.092l-1.014-3.092L9.998 18.72l8.714-6.468a.701.701 0 0 0 .25-.773zM15.96 2.243a.352.352 0 0 0-.663 0L13.31 8.387h4.638L15.96 2.243z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-google"> - <path d="M18.71 10.21c0 5.136-3.517 8.79-8.71 8.79a8.99 8.99 0 0 1-9-9c0-4.979 4.021-9 9-9 2.424 0 4.464.89 6.035 2.355l-2.45 2.355C10.381 2.62 4.422 4.941 4.422 10c0 3.14 2.508 5.683 5.578 5.683 3.564 0 4.9-2.555 5.11-3.88H10V8.709h8.568c.084.461.142.904.142 1.502z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-hashtag"> - <path d="M7.5 4.167v11.666m5-11.666v11.666M4.167 7.5h11.666m-11.666 5h11.666" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="square" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-help"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <circle cx="10" cy="10" r="8.333" /> - <circle cx="10" cy="10" r="4.167" /> - <path d="M10 2.083v3.434m0 9.066v3.434m3.958-14.873l-1.716 2.973M7.708 13.97l-1.716 2.973M17.912 10.276l-3.431-.12M5.42 9.84l-3.432-.12m14.726 4.475l-2.912-1.82M6.113 7.571l-2.912-1.82" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-hubot"> - <path d="M4.858 7.143A1.29 1.29 0 0 0 3.572 8.43v2.572a1.29 1.29 0 0 0 1.286 1.286h10.286A1.29 1.29 0 0 0 16.43 11V8.429a1.29 1.29 0 0 0-1.286-1.286H4.858zm10.286 2.25l-1.607 1.608h-1.929l-1.607-1.607L8.394 11h-1.93L4.859 9.394v-.965h.964l1.607 1.607L9.036 8.43h1.93l1.607 1.607L14.18 8.43h.964v.965zm-7.715 4.18h5.144v1.285H7.429v-1.285zM10.001 2C5.038 2 1 5.742 1 10.358v5.786a1.29 1.29 0 0 0 1.286 1.286h15.43a1.29 1.29 0 0 0 1.286-1.286v-5.786C19.002 5.742 14.964 2 10 2zm7.715 14.144H2.286v-5.786C2.286 6.385 5.68 3.17 10 3.17s7.715 3.215 7.715 7.188v5.786z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-info-circled"> - <path d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-italic"> - <path d="M9.357 16.25h1.365V7.697H9.357v8.553zm.683-10.291a.91.91 0 0 0 .674-.282.922.922 0 0 0 .278-.67.918.918 0 0 0-.278-.675.918.918 0 0 0-.674-.277.922.922 0 0 0-.67.277.912.912 0 0 0-.282.675c0 .259.093.482.281.67a.916.916 0 0 0 .67.282z" fill-rule="evenodd" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-key"> - <g transform="matrix(-1 0 0 1 18 2)" fill="none" fill-rule="evenodd"> - <path d="M5.714 5.143c0 .944.15 1.654.448 2.13L.646 12.615 0 14.709 1.292 16l2.088-.642.41-.409v-1.475h1.475l.63-.63v-1.476h1.476l1.566-1.572c.582.327 1.223.49 1.92.49a5.143 5.143 0 0 0 5.141-5.289c-.075-2.684-2.312-4.92-4.995-4.995a5.143 5.143 0 0 0-5.288 5.14z" stroke="currentColor" stroke-width="1.5" /> - <circle fill="currentColor" cx="11.5" cy="4.5" r="1.5" /> - </g> - </symbol> - <symbol viewBox="0 0 576 512" id="icon-keyboard"> - <path d="M528 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h480c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.823-7.177 16-16 16H48c-8.823 0-16-7.177-16-16V112c0-8.823 7.177-16 16-16h480c8.823 0 16 7.177 16 16v288zM168 268v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-336 80v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm384 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zM120 188v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-96 152v-8c0-6.627-5.373-12-12-12H180c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h216c6.627 0 12-5.373 12-12z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-language"> - <path d="M18.778 11.018c-.562-1.536-2.13-2.453-4.195-2.453-.081 0-.159.001-.236.005l-.014-1.857 3.172-.546c.104-.017.12-.107.098-.208-.02-.1-.15-.795-.169-.878-.024-.118-.092-.115-.186-.098-.094.016-2.925.415-2.925.415l-.012-1.87c0-.113-.062-.143-.175-.141l-.922.014c-.095.002-.148.044-.146.134l.03 2.076s-2.755.474-2.83.489c-.075.012-.153.047-.136.128.017.081.171.985.187 1.055.017.072.065.116.17.096l2.631-.453.032 1.816a4.23 4.23 0 0 0-2.03 1.173 3.915 3.915 0 0 0-1.087 2.666c0 1.428.874 2.276 2.095 2.426 2.846.348 4.607-2.754 5.192-4.244.988 1.356.23 3.919-1.884 5.382-.039.026-.088.116-.03.187l.557.68c.072.086.186.053.23.02 2.26-1.556 3.295-4.063 2.583-6.014zm-6.648 2.87c-.87-.11-.85-.823-.85-1.308 0-.696.295-1.422.79-1.94a2.889 2.889 0 0 1 1.105-.72l.074 3.85c-.347.117-.72.166-1.119.117zm2.185-.498l.041-3.699c.076-.003.15-.009.227-.009.695 0 1.344.131 1.696.325.352.196-.92 2.442-1.964 3.383zM6.26 6.488a.176.176 0 0 0-.177-.13H4.328a.175.175 0 0 0-.174.13l-3.147 9.936c-.015.046-.01.069.056.069h1.56c.067 0 .089-.02.102-.065l.907-2.986H6.78l.907 2.986c.014.044.035.065.102.065h1.56c.065 0 .07-.023.056-.069-.012-.045-2.775-8.767-3.144-9.936zm-2.357 5.687L5.206 7.45l1.302 4.725H3.903z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-linkedin"> - <path d="M17.714 1H2.282C1.575 1 1 1.583 1 2.298v15.404C1 18.417 1.575 19 2.282 19h15.432c.707 0 1.286-.583 1.286-1.298V2.298C19 1.583 18.421 1 17.714 1zM6.44 16.429H3.772v-8.59h2.672v8.59H6.44zM5.106 6.665a1.548 1.548 0 0 1 0-3.094 1.55 1.55 0 0 1 1.547 1.547c0 .856-.69 1.547-1.547 1.547zm11.335 9.764h-2.668V12.25c0-.996-.02-2.278-1.386-2.278-1.39 0-1.604 1.085-1.604 2.206v4.25H8.116v-8.59h2.559v1.174h.036c.358-.675 1.23-1.387 2.527-1.387 2.7 0 3.203 1.78 3.203 4.095v4.709z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-livechat"> - <path d="M15.274 6.595c-.39-2.5-2.55-4.361-5.245-4.361-2.98 0-5.314 2.272-5.314 5.173v.262c-.001.016-.005.032-.005.05v5.121a2.577 2.577 0 0 1-2.397-2.563c0-.933.506-1.793 1.321-2.247a.572.572 0 0 0-.556-.999 3.717 3.717 0 0 0-1.908 3.246 3.718 3.718 0 0 0 3.714 3.714c.127 0 .256-.01.385-.023h.012c.019 0 .036-.003.054-.004h.004a.57.57 0 0 0 .514-.567V8.344c.002-.017.005-.033.005-.05v-.887c0-2.298 1.793-4.03 4.171-4.03S14.2 5.108 14.2 7.406v.237c-.003.024-.007.049-.007.075v5.519c-.001.015-.004.03-.004.044v1.286c0 .878-.614 1.491-1.492 1.491h-.77l-.016.002a1.49 1.49 0 0 0-1.388-.96h-.98a1.493 1.493 0 0 0-1.49 1.491c0 .823.668 1.49 1.49 1.491h.98c.606 0 1.126-.365 1.36-.886.015.002.029.006.045.006h.77c1.526 0 2.633-1.109 2.633-2.635v-.559a3.716 3.716 0 0 0 3.544-3.706 3.716 3.716 0 0 0-3.601-3.708zM10.523 16.94h-.98a.353.353 0 0 1-.347-.348c0-.189.16-.348.348-.348h.979c.188 0 .348.16.348.348 0 .189-.16.348-.348.348zm4.813-4.074V8.369c.003-.025.007-.05.007-.075v-.553a2.572 2.572 0 0 1 2.39 2.562c0 1.36-1.06 2.473-2.397 2.563z" fill-rule="nonzero" fill="currentColor" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-lock"> - <path d="M5 8h10v9H5zm2-3c0-1.657 1.347-3 3-3 1.657 0 3 1.347 3 3v3H7V5z" stroke="currentColor" stroke-width="1.5" fill="none" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-magnifier"> - <path d="M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395 312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5 0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17 0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208 32s176 78.7 176 176-78.7 176-176 176z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-mail"> - <path d="M464 64H48C21.5 64 0 85.5 0 112v288c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM48 96h416c8.8 0 16 7.2 16 16v41.4c-21.9 18.5-53.2 44-150.6 121.3-16.9 13.4-50.2 45.7-73.4 45.3-23.2.4-56.6-31.9-73.4-45.3C85.2 197.4 53.9 171.9 32 153.4V112c0-8.8 7.2-16 16-16zm416 320H48c-8.8 0-16-7.2-16-16V195c22.8 18.7 58.8 47.6 130.7 104.7 20.5 16.4 56.7 52.5 93.3 52.3 36.4.3 72.3-35.5 93.3-52.3 71.9-57.1 107.9-86 130.7-104.7v205c0 8.8-7.2 16-16 16z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-map-pin"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M10 17s5-4.15 5-9.027C15 5.226 12.761 3 10 3S5 5.226 5 7.973C5 12.85 10 17 10 17z" /> - <circle cx="10" cy="8" r="2" /> - </g> - </symbol> - <symbol viewBox="0 0 64 512" id="icon-menu"> - <path d="M32 224c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM0 136c0 17.7 14.3 32 32 32s32-14.3 32-32-14.3-32-32-32-32 14.3-32 32zm0 240c0 17.7 14.3 32 32 32s32-14.3 32-32-14.3-32-32-32-32 14.3-32 32z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-message"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M11 18c1.219 0 7 .127 8-2 .506-1.076-2.891-1.076-2-3 .391-.943 1-1.915 1-3a8 8 0 1 0-16 0c0 4.418 3.582 8 9 8z" /> - <path d="M6.5 8.5h6.083m-6.083 3h7.083" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-mic"> - <g fill="none" fill-rule="evenodd"> - <path d="M10 2.75A2.75 2.75 0 0 0 7.25 5.5v3a2.75 2.75 0 0 0 5.5 0v-3A2.75 2.75 0 0 0 10 2.75zM10 14v3" stroke="currentColor" stroke-width="1.5" /> - <path fill="currentColor" d="M7 17h6v1H7z" /> - <path d="M5 8c.049 4 1.716 6 5 6s4.951-2 5-6" stroke="currentColor" stroke-width="1.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-multi-line"> - <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> - <path d="M12.5 5h5v6.25H5" /> - <path d="M8.17 15.714l-4.42-4.42 4.42-4.419" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-mute"> - <g fill="none" fill-rule="evenodd"> - <path d="M9.47 2.265A2.735 2.735 0 0 0 6.737 5v3.53a2.735 2.735 0 0 0 5.47 0V5A2.735 2.735 0 0 0 9.47 2.265zm0 11.559v3.529-3.53z" stroke="currentColor" stroke-width="1.5" /> - <path fill="currentColor" fill-rule="nonzero" d="M6.824 16.47h5.294v1.324H6.824z" /> - <path d="M4.176 8.53c.052 3.529 1.817 5.294 5.295 5.294 3.477 0 5.242-1.765 5.294-5.294" stroke="currentColor" stroke-width="1.5" /> - <path d="M18.238 2.353L1.529 16.676" stroke="currentColor" stroke-width="2.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-permalink"> - <path d="M9.548 14.23l-2.651 2.652a2.676 2.676 0 0 1-3.78 0 2.676 2.676 0 0 1 0-3.78L6.91 9.311a2.677 2.677 0 0 1 3.781 0 .669.669 0 0 0 .945-.946 4.015 4.015 0 0 0-5.67 0l-3.792 3.792a4.014 4.014 0 0 0 0 5.67 4.014 4.014 0 0 0 5.67 0l2.65-2.65a.669.669 0 0 0-.945-.947zm8.28-12.057a4.014 4.014 0 0 0-5.67 0L9.506 4.824a.668.668 0 1 0 .946.945l2.651-2.651a2.676 2.676 0 0 1 3.78 0 2.676 2.676 0 0 1 0 3.78L13.09 10.69a2.678 2.678 0 0 1-3.781 0 .668.668 0 1 0-.945.945 4.015 4.015 0 0 0 5.67 0l3.793-3.792a4.014 4.014 0 0 0 0-5.67z" fill-rule="nonzero" fill="currentColor" /> - </symbol> - <symbol viewBox="0 0 384 512" id="icon-pin"> - <path d="M300.79 203.91L290.67 128H328c13.25 0 24-10.75 24-24V24c0-13.25-10.75-24-24-24H56C42.75 0 32 10.75 32 24v80c0 13.25 10.75 24 24 24h37.33l-10.12 75.91C34.938 231.494 0 278.443 0 335.24c0 8.84 7.16 16 16 16h160v120.779c0 .654.08 1.306.239 1.94l8 32c2.009 8.037 13.504 8.072 15.522 0l8-32a7.983 7.983 0 0 0 .239-1.94V351.24h160c8.84 0 16-7.16 16-16 0-56.797-34.938-103.746-83.21-131.33zM33.26 319.24c6.793-42.889 39.635-76.395 79.46-94.48L128 96H64V32h256v64h-64l15.28 128.76c40.011 18.17 72.694 51.761 79.46 94.48H33.26z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-plus"> - <path d="M10 5v10m5-5H5" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="square" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-queue"> - <path d="M9.98 11.894c-.1 0-.2-.025-.29-.077L1.493 7.161a.587.587 0 0 1-.01-1.017l8.238-4.899a.588.588 0 0 1 .6-.001l8.195 4.828a.588.588 0 0 1-.006 1.017l-8.237 4.727a.586.586 0 0 1-.293.078zM2.954 6.638l7.025 3.991 7.069-4.057-7.025-4.138-7.07 4.204z" /> - <path d="M9.98 15.172c-.1 0-.2-.025-.29-.076l-8.197-4.657a.588.588 0 1 1 .581-1.022l7.905 4.49 7.946-4.56a.588.588 0 1 1 .585 1.02l-8.237 4.727a.584.584 0 0 1-.293.078z" /> - <path d="M9.98 18.447c-.1 0-.2-.025-.29-.076l-8.197-4.657a.588.588 0 1 1 .581-1.022l7.905 4.49 7.946-4.56a.588.588 0 0 1 .585 1.02l-8.237 4.727a.584.584 0 0 1-.293.078z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-quote"> - <path d="M5.7 4.5a3.2 3.2 0 0 1 3.2 3.2c0 4.8-2.622 8-5.6 8 1.467-2.113 1.933-3.713 1.4-4.8a2.2 2.2 0 0 1-2.2-2.2v-1a3.2 3.2 0 0 1 3.2-3.2zm9 0a3.2 3.2 0 0 1 3.2 3.2c0 4.8-2.622 8-5.6 8 1.467-2.113 1.933-3.713 1.4-4.8a2.2 2.2 0 0 1-2.2-2.2v-1a3.2 3.2 0 0 1 3.2-3.2z" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linejoin="round" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-reload"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M5.905 14.322a6 6 0 1 0 0-8.485l-.873.873" /> - <path d="M9.981 7.417L5 6.98 5.436 2" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-send"> - <path d="M17.28 1.186L1.562 10.253a1.123 1.123 0 0 0 .134 2.01l4.919 2.035v3.58c0 1.059 1.326 1.518 1.99.711L10.734 16l4.434 1.832a1.122 1.122 0 0 0 1.537-.867L18.951 2.33c.144-.937-.863-1.61-1.67-1.144zM7.738 17.877v-3.116l1.912.79-1.912 2.326zm7.86-1.084l-7.236-2.99 7-8.273c.169-.197-.101-.463-.298-.295L6.105 12.87l-3.982-1.642 15.72-9.07-2.247 14.635z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-share"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M10 12.5V2.917" stroke-linecap="square" /> - <path d="M6.667 5.419l3.291-3.292L13.25 5.42m-.75 2.913H15V17.5H5V8.333h2.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-sign-out"> - <path d="M2.684 3h4.63c.232 0 .421.19.421.42v.282c0 .231-.19.42-.42.42H2.683a.563.563 0 0 0-.561.562v10.102c0 .309.252.562.56.562h4.631c.232 0 .421.189.421.42v.281c0 .232-.19.421-.42.421H2.683c-.93 0-1.684-.754-1.684-1.684V4.684A1.686 1.686 0 0 1 2.684 3zm9.787.684l-.25.25a.421.421 0 0 0 0 .595l4.63 4.61H7.034c-.231 0-.42.19-.42.42v.351c0 .232.189.421.42.421h9.819l-4.627 4.61a.421.421 0 0 0 0 .596l.25.249a.421.421 0 0 0 .595 0l5.77-5.753a.421.421 0 0 0 0-.596l-5.773-5.753a.421.421 0 0 0-.596 0z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-snippet"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M5.833 13.88L1.953 10l3.88-3.88" /> - <path d="M11.941 2.756L8.06 17.244" stroke-linecap="square" /> - <path d="M14.167 6.12l3.88 3.88-3.88 3.88" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-star"> - <path d="M10 15l-4.898 2.575.935-5.454-3.962-3.863 5.476-.796L10 2.5l2.45 4.962 5.475.796-3.962 3.863.935 5.454z" stroke-width="1.5" stroke="currentColor" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-strike"> - <g fill="none" fill-rule="evenodd"> - <path d="M5.775 13.259c.074.989.498 1.78 1.273 2.372.775.593 1.768.889 2.98.889 1.312 0 2.352-.311 3.122-.933.77-.621 1.155-1.458 1.155-2.51 0-.842-.258-1.511-.774-2.008-.516-.498-1.366-.894-2.551-1.19L9.782 9.56c-.788-.2-1.353-.442-1.694-.722a1.35 1.35 0 0 1-.512-1.095c0-.582.231-1.047.694-1.396.463-.35 1.075-.524 1.837-.524.714 0 1.3.165 1.758.496.457.33.736.79.837 1.377h1.436c-.058-.926-.46-1.685-1.206-2.278-.746-.592-1.672-.888-2.777-.888-1.212 0-2.189.3-2.932.9-.743.6-1.115 1.387-1.115 2.36 0 .815.235 1.467.706 1.957.471.489 1.225.866 2.262 1.13l1.468.381c.788.196 1.367.455 1.737.778.37.322.556.727.556 1.214 0 .566-.253 1.035-.758 1.408s-1.144.56-1.916.56c-.815 0-1.487-.178-2.016-.532-.529-.355-.838-.83-.928-1.428H5.775z" fill="currentColor" /> - <path d="M4.375 10h11.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 640 512" id="icon-team"> - <path d="M408.795 244.28C423.843 224.794 432 201.025 432 176c0-61.855-50.043-112-112-112-61.853 0-112 50.041-112 112 0 25.025 8.157 48.794 23.205 68.28-12.93 3.695-71.205 25.768-71.205 92.757v60.677C160 425.442 182.558 448 210.286 448h219.429C457.442 448 480 425.442 480 397.714v-60.677c0-66.985-58.234-89.051-71.205-92.757zM320 96c44.183 0 80 35.817 80 80s-35.817 80-80 80-80-35.817-80-80 35.817-80 80-80zm128 301.714c0 10.099-8.187 18.286-18.286 18.286H210.286C200.187 416 192 407.813 192 397.714v-60.677c0-28.575 18.943-53.688 46.418-61.538l20.213-5.775C276.708 281.614 297.862 288 320 288s43.292-6.386 61.369-18.275l20.213 5.775C429.057 283.35 448 308.462 448 337.037v60.677zm-304 0V384H45.714C38.14 384 32 377.86 32 370.286v-45.508c0-21.431 14.207-40.266 34.813-46.153l12.895-3.684C93.904 283.237 110.405 288 128 288a95.582 95.582 0 0 0 29.234-4.564c5.801-10.547 13.46-20.108 22.904-28.483 9.299-8.247 18.915-14.143 27.098-18.247C197.22 218.209 192 197.557 192 176c0-16.214 2.993-31.962 8.708-46.618C183.09 108.954 157.03 96 128 96c-52.935 0-96 43.065-96 96 0 21.776 7.293 41.878 19.558 58.003C25.677 259.796 0 286.423 0 324.778v45.508C0 395.493 20.507 416 45.714 416h100.871A66.078 66.078 0 0 1 144 397.714zM128 128c35.346 0 64 28.654 64 64s-28.654 64-64 64-64-28.654-64-64 28.654-64 64-64zm460.442 122.003C600.707 233.878 608 213.776 608 192c0-52.935-43.065-96-96-96-29.031 0-55.091 12.955-72.71 33.385C445.006 144.041 448 159.788 448 176c0 21.557-5.219 42.207-15.235 60.704 8.19 4.106 17.812 10.004 27.115 18.256 9.439 8.373 17.094 17.933 22.892 28.478A95.573 95.573 0 0 0 512 288c17.595 0 34.096-4.763 48.292-13.06l12.895 3.684C593.793 284.512 608 303.347 608 324.778v45.508c0 7.574-6.14 13.714-13.714 13.714H496v13.714c0 6.343-.914 12.473-2.585 18.286h100.871C619.493 416 640 395.493 640 370.286v-45.508c0-38.369-25.689-64.987-51.558-74.775zM512 256c-35.346 0-64-28.654-64-64s28.654-64 64-64 64 28.654 64 64-28.654 64-64 64z" /> - </symbol> - <symbol viewBox="0 0 20 20 " id="icon-trash"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M4 6h12l-1 12H5zm8-2.5a2 2 0 1 0-4 0" /> - <path d="M8 10v5m4-5v5" stroke-linecap="square" /> - <path d="M2.5 3.5h15" stroke-linecap="round" stroke-linejoin="round" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-twitter"> - <path d="M17.13 6.634c.01.16.01.319.01.478 0 4.864-3.701 10.468-10.467 10.468-2.085 0-4.021-.604-5.65-1.652.296.035.58.046.888.046 1.72 0 3.304-.581 4.568-1.572a3.686 3.686 0 0 1-3.44-2.551c.228.034.456.056.695.056.33 0 .66-.045.968-.125a3.68 3.68 0 0 1-2.95-3.61v-.046c.49.273 1.06.444 1.663.467a3.677 3.677 0 0 1-1.64-3.064c0-.684.182-1.31.5-1.857a10.458 10.458 0 0 0 7.587 3.85 4.153 4.153 0 0 1-.091-.843A3.676 3.676 0 0 1 13.45 3a3.67 3.67 0 0 1 2.688 1.162 7.243 7.243 0 0 0 2.335-.889 3.67 3.67 0 0 1-1.617 2.028 7.376 7.376 0 0 0 2.118-.57 7.91 7.91 0 0 1-1.845 1.903z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-upload"> - <path d="M16.89 16.188a.705.705 0 0 1-.703.703.705.705 0 0 1-.703-.703c0-.387.317-.704.704-.704.386 0 .703.317.703.704zm-2.953-.704a.705.705 0 0 0-.703.704c0 .386.317.703.704.703a.705.705 0 0 0 .703-.703.705.705 0 0 0-.704-.704zM19 13.797v3.656c0 .854-.693 1.547-1.547 1.547H2.547A1.547 1.547 0 0 1 1 17.453v-3.656c0-.854.693-1.547 1.547-1.547h4.36V8.759H5.053c-1.252 0-1.878-1.515-.995-2.401L9.005 1.41a1.409 1.409 0 0 1 1.99 0l4.946 4.947c.886.886.257 2.401-.995 2.401h-1.852v3.491h4.36c.853 0 1.546.693 1.546 1.547zM8.031 7.634v6.585c0 .154.127.281.281.281h3.376a.282.282 0 0 0 .28-.281V7.634h2.978c.25 0 .377-.302.2-.482L10.2 2.206a.282.282 0 0 0-.397 0L4.857 7.152a.283.283 0 0 0 .2.482h2.974zm9.844 6.163a.423.423 0 0 0-.422-.422h-4.36v.844c0 .777-.629 1.406-1.405 1.406H8.312c-.776 0-1.406-.63-1.406-1.406v-.844h-4.36a.423.423 0 0 0-.421.422v3.656c0 .232.19.422.422.422h14.906c.232 0 .422-.19.422-.422v-3.656z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-user"> - <g fill="none" fill-rule="evenodd"> - <path d="M0 0h20v20H0z" /> - <path d="M4 15.106c0-3.783 4.5-3.026 4.5-4.54 0 0 .086-1.004-.41-1.513C7.473 8.423 7 7.665 7 6.405 7 4.525 8.343 3 10 3s3 1.524 3 3.405c0 1.243-.46 2.017-1.105 2.648-.472.496-.395 1.514-.395 1.514 0 1.513 4.5.756 4.5 4.54 0 0-1.195.893-6 .893s-6-.894-6-.894z" stroke="currentColor" stroke-width="1.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-user-rounded"> - <g transform="translate(2.5 2.5)" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <rect x=".75" y=".75" width="13.5" height="13.5" rx="2" /> - <path d="M2.502 14.167c0-3.125 3.748-2.5 3.748-3.75 0 0 .072-.83-.34-1.25C5.394 8.646 5 8.02 5 6.979c0-1.553 1.12-2.812 2.5-2.812S10 5.426 10 6.979c0 1.027-.384 1.666-.922 2.188-.392.41-.328 1.25-.328 1.25 0 1.25 3.748.625 3.748 3.75" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-video"> - <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> - <path d="M3 8h10v8H3zm10 2.376l4.048-1.314c.526-.171.952.137.952.69v4.494c0 .552-.426.862-.952.69L13 13.624v-3.247z" /> - <path d="M5.5 5h5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-warning"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M10 3l8 14H2z" /> - <path d="M10 9v5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-checkmark-circled"> - <path d="M486.4 1024c-129.922 0-252.067-50.594-343.936-142.464s-142.464-214.014-142.464-343.936c0-129.923 50.595-252.067 142.464-343.936s214.013-142.464 343.936-142.464c129.922 0 252.067 50.595 343.936 142.464s142.464 214.014 142.464 343.936-50.594 252.067-142.464 343.936c-91.869 91.87-214.014 142.464-343.936 142.464zM486.4 102.4c-239.97 0-435.2 195.23-435.2 435.2s195.23 435.2 435.2 435.2 435.2-195.23 435.2-435.2-195.23-435.2-435.2-435.2z" /> - <path d="M384 742.4c-6.552 0-13.102-2.499-18.102-7.499l-153.6-153.6c-9.997-9.997-9.997-26.206 0-36.203 9.998-9.997 26.206-9.997 36.205 0l135.498 135.498 340.299-340.298c9.997-9.997 26.206-9.997 36.203 0 9.998 9.998 9.998 26.206 0 36.205l-358.4 358.4c-5 4.998-11.55 7.498-18.102 7.498z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-cross-circled"> - <path d="M733.808 723.266l-208.874-185.666 208.874-185.667c10.566-9.394 11.518-25.574 2.126-36.141-9.394-10.566-25.574-11.522-36.142-2.126l-213.392 189.682-213.392-189.68c-10.568-9.392-26.749-8.44-36.141 2.126-9.394 10.566-8.442 26.749 2.126 36.141l208.874 185.666-208.875 185.666c-10.566 9.394-11.518 25.574-2.126 36.142 5.059 5.691 12.085 8.592 19.142 8.592 6.048 0 12.122-2.131 16.998-6.466l213.394-189.683 213.392 189.683c4.878 4.334 10.949 6.466 16.998 6.466 7.058 0 14.086-2.902 19.144-8.592 9.392-10.568 8.44-26.749-2.126-36.142z" /> - <path d="M486.4 1024c-129.922 0-252.067-50.594-343.936-142.464s-142.464-214.014-142.464-343.936c0-129.923 50.595-252.067 142.464-343.936s214.013-142.464 343.936-142.464c129.922 0 252.067 50.595 343.936 142.464s142.464 214.014 142.464 343.936-50.594 252.067-142.464 343.936c-91.869 91.87-214.014 142.464-343.936 142.464zM486.4 102.4c-239.97 0-435.2 195.23-435.2 435.2s195.23 435.2 435.2 435.2 435.2-195.23 435.2-435.2-195.23-435.2-435.2-435.2z" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-jump"> - <path d="M11.6 7.257V4L18 9.7l-6.4 5.7v-3.257S2 11.385 2 16c0-9.23 9.6-8.743 9.6-8.743z" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-flag"> - <path d="M12.928 6.063l3.263-3.77a.74.74 0 0 0 .112-.759.71.71 0 0 0-.647-.422H3.141v17.72h1.406v-7.876h11.11a.71.71 0 0 0 .646-.422.691.691 0 0 0-.112-.759l-3.263-3.713zM4.547 9.578V2.547h9.562l-2.643 3.065c-.225.254-.225.647 0 .929l2.643 3.065H4.547v-.028z" fill-rule="nonzero" fill="currentColor" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-circle"> - <circle cx="10" cy="10" r="9" fill="currentColor" fill-rule="evenodd" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-cross"> - <path d="M16.364 3.636l-6.018 6.018-6.71 6.71m12.728 0L3.636 3.636" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="square" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-post"> - <g fill="none" fill-rule="evenodd"> - <path d="M0 0h20v20H0z" /> - <path stroke="currentColor" stroke-width="1.5" d="M5 3h6l4 4v10H5z" /> - <path d="M8.5 10.5h3M8.5 13.5h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-discover"> - <g transform="translate(1 1)" fill="none" fill-rule="evenodd"> - <path d="M0 0h18v18H0z" /> - <circle stroke="currentColor" stroke-width="1.5" cx="9" cy="9" r="6.75" /> - <path d="M9 15.75c1.5 0 3-3.022 3-6.75s-1.5-6.75-3-6.75S6 5.272 6 9s1.5 6.75 3 6.75z" stroke="currentColor" stroke-width="1.5" /> - <path d="M2.25 9c0 1.5 3.022 3 6.75 3s6.75-1.5 6.75-3S12.728 6 9 6 2.25 7.5 2.25 9z" stroke="currentColor" stroke-width="1.5" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-import"> - <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> - <path d="M10 7.5v9.583" stroke-linecap="square" /> - <path d="M13.333 14.581l-3.291 3.292L6.75 14.58m.75-2.913H5V2.5h10v9.167h-2.5" /> - </g> - </symbol> - <symbol viewBox="0 0 89 83" id="icon-modal-warning"> - <g fill="currentColor" fill-rule="evenodd"> - <path d="M44.488 29a2.988 2.988 0 0 1 2.989 2.988v23.056a2.988 2.988 0 1 1-5.977 0V31.988A2.988 2.988 0 0 1 44.488 29zm-.106 34.021c1.947 0 3.382 1.52 3.382 3.493 0 2-1.435 3.519-3.382 3.519-1.946 0-3.382-1.52-3.382-3.52 0-1.972 1.436-3.492 3.382-3.492z" /> - <path d="M7.471 76.881H81.46L44.465 8.713 7.471 76.881zm78.4 5.626H3.06a3 3 0 0 1-2.637-4.431L41.828 1.778a3 3 0 0 1 5.274 0l41.405 76.298a3 3 0 0 1-2.637 4.43z" fill-rule="nonzero" /> - </g> - </symbol> - <symbol viewBox="0 0 82 82" id="icon-modal-success"> - <g fill="currentColor" fill-rule="evenodd"> - <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> - <path d="M33.86 50.585l-8.671-8.723a2.446 2.446 0 0 0-3.47 0 2.475 2.475 0 0 0 0 3.49L31.76 55.45a3 3 0 0 0 4.23.026l23.716-23.277a2.444 2.444 0 0 0 .021-3.467 2.48 2.48 0 0 0-3.493-.025L33.861 50.585z" stroke="currentColor" /> - </g> - </symbol> - <symbol viewBox="0 0 82 82" id="icon-modal-info"> - <g fill="currentColor" fill-rule="evenodd"> - <path d="M40.988 61.033a2.988 2.988 0 0 0 2.989-2.989V34.988a2.988 2.988 0 1 0-5.977 0v23.056a2.988 2.988 0 0 0 2.988 2.989zm-.106-34.021c1.947 0 3.382-1.52 3.382-3.493 0-2-1.435-3.519-3.382-3.519-1.946 0-3.382 1.52-3.382 3.52 0 1.972 1.436 3.492 3.382 3.492z" /> - <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> - </g> - </symbol> - <symbol viewBox="0 0 82 82" id="icon-modal-error"> - <g fill="currentColor" fill-rule="evenodd"> - <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> - <path d="M56.6 56.203a2.988 2.988 0 0 0 0-4.226L30.796 26.175a2.988 2.988 0 0 0-4.226 4.226l25.802 25.802a2.988 2.988 0 0 0 4.226 0z" /> - <path d="M26.57 56.203a2.988 2.988 0 0 0 4.227 0L56.6 30.401a2.988 2.988 0 0 0-4.226-4.226L26.571 51.977a2.988 2.988 0 0 0 0 4.226z" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-file-document"> - <g fill="#FFF" fill-rule="evenodd"> - <path opacity=".75" d="M1 5h7.637v7.637H1z" /> - <path d="M10.273 5h5.455v1.091h-5.455z" /> - <path opacity=".6" d="M10.273 8.273h7.091v1.091h-7.091zM10.273 11.546h8.728v1.091h-8.728zM1 14.819h15.82v1.091H1z" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-file-generic"> - <g fill="#FFF" fill-rule="evenodd"> - <path d="M1 5h5.455v1.091H1z" /> - <path opacity=".6" d="M1 8.273h16.365v1.091H1zM1 11.546h18.001v1.091H1zM1 14.819h15.82v1.091H1z" /> - </g> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-file-pdf"> - <path d="M8.848 2.08l11.217 15.147L2.24 19.732z" fill="#FFF" fill-rule="evenodd" opacity=".75" /> - </symbol> - <symbol viewBox="0 0 20 20" id="icon-file-sheets"> - <g transform="translate(1 1)" fill="#FFF" fill-rule="evenodd" opacity=".75"> - <circle cx="6.12" cy="6.12" r="6.12" /> - <path d="M4.32 5.04h12.96V18H4.32z" /> - </g> - </symbol> - <symbol viewBox="0 0 24 24" id="icon-arrow-down"> - <path d="M18.585 12.1L12.5 18.185 6.415 12.1" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" /> - </symbol> - <symbol viewBox="0 0 320 512" id="icon-mobile"> - <path d="M192 416c0 17.7-14.3 32-32 32s-32-14.3-32-32 14.3-32 32-32 32 14.3 32 32zM320 48v416c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V48C0 21.5 21.5 0 48 0h224c26.5 0 48 21.5 48 48zm-32 0c0-8.8-7.2-16-16-16H48c-8.8 0-16 7.2-16 16v416c0 8.8 7.2 16 16 16h224c8.8 0 16-7.2 16-16V48z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-hand-pointer"> - <path d="M360.543 188.156c-17.46-28.491-54.291-37.063-82.138-19.693-15.965-20.831-42.672-28.278-66.119-20.385V60.25c0-33.222-26.788-60.25-59.714-60.25S92.857 27.028 92.857 60.25v181.902c-20.338-13.673-47.578-13.89-68.389 1.472-26.556 19.605-32.368 57.08-13.132 83.926l114.271 159.5C136.803 502.673 154.893 512 174 512h185.714c27.714 0 51.832-19.294 58.145-46.528l28.571-123.25a60.769 60.769 0 0 0 1.57-13.723v-87c0-45.365-48.011-74.312-87.457-53.343zM82.097 275.588l28.258 39.439a7.999 7.999 0 1 0 14.503-4.659V60.25c0-37.35 55.428-37.41 55.428 0V241.5a8 8 0 0 0 8 8h7.144a8 8 0 0 0 8-8v-36.25c0-37.35 55.429-37.41 55.429 0v36.25a8 8 0 0 0 8 8H274a8 8 0 0 0 8-8v-21.75c0-37.351 55.429-37.408 55.429 0v21.75a8 8 0 0 0 8 8h7.143a8 8 0 0 0 8-8c0-37.35 55.429-37.41 55.429 0v87c0 2.186-.25 4.371-.742 6.496l-28.573 123.251C383.717 471.055 372.626 480 359.715 480H174c-8.813 0-17.181-4.332-22.381-11.588l-114.283-159.5c-22.213-31.004 23.801-62.575 44.761-33.324zM180.285 401v-87a8 8 0 0 1 8-8h7.144a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.144a8 8 0 0 1-8-8zm78.572 0v-87a8 8 0 0 1 8-8H274a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.143a8 8 0 0 1-8-8zm78.572 0v-87a8 8 0 0 1 8-8h7.143a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.143a8 8 0 0 1-8-8z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-shield"> - <path d="M466.5 83.7l-192-80a48.15 48.15 0 0 0-36.9 0l-192 80C27.7 91.1 16 108.6 16 128c0 198.5 114.5 335.7 221.5 380.3 11.8 4.9 25.1 4.9 36.9 0C360.1 472.6 496 349.3 496 128c0-19.4-11.7-36.9-29.5-44.3zM262.2 478.8c-3.9 1.6-8.3 1.6-12.3 0C152 440 48 304 48 128c0-6.5 3.9-12.3 9.8-14.8l192-80c3.8-1.6 8.3-1.7 12.3 0l192 80c6 2.5 9.8 8.3 9.8 14.8.1 176-103.9 312-201.7 350.8z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-shield-alt"> - <path d="M256 410.955V99.999l-142.684 59.452C123.437 279.598 190.389 374.493 256 410.955zm-32-66.764c-36.413-39.896-65.832-97.846-76.073-164.495L224 147.999v196.192zM466.461 83.692l-192-80a47.996 47.996 0 0 0-36.923 0l-192 80A48 48 0 0 0 16 128c0 198.487 114.495 335.713 221.539 380.308a48 48 0 0 0 36.923 0C360.066 472.645 496 349.282 496 128a48 48 0 0 0-29.539-44.308zM262.154 478.768a16.64 16.64 0 0 1-12.31-.001C152 440 48 304 48 128c0-6.48 3.865-12.277 9.846-14.769l192-80a15.99 15.99 0 0 1 12.308 0l192 80A15.957 15.957 0 0 1 464 128c0 176-104 312-201.846 350.768z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-shield-check"> - <path d="M466.461 83.692l-192-80a47.996 47.996 0 0 0-36.923 0l-192 80A48 48 0 0 0 16 128c0 198.487 114.495 335.713 221.539 380.308a48 48 0 0 0 36.923 0C360.066 472.645 496 349.282 496 128a48 48 0 0 0-29.539-44.308zM262.154 478.768a16.64 16.64 0 0 1-12.31-.001C152 440 48 304 48 128c0-6.48 3.865-12.277 9.846-14.769l192-80a15.99 15.99 0 0 1 12.308 0l192 80A15.957 15.957 0 0 1 464 128c0 176-104 312-201.846 350.768zm144.655-299.505l-180.48 179.032c-4.705 4.667-12.303 4.637-16.97-.068l-85.878-86.572c-4.667-4.705-4.637-12.303.068-16.97l8.52-8.451c4.705-4.667 12.303-4.637 16.97.068l68.976 69.533 163.441-162.13c4.705-4.667 12.303-4.637 16.97.068l8.451 8.52c4.668 4.705 4.637 12.303-.068 16.97z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-check"> - <path d="M413.505 91.951L133.49 371.966l-98.995-98.995c-4.686-4.686-12.284-4.686-16.971 0L6.211 284.284c-4.686 4.686-4.686 12.284 0 16.971l118.794 118.794c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-11.314-11.314c-4.686-4.686-12.284-4.686-16.97 0z" /> - </symbol> - <symbol viewBox="0 0 640 512" id="icon-team"> - <path d="M573.127 249.095C584.979 233.127 592 213.369 592 192c0-52.935-43.065-96-96-96-26.331 0-50.217 10.658-67.578 27.885a128.993 128.993 0 0 0-17.913-22.394C386.334 77.314 354.19 64 320 64s-66.334 13.314-90.51 37.49a129.115 129.115 0 0 0-17.913 22.394C194.217 106.658 170.331 96 144 96c-52.935 0-96 43.065-96 96 0 21.369 7.021 41.127 18.873 57.095C28.987 255.378 0 288.36 0 328v44c0 24.262 19.738 44 44 44h117.677c5.238 18.445 22.222 32 42.323 32h232c20.102 0 37.085-13.555 42.323-32H596c24.262 0 44-19.738 44-44v-44c0-39.64-28.986-72.622-66.873-78.905zM496 128c35.346 0 64 28.654 64 64s-28.654 64-64 64c-22.083 0-41.554-11.185-53.057-28.199C446.27 216.314 448 204.291 448 192s-1.73-24.314-5.057-35.801C454.446 139.185 473.917 128 496 128zM320 96c53.02 0 96 42.981 96 96s-42.98 96-96 96-96-42.981-96-96 42.98-96 96-96zm-176 32c22.083 0 41.554 11.185 53.057 28.199C193.73 167.686 192 179.709 192 192s1.73 24.314 5.057 35.801C185.554 244.815 166.083 256 144 256c-35.346 0-64-28.654-64-64s28.654-64 64-64zm16 224v32H44c-6.627 0-12-5.373-12-12v-44c0-26.51 21.49-48 48-48h25.655c24.374 10.662 52.272 10.681 76.689 0h22.81C178.452 292.976 160 320.372 160 352zm288 52c0 6.627-5.373 12-12 12H204c-6.627 0-12-5.373-12-12v-52c0-26.51 21.49-48 48-48h17.929c37.818 21.031 85.208 21.651 124.142 0H400c26.51 0 48 21.49 48 48v52zm160-32c0 6.627-5.373 12-12 12H480v-32c0-31.628-18.452-59.024-45.154-72h22.81c24.374 10.662 52.272 10.681 76.689 0H560c26.51 0 48 21.49 48 48v44z" /> - </symbol> - <symbol viewBox="0 0 640 512" id="icon-user-plus"> - <path d="M640 252v8c0 6.627-5.373 12-12 12h-68v68c0 6.627-5.373 12-12 12h-8c-6.627 0-12-5.373-12-12v-68h-68c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h68v-68c0-6.627 5.373-12 12-12h8c6.627 0 12 5.373 12 12v68h68c6.627 0 12 5.373 12 12zm-264.942 32.165l-43.497-12.428C355.115 245.342 368 211.663 368 176c0-79.525-64.339-144-144-144-79.525 0-144 64.339-144 144 0 35.663 12.885 69.342 36.439 95.737l-43.497 12.428C17.501 300.005 0 350.424 0 380.866v39.705C0 453.34 26.66 480 59.429 480h329.143C421.34 480 448 453.34 448 420.571v-39.705c0-57.659-43.675-88.339-72.942-96.701zM224 64c61.856 0 112 50.144 112 112s-50.144 112-112 112-112-50.144-112-112S162.144 64 224 64zm192 356.571C416 435.72 403.72 448 388.571 448H59.429C44.28 448 32 435.72 32 420.571v-39.705c0-30.616 20.296-57.522 49.733-65.933l63.712-18.203C168.611 311.87 195.679 320 224 320s55.389-8.13 78.555-23.27l63.712 18.203C395.704 323.344 416 350.251 416 380.866v39.705z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-globe"> - <path d="M504 256C504 118.815 392.705 8 256 8 119.371 8 8 118.74 8 256c0 136.938 111.041 248 248 248 136.886 0 248-110.987 248-248zm-41.625 64h-99.434c6.872-42.895 6.6-86.714.055-128h99.38c12.841 41.399 12.843 86.598-.001 128zM256.001 470.391c-30.732-27.728-54.128-69.513-67.459-118.391h134.917c-13.332 48.887-36.73 90.675-67.458 118.391zM181.442 320c-7.171-41.387-7.349-85.537.025-128h149.067c7.371 42.453 7.197 86.6.025 128H181.442zM256 41.617c33.557 30.295 55.554 74.948 67.418 118.383H188.582c11.922-43.649 33.98-88.195 67.418-118.383zM449.544 160h-93.009c-10.928-44.152-29.361-83.705-53.893-114.956C366.825 59.165 420.744 101.964 449.544 160zM209.357 45.044C184.826 76.293 166.393 115.847 155.464 160H62.456C91.25 101.975 145.162 59.169 209.357 45.044zM49.625 192h99.38c-6.544 41.28-6.818 85.1.055 128H49.625c-12.842-41.399-12.844-86.598 0-128zm12.831 160h93.122c11.002 44.176 29.481 83.824 53.833 114.968C144.875 452.786 91.108 409.738 62.456 352zm240.139 114.966c24.347-31.138 42.825-70.787 53.827-114.966h93.121c-28.695 57.827-82.504 100.802-146.948 114.966z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-th-list"> - <path d="M0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48H48C21.49 32 0 53.49 0 80zm480 0v90.667H192V64h272c8.837 0 16 7.163 16 16zm0 229.333H192V202.667h288v106.666zM32 202.667h128v106.667H32V202.667zM160 64v106.667H32V80c0-8.837 7.163-16 16-16h112zM32 432v-90.667h128V448H48c-8.837 0-16-7.163-16-16zm160 16V341.333h288V432c0 8.837-7.163 16-16 16H192z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-list"> - <path d="M506 114H134a6 6 0 0 1-6-6V84a6 6 0 0 1 6-6h372a6 6 0 0 1 6 6v24a6 6 0 0 1-6 6zm6 154v-24a6 6 0 0 0-6-6H134a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h372a6 6 0 0 0 6-6zm0 160v-24a6 6 0 0 0-6-6H134a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h372a6 6 0 0 0 6-6zM84 120V72c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12zm0 160v-48c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12zm0 160v-48c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-list-alt"> - <path d="M464 64c8.823 0 16 7.178 16 16v352c0 8.822-7.177 16-16 16H48c-8.823 0-16-7.178-16-16V80c0-8.822 7.177-16 16-16h416m0-32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zm-336 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm288-148v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6zm0 96v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6zm0 96v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-sort"> - <path d="M204.485 392l-84 84.485c-4.686 4.686-12.284 4.686-16.971 0l-84-84.485c-4.686-4.686-4.686-12.284 0-16.97l7.07-7.071c4.686-4.686 12.284-4.686 16.971 0L95 419.887V44c0-6.627 5.373-12 12-12h10c6.627 0 12 5.373 12 12v375.887l51.444-51.928c4.686-4.686 12.284-4.686 16.971 0l7.07 7.071c4.687 4.686 4.687 12.284 0 16.97zm100.492-220.355h61.547l15.5 44.317A12 12 0 0 0 393.351 224h11.552c8.31 0 14.105-8.243 11.291-16.062l-60.441-168A11.999 11.999 0 0 0 344.462 32h-16.924a11.999 11.999 0 0 0-11.291 7.938l-60.441 168c-2.813 7.82 2.981 16.062 11.291 16.062h11.271c5.12 0 9.676-3.248 11.344-8.088l15.265-44.267zm10.178-31.067l18.071-51.243c.853-2.56 1.776-5.626 2.668-8.743.871 3.134 1.781 6.219 2.644 8.806l17.821 51.18h-41.204zm-3.482 307.342c4.795-6.044-1.179 2.326 92.917-133.561a12.011 12.011 0 0 0 2.136-6.835V300c0-6.627-5.373-12-12-12h-113.84c-6.627 0-12 5.373-12 12v8.068c0 6.644 5.393 12.031 12.037 12.031 81.861.001 76.238.011 78.238-.026-2.973 3.818 4.564-7.109-92.776 133.303a12.022 12.022 0 0 0-2.142 6.847V468c0 6.627 5.373 12 12 12h119.514c6.627 0 12-5.373 12-12v-8.099c0-6.627-5.373-12-12-12-87.527-.001-81.97-.01-84.084.019z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-podcast"> - <path d="M326.011 313.366a81.658 81.658 0 0 0-11.127-16.147c-1.855-2.1-1.913-5.215-.264-7.481C328.06 271.264 336 248.543 336 224c0-63.221-52.653-114.375-116.41-111.915-57.732 2.228-104.69 48.724-107.458 106.433-1.278 26.636 6.812 51.377 21.248 71.22 1.648 2.266 1.592 5.381-.263 7.481a81.609 81.609 0 0 0-11.126 16.145c-2.003 3.816-7.25 4.422-9.961 1.072C92.009 289.7 80 258.228 80 224c0-79.795 65.238-144.638 145.178-143.995 77.583.624 141.19 63.4 142.79 140.969.73 35.358-11.362 67.926-31.928 93.377-2.738 3.388-8.004 2.873-10.029-.985zM224 0C100.206 0 0 100.185 0 224c0 82.003 43.765 152.553 107.599 191.485 4.324 2.637 9.775-.93 9.078-5.945-1.244-8.944-2.312-17.741-3.111-26.038a6.025 6.025 0 0 0-2.461-4.291c-48.212-35.164-79.495-92.212-79.101-156.409.636-103.637 84.348-188.625 187.964-190.76C327.674 29.822 416 116.79 416 224c0 63.708-31.192 120.265-79.104 155.21a6.027 6.027 0 0 0-2.462 4.292c-.799 8.297-1.866 17.092-3.11 26.035-.698 5.015 4.753 8.584 9.075 5.947C403.607 376.922 448 306.75 448 224 448 100.204 347.814 0 224 0zm64 355.75c0 32.949-12.871 104.179-20.571 132.813C262.286 507.573 242.858 512 224 512c-18.857 0-38.286-4.427-43.428-23.438C172.927 460.134 160 388.898 160 355.75c0-35.156 31.142-43.75 64-43.75 32.858 0 64 8.594 64 43.75zm-32 0c0-16.317-64-16.3-64 0 0 27.677 11.48 93.805 19.01 122.747 6.038 2.017 19.948 2.016 25.981 0C244.513 449.601 256 383.437 256 355.75zM288 224c0 35.346-28.654 64-64 64s-64-28.654-64-64 28.654-64 64-64 64 28.654 64 64zm-32 0c0-17.645-14.355-32-32-32s-32 14.355-32 32 14.355 32 32 32 32-14.355 32-32z" /> - </symbol> - <symbol viewBox="0 0 256 512" id="icon-angle-down"> - <path d="M119.5 326.9L3.5 209.1c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0L128 287.3l100.4-102.2c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L136.5 327c-4.7 4.6-12.3 4.6-17-.1z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-play-solid"> - <path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-play"> - <path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6zm-16.2 55.1l-352 208C45.6 483.9 32 476.6 32 464V47.9c0-16.3 16.4-18.4 24.1-13.8l352 208.1c10.5 6.2 10.5 21.4.1 27.6z" /> - </symbol> - <symbol viewBox="0 0 448 512" id="icon-pause"> - <path d="M48 479h96c26.5 0 48-21.5 48-48V79c0-26.5-21.5-48-48-48H48C21.5 31 0 52.5 0 79v352c0 26.5 21.5 48 48 48zM32 79c0-8.8 7.2-16 16-16h96c8.8 0 16 7.2 16 16v352c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V79zm272 400h96c26.5 0 48-21.5 48-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48zM288 79c0-8.8 7.2-16 16-16h96c8.8 0 16 7.2 16 16v352c0 8.8-7.2 16-16 16h-96c-8.8 0-16-7.2-16-16V79z" /> - </symbol> - <symbol viewBox="0 0 640 512" id="icon-volume-mute"> - <path d="M615.554 509.393L4.534 27.657c-5.188-4.124-6.051-11.673-1.927-16.861l4.978-6.263c4.124-5.188 11.673-6.051 16.861-1.927l611.021 481.736c5.188 4.124 6.051 11.673 1.927 16.861l-4.978 6.263c-4.125 5.189-11.674 6.051-16.862 1.927zM407.172 126.221C450.902 152.963 480 201.134 480 256c0 19.945-3.861 38.996-10.856 56.463l26.002 20.5C505.972 309.488 512 283.404 512 256c0-66.099-34.976-124.573-88.133-157.079-7.538-4.611-17.388-2.235-21.997 5.302-4.61 7.539-2.236 17.387 5.302 21.998zm-171.913 1.844L256 107.328v37.089l32 25.229v-81.63c0-21.466-25.963-31.979-40.97-16.971l-37.075 37.068 25.304 19.952zm221.925-83.804C528.548 87.899 576 166.532 576 256c0 42.442-10.685 82.442-29.529 117.428l25.467 20.078C594.94 352.775 608 305.811 608 256c0-100.587-53.23-189.576-134.123-239.04-7.541-4.61-17.389-2.235-21.997 5.304-4.609 7.539-2.235 17.387 5.304 21.997zM357.159 208.178c13.422 8.213 22.517 21.271 25.639 36.209l32.141 25.341a89.491 89.491 0 0 0 1.06-13.728c0-30.891-15.753-58.972-42.14-75.117-7.538-4.615-17.388-2.239-21.998 5.297-4.611 7.537-2.24 17.386 5.298 21.998zm128.318 239.41a248.52 248.52 0 0 1-28.293 20.151c-7.539 4.609-9.913 14.458-5.304 21.997 4.612 7.544 14.465 9.91 21.997 5.304a280.708 280.708 0 0 0 37.246-27.233l-25.646-20.219zM256 266.666V404.67l-77.659-77.643a24 24 0 0 0-16.969-7.028H64V192h97.296l-40.588-32H56c-13.255 0-24 10.745-24 24v144c0 13.255 10.745 24 24 24h102.059l88.971 88.952c15.029 15.028 40.97 4.465 40.97-16.971V291.895l-32-25.229zm151.123 119.147c-7.498 4.624-9.853 14.443-5.253 21.965 4.611 7.541 14.462 9.911 21.997 5.302a184.087 184.087 0 0 0 9.738-6.387l-26.482-20.88z" /> - </symbol> - <symbol viewBox="0 0 576 512" id="icon-volume"> - <path d="M576 256c0 100.586-53.229 189.576-134.123 239.04-7.532 4.606-17.385 2.241-21.997-5.304-4.609-7.539-2.235-17.388 5.304-21.997C496.549 424.101 544 345.467 544 256c0-89.468-47.452-168.101-118.816-211.739-7.539-4.609-9.913-14.458-5.304-21.997 4.608-7.539 14.456-9.914 21.997-5.304C522.77 66.424 576 155.413 576 256zm-96 0c0-66.099-34.976-124.572-88.133-157.079-7.538-4.611-17.388-2.235-21.997 5.302-4.61 7.539-2.236 17.388 5.302 21.998C418.902 152.963 448 201.134 448 256c0 54.872-29.103 103.04-72.828 129.779-7.538 4.61-9.912 14.459-5.302 21.998 4.611 7.541 14.462 9.911 21.997 5.302C445.024 380.572 480 322.099 480 256zm-138.14-75.117c-7.538-4.615-17.388-2.239-21.998 5.297-4.612 7.537-2.241 17.387 5.297 21.998C341.966 218.462 352 236.34 352 256s-10.034 37.538-26.841 47.822c-7.538 4.611-9.909 14.461-5.297 21.998 4.611 7.538 14.463 9.909 21.998 5.297C368.247 314.972 384 286.891 384 256s-15.753-58.972-42.14-75.117zM256 88.017v335.964c0 21.436-25.942 31.999-40.971 16.971L126.059 352H24c-13.255 0-24-10.745-24-24V184c0-13.255 10.745-24 24-24h102.059l88.971-88.954C230.037 56.038 256 66.551 256 88.017zm-32 19.311l-77.659 77.644A24.001 24.001 0 0 1 129.372 192H32v128h97.372a24.001 24.001 0 0 1 16.969 7.028L224 404.67V107.328z" /> - </symbol> - <symbol viewBox="0 0 320 512" id="icon-sort"> - <path d="M288 288H32c-28.4 0-42.8 34.5-22.6 54.6l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c20-20.1 5.7-54.6-22.7-54.6zM160 448L32 320h256L160 448zM32 224h256c28.4 0 42.8-34.5 22.6-54.6l-128-128c-12.5-12.5-32.8-12.5-45.3 0l-128 128C-10.7 189.5 3.6 224 32 224zM160 64l128 128H32L160 64z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-clock"> - <path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm216 248c0 118.7-96.1 216-216 216-118.7 0-216-96.1-216-216 0-118.7 96.1-216 216-216 118.7 0 216 96.1 216 216zm-148.9 88.3l-81.2-59c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h14c6.6 0 12 5.4 12 12v146.3l70.5 51.3c5.4 3.9 6.5 11.4 2.6 16.8l-8.2 11.3c-3.9 5.3-11.4 6.5-16.8 2.6z" /> - </symbol> - <symbol viewBox="0 0 512 512" id="icon-sort-amount-down"> - <path d="M204.485 392l-84 84.485c-4.686 4.686-12.284 4.686-16.971 0l-84-84.485c-4.686-4.686-4.686-12.284 0-16.97l7.07-7.071c4.686-4.686 12.284-4.686 16.971 0L95 419.887V44c0-6.627 5.373-12 12-12h10c6.627 0 12 5.373 12 12v375.887l51.444-51.928c4.686-4.686 12.284-4.686 16.971 0l7.07 7.071c4.687 4.686 4.687 12.284 0 16.97zM384 308v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h104c6.627 0 12-5.373 12-12zm64-96v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h168c6.627 0 12-5.373 12-12zm64-96v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zM320 404v-8c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" /> - </symbol> - <symbol viewBox="0 0 576 512" id="icon-edit-rounded"> - <path d="M417.8 315.5l20-20c3.8-3.8 10.2-1.1 10.2 4.2V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V112c0-26.5 21.5-48 48-48h292.3c5.3 0 8 6.5 4.2 10.2l-20 20c-1.1 1.1-2.7 1.8-4.2 1.8H48c-8.8 0-16 7.2-16 16v352c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16V319.7c0-1.6.6-3.1 1.8-4.2zm145.9-191.2L251.2 436.8l-99.9 11.1c-13.4 1.5-24.7-9.8-23.2-23.2l11.1-99.9L451.7 12.3c16.4-16.4 43-16.4 59.4 0l52.6 52.6c16.4 16.4 16.4 43 0 59.4zm-93.6 48.4L403.4 106 169.8 339.5l-8.3 75.1 75.1-8.3 233.5-233.6zm71-85.2l-52.6-52.6c-3.8-3.8-10.2-4-14.1 0L426 83.3l66.7 66.7 48.4-48.4c3.9-3.8 3.9-10.2 0-14.1z" /> - </symbol> - <symbol id="icon-reply" viewBox="0 0 576 512"> - <path d="M11.093 251.65l175.998 184C211.81 461.494 256 444.239 256 408v-87.84c154.425 1.812 219.063 16.728 181.19 151.091-8.341 29.518 25.447 52.232 49.68 34.51C520.16 481.421 576 426.17 576 331.19c0-171.087-154.548-201.035-320-203.02V40.016c0-36.27-44.216-53.466-68.91-27.65L11.093 196.35c-14.791 15.47-14.791 39.83 0 55.3zm23.127-33.18l176-184C215.149 29.31 224 32.738 224 40v120c157.114 0 320 11.18 320 171.19 0 74.4-40 122.17-76.02 148.51C519.313 297.707 395.396 288 224 288v120c0 7.26-8.847 10.69-13.78 5.53l-176-184a7.978 7.978 0 0 1 0-11.06z" /> - </symbol> - <symbol id="icon-clipboard" viewBox="0 0 384 512"> - <path d="M336 64h-88.581c.375-2.614.581-5.283.581-8 0-30.879-25.122-56-56-56s-56 25.121-56 56c0 2.717.205 5.386.581 8H48C21.49 64 0 85.49 0 112v352c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 400c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V112c0-8.822 7.178-16 16-16h48v20c0 6.627 5.373 12 12 12h168c6.627 0 12-5.373 12-12V96h48c8.822 0 16 7.178 16 16v352zM192 32c13.255 0 24 10.745 24 24s-10.745 24-24 24-24-10.745-24-24 10.745-24 24-24" /> - </symbol> - <symbol id="icon-cube" viewBox="0 0 512 512"> - <path d="M239.1 6.3l-208 78c-18.7 7-31.1 25-31.1 45v225.1c0 18.2 10.3 34.8 26.5 42.9l208 104c13.5 6.8 29.4 6.8 42.9 0l208-104c16.3-8.1 26.5-24.8 26.5-42.9V129.3c0-20-12.4-37.9-31.1-44.9l-208-78C262 2.2 250 2.2 239.1 6.3zM256 34.2l224 84v.3l-224 97.1-224-97.1v-.3l224-84zM32 153.4l208 90.1v224.7l-208-104V153.4zm240 314.8V243.5l208-90.1v210.9L272 468.2z" /> - </symbol> - <symbol id="icon-circle-cross" viewBox="0 0 256 256"> - <path d="M183.191,174.141c2.5,2.498,2.5,6.552,0,9.05c-1.249,1.25-2.889,1.875-4.525,1.875c-1.638,0-3.277-0.625-4.525-1.875 l-46.142-46.142L81.856,183.19c-1.249,1.25-2.888,1.875-4.525,1.875c-1.638,0-3.277-0.625-4.525-1.875c-2.5-2.498-2.5-6.552,0-9.05 l46.143-46.143L72.806,81.856c-2.5-2.499-2.5-6.552,0-9.05c2.497-2.5,6.553-2.5,9.05,0l46.142,46.142l46.142-46.142 c2.497-2.5,6.553-2.5,9.051,0c2.5,2.499,2.5,6.552,0,9.05l-46.143,46.142L183.191,174.141z M256,128C256,57.42,198.58,0,128,0 C57.42,0,0,57.42,0,128c0,70.58,57.42,128,128,128C198.58,256,256,198.58,256,128z M243.2,128c0,63.521-51.679,115.2-115.2,115.2 c-63.522,0-115.2-51.679-115.2-115.2C12.8,64.478,64.478,12.8,128,12.8C191.521,12.8,243.2,64.478,243.2,128z" fill="#ef0e0e" /> - </symbol> - <symbol id="icon-circle-check" viewBox="0 0 32 32"> - <g> - <path d="M16,0C7.163,0,0,7.163,0,16c0,8.837,7.163,16,16,16c8.836,0,16-7.164,16-16C32,7.163,24.836,0,16,0z M16,30 C8.268,30,2,23.732,2,16C2,8.268,8.268,2,16,2s14,6.268,14,14C30,23.732,23.732,30,16,30z" fill="#2db730" /> - <path d="M23.3,10.393L13.012,20.589l-4.281-4.196c-0.394-0.391-1.034-0.391-1.428,0 c-0.395,0.391-0.395,1.024,0,1.414l4.999,4.899c0.41,0.361,1.023,0.401,1.428,0l10.999-10.899c0.394-0.39,0.394-1.024,0-1.414 C24.334,10.003,23.695,10.003,23.3,10.393z" fill="#2db730" /> - </g> - </symbol> - <symbol id="icon-loading" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-rolling"> - <circle cx="50" cy="50" fill="none" stroke="currentColor" stroke-width="5" r="26" stroke-dasharray="122.52211349000194 42.840704496667314" transform="rotate(150 50 50)"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></circle> - </symbol> - <symbol id="icon-sort-down" viewBox="0 0 16 16"><g stroke-width="1.5" fill="none" fill-rule="evenodd"><path stroke="#CBCED1" d="M10.657 6L8 3.343 5.343 6"/><path stroke="#9EA2A8" d="M10.657 10L8 12.657 5.343 10"/></g></symbol> - <symbol id="icon-sort-up" viewBox="0 0 16 16"><g stroke-width="1.5" fill="none" fill-rule="evenodd"><path stroke="#CBCED1" d="M10.657 10L8 12.657 5.343 10"/><path stroke="#9EA2A8" d="M10.657 6L8 3.343 5.343 6"/></g></symbol> - <symbol id="icon-ban" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> - <path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zM103.265 408.735c-80.622-80.622-84.149-208.957-10.9-293.743l304.644 304.643c-84.804 73.264-213.138 69.706-293.744-10.9zm316.37-11.727L114.992 92.365c84.804-73.263 213.137-69.705 293.743 10.9 80.622 80.621 84.149 208.957 10.9 293.743z" /> - </symbol> + <symbol id="icon-add-reaction" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <g transform="translate(3 3)"> + <circle fill="currentColor" cx="9" cy="5" r="1" /> + <circle fill="currentColor" cx="5" cy="5" r="1" /> + <path d="M7 0a7 7 0 1 0 0 14 7 7 0 0 0 7-7M4.172 9.328a4 4 0 0 0 5.656 0" stroke="currentColor" stroke-width="1.5" /> + </g> + <path d="M16.2 1.2v5.2m-2.6-2.6h5.2" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-angle-down" viewBox="0 0 256 512"> + <path d="M119.5 326.9L3.5 209.1c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0L128 287.3l100.4-102.2c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L136.5 327c-4.7 4.6-12.3 4.6-17-.1z" /> + </symbol> + <symbol id="icon-arrow-down" viewBox="0 0 24 24"> + <path d="M18.585 12.1L12.5 18.185 6.415 12.1" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" /> + </symbol> + <symbol id="icon-at" viewBox="0 0 512 512"> + <path d="M256 8C118.941 8 8 118.919 8 256c0 137.058 110.919 248 248 248 52.925 0 104.68-17.078 147.092-48.319 5.501-4.052 6.423-11.924 2.095-17.211l-5.074-6.198c-4.018-4.909-11.193-5.883-16.307-2.129C346.93 457.208 301.974 472 256 472c-119.373 0-216-96.607-216-216 0-119.375 96.607-216 216-216 118.445 0 216 80.024 216 200 0 72.873-52.819 108.241-116.065 108.241-19.734 0-23.695-10.816-19.503-33.868l32.07-164.071c1.449-7.411-4.226-14.302-11.777-14.302h-12.421a12 12 0 0 0-11.781 9.718c-2.294 11.846-2.86 13.464-3.861 25.647-11.729-27.078-38.639-43.023-73.375-43.023-68.044 0-133.176 62.95-133.176 157.027 0 61.587 33.915 98.354 90.723 98.354 39.729 0 70.601-24.278 86.633-46.982-1.211 27.786 17.455 42.213 45.975 42.213C453.089 378.954 504 321.729 504 240 504 103.814 393.863 8 256 8zm-37.92 342.627c-36.681 0-58.58-25.108-58.58-67.166 0-74.69 50.765-121.545 97.217-121.545 38.857 0 58.102 27.79 58.102 65.735 0 58.133-38.369 122.976-96.739 122.976z" /> + </symbol> + <symbol id="icon-back" viewBox="0 0 20 20"> + <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> + <path d="M7.5 15.06L2.44 10 7.5 4.94" /> + <path d="M3.333 10h11.67c.918 0 1.664.74 1.664 1.667v2.916" /> + </g> + </symbol> + <symbol id="icon-ban" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> + <path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zM103.265 408.735c-80.622-80.622-84.149-208.957-10.9-293.743l304.644 304.643c-84.804 73.264-213.138 69.706-293.744-10.9zm316.37-11.727L114.992 92.365c84.804-73.263 213.137-69.705 293.743 10.9 80.622 80.621 84.149 208.957 10.9 293.743z" /> + </symbol> + <symbol id="icon-bell" viewBox="0 0 448 512"> + <path d="M433.884 366.059C411.634 343.809 384 316.118 384 208c0-79.394-57.831-145.269-133.663-157.83A31.845 31.845 0 0 0 256 32c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 6.75 2.095 13.008 5.663 18.17C121.831 62.731 64 128.606 64 208c0 108.118-27.643 135.809-49.893 158.059C-16.042 396.208 5.325 448 48.048 448H160c0 35.29 28.71 64 64 64s64-28.71 64-64h111.943c42.638 0 64.151-51.731 33.941-81.941zM224 480c-17.645 0-32-14.355-32-32h64c0 17.645-14.355 32-32 32zm175.943-64H48.048c-14.223 0-21.331-17.296-11.314-27.314C71.585 353.836 96 314.825 96 208c0-70.741 57.249-128 128-128 70.74 0 128 57.249 128 128 0 106.419 24.206 145.635 59.257 180.686C421.314 398.744 414.11 416 399.943 416z" /> + </symbol> + <symbol id="icon-bold" viewBox="0 0 20 20"> + <path d="M10.627 16.25H6.16V4.8h4.38c1.052 0 1.88.257 2.483.77.603.513.905 1.209.905 2.087a2.49 2.49 0 0 1-.576 1.603c-.383.476-.85.764-1.4.865v.126c.783.101 1.404.401 1.865.901.46.5.69 1.128.69 1.884 0 1.006-.343 1.792-1.028 2.361-.685.569-1.636.853-2.852.853zM7.587 6.062v3.674h2.286c.868 0 1.52-.154 1.956-.46.436-.307.655-.765.655-1.373 0-.582-.198-1.034-.592-1.357-.394-.323-.945-.484-1.654-.484h-2.65zm0 8.926h2.826c.862 0 1.515-.172 1.96-.515.444-.344.666-.847.666-1.508s-.231-1.16-.694-1.496c-.463-.335-1.152-.503-2.067-.503h-2.69v4.022z" fill-rule="evenodd" /> + </symbol> + <symbol id="icon-chat" viewBox="0 0 28 28"> + <path d="M11 6c-4.875 0-9 2.75-9 6 0 1.719 1.156 3.375 3.156 4.531l1.516 0.875-0.547 1.313c0.328-0.187 0.656-0.391 0.969-0.609l0.688-0.484 0.828 0.156c0.781 0.141 1.578 0.219 2.391 0.219 4.875 0 9-2.75 9-6s-4.125-6-9-6zM11 4c6.078 0 11 3.578 11 8s-4.922 8-11 8c-0.953 0-1.875-0.094-2.75-0.25-1.297 0.922-2.766 1.594-4.344 2-0.422 0.109-0.875 0.187-1.344 0.25h-0.047c-0.234 0-0.453-0.187-0.5-0.453v0c-0.063-0.297 0.141-0.484 0.313-0.688 0.609-0.688 1.297-1.297 1.828-2.594-2.531-1.469-4.156-3.734-4.156-6.266 0-4.422 4.922-8 11-8zM23.844 22.266c0.531 1.297 1.219 1.906 1.828 2.594 0.172 0.203 0.375 0.391 0.313 0.688v0c-0.063 0.281-0.297 0.484-0.547 0.453-0.469-0.063-0.922-0.141-1.344-0.25-1.578-0.406-3.047-1.078-4.344-2-0.875 0.156-1.797 0.25-2.75 0.25-2.828 0-5.422-0.781-7.375-2.063 0.453 0.031 0.922 0.063 1.375 0.063 3.359 0 6.531-0.969 8.953-2.719 2.609-1.906 4.047-4.484 4.047-7.281 0-0.812-0.125-1.609-0.359-2.375 2.641 1.453 4.359 3.766 4.359 6.375 0 2.547-1.625 4.797-4.156 6.266z" /> + </symbol> + <symbol id="icon-check" viewBox="0 0 448 512"> + <path d="M413.505 91.951L133.49 371.966l-98.995-98.995c-4.686-4.686-12.284-4.686-16.971 0L6.211 284.284c-4.686 4.686-4.686 12.284 0 16.971l118.794 118.794c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-11.314-11.314c-4.686-4.686-12.284-4.686-16.97 0z" /> + </symbol> + <symbol id="icon-checkmark-circled" viewBox="0 0 20 20"> + <path d="M486.4 1024c-129.922 0-252.067-50.594-343.936-142.464s-142.464-214.014-142.464-343.936c0-129.923 50.595-252.067 142.464-343.936s214.013-142.464 343.936-142.464c129.922 0 252.067 50.595 343.936 142.464s142.464 214.014 142.464 343.936-50.594 252.067-142.464 343.936c-91.869 91.87-214.014 142.464-343.936 142.464zM486.4 102.4c-239.97 0-435.2 195.23-435.2 435.2s195.23 435.2 435.2 435.2 435.2-195.23 435.2-435.2-195.23-435.2-435.2-435.2z" /> + <path d="M384 742.4c-6.552 0-13.102-2.499-18.102-7.499l-153.6-153.6c-9.997-9.997-9.997-26.206 0-36.203 9.998-9.997 26.206-9.997 36.205 0l135.498 135.498 340.299-340.298c9.997-9.997 26.206-9.997 36.203 0 9.998 9.998 9.998 26.206 0 36.205l-358.4 358.4c-5 4.998-11.55 7.498-18.102 7.498z" /> + </symbol> + <symbol id="icon-circle" viewBox="0 0 20 20"> + <circle cx="10" cy="10" r="9" fill="currentColor" fill-rule="evenodd" /> + </symbol> + <symbol id="icon-circle-check" viewBox="0 0 32 32"> + <g> + <path d="M16,0C7.163,0,0,7.163,0,16c0,8.837,7.163,16,16,16c8.836,0,16-7.164,16-16C32,7.163,24.836,0,16,0z M16,30 C8.268,30,2,23.732,2,16C2,8.268,8.268,2,16,2s14,6.268,14,14C30,23.732,23.732,30,16,30z" fill="#2db730" /> + <path d="M23.3,10.393L13.012,20.589l-4.281-4.196c-0.394-0.391-1.034-0.391-1.428,0 c-0.395,0.391-0.395,1.024,0,1.414l4.999,4.899c0.41,0.361,1.023,0.401,1.428,0l10.999-10.899c0.394-0.39,0.394-1.024,0-1.414 C24.334,10.003,23.695,10.003,23.3,10.393z" fill="#2db730" /> + </g> + </symbol> + <symbol id="icon-circle-cross" viewBox="0 0 256 256"> + <path d="M183.191,174.141c2.5,2.498,2.5,6.552,0,9.05c-1.249,1.25-2.889,1.875-4.525,1.875c-1.638,0-3.277-0.625-4.525-1.875 l-46.142-46.142L81.856,183.19c-1.249,1.25-2.888,1.875-4.525,1.875c-1.638,0-3.277-0.625-4.525-1.875c-2.5-2.498-2.5-6.552,0-9.05 l46.143-46.143L72.806,81.856c-2.5-2.499-2.5-6.552,0-9.05c2.497-2.5,6.553-2.5,9.05,0l46.142,46.142l46.142-46.142 c2.497-2.5,6.553-2.5,9.051,0c2.5,2.499,2.5,6.552,0,9.05l-46.143,46.142L183.191,174.141z M256,128C256,57.42,198.58,0,128,0 C57.42,0,0,57.42,0,128c0,70.58,57.42,128,128,128C198.58,256,256,198.58,256,128z M243.2,128c0,63.521-51.679,115.2-115.2,115.2 c-63.522,0-115.2-51.679-115.2-115.2C12.8,64.478,64.478,12.8,128,12.8C191.521,12.8,243.2,64.478,243.2,128z" fill="#ef0e0e" /> + </symbol> + <symbol id="icon-clip" viewBox="0 0 512 512"> + <path d="M149.106 512c-33.076 0-66.153-12.59-91.333-37.771-50.364-50.361-50.364-132.305-.002-182.665L319.842 29.498c39.331-39.331 103.328-39.331 142.66 0 39.331 39.332 39.331 103.327 0 142.657l-222.63 222.626c-28.297 28.301-74.347 28.303-102.65 0-28.3-28.301-28.3-74.349 0-102.649l170.301-170.298c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971l-170.3 170.297c-15.821 15.821-15.821 41.563.001 57.385 15.821 15.82 41.564 15.82 57.385 0l222.63-222.626c26.851-26.851 26.851-70.541 0-97.394-26.855-26.851-70.544-26.849-97.395 0L80.404 314.196c-37.882 37.882-37.882 99.519 0 137.401 37.884 37.881 99.523 37.882 137.404.001l217.743-217.739c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971L240.44 474.229C215.26 499.41 182.183 512 149.106 512z" /> + </symbol> + <symbol id="icon-clipboard" viewBox="0 0 384 512"> + <path d="M336 64h-88.581c.375-2.614.581-5.283.581-8 0-30.879-25.122-56-56-56s-56 25.121-56 56c0 2.717.205 5.386.581 8H48C21.49 64 0 85.49 0 112v352c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 400c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V112c0-8.822 7.178-16 16-16h48v20c0 6.627 5.373 12 12 12h168c6.627 0 12-5.373 12-12V96h48c8.822 0 16 7.178 16 16v352zM192 32c13.255 0 24 10.745 24 24s-10.745 24-24 24-24-10.745-24-24 10.745-24 24-24" /> + </symbol> + <symbol id="icon-clock" viewBox="0 0 512 512"> + <path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm216 248c0 118.7-96.1 216-216 216-118.7 0-216-96.1-216-216 0-118.7 96.1-216 216-216 118.7 0 216 96.1 216 216zm-148.9 88.3l-81.2-59c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h14c6.6 0 12 5.4 12 12v146.3l70.5 51.3c5.4 3.9 6.5 11.4 2.6 16.8l-8.2 11.3c-3.9 5.3-11.4 6.5-16.8 2.6z" /> + </symbol> + <symbol id="icon-code" viewBox="0 0 20 20"> + <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> + <path d="M5.833 13.88L1.953 10l3.88-3.88" /> + <path d="M11.661 3.8l-3.37 12.576L11.662 3.8z" stroke-linecap="square" /> + <path d="M14.167 6.12l3.88 3.88-3.88 3.88" /> + </g> + </symbol> + <symbol id="icon-cog" viewBox="0 0 20 20"> + <g transform="translate(1.667 2.5)" stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> + <circle cx="8.333" cy="7.5" r="2.083" /> + <path d="M6.47 14.883l1.863-1.966 1.864 1.966a7.568 7.568 0 0 0 2.04-.845l-.074-2.708 2.708.073a7.568 7.568 0 0 0 .846-2.04L13.75 7.5l1.967-1.864a7.568 7.568 0 0 0-.846-2.04l-2.708.074.073-2.708a7.568 7.568 0 0 0-2.04-.845L8.334 2.083 6.47.117a7.568 7.568 0 0 0-2.04.845l.073 2.708-2.707-.073a7.568 7.568 0 0 0-.846 2.04L2.917 7.5.95 9.364a7.56 7.56 0 0 0 .846 2.04l2.707-.074-.073 2.708a7.568 7.568 0 0 0 2.04.845z" /> + </g> + </symbol> + <symbol id="icon-computer" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M3 4h14v9H3z" /> + <path d="M7.5 16h5" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-copy" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M0 0h20v20H0z" /> + <path stroke="currentColor" stroke-width="1.5" d="M7 5h5l5 5v8H7z" /> + <path stroke="currentColor" stroke-width="1.5" d="M6.959 15H3V2h5l3.043 3.043M17 10h-5V5" /> + </g> + </symbol> + <symbol id="icon-cross" viewBox="0 0 20 20"> + <path d="M16.364 3.636l-6.018 6.018-6.71 6.71m12.728 0L3.636 3.636" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="square" /> + </symbol> + <symbol id="icon-cross-circled" viewBox="0 0 20 20"> + <path d="M733.808 723.266l-208.874-185.666 208.874-185.667c10.566-9.394 11.518-25.574 2.126-36.141-9.394-10.566-25.574-11.522-36.142-2.126l-213.392 189.682-213.392-189.68c-10.568-9.392-26.749-8.44-36.141 2.126-9.394 10.566-8.442 26.749 2.126 36.141l208.874 185.666-208.875 185.666c-10.566 9.394-11.518 25.574-2.126 36.142 5.059 5.691 12.085 8.592 19.142 8.592 6.048 0 12.122-2.131 16.998-6.466l213.394-189.683 213.392 189.683c4.878 4.334 10.949 6.466 16.998 6.466 7.058 0 14.086-2.902 19.144-8.592 9.392-10.568 8.44-26.749-2.126-36.142z" /> + <path d="M486.4 1024c-129.922 0-252.067-50.594-343.936-142.464s-142.464-214.014-142.464-343.936c0-129.923 50.595-252.067 142.464-343.936s214.013-142.464 343.936-142.464c129.922 0 252.067 50.595 343.936 142.464s142.464 214.014 142.464 343.936-50.594 252.067-142.464 343.936c-91.869 91.87-214.014 142.464-343.936 142.464zM486.4 102.4c-239.97 0-435.2 195.23-435.2 435.2s195.23 435.2 435.2 435.2 435.2-195.23 435.2-435.2-195.23-435.2-435.2-435.2z" /> + </symbol> + <symbol id="icon-cube" viewBox="0 0 512 512"> + <path d="M239.1 6.3l-208 78c-18.7 7-31.1 25-31.1 45v225.1c0 18.2 10.3 34.8 26.5 42.9l208 104c13.5 6.8 29.4 6.8 42.9 0l208-104c16.3-8.1 26.5-24.8 26.5-42.9V129.3c0-20-12.4-37.9-31.1-44.9l-208-78C262 2.2 250 2.2 239.1 6.3zM256 34.2l224 84v.3l-224 97.1-224-97.1v-.3l224-84zM32 153.4l208 90.1v224.7l-208-104V153.4zm240 314.8V243.5l208-90.1v210.9L272 468.2z" /> + </symbol> + <symbol id="icon-customize" viewBox="0 0 20 20"> + <g transform="translate(3 3)" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M0 7h8m4 0h2M0 2h2m3.996 0H14M0 12h3m4 0h7" stroke-linecap="square" /> + <circle cx="4" cy="2" r="2" /> + <circle cx="10" cy="7" r="2" /> + <circle cx="5" cy="12" r="2" /> + </g> + </symbol> + <symbol id="icon-discover" viewBox="0 0 20 20"> + <g transform="translate(1 1)" fill="none" fill-rule="evenodd"> + <path d="M0 0h18v18H0z" /> + <circle stroke="currentColor" stroke-width="1.5" cx="9" cy="9" r="6.75" /> + <path d="M9 15.75c1.5 0 3-3.022 3-6.75s-1.5-6.75-3-6.75S6 5.272 6 9s1.5 6.75 3 6.75z" stroke="currentColor" stroke-width="1.5" /> + <path d="M2.25 9c0 1.5 3.022 3 6.75 3s6.75-1.5 6.75-3S12.728 6 9 6 2.25 7.5 2.25 9z" stroke="currentColor" stroke-width="1.5" /> + </g> + </symbol> + <symbol id="icon-edit" viewBox="0 0 20 20"> + <g stroke-width="1.2" stroke="currentColor" fill="none" fill-rule="evenodd"> + <path d="M12.73 3.412c.78-.78 2.044-.78 2.83.006l.7.7c.783.783.788 2.047.005 2.83l-8.901 8.901-4.596 1.06 1.06-4.595 8.902-8.902z" /> + <path d="M11.24 5.609l2.829 2.828" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-edit-rounded" viewBox="0 0 576 512"> + <path d="M417.8 315.5l20-20c3.8-3.8 10.2-1.1 10.2 4.2V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V112c0-26.5 21.5-48 48-48h292.3c5.3 0 8 6.5 4.2 10.2l-20 20c-1.1 1.1-2.7 1.8-4.2 1.8H48c-8.8 0-16 7.2-16 16v352c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16V319.7c0-1.6.6-3.1 1.8-4.2zm145.9-191.2L251.2 436.8l-99.9 11.1c-13.4 1.5-24.7-9.8-23.2-23.2l11.1-99.9L451.7 12.3c16.4-16.4 43-16.4 59.4 0l52.6 52.6c16.4 16.4 16.4 43 0 59.4zm-93.6 48.4L403.4 106 169.8 339.5l-8.3 75.1 75.1-8.3 233.5-233.6zm71-85.2l-52.6-52.6c-3.8-3.8-10.2-4-14.1 0L426 83.3l66.7 66.7 48.4-48.4c3.9-3.8 3.9-10.2 0-14.1z" /> + </symbol> + <symbol id="icon-emoji" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M0 0h20v20H0z" /> + <circle fill="currentColor" cx="12" cy="8" r="1" /> + <circle fill="currentColor" cx="8" cy="8" r="1" /> + <circle stroke="currentColor" stroke-width="1.5" cx="10" cy="10" r="7" /> + <path d="M7.172 12.328a4 4 0 0 0 5.656 0" stroke="currentColor" stroke-width="1.5" /> + </g> + </symbol> + <symbol id="icon-eye-off" viewBox="0 0 20 20"> + <g fill="currentColor" fill-rule="nonzero"> + <path d="M10 3.99c-3.79.004-6.658 2.896-8.542 5.44a.62.62 0 0 0 0 .739c.679.919 1.482 1.873 2.41 2.725a.62.62 0 1 0 .84-.915c-.729-.668-1.371-1.421-1.95-2.172C4.533 7.51 7.014 5.235 10 5.232c.515 0 1.011.066 1.493.187a.62.62 0 1 0 .303-1.203A7.392 7.392 0 0 0 10 3.99zM14.14 5.32a.62.62 0 0 0-.327 1.145c1.33.867 2.47 2.11 3.422 3.342-1.776 2.294-4.254 4.562-7.234 4.566-1.22-.003-2.346-.37-3.389-.972a.62.62 0 1 0-.62 1.076c1.19.686 2.537 1.135 4.008 1.137 3.79-.004 6.659-2.895 8.543-5.44a.62.62 0 0 0 0-.738c-1.052-1.424-2.399-2.934-4.051-4.011a.62.62 0 0 0-.352-.106z"/> + <path d="M11.722 7.895a.62.62 0 0 0-.468.993c.188.257.298.57.298.914 0 .864-.687 1.552-1.552 1.552-.371 0-.706-.127-.972-.342a.62.62 0 1 0-.779.967 2.79 2.79 0 0 0 1.751.616 2.803 2.803 0 0 0 2.793-2.793c0-.612-.2-1.185-.536-1.646a.62.62 0 0 0-.535-.261z"/> + <path d="M18.057 1.107a.62.62 0 0 0-.427.187L1.492 17.432a.62.62 0 1 0 .878.879L18.508 2.172a.62.62 0 0 0-.45-1.065z"/> + </g> + </symbol> + <symbol id="icon-facebook" viewBox="0 0 20 20"> + <path d="M19 1.992v16.012c0 .55-.446.992-.992.992h-4.589v-6.967h2.339l.35-2.716h-2.693V7.577c0-.787.217-1.322 1.346-1.322H16.2v-2.43a19.082 19.082 0 0 0-2.098-.109c-2.073 0-3.495 1.266-3.495 3.592v2.005H8.26v2.716h2.347V19H1.992A.995.995 0 0 1 1 18.008V1.992C1 1.446 1.446 1 1.992 1h16.012c.55 0 .996.446.996.992z" /> + </symbol> + <symbol id="icon-file-document" viewBox="0 0 20 20"> + <g fill="#FFF" fill-rule="evenodd"> + <path opacity=".75" d="M1 5h7.637v7.637H1z" /> + <path d="M10.273 5h5.455v1.091h-5.455z" /> + <path opacity=".6" d="M10.273 8.273h7.091v1.091h-7.091zM10.273 11.546h8.728v1.091h-8.728zM1 14.819h15.82v1.091H1z" /> + </g> + </symbol> + <symbol id="icon-file-generic" viewBox="0 0 20 20"> + <g fill="#FFF" fill-rule="evenodd"> + <path d="M1 5h5.455v1.091H1z" /> + <path opacity=".6" d="M1 8.273h16.365v1.091H1zM1 11.546h18.001v1.091H1zM1 14.819h15.82v1.091H1z" /> + </g> + </symbol> + <symbol id="icon-file-pdf" viewBox="0 0 20 20"> + <path d="M8.848 2.08l11.217 15.147L2.24 19.732z" fill="#FFF" fill-rule="evenodd" opacity=".75" /> + </symbol> + <symbol id="icon-file-sheets" viewBox="0 0 20 20"> + <g transform="translate(1 1)" fill="#FFF" fill-rule="evenodd" opacity=".75"> + <circle cx="6.12" cy="6.12" r="6.12" /> + <path d="M4.32 5.04h12.96V18H4.32z" /> + </g> + </symbol> + <symbol id="icon-flag" viewBox="0 0 20 20"> + <path d="M12.928 6.063l3.263-3.77a.74.74 0 0 0 .112-.759.71.71 0 0 0-.647-.422H3.141v17.72h1.406v-7.876h11.11a.71.71 0 0 0 .646-.422.691.691 0 0 0-.112-.759l-3.263-3.713zM4.547 9.578V2.547h9.562l-2.643 3.065c-.225.254-.225.647 0 .929l2.643 3.065H4.547v-.028z" fill-rule="nonzero" fill="currentColor" /> + </symbol> + <symbol id="icon-github" viewBox="0 0 20 20"> + <path d="M7.019 15.128c0 .072-.083.13-.189.13-.12.011-.203-.047-.203-.13 0-.073.084-.13.189-.13.109-.012.203.046.203.13zm-1.128-.163c-.026.072.047.156.156.177.094.037.203 0 .225-.072.021-.073-.048-.156-.156-.189-.095-.025-.2.011-.225.084zm1.603-.062c-.105.025-.177.094-.167.178.011.072.106.12.214.094.106-.025.178-.094.167-.167-.01-.069-.108-.116-.214-.105zM9.882 1C4.849 1 1 4.82 1 9.853c0 4.023 2.532 7.466 6.15 8.678.464.083.627-.203.627-.439 0-.225-.01-1.466-.01-2.228 0 0-2.54.545-3.074-1.08 0 0-.413-1.057-1.008-1.329 0 0-.831-.57.058-.558 0 0 .903.072 1.4.936.795 1.4 2.126.997 2.645.758.084-.58.32-.983.58-1.223-2.027-.225-4.074-.519-4.074-4.009 0-.998.276-1.498.857-2.137-.095-.236-.403-1.208.094-2.463.758-.236 2.503.98 2.503.98a8.524 8.524 0 0 1 2.279-.31 8.49 8.49 0 0 1 2.278.31s1.745-1.22 2.504-.98c.497 1.259.188 2.227.094 2.463.58.642.936 1.143.936 2.137 0 3.501-2.137 3.78-4.165 4.01.334.286.617.83.617 1.683 0 1.222-.011 2.735-.011 3.033 0 .236.167.522.627.439 3.629-1.205 6.088-4.648 6.088-8.671C18.995 4.82 14.914 1 9.882 1zM4.527 13.513c-.048.037-.037.12.025.189.058.058.141.083.189.036.047-.036.036-.12-.026-.188-.058-.058-.141-.084-.188-.037zm-.392-.294c-.026.048.01.106.083.142.058.036.13.025.156-.025.026-.048-.01-.106-.083-.142-.073-.022-.13-.01-.156.025zm1.175 1.292c-.058.047-.036.156.047.225.084.083.189.094.236.036.047-.047.026-.156-.047-.225-.08-.083-.189-.094-.236-.036zm-.413-.533c-.058.036-.058.13 0 .214.058.083.156.12.203.083.058-.047.058-.141 0-.225-.051-.083-.145-.12-.203-.072z" /> + </symbol> + <symbol id="icon-gitlab" viewBox="0 0 20 20"> + <path d="M2.047 8.387l7.95 10.333-8.713-6.468a.701.701 0 0 1-.251-.773l1.014-3.092zm2.65-6.144a.352.352 0 0 0-.663 0L2.047 8.387h4.638L4.697 2.243zm1.988 6.144L9.998 18.72 13.31 8.387H6.685zm12.278 3.092l-1.014-3.092L9.998 18.72l8.714-6.468a.701.701 0 0 0 .25-.773zM15.96 2.243a.352.352 0 0 0-.663 0L13.31 8.387h4.638L15.96 2.243z" /> + </symbol> + <symbol id="icon-globe" viewBox="0 0 512 512"> + <path d="M504 256C504 118.815 392.705 8 256 8 119.371 8 8 118.74 8 256c0 136.938 111.041 248 248 248 136.886 0 248-110.987 248-248zm-41.625 64h-99.434c6.872-42.895 6.6-86.714.055-128h99.38c12.841 41.399 12.843 86.598-.001 128zM256.001 470.391c-30.732-27.728-54.128-69.513-67.459-118.391h134.917c-13.332 48.887-36.73 90.675-67.458 118.391zM181.442 320c-7.171-41.387-7.349-85.537.025-128h149.067c7.371 42.453 7.197 86.6.025 128H181.442zM256 41.617c33.557 30.295 55.554 74.948 67.418 118.383H188.582c11.922-43.649 33.98-88.195 67.418-118.383zM449.544 160h-93.009c-10.928-44.152-29.361-83.705-53.893-114.956C366.825 59.165 420.744 101.964 449.544 160zM209.357 45.044C184.826 76.293 166.393 115.847 155.464 160H62.456C91.25 101.975 145.162 59.169 209.357 45.044zM49.625 192h99.38c-6.544 41.28-6.818 85.1.055 128H49.625c-12.842-41.399-12.844-86.598 0-128zm12.831 160h93.122c11.002 44.176 29.481 83.824 53.833 114.968C144.875 452.786 91.108 409.738 62.456 352zm240.139 114.966c24.347-31.138 42.825-70.787 53.827-114.966h93.121c-28.695 57.827-82.504 100.802-146.948 114.966z" /> + </symbol> + <symbol id="icon-google" viewBox="0 0 20 20"> + <path d="M18.71 10.21c0 5.136-3.517 8.79-8.71 8.79a8.99 8.99 0 0 1-9-9c0-4.979 4.021-9 9-9 2.424 0 4.464.89 6.035 2.355l-2.45 2.355C10.381 2.62 4.422 4.941 4.422 10c0 3.14 2.508 5.683 5.578 5.683 3.564 0 4.9-2.555 5.11-3.88H10V8.709h8.568c.084.461.142.904.142 1.502z" /> + </symbol> + <symbol id="icon-hand-pointer" viewBox="0 0 448 512"> + <path d="M360.543 188.156c-17.46-28.491-54.291-37.063-82.138-19.693-15.965-20.831-42.672-28.278-66.119-20.385V60.25c0-33.222-26.788-60.25-59.714-60.25S92.857 27.028 92.857 60.25v181.902c-20.338-13.673-47.578-13.89-68.389 1.472-26.556 19.605-32.368 57.08-13.132 83.926l114.271 159.5C136.803 502.673 154.893 512 174 512h185.714c27.714 0 51.832-19.294 58.145-46.528l28.571-123.25a60.769 60.769 0 0 0 1.57-13.723v-87c0-45.365-48.011-74.312-87.457-53.343zM82.097 275.588l28.258 39.439a7.999 7.999 0 1 0 14.503-4.659V60.25c0-37.35 55.428-37.41 55.428 0V241.5a8 8 0 0 0 8 8h7.144a8 8 0 0 0 8-8v-36.25c0-37.35 55.429-37.41 55.429 0v36.25a8 8 0 0 0 8 8H274a8 8 0 0 0 8-8v-21.75c0-37.351 55.429-37.408 55.429 0v21.75a8 8 0 0 0 8 8h7.143a8 8 0 0 0 8-8c0-37.35 55.429-37.41 55.429 0v87c0 2.186-.25 4.371-.742 6.496l-28.573 123.251C383.717 471.055 372.626 480 359.715 480H174c-8.813 0-17.181-4.332-22.381-11.588l-114.283-159.5c-22.213-31.004 23.801-62.575 44.761-33.324zM180.285 401v-87a8 8 0 0 1 8-8h7.144a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.144a8 8 0 0 1-8-8zm78.572 0v-87a8 8 0 0 1 8-8H274a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.143a8 8 0 0 1-8-8zm78.572 0v-87a8 8 0 0 1 8-8h7.143a8 8 0 0 1 8 8v87a8 8 0 0 1-8 8h-7.143a8 8 0 0 1-8-8z" /> + </symbol> + <symbol id="icon-hashtag" viewBox="0 0 20 20"> + <path d="M7.5 4.167v11.666m5-11.666v11.666M4.167 7.5h11.666m-11.666 5h11.666" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="square" /> + </symbol> + <symbol id="icon-help" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <circle cx="10" cy="10" r="8.333" /> + <circle cx="10" cy="10" r="4.167" /> + <path d="M10 2.083v3.434m0 9.066v3.434m3.958-14.873l-1.716 2.973M7.708 13.97l-1.716 2.973M17.912 10.276l-3.431-.12M5.42 9.84l-3.432-.12m14.726 4.475l-2.912-1.82M6.113 7.571l-2.912-1.82" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-hubot" viewBox="0 0 20 20"> + <path d="M4.858 7.143A1.29 1.29 0 0 0 3.572 8.43v2.572a1.29 1.29 0 0 0 1.286 1.286h10.286A1.29 1.29 0 0 0 16.43 11V8.429a1.29 1.29 0 0 0-1.286-1.286H4.858zm10.286 2.25l-1.607 1.608h-1.929l-1.607-1.607L8.394 11h-1.93L4.859 9.394v-.965h.964l1.607 1.607L9.036 8.43h1.93l1.607 1.607L14.18 8.43h.964v.965zm-7.715 4.18h5.144v1.285H7.429v-1.285zM10.001 2C5.038 2 1 5.742 1 10.358v5.786a1.29 1.29 0 0 0 1.286 1.286h15.43a1.29 1.29 0 0 0 1.286-1.286v-5.786C19.002 5.742 14.964 2 10 2zm7.715 14.144H2.286v-5.786C2.286 6.385 5.68 3.17 10 3.17s7.715 3.215 7.715 7.188v5.786z" /> + </symbol> + <symbol id="icon-import" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M10 7.5v9.583" stroke-linecap="square" /> + <path d="M13.333 14.581l-3.291 3.292L6.75 14.58m.75-2.913H5V2.5h10v9.167h-2.5" /> + </g> + </symbol> + <symbol id="icon-info-circled" viewBox="0 0 512 512"> + <path d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z" /> + </symbol> + <symbol id="icon-italic" viewBox="0 0 20 20"> + <path d="M9.357 16.25h1.365V7.697H9.357v8.553zm.683-10.291a.91.91 0 0 0 .674-.282.922.922 0 0 0 .278-.67.918.918 0 0 0-.278-.675.918.918 0 0 0-.674-.277.922.922 0 0 0-.67.277.912.912 0 0 0-.282.675c0 .259.093.482.281.67a.916.916 0 0 0 .67.282z" fill-rule="evenodd" /> + </symbol> + <symbol id="icon-jump" viewBox="0 0 20 20"> + <path d="M11.6 7.257V4L18 9.7l-6.4 5.7v-3.257S2 11.385 2 16c0-9.23 9.6-8.743 9.6-8.743z" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd" /> + </symbol> + <symbol id="icon-key" viewBox="0 0 20 20"> + <g transform="matrix(-1 0 0 1 18 2)" fill="none" fill-rule="evenodd"> + <path d="M5.714 5.143c0 .944.15 1.654.448 2.13L.646 12.615 0 14.709 1.292 16l2.088-.642.41-.409v-1.475h1.475l.63-.63v-1.476h1.476l1.566-1.572c.582.327 1.223.49 1.92.49a5.143 5.143 0 0 0 5.141-5.289c-.075-2.684-2.312-4.92-4.995-4.995a5.143 5.143 0 0 0-5.288 5.14z" stroke="currentColor" stroke-width="1.5" /> + <circle fill="currentColor" cx="11.5" cy="4.5" r="1.5" /> + </g> + </symbol> + <symbol id="icon-keyboard" viewBox="0 0 576 512"> + <path d="M528 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h480c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.823-7.177 16-16 16H48c-8.823 0-16-7.177-16-16V112c0-8.823 7.177-16 16-16h480c8.823 0 16 7.177 16 16v288zM168 268v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-336 80v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm384 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zM120 188v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-96 152v-8c0-6.627-5.373-12-12-12H180c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h216c6.627 0 12-5.373 12-12z" /> + </symbol> + <symbol id="icon-language" viewBox="0 0 20 20"> + <path d="M18.778 11.018c-.562-1.536-2.13-2.453-4.195-2.453-.081 0-.159.001-.236.005l-.014-1.857 3.172-.546c.104-.017.12-.107.098-.208-.02-.1-.15-.795-.169-.878-.024-.118-.092-.115-.186-.098-.094.016-2.925.415-2.925.415l-.012-1.87c0-.113-.062-.143-.175-.141l-.922.014c-.095.002-.148.044-.146.134l.03 2.076s-2.755.474-2.83.489c-.075.012-.153.047-.136.128.017.081.171.985.187 1.055.017.072.065.116.17.096l2.631-.453.032 1.816a4.23 4.23 0 0 0-2.03 1.173 3.915 3.915 0 0 0-1.087 2.666c0 1.428.874 2.276 2.095 2.426 2.846.348 4.607-2.754 5.192-4.244.988 1.356.23 3.919-1.884 5.382-.039.026-.088.116-.03.187l.557.68c.072.086.186.053.23.02 2.26-1.556 3.295-4.063 2.583-6.014zm-6.648 2.87c-.87-.11-.85-.823-.85-1.308 0-.696.295-1.422.79-1.94a2.889 2.889 0 0 1 1.105-.72l.074 3.85c-.347.117-.72.166-1.119.117zm2.185-.498l.041-3.699c.076-.003.15-.009.227-.009.695 0 1.344.131 1.696.325.352.196-.92 2.442-1.964 3.383zM6.26 6.488a.176.176 0 0 0-.177-.13H4.328a.175.175 0 0 0-.174.13l-3.147 9.936c-.015.046-.01.069.056.069h1.56c.067 0 .089-.02.102-.065l.907-2.986H6.78l.907 2.986c.014.044.035.065.102.065h1.56c.065 0 .07-.023.056-.069-.012-.045-2.775-8.767-3.144-9.936zm-2.357 5.687L5.206 7.45l1.302 4.725H3.903z" /> + </symbol> + <symbol id="icon-linkedin" viewBox="0 0 20 20"> + <path d="M17.714 1H2.282C1.575 1 1 1.583 1 2.298v15.404C1 18.417 1.575 19 2.282 19h15.432c.707 0 1.286-.583 1.286-1.298V2.298C19 1.583 18.421 1 17.714 1zM6.44 16.429H3.772v-8.59h2.672v8.59H6.44zM5.106 6.665a1.548 1.548 0 0 1 0-3.094 1.55 1.55 0 0 1 1.547 1.547c0 .856-.69 1.547-1.547 1.547zm11.335 9.764h-2.668V12.25c0-.996-.02-2.278-1.386-2.278-1.39 0-1.604 1.085-1.604 2.206v4.25H8.116v-8.59h2.559v1.174h.036c.358-.675 1.23-1.387 2.527-1.387 2.7 0 3.203 1.78 3.203 4.095v4.709z" /> + </symbol> + <symbol id="icon-list" viewBox="0 0 512 512"> + <path d="M506 114H134a6 6 0 0 1-6-6V84a6 6 0 0 1 6-6h372a6 6 0 0 1 6 6v24a6 6 0 0 1-6 6zm6 154v-24a6 6 0 0 0-6-6H134a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h372a6 6 0 0 0 6-6zm0 160v-24a6 6 0 0 0-6-6H134a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h372a6 6 0 0 0 6-6zM84 120V72c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12zm0 160v-48c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12zm0 160v-48c0-6.627-5.373-12-12-12H24c-6.627 0-12 5.373-12 12v48c0 6.627 5.373 12 12 12h48c6.627 0 12-5.373 12-12z" /> + </symbol> + <symbol id="icon-list-alt" viewBox="0 0 512 512"> + <path d="M464 64c8.823 0 16 7.178 16 16v352c0 8.822-7.177 16-16 16H48c-8.823 0-16-7.178-16-16V80c0-8.822 7.177-16 16-16h416m0-32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zm-336 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 96c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm288-148v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6zm0 96v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6zm0 96v-24a6 6 0 0 0-6-6H198a6 6 0 0 0-6 6v24a6 6 0 0 0 6 6h212a6 6 0 0 0 6-6z" /> + </symbol> + <symbol id="icon-livechat" viewBox="0 0 20 20"> + <path d="M15.274 6.595c-.39-2.5-2.55-4.361-5.245-4.361-2.98 0-5.314 2.272-5.314 5.173v.262c-.001.016-.005.032-.005.05v5.121a2.577 2.577 0 0 1-2.397-2.563c0-.933.506-1.793 1.321-2.247a.572.572 0 0 0-.556-.999 3.717 3.717 0 0 0-1.908 3.246 3.718 3.718 0 0 0 3.714 3.714c.127 0 .256-.01.385-.023h.012c.019 0 .036-.003.054-.004h.004a.57.57 0 0 0 .514-.567V8.344c.002-.017.005-.033.005-.05v-.887c0-2.298 1.793-4.03 4.171-4.03S14.2 5.108 14.2 7.406v.237c-.003.024-.007.049-.007.075v5.519c-.001.015-.004.03-.004.044v1.286c0 .878-.614 1.491-1.492 1.491h-.77l-.016.002a1.49 1.49 0 0 0-1.388-.96h-.98a1.493 1.493 0 0 0-1.49 1.491c0 .823.668 1.49 1.49 1.491h.98c.606 0 1.126-.365 1.36-.886.015.002.029.006.045.006h.77c1.526 0 2.633-1.109 2.633-2.635v-.559a3.716 3.716 0 0 0 3.544-3.706 3.716 3.716 0 0 0-3.601-3.708zM10.523 16.94h-.98a.353.353 0 0 1-.347-.348c0-.189.16-.348.348-.348h.979c.188 0 .348.16.348.348 0 .189-.16.348-.348.348zm4.813-4.074V8.369c.003-.025.007-.05.007-.075v-.553a2.572 2.572 0 0 1 2.39 2.562c0 1.36-1.06 2.473-2.397 2.563z" fill-rule="nonzero" fill="currentColor" /> + </symbol> + <symbol id="icon-loading" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-rolling"> + <circle cx="50" cy="50" fill="none" stroke="currentColor" stroke-width="5" r="26" stroke-dasharray="122.52211349000194 42.840704496667314" transform="rotate(318 50 50)"> + <animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite" /> + </circle> + </symbol> + <symbol id="icon-lock" viewBox="0 0 20 20"> + <path d="M5 8h10v9H5zm2-3c0-1.657 1.347-3 3-3 1.657 0 3 1.347 3 3v3H7V5z" stroke="currentColor" stroke-width="1.5" fill="none" /> + </symbol> + <symbol id="icon-magnifier" viewBox="0 0 512 512"> + <path d="M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395 312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5 0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17 0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208 32s176 78.7 176 176-78.7 176-176 176z" /> + </symbol> + <symbol id="icon-mail" viewBox="0 0 512 512"> + <path d="M464 64H48C21.5 64 0 85.5 0 112v288c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM48 96h416c8.8 0 16 7.2 16 16v41.4c-21.9 18.5-53.2 44-150.6 121.3-16.9 13.4-50.2 45.7-73.4 45.3-23.2.4-56.6-31.9-73.4-45.3C85.2 197.4 53.9 171.9 32 153.4V112c0-8.8 7.2-16 16-16zm416 320H48c-8.8 0-16-7.2-16-16V195c22.8 18.7 58.8 47.6 130.7 104.7 20.5 16.4 56.7 52.5 93.3 52.3 36.4.3 72.3-35.5 93.3-52.3 71.9-57.1 107.9-86 130.7-104.7v205c0 8.8-7.2 16-16 16z" /> + </symbol> + <symbol id="icon-map-pin" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M10 17s5-4.15 5-9.027C15 5.226 12.761 3 10 3S5 5.226 5 7.973C5 12.85 10 17 10 17z" /> + <circle cx="10" cy="8" r="2" /> + </g> + </symbol> + <symbol id="icon-menu" viewBox="0 0 64 512"> + <path d="M32 224c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM0 136c0 17.7 14.3 32 32 32s32-14.3 32-32-14.3-32-32-32-32 14.3-32 32zm0 240c0 17.7 14.3 32 32 32s32-14.3 32-32-14.3-32-32-32-32 14.3-32 32z" /> + </symbol> + <symbol id="icon-message" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M11 18c1.219 0 7 .127 8-2 .506-1.076-2.891-1.076-2-3 .391-.943 1-1.915 1-3a8 8 0 1 0-16 0c0 4.418 3.582 8 9 8z" /> + <path d="M6.5 8.5h6.083m-6.083 3h7.083" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-mic" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M10 2.75A2.75 2.75 0 0 0 7.25 5.5v3a2.75 2.75 0 0 0 5.5 0v-3A2.75 2.75 0 0 0 10 2.75zM10 14v3" stroke="currentColor" stroke-width="1.5" /> + <path fill="currentColor" d="M7 17h6v1H7z" /> + <path d="M5 8c.049 4 1.716 6 5 6s4.951-2 5-6" stroke="currentColor" stroke-width="1.5" /> + </g> + </symbol> + <symbol id="icon-mobile" viewBox="0 0 320 512"> + <path d="M192 416c0 17.7-14.3 32-32 32s-32-14.3-32-32 14.3-32 32-32 32 14.3 32 32zM320 48v416c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V48C0 21.5 21.5 0 48 0h224c26.5 0 48 21.5 48 48zm-32 0c0-8.8-7.2-16-16-16H48c-8.8 0-16 7.2-16 16v416c0 8.8 7.2 16 16 16h224c8.8 0 16-7.2 16-16V48z" /> + </symbol> + <symbol id="icon-modal-error" viewBox="0 0 82 82"> + <g fill="currentColor" fill-rule="evenodd"> + <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> + <path d="M56.6 56.203a2.988 2.988 0 0 0 0-4.226L30.796 26.175a2.988 2.988 0 0 0-4.226 4.226l25.802 25.802a2.988 2.988 0 0 0 4.226 0z" /> + <path d="M26.57 56.203a2.988 2.988 0 0 0 4.227 0L56.6 30.401a2.988 2.988 0 0 0-4.226-4.226L26.571 51.977a2.988 2.988 0 0 0 0 4.226z" /> + </g> + </symbol> + <symbol id="icon-modal-info" viewBox="0 0 82 82"> + <g fill="currentColor" fill-rule="evenodd"> + <path d="M40.988 61.033a2.988 2.988 0 0 0 2.989-2.989V34.988a2.988 2.988 0 1 0-5.977 0v23.056a2.988 2.988 0 0 0 2.988 2.989zm-.106-34.021c1.947 0 3.382-1.52 3.382-3.493 0-2-1.435-3.519-3.382-3.519-1.946 0-3.382 1.52-3.382 3.52 0 1.972 1.436 3.492 3.382 3.492z" /> + <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> + </g> + </symbol> + <symbol id="icon-modal-success" viewBox="0 0 82 82"> + <g fill="currentColor" fill-rule="evenodd"> + <path d="M41 82C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41zm0-6.308c19.16 0 34.692-15.532 34.692-34.692S60.16 6.308 41 6.308 6.308 21.84 6.308 41 21.84 75.692 41 75.692z" fill-rule="nonzero" /> + <path d="M33.86 50.585l-8.671-8.723a2.446 2.446 0 0 0-3.47 0 2.475 2.475 0 0 0 0 3.49L31.76 55.45a3 3 0 0 0 4.23.026l23.716-23.277a2.444 2.444 0 0 0 .021-3.467 2.48 2.48 0 0 0-3.493-.025L33.861 50.585z" stroke="currentColor" /> + </g> + </symbol> + <symbol id="icon-modal-warning" viewBox="0 0 89 83"> + <g fill="currentColor" fill-rule="evenodd"> + <path d="M44.488 29a2.988 2.988 0 0 1 2.989 2.988v23.056a2.988 2.988 0 1 1-5.977 0V31.988A2.988 2.988 0 0 1 44.488 29zm-.106 34.021c1.947 0 3.382 1.52 3.382 3.493 0 2-1.435 3.519-3.382 3.519-1.946 0-3.382-1.52-3.382-3.52 0-1.972 1.436-3.492 3.382-3.492z" /> + <path d="M7.471 76.881H81.46L44.465 8.713 7.471 76.881zm78.4 5.626H3.06a3 3 0 0 1-2.637-4.431L41.828 1.778a3 3 0 0 1 5.274 0l41.405 76.298a3 3 0 0 1-2.637 4.43z" fill-rule="nonzero" /> + </g> + </symbol> + <symbol id="icon-multi-line" viewBox="0 0 20 20"> + <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> + <path d="M12.5 5h5v6.25H5" /> + <path d="M8.17 15.714l-4.42-4.42 4.42-4.419" /> + </g> + </symbol> + <symbol id="icon-mute" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M9.47 2.265A2.735 2.735 0 0 0 6.737 5v3.53a2.735 2.735 0 0 0 5.47 0V5A2.735 2.735 0 0 0 9.47 2.265zm0 11.559v3.529-3.53z" stroke="currentColor" stroke-width="1.5" /> + <path fill="currentColor" fill-rule="nonzero" d="M6.824 16.47h5.294v1.324H6.824z" /> + <path d="M4.176 8.53c.052 3.529 1.817 5.294 5.295 5.294 3.477 0 5.242-1.765 5.294-5.294" stroke="currentColor" stroke-width="1.5" /> + <path d="M18.238 2.353L1.529 16.676" stroke="currentColor" stroke-width="2.5" /> + </g> + </symbol> + <symbol id="icon-pause" viewBox="0 0 448 512"> + <path d="M48 479h96c26.5 0 48-21.5 48-48V79c0-26.5-21.5-48-48-48H48C21.5 31 0 52.5 0 79v352c0 26.5 21.5 48 48 48zM32 79c0-8.8 7.2-16 16-16h96c8.8 0 16 7.2 16 16v352c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V79zm272 400h96c26.5 0 48-21.5 48-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48zM288 79c0-8.8 7.2-16 16-16h96c8.8 0 16 7.2 16 16v352c0 8.8-7.2 16-16 16h-96c-8.8 0-16-7.2-16-16V79z" /> + </symbol> + <symbol id="icon-permalink" viewBox="0 0 20 20"> + <path d="M9.548 14.23l-2.651 2.652a2.676 2.676 0 0 1-3.78 0 2.676 2.676 0 0 1 0-3.78L6.91 9.311a2.677 2.677 0 0 1 3.781 0 .669.669 0 0 0 .945-.946 4.015 4.015 0 0 0-5.67 0l-3.792 3.792a4.014 4.014 0 0 0 0 5.67 4.014 4.014 0 0 0 5.67 0l2.65-2.65a.669.669 0 0 0-.945-.947zm8.28-12.057a4.014 4.014 0 0 0-5.67 0L9.506 4.824a.668.668 0 1 0 .946.945l2.651-2.651a2.676 2.676 0 0 1 3.78 0 2.676 2.676 0 0 1 0 3.78L13.09 10.69a2.678 2.678 0 0 1-3.781 0 .668.668 0 1 0-.945.945 4.015 4.015 0 0 0 5.67 0l3.793-3.792a4.014 4.014 0 0 0 0-5.67z" fill-rule="nonzero" fill="currentColor" /> + </symbol> + <symbol id="icon-pin" viewBox="0 0 384 512"> + <path d="M300.79 203.91L290.67 128H328c13.25 0 24-10.75 24-24V24c0-13.25-10.75-24-24-24H56C42.75 0 32 10.75 32 24v80c0 13.25 10.75 24 24 24h37.33l-10.12 75.91C34.938 231.494 0 278.443 0 335.24c0 8.84 7.16 16 16 16h160v120.779c0 .654.08 1.306.239 1.94l8 32c2.009 8.037 13.504 8.072 15.522 0l8-32a7.983 7.983 0 0 0 .239-1.94V351.24h160c8.84 0 16-7.16 16-16 0-56.797-34.938-103.746-83.21-131.33zM33.26 319.24c6.793-42.889 39.635-76.395 79.46-94.48L128 96H64V32h256v64h-64l15.28 128.76c40.011 18.17 72.694 51.761 79.46 94.48H33.26z" /> + </symbol> + <symbol id="icon-play" viewBox="0 0 448 512"> + <path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6zm-16.2 55.1l-352 208C45.6 483.9 32 476.6 32 464V47.9c0-16.3 16.4-18.4 24.1-13.8l352 208.1c10.5 6.2 10.5 21.4.1 27.6z" /> + </symbol> + <symbol id="icon-play-solid" viewBox="0 0 448 512"> + <path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z" /> + </symbol> + <symbol id="icon-plus" viewBox="0 0 20 20"> + <path d="M10 5v10m5-5H5" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="square" /> + </symbol> + <symbol id="icon-podcast" viewBox="0 0 448 512"> + <path d="M326.011 313.366a81.658 81.658 0 0 0-11.127-16.147c-1.855-2.1-1.913-5.215-.264-7.481C328.06 271.264 336 248.543 336 224c0-63.221-52.653-114.375-116.41-111.915-57.732 2.228-104.69 48.724-107.458 106.433-1.278 26.636 6.812 51.377 21.248 71.22 1.648 2.266 1.592 5.381-.263 7.481a81.609 81.609 0 0 0-11.126 16.145c-2.003 3.816-7.25 4.422-9.961 1.072C92.009 289.7 80 258.228 80 224c0-79.795 65.238-144.638 145.178-143.995 77.583.624 141.19 63.4 142.79 140.969.73 35.358-11.362 67.926-31.928 93.377-2.738 3.388-8.004 2.873-10.029-.985zM224 0C100.206 0 0 100.185 0 224c0 82.003 43.765 152.553 107.599 191.485 4.324 2.637 9.775-.93 9.078-5.945-1.244-8.944-2.312-17.741-3.111-26.038a6.025 6.025 0 0 0-2.461-4.291c-48.212-35.164-79.495-92.212-79.101-156.409.636-103.637 84.348-188.625 187.964-190.76C327.674 29.822 416 116.79 416 224c0 63.708-31.192 120.265-79.104 155.21a6.027 6.027 0 0 0-2.462 4.292c-.799 8.297-1.866 17.092-3.11 26.035-.698 5.015 4.753 8.584 9.075 5.947C403.607 376.922 448 306.75 448 224 448 100.204 347.814 0 224 0zm64 355.75c0 32.949-12.871 104.179-20.571 132.813C262.286 507.573 242.858 512 224 512c-18.857 0-38.286-4.427-43.428-23.438C172.927 460.134 160 388.898 160 355.75c0-35.156 31.142-43.75 64-43.75 32.858 0 64 8.594 64 43.75zm-32 0c0-16.317-64-16.3-64 0 0 27.677 11.48 93.805 19.01 122.747 6.038 2.017 19.948 2.016 25.981 0C244.513 449.601 256 383.437 256 355.75zM288 224c0 35.346-28.654 64-64 64s-64-28.654-64-64 28.654-64 64-64 64 28.654 64 64zm-32 0c0-17.645-14.355-32-32-32s-32 14.355-32 32 14.355 32 32 32 32-14.355 32-32z" /> + </symbol> + <symbol id="icon-post" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M0 0h20v20H0z" /> + <path stroke="currentColor" stroke-width="1.5" d="M5 3h6l4 4v10H5z" /> + <path d="M8.5 10.5h3M8.5 13.5h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-queue" viewBox="0 0 20 20"> + <path d="M9.98 11.894c-.1 0-.2-.025-.29-.077L1.493 7.161a.587.587 0 0 1-.01-1.017l8.238-4.899a.588.588 0 0 1 .6-.001l8.195 4.828a.588.588 0 0 1-.006 1.017l-8.237 4.727a.586.586 0 0 1-.293.078zM2.954 6.638l7.025 3.991 7.069-4.057-7.025-4.138-7.07 4.204z" /> + <path d="M9.98 15.172c-.1 0-.2-.025-.29-.076l-8.197-4.657a.588.588 0 1 1 .581-1.022l7.905 4.49 7.946-4.56a.588.588 0 1 1 .585 1.02l-8.237 4.727a.584.584 0 0 1-.293.078z" /> + <path d="M9.98 18.447c-.1 0-.2-.025-.29-.076l-8.197-4.657a.588.588 0 1 1 .581-1.022l7.905 4.49 7.946-4.56a.588.588 0 0 1 .585 1.02l-8.237 4.727a.584.584 0 0 1-.293.078z" /> + </symbol> + <symbol id="icon-quote" viewBox="0 0 20 20"> + <path d="M5.7 4.5a3.2 3.2 0 0 1 3.2 3.2c0 4.8-2.622 8-5.6 8 1.467-2.113 1.933-3.713 1.4-4.8a2.2 2.2 0 0 1-2.2-2.2v-1a3.2 3.2 0 0 1 3.2-3.2zm9 0a3.2 3.2 0 0 1 3.2 3.2c0 4.8-2.622 8-5.6 8 1.467-2.113 1.933-3.713 1.4-4.8a2.2 2.2 0 0 1-2.2-2.2v-1a3.2 3.2 0 0 1 3.2-3.2z" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linejoin="round" /> + </symbol> + <symbol id="icon-reload" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M5.905 14.322a6 6 0 1 0 0-8.485l-.873.873" /> + <path d="M9.981 7.417L5 6.98 5.436 2" /> + </g> + </symbol> + <symbol id="icon-reply" viewBox="0 0 576 512"> + <path d="M11.093 251.65l175.998 184C211.81 461.494 256 444.239 256 408v-87.84c154.425 1.812 219.063 16.728 181.19 151.091-8.341 29.518 25.447 52.232 49.68 34.51C520.16 481.421 576 426.17 576 331.19c0-171.087-154.548-201.035-320-203.02V40.016c0-36.27-44.216-53.466-68.91-27.65L11.093 196.35c-14.791 15.47-14.791 39.83 0 55.3zm23.127-33.18l176-184C215.149 29.31 224 32.738 224 40v120c157.114 0 320 11.18 320 171.19 0 74.4-40 122.17-76.02 148.51C519.313 297.707 395.396 288 224 288v120c0 7.26-8.847 10.69-13.78 5.53l-176-184a7.978 7.978 0 0 1 0-11.06z" /> + </symbol> + <symbol id="icon-send" viewBox="0 0 20 20"> + <path d="M17.28 1.186L1.562 10.253a1.123 1.123 0 0 0 .134 2.01l4.919 2.035v3.58c0 1.059 1.326 1.518 1.99.711L10.734 16l4.434 1.832a1.122 1.122 0 0 0 1.537-.867L18.951 2.33c.144-.937-.863-1.61-1.67-1.144zM7.738 17.877v-3.116l1.912.79-1.912 2.326zm7.86-1.084l-7.236-2.99 7-8.273c.169-.197-.101-.463-.298-.295L6.105 12.87l-3.982-1.642 15.72-9.07-2.247 14.635z" /> + </symbol> + <symbol id="icon-share" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M10 12.5V2.917" stroke-linecap="square" /> + <path d="M6.667 5.419l3.291-3.292L13.25 5.42m-.75 2.913H15V17.5H5V8.333h2.5" /> + </g> + </symbol> + <symbol id="icon-shield" viewBox="0 0 512 512"> + <path d="M466.5 83.7l-192-80a48.15 48.15 0 0 0-36.9 0l-192 80C27.7 91.1 16 108.6 16 128c0 198.5 114.5 335.7 221.5 380.3 11.8 4.9 25.1 4.9 36.9 0C360.1 472.6 496 349.3 496 128c0-19.4-11.7-36.9-29.5-44.3zM262.2 478.8c-3.9 1.6-8.3 1.6-12.3 0C152 440 48 304 48 128c0-6.5 3.9-12.3 9.8-14.8l192-80c3.8-1.6 8.3-1.7 12.3 0l192 80c6 2.5 9.8 8.3 9.8 14.8.1 176-103.9 312-201.7 350.8z" /> + </symbol> + <symbol id="icon-shield-alt" viewBox="0 0 512 512"> + <path d="M256 410.955V99.999l-142.684 59.452C123.437 279.598 190.389 374.493 256 410.955zm-32-66.764c-36.413-39.896-65.832-97.846-76.073-164.495L224 147.999v196.192zM466.461 83.692l-192-80a47.996 47.996 0 0 0-36.923 0l-192 80A48 48 0 0 0 16 128c0 198.487 114.495 335.713 221.539 380.308a48 48 0 0 0 36.923 0C360.066 472.645 496 349.282 496 128a48 48 0 0 0-29.539-44.308zM262.154 478.768a16.64 16.64 0 0 1-12.31-.001C152 440 48 304 48 128c0-6.48 3.865-12.277 9.846-14.769l192-80a15.99 15.99 0 0 1 12.308 0l192 80A15.957 15.957 0 0 1 464 128c0 176-104 312-201.846 350.768z" /> + </symbol> + <symbol id="icon-shield-check" viewBox="0 0 512 512"> + <path d="M466.461 83.692l-192-80a47.996 47.996 0 0 0-36.923 0l-192 80A48 48 0 0 0 16 128c0 198.487 114.495 335.713 221.539 380.308a48 48 0 0 0 36.923 0C360.066 472.645 496 349.282 496 128a48 48 0 0 0-29.539-44.308zM262.154 478.768a16.64 16.64 0 0 1-12.31-.001C152 440 48 304 48 128c0-6.48 3.865-12.277 9.846-14.769l192-80a15.99 15.99 0 0 1 12.308 0l192 80A15.957 15.957 0 0 1 464 128c0 176-104 312-201.846 350.768zm144.655-299.505l-180.48 179.032c-4.705 4.667-12.303 4.637-16.97-.068l-85.878-86.572c-4.667-4.705-4.637-12.303.068-16.97l8.52-8.451c4.705-4.667 12.303-4.637 16.97.068l68.976 69.533 163.441-162.13c4.705-4.667 12.303-4.637 16.97.068l8.451 8.52c4.668 4.705 4.637 12.303-.068 16.97z" /> + </symbol> + <symbol id="icon-sign-out" viewBox="0 0 20 20"> + <path d="M2.684 3h4.63c.232 0 .421.19.421.42v.282c0 .231-.19.42-.42.42H2.683a.563.563 0 0 0-.561.562v10.102c0 .309.252.562.56.562h4.631c.232 0 .421.189.421.42v.281c0 .232-.19.421-.42.421H2.683c-.93 0-1.684-.754-1.684-1.684V4.684A1.686 1.686 0 0 1 2.684 3zm9.787.684l-.25.25a.421.421 0 0 0 0 .595l4.63 4.61H7.034c-.231 0-.42.19-.42.42v.351c0 .232.189.421.42.421h9.819l-4.627 4.61a.421.421 0 0 0 0 .596l.25.249a.421.421 0 0 0 .595 0l5.77-5.753a.421.421 0 0 0 0-.596l-5.773-5.753a.421.421 0 0 0-.596 0z" /> + </symbol> + <symbol id="icon-snippet" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M5.833 13.88L1.953 10l3.88-3.88" /> + <path d="M11.941 2.756L8.06 17.244" stroke-linecap="square" /> + <path d="M14.167 6.12l3.88 3.88-3.88 3.88" /> + </g> + </symbol> + <symbol id="icon-sort" viewBox="0 0 448 512"> + <path d="M204.485 392l-84 84.485c-4.686 4.686-12.284 4.686-16.971 0l-84-84.485c-4.686-4.686-4.686-12.284 0-16.97l7.07-7.071c4.686-4.686 12.284-4.686 16.971 0L95 419.887V44c0-6.627 5.373-12 12-12h10c6.627 0 12 5.373 12 12v375.887l51.444-51.928c4.686-4.686 12.284-4.686 16.971 0l7.07 7.071c4.687 4.686 4.687 12.284 0 16.97zm100.492-220.355h61.547l15.5 44.317A12 12 0 0 0 393.351 224h11.552c8.31 0 14.105-8.243 11.291-16.062l-60.441-168A11.999 11.999 0 0 0 344.462 32h-16.924a11.999 11.999 0 0 0-11.291 7.938l-60.441 168c-2.813 7.82 2.981 16.062 11.291 16.062h11.271c5.12 0 9.676-3.248 11.344-8.088l15.265-44.267zm10.178-31.067l18.071-51.243c.853-2.56 1.776-5.626 2.668-8.743.871 3.134 1.781 6.219 2.644 8.806l17.821 51.18h-41.204zm-3.482 307.342c4.795-6.044-1.179 2.326 92.917-133.561a12.011 12.011 0 0 0 2.136-6.835V300c0-6.627-5.373-12-12-12h-113.84c-6.627 0-12 5.373-12 12v8.068c0 6.644 5.393 12.031 12.037 12.031 81.861.001 76.238.011 78.238-.026-2.973 3.818 4.564-7.109-92.776 133.303a12.022 12.022 0 0 0-2.142 6.847V468c0 6.627 5.373 12 12 12h119.514c6.627 0 12-5.373 12-12v-8.099c0-6.627-5.373-12-12-12-87.527-.001-81.97-.01-84.084.019z" /> + </symbol> + <symbol id="icon-sort" viewBox="0 0 320 512"> + <path d="M288 288H32c-28.4 0-42.8 34.5-22.6 54.6l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c20-20.1 5.7-54.6-22.7-54.6zM160 448L32 320h256L160 448zM32 224h256c28.4 0 42.8-34.5 22.6-54.6l-128-128c-12.5-12.5-32.8-12.5-45.3 0l-128 128C-10.7 189.5 3.6 224 32 224zM160 64l128 128H32L160 64z" /> + </symbol> + <symbol id="icon-sort-amount-down" viewBox="0 0 512 512"> + <path d="M204.485 392l-84 84.485c-4.686 4.686-12.284 4.686-16.971 0l-84-84.485c-4.686-4.686-4.686-12.284 0-16.97l7.07-7.071c4.686-4.686 12.284-4.686 16.971 0L95 419.887V44c0-6.627 5.373-12 12-12h10c6.627 0 12 5.373 12 12v375.887l51.444-51.928c4.686-4.686 12.284-4.686 16.971 0l7.07 7.071c4.687 4.686 4.687 12.284 0 16.97zM384 308v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h104c6.627 0 12-5.373 12-12zm64-96v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h168c6.627 0 12-5.373 12-12zm64-96v-8c0-6.627-5.373-12-12-12H268c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zM320 404v-8c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" /> + </symbol> + <symbol id="icon-sort-down" viewBox="0 0 16 16"> + <g stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path stroke="#CBCED1" d="M10.657 6L8 3.343 5.343 6" /> + <path stroke="#9EA2A8" d="M10.657 10L8 12.657 5.343 10" /> + </g> + </symbol> + <symbol id="icon-sort-up" viewBox="0 0 16 16"> + <g stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path stroke="#CBCED1" d="M10.657 10L8 12.657 5.343 10" /> + <path stroke="#9EA2A8" d="M10.657 6L8 3.343 5.343 6" /> + </g> + </symbol> + <symbol id="icon-star" viewBox="0 0 20 20"> + <path d="M10 15l-4.898 2.575.935-5.454-3.962-3.863 5.476-.796L10 2.5l2.45 4.962 5.475.796-3.962 3.863.935 5.454z" stroke-width="1.5" stroke="currentColor" /> + </symbol> + <symbol id="icon-strike" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M5.775 13.259c.074.989.498 1.78 1.273 2.372.775.593 1.768.889 2.98.889 1.312 0 2.352-.311 3.122-.933.77-.621 1.155-1.458 1.155-2.51 0-.842-.258-1.511-.774-2.008-.516-.498-1.366-.894-2.551-1.19L9.782 9.56c-.788-.2-1.353-.442-1.694-.722a1.35 1.35 0 0 1-.512-1.095c0-.582.231-1.047.694-1.396.463-.35 1.075-.524 1.837-.524.714 0 1.3.165 1.758.496.457.33.736.79.837 1.377h1.436c-.058-.926-.46-1.685-1.206-2.278-.746-.592-1.672-.888-2.777-.888-1.212 0-2.189.3-2.932.9-.743.6-1.115 1.387-1.115 2.36 0 .815.235 1.467.706 1.957.471.489 1.225.866 2.262 1.13l1.468.381c.788.196 1.367.455 1.737.778.37.322.556.727.556 1.214 0 .566-.253 1.035-.758 1.408s-1.144.56-1.916.56c-.815 0-1.487-.178-2.016-.532-.529-.355-.838-.83-.928-1.428H5.775z" fill="currentColor" /> + <path d="M4.375 10h11.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-team" viewBox="0 0 640 512"> + <path d="M573.127 249.095C584.979 233.127 592 213.369 592 192c0-52.935-43.065-96-96-96-26.331 0-50.217 10.658-67.578 27.885a128.993 128.993 0 0 0-17.913-22.394C386.334 77.314 354.19 64 320 64s-66.334 13.314-90.51 37.49a129.115 129.115 0 0 0-17.913 22.394C194.217 106.658 170.331 96 144 96c-52.935 0-96 43.065-96 96 0 21.369 7.021 41.127 18.873 57.095C28.987 255.378 0 288.36 0 328v44c0 24.262 19.738 44 44 44h117.677c5.238 18.445 22.222 32 42.323 32h232c20.102 0 37.085-13.555 42.323-32H596c24.262 0 44-19.738 44-44v-44c0-39.64-28.986-72.622-66.873-78.905zM496 128c35.346 0 64 28.654 64 64s-28.654 64-64 64c-22.083 0-41.554-11.185-53.057-28.199C446.27 216.314 448 204.291 448 192s-1.73-24.314-5.057-35.801C454.446 139.185 473.917 128 496 128zM320 96c53.02 0 96 42.981 96 96s-42.98 96-96 96-96-42.981-96-96 42.98-96 96-96zm-176 32c22.083 0 41.554 11.185 53.057 28.199C193.73 167.686 192 179.709 192 192s1.73 24.314 5.057 35.801C185.554 244.815 166.083 256 144 256c-35.346 0-64-28.654-64-64s28.654-64 64-64zm16 224v32H44c-6.627 0-12-5.373-12-12v-44c0-26.51 21.49-48 48-48h25.655c24.374 10.662 52.272 10.681 76.689 0h22.81C178.452 292.976 160 320.372 160 352zm288 52c0 6.627-5.373 12-12 12H204c-6.627 0-12-5.373-12-12v-52c0-26.51 21.49-48 48-48h17.929c37.818 21.031 85.208 21.651 124.142 0H400c26.51 0 48 21.49 48 48v52zm160-32c0 6.627-5.373 12-12 12H480v-32c0-31.628-18.452-59.024-45.154-72h22.81c24.374 10.662 52.272 10.681 76.689 0H560c26.51 0 48 21.49 48 48v44z" /> + </symbol> + <symbol id="icon-team" viewBox="0 0 640 512"> + <path d="M408.795 244.28C423.843 224.794 432 201.025 432 176c0-61.855-50.043-112-112-112-61.853 0-112 50.041-112 112 0 25.025 8.157 48.794 23.205 68.28-12.93 3.695-71.205 25.768-71.205 92.757v60.677C160 425.442 182.558 448 210.286 448h219.429C457.442 448 480 425.442 480 397.714v-60.677c0-66.985-58.234-89.051-71.205-92.757zM320 96c44.183 0 80 35.817 80 80s-35.817 80-80 80-80-35.817-80-80 35.817-80 80-80zm128 301.714c0 10.099-8.187 18.286-18.286 18.286H210.286C200.187 416 192 407.813 192 397.714v-60.677c0-28.575 18.943-53.688 46.418-61.538l20.213-5.775C276.708 281.614 297.862 288 320 288s43.292-6.386 61.369-18.275l20.213 5.775C429.057 283.35 448 308.462 448 337.037v60.677zm-304 0V384H45.714C38.14 384 32 377.86 32 370.286v-45.508c0-21.431 14.207-40.266 34.813-46.153l12.895-3.684C93.904 283.237 110.405 288 128 288a95.582 95.582 0 0 0 29.234-4.564c5.801-10.547 13.46-20.108 22.904-28.483 9.299-8.247 18.915-14.143 27.098-18.247C197.22 218.209 192 197.557 192 176c0-16.214 2.993-31.962 8.708-46.618C183.09 108.954 157.03 96 128 96c-52.935 0-96 43.065-96 96 0 21.776 7.293 41.878 19.558 58.003C25.677 259.796 0 286.423 0 324.778v45.508C0 395.493 20.507 416 45.714 416h100.871A66.078 66.078 0 0 1 144 397.714zM128 128c35.346 0 64 28.654 64 64s-28.654 64-64 64-64-28.654-64-64 28.654-64 64-64zm460.442 122.003C600.707 233.878 608 213.776 608 192c0-52.935-43.065-96-96-96-29.031 0-55.091 12.955-72.71 33.385C445.006 144.041 448 159.788 448 176c0 21.557-5.219 42.207-15.235 60.704 8.19 4.106 17.812 10.004 27.115 18.256 9.439 8.373 17.094 17.933 22.892 28.478A95.573 95.573 0 0 0 512 288c17.595 0 34.096-4.763 48.292-13.06l12.895 3.684C593.793 284.512 608 303.347 608 324.778v45.508c0 7.574-6.14 13.714-13.714 13.714H496v13.714c0 6.343-.914 12.473-2.585 18.286h100.871C619.493 416 640 395.493 640 370.286v-45.508c0-38.369-25.689-64.987-51.558-74.775zM512 256c-35.346 0-64-28.654-64-64s28.654-64 64-64 64 28.654 64 64-28.654 64-64 64z" /> + </symbol> + <symbol id="icon-th-list" viewBox="0 0 512 512"> + <path d="M0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48H48C21.49 32 0 53.49 0 80zm480 0v90.667H192V64h272c8.837 0 16 7.163 16 16zm0 229.333H192V202.667h288v106.666zM32 202.667h128v106.667H32V202.667zM160 64v106.667H32V80c0-8.837 7.163-16 16-16h112zM32 432v-90.667h128V448H48c-8.837 0-16-7.163-16-16zm160 16V341.333h288V432c0 8.837-7.163 16-16 16H192z" /> + </symbol> + <symbol id="icon-trash" viewBox="0 0 20 20 "> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M4 6h12l-1 12H5zm8-2.5a2 2 0 1 0-4 0" /> + <path d="M8 10v5m4-5v5" stroke-linecap="square" /> + <path d="M2.5 3.5h15" stroke-linecap="round" stroke-linejoin="round" /> + </g> + </symbol> + <symbol id="icon-twitter" viewBox="0 0 20 20"> + <path d="M17.13 6.634c.01.16.01.319.01.478 0 4.864-3.701 10.468-10.467 10.468-2.085 0-4.021-.604-5.65-1.652.296.035.58.046.888.046 1.72 0 3.304-.581 4.568-1.572a3.686 3.686 0 0 1-3.44-2.551c.228.034.456.056.695.056.33 0 .66-.045.968-.125a3.68 3.68 0 0 1-2.95-3.61v-.046c.49.273 1.06.444 1.663.467a3.677 3.677 0 0 1-1.64-3.064c0-.684.182-1.31.5-1.857a10.458 10.458 0 0 0 7.587 3.85 4.153 4.153 0 0 1-.091-.843A3.676 3.676 0 0 1 13.45 3a3.67 3.67 0 0 1 2.688 1.162 7.243 7.243 0 0 0 2.335-.889 3.67 3.67 0 0 1-1.617 2.028 7.376 7.376 0 0 0 2.118-.57 7.91 7.91 0 0 1-1.845 1.903z" /> + </symbol> + <symbol id="icon-upload" viewBox="0 0 20 20"> + <path d="M16.89 16.188a.705.705 0 0 1-.703.703.705.705 0 0 1-.703-.703c0-.387.317-.704.704-.704.386 0 .703.317.703.704zm-2.953-.704a.705.705 0 0 0-.703.704c0 .386.317.703.704.703a.705.705 0 0 0 .703-.703.705.705 0 0 0-.704-.704zM19 13.797v3.656c0 .854-.693 1.547-1.547 1.547H2.547A1.547 1.547 0 0 1 1 17.453v-3.656c0-.854.693-1.547 1.547-1.547h4.36V8.759H5.053c-1.252 0-1.878-1.515-.995-2.401L9.005 1.41a1.409 1.409 0 0 1 1.99 0l4.946 4.947c.886.886.257 2.401-.995 2.401h-1.852v3.491h4.36c.853 0 1.546.693 1.546 1.547zM8.031 7.634v6.585c0 .154.127.281.281.281h3.376a.282.282 0 0 0 .28-.281V7.634h2.978c.25 0 .377-.302.2-.482L10.2 2.206a.282.282 0 0 0-.397 0L4.857 7.152a.283.283 0 0 0 .2.482h2.974zm9.844 6.163a.423.423 0 0 0-.422-.422h-4.36v.844c0 .777-.629 1.406-1.405 1.406H8.312c-.776 0-1.406-.63-1.406-1.406v-.844h-4.36a.423.423 0 0 0-.421.422v3.656c0 .232.19.422.422.422h14.906c.232 0 .422-.19.422-.422v-3.656z" /> + </symbol> + <symbol id="icon-user" viewBox="0 0 20 20"> + <g fill="none" fill-rule="evenodd"> + <path d="M0 0h20v20H0z" /> + <path d="M4 15.106c0-3.783 4.5-3.026 4.5-4.54 0 0 .086-1.004-.41-1.513C7.473 8.423 7 7.665 7 6.405 7 4.525 8.343 3 10 3s3 1.524 3 3.405c0 1.243-.46 2.017-1.105 2.648-.472.496-.395 1.514-.395 1.514 0 1.513 4.5.756 4.5 4.54 0 0-1.195.893-6 .893s-6-.894-6-.894z" stroke="currentColor" stroke-width="1.5" /> + </g> + </symbol> + <symbol id="icon-user-plus" viewBox="0 0 640 512"> + <path d="M640 252v8c0 6.627-5.373 12-12 12h-68v68c0 6.627-5.373 12-12 12h-8c-6.627 0-12-5.373-12-12v-68h-68c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h68v-68c0-6.627 5.373-12 12-12h8c6.627 0 12 5.373 12 12v68h68c6.627 0 12 5.373 12 12zm-264.942 32.165l-43.497-12.428C355.115 245.342 368 211.663 368 176c0-79.525-64.339-144-144-144-79.525 0-144 64.339-144 144 0 35.663 12.885 69.342 36.439 95.737l-43.497 12.428C17.501 300.005 0 350.424 0 380.866v39.705C0 453.34 26.66 480 59.429 480h329.143C421.34 480 448 453.34 448 420.571v-39.705c0-57.659-43.675-88.339-72.942-96.701zM224 64c61.856 0 112 50.144 112 112s-50.144 112-112 112-112-50.144-112-112S162.144 64 224 64zm192 356.571C416 435.72 403.72 448 388.571 448H59.429C44.28 448 32 435.72 32 420.571v-39.705c0-30.616 20.296-57.522 49.733-65.933l63.712-18.203C168.611 311.87 195.679 320 224 320s55.389-8.13 78.555-23.27l63.712 18.203C395.704 323.344 416 350.251 416 380.866v39.705z" /> + </symbol> + <symbol id="icon-user-rounded" viewBox="0 0 20 20"> + <g transform="translate(2.5 2.5)" stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <rect x=".75" y=".75" width="13.5" height="13.5" rx="2" /> + <path d="M2.502 14.167c0-3.125 3.748-2.5 3.748-3.75 0 0 .072-.83-.34-1.25C5.394 8.646 5 8.02 5 6.979c0-1.553 1.12-2.812 2.5-2.812S10 5.426 10 6.979c0 1.027-.384 1.666-.922 2.188-.392.41-.328 1.25-.328 1.25 0 1.25 3.748.625 3.748 3.75" /> + </g> + </symbol> + <symbol id="icon-video" viewBox="0 0 20 20"> + <g stroke-width="1.5" stroke="currentColor" fill="none" fill-rule="evenodd"> + <path d="M3 8h10v8H3zm10 2.376l4.048-1.314c.526-.171.952.137.952.69v4.494c0 .552-.426.862-.952.69L13 13.624v-3.247z" /> + <path d="M5.5 5h5" stroke-linecap="square" /> + </g> + </symbol> + <symbol id="icon-volume" viewBox="0 0 576 512"> + <path d="M576 256c0 100.586-53.229 189.576-134.123 239.04-7.532 4.606-17.385 2.241-21.997-5.304-4.609-7.539-2.235-17.388 5.304-21.997C496.549 424.101 544 345.467 544 256c0-89.468-47.452-168.101-118.816-211.739-7.539-4.609-9.913-14.458-5.304-21.997 4.608-7.539 14.456-9.914 21.997-5.304C522.77 66.424 576 155.413 576 256zm-96 0c0-66.099-34.976-124.572-88.133-157.079-7.538-4.611-17.388-2.235-21.997 5.302-4.61 7.539-2.236 17.388 5.302 21.998C418.902 152.963 448 201.134 448 256c0 54.872-29.103 103.04-72.828 129.779-7.538 4.61-9.912 14.459-5.302 21.998 4.611 7.541 14.462 9.911 21.997 5.302C445.024 380.572 480 322.099 480 256zm-138.14-75.117c-7.538-4.615-17.388-2.239-21.998 5.297-4.612 7.537-2.241 17.387 5.297 21.998C341.966 218.462 352 236.34 352 256s-10.034 37.538-26.841 47.822c-7.538 4.611-9.909 14.461-5.297 21.998 4.611 7.538 14.463 9.909 21.998 5.297C368.247 314.972 384 286.891 384 256s-15.753-58.972-42.14-75.117zM256 88.017v335.964c0 21.436-25.942 31.999-40.971 16.971L126.059 352H24c-13.255 0-24-10.745-24-24V184c0-13.255 10.745-24 24-24h102.059l88.971-88.954C230.037 56.038 256 66.551 256 88.017zm-32 19.311l-77.659 77.644A24.001 24.001 0 0 1 129.372 192H32v128h97.372a24.001 24.001 0 0 1 16.969 7.028L224 404.67V107.328z" /> + </symbol> + <symbol id="icon-volume-mute" viewBox="0 0 640 512"> + <path d="M615.554 509.393L4.534 27.657c-5.188-4.124-6.051-11.673-1.927-16.861l4.978-6.263c4.124-5.188 11.673-6.051 16.861-1.927l611.021 481.736c5.188 4.124 6.051 11.673 1.927 16.861l-4.978 6.263c-4.125 5.189-11.674 6.051-16.862 1.927zM407.172 126.221C450.902 152.963 480 201.134 480 256c0 19.945-3.861 38.996-10.856 56.463l26.002 20.5C505.972 309.488 512 283.404 512 256c0-66.099-34.976-124.573-88.133-157.079-7.538-4.611-17.388-2.235-21.997 5.302-4.61 7.539-2.236 17.387 5.302 21.998zm-171.913 1.844L256 107.328v37.089l32 25.229v-81.63c0-21.466-25.963-31.979-40.97-16.971l-37.075 37.068 25.304 19.952zm221.925-83.804C528.548 87.899 576 166.532 576 256c0 42.442-10.685 82.442-29.529 117.428l25.467 20.078C594.94 352.775 608 305.811 608 256c0-100.587-53.23-189.576-134.123-239.04-7.541-4.61-17.389-2.235-21.997 5.304-4.609 7.539-2.235 17.387 5.304 21.997zM357.159 208.178c13.422 8.213 22.517 21.271 25.639 36.209l32.141 25.341a89.491 89.491 0 0 0 1.06-13.728c0-30.891-15.753-58.972-42.14-75.117-7.538-4.615-17.388-2.239-21.998 5.297-4.611 7.537-2.24 17.386 5.298 21.998zm128.318 239.41a248.52 248.52 0 0 1-28.293 20.151c-7.539 4.609-9.913 14.458-5.304 21.997 4.612 7.544 14.465 9.91 21.997 5.304a280.708 280.708 0 0 0 37.246-27.233l-25.646-20.219zM256 266.666V404.67l-77.659-77.643a24 24 0 0 0-16.969-7.028H64V192h97.296l-40.588-32H56c-13.255 0-24 10.745-24 24v144c0 13.255 10.745 24 24 24h102.059l88.971 88.952c15.029 15.028 40.97 4.465 40.97-16.971V291.895l-32-25.229zm151.123 119.147c-7.498 4.624-9.853 14.443-5.253 21.965 4.611 7.541 14.462 9.911 21.997 5.302a184.087 184.087 0 0 0 9.738-6.387l-26.482-20.88z" /> + </symbol> + <symbol id="icon-warning" viewBox="0 0 20 20"> + <g stroke="currentColor" stroke-width="1.5" fill="none" fill-rule="evenodd"> + <path d="M10 3l8 14H2z" /> + <path d="M10 9v5" stroke-linecap="square" /> + </g> + </symbol> </svg> diff --git a/packages/rocketchat-ui-message/client/popup/messagePopup.js b/packages/rocketchat-ui-message/client/popup/messagePopup.js index db9ed11f1d3a09b7913abe7255552d3ad7a1a12d..e04b317e880646593ca84c4739f0611d8738b270 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 fc2eae0c225d04603ac7b17e644eb1acc9954f1a..52ff78339509625b345ede3f50eab94fbc6b1c60 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html +++ b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.html @@ -1,4 +1,4 @@ <template name="messagePopupChannel"> - <i class="{{icon}}"></i> + {{> icon icon=channelIcon }} {{name}} </template> diff --git a/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js b/packages/rocketchat-ui-message/client/popup/messagePopupChannel.js index a3836b1fa177234324e4272fbd76c148ce8338e8..8686c9e6b65ea55ab732e7f4ce8052c103af2b68 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 c1f1ec4dd8133ad747cd54635630ea2e23b68595..f23ab768ea617bc3f97edc4d80bb5f12d1894b60 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html +++ b/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommand.html @@ -1,4 +1,6 @@ <template name="messagePopupSlashCommand"> - <strong>/{{_id}}</strong>{{#if params}} {{params}}{{/if}} - <div class="popup-slash-command-description"><i>{{description}}</i></div> -</template> \ No newline at end of file + <div class="popup-slash-command"> + <div class="popup-slash-command-format"><strong>/{{_id}}</strong>{{#if params}} {{params}}{{/if}}</div> + <div class="popup-slash-command-description">{{description}}</div> + </div> +</template> diff --git a/packages/rocketchat-ui-sidenav/client/roomList.js b/packages/rocketchat-ui-sidenav/client/roomList.js index 8088f8d67376c3df7be5625af9abe72d398f7f51..928d6683aca06d4a1f5042f9fb17998757777668 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 765a2867110ee5e32c93e72ee82e8cfdae766e3d..e8450d88a69ae4f37ebc91cc9c0c9cc9ed9b6d28 100644 --- a/packages/rocketchat-ui/client/views/app/directory.html +++ b/packages/rocketchat-ui/client/views/app/directory.html @@ -18,8 +18,8 @@ <th class="js-sort {{#if searchSortBy 'name'}}is-sorting{{/if}}" data-sort="name"> <div class="table-fake-th"><span>{{_ "Name"}}</span> {{> icon icon=(sortIcon 'name')}}</div> </th> - <th class="rc-directory-td--users"> - <div class="table-fake-th"><span>{{_ "Users"}}</span></div> + <th class="rc-directory-td--users js-sort {{#if searchSortBy 'usersCount'}}is-sorting{{/if}}" data-sort="usersCount"> + <div class="table-fake-th"><span>{{_ "Users"}}</span> {{> icon icon=(sortIcon 'usersCount')}}</div> </th> {{#if showLastMessage}} <th class="table-column-date"> diff --git a/packages/rocketchat-ui/client/views/app/directory.js b/packages/rocketchat-ui/client/views/app/directory.js index 70465f6a69307c49c72b94182ffca45896dfca37..3c8a3e68cc9f5c69b1285eba0b93b8b3603a57ac 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 985171edac32a5125b5f65da23895614384a782a..d1f56f6e8dcf189f2d5c4e8d55302cdd85371f51 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 36be779fed280cb4d3e0e8afee29fb7a8240632b..b846cad037e96047dec0868e428a923151e618d2 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 14a6d1a770f0b68ab1e519c61a4976d7ed4ac3ab..bf8978303065f19205b8deecd64e60ad4bcc1556 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 6f8be8edf325f3d8354aba3bf89429c45e913ca1..f92dad8d7523b96e62c4aee907baaa533db160eb 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 b8dcea5077e36af254dce024d389b644ae68b6e2..9a8d03c57f97552ae6f19761fe06c23e6884b03d 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 c481160caa4d018fbab885fb0e20dc16c7e8c81d..8c21d855d0312078c99146527c0b6f65b1e8ceca 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 116e7cfe8d97e7f3b9f51e00372830b4e404fd8c..67f6baff54c3518d4f1f36969207861a422989da 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 1228eda83278f9a4b2ae6df2e566bc185786ff1f..bff4f28cda25895ec8b8ffc78c3da45cabb1bbe6 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 3a6bc5c2e66ab445e43b2a5f145a3f3ac122b83f..e65391cc2112a22127bc4c56cc3e9c30ecc1cc7f 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 a235e9bf44cd348d26951995a18a7f67c12d8b03..0000000000000000000000000000000000000000 --- 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 76ef737b2eef246cb05dabd2a0d0ddfbaafdc20f..cd7c0cde20f95b8765c99dbf464bfa9f2cc48d1e 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 862ee9a9511df1bacc5580bf23f04fbd5298a6ab..e6d2b42dba7c75367006eb0844da8bb59fd0e0b7 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 4a2984d4ebf6e9384e6897d45f5d54c86a3cde3b..47c7f6a18b9ff0b1324d464e1857ae0bcb01a93c 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 3c75b44921f3e36df9d9c726af8aa3cff8e75140..dce8f868c9091fac4ecdbc45cb754176290d309d 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 fec41bbeafdcedafe1114b4481a5389d4b1888dd..32f3a8fe717dc0c19be49717f0044224fa98ebf7 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 a2bfa46da5e8ac8a949866664ec6cbf14fa11443..7d97405597d0dc90c65db2aece7d1b6cdeda3e2b 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 85708e9f7369589cefcc559fb0f042a493a3b616..57e6c447b89ed80d9d266fd345ec26c4e055c57e 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 88758879c124b6d5064e17dff0909823c9478529..2ab526a6775b02fd1311db26ad60271f214b73eb 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 bada6f920803792bfaf763d2ad7a38f4af0d1eca..97facd5389b819bb30b5d76fdaa21991d160aebc 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 0000000000000000000000000000000000000000..80b3f63a52a73da0abeaa00e92515272f65b8f3c --- /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 20fb57c259e01ff400e9b677ea716e472d20d361..0000000000000000000000000000000000000000 --- 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 71c68bcdfe0dc376c17ba1e3792ea3eeb563c51f..4f5537cde49b2af38acf806540d8a1f350321f7d 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 871ad79bcf7c2aee0a039672aec00ae42a39dd3e..26ac0426d051ff7d39d1a072006a46af42ef7616 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 84ee725275549db4f5f25b3015e4620716f5f8a4..c17cf64684a019764c793ae74d159f79a6b21ec5 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 c7bb2e890355e8a30405182579c55c3e371138bb..734aecdd2c2aec6b438ed51daa02031fb06f945a 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); });