Merge remote-tracking branch 'origin/release-candidate' into release-3.3.0

# Conflicts:
#	.docker/Dockerfile.rhel
#	.github/history.json
#	HISTORY.md
#	app/lib/server/startup/settings.js
#	app/utils/rocketchat.info
#	package-lock.json
#	package.json
pull/17762/head
Rodrigo Nascimento 6 years ago
commit abd5ebf0aa
  1. 2
      .docker/Dockerfile.rhel
  2. 239
      .github/CONTRIBUTING.md
  3. 45
      .github/PULL_REQUEST_TEMPLATE.md
  4. 1312
      .github/history.json
  5. 2
      .github/workflows/build_and_test.yml
  6. 0
      .houston/metadata.js
  7. 2
      .houston/templates/prs.hbs
  8. 13
      .houston/templates/versions.hbs
  9. 40
      .meteor/packages
  10. 2
      .meteor/release
  11. 66
      .meteor/versions
  12. 3
      .postcssrc
  13. 2
      .scripts/start.js
  14. 2
      .snapcraft/resources/prepareRocketChat
  15. 2
      .snapcraft/snap/snapcraft.yaml
  16. 19
      .storybook/.babelrc
  17. 4
      .storybook/addons.js
  18. 20
      .storybook/babel.config.js
  19. 20
      .storybook/config.js
  20. 11
      .storybook/main.js
  21. 2
      .storybook/mocks/decorators.js
  22. 16
      .storybook/mocks/meteor.js
  23. 7
      .storybook/preview.js
  24. 38
      .storybook/webpack.config.js
  25. 475
      HISTORY.md
  26. 11
      README.md
  27. 1
      app/accounts/index.js
  28. 3
      app/action-links/client/index.js
  29. 8
      app/action-links/client/init.js
  30. 65
      app/action-links/client/lib/actionLinks.js
  31. 8
      app/action-links/index.js
  32. 2
      app/action-links/server/actionLinkHandler.js
  33. 2
      app/action-links/server/index.js
  34. 2
      app/action-links/server/lib/actionLinks.js
  35. 1
      app/api/index.js
  36. 2
      app/api/server/api.js
  37. 28
      app/api/server/lib/rooms.js
  38. 2
      app/api/server/v1/assets.js
  39. 2
      app/api/server/v1/chat.js
  40. 2
      app/api/server/v1/permissions.js
  41. 43
      app/api/server/v1/rooms.js
  42. 2
      app/api/server/v1/settings.js
  43. 4
      app/api/server/v1/users.js
  44. 26
      app/apps/client/admin/appManage.html
  45. 6
      app/apps/client/admin/appManage.js
  46. 4
      app/apps/client/admin/apps.js
  47. 3
      app/apps/client/admin/helpers.js
  48. 28
      app/apps/client/admin/marketplace.js
  49. 2
      app/apps/client/orchestrator.js
  50. 2
      app/apps/client/routes.js
  51. 4
      app/apps/server/bridges/internal.js
  52. 107
      app/apps/server/bridges/listeners.js
  53. 15
      app/apps/server/bridges/messages.js
  54. 5
      app/apps/server/bridges/users.js
  55. 2
      app/apps/server/converters/messages.js
  56. 3
      app/apps/server/converters/rooms.js
  57. 2
      app/apps/server/index.js
  58. 22
      app/apps/server/orchestrator.js
  59. 1
      app/assets/index.js
  60. 2
      app/authorization/client/route.js
  61. 2
      app/authorization/client/startup.js
  62. 209
      app/authorization/server/startup.js
  63. 4
      app/autotranslate/client/views/autoTranslateFlexTab.html
  64. 2
      app/autotranslate/server/autotranslate.js
  65. 1
      app/bigbluebutton/index.js
  66. 1
      app/bigbluebutton/server/index.js
  67. 2
      app/blockstack/server/routes.js
  68. 1
      app/bot-helpers/index.js
  69. 4
      app/channel-settings-mail-messages/client/views/mailMessagesInstructions.js
  70. 4
      app/channel-settings-mail-messages/server/lib/startup.js
  71. 2
      app/channel-settings/client/views/Multiselect.js
  72. 2
      app/channel-settings/client/views/channelSettings.html
  73. 6
      app/channel-settings/server/startup.js
  74. 2
      app/chatpal-search/client/route.js
  75. 2
      app/chatpal-search/client/template/admin.html
  76. 27
      app/chatpal-search/server/provider/provider.js
  77. 1
      app/chatpal-search/server/utils/settings.js
  78. 6
      app/chatpal-search/server/utils/utils.js
  79. 16
      app/cloud/client/admin/callback.html
  80. 46
      app/cloud/client/admin/callback.js
  81. 145
      app/cloud/client/admin/cloud.html
  82. 233
      app/cloud/client/admin/cloud.js
  83. 26
      app/cloud/client/admin/cloudRegisterManually.css
  84. 36
      app/cloud/client/admin/cloudRegisterManually.html
  85. 106
      app/cloud/client/admin/cloudRegisterManually.js
  86. 2
      app/cloud/client/admin/index.js
  87. 33
      app/cloud/client/index.js
  88. 2
      app/cloud/server/index.js
  89. 27
      app/cors/server/cors.js
  90. 8
      app/crowd/server/crowd.js
  91. 1
      app/crowd/server/settings.js
  92. 111
      app/custom-sounds/assets/stylesheets/customSoundsAdmin.css
  93. 7
      app/custom-sounds/client/admin/adminSoundEdit.html
  94. 7
      app/custom-sounds/client/admin/adminSoundInfo.html
  95. 78
      app/custom-sounds/client/admin/adminSounds.html
  96. 176
      app/custom-sounds/client/admin/adminSounds.js
  97. 11
      app/custom-sounds/client/admin/route.js
  98. 25
      app/custom-sounds/client/admin/soundEdit.html
  99. 155
      app/custom-sounds/client/admin/soundEdit.js
  100. 19
      app/custom-sounds/client/admin/soundInfo.html
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,6 +1,6 @@
FROM registry.access.redhat.com/rhscl/nodejs-8-rhel7
ENV RC_VERSION 3.2.2
ENV RC_VERSION 3.3.0-rc.4
MAINTAINER buildmaster@rocket.chat

@ -1,42 +1,253 @@
# Contributing to Rocket.Chat
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
**First off, thanks for taking the time to contribute! :tada::+1:**
The following is a set of guidelines for contributing to Rocket.Chat and its packages, which are hosted in the [Rocket.Chat Organization](https://github.com/RocketChat) on GitHub.
> There are many ways to contribute to Rocket.Chat even if you're not technical or a developer:
>
> * Email us at marketing@rocket.chat to tell us how much you love the project
> * Write about us in your blogs
> * Fix some small typos in our [documentation](https://docs.rocket.chat/contributing)
> * Become our [GitHub sponsor](https://github.com/sponsors/RocketChat)
> * Tell others about us and help us spread the word
>
> Every bit of contribution is appreciated 🙂 thank you!
The following is a set of guidelines for contributing to Rocket.Chat, which are hosted in the [Rocket.Chat Organization](https://github.com/RocketChat) on GitHub.
__Note:__ If there's a feature you'd like, there's a bug you'd like to fix, or you'd just like to get involved please raise an issue and start a conversation. We'll help as much as we can so you can get contributing - although we may not always be able to respond right away :)
## ECMAScript 2015 vs CoffeeScript
## Setup
Your development workstation needs to have at least 8GB or RAM to be able to build the Rocket.Chat's source code.
Rocket.Chat runs on top of [Meteor](https://www.meteor.com/). To run it on development mode you need to [install Meteor](https://www.meteor.com/install) and clone/download the Rocket.Chat's code, then just open the code folder and run:
```shell
meteor npm install && meteor
```
It should build and run the application and database for you, now you can access the UI on (http://localhost:3000)
It's not necessary to install Nodejs or NPM, every time you need to use them you can run `meteor node` or `meteor npm`.
It's important to always run the NPM commands using `meteor npm` to ensure that you are installing the modules using the right Nodejs version.
## Coding
We provide a [.editorconfig](../.editorconfig) file that will help you to keep some standards in place.
### ECMAScript vs TypeScript
We are currently adopting TypeScript as the default language on our projects, the current codebase will be migrated incrementally from JavaScript to TypeScript.
While we still have a lot of JavaScript files you should not create new ones. As much as possible new code contributions should be in **TypeScript**.
While we still have a lot of CoffeeScript files you should not create new ones. New code contributions should be in **ECMAScript 2015**.
### Blaze vs React
## Coding standards
We are currently adopting React over Blaze as our UI engine, the current codebase is under migration and will continue. You will still find Blaze templates in our code. Code changes or contributions may need to be made in Blaze while we continue to evolve our components library.
Most of the coding standards are covered by `.editorconfig` and `.eslintrc.js`.
[Fuselage](https://github.com/RocketChat/Rocket.Chat.Fuselage) is our component library based on React, check it out when contributing to the Rocket.Chat UI and feel free to contribute new components or fixes.
### Standards
Most of the coding standards are covered by ESLint configured at [.eslintrc](../.eslintrc), and most of them came from our own [ESLint Config Package](https://github.com/RocketChat/eslint-config-rocketchat).
Things not covered by `eslint`:
* `exports`/`module.exports` should be at the end of the file
* Longer, descriptive variable names are preferred, e.g. `error` vs `err`
* Prefer longer/descriptive variable names, e.g. `error` vs `err`, unless dealing with common record properties already shortened, e.g. `rid` and `uid`
* Use return early pattern. [See more](https://blog.timoxley.com/post/47041269194/avoid-else-return-early)
* Prefer `Promise` over `callbacks`
* Prefer `await` over `then/catch`
* Don't create queries outside models, the query description should be inside the model class.
* Don't hardcode fields inside models. Same method can be used for different purposes, using different fields.
* Prefer create REST endpoints over Meteor methods
* Prefer call REST endpoints over Meteor methods when both are available
* v1 REST endpoints should follow the following pattern: `/api/v1/dashed-namespace.camelCaseAction`
* Prefer TypeScript over JavaScript. Check [ECMAScript vs TypeScript](#ecmascript-vs-typescript)
We acknowledge all the code does not meet these standards but we are working to change this over time.
#### Blaze
* Import the HTML file from it's sibling JS/TS file
### Syntax check
Before submitting a PR you should get no errors on `eslint`.
To check your files, first install `eslint`:
To check your files run:
```shell
meteor npm run lint
```
## Tests
There are 2 types of tests we run on Rocket.Chat, **Unit** tests and **End to End** tests. The major difference is that End to End tests require a Rocket.Chat instance running to execute the API and UI checks.
### End to End Tests
First you need to run a Rocket.Chat server on **Test Mode** and on a **Empty Database**:
```shell
# Running with a local mongodb database
MONGO_URL=mongodb://localhost/empty MONGO_OPLOG_URL=mongodb://localhost/local TEST_MODE=true meteor
```
```shell
# Running with a local mongodb database but cleaning it before
mongo --eval "db.dropDatabase()" empty && MONGO_URL=mongodb://localhost/empty MONGO_OPLOG_URL=mongodb://localhost/local TEST_MODE=true meteor
```
Now you can run the tests:
```shell
meteor npm test
```
### Unit Tests
Unit tests are simpler to setup and run. They do not require a working Rocket.Chat instance.
```shell
meteor npm run testunit
```
It's possible to run on watch mode as well:
```shell
meteor npm run testunit-watch
```
<!-- ### Storybook -->
## Before Push your code
It's important to run the lint and tests before push your code or submit a Pull Request, otherwise your contribution may fail quickly on the CI. Reviewers are forced to demand fixes and the review of your contribution will be further delayed.
Rocket.Chat uses [husky](https://www.npmjs.com/package/husky) to run the **lint** and **unit tests** before proceed to the code push process, so you may notice a delay when pushing your code to your repository.
## Choosing a good PR title
It is very important to note that we use PR titles when creating our change log. Keep this in mind when you title your PR. Make sure the title makes sense to a person reading a releases' change log!
Keep your PR's title as short and concise as possible, use PR's description section, which you can find in the PR's template, to provide more details into the changelog.
Good titles require thinking from a user's point of view. Don't get technical and talk code or architecture. What is the actual user-facing feature or the bug fixed? For example:
```
[NEW] Allow search permissions and settings by name instead of only ID
```
Even it's being something new in the code the users already expect the filter to filter by what they see (translations), a better one would be:
```
[FIX] Permissions' search doesn't filter base on presented translation, only on internal ids
```
## Choosing the right PR tag
You can use several tags do describe your PR, i.e.: `[FIX]`, `[NEW]`, etc. You can use the descriptions below to better understand the meaning of each one, and decide which one you should use:
### `[NEW]`
#### When
- When adding a new feature that is important to the end user
#### How
Do not start repeating the section (`Add ...` or `New ...`)
Always describe what's being fixed, improved or added and not *how* it was fixed, improved or added.
Exemple of **bad** PR titles:
```
[NEW] Add ability to set tags in the Omnichannel room closing dialog
[NEW] Adds ability for Rocket.Chat Apps to create discussions
[NEW] Add MMS support to Voxtelesys
[NEW] Add Color variable to left sidebar
```
Exemple of **good** PR titles:
```
npm install -g eslint
[NEW] Ability to set tags in the Omnichannel room closing dialog
[NEW] Ability for Rocket.Chat Apps to create discussions
[NEW] MMS support to Voxtelesys
[NEW] Color variable to left sidebar
```
Then run:
### `[FIX]`
#### When
- When fixing something not working or behaving wrong from the end user perspective
#### How
Always describe what's being fixed and not *how* it was fixed.
Exemple of a **bad** PR title:
```
eslint .
[FIX] Add Content-Type for public files with JWT
```
# Contributor License Agreement
Exemple of a **good** PR title:
```
[FIX] Missing Content-Type header for public files with JWT
```
### `[IMPROVE]`
#### When
- When a change enhances a not buggy behavior. When in doubt if it's a Improve or Fix prefer to use as fix.
#### How
Always describe what's being improved and not *how* it was improved.
Exemple of **good** PR title:
```
[IMPROVE] Displays Nothing found on admin sidebar when search returns nothing
```
### `[BREAK]`
#### When
- When the changes affect a working feature
##### Back-End
- When the API contract (data structure and endpoints) are limited, expanded as required or removed
- When the business logic (permissions and roles) are limited, expanded (without migration) or removed
##### Front-End
- When the change limits (format, size, etc) or removes the ability of read or change the data (when the limitation was not caused by the back-end)
### Second tag e.g. `[NEW][ENTERPRISE]`
Use a second tag to group entries on the change log, we currently use it only for the Enterprise items but we are going to expand it's usage soon, please do not use it until we create a patter for it.
### Minor Changes
For those PRs that aren't important for the end user, we are working on a better pattern, but for now please use the same tags, use them without the brackets and in camel case:
```
Fix: Missing Content-Type header for public files with JWT
```
All those PRs will be grouped under the `Minor changes` section which is collapsed, so users can expand it to check for those minor things but they are not visible directly on changelog.
## Security Best Practices
- Never expose unnecessary data to the APIs' responses
- Always check for permissions or create new ones when you must expose sensitive data
- Never provide new APIs without rate limiters
- Always escape the user's input when rendering data
- Always limit the user's input size on server side
- Always execute the validations on the server side even when executing on the client side as well
## Performance Best Practices
- Prefer inform the fields you want, and only the necessary ones, when querying data from database over query the full documents
- Limit the number of returned records to a reasonable value
- Check if the query is using indexes, it it's not create new indexes
- Prefer queues over long executions
- Create new metrics to mesure things whenever possible
- Cache data and returns whenever possible
## Contributor License Agreement
To have your contribution accepted you must sign our [Contributor License Agreement](https://cla-assistant.io/RocketChat/Rocket.Chat). In case you submit a Pull Request before sign the CLA GitHub will alert you with a new comment asking you to sign and will block the Pull Request from be merged by us.
Please review and sign our CLA at https://cla-assistant.io/RocketChat/Rocket.Chat

@ -1,11 +1,46 @@
<!-- INSTRUCTION: Your Pull Request name should start with one of the following tags -->
<!-- This is a pull request template, you do not need to uncomment or remove the comments, they won't show up in the PR text. -->
<!-- Your Pull Request name should start with one of the following tags -->
<!-- [NEW] For new features -->
<!-- [FIX] For bug fixes -->
<!-- [BREAK] For pull requests including breaking changes -->
<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #ISSUE_NUMBER
## Proposed changes
<!-- Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue below. -->
<!-- INSTRUCTION: Link to a https://github.com/RocketChat/docs PR with added/updated documentation or an update to the missing/outdated documentation list, see https://rocket.chat/docs/contributing/documentation/ -->
## Issue(s)
<!-- Link the issues being closed by or related to this PR. For example, you can use #594 if this PR closes issue number 594 -->
## How to test or reproduce
<!-- Mention how you would reproduce the bug if not mentioned on the issue page already. Also mention which screens are going to have the changes if applicable -->
## Screenshots
## Types of changes
<!-- What types of changes does your code introduce to Rocket.Chat? -->
<!-- Put an `x` in the boxes that apply -->
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] Improvement (non-breaking change which improves a current function)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Hotfix (a major bugfix that has to be merged asap)
- [ ] Documentation Update (if none of the other choices apply)
## Checklist
<!-- Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code. -->
- [ ] I have read the [CONTRIBUTING](https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat) doc
- [ ] I have signed the [CLA](https://cla-assistant.io/RocketChat/Rocket.Chat)
- [ ] Lint and unit tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable)
- [ ] I have added necessary documentation (if applicable)
- [ ] Any dependent changes have been merged and published in downstream modules
## Changelog
<!-- CHANGELOG -->
<!-- Enter HERE a brief text that would go up on the changelog on our releases page -->
<!-- END CHANGELOG -->
## Further comments
<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->
<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

File diff suppressed because it is too large Load Diff

@ -227,7 +227,7 @@ jobs:
MONGO_URL: mongodb://localhost:27017/rocketchat
MONGO_OPLOG_URL: mongodb://localhost:27017/local
run: |
for i in $(seq 1 5); do (docker exec mongo mongo rocketchat --eval 'db.dropDatabase()') && xvfb-run --auto-servernum npm test && s=0 && break || s=$? && sleep 1; done; (exit $s)
for i in $(seq 1 5); do (docker exec mongo mongo rocketchat --eval 'db.dropDatabase()') && xvfb-run --auto-servernum npm run testci && s=0 && break || s=$? && sleep 1; done; (exit $s)
# notification:
# runs-on: ubuntu-latest

@ -0,0 +1,2 @@
{{> versions}}
{{> _prs}}

@ -0,0 +1,13 @@
{{#if (or release.node_version release.npm_version release.mongo_versions)}}
### Engine versions
{{#if release.node_version}}
- Node: `{{ release.node_version }}`
{{/if}}
{{#if release.npm_version}}
- NPM: `{{ release.npm_version }}`
{{/if}}
{{#if release.mongo_versions}}
- MongoDB: `{{ join release.mongo_versions ', ' }}`
{{/if}}
{{/if}}

@ -6,18 +6,18 @@
rocketchat:mongo-config
accounts-facebook@1.3.2
accounts-github@1.4.2
accounts-github@1.4.3
accounts-google@1.3.3
accounts-meteor-developer@1.4.2
accounts-password@1.5.2
accounts-password@1.6.0
accounts-twitter@1.4.2
blaze-html-templates
check@1.3.1
ddp-rate-limiter@1.0.7
ddp-common@1.4.0
dynamic-import@0.5.1
ecmascript@0.14.2
typescript@3.7.5
dynamic-import@0.5.2
ecmascript@0.14.3
typescript@3.7.6
ejson@1.1.1
email@1.2.3
fastclick@1.0.13
@ -25,16 +25,16 @@ http@1.4.2
jquery@1.11.10
logging@1.1.20
meteor-base@1.4.0
mobile-experience@1.0.5
mongo@1.8.0
random@1.1.0
mobile-experience@1.1.0
mongo@1.10.0
random@1.2.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.0
service-configuration@1.0.11
session@1.2.0
shell-server@0.4.0
shell-server@0.5.0
spacebars
standard-minifier-js@2.6.0
tracker@1.2.0
@ -44,9 +44,6 @@ rocketchat:livechat
rocketchat:streamer
rocketchat:version
konecty:change-case
konecty:delayed-task
konecty:mongo-counter
konecty:multiple-instances-status
konecty:user-presence
@ -57,7 +54,6 @@ jalik:ufs-gridfs@1.0.2
jparker:gravatar
kadira:blaze-layout
kadira:flow-router
keepnox:perfect-scrollbar
mizzao:timesync
mrt:reactive-store
mystor:device-detection
@ -70,28 +66,28 @@ raix:ui-dropped-event
rocketchat:tap-i18n
underscore@1.0.10
juliancwirko:postcss
littledata:synced-cron
edgee:slingshot
jalik:ufs-local@1.0.2
accounts-base@1.5.0
accounts-oauth@1.1.16
accounts-base@1.6.0
accounts-oauth@1.2.0
autoupdate@1.6.0
babel-compiler@7.5.2
google-oauth@1.2.6
babel-compiler@7.5.3
google-oauth@1.3.0
htmljs
less
matb33:collection-hooks
meteorhacks:inject-initial
oauth@1.2.8
oauth2@1.2.1
oauth@1.3.0
oauth2@1.3.0
routepolicy@1.1.0
sha@1.0.9
templating
webapp@1.8.0
webapp@1.9.1
webapp-hashing@1.0.9
rocketchat:oauth2-server
rocketchat:i18n
rocketchat:postcss
dandv:caret-position
facts-base
facts-base@1.0.1

@ -1 +1 @@
METEOR@1.9.2
METEOR@1.10.2

@ -1,23 +1,23 @@
accounts-base@1.5.0
accounts-base@1.6.0
accounts-facebook@1.3.2
accounts-github@1.4.3
accounts-google@1.3.3
accounts-meteor-developer@1.4.2
accounts-oauth@1.1.16
accounts-password@1.5.3
accounts-oauth@1.2.0
accounts-password@1.6.0
accounts-twitter@1.4.2
aldeed:simple-schema@1.5.4
allow-deny@1.1.0
autoupdate@1.6.0
babel-compiler@7.5.2
babel-compiler@7.5.3
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-html-templates@1.1.2
blaze-tools@1.0.10
boilerplate-generator@1.6.0
caching-compiler@1.2.1
boilerplate-generator@1.7.0
caching-compiler@1.2.2
caching-html-compiler@1.1.3
callback-hook@1.3.0
cfs:http-methods@0.0.32
@ -33,8 +33,8 @@ deepwell:bootstrap-datepicker2@1.3.0
deps@1.0.12
diff-sequence@1.1.1
dispatch:run-as-user@1.1.1
dynamic-import@0.5.1
ecmascript@0.14.2
dynamic-import@0.5.2
ecmascript@0.14.3
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.10.0
ecmascript-runtime-server@0.9.0
@ -42,19 +42,19 @@ edgee:slingshot@0.7.1
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
facebook-oauth@1.6.0
facebook-oauth@1.7.0
facts-base@1.0.1
fastclick@1.0.13
fetch@0.1.1
geojson-utils@1.0.10
github-oauth@1.2.3
google-oauth@1.2.6
google-oauth@1.3.0
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.2
id-map@1.1.0
inter-process-messaging@0.1.0
inter-process-messaging@0.1.1
jalik:ufs@1.0.2
jalik:ufs-gridfs@1.0.2
jalik:ufs-local@1.0.2
@ -62,17 +62,12 @@ jparker:crypto-core@0.1.0
jparker:crypto-md5@0.1.1
jparker:gravatar@0.5.1
jquery@1.11.11
juliancwirko:postcss@2.0.3
kadira:blaze-layout@2.3.0
kadira:flow-router@2.12.1
keepnox:perfect-scrollbar@0.6.8
konecty:change-case@2.3.0
konecty:delayed-task@1.0.0
konecty:mongo-counter@0.0.5_3
konecty:multiple-instances-status@1.1.0
konecty:user-presence@2.6.3
launch-screen@1.1.1
less@2.8.0
launch-screen@1.2.0
less@3.0.1
littledata:synced-cron@1.5.1
livedata@1.0.18
localstorage@1.2.0
@ -86,37 +81,36 @@ meteorhacks:inject-initial@1.0.4
meteorspark:util@0.2.0
minifier-css@1.5.0
minifier-js@2.6.0
minimongo@1.4.5
minimongo@1.6.0
mizzao:timesync@0.3.4
mobile-experience@1.0.5
mobile-status-bar@1.0.14
mobile-experience@1.1.0
mobile-status-bar@1.1.0
modern-browsers@0.1.5
modules@0.15.0
modules-runtime@0.12.0
mongo@1.8.1
mongo@1.10.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-livedata@1.0.12
mrt:reactive-store@0.0.1
mystor:device-detection@0.2.0
nimble:restivus@0.8.12
nooitaf:colors@1.1.2_1
npm-bcrypt@0.9.3
npm-mongo@3.3.0
oauth@1.2.8
oauth1@1.2.2
oauth2@1.2.1
npm-mongo@3.7.0
oauth@1.3.0
oauth1@1.3.0
oauth2@1.3.0
observe-sequence@1.0.16
ordered-dict@1.1.0
ostrio:cookies@2.5.0
ostrio:cookies@2.6.0
pauli:accounts-linkedin@5.0.0
pauli:linkedin-oauth@5.0.0
promise@0.11.2
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
raix:ui-dropped-event@0.0.7
random@1.1.0
random@1.2.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
@ -126,6 +120,7 @@ rocketchat:i18n@0.0.1
rocketchat:livechat@0.0.1
rocketchat:mongo-config@0.0.1
rocketchat:oauth2-server@2.1.0
rocketchat:postcss@1.0.0
rocketchat:streamer@1.1.0
rocketchat:tap-i18n@1.9.1
rocketchat:version@1.0.0
@ -133,23 +128,22 @@ routepolicy@1.1.0
service-configuration@1.0.11
session@1.2.0
sha@1.0.9
shell-server@0.4.0
shell-server@0.5.0
simple:json-routes@2.1.0
socket-stream-client@0.2.3
socket-stream-client@0.3.0
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
srp@1.1.0
standard-minifier-js@2.6.0
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tmeasday:check-npm-versions@0.3.2
tracker@1.2.0
twitter-oauth@1.2.0
typescript@3.7.5
typescript@3.7.6
ui@1.0.13
underscore@1.0.10
url@1.2.0
webapp@1.8.2
url@1.3.0
webapp@1.9.1
webapp-hashing@1.0.9

@ -10,7 +10,6 @@
"autoprefixer": {}
},
"excludedPackages": [
"deepwell:bootstrap-datepicker2",
"smoral:sweetalert"
"deepwell:bootstrap-datepicker2"
]
}

@ -112,7 +112,7 @@ function startChimp() {
startProcess({
name: 'Chimp',
command: 'npm',
params: ['run', 'testci'],
params: ['test'],
// command: 'exit',
// params: ['2'],
options: {

@ -1,6 +1,6 @@
#!/bin/bash
curl -SLf "https://releases.rocket.chat/#{RC_VERSION}/download/" -o rocket.chat.tgz
curl -SLf "https://releases.rocket.chat/3.3.0-rc.4/download/" -o rocket.chat.tgz
tar xf rocket.chat.tgz --strip 1

@ -7,7 +7,7 @@
# 5. `snapcraft snap`
name: rocketchat-server
version: #{RC_VERSION}
version: 3.3.0-rc.4
summary: Rocket.Chat server
description: Have your own Slack like online chat, built with Meteor. https://rocket.chat/
confinement: strict

@ -1,19 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"shippedProposals": true,
"useBuiltIns": "usage",
"corejs": "3",
"modules": "commonjs"
}
],
"@babel/preset-react",
"@babel/preset-flow"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining"
]
}

@ -1,4 +0,0 @@
import '@storybook/addon-actions/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-links/register';
import '@storybook/addon-viewport/register';

@ -0,0 +1,20 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
shippedProposals: true,
useBuiltIns: 'usage',
corejs: '3',
modules: 'commonjs',
},
],
'@babel/preset-react',
'@babel/preset-flow',
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
],
};

@ -1,20 +0,0 @@
import { withKnobs } from '@storybook/addon-knobs';
import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport/dist/defaults';
import { addDecorator, addParameters, configure } from '@storybook/react';
import { rocketChatDecorator } from './mocks/decorators';
addParameters({
viewport: {
viewports: MINIMAL_VIEWPORTS,
},
});
addDecorator(rocketChatDecorator);
addDecorator(withKnobs);
configure([
require.context('../app', true, /\.stories\.js$/),
require.context('../client', true, /\.stories\.js$/),
require.context('../ee/app', true, /\.stories\.js$/),
], module);

@ -0,0 +1,11 @@
module.exports = {
stories: [
'../app/**/*.stories.js',
'../client/**/*.stories.js',
'../ee/app/**/*.stories.js',
],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
],
};

@ -24,7 +24,7 @@ export const rocketChatDecorator = (fn) => {
}
`}</style>
<div dangerouslySetInnerHTML={{ __html: icons }} />
<div className='global-font-family color-primary-font-color'>
<div className='color-primary-font-color'>
{fn()}
</div>
</MeteorProviderMock>;

@ -4,7 +4,10 @@ export const Meteor = {
_localStorage: window.localStorage,
absoluteUrl: () => {},
userId: () => {},
Streamer: () => {},
Streamer: () => ({
on: () => {},
removeListener: () => {},
}),
startup: () => {},
methods: () => {},
call: () => {},
@ -31,10 +34,13 @@ export const Mongo = {
}),
};
export const ReactiveVar = () => ({
get: () => {},
set: () => {},
});
export const ReactiveVar = (val) => {
let currentVal = val;
return {
get: () => currentVal,
set: (val) => { currentVal = val; },
};
};
export const ReactiveDict = () => ({
get: () => {},

@ -0,0 +1,7 @@
import { withKnobs } from '@storybook/addon-knobs';
import { addDecorator } from '@storybook/react';
import { rocketChatDecorator } from './mocks/decorators';
addDecorator(rocketChatDecorator);
addDecorator(withKnobs);

@ -31,15 +31,35 @@ module.exports = async ({ config }) => {
use: '@settlin/spacebars-loader',
});
config.plugins.push(new webpack.NormalModuleReplacementPlugin(
/^meteor/,
require.resolve('./mocks/meteor.js'),
));
config.plugins.push(new webpack.NormalModuleReplacementPlugin(
/\.\/server\/index.js/,
require.resolve('./mocks/empty.js'),
));
config.module.rules.push({
test: /\.(ts|tsx)$/,
use: [
{
loader: 'ts-loader',
options: {
compilerOptions: {
noEmit: false,
},
},
},
{
loader: 'react-docgen-typescript-loader',
},
],
});
config.resolve.extensions.push('.ts', '.tsx');
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(
/^meteor/,
require.resolve('./mocks/meteor.js'),
),
new webpack.NormalModuleReplacementPlugin(
/\/server(\/index.js)$/,
require.resolve('./mocks/empty.js'),
),
);
config.mode = 'development';
config.optimization.usedExports = true;

@ -1,6 +1,374 @@
# 3.3.0 (Under Release Candidate Process)
## 3.3.0-rc.4
`2020-05-27 · 2 🔍 · 2 👩💻👨💻`
<details>
<summary>🔍 Minor changes</summary>
- Regression: Fix Unread bar design ([#17750](https://github.com/RocketChat/Rocket.Chat/pull/17750))
- Regression: Adjusting spaces between OAuth login buttons ([#17745](https://github.com/RocketChat/Rocket.Chat/pull/17745))
</details>
### 👩💻👨💻 Core Team 🤓
- [@dudizilla](https://github.com/dudizilla)
- [@ggazzo](https://github.com/ggazzo)
## 3.3.0-rc.3
`2020-05-25 · 1 🎉 · 3 🔍 · 3 👩💻👨💻`
### 🎉 New features
- **APPS-ENGINE:** Essentials mechanism ([#17656](https://github.com/RocketChat/Rocket.Chat/pull/17656))
<details>
<summary>🔍 Minor changes</summary>
- Regression: Scroll on admin user info ([#17711](https://github.com/RocketChat/Rocket.Chat/pull/17711))
- Regression: Removed status border on mentions list ([#17741](https://github.com/RocketChat/Rocket.Chat/pull/17741) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Regression: Force unread-rooms bar to appears over the room list ([#17728](https://github.com/RocketChat/Rocket.Chat/pull/17728) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
</details>
### 👩💻👨💻 Contributors 😍
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
### 👩💻👨💻 Core Team 🤓
- [@d-gubert](https://github.com/d-gubert)
- [@ggazzo](https://github.com/ggazzo)
## 3.3.0-rc.2
`2020-05-22 · 1 🐛 · 1 🔍 · 1 👩💻👨💻`
### 🐛 Bug fixes
- SAML IDP initiated logout error ([#17482](https://github.com/RocketChat/Rocket.Chat/pull/17482))
<details>
<summary>🔍 Minor changes</summary>
- Regression: Fix error preventing creation of group DMs ([#17726](https://github.com/RocketChat/Rocket.Chat/pull/17726))
</details>
### 👩💻👨💻 Core Team 🤓
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
## 3.3.0-rc.1
`2020-05-21 · 2 🔍 · 2 👩💻👨💻`
<details>
<summary>🔍 Minor changes</summary>
- Regression: Threads list was fetching all threads ([#17716](https://github.com/RocketChat/Rocket.Chat/pull/17716))
- Regression: Add missing return to afterSaveMessage callbacks ([#17715](https://github.com/RocketChat/Rocket.Chat/pull/17715))
</details>
### 👩💻👨💻 Core Team 🤓
- [@ggazzo](https://github.com/ggazzo)
- [@sampaiodiego](https://github.com/sampaiodiego)
## 3.3.0-rc.0
`2020-05-21 · 19 🎉 · 8 🚀 · 40 🐛 · 36 🔍 · 35 👩💻👨💻`
### 🎉 New features
- **ENTERPRISE:** Support Omnichannel conversations auditing ([#17692](https://github.com/RocketChat/Rocket.Chat/pull/17692))
- **ENTERPRISE:** Support for custom Livechat registration form fields ([#17581](https://github.com/RocketChat/Rocket.Chat/pull/17581))
- **ENTERPRISE:** Omnichannel Last-Chatted Agent Preferred option ([#17666](https://github.com/RocketChat/Rocket.Chat/pull/17666))
If activated, this feature will store the last agent that assisted each Omnichannel visitor when a conversation is taken. So, when a visitor returns(it works with any entry point, Livechat, Facebook, REST API, and so on) and starts a new chat, the routing system checks:
1 - The visitor object for any stored agent that the visitor has previously talked to;
2 - If a previous agent is not found, the system will try to find a previous conversation of the same visitor. If a room is found, the system will get the previous agent from the room;
After this process, if an agent has been found, the system will check the agent's availability to assist the new chat. If it's not available, then the routing system will get the next available agent in the queue.
- **Apps-Engine:** New Room events ([#17487](https://github.com/RocketChat/Rocket.Chat/pull/17487))
- **Apps-Engine:** New Livechat event handlers ([#17033](https://github.com/RocketChat/Rocket.Chat/pull/17033))
- API endpoint to fetch Omnichannel's room transfer history ([#17694](https://github.com/RocketChat/Rocket.Chat/pull/17694))
- Option to remove users from RocketChat if not found in Crowd ([#17619](https://github.com/RocketChat/Rocket.Chat/pull/17619) by [@ocanema](https://github.com/ocanema))
- Added custom fields to Add/Edit user ([#17681](https://github.com/RocketChat/Rocket.Chat/pull/17681))
- Admin refactor Second phase ([#17551](https://github.com/RocketChat/Rocket.Chat/pull/17551))
- Added "Add custom emoji" link to emoji picker ([#16250](https://github.com/RocketChat/Rocket.Chat/pull/16250))
- Add Permissions to deal with Omnichannel visitor past chats history ([#17580](https://github.com/RocketChat/Rocket.Chat/pull/17580))
- Add permissions to deal with Omnichannel custom fields ([#17567](https://github.com/RocketChat/Rocket.Chat/pull/17567))
- Unread bars on sidebar (#16853) ([#16862](https://github.com/RocketChat/Rocket.Chat/pull/16862) by [@juzser](https://github.com/juzser))
- Show user's status description by the usernames in messages list ([#14892](https://github.com/RocketChat/Rocket.Chat/pull/14892) by [@wreiske](https://github.com/wreiske))
![image](https://user-images.githubusercontent.com/6295044/60321979-5d191580-994c-11e9-9cd6-15f4565ff0ae.png)
- Screen Lock settings - mobile client ([#17523](https://github.com/RocketChat/Rocket.Chat/pull/17523))
- Rewrite admin pages ([#17388](https://github.com/RocketChat/Rocket.Chat/pull/17388) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Allow filtering Omnichannel analytics dashboards by department ([#17463](https://github.com/RocketChat/Rocket.Chat/pull/17463))
- Add the ability to send Livechat offline messages to a channel ([#17442](https://github.com/RocketChat/Rocket.Chat/pull/17442))
- Add Livechat website URL to the offline message e-mail ([#17429](https://github.com/RocketChat/Rocket.Chat/pull/17429))
### 🚀 Improvements
- **Apps-Engine:** App user as the default notifier ([#17050](https://github.com/RocketChat/Rocket.Chat/pull/17050))
- Always shows the exact match first on user's and room's autocomplete for mentions and on sidebar search ([#16394](https://github.com/RocketChat/Rocket.Chat/pull/16394))
- Display status information in the Omnichannel Agents list ([#17701](https://github.com/RocketChat/Rocket.Chat/pull/17701))
- Add env var to configure Chatpal URL and remove it from beta ([#16665](https://github.com/RocketChat/Rocket.Chat/pull/16665) by [@tkurz](https://github.com/tkurz))
- Added divider between tables and paginations ([#17680](https://github.com/RocketChat/Rocket.Chat/pull/17680))
- Starred Messages ([#17685](https://github.com/RocketChat/Rocket.Chat/pull/17685))
- Unused styles ([#17554](https://github.com/RocketChat/Rocket.Chat/pull/17554))
- Add new webhooks to the Omnichannel integration feature ([#17503](https://github.com/RocketChat/Rocket.Chat/pull/17503))
### 🐛 Bug fixes
- Missing dropdown to select custom status color on user's profile ([#16537](https://github.com/RocketChat/Rocket.Chat/pull/16537) by [@ritwizsinha](https://github.com/ritwizsinha))
- Password reset/change accepting current password as new password ([#16331](https://github.com/RocketChat/Rocket.Chat/pull/16331) by [@ashwaniYDV](https://github.com/ashwaniYDV))
- Can't click on room's actions menu of sidebar list when in search mode ([#16548](https://github.com/RocketChat/Rocket.Chat/pull/16548) by [@ritvikjain99](https://github.com/ritvikjain99))
- Remove a non working setting "Notification Duration" ([#15737](https://github.com/RocketChat/Rocket.Chat/pull/15737))
- Elements of "Personal Access Tokens" section out of alignment and unusable on very small screens ([#17129](https://github.com/RocketChat/Rocket.Chat/pull/17129) by [@Nikhil713](https://github.com/Nikhil713))
- Allow owners to react inside broadcast channels ([#17687](https://github.com/RocketChat/Rocket.Chat/pull/17687) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Default filters on Omnichannel Current Chats screen not showing on first load ([#17522](https://github.com/RocketChat/Rocket.Chat/pull/17522))
- UI KIT Modal Width ([#17697](https://github.com/RocketChat/Rocket.Chat/pull/17697))
- Agent's custom fields being leaked through the Livechat configuration endpoint ([#17640](https://github.com/RocketChat/Rocket.Chat/pull/17640))
- Avatar url provider ignoring subfolders ([#17675](https://github.com/RocketChat/Rocket.Chat/pull/17675))
- Queued Omnichannel webhook being triggered unnecessarily ([#17661](https://github.com/RocketChat/Rocket.Chat/pull/17661))
- Not redirecting to `First Channel After Login` on register ([#17664](https://github.com/RocketChat/Rocket.Chat/pull/17664))
- Directory search user placeholder ([#17652](https://github.com/RocketChat/Rocket.Chat/pull/17652) by [@zdumitru](https://github.com/zdumitru))
- Marketplace tiered pricing plan wording ([#17644](https://github.com/RocketChat/Rocket.Chat/pull/17644))
- Secret Registration not properly validating Invite Token ([#17618](https://github.com/RocketChat/Rocket.Chat/pull/17618))
- Hyper.sh went out of business in early 2019 ([#17622](https://github.com/RocketChat/Rocket.Chat/pull/17622) by [@fbartels](https://github.com/fbartels))
- Do not allow passwords on private channels ([#15642](https://github.com/RocketChat/Rocket.Chat/pull/15642))
- Mail Messages > Cannot mail own user ([#17625](https://github.com/RocketChat/Rocket.Chat/pull/17625))
- remove multiple options from dontAskMeAgain ([#17514](https://github.com/RocketChat/Rocket.Chat/pull/17514) by [@TaimurAzhar](https://github.com/TaimurAzhar))
- Notification sounds ([#17616](https://github.com/RocketChat/Rocket.Chat/pull/17616))
* Global CDN config was ignored when loading the sound files
* Upload of custom sounds wasn't getting the file extension correctly
* Some translations were missing
* Edit and delete of custom sounds were not working correctly
- Resolve 'app already exists' error on app update ([#17544](https://github.com/RocketChat/Rocket.Chat/pull/17544))
- Relative image path in oembededUrlWidget ([#15902](https://github.com/RocketChat/Rocket.Chat/pull/15902) by [@machester4](https://github.com/machester4))
- Push settings enabled when push gateway is selected ([#17582](https://github.com/RocketChat/Rocket.Chat/pull/17582))
- LDAP login on Enteprise Version ([#17508](https://github.com/RocketChat/Rocket.Chat/pull/17508))
- Login Forbidden on servers that had LDAP enabled in the past ([#17579](https://github.com/RocketChat/Rocket.Chat/pull/17579))
- Email configs not updating after setting changes ([#17578](https://github.com/RocketChat/Rocket.Chat/pull/17578))
- Error during data export for DMs ([#17577](https://github.com/RocketChat/Rocket.Chat/pull/17577))
- Emoji picker search broken ([#17570](https://github.com/RocketChat/Rocket.Chat/pull/17570))
- Omnichannel departments are not saved when the offline channel name is not defined ([#17553](https://github.com/RocketChat/Rocket.Chat/pull/17553))
- Invalid CSS syntax ([#17541](https://github.com/RocketChat/Rocket.Chat/pull/17541))
- Replace postcss Meteor package ([#15929](https://github.com/RocketChat/Rocket.Chat/pull/15929))
- Increasing highlight time in 3 seconds ([#17540](https://github.com/RocketChat/Rocket.Chat/pull/17540) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Remove deprecated Omnichannel Knowledge Base feature ([#17387](https://github.com/RocketChat/Rocket.Chat/pull/17387))
- Reactions may present empty names of who reacted when using Real Names ([#17536](https://github.com/RocketChat/Rocket.Chat/pull/17536))
When changing usernames the reactions became outdated since it's not possible to update the usernames stored there, so when the server users Real Name setting enabled the system process all messages before return to the clients and get the names of the usernames to show since the usernames are outdated the names will not be found. Now the usernames will be displayed when the name can't be found as a temporary fix until we change the architecture of the data to fix the issue.
- Uncessary updates on Settings, Roles and Permissions on startup ([#17160](https://github.com/RocketChat/Rocket.Chat/pull/17160))
- Federation attachment URL for audio and video files ([#16430](https://github.com/RocketChat/Rocket.Chat/pull/16430) by [@qwertiko](https://github.com/qwertiko))
- Replace obsolete X-FRAME-OPTIONS header on Livechat route ([#17419](https://github.com/RocketChat/Rocket.Chat/pull/17419))
- LDAP login error on Enterprise version ([#17497](https://github.com/RocketChat/Rocket.Chat/pull/17497))
- Change email verification label ([#17450](https://github.com/RocketChat/Rocket.Chat/pull/17450))
- Omnichannel room priorities system messages were create on every saved room info ([#17479](https://github.com/RocketChat/Rocket.Chat/pull/17479))
<details>
<summary>🔍 Minor changes</summary>
- Upgrade Livechat Widget version to 1.5.0 ([#17710](https://github.com/RocketChat/Rocket.Chat/pull/17710))
- Update Fuselage version ([#17708](https://github.com/RocketChat/Rocket.Chat/pull/17708))
- Regression: Status presence color ([#17707](https://github.com/RocketChat/Rocket.Chat/pull/17707) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Improve: Remove index files from action-links, accounts and assets ([#17607](https://github.com/RocketChat/Rocket.Chat/pull/17607))
- Update Apps-Engine version ([#17706](https://github.com/RocketChat/Rocket.Chat/pull/17706))
- Regression: Click to join button not working ([#17705](https://github.com/RocketChat/Rocket.Chat/pull/17705))
- Fix typo "You aren't part of any channel yet" ([#17498](https://github.com/RocketChat/Rocket.Chat/pull/17498) by [@huzaifahj](https://github.com/huzaifahj))
- Regression: Integrations edit/history crashing ([#17702](https://github.com/RocketChat/Rocket.Chat/pull/17702))
- Regression: User edit form missing fields ([#17699](https://github.com/RocketChat/Rocket.Chat/pull/17699))
- Regression: Fix error when performing Omnichannel queue checking ([#17700](https://github.com/RocketChat/Rocket.Chat/pull/17700))
- Update Contributing Guide ([#17653](https://github.com/RocketChat/Rocket.Chat/pull/17653))
- LingoHub based on develop ([#17693](https://github.com/RocketChat/Rocket.Chat/pull/17693))
- Regression: Fix incorrect imports of the Apps-Engine ([#17695](https://github.com/RocketChat/Rocket.Chat/pull/17695))
- Improve: Remove uncessary RegExp query by email ([#17654](https://github.com/RocketChat/Rocket.Chat/pull/17654))
- Regression: Set retryWrites=false as default Mongo options ([#17683](https://github.com/RocketChat/Rocket.Chat/pull/17683))
- Regression: status-color-online ([#17684](https://github.com/RocketChat/Rocket.Chat/pull/17684))
- Add snapcraft files to be bumped with Houston ([#17611](https://github.com/RocketChat/Rocket.Chat/pull/17611))
- Regression: Outgoing List ([#17667](https://github.com/RocketChat/Rocket.Chat/pull/17667))
- Regression: Pressing enter on search reloads the page - admin pages ([#17663](https://github.com/RocketChat/Rocket.Chat/pull/17663))
- Improve: New PR Template ([#16968](https://github.com/RocketChat/Rocket.Chat/pull/16968) by [@regalstreak](https://github.com/regalstreak))
- Add engine versions for houston with templates ([#17403](https://github.com/RocketChat/Rocket.Chat/pull/17403))
- Use Users.findOneByAppId instead of querying directly ([#16480](https://github.com/RocketChat/Rocket.Chat/pull/16480))
- Remove unnecessary setting redefinition ([#17587](https://github.com/RocketChat/Rocket.Chat/pull/17587))
- Deprecate compatibility cordova setting ([#17586](https://github.com/RocketChat/Rocket.Chat/pull/17586))
- Livechat iframe allow microphone and camera ([#9956](https://github.com/RocketChat/Rocket.Chat/pull/9956) by [@kolorafa](https://github.com/kolorafa))
- Regression: Do not show custom status inside sequential messages ([#17613](https://github.com/RocketChat/Rocket.Chat/pull/17613))
- Regression: Override via env for string settings not working ([#17576](https://github.com/RocketChat/Rocket.Chat/pull/17576))
- Add some missing metadata information ([#17524](https://github.com/RocketChat/Rocket.Chat/pull/17524))
- Bump jquery from 3.3.1 to 3.5.0 ([#17486](https://github.com/RocketChat/Rocket.Chat/pull/17486) by [@dependabot[bot]](https://github.com/dependabot[bot]))
- DPlatform is deprecated and the replacement does not support rocket.chat ([#17040](https://github.com/RocketChat/Rocket.Chat/pull/17040) by [@ryjones](https://github.com/ryjones))
- Regression: RegExp callbacks of settings were not being called ([#17552](https://github.com/RocketChat/Rocket.Chat/pull/17552))
- Meteor update to version 1.10.2 ([#17533](https://github.com/RocketChat/Rocket.Chat/pull/17533))
- Regression: Fix Avatar Url Provider when CDN_PREFIX_ALL is false ([#17542](https://github.com/RocketChat/Rocket.Chat/pull/17542))
- LingoHub based on develop ([#17520](https://github.com/RocketChat/Rocket.Chat/pull/17520))
- RegExp improvements suggested by LGTM ([#17500](https://github.com/RocketChat/Rocket.Chat/pull/17500))
- Merge master into develop & Set version to 3.3.0-develop ([#17468](https://github.com/RocketChat/Rocket.Chat/pull/17468))
</details>
### 👩💻👨💻 Contributors 😍
- [@Nikhil713](https://github.com/Nikhil713)
- [@TaimurAzhar](https://github.com/TaimurAzhar)
- [@ashwaniYDV](https://github.com/ashwaniYDV)
- [@dependabot[bot]](https://github.com/dependabot[bot])
- [@fbartels](https://github.com/fbartels)
- [@huzaifahj](https://github.com/huzaifahj)
- [@juzser](https://github.com/juzser)
- [@kolorafa](https://github.com/kolorafa)
- [@machester4](https://github.com/machester4)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@ocanema](https://github.com/ocanema)
- [@qwertiko](https://github.com/qwertiko)
- [@regalstreak](https://github.com/regalstreak)
- [@ritvikjain99](https://github.com/ritvikjain99)
- [@ritwizsinha](https://github.com/ritwizsinha)
- [@ryjones](https://github.com/ryjones)
- [@tkurz](https://github.com/tkurz)
- [@wreiske](https://github.com/wreiske)
- [@zdumitru](https://github.com/zdumitru)
### 👩💻👨💻 Core Team 🤓
- [@MarcosSpessatto](https://github.com/MarcosSpessatto)
- [@MartinSchoeler](https://github.com/MartinSchoeler)
- [@d-gubert](https://github.com/d-gubert)
- [@djorkaeffalexandre](https://github.com/djorkaeffalexandre)
- [@engelgabriel](https://github.com/engelgabriel)
- [@gabriellsh](https://github.com/gabriellsh)
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@lolimay](https://github.com/lolimay)
- [@mtmr0x](https://github.com/mtmr0x)
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@renatobecker](https://github.com/renatobecker)
- [@rodrigok](https://github.com/rodrigok)
- [@sampaiodiego](https://github.com/sampaiodiego)
- [@tassoevan](https://github.com/tassoevan)
- [@thassiov](https://github.com/thassiov)
# 3.2.2
`2020-05-11 · 7 🐛 · 6 👩💻👨💻`
`2020-05-11 · 7 🐛 · 1 🔍 · 6 👩💻👨💻`
### Engine versions
- Node: `12.16.1`
@ -26,6 +394,14 @@
When changing usernames the reactions became outdated since it's not possible to update the usernames stored there, so when the server users Real Name setting enabled the system process all messages before return to the clients and get the names of the usernames to show since the usernames are outdated the names will not be found. Now the usernames will be displayed when the name can't be found as a temporary fix until we change the architecture of the data to fix the issue.
<details>
<summary>🔍 Minor changes</summary>
- Release 3.2.2 ([#17600](https://github.com/RocketChat/Rocket.Chat/pull/17600))
</details>
### 👩💻👨💻 Core Team 🤓
- [@geekgonecrazy](https://github.com/geekgonecrazy)
@ -36,20 +412,39 @@
- [@sampaiodiego](https://github.com/sampaiodiego)
# 3.2.1
`2020-05-01 · 1 🐛 · 1 👩💻👨💻`
`2020-05-01 · 1 🐛 · 1 🔍 · 2 👩💻👨💻`
### Engine versions
- Node: `12.16.1`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🐛 Bug fixes
- LDAP login error on Enterprise version ([#17497](https://github.com/RocketChat/Rocket.Chat/pull/17497))
<details>
<summary>🔍 Minor changes</summary>
- Release 3.2.1 ([#17506](https://github.com/RocketChat/Rocket.Chat/pull/17506))
</details>
### 👩💻👨💻 Core Team 🤓
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@sampaiodiego](https://github.com/sampaiodiego)
# 3.2.0
`2020-04-27 · 19 🎉 · 10 🚀 · 34 🐛 · 19 🔍 · 34 👩💻👨💻`
### Engine versions
- Node: `12.16.1`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🎉 New features
@ -281,9 +676,31 @@
- [@sampaiodiego](https://github.com/sampaiodiego)
- [@tassoevan](https://github.com/tassoevan)
# 3.1.3
`2020-05-11 · 1 🐛 · 1 👩💻👨💻`
### Engine versions
- Node: `12.16.1`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🐛 Bug fixes
- Email configs not updating after setting changes ([#17578](https://github.com/RocketChat/Rocket.Chat/pull/17578))
### 👩💻👨💻 Core Team 🤓
- [@rodrigok](https://github.com/rodrigok)
# 3.1.2
`2020-04-27 · 8 🐛 · 3 🔍 · 5 👩💻👨💻`
### Engine versions
- Node: `12.16.1`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🐛 Bug fixes
@ -851,6 +1268,23 @@
- [@sampaiodiego](https://github.com/sampaiodiego)
- [@tassoevan](https://github.com/tassoevan)
# 3.0.13
`2020-05-11 · 1 🐛 · 1 👩💻👨💻`
### Engine versions
- Node: `12.14.0`
- NPM: `6.13.4`
- MongoDB: `3.4, 3.6, 4.0`
### 🐛 Bug fixes
- Email configs not updating after setting changes ([#17578](https://github.com/RocketChat/Rocket.Chat/pull/17578))
### 👩💻👨💻 Core Team 🤓
- [@rodrigok](https://github.com/rodrigok)
# 3.0.12
`2020-04-03 · 3 🔍 · 2 👩💻👨💻`
@ -1195,11 +1629,11 @@
### ⚠ BREAKING CHANGES
- Filter System messages per room ([#16369](https://github.com/RocketChat/Rocket.Chat/pull/16369))
- Filter System messages per room ([#16369](https://github.com/RocketChat/Rocket.Chat/pull/16369) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Remove deprecated publications ([#16351](https://github.com/RocketChat/Rocket.Chat/pull/16351))
- Hide system messages ([#16243](https://github.com/RocketChat/Rocket.Chat/pull/16243))
- Hide system messages ([#16243](https://github.com/RocketChat/Rocket.Chat/pull/16243) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Upgrade to Meteor 1.9 and NodeJS 12 ([#16252](https://github.com/RocketChat/Rocket.Chat/pull/16252))
@ -1274,11 +1708,11 @@
- Highlight freezing the UI ([#16378](https://github.com/RocketChat/Rocket.Chat/pull/16378))
- Adding 'lang' tag ([#16375](https://github.com/RocketChat/Rocket.Chat/pull/16375))
- Adding 'lang' tag ([#16375](https://github.com/RocketChat/Rocket.Chat/pull/16375) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- App removal was moving logs to the trash collection ([#16362](https://github.com/RocketChat/Rocket.Chat/pull/16362))
- Role tags missing - Description field explanation ([#16356](https://github.com/RocketChat/Rocket.Chat/pull/16356))
- Role tags missing - Description field explanation ([#16356](https://github.com/RocketChat/Rocket.Chat/pull/16356) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Invite links usage by channel owners/moderators ([#16176](https://github.com/RocketChat/Rocket.Chat/pull/16176))
@ -1308,7 +1742,7 @@
- SafePorts: Ports 80, 8080 & 443 linked to respective protocols (#16108) ([#16108](https://github.com/RocketChat/Rocket.Chat/pull/16108))
- Drag and drop disabled when file upload is disabled ([#16049](https://github.com/RocketChat/Rocket.Chat/pull/16049))
- Drag and drop disabled when file upload is disabled ([#16049](https://github.com/RocketChat/Rocket.Chat/pull/16049) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Video message sent to wrong room ([#16113](https://github.com/RocketChat/Rocket.Chat/pull/16113))
@ -1318,7 +1752,7 @@
- Thread message icon overlapping text ([#16083](https://github.com/RocketChat/Rocket.Chat/pull/16083))
- Login change language button ([#16085](https://github.com/RocketChat/Rocket.Chat/pull/16085))
- Login change language button ([#16085](https://github.com/RocketChat/Rocket.Chat/pull/16085) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- api-bypass-rate-limiter permission was not working ([#16080](https://github.com/RocketChat/Rocket.Chat/pull/16080))
@ -1452,6 +1886,7 @@
- [@antkaz](https://github.com/antkaz)
- [@ashwaniYDV](https://github.com/ashwaniYDV)
- [@aviral243](https://github.com/aviral243)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@mrsimpson](https://github.com/mrsimpson)
- [@ritwizsinha](https://github.com/ritwizsinha)
- [@vickyokrm](https://github.com/vickyokrm)
@ -1466,7 +1901,6 @@
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@lolimay](https://github.com/lolimay)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@renatobecker](https://github.com/renatobecker)
- [@rodrigok](https://github.com/rodrigok)
@ -1732,20 +2166,23 @@
- Add missing password field back to administration area ([#16171](https://github.com/RocketChat/Rocket.Chat/pull/16171))
- JS errors on Administration page ([#16139](https://github.com/RocketChat/Rocket.Chat/pull/16139))
- JS errors on Administration page ([#16139](https://github.com/RocketChat/Rocket.Chat/pull/16139) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
<details>
<summary>🔍 Minor changes</summary>
- Release 2.4.1 ([#16195](https://github.com/RocketChat/Rocket.Chat/pull/16195))
- Release 2.4.1 ([#16195](https://github.com/RocketChat/Rocket.Chat/pull/16195) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
</details>
### 👩💻👨💻 Contributors 😍
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
### 👩💻👨💻 Core Team 🤓
- [@d-gubert](https://github.com/d-gubert)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@rodrigok](https://github.com/rodrigok)
- [@sampaiodiego](https://github.com/sampaiodiego)
@ -1836,17 +2273,17 @@
- Admin Setting descriptions and Storybook ([#15994](https://github.com/RocketChat/Rocket.Chat/pull/15994))
- width of upload-progress-text ([#16023](https://github.com/RocketChat/Rocket.Chat/pull/16023))
- width of upload-progress-text ([#16023](https://github.com/RocketChat/Rocket.Chat/pull/16023) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Message list scrolling to bottom on reactions ([#16018](https://github.com/RocketChat/Rocket.Chat/pull/16018))
- Message list scrolling to bottom on reactions ([#16018](https://github.com/RocketChat/Rocket.Chat/pull/16018) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- SAML logout error ([#15978](https://github.com/RocketChat/Rocket.Chat/pull/15978))
- Added Join button to Read Only rooms. ([#16016](https://github.com/RocketChat/Rocket.Chat/pull/16016))
- z-index of new message button ([#16013](https://github.com/RocketChat/Rocket.Chat/pull/16013))
- z-index of new message button ([#16013](https://github.com/RocketChat/Rocket.Chat/pull/16013) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- new message popup ([#16017](https://github.com/RocketChat/Rocket.Chat/pull/16017))
- new message popup ([#16017](https://github.com/RocketChat/Rocket.Chat/pull/16017) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Changed renderMessage priority, fixed Katex on/off setting ([#16012](https://github.com/RocketChat/Rocket.Chat/pull/16012))
@ -1939,6 +2376,7 @@
- [@breaking-let](https://github.com/breaking-let)
- [@iannuzzelli](https://github.com/iannuzzelli)
- [@localguru](https://github.com/localguru)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@n-se](https://github.com/n-se)
- [@ritwizsinha](https://github.com/ritwizsinha)
- [@wreiske](https://github.com/wreiske)
@ -1953,7 +2391,6 @@
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@lolimay](https://github.com/lolimay)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@renatobecker](https://github.com/renatobecker)
- [@rodrigok](https://github.com/rodrigok)
@ -2127,7 +2564,7 @@
- Mentions before blockquote ([#15774](https://github.com/RocketChat/Rocket.Chat/pull/15774))
- Sidebar font color was not respecting theming ([#15745](https://github.com/RocketChat/Rocket.Chat/pull/15745))
- Sidebar font color was not respecting theming ([#15745](https://github.com/RocketChat/Rocket.Chat/pull/15745) by [@mariaeduardacunha](https://github.com/mariaeduardacunha))
- Add livechat agents into departments ([#15732](https://github.com/RocketChat/Rocket.Chat/pull/15732))
@ -2202,6 +2639,7 @@
### 👩💻👨💻 Contributors 😍
- [@Exordian](https://github.com/Exordian)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@mpdbl](https://github.com/mpdbl)
- [@nstseek](https://github.com/nstseek)
- [@rajvaibhavdubey](https://github.com/rajvaibhavdubey)
@ -2215,7 +2653,6 @@
- [@gabriellsh](https://github.com/gabriellsh)
- [@geekgonecrazy](https://github.com/geekgonecrazy)
- [@ggazzo](https://github.com/ggazzo)
- [@mariaeduardacunha](https://github.com/mariaeduardacunha)
- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
- [@renatobecker](https://github.com/renatobecker)
- [@rodrigok](https://github.com/rodrigok)

@ -23,7 +23,6 @@
* [RocketChatLauncher](#rocketchatlauncher)
* [Layershift](#layershift)
* [Yunohost.org](#yunohostorg)
* [DPlatform](#dplatform)
* [IndieHosters](#indiehosters)
* [Ubuntu 16.04](#ubuntu-1604)
* [Cloudron.io](#cloudronio)
@ -36,7 +35,6 @@
* [Raspberry Pi 2](#raspberry-pi-2)
* [Koozali SME](#koozali-sme)
* [Ubuntu VPS](#ubuntu-vps)
* [Hyper.sh](#hypersh)
* [WeDeploy](#wedeploy)
* [D2C.io](#d2cio)
* [Syncloud.org](#syncloudorg)
@ -127,12 +125,6 @@ Host your own Rocket.Chat server in a few seconds.
[![Install RocketChat with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=rocketchat)
## DPlatform
The easiest way to install a ready-to-run Rocket.Chat server on a Linux machine, VM, or VPS.
[![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/images/logo.png)](https://dfabric.github.io/DPlatform-ShellCore)
## IndieHosters
Get your Rocket.Chat instance hosted in an "as a Service" style. You register and we manage it for you! (updates, backup...).
@ -203,9 +195,6 @@ Add Rocket.Chat to this world famous time tested small enterprise server today.
## Ubuntu VPS
Follow these [deployment instructions](https://rocket.chat/docs/installation/manual-installation/ubuntu/).
## Hyper.sh
Follow their [deployment instructions](https://rocket.chat/docs/installation/paas-deployments/hyper-sh/) to install a per-second billed Rocket.Chat instance on [Hyper.sh](https://rocket.chat/docs/installation/paas-deployments/hyper-sh/).
## WeDeploy
Install Rocket.Chat on [WeDeploy](https://wedeploy.com):

@ -1 +0,0 @@
export * from './server/index';

@ -1,5 +1,4 @@
import { actionLinks } from '../both/lib/actionLinks';
import './lib/actionLinks';
import { actionLinks } from './lib/actionLinks';
import './init';
import './stylesheets/actionLinks.css';

@ -1,14 +1,14 @@
import { Blaze } from 'meteor/blaze';
import { Template } from 'meteor/templating';
import { handleError } from '../../utils';
import { fireGlobalEvent, Layout } from '../../ui-utils';
import { handleError } from '../../utils/client';
import { fireGlobalEvent, Layout } from '../../ui-utils/client';
import { messageArgs } from '../../ui-utils/client/lib/messageArgs';
import { actionLinks } from '../both/lib/actionLinks';
import { actionLinks } from './lib/actionLinks';
Template.room.events({
'click .action-link'(event, instance) {
'click [data-actionlink]'(event, instance) {
event.preventDefault();
event.stopPropagation();

@ -1,27 +1,58 @@
import { Meteor } from 'meteor/meteor';
import { handleError } from '../../../utils';
import { actionLinks } from '../../both/lib/actionLinks';
// Action Links Handler. This method will be called off the client.
import { handleError } from '../../../utils/client';
import { Messages, Subscriptions } from '../../../models/client';
actionLinks.run = (name, messageId, instance) => {
const message = actionLinks.getMessage(name, messageId);
// Action Links namespace creation.
export const actionLinks = {
actions: {},
register(name, funct) {
actionLinks.actions[name] = funct;
},
getMessage(name, messageId) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'actionLinks.getMessage' });
}
const message = Messages.findOne({ _id: messageId });
if (!message) {
throw new Meteor.Error('error-invalid-message', 'Invalid message', { function: 'actionLinks.getMessage' });
}
const subscription = Subscriptions.findOne({
rid: message.rid,
'u._id': userId,
});
if (!subscription) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { function: 'actionLinks.getMessage' });
}
if (!message.actionLinks || !message.actionLinks[name]) {
throw new Meteor.Error('error-invalid-actionlink', 'Invalid action link', { function: 'actionLinks.getMessage' });
}
const actionLink = message.actionLinks[name];
return message;
},
run(name, messageId, instance) {
const message = actionLinks.getMessage(name, messageId);
let ranClient = false;
const actionLink = message.actionLinks[name];
if (actionLinks && actionLinks.actions && actionLinks.actions[actionLink.method_id]) {
// run just on client side
actionLinks.actions[actionLink.method_id](message, actionLink.params, instance);
let ranClient = false;
ranClient = true;
}
if (actionLinks && actionLinks.actions && actionLinks.actions[actionLink.method_id]) {
// run just on client side
actionLinks.actions[actionLink.method_id](message, actionLink.params, instance);
// and run on server side
Meteor.call('actionLinkHandler', name, messageId, (err) => {
if (err && !ranClient) {
handleError(err);
ranClient = true;
}
});
// and run on server side
Meteor.call('actionLinkHandler', name, messageId, (err) => {
if (err && !ranClient) {
handleError(err);
}
});
},
};

@ -1,8 +0,0 @@
import { Meteor } from 'meteor/meteor';
if (Meteor.isClient) {
module.exports = require('./client/index.js');
}
if (Meteor.isServer) {
module.exports = require('./server/index.js');
}

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { actionLinks } from '../both/lib/actionLinks';
import { actionLinks } from './lib/actionLinks';
// Action Links Handler. This method will be called off the client.
Meteor.methods({

@ -1,4 +1,4 @@
import { actionLinks } from '../both/lib/actionLinks';
import { actionLinks } from './lib/actionLinks';
import './actionLinkHandler';
export {

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { Messages, Subscriptions } from '../../../models';
import { Messages, Subscriptions } from '../../../models/server';
// Action Links namespace creation.
export const actionLinks = {

@ -1 +0,0 @@
export * from './server/index';

@ -374,7 +374,7 @@ export class APIClass extends Restivus {
'error-unauthorized': 'unauthorized',
}[e.error] || 'failure';
result = API.v1[apiMethod](typeof e === 'string' ? e : e.message, e.error, undefined, e);
result = API.v1[apiMethod](typeof e === 'string' ? e : e.message, e.error, process.env.TEST_MODE ? e.stack : undefined, e);
} finally {
delete Accounts._accountData[connection.id];
}

@ -56,6 +56,34 @@ export async function findAdminRooms({ uid, filter, types = [], pagination: { of
};
}
export async function findAdminRoom({ uid, rid }) {
if (!await hasPermissionAsync(uid, 'view-room-administration')) {
throw new Error('error-not-authorized');
}
const fields = {
prid: 1,
fname: 1,
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
usersCount: 1,
muted: 1,
unmuted: 1,
ro: 1,
default: 1,
favorite: 1,
featured: 1,
topic: 1,
msgs: 1,
archived: 1,
tokenpass: 1,
};
return Rooms.findOneById(rid, { fields });
}
export async function findChannelAndPrivateAutocomplete({ uid, selector }) {
if (!await hasPermissionAsync(uid, 'view-other-user-channels')) {
return { items: [] };

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import Busboy from 'busboy';
import { RocketChatAssets } from '../../../assets';
import { RocketChatAssets } from '../../../assets/server';
import { API } from '../api';
API.v1.addRoute('assets.setAsset', { authRequired: true }, {

@ -4,7 +4,7 @@ import { Match, check } from 'meteor/check';
import { Messages } from '../../../models';
import { canAccessRoom, hasPermission } from '../../../authorization';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { processWebhookMessage } from '../../../lib';
import { processWebhookMessage } from '../../../lib/server';
import { API } from '../api';
import Rooms from '../../../models/server/models/Rooms';
import Users from '../../../models/server/models/Users';

@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { hasPermission } from '../../../authorization';
import { Permissions, Roles } from '../../../models';
import { Permissions, Roles } from '../../../models/server';
import { API } from '../api';
/**

@ -4,7 +4,7 @@ import Busboy from 'busboy';
import { FileUpload } from '../../../file-upload';
import { Rooms, Messages } from '../../../models';
import { API } from '../api';
import { findAdminRooms, findChannelAndPrivateAutocomplete } from '../lib/rooms';
import { findAdminRooms, findChannelAndPrivateAutocomplete, findAdminRoom } from '../lib/rooms';
function findRoomByIdOrName({ params, checkedArchived = true }) {
if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) {
@ -299,6 +299,22 @@ API.v1.addRoute('rooms.adminRooms', { authRequired: true }, {
},
});
API.v1.addRoute('rooms.adminRooms.getRoom', { authRequired: true }, {
get() {
const { rid } = this.requestParams();
const room = Promise.await(findAdminRoom({
uid: this.userId,
rid,
}));
if (!room) {
return API.v1.failure('not-allowed', 'Not Allowed');
}
return API.v1.success(room);
},
});
API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true }, {
get() {
const { selector } = this.queryParams;
@ -312,3 +328,28 @@ API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true },
})));
},
});
API.v1.addRoute('rooms.saveRoomSettings', { authRequired: true }, {
post() {
const { rid, ...params } = this.bodyParams;
const result = Meteor.runAsUser(this.userId, () => Meteor.call('saveRoomSettings', rid, params));
return API.v1.success({ rid: result.rid });
},
});
API.v1.addRoute('rooms.changeArchivationState', { authRequired: true }, {
post() {
const { rid, action } = this.bodyParams;
let result;
if (action === 'archive') {
result = Meteor.runAsUser(this.userId, () => Meteor.call('archiveRoom', rid));
} else {
result = Meteor.runAsUser(this.userId, () => Meteor.call('unarchiveRoom', rid));
}
return API.v1.success({ result });
},
});

@ -3,7 +3,7 @@ import { Match, check } from 'meteor/check';
import { ServiceConfiguration } from 'meteor/service-configuration';
import _ from 'underscore';
import { Settings } from '../../../models';
import { Settings } from '../../../models/server';
import { hasPermission } from '../../../authorization';
import { API } from '../api';

@ -30,6 +30,8 @@ API.v1.addRoute('users.create', { authRequired: true }, {
password: String,
username: String,
active: Match.Maybe(Boolean),
bio: Match.Maybe(String),
statusText: Match.Maybe(String),
roles: Match.Maybe(Array),
joinDefaultChannels: Match.Maybe(Boolean),
requirePasswordChange: Match.Maybe(Boolean),
@ -431,6 +433,7 @@ API.v1.addRoute('users.update', { authRequired: true, twoFactorRequired: true },
name: Match.Maybe(String),
password: Match.Maybe(String),
username: Match.Maybe(String),
bio: Match.Maybe(String),
statusText: Match.Maybe(String),
active: Match.Maybe(Boolean),
roles: Match.Maybe(Array),
@ -536,7 +539,6 @@ API.v1.addRoute('users.setPreferences', { authRequired: true }, {
mobileNotifications: Match.Maybe(String),
enableAutoAway: Match.Maybe(Boolean),
highlights: Match.Maybe(Array),
desktopNotificationDuration: Match.Maybe(Number),
desktopNotificationRequireInteraction: Match.Maybe(Boolean),
messageViewMode: Match.Maybe(Number),
hideUsernames: Match.Maybe(Boolean),

@ -104,6 +104,22 @@
</div>
{{/each}}
{{#if essentials}}
<div class="rc-apps-details__alert rc-apps-details__alert-warning">
{{_ "Apps_Essential_Alert"}}
<ul>
{{#each essentials}}
<li>
<strong>{{interfaceName}}</strong> -
{{_ i18nKey}}
</li>
{{/each}}
</ul>
<br/>
<p>{{_ "Apps_Essential_Disclaimer"}}</p>
</div>
{{/if}}
{{#if categories}}
<div class="rc-apps-details__row rc-apps-details__block">
<h2> {{_ "Categories"}} </h2>
@ -359,8 +375,8 @@
</div>
{{> CodeMirror name=id options=getEditorOptions code=value }}
<div class="buttons">
<button class="button primary button-fullscreen">{{_ "Full_Screen"}}</button>
<button class="button primary button-restore">{{_ "Exit_Full_Screen"}}</button>
<button class="rc-button rc-button--primary button-fullscreen">{{_ "Full_Screen"}}</button>
<button class="rc-button rc-button--primary button-restore">{{_ "Exit_Full_Screen"}}</button>
</div>
</div>
{{/if}}
@ -463,7 +479,7 @@
{{#if hasChanges section}}
<span style="line-height: 40px" class="secondary-font-color">{{_ "Save_to_enable_this_action"}}</span>
{{else}}
<button type="button" class="button primary action" data-setting="{{id}}" data-action="{{value}}">{{_ actionText}}</button>
<button type="button" class="rc-button rc-button--primary action" data-setting="{{id}}" data-action="{{value}}">{{_ actionText}}</button>
{{/if}}
{{/if}}
@ -472,14 +488,14 @@
<div class="settings-file-preview">
<div class="preview" style="background-image:url({{value.url}}?_dc={{random}});"></div>
<div class="action">
<button type="button" class="button danger delete-asset"><i class="icon-trash secondary-font-color"></i>{{_ 'Delete'}}</button>
<button type="button" class="rc-button rc-button--danger delete-asset"><i class="icon-trash secondary-font-color"></i>{{_ 'Delete'}}</button>
</div>
</div>
{{else}}
<div class="settings-file-preview">
<div class="preview no-file background-transparent-light secondary-font-color"><i class="icon-upload secondary-font-color"></i></div>
<div class="action">
<div class="button primary">{{_ 'Select_file'}}
<div class="rc-button rc-button--primary">{{_ 'Select_file'}}
<input type="file" accept="{{assetAccept fileConstraints}}" />
</div>
</div>

@ -317,6 +317,12 @@ Template.appManage.helpers({
bundleAppNames(apps) {
return apps.map((app) => app.latest.name).join(', ');
},
essentials() {
return Template.instance()._app.get('essentials')?.map((interfaceName) => ({
interfaceName,
i18nKey: `Apps_Interface_${ interfaceName }`,
}));
},
});
Template.appManage.events({

@ -226,7 +226,7 @@ Template.apps.events({
} = instance.state.get('apps').find(({ id }) => id === currentTarget.dataset.id);
FlowRouter.go('app-manage', { appId }, { version });
},
async 'click .js-install, click .js-update'(event, instance) {
async 'click .js-update'(event, instance) {
event.preventDefault();
event.stopPropagation();
@ -240,7 +240,7 @@ Template.apps.events({
instance.startAppWorking(app.id);
try {
const { status } = await Apps.installApp(app.id, app.marketplaceVersion);
const { status } = await Apps.updateApp(app.id, app.marketplaceVersion);
warnStatusChange(app.name, status);
} catch (error) {
handleAPIError(error);

@ -363,11 +363,12 @@ export const appStatusSpanProps = ({
export const formatPrice = (price) => `\$${ Number.parseFloat(price).toFixed(2) }`;
export const formatPricingPlan = ({ strategy, price, tiers }) => {
export const formatPricingPlan = ({ strategy, price, tiers = [] }) => {
const { perUnit = false } = (Array.isArray(tiers) && tiers.find((tier) => tier.price === price)) || {};
const pricingPlanTranslationString = [
'Apps_Marketplace_pricingPlan',
Array.isArray(tiers) && tiers.length > 0 && 'startingAt',
strategy,
perUnit && 'perUser',
].filter(Boolean).join('_');

@ -273,7 +273,7 @@ Template.marketplace.events({
} = instance.state.get('apps').find(({ id }) => id === currentTarget.dataset.id);
FlowRouter.go('marketplace-app', { appId }, { version: version || marketplaceVersion });
},
async 'click .js-install, click .js-update'(event, instance) {
async 'click .js-install'(event, instance) {
event.preventDefault();
event.stopPropagation();
@ -284,6 +284,7 @@ Template.marketplace.events({
}
const { currentTarget: button } = event;
const app = instance.state.get('apps').find(({ id }) => id === button.dataset.id);
instance.startAppWorking(app.id);
@ -297,6 +298,31 @@ Template.marketplace.events({
instance.stopAppWorking(app.id);
}
},
async 'click .js-update'(event, instance) {
event.preventDefault();
event.stopPropagation();
const isLoggedInCloud = await checkCloudLogin();
instance.state.set('isLoggedInCloud', isLoggedInCloud);
if (!isLoggedInCloud) {
return;
}
const { currentTarget: button } = event;
const app = instance.state.get('apps').find(({ id }) => id === button.dataset.id);
instance.startAppWorking(app.id);
try {
const { status } = await Apps.updateApp(app.id, app.marketplaceVersion);
warnStatusChange(app.name, status);
} catch (error) {
handleAPIError(error);
} finally {
instance.stopAppWorking(app.id);
}
},
async 'click .js-purchase'(event, instance) {
event.preventDefault();
event.stopPropagation();

@ -4,7 +4,7 @@ import toastr from 'toastr';
import { AppWebsocketReceiver } from './communication';
import { APIClient } from '../../utils';
import { registerAdminSidebarItem } from '../../ui-admin/client';
import { registerAdminSidebarItem } from '../../../client/admin';
import { CachedCollectionManager } from '../../ui-cached-collection';
import { hasAtLeastOnePermission } from '../../authorization';
import { handleI18nResources } from './i18n';

@ -1,7 +1,7 @@
import { FlowRouter } from 'meteor/kadira:flow-router';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute } from '../../ui-admin/client';
import { registerAdminRoute } from '../../../client/admin';
import { Apps } from './orchestrator';
registerAdminRoute('/apps/what-is-it', {

@ -6,6 +6,10 @@ export class AppInternalBridge {
}
getUsernamesOfRoomById(roomId) {
if (!roomId) {
return [];
}
const records = Subscriptions.findByRoomIdWhenUsernameExists(roomId, {
fields: {
'u.username': 1,

@ -1,8 +1,53 @@
import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata';
export class AppListenerBridge {
constructor(orch) {
this.orch = orch;
}
async handleEvent(event, ...payload) {
const method = (() => {
switch (event) {
case AppInterface.IPreMessageSentPrevent:
case AppInterface.IPreMessageSentExtend:
case AppInterface.IPreMessageSentModify:
case AppInterface.IPostMessageSent:
case AppInterface.IPreMessageDeletePrevent:
case AppInterface.IPostMessageDeleted:
case AppInterface.IPreMessageUpdatedPrevent:
case AppInterface.IPreMessageUpdatedExtend:
case AppInterface.IPreMessageUpdatedModify:
case AppInterface.IPostMessageUpdated:
return 'messageEvent';
case AppInterface.IPreRoomCreatePrevent:
case AppInterface.IPreRoomCreateExtend:
case AppInterface.IPreRoomCreateModify:
case AppInterface.IPostRoomCreate:
case AppInterface.IPreRoomDeletePrevent:
case AppInterface.IPostRoomDeleted:
case AppInterface.IPreRoomUserJoined:
case AppInterface.IPostRoomUserJoined:
return 'roomEvent';
case AppInterface.IPostExternalComponentOpened:
case AppInterface.IPostExternalComponentClosed:
return 'externalComponentEvent';
/**
* @deprecated please prefer the AppInterface.IPostLivechatRoomClosed event
*/
case AppInterface.ILivechatRoomClosedHandler:
case AppInterface.IPostLivechatRoomStarted:
case AppInterface.IPostLivechatRoomClosed:
case AppInterface.IPostLivechatAgentAssigned:
case AppInterface.IPostLivechatAgentUnassigned:
return 'livechatEvent';
case AppInterface.IUIKitInteractionHandler:
return 'uiKitInteractionEvent';
}
})();
return this[method](event, ...payload);
}
async messageEvent(inte, message) {
const msg = this.orch.getConverters().get('messages').convertMessage(message);
const result = await this.orch.getManager().getListenerManager().executeListener(inte, msg);
@ -11,56 +56,54 @@ export class AppListenerBridge {
return result;
}
return this.orch.getConverters().get('messages').convertAppMessage(result);
// try {
// } catch (e) {
// this.orch.debugLog(`${ e.name }: ${ e.message }`);
// this.orch.debugLog(e.stack);
// }
}
async roomEvent(inte, room) {
async roomEvent(inte, room, ...payload) {
const rm = this.orch.getConverters().get('rooms').convertRoom(room);
const result = await this.orch.getManager().getListenerManager().executeListener(inte, rm);
const params = (() => {
switch (inte) {
case AppInterface.IPreRoomUserJoined:
case AppInterface.IPostRoomUserJoined:
const [joiningUser, invitingUser] = payload;
return {
room: rm,
joiningUser: this.orch.getConverters().get('users').convertToApp(joiningUser),
invitingUser: this.orch.getConverters().get('users').convertToApp(invitingUser),
};
default:
return rm;
}
})();
const result = await this.orch.getManager().getListenerManager().executeListener(inte, params);
if (typeof result === 'boolean') {
return result;
}
return this.orch.getConverters().get('rooms').convertAppRoom(result);
// try {
// } catch (e) {
// this.orch.debugLog(`${ e.name }: ${ e.message }`);
// this.orch.debugLog(e.stack);
// }
}
async externalComponentEvent(inte, externalComponent) {
const result = await this.orch.getManager().getListenerManager().executeListener(inte, externalComponent);
return result;
return this.orch.getManager().getListenerManager().executeListener(inte, externalComponent);
}
async uiKitInteractionEvent(inte, action) {
return this.orch.getManager().getListenerManager().executeListener(inte, action);
// try {
// } catch (e) {
// this.orch.debugLog(`${ e.name }: ${ e.message }`);
// this.orch.debugLog(e.stack);
// }
}
async livechatEvent(inte, room) {
const rm = this.orch.getConverters().get('rooms').convertRoom(room);
const result = await this.orch.getManager().getListenerManager().executeListener(inte, rm);
async livechatEvent(inte, data) {
switch (inte) {
case AppInterface.IPostLivechatAgentAssigned:
case AppInterface.IPostLivechatAgentUnassigned:
return this.orch.getManager().getListenerManager().executeListener(inte, {
room: this.orch.getConverters().get('rooms').convertRoom(data.room),
agent: this.orch.getConverters().get('users').convertToApp(data.user),
});
default:
const room = this.orch.getConverters().get('rooms').convertRoom(data);
if (typeof result === 'boolean') {
return result;
return this.orch.getManager().getListenerManager().executeListener(inte, room);
}
return this.orch.getConverters().get('rooms').convertAppRoom(result);
}
}

@ -1,6 +1,6 @@
import { Random } from 'meteor/random';
import { Messages, Users, Subscriptions } from '../../../models';
import { Messages, Users, Subscriptions } from '../../../models/server';
import { Notifications } from '../../../notifications';
import { updateMessage } from '../../../lib/server/functions/updateMessage';
import { executeSendMessage } from '../../../lib/server/methods/sendMessage';
@ -48,12 +48,15 @@ export class AppMessageBridge {
const msg = this.orch.getConverters().get('messages').convertAppMessage(message);
Notifications.notifyUser(user.id, 'message', Object.assign(msg, {
if (!msg) {
return;
}
Notifications.notifyUser(user.id, 'message', {
...msg,
_id: Random.id(),
ts: new Date(),
u: undefined,
editor: undefined,
}));
});
}
async notifyRoom(room, message, appId) {
@ -68,8 +71,6 @@ export class AppMessageBridge {
_id: Random.id(),
rid: room.id,
ts: new Date(),
u: undefined,
editor: undefined,
});
const users = Subscriptions.findByRoomIdWhenUserIdExists(room.id, { fields: { 'u._id': 1 } })

@ -1,10 +1,9 @@
import { Random } from 'meteor/random';
import { setUserAvatar, checkUsernameAvailability, deleteUser, _setStatusTextPromise } from '../../../lib/server/functions';
import { Users } from '../../../models';
import { Users } from '../../../models/server';
import { Users as UsersRaw } from '../../../models/server/raw';
export class AppUserBridge {
constructor(orch) {
this.orch = orch;
@ -25,7 +24,7 @@ export class AppUserBridge {
async getAppUser(appId) {
this.orch.debugLog(`The App ${ appId } is getting its assigned user`);
const user = Users.findOne({ appId });
const user = Users.findOneByAppId(appId);
return this.orch.getConverters().get('users').convertToApp(user);
}

@ -78,7 +78,7 @@ export class AppMessagesConverter {
}
convertAppMessage(message) {
if (!message) {
if (!message || !message.room) {
return undefined;
}

@ -69,7 +69,7 @@ export class AppRoomsConverter {
}
const newRoom = {
_id: room.id,
...room.id && { _id: room.id },
fname: room.displayName,
name: room.slugifiedName,
t: room.type,
@ -116,6 +116,7 @@ export class AppRoomsConverter {
customFields: 'customFields',
isWaitingResponse: 'waitingResponse',
isOpen: 'open',
_USERNAMES: '_USERNAMES',
isDefault: (room) => {
const result = !!room.default;
delete room.default;

@ -1,3 +1,3 @@
import './cron';
export { Apps } from './orchestrator';
export { Apps, AppEvents } from './orchestrator';

@ -1,5 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { EssentialAppDisabledException } from '@rocket.chat/apps-engine/definition/exceptions';
import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata';
import { AppManager } from '@rocket.chat/apps-engine/server/AppManager';
import { Meteor } from 'meteor/meteor';
import { Logger } from '../../logger';
import { AppsLogsModel, AppsModel, AppsPersistenceModel, Permissions } from '../../models';
@ -16,7 +18,6 @@ function isTesting() {
return process.env.TEST_MODE === 'true';
}
class AppServerOrchestrator {
constructor() {
this._isInitialized = false;
@ -24,7 +25,7 @@ class AppServerOrchestrator {
initialize() {
this._rocketchatLogger = new Logger('Rocket.Chat Apps');
Permissions.createOrUpdate('manage-apps', ['admin']);
Permissions.create('manage-apps', ['admin']);
this._marketplaceUrl = 'https://marketplace.rocket.chat';
@ -155,8 +156,23 @@ class AppServerOrchestrator {
return this._manager.updateAppsMarketplaceInfo(apps)
.then(() => this._manager.get());
}
async triggerEvent(event, ...payload) {
if (!this.isLoaded()) {
return;
}
return this.getBridges().getListenerBridge().handleEvent(event, ...payload).catch((error) => {
if (error instanceof EssentialAppDisabledException) {
throw new Meteor.Error('error-essential-app-disabled');
}
throw error;
});
}
}
export const AppEvents = AppInterface;
export const Apps = new AppServerOrchestrator();
settings.addGroup('General', function() {

@ -1 +0,0 @@
export * from './server/index';

@ -1,6 +1,6 @@
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute } from '../../ui-admin/client';
import { registerAdminRoute } from '../../../client/admin';
import { t } from '../../utils/client';
registerAdminRoute('/permissions', {

@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import { hasAtLeastOnePermission } from './hasPermission';
import { registerAdminSidebarItem } from '../../ui-admin/client';
import { registerAdminSidebarItem } from '../../../client/admin';
import { CachedCollectionManager } from '../../ui-cached-collection';
import { APIClient } from '../../utils/client';
import { Roles } from '../../models/client';

@ -1,7 +1,7 @@
/* eslint no-multi-spaces: 0 */
import { Meteor } from 'meteor/meteor';
import { Roles, Permissions, Settings } from '../../models';
import { Roles, Permissions, Settings } from '../../models/server';
import { settings } from '../../settings/server';
import { getSettingPermissionId, CONSTANTS } from '../lib';
import { clearCache } from './functions/hasPermission';
@ -12,97 +12,97 @@ Meteor.startup(function() {
// then we can define edit-<type>-message instead of edit-message
// 2. admin, moderator, and user roles should not be deleted as they are referened in the code.
const permissions = [
{ _id: 'access-permissions', roles: ['admin'] },
{ _id: 'access-setting-permissions', roles: ['admin'] },
{ _id: 'add-oauth-service', roles: ['admin'] },
{ _id: 'add-user-to-joined-room', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'add-user-to-any-c-room', roles: ['admin'] },
{ _id: 'add-user-to-any-p-room', roles: [] },
{ _id: 'api-bypass-rate-limit', roles: ['admin', 'bot', 'app'] },
{ _id: 'archive-room', roles: ['admin', 'owner'] },
{ _id: 'assign-admin-role', roles: ['admin'] },
{ _id: 'assign-roles', roles: ['admin'] },
{ _id: 'ban-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'bulk-register-user', roles: ['admin'] },
{ _id: 'create-c', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-d', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-p', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-personal-access-tokens', roles: ['admin', 'user'] },
{ _id: 'create-user', roles: ['admin'] },
{ _id: 'clean-channel-history', roles: ['admin'] },
{ _id: 'delete-c', roles: ['admin', 'owner'] },
{ _id: 'delete-d', roles: ['admin'] },
{ _id: 'delete-message', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'delete-own-message', roles: ['admin', 'user'] },
{ _id: 'delete-p', roles: ['admin', 'owner'] },
{ _id: 'delete-user', roles: ['admin'] },
{ _id: 'edit-message', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'edit-other-user-active-status', roles: ['admin'] },
{ _id: 'edit-other-user-info', roles: ['admin'] },
{ _id: 'edit-other-user-password', roles: ['admin'] },
{ _id: 'edit-other-user-avatar', roles: ['admin'] },
{ _id: 'edit-privileged-setting', roles: ['admin'] },
{ _id: 'edit-room', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'edit-room-retention-policy', roles: ['admin'] },
{ _id: 'force-delete-message', roles: ['admin', 'owner'] },
{ _id: 'join-without-join-code', roles: ['admin', 'bot', 'app'] },
{ _id: 'leave-c', roles: ['admin', 'user', 'bot', 'anonymous', 'app'] },
{ _id: 'leave-p', roles: ['admin', 'user', 'bot', 'anonymous', 'app'] },
{ _id: 'manage-assets', roles: ['admin'] },
{ _id: 'manage-emoji', roles: ['admin'] },
{ _id: 'manage-user-status', roles: ['admin'] },
{ _id: 'manage-outgoing-integrations', roles: ['admin'] },
{ _id: 'manage-incoming-integrations', roles: ['admin'] },
{ _id: 'manage-own-outgoing-integrations', roles: ['admin'] },
{ _id: 'manage-own-incoming-integrations', roles: ['admin'] },
{ _id: 'manage-oauth-apps', roles: ['admin'] },
{ _id: 'manage-selected-settings', roles: ['admin'] },
{ _id: 'mention-all', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'mention-here', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'mute-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'remove-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'run-import', roles: ['admin'] },
{ _id: 'run-migration', roles: ['admin'] },
{ _id: 'set-moderator', roles: ['admin', 'owner'] },
{ _id: 'set-owner', roles: ['admin', 'owner'] },
{ _id: 'send-many-messages', roles: ['admin', 'bot', 'app'] },
{ _id: 'set-leader', roles: ['admin', 'owner'] },
{ _id: 'unarchive-room', roles: ['admin'] },
{ _id: 'view-c-room', roles: ['admin', 'user', 'bot', 'app', 'anonymous'] },
{ _id: 'user-generate-access-token', roles: ['admin'] },
{ _id: 'view-d-room', roles: ['admin', 'user', 'bot', 'app', 'guest'] },
{ _id: 'view-full-other-user-info', roles: ['admin'] },
{ _id: 'view-history', roles: ['admin', 'user', 'anonymous'] },
{ _id: 'view-joined-room', roles: ['guest', 'bot', 'app', 'anonymous'] },
{ _id: 'view-join-code', roles: ['admin'] },
{ _id: 'view-logs', roles: ['admin'] },
{ _id: 'view-other-user-channels', roles: ['admin'] },
{ _id: 'view-p-room', roles: ['admin', 'user', 'anonymous', 'guest'] },
{ _id: 'view-privileged-setting', roles: ['admin'] },
{ _id: 'view-room-administration', roles: ['admin'] },
{ _id: 'view-statistics', roles: ['admin'] },
{ _id: 'view-user-administration', roles: ['admin'] },
{ _id: 'preview-c-room', roles: ['admin', 'user', 'anonymous'] },
{ _id: 'view-outside-room', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'view-broadcast-member-list', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'call-management', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'view-l-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] },
{ _id: 'view-livechat-manager', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-rooms', roles: ['livechat-manager', 'admin'] },
{ _id: 'close-livechat-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] },
{ _id: 'close-others-livechat-room', roles: ['livechat-manager', 'admin'] },
{ _id: 'save-others-livechat-room-info', roles: ['livechat-manager'] },
{ _id: 'remove-closed-livechat-rooms', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-analytics', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-queue', roles: ['livechat-manager', 'admin'] },
{ _id: 'transfer-livechat-guest', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-managers', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-agents', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-departments', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-departments', roles: ['livechat-manager', 'admin'] },
{ _id: 'add-livechat-department-agents', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-current-chats', roles: ['livechat-manager', 'admin'] },
{ _id: 'access-permissions', roles: ['admin'] },
{ _id: 'access-setting-permissions', roles: ['admin'] },
{ _id: 'add-oauth-service', roles: ['admin'] },
{ _id: 'add-user-to-joined-room', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'add-user-to-any-c-room', roles: ['admin'] },
{ _id: 'add-user-to-any-p-room', roles: [] },
{ _id: 'api-bypass-rate-limit', roles: ['admin', 'bot', 'app'] },
{ _id: 'archive-room', roles: ['admin', 'owner'] },
{ _id: 'assign-admin-role', roles: ['admin'] },
{ _id: 'assign-roles', roles: ['admin'] },
{ _id: 'ban-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'bulk-register-user', roles: ['admin'] },
{ _id: 'create-c', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-d', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-p', roles: ['admin', 'user', 'bot', 'app'] },
{ _id: 'create-personal-access-tokens', roles: ['admin', 'user'] },
{ _id: 'create-user', roles: ['admin'] },
{ _id: 'clean-channel-history', roles: ['admin'] },
{ _id: 'delete-c', roles: ['admin', 'owner'] },
{ _id: 'delete-d', roles: ['admin'] },
{ _id: 'delete-message', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'delete-own-message', roles: ['admin', 'user'] },
{ _id: 'delete-p', roles: ['admin', 'owner'] },
{ _id: 'delete-user', roles: ['admin'] },
{ _id: 'edit-message', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'edit-other-user-active-status', roles: ['admin'] },
{ _id: 'edit-other-user-info', roles: ['admin'] },
{ _id: 'edit-other-user-password', roles: ['admin'] },
{ _id: 'edit-other-user-avatar', roles: ['admin'] },
{ _id: 'edit-privileged-setting', roles: ['admin'] },
{ _id: 'edit-room', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'edit-room-retention-policy', roles: ['admin'] },
{ _id: 'force-delete-message', roles: ['admin', 'owner'] },
{ _id: 'join-without-join-code', roles: ['admin', 'bot', 'app'] },
{ _id: 'leave-c', roles: ['admin', 'user', 'bot', 'anonymous', 'app'] },
{ _id: 'leave-p', roles: ['admin', 'user', 'bot', 'anonymous', 'app'] },
{ _id: 'manage-assets', roles: ['admin'] },
{ _id: 'manage-emoji', roles: ['admin'] },
{ _id: 'manage-user-status', roles: ['admin'] },
{ _id: 'manage-outgoing-integrations', roles: ['admin'] },
{ _id: 'manage-incoming-integrations', roles: ['admin'] },
{ _id: 'manage-own-outgoing-integrations', roles: ['admin'] },
{ _id: 'manage-own-incoming-integrations', roles: ['admin'] },
{ _id: 'manage-oauth-apps', roles: ['admin'] },
{ _id: 'manage-selected-settings', roles: ['admin'] },
{ _id: 'mention-all', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'mention-here', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'mute-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'remove-user', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'run-import', roles: ['admin'] },
{ _id: 'run-migration', roles: ['admin'] },
{ _id: 'set-moderator', roles: ['admin', 'owner'] },
{ _id: 'set-owner', roles: ['admin', 'owner'] },
{ _id: 'send-many-messages', roles: ['admin', 'bot', 'app'] },
{ _id: 'set-leader', roles: ['admin', 'owner'] },
{ _id: 'unarchive-room', roles: ['admin'] },
{ _id: 'view-c-room', roles: ['admin', 'user', 'bot', 'app', 'anonymous'] },
{ _id: 'user-generate-access-token', roles: ['admin'] },
{ _id: 'view-d-room', roles: ['admin', 'user', 'bot', 'app', 'guest'] },
{ _id: 'view-full-other-user-info', roles: ['admin'] },
{ _id: 'view-history', roles: ['admin', 'user', 'anonymous'] },
{ _id: 'view-joined-room', roles: ['guest', 'bot', 'app', 'anonymous'] },
{ _id: 'view-join-code', roles: ['admin'] },
{ _id: 'view-logs', roles: ['admin'] },
{ _id: 'view-other-user-channels', roles: ['admin'] },
{ _id: 'view-p-room', roles: ['admin', 'user', 'anonymous', 'guest'] },
{ _id: 'view-privileged-setting', roles: ['admin'] },
{ _id: 'view-room-administration', roles: ['admin'] },
{ _id: 'view-statistics', roles: ['admin'] },
{ _id: 'view-user-administration', roles: ['admin'] },
{ _id: 'preview-c-room', roles: ['admin', 'user', 'anonymous'] },
{ _id: 'view-outside-room', roles: ['admin', 'owner', 'moderator', 'user'] },
{ _id: 'view-broadcast-member-list', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'call-management', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'view-l-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] },
{ _id: 'view-livechat-manager', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-rooms', roles: ['livechat-manager', 'admin'] },
{ _id: 'close-livechat-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] },
{ _id: 'close-others-livechat-room', roles: ['livechat-manager', 'admin'] },
{ _id: 'save-others-livechat-room-info', roles: ['livechat-manager'] },
{ _id: 'remove-closed-livechat-rooms', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-analytics', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-queue', roles: ['livechat-manager', 'admin'] },
{ _id: 'transfer-livechat-guest', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-managers', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-agents', roles: ['livechat-manager', 'admin'] },
{ _id: 'manage-livechat-departments', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-departments', roles: ['livechat-manager', 'admin'] },
{ _id: 'add-livechat-department-agents', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-current-chats', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-real-time-monitoring', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-triggers', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-customfields', roles: ['livechat-manager', 'admin'] },
@ -111,12 +111,14 @@ Meteor.startup(function() {
{ _id: 'view-livechat-webhooks', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-facebook', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-officeHours', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-room-closed-same-department', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-room-closed-by-another-agent', roles: ['livechat-manager', 'admin'] },
{ _id: 'view-livechat-room-customfields', roles: ['livechat-manager', 'livechat-agent', 'admin'] },
{ _id: 'edit-livechat-room-customfields', roles: ['livechat-manager', 'livechat-agent', 'admin'] },
];
for (const permission of permissions) {
if (!Permissions.findOneById(permission._id)) {
Permissions.upsert(permission._id, { $set: permission });
}
Permissions.create(permission._id, permission.roles);
}
const defaultRoles = [
@ -134,7 +136,7 @@ Meteor.startup(function() {
];
for (const role of defaultRoles) {
Roles.upsert({ _id: role.name }, { $setOnInsert: { scope: role.scope, description: role.description || '', protected: true, mandatory2fa: false } });
Roles.createOrUpdate(role.name, role.scope, role.description, true, false);
}
const getPreviousPermissions = function(settingId) {
@ -155,19 +157,17 @@ Meteor.startup(function() {
const createSettingPermission = function(setting, previousSettingPermissions) {
const permissionId = getSettingPermissionId(setting._id);
const permission = {
_id: permissionId,
level: CONSTANTS.SETTINGS_LEVEL,
// copy those setting-properties which are needed to properly publish the setting-based permissions
settingId: setting._id,
group: setting.group,
section: setting.section,
sorter: setting.sorter,
roles: [],
};
// copy previously assigned roles if available
if (previousSettingPermissions[permissionId] && previousSettingPermissions[permissionId].roles) {
permission.roles = previousSettingPermissions[permissionId].roles;
} else {
permission.roles = [];
}
if (setting.group) {
permission.groupPermissionId = getSettingPermissionId(setting.group);
@ -175,7 +175,16 @@ Meteor.startup(function() {
if (setting.section) {
permission.sectionPermissionId = getSettingPermissionId(setting.section);
}
Permissions.upsert(permission._id, { $set: permission });
const existent = Permissions.findOne({
_id: permissionId,
...permission,
}, { fields: { _id: 1 } });
if (!existent) {
Permissions.upsert({ _id: permissionId }, { $set: permission });
}
delete previousSettingPermissions[permissionId];
};

@ -30,8 +30,8 @@
<div class="alert">
{{_ "AutoTranslate_Change_Language_Description"}}
</div>
<button type="button" class="button cancel">{{_ "Cancel"}}</button>
<button type="button" class="button primary save">{{_ "Save"}}</button>
<button type="button" class="rc-button rc-button--cancel">{{_ "Cancel"}}</button>
<button type="button" class="rc-button rc-button--primary save">{{_ "Save"}}</button>
{{else}}
<span class="current-setting">{{languageName autoTranslateLanguage}} <i class="icon-pencil" data-edit="autoTranslateLanguage"></i></span>
{{/if}}

@ -156,7 +156,7 @@ export class AutoTranslate {
message = Markdown.parseMessageNotEscaped(message);
// Some parsers (e. g. Marked) wrap the complete message in a <p> - this is unnecessary and should be ignored with respect to translations
const regexWrappedParagraph = new RegExp('^\s*<p>|<\/p>\s*$', 'gm');
const regexWrappedParagraph = new RegExp('^\\s*<p>|</p>\\s*$', 'gm');
message.msg = message.msg.replace(regexWrappedParagraph, '');
for (const tokenIndex in message.tokens) {

@ -1 +0,0 @@
export { default } from './server/bigbluebutton-api';

@ -0,0 +1 @@
export { default } from './bigbluebutton-api';

@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
import { settings } from '../../settings';
import { RocketChatAssets } from '../../assets';
import { RocketChatAssets } from '../../assets/server';
WebApp.connectHandlers.use('/_blockstack/manifest', Meteor.bindEnvironment(function(req, res) {
const name = settings.get('Site_Name');

@ -1 +0,0 @@
import './server/index';

@ -240,9 +240,9 @@ Template.mailMessagesInstructions.onCreated(function() {
this.selectedUsers = new ReactiveVar([]);
this.userFilter = new ReactiveVar('');
const filter = { exceptions: [Meteor.user().username].concat(this.selectedUsers.get().map((u) => u.username)) };
const filter = { exceptions: this.selectedUsers.get().map((u) => u.username) };
Deps.autorun(() => {
filter.exceptions = [Meteor.user().username].concat(this.selectedUsers.get().map((u) => u.username));
filter.exceptions = this.selectedUsers.get().map((u) => u.username);
});
this.ac = new AutoComplete(

@ -7,7 +7,5 @@ Meteor.startup(function() {
_id: 'mail-messages',
roles: ['admin'],
};
return Permissions.upsert(permission._id, {
$setOnInsert: permission,
});
return Permissions.create(permission._id, permission.roles);
});

@ -4,7 +4,7 @@ import { createTemplateForComponent } from '../../../../client/reactAdapters';
createTemplateForComponent(
'Multiselect',
() => import('../../../ui-admin/client/components/settings/inputs/MultiSelectSettingInput'),
() => import('../../../../client/admin/settings/inputs/MultiSelectSettingInput'),
{
// eslint-disable-next-line new-cap
renderContainerView: () => HTML.DIV({ class: 'rc-multiselect', style: 'display: flex;' }),

@ -226,6 +226,7 @@
{{/if}}
{{/with}}
{{#with settings.joinCode}}
{{#if canView}}
<div class="rc-user-info__row rc-user-info__row--separator">
<div class="rc-input">
<label class="rc-input__label">
@ -236,6 +237,7 @@
</label>
</div>
</div>
{{/if}}
{{/with}}
{{#if hasRetentionPermission}}

@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { Permissions } from '../../models';
Meteor.startup(function() {
Permissions.upsert('post-readonly', { $setOnInsert: { roles: ['admin', 'owner', 'moderator'] } });
Permissions.upsert('set-readonly', { $setOnInsert: { roles: ['admin', 'owner'] } });
Permissions.upsert('set-react-when-readonly', { $setOnInsert: { roles: ['admin', 'owner'] } });
Permissions.create('post-readonly', ['admin', 'owner', 'moderator']);
Permissions.create('set-readonly', ['admin', 'owner']);
Permissions.create('set-react-when-readonly', ['admin', 'owner']);
});

@ -1,6 +1,6 @@
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute } from '../../ui-admin/client';
import { registerAdminRoute } from '../../../client/admin';
import { t } from '../../utils';
registerAdminRoute('/chatpal', {

@ -37,7 +37,7 @@
</div>
</fieldset>
<div class="submit">
<button class="button primary send"><span>{{_ "Chatpal_create_key"}}</span></button>
<button class="rc-button rc-button--primary send"><span>{{_ "Chatpal_create_key"}}</span></button>
</div>
</div>
</form>

@ -3,6 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { searchProviderService, SearchProvider } from '../../../search/server';
import ChatpalLogger from '../utils/logger';
import { Subscriptions } from '../../../models';
import { baseUrl } from '../utils/settings';
import Index from './index';
@ -16,7 +17,9 @@ class ChatpalProvider extends SearchProvider {
constructor() {
super('chatpalProvider');
this.chatpalBaseUrl = 'https://beta.chatpal.io/v1';
this.chatpalBaseUrl = `${ baseUrl }`;
ChatpalLogger.debug(`Using ${ this.chatpalBaseUrl } as chatpal base url`);
this._settings.add('Backend', 'select', 'cloud', {
values: [
@ -220,24 +223,24 @@ class ChatpalProvider extends SearchProvider {
if (this._settings.get('Backend') === 'cloud') {
config.baseurl = this.chatpalBaseUrl;
config.language = this._settings.get('Main_Language');
config.searchpath = '/search/search';
config.updatepath = '/search/update';
config.pingpath = '/search/ping';
config.clearpath = '/search/clear';
config.suggestionpath = '/search/suggest';
config.searchpath = 'search/search';
config.updatepath = 'search/update';
config.pingpath = 'search/ping';
config.clearpath = 'search/clear';
config.suggestionpath = 'search/suggest';
config.httpOptions = {
headers: {
'X-Api-Key': this._settings.get('API_Key'),
},
};
} else {
config.baseurl = this._settings.get('Base_URL').endsWith('/') ? this._settings.get('Base_URL').slice(0, -1) : this._settings.get('Base_URL');
config.baseurl = this._settings.get('Base_URL').replace(/\/?$/, '/');
config.language = this._settings.get('Main_Language');
config.searchpath = '/chatpal/search';
config.updatepath = '/chatpal/update';
config.pingpath = '/chatpal/ping';
config.clearpath = '/chatpal/clear';
config.suggestionpath = '/chatpal/suggest';
config.searchpath = 'chatpal/search';
config.updatepath = 'chatpal/update';
config.pingpath = 'chatpal/ping';
config.clearpath = 'chatpal/clear';
config.suggestionpath = 'chatpal/suggest';
config.httpOptions = {
headers: this._parseHeaders(),
};

@ -0,0 +1 @@
export const baseUrl = (process.env.CHATPAL_URL || 'https://api.chatpal.io/v1').replace(/\/?$/, '/');

@ -1,10 +1,12 @@
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import { baseUrl } from './settings';
Meteor.methods({
'chatpalUtilsCreateKey'(email) {
try {
const response = HTTP.call('POST', 'https://beta.chatpal.io/v1/account', { data: { email, tier: 'free' } });
const response = HTTP.call('POST', `${ baseUrl }account`, { data: { email, tier: 'free' } });
if (response.statusCode === 201) {
return response.data.key;
}
@ -15,7 +17,7 @@ Meteor.methods({
},
'chatpalUtilsGetTaC'(lang) {
try {
const response = HTTP.call('GET', `https://beta.chatpal.io/v1/terms/${ lang }.html`);
const response = HTTP.call('GET', `${ baseUrl }terms/${ lang }.html`);
if (response.statusCode === 200) {
return response.content;
}

@ -1,16 +0,0 @@
<template name="cloudCallback">
<div class="main-content-flex">
<section class="page-container page-home page-static page-settings">
{{> header sectionName="Cloud_connect"}}
<div class="content">
{{#requiresPermission 'manage-cloud'}}
{{#if callbackError.error}}
<p>{{_ "Cloud_error_in_authenticating"}}</p>
<p>{{_ "Cloud_error_code"}} {{ callbackError.errorCode }}</p>
{{/if}}
{{/requiresPermission}}
</div>
</section>
</div>
</template>

@ -1,46 +0,0 @@
import './callback.html';
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import queryString from 'query-string';
import { SideNav } from '../../../ui-utils/client';
Template.cloudCallback.onCreated(function() {
const instance = this;
instance.loading = new ReactiveVar(true);
instance.callbackError = new ReactiveVar({ error: false });
const params = queryString.parse(location.search);
if (params.error_code) {
instance.callbackError.set({ error: true, errorCode: params.error_code });
} else {
Meteor.call('cloud:finishOAuthAuthorization', params.code, params.state, (error) => {
if (error) {
console.warn('cloud:finishOAuthAuthorization', error);
return;
}
FlowRouter.go('/admin/cloud');
});
}
});
Template.cloudCallback.helpers({
callbackError() {
return Template.instance().callbackError.get();
},
});
Template.cloudCallback.onRendered(() => {
Tracker.afterFlush(() => {
SideNav.setFlex('adminFlex');
SideNav.openFlex();
});
});

@ -1,145 +0,0 @@
<template name="cloud">
<div class="main-content-flex">
<section class="page-container page-home page-static page-settings">
{{#header sectionName="Connectivity_Services" hideHelp=true fixedHeight=true fullpage=true}}
<div class="rc-header__section-button">
{{#unless info.workspaceRegistered}}
<button class="rc-button rc-button--small rc-button--primary rc-button--outline js-register">
{{_ "Cloud_Register_manually"}}
</button>
{{/unless}}
<a href="https://cloud.rocket.chat" class="rc-button rc-button--primary action cloud-console-btn" target="_blank">{{_ "Cloud_console"}}</a>
</div>
{{/header}}
<div class="content">
{{#requiresPermission 'manage-cloud'}}
<div class="section">
<div class="section-title">
<div class="section-title-text">
{{_ "Cloud_what_is_it"}}
</div>
</div>
<div class="section-content">
<p>{{_ "Cloud_what_is_it_description"}}</p>
</div>
<details>
<div class="section-content">
<p>{{_ "Cloud_what_is_it_services_like"}}</p>
<ul style="list-style-type:disc;margin-left:15px;">
<li>{{_ "Register_Server_Registered_Push_Notifications"}}</li>
<li>{{_ "Register_Server_Registered_Livechat"}}</li>
<li>{{_ "Register_Server_Registered_OAuth"}}</li>
<li>{{_ "Register_Server_Registered_Marketplace"}}</li>
</ul>
</div>
<div class="section-content">
{{_ "Cloud_what_is_it_additional"}}
</div>
</details>
</div>
<div class="section">
{{#if info.connectToCloud}}
{{#if info.workspaceRegistered}}
<div class="section-content border-component-color">
<p>{{_ "Cloud_workspace_connected"}}</p>
<div class="input-line double-col">
{{#if isLoggedIn}}
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--primary action logout-btn" target="_blank">{{_ "Cloud_logout"}}</button>
</div>
{{else}}
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--primary action login-btn" target="_blank">{{_ "Cloud_login_to_cloud"}}</button>
</div>
{{/if}}
</div>
</div>
<div class="section-content border-component-color">
<p>{{_ "Cloud_workspace_disconnect"}}</p>
<div class="input-line double-col">
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--danger action disconnect-btn">{{_ "Disconnect"}}</button>
</div>
</div>
</div>
{{else}}
<div class="section-content border-component-color">
<div class="input-line double-col">
<label class="setting-label" title="cloudEmail">{{_ "Email"}}</label>
<div class="setting-field">
<input class="input-monitor rc-input__element" type="text" name="cloudEmail" value="{{ info.email }}">
<div class="settings-description secondary-font-color">{{_ "Cloud_address_to_send_registration_to"}}</div>
</div>
</div>
<div class="input-line double-col">
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--primary action update-email-btn" style="float:left">{{_ "Cloud_update_email"}}</button>
<button type="button" class="rc-button rc-button--primary action resend-email-btn" style="float:left;margin-left:5px;">{{_ "Cloud_resend_email"}}</button>
</div>
</div>
<div class="input-line double-col">
<label class="setting-label" title="cloudToken">{{_ "Token"}}</label>
<div class="setting-field">
<input class="input-monitor rc-input__element" type="text" name="cloudToken" value="{{ info.token }}">
<div class="settings-description secondary-font-color">{{_ "Cloud_manually_input_token"}}</div>
</div>
</div>
<div class="input-line double-col">
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--primary action connect-btn">{{_ "Connect"}}</button>
</div>
</div>
<p>{{_ "Cloud_connect_support"}}: <a href="mailto:support@rocket.chat?subject=[Self Hosted Registration]&body=WorkspaceId: {{ info.workspaceId }}%0D%0ADeployment Id: {{ info.uniqueId }}%0D%0AIssue: <please describe your issue here>">support@rocket.chat</a></p>
</div>
{{/if}}
{{else}}
<div class="section-title">
<div class="section-title-text">
{{_ "Cloud_registration_required"}}
</div>
</div>
<div class="section-content border-component-color">
<p>{{_ "Cloud_registration_required_description"}}</p>
<button type="button" class="rc-button rc-button--primary action register-btn">{{_ "Cloud_registration_requried_link_text"}}</button>
</div>
{{/if}}
</div>
{{#if info.connectToCloud}}
<div class="section">
<div class="section-title">
<div class="section-title-text">
{{_ "Cloud_troubleshooting"}}
</div>
</div>
<div class="section-content border-component-color">
<p>{{_ "Cloud_workspace_support"}}</p>
<div class="input-line double-col">
<label class="setting-label" title=""></label>
<div class="setting-field">
<button type="button" class="rc-button rc-button--danger action sync-btn">{{_ "Sync"}}</button>
</div>
</div>
</div>
<div class="section-content">
{{_ "Cloud_status_page_description"}}: <a href="https://status.rocket.chat" target="_blank">status.rocket.chat</a>
</div>
</div>
{{/if}}
{{/requiresPermission}}
</div>
</section>
</div>
</template>

@ -1,233 +0,0 @@
import './cloud.html';
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import queryString from 'query-string';
import toastr from 'toastr';
import { t } from '../../../utils';
import { SideNav, modal } from '../../../ui-utils/client';
Template.cloud.onCreated(function() {
const instance = this;
instance.info = new ReactiveVar();
instance.loading = new ReactiveVar(true);
instance.isLoggedIn = new ReactiveVar(false);
instance.loadRegStatus = function _loadRegStatus() {
Meteor.call('cloud:checkRegisterStatus', (error, info) => {
if (error) {
console.warn('cloud:checkRegisterStatus', error);
return;
}
instance.info.set(info);
instance.loading.set(false);
});
};
instance.getLoggedIn = function _getLoggedIn() {
Meteor.call('cloud:checkUserLoggedIn', (error, result) => {
if (error) {
console.warn(error);
return;
}
instance.isLoggedIn.set(result);
});
};
instance.oauthAuthorize = function _oauthAuthorize() {
Meteor.call('cloud:getOAuthAuthorizationUrl', (error, url) => {
if (error) {
console.warn(error);
return;
}
window.location.href = url;
});
};
instance.logout = function _logout() {
Meteor.call('cloud:logout', (error) => {
if (error) {
console.warn(error);
return;
}
instance.getLoggedIn();
});
};
instance.connectWorkspace = function _connectWorkspace(token) {
Meteor.call('cloud:connectWorkspace', token, (error, success) => {
if (error) {
toastr.error(error);
instance.loadRegStatus();
return;
}
if (!success) {
toastr.error('An error occured connecting');
instance.loadRegStatus();
return;
}
toastr.success(t('Connected'));
instance.loadRegStatus();
});
};
instance.disconnectWorkspace = function _disconnectWorkspace() {
Meteor.call('cloud:disconnectWorkspace', (error, success) => {
if (error) {
toastr.error(error);
instance.loadRegStatus();
return;
}
if (!success) {
toastr.error('An error occured disconnecting');
instance.loadRegStatus();
return;
}
toastr.success(t('Disconnected'));
instance.loadRegStatus();
});
};
instance.syncWorkspace = function _syncWorkspace() {
Meteor.call('cloud:syncWorkspace', (error, success) => {
if (error) {
toastr.error(error);
instance.loadRegStatus();
return;
}
if (!success) {
toastr.error('An error occured syncing');
instance.loadRegStatus();
return;
}
toastr.success(t('Sync Complete'));
instance.loadRegStatus();
});
};
instance.registerWorkspace = function _registerWorkspace() {
Meteor.call('cloud:registerWorkspace', (error, success) => {
if (error) {
toastr.error(error);
instance.loadRegStatus();
return;
}
if (!success) {
toastr.error('An error occured');
instance.loadRegStatus();
return;
}
return instance.syncWorkspace();
});
};
const params = queryString.parse(location.search);
if (params.token) {
instance.connectWorkspace(params.token);
} else {
instance.loadRegStatus();
}
instance.getLoggedIn();
});
Template.cloud.helpers({
info() {
return Template.instance().info.get();
},
isLoggedIn() {
return Template.instance().isLoggedIn.get();
},
});
Template.cloud.events({
'click .js-register'() {
modal.open({
template: 'cloudRegisterManually',
showCancelButton: false,
showConfirmButton: false,
showFooter: false,
closeOnCancel: true,
html: true,
confirmOnEnter: false,
});
},
'click .update-email-btn'() {
const val = $('input[name=cloudEmail]').val();
Meteor.call('cloud:updateEmail', val, false, (error) => {
if (error) {
console.warn(error);
return;
}
toastr.success(t('Saved'));
});
},
'click .resend-email-btn'() {
const val = $('input[name=cloudEmail]').val();
Meteor.call('cloud:updateEmail', val, true, (error) => {
if (error) {
console.warn(error);
return;
}
toastr.success(t('Requested'));
});
},
'click .login-btn'(e, i) {
i.oauthAuthorize();
},
'click .logout-btn'(e, i) {
i.logout();
},
'click .connect-btn'(e, i) {
const token = $('input[name=cloudToken]').val();
i.connectWorkspace(token);
},
'click .register-btn'(e, i) {
i.registerWorkspace();
},
'click .disconnect-btn'(e, i) {
i.disconnectWorkspace();
},
'click .sync-btn'(e, i) {
i.syncWorkspace();
},
});
Template.cloud.onRendered(() => {
Tracker.afterFlush(() => {
SideNav.setFlex('adminFlex');
SideNav.openFlex();
});
});

@ -1,26 +0,0 @@
.rc-promtp {
display: flex;
min-height: 188px;
padding: 1rem;
border-radius: 2px;
background: #2f343d;
flex-flow: column wrap;
justify-content: space-between;
&--element,
&--element[disabled] {
flex: 1 1 auto;
resize: none;
color: #cbced1;
border: none;
background: none;
font-family: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
font-size: 14px;
line-height: 20px;
}
}

@ -1,36 +0,0 @@
<template name="cloudRegisterManually">
{{> header sectionName="Cloud_Register_manually" hideHelp=true fullpage=true}}
{{# if copyStep }}
<form class="preferences-page__content">
<p class="rc-modal__description">{{_ "Cloud_register_offline_helper" }}</p>
<div class="rc-promtp">
<textarea class="rc-promtp--element" disabled>{{clientKey}}</textarea>
<button class="rc-button rc-button--primary js-copy" data-clipboard-text="{{clientKey}}">
{{>icon icon='copy'}} {{_ "Copy"}}
</button>
</div>
<p class="rc-modal__description js-cloud">{{#if cloudLink}} {{{cloudLink}}} {{else}} <a href="https://cloud.rocket.chat" rel="noopener noreferrer" class="cloud-console-btn" target="_blank"></a>{{/if}}</p>
</form>
<footer class="rc-modal__footer rc-modal__footer--empty">
<button class="rc-button rc-button--primary js-next">{{_ "Next"}}</button>
</footer>
{{else}}
<form class="preferences-page__content">
<p class="rc-modal__description">{{_ "Cloud_register_offline_finish_helper"}}</p>
<div class="rc-promtp">
<textarea class="js-cloud-key rc-promtp--element" placeholder="{{_ "Paste_here"}}" disabled={{isLoading}}></textarea>
</div>
</form>
<footer class="rc-modal__footer rc-modal__footer--empty">
<button class="rc-button rc-button--secondary js-back">{{_ "Back"}}</button>
<button class="rc-button rc-button--primary js-finish" disabled='{{disabled}}'>
{{#if isLoading}} {{> loading}} {{/if}}
<span style="{{#if isLoading}} visibility:hidden {{/if}}">{{_ "Finish Registration"}}</span>
</button>
</footer>
{{/if}}
</template>

@ -1,106 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveDict } from 'meteor/reactive-dict';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import Clipboard from 'clipboard';
import toastr from 'toastr';
import { APIClient } from '../../../utils/client';
import { modal } from '../../../ui-utils/client';
import './cloudRegisterManually.html';
import './cloudRegisterManually.css';
const CLOUD_STEPS = {
COPY: 0,
PASTE: 1,
DONE: 2,
ERROR: 3,
};
Template.cloudRegisterManually.events({
'submit form'(e) {
e.preventDefault();
},
'input .js-cloud-key'(e, instance) {
instance.state.set('cloudKey', e.currentTarget.value);
},
'click .js-next'(event, instance) {
instance.state.set('step', CLOUD_STEPS.PASTE);
},
'click .js-back'(event, instance) {
instance.state.set('step', CLOUD_STEPS.COPY);
},
'click .js-finish'(event, instance) {
instance.state.set('loading', true);
APIClient
.post('v1/cloud.manualRegister', {}, { cloudBlob: instance.state.get('cloudKey') })
.then(() => modal.open({
type: 'success',
title: TAPi18n.__('Success'),
text: TAPi18n.__('Cloud_register_success'),
confirmButtonText: TAPi18n.__('Ok'),
closeOnConfirm: false,
showCancelButton: false,
}, () => window.location.reload()))
.catch(() => modal.open({
type: 'error',
title: TAPi18n.__('Error'),
text: TAPi18n.__('Cloud_register_error'),
}))
.then(() => instance.state.set('loading', false));
},
});
Template.cloudRegisterManually.helpers({
cloudLink() {
return Template.instance().cloudLink.get();
},
copyStep() {
return Template.instance().state.get('step') === CLOUD_STEPS.COPY;
},
clientKey() {
return Template.instance().state.get('clientKey');
},
isLoading() {
return Template.instance().state.get('loading');
},
step() {
return Template.instance().state.get('step');
},
disabled() {
const { state } = Template.instance();
const shouldDisable = state.get('cloudKey').trim().length === 0 || state.get('loading');
return shouldDisable && 'disabled';
},
});
Template.cloudRegisterManually.onRendered(function() {
const clipboard = new Clipboard('.js-copy');
clipboard.on('success', function() {
toastr.success(TAPi18n.__('Copied'));
});
const btn = this.find('.cloud-console-btn');
// After_copy_the_text_go_to_cloud
this.cloudLink.set(TAPi18n.__('Cloud_click_here').replace(/(\[(.*)\]\(\))/ig, (_, __, text) => btn.outerHTML.replace('</a>', `${ text }</a>`)));
});
Template.cloudRegisterManually.onCreated(function() {
this.cloudLink = new ReactiveVar();
this.state = new ReactiveDict({
step: CLOUD_STEPS.COPY,
loading: false,
clientKey: '',
cloudKey: '',
error: '',
});
Meteor.call('cloud:getWorkspaceRegisterData', (error, result) => {
this.state.set('clientKey', result);
});
});

@ -1,2 +0,0 @@
import './cloud';
import './callback';

@ -1,33 +0,0 @@
import './admin/callback';
import './admin/cloud';
import './admin/cloudRegisterManually';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute, registerAdminSidebarItem } from '../../ui-admin/client';
import { hasAtLeastOnePermission } from '../../authorization';
registerAdminRoute('/cloud', {
name: 'cloud',
async action() {
await import('./admin');
BlazeLayout.render('main', { center: 'cloud', old: true });
},
});
registerAdminRoute('/cloud/oauth-callback', {
name: 'cloud-oauth-callback',
async action() {
await import('./admin');
BlazeLayout.render('main', { center: 'cloudCallback', old: true });
},
});
registerAdminSidebarItem({
icon: 'cloud-plus',
href: 'cloud',
i18nLabel: 'Connectivity_Services',
permissionGranted() {
return hasAtLeastOnePermission(['manage-cloud']);
},
});

@ -11,7 +11,7 @@ import { Permissions } from '../../models';
import { settings } from '../../settings/server';
if (Permissions) {
Permissions.createOrUpdate('manage-cloud', ['admin']);
Permissions.create('manage-cloud', ['admin']);
}
const licenseCronName = 'Cloud Workspace Sync';

@ -44,6 +44,7 @@ WebApp.rawConnectHandlers.use(Meteor.bindEnvironment(function(req, res, next) {
});
}));
// Deprecated setting
let Support_Cordova_App = false;
settings.get('Support_Cordova_App', (key, value) => {
Support_Cordova_App = value;
@ -53,24 +54,24 @@ WebApp.rawConnectHandlers.use(function(req, res, next) {
// XSS Protection for old browsers (IE)
res.setHeader('X-XSS-Protection', '1');
if (Support_Cordova_App !== true) {
return next();
}
if (/^\/(api|_timesync|sockjs|tap-i18n)(\/|$)/.test(req.url)) {
res.setHeader('Access-Control-Allow-Origin', '*');
}
if (settings.get('Iframe_Restrict_Access')) {
res.setHeader('X-Frame-Options', settings.get('Iframe_X_Frame_Options'));
}
const { setHeader } = res;
res.setHeader = function(key, val, ...args) {
if (key.toLowerCase() === 'access-control-allow-origin' && val === 'http://meteor.local') {
return;
// Deprecated behavior
if (Support_Cordova_App === true) {
if (/^\/(api|_timesync|sockjs|tap-i18n)(\/|$)/.test(req.url)) {
res.setHeader('Access-Control-Allow-Origin', '*');
}
return setHeader.apply(this, [key, val, ...args]);
};
const { setHeader } = res;
res.setHeader = function(key, val, ...args) {
if (key.toLowerCase() === 'access-control-allow-origin' && val === 'http://meteor.local') {
return;
}
return setHeader.apply(this, [key, val, ...args]);
};
}
return next();
});

@ -9,6 +9,7 @@ import { _setRealName } from '../../lib';
import { Users } from '../../models';
import { settings } from '../../settings';
import { hasRole } from '../../authorization';
import { deleteUser } from '../../lib/server/functions';
const logger = new Logger('CROWD', {});
@ -203,6 +204,13 @@ export class CROWD {
const response = self.crowdClient.searchSync('user', `email=" ${ email } "`);
if (!response || response.users.length === 0) {
logger.warn('Could not find user in CROWD with username or email:', crowd_username, email);
if (settings.get('CROWD_Remove_Orphaned_Users') === true) {
logger.info('Removing user:', crowd_username);
Meteor.defer(function() {
deleteUser(user._id);
logger.info('User removed:', crowd_username);
});
}
return;
}
crowd_username = response.users[0].name;

@ -14,6 +14,7 @@ Meteor.startup(function() {
this.add('CROWD_APP_PASSWORD', '', { type: 'password', enableQuery, i18nLabel: 'Password', secret: true });
this.add('CROWD_Sync_User_Data', false, { type: 'boolean', enableQuery, i18nLabel: 'Sync_Users' });
this.add('CROWD_Sync_Interval', 'Every 60 mins', { type: 'string', enableQuery: enableSyncQuery, i18nLabel: 'Sync_Interval', i18nDescription: 'Crowd_sync_interval_Description' });
this.add('CROWD_Remove_Orphaned_Users', false, { type: 'boolean', public: true, i18nLabel: 'Crowd_Remove_Orphaned_Users' });
this.add('CROWD_Clean_Usernames', true, { type: 'boolean', enableQuery, i18nLabel: 'Clean_Usernames', i18nDescription: 'Crowd_clean_usernames_Description' });
this.add('CROWD_Allow_Custom_Username', true, { type: 'boolean', i18nLabel: 'CROWD_Allow_Custom_Username' });
this.add('CROWD_Test_Connection', 'crowd_test_connection', { type: 'action', actionText: 'Test_Connection', i18nLabel: 'Test_Connection' });

@ -1,111 +0,0 @@
.sound-info {
& .icon-play-circled {
cursor: pointer;
}
}
.sound-view {
z-index: 15;
overflow-x: hidden;
overflow-y: auto;
& .thumb {
width: 100%;
height: 350px;
padding: 20px;
}
& nav {
padding: 0 20px;
}
& .info {
padding: 0 20px;
white-space: normal;
& h3 {
overflow: hidden;
width: 100%;
margin: 8px 0;
user-select: text;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 24px;
line-height: 27px;
& i::after {
display: inline-block;
width: 8px;
height: 8px;
content: " ";
vertical-align: middle;
border-radius: 4px;
}
}
& p {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
font-size: 12px;
font-weight: 300;
line-height: 18px;
}
}
& .edit-form {
padding: 20px 20px 0;
white-space: normal;
& h3 {
margin-bottom: 8px;
font-size: 24px;
line-height: 22px;
}
& p {
font-size: 12px;
font-weight: 300;
line-height: 18px;
}
& > .input-line {
margin-top: 20px;
}
& nav {
padding: 0;
&.buttons {
margin-top: 2em;
}
}
& .form-divisor {
height: 9px;
margin: 2em 0;
text-align: center;
& > span {
padding: 0 1em;
}
}
}
& .room-info-content > div {
margin: 0 0 20px;
}
}

@ -1,7 +0,0 @@
<template name="adminSoundEdit">
<div class="content">
<div class="sound-view">
{{> soundEdit .}}
</div>
</div>
</template>

@ -1,7 +0,0 @@
<template name="adminSoundInfo">
<div class="content">
<div class="sound-view">
{{> soundInfo .}}
</div>
</div>
</template>

@ -1,78 +0,0 @@
<template name="adminSounds">
<div class="main-content-flex">
<section class="page-container page-list flex-tab-main-content">
{{> header sectionName="Custom_Sounds"}}
<div class="content">
{{#requiresPermission 'manage-sounds'}}
<form class="search-form" role="form">
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{#if isLoading}}
{{> loading }}
{{else}}
{{> icon block="rc-input__icon-svg" icon="magnifier" }}
{{/if}}
</div>
<input id="sound-filter" type="text" class="rc-input__element"
placeholder="{{_ "Search"}}" autofocus dir="auto">
</div>
</form>
<div class="results">
{{{_ "Showing_results" customsounds.length}}}
</div>
{{#table fixed='true' onItemClick=onTableItemClick onScroll=onTableScroll onResize=onTableResize}}
<thead>
<tr>
<th width="95%">
<div class="table-fake-th">{{_ "Name"}}</div>
</th>
<th width="5%">
<div class="table-fake-th">{{_ "Action"}}</div>
</th>
</tr>
</thead>
<tbody>
{{#each customsounds}}
<tr>
<td width="80%">
<div class="rc-table-wrapper">
<div class="rc-table-info">
<span class="rc-table-title">
{{name}}
</span>
</div>
</div>
</td>
<td width="20%">
<div class="rc-table-wrapper">
{{#if isPlaying _id}}
{{>icon _id=_id icon="pause" block="icon-pause-circled"}}
{{else}}
{{>icon _id=_id icon="play" block="icon-play-circled"}}
{{/if}}
{{>icon _id=_id icon="ban" block="icon-reset-circled"}}
</div>
</td>
</tr>
{{else}}
{{# with searchText}}
<tr class="table-no-click">
<td>{{_ "No_results_found_for"}} {{.}}</td>
</tr>
{{/with}}
{{/each}}
{{#if isLoading}}
<tr class="table-no-click">
<td class="table-loading-td">{{> loading}}</td>
</tr>
{{/if}}
</tbody>
{{/table}}
{{/requiresPermission}}
</div>
</section>
{{#with flexData}}
{{> flexTabBar}}
{{/with}}
</div>
</template>

@ -1,176 +0,0 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import _ from 'underscore';
import { RocketChatTabBar, SideNav, TabBar } from '../../../ui-utils';
import { CustomSounds } from '../lib/CustomSounds';
import { APIClient } from '../../../utils/client';
const LIST_SIZE = 50;
const DEBOUNCE_TIME_TO_SEARCH_IN_MS = 500;
Template.adminSounds.helpers({
searchText() {
const instance = Template.instance();
return instance.filter && instance.filter.get();
},
isPlaying(_id) {
return Template.instance().isPlayingId.get() === _id;
},
customsounds() {
return Template.instance().sounds.get();
},
isLoading() {
return Template.instance().isLoading.get();
},
flexData() {
return {
tabBar: Template.instance().tabBar,
data: Template.instance().tabBarData.get(),
};
},
onTableScroll() {
const instance = Template.instance();
return function(currentTarget) {
if (currentTarget.offsetHeight + currentTarget.scrollTop < currentTarget.scrollHeight - 100) {
return;
}
const sounds = instance.sounds.get();
if (instance.total.get() > sounds.length) {
instance.offset.set(instance.offset.get() + LIST_SIZE);
}
};
},
onTableItemClick() {
const instance = Template.instance();
return function(item) {
instance.tabBarData.set({
sound: instance.sounds.get().find((sound) => sound._id === item._id),
onSuccess: instance.onSuccessCallback,
});
instance.tabBar.showGroup('custom-sounds-selected');
instance.tabBar.open('admin-sound-info');
};
},
});
Template.adminSounds.onCreated(function() {
const instance = this;
this.sounds = new ReactiveVar([]);
this.offset = new ReactiveVar(0);
this.total = new ReactiveVar(0);
this.query = new ReactiveVar({});
this.isLoading = new ReactiveVar(false);
this.filter = new ReactiveVar('');
this.isPlayingId = new ReactiveVar('');
this.tabBar = new RocketChatTabBar();
this.tabBar.showGroup(FlowRouter.current().route.name);
this.tabBarData = new ReactiveVar();
TabBar.addButton({
groups: ['custom-sounds', 'custom-sounds-selected'],
id: 'add-sound',
i18nTitle: 'Custom_Sound_Add',
icon: 'plus',
template: 'adminSoundEdit',
order: 1,
});
TabBar.addButton({
groups: ['custom-sounds-selected'],
id: 'admin-sound-info',
i18nTitle: 'Custom_Sound_Info',
icon: 'customize',
template: 'adminSoundInfo',
order: 2,
});
this.onSuccessCallback = () => {
this.offset.set(0);
return this.loadSounds(this.query.get(), this.offset.get());
};
this.tabBarData.set({
onSuccess: instance.onSuccessCallback,
});
this.loadSounds = _.debounce(async (query, offset) => {
this.isLoading.set(true);
const { sounds, total } = await APIClient.v1.get(`custom-sounds.list?count=${ LIST_SIZE }&offset=${ offset }&query=${ JSON.stringify(query) }`);
this.total.set(total);
if (offset === 0) {
this.sounds.set(sounds);
} else {
this.sounds.set(this.sounds.get().concat(sounds));
}
this.isLoading.set(false);
}, DEBOUNCE_TIME_TO_SEARCH_IN_MS);
this.autorun(() => {
const filter = this.filter.get() && this.filter.get().trim();
const offset = this.offset.get();
if (filter) {
const regex = { $regex: filter, $options: 'i' };
return this.loadSounds({ name: regex }, offset);
}
return this.loadSounds({}, offset);
});
});
Template.adminSounds.onRendered(() =>
Tracker.afterFlush(function() {
SideNav.setFlex('adminFlex');
SideNav.openFlex();
}),
);
Template.adminSounds.events({
'keydown #sound-filter'(e) {
// stop enter key
if (e.which === 13) {
e.stopPropagation();
e.preventDefault();
}
},
'keyup #sound-filter'(e, t) {
e.stopPropagation();
e.preventDefault();
t.filter.set(e.currentTarget.value);
t.offset.set(0);
},
'click .icon-play-circled'(e, t) {
e.preventDefault();
e.stopPropagation();
CustomSounds.play(this._id);
const audio = document.getElementById(t.isPlayingId.get());
if (audio) {
audio.pause();
}
document.getElementById(this._id).onended = () => {
t.isPlayingId.set('');
this.onended = null;
};
t.isPlayingId.set(this._id);
},
'click .icon-pause-circled'(e, t) {
e.preventDefault();
e.stopPropagation();
const audio = document.getElementById(this._id);
if (audio && !audio.paused) {
audio.pause();
}
t.isPlayingId.set('');
},
'click .icon-reset-circled'(e) {
e.preventDefault();
e.stopPropagation();
const audio = document.getElementById(this._id);
if (audio) {
audio.currentTime = 0;
}
},
});

@ -1,11 +0,0 @@
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute } from '../../../ui-admin/client';
registerAdminRoute('/custom-sounds', {
name: 'custom-sounds',
async action(/* params*/) {
await import('./views');
BlazeLayout.render('main', { center: 'adminSounds' });
},
});

@ -1,25 +0,0 @@
<template name="soundEdit">
{{#requiresPermission 'manage-sounds'}}
<div class="about clearfix">
<form class="edit-form" autocomplete="off">
{{#if sound}}
<h3>{{sound.name}}</h3>
{{else}}
<h3>{{_ "Custom_Sound_Add"}}</h3>
{{/if}}
<div class="input-line">
<label for="name">{{_ "Name"}}</label>
<input type="text" id="name" autocomplete="off" value="{{sound.name}}">
</div>
<div class="input-line">
<label for="image">{{_ "Sound_File_mp3"}}</label>
<input id="image" type="file" accept="audio/mp3,audio/mpeg,audio/x-mpeg,audio/mpeg3,audio/x-mpeg-3,.mp3"/>
</div>
<nav>
<button class='button button-block cancel' type="button"><span>{{_ "Cancel"}}</span></button>
<button class='button button-block primary save'><span>{{_ "Save"}}</span></button>
</nav>
</form>
</div>
{{/requiresPermission}}
</template>

@ -1,155 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import toastr from 'toastr';
import s from 'underscore.string';
import { t, handleError } from '../../../utils';
Template.soundEdit.helpers({
sound() {
return Template.instance().sound;
},
name() {
return this.name || this._id;
},
});
Template.soundEdit.events({
'click .cancel'(e, t) {
e.stopPropagation();
e.preventDefault();
delete Template.instance().soundFile;
t.cancel(t.find('form'));
},
'submit form'(e, t) {
e.stopPropagation();
e.preventDefault();
t.save(e.currentTarget);
},
'change input[type=file]'(ev) {
const e = ev.originalEvent != null ? ev.originalEvent : ev;
let { files } = e.target;
if (e.target.files == null || files.length === 0) {
if (e.dataTransfer.files != null) {
files = e.dataTransfer.files;
} else {
files = [];
}
}
// using let x of y here seems to have incompatibility with some phones
for (const file in files) {
if (files.hasOwnProperty(file)) {
Template.instance().soundFile = files[file];
}
}
},
});
Template.soundEdit.onCreated(function() {
if (this.data != null) {
this.sound = this.data.sound;
} else {
this.sound = undefined;
this.data.tabBar.showGroup('custom-sounds');
}
this.onSuccess = Template.currentData().onSuccess;
this.cancel = (form, name) => {
form.reset();
this.data.tabBar.close();
if (this.sound) {
this.data.back(name);
}
};
this.getSoundData = () => {
const soundData = {};
if (this.sound != null) {
soundData._id = this.sound._id;
soundData.previousName = this.sound.name;
soundData.extension = this.sound.extension;
soundData.previousExtension = this.sound.extension;
}
soundData.name = s.trim(this.$('#name').val());
soundData.newFile = false;
return soundData;
};
this.validate = () => {
const soundData = this.getSoundData();
const errors = [];
if (!soundData.name) {
errors.push('Name');
}
if (!soundData._id) {
if (!this.soundFile) {
errors.push('Sound_File_mp3');
}
}
for (const error of errors) {
toastr.error(TAPi18n.__('error-the-field-is-required', { field: TAPi18n.__(error) }));
}
if (this.soundFile) {
if (!/audio\/mp3/.test(this.soundFile.type) && !/audio\/mpeg/.test(this.soundFile.type) && !/audio\/x-mpeg/.test(this.soundFile.type)) {
errors.push('FileType');
toastr.error(TAPi18n.__('error-invalid-file-type'));
}
}
return errors.length === 0;
};
this.save = (form) => {
if (this.validate()) {
const soundData = this.getSoundData();
if (this.soundFile) {
soundData.newFile = true;
soundData.extension = this.soundFile.name.split('.').pop();
soundData.type = this.soundFile.type;
}
Meteor.call('insertOrUpdateSound', soundData, (error, result) => {
if (result) {
soundData._id = result;
soundData.random = Math.round(Math.random() * 1000);
if (this.soundFile) {
toastr.info(TAPi18n.__('Uploading_file'));
const reader = new FileReader();
reader.readAsBinaryString(this.soundFile);
reader.onloadend = () => {
Meteor.call('uploadCustomSound', reader.result, this.soundFile.type, soundData, (uploadError/* , data*/) => {
if (uploadError != null) {
handleError(uploadError);
console.log(uploadError);
}
},
);
delete this.soundFile;
toastr.success(TAPi18n.__('File_uploaded'));
};
}
toastr.success(t('Custom_Sound_Saved_Successfully'));
this.onSuccess();
this.cancel(form, soundData.name);
}
if (error) {
handleError(error);
}
});
}
};
});

@ -1,19 +0,0 @@
<template name="soundInfo">
{{#if editingSound}}
{{> soundEdit (soundToEdit)}}
{{else}}
{{#with sound}}
<div class="about clearfix">
<div class="info">
<h3 title="{{name}}">{{name}}</h3>
</div>
</div>
{{/with}}
<nav>
{{#if hasPermission 'manage-sounds'}}
<button class='button button-block danger delete'><span><i class='icon-trash'></i> {{_ "Delete"}}</span></button>
<button class='button button-block primary edit-sound'><span><i class='icon-edit'></i> {{_ "Edit"}}</span></button>
{{/if}}
</nav>
{{/if}}
</template>

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

Loading…
Cancel
Save