diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db905f2f947a..b7ce1a785259 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,46 @@ - + + - -Closes #ISSUE_NUMBER +## Proposed changes + - +## Issue(s) + + +## How to test or reproduce + + +## Screenshots + +## Types of changes + + + +- [ ] 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 + + +- [ ] 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 + + + + +## Further comments + - diff --git a/.scripts/houstonMetadata.js b/.houston/metadata.js similarity index 100% rename from .scripts/houstonMetadata.js rename to .houston/metadata.js diff --git a/.houston/templates/prs.hbs b/.houston/templates/prs.hbs new file mode 100644 index 000000000000..34a9666df2ba --- /dev/null +++ b/.houston/templates/prs.hbs @@ -0,0 +1,2 @@ +{{> versions}} +{{> _prs}} diff --git a/.houston/templates/versions.hbs b/.houston/templates/versions.hbs new file mode 100644 index 000000000000..8ee2a4f98551 --- /dev/null +++ b/.houston/templates/versions.hbs @@ -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}} diff --git a/app/apps/client/admin/helpers.js b/app/apps/client/admin/helpers.js index f78b4c661599..a1b0b2cdf9c5 100644 --- a/app/apps/client/admin/helpers.js +++ b/app/apps/client/admin/helpers.js @@ -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('_'); diff --git a/app/apps/server/bridges/users.js b/app/apps/server/bridges/users.js index 5aac0567bed6..cbcdab1795d8 100644 --- a/app/apps/server/bridges/users.js +++ b/app/apps/server/bridges/users.js @@ -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); } diff --git a/app/cors/server/cors.js b/app/cors/server/cors.js index c5853db94059..9c6d8eb31803 100644 --- a/app/cors/server/cors.js +++ b/app/cors/server/cors.js @@ -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(); }); diff --git a/app/emoji/client/emojiPicker.html b/app/emoji/client/emojiPicker.html index 0e3a5c7546e1..498f4ec1e487 100644 --- a/app/emoji/client/emojiPicker.html +++ b/app/emoji/client/emojiPicker.html @@ -44,6 +44,9 @@

{{_ ca {{/each}} {{/if}} - + diff --git a/app/emoji/client/emojiPicker.js b/app/emoji/client/emojiPicker.js index 410bbfa5682c..86360cc47575 100644 --- a/app/emoji/client/emojiPicker.js +++ b/app/emoji/client/emojiPicker.js @@ -1,6 +1,7 @@ import _ from 'underscore'; import { ReactiveVar } from 'meteor/reactive-var'; import { ReactiveDict } from 'meteor/reactive-dict'; +import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; import { t } from '../../utils/client'; @@ -142,6 +143,12 @@ Template.emojiPicker.events({ event.stopPropagation(); event.preventDefault(); }, + 'click .add-custom'(event) { + event.stopPropagation(); + event.preventDefault(); + FlowRouter.go('/admin/emoji-custom'); + EmojiPicker.close(); + }, 'click .category-link'(event) { event.stopPropagation(); event.preventDefault(); diff --git a/app/lib/server/startup/settings.js b/app/lib/server/startup/settings.js index 618e917331c9..63241e0b0bed 100644 --- a/app/lib/server/startup/settings.js +++ b/app/lib/server/startup/settings.js @@ -874,6 +874,8 @@ settings.addGroup('General', function() { type: 'boolean', public: true, }); + + // Deprecated setting this.add('Support_Cordova_App', false, { type: 'boolean', i18nDescription: 'Support_Cordova_App_Description', diff --git a/app/mailer/server/api.js b/app/mailer/server/api.js index 45c7f10030f3..38bb11418273 100644 --- a/app/mailer/server/api.js +++ b/app/mailer/server/api.js @@ -23,7 +23,7 @@ settings.get('Language', (key, value) => { lng = value || 'en'; }); -export const replacekey = (str, key, value = '') => str.replace(new RegExp(`(\\[${ key }\\]|__${ key }__)`, 'igm'), value); +export const replacekey = (str, key, value = '') => str.replace(new RegExp(`(\\[${ key }\\]|__${ key }__)`, 'igm'), s.escapeHTML(value)); export const translate = (str) => str.replace(/\{ ?([^\} ]+)(( ([^\}]+))+)? ?\}/gmi, (match, key) => TAPi18n.__(key, { lng })); export const replace = function replace(str, data = {}) { if (!str) { diff --git a/app/theme/client/imports/components/emojiPicker.css b/app/theme/client/imports/components/emojiPicker.css index bd2d72a0b9c1..f8601a798a4c 100644 --- a/app/theme/client/imports/components/emojiPicker.css +++ b/app/theme/client/imports/components/emojiPicker.css @@ -122,6 +122,12 @@ font-size: 12px; font-weight: 500; + + & .add-custom { + display: inline-block; + + width: 100%; + } } .emoji-top { diff --git a/app/ui/client/views/app/components/Directory/UserTab.js b/app/ui/client/views/app/components/Directory/UserTab.js index 25145c0a7c70..58a997e1784d 100644 --- a/app/ui/client/views/app/components/Directory/UserTab.js +++ b/app/ui/client/views/app/components/Directory/UserTab.js @@ -23,7 +23,7 @@ const FilterByText = ({ setFilter, ...props }) => { }, [text]); return - } onChange={handleChange} value={text} /> + } onChange={handleChange} value={text} /> ; }; diff --git a/package.json b/package.json index bb91d7d6aa59..c5783cac5b30 100644 --- a/package.json +++ b/package.json @@ -253,9 +253,10 @@ } }, "houston": { - "metadata": ".scripts/houstonMetadata.js", + "minTag": "0.55.0-rc.0", "updateFiles": [ "package.json", + "package-lock.json", ".circleci/snap.sh", ".circleci/update-releases.sh", ".docker/Dockerfile", diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index a0f25130867f..ffc3965f6d9c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -233,6 +233,7 @@ "MAU_value": "MAU __value__", "Activity": "Activity", "Add": "Add", + "Add_custom_emoji": "Add custom emoji", "add-oauth-service": "Add Oauth Service", "add-oauth-service_description": "Permission to add a new Oauth service", "add-user": "Add User", @@ -410,9 +411,13 @@ "Apps_Marketplace_Login_Required_Title": "Marketplace Login Required", "Apps_Marketplace_Login_Required_Description": "Purchasing apps from the Rocket.Chat Marketplace requires registering your workspace and logging in.", "Apps_Marketplace_pricingPlan_monthly": "__price__ / month", + "Apps_Marketplace_pricingPlan_startingAt_monthly": "starting at __price__ / month", "Apps_Marketplace_pricingPlan_monthly_perUser": "__price__ / month per user", + "Apps_Marketplace_pricingPlan_startingAt_monthly_perUser": "starting at __price__ / month per user", "Apps_Marketplace_pricingPlan_yearly": "__price__ / year", + "Apps_Marketplace_pricingPlan_startingAt_yearly": "starting at __price__ / year", "Apps_Marketplace_pricingPlan_yearly_perUser": "__price__ / year per user", + "Apps_Marketplace_pricingPlan_startingAt_yearly_perUser": "starting at __price__ / year per user", "Apps_Settings": "App's Settings", "Apps_User_Already_Exists": "The username \"__username__\" is already being used. Rename or remove the user using it to install this App", "Apps_WhatIsIt": "Apps: What Are They?", diff --git a/server/methods/registerUser.js b/server/methods/registerUser.js index d95098efcae0..9ef019fab962 100644 --- a/server/methods/registerUser.js +++ b/server/methods/registerUser.js @@ -3,18 +3,11 @@ import { Match, check } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; import s from 'underscore.string'; -import * as Mailer from '../../app/mailer'; import { Users } from '../../app/models'; import { settings } from '../../app/settings'; import { saveCustomFields, validateEmailDomain, passwordPolicy } from '../../app/lib'; import { validateInviteToken } from '../../app/invites/server/functions/validateInviteToken'; -let verifyEmailTemplate = ''; -Meteor.startup(() => { - Mailer.getTemplateWrapped('Verification_Email', (value) => { - verifyEmailTemplate = value; - }); -}); Meteor.methods({ registerUser(formData) { const AllowAnonymousRead = settings.get('Accounts_AllowAnonymousRead'); @@ -46,12 +39,14 @@ Meteor.methods({ } if (settings.get('Accounts_RegistrationForm') === 'Secret URL' && (!formData.secretURL || formData.secretURL !== settings.get('Accounts_RegistrationForm_SecretURL'))) { - if (formData.secretURL) { - try { - validateInviteToken(formData.secretURL); - } catch (e) { - throw new Meteor.Error('error-user-registration-secret', 'User registration is only allowed via Secret URL', { method: 'registerUser' }); - } + if (!formData.secretURL) { + throw new Meteor.Error('error-user-registration-secret', 'User registration is only allowed via Secret URL', { method: 'registerUser' }); + } + + try { + validateInviteToken(formData.secretURL); + } catch (e) { + throw new Meteor.Error('error-user-registration-secret', 'User registration is only allowed via Secret URL', { method: 'registerUser' }); } } @@ -86,11 +81,6 @@ Meteor.methods({ saveCustomFields(userId, formData); try { - const subject = Mailer.replace(settings.get('Verification_Email_Subject')); - - Accounts.emailTemplates.verifyEmail.subject = () => subject; - Accounts.emailTemplates.verifyEmail.html = (userModel, url) => Mailer.replace(Mailer.replacekey(verifyEmailTemplate, 'Verification_Url', url), userModel); - Accounts.sendVerificationEmail(userId, userData.email); } catch (error) { // throw new Meteor.Error 'error-email-send-failed', 'Error trying to send email: ' + error.message, { method: 'registerUser', message: error.message }