From 08e7788afb732151d99bf12e63ccc2827ae41a95 Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt Date: Tue, 29 Jan 2019 09:51:22 -0200 Subject: [PATCH 001/399] Update LIMITATION_OF_RESPONSIBILITY.md --- LIMITATION_OF_RESPONSIBILITY.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/LIMITATION_OF_RESPONSIBILITY.md b/LIMITATION_OF_RESPONSIBILITY.md index f451e30c7128..531868d2c0d3 100644 --- a/LIMITATION_OF_RESPONSIBILITY.md +++ b/LIMITATION_OF_RESPONSIBILITY.md @@ -1,4 +1,4 @@ -## WARNING to ROCKET.CHAT USERS +## WARNING Rocket.Chat is open source software. Anyone in the world can download and run a Rocket.Chat server at any time. @@ -10,10 +10,14 @@ In particular: - Rocket.Chat Technologies Corp. do not and cannot control or regulate how these servers are operated. - Rocket.Chat Technologies Corp. cannot access, determine or regulate any contents or information flow on these servers. -## IMPORTANT +## PUBLIC SERVER For total transparency, Rocket.Chat Technologies Corp. owns and operates only ONE publicly available Rocket.Chat server in the world. The server that Rocket.Chat Technologies Corp. operates can only be accessed at: https://open.rocket.chat Any other Rocket.Chat server you access is not operated by Rocket.Chat Technologies Corp. and is subjected to the usage warning above. + +## ROCKET.CHAT CLOUD + +Rocket.Chat Technologies Corp. provides a cloud service for hosting Rocket.Chat instances. The data, messages and files on those instances are subject to our [Terms of Use](https://rocket.chat/terms). If you have evidence of misuse or a breach of our terms, contact us at [contact@rocket.chat](mailto:contact@rocket.chat) and include a description of the breach as well as the instance's URL. From c420a28a1d20f72501747c10f0aca92633db454d Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Tue, 29 Jan 2019 18:23:42 -0200 Subject: [PATCH 002/399] [FIX] Fix bug when user try recreate channel or group with same name and remove room from cache when user leaves room (#12341) * Fix bug when user try recreate channel or group with same name * Remove flowrouter from cachedcollection * Remove room from cache when user leaves room * Fix deletions and leaves from rooms in client, and rooms stream when delete room --- .../client/models/CachedCollection.js | 19 +++++++++++++++++-- .../client/lib/ChannelActions.js | 18 +----------------- server/publications/room.js | 10 ++++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js b/packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js index 2c1bf8dde23e..b3a509eba21c 100644 --- a/packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js +++ b/packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js @@ -339,20 +339,35 @@ export class CachedCollection { this.collection.remove({}); } + removeRoomFromCacheWhenUserLeaves(roomId, ChatRoom, CachedChatRoom) { + ChatRoom.remove(roomId); + CachedChatRoom.saveCache(); + } + async setupListener(eventType, eventName) { Meteor.startup(async() => { const { Notifications } = await import('meteor/rocketchat:notifications'); const { RoomManager } = await import('meteor/rocketchat:ui'); + const { ChatRoom } = await import('meteor/rocketchat:models'); + const { CachedChatRoom } = await import('meteor/rocketchat:models'); Notifications[eventType || this.eventType](eventName || this.eventName, (t, record) => { this.log('record received', t, record); callbacks.run(`cachedCollection-received-${ this.name }`, record, t); if (t === 'removed') { + let room; + if (this.eventName === 'subscriptions-changed') { + room = ChatRoom.findOne(record.rid); + this.removeRoomFromCacheWhenUserLeaves(room._id, ChatRoom, CachedChatRoom); + } else { + room = this.collection.findOne({ _id: record._id }); + } + if (room) { + RoomManager.close(room.t + room.name); + } this.collection.remove(record._id); - RoomManager.close(record.t + record.name); } else { this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); } - this.saveCache(); }); }); diff --git a/packages/rocketchat-ui-utils/client/lib/ChannelActions.js b/packages/rocketchat-ui-utils/client/lib/ChannelActions.js index 3fa348c3100f..5782aa9a8548 100644 --- a/packages/rocketchat-ui-utils/client/lib/ChannelActions.js +++ b/packages/rocketchat-ui-utils/client/lib/ChannelActions.js @@ -1,9 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { t, UiTextContext, roomTypes, handleError } from 'meteor/rocketchat:utils'; import { modal } from './modal'; -import { ChatSubscription } from 'meteor/rocketchat:models'; import { call } from './callMethod'; export function hide(type, rid, name) { @@ -36,20 +34,6 @@ export function hide(type, rid, name) { return false; } -const leaveRoom = async(rid) => { - if (!Meteor.userId()) { - return false; - } - const tmp = ChatSubscription.findOne({ rid, 'u._id': Meteor.userId() }); - ChatSubscription.remove({ rid, 'u._id': Meteor.userId() }); - try { - await call('leaveRoom', rid); - } catch (error) { - ChatSubscription.insert(tmp); - throw error; - } -}; - export async function leave(type, rid, name) { const { RoomManager } = await import('meteor/rocketchat:ui'); const warnText = roomTypes.roomTypes[type].getUiText(UiTextContext.LEAVE_WARNING); @@ -69,7 +53,7 @@ export async function leave(type, rid, name) { return; } try { - await leaveRoom(rid); + await call('leaveRoom', rid); modal.close(); if (['channel', 'group', 'direct'].includes(FlowRouter.getRouteName()) && (Session.get('openedRoom') === rid)) { FlowRouter.go('home'); diff --git a/server/publications/room.js b/server/publications/room.js index dd50b60610dd..a67b92d986d8 100644 --- a/server/publications/room.js +++ b/server/publications/room.js @@ -112,6 +112,11 @@ Meteor.methods({ }, }); +const getSubscriptions = (id) => { + const fields = { 'u._id': 1 }; + return RocketChat.models.Subscriptions.trashFind({ rid: id }, { fields }); +}; + RocketChat.models.Rooms.on('change', ({ clientAction, id, data }) => { switch (clientAction) { case 'updated': @@ -126,6 +131,11 @@ RocketChat.models.Rooms.on('change', ({ clientAction, id, data }) => { } if (data) { + if (clientAction === 'removed') { + getSubscriptions(clientAction, id).forEach(({ u }) => { + RocketChat.Notifications.notifyUserInThisInstance(u._id, 'rooms-changed', clientAction, data); + }); + } RocketChat.Notifications.streamUser.__emit(id, clientAction, data); } }); From 8d997a02d8810570698cfe9f845c586a6905f8cb Mon Sep 17 00:00:00 2001 From: Aaron Ogle Date: Tue, 29 Jan 2019 14:24:39 -0600 Subject: [PATCH 003/399] Fix: Missing export in cloud package (#13282) * Fix missing export * Removed trailing s from filename to match export --- ...etWorkspaceAccessTokens.js => getWorkspaceAccessToken.js} | 0 packages/rocketchat-cloud/server/index.js | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) rename packages/rocketchat-cloud/server/functions/{getWorkspaceAccessTokens.js => getWorkspaceAccessToken.js} (100%) diff --git a/packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js b/packages/rocketchat-cloud/server/functions/getWorkspaceAccessToken.js similarity index 100% rename from packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js rename to packages/rocketchat-cloud/server/functions/getWorkspaceAccessToken.js diff --git a/packages/rocketchat-cloud/server/index.js b/packages/rocketchat-cloud/server/index.js index c64f14941528..eb6f32729334 100644 --- a/packages/rocketchat-cloud/server/index.js +++ b/packages/rocketchat-cloud/server/index.js @@ -1,5 +1,6 @@ import './methods'; -import { getWorkspaceAccessToken } from './functions/getWorkspaceAccessTokens'; +import { getWorkspaceAccessToken } from './functions/getWorkspaceAccessToken'; +import { getWorkspaceLicense } from './functions/getWorkspaceLicense'; if (RocketChat.models && RocketChat.models.Permissions) { RocketChat.models.Permissions.createOrUpdate('manage-cloud', ['admin']); @@ -8,4 +9,4 @@ if (RocketChat.models && RocketChat.models.Permissions) { // Ensure the client/workspace access token is valid getWorkspaceAccessToken(); -export { getWorkspaceAccessToken }; +export { getWorkspaceAccessToken, getWorkspaceLicense }; From 2fb16e7774c92933f1547a476edd495e7ffaddbd Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 29 Jan 2019 18:25:04 -0200 Subject: [PATCH 004/399] Add parseUrls field to the apps message converter (#13248) --- packages/rocketchat-apps/server/converters/messages.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/rocketchat-apps/server/converters/messages.js b/packages/rocketchat-apps/server/converters/messages.js index d53cc5d297bb..ebac12f73228 100644 --- a/packages/rocketchat-apps/server/converters/messages.js +++ b/packages/rocketchat-apps/server/converters/messages.js @@ -50,6 +50,7 @@ export class AppMessagesConverter { groupable: msgObj.groupable, attachments, reactions: msgObj.reactions, + parseUrls: msgObj.parseUrls, }; } @@ -110,6 +111,7 @@ export class AppMessagesConverter { groupable: message.groupable, attachments, reactions: message.reactions, + parseUrls: message.parseUrls, }; } From c42452fa31404393346cceecd4675de26061a34a Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Wed, 30 Jan 2019 12:03:07 -0200 Subject: [PATCH 005/399] [NEW] Collect data for Monthly/Daily Active Users for a future dashboard (#11525) * Initial implementation on Server Session Monitor. * Session model created. * Session model design done. * Removed unused underscore lib. * several improvements on codebase. * Recreating sessions when the current day changes. * Storing `instanceId` for every new session. * Improvements in cloning sessions. * Add a specific parser for user-agents of mobile apps. * Formatting props of mobile user-agents. * Improvements on bucket storage. * Time monitor changed. * Final improvements. * Removing Bucket Storage. * Start using Promises to clone the current sessions when the server date changes. * Fix Missing semicolon. * increased monitoring time. * Fix eslint new rules. * Add a new method to compare dates. * Remove global variables * Small code changes * Adapt to new code patterns * Aggregate by user and day * Generate statistics * Resolve review --- packages/rocketchat-models/server/index.js | 2 + .../server/models/Sessions.js | 280 +++++++++++++ .../server/functions/get.js | 6 + .../rocketchat-statistics/server/index.js | 3 +- .../server/lib/SAUMonitor.js | 382 ++++++++++++++++++ .../server/lib/UAParserMobile.js | 106 +++++ .../{Statistics_import.js => import.js} | 0 .../server/startup/monitor.js | 10 + 8 files changed, 788 insertions(+), 1 deletion(-) create mode 100644 packages/rocketchat-models/server/models/Sessions.js create mode 100644 packages/rocketchat-statistics/server/lib/SAUMonitor.js create mode 100644 packages/rocketchat-statistics/server/lib/UAParserMobile.js rename packages/rocketchat-statistics/server/models/{Statistics_import.js => import.js} (100%) create mode 100644 packages/rocketchat-statistics/server/startup/monitor.js diff --git a/packages/rocketchat-models/server/index.js b/packages/rocketchat-models/server/index.js index d504e4720ca2..3dcffefc6df4 100644 --- a/packages/rocketchat-models/server/index.js +++ b/packages/rocketchat-models/server/index.js @@ -10,6 +10,7 @@ import Subscriptions from './models/Subscriptions'; import Uploads from './models/Uploads'; import UserDataFiles from './models/UserDataFiles'; import Users from './models/Users'; +import Sessions from './models/Sessions'; import Statistics from './models/Statistics'; import Permissions from './models/Permissions'; import Roles from './models/Roles'; @@ -28,6 +29,7 @@ export { Uploads, UserDataFiles, Users, + Sessions, Statistics, Permissions, Roles, diff --git a/packages/rocketchat-models/server/models/Sessions.js b/packages/rocketchat-models/server/models/Sessions.js new file mode 100644 index 000000000000..cb7fb3151b19 --- /dev/null +++ b/packages/rocketchat-models/server/models/Sessions.js @@ -0,0 +1,280 @@ +import { Base } from './_Base'; + +export class Sessions extends Base { + constructor(...args) { + super(...args); + + this.tryEnsureIndex({ instanceId: 1, sessionId: 1, year: 1, month: 1, day: 1 }); + this.tryEnsureIndex({ instanceId: 1, sessionId: 1, userId: 1 }); + this.tryEnsureIndex({ instanceId: 1, sessionId: 1 }); + this.tryEnsureIndex({ year: 1, month: 1, day: 1, type: 1 }); + this.tryEnsureIndex({ _computedAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 45 }); + } + + getUniqueUsersOfYesterday() { + const date = new Date(); + date.setDate(date.getDate() - 1); + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + + return { + year, + month, + day, + data: Promise.await(this.model.rawCollection().aggregate([{ + $match: { + year, + month, + day, + type: 'user_daily', + }, + }, { + $group: { + _id: { + day: '$day', + month: '$month', + year: '$year', + }, + count: { + $sum: '$count', + }, + time: { + $sum: '$time', + }, + }, + }, { + $project: { + _id: 0, + count: 1, + time: 1, + }, + }]).toArray()), + }; + } + + getUniqueUsersOfLastMonth() { + const date = new Date(); + date.setMonth(date.getMonth() - 1); + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + + return { + year, + month, + data: Promise.await(this.model.rawCollection().aggregate([{ + $match: { + year, + month, + type: 'user_daily', + }, + }, { + $group: { + _id: { + month: '$month', + year: '$year', + }, + count: { + $sum: '$count', + }, + time: { + $sum: '$time', + }, + }, + }, { + $project: { + _id: 0, + count: 1, + time: 1, + }, + }]).toArray()), + }; + } + + getUniqueDevicesOfYesterday() { + const date = new Date(); + date.setDate(date.getDate() - 1); + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + + return { + year, + month, + day, + data: Promise.await(this.model.rawCollection().aggregate([{ + $match: { + year, + month, + day, + type: 'user_daily', + }, + }, { + $unwind: '$devices', + }, { + $group: { + _id: { + type : '$devices.type', + name : '$devices.name', + version : '$devices.version', + }, + count: { + $sum: '$count', + }, + }, + }, { + $project: { + _id: 0, + type: '$_id.type', + name: '$_id.name', + version: '$_id.version', + count: 1, + }, + }]).toArray()), + }; + } + + getUniqueOSOfYesterday() { + const date = new Date(); + date.setDate(date.getDate() - 1); + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + + return { + year, + month, + day, + data: Promise.await(this.model.rawCollection().aggregate([{ + $match: { + year, + month, + day, + type: 'user_daily', + 'devices.os.name': { + $exists: true, + }, + }, + }, { + $unwind: '$devices', + }, { + $group: { + _id: { + name : '$devices.os.name', + version : '$devices.os.version', + }, + count: { + $sum: '$count', + }, + }, + }, { + $project: { + _id: 0, + name: '$_id.name', + version: '$_id.version', + count: 1, + }, + }]).toArray()), + }; + } + + createOrUpdate(data = {}) { + const { year, month, day, sessionId, instanceId } = data; + + if (!year || !month || !day || !sessionId || !instanceId) { + return; + } + + const now = new Date; + + return this.upsert({ instanceId, sessionId, year, month, day }, { + $set: data, + $setOnInsert: { + createdAt: now, + }, + }); + } + + closeByInstanceIdAndSessionId(instanceId, sessionId) { + const query = { + instanceId, + sessionId, + closedAt: { $exists: 0 }, + }; + + const closeTime = new Date(); + const update = { + $set: { + closedAt: closeTime, + lastActivityAt: closeTime, + }, + }; + + return this.update(query, update); + } + + updateActiveSessionsByDateAndInstanceIdAndIds({ year, month, day } = {}, instanceId, sessions, data = {}) { + const query = { + instanceId, + year, + month, + day, + sessionId: { $in: sessions }, + closedAt: { $exists: 0 }, + }; + + const update = { + $set: data, + }; + + return this.update(query, update, { multi: true }); + } + + logoutByInstanceIdAndSessionIdAndUserId(instanceId, sessionId, userId) { + const query = { + instanceId, + sessionId, + userId, + logoutAt: { $exists: 0 }, + }; + + const logoutAt = new Date(); + const update = { + $set: { + logoutAt, + }, + }; + + return this.update(query, update, { multi: true }); + } + + createBatch(sessions) { + if (!sessions || sessions.length === 0) { + return; + } + + const ops = []; + sessions.forEach((doc) => { + const { year, month, day, sessionId, instanceId } = doc; + delete doc._id; + + ops.push({ + updateOne: { + filter: { year, month, day, sessionId, instanceId }, + update: { + $set: doc, + }, + upsert: true, + }, + }); + }); + + return this.model.rawCollection().bulkWrite(ops, { ordered: false }); + } +} + +export default new Sessions('sessions'); diff --git a/packages/rocketchat-statistics/server/functions/get.js b/packages/rocketchat-statistics/server/functions/get.js index 2b0cd965c7cf..5c70902beee4 100644 --- a/packages/rocketchat-statistics/server/functions/get.js +++ b/packages/rocketchat-statistics/server/functions/get.js @@ -5,6 +5,7 @@ import os from 'os'; import LivechatVisitors from 'meteor/rocketchat:livechat/server/models/LivechatVisitors'; import { RocketChat } from 'meteor/rocketchat:lib'; import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; +import { Sessions } from 'meteor/rocketchat:models'; const wizardFields = [ 'Organization_Type', @@ -130,5 +131,10 @@ RocketChat.statistics.get = function _getStatistics() { console.error('Error getting MongoDB version'); } + statistics.uniqueUsersOfYesterday = Sessions.getUniqueUsersOfYesterday(); + statistics.uniqueUsersOfLastMonth = Sessions.getUniqueUsersOfLastMonth(); + statistics.uniqueDevicesOfYesterday = Sessions.getUniqueDevicesOfYesterday(); + statistics.uniqueOSOfYesterday = Sessions.getUniqueOSOfYesterday(); + return statistics; }; diff --git a/packages/rocketchat-statistics/server/index.js b/packages/rocketchat-statistics/server/index.js index b6c264b84f2b..150f49e1179f 100644 --- a/packages/rocketchat-statistics/server/index.js +++ b/packages/rocketchat-statistics/server/index.js @@ -1,5 +1,6 @@ import '../lib/rocketchat'; -import './models/Statistics_import'; +import './models/import'; import './functions/get'; import './functions/save'; import './methods/getStatistics'; +import './startup/monitor'; diff --git a/packages/rocketchat-statistics/server/lib/SAUMonitor.js b/packages/rocketchat-statistics/server/lib/SAUMonitor.js new file mode 100644 index 000000000000..dc66c06d4a16 --- /dev/null +++ b/packages/rocketchat-statistics/server/lib/SAUMonitor.js @@ -0,0 +1,382 @@ +import { Meteor } from 'meteor/meteor'; +import { Accounts } from 'meteor/accounts-base'; +import UAParser from 'ua-parser-js'; +import { UAParserMobile } from './UAParserMobile'; +import { Sessions } from 'meteor/rocketchat:models'; +import { Logger } from 'meteor/rocketchat:logger'; +import { SyncedCron } from 'meteor/littledata:synced-cron'; + +const getDateObj = (dateTime = new Date()) => ({ + day: dateTime.getDate(), + month: dateTime.getMonth() + 1, + year: dateTime.getFullYear(), +}); + +const isSameDateObj = (oldest, newest) => oldest.year === newest.year && oldest.month === newest.month && oldest.day === newest.day; + +const logger = new Logger('SAUMonitor'); + +/** + * Server Session Monitor for SAU(Simultaneously Active Users) based on Meteor server sessions + */ +export class SAUMonitorClass { + constructor() { + this._started = false; + this._monitorTime = 60000; + this._timer = null; + this._today = getDateObj(); + this._instanceId = null; + } + + start(instanceId) { + if (this.isRunning()) { + return; + } + + this._instanceId = instanceId; + + if (!this._instanceId) { + logger.debug('[start] - InstanceId is not defined.'); + return; + } + + this._startMonitoring(() => { + this._started = true; + logger.debug(`[start] - InstanceId: ${ this._instanceId }`); + }); + } + + stop() { + if (!this.isRunning()) { + return; + } + + this._started = false; + + if (this._timer) { + Meteor.clearInterval(this._timer); + } + + logger.debug(`[stop] - InstanceId: ${ this._instanceId }`); + } + + isRunning() { + return this._started === true; + } + + _startMonitoring(callback) { + try { + this._handleAccountEvents(); + this._handleOnConnection(); + this._startSessionControl(); + this._initActiveServerSessions(); + this._startAggregation(); + if (callback) { + callback(); + } + } catch (err) { + throw new Meteor.Error(err); + } + } + + _startSessionControl() { + if (this.isRunning()) { + return; + } + + if (this._monitorTime < 0) { + return; + } + + this._timer = Meteor.setInterval(() => { + this._updateActiveSessions(); + }, this._monitorTime); + } + + _handleOnConnection() { + if (this.isRunning()) { + return; + } + + Meteor.onConnection((connection) => { + // this._handleSession(connection, getDateObj()); + + connection.onClose(() => { + Sessions.closeByInstanceIdAndSessionId(this._instanceId, connection.id); + }); + }); + } + + _handleAccountEvents() { + if (this.isRunning()) { + return; + } + + Accounts.onLogin((info) => { + const userId = info.user._id; + const loginAt = new Date(); + const params = { userId, loginAt, ...getDateObj() }; + this._handleSession(info.connection, params); + this._updateConnectionInfo(info.connection.id, { loginAt }); + }); + + Accounts.onLogout((info) => { + const sessionId = info.connection.id; + const userId = info.user._id; + Sessions.logoutByInstanceIdAndSessionIdAndUserId(this._instanceId, sessionId, userId); + }); + } + + _handleSession(connection, params) { + const data = this._getConnectionInfo(connection, params); + Sessions.createOrUpdate(data); + } + + _updateActiveSessions() { + if (!this.isRunning()) { + return; + } + + const { year, month, day } = this._today; + const currentDateTime = new Date(); + const currentDay = getDateObj(currentDateTime); + + if (!isSameDateObj(this._today, currentDay)) { + const beforeDateTime = new Date(this._today.year, this._today.month - 1, this._today.day, 23, 59, 59, 999); + const nextDateTime = new Date(currentDay.year, currentDay.month - 1, currentDay.day); + + const createSessions = ((objects, ids) => { + Sessions.createBatch(objects); + + Meteor.defer(() => { + Sessions.updateActiveSessionsByDateAndInstanceIdAndIds({ year, month, day }, this._instanceId, ids, { lastActivityAt: beforeDateTime }); + }); + }); + this._applyAllServerSessionsBatch(createSessions, { createdAt: nextDateTime, lastActivityAt: nextDateTime, ...currentDay }); + this._today = currentDay; + return; + } + + // Otherwise, just update the lastActivityAt field + this._applyAllServerSessionsIds((sessions) => { + Sessions.updateActiveSessionsByDateAndInstanceIdAndIds({ year, month, day }, this._instanceId, sessions, { lastActivityAt: currentDateTime }); + }); + } + + _getConnectionInfo(connection, params = {}) { + if (!connection) { + return; + } + + const ip = connection.httpHeaders ? connection.httpHeaders['x-real-ip'] || connection.httpHeaders['x-forwarded-for'] : connection.clientAddress; + const host = connection.httpHeaders && connection.httpHeaders.host; + const info = { + type: 'session', + sessionId: connection.id, + instanceId: this._instanceId, + ip, + host, + ...this._getUserAgentInfo(connection), + ...params, + }; + + if (connection.loginAt) { + info.loginAt = connection.loginAt; + } + + return info; + } + + _getUserAgentInfo(connection) { + if (!(connection && connection.httpHeaders && connection.httpHeaders['user-agent'])) { + return; + } + + const uaString = connection.httpHeaders['user-agent']; + let result; + + if (UAParserMobile.isMobileApp(uaString)) { + result = UAParserMobile.uaObject(uaString); + } else { + const ua = new UAParser(uaString); + result = ua.getResult(); + } + + const info = { + type: 'other', + }; + + const removeEmptyProps = (obj) => { + Object.keys(obj).forEach((p) => (!obj[p] || obj[p] === undefined) && delete obj[p]); + return obj; + }; + + if (result.browser && result.browser.name) { + info.type = 'browser'; + info.name = result.browser.name; + info.longVersion = result.browser.version; + } + + if (result.os && result.os.name) { + info.os = removeEmptyProps(result.os); + } + + if (result.device && (result.device.type || result.device.model)) { + info.type = 'mobile-app'; + + if (result.app && result.app.name) { + info.name = result.app.name; + info.longVersion = result.app.version; + if (result.app.bundle) { + info.longVersion += ` ${ result.app.bundle }`; + } + } + } + + if (typeof info.longVersion === 'string') { + info.version = info.longVersion.match(/(\d+\.){0,2}\d+/)[0]; + } + + return { + device: info, + }; + } + + _initActiveServerSessions() { + this._applyAllServerSessions((connectionHandle) => { + this._handleSession(connectionHandle, getDateObj()); + }); + } + + _applyAllServerSessions(callback) { + if (!callback || typeof callback !== 'function') { + return; + } + + const sessions = Object.values(Meteor.server.sessions).filter((session) => session.userId); + sessions.forEach((session) => { + callback(session.connectionHandle); + }); + } + + _applyAllServerSessionsIds(callback) { + if (!callback || typeof callback !== 'function') { + return; + } + + const sessionIds = Object.values(Meteor.server.sessions).filter((session) => session.userId).map((s) => s.id); + while (sessionIds.length) { + callback(sessionIds.splice(0, 500)); + } + } + + _updateConnectionInfo(sessionId, data = {}) { + if (!sessionId) { + return; + } + if (Meteor.server.sessions[sessionId]) { + Object.keys(data).forEach((p) => { + Object.defineProperty(Meteor.server.sessions[sessionId].connectionHandle, p, { + value: data[p], + }); + }); + } + } + + _applyAllServerSessionsBatch(callback, params) { + const batch = (arr, limit) => { + if (!arr.length) { + return Promise.resolve(); + } + const ids = []; + return Promise.all(arr.splice(0, limit).map((item) => { + ids.push(item.id); + return this._getConnectionInfo(item.connectionHandle, params); + })).then((data) => { + callback(data, ids); + return batch(arr, limit); + }).catch((e) => { + logger.debug(`Error: ${ e.message }`); + }); + }; + + const sessions = Object.values(Meteor.server.sessions).filter((session) => session.userId); + batch(sessions, 500); + } + + _startAggregation() { + logger.info('[aggregate] - Start Cron.'); + SyncedCron.add({ + name: 'aggregate-sessions', + schedule: (parser) => parser.text('at 2:00 am'), + job: () => { + this.aggregate(); + }, + }); + + SyncedCron.start(); + } + + aggregate() { + logger.info('[aggregate] - Aggregatting data.'); + + const date = new Date(); + date.setDate(date.getDate() - 0); // yesterday + const yesterday = getDateObj(date); + + const match = { + type: 'session', + year: { $lte: yesterday.year }, + month: { $lte: yesterday.month }, + day: { $lte: yesterday.day }, + }; + + Sessions.model.rawCollection().aggregate([{ + $match: { + userId: { $exists: true }, + lastActivityAt: { $exists: true }, + device: { $exists: true }, + ...match, + }, + }, { + $group: { + _id: { + userId: '$userId', + day: '$day', + month: '$month', + year: '$year', + }, + times: { $push: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } } }, + devices: { $addToSet: '$device' }, + }, + }, { + $project: { + _id: '$_id', + times: { $filter: { input: '$times', as: 'item', cond: { $gt: ['$$item', 0] } } }, + devices: '$devices', + }, + }, { + $project: { + type: 'user_daily', + _computedAt: new Date(), + day: '$_id.day', + month: '$_id.month', + year: '$_id.year', + userId: '$_id.userId', + time: { $sum: '$times' }, + count: { $size: '$times' }, + devices: '$devices', + }, + }]).forEach(Meteor.bindEnvironment((record) => { + record._id = `${ record.userId }-${ record.year }-${ record.month }-${ record.day }`; + Sessions.upsert({ _id: record._id }, record); + })); + + Sessions.update(match, { + $set: { + type: 'computed-session', + _computedAt: new Date(), + }, + }, { multi: true }); + } +} diff --git a/packages/rocketchat-statistics/server/lib/UAParserMobile.js b/packages/rocketchat-statistics/server/lib/UAParserMobile.js new file mode 100644 index 000000000000..7f1a48ab38af --- /dev/null +++ b/packages/rocketchat-statistics/server/lib/UAParserMobile.js @@ -0,0 +1,106 @@ +const mergeDeep = ((target, source) => { + if (!(typeof target === 'object' && typeof source === 'object')) { + return target; + } + + for (const key in source) { + if (source[key] === null && (target[key] === undefined || target[key] === null)) { + target[key] = null; + } else if (source[key] instanceof Array) { + if (!target[key]) { target[key] = []; } + target[key] = target[key].concat(source[key]); + } else if (typeof source[key] === 'object') { + if (!target[key]) { target[key] = {}; } + mergeDeep(target[key], source[key]); + } else { + target[key] = source[key]; + } + } + + return target; +}); + +const UAParserMobile = { + appName: 'RC Mobile', + device: 'mobile', + uaSeparator: ';', + props: { + os: { + list: ['name', 'version'], + }, + app: { + list: ['version', 'bundle'], + get: (prop, value) => { + if (prop === 'bundle') { + return value.replace(/([()])/g, ''); + } + + if (prop === 'version') { + return value.replace(/^v/g, ''); + } + + return value; + }, + }, + }, + + isMobileApp(uaString) { + if (!uaString || typeof uaString !== 'string') { + return false; + } + + const splitUA = uaString.split(this.uaSeparator); + return splitUA && splitUA[0] && splitUA[0].trim() === this.appName; + }, + + uaObject(uaString) { + if (!this.isMobileApp(uaString)) { + return {}; + } + + const splitUA = uaString.split(this.uaSeparator); + + let obj = { + device: { + type: this.device, + }, + app: { + name: splitUA[0], + }, + }; + + splitUA.shift(); // remove first element + if (splitUA.length === 0) { + return obj; + } + + splitUA.forEach((element, index) => { + const splitProps = element.trim().split(' '); + const key = Object.keys(this.props)[index]; + if (!key) { + return; + } + + const props = this.props[key]; + if (!props.list || !Array.isArray(props.list) || props.list.length === 0) { + return; + } + + const subProps = {}; + splitProps.forEach((value, idx) => { + if (props.list.length > idx) { + const propName = props.list[idx]; + subProps[propName] = props.get ? props.get(propName, value) : value; + } + }); + + const prop = {}; + prop[key] = subProps; + obj = mergeDeep(obj, prop); + }); + + return obj; + }, +}; + +export { UAParserMobile }; diff --git a/packages/rocketchat-statistics/server/models/Statistics_import.js b/packages/rocketchat-statistics/server/models/import.js similarity index 100% rename from packages/rocketchat-statistics/server/models/Statistics_import.js rename to packages/rocketchat-statistics/server/models/import.js diff --git a/packages/rocketchat-statistics/server/startup/monitor.js b/packages/rocketchat-statistics/server/startup/monitor.js new file mode 100644 index 000000000000..f86bc7d099c3 --- /dev/null +++ b/packages/rocketchat-statistics/server/startup/monitor.js @@ -0,0 +1,10 @@ +import { Meteor } from 'meteor/meteor'; +import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; + +import { SAUMonitorClass } from '../lib/SAUMonitor'; + +const SAUMonitor = new SAUMonitorClass(); + +Meteor.startup(() => { + SAUMonitor.start(InstanceStatus.id()); +}); From d58b20c04c2de9f306db35916ae7a8b571292e2c Mon Sep 17 00:00:00 2001 From: "Pierre H. Lehnen" Date: Wed, 30 Jan 2019 12:11:21 -0200 Subject: [PATCH 006/399] [FIX] HipChat Enterprise importer fails when importing a large amount of messages (millions) (#13221) * Added new screen to check the importer history * Fixed conditional check * Better error handling and local file import * Improved error readability * Improved error handling * Allow @here and @all on messages that won't trigger notifications, regardless of permissions * Fixed room owners * Fixed issue when accessing import adm for the first time * Included Imported count on history screen * Increased timeout for fetching import file data * Improved Importer Counters * Missing comma * Improved RAM usage * import in batches * Avoid hitting a mongo cursor timeout * Additional fixes for the importer * Improved code * Show room owner at admin room info * Get room owner instead of room creator in admin room info * Insert messages faster * Skip messages that were already imported * Improved perfomance for hipchat imports * Improved hipchat importer perfomance * Improved message count handling * skip import operations marked as invalid --- package-lock.json | 4 +- packages/rocketchat-i18n/i18n/en.i18n.json | 3 +- .../server/importer.js | 342 ++++++++++++------ .../client/admin/adminImportProgress.html | 2 + .../server/methods/getImportFileData.js | 7 +- packages/rocketchat-lib/package.js | 1 + .../server/functions/insertMessage.js | 149 ++++++++ .../client/rooms/adminRoomInfo.html | 8 + .../client/rooms/adminRoomInfo.js | 15 + 9 files changed, 409 insertions(+), 122 deletions(-) create mode 100644 packages/rocketchat-lib/server/functions/insertMessage.js diff --git a/package-lock.json b/package-lock.json index e4d39a2457ec..457819cdb171 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10436,7 +10436,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10458,7 +10458,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index ef17421147d7..2ea8862066a8 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3064,6 +3064,7 @@ "You_are_logged_in_as": "You are logged in as", "You_are_not_authorized_to_view_this_page": "You are not authorized to view this page.", "You_can_change_a_different_avatar_too": "You can override the avatar used to post from this integration.", + "You_can_close_this_window_now": "You can close this window now.", "You_can_search_using_RegExp_eg": "You can search using RegExp. e.g. /^text$/i", "You_can_use_an_emoji_as_avatar": "You can also use an emoji as an avatar.", "You_can_use_webhooks_to_easily_integrate_livechat_with_your_CRM": "You can use webhooks to easily integrate livechat with your CRM.", @@ -3096,4 +3097,4 @@ "Your_push_was_sent_to_s_devices": "Your push was sent to %s devices", "Your_server_link": "Your server link", "Your_workspace_is_ready": "Your workspace is ready to use 🎉" -} \ No newline at end of file +} diff --git a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js index e2e67a1d536b..1419196a060c 100644 --- a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js +++ b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js @@ -189,7 +189,7 @@ export class HipChatEnterpriseImporter extends Base { } async storeUserTempMessages(tempMessages, roomIdentifier, index) { - this.logger.debug('dumping messages to database'); + this.logger.debug(`dumping ${ tempMessages.length } messages from room ${ roomIdentifier } to database`); await this.collection.insert({ import: this.importRecord._id, importer: this.name, @@ -207,16 +207,30 @@ export class HipChatEnterpriseImporter extends Base { this.logger.debug(`preparing room with ${ file.length } messages `); for (const m of file) { if (m.PrivateUserMessage) { - msgs.push({ - type: 'user', - id: `hipchatenterprise-${ m.PrivateUserMessage.id }`, - senderId: m.PrivateUserMessage.sender.id, - receiverId: m.PrivateUserMessage.receiver.id, - text: m.PrivateUserMessage.message.indexOf('/me ') === -1 ? m.PrivateUserMessage.message : `${ m.PrivateUserMessage.message.replace(/\/me /, '_') }_`, - ts: new Date(m.PrivateUserMessage.timestamp.split(' ')[0]), - attachment: m.PrivateUserMessage.attachment, - attachment_path: m.PrivateUserMessage.attachment_path, - }); + // If the message id is already on the list, skip it + if (this.preparedMessages[m.PrivateUserMessage.id] !== undefined) { + continue; + } + this.preparedMessages[m.PrivateUserMessage.id] = true; + + const newId = `hipchatenterprise-private-${ m.PrivateUserMessage.id }`; + const skipMessage = this._checkIfMessageExists(newId); + const skipAttachment = skipMessage && (m.PrivateUserMessage.attachment_path ? this._checkIfMessageExists(`${ newId }-attachment`) : true); + + if (!skipMessage || !skipAttachment) { + msgs.push({ + type: 'user', + id: newId, + senderId: m.PrivateUserMessage.sender.id, + receiverId: m.PrivateUserMessage.receiver.id, + text: m.PrivateUserMessage.message.indexOf('/me ') === -1 ? m.PrivateUserMessage.message : `${ m.PrivateUserMessage.message.replace(/\/me /, '_') }_`, + ts: new Date(m.PrivateUserMessage.timestamp.split(' ')[0]), + attachment: m.PrivateUserMessage.attachment, + attachment_path: m.PrivateUserMessage.attachment_path, + skip: skipMessage, + skipAttachment, + }); + } } if (msgs.length >= 500) { @@ -232,6 +246,14 @@ export class HipChatEnterpriseImporter extends Base { return msgs.length; } + _checkIfMessageExists(messageId) { + if (this._hasAnyImportedMessage === false) { + return false; + } + + return Boolean(RocketChat.models.Messages.findOne({ _id: messageId }, { fields: { _id: 1 }, limit: 1 })); + } + async prepareRoomMessagesFile(file, roomIdentifier, id, index) { let roomMsgs = []; this.logger.debug(`preparing room with ${ file.length } messages `); @@ -239,36 +261,56 @@ export class HipChatEnterpriseImporter extends Base { for (const m of file) { if (m.UserMessage) { - roomMsgs.push({ - type: 'user', - id: `hipchatenterprise-${ id }-${ m.UserMessage.id }`, - userId: m.UserMessage.sender.id, - text: m.UserMessage.message.indexOf('/me ') === -1 ? m.UserMessage.message : `${ m.UserMessage.message.replace(/\/me /, '_') }_`, - ts: new Date(m.UserMessage.timestamp.split(' ')[0]), - attachment: m.UserMessage.attachment, - attachment_path: m.UserMessage.attachment_path, - }); + const newId = `hipchatenterprise-${ id }-user-${ m.UserMessage.id }`; + const skipMessage = this._checkIfMessageExists(newId); + const skipAttachment = (skipMessage && m.UserMessage.attachment_path ? this._checkIfMessageExists(`${ newId }-attachment`) : true); + + if (!skipMessage || !skipAttachment) { + roomMsgs.push({ + type: 'user', + id: newId, + userId: m.UserMessage.sender.id, + text: m.UserMessage.message.indexOf('/me ') === -1 ? m.UserMessage.message : `${ m.UserMessage.message.replace(/\/me /, '_') }_`, + ts: new Date(m.UserMessage.timestamp.split(' ')[0]), + attachment: m.UserMessage.attachment, + attachment_path: m.UserMessage.attachment_path, + skip: skipMessage, + skipAttachment, + }); + } } else if (m.NotificationMessage) { const text = m.NotificationMessage.message.indexOf('/me ') === -1 ? m.NotificationMessage.message : `${ m.NotificationMessage.message.replace(/\/me /, '_') }_`; - - roomMsgs.push({ - type: 'user', - id: `hipchatenterprise-${ id }-${ m.NotificationMessage.id }`, - userId: 'rocket.cat', - alias: m.NotificationMessage.sender, - text: m.NotificationMessage.message_format === 'html' ? turndownService.turndown(text) : text, - ts: new Date(m.NotificationMessage.timestamp.split(' ')[0]), - attachment: m.NotificationMessage.attachment, - attachment_path: m.NotificationMessage.attachment_path, - }); + const newId = `hipchatenterprise-${ id }-notif-${ m.NotificationMessage.id }`; + const skipMessage = this._checkIfMessageExists(newId); + const skipAttachment = skipMessage && (m.NotificationMessage.attachment_path ? this._checkIfMessageExists(`${ newId }-attachment`) : true); + + if (!skipMessage || !skipAttachment) { + roomMsgs.push({ + type: 'user', + id: newId, + userId: 'rocket.cat', + alias: m.NotificationMessage.sender, + text: m.NotificationMessage.message_format === 'html' ? turndownService.turndown(text) : text, + ts: new Date(m.NotificationMessage.timestamp.split(' ')[0]), + attachment: m.NotificationMessage.attachment, + attachment_path: m.NotificationMessage.attachment_path, + skip: skipMessage, + skipAttachment, + }); + } } else if (m.TopicRoomMessage) { - roomMsgs.push({ - type: 'topic', - id: `hipchatenterprise-${ id }-${ m.TopicRoomMessage.id }`, - userId: m.TopicRoomMessage.sender.id, - ts: new Date(m.TopicRoomMessage.timestamp.split(' ')[0]), - text: m.TopicRoomMessage.message, - }); + const newId = `hipchatenterprise-${ id }-topic-${ m.TopicRoomMessage.id }`; + const skipMessage = this._checkIfMessageExists(newId); + if (!skipMessage) { + roomMsgs.push({ + type: 'topic', + id: newId, + userId: m.TopicRoomMessage.sender.id, + ts: new Date(m.TopicRoomMessage.timestamp.split(' ')[0]), + text: m.TopicRoomMessage.message, + skip: skipMessage, + }); + } } else { this.logger.warn('HipChat Enterprise importer isn\'t configured to handle this message:', m); } @@ -326,6 +368,9 @@ export class HipChatEnterpriseImporter extends Base { break; case 'history.json': return await this.prepareMessagesFile(file, info); + case 'emoticons.json': + this.logger.warn('HipChat Enterprise importer doesn\'t import emoticons.', info); + break; default: this.logger.warn(`HipChat Enterprise importer doesn't know what to do with the file "${ fileName }" :o`, info); break; @@ -339,10 +384,16 @@ export class HipChatEnterpriseImporter extends Base { this.collection.remove({}); this.emailList = []; + this._hasAnyImportedMessage = Boolean(RocketChat.models.Messages.findOne({ _id: /hipchatenterprise\-.*/ })); + this.usersCount = 0; this.channelsCount = 0; this.messagesCount = 0; + // HipChat duplicates direct messages (one for each user) + // This object will keep track of messages that have already been prepared so it doesn't try to do it twice + this.preparedMessages = {}; + const promise = new Promise((resolve, reject) => { this.extract.on('entry', Meteor.bindEnvironment((header, stream, next) => { this.logger.debug(`new entry from import file: ${ header.name }`); @@ -401,7 +452,7 @@ export class HipChatEnterpriseImporter extends Base { } // Ensure we have some users, channels, and messages - if (!this.usersCount || !this.channelsCount || this.messagesCount === 0) { + if (!this.usersCount && this.channelsCount && !this.messagesCount) { this.logger.debug(`users: ${ this.usersCount }, channels: ${ this.channelsCount }, messages = ${ this.messagesCount }`); super.updateProgress(ProgressStep.ERROR); reject(new Meteor.Error('error-import-file-is-empty')); @@ -485,7 +536,7 @@ export class HipChatEnterpriseImporter extends Base { Meteor.runAsUser(existingUserId, () => { RocketChat.models.Users.update({ _id: existingUserId }, { $addToSet: { importIds: userToImport.id } }); - Meteor.call('setUsername', userToImport.username, { joinDefaultChannelsSilenced: true }); + // Meteor.call('setUsername', userToImport.username, { joinDefaultChannelsSilenced: true }); // TODO: Use moment timezone to calc the time offset - Meteor.call 'userSetUtcOffset', user.tz_offset / 3600 RocketChat.models.Users.setName(existingUserId, userToImport.name); @@ -525,13 +576,12 @@ export class HipChatEnterpriseImporter extends Base { this.addUserError(userToImport.id, e); } } else { - const user = { email: userToImport.email, password: Random.id() }; - // if (u.is_email_taken && u.email) { - // user.email = user.email.replace('@', `+rocket.chat_${ Math.floor(Math.random() * 10000).toString() }@`); - // } + const user = { email: userToImport.email, password: Random.id(), username: userToImport.username }; if (!user.email) { delete user.email; - user.username = userToImport.username; + } + if (!user.username) { + delete user.username; } try { @@ -655,25 +705,26 @@ export class HipChatEnterpriseImporter extends Base { startImport(importSelection) { super.startImport(importSelection); + this._userDataCache = {}; const started = Date.now(); this._applyUserSelections(importSelection); const startedByUserId = Meteor.userId(); - Meteor.defer(() => { + Meteor.defer(async() => { try { - super.updateProgress(ProgressStep.IMPORTING_USERS); - this._importUsers(startedByUserId); + await super.updateProgress(ProgressStep.IMPORTING_USERS); + await this._importUsers(startedByUserId); - super.updateProgress(ProgressStep.IMPORTING_CHANNELS); - this._importChannels(startedByUserId); + await super.updateProgress(ProgressStep.IMPORTING_CHANNELS); + await this._importChannels(startedByUserId); - super.updateProgress(ProgressStep.IMPORTING_MESSAGES); - this._importMessages(startedByUserId); - this._importDirectMessages(); + await super.updateProgress(ProgressStep.IMPORTING_MESSAGES); + await this._importDirectMessages(); + await this._importMessages(startedByUserId); // super.updateProgress(ProgressStep.FINISHING); - super.updateProgress(ProgressStep.DONE); + await super.updateProgress(ProgressStep.DONE); } catch (e) { super.updateRecord({ 'error-record': JSON.stringify(e, Object.getOwnPropertyNames(e)) }); this.logger.error(e); @@ -768,7 +819,7 @@ export class HipChatEnterpriseImporter extends Base { } _importAttachment(msg, room, sender) { - if (msg.attachment_path) { + if (msg.attachment_path && !msg.skipAttachment) { const details = { message_id: `${ msg.id }-attachment`, name: msg.attachment.name, @@ -784,7 +835,6 @@ export class HipChatEnterpriseImporter extends Base { _importSingleMessage(msg, roomIdentifier, room) { if (isNaN(msg.ts)) { this.logger.warn(`Timestamp on a message in ${ roomIdentifier } is invalid`); - super.addCountCompleted(1); return; } @@ -795,17 +845,19 @@ export class HipChatEnterpriseImporter extends Base { switch (msg.type) { case 'user': - RocketChat.sendMessage(creator, { - _id: msg.id, - ts: msg.ts, - msg: msg.text, - rid: room._id, - alias: msg.alias, - u: { - _id: creator._id, - username: creator.username, - }, - }, room, true); + if (!msg.skip) { + RocketChat.insertMessage(creator, { + _id: msg.id, + ts: msg.ts, + msg: msg.text, + rid: room._id, + alias: msg.alias, + u: { + _id: creator._id, + username: creator.username, + }, + }, room, false); + } break; case 'topic': RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', room._id, msg.text, creator, { _id: msg.id, ts: msg.ts }); @@ -816,48 +868,65 @@ export class HipChatEnterpriseImporter extends Base { console.error(e); this.addMessageError(e, msg); } - - super.addCountCompleted(1); } - _importMessages(startedByUserId) { - const messageListIds = this.collection.find({ - import: this.importRecord._id, - importer: this.name, - type: 'messages', - }, { _id : true }).fetch(); + async _importMessageList(startedByUserId, messageListId) { + const list = this.collection.findOneById(messageListId); + if (!list) { + return; + } - messageListIds.forEach((item) => { - const list = this.collection.findOneById(item._id); - if (!list) { - return; - } + if (!list.messages) { + return; + } - if (!list.messages) { - return; - } + const { roomIdentifier, hipchatRoomId, name } = list; + const rid = await this._getRoomRocketId(hipchatRoomId); - const { roomIdentifier, hipchatRoomId, name } = list; - const rid = this._getRoomRocketId(hipchatRoomId); + // If there's no rocketId for the channel, then it wasn't imported + if (!rid) { + this.logger.debug(`Ignoring room ${ roomIdentifier } ( ${ name } ), as there's no rid to use.`); + return; + } - // If there's no rocketId for the channel, then it wasn't imported - if (!rid) { - this.logger.debug(`Ignoring room ${ roomIdentifier } ( ${ name } ), as there's no rid to use.`); - return; - } + const room = await RocketChat.models.Rooms.findOneById(rid, { fields: { usernames: 1, t: 1, name: 1 } }); + await super.updateRecord({ + messagesstatus: `${ roomIdentifier }.${ list.messages.length }`, + 'count.completed': this.progress.count.completed, + }); - const room = RocketChat.models.Rooms.findOneById(rid, { fields: { usernames: 1, t: 1, name: 1 } }); - super.updateRecord({ - messagesstatus: `${ roomIdentifier }.${ list.messages.length }`, - 'count.completed': this.progress.count.completed, - }); + await Meteor.runAsUser(startedByUserId, async() => { + let msgCount = 0; + try { + for (const msg of list.messages) { + await this._importSingleMessage(msg, roomIdentifier, room); + msgCount++; + if (msgCount >= 50) { + super.addCountCompleted(msgCount); + msgCount = 0; + } + } + } catch (e) { + this.logger.error(e); + } - Meteor.runAsUser(startedByUserId, () => { - list.messages.forEach((msg) => { - this._importSingleMessage(msg, roomIdentifier, room); - }); - }); + if (msgCount > 0) { + super.addCountCompleted(msgCount); + } }); + + } + + async _importMessages(startedByUserId) { + const messageListIds = this.collection.find({ + import: this.importRecord._id, + importer: this.name, + type: 'messages', + }, { fields: { _id: true } }).fetch(); + + for (const item of messageListIds) { + await this._importMessageList(startedByUserId, item._id); + } } _importDirectMessages() { @@ -865,15 +934,24 @@ export class HipChatEnterpriseImporter extends Base { import: this.importRecord._id, importer: this.name, type: 'user-messages', - }, { _id : true }).fetch(); + }, { fields: { _id: true } }).fetch(); + + this.logger.info(`${ messageListIds.length } lists of messages to import.`); + + // HipChat duplicates direct messages (one for each user) + // This object will keep track of messages that have already been imported so it doesn't try to insert them twice + const importedMessages = {}; messageListIds.forEach((item) => { + this.logger.debug(`New list of user messages: ${ item._id }`); const list = this.collection.findOneById(item._id); if (!list) { + this.logger.warn('Record of user-messages list not found'); return; } if (!list.messages) { + this.logger.warn('No message list found on record.'); return; } @@ -883,18 +961,20 @@ export class HipChatEnterpriseImporter extends Base { return; } + this.logger.debug(`${ list.messages.length } messages on this list`); super.updateRecord({ messagesstatus: `${ list.name }.${ list.messages.length }`, 'count.completed': this.progress.count.completed, }); + let msgCount = 0; const roomUsers = {}; const roomObjects = {}; list.messages.forEach((msg) => { + msgCount++; if (isNaN(msg.ts)) { this.logger.warn(`Timestamp on a message in ${ list.name } is invalid`); - super.addCountCompleted(1); return; } @@ -905,7 +985,6 @@ export class HipChatEnterpriseImporter extends Base { if (!roomUsers[msg.senderId]) { this.logger.warn('Skipping message due to missing sender.'); - super.addCountCompleted(1); return; } @@ -916,7 +995,6 @@ export class HipChatEnterpriseImporter extends Base { if (!roomUsers[msg.receiverId]) { this.logger.warn('Skipping message due to missing receiver.'); - super.addCountCompleted(1); return; } @@ -930,6 +1008,7 @@ export class HipChatEnterpriseImporter extends Base { let room = roomObjects[roomId]; if (!room) { + this.logger.debug('DM room not found, creating it.'); Meteor.runAsUser(sender._id, () => { const roomInfo = Meteor.call('createDirectMessage', receiver.username); @@ -940,17 +1019,28 @@ export class HipChatEnterpriseImporter extends Base { try { Meteor.runAsUser(sender._id, () => { + if (importedMessages[msg.id] !== undefined) { + return; + } + importedMessages[msg.id] = true; + if (msg.attachment_path) { - const details = { - message_id: `${ msg.id }-attachment`, - name: msg.attachment.name, - size: msg.attachment.size, - userId: sender._id, - rid: room._id, - }; - this.uploadFile(details, msg.attachment.url, sender, room, msg.ts); - } else { - RocketChat.sendMessage(sender, { + if (!msg.skipAttachment) { + this.logger.debug('Uploading DM file'); + const details = { + message_id: `${ msg.id }-attachment`, + name: msg.attachment.name, + size: msg.attachment.size, + userId: sender._id, + rid: room._id, + }; + this.uploadFile(details, msg.attachment.url, sender, room, msg.ts); + } + } + + if (!msg.skip) { + this.logger.debug('Inserting DM message'); + RocketChat.insertMessage(sender, { _id: msg.id, ts: msg.ts, msg: msg.text, @@ -959,7 +1049,7 @@ export class HipChatEnterpriseImporter extends Base { _id: sender._id, username: sender.username, }, - }, room, true); + }, room, false); } }); } catch (e) { @@ -967,8 +1057,15 @@ export class HipChatEnterpriseImporter extends Base { this.addMessageError(e, msg); } - super.addCountCompleted(1); + if (msgCount >= 50) { + super.addCountCompleted(msgCount); + msgCount = 0; + } }); + + if (msgCount > 0) { + super.addCountCompleted(msgCount); + } }); } @@ -992,14 +1089,23 @@ export class HipChatEnterpriseImporter extends Base { return new Selection(this.name, selectionUsers, selectionChannels, selectionMessages); } + _getBasicUserData(userId) { + if (this._userDataCache[userId]) { + return this._userDataCache[userId]; + } + + this._userDataCache[userId] = RocketChat.models.Users.findOneById(userId, { fields: { username: 1 } }); + return this._userDataCache[userId]; + } + getRocketUserFromUserId(userId) { if (userId === 'rocket.cat') { - return RocketChat.models.Users.findOneById(userId, { fields: { username: 1 } }); + return this._getBasicUserData('rocket.cat'); } const rocketId = this._getUserRocketId(userId); if (rocketId) { - return RocketChat.models.Users.findOneById(rocketId, { fields: { username: 1 } }); + return this._getBasicUserData(rocketId); } } diff --git a/packages/rocketchat-importer/client/admin/adminImportProgress.html b/packages/rocketchat-importer/client/admin/adminImportProgress.html index 6f8d95c00e66..9acfb08a8ec1 100644 --- a/packages/rocketchat-importer/client/admin/adminImportProgress.html +++ b/packages/rocketchat-importer/client/admin/adminImportProgress.html @@ -2,4 +2,6 @@ {{> loading}}

{{step}}

{{completed}} / {{total}}

+ +

{{_ "You_can_close_this_window_now"}}

diff --git a/packages/rocketchat-importer/server/methods/getImportFileData.js b/packages/rocketchat-importer/server/methods/getImportFileData.js index 6e143aca2f8d..242954bf62d9 100644 --- a/packages/rocketchat-importer/server/methods/getImportFileData.js +++ b/packages/rocketchat-importer/server/methods/getImportFileData.js @@ -34,7 +34,11 @@ Meteor.methods({ ]; if (waitingSteps.indexOf(importer.instance.progress.step) >= 0) { - return { waiting: true }; + if (importer.instance.importRecord && importer.instance.importRecord.valid) { + return { waiting: true }; + } else { + throw new Meteor.Error('error-import-operation-invalid', 'Invalid Import Operation', { method: 'getImportFileData' }); + } } const readySteps = [ @@ -62,6 +66,7 @@ Meteor.methods({ return data; }).catch((e) => { + console.error(e); throw new Meteor.Error(e); }); diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index b7ebecbd8118..335ae9f3f588 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -125,6 +125,7 @@ Package.onUse(function(api) { api.addFiles('server/functions/saveCustomFields.js', 'server'); api.addFiles('server/functions/saveCustomFieldsWithoutValidation.js', 'server'); api.addFiles('server/functions/sendMessage.js', 'server'); + api.addFiles('server/functions/insertMessage.js', 'server'); api.addFiles('server/functions/settings.js', 'server'); api.addFiles('server/functions/setUserAvatar.js', 'server'); api.addFiles('server/functions/setUsername.js', 'server'); diff --git a/packages/rocketchat-lib/server/functions/insertMessage.js b/packages/rocketchat-lib/server/functions/insertMessage.js new file mode 100644 index 000000000000..6979fd0af05c --- /dev/null +++ b/packages/rocketchat-lib/server/functions/insertMessage.js @@ -0,0 +1,149 @@ +import { Match, check } from 'meteor/check'; + +const objectMaybeIncluding = (types) => Match.Where((value) => { + Object.keys(types).forEach((field) => { + if (value[field] != null) { + try { + check(value[field], types[field]); + } catch (error) { + error.path = field; + throw error; + } + } + }); + + return true; +}); + +const validateAttachmentsFields = (attachmentField) => { + check(attachmentField, objectMaybeIncluding({ + short: Boolean, + title: String, + value: Match.OneOf(String, Match.Integer, Boolean), + })); + + if (typeof attachmentField.value !== 'undefined') { + attachmentField.value = String(attachmentField.value); + } +}; + +const validateAttachmentsActions = (attachmentActions) => { + check(attachmentActions, objectMaybeIncluding({ + type: String, + text: String, + url: String, + image_url: String, + is_webview: Boolean, + webview_height_ratio: String, + msg: String, + msg_in_chat_window: Boolean, + })); +}; + +const validateAttachment = (attachment) => { + check(attachment, objectMaybeIncluding({ + color: String, + text: String, + ts: Match.OneOf(String, Match.Integer), + thumb_url: String, + button_alignment: String, + actions: [Match.Any], + message_link: String, + collapsed: Boolean, + author_name: String, + author_link: String, + author_icon: String, + title: String, + title_link: String, + title_link_download: Boolean, + image_url: String, + audio_url: String, + video_url: String, + fields: [Match.Any], + })); + + if (attachment.fields && attachment.fields.length) { + attachment.fields.map(validateAttachmentsFields); + } + + if (attachment.actions && attachment.actions.length) { + attachment.actions.map(validateAttachmentsActions); + } +}; + +const validateBodyAttachments = (attachments) => attachments.map(validateAttachment); + +RocketChat.insertMessage = function(user, message, room, upsert = false) { + if (!user || !message || !room._id) { + return false; + } + + check(message, objectMaybeIncluding({ + _id: String, + msg: String, + text: String, + alias: String, + emoji: String, + avatar: String, + attachments: [Match.Any], + })); + + if (Array.isArray(message.attachments) && message.attachments.length) { + validateBodyAttachments(message.attachments); + } + + if (!message.ts) { + message.ts = new Date(); + } + const { _id, username } = user; + message.u = { + _id, + username, + }; + message.rid = room._id; + + if (!Match.test(message.msg, String)) { + message.msg = ''; + } + + if (message.ts == null) { + message.ts = new Date(); + } + + if (message.parseUrls !== false) { + message.html = message.msg; + message = RocketChat.Markdown.code(message); + + const urls = message.html.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\(\)\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g); + if (urls) { + message.urls = urls.map((url) => ({ url })); + } + + message = RocketChat.Markdown.mountTokensBack(message, false); + message.msg = message.html; + delete message.html; + delete message.tokens; + } + + // Avoid saving sandstormSessionId to the database + let sandstormSessionId = null; + if (message.sandstormSessionId) { + sandstormSessionId = message.sandstormSessionId; + delete message.sandstormSessionId; + } + + if (message._id && upsert) { + const { _id } = message; + delete message._id; + RocketChat.models.Messages.upsert({ + _id, + 'u._id': message.u._id, + }, message); + message._id = _id; + } else { + message._id = RocketChat.models.Messages.insert(message); + } + + message.sandstormSessionId = sandstormSessionId; + return message; +}; diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.html b/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.html index 8edb049bfa7a..162c85960d93 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.html +++ b/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.html @@ -21,6 +21,14 @@

{{_ "Room_Info"}}

{{/if}} + {{#if roomOwner}} +
  • + +
    + {{roomOwner}} +
    +
  • + {{/if}}
  • diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.js b/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.js index 4847e6955b61..ec8d61ca028e 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRoomInfo.js @@ -6,6 +6,7 @@ import { TAPi18n } from 'meteor/tap:i18n'; import { RocketChat, handleError } from 'meteor/rocketchat:lib'; import { modal } from 'meteor/rocketchat:ui'; import { t } from 'meteor/rocketchat:utils'; +import { call } from 'meteor/rocketchat:ui-utils'; import { AdminChatRoom } from './adminRooms'; import toastr from 'toastr'; @@ -43,6 +44,10 @@ Template.adminRoomInfo.helpers({ const room = AdminChatRoom.findOne(this.rid, { fields: { name: 1 } }); return room && room.name; }, + roomOwner() { + const roomOwner = Template.instance().roomOwner.get(); + return roomOwner && (roomOwner.name || roomOwner.username); + }, roomTopic() { const room = AdminChatRoom.findOne(this.rid, { fields: { topic: 1 } }); return room && room.topic; @@ -134,6 +139,7 @@ Template.adminRoomInfo.events({ Template.adminRoomInfo.onCreated(function() { this.editing = new ReactiveVar; + this.roomOwner = new ReactiveVar; this.validateRoomType = () => { const type = this.$('input[name=roomType]:checked').val(); if (type !== 'c' && type !== 'p') { @@ -260,4 +266,13 @@ Template.adminRoomInfo.onCreated(function() { } this.editing.set(); }; + + this.autorun(async() => { + this.roomOwner.set(null); + for (const { roles, u } of await call('getRoomRoles', Session.get('adminRoomsSelected').rid)) { + if (roles.includes('owner')) { + this.roomOwner.set(u); + } + } + }); }); From 50878fb42319eed67206da49d442d40dbb53569f Mon Sep 17 00:00:00 2001 From: Laurent Montel Date: Thu, 31 Jan 2019 15:19:30 +0100 Subject: [PATCH 007/399] Add missing channel RESTAPI Add removeLeader/addLeader --- packages/rocketchat-api/server/v1/channels.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js index 6332e0ef840e..29d208d57f19 100644 --- a/packages/rocketchat-api/server/v1/channels.js +++ b/packages/rocketchat-api/server/v1/channels.js @@ -967,3 +967,32 @@ RocketChat.API.v1.addRoute('channels.moderators', { authRequired: true }, { }); }, }); + +RocketChat.API.v1.addRoute('channels.addLeader', { authRequired: true }, { + post() { + const findResult = findChannelByIdOrName({ params: this.requestParams() }); + + const user = this.getUserFromParams(); + + Meteor.runAsUser(this.userId, () => { + Meteor.call('addRoomLeader', findResult._id, user._id); + }); + + return RocketChat.API.v1.success(); + }, +}); + +RocketChat.API.v1.addRoute('channels.removeLeader', { authRequired: true }, { + post() { + const findResult = findChannelByIdOrName({ params: this.requestParams() }); + + const user = this.getUserFromParams(); + + Meteor.runAsUser(this.userId, () => { + Meteor.call('removeRoomLeader', findResult._id, user._id); + }); + + return RocketChat.API.v1.success(); + }, +}); + From 68e6be914d2afd2206bbeacff430637052268413 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 31 Jan 2019 15:42:06 -0200 Subject: [PATCH 008/399] [FIX] Preview of image uploads were not working when apps framework is enable (#13303) --- .../server/converters/messages.js | 16 ++++++++++++++++ .../server/functions/sendMessage.js | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/rocketchat-apps/server/converters/messages.js b/packages/rocketchat-apps/server/converters/messages.js index ebac12f73228..215e278d7ed5 100644 --- a/packages/rocketchat-apps/server/converters/messages.js +++ b/packages/rocketchat-apps/server/converters/messages.js @@ -133,9 +133,17 @@ export class AppMessagesConverter { title: attachment.title ? attachment.title.value : undefined, title_link: attachment.title ? attachment.title.link : undefined, title_link_download: attachment.title ? attachment.title.displayDownloadLink : undefined, + image_dimensions: attachment.imageDimensions, + image_preview: attachment.imagePreview, image_url: attachment.imageUrl, + image_type: attachment.imageType, + image_size: attachment.imageSize, audio_url: attachment.audioUrl, + audio_type: attachment.audioType, + audio_size: attachment.audioSize, video_url: attachment.videoUrl, + video_type: attachment.videoType, + video_size: attachment.videoSize, fields: attachment.fields, button_alignment: attachment.actionButtonsAlignment, actions: attachment.actions, @@ -185,9 +193,17 @@ export class AppMessagesConverter { thumbnailUrl: attachment.thumb_url, author, title, + imageDimensions: attachment.image_dimensions, + imagePreview: attachment.image_preview, imageUrl: attachment.image_url, + imageType: attachment.image_type, + imageSize: attachment.image_size, audioUrl: attachment.audio_url, + audioType: attachment.audio_type, + audioSize: attachment.audio_size, videoUrl: attachment.video_url, + videoType: attachment.video_type, + videoSize: attachment.video_size, fields: attachment.fields, actionButtonsAlignment: attachment.button_alignment, actions: attachment.actions, diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js index 4a5218e7ad65..4fcda633e5c6 100644 --- a/packages/rocketchat-lib/server/functions/sendMessage.js +++ b/packages/rocketchat-lib/server/functions/sendMessage.js @@ -58,9 +58,17 @@ const validateAttachment = (attachment) => { title: String, title_link: String, title_link_download: Boolean, + image_dimensions: Object, image_url: String, + image_preview: String, + image_type: String, + image_size: Number, audio_url: String, + audio_type: String, + audio_size: Number, video_url: String, + video_type: String, + video_size: Number, fields: [Match.Any], })); From 5795b3f870f58f631e6d74a6aed37f03d4a44285 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 1 Feb 2019 10:28:28 -0200 Subject: [PATCH 009/399] [FIX] REST endpoint for creating custom emojis (#13306) * Skip custom emoji tests * Fix emoji.create endpoint and re-enable tests --- .../rocketchat-api/server/v1/emoji-custom.js | 26 +++++++++++-------- tests/end-to-end/ui/11-admin.js | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/rocketchat-api/server/v1/emoji-custom.js b/packages/rocketchat-api/server/v1/emoji-custom.js index 94f427511ba5..10ebcb64bb27 100644 --- a/packages/rocketchat-api/server/v1/emoji-custom.js +++ b/packages/rocketchat-api/server/v1/emoji-custom.js @@ -16,35 +16,39 @@ RocketChat.API.v1.addRoute('emoji-custom.create', { authRequired: true }, { Meteor.runAsUser(this.userId, () => { const fields = {}; const busboy = new Busboy({ headers: this.request.headers }); + const emojiData = []; + let emojiMimetype = ''; Meteor.wrapAsync((callback) => { busboy.on('file', Meteor.bindEnvironment((fieldname, file, filename, encoding, mimetype) => { if (fieldname !== 'emoji') { return callback(new Meteor.Error('invalid-field')); } - const emojiData = []; + file.on('data', Meteor.bindEnvironment((data) => emojiData.push(data))); file.on('end', Meteor.bindEnvironment(() => { const extension = mimetype.split('/')[1]; + emojiMimetype = mimetype; fields.extension = extension; - fields.newFile = true; - fields.aliases = fields.aliases || ''; - try { - Meteor.call('insertOrUpdateEmoji', fields); - Meteor.call('uploadEmojiCustom', Buffer.concat(emojiData), mimetype, fields); - callback(); - } catch (error) { - return callback(error); - } })); })); busboy.on('field', (fieldname, val) => { fields[fieldname] = val; }); + busboy.on('finish', Meteor.bindEnvironment(() => { + fields.newFile = true; + fields.aliases = fields.aliases || ''; + try { + Meteor.call('insertOrUpdateEmoji', fields); + Meteor.call('uploadEmojiCustom', Buffer.concat(emojiData), emojiMimetype, fields); + callback(); + } catch (error) { + return callback(error); + } + })); this.request.pipe(busboy); })(); - }); }, }); diff --git a/tests/end-to-end/ui/11-admin.js b/tests/end-to-end/ui/11-admin.js index 4fe4746b9f6e..da7aede7ed9c 100644 --- a/tests/end-to-end/ui/11-admin.js +++ b/tests/end-to-end/ui/11-admin.js @@ -874,6 +874,7 @@ describe('[Administration]', () => { }); it('it should show the enter key behavior field', () => { + browser.scroll(0, 500); admin.accountsSendOnEnter.click(); admin.accountsSendOnEnter.isVisible().should.be.true; }); From 092297583781812d2ce5d7a857343cb858dd99db Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 1 Feb 2019 11:51:19 -0200 Subject: [PATCH 010/399] [FIX] Message updating by Apps (#13294) --- packages/rocketchat-apps/server/converters/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-apps/server/converters/messages.js b/packages/rocketchat-apps/server/converters/messages.js index 215e278d7ed5..27caf85431ae 100644 --- a/packages/rocketchat-apps/server/converters/messages.js +++ b/packages/rocketchat-apps/server/converters/messages.js @@ -6,7 +6,7 @@ export class AppMessagesConverter { } convertById(msgId) { - const msg = RocketChat.models.Messages.getOneById(msgId); + const msg = RocketChat.models.Messages.findOneById(msgId); return this.convertMessage(msg); } From fce665feb707beee3168e02af15f3148f4d99239 Mon Sep 17 00:00:00 2001 From: Laurent Montel Date: Fri, 1 Feb 2019 15:35:39 +0100 Subject: [PATCH 011/399] Add missing RESTAPI add/remove leader role --- packages/rocketchat-api/server/v1/channels.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js index 29d208d57f19..875fd434ef35 100644 --- a/packages/rocketchat-api/server/v1/channels.js +++ b/packages/rocketchat-api/server/v1/channels.js @@ -969,30 +969,30 @@ RocketChat.API.v1.addRoute('channels.moderators', { authRequired: true }, { }); RocketChat.API.v1.addRoute('channels.addLeader', { authRequired: true }, { - post() { - const findResult = findChannelByIdOrName({ params: this.requestParams() }); + post() { + const findResult = findChannelByIdOrName({ params: this.requestParams() }); - const user = this.getUserFromParams(); + const user = this.getUserFromParams(); - Meteor.runAsUser(this.userId, () => { - Meteor.call('addRoomLeader', findResult._id, user._id); - }); + Meteor.runAsUser(this.userId, () => { + Meteor.call('addRoomLeader', findResult._id, user._id); + }); - return RocketChat.API.v1.success(); - }, + return RocketChat.API.v1.success(); + }, }); RocketChat.API.v1.addRoute('channels.removeLeader', { authRequired: true }, { - post() { - const findResult = findChannelByIdOrName({ params: this.requestParams() }); + post() { + const findResult = findChannelByIdOrName({ params: this.requestParams() }); - const user = this.getUserFromParams(); + const user = this.getUserFromParams(); - Meteor.runAsUser(this.userId, () => { - Meteor.call('removeRoomLeader', findResult._id, user._id); - }); + Meteor.runAsUser(this.userId, () => { + Meteor.call('removeRoomLeader', findResult._id, user._id); + }); - return RocketChat.API.v1.success(); - }, + return RocketChat.API.v1.success(); + }, }); From 4c43a38a042d5b26f4e99d22bfc5076b399d32da Mon Sep 17 00:00:00 2001 From: "Pierre H. Lehnen" Date: Fri, 1 Feb 2019 15:24:59 -0200 Subject: [PATCH 012/399] [FIX] Hipchat Enterprise Importer not generating subscriptions (#13293) --- .../server/importer.js | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js index 1419196a060c..92b7a69c8096 100644 --- a/packages/rocketchat-importer-hipchat-enterprise/server/importer.js +++ b/packages/rocketchat-importer-hipchat-enterprise/server/importer.js @@ -154,6 +154,7 @@ export class HipChatEnterpriseImporter extends Base { isPrivate: r.Room.privacy === 'private', isArchived: r.Room.is_archived, topic: r.Room.topic, + members: r.Room.members, }); count++; @@ -433,7 +434,6 @@ export class HipChatEnterpriseImporter extends Base { super.addCountToTotal(this.messagesCount); // Check if any of the emails used are already taken - if (this.emailList.length > 0) { const conflictingUsers = RocketChat.models.Users.find({ 'emails.address': { $in: this.emailList } }); const conflictingUserEmails = []; @@ -452,7 +452,7 @@ export class HipChatEnterpriseImporter extends Base { } // Ensure we have some users, channels, and messages - if (!this.usersCount && this.channelsCount && !this.messagesCount) { + if (!this.usersCount && !this.channelsCount && !this.messagesCount) { this.logger.debug(`users: ${ this.usersCount }, channels: ${ this.channelsCount }, messages = ${ this.messagesCount }`); super.updateProgress(ProgressStep.ERROR); reject(new Meteor.Error('error-import-file-is-empty')); @@ -536,12 +536,10 @@ export class HipChatEnterpriseImporter extends Base { Meteor.runAsUser(existingUserId, () => { RocketChat.models.Users.update({ _id: existingUserId }, { $addToSet: { importIds: userToImport.id } }); - // Meteor.call('setUsername', userToImport.username, { joinDefaultChannelsSilenced: true }); - // TODO: Use moment timezone to calc the time offset - Meteor.call 'userSetUtcOffset', user.tz_offset / 3600 RocketChat.models.Users.setName(existingUserId, userToImport.name); - // TODO: Think about using a custom field for the users "title" field + // TODO: Think about using a custom field for the users "title" field if (userToImport.avatar) { Meteor.call('setAvatarFromService', `data:image/png;base64,${ userToImport.avatar }`); } @@ -720,8 +718,8 @@ export class HipChatEnterpriseImporter extends Base { await this._importChannels(startedByUserId); await super.updateProgress(ProgressStep.IMPORTING_MESSAGES); - await this._importDirectMessages(); await this._importMessages(startedByUserId); + await this._importDirectMessages(); // super.updateProgress(ProgressStep.FINISHING); await super.updateProgress(ProgressStep.DONE); @@ -763,6 +761,36 @@ export class HipChatEnterpriseImporter extends Base { }); } + _createSubscriptions(channelToImport, roomOrRoomId) { + if (!channelToImport || !channelToImport.members) { + return; + } + + let room; + if (roomOrRoomId && typeof roomOrRoomId === 'string') { + room = RocketChat.models.Rooms.findOneByIdOrName(roomOrRoomId); + } else { + room = roomOrRoomId; + } + + const extra = { open: true }; + channelToImport.members.forEach((hipchatUserId) => { + if (hipchatUserId === channelToImport.creator) { + // Creators are subscribed automatically + return; + } + + const user = this.getRocketUserFromUserId(hipchatUserId); + if (!user) { + this.logger.warn(`User ${ hipchatUserId } not found on Rocket.Chat database.`); + return; + } + + this.logger.info(`Creating user's subscription to room ${ room._id }, rocket.chat user is ${ user._id }, hipchat user is ${ hipchatUserId }`); + RocketChat.models.Subscriptions.createWithRoomAndUser(room, user, extra); + }); + } + _importChannel(channelToImport, startedByUserId) { Meteor.runAsUser(startedByUserId, () => { const existingRoom = RocketChat.models.Rooms.findOneByName(channelToImport.name); @@ -771,6 +799,8 @@ export class HipChatEnterpriseImporter extends Base { channelToImport.rocketId = channelToImport.name.toUpperCase() === 'GENERAL' ? 'GENERAL' : existingRoom._id; this._saveRoomIdReference(channelToImport.id, channelToImport.rocketId); RocketChat.models.Rooms.update({ _id: channelToImport.rocketId }, { $addToSet: { importIds: channelToImport.id } }); + + this._createSubscriptions(channelToImport, existingRoom || 'general'); } else { // Find the rocketchatId of the user who created this channel const creatorId = this._getUserRocketId(channelToImport.creator) || startedByUserId; @@ -788,6 +818,7 @@ export class HipChatEnterpriseImporter extends Base { if (channelToImport.rocketId) { RocketChat.models.Rooms.update({ _id: channelToImport.rocketId }, { $set: { ts: channelToImport.created, topic: channelToImport.topic }, $addToSet: { importIds: channelToImport.id } }); + this._createSubscriptions(channelToImport, channelToImport.rocketId); } } From 9dd44bd03469f2fde54d8b1faf379b6f6d1c51fe Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Fri, 1 Feb 2019 17:21:46 -0200 Subject: [PATCH 013/399] [NEW] REST endpoint to forward livechat rooms (#13308) * Add new rest endpoint allowing agents/managers to forward livechat rooms. --- packages/rocketchat-livechat/server/api/v1/room.js | 6 ++++++ packages/rocketchat-livechat/server/methods/transfer.js | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-livechat/server/api/v1/room.js b/packages/rocketchat-livechat/server/api/v1/room.js index e2e544cfec18..246f9451af11 100644 --- a/packages/rocketchat-livechat/server/api/v1/room.js +++ b/packages/rocketchat-livechat/server/api/v1/room.js @@ -153,3 +153,9 @@ RocketChat.API.v1.addRoute('livechat/room.survey', { } }, }); + +RocketChat.API.v1.addRoute('livechat/room.forward', { authRequired: true }, { + post() { + RocketChat.API.v1.success(Meteor.runAsUser(this.userId, () => Meteor.call('livechat:transfer', this.bodyParams))); + }, +}); diff --git a/packages/rocketchat-livechat/server/methods/transfer.js b/packages/rocketchat-livechat/server/methods/transfer.js index eabe665348da..d6a59b3ac243 100644 --- a/packages/rocketchat-livechat/server/methods/transfer.js +++ b/packages/rocketchat-livechat/server/methods/transfer.js @@ -16,14 +16,17 @@ Meteor.methods({ }); const room = RocketChat.models.Rooms.findOneById(transferData.roomId); - - const guest = LivechatVisitors.findOneById(room.v._id); + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'livechat:transfer' }); + } const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, Meteor.userId(), { fields: { _id: 1 } }); if (!subscription && !RocketChat.authz.hasRole(Meteor.userId(), 'livechat-manager')) { throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:transfer' }); } + const guest = LivechatVisitors.findOneById(room.v && room.v._id); + return RocketChat.Livechat.transfer(room, guest, transferData); }, }); From 1c5ca3bbf743aee03d845fd154440c3b3284ddc7 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 1 Feb 2019 17:29:51 -0200 Subject: [PATCH 014/399] [FIX] Mobile view and re-enable E2E tests (#13322) --- packages/rocketchat-ui-master/client/index.js | 2 -- packages/rocketchat-ui-master/package.js | 4 +++ tests/end-to-end/ui/08-resolutions.js | 33 ++++++++----------- tests/pageobjects/side-nav.page.js | 2 ++ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/rocketchat-ui-master/client/index.js b/packages/rocketchat-ui-master/client/index.js index fd3af97d4fe6..f6f55f64a219 100644 --- a/packages/rocketchat-ui-master/client/index.js +++ b/packages/rocketchat-ui-master/client/index.js @@ -1,5 +1,3 @@ -import './main.html'; import './loading.html'; import './error.html'; import './logoLayout.html'; -import './main'; diff --git a/packages/rocketchat-ui-master/package.js b/packages/rocketchat-ui-master/package.js index 4ac4f0471624..b5ca30505859 100644 --- a/packages/rocketchat-ui-master/package.js +++ b/packages/rocketchat-ui-master/package.js @@ -22,8 +22,12 @@ Package.onUse(function(api) { 'rocketchat:ui-sidenav', 'meteorhacks:inject-initial', ]); + api.addFiles('client/main.html', 'client'); + api.addFiles('client/main.js', 'client'); + api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); + api.addAssets('server/dynamic-css.js', 'server'); api.addAssets('public/icons.svg', 'server'); }); diff --git a/tests/end-to-end/ui/08-resolutions.js b/tests/end-to-end/ui/08-resolutions.js index 1bf01c7ea65f..8346d488f5b4 100644 --- a/tests/end-to-end/ui/08-resolutions.js +++ b/tests/end-to-end/ui/08-resolutions.js @@ -7,7 +7,7 @@ import { checkIfUserIsValid } from '../../data/checks'; // skipping this since the main content its not moved anymore, instead there is a overlay of the side nav over the main content -describe.skip('[Resolution]', () => { +describe('[Resolution]', () => { describe('[Mobile Render]', () => { before(() => { checkIfUserIsValid(username, email, password); @@ -17,15 +17,13 @@ describe.skip('[Resolution]', () => { }); after(() => { - Global.setWindowSize(1450, 900); - sideNav.preferencesClose.waitForVisible(5000); - sideNav.preferencesClose.click(); - sideNav.spotlightSearch.waitForVisible(5000); + Global.setWindowSize(1600, 1600); + sideNav.spotlightSearchIcon.waitForVisible(5000); }); describe('moving elements:', () => { it('it should close de sidenav', () => { - mainContent.mainContent.getLocation().should.deep.equal({ x:0, y:0 }); + mainContent.mainContent.getLocation().should.deep.include({ x:0 }); }); it('it should press the navbar button', () => { @@ -33,7 +31,7 @@ describe.skip('[Resolution]', () => { }); it('it should open de sidenav', () => { - mainContent.mainContent.getLocation().should.not.deep.equal({ x:0, y:0 }); + mainContent.mainContent.getLocation().should.not.deep.equal({ x:0 }); }); it('it should open general channel', () => { @@ -41,7 +39,7 @@ describe.skip('[Resolution]', () => { }); it('it should close de sidenav', () => { - mainContent.mainContent.getLocation().should.deep.equal({ x:0, y:0 }); + mainContent.mainContent.getLocation().should.deep.include({ x:0 }); }); it('it should press the navbar button', () => { @@ -49,9 +47,9 @@ describe.skip('[Resolution]', () => { }); it('it should open the user preferences screen', () => { - sideNav.accountMenu.waitForVisible(5000); - sideNav.accountMenu.click(); - sideNav.account.waitForVisible(5000); + sideNav.sidebarUserMenu.waitForVisible(); + sideNav.sidebarUserMenu.click(); + sideNav.account.waitForVisible(); sideNav.account.click(); }); @@ -61,7 +59,7 @@ describe.skip('[Resolution]', () => { }); it('it should close de sidenav', () => { - mainContent.mainContent.getLocation().should.deep.equal({ x:0, y:0 }); + mainContent.mainContent.getLocation().should.deep.include({ x:0 }); }); it('it should press the navbar button', () => { @@ -74,20 +72,17 @@ describe.skip('[Resolution]', () => { }); it('it should close de sidenav', () => { - mainContent.mainContent.getLocation().should.deep.equal({ x:0, y:0 }); + mainContent.mainContent.getLocation().should.deep.include({ x:0 }); }); it('it should press the navbar button', () => { sideNav.burgerBtn.click(); }); - it('it should press the avatar link', () => { - sideNav.avatar.waitForVisible(5000); - sideNav.avatar.click(); - }); - it('it should close de sidenav', () => { - mainContent.mainContent.getLocation().should.deep.equal({ x:0, y:0 }); + sideNav.preferencesClose.waitForVisible(5000); + sideNav.preferencesClose.click(); + sideNav.sidebarWrap.click(); }); it('it should press the navbar button', () => { diff --git a/tests/pageobjects/side-nav.page.js b/tests/pageobjects/side-nav.page.js index 104111c70ed5..25640d0bec20 100644 --- a/tests/pageobjects/side-nav.page.js +++ b/tests/pageobjects/side-nav.page.js @@ -46,6 +46,8 @@ class SideNav extends Page { get burgerBtn() { return browser.element('.burger'); } + get sidebarWrap() { return browser.element('.sidebar-wrap'); } + // Opens a channel via rooms list openChannel(channelName) { browser.waitForVisible(`.sidebar-item__name=${ channelName }`, 5000); From 9d7d2705b884d01ccff402c26cd9e38006181825 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 1 Feb 2019 17:32:38 -0200 Subject: [PATCH 015/399] [NEW] Limit all DDP/Websocket requests (configurable via admin panel) (#13311) --- packages/rocketchat-api/server/settings.js | 3 - packages/rocketchat-i18n/i18n/en.i18n.json | 15 ++ packages/rocketchat-lib/package.js | 2 + .../server/startup/rateLimiter.js | 187 ++++++++++++++++++ .../rocketchat-lib/server/startup/settings.js | 30 +++ .../rocketchat-metrics/server/lib/metrics.js | 1 + 6 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 packages/rocketchat-lib/server/startup/rateLimiter.js diff --git a/packages/rocketchat-api/server/settings.js b/packages/rocketchat-api/server/settings.js index 6e17f1c91e05..6b4c06e7a83a 100644 --- a/packages/rocketchat-api/server/settings.js +++ b/packages/rocketchat-api/server/settings.js @@ -4,9 +4,6 @@ RocketChat.settings.addGroup('General', function() { this.section('REST API', function() { this.add('API_Upper_Count_Limit', 100, { type: 'int', public: false }); this.add('API_Default_Count', 50, { type: 'int', public: false }); - this.add('API_Enable_Rate_Limiter_Dev', true, { type: 'boolean', public: false }); - this.add('API_Enable_Rate_Limiter_Limit_Calls_Default', 10, { type: 'int', public: false }); - this.add('API_Enable_Rate_Limiter_Limit_Time_Default', 60000, { type: 'int', public: false }); this.add('API_Allow_Infinite_Count', true, { type: 'boolean', public: false }); this.add('API_Enable_Direct_Message_History_EndPoint', false, { type: 'boolean', public: false }); this.add('API_Enable_Shields', true, { type: 'boolean', public: false }); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 2ea8862066a8..60ab3a5282e1 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -948,6 +948,21 @@ "days": "days", "DB_Migration": "Database Migration", "DB_Migration_Date": "Database Migration Date", + "DDP_Rate_Limit_IP_Enabled": "Limit by IP: enabled", + "DDP_Rate_Limit_IP_Requests_Allowed": "Limit by IP: requests allowed", + "DDP_Rate_Limit_IP_Interval_Time": "Limit by IP: interval time", + "DDP_Rate_Limit_User_Enabled": "Limit by User: enabled", + "DDP_Rate_Limit_User_Requests_Allowed": "Limit by User: requests allowed", + "DDP_Rate_Limit_User_Interval_Time": "Limit by User: interval time", + "DDP_Rate_Limit_Connection_Enabled": "Limit by Connection: enabled", + "DDP_Rate_Limit_Connection_Requests_Allowed": "Limit by Connection: requests allowed", + "DDP_Rate_Limit_Connection_Interval_Time": "Limit by Connection: interval time", + "DDP_Rate_Limit_User_By_Method_Enabled": "Limit by User per Method: enabled", + "DDP_Rate_Limit_User_By_Method_Requests_Allowed": "Limit by User per Method: requests allowed", + "DDP_Rate_Limit_User_By_Method_Interval_Time": "Limit by User per Method: interval time", + "DDP_Rate_Limit_Connection_By_Method_Enabled": "Limit by Connection per Method: enabled", + "DDP_Rate_Limit_Connection_By_Method_Requests_Allowed": "Limit by Connection per Method: requests allowed", + "DDP_Rate_Limit_Connection_By_Method_Interval_Time": "Limit by Connection per Method: interval time", "Deactivate": "Deactivate", "Decline": "Decline", "Decode_Key": "Decode Key", diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 335ae9f3f588..93051b49fa76 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -272,6 +272,8 @@ Package.onUse(function(api) { api.addFiles('startup/defaultRoomTypes.js'); api.addFiles('startup/index.js', 'server'); + api.addFiles('server/startup/rateLimiter.js', 'server'); + // EXPORT api.export('RocketChat'); api.export('handleError', 'client'); diff --git a/packages/rocketchat-lib/server/startup/rateLimiter.js b/packages/rocketchat-lib/server/startup/rateLimiter.js new file mode 100644 index 000000000000..8e936f827cc6 --- /dev/null +++ b/packages/rocketchat-lib/server/startup/rateLimiter.js @@ -0,0 +1,187 @@ +import _ from 'underscore'; +import { Meteor } from 'meteor/meteor'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { RateLimiter } from 'meteor/rate-limit'; +import { settings } from 'meteor/rocketchat:settings'; +import { metrics } from 'meteor/rocketchat:metrics'; + +// Get initial set of names already registered for rules +const names = new Set(Object.values(DDPRateLimiter.printRules()) + .map((rule) => rule._matchers) + .filter((match) => typeof match.name === 'string') + .map((match) => match.name)); + +// Override the addRule to save new names added after this point +const { addRule } = DDPRateLimiter; +DDPRateLimiter.addRule = (matcher, calls, time, callback) => { + if (matcher && typeof matcher.name === 'string') { + names.add(matcher.name); + } + return addRule.call(DDPRateLimiter, matcher, calls, time, callback); +}; + +// Need to override the meteor's code duo to a problem with the callback reply +// being shared among all matchs +RateLimiter.prototype.check = function(input) { + const self = this; + const reply = { + allowed: true, + timeToReset: 0, + numInvocationsLeft: Infinity, + }; + + const matchedRules = self._findAllMatchingRules(input); + _.each(matchedRules, function(rule) { + // ==== BEGIN OVERRIDE ==== + const callbackReply = { + allowed: true, + timeToReset: 0, + numInvocationsLeft: Infinity, + }; + // ==== END OVERRIDE ==== + + const ruleResult = rule.apply(input); + let numInvocations = rule.counters[ruleResult.key]; + + if (ruleResult.timeToNextReset < 0) { + // Reset all the counters since the rule has reset + rule.resetCounter(); + ruleResult.timeSinceLastReset = new Date().getTime() - rule._lastResetTime; + ruleResult.timeToNextReset = rule.options.intervalTime; + numInvocations = 0; + } + + if (numInvocations > rule.options.numRequestsAllowed) { + // Only update timeToReset if the new time would be longer than the + // previously set time. This is to ensure that if this input triggers + // multiple rules, we return the longest period of time until they can + // successfully make another call + if (reply.timeToReset < ruleResult.timeToNextReset) { + reply.timeToReset = ruleResult.timeToNextReset; + } + reply.allowed = false; + reply.numInvocationsLeft = 0; + + // ==== BEGIN OVERRIDE ==== + callbackReply.timeToReset = ruleResult.timeToNextReset; + callbackReply.allowed = false; + callbackReply.numInvocationsLeft = 0; + rule._executeCallback(callbackReply, input); + // ==== END OVERRIDE ==== + } else { + // If this is an allowed attempt and we haven't failed on any of the + // other rules that match, update the reply field. + if (rule.options.numRequestsAllowed - numInvocations < reply.numInvocationsLeft && reply.allowed) { + reply.timeToReset = ruleResult.timeToNextReset; + reply.numInvocationsLeft = rule.options.numRequestsAllowed - numInvocations; + } + + // ==== BEGIN OVERRIDE ==== + callbackReply.timeToReset = ruleResult.timeToNextReset; + callbackReply.numInvocationsLeft = rule.options.numRequestsAllowed - numInvocations; + rule._executeCallback(callbackReply, input); + // ==== END OVERRIDE ==== + } + }); + return reply; +}; + +const checkNameNonStream = (name) => name && !names.has(name) && !name.startsWith('stream-'); +const checkNameForStream = (name) => name && !names.has(name) && name.startsWith('stream-'); + +const ruleIds = {}; + +const callback = (message, name) => (reply, input) => { + if (reply.allowed === false) { + console.warn('DDP RATE LIMIT:', message); + console.warn(JSON.stringify({ ...reply, ...input }, null, 2)); + metrics.ddpRateLimitExceeded.inc({ + limit_name: name, + user_id: input.userId, + client_address: input.clientAddress, + type: input.type, + name: input.name, + connection_id: input.connectionId, + }); + // } else { + // console.log('DDP RATE LIMIT:', message); + // console.log(JSON.stringify({ ...reply, ...input }, null, 2)); + } +}; + +const messages = { + IP: 'address', + User: 'userId', + Connection: 'connectionId', + User_By_Method: 'userId per method', + Connection_By_Method: 'connectionId per method', +}; + +const reconfigureLimit = Meteor.bindEnvironment((name, rules, factor = 1) => { + if (ruleIds[name + factor]) { + DDPRateLimiter.removeRule(ruleIds[name + factor]); + } + + if (!settings.get(`DDP_Rate_Limit_${ name }_Enabled`)) { + return; + } + + ruleIds[name + factor] = addRule( + rules, + settings.get(`DDP_Rate_Limit_${ name }_Requests_Allowed`) * factor, + settings.get(`DDP_Rate_Limit_${ name }_Interval_Time`) * factor, + callback(`limit by ${ messages[name] }`, name) + ); +}); + +const configIP = _.debounce(() => { + reconfigureLimit('IP', { + clientAddress: (clientAddress) => clientAddress !== '127.0.0.1', + }); +}, 1000); + +const configUser = _.debounce(() => { + reconfigureLimit('User', { + userId: (userId) => userId != null, + }); +}, 1000); + +const configConnection = _.debounce(() => { + reconfigureLimit('Connection', { + connectionId: () => true, + }); +}, 1000); + +const configUserByMethod = _.debounce(() => { + reconfigureLimit('User_By_Method', { + type: () => true, + name: checkNameNonStream, + userId: (userId) => userId != null, + }); + reconfigureLimit('User_By_Method', { + type: () => true, + name: checkNameForStream, + userId: (userId) => userId != null, + }, 4); +}, 1000); + +const configConnectionByMethod = _.debounce(() => { + reconfigureLimit('Connection_By_Method', { + type: () => true, + name: checkNameNonStream, + connectionId: () => true, + }); + reconfigureLimit('Connection_By_Method', { + type: () => true, + name: checkNameForStream, + connectionId: () => true, + }, 4); +}, 1000); + +if (!process.env.TEST_MODE) { + settings.get(/^DDP_Rate_Limit_IP_.+/, configIP); + settings.get(/^DDP_Rate_Limit_User_[^B].+/, configUser); + settings.get(/^DDP_Rate_Limit_Connection_[^B].+/, configConnection); + settings.get(/^DDP_Rate_Limit_User_By_Method_.+/, configUserByMethod); + settings.get(/^DDP_Rate_Limit_Connection_By_Method_.+/, configConnectionByMethod); +} diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js index 6204b2c91593..6d08ae5ffa68 100644 --- a/packages/rocketchat-lib/server/startup/settings.js +++ b/packages/rocketchat-lib/server/startup/settings.js @@ -2666,4 +2666,34 @@ RocketChat.settings.addGroup('Setup_Wizard', function() { }); }); +RocketChat.settings.addGroup('Rate Limiter', function() { + this.section('DDP Rate Limiter', function() { + this.add('DDP_Rate_Limit_IP_Enabled', true, { type: 'boolean' }); + this.add('DDP_Rate_Limit_IP_Requests_Allowed', 120000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } }); + this.add('DDP_Rate_Limit_IP_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } }); + + this.add('DDP_Rate_Limit_User_Enabled', true, { type: 'boolean' }); + this.add('DDP_Rate_Limit_User_Requests_Allowed', 1200, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_User_Enabled', value: true } }); + this.add('DDP_Rate_Limit_User_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_User_Enabled', value: true } }); + + this.add('DDP_Rate_Limit_Connection_Enabled', true, { type: 'boolean' }); + this.add('DDP_Rate_Limit_Connection_Requests_Allowed', 600, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_Enabled', value: true } }); + this.add('DDP_Rate_Limit_Connection_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_Enabled', value: true } }); + + this.add('DDP_Rate_Limit_User_By_Method_Enabled', true, { type: 'boolean' }); + this.add('DDP_Rate_Limit_User_By_Method_Requests_Allowed', 20, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_User_By_Method_Enabled', value: true } }); + this.add('DDP_Rate_Limit_User_By_Method_Interval_Time', 10000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_User_By_Method_Enabled', value: true } }); + + this.add('DDP_Rate_Limit_Connection_By_Method_Enabled', true, { type: 'boolean' }); + this.add('DDP_Rate_Limit_Connection_By_Method_Requests_Allowed', 10, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_By_Method_Enabled', value: true } }); + this.add('DDP_Rate_Limit_Connection_By_Method_Interval_Time', 10000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_By_Method_Enabled', value: true } }); + }); + + this.section('API Rate Limiter', function() { + this.add('API_Enable_Rate_Limiter_Dev', true, { type: 'boolean' }); + this.add('API_Enable_Rate_Limiter_Limit_Calls_Default', 10, { type: 'int' }); + this.add('API_Enable_Rate_Limiter_Limit_Time_Default', 60000, { type: 'int' }); + }); +}); + RocketChat.settings.init(); diff --git a/packages/rocketchat-metrics/server/lib/metrics.js b/packages/rocketchat-metrics/server/lib/metrics.js index 7ea170de960d..95b44d5130be 100644 --- a/packages/rocketchat-metrics/server/lib/metrics.js +++ b/packages/rocketchat-metrics/server/lib/metrics.js @@ -48,6 +48,7 @@ metrics.notificationsSent = new client.Counter({ name: 'rocketchat_notification_ metrics.ddpSessions = new client.Gauge({ name: 'rocketchat_ddp_sessions_count', help: 'number of open ddp sessions' }); metrics.ddpAthenticatedSessions = new client.Gauge({ name: 'rocketchat_ddp_sessions_auth', help: 'number of authenticated open ddp sessions' }); metrics.ddpConnectedUsers = new client.Gauge({ name: 'rocketchat_ddp_connected_users', help: 'number of unique connected users' }); +metrics.ddpRateLimitExceeded = new client.Counter({ name: 'rocketchat_ddp_rate_limit_exceeded', labelNames: ['limit_name', 'user_id', 'client_address', 'type', 'name', 'connection_id'], help: 'number of times a ddp rate limiter was exceeded' }); metrics.version = new client.Gauge({ name: 'rocketchat_version', labelNames: ['version'], help: 'Rocket.Chat version' }); metrics.migration = new client.Gauge({ name: 'rocketchat_migration', help: 'migration versoin' }); From 18932c156452599d8cf1fa8f2da77bff4ae6474a Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 5 Feb 2019 01:07:02 -0200 Subject: [PATCH 016/399] [IMPROVE] new icons (#13289) --- .../assets/stylesheets/apps.css | 25 +- .../rocketchat-apps/client/admin/apps.html | 13 +- packages/rocketchat-apps/client/admin/apps.js | 15 +- .../views/mailMessagesInstructions.html | 10 +- .../client/messageAttachment.html | 2 +- .../client/stylesheets/messageAttachments.css | 13 +- .../server/cronPruneMessages.js | 9 +- .../client/accountProfile.html | 2 +- .../client/lib/startup.js | 2 +- .../client/views/cleanHistory.html | 4 +- .../client/views/stylesheets/cleanHistory.css | 18 +- .../client/tabs/uploadedFilesList.js | 4 +- .../rocketchat-ui-master/public/README.md | 13 + .../public/generateHTML.js | 79 ++ .../public/generateSprite.js | 87 ++ .../rocketchat-ui-master/public/icons.html | 383 +++++ .../rocketchat-ui-master/public/icons.svg | 1226 +++++------------ .../public/icons/Bell-off.svg | 3 + .../public/icons/Download.svg | 3 + .../rocketchat-ui-master/public/icons/Eye.svg | 3 + .../public/icons/File-google-drive.svg | 3 + .../public/icons/File-keynote.svg | 3 + .../public/icons/Files-audio.svg | 3 + .../public/icons/Files-video.svg | 3 + .../public/icons/Files-zip.svg | 3 + .../public/icons/Multiline.svg | 3 + .../public/icons/Send-active.svg | 3 + .../public/icons/Star-filled.svg | 3 + .../public/icons/Video-off.svg | 3 + .../public/icons/Volume-disable.svg | 3 + .../public/icons/add-reaction.svg | 3 + .../public/icons/arrow-down.svg | 3 + .../rocketchat-ui-master/public/icons/at.svg | 3 + .../public/icons/back.svg | 3 + .../rocketchat-ui-master/public/icons/ban.svg | 3 + .../public/icons/bell.svg | 3 + .../public/icons/bold.svg | 3 + .../public/icons/calendar.svg | 3 + .../public/icons/chat.svg | 3 + .../public/icons/check.svg | 3 + .../public/icons/checkmark-circled.svg | 3 + .../public/icons/circle-cross.svg | 3 + .../public/icons/circle.svg | 3 + .../public/icons/circled-arrow-down.svg | 3 + .../public/icons/clip.svg | 3 + .../public/icons/clipboard.svg | 3 + .../public/icons/clock.svg | 3 + .../public/icons/cloud-plus.svg | 3 + .../public/icons/code.svg | 3 + .../rocketchat-ui-master/public/icons/cog.svg | 3 + .../public/icons/computer.svg | 3 + .../public/icons/copy.svg | 3 + .../public/icons/cross.svg | 3 + .../public/icons/cube.svg | 3 + .../public/icons/customize.svg | 3 + .../public/icons/discover.svg | 3 + .../public/icons/edit-rounded.svg | 3 + .../public/icons/edit.svg | 3 + .../public/icons/emoji.svg | 3 + .../public/icons/eraser.svg | 3 + .../public/icons/eye-off.svg | 3 + .../public/icons/facebook.svg | 3 + .../public/icons/file-document.svg | 3 + .../public/icons/file-generic.svg | 3 + .../public/icons/file-pdf.svg | 3 + .../public/icons/file-sheets.svg | 3 + .../public/icons/flag.svg | 3 + .../public/icons/folder.svg | 3 + .../public/icons/github.svg | 3 + .../public/icons/gitlab.svg | 3 + .../public/icons/google.svg | 3 + .../public/icons/hand-pointer.svg | 3 + .../public/icons/hashtag.svg | 3 + .../public/icons/help.svg | 3 + .../public/icons/hubot.svg | 3 + .../public/icons/import.svg | 3 + .../public/icons/info-circled.svg | 3 + .../public/icons/italic.svg | 3 + .../public/icons/jump.svg | 3 + .../rocketchat-ui-master/public/icons/key.svg | 3 + .../public/icons/keyboard.svg | 3 + .../public/icons/language.svg | 3 + .../public/icons/linkedin.svg | 3 + .../public/icons/list-alt.svg | 3 + .../public/icons/list.svg | 3 + .../public/icons/livechat.svg | 3 + .../public/icons/loading.svg | 3 + .../public/icons/lock.svg | 3 + .../public/icons/magnifier.svg | 3 + .../public/icons/mail.svg | 3 + .../public/icons/map-pin.svg | 3 + .../public/icons/menu.svg | 3 + .../public/icons/message.svg | 3 + .../rocketchat-ui-master/public/icons/mic.svg | 3 + .../public/icons/mobile.svg | 3 + .../public/icons/modal-warning.svg | 3 + .../public/icons/mute.svg | 3 + .../public/icons/pause.svg | 3 + .../public/icons/permalink.svg | 3 + .../rocketchat-ui-master/public/icons/pin.svg | 3 + .../public/icons/play-solid.svg | 3 + .../public/icons/play.svg | 3 + .../public/icons/plus.svg | 3 + .../public/icons/podcast.svg | 3 + .../public/icons/post.svg | 3 + .../public/icons/queue.svg | 3 + .../public/icons/quote.svg | 3 + .../public/icons/reload.svg | 3 + .../public/icons/reply.svg | 3 + .../public/icons/send.svg | 3 + .../public/icons/share.svg | 3 + .../public/icons/shield-alt.svg | 3 + .../public/icons/shield-check.svg | 3 + .../public/icons/shield.svg | 3 + .../public/icons/sign-out.svg | 3 + .../public/icons/sort-amount-down.svg | 3 + .../public/icons/sort-down.svg | 6 + .../public/icons/sort-up.svg | 6 + .../public/icons/sort.svg | 3 + .../public/icons/star.svg | 3 + .../public/icons/strike.svg | 3 + .../public/icons/team.svg | 3 + .../public/icons/th-list.svg | 3 + .../public/icons/trash.svg | 3 + .../public/icons/twitter.svg | 3 + .../public/icons/upload.svg | 3 + .../public/icons/user-plus.svg | 3 + .../public/icons/user-rounded.svg | 3 + .../public/icons/user.svg | 3 + .../public/icons/video.svg | 3 + .../public/icons/volume-mute.svg | 3 + .../public/icons/volume.svg | 3 + .../public/icons/warning-empty.svg | 3 + .../public/icons/warning.svg | 3 + .../client/messageBox.html | 2 +- .../client/messageBox.js | 2 +- .../client/sidebarHeader.js | 2 +- .../client/sidebarItem.html | 8 +- .../client/lib/MessageAction.js | 2 +- .../rocketchat-ui-utils/client/lib/modal.html | 2 +- .../rocketchat-ui-utils/client/lib/modal.js | 15 +- .../client/lib/popout.html | 2 +- .../client/webdavFilePicker.html | 2 +- .../client/webdavFilePicker.js | 2 +- .../client/imports/components/header.css | 13 +- .../client/imports/components/message-box.css | 2 +- .../client/imports/components/popout.css | 5 - .../components/sidebar/sidebar-header.css | 2 +- .../components/sidebar/sidebar-item.css | 11 +- .../client/imports/general/base.css | 7 +- tests/end-to-end/ui/11-admin.js | 1 + 151 files changed, 1375 insertions(+), 967 deletions(-) create mode 100644 packages/rocketchat-ui-master/public/README.md create mode 100644 packages/rocketchat-ui-master/public/generateHTML.js create mode 100644 packages/rocketchat-ui-master/public/generateSprite.js create mode 100644 packages/rocketchat-ui-master/public/icons.html create mode 100644 packages/rocketchat-ui-master/public/icons/Bell-off.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Download.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Eye.svg create mode 100644 packages/rocketchat-ui-master/public/icons/File-google-drive.svg create mode 100644 packages/rocketchat-ui-master/public/icons/File-keynote.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Files-audio.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Files-video.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Files-zip.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Multiline.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Send-active.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Star-filled.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Video-off.svg create mode 100644 packages/rocketchat-ui-master/public/icons/Volume-disable.svg create mode 100644 packages/rocketchat-ui-master/public/icons/add-reaction.svg create mode 100644 packages/rocketchat-ui-master/public/icons/arrow-down.svg create mode 100644 packages/rocketchat-ui-master/public/icons/at.svg create mode 100644 packages/rocketchat-ui-master/public/icons/back.svg create mode 100644 packages/rocketchat-ui-master/public/icons/ban.svg create mode 100644 packages/rocketchat-ui-master/public/icons/bell.svg create mode 100644 packages/rocketchat-ui-master/public/icons/bold.svg create mode 100644 packages/rocketchat-ui-master/public/icons/calendar.svg create mode 100644 packages/rocketchat-ui-master/public/icons/chat.svg create mode 100644 packages/rocketchat-ui-master/public/icons/check.svg create mode 100644 packages/rocketchat-ui-master/public/icons/checkmark-circled.svg create mode 100644 packages/rocketchat-ui-master/public/icons/circle-cross.svg create mode 100644 packages/rocketchat-ui-master/public/icons/circle.svg create mode 100644 packages/rocketchat-ui-master/public/icons/circled-arrow-down.svg create mode 100644 packages/rocketchat-ui-master/public/icons/clip.svg create mode 100644 packages/rocketchat-ui-master/public/icons/clipboard.svg create mode 100644 packages/rocketchat-ui-master/public/icons/clock.svg create mode 100644 packages/rocketchat-ui-master/public/icons/cloud-plus.svg create mode 100644 packages/rocketchat-ui-master/public/icons/code.svg create mode 100644 packages/rocketchat-ui-master/public/icons/cog.svg create mode 100644 packages/rocketchat-ui-master/public/icons/computer.svg create mode 100644 packages/rocketchat-ui-master/public/icons/copy.svg create mode 100644 packages/rocketchat-ui-master/public/icons/cross.svg create mode 100644 packages/rocketchat-ui-master/public/icons/cube.svg create mode 100644 packages/rocketchat-ui-master/public/icons/customize.svg create mode 100644 packages/rocketchat-ui-master/public/icons/discover.svg create mode 100644 packages/rocketchat-ui-master/public/icons/edit-rounded.svg create mode 100644 packages/rocketchat-ui-master/public/icons/edit.svg create mode 100644 packages/rocketchat-ui-master/public/icons/emoji.svg create mode 100644 packages/rocketchat-ui-master/public/icons/eraser.svg create mode 100644 packages/rocketchat-ui-master/public/icons/eye-off.svg create mode 100644 packages/rocketchat-ui-master/public/icons/facebook.svg create mode 100644 packages/rocketchat-ui-master/public/icons/file-document.svg create mode 100644 packages/rocketchat-ui-master/public/icons/file-generic.svg create mode 100644 packages/rocketchat-ui-master/public/icons/file-pdf.svg create mode 100644 packages/rocketchat-ui-master/public/icons/file-sheets.svg create mode 100644 packages/rocketchat-ui-master/public/icons/flag.svg create mode 100644 packages/rocketchat-ui-master/public/icons/folder.svg create mode 100644 packages/rocketchat-ui-master/public/icons/github.svg create mode 100644 packages/rocketchat-ui-master/public/icons/gitlab.svg create mode 100644 packages/rocketchat-ui-master/public/icons/google.svg create mode 100644 packages/rocketchat-ui-master/public/icons/hand-pointer.svg create mode 100644 packages/rocketchat-ui-master/public/icons/hashtag.svg create mode 100644 packages/rocketchat-ui-master/public/icons/help.svg create mode 100644 packages/rocketchat-ui-master/public/icons/hubot.svg create mode 100644 packages/rocketchat-ui-master/public/icons/import.svg create mode 100644 packages/rocketchat-ui-master/public/icons/info-circled.svg create mode 100644 packages/rocketchat-ui-master/public/icons/italic.svg create mode 100644 packages/rocketchat-ui-master/public/icons/jump.svg create mode 100644 packages/rocketchat-ui-master/public/icons/key.svg create mode 100644 packages/rocketchat-ui-master/public/icons/keyboard.svg create mode 100644 packages/rocketchat-ui-master/public/icons/language.svg create mode 100644 packages/rocketchat-ui-master/public/icons/linkedin.svg create mode 100644 packages/rocketchat-ui-master/public/icons/list-alt.svg create mode 100644 packages/rocketchat-ui-master/public/icons/list.svg create mode 100644 packages/rocketchat-ui-master/public/icons/livechat.svg create mode 100644 packages/rocketchat-ui-master/public/icons/loading.svg create mode 100644 packages/rocketchat-ui-master/public/icons/lock.svg create mode 100644 packages/rocketchat-ui-master/public/icons/magnifier.svg create mode 100644 packages/rocketchat-ui-master/public/icons/mail.svg create mode 100644 packages/rocketchat-ui-master/public/icons/map-pin.svg create mode 100644 packages/rocketchat-ui-master/public/icons/menu.svg create mode 100644 packages/rocketchat-ui-master/public/icons/message.svg create mode 100644 packages/rocketchat-ui-master/public/icons/mic.svg create mode 100644 packages/rocketchat-ui-master/public/icons/mobile.svg create mode 100644 packages/rocketchat-ui-master/public/icons/modal-warning.svg create mode 100644 packages/rocketchat-ui-master/public/icons/mute.svg create mode 100644 packages/rocketchat-ui-master/public/icons/pause.svg create mode 100644 packages/rocketchat-ui-master/public/icons/permalink.svg create mode 100644 packages/rocketchat-ui-master/public/icons/pin.svg create mode 100644 packages/rocketchat-ui-master/public/icons/play-solid.svg create mode 100644 packages/rocketchat-ui-master/public/icons/play.svg create mode 100644 packages/rocketchat-ui-master/public/icons/plus.svg create mode 100644 packages/rocketchat-ui-master/public/icons/podcast.svg create mode 100644 packages/rocketchat-ui-master/public/icons/post.svg create mode 100644 packages/rocketchat-ui-master/public/icons/queue.svg create mode 100644 packages/rocketchat-ui-master/public/icons/quote.svg create mode 100644 packages/rocketchat-ui-master/public/icons/reload.svg create mode 100644 packages/rocketchat-ui-master/public/icons/reply.svg create mode 100644 packages/rocketchat-ui-master/public/icons/send.svg create mode 100644 packages/rocketchat-ui-master/public/icons/share.svg create mode 100644 packages/rocketchat-ui-master/public/icons/shield-alt.svg create mode 100644 packages/rocketchat-ui-master/public/icons/shield-check.svg create mode 100644 packages/rocketchat-ui-master/public/icons/shield.svg create mode 100644 packages/rocketchat-ui-master/public/icons/sign-out.svg create mode 100644 packages/rocketchat-ui-master/public/icons/sort-amount-down.svg create mode 100644 packages/rocketchat-ui-master/public/icons/sort-down.svg create mode 100644 packages/rocketchat-ui-master/public/icons/sort-up.svg create mode 100644 packages/rocketchat-ui-master/public/icons/sort.svg create mode 100644 packages/rocketchat-ui-master/public/icons/star.svg create mode 100644 packages/rocketchat-ui-master/public/icons/strike.svg create mode 100644 packages/rocketchat-ui-master/public/icons/team.svg create mode 100644 packages/rocketchat-ui-master/public/icons/th-list.svg create mode 100644 packages/rocketchat-ui-master/public/icons/trash.svg create mode 100644 packages/rocketchat-ui-master/public/icons/twitter.svg create mode 100644 packages/rocketchat-ui-master/public/icons/upload.svg create mode 100644 packages/rocketchat-ui-master/public/icons/user-plus.svg create mode 100644 packages/rocketchat-ui-master/public/icons/user-rounded.svg create mode 100644 packages/rocketchat-ui-master/public/icons/user.svg create mode 100644 packages/rocketchat-ui-master/public/icons/video.svg create mode 100644 packages/rocketchat-ui-master/public/icons/volume-mute.svg create mode 100644 packages/rocketchat-ui-master/public/icons/volume.svg create mode 100644 packages/rocketchat-ui-master/public/icons/warning-empty.svg create mode 100644 packages/rocketchat-ui-master/public/icons/warning.svg diff --git a/packages/rocketchat-apps/assets/stylesheets/apps.css b/packages/rocketchat-apps/assets/stylesheets/apps.css index 17f433b782f6..eac6f67a75db 100644 --- a/packages/rocketchat-apps/assets/stylesheets/apps.css +++ b/packages/rocketchat-apps/assets/stylesheets/apps.css @@ -146,23 +146,20 @@ margin: 0 7px; } - .installer { - position: relative; - - overflow: hidden; - - width: 20px; - height: 20px; + &__wrap-actions { + & > .rc-icon--loading { + display: none; + } - &--marketplace-installer { - position: absolute; - top: 0; + &.loading { + & > .rc-icon--loading { + display: block; - width: 818px; - height: 20px; + animation: spin 1s linear infinite; + } - &.play { - animation: play90 steps(40) 4s forwards; + & > .apps-installer { + display: none; } } } diff --git a/packages/rocketchat-apps/client/admin/apps.html b/packages/rocketchat-apps/client/admin/apps.html index 284f8ec0c0ac..888c2a9c5c4c 100644 --- a/packages/rocketchat-apps/client/admin/apps.html +++ b/packages/rocketchat-apps/client/admin/apps.html @@ -66,14 +66,17 @@ {{/if}} - + {{#if $eq latest._installed true}} - {{> icon icon="app-installed"}} + {{> icon icon="checkmark-circled" block="rc-icon--default-size"}} {{/if}} {{#if renderDownloadButton latest}} - +
    + {{> icon block="rc-icon--default-size rc-icon" icon="loading"}} + +
    {{/if}} diff --git a/packages/rocketchat-apps/client/admin/apps.js b/packages/rocketchat-apps/client/admin/apps.js index a12d2a77db9d..c17f31b66947 100644 --- a/packages/rocketchat-apps/client/admin/apps.js +++ b/packages/rocketchat-apps/client/admin/apps.js @@ -253,14 +253,15 @@ Template.apps.events({ const url = `${ HOST }/v1/apps/${ this.latest.id }/download/${ this.latest.version }`; - RocketChat.API.post('apps/', { url }).then(() => { - getInstalledApps(template); - }).catch((e) => { - toastr.error((e.xhr.responseJSON && e.xhr.responseJSON.error) || e.message); - }); - // play animation - $(e.currentTarget).find('.rc-icon').addClass('play'); + e.currentTarget.parentElement.classList.add('loading'); + + RocketChat.API.post('apps/', { url }) + .then(() => { + getApps(template); + getInstalledApps(template); + }) + .catch((e) => toastr.error((e.xhr.responseJSON && e.xhr.responseJSON.error) || e.message)); }, 'keyup .js-search'(e, t) { t.searchText.set(e.currentTarget.value); diff --git a/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html index 9cdfbe698a9b..87c3faab6719 100644 --- a/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html +++ b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html @@ -3,7 +3,7 @@ {{#if selectedMessages}}
    - {{> icon block="mail-messages__instructions-icon" icon="modal-success"}} + {{> icon block="mail-messages__instructions-icon rc-icon--default-size" icon="checkmark-circled"}}
    {{selectedMessages.length}} Messages selected Click here to clear the selection @@ -13,7 +13,7 @@ {{else}}
    - {{> icon block="mail-messages__instructions-icon" icon="hand-pointer"}} + {{> icon block="mail-messages__instructions-icon rc-icon--default-size" icon="hand-pointer"}}
    {{_ "Click_the_messages_you_would_like_to_send_by_email"}}
    @@ -25,7 +25,7 @@
    {{_ "To_users"}}
    - {{> icon block="rc-input__icon-svg" icon="at"}} + {{> icon block="rc-input__icon-svg rc-icon--default-size" icon="at"}}
    {{#each user in selectedUsers}} @@ -48,7 +48,7 @@
    {{_ "To_additional_emails"}}
    - {{> icon block="rc-input__icon-svg" icon="mail"}} + {{> icon block="rc-input__icon-svg rc-icon--default-size" icon="mail"}}
    {{#each selectedEmails}} @@ -64,7 +64,7 @@
    {{_ "Subject"}}
    - {{> icon block="rc-input__icon-svg" icon="edit"}} + {{> icon block="rc-input__icon-svg rc-icon--default-size" icon="edit"}}
    diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.html b/packages/rocketchat-message-attachments/client/messageAttachment.html index 99a8a5633055..d42a913efb51 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.html +++ b/packages/rocketchat-message-attachments/client/messageAttachment.html @@ -52,7 +52,7 @@ {{#if title_link}} {{#if isFile}} {{_ "Attachment_File_Uploaded"}}: {{/if}}{{title}} {{#if title_link_download}} - + {{> icon icon="download"}} {{/if}} {{else}} {{title}} diff --git a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css index 976f9c6756f2..8ae0918f2f4e 100644 --- a/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css +++ b/packages/rocketchat-message-attachments/client/stylesheets/messageAttachments.css @@ -65,13 +65,12 @@ html.rtl .attachment { } & .attachment-title { + + color: #13679a; + font-size: 1.02rem; font-weight: 500; line-height: 1.5rem; - - & > a { - font-weight: 500; - } } & .attachment-text { @@ -138,11 +137,7 @@ html.rtl .attachment { } & .attachment-download-icon { - margin-left: 5px; - padding: 2px 5px; - - border-width: 1px; - border-radius: 5px; + padding: 0 5px; } & .attachment-canvas { diff --git a/packages/rocketchat-retention-policy/server/cronPruneMessages.js b/packages/rocketchat-retention-policy/server/cronPruneMessages.js index e570806e7b8d..4452d866ee9f 100644 --- a/packages/rocketchat-retention-policy/server/cronPruneMessages.js +++ b/packages/rocketchat-retention-policy/server/cronPruneMessages.js @@ -27,10 +27,13 @@ function job() { RocketChat.models.Rooms.find({ t: type, - _updatedAt: { $gte: lastPrune }, - $or: [{ 'retention.enabled': { $eq: true } }, { 'retention.enabled': { $exists: false } }], + _updatedAt: { $gte: latest }, + $or: [ + { 'retention.enabled': { $eq: true } }, + { 'retention.enabled': { $exists: false } }, + ], 'retention.overrideGlobal': { $ne: true }, - }).forEach(({ _id: rid }) => { + }, { fields : { _id: 1 } }).forEach(({ _id: rid }) => { RocketChat.cleanRoomHistory({ rid, latest, oldest, filesOnly, excludePinned }); }); }); diff --git a/packages/rocketchat-ui-account/client/accountProfile.html b/packages/rocketchat-ui-account/client/accountProfile.html index a60e73e9df44..c3e3e5a40a57 100644 --- a/packages/rocketchat-ui-account/client/accountProfile.html +++ b/packages/rocketchat-ui-account/client/accountProfile.html @@ -118,7 +118,7 @@ {{#unless emailVerified}}
    - {{> icon block="rc-input__icon-svg" icon="cross-circled"}} + {{> icon block="rc-input__icon-svg" icon="circle-cross"}}
    {{else}}
    diff --git a/packages/rocketchat-ui-clean-history/client/lib/startup.js b/packages/rocketchat-ui-clean-history/client/lib/startup.js index 3c7321c387bd..f8e992ff5d87 100644 --- a/packages/rocketchat-ui-clean-history/client/lib/startup.js +++ b/packages/rocketchat-ui-clean-history/client/lib/startup.js @@ -8,7 +8,7 @@ Meteor.startup(() => { id: 'clean-history', anonymous: true, i18nTitle: 'Prune_Messages', - icon: 'trash', + icon: 'eraser', template: 'cleanHistory', order: 250, condition: () => RocketChat.authz.hasAllPermission('clean-channel-history', Session.get('openedRoom')), diff --git a/packages/rocketchat-ui-clean-history/client/views/cleanHistory.html b/packages/rocketchat-ui-clean-history/client/views/cleanHistory.html index b9063705f88b..9d9ea3099e5c 100644 --- a/packages/rocketchat-ui-clean-history/client/views/cleanHistory.html +++ b/packages/rocketchat-ui-clean-history/client/views/cleanHistory.html @@ -107,7 +107,7 @@ {{/unless}}

    - {{> icon icon="loading-thin"}} + {{> icon icon="loading" block="rc-icon"}}
    {{prunedCount}}
    @@ -124,7 +124,7 @@

    {{_ "Prune_finished"}}

    - {{> icon icon="loading-thin-done"}} + {{> icon icon="check" block="rc-icon"}}
    {{prunedCount}}
    diff --git a/packages/rocketchat-ui-clean-history/client/views/stylesheets/cleanHistory.css b/packages/rocketchat-ui-clean-history/client/views/stylesheets/cleanHistory.css index 2c40385b96bb..57048d469b90 100644 --- a/packages/rocketchat-ui-clean-history/client/views/stylesheets/cleanHistory.css +++ b/packages/rocketchat-ui-clean-history/client/views/stylesheets/cleanHistory.css @@ -33,12 +33,16 @@ color: #12c212; } - & svg { + & .rc-icon--loading { width: 16rem; height: 16rem; margin: 1rem 0; - animation: pruningSpin 2s linear infinite; + animation: spin 2s linear infinite; + } + + & .rc-icon--check { + font-size: 1rem; } & .pruning__text { @@ -53,13 +57,3 @@ margin-top: calc(-8rem + 1.5em); } } - -@keyframes pruningSpin { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} diff --git a/packages/rocketchat-ui-flextab/client/tabs/uploadedFilesList.js b/packages/rocketchat-ui-flextab/client/tabs/uploadedFilesList.js index 811c4115612b..eb8bf714816c 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/uploadedFilesList.js +++ b/packages/rocketchat-ui-flextab/client/tabs/uploadedFilesList.js @@ -87,7 +87,7 @@ Template.uploadedFilesList.helpers({ } return { - id: 'file-generic', + id: 'clip', type: 'generic', extension, }; @@ -132,7 +132,7 @@ Template.uploadedFilesList.events({ { items: [ { - icon: 'import', + icon: 'download', name: t('Download'), action: () => { const a = document.createElement('a'); diff --git a/packages/rocketchat-ui-master/public/README.md b/packages/rocketchat-ui-master/public/README.md new file mode 100644 index 000000000000..fdb77b13ca3a --- /dev/null +++ b/packages/rocketchat-ui-master/public/README.md @@ -0,0 +1,13 @@ +HOW INSERT A NEW ICON + +paste your icon on `./icons` folder + +run + +```` +node generateSprite.js +node generateHTML.js + +git add icon.html icons icons.svg +.... +``` diff --git a/packages/rocketchat-ui-master/public/generateHTML.js b/packages/rocketchat-ui-master/public/generateHTML.js new file mode 100644 index 000000000000..e240a336a6e3 --- /dev/null +++ b/packages/rocketchat-ui-master/public/generateHTML.js @@ -0,0 +1,79 @@ +const fs = require('fs'); + +const sort = function(a, b) { + if (a.toLocaleLowerCase() < b.toLocaleLowerCase()) { + return -1; + } + if (a.toLocaleLowerCase() > b.toLocaleLowerCase()) { + return 1; + } + return 0; +}; + +const iconHTML = (name) => `
    `; + +const header = `
    `; + +const start = async() => { + const html = fs.createWriteStream('icons.html', { flags: 'w' }); + html.write(header); + try { + await new Promise((resolve) => { + fs.readdir('./icons', async(err, files) => { + files + .sort(sort) + .filter((file) => { + if (!/\.svg/.test(file)) { + console.log(`invalid extension ${ file }`); + return false; + } + return true; + }).forEach(async(file) => { + const name = file.replace('.svg', '').toLocaleLowerCase(); + console.log(name); + html.write(iconHTML(name)); + }); + resolve(); + }); + + }); + + } catch (error) { + console.error(error); + } finally { + html.write(fs.readFileSync('./icons.svg')); + html.write('
    '); + html.end(); + } +}; + +try { + start(); + +} catch (error) { + console.error(error); +} diff --git a/packages/rocketchat-ui-master/public/generateSprite.js b/packages/rocketchat-ui-master/public/generateSprite.js new file mode 100644 index 000000000000..b0ff105e2a6d --- /dev/null +++ b/packages/rocketchat-ui-master/public/generateSprite.js @@ -0,0 +1,87 @@ +const fs = require('fs'); + +const { parseString, Builder } = require('xml2js'); +// const jp = require('@f5io/jsonpath').default; + +// const path = '$..path'; + +const builder = new Builder({ + headless: true, +}); + +const parse = (str) => new Promise((resolve, reject) => parseString(str, (err, json) => (err ? reject(err) : resolve(json)))); + +const sort = function(a, b) { + if (a.toLocaleLowerCase() < b.toLocaleLowerCase()) { + return -1; + } + if (a.toLocaleLowerCase() > b.toLocaleLowerCase()) { + return 1; + } + return 0; +}; + +const toSymbol = (id, viewBox, { id: _, ...args }) => ({ + symbol: { + $: { + id: `icon-${ id }`, + viewBox, + fill: 'currentColor', + }, + ...args, + }, +}); + +const xml = ''; +const start = async() => { + const stream = fs.createWriteStream('icons.svg', { flags: 'w' }); + stream.write(`${ xml }\n`); + try { + await new Promise((resolve) => { + const path = './icons'; + fs.readdir(path, async(err, files) => { + const promises = files + .sort(sort) + .filter((file) => { + if (!/\.svg/.test(file)) { + console.log(`invalid extension ${ file }`); + return false; + } + return true; + }).map(async(file) => { + const name = file.replace('.svg', '').toLocaleLowerCase(); + try { + const content = fs.readFileSync(`${ path }/${ file }`, 'utf8'); + const { + svg, + } = await parse(content); + + const { $, ...args } = svg; + const { + viewBox, + } = $; + stream.write(`${ builder.buildObject(toSymbol(name, viewBox, args)) }\n`); + } catch (error) { + console.log(error); + return Promise.resolve(error); + } + }); + + resolve(Promise.all(promises)); + }); + + }); + + } catch (error) { + console.error(error); + } finally { + stream.write(''); + stream.end(); + } +}; + +try { + start(); +} catch (error) { + console.error(error); +} diff --git a/packages/rocketchat-ui-master/public/icons.html b/packages/rocketchat-ui-master/public/icons.html new file mode 100644 index 000000000000..1bc440297b02 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons.html @@ -0,0 +1,383 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons.svg b/packages/rocketchat-ui-master/public/icons.svg index d6360bedf2d6..315d4199dda2 100644 --- a/packages/rocketchat-ui-master/public/icons.svg +++ b/packages/rocketchat-ui-master/public/icons.svg @@ -1,869 +1,359 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Bell-off.svg b/packages/rocketchat-ui-master/public/icons/Bell-off.svg new file mode 100644 index 000000000000..b4e718748d86 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Bell-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Download.svg b/packages/rocketchat-ui-master/public/icons/Download.svg new file mode 100644 index 000000000000..79340b33ba3d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Download.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Eye.svg b/packages/rocketchat-ui-master/public/icons/Eye.svg new file mode 100644 index 000000000000..2eb56b51cc7a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Eye.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/File-google-drive.svg b/packages/rocketchat-ui-master/public/icons/File-google-drive.svg new file mode 100644 index 000000000000..783f32396b71 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/File-google-drive.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/File-keynote.svg b/packages/rocketchat-ui-master/public/icons/File-keynote.svg new file mode 100644 index 000000000000..a3480710c1f0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/File-keynote.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Files-audio.svg b/packages/rocketchat-ui-master/public/icons/Files-audio.svg new file mode 100644 index 000000000000..b02c7fbc7a6c --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Files-audio.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Files-video.svg b/packages/rocketchat-ui-master/public/icons/Files-video.svg new file mode 100644 index 000000000000..6efd3129871f --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Files-video.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Files-zip.svg b/packages/rocketchat-ui-master/public/icons/Files-zip.svg new file mode 100644 index 000000000000..158ed2552afc --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Files-zip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Multiline.svg b/packages/rocketchat-ui-master/public/icons/Multiline.svg new file mode 100644 index 000000000000..c050900bb159 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Multiline.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Send-active.svg b/packages/rocketchat-ui-master/public/icons/Send-active.svg new file mode 100644 index 000000000000..3dd2213f6d6d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Send-active.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Star-filled.svg b/packages/rocketchat-ui-master/public/icons/Star-filled.svg new file mode 100644 index 000000000000..eb0712d30d49 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Star-filled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Video-off.svg b/packages/rocketchat-ui-master/public/icons/Video-off.svg new file mode 100644 index 000000000000..f817b059b191 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Video-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/Volume-disable.svg b/packages/rocketchat-ui-master/public/icons/Volume-disable.svg new file mode 100644 index 000000000000..be0f1a95f795 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/Volume-disable.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/add-reaction.svg b/packages/rocketchat-ui-master/public/icons/add-reaction.svg new file mode 100644 index 000000000000..11cb0c273edb --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/add-reaction.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/arrow-down.svg b/packages/rocketchat-ui-master/public/icons/arrow-down.svg new file mode 100644 index 000000000000..c3a9a59d1301 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/arrow-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/at.svg b/packages/rocketchat-ui-master/public/icons/at.svg new file mode 100644 index 000000000000..7c72a75c209f --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/at.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/back.svg b/packages/rocketchat-ui-master/public/icons/back.svg new file mode 100644 index 000000000000..62042b6101b0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/back.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/ban.svg b/packages/rocketchat-ui-master/public/icons/ban.svg new file mode 100644 index 000000000000..24bce313923c --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/ban.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/bell.svg b/packages/rocketchat-ui-master/public/icons/bell.svg new file mode 100644 index 000000000000..d836e1bb8e05 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/bell.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/bold.svg b/packages/rocketchat-ui-master/public/icons/bold.svg new file mode 100644 index 000000000000..1f9aa2e5e063 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/bold.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/calendar.svg b/packages/rocketchat-ui-master/public/icons/calendar.svg new file mode 100644 index 000000000000..19f43e02f96a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/calendar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/chat.svg b/packages/rocketchat-ui-master/public/icons/chat.svg new file mode 100644 index 000000000000..70a8e22aaba8 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/chat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/check.svg b/packages/rocketchat-ui-master/public/icons/check.svg new file mode 100644 index 000000000000..a52819c469cf --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/checkmark-circled.svg b/packages/rocketchat-ui-master/public/icons/checkmark-circled.svg new file mode 100644 index 000000000000..71beee049cf7 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/checkmark-circled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/circle-cross.svg b/packages/rocketchat-ui-master/public/icons/circle-cross.svg new file mode 100644 index 000000000000..88b70f711309 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/circle-cross.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/circle.svg b/packages/rocketchat-ui-master/public/icons/circle.svg new file mode 100644 index 000000000000..86e19fd2406b --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/circled-arrow-down.svg b/packages/rocketchat-ui-master/public/icons/circled-arrow-down.svg new file mode 100644 index 000000000000..e4c611e5c577 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/circled-arrow-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/clip.svg b/packages/rocketchat-ui-master/public/icons/clip.svg new file mode 100644 index 000000000000..2203226e13bb --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/clip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/clipboard.svg b/packages/rocketchat-ui-master/public/icons/clipboard.svg new file mode 100644 index 000000000000..65848e463435 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/clipboard.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/clock.svg b/packages/rocketchat-ui-master/public/icons/clock.svg new file mode 100644 index 000000000000..4559417260aa --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/clock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/cloud-plus.svg b/packages/rocketchat-ui-master/public/icons/cloud-plus.svg new file mode 100644 index 000000000000..16d7447f32a7 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/cloud-plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/code.svg b/packages/rocketchat-ui-master/public/icons/code.svg new file mode 100644 index 000000000000..8185a1f713d0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/code.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/cog.svg b/packages/rocketchat-ui-master/public/icons/cog.svg new file mode 100644 index 000000000000..56077284bb39 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/cog.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/computer.svg b/packages/rocketchat-ui-master/public/icons/computer.svg new file mode 100644 index 000000000000..7231deaa37a2 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/computer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/copy.svg b/packages/rocketchat-ui-master/public/icons/copy.svg new file mode 100644 index 000000000000..2d9e5bcb31dd --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/copy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/cross.svg b/packages/rocketchat-ui-master/public/icons/cross.svg new file mode 100644 index 000000000000..f2f697692a30 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/cross.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/cube.svg b/packages/rocketchat-ui-master/public/icons/cube.svg new file mode 100644 index 000000000000..af4acb99d691 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/cube.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/customize.svg b/packages/rocketchat-ui-master/public/icons/customize.svg new file mode 100644 index 000000000000..4aff99dec644 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/customize.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/discover.svg b/packages/rocketchat-ui-master/public/icons/discover.svg new file mode 100644 index 000000000000..624b3d12174a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/discover.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/edit-rounded.svg b/packages/rocketchat-ui-master/public/icons/edit-rounded.svg new file mode 100644 index 000000000000..3566d64760ca --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/edit-rounded.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/edit.svg b/packages/rocketchat-ui-master/public/icons/edit.svg new file mode 100644 index 000000000000..4cd56a7d22c8 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/edit.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/emoji.svg b/packages/rocketchat-ui-master/public/icons/emoji.svg new file mode 100644 index 000000000000..d633ffd49d7e --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/emoji.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/eraser.svg b/packages/rocketchat-ui-master/public/icons/eraser.svg new file mode 100644 index 000000000000..39dec447c82a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/eraser.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/rocketchat-ui-master/public/icons/eye-off.svg b/packages/rocketchat-ui-master/public/icons/eye-off.svg new file mode 100644 index 000000000000..cba266a69d3d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/eye-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/facebook.svg b/packages/rocketchat-ui-master/public/icons/facebook.svg new file mode 100644 index 000000000000..ba6c84896cd6 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/facebook.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/file-document.svg b/packages/rocketchat-ui-master/public/icons/file-document.svg new file mode 100644 index 000000000000..1cd874d96d3f --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/file-document.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/file-generic.svg b/packages/rocketchat-ui-master/public/icons/file-generic.svg new file mode 100644 index 000000000000..461098931210 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/file-generic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/file-pdf.svg b/packages/rocketchat-ui-master/public/icons/file-pdf.svg new file mode 100644 index 000000000000..2dde0f68e3ae --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/file-pdf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/file-sheets.svg b/packages/rocketchat-ui-master/public/icons/file-sheets.svg new file mode 100644 index 000000000000..289a7016e483 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/file-sheets.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/flag.svg b/packages/rocketchat-ui-master/public/icons/flag.svg new file mode 100644 index 000000000000..e870e994424a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/flag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/folder.svg b/packages/rocketchat-ui-master/public/icons/folder.svg new file mode 100644 index 000000000000..429935427058 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/folder.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/github.svg b/packages/rocketchat-ui-master/public/icons/github.svg new file mode 100644 index 000000000000..5884aae89917 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/github.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/gitlab.svg b/packages/rocketchat-ui-master/public/icons/gitlab.svg new file mode 100644 index 000000000000..ac4a93744287 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/gitlab.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/google.svg b/packages/rocketchat-ui-master/public/icons/google.svg new file mode 100644 index 000000000000..efcfb74cae00 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/google.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/hand-pointer.svg b/packages/rocketchat-ui-master/public/icons/hand-pointer.svg new file mode 100644 index 000000000000..c61b14532928 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/hand-pointer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/hashtag.svg b/packages/rocketchat-ui-master/public/icons/hashtag.svg new file mode 100644 index 000000000000..c11f92157309 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/hashtag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/help.svg b/packages/rocketchat-ui-master/public/icons/help.svg new file mode 100644 index 000000000000..656dd07b1d1d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/help.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/hubot.svg b/packages/rocketchat-ui-master/public/icons/hubot.svg new file mode 100644 index 000000000000..dda813a4f76c --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/hubot.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/import.svg b/packages/rocketchat-ui-master/public/icons/import.svg new file mode 100644 index 000000000000..a88b65a9e4b5 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/import.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/info-circled.svg b/packages/rocketchat-ui-master/public/icons/info-circled.svg new file mode 100644 index 000000000000..4b077546076d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/info-circled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/italic.svg b/packages/rocketchat-ui-master/public/icons/italic.svg new file mode 100644 index 000000000000..66422596ee62 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/italic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/jump.svg b/packages/rocketchat-ui-master/public/icons/jump.svg new file mode 100644 index 000000000000..8e22b800dfc8 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/jump.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/key.svg b/packages/rocketchat-ui-master/public/icons/key.svg new file mode 100644 index 000000000000..062d9be7039a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/key.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/keyboard.svg b/packages/rocketchat-ui-master/public/icons/keyboard.svg new file mode 100644 index 000000000000..b59d8c7f2d75 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/keyboard.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/language.svg b/packages/rocketchat-ui-master/public/icons/language.svg new file mode 100644 index 000000000000..7721614724d1 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/language.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/linkedin.svg b/packages/rocketchat-ui-master/public/icons/linkedin.svg new file mode 100644 index 000000000000..3c58c9ad3c7b --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/linkedin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/list-alt.svg b/packages/rocketchat-ui-master/public/icons/list-alt.svg new file mode 100644 index 000000000000..c339810f295e --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/list-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/list.svg b/packages/rocketchat-ui-master/public/icons/list.svg new file mode 100644 index 000000000000..c48cf9b406d9 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/livechat.svg b/packages/rocketchat-ui-master/public/icons/livechat.svg new file mode 100644 index 000000000000..18be34ba8acd --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/livechat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/loading.svg b/packages/rocketchat-ui-master/public/icons/loading.svg new file mode 100644 index 000000000000..d8e437feb84a --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/loading.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/lock.svg b/packages/rocketchat-ui-master/public/icons/lock.svg new file mode 100644 index 000000000000..c5f48cbea9cd --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/magnifier.svg b/packages/rocketchat-ui-master/public/icons/magnifier.svg new file mode 100644 index 000000000000..25f117cec7bb --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/magnifier.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/mail.svg b/packages/rocketchat-ui-master/public/icons/mail.svg new file mode 100644 index 000000000000..c1cf818ddeba --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/mail.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/map-pin.svg b/packages/rocketchat-ui-master/public/icons/map-pin.svg new file mode 100644 index 000000000000..6e00e18d65fa --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/map-pin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/menu.svg b/packages/rocketchat-ui-master/public/icons/menu.svg new file mode 100644 index 000000000000..30cfcb0ab331 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/menu.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/message.svg b/packages/rocketchat-ui-master/public/icons/message.svg new file mode 100644 index 000000000000..8fcaf11fd411 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/message.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/mic.svg b/packages/rocketchat-ui-master/public/icons/mic.svg new file mode 100644 index 000000000000..6edf3c83e08e --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/mic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/mobile.svg b/packages/rocketchat-ui-master/public/icons/mobile.svg new file mode 100644 index 000000000000..e00b60cff980 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/mobile.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/modal-warning.svg b/packages/rocketchat-ui-master/public/icons/modal-warning.svg new file mode 100644 index 000000000000..15c2f6f6cff8 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/modal-warning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/mute.svg b/packages/rocketchat-ui-master/public/icons/mute.svg new file mode 100644 index 000000000000..290fb4e34c11 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/mute.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/pause.svg b/packages/rocketchat-ui-master/public/icons/pause.svg new file mode 100644 index 000000000000..c3836077cb2b --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/pause.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/permalink.svg b/packages/rocketchat-ui-master/public/icons/permalink.svg new file mode 100644 index 000000000000..b85198472686 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/permalink.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/pin.svg b/packages/rocketchat-ui-master/public/icons/pin.svg new file mode 100644 index 000000000000..670961ab89d0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/pin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/play-solid.svg b/packages/rocketchat-ui-master/public/icons/play-solid.svg new file mode 100644 index 000000000000..fe7acc3df6b0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/play-solid.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/play.svg b/packages/rocketchat-ui-master/public/icons/play.svg new file mode 100644 index 000000000000..43d74b70c2dd --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/plus.svg b/packages/rocketchat-ui-master/public/icons/plus.svg new file mode 100644 index 000000000000..a9244b228b06 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/podcast.svg b/packages/rocketchat-ui-master/public/icons/podcast.svg new file mode 100644 index 000000000000..20e16a7bf060 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/podcast.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/post.svg b/packages/rocketchat-ui-master/public/icons/post.svg new file mode 100644 index 000000000000..5c3b87bf2337 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/post.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/queue.svg b/packages/rocketchat-ui-master/public/icons/queue.svg new file mode 100644 index 000000000000..82d586b7faa2 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/queue.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/quote.svg b/packages/rocketchat-ui-master/public/icons/quote.svg new file mode 100644 index 000000000000..787a3dd97ab8 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/quote.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/reload.svg b/packages/rocketchat-ui-master/public/icons/reload.svg new file mode 100644 index 000000000000..af1f8724277c --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/reload.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/reply.svg b/packages/rocketchat-ui-master/public/icons/reply.svg new file mode 100644 index 000000000000..54b67f92ebf0 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/reply.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/send.svg b/packages/rocketchat-ui-master/public/icons/send.svg new file mode 100644 index 000000000000..e7bac9a20777 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/send.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/share.svg b/packages/rocketchat-ui-master/public/icons/share.svg new file mode 100644 index 000000000000..410ba11b6e76 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/share.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/shield-alt.svg b/packages/rocketchat-ui-master/public/icons/shield-alt.svg new file mode 100644 index 000000000000..78ae1fd500b5 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/shield-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/shield-check.svg b/packages/rocketchat-ui-master/public/icons/shield-check.svg new file mode 100644 index 000000000000..c3496e724d1d --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/shield-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/shield.svg b/packages/rocketchat-ui-master/public/icons/shield.svg new file mode 100644 index 000000000000..43840adae9ff --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/shield.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/sign-out.svg b/packages/rocketchat-ui-master/public/icons/sign-out.svg new file mode 100644 index 000000000000..7baaa8c89e06 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/sign-out.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/sort-amount-down.svg b/packages/rocketchat-ui-master/public/icons/sort-amount-down.svg new file mode 100644 index 000000000000..d15d02542ced --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/sort-amount-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/sort-down.svg b/packages/rocketchat-ui-master/public/icons/sort-down.svg new file mode 100644 index 000000000000..863ee81933b9 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/sort-down.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/sort-up.svg b/packages/rocketchat-ui-master/public/icons/sort-up.svg new file mode 100644 index 000000000000..d44b304480c2 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/sort-up.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/rocketchat-ui-master/public/icons/sort.svg b/packages/rocketchat-ui-master/public/icons/sort.svg new file mode 100644 index 000000000000..6523d000df34 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/sort.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/star.svg b/packages/rocketchat-ui-master/public/icons/star.svg new file mode 100644 index 000000000000..7765b56897ca --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/star.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/strike.svg b/packages/rocketchat-ui-master/public/icons/strike.svg new file mode 100644 index 000000000000..844764ab4590 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/strike.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/team.svg b/packages/rocketchat-ui-master/public/icons/team.svg new file mode 100644 index 000000000000..117baef64a51 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/team.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/th-list.svg b/packages/rocketchat-ui-master/public/icons/th-list.svg new file mode 100644 index 000000000000..3497ac19855b --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/th-list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/trash.svg b/packages/rocketchat-ui-master/public/icons/trash.svg new file mode 100644 index 000000000000..1875b2f93d2c --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/trash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/twitter.svg b/packages/rocketchat-ui-master/public/icons/twitter.svg new file mode 100644 index 000000000000..2046fc6e7a52 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/twitter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/upload.svg b/packages/rocketchat-ui-master/public/icons/upload.svg new file mode 100644 index 000000000000..6919fa95568b --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/upload.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/user-plus.svg b/packages/rocketchat-ui-master/public/icons/user-plus.svg new file mode 100644 index 000000000000..92528c14344f --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/user-plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/user-rounded.svg b/packages/rocketchat-ui-master/public/icons/user-rounded.svg new file mode 100644 index 000000000000..5509d859cf84 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/user-rounded.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/user.svg b/packages/rocketchat-ui-master/public/icons/user.svg new file mode 100644 index 000000000000..db8639df3425 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/user.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/video.svg b/packages/rocketchat-ui-master/public/icons/video.svg new file mode 100644 index 000000000000..5bcb66e9b8c4 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/video.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/volume-mute.svg b/packages/rocketchat-ui-master/public/icons/volume-mute.svg new file mode 100644 index 000000000000..52d8ac1efa19 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/volume-mute.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/volume.svg b/packages/rocketchat-ui-master/public/icons/volume.svg new file mode 100644 index 000000000000..2ed8b943c43f --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/volume.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/warning-empty.svg b/packages/rocketchat-ui-master/public/icons/warning-empty.svg new file mode 100644 index 000000000000..6d8a20132320 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/warning-empty.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-master/public/icons/warning.svg b/packages/rocketchat-ui-master/public/icons/warning.svg new file mode 100644 index 000000000000..49b2573fae00 --- /dev/null +++ b/packages/rocketchat-ui-master/public/icons/warning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/rocketchat-ui-message/client/messageBox.html b/packages/rocketchat-ui-message/client/messageBox.html index e0e0dcc340f3..44dcbbbe9e22 100644 --- a/packages/rocketchat-ui-message/client/messageBox.html +++ b/packages/rocketchat-ui-message/client/messageBox.html @@ -117,7 +117,7 @@ 00:00
    - {{> icon block="rc-input__icon-svg" icon="circle-check"}} + {{> icon block="rc-input__icon-svg" icon="checkmark-circled"}}
    {{> icon block="rc-input__icon-svg" icon="mic"}} diff --git a/packages/rocketchat-ui-message/client/messageBox.js b/packages/rocketchat-ui-message/client/messageBox.js index a8d80f1b01fc..30ee6dcde2fb 100644 --- a/packages/rocketchat-ui-message/client/messageBox.js +++ b/packages/rocketchat-ui-message/client/messageBox.js @@ -127,7 +127,7 @@ const markdownButtons = [ }, { label: 'multi_line', - icon: 'multi-line', + icon: 'multiline', pattern: '```\n{{text}}\n``` ', condition: () => RocketChat.Markdown && RocketChat.settings.get('Markdown_Parser') !== 'disabled', }, diff --git a/packages/rocketchat-ui-sidenav/client/sidebarHeader.js b/packages/rocketchat-ui-sidenav/client/sidebarHeader.js index f1b4e521ebec..609238cbf3fb 100644 --- a/packages/rocketchat-ui-sidenav/client/sidebarHeader.js +++ b/packages/rocketchat-ui-sidenav/client/sidebarHeader.js @@ -77,7 +77,7 @@ const toolbarButtons = (user) => [{ }, { name: t('Directory'), - icon: 'globe', + icon: 'discover', action: () => { menu.close(); FlowRouter.go('directory'); diff --git a/packages/rocketchat-ui-sidenav/client/sidebarItem.html b/packages/rocketchat-ui-sidenav/client/sidebarItem.html index 7c4203ac24c7..5a4e841fad4b 100644 --- a/packages/rocketchat-ui-sidenav/client/sidebarItem.html +++ b/packages/rocketchat-ui-sidenav/client/sidebarItem.html @@ -1,10 +1,10 @@ @@ -17,7 +17,7 @@ {{#if darken}} {{#if icon}}
    - {{> icon block="sidebar-item__icon" icon=icon}} + {{> icon block="sidebar-item__icon rc-icon--default-size" icon=icon}}
    {{/if}} {{else}} @@ -65,7 +65,7 @@ {{#if isRoom}} {{/if}}
    diff --git a/packages/rocketchat-ui-utils/client/lib/MessageAction.js b/packages/rocketchat-ui-utils/client/lib/MessageAction.js index f004e751ded3..27723cbaa33e 100644 --- a/packages/rocketchat-ui-utils/client/lib/MessageAction.js +++ b/packages/rocketchat-ui-utils/client/lib/MessageAction.js @@ -136,7 +136,7 @@ Meteor.startup(async function() { const { chatMessages } = await import('meteor/rocketchat:ui'); MessageAction.addButton({ id: 'reply-message', - icon: 'message', + icon: 'reply', label: 'Reply', context: ['message', 'message-mobile'], action() { diff --git a/packages/rocketchat-ui-utils/client/lib/modal.html b/packages/rocketchat-ui-utils/client/lib/modal.html index aaff574bf54b..5abcd2ac5d81 100644 --- a/packages/rocketchat-ui-utils/client/lib/modal.html +++ b/packages/rocketchat-ui-utils/client/lib/modal.html @@ -22,7 +22,7 @@

    {{> Template.dynamic template=content data=data}} {{/if}} {{#if type}} - {{> icon block="rc-modal__content-icon" icon=modalIcon}} + {{> icon block=type icon=modalIcon}} {{/if}} {{#if text}}
    diff --git a/packages/rocketchat-ui-utils/client/lib/modal.js b/packages/rocketchat-ui-utils/client/lib/modal.js index 1046165094f2..897316a2e1b9 100644 --- a/packages/rocketchat-ui-utils/client/lib/modal.js +++ b/packages/rocketchat-ui-utils/client/lib/modal.js @@ -96,8 +96,21 @@ Template.rc_modal.helpers({ hasAction() { return !!this.action; }, + type() { + return `rc-modal__content-icon rc-modal__content-icon--modal-${ this.type }`; + + }, modalIcon() { - return `modal-${ this.type }`; + switch (this.type) { + case 'success': + return 'checkmark-circled'; + case 'error': + return 'circle-cross'; + case 'info': + return 'info-circled'; + default: + return `modal-${ this.type }`; + } }, }); diff --git a/packages/rocketchat-ui-utils/client/lib/popout.html b/packages/rocketchat-ui-utils/client/lib/popout.html index 4d2691f965c4..2424ae45ffff 100644 --- a/packages/rocketchat-ui-utils/client/lib/popout.html +++ b/packages/rocketchat-ui-utils/client/lib/popout.html @@ -25,7 +25,7 @@

    {{> icon icon="podcast"}}

    {{/if}} {{#unless isAudioOnly}} {{/unless}} - {{/if}} -
    + {{#table fixed='true' onItemClick=onTableItemClick onScroll=onTableScroll onResize=onTableResize}} + + + +
    {{_ "Name"}}
    + + +
    {{_ "Action"}}
    + + + + + {{#each customsounds}} + + +
    +
    + + {{name}} + +
    +
    + + +
    + {{>icon _id=_id icon="play" block="icon-play-circled"}} +
    + + + {{else}} + {{# with searchText}} + + {{_ "No_results_found_for"}} {{.}} + + {{/with}} + {{/each}} + {{#unless isReady}} + + {{> loading}} + + {{/unless}} + + {{/table}} {{/requiresPermission}}

    diff --git a/packages/rocketchat-custom-sounds/client/admin/adminSounds.js b/packages/rocketchat-custom-sounds/client/admin/adminSounds.js index 60e4869786af..500a18c7471f 100644 --- a/packages/rocketchat-custom-sounds/client/admin/adminSounds.js +++ b/packages/rocketchat-custom-sounds/client/admin/adminSounds.js @@ -7,6 +7,10 @@ import { CustomSounds } from 'meteor/rocketchat:models'; import s from 'underscore.string'; Template.adminSounds.helpers({ + searchText() { + const instance = Template.instance(); + return instance.filter && instance.filter.get(); + }, isReady() { if (Template.instance().ready != null) { return Template.instance().ready.get(); @@ -37,6 +41,26 @@ Template.adminSounds.helpers({ data: Template.instance().tabBarData.get(), }; }, + + onTableScroll() { + const instance = Template.instance(); + return function(currentTarget) { + if ( + currentTarget.offsetHeight + currentTarget.scrollTop >= + currentTarget.scrollHeight - 100 + ) { + return instance.limit.set(instance.limit.get() + 50); + } + }; + }, + onTableItemClick() { + const instance = Template.instance(); + return function(item) { + instance.tabBarData.set(CustomSounds.findOne({ _id: item._id })); + instance.tabBar.showGroup('custom-sounds-selected'); + instance.tabBar.open('admin-sound-info'); + }; + }, }); Template.adminSounds.onCreated(function() { @@ -108,26 +132,11 @@ Template.adminSounds.events({ e.preventDefault(); } }, - 'keyup #sound-filter'(e, t) { e.stopPropagation(); e.preventDefault(); t.filter.set(e.currentTarget.value); }, - - 'click .sound-info'(e, instance) { - e.preventDefault(); - instance.tabBarData.set(CustomSounds.findOne({ _id: this._id })); - instance.tabBar.showGroup('custom-sounds-selected'); - instance.tabBar.open('admin-sound-info'); - }, - - 'click .load-more'(e, t) { - e.preventDefault(); - e.stopPropagation(); - t.limit.set(t.limit.get() + 50); - }, - 'click .icon-play-circled'(e) { e.preventDefault(); e.stopPropagation(); diff --git a/packages/rocketchat-emoji-custom/client/admin/adminEmoji.html b/packages/rocketchat-emoji-custom/client/admin/adminEmoji.html index 362abe9f8553..b85042ea49d3 100644 --- a/packages/rocketchat-emoji-custom/client/admin/adminEmoji.html +++ b/packages/rocketchat-emoji-custom/client/admin/adminEmoji.html @@ -22,33 +22,50 @@
    {{{_ "Showing_results" customemoji.length}}}
    -
    - - - - - - - - - - {{#each customemoji}} - - + + + + + + + {{#each customemoji}} + + - - - - {{/each}} - -
     {{_ "Name"}}{{_ "Aliases"}}
    +
    {{_ "Name"}}
    +
    +
    {{_ "Aliases"}}
    +
    +
    +
    + + {{name}} +
    -
    {{name}}{{aliases}}
    - {{#if hasMore}} - - {{/if}} -
    +
    + + +
    +
    + + {{aliases}} + +
    +
    + + + {{else}} {{# with searchText}} + + {{_ "No_results_found_for"}} {{.}} + + {{/with}} {{/each}} {{#unless isReady}} + + {{> loading}} + + {{/unless}} + + {{/table}} {{/unless}}
    diff --git a/packages/rocketchat-emoji-custom/client/admin/adminEmoji.js b/packages/rocketchat-emoji-custom/client/admin/adminEmoji.js index b73fe2b8829a..f625e8787b09 100644 --- a/packages/rocketchat-emoji-custom/client/admin/adminEmoji.js +++ b/packages/rocketchat-emoji-custom/client/admin/adminEmoji.js @@ -7,6 +7,10 @@ import { SideNav } from 'meteor/rocketchat:ui'; import s from 'underscore.string'; Template.adminEmoji.helpers({ + searchText() { + const instance = Template.instance(); + return instance.filter && instance.filter.get(); + }, isReady() { if (Template.instance().ready != null) { return Template.instance().ready.get(); @@ -37,6 +41,13 @@ Template.adminEmoji.helpers({ data: Template.instance().tabBarData.get(), }; }, + onTableItemClick() { + const instance = Template.instance(); + return function({ _id }) { + instance.tabBarData.set(RocketChat.models.EmojiCustom.findOne({ _id })); + instance.tabBar.open('admin-emoji-info'); + }; + }, }); Template.adminEmoji.onCreated(function() { @@ -110,16 +121,4 @@ Template.adminEmoji.events({ e.preventDefault(); t.filter.set(e.currentTarget.value); }, - - 'click .emoji-info'(e, instance) { - e.preventDefault(); - instance.tabBarData.set(RocketChat.models.EmojiCustom.findOne({ _id: this._id })); - instance.tabBar.open('admin-emoji-info'); - }, - - 'click .load-more'(e, t) { - e.preventDefault(); - e.stopPropagation(); - t.limit.set(t.limit.get() + 50); - }, }); diff --git a/packages/rocketchat-importer/client/admin/adminImport.html b/packages/rocketchat-importer/client/admin/adminImport.html index aa4b91a5bef0..644beafea6a5 100644 --- a/packages/rocketchat-importer/client/admin/adminImport.html +++ b/packages/rocketchat-importer/client/admin/adminImport.html @@ -1,6 +1,6 @@