diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 99c8cbef0c485..961e3dd4fdba8 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,6 +1,6 @@ FROM node:12.22.1-buster-slim -LABEL maintainer="buildmaster@rocket.chat" +LABEL maintainer="lltcggie@gmail.com" # dependencies RUN groupadd -g 65533 -r rocketchat \ diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test_custom.yml similarity index 76% rename from .github/workflows/build_and_test.yml rename to .github/workflows/build_and_test_custom.yml index 142abe4ad4f5d..b2f11ad0b50e2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test_custom.yml @@ -1,13 +1,11 @@ -name: Build and Test +name: Build and Test Custom on: - release: - types: [published] - pull_request: - branches: '**' push: branches: - - develop + - customize-develop + tags: + - "*-custom" env: CI: true @@ -142,7 +140,7 @@ jobs: # git checkout -- server/main.js client/main.js .meteor/packages - name: Reset Meteor - if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop' + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/customize-develop' run: | meteor reset @@ -404,7 +402,7 @@ jobs: deploy: runs-on: ubuntu-latest - if: github.event_name == 'release' || github.ref == 'refs/heads/develop' + if: startsWith(github.ref, 'refs/tags/') && endsWith(github.ref, '-custom') || github.ref == 'refs/heads/customize-develop' needs: test steps: @@ -416,69 +414,39 @@ jobs: name: build path: /tmp/build - - name: Publish assets - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: 'us-east-1' - GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - REDHAT_REGISTRY_PID: ${{ secrets.REDHAT_REGISTRY_PID }} - REDHAT_REGISTRY_KEY: ${{ secrets.REDHAT_REGISTRY_KEY }} - UPDATE_TOKEN: ${{ secrets.UPDATE_TOKEN }} + - name: Prepare publish assets run: | - if [[ '${{ github.event_name }}' = 'release' ]]; then - GIT_TAG="${GITHUB_REF#*tags/}" - GIT_BRANCH="" ARTIFACT_NAME="$(npm run version --silent)" - RC_VERSION=$GIT_TAG - - if [[ $GIT_TAG =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+ ]]; then - SNAP_CHANNEL=candidate - RC_RELEASE=candidate - elif [[ $GIT_TAG =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - SNAP_CHANNEL=stable - RC_RELEASE=stable - fi - else - GIT_TAG="" - GIT_BRANCH="${GITHUB_REF#*heads/}" - ARTIFACT_NAME="$(npm run version --silent).$GITHUB_SHA" - RC_VERSION="$(npm run version --silent)" - SNAP_CHANNEL=edge - RC_RELEASE=develop - fi; + ROCKET_DEPLOY_DIR="/tmp/deploy" FILENAME="$ROCKET_DEPLOY_DIR/rocket.chat-$ARTIFACT_NAME.tgz"; - aws s3 cp s3://rocketchat/sign.key.gpg .github/sign.key.gpg - mkdir -p $ROCKET_DEPLOY_DIR - cp .github/sign.key.gpg /tmp - gpg --yes --batch --passphrase=$GPG_PASSWORD /tmp/sign.key.gpg - gpg --allow-secret-key-import --import /tmp/sign.key - rm /tmp/sign.key - ln -s /tmp/build/Rocket.Chat.tar.gz "$FILENAME" - gpg --armor --detach-sign "$FILENAME" - - aws s3 cp $ROCKET_DEPLOY_DIR/ s3://download.rocket.chat/build/ --recursive - curl -H "Content-Type: application/json" -H "X-Update-Token: $UPDATE_TOKEN" -d \ - "{\"commit\": \"$GITHUB_SHA\", \"tag\": \"$RC_VERSION\", \"branch\": \"$GIT_BRANCH\", \"artifactName\": \"$ARTIFACT_NAME\", \"releaseType\": \"$RC_RELEASE\" }" \ - https://releases.rocket.chat/update - - # Makes build fail if the release isn't there - curl --fail https://releases.rocket.chat/$RC_VERSION/info - - if [[ $GIT_TAG ]]; then - curl -X POST \ - https://connect.redhat.com/api/v2/projects/$REDHAT_REGISTRY_PID/build \ - -H "Authorization: Bearer $REDHAT_REGISTRY_KEY" \ - -H 'Cache-Control: no-cache' \ - -H 'Content-Type: application/json' \ - -d '{"tag":"'$GIT_TAG'"}' - fi + - name: Create Release + id: create_release + uses: actions/create-release@v1.0.0 + if: startsWith(github.ref, 'refs/tags/') + env: + GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + prerelease: false + + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1.0.1 + if: startsWith(github.ref, 'refs/tags/') + env: + GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: /tmp/build/Rocket.Chat.tar.gz + asset_name: Rocket.Chat.tar.gz + asset_content_type: application/gzip image-build: runs-on: ubuntu-latest @@ -489,7 +457,7 @@ jobs: release: ["official", "preview"] env: - IMAGE: "rocketchat/rocket.chat" + IMAGE: "lltcggie/rocket.chat" steps: - uses: actions/checkout@v2 @@ -524,7 +492,7 @@ jobs: fi; - name: Build Docker image for tag - if: github.event_name == 'release' + if: startsWith(github.ref, 'refs/tags/') && endsWith(github.ref, '-custom') run: | cd /tmp/build GIT_TAG="${GITHUB_REF#*tags/}" @@ -533,6 +501,10 @@ jobs: IMAGE="${IMAGE}.preview" fi; + if echo "$GIT_TAG" | grep -Eq '.+-custom$' ; then + export GIT_TAG=`echo "$GIT_TAG" | sed -e 's/-custom$//g'` + fi; + docker build -t ${IMAGE}:$GIT_TAG . docker push ${IMAGE}:$GIT_TAG @@ -546,7 +518,7 @@ jobs: docker push ${IMAGE}:${RELEASE} - name: Build Docker image for develop - if: github.ref == 'refs/heads/develop' + if: github.ref == 'refs/heads/customize-develop' run: | cd /tmp/build @@ -556,59 +528,3 @@ jobs: docker build -t ${IMAGE}:develop . docker push ${IMAGE}:develop - - services-image-build: - runs-on: ubuntu-latest - needs: deploy - - strategy: - matrix: - service: ["account", "authorization", "ddp-streamer", "presence", "stream-hub"] - - steps: - - uses: actions/checkout@v2 - - - name: Use Node.js 12.22.1 - uses: actions/setup-node@v2 - with: - node-version: "12.22.1" - - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASS }} - - - name: Build Docker images - run: | - # defines image tag - if [[ $GITHUB_REF == refs/tags/* ]]; then - IMAGE_TAG="${GITHUB_REF#refs/tags/}" - else - IMAGE_TAG="${GITHUB_REF#refs/heads/}" - fi - - # first install repo dependencies - npm i - - # then micro services dependencies - cd ./ee/server/services - npm i - npm run build - - echo "Building Docker image for service: ${{ matrix.service }}:${IMAGE_TAG}" - - docker build --build-arg SERVICE=${{ matrix.service }} -t rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} . - - docker push rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} - - if [[ $GITHUB_REF == refs/tags/* ]]; then - if echo "$IMAGE_TAG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' ; then - RELEASE="latest" - elif echo "$IMAGE_TAG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' ; then - RELEASE="release-candidate" - fi - - docker tag rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} rocketchat/${{ matrix.service }}-service:${RELEASE} - docker push rocketchat/${{ matrix.service }}-service:${RELEASE} - fi diff --git a/README.md b/README.md index 0dbdc8df434c5..f9518dde9157d 100644 --- a/README.md +++ b/README.md @@ -90,3 +90,306 @@ We're hiring developers, support people, and product managers all the time. Plea Any other questions, mail us at [info@rocket.chat](info@rocket.chat). We’d love to meet you! +[![Layershift Hosting](https://github.com/Sing-Li/bbug/raw/master/images/layershift.png)](http://jps.layershift.com/rocketchat/deploy.html) + +Painless SSL. Automatically scale your server cluster based on usage demand. + +## Yunohost.org +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) + +## IndieHosters +Get your Rocket.Chat instance hosted in an "as a Service" style. You register and we manage it for you! (updates, backup...). + + +Rocket.Chat on IndieHosters + +## Cloudron.io + +Install Rocket.Chat on [Cloudron](https://cloudron.io) Smartserver: + +[![Install](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=chat.rocket.cloudronapp) + +## Helm Kubernetes +Deploy on Kubernetes using the official [helm chart](https://github.com/helm/charts/tree/master/stable/rocketchat). + +## Scalingo +Deploy your own Rocket.Chat server instantly on [Scalingo](https://scalingo.com). + +[![Deploy on Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy?source=https://github.com/RocketChat/Rocket.Chat#master) + + +## Sloppy.io +Host your docker container at [sloppy.io](http://sloppy.io). Get an account and use the [quickstarter](https://github.com/sloppyio/quickstarters/tree/master/rocketchat). + + +## Docker +[Deploy with docker compose](https://rocket.chat/docs/installation/docker-containers/docker-compose/) + +[![Rocket.Chat logo](https://d207aa93qlcgug.cloudfront.net/1.95.5.qa/img/nav/docker-logo-loggedout.png)](https://hub.docker.com/r/rocketchat/rocket.chat/) + +OR Use the automated build image of our [most recent release](https://hub.docker.com/r/lltcggie/rocket.chat/) + +``` +docker pull lltcggie/rocket.chat:latest +``` + +OR select a specific release ([details of releases available](https://github.com/RocketChat/Rocket.Chat/releases)): +``` +docker pull lltcggie/rocket.chat:X.X.X +``` + +## Ansible +Automated production-grade deployment in minutes, for RHEL / CentOS 7 or Ubuntu 14.04 LTS / 15.04. + +[![Ansible deployment](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/ansible.png)](https://rocket.chat/docs/installation/automation-tools/ansible/) + +## Raspberry Pi 4 +Run Rocket.Chat on this world famous $35 quad-core server. + +[![Raspberry Pi 4](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/pitiny.png)](https://github.com/RocketChat/Rocket.Chat.RaspberryPi) + +## Koozali SME + +Add Rocket.Chat to this world famous time tested small enterprise server today. + +[![Koozali SME](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/koozali.png)](https://wiki.contribs.org/Rocket_Chat) + +## Ubuntu VPS +Follow these [deployment instructions](https://rocket.chat/docs/installation/manual-installation/ubuntu/). + +## D2C.io +Deploy Rocket.Chat stack to your server with [D2C](https://d2c.io/). Scale with a single click, check live logs and metrics: + +[![Deploy](https://github.com/mastappl/images/blob/master/deployTo.png)](https://panel.d2c.io/?import=https://github.com/d2cio/rocketchat-stack/archive/master.zip/) + +## Syncloud.org +Run Rocket.Chat on your easy to use personal device. + +[![Deploy](https://syncloud.org/images/logo_min.svg)](https://syncloud.org) + +# About Rocket.Chat + +Rocket.Chat is a Web Chat Server, developed in JavaScript, using the [Meteor](https://www.meteor.com/install) full stack framework. + +It is a great solution for communities and companies wanting to privately host their own chat service or for developers looking forward to build and evolve their own chat platforms. + +## In the News + +##### [Wired](http://www.wired.com/2016/03/open-source-devs-racing-build-better-versions-slack/) +> Open Sourcers Race to Build Better Versions of Slack + +##### [Hacker News](https://news.ycombinator.com/item?id=9624737) +> Yes, we made it to the #1 + +##### [Product Hunt](https://www.producthunt.com/tech/rocket-chat) +> Your own open source Slack-like chat + +##### [JavaScript Weekly](http://javascriptweekly.com/issues/234) +> An open source Web based, channel based chat system (a la Slack) built using Meteor, the full stack JavaScript development platform. + +##### [Open Source China](http://www.oschina.net/p/rocket-chat) +> Rocket.Chat 是特性最丰富的 Slack 开源替代品之一。 主要功能:群组聊天,直接通信,私聊群,桌面通知,媒体嵌入,链接预览,文件上传,语音/视频 聊天,截图等等。 + +##### [wwwhatsnew.com](http://wwwhatsnew.com/2015/05/30/rocket-chat-para-los-programadores-que-quieran-ofrecer-un-chat-en-su-web/) +> Para los programadores que quieran ofrecer un chat en su web + +##### [clasesdeperiodismo.com](http://www.clasesdeperiodismo.com/2015/05/30/un-chat-de-codigo-abierto-que-puedes-anadir-a-la-web/) +> Un chat de código abierto que puedes añadir a la web + +##### [snowulf.com](https://snowulf.com/2015/09/25/why-slack-when-you-can-rocket-chat/) +> Why Slack when you can Rocket.chat? + +##### [liminality.xyz](http://liminality.xyz/self-hosting/) +> Self-hosted alternatives to popular cloud services + + +## Features + +- BYOS (Bring Your Own Server) +- Multiple Rooms +- Direct Messages +- Private Groups +- Public Channels +- Desktop Notifications +- Mentions +- Avatars +- Markdown +- Emojis +- Custom Emojis +- Reactions +- One touch Geolocation +- TeX Math Rendering - inline math typesetting +- Media Embeds +- Link Previews +- Sent Message Edit and Deletion +- Transcripts / History +- File Upload / Sharing +- Scalable file sharing - S3 uploads with CDN downloads +- Full text search +- Global search (from all channels/rooms at once) +- Live chat / Messaging call center +- LDAP Authentication +- CAS 1.0, 2.0 support for educational institutions and hosting providers worldwide +- Support for Okta SSO through SAML v2 +- I18n - Supports 22 Languages +- Hubot Friendly +- (Beta) Face to Face Video Conferencing (aka WebRTC ) +- (Beta) Multi-users Video Group Chat +- (Beta) Jitsi integration +- Audio calls +- Multi-users Audio Conference +- Screen sharing +- Drupal 7.x and 8.x Plug-in (both stable and development flavours) ([download](https://www.drupal.org/project/rocket_chat) and [source code](https://git.drupal.org/project/rocket_chat.git) ) +- XMPP bridge ([try it](https://open.rocket.chat/channel/general)) +- REST APIs +- Remote Video Monitoring +- Native real-time APIs for Microsoft C#, Visual Basic, F# and other .NET supported languages ([Get it!](https://www.nuget.org/packages/Rocket.Chat.Net/0.0.12-pre)) +- API access from [Perl](https://metacpan.org/pod/Net::RocketChat) and [Java](https://github.com/baloise/rocket-chat-rest-client) (community contributions) +- Chat-ops powered by Hubot: scalable horizontal app integration (early access) +- Massively scalable hosting and provisioning (beta testing now) +- Native Cross-Platform Desktop Application [Windows, macOS, or Linux](https://rocket.chat/) +- Mobile app for iPhone, iPad, and iPod touch [Download on App Store](https://geo.itunes.apple.com/us/app/rocket-chat/id1148741252?mt=8) +- Mobile app for Android phone, tablet, and TV stick [Available now on Google Play](https://play.google.com/store/apps/details?id=chat.rocket.android) +- Available on [Cloudron Store](https://cloudron.io/appstore.html#chat.rocket.cloudronapp) + +## Roadmap + +To see an up to date view of what we have planned, view our [milestones](https://github.com/RocketChat/Rocket.Chat/milestones). + + +## How it all started + +Read about [how it all started](https://www.synopsys.com/blogs/software-security/rocket-chat-privately-hosted-chat-services/). + +## Awards +[![InfoWorld Bossie Awards 2016 - Best Open Source Applications](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/bossie.png)](http://www.infoworld.com/article/3122000/open-source-tools/bossie-awards-2016-the-best-open-source-applications.html#slide4) + +[![Black Duck Open Source Rookie of the Year for 2015](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/blackducksm.png)](https://info.blackducksoftware.com/OpenSourceRookies2015) + +[![Softpedia 100% Free and Clean Award for 2017](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/softpedia.gif)](http://www.softpedia.com/get/Internet/Chat/Other-Chat-Tools/Rocket-Chat.shtml#status) + +## Issues + +[GitHub Issues](https://github.com/RocketChat/Rocket.Chat/issues) are used to track bugs and tasks on the roadmap. + +## Feature Requests + +[RocketChat/feature-requests](https://github.com/RocketChat/feature-requests) is used to track Rocket.Chat feature requests and discussions. Click [here](https://github.com/RocketChat/feature-requests/issues/new?template=feature_request.md) to open a new feature request. +[Feature Request Forums](https://forums.rocket.chat/c/feature-requests) stores the historical archives of old feature requests (up to 2018). + +### Stack Overflow + +Please use the [Stack Overflow TAG](http://stackoverflow.com/questions/tagged/rocket.chat) + +## Integrations + +#### Hubot + +The docker image is ready. +Everyone can start hacking the adapter code or launch his/her own bot within a few minutes now. +Please head over to the [Hubot Integration Project](https://github.com/RocketChat/hubot-rocketchat) for more information. + + +#### Chat-ops integrations powered by Hubot + +Integrate your application with fly-in panels today! Early access is available for developers. + +![Sample integration of a Drones Fleet Management System](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/dronechatops.png) + +#### Many, many, many more to come! + +We are developing the APIs based on the competition, so stay tuned and you will see a lot happening here. + +## Documentation + +Check out [Rocket.Chat documentation](https://rocket.chat/docs/). + +## License + +Note that Rocket.Chat is distributed under the [MIT License](http://opensource.org/licenses/MIT). + +# Development + +## Quick start for code developers +Prerequisites: + +* [Git](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Meteor](https://www.meteor.com/install) + +> Meteor automatically installs a hidden [NodeJS v12](https://nodejs.org/download/release/v12.18.4/) and [MongoDB v4.2](https://docs.mongodb.com/manual/introduction/) to be used when you run your app in development mode using the `meteor` command. + +Now just clone and start the app: + +```sh +git clone https://github.com/RocketChat/Rocket.Chat.git +cd Rocket.Chat +meteor npm install +meteor npm start +``` +For more detailed step-by-step, see our [quick start for developers](https://docs.rocket.chat/guides/developer/quick-start) docs. + +To debug the server part, use [meteor debugging](https://docs.meteor.com/commandline.html#meteordebug). You should use Chrome for best debugging experience: + +```sh +meteor debug +``` +You'll find a nodejs icon in the developer console. + +If you are not a developer and just want to run the server - see [deployment methods](https://rocket.chat/docs/installation/paas-deployments/). + +## Branching Model + +See [Branches and Releases](https://rocket.chat/docs/developer-guides/branches-and-releases/). + +It is based on [Gitflow Workflow](http://nvie.com/posts/a-successful-git-branching-model/), reference section below is derived from Vincent Driessen at nvie. + +See also this [Git Workflows Comparison](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) for more details. + +## Translations +We are experimenting with [Lingohub](https://translate.lingohub.com/rocketchat/dashboard). +If you want to help, send an email to support at rocket.chat to be invited to the translation project. + +## How to Contribute + +Already a JavaScript developer? Familiar with Meteor? [Pick an issue](https://github.com/RocketChat/Rocket.Chat/labels/contrib%3A%20easy), push a PR and instantly become a member of Rocket.Chat's international contributors' community. For more information, check out our [Contributing Guide](.github/CONTRIBUTING.md) and our [Official Documentation for Contributors](https://rocket.chat/docs/contributing/). + +A lot of work has already gone into Rocket.Chat, but we have much bigger plans for it! + +### Contributor License Agreement + +Please review and sign our CLA at https://cla-assistant.io/RocketChat/Rocket.Chat + +# Credits + +Thanks to our core team +[Aaron Ogle](https://github.com/geekgonecrazy), +[Bradley Hilton](https://github.com/Graywolf336), +[Diego Sampaio](https://github.com/sampaiodiego), +[Gabriel Engel](https://github.com/engelgabriel), +[Marcelo Schmidt](https://github.com/marceloschmidt), +[Rodrigo Nascimento](https://github.com/rodrigok), +[Sing Li](https://github.com/Sing-Li), +and hundreds of awesome [contributors](https://github.com/RocketChat/Rocket.Chat/graphs/contributors). + +![JoyPixels](https://i.imgur.com/OrhYvLe.png) + +Emoji provided graciously by [JoyPixels](https://www.joypixels.com/) + +![BrowserStack](https://cloud.githubusercontent.com/assets/1986378/24772879/57d57b88-1ae9-11e7-98b4-4af824b47933.png) + +Testing with [BrowserStack](https://www.browserstack.com) + +![LingoHub](https://user-images.githubusercontent.com/20868078/69438584-0dd0e880-0d24-11ea-9127-de61dcfa6cd6.png) + +Translations done with [LingoHub](https://www.lingohub.com) + +# Donate + +Rocket.Chat will be free forever, but you can help us speed up the development! + +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9MT88JJ9X4A6U&source=url) + + +[BountySource](https://www.bountysource.com/teams/rocketchat) diff --git a/app/file-upload/server/lib/FileUpload.js b/app/file-upload/server/lib/FileUpload.js index 7a526e30ce8c9..823d767aa5a21 100644 --- a/app/file-upload/server/lib/FileUpload.js +++ b/app/file-upload/server/lib/FileUpload.js @@ -267,7 +267,7 @@ export const FileUpload = { }, uploadsOnValidate(file) { - if (!/^image\/((x-windows-)?bmp|p?jpeg|png|gif)$/.test(file.type)) { + if (!/^image\/(p?jpeg)$/.test(file.type)) { return; } @@ -375,7 +375,7 @@ export const FileUpload = { // This file type can be pretty much anything, so it's better if we don't mess with the file extension if (file.type !== 'application/octet-stream') { const ext = mime.extension(file.type); - if (ext && new RegExp(`\\.${ ext }$`, 'i').test(file.name) === false) { + if (ext && new RegExp(`\\.${ ext }$`, 'i').test(file.name) === false && ext !== 'bin') { file.name = `${ file.name }.${ ext }`; } } diff --git a/app/lib/server/startup/settings.js b/app/lib/server/startup/settings.js index 4e1153dd30768..d43bdbb8a3731 100644 --- a/app/lib/server/startup/settings.js +++ b/app/lib/server/startup/settings.js @@ -1196,6 +1196,11 @@ settings.addGroup('Message', function() { this.add('API_EmbedSafePorts', '80, 443', { type: 'string', }); + this.add('API_EmbedParameter', '', { + type: 'string', + multiline: true, + i18nDescription: 'API_EmbedParameter_Description', + }); this.add('Message_TimeFormat', 'LT', { type: 'string', public: true, diff --git a/app/notification-queue/server/NotificationQueue.ts b/app/notification-queue/server/NotificationQueue.ts index a1533e958a165..363f46ae38047 100644 --- a/app/notification-queue/server/NotificationQueue.ts +++ b/app/notification-queue/server/NotificationQueue.ts @@ -9,7 +9,7 @@ import { IUser } from '../../../definition/IUser'; const { NOTIFICATIONS_WORKER_TIMEOUT = 2000, NOTIFICATIONS_BATCH_SIZE = 100, - NOTIFICATIONS_SCHEDULE_DELAY_ONLINE = 120, + NOTIFICATIONS_SCHEDULE_DELAY_ONLINE = 15, NOTIFICATIONS_SCHEDULE_DELAY_AWAY = 0, NOTIFICATIONS_SCHEDULE_DELAY_OFFLINE = 0, } = process.env; diff --git a/app/oembed/server/jumpToMessage.js b/app/oembed/server/jumpToMessage.js index 961fbb656aaf6..7db82a7c28540 100644 --- a/app/oembed/server/jumpToMessage.js +++ b/app/oembed/server/jumpToMessage.js @@ -4,11 +4,10 @@ import QueryString from 'querystring'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { Messages, Rooms, Users } from '../../models/server'; +import { Messages } from '../../models/server'; import { settings } from '../../settings/server'; import { callbacks } from '../../callbacks/server'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; -import { canAccessRoom } from '../../authorization/server/functions/canAccessRoom'; const recursiveRemove = (message, deep = 1) => { if (message) { @@ -27,8 +26,6 @@ callbacks.add('beforeSaveMessage', (msg) => { return msg; } - const currentUser = Users.findOneById(msg.u._id); - msg.urls.forEach((item) => { // if the URL is not internal, skip if (!item.url.includes(Meteor.absoluteUrl())) { @@ -53,14 +50,6 @@ callbacks.add('beforeSaveMessage', (msg) => { return; } - // validates if user can see the message - // user has to belong to the room the message was first wrote in - const room = Rooms.findOneById(jumpToMessage.rid); - const canAccessRoomForUser = canAccessRoom(room, currentUser); - if (!canAccessRoomForUser) { - return; - } - msg.attachments = msg.attachments || []; const index = msg.attachments.findIndex((a) => a.message_link === item.url); if (index > -1) { diff --git a/app/oembed/server/server.js b/app/oembed/server/server.js index 34aa1a8eee1a6..22137b873e5d6 100644 --- a/app/oembed/server/server.js +++ b/app/oembed/server/server.js @@ -60,6 +60,58 @@ const toUtf8 = function(contentType, body) { return iconv.decode(body, getCharset(contentType, body)); }; +const addQueryTo = (href, query) => { + const urlObject = URL.parse(href, true); + + urlObject.search = undefined; + Object.assign(urlObject.query, query); + + return URL.format(urlObject); +}; + +const getSendHeaders = function(url) { + const addSendQuerys = { + }; + const sendHeaders = { + 'User-Agent': settings.get('API_Embed_UserAgent'), + }; + + const embedParameter = url && settings.get('API_EmbedParameter'); + try { + if (/\S/.test(embedParameter)) { + // embedParameter is not empty or whitespace only + const embedParameterJSON = JSON.parse(embedParameter); + for (let i = 0; i < embedParameterJSON.length; i++) { + const headerElm = embedParameterJSON[i]; + if (!url.indexOf(headerElm.URL)) { + if ('Query' in headerElm) { + for (const key in headerElm.Query) { + if (headerElm.Query.hasOwnProperty(key)) { + addSendQuerys[key] = headerElm.Query[key]; + } + } + } + if ('Header' in headerElm) { + for (const key in headerElm.Header) { + if (headerElm.Header.hasOwnProperty(key)) { + sendHeaders[key] = headerElm.Header[key]; + } + } + } + } + } + } + } catch (e) { + // parsing JSON faild + console.log('Error while parsing JSON value of "API_EmbedParameter": ', e); + } + let retUrl = url; + if (Object.keys(addSendQuerys).length > 0) { + retUrl = addQueryTo(url, addSendQuerys); + } + return [retUrl, sendHeaders]; +}; + const getUrlContent = Meteor.wrapAsync(function(urlObj, redirectCount = 5, callback) { if (_.isString(urlObj)) { urlObj = URL.parse(urlObj); @@ -94,16 +146,13 @@ const getUrlContent = Meteor.wrapAsync(function(urlObj, redirectCount = 5, callb if (data.attachments != null) { return callback(null, data); } - const url = URL.format(data.urlObj); + const [url, sendHeaders] = getSendHeaders(URL.format(data.urlObj)); const opts = { url, strictSSL: !settings.get('Allow_Invalid_SelfSigned_Certs'), gzip: true, maxRedirects: redirectCount, - headers: { - 'User-Agent': settings.get('API_Embed_UserAgent'), - 'Accept-Language': settings.get('Language') || 'en', - }, + headers: sendHeaders, }; let headers = null; let statusCode = null; diff --git a/client/components/MarkdownText.tsx b/client/components/MarkdownText.tsx index 7f90ab538be4f..2ee9be70e4a83 100644 --- a/client/components/MarkdownText.tsx +++ b/client/components/MarkdownText.tsx @@ -3,6 +3,8 @@ import dompurify from 'dompurify'; import marked from 'marked'; import React, { FC, useMemo } from 'react'; +import { escapeHTML } from '@rocket.chat/string-helpers'; + type MarkdownTextParams = { content: string; variant: 'inline' | 'inlineWithoutBreaks' | 'document'; @@ -28,18 +30,30 @@ const listItemMarked = (text: string): string => { const cleanText = text.replace(/|<\/p>/gi, ''); return `
  • ${cleanText}
  • `; }; +const codeMarked = (code: string, lang: string | undefined, escaped: boolean): string => + (!lang + ? `${ escaped ? code : escapeHTML(code) }` + : `${ escaped ? code : escapeHTML(code) }`); +const codespanMarked = (text: string): string => + `${ text }`; documentRenderer.link = linkMarked; documentRenderer.listitem = listItemMarked; +documentRenderer.code = codeMarked; +documentRenderer.codespan = codespanMarked; inlineRenderer.link = linkMarked; inlineRenderer.paragraph = paragraphMarked; inlineRenderer.listitem = listItemMarked; +inlineRenderer.code = codeMarked; +inlineRenderer.codespan = codespanMarked; inlineWithoutBreaks.link = linkMarked; inlineWithoutBreaks.paragraph = paragraphMarked; inlineWithoutBreaks.br = brMarked; inlineWithoutBreaks.listitem = listItemMarked; +inlineWithoutBreaks.code = codeMarked; +inlineWithoutBreaks.codespan = codespanMarked; const defaultOptions = { gfm: true, @@ -89,7 +103,7 @@ const MarkdownText: FC> = ({ const html = content && typeof content === 'string' && - marked(new Option(content).innerHTML, markedOptions); + `
    ${ marked(new Option(content).innerHTML, markedOptions).replace('
    ', '').replace('
    ', '') }
    `; return preserveHtml ? html : html && sanitizer(html, { ADD_ATTR: ['target'] }); }, [content, preserveHtml, sanitizer, markedOptions]); diff --git a/client/components/Message/Attachments/Files/GenericFileAttachment.tsx b/client/components/Message/Attachments/Files/GenericFileAttachment.tsx index af0883d026960..233a8f8018c2c 100644 --- a/client/components/Message/Attachments/Files/GenericFileAttachment.tsx +++ b/client/components/Message/Attachments/Files/GenericFileAttachment.tsx @@ -1,8 +1,9 @@ +import { escapeHTML } from '@rocket.chat/string-helpers'; import React, { FC } from 'react'; import { FileProp } from '../../../../../definition/IMessage/MessageAttachment/Files/FileProp'; import { MessageAttachmentBase } from '../../../../../definition/IMessage/MessageAttachment/MessageAttachmentBase'; -import MarkdownText from '../../../MarkdownText'; +import RawText from '../../../RawText'; import Attachment from '../Attachment'; import { useMediaUrl } from '../context/AttachmentContext'; @@ -26,24 +27,21 @@ export const GenericFileAttachment: FC = ({ const getURL = useMediaUrl(); return ( - {description && } - - {hasDownload && link ? ( - - ) : ( - {title} - )} - {size && } - {/* {collapse} */} - {hasDownload && link && } - - {/* { !collapsed && - - {hasDownload && link && } - {name} - {size && }{format && size && ' | '}{format} - - } */} + + {description && {escapeHTML(description)}} + + + {hasDownload && link ? ( + + ) : ( + {title} + )} + {size && } + {/* {collapse} */} + {hasDownload && link && } + + + ); }; diff --git a/client/sidebar/hooks/useRoomList.ts b/client/sidebar/hooks/useRoomList.ts index c6321bcde0238..59d1190c54538 100644 --- a/client/sidebar/hooks/useRoomList.ts +++ b/client/sidebar/hooks/useRoomList.ts @@ -38,7 +38,8 @@ export const useRoomList = (): Array => { const team = new Set(); const omnichannel = new Set(); const unread = new Set(); - const channels = new Set(); + const _private = new Set(); + const _public = new Set(); const direct = new Set(); const discussion = new Set(); const conversation = new Set(); @@ -61,8 +62,12 @@ export const useRoomList = (): Array => { return discussion.add(room); } - if (room.t === 'c' || room.t === 'p') { - channels.add(room); + if (room.t === 'c') { + _public.add(room); + } + + if (room.t === 'p') { + _private.add(room); } if (room.t === 'l' && room.onHold) { @@ -95,8 +100,9 @@ export const useRoomList = (): Array => { isDiscussionEnabled && discussion.size && groups.set('Discussions', discussion); - sidebarGroupByType && channels.size && groups.set('Channels', channels); - sidebarGroupByType && direct.size && groups.set('Direct_Messages', direct); + sidebarGroupByType && _private.size && groups.set('Private', _private); + sidebarGroupByType && _public.size && groups.set('Public', _public); + sidebarGroupByType && direct.size && groups.set('Direct', direct); !sidebarGroupByType && groups.set('Conversations', conversation); return [...groups.entries()].flatMap(([key, group]) => [key, ...group]); }); diff --git a/client/views/room/contextualBar/Threads/Row.js b/client/views/room/contextualBar/Threads/Row.js index 0e9fcde392930..5a7702be1a7e7 100644 --- a/client/views/room/contextualBar/Threads/Row.js +++ b/client/views/room/contextualBar/Threads/Row.js @@ -34,6 +34,7 @@ const Row = memo(function Row({ const formatDate = useTimeAgo(); const msg = normalizeThreadMessage(thread); + const reactions = thread.reactions ? thread.reactions : null; const { name = thread.u.username } = thread.u; @@ -52,6 +53,7 @@ const Row = memo(function Row({ following={thread.replies && thread.replies.includes(userId)} data-id={thread._id} msg={msg} + reactions={reactions} t={t} formatDate={formatDate} handleFollowButton={(e) => handleFollowButton(e, thread._id)} diff --git a/client/views/room/contextualBar/Threads/components/Message.js b/client/views/room/contextualBar/Threads/components/Message.js index a0b05d29c8b86..833992a409a77 100644 --- a/client/views/room/contextualBar/Threads/components/Message.js +++ b/client/views/room/contextualBar/Threads/components/Message.js @@ -1,12 +1,14 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button, Icon, Box } from '@rocket.chat/fuselage'; import React, { memo } from 'react'; +import { renderEmoji } from '../../../../../../app/emoji/client/index'; import Metrics from '../../../../../components/Message/Metrics'; import * as NotificationStatus from '../../../../../components/Message/NotificationStatus'; import { followStyle, anchor } from '../../../../../components/Message/helpers/followSyle'; import RawText from '../../../../../components/RawText'; import UserAvatar from '../../../../../components/avatar/UserAvatar'; import * as MessageTemplate from '../../../components/MessageTemplate'; +import { t } from '../../../../../../app/utils'; function isIterable(obj) { // checks for null and undefined @@ -22,6 +24,7 @@ export default memo(function Message({ following, username, name = username, + reactions, ts, replies, participants, @@ -38,6 +41,58 @@ export default memo(function Message({ const button = !following ? 'bell-off' : 'bell'; const actionLabel = t(!following ? 'Not_Following' : 'Following'); + const emoji = []; + const list = []; + + if (reactions != null) { + const myUsername = Meteor.user().username; + const titleRegex = /\stitle="[^"]+"/; + + Object.keys(reactions).forEach((key) => { + const reaction = reactions[key] + + // app\ui-message\client\message.js の Template.message.helpers.reactions() から持ってきた文字列生成処理 + const myDisplayName = reaction.names ? myName : `@${ myUsername }`; + const displayNames = reaction.names || reaction.usernames.map((username) => `@${ username }`); + const selectedDisplayNames = displayNames.slice(0, 15).filter((displayName) => displayName !== myDisplayName); + + if (displayNames.some((displayName) => displayName === myDisplayName)) { + selectedDisplayNames.unshift(t('You')); + } + + let usernames; + + if (displayNames.length > 15) { + usernames = `${ selectedDisplayNames.join(', ') } ${ t('And_more', { length: displayNames.length - 15 }).toLowerCase() }`; + } else if (displayNames.length > 1) { + usernames = `${ selectedDisplayNames.slice(0, -1).join(', ') } ${ t('and') } ${ selectedDisplayNames[selectedDisplayNames.length - 1] }`; + } else { + usernames = selectedDisplayNames[0]; + } + + const title = ` ${ usernames } ${ t('Reacted_with').toLowerCase() } ${ key }` + emoji.push( + +
    + {reactions[key].usernames.length} + , + ); + }); + list.push( + + + {emoji} + + , + ); + } + return ( {msg} + {list} diff --git a/docker-compose.yml b/docker-compose.yml index 037deec030990..9e06529ef3c3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '2' services: rocketchat: - image: registry.rocket.chat/rocketchat/rocket.chat:latest + image: lltcggie/rocket.chat:latest command: > bash -c "for i in `seq 1 30`; do diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 64475d5ecb0fd..8a3c927ed5000 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -360,6 +360,8 @@ "API_EmbedIgnoredHosts_Description": "Comma-separated list of hosts or CIDR addresses, eg. localhost, 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16", "API_EmbedSafePorts": "Safe Ports", "API_EmbedSafePorts_Description": "Comma-separated list of ports allowed for previewing.", + "API_EmbedParameter": "Using headers", + "API_EmbedParameter_Description": "Specify the headers to be used for preview in JSON format. eg.
    [ { \"URL\" : \"https://www.google.co.jp/\", \"Query\" : { \"q\": \"s\" }, \"Header\" : { \"Cookie\": \"name1=val1; name2=val2\", \"test\": \"val\" } } ]", "API_Enable_CORS": "Enable CORS", "API_Enable_Direct_Message_History_EndPoint": "Enable Direct Message History Endpoint", "API_Enable_Direct_Message_History_EndPoint_Description": "This enables the `/api/v1/im.history.others` which allows the viewing of direct messages sent by other users that the caller is not part of.", diff --git a/packages/rocketchat-i18n/i18n/ja.i18n.json b/packages/rocketchat-i18n/i18n/ja.i18n.json index 31920e1eb5c25..565096ec1c42b 100644 --- a/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -367,6 +367,8 @@ "API_Enable_Rate_Limiter_Limit_Time_Default_Description": "REST APIの各エンドポイントの呼び出し数を制限するためのデフォルトタイムアウト(ミリ秒)", "API_Enable_Shields": "シールドを有効にする", "API_Enable_Shields_Description": "`/api/v1/shield.svg` でシールドを有効にする", + "API_EmbedParameter": "使用するパラメータ", + "API_EmbedParameter_Description": "プレビューするために使用するパラメータをJSON形式で指定します。例えば
    [ { \"URL\" : \"https://www.google.co.jp/\", \"Query\" : { \"q\": \"s\" }, \"Header\" : { \"Cookie\": \"name1=val1; name2=val2\", \"test\": \"val\" } } ]
    URLは前方一致。QueryはURLクエリパラメータ。Headerは追加するヘッダー。該当するURLが複数ある場合は、各要素は新しい要素で上書きされます", "API_GitHub_Enterprise_URL": "サーバー URL", "API_GitHub_Enterprise_URL_Description": "例: http://domain.com (末尾のスラッシュを除く)", "API_Gitlab_URL": "GitLab URL", diff --git a/packages/rocketchat-livechat/plugin/build.bat b/packages/rocketchat-livechat/plugin/build.bat index bf51ac5c687bb..a11240e7f7aaf 100644 --- a/packages/rocketchat-livechat/plugin/build.bat +++ b/packages/rocketchat-livechat/plugin/build.bat @@ -1,21 +1,23 @@ @echo off -SET NODE_ENV="production" -cd packages/rocketchat-livechat/.app -call meteor npm install --production -call meteor build --headless --directory .meteor/build/ +SET NODE_ENV=production +SET LIVECHAT_DIR=public\livechat +SET LIVECHAT_ASSETS_DIR=private\livechat -SET LIVECHAT_DIR="../../../public/livechat" -SET BUILD_DIR=".meteor/build/bundle/programs/web.browser" +SET ROOT=%CD% -mkdir ..\public -del /q /s ..\public\* - -rmdir /q /s %LIVECHAT_DIR% +del /q /s %LIVECHAT_DIR% mkdir %LIVECHAT_DIR% -xcopy /y %BUILD_DIR%\*.css %LIVECHAT_DIR%\livechat.css* -xcopy /y %BUILD_DIR%\*.js %LIVECHAT_DIR%\livechat.js* -xcopy /y %BUILD_DIR%\head.html ..\public\head.html* +del /q /s %LIVECHAT_ASSETS_DIR% +mkdir %LIVECHAT_ASSETS_DIR% + +echo Installing Livechat %LATEST_LIVECHAT_VERSION%... +cd %LIVECHAT_DIR% + +copy /y %ROOT%\node_modules\@rocket.chat\livechat\build\* . + +call meteor node -e "fs.writeFileSync(^"index.html^", fs.readFileSync(^"index.html^").toString().replace(^"