From eb36f8bed5d8e8b241765db2ba40b37729d17329 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 21 Dec 2018 19:14:54 -0200 Subject: [PATCH 01/59] Move rocketchat settings to specific package --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-lib/lib/settings.js | 102 +------------------ packages/rocketchat-lib/package.js | 1 + packages/rocketchat-settings/client/index.js | 5 + packages/rocketchat-settings/lib/settings.js | 97 ++++++++++++++++++ packages/rocketchat-settings/package.js | 14 +++ packages/rocketchat-settings/server/index.js | 5 + 8 files changed, 127 insertions(+), 101 deletions(-) create mode 100644 packages/rocketchat-settings/client/index.js create mode 100644 packages/rocketchat-settings/lib/settings.js create mode 100644 packages/rocketchat-settings/package.js create mode 100644 packages/rocketchat-settings/server/index.js diff --git a/.meteor/packages b/.meteor/packages index b77a8aa4096c..52b54f3db3fa 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -200,4 +200,5 @@ rocketchat:bigbluebutton rocketchat:mailmessages juliancwirko:postcss littledata:synced-cron -rocketchat:utils \ No newline at end of file +rocketchat:utils +rocketchat:settings \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index 447e1583da9c..c422d1b1304f 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -212,6 +212,7 @@ rocketchat:reactions@0.0.1 rocketchat:retention-policy@0.0.1 rocketchat:sandstorm@0.0.1 rocketchat:search@0.0.1 +rocketchat:settings@0.0.1 rocketchat:setup-wizard@0.0.1 rocketchat:slackbridge@0.0.1 rocketchat:slashcommands-archive@0.0.1 diff --git a/packages/rocketchat-lib/lib/settings.js b/packages/rocketchat-lib/lib/settings.js index f86ab11bcd25..7b90e489b732 100644 --- a/packages/rocketchat-lib/lib/settings.js +++ b/packages/rocketchat-lib/lib/settings.js @@ -1,101 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; +import { settings } from 'meteor/rocketchat:settings'; -/* -* RocketChat.settings holds all packages settings -* @namespace RocketChat.settings -*/ -RocketChat.settings = { - callbacks: {}, - regexCallbacks: {}, - ts: new Date, - get(_id, callback) { - if (callback != null) { - RocketChat.settings.onload(_id, callback); - if (!Meteor.settings) { - return; - } - if (_id === '*') { - return Object.keys(Meteor.settings).forEach((key) => { - const value = Meteor.settings[key]; - callback(key, value); - }); - } - if (_.isRegExp(_id) && Meteor.settings) { - return Object.keys(Meteor.settings).forEach((key) => { - if (!_id.test(key)) { - return; - } - const value = Meteor.settings[key]; - callback(key, value); - }); - } - return Meteor.settings[_id] != null && callback(_id, Meteor.settings[_id]); - } else { - if (!Meteor.settings) { - return; - } - if (_.isRegExp(_id)) { - return Object.keys(Meteor.settings).reduce((items, key) => { - const value = Meteor.settings[key]; - if (_id.test(key)) { - items.push({ - key, - value, - }); - } - return items; - }, []); - } - return Meteor.settings && Meteor.settings[_id]; - } - }, - set(_id, value, callback) { - return Meteor.call('saveSetting', _id, value, callback); - }, - batchSet(settings, callback) { - // async -> sync - // http://daemon.co.za/2012/04/simple-async-with-only-underscore/ - const save = function(setting) { - return function(callback) { - return Meteor.call('saveSetting', setting._id, setting.value, setting.editor, callback); - }; - }; - const actions = _.map(settings, (setting) => save(setting)); - return _(actions).reduceRight(_.wrap, (err, success) => callback(err, success))(); - }, - load(key, value, initialLoad) { - ['*', key].forEach((item) => { - if (RocketChat.settings.callbacks[item]) { - RocketChat.settings.callbacks[item].forEach((callback) => callback(key, value, initialLoad)); - } - }); - Object.keys(RocketChat.settings.regexCallbacks).forEach((cbKey) => { - const cbValue = RocketChat.settings.regexCallbacks[cbKey]; - if (!cbValue.regex.test(key)) { - return; - } - cbValue.callbacks.forEach((callback) => callback(key, value, initialLoad)); - }); - }, - onload(key, callback) { - // if key is '*' - // for key, value in Meteor.settings - // callback key, value, false - // else if Meteor.settings?[_id]? - // callback key, Meteor.settings[_id], false - const keys = [].concat(key); - keys.forEach((k) => { - if (_.isRegExp(k)) { - RocketChat.settings.regexCallbacks[name = k.source] = RocketChat.settings.regexCallbacks[name = k.source] || { - regex: k, - callbacks: [], - }; - RocketChat.settings.regexCallbacks[k.source].callbacks.push(callback); - } else { - RocketChat.settings.callbacks[k] = RocketChat.settings.callbacks[k] || []; - RocketChat.settings.callbacks[k].push(callback); - } - }); - }, -}; +RocketChat.settings = settings; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index d055661ffbbc..b81ad6206f9f 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -32,6 +32,7 @@ Package.onUse(function(api) { api.use('rocketchat:version'); api.use('rocketchat:logger'); api.use('rocketchat:mailer'); + api.use('rocketchat:settings'); api.use('mizzao:timesync'); api.use('rocketchat:custom-oauth'); api.use('konecty:multiple-instances-status'); diff --git a/packages/rocketchat-settings/client/index.js b/packages/rocketchat-settings/client/index.js new file mode 100644 index 000000000000..8f93e01fb4f4 --- /dev/null +++ b/packages/rocketchat-settings/client/index.js @@ -0,0 +1,5 @@ +import { settings } from '../lib/settings'; + +export { + settings, +}; diff --git a/packages/rocketchat-settings/lib/settings.js b/packages/rocketchat-settings/lib/settings.js new file mode 100644 index 000000000000..35b08414514f --- /dev/null +++ b/packages/rocketchat-settings/lib/settings.js @@ -0,0 +1,97 @@ +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; + +export const settings = { + callbacks: {}, + regexCallbacks: {}, + ts: new Date, + get(_id, callback) { + if (callback != null) { + settings.onload(_id, callback); + if (!Meteor.settings) { + return; + } + if (_id === '*') { + return Object.keys(Meteor.settings).forEach((key) => { + const value = Meteor.settings[key]; + callback(key, value); + }); + } + if (_.isRegExp(_id) && Meteor.settings) { + return Object.keys(Meteor.settings).forEach((key) => { + if (!_id.test(key)) { + return; + } + const value = Meteor.settings[key]; + callback(key, value); + }); + } + return Meteor.settings[_id] != null && callback(_id, Meteor.settings[_id]); + } else { + if (!Meteor.settings) { + return; + } + if (_.isRegExp(_id)) { + return Object.keys(Meteor.settings).reduce((items, key) => { + const value = Meteor.settings[key]; + if (_id.test(key)) { + items.push({ + key, + value, + }); + } + return items; + }, []); + } + return Meteor.settings && Meteor.settings[_id]; + } + }, + set(_id, value, callback) { + return Meteor.call('saveSetting', _id, value, callback); + }, + batchSet(settings, callback) { + // async -> sync + // http://daemon.co.za/2012/04/simple-async-with-only-underscore/ + const save = function(setting) { + return function(callback) { + return Meteor.call('saveSetting', setting._id, setting.value, setting.editor, callback); + }; + }; + const actions = _.map(settings, (setting) => save(setting)); + return _(actions).reduceRight(_.wrap, (err, success) => callback(err, success))(); + }, + load(key, value, initialLoad) { + ['*', key].forEach((item) => { + if (settings.callbacks[item]) { + settings.callbacks[item].forEach((callback) => callback(key, value, initialLoad)); + } + }); + Object.keys(settings.regexCallbacks).forEach((cbKey) => { + const cbValue = settings.regexCallbacks[cbKey]; + if (!cbValue.regex.test(key)) { + return; + } + cbValue.callbacks.forEach((callback) => callback(key, value, initialLoad)); + }); + }, + onload(key, callback) { + // if key is '*' + // for key, value in Meteor.settings + // callback key, value, false + // else if Meteor.settings?[_id]? + // callback key, Meteor.settings[_id], false + const keys = [].concat(key); + keys.forEach((k) => { + if (_.isRegExp(k)) { + settings.regexCallbacks[name = k.source] = settings.regexCallbacks[name = k.source] || { + regex: k, + callbacks: [], + }; + settings.regexCallbacks[k.source].callbacks.push(callback); + } else { + settings.callbacks[k] = settings.callbacks[k] || []; + settings.callbacks[k].push(callback); + } + }); + }, +}; diff --git a/packages/rocketchat-settings/package.js b/packages/rocketchat-settings/package.js new file mode 100644 index 000000000000..9d63fec60647 --- /dev/null +++ b/packages/rocketchat-settings/package.js @@ -0,0 +1,14 @@ +Package.describe({ + name: 'rocketchat:settings', + version: '0.0.1', + summary: '', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-settings/server/index.js b/packages/rocketchat-settings/server/index.js new file mode 100644 index 000000000000..8f93e01fb4f4 --- /dev/null +++ b/packages/rocketchat-settings/server/index.js @@ -0,0 +1,5 @@ +import { settings } from '../lib/settings'; + +export { + settings, +}; From 4428a97671f21cdf41ce8a3b2788cd5c58d564b5 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Mon, 24 Dec 2018 13:55:20 -0200 Subject: [PATCH 02/59] WIP: Move models from rocketchat-lib to a specific package (server) --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-lib/package.js | 1 + .../rocketchat-lib/server/models/Avatars.js | 114 +-- .../server/models/ExportOperations.js | 79 +- .../rocketchat-lib/server/models/Messages.js | 834 +---------------- .../rocketchat-lib/server/models/Reports.js | 19 +- .../rocketchat-lib/server/models/Rooms.js | 806 +---------------- .../rocketchat-lib/server/models/Settings.js | 183 +--- .../server/models/Subscriptions.js | 850 +---------------- .../rocketchat-lib/server/models/Uploads.js | 111 +-- .../server/models/UserDataFiles.js | 41 +- .../rocketchat-lib/server/models/Users.js | 669 +------------- .../rocketchat-lib/server/models/_Base.js | 286 +----- packages/rocketchat-models/client/index.js | 0 packages/rocketchat-models/package.js | 17 + packages/rocketchat-models/server/index.js | 27 + .../server/models/Avatars.js | 116 +++ .../server/models/ExportOperations.js | 81 ++ .../server/models/Messages.js | 839 +++++++++++++++++ .../server/models/Reports.js | 21 + .../rocketchat-models/server/models/Rooms.js | 809 +++++++++++++++++ .../server/models/Settings.js | 184 ++++ .../server/models/Subscriptions.js | 853 ++++++++++++++++++ .../server/models/Uploads.js | 113 +++ .../server/models/UserDataFiles.js | 43 + .../rocketchat-models/server/models/Users.js | 672 ++++++++++++++ .../rocketchat-models/server/models/_Base.js | 283 ++++++ .../server/models/_BaseDb.js | 9 +- 29 files changed, 4088 insertions(+), 3976 deletions(-) create mode 100644 packages/rocketchat-models/client/index.js create mode 100755 packages/rocketchat-models/package.js create mode 100644 packages/rocketchat-models/server/index.js create mode 100644 packages/rocketchat-models/server/models/Avatars.js create mode 100644 packages/rocketchat-models/server/models/ExportOperations.js create mode 100644 packages/rocketchat-models/server/models/Messages.js create mode 100644 packages/rocketchat-models/server/models/Reports.js create mode 100644 packages/rocketchat-models/server/models/Rooms.js create mode 100644 packages/rocketchat-models/server/models/Settings.js create mode 100644 packages/rocketchat-models/server/models/Subscriptions.js create mode 100644 packages/rocketchat-models/server/models/Uploads.js create mode 100644 packages/rocketchat-models/server/models/UserDataFiles.js create mode 100644 packages/rocketchat-models/server/models/Users.js create mode 100644 packages/rocketchat-models/server/models/_Base.js rename packages/{rocketchat-lib => rocketchat-models}/server/models/_BaseDb.js (98%) diff --git a/.meteor/packages b/.meteor/packages index 52b54f3db3fa..98585f8b6d31 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -201,4 +201,5 @@ rocketchat:mailmessages juliancwirko:postcss littledata:synced-cron rocketchat:utils -rocketchat:settings \ No newline at end of file +rocketchat:settings +rocketchat:models \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index c422d1b1304f..e759dba2d774 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -200,6 +200,7 @@ rocketchat:message-pin@0.0.1 rocketchat:message-snippet@0.0.1 rocketchat:message-star@0.0.1 rocketchat:migrations@0.0.1 +rocketchat:models@1.0.0 rocketchat:monitoring@2.30.2_3 rocketchat:nrr@1.0.0 rocketchat:oauth2-server@2.0.0 diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index b81ad6206f9f..c994b17fc1b3 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -25,6 +25,7 @@ Package.onUse(function(api) { api.use('service-configuration'); api.use('check'); api.use('rocketchat:utils'); + api.use('rocketchat:models'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); diff --git a/packages/rocketchat-lib/server/models/Avatars.js b/packages/rocketchat-lib/server/models/Avatars.js index b056909215cb..455ca64a97d1 100644 --- a/packages/rocketchat-lib/server/models/Avatars.js +++ b/packages/rocketchat-lib/server/models/Avatars.js @@ -1,113 +1,3 @@ -import _ from 'underscore'; -import s from 'underscore.string'; -import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; +import { Avatars } from 'meteor/rocketchat:models'; -RocketChat.models.Avatars = new class extends RocketChat.models._Base { - constructor() { - super('avatars'); - - this.model.before.insert((userId, doc) => { - doc.instanceId = InstanceStatus.id(); - }); - - this.tryEnsureIndex({ name: 1 }); - } - - insertAvatarFileInit(name, userId, store, file, extra) { - const fileData = { - _id: name, - name, - userId, - store, - complete: false, - uploading: true, - progress: 0, - extension: s.strRightBack(file.name, '.'), - uploadedAt: new Date(), - }; - - _.extend(fileData, file, extra); - - return this.insertOrUpsert(fileData); - } - - updateFileComplete(fileId, userId, file) { - if (!fileId) { - return; - } - - const filter = { - _id: fileId, - userId, - }; - - const update = { - $set: { - complete: true, - uploading: false, - progress: 1, - }, - }; - - update.$set = _.extend(file, update.$set); - - if (this.model.direct && this.model.direct.update) { - return this.model.direct.update(filter, update); - } else { - return this.update(filter, update); - } - } - - findOneByName(name) { - return this.findOne({ name }); - } - - updateFileNameById(fileId, name) { - const filter = { _id: fileId }; - const update = { - $set: { - name, - }, - }; - if (this.model.direct && this.model.direct.update) { - return this.model.direct.update(filter, update); - } else { - return this.update(filter, update); - } - } - - // @TODO deprecated - updateFileCompleteByNameAndUserId(name, userId, url) { - if (!name) { - return; - } - - const filter = { - name, - userId, - }; - - const update = { - $set: { - complete: true, - uploading: false, - progress: 1, - url, - }, - }; - - if (this.model.direct && this.model.direct.update) { - return this.model.direct.update(filter, update); - } else { - return this.update(filter, update); - } - } - - deleteFile(fileId) { - if (this.model.direct && this.model.direct.remove) { - return this.model.direct.remove({ _id: fileId }); - } else { - return this.remove({ _id: fileId }); - } - } -}; +RocketChat.models.Avatars = Avatars; diff --git a/packages/rocketchat-lib/server/models/ExportOperations.js b/packages/rocketchat-lib/server/models/ExportOperations.js index 095e8ec885b0..5f2375cc281c 100644 --- a/packages/rocketchat-lib/server/models/ExportOperations.js +++ b/packages/rocketchat-lib/server/models/ExportOperations.js @@ -1,78 +1,3 @@ -import _ from 'underscore'; +import { ExportOperations } from 'meteor/rocketchat:models'; -RocketChat.models.ExportOperations = new class ModelExportOperations extends RocketChat.models._Base { - constructor() { - super('export_operations'); - - this.tryEnsureIndex({ userId: 1 }); - this.tryEnsureIndex({ status: 1 }); - } - - // FIND - findById(id) { - const query = { _id: id }; - - return this.find(query); - } - - findLastOperationByUser(userId, fullExport = false, options = {}) { - const query = { - userId, - fullExport, - }; - - options.sort = { createdAt : -1 }; - return this.findOne(query, options); - } - - findPendingByUser(userId, options) { - const query = { - userId, - status: { - $nin: ['completed'], - }, - }; - - return this.find(query, options); - } - - findAllPending(options) { - const query = { - status: { $nin: ['completed'] }, - }; - - return this.find(query, options); - } - - // UPDATE - updateOperation(data) { - const update = { - $set: { - roomList: data.roomList, - status: data.status, - fileList: data.fileList, - generatedFile: data.generatedFile, - }, - }; - - return this.update(data._id, update); - } - - - // INSERT - create(data) { - const exportOperation = { - createdAt: new Date, - }; - - _.extend(exportOperation, data); - - return this.insert(exportOperation); - } - - - // REMOVE - removeById(_id) { - return this.remove(_id); - } -}; +RocketChat.models.ExportOperations = ExportOperations; diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js index bc11062fa660..8a9de256ce0b 100644 --- a/packages/rocketchat-lib/server/models/Messages.js +++ b/packages/rocketchat-lib/server/models/Messages.js @@ -1,833 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import _ from 'underscore'; +import { Messages } from 'meteor/rocketchat:models'; -RocketChat.models.Messages = new class extends RocketChat.models._Base { - constructor() { - super('message'); - - this.tryEnsureIndex({ rid: 1, ts: 1 }); - this.tryEnsureIndex({ ts: 1 }); - this.tryEnsureIndex({ 'u._id': 1 }); - this.tryEnsureIndex({ editedAt: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ 'editedBy._id': 1 }, { sparse: 1 }); - this.tryEnsureIndex({ rid: 1, t: 1, 'u._id': 1 }); - this.tryEnsureIndex({ expireAt: 1 }, { expireAfterSeconds: 0 }); - this.tryEnsureIndex({ msg: 'text' }); - this.tryEnsureIndex({ 'file._id': 1 }, { sparse: 1 }); - this.tryEnsureIndex({ 'mentions.username': 1 }, { sparse: 1 }); - this.tryEnsureIndex({ pinned: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ snippeted: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ location: '2dsphere' }); - this.tryEnsureIndex({ slackBotId: 1, slackTs: 1 }, { sparse: 1 }); - } - - countVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $gte: afterTimestamp, - $lte: beforeTimestamp, - }, - }; - - return this.find(query, options).count(); - } - - // FIND - findByMention(username, options) { - const query = { 'mentions.username': username }; - - return this.find(query, options); - } - - findFilesByUserId(userId, options = {}) { - const query = { - 'u._id': userId, - 'file._id': { $exists: true }, - }; - return this.find(query, { fields: { 'file._id': 1 }, ...options }); - } - - findFilesByRoomIdPinnedTimestampAndUsers(rid, excludePinned, ts, users = [], options = {}) { - const query = { - rid, - ts, - 'file._id': { $exists: true }, - }; - - if (excludePinned) { - query.pinned = { $ne: true }; - } - - if (users.length) { - query['u.username'] = { $in: users }; - } - - return this.find(query, { fields: { 'file._id': 1 }, ...options }); - } - findVisibleByMentionAndRoomId(username, rid, options) { - const query = { - _hidden: { $ne: true }, - 'mentions.username': username, - rid, - }; - - return this.find(query, options); - } - - findVisibleByRoomId(roomId, options) { - const query = { - _hidden: { - $ne: true, - }, - - rid: roomId, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdNotContainingTypes(roomId, types, options) { - const query = { - _hidden: { - $ne: true, - }, - - rid: roomId, - }; - - if (Match.test(types, [String]) && (types.length > 0)) { - query.t = - { $nin: types }; - } - - return this.find(query, options); - } - - findInvisibleByRoomId(roomId, options) { - const query = { - _hidden: true, - rid: roomId, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdAfterTimestamp(roomId, timestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $gt: timestamp, - }, - }; - - return this.find(query, options); - } - - findForUpdates(roomId, timestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - _updatedAt: { - $gt: timestamp, - }, - }; - return this.find(query, options); - } - - findVisibleByRoomIdBeforeTimestamp(roomId, timestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $lt: timestamp, - }, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdBeforeTimestampInclusive(roomId, timestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $lte: timestamp, - }, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdBetweenTimestamps(roomId, afterTimestamp, beforeTimestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $gt: afterTimestamp, - $lt: beforeTimestamp, - }, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $gte: afterTimestamp, - $lte: beforeTimestamp, - }, - }; - - return this.find(query, options); - } - - findVisibleByRoomIdBeforeTimestampNotContainingTypes(roomId, timestamp, types, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $lt: timestamp, - }, - }; - - if (Match.test(types, [String]) && (types.length > 0)) { - query.t = - { $nin: types }; - } - - return this.find(query, options); - } - - findVisibleByRoomIdBetweenTimestampsNotContainingTypes(roomId, afterTimestamp, beforeTimestamp, types, options) { - const query = { - _hidden: { - $ne: true, - }, - rid: roomId, - ts: { - $gt: afterTimestamp, - $lt: beforeTimestamp, - }, - }; - - if (Match.test(types, [String]) && (types.length > 0)) { - query.t = - { $nin: types }; - } - - return this.find(query, options); - } - - findVisibleCreatedOrEditedAfterTimestamp(timestamp, options) { - const query = { - _hidden: { $ne: true }, - $or: [{ - ts: { - $gt: timestamp, - }, - }, - { - editedAt: { - $gt: timestamp, - }, - }, - ], - }; - - return this.find(query, options); - } - - findStarredByUserAtRoom(userId, roomId, options) { - const query = { - _hidden: { $ne: true }, - 'starred._id': userId, - rid: roomId, - }; - - return this.find(query, options); - } - - findPinnedByRoom(roomId, options) { - const query = { - t: { $ne: 'rm' }, - _hidden: { $ne: true }, - pinned: true, - rid: roomId, - }; - - return this.find(query, options); - } - - findSnippetedByRoom(roomId, options) { - const query = { - _hidden: { $ne: true }, - snippeted: true, - rid: roomId, - }; - - return this.find(query, options); - } - - getLastTimestamp(options) { - if (options == null) { options = {}; } - const query = { ts: { $exists: 1 } }; - options.sort = { ts: -1 }; - options.limit = 1; - const [message] = this.find(query, options).fetch(); - return message && message.ts; - } - - findByRoomIdAndMessageIds(rid, messageIds, options) { - const query = { - rid, - _id: { - $in: messageIds, - }, - }; - - return this.find(query, options); - } - - findOneBySlackBotIdAndSlackTs(slackBotId, slackTs) { - const query = { - slackBotId, - slackTs, - }; - - return this.findOne(query); - } - - findOneBySlackTs(slackTs) { - const query = { slackTs }; - - return this.findOne(query); - } - - findByRoomIdAndType(roomId, type, options) { - const query = { - rid: roomId, - t: type, - }; - - if (options == null) { options = {}; } - - return this.find(query, options); - } - - findByRoomId(roomId, options) { - const query = { - rid: roomId, - }; - - return this.find(query, options); - } - - getLastVisibleMessageSentWithNoTypeByRoomId(rid, messageId) { - const query = { - rid, - _hidden: { $ne: true }, - t: { $exists: false }, - }; - - if (messageId) { - query._id = { $ne: messageId }; - } - - const options = { - sort: { - ts: -1, - }, - }; - - return this.findOne(query, options); - } - - cloneAndSaveAsHistoryById(_id) { - const me = RocketChat.models.Users.findOneById(Meteor.userId()); - const record = this.findOneById(_id); - record._hidden = true; - record.parent = record._id; - record.editedAt = new Date; - record.editedBy = { - _id: Meteor.userId(), - username: me.username, - }; - delete record._id; - return this.insert(record); - } - - // UPDATE - setHiddenById(_id, hidden) { - if (hidden == null) { hidden = true; } - const query = { _id }; - - const update = { - $set: { - _hidden: hidden, - }, - }; - - return this.update(query, update); - } - - setAsDeletedByIdAndUser(_id, user) { - const query = { _id }; - - const update = { - $set: { - msg: '', - t: 'rm', - urls: [], - mentions: [], - attachments: [], - reactions: [], - editedAt: new Date(), - editedBy: { - _id: user._id, - username: user.username, - }, - }, - }; - - return this.update(query, update); - } - - setPinnedByIdAndUserId(_id, pinnedBy, pinned, pinnedAt) { - if (pinned == null) { pinned = true; } - if (pinnedAt == null) { pinnedAt = 0; } - const query = { _id }; - - const update = { - $set: { - pinned, - pinnedAt: pinnedAt || new Date, - pinnedBy, - }, - }; - - return this.update(query, update); - } - - setSnippetedByIdAndUserId(message, snippetName, snippetedBy, snippeted, snippetedAt) { - if (snippeted == null) { snippeted = true; } - if (snippetedAt == null) { snippetedAt = 0; } - const query = { _id: message._id }; - - const msg = `\`\`\`${ message.msg }\`\`\``; - - const update = { - $set: { - msg, - snippeted, - snippetedAt: snippetedAt || new Date, - snippetedBy, - snippetName, - }, - }; - - return this.update(query, update); - } - - setUrlsById(_id, urls) { - const query = { _id }; - - const update = { - $set: { - urls, - }, - }; - - return this.update(query, update); - } - - updateAllUsernamesByUserId(userId, username) { - const query = { 'u._id': userId }; - - const update = { - $set: { - 'u.username': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateUsernameOfEditByUserId(userId, username) { - const query = { 'editedBy._id': userId }; - - const update = { - $set: { - 'editedBy.username': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateUsernameAndMessageOfMentionByIdAndOldUsername(_id, oldUsername, newUsername, newMessage) { - const query = { - _id, - 'mentions.username': oldUsername, - }; - - const update = { - $set: { - 'mentions.$.username': newUsername, - msg: newMessage, - }, - }; - - return this.update(query, update); - } - - updateUserStarById(_id, userId, starred) { - let update; - const query = { _id }; - - if (starred) { - update = { - $addToSet: { - starred: { _id: userId }, - }, - }; - } else { - update = { - $pull: { - starred: { _id: Meteor.userId() }, - }, - }; - } - - return this.update(query, update); - } - - upgradeEtsToEditAt() { - const query = { ets: { $exists: 1 } }; - - const update = { - $rename: { - ets: 'editedAt', - }, - }; - - return this.update(query, update, { multi: true }); - } - - setMessageAttachments(_id, attachments) { - const query = { _id }; - - const update = { - $set: { - attachments, - }, - }; - - return this.update(query, update); - } - - setSlackBotIdAndSlackTs(_id, slackBotId, slackTs) { - const query = { _id }; - - const update = { - $set: { - slackBotId, - slackTs, - }, - }; - - return this.update(query, update); - } - - unlinkUserId(userId, newUserId, newUsername, newNameAlias) { - const query = { - 'u._id': userId, - }; - - const update = { - $set: { - alias: newNameAlias, - 'u._id': newUserId, - 'u.username' : newUsername, - 'u.name' : undefined, - }, - }; - - return this.update(query, update, { multi: true }); - } - - // INSERT - createWithTypeRoomIdMessageAndUser(type, roomId, message, user, extraData) { - const room = RocketChat.models.Rooms.findOneById(roomId, { fields: { sysMes: 1 } }); - if ((room != null ? room.sysMes : undefined) === false) { - return; - } - const record = { - t: type, - rid: roomId, - ts: new Date, - msg: message, - u: { - _id: user._id, - username: user.username, - }, - groupable: false, - }; - - if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { - record.unread = true; - } - - _.extend(record, extraData); - - record._id = this.insertOrUpsert(record); - RocketChat.models.Rooms.incMsgCountById(room._id, 1); - return record; - } - - createNavigationHistoryWithRoomIdMessageAndUser(roomId, message, user, extraData) { - const type = 'livechat_navigation_history'; - const room = RocketChat.models.Rooms.findOneById(roomId, { fields: { sysMes: 1 } }); - if ((room != null ? room.sysMes : undefined) === false) { - return; - } - const record = { - t: type, - rid: roomId, - ts: new Date, - msg: message, - u: { - _id: user._id, - username: user.username, - }, - groupable: false, - }; - - if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { - record.unread = true; - } - - _.extend(record, extraData); - - record._id = this.insertOrUpsert(record); - return record; - } - - createUserJoinWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('uj', roomId, message, user, extraData); - } - - createUserLeaveWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('ul', roomId, message, user, extraData); - } - - createUserRemovedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('ru', roomId, message, user, extraData); - } - - createUserAddedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('au', roomId, message, user, extraData); - } - - createCommandWithRoomIdAndUser(command, roomId, user, extraData) { - return this.createWithTypeRoomIdMessageAndUser('command', roomId, command, user, extraData); - } - - createUserMutedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('user-muted', roomId, message, user, extraData); - } - - createUserUnmutedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('user-unmuted', roomId, message, user, extraData); - } - - createNewModeratorWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('new-moderator', roomId, message, user, extraData); - } - - createModeratorRemovedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('moderator-removed', roomId, message, user, extraData); - } - - createNewOwnerWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('new-owner', roomId, message, user, extraData); - } - - createOwnerRemovedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('owner-removed', roomId, message, user, extraData); - } - - createNewLeaderWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('new-leader', roomId, message, user, extraData); - } - - createLeaderRemovedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('leader-removed', roomId, message, user, extraData); - } - - createSubscriptionRoleAddedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('subscription-role-added', roomId, message, user, extraData); - } - - createSubscriptionRoleRemovedWithRoomIdAndUser(roomId, user, extraData) { - const message = user.username; - return this.createWithTypeRoomIdMessageAndUser('subscription-role-removed', roomId, message, user, extraData); - } - - // REMOVE - removeById(_id) { - const query = { _id }; - - return this.remove(query); - } - - removeByRoomId(roomId) { - const query = { rid: roomId }; - - return this.remove(query); - } - - removeByIdPinnedTimestampAndUsers(rid, pinned, ts, users = []) { - const query = { - rid, - ts, - }; - - if (pinned) { - query.pinned = { $ne: true }; - } - - if (users.length) { - query['u.username'] = { $in: users }; - } - - return this.remove(query); - } - - removeByIdPinnedTimestampLimitAndUsers(rid, pinned, ts, limit, users = []) { - const query = { - rid, - ts, - }; - - if (pinned) { - query.pinned = { $ne: true }; - } - - if (users.length) { - query['u.username'] = { $in: users }; - } - - const messagesToDelete = RocketChat.models.Messages.find(query, { - fields: { - _id: 1, - }, - limit, - }).map(({ _id }) => _id); - - return this.remove({ - _id: { - $in: messagesToDelete, - }, - }); - } - - removeByUserId(userId) { - const query = { 'u._id': userId }; - - return this.remove(query); - } - - removeFilesByRoomId(roomId) { - this.find({ - rid: roomId, - 'file._id': { - $exists: true, - }, - }, { - fields: { - 'file._id': 1, - }, - }).fetch().forEach((document) => FileUpload.getStore('Uploads').deleteById(document.file._id)); - } - - getMessageByFileId(fileID) { - return this.findOne({ 'file._id': fileID }); - } - - setAsRead(rid, until) { - return this.update({ - rid, - unread: true, - ts: { $lt: until }, - }, { - $unset: { - unread: 1, - }, - }, { - multi: true, - }); - } - - setAsReadById(_id) { - return this.update({ - _id, - }, { - $unset: { - unread: 1, - }, - }); - } - - findUnreadMessagesByRoomAndDate(rid, after) { - const query = { - unread: true, - rid, - }; - - if (after) { - query.ts = { $gt: after }; - } - - return this.find(query, { - fields: { - _id: 1, - }, - }); - } -}; +RocketChat.models.Messages = Messages; diff --git a/packages/rocketchat-lib/server/models/Reports.js b/packages/rocketchat-lib/server/models/Reports.js index 923d0dccee3e..ec1aac6ee255 100644 --- a/packages/rocketchat-lib/server/models/Reports.js +++ b/packages/rocketchat-lib/server/models/Reports.js @@ -1,18 +1,3 @@ -import _ from 'underscore'; +import { Reports } from 'meteor/rocketchat:models'; -RocketChat.models.Reports = new class extends RocketChat.models._Base { - constructor() { - super('reports'); - } - createWithMessageDescriptionAndUserId(message, description, userId, extraData) { - const record = { - message, - description, - ts: new Date(), - userId, - }; - _.extend(record, extraData); - record._id = this.insert(record); - return record; - } -}; +RocketChat.models.Reports = Reports; diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js index 2105c43a1112..d664591ad794 100644 --- a/packages/rocketchat-lib/server/models/Rooms.js +++ b/packages/rocketchat-lib/server/models/Rooms.js @@ -1,806 +1,4 @@ -import _ from 'underscore'; -import s from 'underscore.string'; +import { Rooms } from 'meteor/rocketchat:models'; -class ModelRooms extends RocketChat.models._Base { - constructor(...args) { - super(...args); +RocketChat.models.Rooms = Rooms; - this.tryEnsureIndex({ name: 1 }, { unique: 1, sparse: 1 }); - this.tryEnsureIndex({ default: 1 }); - this.tryEnsureIndex({ t: 1 }); - this.tryEnsureIndex({ 'u._id': 1 }); - } - - findOneByIdOrName(_idOrName, options) { - const query = { - $or: [{ - _id: _idOrName, - }, { - name: _idOrName, - }], - }; - - return this.findOne(query, options); - } - - findOneByImportId(_id, options) { - const query = { importIds: _id }; - - return this.findOne(query, options); - } - - findOneByName(name, options) { - const query = { name }; - - return this.findOne(query, options); - } - - findOneByNameAndNotId(name, rid) { - const query = { - _id: { $ne: rid }, - name, - }; - - return this.findOne(query); - } - - findOneByDisplayName(fname, options) { - const query = { fname }; - - return this.findOne(query, options); - } - - findOneByNameAndType(name, type, options) { - const query = { - name, - t: type, - }; - - return this.findOne(query, options); - } - - // FIND - - findById(roomId, options) { - return this.find({ _id: roomId }, options); - } - - findByIds(roomIds, options) { - return this.find({ _id: { $in: [].concat(roomIds) } }, options); - } - - findByType(type, options) { - const query = { t: type }; - - return this.find(query, options); - } - - findByTypeInIds(type, ids, options) { - const query = { - _id: { - $in: ids, - }, - t: type, - }; - - return this.find(query, options); - } - - findByTypes(types, options) { - const query = { - t: { - $in: types, - }, - }; - - return this.find(query, options); - } - - findByUserId(userId, options) { - const query = { 'u._id': userId }; - - return this.find(query, options); - } - - findBySubscriptionUserId(userId, options) { - const data = RocketChat.models.Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() - .map((item) => item.rid); - - const query = { - _id: { - $in: data, - }, - }; - - return this.find(query, options); - } - - findBySubscriptionTypeAndUserId(type, userId, options) { - const data = RocketChat.models.Subscriptions.findByUserIdAndType(userId, type, { fields: { rid: 1 } }).fetch() - .map((item) => item.rid); - - const query = { - t: type, - _id: { - $in: data, - }, - }; - - return this.find(query, options); - } - - findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) { - const ids = RocketChat.models.Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() - .map((item) => item.rid); - - const query = { - _id: { - $in: ids, - }, - _updatedAt: { - $gt: _updatedAt, - }, - }; - - return this.find(query, options); - } - - findByNameContaining(name, options) { - const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); - - const query = { - $or: [ - { name: nameRegex }, - { - t: 'd', - usernames: nameRegex, - }, - ], - }; - - return this.find(query, options); - } - - findByNameContainingAndTypes(name, types, options) { - const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); - - const query = { - t: { - $in: types, - }, - $or: [ - { name: nameRegex }, - { - t: 'd', - usernames: nameRegex, - }, - ], - }; - - return this.find(query, options); - } - - findByNameAndType(name, type, options) { - const query = { - t: type, - name, - }; - - // do not use cache - return this._db.find(query, options); - } - - findByNameAndTypeNotDefault(name, type, options) { - const query = { - t: type, - name, - default: { - $ne: true, - }, - }; - - // do not use cache - return this._db.find(query, options); - } - - findByNameAndTypesNotInIds(name, types, ids, options) { - const query = { - _id: { - $ne: ids, - }, - t: { - $in: types, - }, - name, - }; - - // do not use cache - return this._db.find(query, options); - } - - findChannelAndPrivateByNameStarting(name, options) { - const nameRegex = new RegExp(`^${ s.trim(s.escapeRegExp(name)) }`, 'i'); - - const query = { - t: { - $in: ['c', 'p'], - }, - name: nameRegex, - }; - - return this.find(query, options); - } - - findByDefaultAndTypes(defaultValue, types, options) { - const query = { - default: defaultValue, - t: { - $in: types, - }, - }; - - return this.find(query, options); - } - - findDirectRoomContainingUsername(username, options) { - const query = { - t: 'd', - usernames: username, - }; - - return this.find(query, options); - } - - findDirectRoomContainingAllUsernames(usernames, options) { - const query = { - t: 'd', - usernames: { $size: usernames.length, $all: usernames }, - }; - - return this.findOne(query, options); - } - - findByTypeAndName(type, name, options) { - const query = { - name, - t: type, - }; - - return this.find(query, options); - } - - findByTypeAndNameContaining(type, name, options) { - const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); - - const query = { - name: nameRegex, - t: type, - }; - - return this.find(query, options); - } - - findByTypeInIdsAndNameContaining(type, ids, name, options) { - const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); - - const query = { - _id: { - $in: ids, - }, - name: nameRegex, - t: type, - }; - - return this.find(query, options); - } - - findByTypeAndArchivationState(type, archivationstate, options) { - const query = { t: type }; - - if (archivationstate) { - query.archived = true; - } else { - query.archived = { $ne: true }; - } - - return this.find(query, options); - } - - // UPDATE - addImportIds(_id, importIds) { - importIds = [].concat(importIds); - const query = { _id }; - - const update = { - $addToSet: { - importIds: { - $each: importIds, - }, - }, - }; - - return this.update(query, update); - } - - archiveById(_id) { - const query = { _id }; - - const update = { - $set: { - archived: true, - }, - }; - - return this.update(query, update); - } - - unarchiveById(_id) { - const query = { _id }; - - const update = { - $set: { - archived: false, - }, - }; - - return this.update(query, update); - } - - setNameById(_id, name, fname) { - const query = { _id }; - - const update = { - $set: { - name, - fname, - }, - }; - - return this.update(query, update); - } - - setFnameById(_id, fname) { - const query = { _id }; - - const update = { - $set: { - fname, - }, - }; - - return this.update(query, update); - } - - incMsgCountById(_id, inc) { - if (inc == null) { inc = 1; } - const query = { _id }; - - const update = { - $inc: { - msgs: inc, - }, - }; - - return this.update(query, update); - } - - incMsgCountAndSetLastMessageById(_id, inc, lastMessageTimestamp, lastMessage) { - if (inc == null) { inc = 1; } - const query = { _id }; - - const update = { - $set: { - lm: lastMessageTimestamp, - }, - $inc: { - msgs: inc, - }, - }; - - if (lastMessage) { - update.$set.lastMessage = lastMessage; - } - - return this.update(query, update); - } - - incUsersCountById(_id, inc = 1) { - const query = { _id }; - - const update = { - $inc: { - usersCount: inc, - }, - }; - - return this.update(query, update); - } - - incUsersCountByIds(ids, inc = 1) { - const query = { - _id: { - $in: ids, - }, - }; - - const update = { - $inc: { - usersCount: inc, - }, - }; - - return this.update(query, update, { multi: true }); - } - - setLastMessageById(_id, lastMessage) { - const query = { _id }; - - const update = { - $set: { - lastMessage, - }, - }; - - return this.update(query, update); - } - - resetLastMessageById(_id, messageId) { - const query = { _id }; - const lastMessage = RocketChat.models.Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id, messageId); - - const update = lastMessage ? { - $set: { - lastMessage, - }, - } : { - $unset: { - lastMessage: 1, - }, - }; - - return this.update(query, update); - } - - replaceUsername(previousUsername, username) { - const query = { usernames: previousUsername }; - - const update = { - $set: { - 'usernames.$': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - replaceMutedUsername(previousUsername, username) { - const query = { muted: previousUsername }; - - const update = { - $set: { - 'muted.$': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - replaceUsernameOfUserByUserId(userId, username) { - const query = { 'u._id': userId }; - - const update = { - $set: { - 'u.username': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - setJoinCodeById(_id, joinCode) { - let update; - const query = { _id }; - - if ((joinCode != null ? joinCode.trim() : undefined) !== '') { - update = { - $set: { - joinCodeRequired: true, - joinCode, - }, - }; - } else { - update = { - $set: { - joinCodeRequired: false, - }, - $unset: { - joinCode: 1, - }, - }; - } - - return this.update(query, update); - } - - setUserById(_id, user) { - const query = { _id }; - - const update = { - $set: { - u: { - _id: user._id, - username: user.username, - }, - }, - }; - - return this.update(query, update); - } - - setTypeById(_id, type) { - const query = { _id }; - const update = { - $set: { - t: type, - }, - }; - if (type === 'p') { - update.$unset = { default: '' }; - } - - return this.update(query, update); - } - - setTopicById(_id, topic) { - const query = { _id }; - - const update = { - $set: { - topic, - }, - }; - - return this.update(query, update); - } - - setAnnouncementById(_id, announcement, announcementDetails) { - const query = { _id }; - - const update = { - $set: { - announcement, - announcementDetails, - }, - }; - - return this.update(query, update); - } - - setCustomFieldsById(_id, customFields) { - const query = { _id }; - - const update = { - $set: { - customFields, - }, - }; - - return this.update(query, update); - } - - muteUsernameByRoomId(_id, username) { - const query = { _id }; - - const update = { - $addToSet: { - muted: username, - }, - }; - - return this.update(query, update); - } - - unmuteUsernameByRoomId(_id, username) { - const query = { _id }; - - const update = { - $pull: { - muted: username, - }, - }; - - return this.update(query, update); - } - - saveDefaultById(_id, defaultValue) { - const query = { _id }; - - const update = { - $set: { - default: defaultValue === 'true', - }, - }; - - return this.update(query, update); - } - - saveRetentionEnabledById(_id, value) { - const query = { _id }; - - const update = {}; - - if (value == null) { - update.$unset = { 'retention.enabled': true }; - } else { - update.$set = { 'retention.enabled': !!value }; - } - - return this.update(query, update); - } - - saveRetentionMaxAgeById(_id, value) { - const query = { _id }; - - value = Number(value); - if (!value) { - value = 30; - } - - const update = { - $set: { - 'retention.maxAge': value, - }, - }; - - return this.update(query, update); - } - - saveRetentionExcludePinnedById(_id, value) { - const query = { _id }; - - const update = { - $set: { - 'retention.excludePinned': value === true, - }, - }; - - return this.update(query, update); - } - - saveRetentionFilesOnlyById(_id, value) { - const query = { _id }; - - const update = { - $set: { - 'retention.filesOnly': value === true, - }, - }; - - return this.update(query, update); - } - - saveRetentionOverrideGlobalById(_id, value) { - const query = { _id }; - - const update = { - $set: { - 'retention.overrideGlobal': value === true, - }, - }; - - return this.update(query, update); - } - - saveEncryptedById(_id, value) { - const query = { _id }; - - const update = { - $set: { - encrypted: value === true, - }, - }; - - return this.update(query, update); - } - - setTopicAndTagsById(_id, topic, tags) { - const setData = {}; - const unsetData = {}; - - if (topic != null) { - if (!_.isEmpty(s.trim(topic))) { - setData.topic = s.trim(topic); - } else { - unsetData.topic = 1; - } - } - - if (tags != null) { - if (!_.isEmpty(s.trim(tags))) { - setData.tags = s.trim(tags).split(',').map((tag) => s.trim(tag)); - } else { - unsetData.tags = 1; - } - } - - const update = {}; - - if (!_.isEmpty(setData)) { - update.$set = setData; - } - - if (!_.isEmpty(unsetData)) { - update.$unset = unsetData; - } - - if (_.isEmpty(update)) { - return; - } - - return this.update({ _id }, update); - } - - // INSERT - createWithTypeNameUserAndUsernames(type, name, fname, user, usernames, extraData) { - const room = { - name, - fname, - t: type, - usernames, - msgs: 0, - usersCount: 0, - u: { - _id: user._id, - username: user.username, - }, - }; - - _.extend(room, extraData); - - room._id = this.insert(room); - return room; - } - - createWithIdTypeAndName(_id, type, name, extraData) { - const room = { - _id, - ts: new Date(), - t: type, - name, - usernames: [], - msgs: 0, - usersCount: 0, - }; - - _.extend(room, extraData); - - this.insert(room); - return room; - } - - createWithFullRoomData(room) { - delete room._id; - - room._id = this.insert(room); - return room; - } - - - // REMOVE - removeById(_id) { - const query = { _id }; - - return this.remove(query); - } - - removeDirectRoomContainingUsername(username) { - const query = { - t: 'd', - usernames: username, - }; - - return this.remove(query); - } -} - -RocketChat.models.Rooms = new ModelRooms('room', true); diff --git a/packages/rocketchat-lib/server/models/Settings.js b/packages/rocketchat-lib/server/models/Settings.js index 3cd5af4fb8c9..6fc6d0da7df2 100644 --- a/packages/rocketchat-lib/server/models/Settings.js +++ b/packages/rocketchat-lib/server/models/Settings.js @@ -1,182 +1,3 @@ -class ModelSettings extends RocketChat.models._Base { - constructor(...args) { - super(...args); +import { Settings } from 'meteor/rocketchat:models'; - this.tryEnsureIndex({ blocked: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ hidden: 1 }, { sparse: 1 }); - } - - // FIND - findById(_id) { - const query = { _id }; - - return this.find(query); - } - - findOneNotHiddenById(_id) { - const query = { - _id, - hidden: { $ne: true }, - }; - - return this.findOne(query); - } - - findByIds(_id = []) { - _id = [].concat(_id); - - const query = { - _id: { - $in: _id, - }, - }; - - return this.find(query); - } - - findByRole(role, options) { - const query = { role }; - - return this.find(query, options); - } - - findPublic(options) { - const query = { public: true }; - - return this.find(query, options); - } - - findNotHiddenPublic(ids = []) { - const filter = { - hidden: { $ne: true }, - public: true, - }; - - if (ids.length > 0) { - filter._id = - { $in: ids }; - } - - return this.find(filter, { fields: { _id: 1, value: 1 } }); - } - - findNotHiddenPublicUpdatedAfter(updatedAt) { - const filter = { - hidden: { $ne: true }, - public: true, - _updatedAt: { - $gt: updatedAt, - }, - }; - - return this.find(filter, { fields: { _id: 1, value: 1 } }); - } - - findNotHiddenPrivate() { - return this.find({ - hidden: { $ne: true }, - public: { $ne: true }, - }); - } - - findNotHidden(options) { - return this.find({ hidden: { $ne: true } }, options); - } - - findNotHiddenUpdatedAfter(updatedAt) { - return this.find({ - hidden: { $ne: true }, - _updatedAt: { - $gt: updatedAt, - }, - }); - } - - findSetupWizardSettings() { - return this.find({ wizard: { $exists: true, $ne: null } }); - } - - // UPDATE - updateValueById(_id, value) { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - updateValueAndEditorById(_id, value, editor) { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - editor, - }, - }; - - return this.update(query, update); - } - - updateValueNotHiddenById(_id, value) { - const query = { - _id, - hidden: { $ne: true }, - blocked: { $ne: true }, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - updateOptionsById(_id, options) { - const query = { - blocked: { $ne: true }, - _id, - }; - - const update = { $set: options }; - - return this.update(query, update); - } - - // INSERT - createWithIdAndValue(_id, value) { - const record = { - _id, - value, - _createdAt: new Date, - }; - - return this.insert(record); - } - - // REMOVE - removeById(_id) { - const query = { - blocked: { $ne: true }, - _id, - }; - - return this.remove(query); - } -} - -RocketChat.models.Settings = new ModelSettings('settings', true); +RocketChat.models.Settings = Settings; diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js index 6b4728a7ead5..36931f09b5ca 100644 --- a/packages/rocketchat-lib/server/models/Subscriptions.js +++ b/packages/rocketchat-lib/server/models/Subscriptions.js @@ -1,850 +1,4 @@ -import { Match } from 'meteor/check'; +import { Subscriptions } from 'meteor/rocketchat:models'; -class ModelSubscriptions extends RocketChat.models._Base { - constructor(...args) { - super(...args); +RocketChat.models.Subscriptions = Subscriptions; - this.tryEnsureIndex({ rid: 1, 'u._id': 1 }, { unique: 1 }); - this.tryEnsureIndex({ rid: 1, 'u.username': 1 }); - this.tryEnsureIndex({ rid: 1, alert: 1, 'u._id': 1 }); - this.tryEnsureIndex({ rid: 1, roles: 1 }); - this.tryEnsureIndex({ 'u._id': 1, name: 1, t: 1 }); - this.tryEnsureIndex({ open: 1 }); - this.tryEnsureIndex({ alert: 1 }); - - this.tryEnsureIndex({ rid: 1, 'u._id': 1, open: 1 }); - - this.tryEnsureIndex({ ts: 1 }); - this.tryEnsureIndex({ ls: 1 }); - this.tryEnsureIndex({ audioNotifications: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ desktopNotifications: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ mobilePushNotifications: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ emailNotifications: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ autoTranslate: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ autoTranslateLanguage: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ 'userHighlights.0': 1 }, { sparse: 1 }); - } - - - // FIND ONE - findOneByRoomIdAndUserId(roomId, userId, options) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - return this.findOne(query, options); - } - - findOneByRoomIdAndUsername(roomId, username, options) { - const query = { - rid: roomId, - 'u.username': username, - }; - - return this.findOne(query, options); - } - - findOneByRoomNameAndUserId(roomName, userId) { - const query = { - name: roomName, - 'u._id': userId, - }; - - return this.findOne(query); - } - - // FIND - findByUserId(userId, options) { - const query = - { 'u._id': userId }; - - return this.find(query, options); - } - - findByUserIdAndType(userId, type, options) { - const query = { - 'u._id': userId, - t: type, - }; - - return this.find(query, options); - } - - findByUserIdAndTypes(userId, types, options) { - const query = { - 'u._id': userId, - t: { - $in: types, - }, - }; - - return this.find(query, options); - } - - findByUserIdUpdatedAfter(userId, updatedAt, options) { - const query = { - 'u._id': userId, - _updatedAt: { - $gt: updatedAt, - }, - }; - - return this.find(query, options); - } - - findByRoomIdAndRoles(roomId, roles, options) { - roles = [].concat(roles); - const query = { - rid: roomId, - roles: { $in: roles }, - }; - - return this.find(query, options); - } - - findByType(types, options) { - const query = { - t: { - $in: types, - }, - }; - - return this.find(query, options); - } - - findByTypeAndUserId(type, userId, options) { - const query = { - t: type, - 'u._id': userId, - }; - - return this.find(query, options); - } - - findByRoomId(roomId, options) { - const query = - { rid: roomId }; - - return this.find(query, options); - } - - findByRoomIdAndNotUserId(roomId, userId, options) { - const query = { - rid: roomId, - 'u._id': { - $ne: userId, - }, - }; - - return this.find(query, options); - } - - findByRoomWithUserHighlights(roomId, options) { - const query = { - rid: roomId, - 'userHighlights.0': { $exists: true }, - }; - - return this.find(query, options); - } - - getLastSeen(options) { - if (options == null) { - options = {}; - } - const query = { ls: { $exists: 1 } }; - options.sort = { ls: -1 }; - options.limit = 1; - const [subscription] = this.find(query, options).fetch(); - return subscription && subscription.ls; - } - - findByRoomIdAndUserIds(roomId, userIds, options) { - const query = { - rid: roomId, - 'u._id': { - $in: userIds, - }, - }; - - return this.find(query, options); - } - - findByRoomIdAndUserIdsOrAllMessages(roomId, userIds) { - const query = { - rid: roomId, - $or: [ - { 'u._id': { $in: userIds } }, - { emailNotifications: 'all' }, - ], - }; - - return this.find(query); - } - - findByRoomIdWhenUserIdExists(rid, options) { - const query = { rid, 'u._id': { $exists: 1 } }; - - return this.find(query, options); - } - - findByRoomIdWhenUsernameExists(rid, options) { - const query = { rid, 'u.username': { $exists: 1 } }; - - return this.find(query, options); - } - - findUnreadByUserId(userId) { - const query = { - 'u._id': userId, - unread: { - $gt: 0, - }, - }; - - return this.find(query, { fields: { unread: 1 } }); - } - - getMinimumLastSeenByRoomId(rid) { - return this.db.findOne({ - rid, - }, { - sort: { - ls: 1, - }, - fields: { - ls: 1, - }, - }); - } - - // UPDATE - archiveByRoomId(roomId) { - const query = - { rid: roomId }; - - const update = { - $set: { - alert: false, - open: false, - archived: true, - }, - }; - - return this.update(query, update, { multi: true }); - } - - unarchiveByRoomId(roomId) { - const query = - { rid: roomId }; - - const update = { - $set: { - alert: false, - open: true, - archived: false, - }, - }; - - return this.update(query, update, { multi: true }); - } - - hideByRoomIdAndUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - const update = { - $set: { - alert: false, - open: false, - }, - }; - - return this.update(query, update); - } - - openByRoomIdAndUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - const update = { - $set: { - open: true, - }, - }; - - return this.update(query, update); - } - - setAsReadByRoomIdAndUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - const update = { - $set: { - open: true, - alert: false, - unread: 0, - userMentions: 0, - groupMentions: 0, - ls: new Date, - }, - }; - - return this.update(query, update); - } - - setAsUnreadByRoomIdAndUserId(roomId, userId, firstMessageUnreadTimestamp) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - const update = { - $set: { - open: true, - alert: true, - ls: firstMessageUnreadTimestamp, - }, - }; - - return this.update(query, update); - } - - setCustomFieldsDirectMessagesByUserId(userId, fields) { - const query = { - 'u._id': userId, - t: 'd', - }; - const update = { $set: { customFields: fields } }; - const options = { multi: true }; - - return this.update(query, update, options); - } - - setFavoriteByRoomIdAndUserId(roomId, userId, favorite) { - if (favorite == null) { - favorite = true; - } - const query = { - rid: roomId, - 'u._id': userId, - }; - - const update = { - $set: { - f: favorite, - }, - }; - - return this.update(query, update); - } - - updateNameAndAlertByRoomId(roomId, name, fname) { - const query = - { rid: roomId }; - - const update = { - $set: { - name, - fname, - alert: true, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateDisplayNameByRoomId(roomId, fname) { - const query = - { rid: roomId }; - - const update = { - $set: { - fname, - }, - }; - - return this.update(query, update, { multi: true }); - } - - setUserUsernameByUserId(userId, username) { - const query = - { 'u._id': userId }; - - const update = { - $set: { - 'u.username': username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - setNameForDirectRoomsWithOldName(oldName, name) { - const query = { - name: oldName, - t: 'd', - }; - - const update = { - $set: { - name, - }, - }; - - return this.update(query, update, { multi: true }); - } - - incUnreadForRoomIdExcludingUserId(roomId, userId, inc) { - if (inc == null) { - inc = 1; - } - const query = { - rid: roomId, - 'u._id': { - $ne: userId, - }, - }; - - const update = { - $set: { - alert: true, - open: true, - }, - $inc: { - unread: inc, - }, - }; - - return this.update(query, update, { multi: true }); - } - - incGroupMentionsAndUnreadForRoomIdExcludingUserId(roomId, userId, incGroup = 1, incUnread = 1) { - const query = { - rid: roomId, - 'u._id': { - $ne: userId, - }, - }; - - const update = { - $set: { - alert: true, - open: true, - }, - $inc: { - unread: incUnread, - groupMentions: incGroup, - }, - }; - - return this.update(query, update, { multi: true }); - } - - incUserMentionsAndUnreadForRoomIdAndUserIds(roomId, userIds, incUser = 1, incUnread = 1) { - const query = { - rid: roomId, - 'u._id': { - $in: userIds, - }, - }; - - const update = { - $set: { - alert: true, - open: true, - }, - $inc: { - unread: incUnread, - userMentions: incUser, - }, - }; - - return this.update(query, update, { multi: true }); - } - - ignoreUser({ _id, ignoredUser : ignored, ignore = true }) { - const query = { - _id, - }; - const update = { - }; - if (ignore) { - update.$addToSet = { ignored }; - } else { - update.$pull = { ignored }; - } - - return this.update(query, update); - } - - setAlertForRoomIdExcludingUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': { - $ne: userId, - }, - alert: { $ne: true }, - }; - - const update = { - $set: { - alert: true, - }, - }; - return this.update(query, update, { multi: true }); - } - - setOpenForRoomIdExcludingUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': { - $ne: userId, - }, - open: { $ne: true }, - }; - - const update = { - $set: { - open: true, - }, - }; - return this.update(query, update, { multi: true }); - } - - setBlockedByRoomId(rid, blocked, blocker) { - const query = { - rid, - 'u._id': blocked, - }; - - const update = { - $set: { - blocked: true, - }, - }; - - const query2 = { - rid, - 'u._id': blocker, - }; - - const update2 = { - $set: { - blocker: true, - }, - }; - - return this.update(query, update) && this.update(query2, update2); - } - - unsetBlockedByRoomId(rid, blocked, blocker) { - const query = { - rid, - 'u._id': blocked, - }; - - const update = { - $unset: { - blocked: 1, - }, - }; - - const query2 = { - rid, - 'u._id': blocker, - }; - - const update2 = { - $unset: { - blocker: 1, - }, - }; - - return this.update(query, update) && this.update(query2, update2); - } - - updateCustomFieldsByRoomId(rid, cfields) { - const query = { rid }; - const customFields = cfields || {}; - const update = { - $set: { - customFields, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateTypeByRoomId(roomId, type) { - const query = - { rid: roomId }; - - const update = { - $set: { - t: type, - }, - }; - - return this.update(query, update, { multi: true }); - } - - addRoleById(_id, role) { - const query = - { _id }; - - const update = { - $addToSet: { - roles: role, - }, - }; - - return this.update(query, update); - } - - removeRoleById(_id, role) { - const query = - { _id }; - - const update = { - $pull: { - roles: role, - }, - }; - - return this.update(query, update); - } - - setArchivedByUsername(username, archived) { - const query = { - t: 'd', - name: username, - }; - - const update = { - $set: { - archived, - }, - }; - - return this.update(query, update, { multi: true }); - } - - clearDesktopNotificationUserPreferences(userId) { - const query = { - 'u._id': userId, - desktopPrefOrigin: 'user', - }; - - const update = { - $unset: { - desktopNotifications: 1, - desktopPrefOrigin: 1, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateDesktopNotificationUserPreferences(userId, desktopNotifications) { - const query = { - 'u._id': userId, - desktopPrefOrigin: { - $ne: 'subscription', - }, - }; - - const update = { - $set: { - desktopNotifications, - desktopPrefOrigin: 'user', - }, - }; - - return this.update(query, update, { multi: true }); - } - - clearMobileNotificationUserPreferences(userId) { - const query = { - 'u._id': userId, - mobilePrefOrigin: 'user', - }; - - const update = { - $unset: { - mobilePushNotifications: 1, - mobilePrefOrigin: 1, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateMobileNotificationUserPreferences(userId, mobilePushNotifications) { - const query = { - 'u._id': userId, - mobilePrefOrigin: { - $ne: 'subscription', - }, - }; - - const update = { - $set: { - mobilePushNotifications, - mobilePrefOrigin: 'user', - }, - }; - - return this.update(query, update, { multi: true }); - } - - clearEmailNotificationUserPreferences(userId) { - const query = { - 'u._id': userId, - emailPrefOrigin: 'user', - }; - - const update = { - $unset: { - emailNotifications: 1, - emailPrefOrigin: 1, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateEmailNotificationUserPreferences(userId, emailNotifications) { - const query = { - 'u._id': userId, - emailPrefOrigin: { - $ne: 'subscription', - }, - }; - - const update = { - $set: { - emailNotifications, - emailPrefOrigin: 'user', - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateUserHighlights(userId, userHighlights) { - const query = { - 'u._id': userId, - }; - - const update = { - $set: { - userHighlights, - }, - }; - - return this.update(query, update, { multi: true }); - } - - updateDirectFNameByName(name, fname) { - const query = { - t: 'd', - name, - }; - - const update = { - $set: { - fname, - }, - }; - - return this.update(query, update, { multi: true }); - } - - // INSERT - createWithRoomAndUser(room, user, extraData) { - const subscription = { - open: false, - alert: false, - unread: 0, - userMentions: 0, - groupMentions: 0, - ts: room.ts, - rid: room._id, - name: room.name, - fname: room.fname, - customFields: room.customFields, - t: room.t, - u: { - _id: user._id, - username: user.username, - name: user.name, - }, - ...RocketChat.getDefaultSubscriptionPref(user), - ...extraData, - }; - - const result = this.insert(subscription); - - RocketChat.models.Rooms.incUsersCountById(room._id); - - return result; - } - - - // REMOVE - removeByUserId(userId) { - const query = { - 'u._id': userId, - }; - - const roomIds = this.findByUserId(userId).map((s) => s.rid); - - const result = this.remove(query); - - if (Match.test(result, Number) && result > 0) { - RocketChat.models.Rooms.incUsersCountByIds(roomIds, -1); - } - - return result; - } - - removeByRoomId(roomId) { - const query = { - rid: roomId, - }; - - const result = this.remove(query); - - if (Match.test(result, Number) && result > 0) { - RocketChat.models.Rooms.incUsersCountById(roomId, - result); - } - - return result; - } - - removeByRoomIdAndUserId(roomId, userId) { - const query = { - rid: roomId, - 'u._id': userId, - }; - - const result = this.remove(query); - - if (Match.test(result, Number) && result > 0) { - RocketChat.models.Rooms.incUsersCountById(roomId, - result); - } - - return result; - } -} - -RocketChat.models.Subscriptions = new ModelSubscriptions('subscription', true); diff --git a/packages/rocketchat-lib/server/models/Uploads.js b/packages/rocketchat-lib/server/models/Uploads.js index e25823640caf..7dc92cf6f67b 100644 --- a/packages/rocketchat-lib/server/models/Uploads.js +++ b/packages/rocketchat-lib/server/models/Uploads.js @@ -1,110 +1,3 @@ -import _ from 'underscore'; -import s from 'underscore.string'; -import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; +import { Uploads } from 'meteor/rocketchat:models'; -RocketChat.models.Uploads = new class extends RocketChat.models._Base { - constructor() { - super('uploads'); - - this.model.before.insert((userId, doc) => { - doc.instanceId = InstanceStatus.id(); - }); - - this.tryEnsureIndex({ rid: 1 }); - this.tryEnsureIndex({ uploadedAt: 1 }); - } - - findNotHiddenFilesOfRoom(roomId, searchText, limit) { - const fileQuery = { - rid: roomId, - complete: true, - uploading: false, - _hidden: { - $ne: true, - }, - }; - - if (searchText) { - fileQuery.name = { $regex: new RegExp(RegExp.escape(searchText), 'i') }; - } - - const fileOptions = { - limit, - sort: { - uploadedAt: -1, - }, - fields: { - _id: 1, - userId: 1, - rid: 1, - name: 1, - description: 1, - type: 1, - url: 1, - uploadedAt: 1, - }, - }; - - return this.find(fileQuery, fileOptions); - } - - insertFileInit(userId, store, file, extra) { - const fileData = { - userId, - store, - complete: false, - uploading: true, - progress: 0, - extension: s.strRightBack(file.name, '.'), - uploadedAt: new Date(), - }; - - _.extend(fileData, file, extra); - - if (this.model.direct && this.model.direct.insert != null) { - file = this.model.direct.insert(fileData); - } else { - file = this.insert(fileData); - } - - return file; - } - - updateFileComplete(fileId, userId, file) { - let result; - if (!fileId) { - return; - } - - const filter = { - _id: fileId, - userId, - }; - - const update = { - $set: { - complete: true, - uploading: false, - progress: 1, - }, - }; - - update.$set = _.extend(file, update.$set); - - if (this.model.direct && this.model.direct.update != null) { - result = this.model.direct.update(filter, update); - } else { - result = this.update(filter, update); - } - - return result; - } - - deleteFile(fileId) { - if (this.model.direct && this.model.direct.remove != null) { - return this.model.direct.remove({ _id: fileId }); - } else { - return this.remove({ _id: fileId }); - } - } -}; +RocketChat.models.Uploads = Uploads; diff --git a/packages/rocketchat-lib/server/models/UserDataFiles.js b/packages/rocketchat-lib/server/models/UserDataFiles.js index 8ed425dcf178..b3eff0ac6125 100644 --- a/packages/rocketchat-lib/server/models/UserDataFiles.js +++ b/packages/rocketchat-lib/server/models/UserDataFiles.js @@ -1,40 +1,3 @@ -import _ from 'underscore'; +import { UserDataFiles } from 'meteor/rocketchat:models'; -RocketChat.models.UserDataFiles = new class ModelUserDataFiles extends RocketChat.models._Base { - constructor() { - super('user_data_files'); - - this.tryEnsureIndex({ userId: 1 }); - } - - // FIND - findById(id) { - const query = { _id: id }; - return this.find(query); - } - - findLastFileByUser(userId, options = {}) { - const query = { - userId, - }; - - options.sort = { _updatedAt : -1 }; - return this.findOne(query, options); - } - - // INSERT - create(data) { - const userDataFile = { - createdAt: new Date, - }; - - _.extend(userDataFile, data); - - return this.insert(userDataFile); - } - - // REMOVE - removeById(_id) { - return this.remove(_id); - } -}; +RocketChat.models.UserDataFiles = UserDataFiles; diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js index bc9ac2f61927..8918c19aa575 100644 --- a/packages/rocketchat-lib/server/models/Users.js +++ b/packages/rocketchat-lib/server/models/Users.js @@ -1,669 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; -import _ from 'underscore'; -import s from 'underscore.string'; +import { Users } from 'meteor/rocketchat:models'; -class ModelUsers extends RocketChat.models._Base { - constructor(...args) { - super(...args); +RocketChat.models.Users = Users; - this.tryEnsureIndex({ roles: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ name: 1 }); - this.tryEnsureIndex({ lastLogin: 1 }); - this.tryEnsureIndex({ status: 1 }); - this.tryEnsureIndex({ active: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ statusConnection: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ type: 1 }); - } - - findOneByImportId(_id, options) { - return this.findOne({ importIds: _id }, options); - } - - findOneByUsername(username, options) { - if (typeof username === 'string') { - username = new RegExp(`^${ username }$`, 'i'); - } - - const query = { username }; - - return this.findOne(query, options); - } - - findOneByEmailAddress(emailAddress, options) { - const query = { 'emails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i') }; - - return this.findOne(query, options); - } - - findOneAdmin(admin, options) { - const query = { admin }; - - return this.findOne(query, options); - } - - findOneByIdAndLoginToken(_id, token, options) { - const query = { - _id, - 'services.resume.loginTokens.hashedToken' : Accounts._hashLoginToken(token), - }; - - return this.findOne(query, options); - } - - findOneById(userId, options) { - const query = { _id: userId }; - - return this.findOne(query, options); - } - - // FIND - findById(userId) { - const query = { _id: userId }; - - return this.find(query); - } - - findByIds(users, options) { - const query = { _id: { $in: users } }; - return this.find(query, options); - } - - findUsersNotOffline(options) { - const query = { - username: { - $exists: 1, - }, - status: { - $in: ['online', 'away', 'busy'], - }, - }; - - return this.find(query, options); - } - - findByRoomId(rid, options) { - const data = RocketChat.models.Subscriptions.findByRoomId(rid).fetch().map((item) => item.u._id); - const query = { - _id: { - $in: data, - }, - }; - - return this.find(query, options); - } - - findByUsername(username, options) { - const query = { username }; - - return this.find(query, options); - } - - findActiveByUsernameOrNameRegexWithExceptions(searchTerm, exceptions, options) { - if (exceptions == null) { exceptions = []; } - if (options == null) { options = {}; } - if (!_.isArray(exceptions)) { - exceptions = [exceptions]; - } - - const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i'); - const query = { - $or: [{ - username: termRegex, - }, { - name: termRegex, - }], - active: true, - type: { - $in: ['user', 'bot'], - }, - $and: [{ - username: { - $exists: true, - }, - }, { - username: { - $nin: exceptions, - }, - }], - }; - - return this.find(query, options); - } - - findByActiveUsersExcept(searchTerm, exceptions, options) { - if (exceptions == null) { exceptions = []; } - if (options == null) { options = {}; } - if (!_.isArray(exceptions)) { - exceptions = [exceptions]; - } - - const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i'); - - const orStmt = _.reduce(RocketChat.settings.get('Accounts_SearchFields').trim().split(','), function(acc, el) { - acc.push({ [el.trim()]: termRegex }); - return acc; - }, []); - const query = { - $and: [ - { - active: true, - $or: orStmt, - }, - { - username: { $exists: true, $nin: exceptions }, - }, - ], - }; - - // do not use cache - return this._db.find(query, options); - } - - findUsersByNameOrUsername(nameOrUsername, options) { - const query = { - username: { - $exists: 1, - }, - - $or: [ - { name: nameOrUsername }, - { username: nameOrUsername }, - ], - - type: { - $in: ['user'], - }, - }; - - return this.find(query, options); - } - - findByUsernameNameOrEmailAddress(usernameNameOrEmailAddress, options) { - const query = { - $or: [ - { name: usernameNameOrEmailAddress }, - { username: usernameNameOrEmailAddress }, - { 'emails.address': usernameNameOrEmailAddress }, - ], - type: { - $in: ['user', 'bot'], - }, - }; - - return this.find(query, options); - } - - findLDAPUsers(options) { - const query = { ldap: true }; - - return this.find(query, options); - } - - findCrowdUsers(options) { - const query = { crowd: true }; - - return this.find(query, options); - } - - getLastLogin(options) { - if (options == null) { options = {}; } - const query = { lastLogin: { $exists: 1 } }; - options.sort = { lastLogin: -1 }; - options.limit = 1; - const [user] = this.find(query, options).fetch(); - return user && user.lastLogin; - } - - findUsersByUsernames(usernames, options) { - const query = { - username: { - $in: usernames, - }, - }; - - return this.find(query, options); - } - - findUsersByIds(ids, options) { - const query = { - _id: { - $in: ids, - }, - }; - return this.find(query, options); - } - - findUsersWithUsernameByIds(ids, options) { - const query = { - _id: { - $in: ids, - }, - username: { - $exists: 1, - }, - }; - - return this.find(query, options); - } - - findUsersWithUsernameByIdsNotOffline(ids, options) { - const query = { - _id: { - $in: ids, - }, - username: { - $exists: 1, - }, - status: { - $in: ['online', 'away', 'busy'], - }, - }; - - return this.find(query, options); - } - - getOldest(fields = { _id: 1 }) { - const query = { - _id: { - $ne: 'rocket.cat', - }, - }; - - const options = { - fields, - sort: { - createdAt: 1, - }, - }; - - return this.findOne(query, options); - } - - // UPDATE - addImportIds(_id, importIds) { - importIds = [].concat(importIds); - - const query = { _id }; - - const update = { - $addToSet: { - importIds: { - $each: importIds, - }, - }, - }; - - return this.update(query, update); - } - - updateLastLoginById(_id) { - const update = { - $set: { - lastLogin: new Date, - }, - }; - - return this.update(_id, update); - } - - setServiceId(_id, serviceName, serviceId) { - const update = - { $set: {} }; - - const serviceIdKey = `services.${ serviceName }.id`; - update.$set[serviceIdKey] = serviceId; - - return this.update(_id, update); - } - - setUsername(_id, username) { - const update = - { $set: { username } }; - - return this.update(_id, update); - } - - setEmail(_id, email) { - const update = { - $set: { - emails: [{ - address: email, - verified: false, - }, - ], - }, - }; - - return this.update(_id, update); - } - - setEmailVerified(_id, email) { - const query = { - _id, - emails: { - $elemMatch: { - address: email, - verified: false, - }, - }, - }; - - const update = { - $set: { - 'emails.$.verified': true, - }, - }; - - return this.update(query, update); - } - - setName(_id, name) { - const update = { - $set: { - name, - }, - }; - - return this.update(_id, update); - } - - setCustomFields(_id, fields) { - const values = {}; - Object.keys(fields).forEach((key) => { - values[`customFields.${ key }`] = fields[key]; - }); - - const update = { $set: values }; - - return this.update(_id, update); - } - - setAvatarOrigin(_id, origin) { - const update = { - $set: { - avatarOrigin: origin, - }, - }; - - return this.update(_id, update); - } - - unsetAvatarOrigin(_id) { - const update = { - $unset: { - avatarOrigin: 1, - }, - }; - - return this.update(_id, update); - } - - setUserActive(_id, active) { - if (active == null) { active = true; } - const update = { - $set: { - active, - }, - }; - - return this.update(_id, update); - } - - setAllUsersActive(active) { - const update = { - $set: { - active, - }, - }; - - return this.update({}, update, { multi: true }); - } - - unsetLoginTokens(_id) { - const update = { - $set: { - 'services.resume.loginTokens' : [], - }, - }; - - return this.update(_id, update); - } - - unsetRequirePasswordChange(_id) { - const update = { - $unset: { - requirePasswordChange : true, - requirePasswordChangeReason : true, - }, - }; - - return this.update(_id, update); - } - - resetPasswordAndSetRequirePasswordChange(_id, requirePasswordChange, requirePasswordChangeReason) { - const update = { - $unset: { - 'services.password': 1, - }, - $set: { - requirePasswordChange, - requirePasswordChangeReason, - }, - }; - - return this.update(_id, update); - } - - setLanguage(_id, language) { - const update = { - $set: { - language, - }, - }; - - return this.update(_id, update); - } - - setProfile(_id, profile) { - const update = { - $set: { - 'settings.profile': profile, - }, - }; - - return this.update(_id, update); - } - - clearSettings(_id) { - const update = { - $set: { - settings: {}, - }, - }; - - return this.update(_id, update); - } - - setPreferences(_id, preferences) { - const settings = Object.assign( - {}, - ...Object.keys(preferences).map((key) => ({ [`settings.preferences.${ key }`]: preferences[key] })) - ); - - const update = { - $set: settings, - }; - if (parseInt(preferences.clockMode) === 0) { - delete update.$set['settings.preferences.clockMode']; - update.$unset = { 'settings.preferences.clockMode': 1 }; - } - - return this.update(_id, update); - } - - setUtcOffset(_id, utcOffset) { - const query = { - _id, - utcOffset: { - $ne: utcOffset, - }, - }; - - const update = { - $set: { - utcOffset, - }, - }; - - return this.update(query, update); - } - - saveUserById(_id, data) { - const setData = {}; - const unsetData = {}; - - if (data.name != null) { - if (!_.isEmpty(s.trim(data.name))) { - setData.name = s.trim(data.name); - } else { - unsetData.name = 1; - } - } - - if (data.email != null) { - if (!_.isEmpty(s.trim(data.email))) { - setData.emails = [{ address: s.trim(data.email) }]; - } else { - unsetData.emails = 1; - } - } - - if (data.phone != null) { - if (!_.isEmpty(s.trim(data.phone))) { - setData.phone = [{ phoneNumber: s.trim(data.phone) }]; - } else { - unsetData.phone = 1; - } - } - - const update = {}; - - if (!_.isEmpty(setData)) { - update.$set = setData; - } - - if (!_.isEmpty(unsetData)) { - update.$unset = unsetData; - } - - if (_.isEmpty(update)) { - return true; - } - - return this.update({ _id }, update); - } - - setReason(_id, reason) { - const update = { - $set: { - reason, - }, - }; - - return this.update(_id, update); - } - - unsetReason(_id) { - const update = { - $unset: { - reason: true, - }, - }; - - return this.update(_id, update); - } - - addBannerById(_id, banner) { - const update = { - $set: { - [`banners.${ banner.id }`]: banner, - }, - }; - - return this.update({ _id }, update); - } - - removeBannerById(_id, banner) { - const update = { - $unset: { - [`banners.${ banner.id }`]: true, - }, - }; - - return this.update({ _id }, update); - } - - removeResumeService(_id) { - const update = { - $unset: { - 'services.resume': '', - }, - }; - - return this.update({ _id }, update); - } - - // INSERT - create(data) { - const user = { - createdAt: new Date, - avatarOrigin: 'none', - }; - - _.extend(user, data); - - return this.insert(user); - } - - - // REMOVE - removeById(_id) { - return this.remove(_id); - } - - /* -Find users to send a message by email if: -- he is not online -- has a verified email -- has not disabled email notifications -- `active` is equal to true (false means they were deactivated and can't login) -*/ - getUsersToSendOfflineEmail(usersIds) { - const query = { - _id: { - $in: usersIds, - }, - active: true, - status: 'offline', - statusConnection: { - $ne: 'online', - }, - 'emails.verified': true, - }; - - const options = { - fields: { - name: 1, - username: 1, - emails: 1, - 'settings.preferences.emailNotificationMode': 1, - language: 1, - }, - }; - - return this.find(query, options); - } -} - -RocketChat.models.Users = new ModelUsers(Meteor.users, true); diff --git a/packages/rocketchat-lib/server/models/_Base.js b/packages/rocketchat-lib/server/models/_Base.js index d09b91760b07..bfda6cf65cce 100644 --- a/packages/rocketchat-lib/server/models/_Base.js +++ b/packages/rocketchat-lib/server/models/_Base.js @@ -1,285 +1,3 @@ -import { check } from 'meteor/check'; -import ModelsBaseDb from './_BaseDb'; -import objectPath from 'object-path'; -import _ from 'underscore'; +import { Base } from 'meteor/rocketchat:models'; -class ModelsBase { - constructor(nameOrModel) { - this._db = new ModelsBaseDb(nameOrModel, this); - this.model = this._db.model; - this.collectionName = this._db.collectionName; - this.name = this._db.name; - - this.on = this._db.on.bind(this._db); - this.emit = this._db.emit.bind(this._db); - - this.db = this; - } - - get origin() { - return '_db'; - } - - arrayToCursor(data) { - return { - fetch() { - return data; - }, - count() { - return data.length; - }, - forEach(fn) { - return data.forEach(fn); - }, - }; - } - - setUpdatedAt(...args/* record, checkQuery, query*/) { - return this._db.setUpdatedAt(...args); - } - - find(...args) { - try { - return this[this.origin].find(...args); - } catch (e) { - console.error('Exception on find', e, ...args); - } - } - - findOne(...args) { - try { - return this[this.origin].findOne(...args); - } catch (e) { - console.error('Exception on find', e, ...args); - } - } - - findOneById(...args) { - try { - return this[this.origin].findOneById(...args); - } catch (e) { - console.error('Exception on find', e, ...args); - } - } - - findOneByIds(ids, options, ...args) { - check(ids, [String]); - - try { - return this[this.origin].findOneByIds(ids, options); - } catch (e) { - console.error('Exception on find', e, [ids, options, ...args]); - } - } - - insert(...args/* record*/) { - return this._db.insert(...args); - } - - update(...args/* query, update, options*/) { - return this._db.update(...args); - } - - upsert(...args/* query, update*/) { - return this._db.upsert(...args); - } - - remove(...args/* query*/) { - return this._db.remove(...args); - } - - insertOrUpsert(...args) { - return this._db.insertOrUpsert(...args); - } - - allow(...args) { - return this._db.allow(...args); - } - - deny(...args) { - return this._db.deny(...args); - } - - ensureIndex(...args) { - return this._db.ensureIndex(...args); - } - - dropIndex(...args) { - return this._db.dropIndex(...args); - } - - tryEnsureIndex(...args) { - return this._db.tryEnsureIndex(...args); - } - - tryDropIndex(...args) { - return this._db.tryDropIndex(...args); - } - - trashFind(...args/* query, options*/) { - return this._db.trashFind(...args); - } - - trashFindOneById(...args/* _id, options*/) { - return this._db.trashFindOneById(...args); - } - - trashFindDeletedAfter(...args/* deletedAt, query, options*/) { - return this._db.trashFindDeletedAfter(...args); - } - - trashFindDeleted(...args) { - return this._db.trashFindDeleted(...args); - } - - processQueryOptionsOnResult(result, options = {}) { - if (result === undefined || result === null) { - return undefined; - } - - if (Array.isArray(result)) { - if (options.sort) { - result = result.sort((a, b) => { - let r = 0; - for (const field in options.sort) { - if (options.sort.hasOwnProperty(field)) { - const direction = options.sort[field]; - let valueA; - let valueB; - if (field.indexOf('.') > -1) { - valueA = objectPath.get(a, field); - valueB = objectPath.get(b, field); - } else { - valueA = a[field]; - valueB = b[field]; - } - if (valueA > valueB) { - r = direction; - break; - } - if (valueA < valueB) { - r = -direction; - break; - } - } - } - return r; - }); - } - - if (typeof options.skip === 'number') { - result.splice(0, options.skip); - } - - if (typeof options.limit === 'number' && options.limit !== 0) { - result.splice(options.limit); - } - } - - if (!options.fields) { - options.fields = {}; - } - - const fieldsToRemove = []; - const fieldsToGet = []; - - for (const field in options.fields) { - if (options.fields.hasOwnProperty(field)) { - if (options.fields[field] === 0) { - fieldsToRemove.push(field); - } else if (options.fields[field] === 1) { - fieldsToGet.push(field); - } - } - } - - if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) { - console.warn('Can\'t mix remove and get fields'); - fieldsToRemove.splice(0, fieldsToRemove.length); - } - - if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id') === -1) { - fieldsToGet.push('_id'); - } - - const pickFields = (obj, fields) => { - const picked = {}; - fields.forEach((field) => { - if (field.indexOf('.') !== -1) { - objectPath.set(picked, field, objectPath.get(obj, field)); - } else { - picked[field] = obj[field]; - } - }); - return picked; - }; - - if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) { - if (Array.isArray(result)) { - result = result.map((record) => { - if (fieldsToRemove.length > 0) { - return _.omit(record, ...fieldsToRemove); - } - - if (fieldsToGet.length > 0) { - return pickFields(record, fieldsToGet); - } - - return null; - }); - } else { - if (fieldsToRemove.length > 0) { - return _.omit(result, ...fieldsToRemove); - } - - if (fieldsToGet.length > 0) { - return pickFields(result, fieldsToGet); - } - } - } - - return result; - } - - // dinamicTrashFindAfter(method, deletedAt, ...args) { - // const scope = { - // find: (query={}) => { - // return this.trashFindDeletedAfter(deletedAt, query, { fields: {_id: 1, _deletedAt: 1} }); - // } - // }; - - // scope.model = { - // find: scope.find - // }; - - // return this[method].apply(scope, args); - // } - - // dinamicFindAfter(method, updatedAt, ...args) { - // const scope = { - // find: (query={}, options) => { - // query._updatedAt = { - // $gt: updatedAt - // }; - - // return this.find(query, options); - // } - // }; - - // scope.model = { - // find: scope.find - // }; - - // return this[method].apply(scope, args); - // } - - // dinamicFindChangesAfter(method, updatedAt, ...args) { - // return { - // update: this.dinamicFindAfter(method, updatedAt, ...args).fetch(), - // remove: this.dinamicTrashFindAfter(method, updatedAt, ...args).fetch() - // }; - // } - -} - -RocketChat.models._Base = ModelsBase; +RocketChat.models._Base = Base; diff --git a/packages/rocketchat-models/client/index.js b/packages/rocketchat-models/client/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rocketchat-models/package.js b/packages/rocketchat-models/package.js new file mode 100755 index 000000000000..780626a8c855 --- /dev/null +++ b/packages/rocketchat-models/package.js @@ -0,0 +1,17 @@ +Package.describe({ + name: 'rocketchat:models', + summary: 'RocketChat Models', + version: '1.0.0', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:settings', + 'rocketchat:utils', + 'konecty:multiple-instances-status', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-models/server/index.js b/packages/rocketchat-models/server/index.js new file mode 100644 index 000000000000..372205cdf8fa --- /dev/null +++ b/packages/rocketchat-models/server/index.js @@ -0,0 +1,27 @@ +import { Base } from './models/_Base'; +import { BaseDb } from './models/_BaseDb'; +import Avatars from './models/Avatars'; +import ExportOperations from './models/ExportOperations'; +import Messages from './models/Messages'; +import Reports from './models/Reports'; +import Rooms from './models/Rooms'; +import Settings from './models/Settings'; +import Subscriptions from './models/Subscriptions'; +import Uploads from './models/Uploads'; +import UserDataFiles from './models/UserDataFiles'; +import Users from './models/Users'; + +export { + Base, + BaseDb, + Avatars, + ExportOperations, + Messages, + Reports, + Rooms, + Settings, + Subscriptions, + Uploads, + UserDataFiles, + Users, +}; diff --git a/packages/rocketchat-models/server/models/Avatars.js b/packages/rocketchat-models/server/models/Avatars.js new file mode 100644 index 000000000000..1859f3d29a6d --- /dev/null +++ b/packages/rocketchat-models/server/models/Avatars.js @@ -0,0 +1,116 @@ +import _ from 'underscore'; +import s from 'underscore.string'; +import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; +import { Base } from './_Base'; + +export class Avatars extends Base { + constructor() { + super('avatars'); + + this.model.before.insert((userId, doc) => { + doc.instanceId = InstanceStatus.id(); + }); + + this.tryEnsureIndex({ name: 1 }); + } + + insertAvatarFileInit(name, userId, store, file, extra) { + const fileData = { + _id: name, + name, + userId, + store, + complete: false, + uploading: true, + progress: 0, + extension: s.strRightBack(file.name, '.'), + uploadedAt: new Date(), + }; + + _.extend(fileData, file, extra); + + return this.insertOrUpsert(fileData); + } + + updateFileComplete(fileId, userId, file) { + if (!fileId) { + return; + } + + const filter = { + _id: fileId, + userId, + }; + + const update = { + $set: { + complete: true, + uploading: false, + progress: 1, + }, + }; + + update.$set = _.extend(file, update.$set); + + if (this.model.direct && this.model.direct.update) { + return this.model.direct.update(filter, update); + } else { + return this.update(filter, update); + } + } + + findOneByName(name) { + return this.findOne({ name }); + } + + updateFileNameById(fileId, name) { + const filter = { _id: fileId }; + const update = { + $set: { + name, + }, + }; + if (this.model.direct && this.model.direct.update) { + return this.model.direct.update(filter, update); + } else { + return this.update(filter, update); + } + } + + // @TODO deprecated + updateFileCompleteByNameAndUserId(name, userId, url) { + if (!name) { + return; + } + + const filter = { + name, + userId, + }; + + const update = { + $set: { + complete: true, + uploading: false, + progress: 1, + url, + }, + }; + + if (this.model.direct && this.model.direct.update) { + return this.model.direct.update(filter, update); + } else { + return this.update(filter, update); + } + } + + deleteFile(fileId) { + if (this.model.direct && this.model.direct.remove) { + return this.model.direct.remove({ _id: fileId }); + } else { + return this.remove({ _id: fileId }); + } + } +} + +export default new Avatars(); diff --git a/packages/rocketchat-models/server/models/ExportOperations.js b/packages/rocketchat-models/server/models/ExportOperations.js new file mode 100644 index 000000000000..5a0d1dc60f6a --- /dev/null +++ b/packages/rocketchat-models/server/models/ExportOperations.js @@ -0,0 +1,81 @@ +import { Base } from './_Base'; +import _ from 'underscore'; + +export class ExportOperations extends Base { + constructor() { + super('export_operations'); + + this.tryEnsureIndex({ userId: 1 }); + this.tryEnsureIndex({ status: 1 }); + } + + // FIND + findById(id) { + const query = { _id: id }; + + return this.find(query); + } + + findLastOperationByUser(userId, fullExport = false, options = {}) { + const query = { + userId, + fullExport, + }; + + options.sort = { createdAt : -1 }; + return this.findOne(query, options); + } + + findPendingByUser(userId, options) { + const query = { + userId, + status: { + $nin: ['completed'], + }, + }; + + return this.find(query, options); + } + + findAllPending(options) { + const query = { + status: { $nin: ['completed'] }, + }; + + return this.find(query, options); + } + + // UPDATE + updateOperation(data) { + const update = { + $set: { + roomList: data.roomList, + status: data.status, + fileList: data.fileList, + generatedFile: data.generatedFile, + }, + }; + + return this.update(data._id, update); + } + + + // INSERT + create(data) { + const exportOperation = { + createdAt: new Date, + }; + + _.extend(exportOperation, data); + + return this.insert(exportOperation); + } + + + // REMOVE + removeById(_id) { + return this.remove(_id); + } +} + +export default new ExportOperations(); diff --git a/packages/rocketchat-models/server/models/Messages.js b/packages/rocketchat-models/server/models/Messages.js new file mode 100644 index 000000000000..f7993db20d48 --- /dev/null +++ b/packages/rocketchat-models/server/models/Messages.js @@ -0,0 +1,839 @@ +import { Meteor } from 'meteor/meteor'; +import { Match } from 'meteor/check'; +import { settings } from 'meteor/rocketchat:settings'; +import { Base } from './_Base'; +import Rooms from './Rooms'; +import Users from './Users'; +import _ from 'underscore'; + +export class Messages extends Base { + constructor() { + super('message'); + + this.tryEnsureIndex({ rid: 1, ts: 1 }); + this.tryEnsureIndex({ ts: 1 }); + this.tryEnsureIndex({ 'u._id': 1 }); + this.tryEnsureIndex({ editedAt: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ 'editedBy._id': 1 }, { sparse: 1 }); + this.tryEnsureIndex({ rid: 1, t: 1, 'u._id': 1 }); + this.tryEnsureIndex({ expireAt: 1 }, { expireAfterSeconds: 0 }); + this.tryEnsureIndex({ msg: 'text' }); + this.tryEnsureIndex({ 'file._id': 1 }, { sparse: 1 }); + this.tryEnsureIndex({ 'mentions.username': 1 }, { sparse: 1 }); + this.tryEnsureIndex({ pinned: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ snippeted: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ location: '2dsphere' }); + this.tryEnsureIndex({ slackBotId: 1, slackTs: 1 }, { sparse: 1 }); + } + + countVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $gte: afterTimestamp, + $lte: beforeTimestamp, + }, + }; + + return this.find(query, options).count(); + } + + // FIND + findByMention(username, options) { + const query = { 'mentions.username': username }; + + return this.find(query, options); + } + + findFilesByUserId(userId, options = {}) { + const query = { + 'u._id': userId, + 'file._id': { $exists: true }, + }; + return this.find(query, { fields: { 'file._id': 1 }, ...options }); + } + + findFilesByRoomIdPinnedTimestampAndUsers(rid, excludePinned, ts, users = [], options = {}) { + const query = { + rid, + ts, + 'file._id': { $exists: true }, + }; + + if (excludePinned) { + query.pinned = { $ne: true }; + } + + if (users.length) { + query['u.username'] = { $in: users }; + } + + return this.find(query, { fields: { 'file._id': 1 }, ...options }); + } + findVisibleByMentionAndRoomId(username, rid, options) { + const query = { + _hidden: { $ne: true }, + 'mentions.username': username, + rid, + }; + + return this.find(query, options); + } + + findVisibleByRoomId(roomId, options) { + const query = { + _hidden: { + $ne: true, + }, + + rid: roomId, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdNotContainingTypes(roomId, types, options) { + const query = { + _hidden: { + $ne: true, + }, + + rid: roomId, + }; + + if (Match.test(types, [String]) && (types.length > 0)) { + query.t = + { $nin: types }; + } + + return this.find(query, options); + } + + findInvisibleByRoomId(roomId, options) { + const query = { + _hidden: true, + rid: roomId, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdAfterTimestamp(roomId, timestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $gt: timestamp, + }, + }; + + return this.find(query, options); + } + + findForUpdates(roomId, timestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + _updatedAt: { + $gt: timestamp, + }, + }; + return this.find(query, options); + } + + findVisibleByRoomIdBeforeTimestamp(roomId, timestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $lt: timestamp, + }, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdBeforeTimestampInclusive(roomId, timestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $lte: timestamp, + }, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdBetweenTimestamps(roomId, afterTimestamp, beforeTimestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $gt: afterTimestamp, + $lt: beforeTimestamp, + }, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $gte: afterTimestamp, + $lte: beforeTimestamp, + }, + }; + + return this.find(query, options); + } + + findVisibleByRoomIdBeforeTimestampNotContainingTypes(roomId, timestamp, types, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $lt: timestamp, + }, + }; + + if (Match.test(types, [String]) && (types.length > 0)) { + query.t = + { $nin: types }; + } + + return this.find(query, options); + } + + findVisibleByRoomIdBetweenTimestampsNotContainingTypes(roomId, afterTimestamp, beforeTimestamp, types, options) { + const query = { + _hidden: { + $ne: true, + }, + rid: roomId, + ts: { + $gt: afterTimestamp, + $lt: beforeTimestamp, + }, + }; + + if (Match.test(types, [String]) && (types.length > 0)) { + query.t = + { $nin: types }; + } + + return this.find(query, options); + } + + findVisibleCreatedOrEditedAfterTimestamp(timestamp, options) { + const query = { + _hidden: { $ne: true }, + $or: [{ + ts: { + $gt: timestamp, + }, + }, + { + editedAt: { + $gt: timestamp, + }, + }, + ], + }; + + return this.find(query, options); + } + + findStarredByUserAtRoom(userId, roomId, options) { + const query = { + _hidden: { $ne: true }, + 'starred._id': userId, + rid: roomId, + }; + + return this.find(query, options); + } + + findPinnedByRoom(roomId, options) { + const query = { + t: { $ne: 'rm' }, + _hidden: { $ne: true }, + pinned: true, + rid: roomId, + }; + + return this.find(query, options); + } + + findSnippetedByRoom(roomId, options) { + const query = { + _hidden: { $ne: true }, + snippeted: true, + rid: roomId, + }; + + return this.find(query, options); + } + + getLastTimestamp(options) { + if (options == null) { options = {}; } + const query = { ts: { $exists: 1 } }; + options.sort = { ts: -1 }; + options.limit = 1; + const [message] = this.find(query, options).fetch(); + return message && message.ts; + } + + findByRoomIdAndMessageIds(rid, messageIds, options) { + const query = { + rid, + _id: { + $in: messageIds, + }, + }; + + return this.find(query, options); + } + + findOneBySlackBotIdAndSlackTs(slackBotId, slackTs) { + const query = { + slackBotId, + slackTs, + }; + + return this.findOne(query); + } + + findOneBySlackTs(slackTs) { + const query = { slackTs }; + + return this.findOne(query); + } + + findByRoomIdAndType(roomId, type, options) { + const query = { + rid: roomId, + t: type, + }; + + if (options == null) { options = {}; } + + return this.find(query, options); + } + + findByRoomId(roomId, options) { + const query = { + rid: roomId, + }; + + return this.find(query, options); + } + + getLastVisibleMessageSentWithNoTypeByRoomId(rid, messageId) { + const query = { + rid, + _hidden: { $ne: true }, + t: { $exists: false }, + }; + + if (messageId) { + query._id = { $ne: messageId }; + } + + const options = { + sort: { + ts: -1, + }, + }; + + return this.findOne(query, options); + } + + cloneAndSaveAsHistoryById(_id) { + const me = Users.findOneById(Meteor.userId()); + const record = this.findOneById(_id); + record._hidden = true; + record.parent = record._id; + record.editedAt = new Date; + record.editedBy = { + _id: Meteor.userId(), + username: me.username, + }; + delete record._id; + return this.insert(record); + } + + // UPDATE + setHiddenById(_id, hidden) { + if (hidden == null) { hidden = true; } + const query = { _id }; + + const update = { + $set: { + _hidden: hidden, + }, + }; + + return this.update(query, update); + } + + setAsDeletedByIdAndUser(_id, user) { + const query = { _id }; + + const update = { + $set: { + msg: '', + t: 'rm', + urls: [], + mentions: [], + attachments: [], + reactions: [], + editedAt: new Date(), + editedBy: { + _id: user._id, + username: user.username, + }, + }, + }; + + return this.update(query, update); + } + + setPinnedByIdAndUserId(_id, pinnedBy, pinned, pinnedAt) { + if (pinned == null) { pinned = true; } + if (pinnedAt == null) { pinnedAt = 0; } + const query = { _id }; + + const update = { + $set: { + pinned, + pinnedAt: pinnedAt || new Date, + pinnedBy, + }, + }; + + return this.update(query, update); + } + + setSnippetedByIdAndUserId(message, snippetName, snippetedBy, snippeted, snippetedAt) { + if (snippeted == null) { snippeted = true; } + if (snippetedAt == null) { snippetedAt = 0; } + const query = { _id: message._id }; + + const msg = `\`\`\`${ message.msg }\`\`\``; + + const update = { + $set: { + msg, + snippeted, + snippetedAt: snippetedAt || new Date, + snippetedBy, + snippetName, + }, + }; + + return this.update(query, update); + } + + setUrlsById(_id, urls) { + const query = { _id }; + + const update = { + $set: { + urls, + }, + }; + + return this.update(query, update); + } + + updateAllUsernamesByUserId(userId, username) { + const query = { 'u._id': userId }; + + const update = { + $set: { + 'u.username': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateUsernameOfEditByUserId(userId, username) { + const query = { 'editedBy._id': userId }; + + const update = { + $set: { + 'editedBy.username': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateUsernameAndMessageOfMentionByIdAndOldUsername(_id, oldUsername, newUsername, newMessage) { + const query = { + _id, + 'mentions.username': oldUsername, + }; + + const update = { + $set: { + 'mentions.$.username': newUsername, + msg: newMessage, + }, + }; + + return this.update(query, update); + } + + updateUserStarById(_id, userId, starred) { + let update; + const query = { _id }; + + if (starred) { + update = { + $addToSet: { + starred: { _id: userId }, + }, + }; + } else { + update = { + $pull: { + starred: { _id: Meteor.userId() }, + }, + }; + } + + return this.update(query, update); + } + + upgradeEtsToEditAt() { + const query = { ets: { $exists: 1 } }; + + const update = { + $rename: { + ets: 'editedAt', + }, + }; + + return this.update(query, update, { multi: true }); + } + + setMessageAttachments(_id, attachments) { + const query = { _id }; + + const update = { + $set: { + attachments, + }, + }; + + return this.update(query, update); + } + + setSlackBotIdAndSlackTs(_id, slackBotId, slackTs) { + const query = { _id }; + + const update = { + $set: { + slackBotId, + slackTs, + }, + }; + + return this.update(query, update); + } + + unlinkUserId(userId, newUserId, newUsername, newNameAlias) { + const query = { + 'u._id': userId, + }; + + const update = { + $set: { + alias: newNameAlias, + 'u._id': newUserId, + 'u.username' : newUsername, + 'u.name' : undefined, + }, + }; + + return this.update(query, update, { multi: true }); + } + + // INSERT + createWithTypeRoomIdMessageAndUser(type, roomId, message, user, extraData) { + const room = Rooms.findOneById(roomId, { fields: { sysMes: 1 } }); + if ((room != null ? room.sysMes : undefined) === false) { + return; + } + const record = { + t: type, + rid: roomId, + ts: new Date, + msg: message, + u: { + _id: user._id, + username: user.username, + }, + groupable: false, + }; + + if (settings.get('Message_Read_Receipt_Enabled')) { + record.unread = true; + } + + _.extend(record, extraData); + + record._id = this.insertOrUpsert(record); + Rooms.incMsgCountById(room._id, 1); + return record; + } + + createNavigationHistoryWithRoomIdMessageAndUser(roomId, message, user, extraData) { + const type = 'livechat_navigation_history'; + const room = Rooms.findOneById(roomId, { fields: { sysMes: 1 } }); + if ((room != null ? room.sysMes : undefined) === false) { + return; + } + const record = { + t: type, + rid: roomId, + ts: new Date, + msg: message, + u: { + _id: user._id, + username: user.username, + }, + groupable: false, + }; + + if (settings.get('Message_Read_Receipt_Enabled')) { + record.unread = true; + } + + _.extend(record, extraData); + + record._id = this.insertOrUpsert(record); + return record; + } + + createUserJoinWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('uj', roomId, message, user, extraData); + } + + createUserLeaveWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('ul', roomId, message, user, extraData); + } + + createUserRemovedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('ru', roomId, message, user, extraData); + } + + createUserAddedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('au', roomId, message, user, extraData); + } + + createCommandWithRoomIdAndUser(command, roomId, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('command', roomId, command, user, extraData); + } + + createUserMutedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('user-muted', roomId, message, user, extraData); + } + + createUserUnmutedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('user-unmuted', roomId, message, user, extraData); + } + + createNewModeratorWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('new-moderator', roomId, message, user, extraData); + } + + createModeratorRemovedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('moderator-removed', roomId, message, user, extraData); + } + + createNewOwnerWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('new-owner', roomId, message, user, extraData); + } + + createOwnerRemovedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('owner-removed', roomId, message, user, extraData); + } + + createNewLeaderWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('new-leader', roomId, message, user, extraData); + } + + createLeaderRemovedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('leader-removed', roomId, message, user, extraData); + } + + createSubscriptionRoleAddedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('subscription-role-added', roomId, message, user, extraData); + } + + createSubscriptionRoleRemovedWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('subscription-role-removed', roomId, message, user, extraData); + } + + // REMOVE + removeById(_id) { + const query = { _id }; + + return this.remove(query); + } + + removeByRoomId(roomId) { + const query = { rid: roomId }; + + return this.remove(query); + } + + removeByIdPinnedTimestampAndUsers(rid, pinned, ts, users = []) { + const query = { + rid, + ts, + }; + + if (pinned) { + query.pinned = { $ne: true }; + } + + if (users.length) { + query['u.username'] = { $in: users }; + } + + return this.remove(query); + } + + removeByIdPinnedTimestampLimitAndUsers(rid, pinned, ts, limit, users = []) { + const query = { + rid, + ts, + }; + + if (pinned) { + query.pinned = { $ne: true }; + } + + if (users.length) { + query['u.username'] = { $in: users }; + } + + const messagesToDelete = this.find(query, { + fields: { + _id: 1, + }, + limit, + }).map(({ _id }) => _id); + + return this.remove({ + _id: { + $in: messagesToDelete, + }, + }); + } + + removeByUserId(userId) { + const query = { 'u._id': userId }; + + return this.remove(query); + } + + removeFilesByRoomId(roomId) { + this.find({ + rid: roomId, + 'file._id': { + $exists: true, + }, + }, { + fields: { + 'file._id': 1, + }, + }).fetch().forEach((document) => FileUpload.getStore('Uploads').deleteById(document.file._id)); + } + + getMessageByFileId(fileID) { + return this.findOne({ 'file._id': fileID }); + } + + setAsRead(rid, until) { + return this.update({ + rid, + unread: true, + ts: { $lt: until }, + }, { + $unset: { + unread: 1, + }, + }, { + multi: true, + }); + } + + setAsReadById(_id) { + return this.update({ + _id, + }, { + $unset: { + unread: 1, + }, + }); + } + + findUnreadMessagesByRoomAndDate(rid, after) { + const query = { + unread: true, + rid, + }; + + if (after) { + query.ts = { $gt: after }; + } + + return this.find(query, { + fields: { + _id: 1, + }, + }); + } +} + +export default new Messages(); diff --git a/packages/rocketchat-models/server/models/Reports.js b/packages/rocketchat-models/server/models/Reports.js new file mode 100644 index 000000000000..09fd9dc6da98 --- /dev/null +++ b/packages/rocketchat-models/server/models/Reports.js @@ -0,0 +1,21 @@ +import { Base } from './_Base'; +import _ from 'underscore'; + +export class Reports extends Base { + constructor() { + super('reports'); + } + createWithMessageDescriptionAndUserId(message, description, userId, extraData) { + const record = { + message, + description, + ts: new Date(), + userId, + }; + _.extend(record, extraData); + record._id = this.insert(record); + return record; + } +} + +export default new Reports(); diff --git a/packages/rocketchat-models/server/models/Rooms.js b/packages/rocketchat-models/server/models/Rooms.js new file mode 100644 index 000000000000..3b0a31c908a5 --- /dev/null +++ b/packages/rocketchat-models/server/models/Rooms.js @@ -0,0 +1,809 @@ +import { Base } from './_Base'; +import Messages from './Messages'; +import Subscriptions from './Subscriptions'; +import _ from 'underscore'; +import s from 'underscore.string'; + +export class Rooms extends Base { + constructor(...args) { + super(...args); + + this.tryEnsureIndex({ name: 1 }, { unique: 1, sparse: 1 }); + this.tryEnsureIndex({ default: 1 }); + this.tryEnsureIndex({ t: 1 }); + this.tryEnsureIndex({ 'u._id': 1 }); + } + + findOneByIdOrName(_idOrName, options) { + const query = { + $or: [{ + _id: _idOrName, + }, { + name: _idOrName, + }], + }; + + return this.findOne(query, options); + } + + findOneByImportId(_id, options) { + const query = { importIds: _id }; + + return this.findOne(query, options); + } + + findOneByName(name, options) { + const query = { name }; + + return this.findOne(query, options); + } + + findOneByNameAndNotId(name, rid) { + const query = { + _id: { $ne: rid }, + name, + }; + + return this.findOne(query); + } + + findOneByDisplayName(fname, options) { + const query = { fname }; + + return this.findOne(query, options); + } + + findOneByNameAndType(name, type, options) { + const query = { + name, + t: type, + }; + + return this.findOne(query, options); + } + + // FIND + + findById(roomId, options) { + return this.find({ _id: roomId }, options); + } + + findByIds(roomIds, options) { + return this.find({ _id: { $in: [].concat(roomIds) } }, options); + } + + findByType(type, options) { + const query = { t: type }; + + return this.find(query, options); + } + + findByTypeInIds(type, ids, options) { + const query = { + _id: { + $in: ids, + }, + t: type, + }; + + return this.find(query, options); + } + + findByTypes(types, options) { + const query = { + t: { + $in: types, + }, + }; + + return this.find(query, options); + } + + findByUserId(userId, options) { + const query = { 'u._id': userId }; + + return this.find(query, options); + } + + findBySubscriptionUserId(userId, options) { + const data = Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() + .map((item) => item.rid); + + const query = { + _id: { + $in: data, + }, + }; + + return this.find(query, options); + } + + findBySubscriptionTypeAndUserId(type, userId, options) { + const data = Subscriptions.findByUserIdAndType(userId, type, { fields: { rid: 1 } }).fetch() + .map((item) => item.rid); + + const query = { + t: type, + _id: { + $in: data, + }, + }; + + return this.find(query, options); + } + + findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) { + const ids = Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch() + .map((item) => item.rid); + + const query = { + _id: { + $in: ids, + }, + _updatedAt: { + $gt: _updatedAt, + }, + }; + + return this.find(query, options); + } + + findByNameContaining(name, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); + + const query = { + $or: [ + { name: nameRegex }, + { + t: 'd', + usernames: nameRegex, + }, + ], + }; + + return this.find(query, options); + } + + findByNameContainingAndTypes(name, types, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); + + const query = { + t: { + $in: types, + }, + $or: [ + { name: nameRegex }, + { + t: 'd', + usernames: nameRegex, + }, + ], + }; + + return this.find(query, options); + } + + findByNameAndType(name, type, options) { + const query = { + t: type, + name, + }; + + // do not use cache + return this._db.find(query, options); + } + + findByNameAndTypeNotDefault(name, type, options) { + const query = { + t: type, + name, + default: { + $ne: true, + }, + }; + + // do not use cache + return this._db.find(query, options); + } + + findByNameAndTypesNotInIds(name, types, ids, options) { + const query = { + _id: { + $ne: ids, + }, + t: { + $in: types, + }, + name, + }; + + // do not use cache + return this._db.find(query, options); + } + + findChannelAndPrivateByNameStarting(name, options) { + const nameRegex = new RegExp(`^${ s.trim(s.escapeRegExp(name)) }`, 'i'); + + const query = { + t: { + $in: ['c', 'p'], + }, + name: nameRegex, + }; + + return this.find(query, options); + } + + findByDefaultAndTypes(defaultValue, types, options) { + const query = { + default: defaultValue, + t: { + $in: types, + }, + }; + + return this.find(query, options); + } + + findDirectRoomContainingUsername(username, options) { + const query = { + t: 'd', + usernames: username, + }; + + return this.find(query, options); + } + + findDirectRoomContainingAllUsernames(usernames, options) { + const query = { + t: 'd', + usernames: { $size: usernames.length, $all: usernames }, + }; + + return this.findOne(query, options); + } + + findByTypeAndName(type, name, options) { + const query = { + name, + t: type, + }; + + return this.find(query, options); + } + + findByTypeAndNameContaining(type, name, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); + + const query = { + name: nameRegex, + t: type, + }; + + return this.find(query, options); + } + + findByTypeInIdsAndNameContaining(type, ids, name, options) { + const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i'); + + const query = { + _id: { + $in: ids, + }, + name: nameRegex, + t: type, + }; + + return this.find(query, options); + } + + findByTypeAndArchivationState(type, archivationstate, options) { + const query = { t: type }; + + if (archivationstate) { + query.archived = true; + } else { + query.archived = { $ne: true }; + } + + return this.find(query, options); + } + + // UPDATE + addImportIds(_id, importIds) { + importIds = [].concat(importIds); + const query = { _id }; + + const update = { + $addToSet: { + importIds: { + $each: importIds, + }, + }, + }; + + return this.update(query, update); + } + + archiveById(_id) { + const query = { _id }; + + const update = { + $set: { + archived: true, + }, + }; + + return this.update(query, update); + } + + unarchiveById(_id) { + const query = { _id }; + + const update = { + $set: { + archived: false, + }, + }; + + return this.update(query, update); + } + + setNameById(_id, name, fname) { + const query = { _id }; + + const update = { + $set: { + name, + fname, + }, + }; + + return this.update(query, update); + } + + setFnameById(_id, fname) { + const query = { _id }; + + const update = { + $set: { + fname, + }, + }; + + return this.update(query, update); + } + + incMsgCountById(_id, inc) { + if (inc == null) { inc = 1; } + const query = { _id }; + + const update = { + $inc: { + msgs: inc, + }, + }; + + return this.update(query, update); + } + + incMsgCountAndSetLastMessageById(_id, inc, lastMessageTimestamp, lastMessage) { + if (inc == null) { inc = 1; } + const query = { _id }; + + const update = { + $set: { + lm: lastMessageTimestamp, + }, + $inc: { + msgs: inc, + }, + }; + + if (lastMessage) { + update.$set.lastMessage = lastMessage; + } + + return this.update(query, update); + } + + incUsersCountById(_id, inc = 1) { + const query = { _id }; + + const update = { + $inc: { + usersCount: inc, + }, + }; + + return this.update(query, update); + } + + incUsersCountByIds(ids, inc = 1) { + const query = { + _id: { + $in: ids, + }, + }; + + const update = { + $inc: { + usersCount: inc, + }, + }; + + return this.update(query, update, { multi: true }); + } + + setLastMessageById(_id, lastMessage) { + const query = { _id }; + + const update = { + $set: { + lastMessage, + }, + }; + + return this.update(query, update); + } + + resetLastMessageById(_id, messageId) { + const query = { _id }; + const lastMessage = Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id, messageId); + + const update = lastMessage ? { + $set: { + lastMessage, + }, + } : { + $unset: { + lastMessage: 1, + }, + }; + + return this.update(query, update); + } + + replaceUsername(previousUsername, username) { + const query = { usernames: previousUsername }; + + const update = { + $set: { + 'usernames.$': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + replaceMutedUsername(previousUsername, username) { + const query = { muted: previousUsername }; + + const update = { + $set: { + 'muted.$': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + replaceUsernameOfUserByUserId(userId, username) { + const query = { 'u._id': userId }; + + const update = { + $set: { + 'u.username': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + setJoinCodeById(_id, joinCode) { + let update; + const query = { _id }; + + if ((joinCode != null ? joinCode.trim() : undefined) !== '') { + update = { + $set: { + joinCodeRequired: true, + joinCode, + }, + }; + } else { + update = { + $set: { + joinCodeRequired: false, + }, + $unset: { + joinCode: 1, + }, + }; + } + + return this.update(query, update); + } + + setUserById(_id, user) { + const query = { _id }; + + const update = { + $set: { + u: { + _id: user._id, + username: user.username, + }, + }, + }; + + return this.update(query, update); + } + + setTypeById(_id, type) { + const query = { _id }; + const update = { + $set: { + t: type, + }, + }; + if (type === 'p') { + update.$unset = { default: '' }; + } + + return this.update(query, update); + } + + setTopicById(_id, topic) { + const query = { _id }; + + const update = { + $set: { + topic, + }, + }; + + return this.update(query, update); + } + + setAnnouncementById(_id, announcement, announcementDetails) { + const query = { _id }; + + const update = { + $set: { + announcement, + announcementDetails, + }, + }; + + return this.update(query, update); + } + + setCustomFieldsById(_id, customFields) { + const query = { _id }; + + const update = { + $set: { + customFields, + }, + }; + + return this.update(query, update); + } + + muteUsernameByRoomId(_id, username) { + const query = { _id }; + + const update = { + $addToSet: { + muted: username, + }, + }; + + return this.update(query, update); + } + + unmuteUsernameByRoomId(_id, username) { + const query = { _id }; + + const update = { + $pull: { + muted: username, + }, + }; + + return this.update(query, update); + } + + saveDefaultById(_id, defaultValue) { + const query = { _id }; + + const update = { + $set: { + default: defaultValue === 'true', + }, + }; + + return this.update(query, update); + } + + saveRetentionEnabledById(_id, value) { + const query = { _id }; + + const update = {}; + + if (value == null) { + update.$unset = { 'retention.enabled': true }; + } else { + update.$set = { 'retention.enabled': !!value }; + } + + return this.update(query, update); + } + + saveRetentionMaxAgeById(_id, value) { + const query = { _id }; + + value = Number(value); + if (!value) { + value = 30; + } + + const update = { + $set: { + 'retention.maxAge': value, + }, + }; + + return this.update(query, update); + } + + saveRetentionExcludePinnedById(_id, value) { + const query = { _id }; + + const update = { + $set: { + 'retention.excludePinned': value === true, + }, + }; + + return this.update(query, update); + } + + saveRetentionFilesOnlyById(_id, value) { + const query = { _id }; + + const update = { + $set: { + 'retention.filesOnly': value === true, + }, + }; + + return this.update(query, update); + } + + saveRetentionOverrideGlobalById(_id, value) { + const query = { _id }; + + const update = { + $set: { + 'retention.overrideGlobal': value === true, + }, + }; + + return this.update(query, update); + } + + saveEncryptedById(_id, value) { + const query = { _id }; + + const update = { + $set: { + encrypted: value === true, + }, + }; + + return this.update(query, update); + } + + setTopicAndTagsById(_id, topic, tags) { + const setData = {}; + const unsetData = {}; + + if (topic != null) { + if (!_.isEmpty(s.trim(topic))) { + setData.topic = s.trim(topic); + } else { + unsetData.topic = 1; + } + } + + if (tags != null) { + if (!_.isEmpty(s.trim(tags))) { + setData.tags = s.trim(tags).split(',').map((tag) => s.trim(tag)); + } else { + unsetData.tags = 1; + } + } + + const update = {}; + + if (!_.isEmpty(setData)) { + update.$set = setData; + } + + if (!_.isEmpty(unsetData)) { + update.$unset = unsetData; + } + + if (_.isEmpty(update)) { + return; + } + + return this.update({ _id }, update); + } + + // INSERT + createWithTypeNameUserAndUsernames(type, name, fname, user, usernames, extraData) { + const room = { + name, + fname, + t: type, + usernames, + msgs: 0, + usersCount: 0, + u: { + _id: user._id, + username: user.username, + }, + }; + + _.extend(room, extraData); + + room._id = this.insert(room); + return room; + } + + createWithIdTypeAndName(_id, type, name, extraData) { + const room = { + _id, + ts: new Date(), + t: type, + name, + usernames: [], + msgs: 0, + usersCount: 0, + }; + + _.extend(room, extraData); + + this.insert(room); + return room; + } + + createWithFullRoomData(room) { + delete room._id; + + room._id = this.insert(room); + return room; + } + + + // REMOVE + removeById(_id) { + const query = { _id }; + + return this.remove(query); + } + + removeDirectRoomContainingUsername(username) { + const query = { + t: 'd', + usernames: username, + }; + + return this.remove(query); + } +} + +export default new Rooms('rooms', true); diff --git a/packages/rocketchat-models/server/models/Settings.js b/packages/rocketchat-models/server/models/Settings.js new file mode 100644 index 000000000000..b692b26292e7 --- /dev/null +++ b/packages/rocketchat-models/server/models/Settings.js @@ -0,0 +1,184 @@ +import { Base } from './_Base'; + +export class Settings extends Base { + constructor(...args) { + super(...args); + + this.tryEnsureIndex({ blocked: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ hidden: 1 }, { sparse: 1 }); + } + + // FIND + findById(_id) { + const query = { _id }; + + return this.find(query); + } + + findOneNotHiddenById(_id) { + const query = { + _id, + hidden: { $ne: true }, + }; + + return this.findOne(query); + } + + findByIds(_id = []) { + _id = [].concat(_id); + + const query = { + _id: { + $in: _id, + }, + }; + + return this.find(query); + } + + findByRole(role, options) { + const query = { role }; + + return this.find(query, options); + } + + findPublic(options) { + const query = { public: true }; + + return this.find(query, options); + } + + findNotHiddenPublic(ids = []) { + const filter = { + hidden: { $ne: true }, + public: true, + }; + + if (ids.length > 0) { + filter._id = + { $in: ids }; + } + + return this.find(filter, { fields: { _id: 1, value: 1 } }); + } + + findNotHiddenPublicUpdatedAfter(updatedAt) { + const filter = { + hidden: { $ne: true }, + public: true, + _updatedAt: { + $gt: updatedAt, + }, + }; + + return this.find(filter, { fields: { _id: 1, value: 1 } }); + } + + findNotHiddenPrivate() { + return this.find({ + hidden: { $ne: true }, + public: { $ne: true }, + }); + } + + findNotHidden(options) { + return this.find({ hidden: { $ne: true } }, options); + } + + findNotHiddenUpdatedAfter(updatedAt) { + return this.find({ + hidden: { $ne: true }, + _updatedAt: { + $gt: updatedAt, + }, + }); + } + + findSetupWizardSettings() { + return this.find({ wizard: { $exists: true, $ne: null } }); + } + + // UPDATE + updateValueById(_id, value) { + const query = { + blocked: { $ne: true }, + value: { $ne: value }, + _id, + }; + + const update = { + $set: { + value, + }, + }; + + return this.update(query, update); + } + + updateValueAndEditorById(_id, value, editor) { + const query = { + blocked: { $ne: true }, + value: { $ne: value }, + _id, + }; + + const update = { + $set: { + value, + editor, + }, + }; + + return this.update(query, update); + } + + updateValueNotHiddenById(_id, value) { + const query = { + _id, + hidden: { $ne: true }, + blocked: { $ne: true }, + }; + + const update = { + $set: { + value, + }, + }; + + return this.update(query, update); + } + + updateOptionsById(_id, options) { + const query = { + blocked: { $ne: true }, + _id, + }; + + const update = { $set: options }; + + return this.update(query, update); + } + + // INSERT + createWithIdAndValue(_id, value) { + const record = { + _id, + value, + _createdAt: new Date, + }; + + return this.insert(record); + } + + // REMOVE + removeById(_id) { + const query = { + blocked: { $ne: true }, + _id, + }; + + return this.remove(query); + } +} + +export default new Settings('settings', true); diff --git a/packages/rocketchat-models/server/models/Subscriptions.js b/packages/rocketchat-models/server/models/Subscriptions.js new file mode 100644 index 000000000000..6e332eea851f --- /dev/null +++ b/packages/rocketchat-models/server/models/Subscriptions.js @@ -0,0 +1,853 @@ +import { Base } from './_Base'; +import { Match } from 'meteor/check'; +import Rooms from './Rooms'; +import { getDefaultSubscriptionPref } from 'meteor/rocketchat:utils'; + +export class Subscriptions extends Base { + constructor(...args) { + super(...args); + + this.tryEnsureIndex({ rid: 1, 'u._id': 1 }, { unique: 1 }); + this.tryEnsureIndex({ rid: 1, 'u.username': 1 }); + this.tryEnsureIndex({ rid: 1, alert: 1, 'u._id': 1 }); + this.tryEnsureIndex({ rid: 1, roles: 1 }); + this.tryEnsureIndex({ 'u._id': 1, name: 1, t: 1 }); + this.tryEnsureIndex({ open: 1 }); + this.tryEnsureIndex({ alert: 1 }); + + this.tryEnsureIndex({ rid: 1, 'u._id': 1, open: 1 }); + + this.tryEnsureIndex({ ts: 1 }); + this.tryEnsureIndex({ ls: 1 }); + this.tryEnsureIndex({ audioNotifications: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ desktopNotifications: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ mobilePushNotifications: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ emailNotifications: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ autoTranslate: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ autoTranslateLanguage: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ 'userHighlights.0': 1 }, { sparse: 1 }); + } + + + // FIND ONE + findOneByRoomIdAndUserId(roomId, userId, options) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + return this.findOne(query, options); + } + + findOneByRoomIdAndUsername(roomId, username, options) { + const query = { + rid: roomId, + 'u.username': username, + }; + + return this.findOne(query, options); + } + + findOneByRoomNameAndUserId(roomName, userId) { + const query = { + name: roomName, + 'u._id': userId, + }; + + return this.findOne(query); + } + + // FIND + findByUserId(userId, options) { + const query = + { 'u._id': userId }; + + return this.find(query, options); + } + + findByUserIdAndType(userId, type, options) { + const query = { + 'u._id': userId, + t: type, + }; + + return this.find(query, options); + } + + findByUserIdAndTypes(userId, types, options) { + const query = { + 'u._id': userId, + t: { + $in: types, + }, + }; + + return this.find(query, options); + } + + findByUserIdUpdatedAfter(userId, updatedAt, options) { + const query = { + 'u._id': userId, + _updatedAt: { + $gt: updatedAt, + }, + }; + + return this.find(query, options); + } + + findByRoomIdAndRoles(roomId, roles, options) { + roles = [].concat(roles); + const query = { + rid: roomId, + roles: { $in: roles }, + }; + + return this.find(query, options); + } + + findByType(types, options) { + const query = { + t: { + $in: types, + }, + }; + + return this.find(query, options); + } + + findByTypeAndUserId(type, userId, options) { + const query = { + t: type, + 'u._id': userId, + }; + + return this.find(query, options); + } + + findByRoomId(roomId, options) { + const query = + { rid: roomId }; + + return this.find(query, options); + } + + findByRoomIdAndNotUserId(roomId, userId, options) { + const query = { + rid: roomId, + 'u._id': { + $ne: userId, + }, + }; + + return this.find(query, options); + } + + findByRoomWithUserHighlights(roomId, options) { + const query = { + rid: roomId, + 'userHighlights.0': { $exists: true }, + }; + + return this.find(query, options); + } + + getLastSeen(options) { + if (options == null) { + options = {}; + } + const query = { ls: { $exists: 1 } }; + options.sort = { ls: -1 }; + options.limit = 1; + const [subscription] = this.find(query, options).fetch(); + return subscription && subscription.ls; + } + + findByRoomIdAndUserIds(roomId, userIds, options) { + const query = { + rid: roomId, + 'u._id': { + $in: userIds, + }, + }; + + return this.find(query, options); + } + + findByRoomIdAndUserIdsOrAllMessages(roomId, userIds) { + const query = { + rid: roomId, + $or: [ + { 'u._id': { $in: userIds } }, + { emailNotifications: 'all' }, + ], + }; + + return this.find(query); + } + + findByRoomIdWhenUserIdExists(rid, options) { + const query = { rid, 'u._id': { $exists: 1 } }; + + return this.find(query, options); + } + + findByRoomIdWhenUsernameExists(rid, options) { + const query = { rid, 'u.username': { $exists: 1 } }; + + return this.find(query, options); + } + + findUnreadByUserId(userId) { + const query = { + 'u._id': userId, + unread: { + $gt: 0, + }, + }; + + return this.find(query, { fields: { unread: 1 } }); + } + + getMinimumLastSeenByRoomId(rid) { + return this.db.findOne({ + rid, + }, { + sort: { + ls: 1, + }, + fields: { + ls: 1, + }, + }); + } + + // UPDATE + archiveByRoomId(roomId) { + const query = + { rid: roomId }; + + const update = { + $set: { + alert: false, + open: false, + archived: true, + }, + }; + + return this.update(query, update, { multi: true }); + } + + unarchiveByRoomId(roomId) { + const query = + { rid: roomId }; + + const update = { + $set: { + alert: false, + open: true, + archived: false, + }, + }; + + return this.update(query, update, { multi: true }); + } + + hideByRoomIdAndUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + const update = { + $set: { + alert: false, + open: false, + }, + }; + + return this.update(query, update); + } + + openByRoomIdAndUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + const update = { + $set: { + open: true, + }, + }; + + return this.update(query, update); + } + + setAsReadByRoomIdAndUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + const update = { + $set: { + open: true, + alert: false, + unread: 0, + userMentions: 0, + groupMentions: 0, + ls: new Date, + }, + }; + + return this.update(query, update); + } + + setAsUnreadByRoomIdAndUserId(roomId, userId, firstMessageUnreadTimestamp) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + const update = { + $set: { + open: true, + alert: true, + ls: firstMessageUnreadTimestamp, + }, + }; + + return this.update(query, update); + } + + setCustomFieldsDirectMessagesByUserId(userId, fields) { + const query = { + 'u._id': userId, + t: 'd', + }; + const update = { $set: { customFields: fields } }; + const options = { multi: true }; + + return this.update(query, update, options); + } + + setFavoriteByRoomIdAndUserId(roomId, userId, favorite) { + if (favorite == null) { + favorite = true; + } + const query = { + rid: roomId, + 'u._id': userId, + }; + + const update = { + $set: { + f: favorite, + }, + }; + + return this.update(query, update); + } + + updateNameAndAlertByRoomId(roomId, name, fname) { + const query = + { rid: roomId }; + + const update = { + $set: { + name, + fname, + alert: true, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateDisplayNameByRoomId(roomId, fname) { + const query = + { rid: roomId }; + + const update = { + $set: { + fname, + }, + }; + + return this.update(query, update, { multi: true }); + } + + setUserUsernameByUserId(userId, username) { + const query = + { 'u._id': userId }; + + const update = { + $set: { + 'u.username': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + + setNameForDirectRoomsWithOldName(oldName, name) { + const query = { + name: oldName, + t: 'd', + }; + + const update = { + $set: { + name, + }, + }; + + return this.update(query, update, { multi: true }); + } + + incUnreadForRoomIdExcludingUserId(roomId, userId, inc) { + if (inc == null) { + inc = 1; + } + const query = { + rid: roomId, + 'u._id': { + $ne: userId, + }, + }; + + const update = { + $set: { + alert: true, + open: true, + }, + $inc: { + unread: inc, + }, + }; + + return this.update(query, update, { multi: true }); + } + + incGroupMentionsAndUnreadForRoomIdExcludingUserId(roomId, userId, incGroup = 1, incUnread = 1) { + const query = { + rid: roomId, + 'u._id': { + $ne: userId, + }, + }; + + const update = { + $set: { + alert: true, + open: true, + }, + $inc: { + unread: incUnread, + groupMentions: incGroup, + }, + }; + + return this.update(query, update, { multi: true }); + } + + incUserMentionsAndUnreadForRoomIdAndUserIds(roomId, userIds, incUser = 1, incUnread = 1) { + const query = { + rid: roomId, + 'u._id': { + $in: userIds, + }, + }; + + const update = { + $set: { + alert: true, + open: true, + }, + $inc: { + unread: incUnread, + userMentions: incUser, + }, + }; + + return this.update(query, update, { multi: true }); + } + + ignoreUser({ _id, ignoredUser : ignored, ignore = true }) { + const query = { + _id, + }; + const update = { + }; + if (ignore) { + update.$addToSet = { ignored }; + } else { + update.$pull = { ignored }; + } + + return this.update(query, update); + } + + setAlertForRoomIdExcludingUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': { + $ne: userId, + }, + alert: { $ne: true }, + }; + + const update = { + $set: { + alert: true, + }, + }; + return this.update(query, update, { multi: true }); + } + + setOpenForRoomIdExcludingUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': { + $ne: userId, + }, + open: { $ne: true }, + }; + + const update = { + $set: { + open: true, + }, + }; + return this.update(query, update, { multi: true }); + } + + setBlockedByRoomId(rid, blocked, blocker) { + const query = { + rid, + 'u._id': blocked, + }; + + const update = { + $set: { + blocked: true, + }, + }; + + const query2 = { + rid, + 'u._id': blocker, + }; + + const update2 = { + $set: { + blocker: true, + }, + }; + + return this.update(query, update) && this.update(query2, update2); + } + + unsetBlockedByRoomId(rid, blocked, blocker) { + const query = { + rid, + 'u._id': blocked, + }; + + const update = { + $unset: { + blocked: 1, + }, + }; + + const query2 = { + rid, + 'u._id': blocker, + }; + + const update2 = { + $unset: { + blocker: 1, + }, + }; + + return this.update(query, update) && this.update(query2, update2); + } + + updateCustomFieldsByRoomId(rid, cfields) { + const query = { rid }; + const customFields = cfields || {}; + const update = { + $set: { + customFields, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateTypeByRoomId(roomId, type) { + const query = + { rid: roomId }; + + const update = { + $set: { + t: type, + }, + }; + + return this.update(query, update, { multi: true }); + } + + addRoleById(_id, role) { + const query = + { _id }; + + const update = { + $addToSet: { + roles: role, + }, + }; + + return this.update(query, update); + } + + removeRoleById(_id, role) { + const query = + { _id }; + + const update = { + $pull: { + roles: role, + }, + }; + + return this.update(query, update); + } + + setArchivedByUsername(username, archived) { + const query = { + t: 'd', + name: username, + }; + + const update = { + $set: { + archived, + }, + }; + + return this.update(query, update, { multi: true }); + } + + clearDesktopNotificationUserPreferences(userId) { + const query = { + 'u._id': userId, + desktopPrefOrigin: 'user', + }; + + const update = { + $unset: { + desktopNotifications: 1, + desktopPrefOrigin: 1, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateDesktopNotificationUserPreferences(userId, desktopNotifications) { + const query = { + 'u._id': userId, + desktopPrefOrigin: { + $ne: 'subscription', + }, + }; + + const update = { + $set: { + desktopNotifications, + desktopPrefOrigin: 'user', + }, + }; + + return this.update(query, update, { multi: true }); + } + + clearMobileNotificationUserPreferences(userId) { + const query = { + 'u._id': userId, + mobilePrefOrigin: 'user', + }; + + const update = { + $unset: { + mobilePushNotifications: 1, + mobilePrefOrigin: 1, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateMobileNotificationUserPreferences(userId, mobilePushNotifications) { + const query = { + 'u._id': userId, + mobilePrefOrigin: { + $ne: 'subscription', + }, + }; + + const update = { + $set: { + mobilePushNotifications, + mobilePrefOrigin: 'user', + }, + }; + + return this.update(query, update, { multi: true }); + } + + clearEmailNotificationUserPreferences(userId) { + const query = { + 'u._id': userId, + emailPrefOrigin: 'user', + }; + + const update = { + $unset: { + emailNotifications: 1, + emailPrefOrigin: 1, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateEmailNotificationUserPreferences(userId, emailNotifications) { + const query = { + 'u._id': userId, + emailPrefOrigin: { + $ne: 'subscription', + }, + }; + + const update = { + $set: { + emailNotifications, + emailPrefOrigin: 'user', + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateUserHighlights(userId, userHighlights) { + const query = { + 'u._id': userId, + }; + + const update = { + $set: { + userHighlights, + }, + }; + + return this.update(query, update, { multi: true }); + } + + updateDirectFNameByName(name, fname) { + const query = { + t: 'd', + name, + }; + + const update = { + $set: { + fname, + }, + }; + + return this.update(query, update, { multi: true }); + } + + // INSERT + createWithRoomAndUser(room, user, extraData) { + const subscription = { + open: false, + alert: false, + unread: 0, + userMentions: 0, + groupMentions: 0, + ts: room.ts, + rid: room._id, + name: room.name, + fname: room.fname, + customFields: room.customFields, + t: room.t, + u: { + _id: user._id, + username: user.username, + name: user.name, + }, + ...getDefaultSubscriptionPref(user), + ...extraData, + }; + + const result = this.insert(subscription); + + Rooms.incUsersCountById(room._id); + + return result; + } + + + // REMOVE + removeByUserId(userId) { + const query = { + 'u._id': userId, + }; + + const roomIds = this.findByUserId(userId).map((s) => s.rid); + + const result = this.remove(query); + + if (Match.test(result, Number) && result > 0) { + Rooms.incUsersCountByIds(roomIds, -1); + } + + return result; + } + + removeByRoomId(roomId) { + const query = { + rid: roomId, + }; + + const result = this.remove(query); + + if (Match.test(result, Number) && result > 0) { + Rooms.incUsersCountById(roomId, - result); + } + + return result; + } + + removeByRoomIdAndUserId(roomId, userId) { + const query = { + rid: roomId, + 'u._id': userId, + }; + + const result = this.remove(query); + + if (Match.test(result, Number) && result > 0) { + Rooms.incUsersCountById(roomId, - result); + } + + return result; + } +} + +export default new Subscriptions('subscription', true); diff --git a/packages/rocketchat-models/server/models/Uploads.js b/packages/rocketchat-models/server/models/Uploads.js new file mode 100644 index 000000000000..12f45b373b83 --- /dev/null +++ b/packages/rocketchat-models/server/models/Uploads.js @@ -0,0 +1,113 @@ +import _ from 'underscore'; +import s from 'underscore.string'; +import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; +import { Base } from './_Base'; + +export class Uploads extends Base { + constructor() { + super('uploads'); + + this.model.before.insert((userId, doc) => { + doc.instanceId = InstanceStatus.id(); + }); + + this.tryEnsureIndex({ rid: 1 }); + this.tryEnsureIndex({ uploadedAt: 1 }); + } + + findNotHiddenFilesOfRoom(roomId, searchText, limit) { + const fileQuery = { + rid: roomId, + complete: true, + uploading: false, + _hidden: { + $ne: true, + }, + }; + + if (searchText) { + fileQuery.name = { $regex: new RegExp(RegExp.escape(searchText), 'i') }; + } + + const fileOptions = { + limit, + sort: { + uploadedAt: -1, + }, + fields: { + _id: 1, + userId: 1, + rid: 1, + name: 1, + description: 1, + type: 1, + url: 1, + uploadedAt: 1, + }, + }; + + return this.find(fileQuery, fileOptions); + } + + insertFileInit(userId, store, file, extra) { + const fileData = { + userId, + store, + complete: false, + uploading: true, + progress: 0, + extension: s.strRightBack(file.name, '.'), + uploadedAt: new Date(), + }; + + _.extend(fileData, file, extra); + + if (this.model.direct && this.model.direct.insert != null) { + file = this.model.direct.insert(fileData); + } else { + file = this.insert(fileData); + } + + return file; + } + + updateFileComplete(fileId, userId, file) { + let result; + if (!fileId) { + return; + } + + const filter = { + _id: fileId, + userId, + }; + + const update = { + $set: { + complete: true, + uploading: false, + progress: 1, + }, + }; + + update.$set = _.extend(file, update.$set); + + if (this.model.direct && this.model.direct.update != null) { + result = this.model.direct.update(filter, update); + } else { + result = this.update(filter, update); + } + + return result; + } + + deleteFile(fileId) { + if (this.model.direct && this.model.direct.remove != null) { + return this.model.direct.remove({ _id: fileId }); + } else { + return this.remove({ _id: fileId }); + } + } +} + +export default new Uploads(); diff --git a/packages/rocketchat-models/server/models/UserDataFiles.js b/packages/rocketchat-models/server/models/UserDataFiles.js new file mode 100644 index 000000000000..7fe3a2b49504 --- /dev/null +++ b/packages/rocketchat-models/server/models/UserDataFiles.js @@ -0,0 +1,43 @@ +import { Base } from './_Base'; +import _ from 'underscore'; + +export class UserDataFiles extends Base { + constructor() { + super('user_data_files'); + + this.tryEnsureIndex({ userId: 1 }); + } + + // FIND + findById(id) { + const query = { _id: id }; + return this.find(query); + } + + findLastFileByUser(userId, options = {}) { + const query = { + userId, + }; + + options.sort = { _updatedAt : -1 }; + return this.findOne(query, options); + } + + // INSERT + create(data) { + const userDataFile = { + createdAt: new Date, + }; + + _.extend(userDataFile, data); + + return this.insert(userDataFile); + } + + // REMOVE + removeById(_id) { + return this.remove(_id); + } +} + +export default new UserDataFiles(); diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js new file mode 100644 index 000000000000..786da5b96aa3 --- /dev/null +++ b/packages/rocketchat-models/server/models/Users.js @@ -0,0 +1,672 @@ +import { Meteor } from 'meteor/meteor'; +import { Accounts } from 'meteor/accounts-base'; +import { settings } from 'meteor/rocketchat:settings'; +import { Base } from './_Base'; +import Subscriptions from './Subscriptions'; +import _ from 'underscore'; +import s from 'underscore.string'; + +export class Users extends Base { + constructor(...args) { + super(...args); + + this.tryEnsureIndex({ roles: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ name: 1 }); + this.tryEnsureIndex({ lastLogin: 1 }); + this.tryEnsureIndex({ status: 1 }); + this.tryEnsureIndex({ active: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ statusConnection: 1 }, { sparse: 1 }); + this.tryEnsureIndex({ type: 1 }); + } + + findOneByImportId(_id, options) { + return this.findOne({ importIds: _id }, options); + } + + findOneByUsername(username, options) { + if (typeof username === 'string') { + username = new RegExp(`^${ username }$`, 'i'); + } + + const query = { username }; + + return this.findOne(query, options); + } + + findOneByEmailAddress(emailAddress, options) { + const query = { 'emails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i') }; + + return this.findOne(query, options); + } + + findOneAdmin(admin, options) { + const query = { admin }; + + return this.findOne(query, options); + } + + findOneByIdAndLoginToken(_id, token, options) { + const query = { + _id, + 'services.resume.loginTokens.hashedToken' : Accounts._hashLoginToken(token), + }; + + return this.findOne(query, options); + } + + findOneById(userId, options) { + const query = { _id: userId }; + + return this.findOne(query, options); + } + + // FIND + findById(userId) { + const query = { _id: userId }; + + return this.find(query); + } + + findByIds(users, options) { + const query = { _id: { $in: users } }; + return this.find(query, options); + } + + findUsersNotOffline(options) { + const query = { + username: { + $exists: 1, + }, + status: { + $in: ['online', 'away', 'busy'], + }, + }; + + return this.find(query, options); + } + + findByRoomId(rid, options) { + const data = Subscriptions.findByRoomId(rid).fetch().map((item) => item.u._id); + const query = { + _id: { + $in: data, + }, + }; + + return this.find(query, options); + } + + findByUsername(username, options) { + const query = { username }; + + return this.find(query, options); + } + + findActiveByUsernameOrNameRegexWithExceptions(searchTerm, exceptions, options) { + if (exceptions == null) { exceptions = []; } + if (options == null) { options = {}; } + if (!_.isArray(exceptions)) { + exceptions = [exceptions]; + } + + const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i'); + const query = { + $or: [{ + username: termRegex, + }, { + name: termRegex, + }], + active: true, + type: { + $in: ['user', 'bot'], + }, + $and: [{ + username: { + $exists: true, + }, + }, { + username: { + $nin: exceptions, + }, + }], + }; + + return this.find(query, options); + } + + findByActiveUsersExcept(searchTerm, exceptions, options) { + if (exceptions == null) { exceptions = []; } + if (options == null) { options = {}; } + if (!_.isArray(exceptions)) { + exceptions = [exceptions]; + } + + const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i'); + + const orStmt = _.reduce(settings.get('Accounts_SearchFields').trim().split(','), function(acc, el) { + acc.push({ [el.trim()]: termRegex }); + return acc; + }, []); + const query = { + $and: [ + { + active: true, + $or: orStmt, + }, + { + username: { $exists: true, $nin: exceptions }, + }, + ], + }; + + // do not use cache + return this._db.find(query, options); + } + + findUsersByNameOrUsername(nameOrUsername, options) { + const query = { + username: { + $exists: 1, + }, + + $or: [ + { name: nameOrUsername }, + { username: nameOrUsername }, + ], + + type: { + $in: ['user'], + }, + }; + + return this.find(query, options); + } + + findByUsernameNameOrEmailAddress(usernameNameOrEmailAddress, options) { + const query = { + $or: [ + { name: usernameNameOrEmailAddress }, + { username: usernameNameOrEmailAddress }, + { 'emails.address': usernameNameOrEmailAddress }, + ], + type: { + $in: ['user', 'bot'], + }, + }; + + return this.find(query, options); + } + + findLDAPUsers(options) { + const query = { ldap: true }; + + return this.find(query, options); + } + + findCrowdUsers(options) { + const query = { crowd: true }; + + return this.find(query, options); + } + + getLastLogin(options) { + if (options == null) { options = {}; } + const query = { lastLogin: { $exists: 1 } }; + options.sort = { lastLogin: -1 }; + options.limit = 1; + const [user] = this.find(query, options).fetch(); + return user && user.lastLogin; + } + + findUsersByUsernames(usernames, options) { + const query = { + username: { + $in: usernames, + }, + }; + + return this.find(query, options); + } + + findUsersByIds(ids, options) { + const query = { + _id: { + $in: ids, + }, + }; + return this.find(query, options); + } + + findUsersWithUsernameByIds(ids, options) { + const query = { + _id: { + $in: ids, + }, + username: { + $exists: 1, + }, + }; + + return this.find(query, options); + } + + findUsersWithUsernameByIdsNotOffline(ids, options) { + const query = { + _id: { + $in: ids, + }, + username: { + $exists: 1, + }, + status: { + $in: ['online', 'away', 'busy'], + }, + }; + + return this.find(query, options); + } + + getOldest(fields = { _id: 1 }) { + const query = { + _id: { + $ne: 'rocket.cat', + }, + }; + + const options = { + fields, + sort: { + createdAt: 1, + }, + }; + + return this.findOne(query, options); + } + + // UPDATE + addImportIds(_id, importIds) { + importIds = [].concat(importIds); + + const query = { _id }; + + const update = { + $addToSet: { + importIds: { + $each: importIds, + }, + }, + }; + + return this.update(query, update); + } + + updateLastLoginById(_id) { + const update = { + $set: { + lastLogin: new Date, + }, + }; + + return this.update(_id, update); + } + + setServiceId(_id, serviceName, serviceId) { + const update = + { $set: {} }; + + const serviceIdKey = `services.${ serviceName }.id`; + update.$set[serviceIdKey] = serviceId; + + return this.update(_id, update); + } + + setUsername(_id, username) { + const update = + { $set: { username } }; + + return this.update(_id, update); + } + + setEmail(_id, email) { + const update = { + $set: { + emails: [{ + address: email, + verified: false, + }, + ], + }, + }; + + return this.update(_id, update); + } + + setEmailVerified(_id, email) { + const query = { + _id, + emails: { + $elemMatch: { + address: email, + verified: false, + }, + }, + }; + + const update = { + $set: { + 'emails.$.verified': true, + }, + }; + + return this.update(query, update); + } + + setName(_id, name) { + const update = { + $set: { + name, + }, + }; + + return this.update(_id, update); + } + + setCustomFields(_id, fields) { + const values = {}; + Object.keys(fields).forEach((key) => { + values[`customFields.${ key }`] = fields[key]; + }); + + const update = { $set: values }; + + return this.update(_id, update); + } + + setAvatarOrigin(_id, origin) { + const update = { + $set: { + avatarOrigin: origin, + }, + }; + + return this.update(_id, update); + } + + unsetAvatarOrigin(_id) { + const update = { + $unset: { + avatarOrigin: 1, + }, + }; + + return this.update(_id, update); + } + + setUserActive(_id, active) { + if (active == null) { active = true; } + const update = { + $set: { + active, + }, + }; + + return this.update(_id, update); + } + + setAllUsersActive(active) { + const update = { + $set: { + active, + }, + }; + + return this.update({}, update, { multi: true }); + } + + unsetLoginTokens(_id) { + const update = { + $set: { + 'services.resume.loginTokens' : [], + }, + }; + + return this.update(_id, update); + } + + unsetRequirePasswordChange(_id) { + const update = { + $unset: { + requirePasswordChange : true, + requirePasswordChangeReason : true, + }, + }; + + return this.update(_id, update); + } + + resetPasswordAndSetRequirePasswordChange(_id, requirePasswordChange, requirePasswordChangeReason) { + const update = { + $unset: { + 'services.password': 1, + }, + $set: { + requirePasswordChange, + requirePasswordChangeReason, + }, + }; + + return this.update(_id, update); + } + + setLanguage(_id, language) { + const update = { + $set: { + language, + }, + }; + + return this.update(_id, update); + } + + setProfile(_id, profile) { + const update = { + $set: { + 'settings.profile': profile, + }, + }; + + return this.update(_id, update); + } + + clearSettings(_id) { + const update = { + $set: { + settings: {}, + }, + }; + + return this.update(_id, update); + } + + setPreferences(_id, preferences) { + const settings = Object.assign( + {}, + ...Object.keys(preferences).map((key) => ({ [`settings.preferences.${ key }`]: preferences[key] })) + ); + + const update = { + $set: settings, + }; + if (parseInt(preferences.clockMode) === 0) { + delete update.$set['settings.preferences.clockMode']; + update.$unset = { 'settings.preferences.clockMode': 1 }; + } + + return this.update(_id, update); + } + + setUtcOffset(_id, utcOffset) { + const query = { + _id, + utcOffset: { + $ne: utcOffset, + }, + }; + + const update = { + $set: { + utcOffset, + }, + }; + + return this.update(query, update); + } + + saveUserById(_id, data) { + const setData = {}; + const unsetData = {}; + + if (data.name != null) { + if (!_.isEmpty(s.trim(data.name))) { + setData.name = s.trim(data.name); + } else { + unsetData.name = 1; + } + } + + if (data.email != null) { + if (!_.isEmpty(s.trim(data.email))) { + setData.emails = [{ address: s.trim(data.email) }]; + } else { + unsetData.emails = 1; + } + } + + if (data.phone != null) { + if (!_.isEmpty(s.trim(data.phone))) { + setData.phone = [{ phoneNumber: s.trim(data.phone) }]; + } else { + unsetData.phone = 1; + } + } + + const update = {}; + + if (!_.isEmpty(setData)) { + update.$set = setData; + } + + if (!_.isEmpty(unsetData)) { + update.$unset = unsetData; + } + + if (_.isEmpty(update)) { + return true; + } + + return this.update({ _id }, update); + } + + setReason(_id, reason) { + const update = { + $set: { + reason, + }, + }; + + return this.update(_id, update); + } + + unsetReason(_id) { + const update = { + $unset: { + reason: true, + }, + }; + + return this.update(_id, update); + } + + addBannerById(_id, banner) { + const update = { + $set: { + [`banners.${ banner.id }`]: banner, + }, + }; + + return this.update({ _id }, update); + } + + removeBannerById(_id, banner) { + const update = { + $unset: { + [`banners.${ banner.id }`]: true, + }, + }; + + return this.update({ _id }, update); + } + + removeResumeService(_id) { + const update = { + $unset: { + 'services.resume': '', + }, + }; + + return this.update({ _id }, update); + } + + // INSERT + create(data) { + const user = { + createdAt: new Date, + avatarOrigin: 'none', + }; + + _.extend(user, data); + + return this.insert(user); + } + + + // REMOVE + removeById(_id) { + return this.remove(_id); + } + + /* +Find users to send a message by email if: +- he is not online +- has a verified email +- has not disabled email notifications +- `active` is equal to true (false means they were deactivated and can't login) +*/ + getUsersToSendOfflineEmail(usersIds) { + const query = { + _id: { + $in: usersIds, + }, + active: true, + status: 'offline', + statusConnection: { + $ne: 'online', + }, + 'emails.verified': true, + }; + + const options = { + fields: { + name: 1, + username: 1, + emails: 1, + 'settings.preferences.emailNotificationMode': 1, + language: 1, + }, + }; + + return this.find(query, options); + } +} + +export default new Users(Meteor.users, true); diff --git a/packages/rocketchat-models/server/models/_Base.js b/packages/rocketchat-models/server/models/_Base.js new file mode 100644 index 000000000000..5180e2ebe25e --- /dev/null +++ b/packages/rocketchat-models/server/models/_Base.js @@ -0,0 +1,283 @@ +import { check } from 'meteor/check'; +import { BaseDb } from './_BaseDb'; +import objectPath from 'object-path'; +import _ from 'underscore'; + +export class Base { + constructor(nameOrModel) { + this._db = new BaseDb(nameOrModel, this); + this.model = this._db.model; + this.collectionName = this._db.collectionName; + this.name = this._db.name; + + this.on = this._db.on.bind(this._db); + this.emit = this._db.emit.bind(this._db); + + this.db = this; + } + + get origin() { + return '_db'; + } + + arrayToCursor(data) { + return { + fetch() { + return data; + }, + count() { + return data.length; + }, + forEach(fn) { + return data.forEach(fn); + }, + }; + } + + setUpdatedAt(...args/* record, checkQuery, query*/) { + return this._db.setUpdatedAt(...args); + } + + find(...args) { + try { + return this[this.origin].find(...args); + } catch (e) { + console.error('Exception on find', e, ...args); + } + } + + findOne(...args) { + try { + return this[this.origin].findOne(...args); + } catch (e) { + console.error('Exception on find', e, ...args); + } + } + + findOneById(...args) { + try { + return this[this.origin].findOneById(...args); + } catch (e) { + console.error('Exception on find', e, ...args); + } + } + + findOneByIds(ids, options, ...args) { + check(ids, [String]); + + try { + return this[this.origin].findOneByIds(ids, options); + } catch (e) { + console.error('Exception on find', e, [ids, options, ...args]); + } + } + + insert(...args/* record*/) { + return this._db.insert(...args); + } + + update(...args/* query, update, options*/) { + return this._db.update(...args); + } + + upsert(...args/* query, update*/) { + return this._db.upsert(...args); + } + + remove(...args/* query*/) { + return this._db.remove(...args); + } + + insertOrUpsert(...args) { + return this._db.insertOrUpsert(...args); + } + + allow(...args) { + return this._db.allow(...args); + } + + deny(...args) { + return this._db.deny(...args); + } + + ensureIndex(...args) { + return this._db.ensureIndex(...args); + } + + dropIndex(...args) { + return this._db.dropIndex(...args); + } + + tryEnsureIndex(...args) { + return this._db.tryEnsureIndex(...args); + } + + tryDropIndex(...args) { + return this._db.tryDropIndex(...args); + } + + trashFind(...args/* query, options*/) { + return this._db.trashFind(...args); + } + + trashFindOneById(...args/* _id, options*/) { + return this._db.trashFindOneById(...args); + } + + trashFindDeletedAfter(...args/* deletedAt, query, options*/) { + return this._db.trashFindDeletedAfter(...args); + } + + trashFindDeleted(...args) { + return this._db.trashFindDeleted(...args); + } + + processQueryOptionsOnResult(result, options = {}) { + if (result === undefined || result === null) { + return undefined; + } + + if (Array.isArray(result)) { + if (options.sort) { + result = result.sort((a, b) => { + let r = 0; + for (const field in options.sort) { + if (options.sort.hasOwnProperty(field)) { + const direction = options.sort[field]; + let valueA; + let valueB; + if (field.indexOf('.') > -1) { + valueA = objectPath.get(a, field); + valueB = objectPath.get(b, field); + } else { + valueA = a[field]; + valueB = b[field]; + } + if (valueA > valueB) { + r = direction; + break; + } + if (valueA < valueB) { + r = -direction; + break; + } + } + } + return r; + }); + } + + if (typeof options.skip === 'number') { + result.splice(0, options.skip); + } + + if (typeof options.limit === 'number' && options.limit !== 0) { + result.splice(options.limit); + } + } + + if (!options.fields) { + options.fields = {}; + } + + const fieldsToRemove = []; + const fieldsToGet = []; + + for (const field in options.fields) { + if (options.fields.hasOwnProperty(field)) { + if (options.fields[field] === 0) { + fieldsToRemove.push(field); + } else if (options.fields[field] === 1) { + fieldsToGet.push(field); + } + } + } + + if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) { + console.warn('Can\'t mix remove and get fields'); + fieldsToRemove.splice(0, fieldsToRemove.length); + } + + if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id') === -1) { + fieldsToGet.push('_id'); + } + + const pickFields = (obj, fields) => { + const picked = {}; + fields.forEach((field) => { + if (field.indexOf('.') !== -1) { + objectPath.set(picked, field, objectPath.get(obj, field)); + } else { + picked[field] = obj[field]; + } + }); + return picked; + }; + + if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) { + if (Array.isArray(result)) { + result = result.map((record) => { + if (fieldsToRemove.length > 0) { + return _.omit(record, ...fieldsToRemove); + } + + if (fieldsToGet.length > 0) { + return pickFields(record, fieldsToGet); + } + + return null; + }); + } else { + if (fieldsToRemove.length > 0) { + return _.omit(result, ...fieldsToRemove); + } + + if (fieldsToGet.length > 0) { + return pickFields(result, fieldsToGet); + } + } + } + + return result; + } + + // dinamicTrashFindAfter(method, deletedAt, ...args) { + // const scope = { + // find: (query={}) => { + // return this.trashFindDeletedAfter(deletedAt, query, { fields: {_id: 1, _deletedAt: 1} }); + // } + // }; + + // scope.model = { + // find: scope.find + // }; + + // return this[method].apply(scope, args); + // } + + // dinamicFindAfter(method, updatedAt, ...args) { + // const scope = { + // find: (query={}, options) => { + // query._updatedAt = { + // $gt: updatedAt + // }; + + // return this.find(query, options); + // } + // }; + + // scope.model = { + // find: scope.find + // }; + + // return this[method].apply(scope, args); + // } + + // dinamicFindChangesAfter(method, updatedAt, ...args) { + // return { + // update: this.dinamicFindAfter(method, updatedAt, ...args).fetch(), + // remove: this.dinamicTrashFindAfter(method, updatedAt, ...args).fetch() + // }; + // } + +} diff --git a/packages/rocketchat-lib/server/models/_BaseDb.js b/packages/rocketchat-models/server/models/_BaseDb.js similarity index 98% rename from packages/rocketchat-lib/server/models/_BaseDb.js rename to packages/rocketchat-models/server/models/_BaseDb.js index 3c97bf1a5749..5955f5c775d9 100644 --- a/packages/rocketchat-lib/server/models/_BaseDb.js +++ b/packages/rocketchat-models/server/models/_BaseDb.js @@ -1,9 +1,10 @@ import { Match } from 'meteor/check'; import { Mongo, MongoInternals } from 'meteor/mongo'; +import { settings } from 'meteor/rocketchat:settings'; import _ from 'underscore'; +import { EventEmitter } from 'events'; const baseName = 'rocketchat_'; -import { EventEmitter } from 'events'; const trash = new Mongo.Collection(`${ baseName }_trash`); try { @@ -15,11 +16,11 @@ try { const isOplogAvailable = MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle && !!MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle.onOplogEntry; let isOplogEnabled = isOplogAvailable; -RocketChat.settings.get('Force_Disable_OpLog_For_Cache', (key, value) => { +settings.get('Force_Disable_OpLog_For_Cache', (key, value) => { isOplogEnabled = isOplogAvailable && value === false; }); -class ModelsBaseDb extends EventEmitter { +export class BaseDb extends EventEmitter { constructor(model, baseModel) { super(); @@ -386,5 +387,3 @@ class ModelsBaseDb extends EventEmitter { return trash.find(query, options); } } - -export default ModelsBaseDb; From 157edc0789b00cb4bc37c1e240fa573d57eeb528 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Mon, 24 Dec 2018 13:56:24 -0200 Subject: [PATCH 03/59] Move function from rocketchat:lib to rocketchat:utils to use it in rocketchat:models --- .../lib/getDefaultSubscriptionPref.js | 32 ++----------------- packages/rocketchat-utils/client/index.js | 2 ++ .../lib/getDefaultSubscriptionPref.js | 31 ++++++++++++++++++ packages/rocketchat-utils/server/index.js | 2 ++ 4 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 packages/rocketchat-utils/lib/getDefaultSubscriptionPref.js diff --git a/packages/rocketchat-lib/lib/getDefaultSubscriptionPref.js b/packages/rocketchat-lib/lib/getDefaultSubscriptionPref.js index 7bf89190e40a..651cbb6435e2 100644 --- a/packages/rocketchat-lib/lib/getDefaultSubscriptionPref.js +++ b/packages/rocketchat-lib/lib/getDefaultSubscriptionPref.js @@ -1,31 +1,3 @@ -RocketChat.getDefaultSubscriptionPref = function _getDefaultSubscriptionPref(userPref) { - const subscription = {}; +import { getDefaultSubscriptionPref } from 'meteor/rocketchat:utils'; - const { - desktopNotifications, - mobileNotifications, - emailNotificationMode, - highlights, - } = (userPref.settings && userPref.settings.preferences) || {}; - - if (Array.isArray(highlights) && highlights.length) { - subscription.userHighlights = highlights; - } - - if (desktopNotifications && desktopNotifications !== 'default') { - subscription.desktopNotifications = desktopNotifications; - subscription.desktopPrefOrigin = 'user'; - } - - if (mobileNotifications && mobileNotifications !== 'default') { - subscription.mobilePushNotifications = mobileNotifications; - subscription.mobilePrefOrigin = 'user'; - } - - if (emailNotificationMode && emailNotificationMode !== 'default') { - subscription.emailNotifications = emailNotificationMode; - subscription.emailPrefOrigin = 'user'; - } - - return subscription; -}; +RocketChat.getDefaultSubscriptionPref = getDefaultSubscriptionPref; diff --git a/packages/rocketchat-utils/client/index.js b/packages/rocketchat-utils/client/index.js index a11bb5877925..b743c400456c 100644 --- a/packages/rocketchat-utils/client/index.js +++ b/packages/rocketchat-utils/client/index.js @@ -1,9 +1,11 @@ import { t, isRtl } from '../lib/tapi18n'; import { isChrome, isFirefox } from './lib/browsers'; +import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; export { t, isRtl, isChrome, isFirefox, + getDefaultSubscriptionPref, }; diff --git a/packages/rocketchat-utils/lib/getDefaultSubscriptionPref.js b/packages/rocketchat-utils/lib/getDefaultSubscriptionPref.js new file mode 100644 index 000000000000..294a7d50a734 --- /dev/null +++ b/packages/rocketchat-utils/lib/getDefaultSubscriptionPref.js @@ -0,0 +1,31 @@ +export const getDefaultSubscriptionPref = (userPref) => { + const subscription = {}; + + const { + desktopNotifications, + mobileNotifications, + emailNotificationMode, + highlights, + } = (userPref.settings && userPref.settings.preferences) || {}; + + if (Array.isArray(highlights) && highlights.length) { + subscription.userHighlights = highlights; + } + + if (desktopNotifications && desktopNotifications !== 'default') { + subscription.desktopNotifications = desktopNotifications; + subscription.desktopPrefOrigin = 'user'; + } + + if (mobileNotifications && mobileNotifications !== 'default') { + subscription.mobilePushNotifications = mobileNotifications; + subscription.mobilePrefOrigin = 'user'; + } + + if (emailNotificationMode && emailNotificationMode !== 'default') { + subscription.emailNotifications = emailNotificationMode; + subscription.emailPrefOrigin = 'user'; + } + + return subscription; +}; diff --git a/packages/rocketchat-utils/server/index.js b/packages/rocketchat-utils/server/index.js index 49c8d6407fbc..1fc8d8d2b0cc 100644 --- a/packages/rocketchat-utils/server/index.js +++ b/packages/rocketchat-utils/server/index.js @@ -1,6 +1,8 @@ import { t, isRtl } from '../lib/tapi18n'; +import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; export { t, isRtl, + getDefaultSubscriptionPref, }; From 24cfb4f0e32af0e02786778446e64bad7c9560c7 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 26 Dec 2018 10:28:51 -0200 Subject: [PATCH 04/59] Move client models from rocketchat:lib to rocketchat:models --- .../rocketchat-lib/client/models/Avatars.js | 9 +-- .../rocketchat-lib/client/models/Uploads.js | 8 +-- .../client/models/UserDataFiles.js | 9 +-- .../rocketchat-lib/client/models/_Base.js | 56 +------------------ packages/rocketchat-models/client/index.js | 11 ++++ .../client/models/Avatars.js | 10 ++++ .../client/models/Uploads.js | 10 ++++ .../client/models/UserDataFiles.js | 10 ++++ .../rocketchat-models/client/models/_Base.js | 55 ++++++++++++++++++ 9 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 packages/rocketchat-models/client/models/Avatars.js create mode 100644 packages/rocketchat-models/client/models/Uploads.js create mode 100644 packages/rocketchat-models/client/models/UserDataFiles.js create mode 100644 packages/rocketchat-models/client/models/_Base.js diff --git a/packages/rocketchat-lib/client/models/Avatars.js b/packages/rocketchat-lib/client/models/Avatars.js index bd8804e746c8..455ca64a97d1 100644 --- a/packages/rocketchat-lib/client/models/Avatars.js +++ b/packages/rocketchat-lib/client/models/Avatars.js @@ -1,6 +1,3 @@ -RocketChat.models.Avatars = new class extends RocketChat.models._Base { - constructor() { - super(); - this._initModel('avatars'); - } -}; +import { Avatars } from 'meteor/rocketchat:models'; + +RocketChat.models.Avatars = Avatars; diff --git a/packages/rocketchat-lib/client/models/Uploads.js b/packages/rocketchat-lib/client/models/Uploads.js index eefe630c708c..7dc92cf6f67b 100644 --- a/packages/rocketchat-lib/client/models/Uploads.js +++ b/packages/rocketchat-lib/client/models/Uploads.js @@ -1,7 +1,3 @@ +import { Uploads } from 'meteor/rocketchat:models'; -RocketChat.models.Uploads = new class extends RocketChat.models._Base { - constructor() { - super(); - this._initModel('uploads'); - } -}; +RocketChat.models.Uploads = Uploads; diff --git a/packages/rocketchat-lib/client/models/UserDataFiles.js b/packages/rocketchat-lib/client/models/UserDataFiles.js index 80e7e22e162e..b3eff0ac6125 100644 --- a/packages/rocketchat-lib/client/models/UserDataFiles.js +++ b/packages/rocketchat-lib/client/models/UserDataFiles.js @@ -1,6 +1,3 @@ -RocketChat.models.UserDataFiles = new class extends RocketChat.models._Base { - constructor() { - super(); - this._initModel('userDataFiles'); - } -}; +import { UserDataFiles } from 'meteor/rocketchat:models'; + +RocketChat.models.UserDataFiles = UserDataFiles; diff --git a/packages/rocketchat-lib/client/models/_Base.js b/packages/rocketchat-lib/client/models/_Base.js index 5a283e2ebcbc..bfda6cf65cce 100644 --- a/packages/rocketchat-lib/client/models/_Base.js +++ b/packages/rocketchat-lib/client/models/_Base.js @@ -1,55 +1,3 @@ -import { check } from 'meteor/check'; -import { Mongo } from 'meteor/mongo'; +import { Base } from 'meteor/rocketchat:models'; -RocketChat.models._Base = class { - - _baseName() { - return 'rocketchat_'; - } - - _initModel(name) { - check(name, String); - return this.model = new Mongo.Collection(this._baseName() + name); - } - - find(...args) { - return this.model.find.apply(this.model, args); - } - - findOne(...args) { - return this.model.findOne.apply(this.model, args); - } - - insert(...args) { - return this.model.insert.apply(this.model, args); - } - - update(...args) { - return this.model.update.apply(this.model, args); - } - - upsert(...args) { - return this.model.upsert.apply(this.model, args); - } - - remove(...args) { - return this.model.remove.apply(this.model, args); - } - - allow(...args) { - return this.model.allow.apply(this.model, args); - } - - deny(...args) { - return this.model.deny.apply(this.model, args); - } - - ensureIndex() {} - - dropIndex() {} - - tryEnsureIndex() {} - - tryDropIndex() {} - -}; +RocketChat.models._Base = Base; diff --git a/packages/rocketchat-models/client/index.js b/packages/rocketchat-models/client/index.js index e69de29bb2d1..a41ae40a2d38 100644 --- a/packages/rocketchat-models/client/index.js +++ b/packages/rocketchat-models/client/index.js @@ -0,0 +1,11 @@ +import { Base } from './models/_Base'; +import Avatars from './models/Avatars'; +import Uploads from './models/Uploads'; +import UserDataFiles from './models/UserDataFiles'; + +export { + Base, + Avatars, + Uploads, + UserDataFiles, +}; diff --git a/packages/rocketchat-models/client/models/Avatars.js b/packages/rocketchat-models/client/models/Avatars.js new file mode 100644 index 000000000000..103df0c8196a --- /dev/null +++ b/packages/rocketchat-models/client/models/Avatars.js @@ -0,0 +1,10 @@ +import { Base } from './_Base'; + +export class Avatars extends Base { + constructor() { + super(); + this._initModel('avatars'); + } +}; + +export default new Avatars(); diff --git a/packages/rocketchat-models/client/models/Uploads.js b/packages/rocketchat-models/client/models/Uploads.js new file mode 100644 index 000000000000..7e3a4f443036 --- /dev/null +++ b/packages/rocketchat-models/client/models/Uploads.js @@ -0,0 +1,10 @@ +import { Base } from './_Base'; + +export class Uploads extends Base { + constructor() { + super(); + this._initModel('uploads'); + } +}; + +export default new Uploads(); diff --git a/packages/rocketchat-models/client/models/UserDataFiles.js b/packages/rocketchat-models/client/models/UserDataFiles.js new file mode 100644 index 000000000000..511a2420fcc0 --- /dev/null +++ b/packages/rocketchat-models/client/models/UserDataFiles.js @@ -0,0 +1,10 @@ +import { Base } from './_Base'; + +export class UserDataFiles extends Base { + constructor() { + super(); + this._initModel('userDataFiles'); + } +}; + +export default new UserDataFiles(); diff --git a/packages/rocketchat-models/client/models/_Base.js b/packages/rocketchat-models/client/models/_Base.js new file mode 100644 index 000000000000..1dc0d3e3bb8d --- /dev/null +++ b/packages/rocketchat-models/client/models/_Base.js @@ -0,0 +1,55 @@ +import { check } from 'meteor/check'; +import { Mongo } from 'meteor/mongo'; + +export class Base { + + _baseName() { + return 'rocketchat_'; + } + + _initModel(name) { + check(name, String); + return this.model = new Mongo.Collection(this._baseName() + name); + } + + find(...args) { + return this.model.find.apply(this.model, args); + } + + findOne(...args) { + return this.model.findOne.apply(this.model, args); + } + + insert(...args) { + return this.model.insert.apply(this.model, args); + } + + update(...args) { + return this.model.update.apply(this.model, args); + } + + upsert(...args) { + return this.model.upsert.apply(this.model, args); + } + + remove(...args) { + return this.model.remove.apply(this.model, args); + } + + allow(...args) { + return this.model.allow.apply(this.model, args); + } + + deny(...args) { + return this.model.deny.apply(this.model, args); + } + + ensureIndex() {} + + dropIndex() {} + + tryEnsureIndex() {} + + tryDropIndex() {} + +}; From 0683f574d6584b604bac118473376f922403ce8a Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 26 Dec 2018 10:37:12 -0200 Subject: [PATCH 05/59] Fix lint --- packages/rocketchat-models/client/models/Avatars.js | 2 +- packages/rocketchat-models/client/models/Uploads.js | 2 +- packages/rocketchat-models/client/models/UserDataFiles.js | 2 +- packages/rocketchat-models/client/models/_Base.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rocketchat-models/client/models/Avatars.js b/packages/rocketchat-models/client/models/Avatars.js index 103df0c8196a..73a35965754a 100644 --- a/packages/rocketchat-models/client/models/Avatars.js +++ b/packages/rocketchat-models/client/models/Avatars.js @@ -5,6 +5,6 @@ export class Avatars extends Base { super(); this._initModel('avatars'); } -}; +} export default new Avatars(); diff --git a/packages/rocketchat-models/client/models/Uploads.js b/packages/rocketchat-models/client/models/Uploads.js index 7e3a4f443036..2c3ee0285d98 100644 --- a/packages/rocketchat-models/client/models/Uploads.js +++ b/packages/rocketchat-models/client/models/Uploads.js @@ -5,6 +5,6 @@ export class Uploads extends Base { super(); this._initModel('uploads'); } -}; +} export default new Uploads(); diff --git a/packages/rocketchat-models/client/models/UserDataFiles.js b/packages/rocketchat-models/client/models/UserDataFiles.js index 511a2420fcc0..93c1b1d44720 100644 --- a/packages/rocketchat-models/client/models/UserDataFiles.js +++ b/packages/rocketchat-models/client/models/UserDataFiles.js @@ -5,6 +5,6 @@ export class UserDataFiles extends Base { super(); this._initModel('userDataFiles'); } -}; +} export default new UserDataFiles(); diff --git a/packages/rocketchat-models/client/models/_Base.js b/packages/rocketchat-models/client/models/_Base.js index 1dc0d3e3bb8d..c8f9f0eafb82 100644 --- a/packages/rocketchat-models/client/models/_Base.js +++ b/packages/rocketchat-models/client/models/_Base.js @@ -52,4 +52,4 @@ export class Base { tryDropIndex() {} -}; +} From b26e192cb20fbb7ef7cc882dd331cdb61d5adfe4 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 11:34:57 -0200 Subject: [PATCH 06/59] Move rocketchat.info from lib to utils --- .scripts/set-version.js | 2 +- packages/rocketchat-lib/lib/info.js | 3 +++ packages/rocketchat-lib/package.js | 3 --- packages/rocketchat-utils/client/index.js | 2 ++ packages/rocketchat-utils/package.js | 1 + packages/{rocketchat-lib => rocketchat-utils}/rocketchat.info | 0 packages/rocketchat-utils/server/index.js | 2 ++ packages/rocketchat-version/plugin/compile-version.js | 3 +-- 8 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 packages/rocketchat-lib/lib/info.js rename packages/{rocketchat-lib => rocketchat-utils}/rocketchat.info (100%) diff --git a/.scripts/set-version.js b/.scripts/set-version.js index 0016f95e8f5a..27852ab15050 100644 --- a/.scripts/set-version.js +++ b/.scripts/set-version.js @@ -26,7 +26,7 @@ const files = [ './.circleci/update-releases.sh', './.docker/Dockerfile', './.docker/Dockerfile.rhel', - './packages/rocketchat-lib/rocketchat.info', + './packages/rocketchat-utils/rocketchat.info', ]; const readFile = (file) => new Promise((resolve, reject) => { fs.readFile(file, 'utf8', (error, result) => { diff --git a/packages/rocketchat-lib/lib/info.js b/packages/rocketchat-lib/lib/info.js new file mode 100644 index 000000000000..a47217dd6d90 --- /dev/null +++ b/packages/rocketchat-lib/lib/info.js @@ -0,0 +1,3 @@ +import { Info } from 'meteor/rocketchat:utils'; + +RocketChat.Info = Info; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index c994b17fc1b3..0337e192a31d 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -255,9 +255,6 @@ Package.onUse(function(api) { api.addFiles('startup/defaultRoomTypes.js'); api.addFiles('startup/index.js', 'server'); - // VERSION - api.addFiles('rocketchat.info'); - // EXPORT api.export('RocketChat'); api.export('handleError', 'client'); diff --git a/packages/rocketchat-utils/client/index.js b/packages/rocketchat-utils/client/index.js index b743c400456c..cb49ba819992 100644 --- a/packages/rocketchat-utils/client/index.js +++ b/packages/rocketchat-utils/client/index.js @@ -1,6 +1,7 @@ import { t, isRtl } from '../lib/tapi18n'; import { isChrome, isFirefox } from './lib/browsers'; import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; +import { Info } from '../rocketchat.info'; export { t, @@ -8,4 +9,5 @@ export { isChrome, isFirefox, getDefaultSubscriptionPref, + Info, }; diff --git a/packages/rocketchat-utils/package.js b/packages/rocketchat-utils/package.js index 6655c606eda4..05fa30074847 100644 --- a/packages/rocketchat-utils/package.js +++ b/packages/rocketchat-utils/package.js @@ -8,6 +8,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'tap:i18n', + 'rocketchat:version', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-lib/rocketchat.info b/packages/rocketchat-utils/rocketchat.info similarity index 100% rename from packages/rocketchat-lib/rocketchat.info rename to packages/rocketchat-utils/rocketchat.info diff --git a/packages/rocketchat-utils/server/index.js b/packages/rocketchat-utils/server/index.js index 1fc8d8d2b0cc..906b3434c665 100644 --- a/packages/rocketchat-utils/server/index.js +++ b/packages/rocketchat-utils/server/index.js @@ -1,8 +1,10 @@ import { t, isRtl } from '../lib/tapi18n'; import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; +import { Info } from '../rocketchat.info'; export { t, isRtl, getDefaultSubscriptionPref, + Info, }; diff --git a/packages/rocketchat-version/plugin/compile-version.js b/packages/rocketchat-version/plugin/compile-version.js index a96fd6fcac8e..8e3a9b6cd5d9 100644 --- a/packages/rocketchat-version/plugin/compile-version.js +++ b/packages/rocketchat-version/plugin/compile-version.js @@ -48,7 +48,6 @@ class VersionCompiler { if (err == null && output.commit != null) { output.commit.tag = result.replace('\n', ''); } - exec('git rev-parse --abbrev-ref HEAD', function(err, result) { if (err == null && output.commit != null) { output.commit.branch = result.replace('\n', ''); @@ -57,7 +56,7 @@ class VersionCompiler { const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); output.marketplaceApiVersion = pkg.dependencies['@rocket.chat/apps-engine'].replace(/[^0-9.]/g, ''); - output = `RocketChat.Info = ${ JSON.stringify(output, null, 4) };`; + output = `exports.Info = ${ JSON.stringify(output, null, 4) };`; file.addJavaScript({ data: output, path: `${ file.getPathInPackage() }.js`, From 8eeac90ecfc66f974809201e2da6d99cba4cbb93 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 11:36:18 -0200 Subject: [PATCH 07/59] Remove directly dependency between lib and migrations --- packages/rocketchat-lib/package.js | 2 ++ packages/rocketchat-lib/server/lib/migrations.js | 3 +++ packages/rocketchat-migrations/package.js | 1 - packages/rocketchat-migrations/server/index.js | 4 +++- packages/rocketchat-migrations/server/migrations.js | 5 +---- 5 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 packages/rocketchat-lib/server/lib/migrations.js diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 0337e192a31d..fade206644d5 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -26,6 +26,7 @@ Package.onUse(function(api) { api.use('check'); api.use('rocketchat:utils'); api.use('rocketchat:models'); + api.use('rocketchat:migrations'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); @@ -133,6 +134,7 @@ Package.onUse(function(api) { api.addFiles('server/lib/sendNotificationsOnMessage.js', 'server'); api.addFiles('server/lib/validateEmailDomain.js', 'server'); api.addFiles('server/lib/passwordPolicy.js', 'server'); + api.addFiles('server/lib/migrations.js', 'server'); // SERVER MODELS api.addFiles('server/models/_Base.js', 'server'); diff --git a/packages/rocketchat-lib/server/lib/migrations.js b/packages/rocketchat-lib/server/lib/migrations.js new file mode 100644 index 000000000000..5fa1a6f6f70d --- /dev/null +++ b/packages/rocketchat-lib/server/lib/migrations.js @@ -0,0 +1,3 @@ +import { Migrations } from 'meteor/rocketchat:migrations'; + +RocketChat.Migrations = Migrations; diff --git a/packages/rocketchat-migrations/package.js b/packages/rocketchat-migrations/package.js index 7caa5b6acd46..311865258ed1 100644 --- a/packages/rocketchat-migrations/package.js +++ b/packages/rocketchat-migrations/package.js @@ -8,7 +8,6 @@ Package.describe({ Package.onUse(function(api) { api.use([ 'ecmascript', - 'rocketchat:lib', 'rocketchat:version', 'logging', 'check', diff --git a/packages/rocketchat-migrations/server/index.js b/packages/rocketchat-migrations/server/index.js index 9f9866dccd95..bd8a290dd2b3 100644 --- a/packages/rocketchat-migrations/server/index.js +++ b/packages/rocketchat-migrations/server/index.js @@ -1 +1,3 @@ -import './migrations'; +import { Migrations } from './migrations'; + +export { Migrations }; diff --git a/packages/rocketchat-migrations/server/migrations.js b/packages/rocketchat-migrations/server/migrations.js index 085de7faf8a8..8ee7902fe2fd 100644 --- a/packages/rocketchat-migrations/server/migrations.js +++ b/packages/rocketchat-migrations/server/migrations.js @@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Mongo } from 'meteor/mongo'; -import { RocketChat } from 'meteor/rocketchat:lib'; import { Log } from 'meteor/logging'; import _ from 'underscore'; import s from 'underscore.string'; @@ -45,7 +44,7 @@ const DefaultMigration = { }, }; -const Migrations = this.Migrations = { +export const Migrations = { _list: [DefaultMigration], options: { // false disables logging @@ -411,5 +410,3 @@ Migrations._reset = function() { }]; this._collection.remove({}); }; - -RocketChat.Migrations = Migrations; From 7080105e98dcad62fa3ac83413d4276d8a9632af Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 11:37:35 -0200 Subject: [PATCH 08/59] Move statistics Model to rocketchat:models --- packages/rocketchat-models/server/index.js | 2 ++ .../server/models/Statistics.js | 28 +++++++++++++++++++ packages/rocketchat-statistics/package.js | 1 + .../server/models/Statistics.js | 26 ++--------------- 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 packages/rocketchat-models/server/models/Statistics.js diff --git a/packages/rocketchat-models/server/index.js b/packages/rocketchat-models/server/index.js index 372205cdf8fa..bb2ea816e180 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 Statistics from './models/Statistics'; export { Base, @@ -24,4 +25,5 @@ export { Uploads, UserDataFiles, Users, + Statistics, }; diff --git a/packages/rocketchat-models/server/models/Statistics.js b/packages/rocketchat-models/server/models/Statistics.js new file mode 100644 index 000000000000..ca95cd5642e6 --- /dev/null +++ b/packages/rocketchat-models/server/models/Statistics.js @@ -0,0 +1,28 @@ +import { Base } from './_Base'; + +export class Statistics extends Base { + constructor() { + super('statistics'); + + this.tryEnsureIndex({ createdAt: 1 }); + } + + // FIND ONE + findOneById(_id, options) { + const query = { _id }; + return this.findOne(query, options); + } + + findLast() { + const options = { + sort: { + createdAt: -1, + }, + limit: 1, + }; + const records = this.find({}, options).fetch(); + return records && records[0]; + } +} + +export default new Statistics(); diff --git a/packages/rocketchat-statistics/package.js b/packages/rocketchat-statistics/package.js index 0ab1fd373172..f379ce963648 100644 --- a/packages/rocketchat-statistics/package.js +++ b/packages/rocketchat-statistics/package.js @@ -10,6 +10,7 @@ Package.onUse(function(api) { 'mongo', 'ecmascript', 'rocketchat:lib', + 'rocketchat:models', 'konecty:multiple-instances-status', ]); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-statistics/server/models/Statistics.js b/packages/rocketchat-statistics/server/models/Statistics.js index cb52ecef6740..d628e60380d1 100644 --- a/packages/rocketchat-statistics/server/models/Statistics.js +++ b/packages/rocketchat-statistics/server/models/Statistics.js @@ -1,26 +1,4 @@ import { RocketChat } from 'meteor/rocketchat:lib'; +import { Statistics } from 'meteor/rocketchat:models'; -RocketChat.models.Statistics = new class extends RocketChat.models._Base { - constructor() { - super('statistics'); - - this.tryEnsureIndex({ createdAt: 1 }); - } - - // FIND ONE - findOneById(_id, options) { - const query = { _id }; - return this.findOne(query, options); - } - - findLast() { - const options = { - sort: { - createdAt: -1, - }, - limit: 1, - }; - const records = this.find({}, options).fetch(); - return records && records[0]; - } -}; +RocketChat.models.Statistics = Statistics; From 3d2395c6e4061976383d0e918cbd60c0a51c49ff Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 11:38:42 -0200 Subject: [PATCH 09/59] Create rocketchat:metrics to be able to depacking rocketchat callbacks --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-lib/package.js | 2 + packages/rocketchat-lib/server/lib/metrics.js | 182 +---------------- .../server/startup/statsTracker.js | 46 +---- packages/rocketchat-metrics/package.js | 16 ++ packages/rocketchat-metrics/server/index.js | 7 + .../rocketchat-metrics/server/lib/metrics.js | 187 ++++++++++++++++++ .../server/lib/statsTracker.js | 48 +++++ 9 files changed, 267 insertions(+), 225 deletions(-) create mode 100644 packages/rocketchat-metrics/package.js create mode 100644 packages/rocketchat-metrics/server/index.js create mode 100644 packages/rocketchat-metrics/server/lib/metrics.js create mode 100644 packages/rocketchat-metrics/server/lib/statsTracker.js diff --git a/.meteor/packages b/.meteor/packages index 98585f8b6d31..9d37a9746896 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -202,4 +202,5 @@ juliancwirko:postcss littledata:synced-cron rocketchat:utils rocketchat:settings -rocketchat:models \ No newline at end of file +rocketchat:models +rocketchat:metrics \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index e759dba2d774..872ff32b8816 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -191,6 +191,7 @@ rocketchat:mailer@0.0.1 rocketchat:mailmessages@0.0.1 rocketchat:mapview@0.0.1 rocketchat:markdown@0.0.2 +rocketchat:metrics@0.0.1 rocketchat:mentions@0.0.1 rocketchat:mentions-flextab@0.0.1 rocketchat:message-action@0.0.1 diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index fade206644d5..354cfa33b8fa 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -27,6 +27,7 @@ Package.onUse(function(api) { api.use('rocketchat:utils'); api.use('rocketchat:models'); api.use('rocketchat:migrations'); + api.use('rocketchat:metrics'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); @@ -82,6 +83,7 @@ Package.onUse(function(api) { api.addFiles('lib/messageBox.js'); api.addFiles('lib/MessageTypes.js'); api.addFiles('lib/templateVarHandler.js'); + api.addFiles('lib/info.js'); api.addFiles('lib/getUserNotificationPreference.js'); api.addFiles('lib/getUserPreference.js'); diff --git a/packages/rocketchat-lib/server/lib/metrics.js b/packages/rocketchat-lib/server/lib/metrics.js index 4608c7176054..79d0541c8349 100644 --- a/packages/rocketchat-lib/server/lib/metrics.js +++ b/packages/rocketchat-lib/server/lib/metrics.js @@ -1,184 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import client from 'prom-client'; -import connect from 'connect'; -import http from 'http'; -import _ from 'underscore'; +import { metrics } from 'meteor/rocketchat:metrics'; RocketChat.promclient = client; -client.collectDefaultMetrics(); -RocketChat.metrics = {}; - -// one sample metrics only - a counter - -RocketChat.metrics.meteorMethods = new client.Summary({ - name: 'rocketchat_meteor_methods', - help: 'summary of meteor methods count and time', - labelNames: ['method', 'has_connection', 'has_user'], -}); - -RocketChat.metrics.rocketchatCallbacks = new client.Summary({ - name: 'rocketchat_callbacks', - help: 'summary of rocketchat callbacks count and time', - labelNames: ['hook', 'callback'], -}); - -RocketChat.metrics.rocketchatHooks = new client.Summary({ - name: 'rocketchat_hooks', - help: 'summary of rocketchat hooks count and time', - labelNames: ['hook', 'callbacks_length'], -}); - -RocketChat.metrics.rocketchatRestApi = new client.Summary({ - name: 'rocketchat_rest_api', - help: 'summary of rocketchat rest api count and time', - labelNames: ['method', 'entrypoint', 'user_agent', 'status', 'version'], -}); - -RocketChat.metrics.meteorSubscriptions = new client.Summary({ - name: 'rocketchat_meteor_subscriptions', - help: 'summary of meteor subscriptions count and time', - labelNames: ['subscription'], -}); - -RocketChat.metrics.messagesSent = new client.Counter({ name: 'rocketchat_message_sent', help: 'cumulated number of messages sent' }); -RocketChat.metrics.notificationsSent = new client.Counter({ name: 'rocketchat_notification_sent', labelNames: ['notification_type'], help: 'cumulated number of notifications sent' }); - -RocketChat.metrics.ddpSessions = new client.Gauge({ name: 'rocketchat_ddp_sessions_count', help: 'number of open ddp sessions' }); -RocketChat.metrics.ddpAthenticatedSessions = new client.Gauge({ name: 'rocketchat_ddp_sessions_auth', help: 'number of authenticated open ddp sessions' }); -RocketChat.metrics.ddpConnectedUsers = new client.Gauge({ name: 'rocketchat_ddp_connected_users', help: 'number of unique connected users' }); - -RocketChat.metrics.version = new client.Gauge({ name: 'rocketchat_version', labelNames: ['version'], help: 'Rocket.Chat version' }); -RocketChat.metrics.migration = new client.Gauge({ name: 'rocketchat_migration', help: 'migration versoin' }); -RocketChat.metrics.instanceCount = new client.Gauge({ name: 'rocketchat_instance_count', help: 'instances running' }); -RocketChat.metrics.oplogEnabled = new client.Gauge({ name: 'rocketchat_oplog_enabled', labelNames: ['enabled'], help: 'oplog enabled' }); - -// User statistics -RocketChat.metrics.totalUsers = new client.Gauge({ name: 'rocketchat_users_total', help: 'total of users' }); -RocketChat.metrics.activeUsers = new client.Gauge({ name: 'rocketchat_users_active', help: 'total of active users' }); -RocketChat.metrics.nonActiveUsers = new client.Gauge({ name: 'rocketchat_users_non_active', help: 'total of non active users' }); -RocketChat.metrics.onlineUsers = new client.Gauge({ name: 'rocketchat_users_online', help: 'total of users online' }); -RocketChat.metrics.awayUsers = new client.Gauge({ name: 'rocketchat_users_away', help: 'total of users away' }); -RocketChat.metrics.offlineUsers = new client.Gauge({ name: 'rocketchat_users_offline', help: 'total of users offline' }); - -// Room statistics -RocketChat.metrics.totalRooms = new client.Gauge({ name: 'rocketchat_rooms_total', help: 'total of rooms' }); -RocketChat.metrics.totalChannels = new client.Gauge({ name: 'rocketchat_channels_total', help: 'total of public rooms/channels' }); -RocketChat.metrics.totalPrivateGroups = new client.Gauge({ name: 'rocketchat_private_groups_total', help: 'total of private rooms' }); -RocketChat.metrics.totalDirect = new client.Gauge({ name: 'rocketchat_direct_total', help: 'total of direct rooms' }); -RocketChat.metrics.totalLivechat = new client.Gauge({ name: 'rocketchat_livechat_total', help: 'total of livechat rooms' }); - -// Message statistics -RocketChat.metrics.totalMessages = new client.Gauge({ name: 'rocketchat_messages_total', help: 'total of messages' }); -RocketChat.metrics.totalChannelMessages = new client.Gauge({ name: 'rocketchat_channel_messages_total', help: 'total of messages in public rooms' }); -RocketChat.metrics.totalPrivateGroupMessages = new client.Gauge({ name: 'rocketchat_private_group_messages_total', help: 'total of messages in private rooms' }); -RocketChat.metrics.totalDirectMessages = new client.Gauge({ name: 'rocketchat_direct_messages_total', help: 'total of messages in direct rooms' }); -RocketChat.metrics.totalLivechatMessages = new client.Gauge({ name: 'rocketchat_livechat_messages_total', help: 'total of messages in livechat rooms' }); - -client.register.setDefaultLabels({ - uniqueId: RocketChat.settings.get('uniqueID'), - siteUrl: RocketChat.settings.get('Site_Url'), -}); - -const setPrometheusData = () => { - const date = new Date(); - - client.register.setDefaultLabels({ - unique_id: RocketChat.settings.get('uniqueID'), - site_url: RocketChat.settings.get('Site_Url'), - version: RocketChat.Info.version, - }); - - const sessions = Object.values(Meteor.server.sessions); - const authenticatedSessions = sessions.filter((s) => s.userId); - RocketChat.metrics.ddpSessions.set(sessions.length, date); - RocketChat.metrics.ddpAthenticatedSessions.set(authenticatedSessions.length, date); - RocketChat.metrics.ddpConnectedUsers.set(_.unique(authenticatedSessions.map((s) => s.userId)).length, date); - - if (!RocketChat.models.Statistics) { - return; - } - - const statistics = RocketChat.models.Statistics.findLast(); - if (!statistics) { - return; - } - - RocketChat.metrics.version.set({ version: statistics.version }, 1, date); - RocketChat.metrics.migration.set(RocketChat.Migrations._getControl().version, date); - RocketChat.metrics.instanceCount.set(statistics.instanceCount, date); - RocketChat.metrics.oplogEnabled.set({ enabled: statistics.oplogEnabled }, 1, date); - - // User statistics - RocketChat.metrics.totalUsers.set(statistics.totalUsers, date); - RocketChat.metrics.activeUsers.set(statistics.activeUsers, date); - RocketChat.metrics.nonActiveUsers.set(statistics.nonActiveUsers, date); - RocketChat.metrics.onlineUsers.set(statistics.onlineUsers, date); - RocketChat.metrics.awayUsers.set(statistics.awayUsers, date); - RocketChat.metrics.offlineUsers.set(statistics.offlineUsers, date); - - // Room statistics - RocketChat.metrics.totalRooms.set(statistics.totalRooms, date); - RocketChat.metrics.totalChannels.set(statistics.totalChannels, date); - RocketChat.metrics.totalPrivateGroups.set(statistics.totalPrivateGroups, date); - RocketChat.metrics.totalDirect.set(statistics.totalDirect, date); - RocketChat.metrics.totalLivechat.set(statistics.totalLivechat, date); - - // Message statistics - RocketChat.metrics.totalMessages.set(statistics.totalMessages, date); - RocketChat.metrics.totalChannelMessages.set(statistics.totalChannelMessages, date); - RocketChat.metrics.totalPrivateGroupMessages.set(statistics.totalPrivateGroupMessages, date); - RocketChat.metrics.totalDirectMessages.set(statistics.totalDirectMessages, date); - RocketChat.metrics.totalLivechatMessages.set(statistics.totalLivechatMessages, date); -}; - -const app = connect(); - -// const compression = require('compression'); -// app.use(compression()); - -app.use('/metrics', (req, res) => { - res.setHeader('Content-Type', 'text/plain'); - res.end(RocketChat.promclient.register.metrics()); -}); - -app.use('/', (req, res) => { - const html = ` - - Rocket.Chat Prometheus Exporter - - -

Rocket.Chat Prometheus Exporter

-

Metrics

- - `; - - res.write(html); - res.end(); -}); - -const server = http.createServer(app); - -let timer; -const updatePrometheusConfig = () => { - const port = RocketChat.settings.get('Prometheus_Port'); - const enabled = RocketChat.settings.get('Prometheus_Enabled'); - - if (port == null || enabled == null) { - return; - } - - if (enabled === true) { - server.listen({ - port, - host: process.env.BIND_IP || '0.0.0.0', - }); - timer = Meteor.setInterval(setPrometheusData, 5000); - } else { - server.close(); - Meteor.clearInterval(timer); - } -}; - -RocketChat.settings.get('Prometheus_Enabled', updatePrometheusConfig); -RocketChat.settings.get('Prometheus_Port', updatePrometheusConfig); +RocketChat.metrics = metrics; diff --git a/packages/rocketchat-lib/server/startup/statsTracker.js b/packages/rocketchat-lib/server/startup/statsTracker.js index db7e215a8372..9957ee8b29cc 100644 --- a/packages/rocketchat-lib/server/startup/statsTracker.js +++ b/packages/rocketchat-lib/server/startup/statsTracker.js @@ -1,45 +1,3 @@ -import { StatsD } from 'node-dogstatsd'; +import { StatsTracker } from 'meteor/rocketchat:metrics'; -RocketChat.statsTracker = new (class StatsTracker { - constructor() { - this.StatsD = StatsD; - this.dogstatsd = new this.StatsD(); - } - - track(type, stats, ...args) { - this.dogstatsd[type](`RocketChat.${ stats }`, ...args); - } - - now() { - const hrtime = process.hrtime(); - return (hrtime[0] * 1000000 + hrtime[1] / 1000); - } - - timing(stats, time, tags) { - this.track('timing', stats, time, tags); - } - - increment(stats, time, tags) { - this.track('increment', stats, time, tags); - } - - decrement(stats, time, tags) { - this.track('decrement', stats, time, tags); - } - - histogram(stats, time, tags) { - this.track('histogram', stats, time, tags); - } - - gauge(stats, time, tags) { - this.track('gauge', stats, time, tags); - } - - unique(stats, time, tags) { - this.track('unique', stats, time, tags); - } - - set(stats, time, tags) { - this.track('set', stats, time, tags); - } -}); +RocketChat.statsTracker = StatsTracker; diff --git a/packages/rocketchat-metrics/package.js b/packages/rocketchat-metrics/package.js new file mode 100644 index 000000000000..9b53cfae1e21 --- /dev/null +++ b/packages/rocketchat-metrics/package.js @@ -0,0 +1,16 @@ +Package.describe({ + name: 'rocketchat:metrics', + version: '0.0.1', + summary: 'Rocketchat Metrics', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:settings', + 'rocketchat:models', + 'rocketchat:version', + ]); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-metrics/server/index.js b/packages/rocketchat-metrics/server/index.js new file mode 100644 index 000000000000..7ba988baa9ef --- /dev/null +++ b/packages/rocketchat-metrics/server/index.js @@ -0,0 +1,7 @@ +import { metrics } from './lib/metrics'; +import StatsTracker from './lib/statsTracker'; + +export { + metrics, + StatsTracker, +}; diff --git a/packages/rocketchat-metrics/server/lib/metrics.js b/packages/rocketchat-metrics/server/lib/metrics.js new file mode 100644 index 000000000000..3c8e37ee82ff --- /dev/null +++ b/packages/rocketchat-metrics/server/lib/metrics.js @@ -0,0 +1,187 @@ +import { Meteor } from 'meteor/meteor'; +import { settings } from 'meteor/rocketchat:settings'; +import { Statistics } from 'meteor/rocketchat:models'; +import { Info } from 'meteor/rocketchat:utils'; +import { Migrations } from 'meteor/rocketchat:migrations'; +import client from 'prom-client'; +import connect from 'connect'; +import http from 'http'; +import _ from 'underscore'; + +client.collectDefaultMetrics(); + +export const metrics = {}; + +// one sample metrics only - a counter + +metrics.meteorMethods = new client.Summary({ + name: 'rocketchat_meteor_methods', + help: 'summary of meteor methods count and time', + labelNames: ['method', 'has_connection', 'has_user'], +}); + +metrics.rocketchatCallbacks = new client.Summary({ + name: 'rocketchat_callbacks', + help: 'summary of rocketchat callbacks count and time', + labelNames: ['hook', 'callback'], +}); + +metrics.rocketchatHooks = new client.Summary({ + name: 'rocketchat_hooks', + help: 'summary of rocketchat hooks count and time', + labelNames: ['hook', 'callbacks_length'], +}); + +metrics.rocketchatRestApi = new client.Summary({ + name: 'rocketchat_rest_api', + help: 'summary of rocketchat rest api count and time', + labelNames: ['method', 'entrypoint', 'user_agent', 'status', 'version'], +}); + +metrics.meteorSubscriptions = new client.Summary({ + name: 'rocketchat_meteor_subscriptions', + help: 'summary of meteor subscriptions count and time', + labelNames: ['subscription'], +}); + +metrics.messagesSent = new client.Counter({ name: 'rocketchat_message_sent', help: 'cumulated number of messages sent' }); +metrics.notificationsSent = new client.Counter({ name: 'rocketchat_notification_sent', labelNames: ['notification_type'], help: 'cumulated number of notifications sent' }); + +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.version = new client.Gauge({ name: 'rocketchat_version', labelNames: ['version'], help: 'Rocket.Chat version' }); +metrics.migration = new client.Gauge({ name: 'rocketchat_migration', help: 'migration versoin' }); +metrics.instanceCount = new client.Gauge({ name: 'rocketchat_instance_count', help: 'instances running' }); +metrics.oplogEnabled = new client.Gauge({ name: 'rocketchat_oplog_enabled', labelNames: ['enabled'], help: 'oplog enabled' }); + +// User statistics +metrics.totalUsers = new client.Gauge({ name: 'rocketchat_users_total', help: 'total of users' }); +metrics.activeUsers = new client.Gauge({ name: 'rocketchat_users_active', help: 'total of active users' }); +metrics.nonActiveUsers = new client.Gauge({ name: 'rocketchat_users_non_active', help: 'total of non active users' }); +metrics.onlineUsers = new client.Gauge({ name: 'rocketchat_users_online', help: 'total of users online' }); +metrics.awayUsers = new client.Gauge({ name: 'rocketchat_users_away', help: 'total of users away' }); +metrics.offlineUsers = new client.Gauge({ name: 'rocketchat_users_offline', help: 'total of users offline' }); + +// Room statistics +metrics.totalRooms = new client.Gauge({ name: 'rocketchat_rooms_total', help: 'total of rooms' }); +metrics.totalChannels = new client.Gauge({ name: 'rocketchat_channels_total', help: 'total of public rooms/channels' }); +metrics.totalPrivateGroups = new client.Gauge({ name: 'rocketchat_private_groups_total', help: 'total of private rooms' }); +metrics.totalDirect = new client.Gauge({ name: 'rocketchat_direct_total', help: 'total of direct rooms' }); +metrics.totalLivechat = new client.Gauge({ name: 'rocketchat_livechat_total', help: 'total of livechat rooms' }); + +// Message statistics +metrics.totalMessages = new client.Gauge({ name: 'rocketchat_messages_total', help: 'total of messages' }); +metrics.totalChannelMessages = new client.Gauge({ name: 'rocketchat_channel_messages_total', help: 'total of messages in public rooms' }); +metrics.totalPrivateGroupMessages = new client.Gauge({ name: 'rocketchat_private_group_messages_total', help: 'total of messages in private rooms' }); +metrics.totalDirectMessages = new client.Gauge({ name: 'rocketchat_direct_messages_total', help: 'total of messages in direct rooms' }); +metrics.totalLivechatMessages = new client.Gauge({ name: 'rocketchat_livechat_messages_total', help: 'total of messages in livechat rooms' }); + +client.register.setDefaultLabels({ + uniqueId: settings.get('uniqueID'), + siteUrl: settings.get('Site_Url'), +}); + +const setPrometheusData = () => { + const date = new Date(); + + client.register.setDefaultLabels({ + unique_id: settings.get('uniqueID'), + site_url: settings.get('Site_Url'), + version: Info.version, + }); + + const sessions = Object.values(Meteor.server.sessions); + const authenticatedSessions = sessions.filter((s) => s.userId); + metrics.ddpSessions.set(sessions.length, date); + metrics.ddpAthenticatedSessions.set(authenticatedSessions.length, date); + metrics.ddpConnectedUsers.set(_.unique(authenticatedSessions.map((s) => s.userId)).length, date); + + if (!Statistics) { + return; + } + + const statistics = Statistics.findLast(); + if (!statistics) { + return; + } + + metrics.version.set({ version: statistics.version }, 1, date); + metrics.migration.set(Migrations._getControl().version, date); + metrics.instanceCount.set(statistics.instanceCount, date); + metrics.oplogEnabled.set({ enabled: statistics.oplogEnabled }, 1, date); + + // User statistics + metrics.totalUsers.set(statistics.totalUsers, date); + metrics.activeUsers.set(statistics.activeUsers, date); + metrics.nonActiveUsers.set(statistics.nonActiveUsers, date); + metrics.onlineUsers.set(statistics.onlineUsers, date); + metrics.awayUsers.set(statistics.awayUsers, date); + metrics.offlineUsers.set(statistics.offlineUsers, date); + + // Room statistics + metrics.totalRooms.set(statistics.totalRooms, date); + metrics.totalChannels.set(statistics.totalChannels, date); + metrics.totalPrivateGroups.set(statistics.totalPrivateGroups, date); + metrics.totalDirect.set(statistics.totalDirect, date); + metrics.totalLivechat.set(statistics.totalLivechat, date); + + // Message statistics + metrics.totalMessages.set(statistics.totalMessages, date); + metrics.totalChannelMessages.set(statistics.totalChannelMessages, date); + metrics.totalPrivateGroupMessages.set(statistics.totalPrivateGroupMessages, date); + metrics.totalDirectMessages.set(statistics.totalDirectMessages, date); + metrics.totalLivechatMessages.set(statistics.totalLivechatMessages, date); +}; + +const app = connect(); + +// const compression = require('compression'); +// app.use(compression()); + +app.use('/metrics', (req, res) => { + res.setHeader('Content-Type', 'text/plain'); + res.end(client.register.metrics()); +}); + +app.use('/', (req, res) => { + const html = ` + + Rocket.Chat Prometheus Exporter + + +

Rocket.Chat Prometheus Exporter

+

Metrics

+ + `; + + res.write(html); + res.end(); +}); + +const server = http.createServer(app); + +let timer; +const updatePrometheusConfig = () => { + const port = settings.get('Prometheus_Port'); + const enabled = settings.get('Prometheus_Enabled'); + + if (port == null || enabled == null) { + return; + } + + if (enabled === true) { + server.listen({ + port, + host: process.env.BIND_IP || '0.0.0.0', + }); + timer = Meteor.setInterval(setPrometheusData, 5000); + } else { + server.close(); + Meteor.clearInterval(timer); + } +}; + +settings.get('Prometheus_Enabled', updatePrometheusConfig); +settings.get('Prometheus_Port', updatePrometheusConfig); diff --git a/packages/rocketchat-metrics/server/lib/statsTracker.js b/packages/rocketchat-metrics/server/lib/statsTracker.js new file mode 100644 index 000000000000..5073e303bf65 --- /dev/null +++ b/packages/rocketchat-metrics/server/lib/statsTracker.js @@ -0,0 +1,48 @@ +import { StatsD } from 'node-dogstatsd'; + +export class StatsTracker { + constructor() { + this.StatsD = StatsD; + this.dogstatsd = new this.StatsD(); + } + + track(type, stats, ...args) { + this.dogstatsd[type](`RocketChat.${ stats }`, ...args); + } + + now() { + const hrtime = process.hrtime(); + return (hrtime[0] * 1000000 + hrtime[1] / 1000); + } + + timing(stats, time, tags) { + this.track('timing', stats, time, tags); + } + + increment(stats, time, tags) { + this.track('increment', stats, time, tags); + } + + decrement(stats, time, tags) { + this.track('decrement', stats, time, tags); + } + + histogram(stats, time, tags) { + this.track('histogram', stats, time, tags); + } + + gauge(stats, time, tags) { + this.track('gauge', stats, time, tags); + } + + unique(stats, time, tags) { + this.track('unique', stats, time, tags); + } + + set(stats, time, tags) { + this.track('set', stats, time, tags); + } +} + +export default new StatsTracker(); + From 289c47869f58e6041643e1614bc2f2416112e537 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 14:31:18 -0200 Subject: [PATCH 10/59] Move callbacks to specific package --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-callbacks/client/index.js | 5 + .../rocketchat-callbacks/lib/callbacks.js | 152 ++++++++++++++++++ packages/rocketchat-callbacks/package.js | 15 ++ packages/rocketchat-callbacks/server/index.js | 5 + packages/rocketchat-lib/lib/callbacks.js | 146 +---------------- packages/rocketchat-lib/package.js | 1 + 8 files changed, 183 insertions(+), 145 deletions(-) create mode 100644 packages/rocketchat-callbacks/client/index.js create mode 100644 packages/rocketchat-callbacks/lib/callbacks.js create mode 100644 packages/rocketchat-callbacks/package.js create mode 100644 packages/rocketchat-callbacks/server/index.js diff --git a/.meteor/packages b/.meteor/packages index 9d37a9746896..aa8c0be5aaa7 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -203,4 +203,5 @@ littledata:synced-cron rocketchat:utils rocketchat:settings rocketchat:models -rocketchat:metrics \ No newline at end of file +rocketchat:metrics +rocketchat:callbacks \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index 872ff32b8816..76cbc06a8557 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -141,6 +141,7 @@ rocketchat:autotranslate@0.0.1 rocketchat:bigbluebutton@0.0.1 rocketchat:blockstack@0.0.1 rocketchat:bot-helpers@0.0.1 +rocketchat:callbacks@0.0.1 rocketchat:cas@1.0.0 rocketchat:channel-settings@0.0.1 rocketchat:channel-settings-mail-messages@0.0.1 diff --git a/packages/rocketchat-callbacks/client/index.js b/packages/rocketchat-callbacks/client/index.js new file mode 100644 index 000000000000..486af6f60697 --- /dev/null +++ b/packages/rocketchat-callbacks/client/index.js @@ -0,0 +1,5 @@ +import { callbacks } from '../lib/callbacks'; + +export { + callbacks, +}; diff --git a/packages/rocketchat-callbacks/lib/callbacks.js b/packages/rocketchat-callbacks/lib/callbacks.js new file mode 100644 index 000000000000..8a3e34ab0a5e --- /dev/null +++ b/packages/rocketchat-callbacks/lib/callbacks.js @@ -0,0 +1,152 @@ +import { Meteor } from 'meteor/meteor'; +import { Random } from 'meteor/random'; +import { metrics, StatsTracker } from 'meteor/rocketchat:metrics'; +import _ from 'underscore'; + +/* +* Callback hooks provide an easy way to add extra steps to common operations. +* @namespace RocketChat.callbacks +*/ + +export const callbacks = {}; + +if (Meteor.isServer) { + callbacks.showTime = true; + callbacks.showTotalTime = true; +} else { + callbacks.showTime = false; + callbacks.showTotalTime = false; +} + + +/* +* Callback priorities +*/ + +callbacks.priority = { + HIGH: -1000, + MEDIUM: 0, + LOW: 1000, +}; + +const getHooks = (hookName) => callbacks[hookName] || []; + +/* +* Add a callback function to a hook +* @param {String} hook - The name of the hook +* @param {Function} callback - The callback function +*/ + +callbacks.add = function(hook, callback, priority, id = Random.id()) { + if (!_.isNumber(priority)) { + priority = callbacks.priority.MEDIUM; + } + callback.priority = priority; + callback.id = id; + callbacks[hook] = getHooks(hook); + + if (callbacks.showTime === true) { + const err = new Error; + callback.stack = err.stack; + } + + if (callbacks[hook].find((cb) => cb.id === callback.id)) { + return; + } + callbacks[hook].push(callback); + callbacks[hook] = _.sortBy(callbacks[hook], function(callback) { + return callback.priority || callbacks.priority.MEDIUM; + }); +}; + + +/* +* Remove a callback from a hook +* @param {string} hook - The name of the hook +* @param {string} id - The callback's id +*/ + +callbacks.remove = function(hook, id) { + callbacks[hook] = getHooks(hook).filter((callback) => callback.id !== id); +}; + + +/* +* Successively run all of a hook's callbacks on an item +* @param {String} hook - The name of the hook +* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks +* @param {Object} [constant] - An optional constant that will be passed along to each callback +* @returns {Object} Returns the item after it's been through all the callbacks for this hook +*/ + +callbacks.run = function(hook, item, constant) { + const callbackItems = callbacks[hook]; + if (!callbackItems || !callbackItems.length) { + return item; + } + + let rocketchatHooksEnd; + if (Meteor.isServer) { + rocketchatHooksEnd = metrics.rocketchatHooks.startTimer({ hook, callbacks_length: callbacks.length }); + } + + let totalTime = 0; + const result = callbackItems.reduce(function(result, callback) { + let rocketchatCallbacksEnd; + if (Meteor.isServer) { + rocketchatCallbacksEnd = metrics.rocketchatCallbacks.startTimer({ hook, callback: callback.id }); + } + const time = callbacks.showTime === true || callbacks.showTotalTime === true ? Date.now() : 0; + + const callbackResult = callback(result, constant); + + if (callbacks.showTime === true || callbacks.showTotalTime === true) { + const currentTime = Date.now() - time; + totalTime += currentTime; + if (callbacks.showTime === true) { + if (Meteor.isServer) { + rocketchatCallbacksEnd(); + StatsTracker.timing('callbacks.time', currentTime, [`hook:${ hook }`, `callback:${ callback.id }`]); + } else { + let stack = callback.stack && typeof callback.stack.split === 'function' && callback.stack.split('\n'); + stack = stack && stack[2] && (stack[2].match(/\(.+\)/) || [])[0]; + console.log(String(currentTime), hook, callback.id, stack); + } + } + } + return (typeof callbackResult === 'undefined') ? result : callbackResult; + }, item); + + if (Meteor.isServer) { + rocketchatHooksEnd(); + } + + if (callbacks.showTotalTime === true) { + if (Meteor.isServer) { + StatsTracker.timing('callbacks.totalTime', totalTime, [`hook:${ hook }`]); + } else { + console.log(`${ hook }:`, totalTime); + } + } + + return result; + +}; + + +/* +* Successively run all of a hook's callbacks on an item, in async mode (only works on server) +* @param {String} hook - The name of the hook +* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks +* @param {Object} [constant] - An optional constant that will be passed along to each callback +*/ + +callbacks.runAsync = function(hook, item, constant) { + const callbackItems = callbacks[hook]; + if (Meteor.isServer && callbackItems && callbackItems.length) { + Meteor.defer(function() { + callbackItems.forEach((callback) => callback(item, constant)); + }); + } + return item; +}; diff --git a/packages/rocketchat-callbacks/package.js b/packages/rocketchat-callbacks/package.js new file mode 100644 index 000000000000..c90fa787f2cb --- /dev/null +++ b/packages/rocketchat-callbacks/package.js @@ -0,0 +1,15 @@ +Package.describe({ + name: 'rocketchat:callbacks', + summary: 'Rocketchat Callbacks', + version: '0.0.1', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:metrics', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-callbacks/server/index.js b/packages/rocketchat-callbacks/server/index.js new file mode 100644 index 000000000000..486af6f60697 --- /dev/null +++ b/packages/rocketchat-callbacks/server/index.js @@ -0,0 +1,5 @@ +import { callbacks } from '../lib/callbacks'; + +export { + callbacks, +}; diff --git a/packages/rocketchat-lib/lib/callbacks.js b/packages/rocketchat-lib/lib/callbacks.js index f4c6c2c8a5e9..00a472c89f48 100644 --- a/packages/rocketchat-lib/lib/callbacks.js +++ b/packages/rocketchat-lib/lib/callbacks.js @@ -1,5 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; +import { callbacks } from 'meteor/rocketchat:callbacks'; import _ from 'underscore'; /* @@ -7,145 +6,4 @@ import _ from 'underscore'; * @namespace RocketChat.callbacks */ -RocketChat.callbacks = {}; - -if (Meteor.isServer) { - RocketChat.callbacks.showTime = true; - RocketChat.callbacks.showTotalTime = true; -} else { - RocketChat.callbacks.showTime = false; - RocketChat.callbacks.showTotalTime = false; -} - - -/* -* Callback priorities -*/ - -RocketChat.callbacks.priority = { - HIGH: -1000, - MEDIUM: 0, - LOW: 1000, -}; - -const getHooks = (hookName) => RocketChat.callbacks[hookName] || []; - -/* -* Add a callback function to a hook -* @param {String} hook - The name of the hook -* @param {Function} callback - The callback function -*/ - -RocketChat.callbacks.add = function(hook, callback, priority, id = Random.id()) { - if (!_.isNumber(priority)) { - priority = RocketChat.callbacks.priority.MEDIUM; - } - callback.priority = priority; - callback.id = id; - RocketChat.callbacks[hook] = getHooks(hook); - - if (RocketChat.callbacks.showTime === true) { - const err = new Error; - callback.stack = err.stack; - } - - if (RocketChat.callbacks[hook].find((cb) => cb.id === callback.id)) { - return; - } - RocketChat.callbacks[hook].push(callback); - RocketChat.callbacks[hook] = _.sortBy(RocketChat.callbacks[hook], function(callback) { - return callback.priority || RocketChat.callbacks.priority.MEDIUM; - }); -}; - - -/* -* Remove a callback from a hook -* @param {string} hook - The name of the hook -* @param {string} id - The callback's id -*/ - -RocketChat.callbacks.remove = function(hook, id) { - RocketChat.callbacks[hook] = getHooks(hook).filter((callback) => callback.id !== id); -}; - - -/* -* Successively run all of a hook's callbacks on an item -* @param {String} hook - The name of the hook -* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks -* @param {Object} [constant] - An optional constant that will be passed along to each callback -* @returns {Object} Returns the item after it's been through all the callbacks for this hook -*/ - -RocketChat.callbacks.run = function(hook, item, constant) { - const callbacks = RocketChat.callbacks[hook]; - if (!callbacks || !callbacks.length) { - return item; - } - - let rocketchatHooksEnd; - if (Meteor.isServer) { - rocketchatHooksEnd = RocketChat.metrics.rocketchatHooks.startTimer({ hook, callbacks_length: callbacks.length }); - } - - let totalTime = 0; - const result = callbacks.reduce(function(result, callback) { - let rocketchatCallbacksEnd; - if (Meteor.isServer) { - rocketchatCallbacksEnd = RocketChat.metrics.rocketchatCallbacks.startTimer({ hook, callback: callback.id }); - } - const time = RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true ? Date.now() : 0; - - const callbackResult = callback(result, constant); - - if (RocketChat.callbacks.showTime === true || RocketChat.callbacks.showTotalTime === true) { - const currentTime = Date.now() - time; - totalTime += currentTime; - if (RocketChat.callbacks.showTime === true) { - if (Meteor.isServer) { - rocketchatCallbacksEnd(); - RocketChat.statsTracker.timing('callbacks.time', currentTime, [`hook:${ hook }`, `callback:${ callback.id }`]); - } else { - let stack = callback.stack && typeof callback.stack.split === 'function' && callback.stack.split('\n'); - stack = stack && stack[2] && (stack[2].match(/\(.+\)/) || [])[0]; - console.log(String(currentTime), hook, callback.id, stack); - } - } - } - return (typeof callbackResult === 'undefined') ? result : callbackResult; - }, item); - - if (Meteor.isServer) { - rocketchatHooksEnd(); - } - - if (RocketChat.callbacks.showTotalTime === true) { - if (Meteor.isServer) { - RocketChat.statsTracker.timing('callbacks.totalTime', totalTime, [`hook:${ hook }`]); - } else { - console.log(`${ hook }:`, totalTime); - } - } - - return result; - -}; - - -/* -* Successively run all of a hook's callbacks on an item, in async mode (only works on server) -* @param {String} hook - The name of the hook -* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks -* @param {Object} [constant] - An optional constant that will be passed along to each callback -*/ - -RocketChat.callbacks.runAsync = function(hook, item, constant) { - const callbacks = RocketChat.callbacks[hook]; - if (Meteor.isServer && callbacks && callbacks.length) { - Meteor.defer(function() { - callbacks.forEach((callback) => callback(item, constant)); - }); - } - return item; -}; +RocketChat.callbacks = callbacks; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 354cfa33b8fa..6c0adfafa5ac 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -28,6 +28,7 @@ Package.onUse(function(api) { api.use('rocketchat:models'); api.use('rocketchat:migrations'); api.use('rocketchat:metrics'); + api.use('rocketchat:callbacks'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); From ccba459815c38cd1a3345470f0078eeca5df3a40 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 14:36:27 -0200 Subject: [PATCH 11/59] Remove unused dependency --- packages/rocketchat-lib/lib/callbacks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rocketchat-lib/lib/callbacks.js b/packages/rocketchat-lib/lib/callbacks.js index 00a472c89f48..0199a6f05921 100644 --- a/packages/rocketchat-lib/lib/callbacks.js +++ b/packages/rocketchat-lib/lib/callbacks.js @@ -1,5 +1,4 @@ import { callbacks } from 'meteor/rocketchat:callbacks'; -import _ from 'underscore'; /* * Callback hooks provide an easy way to add extra steps to common operations. From 869c15d2c62d2de8f69510e0e11184fa638c6276 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 15:24:13 -0200 Subject: [PATCH 12/59] Move rocketchat-notifications to a specific package --- .meteor/packages | 3 +- .meteor/versions | 1 + .../rocketchat-lib/client/Notifications.js | 88 +------- packages/rocketchat-lib/package.js | 1 + .../server/functions/Notifications.js | 200 +---------------- .../rocketchat-notifications/client/index.js | 5 + .../client/lib/Notifications.js | 88 ++++++++ packages/rocketchat-notifications/package.js | 17 ++ .../rocketchat-notifications/server/index.js | 5 + .../server/lib/Notifications.js | 205 ++++++++++++++++++ 10 files changed, 328 insertions(+), 285 deletions(-) create mode 100644 packages/rocketchat-notifications/client/index.js create mode 100644 packages/rocketchat-notifications/client/lib/Notifications.js create mode 100644 packages/rocketchat-notifications/package.js create mode 100644 packages/rocketchat-notifications/server/index.js create mode 100644 packages/rocketchat-notifications/server/lib/Notifications.js diff --git a/.meteor/packages b/.meteor/packages index aa8c0be5aaa7..5f1a607c9c43 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -204,4 +204,5 @@ rocketchat:utils rocketchat:settings rocketchat:models rocketchat:metrics -rocketchat:callbacks \ No newline at end of file +rocketchat:callbacks +rocketchat:notifications \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index 76cbc06a8557..62a4ab476cf2 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -204,6 +204,7 @@ rocketchat:message-star@0.0.1 rocketchat:migrations@0.0.1 rocketchat:models@1.0.0 rocketchat:monitoring@2.30.2_3 +rocketchat:notifications@0.0.1 rocketchat:nrr@1.0.0 rocketchat:oauth2-server@2.0.0 rocketchat:oauth2-server-config@1.0.0 diff --git a/packages/rocketchat-lib/client/Notifications.js b/packages/rocketchat-lib/client/Notifications.js index e24840f2baae..beb33a23d219 100644 --- a/packages/rocketchat-lib/client/Notifications.js +++ b/packages/rocketchat-lib/client/Notifications.js @@ -1,87 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; +import { Notifications } from 'meteor/rocketchat:notifications'; -RocketChat.Notifications = new class { - constructor(...args) { - this.logged = Meteor.userId() !== null; - this.loginCb = []; - Tracker.autorun(() => { - if (Meteor.userId() !== null && this.logged === false) { - this.loginCb.forEach((cb) => cb()); - } - return this.logged = Meteor.userId() !== null; - }); - this.debug = false; - this.streamAll = new Meteor.Streamer('notify-all'); - this.streamLogged = new Meteor.Streamer('notify-logged'); - this.streamRoom = new Meteor.Streamer('notify-room'); - this.streamRoomUsers = new Meteor.Streamer('notify-room-users'); - this.streamUser = new Meteor.Streamer('notify-user'); - if (this.debug === true) { - this.onAll(function() { - return console.log('RocketChat.Notifications: onAll', args); - }); - this.onUser(function() { - return console.log('RocketChat.Notifications: onAll', args); - }); - } - } - - onLogin(cb) { - this.loginCb.push(cb); - if (this.logged) { - return cb(); - } - } - notifyRoom(room, eventName, ...args) { - if (this.debug === true) { - console.log('RocketChat.Notifications: notifyRoom', [room, eventName, ...args]); - } - args.unshift(`${ room }/${ eventName }`); - return this.streamRoom.emit.apply(this.streamRoom, args); - } - notifyUser(userId, eventName, ...args) { - if (this.debug === true) { - console.log('RocketChat.Notifications: notifyUser', [userId, eventName, ...args]); - } - args.unshift(`${ userId }/${ eventName }`); - return this.streamUser.emit.apply(this.streamUser, args); - } - notifyUsersOfRoom(room, eventName, ...args) { - if (this.debug === true) { - console.log('RocketChat.Notifications: notifyUsersOfRoom', [room, eventName, ...args]); - } - args.unshift(`${ room }/${ eventName }`); - return this.streamRoomUsers.emit.apply(this.streamRoomUsers, args); - } - onAll(eventName, callback) { - return this.streamAll.on(eventName, callback); - } - onLogged(eventName, callback) { - return this.onLogin(() => this.streamLogged.on(eventName, callback)); - } - onRoom(room, eventName, callback) { - if (this.debug === true) { - this.streamRoom.on(room, function() { - return console.log(`RocketChat.Notifications: onRoom ${ room }`, [room, eventName, callback]); - }); - } - return this.streamRoom.on(`${ room }/${ eventName }`, callback); - } - onUser(eventName, callback) { - return this.streamUser.on(`${ Meteor.userId() }/${ eventName }`, callback); - } - unAll(callback) { - return this.streamAll.removeListener('notify', callback); - } - unLogged(callback) { - return this.streamLogged.removeListener('notify', callback); - } - unRoom(room, eventName, callback) { - return this.streamRoom.removeListener(`${ room }/${ eventName }`, callback); - } - unUser(eventName, callback) { - return this.streamUser.removeListener(`${ Meteor.userId() }/${ eventName }`, callback); - } - -}; +RocketChat.Notifications = Notifications; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 6c0adfafa5ac..2fa245e2e3d5 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -29,6 +29,7 @@ Package.onUse(function(api) { api.use('rocketchat:migrations'); api.use('rocketchat:metrics'); api.use('rocketchat:callbacks'); + api.use('rocketchat:notifications'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); diff --git a/packages/rocketchat-lib/server/functions/Notifications.js b/packages/rocketchat-lib/server/functions/Notifications.js index 221c47fb1156..beb33a23d219 100644 --- a/packages/rocketchat-lib/server/functions/Notifications.js +++ b/packages/rocketchat-lib/server/functions/Notifications.js @@ -1,199 +1,3 @@ +import { Notifications } from 'meteor/rocketchat:notifications'; -import { Meteor } from 'meteor/meteor'; -import { DDPCommon } from 'meteor/ddp-common'; - -const changedPayload = function(collection, id, fields) { - return DDPCommon.stringifyDDP({ - msg: 'changed', - collection, - id, - fields, - }); -}; -const send = function(self, msg) { - if (!self.socket) { - return; - } - self.socket.send(msg); -}; -class RoomStreamer extends Meteor.Streamer { - _publish(publication, eventName, options) { - super._publish(publication, eventName, options); - const uid = Meteor.userId(); - if (/rooms-changed/.test(eventName)) { - const roomEvent = (...args) => send(publication._session, changedPayload(this.subscriptionName, 'id', { - eventName: `${ uid }/rooms-changed`, - args, - })); - const rooms = RocketChat.models.Subscriptions.find({ 'u._id': uid }, { fields: { rid: 1 } }).fetch(); - rooms.forEach(({ rid }) => { - this.on(rid, roomEvent); - }); - - const userEvent = (clientAction, { rid }) => { - switch (clientAction) { - case 'inserted': - rooms.push({ rid }); - this.on(rid, roomEvent); - break; - - case 'removed': - this.removeListener(rid, roomEvent); - break; - } - }; - this.on(uid, userEvent); - - publication.onStop(() => { - this.removeListener(uid, userEvent); - rooms.forEach(({ rid }) => this.removeListener(rid, roomEvent)); - }); - } - } -} - -RocketChat.Notifications = new class { - constructor() { - this.debug = false; - this.streamAll = new Meteor.Streamer('notify-all'); - this.streamLogged = new Meteor.Streamer('notify-logged'); - this.streamRoom = new Meteor.Streamer('notify-room'); - this.streamRoomUsers = new Meteor.Streamer('notify-room-users'); - this.streamUser = new RoomStreamer('notify-user'); - this.streamAll.allowWrite('none'); - this.streamLogged.allowWrite('none'); - this.streamRoom.allowWrite('none'); - this.streamRoomUsers.allowWrite(function(eventName, ...args) { - const [roomId, e] = eventName.split('/'); - // const user = Meteor.users.findOne(this.userId, { - // fields: { - // username: 1 - // } - // }); - if (RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId) != null) { - const subscriptions = RocketChat.models.Subscriptions.findByRoomIdAndNotUserId(roomId, this.userId).fetch(); - subscriptions.forEach((subscription) => RocketChat.Notifications.notifyUser(subscription.u._id, e, ...args)); - } - return false; - }); - this.streamUser.allowWrite('logged'); - this.streamAll.allowRead('all'); - this.streamLogged.allowRead('logged'); - this.streamRoom.allowRead(function(eventName, extraData) { - const [roomId] = eventName.split('/'); - const room = RocketChat.models.Rooms.findOneById(roomId); - if (!room) { - console.warn(`Invalid streamRoom eventName: "${ eventName }"`); - return false; - } - if (room.t === 'l' && extraData && extraData.token && room.v.token === extraData.token) { - return true; - } - if (this.userId == null) { - return false; - } - const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, { fields: { _id: 1 } }); - return subscription != null; - }); - this.streamRoomUsers.allowRead('none'); - this.streamUser.allowRead(function(eventName) { - const [userId] = eventName.split('/'); - return (this.userId != null) && this.userId === userId; - }); - } - - notifyAll(eventName, ...args) { - if (this.debug === true) { - console.log('notifyAll', [eventName, ...args]); - } - args.unshift(eventName); - return this.streamAll.emit.apply(this.streamAll, args); - } - - notifyLogged(eventName, ...args) { - if (this.debug === true) { - console.log('notifyLogged', [eventName, ...args]); - } - args.unshift(eventName); - return this.streamLogged.emit.apply(this.streamLogged, args); - } - - notifyRoom(room, eventName, ...args) { - if (this.debug === true) { - console.log('notifyRoom', [room, eventName, ...args]); - } - args.unshift(`${ room }/${ eventName }`); - return this.streamRoom.emit.apply(this.streamRoom, args); - } - - notifyUser(userId, eventName, ...args) { - if (this.debug === true) { - console.log('notifyUser', [userId, eventName, ...args]); - } - args.unshift(`${ userId }/${ eventName }`); - return this.streamUser.emit.apply(this.streamUser, args); - } - - notifyAllInThisInstance(eventName, ...args) { - if (this.debug === true) { - console.log('notifyAll', [eventName, ...args]); - } - args.unshift(eventName); - return this.streamAll.emitWithoutBroadcast.apply(this.streamAll, args); - } - - notifyLoggedInThisInstance(eventName, ...args) { - if (this.debug === true) { - console.log('notifyLogged', [eventName, ...args]); - } - args.unshift(eventName); - return this.streamLogged.emitWithoutBroadcast.apply(this.streamLogged, args); - } - - notifyRoomInThisInstance(room, eventName, ...args) { - if (this.debug === true) { - console.log('notifyRoomAndBroadcast', [room, eventName, ...args]); - } - args.unshift(`${ room }/${ eventName }`); - return this.streamRoom.emitWithoutBroadcast.apply(this.streamRoom, args); - } - - notifyUserInThisInstance(userId, eventName, ...args) { - if (this.debug === true) { - console.log('notifyUserAndBroadcast', [userId, eventName, ...args]); - } - args.unshift(`${ userId }/${ eventName }`); - return this.streamUser.emitWithoutBroadcast.apply(this.streamUser, args); - } -}; - -RocketChat.Notifications.streamRoom.allowWrite(function(eventName, username, typing, extraData) { - const [roomId, e] = eventName.split('/'); - - if (e === 'webrtc') { - return true; - } - if (e === 'typing') { - const key = RocketChat.settings.get('UI_Use_Real_Name') ? 'name' : 'username'; - // typing from livechat widget - if (extraData && extraData.token) { - const room = RocketChat.models.Rooms.findOneById(roomId); - if (room && room.t === 'l' && room.v.token === extraData.token) { - return true; - } - } - - const user = Meteor.users.findOne(this.userId, { - fields: { - [key]: 1, - }, - }); - - if (!user) { - return false; - } - - return user[key] === username; - } - return false; -}); +RocketChat.Notifications = Notifications; diff --git a/packages/rocketchat-notifications/client/index.js b/packages/rocketchat-notifications/client/index.js new file mode 100644 index 000000000000..edafa8c4a7fd --- /dev/null +++ b/packages/rocketchat-notifications/client/index.js @@ -0,0 +1,5 @@ +import Notifications from './lib/Notifications'; + +export { + Notifications, +}; diff --git a/packages/rocketchat-notifications/client/lib/Notifications.js b/packages/rocketchat-notifications/client/lib/Notifications.js new file mode 100644 index 000000000000..e9dd1853189d --- /dev/null +++ b/packages/rocketchat-notifications/client/lib/Notifications.js @@ -0,0 +1,88 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + +class Notifications { + constructor(...args) { + this.logged = Meteor.userId() !== null; + this.loginCb = []; + Tracker.autorun(() => { + if (Meteor.userId() !== null && this.logged === false) { + this.loginCb.forEach((cb) => cb()); + } + return this.logged = Meteor.userId() !== null; + }); + this.debug = false; + this.streamAll = new Meteor.Streamer('notify-all'); + this.streamLogged = new Meteor.Streamer('notify-logged'); + this.streamRoom = new Meteor.Streamer('notify-room'); + this.streamRoomUsers = new Meteor.Streamer('notify-room-users'); + this.streamUser = new Meteor.Streamer('notify-user'); + if (this.debug === true) { + this.onAll(function() { + return console.log('RocketChat.Notifications: onAll', args); + }); + this.onUser(function() { + return console.log('RocketChat.Notifications: onAll', args); + }); + } + } + + onLogin(cb) { + this.loginCb.push(cb); + if (this.logged) { + return cb(); + } + } + notifyRoom(room, eventName, ...args) { + if (this.debug === true) { + console.log('RocketChat.Notifications: notifyRoom', [room, eventName, ...args]); + } + args.unshift(`${ room }/${ eventName }`); + return this.streamRoom.emit.apply(this.streamRoom, args); + } + notifyUser(userId, eventName, ...args) { + if (this.debug === true) { + console.log('RocketChat.Notifications: notifyUser', [userId, eventName, ...args]); + } + args.unshift(`${ userId }/${ eventName }`); + return this.streamUser.emit.apply(this.streamUser, args); + } + notifyUsersOfRoom(room, eventName, ...args) { + if (this.debug === true) { + console.log('RocketChat.Notifications: notifyUsersOfRoom', [room, eventName, ...args]); + } + args.unshift(`${ room }/${ eventName }`); + return this.streamRoomUsers.emit.apply(this.streamRoomUsers, args); + } + onAll(eventName, callback) { + return this.streamAll.on(eventName, callback); + } + onLogged(eventName, callback) { + return this.onLogin(() => this.streamLogged.on(eventName, callback)); + } + onRoom(room, eventName, callback) { + if (this.debug === true) { + this.streamRoom.on(room, function() { + return console.log(`RocketChat.Notifications: onRoom ${ room }`, [room, eventName, callback]); + }); + } + return this.streamRoom.on(`${ room }/${ eventName }`, callback); + } + onUser(eventName, callback) { + return this.streamUser.on(`${ Meteor.userId() }/${ eventName }`, callback); + } + unAll(callback) { + return this.streamAll.removeListener('notify', callback); + } + unLogged(callback) { + return this.streamLogged.removeListener('notify', callback); + } + unRoom(room, eventName, callback) { + return this.streamRoom.removeListener(`${ room }/${ eventName }`, callback); + } + unUser(eventName, callback) { + return this.streamUser.removeListener(`${ Meteor.userId() }/${ eventName }`, callback); + } +} + +export default new Notifications(); diff --git a/packages/rocketchat-notifications/package.js b/packages/rocketchat-notifications/package.js new file mode 100644 index 000000000000..78368ec7c4ad --- /dev/null +++ b/packages/rocketchat-notifications/package.js @@ -0,0 +1,17 @@ +Package.describe({ + name: 'rocketchat:notifications', + version: '0.0.1', + summary: 'Rocketchat Notifications', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:models', + 'rocketchat:settings', + 'rocketchat:streamer', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-notifications/server/index.js b/packages/rocketchat-notifications/server/index.js new file mode 100644 index 000000000000..edafa8c4a7fd --- /dev/null +++ b/packages/rocketchat-notifications/server/index.js @@ -0,0 +1,5 @@ +import Notifications from './lib/Notifications'; + +export { + Notifications, +}; diff --git a/packages/rocketchat-notifications/server/lib/Notifications.js b/packages/rocketchat-notifications/server/lib/Notifications.js new file mode 100644 index 000000000000..f424c2222c3e --- /dev/null +++ b/packages/rocketchat-notifications/server/lib/Notifications.js @@ -0,0 +1,205 @@ +import { Meteor } from 'meteor/meteor'; +import { DDPCommon } from 'meteor/ddp-common'; +import { Subscriptions, Rooms } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; + +const changedPayload = function(collection, id, fields) { + return DDPCommon.stringifyDDP({ + msg: 'changed', + collection, + id, + fields, + }); +}; +const send = function(self, msg) { + if (!self.socket) { + return; + } + self.socket.send(msg); +}; +class RoomStreamer extends Meteor.Streamer { + _publish(publication, eventName, options) { + super._publish(publication, eventName, options); + const uid = Meteor.userId(); + if (/rooms-changed/.test(eventName)) { + const roomEvent = (...args) => send(publication._session, changedPayload(this.subscriptionName, 'id', { + eventName: `${ uid }/rooms-changed`, + args, + })); + const rooms = Subscriptions.find({ 'u._id': uid }, { fields: { rid: 1 } }).fetch(); + rooms.forEach(({ rid }) => { + this.on(rid, roomEvent); + }); + + const userEvent = (clientAction, { rid }) => { + switch (clientAction) { + case 'inserted': + rooms.push({ rid }); + this.on(rid, roomEvent); + break; + + case 'removed': + this.removeListener(rid, roomEvent); + break; + } + }; + this.on(uid, userEvent); + + publication.onStop(() => { + this.removeListener(uid, userEvent); + rooms.forEach(({ rid }) => this.removeListener(rid, roomEvent)); + }); + } + } +} + +class Notifications { + constructor() { + this.debug = false; + this.notifyUser = this.notifyUser.bind(this); + this.streamAll = new Meteor.Streamer('notify-all'); + this.streamLogged = new Meteor.Streamer('notify-logged'); + this.streamRoom = new Meteor.Streamer('notify-room'); + this.streamRoomUsers = new Meteor.Streamer('notify-room-users'); + this.streamUser = new RoomStreamer('notify-user'); + this.streamAll.allowWrite('none'); + this.streamLogged.allowWrite('none'); + this.streamRoom.allowWrite('none'); + this.streamRoomUsers.allowWrite(function(eventName, ...args) { + const [roomId, e] = eventName.split('/'); + // const user = Meteor.users.findOne(this.userId, { + // fields: { + // username: 1 + // } + // }); + if (Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId) != null) { + const subscriptions = Subscriptions.findByRoomIdAndNotUserId(roomId, this.userId).fetch(); + subscriptions.forEach((subscription) => this.notifyUser(subscription.u._id, e, ...args)); + } + return false; + }); + this.streamUser.allowWrite('logged'); + this.streamAll.allowRead('all'); + this.streamLogged.allowRead('logged'); + this.streamRoom.allowRead(function(eventName, extraData) { + const [roomId] = eventName.split('/'); + const room = Rooms.findOneById(roomId); + if (!room) { + console.warn(`Invalid streamRoom eventName: "${ eventName }"`); + return false; + } + if (room.t === 'l' && extraData && extraData.token && room.v.token === extraData.token) { + return true; + } + if (this.userId == null) { + return false; + } + const subscription = Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, { fields: { _id: 1 } }); + return subscription != null; + }); + this.streamRoomUsers.allowRead('none'); + this.streamUser.allowRead(function(eventName) { + const [userId] = eventName.split('/'); + return (this.userId != null) && this.userId === userId; + }); + } + + notifyAll(eventName, ...args) { + if (this.debug === true) { + console.log('notifyAll', [eventName, ...args]); + } + args.unshift(eventName); + return this.streamAll.emit.apply(this.streamAll, args); + } + + notifyLogged(eventName, ...args) { + if (this.debug === true) { + console.log('notifyLogged', [eventName, ...args]); + } + args.unshift(eventName); + return this.streamLogged.emit.apply(this.streamLogged, args); + } + + notifyRoom(room, eventName, ...args) { + if (this.debug === true) { + console.log('notifyRoom', [room, eventName, ...args]); + } + args.unshift(`${ room }/${ eventName }`); + return this.streamRoom.emit.apply(this.streamRoom, args); + } + + notifyUser(userId, eventName, ...args) { + if (this.debug === true) { + console.log('notifyUser', [userId, eventName, ...args]); + } + args.unshift(`${ userId }/${ eventName }`); + return this.streamUser.emit.apply(this.streamUser, args); + } + + notifyAllInThisInstance(eventName, ...args) { + if (this.debug === true) { + console.log('notifyAll', [eventName, ...args]); + } + args.unshift(eventName); + return this.streamAll.emitWithoutBroadcast.apply(this.streamAll, args); + } + + notifyLoggedInThisInstance(eventName, ...args) { + if (this.debug === true) { + console.log('notifyLogged', [eventName, ...args]); + } + args.unshift(eventName); + return this.streamLogged.emitWithoutBroadcast.apply(this.streamLogged, args); + } + + notifyRoomInThisInstance(room, eventName, ...args) { + if (this.debug === true) { + console.log('notifyRoomAndBroadcast', [room, eventName, ...args]); + } + args.unshift(`${ room }/${ eventName }`); + return this.streamRoom.emitWithoutBroadcast.apply(this.streamRoom, args); + } + + notifyUserInThisInstance(userId, eventName, ...args) { + if (this.debug === true) { + console.log('notifyUserAndBroadcast', [userId, eventName, ...args]); + } + args.unshift(`${ userId }/${ eventName }`); + return this.streamUser.emitWithoutBroadcast.apply(this.streamUser, args); + } +} + +const notifications = new Notifications(); + +notifications.streamRoom.allowWrite(function(eventName, username, typing, extraData) { + const [roomId, e] = eventName.split('/'); + + if (e === 'webrtc') { + return true; + } + if (e === 'typing') { + const key = settings.get('UI_Use_Real_Name') ? 'name' : 'username'; + // typing from livechat widget + if (extraData && extraData.token) { + const room = Rooms.findOneById(roomId); + if (room && room.t === 'l' && room.v.token === extraData.token) { + return true; + } + } + + const user = Meteor.users.findOne(this.userId, { + fields: { + [key]: 1, + }, + }); + + if (!user) { + return false; + } + + return user[key] === username; + } + return false; +}); + +export default notifications; From af817dfb25a22b3b118feff0509bbc30c243dc9d Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 27 Dec 2018 22:37:21 -0200 Subject: [PATCH 13/59] Move rocketchat-promises to a specific package --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-lib/lib/promises.js | 85 +------------------- packages/rocketchat-lib/package.js | 1 + packages/rocketchat-promises/client/index.js | 5 ++ packages/rocketchat-promises/lib/promises.js | 84 +++++++++++++++++++ packages/rocketchat-promises/package.js | 14 ++++ packages/rocketchat-promises/server/index.js | 5 ++ 8 files changed, 114 insertions(+), 84 deletions(-) create mode 100644 packages/rocketchat-promises/client/index.js create mode 100644 packages/rocketchat-promises/lib/promises.js create mode 100644 packages/rocketchat-promises/package.js create mode 100644 packages/rocketchat-promises/server/index.js diff --git a/.meteor/packages b/.meteor/packages index 5f1a607c9c43..4973ecae2af6 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -205,4 +205,5 @@ rocketchat:settings rocketchat:models rocketchat:metrics rocketchat:callbacks -rocketchat:notifications \ No newline at end of file +rocketchat:notifications +rocketchat:promises \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index 62a4ab476cf2..d4523b1fd35b 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -210,6 +210,7 @@ rocketchat:oauth2-server@2.0.0 rocketchat:oauth2-server-config@1.0.0 rocketchat:oembed@0.0.1 rocketchat:otr@0.0.1 +rocketchat:promises@0.0.1 rocketchat:push@3.3.1 rocketchat:push-notifications@0.0.1 rocketchat:reactions@0.0.1 diff --git a/packages/rocketchat-lib/lib/promises.js b/packages/rocketchat-lib/lib/promises.js index a306ccceeef6..875676508810 100644 --- a/packages/rocketchat-lib/lib/promises.js +++ b/packages/rocketchat-lib/lib/promises.js @@ -1,84 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; -import _ from 'underscore'; +import { promises } from 'meteor/rocketchat:promises'; -/* -* Callback hooks provide an easy way to add extra steps to common operations. -* @namespace RocketChat.promises -*/ - -RocketChat.promises = {}; - - -/* -* Callback priorities -*/ - -RocketChat.promises.priority = { - HIGH: -1000, - MEDIUM: 0, - LOW: 1000, -}; - -const getHook = (hookName) => RocketChat.promises[hookName] || []; - -/* -* Add a callback function to a hook -* @param {String} hook - The name of the hook -* @param {Function} callback - The callback function -*/ - -RocketChat.promises.add = function(hook, callback, p = RocketChat.promises.priority.MEDIUM, id) { - callback.priority = _.isNumber(p) ? p : RocketChat.promises.priority.MEDIUM; - callback.id = id || Random.id(); - RocketChat.promises[hook] = getHook(hook); - if (RocketChat.promises[hook].find((cb) => cb.id === callback.id)) { - return; - } - RocketChat.promises[hook].push(callback); - RocketChat.promises[hook] = _.sortBy(RocketChat.promises[hook], (callback) => callback.priority || RocketChat.promises.priority.MEDIUM); -}; - - -/* -* Remove a callback from a hook -* @param {string} hook - The name of the hook -* @param {string} id - The callback's id -*/ - -RocketChat.promises.remove = function(hook, id) { - RocketChat.promises[hook] = getHook(hook).filter((callback) => callback.id !== id); -}; - - -/* -* Successively run all of a hook's callbacks on an item -* @param {String} hook - The name of the hook -* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks -* @param {Object} [constant] - An optional constant that will be passed along to each callback -* @returns {Object} Returns the item after it's been through all the callbacks for this hook -*/ - -RocketChat.promises.run = function(hook, item, constant) { - const callbacks = RocketChat.promises[hook]; - if (callbacks == null || callbacks.length === 0) { - return Promise.resolve(item); - } - return callbacks.reduce((previousPromise, callback) => previousPromise.then((result) => callback(result, constant)), Promise.resolve(item)); -}; - - -/* -* Successively run all of a hook's callbacks on an item, in async mode (only works on server) -* @param {String} hook - The name of the hook -* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks -* @param {Object} [constant] - An optional constant that will be passed along to each callback -*/ - -RocketChat.promises.runAsync = function(hook, item, constant) { - const callbacks = RocketChat.promises[hook]; - if (!Meteor.isServer || callbacks == null || callbacks.length === 0) { - return item; - } - Meteor.defer(() => callbacks.forEach((callback) => callback(item, constant))); -}; +RocketChat.promises = promises; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 2fa245e2e3d5..3711153a6d6c 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -30,6 +30,7 @@ Package.onUse(function(api) { api.use('rocketchat:metrics'); api.use('rocketchat:callbacks'); api.use('rocketchat:notifications'); + api.use('rocketchat:promises'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); diff --git a/packages/rocketchat-promises/client/index.js b/packages/rocketchat-promises/client/index.js new file mode 100644 index 000000000000..999b1f62bc34 --- /dev/null +++ b/packages/rocketchat-promises/client/index.js @@ -0,0 +1,5 @@ +import { promises } from '../lib/promises'; + +export { + promises, +}; diff --git a/packages/rocketchat-promises/lib/promises.js b/packages/rocketchat-promises/lib/promises.js new file mode 100644 index 000000000000..62ad6b51af37 --- /dev/null +++ b/packages/rocketchat-promises/lib/promises.js @@ -0,0 +1,84 @@ +import { Meteor } from 'meteor/meteor'; +import { Random } from 'meteor/random'; +import _ from 'underscore'; + +/* +* Callback hooks provide an easy way to add extra steps to common operations. +* @namespace RocketChat.promises +*/ + +export const promises = {}; + + +/* +* Callback priorities +*/ + +promises.priority = { + HIGH: -1000, + MEDIUM: 0, + LOW: 1000, +}; + +const getHook = (hookName) => promises[hookName] || []; + +/* +* Add a callback function to a hook +* @param {String} hook - The name of the hook +* @param {Function} callback - The callback function +*/ + +promises.add = function(hook, callback, p = promises.priority.MEDIUM, id) { + callback.priority = _.isNumber(p) ? p : promises.priority.MEDIUM; + callback.id = id || Random.id(); + promises[hook] = getHook(hook); + if (promises[hook].find((cb) => cb.id === callback.id)) { + return; + } + promises[hook].push(callback); + promises[hook] = _.sortBy(promises[hook], (callback) => callback.priority || promises.priority.MEDIUM); +}; + + +/* +* Remove a callback from a hook +* @param {string} hook - The name of the hook +* @param {string} id - The callback's id +*/ + +promises.remove = function(hook, id) { + promises[hook] = getHook(hook).filter((callback) => callback.id !== id); +}; + + +/* +* Successively run all of a hook's callbacks on an item +* @param {String} hook - The name of the hook +* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks +* @param {Object} [constant] - An optional constant that will be passed along to each callback +* @returns {Object} Returns the item after it's been through all the callbacks for this hook +*/ + +promises.run = function(hook, item, constant) { + const callbacks = promises[hook]; + if (callbacks == null || callbacks.length === 0) { + return Promise.resolve(item); + } + return callbacks.reduce((previousPromise, callback) => previousPromise.then((result) => callback(result, constant)), Promise.resolve(item)); +}; + + +/* +* Successively run all of a hook's callbacks on an item, in async mode (only works on server) +* @param {String} hook - The name of the hook +* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks +* @param {Object} [constant] - An optional constant that will be passed along to each callback +*/ + +promises.runAsync = function(hook, item, constant) { + const callbacks = promises[hook]; + if (!Meteor.isServer || callbacks == null || callbacks.length === 0) { + return item; + } + Meteor.defer(() => callbacks.forEach((callback) => callback(item, constant))); +}; diff --git a/packages/rocketchat-promises/package.js b/packages/rocketchat-promises/package.js new file mode 100644 index 000000000000..98a03be8c724 --- /dev/null +++ b/packages/rocketchat-promises/package.js @@ -0,0 +1,14 @@ +Package.describe({ + name: 'rocketchat:promises', + version: '0.0.1', + summary: 'Rocketchat Promises', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-promises/server/index.js b/packages/rocketchat-promises/server/index.js new file mode 100644 index 000000000000..999b1f62bc34 --- /dev/null +++ b/packages/rocketchat-promises/server/index.js @@ -0,0 +1,5 @@ +import { promises } from '../lib/promises'; + +export { + promises, +}; From a8d506b9c7ecfb57d10944beb80f5bc9f8cbbda7 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 2 Jan 2019 22:01:55 -0200 Subject: [PATCH 14/59] remove directly dependency from metrics and models --- packages/rocketchat-metrics/package.js | 2 +- packages/rocketchat-metrics/server/lib/metrics.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/rocketchat-metrics/package.js b/packages/rocketchat-metrics/package.js index 9b53cfae1e21..21b91460874c 100644 --- a/packages/rocketchat-metrics/package.js +++ b/packages/rocketchat-metrics/package.js @@ -9,7 +9,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:settings', - 'rocketchat:models', + 'rocketchat:migrations', 'rocketchat:version', ]); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-metrics/server/lib/metrics.js b/packages/rocketchat-metrics/server/lib/metrics.js index 3c8e37ee82ff..5ba4efa3a350 100644 --- a/packages/rocketchat-metrics/server/lib/metrics.js +++ b/packages/rocketchat-metrics/server/lib/metrics.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; import { settings } from 'meteor/rocketchat:settings'; -import { Statistics } from 'meteor/rocketchat:models'; import { Info } from 'meteor/rocketchat:utils'; import { Migrations } from 'meteor/rocketchat:migrations'; import client from 'prom-client'; @@ -83,7 +82,7 @@ client.register.setDefaultLabels({ siteUrl: settings.get('Site_Url'), }); -const setPrometheusData = () => { +const setPrometheusData = async() => { const date = new Date(); client.register.setDefaultLabels({ @@ -97,6 +96,7 @@ const setPrometheusData = () => { metrics.ddpSessions.set(sessions.length, date); metrics.ddpAthenticatedSessions.set(authenticatedSessions.length, date); metrics.ddpConnectedUsers.set(_.unique(authenticatedSessions.map((s) => s.userId)).length, date); + const { Statistics } = await import('meteor/rocketchat:models'); if (!Statistics) { return; @@ -166,7 +166,6 @@ let timer; const updatePrometheusConfig = () => { const port = settings.get('Prometheus_Port'); const enabled = settings.get('Prometheus_Enabled'); - if (port == null || enabled == null) { return; } From 42c1ac9e17b94b35fa37353d1f86399face6c30b Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 2 Jan 2019 22:04:27 -0200 Subject: [PATCH 15/59] Move CachedCollection from lib to models --- .../client/lib/cachedCollection.js | 398 +---------------- packages/rocketchat-models/client/index.js | 3 + .../client/models/CachedCollection.js | 399 ++++++++++++++++++ packages/rocketchat-models/package.js | 1 + 4 files changed, 405 insertions(+), 396 deletions(-) create mode 100644 packages/rocketchat-models/client/models/CachedCollection.js diff --git a/packages/rocketchat-lib/client/lib/cachedCollection.js b/packages/rocketchat-lib/client/lib/cachedCollection.js index 7a2609e663fb..d41e8d115676 100644 --- a/packages/rocketchat-lib/client/lib/cachedCollection.js +++ b/packages/rocketchat-lib/client/lib/cachedCollection.js @@ -1,398 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Mongo } from 'meteor/mongo'; -import { Accounts } from 'meteor/accounts-base'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import localforage from 'localforage'; -import _ from 'underscore'; - -class CachedCollectionManager { - constructor() { - this.items = []; - this._syncEnabled = false; - this.reconnectCb = []; - this.loginCb = []; - this.logged = false; - - const { _unstoreLoginToken } = Accounts; - Accounts._unstoreLoginToken = (...args) => { - _unstoreLoginToken.apply(Accounts, args); - this.clearAllCacheOnLogout(); - }; - - // Wait 1s to start or the code will run before the connection and - // on first connection the `reconnect` callbacks will run - Meteor.setTimeout(() => { - let connectionWasOnline = true; - Tracker.autorun(() => { - const { connected } = Meteor.connection.status(); - - if (connected === true && connectionWasOnline === false) { - for (const cb of this.reconnectCb) { - cb(); - } - } - - connectionWasOnline = connected; - }); - }, 1000); - - Tracker.autorun(() => { - if (Meteor.userId() !== null) { - if (this.logged === false) { - for (const cb of this.loginCb) { - cb(); - } - } - } - - this.logged = Meteor.userId() !== null; - }); - } - - register(cachedCollection) { - this.items.push(cachedCollection); - } - - clearAllCache() { - for (const item of this.items) { - item.clearCache(); - } - } - - clearAllCacheOnLogout() { - for (const item of this.items) { - item.clearCacheOnLogout(); - } - } - - countQueries() { - for (const item of this.items) { - item.countQueries(); - } - } - - set syncEnabled(value) { - check(value, Boolean); - this._syncEnabled = value; - } - - get syncEnabled() { - return this._syncEnabled; - } - - onReconnect(cb) { - this.reconnectCb.push(cb); - } - - onLogin(cb) { - this.loginCb.push(cb); - if (this.logged) { - cb(); - } - } -} - -RocketChat.CachedCollectionManager = new CachedCollectionManager; - -const debug = false; - -const nullLog = function() {}; - -const log = function(...args) { - console.log(`CachedCollection ${ this.name } =>`, ...args); -}; - -class CachedCollection { - constructor({ - collection, - name, - methodName, - syncMethodName, - eventName, - eventType = 'onUser', - userRelated = true, - listenChangesForLoggedUsersOnly = false, - useSync = true, - useCache = true, - version = 8, - maxCacheTime = 60 * 60 * 24 * 30, - onSyncData = (/* action, record */) => {}, - }) { - this.collection = collection || new Mongo.Collection(null); - - this.ready = new ReactiveVar(false); - this.name = name; - this.methodName = methodName || `${ name }/get`; - this.syncMethodName = syncMethodName || `${ name }/get`; - this.eventName = eventName || `${ name }-changed`; - this.eventType = eventType; - this.useSync = useSync; - this.useCache = useCache; - this.listenChangesForLoggedUsersOnly = listenChangesForLoggedUsersOnly; - this.debug = debug; - this.version = version; - this.userRelated = userRelated; - this.updatedAt = new Date(0); - this.maxCacheTime = maxCacheTime; - this.onSyncData = onSyncData; - this.log = debug ? log : nullLog; - RocketChat.CachedCollectionManager.register(this); - - if (userRelated === true) { - RocketChat.CachedCollectionManager.onLogin(() => { - this.log('Init on login'); - this.ready.set(false); - this.updatedAt = new Date(0); - this.initiated = false; - this.init(); - }); - } - - if (this.useCache === false) { - return this.clearCache(); - } - } - - countQueries() { - this.log(`${ Object.keys(this.collection._collection.queries).length } queries`); - } - - recomputeCollectionQueries() { - this.log(`recomputing ${ Object.keys(this.collection._collection.queries).length } queries`); - _.each(this.collection._collection.queries, (query) => { - this.collection._collection._recomputeResults(query); - }); - } - - getToken() { - if (this.userRelated === false) { - return undefined; - } - - return Accounts._storedLoginToken(); - } - - loadFromCache(callback = () => {}) { - if (this.useCache === false) { - return callback(false); - } - - localforage.getItem(this.name, (error, data) => { - if (data && (data.version < this.version || data.token !== this.getToken() || this.getToken() === undefined)) { - this.clearCache(); - callback(false); - return; - } - - const now = new Date(); - if (data && now - data.updatedAt >= 1000 * this.maxCacheTime) { - this.clearCache(); - callback(false); - return; - } - - if (data && data.records && data.records.length > 0) { - this.log(`${ data.records.length } records loaded from cache`); - data.records.forEach((record) => { - RocketChat.callbacks.run(`cachedCollection-loadFromCache-${ this.name }`, record); - record.__cache__ = true; - this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); - - if (record._updatedAt) { - const _updatedAt = new Date(record._updatedAt); - if (_updatedAt > this.updatedAt) { - this.updatedAt = _updatedAt; - } - } - }); - - callback(true); - } else { - callback(false); - } - }); - } - - loadFromServer(callback = () => {}) { - Meteor.call(this.methodName, (error, data) => { - this.log(`${ data.length } records loaded from server`); - data.forEach((record) => { - RocketChat.callbacks.run(`cachedCollection-loadFromServer-${ this.name }`, record, 'changed'); - this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); - - this.onSyncData('changed', record); - - if (record._updatedAt && record._updatedAt > this.updatedAt) { - this.updatedAt = record._updatedAt; - } - }); - this.recomputeCollectionQueries(); - - if (this.updatedAt < new Date) { - this.updatedAt = new Date; - } - - callback(data); - }); - } - - loadFromServerAndPopulate() { - this.loadFromServer((loadedData) => { - this.ready.set(true); - this.saveCache(loadedData); - }); - } - - sync() { - if (RocketChat.CachedCollectionManager.syncEnabled === false || Meteor.connection._outstandingMethodBlocks.length !== 0) { - return false; - } - - this.log(`syncing from ${ this.updatedAt }`); - - Meteor.call(this.syncMethodName, this.updatedAt, (error, data) => { - let changes = []; - - if (data.update && data.update.length > 0) { - this.log(`${ data.update.length } records updated in sync`); - changes.push(...data.update); - } - - if (data.remove && data.remove.length > 0) { - this.log(`${ data.remove.length } records removed in sync`); - changes.push(...data.remove); - } - - changes = changes.sort((a, b) => { - const valueA = a._updatedAt || a._deletedAt; - const valueB = b._updatedAt || b._deletedAt; - - if (valueA < valueB) { - return -1; - } - - if (valueA > valueB) { - return 1; - } - - return 0; - }); - - for (const record of changes) { - RocketChat.callbacks.run(`cachedCollection-sync-${ this.name }`, record, record._deletedAt ? 'removed' : 'changed'); - if (record._deletedAt) { - this.collection.remove({ _id: record._id }); - - this.onSyncData('removed', record); - - if (record._deletedAt && record._deletedAt > this.updatedAt) { - this.updatedAt = record._deletedAt; - } - } else { - this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); - - this.onSyncData('changed', record); - - if (record._updatedAt && record._updatedAt > this.updatedAt) { - this.updatedAt = record._updatedAt; - } - } - } - - this.saveCache(); - }); - - return true; - } - - saveCache(data) { - if (this.useCache === false) { - return; - } - - this.log('saving cache'); - if (!data) { - data = this.collection.find().fetch(); - } - - localforage.setItem(this.name, { - updatedAt: new Date, - version: this.version, - token: this.getToken(), - records: data, - }); - this.log('saving cache (done)'); - } - - clearCacheOnLogout() { - if (this.userRelated === true) { - this.clearCache(); - } - } - - clearCache() { - this.log('clearing cache'); - localforage.removeItem(this.name); - this.collection.remove({}); - } - - setupListener(eventType, eventName) { - RocketChat.Notifications[eventType || this.eventType](eventName || this.eventName, (t, record) => { - this.log('record received', t, record); - RocketChat.callbacks.run(`cachedCollection-received-${ this.name }`, record, t); - if (t === 'removed') { - this.collection.remove(record._id); - RoomManager.close(record.t + record.name); - } else { - this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); - } - - this.saveCache(); - }); - } - - trySync() { - // Wait for an empty queue to load data again and sync - const interval = Meteor.setInterval(() => { - if (this.sync()) { - Meteor.clearInterval(interval); - } - }, 200); - } - - init() { - if (this.initiated === true) { - return; - } - - this.initiated = true; - this.loadFromCache((cacheLoaded) => { - this.ready.set(cacheLoaded); - - if (cacheLoaded === false) { - // If there is no cache load data immediately - this.loadFromServerAndPopulate(); - } else if (this.useSync === true) { - this.trySync(); - } - - if (this.useSync === true) { - RocketChat.CachedCollectionManager.onReconnect(() => { - this.trySync(); - }); - } - - if (this.listenChangesForLoggedUsersOnly) { - RocketChat.CachedCollectionManager.onLogin(() => { - this.setupListener(); - }); - } else { - this.setupListener(); - } - }); - } -} +import { CachedCollection, CachedCollectionManager } from 'meteor/rocketchat:models'; +RocketChat.CachedCollectionManager = CachedCollectionManager; RocketChat.CachedCollection = CachedCollection; diff --git a/packages/rocketchat-models/client/index.js b/packages/rocketchat-models/client/index.js index a41ae40a2d38..95e099e64e76 100644 --- a/packages/rocketchat-models/client/index.js +++ b/packages/rocketchat-models/client/index.js @@ -2,10 +2,13 @@ import { Base } from './models/_Base'; import Avatars from './models/Avatars'; import Uploads from './models/Uploads'; import UserDataFiles from './models/UserDataFiles'; +import { CachedCollection, CachedCollectionManager } from './models/CachedCollection'; export { Base, Avatars, Uploads, UserDataFiles, + CachedCollection, + CachedCollectionManager, }; diff --git a/packages/rocketchat-models/client/models/CachedCollection.js b/packages/rocketchat-models/client/models/CachedCollection.js new file mode 100644 index 000000000000..46138d4a9a55 --- /dev/null +++ b/packages/rocketchat-models/client/models/CachedCollection.js @@ -0,0 +1,399 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import { Mongo } from 'meteor/mongo'; +import { Accounts } from 'meteor/accounts-base'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import localforage from 'localforage'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import _ from 'underscore'; + +class CachedCollectionManagerClass { + constructor() { + this.items = []; + this._syncEnabled = false; + this.reconnectCb = []; + this.loginCb = []; + this.logged = false; + + const { _unstoreLoginToken } = Accounts; + Accounts._unstoreLoginToken = (...args) => { + _unstoreLoginToken.apply(Accounts, args); + this.clearAllCacheOnLogout(); + }; + + // Wait 1s to start or the code will run before the connection and + // on first connection the `reconnect` callbacks will run + Meteor.setTimeout(() => { + let connectionWasOnline = true; + Tracker.autorun(() => { + const { connected } = Meteor.connection.status(); + + if (connected === true && connectionWasOnline === false) { + for (const cb of this.reconnectCb) { + cb(); + } + } + + connectionWasOnline = connected; + }); + }, 1000); + + Tracker.autorun(() => { + if (Meteor.userId() !== null) { + if (this.logged === false) { + for (const cb of this.loginCb) { + cb(); + } + } + } + + this.logged = Meteor.userId() !== null; + }); + } + + register(cachedCollection) { + this.items.push(cachedCollection); + } + + clearAllCache() { + for (const item of this.items) { + item.clearCache(); + } + } + + clearAllCacheOnLogout() { + for (const item of this.items) { + item.clearCacheOnLogout(); + } + } + + countQueries() { + for (const item of this.items) { + item.countQueries(); + } + } + + set syncEnabled(value) { + check(value, Boolean); + this._syncEnabled = value; + } + + get syncEnabled() { + return this._syncEnabled; + } + + onReconnect(cb) { + this.reconnectCb.push(cb); + } + + onLogin(cb) { + this.loginCb.push(cb); + if (this.logged) { + cb(); + } + } +} + +export const CachedCollectionManager = new CachedCollectionManagerClass; + +const debug = false; + +const nullLog = function() {}; + +const log = function(...args) { + console.log(`CachedCollection ${ this.name } =>`, ...args); +}; + +export class CachedCollection { + constructor({ + collection, + name, + methodName, + syncMethodName, + eventName, + eventType = 'onUser', + userRelated = true, + listenChangesForLoggedUsersOnly = false, + useSync = true, + useCache = true, + version = 8, + maxCacheTime = 60 * 60 * 24 * 30, + onSyncData = (/* action, record */) => {}, + }) { + this.collection = collection || new Mongo.Collection(null); + + this.ready = new ReactiveVar(false); + this.name = name; + this.methodName = methodName || `${ name }/get`; + this.syncMethodName = syncMethodName || `${ name }/get`; + this.eventName = eventName || `${ name }-changed`; + this.eventType = eventType; + this.useSync = useSync; + this.useCache = useCache; + this.listenChangesForLoggedUsersOnly = listenChangesForLoggedUsersOnly; + this.debug = debug; + this.version = version; + this.userRelated = userRelated; + this.updatedAt = new Date(0); + this.maxCacheTime = maxCacheTime; + this.onSyncData = onSyncData; + this.log = debug ? log : nullLog; + CachedCollectionManager.register(this); + + if (userRelated === true) { + CachedCollectionManager.onLogin(() => { + this.log('Init on login'); + this.ready.set(false); + this.updatedAt = new Date(0); + this.initiated = false; + this.init(); + }); + } + + if (this.useCache === false) { + return this.clearCache(); + } + } + + countQueries() { + this.log(`${ Object.keys(this.collection._collection.queries).length } queries`); + } + + recomputeCollectionQueries() { + this.log(`recomputing ${ Object.keys(this.collection._collection.queries).length } queries`); + _.each(this.collection._collection.queries, (query) => { + this.collection._collection._recomputeResults(query); + }); + } + + getToken() { + if (this.userRelated === false) { + return undefined; + } + + return Accounts._storedLoginToken(); + } + + loadFromCache(callback = () => {}) { + if (this.useCache === false) { + return callback(false); + } + + localforage.getItem(this.name, (error, data) => { + if (data && (data.version < this.version || data.token !== this.getToken() || this.getToken() === undefined)) { + this.clearCache(); + callback(false); + return; + } + + const now = new Date(); + if (data && now - data.updatedAt >= 1000 * this.maxCacheTime) { + this.clearCache(); + callback(false); + return; + } + + if (data && data.records && data.records.length > 0) { + this.log(`${ data.records.length } records loaded from cache`); + data.records.forEach((record) => { + callbacks.run(`cachedCollection-loadFromCache-${ this.name }`, record); + record.__cache__ = true; + this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); + + if (record._updatedAt) { + const _updatedAt = new Date(record._updatedAt); + if (_updatedAt > this.updatedAt) { + this.updatedAt = _updatedAt; + } + } + }); + + callback(true); + } else { + callback(false); + } + }); + } + + loadFromServer(callback = () => {}) { + Meteor.call(this.methodName, (error, data) => { + this.log(`${ data.length } records loaded from server`); + data.forEach((record) => { + callbacks.run(`cachedCollection-loadFromServer-${ this.name }`, record, 'changed'); + this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); + + this.onSyncData('changed', record); + + if (record._updatedAt && record._updatedAt > this.updatedAt) { + this.updatedAt = record._updatedAt; + } + }); + this.recomputeCollectionQueries(); + + if (this.updatedAt < new Date) { + this.updatedAt = new Date; + } + + callback(data); + }); + } + + loadFromServerAndPopulate() { + this.loadFromServer((loadedData) => { + this.ready.set(true); + this.saveCache(loadedData); + }); + } + + sync() { + if (CachedCollectionManager.syncEnabled === false || Meteor.connection._outstandingMethodBlocks.length !== 0) { + return false; + } + + this.log(`syncing from ${ this.updatedAt }`); + + Meteor.call(this.syncMethodName, this.updatedAt, (error, data) => { + let changes = []; + + if (data.update && data.update.length > 0) { + this.log(`${ data.update.length } records updated in sync`); + changes.push(...data.update); + } + + if (data.remove && data.remove.length > 0) { + this.log(`${ data.remove.length } records removed in sync`); + changes.push(...data.remove); + } + + changes = changes.sort((a, b) => { + const valueA = a._updatedAt || a._deletedAt; + const valueB = b._updatedAt || b._deletedAt; + + if (valueA < valueB) { + return -1; + } + + if (valueA > valueB) { + return 1; + } + + return 0; + }); + + for (const record of changes) { + callbacks.run(`cachedCollection-sync-${ this.name }`, record, record._deletedAt ? 'removed' : 'changed'); + if (record._deletedAt) { + this.collection.remove({ _id: record._id }); + + this.onSyncData('removed', record); + + if (record._deletedAt && record._deletedAt > this.updatedAt) { + this.updatedAt = record._deletedAt; + } + } else { + this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); + + this.onSyncData('changed', record); + + if (record._updatedAt && record._updatedAt > this.updatedAt) { + this.updatedAt = record._updatedAt; + } + } + } + + this.saveCache(); + }); + + return true; + } + + saveCache(data) { + if (this.useCache === false) { + return; + } + + this.log('saving cache'); + if (!data) { + data = this.collection.find().fetch(); + } + + localforage.setItem(this.name, { + updatedAt: new Date, + version: this.version, + token: this.getToken(), + records: data, + }); + this.log('saving cache (done)'); + } + + clearCacheOnLogout() { + if (this.userRelated === true) { + this.clearCache(); + } + } + + clearCache() { + this.log('clearing cache'); + localforage.removeItem(this.name); + this.collection.remove({}); + } + + async setupListener(eventType, eventName) { + const { Notifications } = await import('meteor/rocketchat:notifications'); + const { RoomManager } = await import('meteor/rocketchat:ui'); + 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') { + this.collection.remove(record._id); + RoomManager.close(record.t + record.name); + } else { + this.collection.upsert({ _id: record._id }, _.omit(record, '_id')); + } + + this.saveCache(); + }); + } + + trySync() { + // Wait for an empty queue to load data again and sync + const interval = Meteor.setInterval(() => { + if (this.sync()) { + Meteor.clearInterval(interval); + } + }, 200); + } + + init() { + if (this.initiated === true) { + return; + } + + this.initiated = true; + this.loadFromCache((cacheLoaded) => { + this.ready.set(cacheLoaded); + + if (cacheLoaded === false) { + // If there is no cache load data immediately + this.loadFromServerAndPopulate(); + } else if (this.useSync === true) { + this.trySync(); + } + + if (this.useSync === true) { + CachedCollectionManager.onReconnect(() => { + this.trySync(); + }); + } + + if (this.listenChangesForLoggedUsersOnly) { + CachedCollectionManager.onLogin(() => { + this.setupListener(); + }); + } else { + this.setupListener(); + } + }); + } +} diff --git a/packages/rocketchat-models/package.js b/packages/rocketchat-models/package.js index 780626a8c855..b116291c4751 100755 --- a/packages/rocketchat-models/package.js +++ b/packages/rocketchat-models/package.js @@ -10,6 +10,7 @@ Package.onUse(function(api) { 'ecmascript', 'rocketchat:settings', 'rocketchat:utils', + 'rocketchat:callbacks', 'konecty:multiple-instances-status', ]); api.mainModule('client/index.js', 'client'); From a05c6f5822a103a0a34c44544acf5581ad92bbc7 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 2 Jan 2019 22:07:04 -0200 Subject: [PATCH 16/59] Move ui models/collections from ui to models --- .../rocketchat-lib/client/models/Messages.js | 4 ++ .../rocketchat-lib/client/models/Roles.js | 4 ++ .../rocketchat-lib/client/models/Rooms.js | 4 ++ .../client/models/Subscriptions.js | 4 ++ .../rocketchat-lib/client/models/Users.js | 4 ++ packages/rocketchat-lib/package.js | 5 +++ packages/rocketchat-models/client/index.js | 39 +++++++++++++++++++ .../client/models/CachedChannelList.js | 3 ++ .../client/models/CachedChatRoom.js | 3 ++ .../client/models/CachedChatSubscription.js | 3 ++ .../client/models/CachedUserList.js | 3 ++ .../client/models/ChatMessage.js | 3 ++ .../client/models/ChatPermissions.js | 8 ++++ .../client/models/ChatRoom.js | 3 ++ .../client/models/ChatSubscription.js | 3 ++ .../client}/models/Roles.js | 12 +++--- .../client/models/RoomRoles.js | 3 ++ .../client}/models/Subscriptions.js | 12 +++--- .../client/models/UserAndRoom.js | 3 ++ .../client/models/UserRoles.js | 3 ++ .../client}/models/Users.js | 9 ++--- .../rocketchat-ui/client/lib/collections.js | 39 +++++++++++-------- packages/rocketchat-ui/package.js | 1 + 23 files changed, 142 insertions(+), 33 deletions(-) create mode 100644 packages/rocketchat-lib/client/models/Messages.js create mode 100644 packages/rocketchat-lib/client/models/Roles.js create mode 100644 packages/rocketchat-lib/client/models/Rooms.js create mode 100644 packages/rocketchat-lib/client/models/Subscriptions.js create mode 100644 packages/rocketchat-lib/client/models/Users.js create mode 100644 packages/rocketchat-models/client/models/CachedChannelList.js create mode 100644 packages/rocketchat-models/client/models/CachedChatRoom.js create mode 100644 packages/rocketchat-models/client/models/CachedChatSubscription.js create mode 100644 packages/rocketchat-models/client/models/CachedUserList.js create mode 100644 packages/rocketchat-models/client/models/ChatMessage.js create mode 100644 packages/rocketchat-models/client/models/ChatPermissions.js create mode 100644 packages/rocketchat-models/client/models/ChatRoom.js create mode 100644 packages/rocketchat-models/client/models/ChatSubscription.js rename packages/{rocketchat-authorization/client/lib => rocketchat-models/client}/models/Roles.js (68%) create mode 100644 packages/rocketchat-models/client/models/RoomRoles.js rename packages/{rocketchat-authorization/client/lib => rocketchat-models/client}/models/Subscriptions.js (71%) create mode 100644 packages/rocketchat-models/client/models/UserAndRoom.js create mode 100644 packages/rocketchat-models/client/models/UserRoles.js rename packages/{rocketchat-authorization/client/lib => rocketchat-models/client}/models/Users.js (68%) diff --git a/packages/rocketchat-lib/client/models/Messages.js b/packages/rocketchat-lib/client/models/Messages.js new file mode 100644 index 000000000000..e93cab1ba1ab --- /dev/null +++ b/packages/rocketchat-lib/client/models/Messages.js @@ -0,0 +1,4 @@ +import { Messages } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +RocketChat.models.Messages = _.extend({}, RocketChat.models.Messages, Messages); diff --git a/packages/rocketchat-lib/client/models/Roles.js b/packages/rocketchat-lib/client/models/Roles.js new file mode 100644 index 000000000000..ab7f37e17afb --- /dev/null +++ b/packages/rocketchat-lib/client/models/Roles.js @@ -0,0 +1,4 @@ +import { Roles } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +RocketChat.models.Roles = _.extend({}, RocketChat.models.Roles, Roles); diff --git a/packages/rocketchat-lib/client/models/Rooms.js b/packages/rocketchat-lib/client/models/Rooms.js new file mode 100644 index 000000000000..c69ecb50558f --- /dev/null +++ b/packages/rocketchat-lib/client/models/Rooms.js @@ -0,0 +1,4 @@ +import { Rooms } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +RocketChat.models.Rooms = _.extend({}, RocketChat.models.Rooms, Rooms); diff --git a/packages/rocketchat-lib/client/models/Subscriptions.js b/packages/rocketchat-lib/client/models/Subscriptions.js new file mode 100644 index 000000000000..fc84237a8773 --- /dev/null +++ b/packages/rocketchat-lib/client/models/Subscriptions.js @@ -0,0 +1,4 @@ +import { Subscriptions } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +RocketChat.models.Subscriptions = _.extend({}, RocketChat.models.Subscriptions, Subscriptions); diff --git a/packages/rocketchat-lib/client/models/Users.js b/packages/rocketchat-lib/client/models/Users.js new file mode 100644 index 000000000000..498a132919d3 --- /dev/null +++ b/packages/rocketchat-lib/client/models/Users.js @@ -0,0 +1,4 @@ +import { Users } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +RocketChat.models.Users = _.extend({}, RocketChat.models.Users, Users); diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 3711153a6d6c..609513c9d161 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -254,6 +254,11 @@ Package.onUse(function(api) { api.addFiles('client/models/_Base.js', 'client'); api.addFiles('client/models/Avatars.js', 'client'); api.addFiles('client/models/Uploads.js', 'client'); + api.addFiles('client/models/Messages.js', 'client'); + api.addFiles('client/models/Roles.js', 'client'); + api.addFiles('client/models/Rooms.js', 'client'); + api.addFiles('client/models/Subscriptions.js', 'client'); + api.addFiles('client/models/Users.js', 'client'); // CLIENT VIEWS api.addFiles('client/views/customFieldsForm.html', 'client'); diff --git a/packages/rocketchat-models/client/index.js b/packages/rocketchat-models/client/index.js index 95e099e64e76..8dc4e8f481b7 100644 --- a/packages/rocketchat-models/client/index.js +++ b/packages/rocketchat-models/client/index.js @@ -1,8 +1,30 @@ +import { Meteor } from 'meteor/meteor'; import { Base } from './models/_Base'; import Avatars from './models/Avatars'; import Uploads from './models/Uploads'; import UserDataFiles from './models/UserDataFiles'; import { CachedCollection, CachedCollectionManager } from './models/CachedCollection'; +import { Roles } from './models/Roles'; +import { Subscriptions as subscriptions } from './models/Subscriptions'; +import { Users as users } from './models/Users'; +import { CachedChannelList } from './models/CachedChannelList'; +import { CachedChatRoom } from './models/CachedChatRoom'; +import { CachedChatSubscription } from './models/CachedChatSubscription'; +import { CachedUserList } from './models/CachedUserList'; +import { ChatRoom } from './models/ChatRoom'; +import { ChatSubscription } from './models/ChatSubscription'; +import { ChatMessage } from './models/ChatMessage'; +import { RoomRoles } from './models/RoomRoles'; +import { UserAndRoom } from './models/UserAndRoom'; +import { UserRoles } from './models/UserRoles'; +import { AuthzCachedCollection, ChatPermissions } from './models/ChatPermissions'; +import _ from 'underscore'; + +const Users = _.extend({}, users, Meteor.users); +const Subscriptions = _.extend({}, subscriptions, ChatSubscription); +const Messages = _.extend({}, ChatMessage); +const Rooms = _.extend({}, ChatRoom); + export { Base, @@ -11,4 +33,21 @@ export { UserDataFiles, CachedCollection, CachedCollectionManager, + Roles, + Subscriptions, + Users, + Messages, + CachedChannelList, + CachedChatRoom, + CachedChatSubscription, + CachedUserList, + ChatRoom, + RoomRoles, + UserAndRoom, + UserRoles, + AuthzCachedCollection, + ChatPermissions, + ChatMessage, + ChatSubscription, + Rooms, }; diff --git a/packages/rocketchat-models/client/models/CachedChannelList.js b/packages/rocketchat-models/client/models/CachedChannelList.js new file mode 100644 index 000000000000..1a34d88e6aeb --- /dev/null +++ b/packages/rocketchat-models/client/models/CachedChannelList.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const CachedChannelList = new Mongo.Collection(null); diff --git a/packages/rocketchat-models/client/models/CachedChatRoom.js b/packages/rocketchat-models/client/models/CachedChatRoom.js new file mode 100644 index 000000000000..9ba03b2e373b --- /dev/null +++ b/packages/rocketchat-models/client/models/CachedChatRoom.js @@ -0,0 +1,3 @@ +import { CachedCollection } from './CachedCollection'; + +export const CachedChatRoom = new CachedCollection({ name: 'rooms' }); diff --git a/packages/rocketchat-models/client/models/CachedChatSubscription.js b/packages/rocketchat-models/client/models/CachedChatSubscription.js new file mode 100644 index 000000000000..8d49f28397c9 --- /dev/null +++ b/packages/rocketchat-models/client/models/CachedChatSubscription.js @@ -0,0 +1,3 @@ +import { CachedCollection } from './CachedCollection'; + +export const CachedChatSubscription = new CachedCollection({ name: 'subscriptions' }); diff --git a/packages/rocketchat-models/client/models/CachedUserList.js b/packages/rocketchat-models/client/models/CachedUserList.js new file mode 100644 index 000000000000..0233659b47d5 --- /dev/null +++ b/packages/rocketchat-models/client/models/CachedUserList.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const CachedUserList = new Mongo.Collection(null); diff --git a/packages/rocketchat-models/client/models/ChatMessage.js b/packages/rocketchat-models/client/models/ChatMessage.js new file mode 100644 index 000000000000..7a7947b1a566 --- /dev/null +++ b/packages/rocketchat-models/client/models/ChatMessage.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const ChatMessage = new Mongo.Collection(null); diff --git a/packages/rocketchat-models/client/models/ChatPermissions.js b/packages/rocketchat-models/client/models/ChatPermissions.js new file mode 100644 index 000000000000..527996dd9d22 --- /dev/null +++ b/packages/rocketchat-models/client/models/ChatPermissions.js @@ -0,0 +1,8 @@ +import { CachedCollection } from './CachedCollection'; + +export const AuthzCachedCollection = new CachedCollection({ + name: 'permissions', + eventType: 'onLogged', +}); + +export const ChatPermissions = AuthzCachedCollection.collection; diff --git a/packages/rocketchat-models/client/models/ChatRoom.js b/packages/rocketchat-models/client/models/ChatRoom.js new file mode 100644 index 000000000000..c7867ba834a2 --- /dev/null +++ b/packages/rocketchat-models/client/models/ChatRoom.js @@ -0,0 +1,3 @@ +import { CachedChatRoom } from './CachedChatRoom'; + +export const ChatRoom = CachedChatRoom.collection; diff --git a/packages/rocketchat-models/client/models/ChatSubscription.js b/packages/rocketchat-models/client/models/ChatSubscription.js new file mode 100644 index 000000000000..964f622db495 --- /dev/null +++ b/packages/rocketchat-models/client/models/ChatSubscription.js @@ -0,0 +1,3 @@ +import { CachedChatSubscription } from './CachedChatSubscription'; + +export const ChatSubscription = CachedChatSubscription.collection; diff --git a/packages/rocketchat-authorization/client/lib/models/Roles.js b/packages/rocketchat-models/client/models/Roles.js similarity index 68% rename from packages/rocketchat-authorization/client/lib/models/Roles.js rename to packages/rocketchat-models/client/models/Roles.js index e98dd63d5aeb..7b52f72f5f38 100644 --- a/packages/rocketchat-authorization/client/lib/models/Roles.js +++ b/packages/rocketchat-models/client/models/Roles.js @@ -1,13 +1,13 @@ import { Mongo } from 'meteor/mongo'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import * as Models from '..'; -RocketChat.models.Roles = new Mongo.Collection('rocketchat_roles'); +const Roles = new Mongo.Collection('rocketchat_roles'); -Object.assign(RocketChat.models.Roles, { +Object.assign(Roles, { findUsersInRole(name, scope, options) { const role = this.findOne(name); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.findUsersInRoles && model.findUsersInRoles(name, scope, options); }, @@ -16,8 +16,10 @@ Object.assign(RocketChat.models.Roles, { return roles.some((roleName) => { const role = this.findOne(roleName); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.isUserInRole && model.isUserInRole(userId, roleName, scope); }); }, }); + +export { Roles }; diff --git a/packages/rocketchat-models/client/models/RoomRoles.js b/packages/rocketchat-models/client/models/RoomRoles.js new file mode 100644 index 000000000000..4085638913bd --- /dev/null +++ b/packages/rocketchat-models/client/models/RoomRoles.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const RoomRoles = new Mongo.Collection(null); diff --git a/packages/rocketchat-authorization/client/lib/models/Subscriptions.js b/packages/rocketchat-models/client/models/Subscriptions.js similarity index 71% rename from packages/rocketchat-authorization/client/lib/models/Subscriptions.js rename to packages/rocketchat-models/client/models/Subscriptions.js index 341bf0a33ac7..38ad38eb4794 100644 --- a/packages/rocketchat-authorization/client/lib/models/Subscriptions.js +++ b/packages/rocketchat-models/client/models/Subscriptions.js @@ -1,11 +1,9 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from '..'; import _ from 'underscore'; -if (_.isUndefined(RocketChat.models.Subscriptions)) { - RocketChat.models.Subscriptions = {}; -} +const Subscriptions = {}; -Object.assign(RocketChat.models.Subscriptions, { +Object.assign(Subscriptions, { isUserInRole(userId, roleName, roomId) { if (roomId == null) { return false; @@ -38,6 +36,8 @@ Object.assign(RocketChat.models.Subscriptions, { } })); - return RocketChat.models.Users.find({ _id: { $in: users } }, options); + return Users.find({ _id: { $in: users } }, options); }, }); + +export { Subscriptions }; diff --git a/packages/rocketchat-models/client/models/UserAndRoom.js b/packages/rocketchat-models/client/models/UserAndRoom.js new file mode 100644 index 000000000000..f43b2c0fdbe0 --- /dev/null +++ b/packages/rocketchat-models/client/models/UserAndRoom.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const UserAndRoom = new Mongo.Collection(null); diff --git a/packages/rocketchat-models/client/models/UserRoles.js b/packages/rocketchat-models/client/models/UserRoles.js new file mode 100644 index 000000000000..de71e371567b --- /dev/null +++ b/packages/rocketchat-models/client/models/UserRoles.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const UserRoles = new Mongo.Collection(null); diff --git a/packages/rocketchat-authorization/client/lib/models/Users.js b/packages/rocketchat-models/client/models/Users.js similarity index 68% rename from packages/rocketchat-authorization/client/lib/models/Users.js rename to packages/rocketchat-models/client/models/Users.js index 632ec2e93e71..fd4bbfbacd9d 100644 --- a/packages/rocketchat-authorization/client/lib/models/Users.js +++ b/packages/rocketchat-models/client/models/Users.js @@ -1,11 +1,8 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; import _ from 'underscore'; -if (_.isUndefined(RocketChat.models.Users)) { - RocketChat.models.Users = {}; -} +const Users = {}; -Object.assign(RocketChat.models.Users, { +Object.assign(Users, { isUserInRole(userId, roleName) { const query = { _id: userId, @@ -25,3 +22,5 @@ Object.assign(RocketChat.models.Users, { return this.find(query, options); }, }); + +export { Users }; diff --git a/packages/rocketchat-ui/client/lib/collections.js b/packages/rocketchat-ui/client/lib/collections.js index 0b54d4fd1a3c..4db4b857d13f 100644 --- a/packages/rocketchat-ui/client/lib/collections.js +++ b/packages/rocketchat-ui/client/lib/collections.js @@ -1,24 +1,29 @@ import { Meteor } from 'meteor/meteor'; -import { Mongo } from 'meteor/mongo'; import { Tracker } from 'meteor/tracker'; -import _ from 'underscore'; +import { + ChatMessage as chatMessage, + CachedChatRoom as cachedChatRoom, + ChatRoom as chatRoom, + CachedChatSubscription as cachedChatSubscription, + ChatSubscription as chatSubscription, + UserRoles as userRoles, + RoomRoles as roomRoles, + UserAndRoom as userAndRoom, + CachedChannelList as cachedChannelList, + CachedUserList as cachedUserList, +} from 'meteor/rocketchat:models'; -ChatMessage = new Mongo.Collection(null); -export const CachedChatRoom = new RocketChat.CachedCollection({ name: 'rooms' }); -ChatRoom = CachedChatRoom.collection; +ChatMessage = chatMessage; +export const CachedChatRoom = cachedChatRoom; +ChatRoom = chatRoom; -CachedChatSubscription = new RocketChat.CachedCollection({ name: 'subscriptions' }); -ChatSubscription = CachedChatSubscription.collection; -UserRoles = new Mongo.Collection(null); -RoomRoles = new Mongo.Collection(null); -this.UserAndRoom = new Mongo.Collection(null); -this.CachedChannelList = new Mongo.Collection(null); -this.CachedUserList = new Mongo.Collection(null); - -RocketChat.models.Users = _.extend({}, RocketChat.models.Users, Meteor.users); -RocketChat.models.Subscriptions = _.extend({}, RocketChat.models.Subscriptions, ChatSubscription); -RocketChat.models.Rooms = _.extend({}, RocketChat.models.Rooms, ChatRoom); -RocketChat.models.Messages = _.extend({}, RocketChat.models.Messages, ChatMessage); +CachedChatSubscription = cachedChatSubscription; +ChatSubscription = chatSubscription; +UserRoles = userRoles; +RoomRoles = roomRoles; +this.UserAndRoom = userAndRoom; +this.CachedChannelList = cachedChannelList; +this.CachedUserList = cachedUserList; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index c6ace6c12b65..372fdcaf32b3 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -24,6 +24,7 @@ Package.onUse(function(api) { 'rocketchat:ui-master', 'rocketchat:push', 'rocketchat:utils', + 'rocketchat:models', 'raix:ui-dropped-event', 'rocketchat:lazy-load', 'rocketchat:e2e', From 36ff49eed842cfeef2e66a7709edc9bea555b1fa Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 2 Jan 2019 22:08:29 -0200 Subject: [PATCH 17/59] Move authorization client/ui models to rocketchat:models to be able to remove lib dependency --- packages/rocketchat-authorization/client/index.js | 4 ---- .../client/lib/ChatPermissions.js | 9 ++------- packages/rocketchat-authorization/lib/rocketchat.js | 3 --- packages/rocketchat-authorization/package.js | 1 + packages/rocketchat-authorization/server/index.js | 1 - packages/rocketchat-lib/client/models/ChatPermissions.js | 3 +++ packages/rocketchat-lib/lib/authorization.js | 1 + packages/rocketchat-lib/package.js | 2 ++ 8 files changed, 9 insertions(+), 15 deletions(-) delete mode 100644 packages/rocketchat-authorization/lib/rocketchat.js create mode 100644 packages/rocketchat-lib/client/models/ChatPermissions.js create mode 100644 packages/rocketchat-lib/lib/authorization.js diff --git a/packages/rocketchat-authorization/client/index.js b/packages/rocketchat-authorization/client/index.js index c48b4ca7d074..7e851af52944 100644 --- a/packages/rocketchat-authorization/client/index.js +++ b/packages/rocketchat-authorization/client/index.js @@ -1,7 +1,3 @@ -import '../lib/rocketchat'; -import './lib/models/Roles'; -import './lib/models/Users'; -import './lib/models/Subscriptions'; import './hasPermission'; import './hasRole'; import './usersNameChanged'; diff --git a/packages/rocketchat-authorization/client/lib/ChatPermissions.js b/packages/rocketchat-authorization/client/lib/ChatPermissions.js index 7c6c0dbd41e2..7fbd3b4da97e 100644 --- a/packages/rocketchat-authorization/client/lib/ChatPermissions.js +++ b/packages/rocketchat-authorization/client/lib/ChatPermissions.js @@ -1,8 +1,3 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { AuthzCachedCollection } from 'meteor/rocketchat:models'; -RocketChat.authz.cachedCollection = new RocketChat.CachedCollection({ - name: 'permissions', - eventType: 'onLogged', -}); - -export const ChatPermissions = RocketChat.authz.cachedCollection.collection; +export const ChatPermissions = AuthzCachedCollection.collection; diff --git a/packages/rocketchat-authorization/lib/rocketchat.js b/packages/rocketchat-authorization/lib/rocketchat.js deleted file mode 100644 index 29a3b64f0063..000000000000 --- a/packages/rocketchat-authorization/lib/rocketchat.js +++ /dev/null @@ -1,3 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.authz = {}; diff --git a/packages/rocketchat-authorization/package.js b/packages/rocketchat-authorization/package.js index 2aab4bc7e2e0..5065377535aa 100644 --- a/packages/rocketchat-authorization/package.js +++ b/packages/rocketchat-authorization/package.js @@ -12,6 +12,7 @@ Package.onUse(function(api) { 'rocketchat:lib', 'mongo', 'rocketchat:utils', + 'rocketchat:models', ]); api.use([ 'templating', diff --git a/packages/rocketchat-authorization/server/index.js b/packages/rocketchat-authorization/server/index.js index e44ac205433b..84f7f5739e3a 100644 --- a/packages/rocketchat-authorization/server/index.js +++ b/packages/rocketchat-authorization/server/index.js @@ -1,4 +1,3 @@ -import '../lib/rocketchat'; import './models/Base'; import './models/Permissions'; import './models/Roles'; diff --git a/packages/rocketchat-lib/client/models/ChatPermissions.js b/packages/rocketchat-lib/client/models/ChatPermissions.js new file mode 100644 index 000000000000..cf1407bf678c --- /dev/null +++ b/packages/rocketchat-lib/client/models/ChatPermissions.js @@ -0,0 +1,3 @@ +import { AuthzCachedCollection } from 'meteor/rocketchat:models'; + +RocketChat.authz.cachedCollection = AuthzCachedCollection; diff --git a/packages/rocketchat-lib/lib/authorization.js b/packages/rocketchat-lib/lib/authorization.js new file mode 100644 index 000000000000..6445a1b8b5f0 --- /dev/null +++ b/packages/rocketchat-lib/lib/authorization.js @@ -0,0 +1 @@ +RocketChat.authz = {}; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 609513c9d161..eaa43b134eb7 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -87,6 +87,7 @@ Package.onUse(function(api) { api.addFiles('lib/MessageTypes.js'); api.addFiles('lib/templateVarHandler.js'); api.addFiles('lib/info.js'); + api.addFiles('lib/authorization.js'); api.addFiles('lib/getUserNotificationPreference.js'); api.addFiles('lib/getUserPreference.js'); @@ -254,6 +255,7 @@ Package.onUse(function(api) { api.addFiles('client/models/_Base.js', 'client'); api.addFiles('client/models/Avatars.js', 'client'); api.addFiles('client/models/Uploads.js', 'client'); + api.addFiles('client/models/ChatPermissions.js', 'client'); api.addFiles('client/models/Messages.js', 'client'); api.addFiles('client/models/Roles.js', 'client'); api.addFiles('client/models/Rooms.js', 'client'); From 15b52fa133fa46bd710ed292baf61745b51fd090 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 3 Jan 2019 14:21:53 -0200 Subject: [PATCH 18/59] Creation of rocketchat:ui-utils to help decouple rocketchat:lib and rocketchat:authz --- .meteor/packages | 3 +- .meteor/versions | 1 + packages/rocketchat-lib/client/AdminBox.js | 24 +-- packages/rocketchat-lib/package.js | 1 + packages/rocketchat-ui-utils/client/index.js | 7 + .../client/lib/AdminBox.js | 23 +++ .../client/lib}/modal.html | 0 .../rocketchat-ui-utils/client/lib/modal.js | 170 ++++++++++++++++++ packages/rocketchat-ui-utils/package.js | 16 ++ .../rocketchat-ui/client/views/app/modal.js | 170 +----------------- packages/rocketchat-ui/package.js | 2 +- 11 files changed, 225 insertions(+), 192 deletions(-) create mode 100644 packages/rocketchat-ui-utils/client/index.js create mode 100644 packages/rocketchat-ui-utils/client/lib/AdminBox.js rename packages/{rocketchat-ui/client/views/app => rocketchat-ui-utils/client/lib}/modal.html (100%) create mode 100644 packages/rocketchat-ui-utils/client/lib/modal.js create mode 100644 packages/rocketchat-ui-utils/package.js diff --git a/.meteor/packages b/.meteor/packages index 4973ecae2af6..3e0d5e62b45d 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -206,4 +206,5 @@ rocketchat:models rocketchat:metrics rocketchat:callbacks rocketchat:notifications -rocketchat:promises \ No newline at end of file +rocketchat:promises +rocketchat:ui-utils \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index d4523b1fd35b..6cae6fc2e85b 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -254,6 +254,7 @@ rocketchat:ui-login@0.1.0 rocketchat:ui-master@0.1.0 rocketchat:ui-message@0.1.0 rocketchat:ui-sidenav@0.1.0 +rocketchat:ui-utils@0.0.1 rocketchat:ui-vrecord@0.0.1 rocketchat:user-data-download@1.0.0 rocketchat:utils@0.0.1 diff --git a/packages/rocketchat-lib/client/AdminBox.js b/packages/rocketchat-lib/client/AdminBox.js index 5cb3438bb26c..1c6be9b9a9cf 100644 --- a/packages/rocketchat-lib/client/AdminBox.js +++ b/packages/rocketchat-lib/client/AdminBox.js @@ -1,23 +1,3 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import _ from 'underscore'; +import { AdminBox } from 'meteor/rocketchat:ui-utils'; -RocketChat.AdminBox = new class { - constructor() { - this.options = new ReactiveVar([]); - } - addOption(option) { - return Tracker.nonreactive(() => { - const actual = this.options.get(); - actual.push(option); - return this.options.set(actual); - }); - } - getOptions() { - return _.filter(this.options.get(), function(option) { - if ((option.permissionGranted == null) || option.permissionGranted()) { - return true; - } - }); - } -}; +RocketChat.AdminBox = AdminBox; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index eaa43b134eb7..8dc05fd7b49c 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -31,6 +31,7 @@ Package.onUse(function(api) { api.use('rocketchat:callbacks'); api.use('rocketchat:notifications'); api.use('rocketchat:promises'); + api.use('rocketchat:ui-utils'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); diff --git a/packages/rocketchat-ui-utils/client/index.js b/packages/rocketchat-ui-utils/client/index.js new file mode 100644 index 000000000000..d015902dc125 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/index.js @@ -0,0 +1,7 @@ +import { AdminBox } from './lib/AdminBox'; +import { modal } from './lib/modal'; + +export { + AdminBox, + modal, +}; diff --git a/packages/rocketchat-ui-utils/client/lib/AdminBox.js b/packages/rocketchat-ui-utils/client/lib/AdminBox.js new file mode 100644 index 000000000000..d4978f9b7d1a --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/AdminBox.js @@ -0,0 +1,23 @@ +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import _ from 'underscore'; + +export const AdminBox = new class { + constructor() { + this.options = new ReactiveVar([]); + } + addOption(option) { + return Tracker.nonreactive(() => { + const actual = this.options.get(); + actual.push(option); + return this.options.set(actual); + }); + } + getOptions() { + return _.filter(this.options.get(), function(option) { + if ((option.permissionGranted == null) || option.permissionGranted()) { + return true; + } + }); + } +}; diff --git a/packages/rocketchat-ui/client/views/app/modal.html b/packages/rocketchat-ui-utils/client/lib/modal.html similarity index 100% rename from packages/rocketchat-ui/client/views/app/modal.html rename to packages/rocketchat-ui-utils/client/lib/modal.html diff --git a/packages/rocketchat-ui-utils/client/lib/modal.js b/packages/rocketchat-ui-utils/client/lib/modal.js new file mode 100644 index 000000000000..9b8ea32cf070 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/modal.js @@ -0,0 +1,170 @@ +import './modal.html'; +import { Meteor } from 'meteor/meteor'; +import { Blaze } from 'meteor/blaze'; +import { Template } from 'meteor/templating'; +import { t, getUserPreference } from 'meteor/rocketchat:utils'; + +export const modal = { + renderedModal: null, + open(config = {}, fn, onCancel) { + config.confirmButtonText = config.confirmButtonText || (config.type === 'error' ? t('Ok') : t('Send')); + config.cancelButtonText = config.cancelButtonText || t('Cancel'); + config.closeOnConfirm = config.closeOnConfirm == null ? true : config.closeOnConfirm; + config.showConfirmButton = config.showConfirmButton == null ? true : config.showConfirmButton; + config.showFooter = config.showConfirmButton === true || config.showCancelButton === true; + config.confirmOnEnter = config.confirmOnEnter == null ? true : config.confirmOnEnter; + + if (config.type === 'input') { + config.input = true; + config.type = false; + + if (!config.inputType) { + config.inputType = 'text'; + } + } + + this.close(); + this.fn = fn; + this.onCancel = onCancel; + this.config = config; + + if (config.dontAskAgain) { + const dontAskAgainList = getUserPreference(Meteor.userId(), 'dontAskAgainList'); + + if (dontAskAgainList && dontAskAgainList.some((dontAsk) => dontAsk.action === config.dontAskAgain.action)) { + this.confirm(true); + return; + } + } + + this.renderedModal = Blaze.renderWithData(Template.rc_modal, config, document.body); + this.timer = null; + if (config.timer) { + this.timer = setTimeout(() => this.close(), config.timer); + } + }, + cancel() { + if (this.onCancel) { + this.onCancel(); + } + this.close(); + }, + close() { + if (this.renderedModal) { + Blaze.remove(this.renderedModal); + } + this.fn = null; + this.onCancel = null; + if (this.timer) { + clearTimeout(this.timer); + } + }, + confirm(value) { + if (this.fn) { + this.fn(value); + } else { + this.close(); + } + + this.config.closeOnConfirm && this.close(); + }, + showInputError(text) { + const errorEl = document.querySelector('.rc-modal__content-error'); + errorEl.innerHTML = text; + errorEl.style.display = 'block'; + }, + onKeydown(e) { + if (modal.config.confirmOnEnter && e.key === 'Enter') { + e.preventDefault(); + e.stopPropagation(); + + if (modal.config.input) { + return modal.confirm($('.js-modal-input').val()); + } + + modal.confirm(true); + } else if (e.key === 'Escape') { + e.preventDefault(); + e.stopPropagation(); + + modal.close(); + } + }, +}; + +Template.rc_modal.helpers({ + hasAction() { + return !!this.action; + }, + modalIcon() { + return `modal-${ this.type }`; + }, +}); + +Template.rc_modal.onRendered(function() { + if (this.data.onRendered) { + this.data.onRendered(); + } + + if (this.data.input) { + $('.js-modal-input').focus(); + } + + document.addEventListener('keydown', modal.onKeydown); +}); + +Template.rc_modal.onDestroyed(function() { + document.removeEventListener('keydown', modal.onKeydown); +}); + +Template.rc_modal.events({ + 'click .js-action'(e, instance) { + !this.action || this.action.call(instance.data.data, e, instance); + e.stopPropagation(); + modal.close(); + }, + 'click .js-close'(e) { + e.stopPropagation(); + modal.cancel(); + }, + 'click .js-confirm'(e, instance) { + e.stopPropagation(); + const { dontAskAgain } = instance.data; + if (dontAskAgain && document.getElementById('dont-ask-me-again').checked) { + const dontAskAgainObject = { + action: dontAskAgain.action, + label: dontAskAgain.label, + }; + + let dontAskAgainList = getUserPreference(Meteor.userId(), 'dontAskAgainList'); + if (dontAskAgainList) { + dontAskAgainList.push(dontAskAgainObject); + } else { + dontAskAgainList = [dontAskAgainObject]; + } + + Meteor.call('saveUserPreferences', { dontAskAgainList }, function(error) { + if (error) { + return handleError(error); + } + }); + } + + if (instance.data.input) { + modal.confirm(document.getElementsByClassName('js-modal-input')[0].value); + return; + } + + modal.confirm(true); + }, + 'click .rc-modal-wrapper'(e, instance) { + if (instance.data.allowOutsideClick === false) { + return false; + } + + if (e.currentTarget === e.target) { + e.stopPropagation(); + modal.close(); + } + }, +}); diff --git a/packages/rocketchat-ui-utils/package.js b/packages/rocketchat-ui-utils/package.js new file mode 100644 index 000000000000..011cfebed1da --- /dev/null +++ b/packages/rocketchat-ui-utils/package.js @@ -0,0 +1,16 @@ +Package.describe({ + name: 'rocketchat:ui-utils', + version: '0.0.1', + summary: 'Rocketchat Ui Utils', + git: '', + documentation: 'README.md', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'templating', + 'rocketchat:utils', + ]); + api.mainModule('client/index.js', 'client'); +}); diff --git a/packages/rocketchat-ui/client/views/app/modal.js b/packages/rocketchat-ui/client/views/app/modal.js index 5bfb5d99d28b..4a055bda4731 100644 --- a/packages/rocketchat-ui/client/views/app/modal.js +++ b/packages/rocketchat-ui/client/views/app/modal.js @@ -1,169 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Blaze } from 'meteor/blaze'; -import { Template } from 'meteor/templating'; -import { t } from 'meteor/rocketchat:utils'; +import { modal as _modal } from 'meteor/rocketchat:ui-utils'; -modal = { - renderedModal: null, - open(config = {}, fn, onCancel) { - config.confirmButtonText = config.confirmButtonText || (config.type === 'error' ? t('Ok') : t('Send')); - config.cancelButtonText = config.cancelButtonText || t('Cancel'); - config.closeOnConfirm = config.closeOnConfirm == null ? true : config.closeOnConfirm; - config.showConfirmButton = config.showConfirmButton == null ? true : config.showConfirmButton; - config.showFooter = config.showConfirmButton === true || config.showCancelButton === true; - config.confirmOnEnter = config.confirmOnEnter == null ? true : config.confirmOnEnter; - - if (config.type === 'input') { - config.input = true; - config.type = false; - - if (!config.inputType) { - config.inputType = 'text'; - } - } - - this.close(); - this.fn = fn; - this.onCancel = onCancel; - this.config = config; - - if (config.dontAskAgain) { - const dontAskAgainList = RocketChat.getUserPreference(Meteor.userId(), 'dontAskAgainList'); - - if (dontAskAgainList && dontAskAgainList.some((dontAsk) => dontAsk.action === config.dontAskAgain.action)) { - this.confirm(true); - return; - } - } - - this.renderedModal = Blaze.renderWithData(Template.rc_modal, config, document.body); - this.timer = null; - if (config.timer) { - this.timer = setTimeout(() => this.close(), config.timer); - } - }, - cancel() { - if (this.onCancel) { - this.onCancel(); - } - this.close(); - }, - close() { - if (this.renderedModal) { - Blaze.remove(this.renderedModal); - } - this.fn = null; - this.onCancel = null; - if (this.timer) { - clearTimeout(this.timer); - } - }, - confirm(value) { - if (this.fn) { - this.fn(value); - } else { - this.close(); - } - - this.config.closeOnConfirm && this.close(); - }, - showInputError(text) { - const errorEl = document.querySelector('.rc-modal__content-error'); - errorEl.innerHTML = text; - errorEl.style.display = 'block'; - }, - onKeydown(e) { - if (modal.config.confirmOnEnter && e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - - if (modal.config.input) { - return modal.confirm($('.js-modal-input').val()); - } - - modal.confirm(true); - } else if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - - modal.close(); - } - }, -}; - -Template.rc_modal.helpers({ - hasAction() { - return !!this.action; - }, - modalIcon() { - return `modal-${ this.type }`; - }, -}); - -Template.rc_modal.onRendered(function() { - if (this.data.onRendered) { - this.data.onRendered(); - } - - if (this.data.input) { - $('.js-modal-input').focus(); - } - - document.addEventListener('keydown', modal.onKeydown); -}); - -Template.rc_modal.onDestroyed(function() { - document.removeEventListener('keydown', modal.onKeydown); -}); - -Template.rc_modal.events({ - 'click .js-action'(e, instance) { - !this.action || this.action.call(instance.data.data, e, instance); - e.stopPropagation(); - modal.close(); - }, - 'click .js-close'(e) { - e.stopPropagation(); - modal.cancel(); - }, - 'click .js-confirm'(e, instance) { - e.stopPropagation(); - const { dontAskAgain } = instance.data; - if (dontAskAgain && document.getElementById('dont-ask-me-again').checked) { - const dontAskAgainObject = { - action: dontAskAgain.action, - label: dontAskAgain.label, - }; - - let dontAskAgainList = RocketChat.getUserPreference(Meteor.userId(), 'dontAskAgainList'); - if (dontAskAgainList) { - dontAskAgainList.push(dontAskAgainObject); - } else { - dontAskAgainList = [dontAskAgainObject]; - } - - Meteor.call('saveUserPreferences', { dontAskAgainList }, function(error) { - if (error) { - return handleError(error); - } - }); - } - - if (instance.data.input) { - modal.confirm(document.getElementsByClassName('js-modal-input')[0].value); - return; - } - - modal.confirm(true); - }, - 'click .rc-modal-wrapper'(e, instance) { - if (instance.data.allowOutsideClick === false) { - return false; - } - - if (e.currentTarget === e.target) { - e.stopPropagation(); - modal.close(); - } - }, -}); +modal = _modal; diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index 372fdcaf32b3..f54f746e2bb7 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -24,6 +24,7 @@ Package.onUse(function(api) { 'rocketchat:ui-master', 'rocketchat:push', 'rocketchat:utils', + 'rocketchat:ui-utils', 'rocketchat:models', 'raix:ui-dropped-event', 'rocketchat:lazy-load', @@ -105,7 +106,6 @@ Package.onUse(function(api) { api.addFiles('client/views/app/videoCall/videoButtons.html', 'client'); api.addFiles('client/views/app/videoCall/videoCall.html', 'client'); api.addFiles('client/views/app/popover.html', 'client'); - api.addFiles('client/views/app/modal.html', 'client'); api.addFiles('client/views/app/photoswipe.html', 'client'); api.addFiles('client/views/cmsPage.js', 'client'); From 6383ad5171d9f3d2f0bd8a0f6c16a166a7b5783f Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 3 Jan 2019 14:23:22 -0200 Subject: [PATCH 19/59] Move some common functions to rocketchat:utils --- .../rocketchat-lib/client/lib/handleError.js | 26 ++----------------- packages/rocketchat-utils/client/index.js | 4 +++ .../client/lib/handleError.js | 25 ++++++++++++++++++ .../rocketchat-utils/lib/getUserPreference.js | 17 ++++++++++++ packages/rocketchat-utils/package.js | 2 ++ packages/rocketchat-utils/server/index.js | 2 ++ 6 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 packages/rocketchat-utils/client/lib/handleError.js create mode 100644 packages/rocketchat-utils/lib/getUserPreference.js diff --git a/packages/rocketchat-lib/client/lib/handleError.js b/packages/rocketchat-lib/client/lib/handleError.js index 04b523e66801..1902d53f55b2 100644 --- a/packages/rocketchat-lib/client/lib/handleError.js +++ b/packages/rocketchat-lib/client/lib/handleError.js @@ -1,25 +1,3 @@ -import { TAPi18n } from 'meteor/tap:i18n'; -import _ from 'underscore'; -import s from 'underscore.string'; -import toastr from 'toastr'; +import { handleError as _handleError } from 'meteor/rocketchat:utils'; -handleError = function(error, useToastr = true) { - if (_.isObject(error.details)) { - for (const key in error.details) { - if (error.details.hasOwnProperty(key)) { - error.details[key] = TAPi18n.__(error.details[key]); - } - } - } - - if (useToastr) { - const details = Object.entries(error.details || {}) - .reduce((obj, [key, value]) => ({ ...obj, [key] : s.escapeHTML(value) }), {}); - const message = TAPi18n.__(error.error, details); - const title = details.errorTitle && TAPi18n.__(details.errorTitle); - - return toastr.error(message, title); - } - - return s.escapeHTML(TAPi18n.__(error.error, error.details)); -}; +handleError = _handleError; diff --git a/packages/rocketchat-utils/client/index.js b/packages/rocketchat-utils/client/index.js index cb49ba819992..4a27a89cbea5 100644 --- a/packages/rocketchat-utils/client/index.js +++ b/packages/rocketchat-utils/client/index.js @@ -2,6 +2,8 @@ import { t, isRtl } from '../lib/tapi18n'; import { isChrome, isFirefox } from './lib/browsers'; import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; import { Info } from '../rocketchat.info'; +import { handleError } from './lib/handleError'; +import { getUserPreference } from '../lib/getUserPreference'; export { t, @@ -10,4 +12,6 @@ export { isFirefox, getDefaultSubscriptionPref, Info, + handleError, + getUserPreference, }; diff --git a/packages/rocketchat-utils/client/lib/handleError.js b/packages/rocketchat-utils/client/lib/handleError.js new file mode 100644 index 000000000000..abe8adcffc24 --- /dev/null +++ b/packages/rocketchat-utils/client/lib/handleError.js @@ -0,0 +1,25 @@ +import { TAPi18n } from 'meteor/tap:i18n'; +import _ from 'underscore'; +import s from 'underscore.string'; +import toastr from 'toastr'; + +export const handleError = function(error, useToastr = true) { + if (_.isObject(error.details)) { + for (const key in error.details) { + if (error.details.hasOwnProperty(key)) { + error.details[key] = TAPi18n.__(error.details[key]); + } + } + } + + if (useToastr) { + const details = Object.entries(error.details || {}) + .reduce((obj, [key, value]) => ({ ...obj, [key] : s.escapeHTML(value) }), {}); + const message = TAPi18n.__(error.error, details); + const title = details.errorTitle && TAPi18n.__(details.errorTitle); + + return toastr.error(message, title); + } + + return s.escapeHTML(TAPi18n.__(error.error, error.details)); +}; diff --git a/packages/rocketchat-utils/lib/getUserPreference.js b/packages/rocketchat-utils/lib/getUserPreference.js new file mode 100644 index 000000000000..391c11b13b8f --- /dev/null +++ b/packages/rocketchat-utils/lib/getUserPreference.js @@ -0,0 +1,17 @@ +import { Users } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; + +export const getUserPreference = (user, key, defaultValue = undefined) => { + let preference; + if (typeof user === typeof '') { + user = Users.findOne(user, { fields: { [`settings.preferences.${ key }`]: 1 } }); + } + if (user && user.settings && user.settings.preferences && + user.settings.preferences.hasOwnProperty(key)) { + preference = user.settings.preferences[key]; + } else if (defaultValue === undefined) { + preference = settings.get(`Accounts_Default_User_Preferences_${ key }`); + } + + return preference !== undefined ? preference : defaultValue; +}; diff --git a/packages/rocketchat-utils/package.js b/packages/rocketchat-utils/package.js index 05fa30074847..59c5294f73fe 100644 --- a/packages/rocketchat-utils/package.js +++ b/packages/rocketchat-utils/package.js @@ -9,6 +9,8 @@ Package.onUse(function(api) { 'ecmascript', 'tap:i18n', 'rocketchat:version', + 'rocketchat:models', + 'rocketchat:settings', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-utils/server/index.js b/packages/rocketchat-utils/server/index.js index 906b3434c665..d95324c1f3e9 100644 --- a/packages/rocketchat-utils/server/index.js +++ b/packages/rocketchat-utils/server/index.js @@ -1,10 +1,12 @@ import { t, isRtl } from '../lib/tapi18n'; import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; import { Info } from '../rocketchat.info'; +import { getUserPreference } from '../lib/getUserPreference'; export { t, isRtl, getDefaultSubscriptionPref, Info, + getUserPreference, }; From 86bf84c2dcb696cecfb371563558c3d1cf107e63 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 3 Jan 2019 14:26:11 -0200 Subject: [PATCH 20/59] Change imports to dynamic imports to avoid directly dependency between some packages --- packages/rocketchat-metrics/server/lib/metrics.js | 6 +++++- packages/rocketchat-models/package.js | 1 - packages/rocketchat-models/server/models/Subscriptions.js | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/rocketchat-metrics/server/lib/metrics.js b/packages/rocketchat-metrics/server/lib/metrics.js index 5ba4efa3a350..0e69cdffb0b2 100644 --- a/packages/rocketchat-metrics/server/lib/metrics.js +++ b/packages/rocketchat-metrics/server/lib/metrics.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; import { settings } from 'meteor/rocketchat:settings'; -import { Info } from 'meteor/rocketchat:utils'; import { Migrations } from 'meteor/rocketchat:migrations'; import client from 'prom-client'; import connect from 'connect'; @@ -10,6 +9,7 @@ import _ from 'underscore'; client.collectDefaultMetrics(); export const metrics = {}; +let Info; // one sample metrics only - a counter @@ -84,6 +84,10 @@ client.register.setDefaultLabels({ const setPrometheusData = async() => { const date = new Date(); + if (!Info) { + const Utils = await import('meteor/rocketchat:utils'); + Info = Utils.Info; + } client.register.setDefaultLabels({ unique_id: settings.get('uniqueID'), diff --git a/packages/rocketchat-models/package.js b/packages/rocketchat-models/package.js index b116291c4751..74c8e04810b0 100755 --- a/packages/rocketchat-models/package.js +++ b/packages/rocketchat-models/package.js @@ -9,7 +9,6 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:settings', - 'rocketchat:utils', 'rocketchat:callbacks', 'konecty:multiple-instances-status', ]); diff --git a/packages/rocketchat-models/server/models/Subscriptions.js b/packages/rocketchat-models/server/models/Subscriptions.js index 6e332eea851f..f9064282a0f9 100644 --- a/packages/rocketchat-models/server/models/Subscriptions.js +++ b/packages/rocketchat-models/server/models/Subscriptions.js @@ -773,7 +773,11 @@ export class Subscriptions extends Base { } // INSERT - createWithRoomAndUser(room, user, extraData) { + async createWithRoomAndUser(room, user, extraData) { + if (!this.getDefaultSubscriptionPref) { + const Utils = await import('meteor/rocketchat:utils'); + this.getDefaultSubscriptionPref = Utils.getDefaultSubscriptionPref; + } const subscription = { open: false, alert: false, @@ -791,7 +795,7 @@ export class Subscriptions extends Base { username: user.username, name: user.name, }, - ...getDefaultSubscriptionPref(user), + ...this.getDefaultSubscriptionPref(user), ...extraData, }; From ec7fd7b782263b3d60d2c430d07435a2b97e0f5e Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 3 Jan 2019 14:27:58 -0200 Subject: [PATCH 21/59] Move authz models to rocketchat:models --- .../server/models/Base.js | 49 ------------------- .../server/models/Subscriptions.js | 36 -------------- .../server/models/Users.js | 15 ------ packages/rocketchat-lib/package.js | 2 + .../server/models/Permissions.js | 3 ++ .../rocketchat-lib/server/models/Roles.js | 3 ++ packages/rocketchat-models/server/index.js | 4 ++ .../server/models/Permissions.js | 6 +-- .../server/models/Roles.js | 15 +++--- .../server/models/Subscriptions.js | 36 +++++++++++++- .../rocketchat-models/server/models/Users.js | 14 ++++++ .../rocketchat-models/server/models/_Base.js | 47 ++++++++++++++++++ 12 files changed, 119 insertions(+), 111 deletions(-) delete mode 100644 packages/rocketchat-authorization/server/models/Base.js delete mode 100644 packages/rocketchat-authorization/server/models/Subscriptions.js delete mode 100644 packages/rocketchat-authorization/server/models/Users.js create mode 100644 packages/rocketchat-lib/server/models/Permissions.js create mode 100644 packages/rocketchat-lib/server/models/Roles.js rename packages/{rocketchat-authorization => rocketchat-models}/server/models/Permissions.js (73%) rename packages/{rocketchat-authorization => rocketchat-models}/server/models/Roles.js (84%) diff --git a/packages/rocketchat-authorization/server/models/Base.js b/packages/rocketchat-authorization/server/models/Base.js deleted file mode 100644 index f3e8b8283468..000000000000 --- a/packages/rocketchat-authorization/server/models/Base.js +++ /dev/null @@ -1,49 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; -import _ from 'underscore'; - -RocketChat.models._Base.prototype.roleBaseQuery = function(/* userId, scope*/) { - return; -}; - -RocketChat.models._Base.prototype.findRolesByUserId = function(userId/* , options*/) { - const query = this.roleBaseQuery(userId); - return this.find(query, { fields: { roles: 1 } }); -}; - -RocketChat.models._Base.prototype.isUserInRole = function(userId, roleName, scope) { - const query = this.roleBaseQuery(userId, scope); - - if (query == null) { - return false; - } - - query.roles = roleName; - return !_.isUndefined(this.findOne(query, { fields: { roles: 1 } })); -}; - -RocketChat.models._Base.prototype.addRolesByUserId = function(userId, roles, scope) { - roles = [].concat(roles); - const query = this.roleBaseQuery(userId, scope); - const update = { - $addToSet: { - roles: { $each: roles }, - }, - }; - return this.update(query, update); -}; - -RocketChat.models._Base.prototype.removeRolesByUserId = function(userId, roles, scope) { - roles = [].concat(roles); - const query = this.roleBaseQuery(userId, scope); - const update = { - $pullAll: { - roles, - }, - }; - return this.update(query, update); -}; - -RocketChat.models._Base.prototype.findUsersInRoles = function() { - throw new Meteor.Error('overwrite-function', 'You must overwrite this function in the extended classes'); -}; diff --git a/packages/rocketchat-authorization/server/models/Subscriptions.js b/packages/rocketchat-authorization/server/models/Subscriptions.js deleted file mode 100644 index 257b75f75e57..000000000000 --- a/packages/rocketchat-authorization/server/models/Subscriptions.js +++ /dev/null @@ -1,36 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; -import _ from 'underscore'; - -RocketChat.models.Subscriptions.roleBaseQuery = function(userId, scope) { - if (scope == null) { - return; - } - - const query = { 'u._id': userId }; - if (!_.isUndefined(scope)) { - query.rid = scope; - } - return query; -}; - -RocketChat.models.Subscriptions.findUsersInRoles = function(roles, scope, options) { - roles = [].concat(roles); - - const query = { - roles: { $in: roles }, - }; - - if (scope) { - query.rid = scope; - } - - const subscriptions = this.find(query).fetch(); - - const users = _.compact(_.map(subscriptions, function(subscription) { - if ('undefined' !== typeof subscription.u && 'undefined' !== typeof subscription.u._id) { - return subscription.u._id; - } - })); - - return RocketChat.models.Users.find({ _id: { $in: users } }, options); -}; diff --git a/packages/rocketchat-authorization/server/models/Users.js b/packages/rocketchat-authorization/server/models/Users.js deleted file mode 100644 index d6d4132a85cc..000000000000 --- a/packages/rocketchat-authorization/server/models/Users.js +++ /dev/null @@ -1,15 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.models.Users.roleBaseQuery = function(userId) { - return { _id: userId }; -}; - -RocketChat.models.Users.findUsersInRoles = function(roles, scope, options) { - roles = [].concat(roles); - - const query = { - roles: { $in: roles }, - }; - - return this.find(query, options); -}; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 8dc05fd7b49c..07a2351488ec 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -155,6 +155,8 @@ Package.onUse(function(api) { api.addFiles('server/models/Users.js', 'server'); api.addFiles('server/models/ExportOperations.js', 'server'); api.addFiles('server/models/UserDataFiles.js', 'server'); + api.addFiles('server/models/Permissions.js', 'server'); + api.addFiles('server/models/Roles.js', 'server'); api.addFiles('server/oauth/oauth.js', 'server'); api.addFiles('server/oauth/facebook.js', 'server'); diff --git a/packages/rocketchat-lib/server/models/Permissions.js b/packages/rocketchat-lib/server/models/Permissions.js new file mode 100644 index 000000000000..c171d3bd4a99 --- /dev/null +++ b/packages/rocketchat-lib/server/models/Permissions.js @@ -0,0 +1,3 @@ +import { Permissions } from 'meteor/rocketchat:models'; + +RocketChat.models.Permissions = Permissions; diff --git a/packages/rocketchat-lib/server/models/Roles.js b/packages/rocketchat-lib/server/models/Roles.js new file mode 100644 index 000000000000..88c23358a0ed --- /dev/null +++ b/packages/rocketchat-lib/server/models/Roles.js @@ -0,0 +1,3 @@ +import { Roles } from 'meteor/rocketchat:models'; + +RocketChat.models.Roles = Roles; diff --git a/packages/rocketchat-models/server/index.js b/packages/rocketchat-models/server/index.js index bb2ea816e180..36730f015ec8 100644 --- a/packages/rocketchat-models/server/index.js +++ b/packages/rocketchat-models/server/index.js @@ -11,6 +11,8 @@ import Uploads from './models/Uploads'; import UserDataFiles from './models/UserDataFiles'; import Users from './models/Users'; import Statistics from './models/Statistics'; +import Permissions from './models/Permissions'; +import Roles from './models/Roles'; export { Base, @@ -26,4 +28,6 @@ export { UserDataFiles, Users, Statistics, + Permissions, + Roles, }; diff --git a/packages/rocketchat-authorization/server/models/Permissions.js b/packages/rocketchat-models/server/models/Permissions.js similarity index 73% rename from packages/rocketchat-authorization/server/models/Permissions.js rename to packages/rocketchat-models/server/models/Permissions.js index d4f787639318..216893605b1f 100644 --- a/packages/rocketchat-authorization/server/models/Permissions.js +++ b/packages/rocketchat-models/server/models/Permissions.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Base } from './_Base'; -class ModelPermissions extends RocketChat.models._Base { +export class Permissions extends Base { constructor(...args) { super(...args); } @@ -31,4 +31,4 @@ class ModelPermissions extends RocketChat.models._Base { } } -RocketChat.models.Permissions = new ModelPermissions('permissions'); +export default new Permissions('permissions'); diff --git a/packages/rocketchat-authorization/server/models/Roles.js b/packages/rocketchat-models/server/models/Roles.js similarity index 84% rename from packages/rocketchat-authorization/server/models/Roles.js rename to packages/rocketchat-models/server/models/Roles.js index af660b6c3130..474f1db9f35d 100644 --- a/packages/rocketchat-authorization/server/models/Roles.js +++ b/packages/rocketchat-models/server/models/Roles.js @@ -1,6 +1,7 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import * as Models from '..'; +import { Base } from './_Base'; -class ModelRoles extends RocketChat.models._Base { +export class Roles extends Base { constructor(...args) { super(...args); this.tryEnsureIndex({ name: 1 }); @@ -10,7 +11,7 @@ class ModelRoles extends RocketChat.models._Base { findUsersInRole(name, scope, options) { const role = this.findOne(name); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.findUsersInRoles && model.findUsersInRoles(name, scope, options); } @@ -20,7 +21,7 @@ class ModelRoles extends RocketChat.models._Base { return roles.some((roleName) => { const role = this.findOne(roleName); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.isUserInRole && model.isUserInRole(userId, roleName, scope); }); @@ -51,7 +52,7 @@ class ModelRoles extends RocketChat.models._Base { for (const roleName of roles) { const role = this.findOne(roleName); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; model && model.addRolesByUserId && model.addRolesByUserId(userId, roleName, scope); } @@ -63,7 +64,7 @@ class ModelRoles extends RocketChat.models._Base { for (const roleName of roles) { const role = this.findOne(roleName); const roleScope = (role && role.scope) || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; model && model.removeRolesByUserId && model.removeRolesByUserId(userId, roleName, scope); } @@ -83,4 +84,4 @@ class ModelRoles extends RocketChat.models._Base { } } -RocketChat.models.Roles = new ModelRoles('roles'); +export default new Roles('roles'); diff --git a/packages/rocketchat-models/server/models/Subscriptions.js b/packages/rocketchat-models/server/models/Subscriptions.js index f9064282a0f9..4dc17cf489f8 100644 --- a/packages/rocketchat-models/server/models/Subscriptions.js +++ b/packages/rocketchat-models/server/models/Subscriptions.js @@ -1,7 +1,8 @@ import { Base } from './_Base'; import { Match } from 'meteor/check'; import Rooms from './Rooms'; -import { getDefaultSubscriptionPref } from 'meteor/rocketchat:utils'; +import Users from './Users'; +import _ from 'underscore'; export class Subscriptions extends Base { constructor(...args) { @@ -28,6 +29,39 @@ export class Subscriptions extends Base { this.tryEnsureIndex({ 'userHighlights.0': 1 }, { sparse: 1 }); } + roleBaseQuery(userId, scope) { + if (scope == null) { + return; + } + + const query = { 'u._id': userId }; + if (!_.isUndefined(scope)) { + query.rid = scope; + } + return query; + } + + findUsersInRoles(roles, scope, options) { + roles = [].concat(roles); + + const query = { + roles: { $in: roles }, + }; + + if (scope) { + query.rid = scope; + } + + const subscriptions = this.find(query).fetch(); + + const users = _.compact(_.map(subscriptions, function(subscription) { + if ('undefined' !== typeof subscription.u && 'undefined' !== typeof subscription.u._id) { + return subscription.u._id; + } + })); + + return Users.find({ _id: { $in: users } }, options); + } // FIND ONE findOneByRoomIdAndUserId(roomId, userId, options) { diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js index 786da5b96aa3..f89431943978 100644 --- a/packages/rocketchat-models/server/models/Users.js +++ b/packages/rocketchat-models/server/models/Users.js @@ -19,6 +19,20 @@ export class Users extends Base { this.tryEnsureIndex({ type: 1 }); } + roleBaseQuery(userId) { + return { _id: userId }; + } + + findUsersInRoles(roles, scope, options) { + roles = [].concat(roles); + + const query = { + roles: { $in: roles }, + }; + + return this.find(query, options); + } + findOneByImportId(_id, options) { return this.findOne({ importIds: _id }, options); } diff --git a/packages/rocketchat-models/server/models/_Base.js b/packages/rocketchat-models/server/models/_Base.js index 5180e2ebe25e..3dc855cb546a 100644 --- a/packages/rocketchat-models/server/models/_Base.js +++ b/packages/rocketchat-models/server/models/_Base.js @@ -1,3 +1,4 @@ +import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import { BaseDb } from './_BaseDb'; import objectPath from 'object-path'; @@ -20,6 +21,52 @@ export class Base { return '_db'; } + roleBaseQuery() { + return; + } + + findRolesByUserId(userId) { + const query = this.roleBaseQuery(userId); + return this.find(query, { fields: { roles: 1 } }); + } + + isUserInRole(userId, roleName, scope) { + const query = this.roleBaseQuery(userId, scope); + + if (query == null) { + return false; + } + + query.roles = roleName; + return !_.isUndefined(this.findOne(query, { fields: { roles: 1 } })); + } + + addRolesByUserId(userId, roles, scope) { + roles = [].concat(roles); + const query = this.roleBaseQuery(userId, scope); + const update = { + $addToSet: { + roles: { $each: roles }, + }, + }; + return this.update(query, update); + } + + removeRolesByUserId(userId, roles, scope) { + roles = [].concat(roles); + const query = this.roleBaseQuery(userId, scope); + const update = { + $pullAll: { + roles, + }, + }; + return this.update(query, update); + } + + findUsersInRoles() { + throw new Meteor.Error('overwrite-function', 'You must overwrite this function in the extended classes'); + } + arrayToCursor(data) { return { fetch() { From 523d704f0318cb30c6dfecd578ab3715de458b51 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 3 Jan 2019 14:30:30 -0200 Subject: [PATCH 22/59] Remove directly dependency between rocketchat:authz and rocketchat:lib --- .../client/hasPermission.js | 27 ++++++--------- .../client/hasRole.js | 6 ++-- .../rocketchat-authorization/client/index.js | 10 ++++-- .../client/startup.js | 12 +++---- .../client/usersNameChanged.js | 6 ++-- .../client/views/permissions.js | 7 ++-- .../client/views/permissionsRole.js | 14 ++++---- packages/rocketchat-authorization/package.js | 3 +- .../server/functions/addUserRoles.js | 13 +++---- .../server/functions/canAccessRoom.js | 20 +++++------ .../server/functions/getRoles.js | 6 ++-- .../server/functions/getUsersInRole.js | 7 ++-- .../server/functions/hasPermission.js | 26 ++++++-------- .../server/functions/hasRole.js | 6 ++-- .../server/functions/removeUserFromRoles.js | 11 +++--- .../rocketchat-authorization/server/index.js | 34 ++++++++++++------- .../server/methods/addPermissionToRole.js | 7 ++-- .../server/methods/addUserToRole.js | 17 ++++++---- .../server/methods/deleteRole.js | 11 +++--- .../methods/removeRoleFromPermission.js | 7 ++-- .../server/methods/removeUserFromRole.js | 13 ++++--- .../server/methods/saveRole.js | 13 ++++--- .../server/publications/permissions.js | 13 +++---- .../server/publications/roles.js | 4 +-- .../server/publications/usersInRole.js | 7 ++-- .../server/startup.js | 8 ++--- .../client/lib/authorization.js | 6 ++++ packages/rocketchat-lib/package.js | 4 ++- .../server/functions/authorization.js | 25 ++++++++++++++ 29 files changed, 196 insertions(+), 147 deletions(-) create mode 100644 packages/rocketchat-lib/client/lib/authorization.js create mode 100644 packages/rocketchat-lib/server/functions/authorization.js diff --git a/packages/rocketchat-authorization/client/hasPermission.js b/packages/rocketchat-authorization/client/hasPermission.js index ca714683ff6e..92cdd76d05a4 100644 --- a/packages/rocketchat-authorization/client/hasPermission.js +++ b/packages/rocketchat-authorization/client/hasPermission.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import * as Models from 'meteor/rocketchat:models'; import { ChatPermissions } from './lib/ChatPermissions'; function atLeastOne(permissions = [], scope) { @@ -9,9 +9,9 @@ function atLeastOne(permissions = [], scope) { const roles = (permission && permission.roles) || []; return roles.some((roleName) => { - const role = RocketChat.models.Roles.findOne(roleName); + const role = Models.Roles.findOne(roleName); const roleScope = role && role.scope; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.isUserInRole && model.isUserInRole(Meteor.userId(), roleName, scope); }); @@ -24,22 +24,22 @@ function all(permissions = [], scope) { const roles = (permission && permission.roles) || []; return roles.some((roleName) => { - const role = RocketChat.models.Roles.findOne(roleName); + const role = Models.Roles.findOne(roleName); const roleScope = role && role.scope; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; return model && model.isUserInRole && model.isUserInRole(Meteor.userId(), roleName, scope); }); }); } -function hasPermission(permissions, scope, strategy) { +function _hasPermission(permissions, scope, strategy) { const userId = Meteor.userId(); if (!userId) { return false; } - if (!RocketChat.authz.cachedCollection.ready.get()) { + if (!Models.AuthzCachedCollection.ready.get()) { return false; } @@ -48,15 +48,8 @@ function hasPermission(permissions, scope, strategy) { } Template.registerHelper('hasPermission', function(permission, scope) { - return hasPermission(permission, scope, atLeastOne); + return _hasPermission(permission, scope, atLeastOne); }); -RocketChat.authz.hasAllPermission = function(permissions, scope) { - return hasPermission(permissions, scope, all); -}; - -RocketChat.authz.hasPermission = RocketChat.authz.hasAllPermission; - -RocketChat.authz.hasAtLeastOnePermission = function(permissions, scope) { - return hasPermission(permissions, scope, atLeastOne); -}; +export const hasAllPermission = (permissions, scope) => _hasPermission(permissions, scope, all); +export const hasAtLeastOnePermission = (permissions, scope) => _hasPermission(permissions, scope, atLeastOne); diff --git a/packages/rocketchat-authorization/client/hasRole.js b/packages/rocketchat-authorization/client/hasRole.js index 60a3b0e56800..5a39c9a03dfb 100644 --- a/packages/rocketchat-authorization/client/hasRole.js +++ b/packages/rocketchat-authorization/client/hasRole.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; -RocketChat.authz.hasRole = function(userId, roleNames, scope) { +export const hasRole = (userId, roleNames, scope) => { roleNames = [].concat(roleNames); - return RocketChat.models.Roles.isUserInRoles(userId, roleNames, scope); + return Roles.isUserInRoles(userId, roleNames, scope); }; diff --git a/packages/rocketchat-authorization/client/index.js b/packages/rocketchat-authorization/client/index.js index 7e851af52944..76bf9a87483d 100644 --- a/packages/rocketchat-authorization/client/index.js +++ b/packages/rocketchat-authorization/client/index.js @@ -1,5 +1,5 @@ -import './hasPermission'; -import './hasRole'; +import { hasAllPermission, hasAtLeastOnePermission } from './hasPermission'; +import { hasRole } from './hasRole'; import './usersNameChanged'; import './requiresPermission.html'; import './route'; @@ -8,3 +8,9 @@ import './views/permissions.html'; import './views/permissions'; import './views/permissionsRole.html'; import './views/permissionsRole'; + +export { + hasAllPermission, + hasAtLeastOnePermission, + hasRole, +}; diff --git a/packages/rocketchat-authorization/client/startup.js b/packages/rocketchat-authorization/client/startup.js index 40ded90ac716..ef220b62edb2 100644 --- a/packages/rocketchat-authorization/client/startup.js +++ b/packages/rocketchat-authorization/client/startup.js @@ -1,15 +1,15 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { AdminBox } from 'meteor/rocketchat:ui-utils'; +import { CachedCollectionManager } from 'meteor/rocketchat:models'; +import { hasAllPermission } from './hasPermission'; -RocketChat.CachedCollectionManager.onLogin(() => { - Meteor.subscribe('roles'); -}); +CachedCollectionManager.onLogin(() => Meteor.subscribe('roles')); -RocketChat.AdminBox.addOption({ +AdminBox.addOption({ href: 'admin-permissions', i18nLabel: 'Permissions', icon: 'lock', permissionGranted() { - return RocketChat.authz.hasAllPermission('access-permissions'); + return hasAllPermission('access-permissions'); }, }); diff --git a/packages/rocketchat-authorization/client/usersNameChanged.js b/packages/rocketchat-authorization/client/usersNameChanged.js index 0c632b888b54..15c2e0604c24 100644 --- a/packages/rocketchat-authorization/client/usersNameChanged.js +++ b/packages/rocketchat-authorization/client/usersNameChanged.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; -import { RoomRoles } from 'meteor/rocketchat:ui'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { RoomRoles } from 'meteor/rocketchat:models'; Meteor.startup(function() { - RocketChat.Notifications.onLogged('Users:NameChanged', function({ _id, name }) { + Notifications.onLogged('Users:NameChanged', function({ _id, name }) { RoomRoles.update({ 'u._id': _id, }, { diff --git a/packages/rocketchat-authorization/client/views/permissions.js b/packages/rocketchat-authorization/client/views/permissions.js index 23c0aefe74a9..2a16bbb874c2 100644 --- a/packages/rocketchat-authorization/client/views/permissions.js +++ b/packages/rocketchat-authorization/client/views/permissions.js @@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; import { Template } from 'meteor/templating'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; import { ChatPermissions } from '../lib/ChatPermissions'; +import { hasAllPermission } from '../hasPermission'; Template.permissions.helpers({ role() { @@ -35,7 +36,7 @@ Template.permissions.helpers({ }, hasPermission() { - return RocketChat.authz.hasAllPermission('access-permissions'); + return hasAllPermission('access-permissions'); }, }); @@ -61,7 +62,7 @@ Template.permissions.onCreated(function() { }; Tracker.autorun(() => { - this.roles.set(RocketChat.models.Roles.find().fetch()); + this.roles.set(Roles.find().fetch()); }); Tracker.autorun(() => { diff --git a/packages/rocketchat-authorization/client/views/permissionsRole.js b/packages/rocketchat-authorization/client/views/permissionsRole.js index 4f06f256f718..f74b98ad97d0 100644 --- a/packages/rocketchat-authorization/client/views/permissionsRole.js +++ b/packages/rocketchat-authorization/client/views/permissionsRole.js @@ -2,15 +2,15 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { modal } from 'meteor/rocketchat:ui'; -import { t } from 'meteor/rocketchat:utils'; -import { RocketChat, handleError } from 'meteor/rocketchat:lib'; - +import { modal } from 'meteor/rocketchat:ui-utils'; +import { t, handleError } from 'meteor/rocketchat:utils'; +import { Roles } from 'meteor/rocketchat:models'; +import { hasAllPermission } from '../hasPermission'; import toastr from 'toastr'; Template.permissionsRole.helpers({ role() { - return RocketChat.models.Roles.findOne({ + return Roles.findOne({ _id: FlowRouter.getParam('name'), }) || {}; }, @@ -30,7 +30,7 @@ Template.permissionsRole.helpers({ }, hasPermission() { - return RocketChat.authz.hasAllPermission('access-permissions'); + return hasAllPermission('access-permissions'); }, protected() { @@ -238,7 +238,7 @@ Template.permissionsRole.onCreated(function() { const subscription = this.subscribe('usersInRole', FlowRouter.getParam('name'), this.searchRoom.get(), limit); this.ready.set(subscription.ready()); - this.usersInRole.set(RocketChat.models.Roles.findUsersInRole(FlowRouter.getParam('name'), this.searchRoom.get(), { + this.usersInRole.set(Roles.findUsersInRole(FlowRouter.getParam('name'), this.searchRoom.get(), { sort: { username: 1, }, diff --git a/packages/rocketchat-authorization/package.js b/packages/rocketchat-authorization/package.js index 5065377535aa..8f871ec8e6e4 100644 --- a/packages/rocketchat-authorization/package.js +++ b/packages/rocketchat-authorization/package.js @@ -9,10 +9,11 @@ Package.describe({ Package.onUse(function(api) { api.use([ 'ecmascript', - 'rocketchat:lib', 'mongo', 'rocketchat:utils', 'rocketchat:models', + 'rocketchat:notifications', + 'rocketchat:ui-utils', ]); api.use([ 'templating', diff --git a/packages/rocketchat-authorization/server/functions/addUserRoles.js b/packages/rocketchat-authorization/server/functions/addUserRoles.js index 044533078d73..784c10813404 100644 --- a/packages/rocketchat-authorization/server/functions/addUserRoles.js +++ b/packages/rocketchat-authorization/server/functions/addUserRoles.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users, Roles } from 'meteor/rocketchat:models'; +import { getRoles } from './getRoles'; import _ from 'underscore'; -RocketChat.authz.addUserRoles = function(userId, roleNames, scope) { +export const addUserRoles = (userId, roleNames, scope) => { if (!userId || !roleNames) { return false; } - const user = RocketChat.models.Users.db.findOneById(userId); + const user = Users.db.findOneById(userId); if (!user) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'RocketChat.authz.addUserRoles', @@ -15,16 +16,16 @@ RocketChat.authz.addUserRoles = function(userId, roleNames, scope) { } roleNames = [].concat(roleNames); - const existingRoleNames = _.pluck(RocketChat.authz.getRoles(), '_id'); + const existingRoleNames = _.pluck(getRoles(), '_id'); const invalidRoleNames = _.difference(roleNames, existingRoleNames); if (!_.isEmpty(invalidRoleNames)) { for (const role of invalidRoleNames) { - RocketChat.models.Roles.createOrUpdate(role); + Roles.createOrUpdate(role); } } - RocketChat.models.Roles.addUserRoles(userId, roleNames, scope); + Roles.addUserRoles(userId, roleNames, scope); return true; }; diff --git a/packages/rocketchat-authorization/server/functions/canAccessRoom.js b/packages/rocketchat-authorization/server/functions/canAccessRoom.js index dc83d5652648..454aa8bcd2c5 100644 --- a/packages/rocketchat-authorization/server/functions/canAccessRoom.js +++ b/packages/rocketchat-authorization/server/functions/canAccessRoom.js @@ -1,13 +1,15 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; +import { Subscriptions } from 'meteor/rocketchat:models'; +import { hasPermission } from './hasPermission'; -RocketChat.authz.roomAccessValidators = [ +export const roomAccessValidators = [ function(room, user = {}) { if (room && room.t === 'c') { - if (!user._id && RocketChat.settings.get('Accounts_AllowAnonymousRead') === true) { + if (!user._id && settings.get('Accounts_AllowAnonymousRead') === true) { return true; } - return RocketChat.authz.hasPermission(user._id, 'view-c-room'); + return hasPermission(user._id, 'view-c-room'); } }, function(room, user) { @@ -15,17 +17,13 @@ RocketChat.authz.roomAccessValidators = [ return; } - const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id); + const subscription = Subscriptions.findOneByRoomIdAndUserId(room._id, user._id); if (subscription) { return true; } }, ]; -RocketChat.authz.canAccessRoom = function(room, user, extraData) { - return RocketChat.authz.roomAccessValidators.some((validator) => validator(room, user, extraData)); -}; +export const canAccessRoom = (room, user, extraData) => roomAccessValidators.some((validator) => validator(room, user, extraData)); -RocketChat.authz.addRoomAccessValidator = function(validator) { - RocketChat.authz.roomAccessValidators.push(validator.bind(this)); -}; +export const addRoomAccessValidator = (validator) => roomAccessValidators.push(validator.bind(this)); diff --git a/packages/rocketchat-authorization/server/functions/getRoles.js b/packages/rocketchat-authorization/server/functions/getRoles.js index eb2bb2846a56..4878a6a8ad4c 100644 --- a/packages/rocketchat-authorization/server/functions/getRoles.js +++ b/packages/rocketchat-authorization/server/functions/getRoles.js @@ -1,5 +1,3 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; -RocketChat.authz.getRoles = function() { - return RocketChat.models.Roles.find().fetch(); -}; +export const getRoles = () => Roles.find().fetch(); diff --git a/packages/rocketchat-authorization/server/functions/getUsersInRole.js b/packages/rocketchat-authorization/server/functions/getUsersInRole.js index f38e1b90cd0b..90c65b9eb242 100644 --- a/packages/rocketchat-authorization/server/functions/getUsersInRole.js +++ b/packages/rocketchat-authorization/server/functions/getUsersInRole.js @@ -1,5 +1,4 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; + +export const getUsersInRole = (roleName, scope, options) => Roles.findUsersInRole(roleName, scope, options); -RocketChat.authz.getUsersInRole = function(roleName, scope, options) { - return RocketChat.models.Roles.findUsersInRole(roleName, scope, options); -}; diff --git a/packages/rocketchat-authorization/server/functions/hasPermission.js b/packages/rocketchat-authorization/server/functions/hasPermission.js index 7aee1113ea31..d23354af5910 100644 --- a/packages/rocketchat-authorization/server/functions/hasPermission.js +++ b/packages/rocketchat-authorization/server/functions/hasPermission.js @@ -1,38 +1,34 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles, Permissions } from 'meteor/rocketchat:models'; function atLeastOne(userId, permissions = [], scope) { return permissions.some((permissionId) => { - const permission = RocketChat.models.Permissions.findOne(permissionId); - return RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope); + const permission = Permissions.findOne(permissionId); + return Roles.isUserInRoles(userId, permission.roles, scope); }); } function all(userId, permissions = [], scope) { return permissions.every((permissionId) => { - const permission = RocketChat.models.Permissions.findOne(permissionId); - return RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope); + const permission = Permissions.findOne(permissionId); + return Roles.isUserInRoles(userId, permission.roles, scope); }); } -function hasPermission(userId, permissions, scope, strategy) { +function _hasPermission(userId, permissions, scope, strategy) { if (!userId) { return false; } return strategy(userId, [].concat(permissions), scope); } -RocketChat.authz.hasAllPermission = function(userId, permissions, scope) { - return hasPermission(userId, permissions, scope, all); -}; +export const hasAllPermission = (userId, permissions, scope) => _hasPermission(userId, permissions, scope, all); -RocketChat.authz.hasPermission = (userId, permissionId, scope) => { +export const hasPermission = (userId, permissionId, scope) => { if (!userId) { return false; } - const permission = RocketChat.models.Permissions.findOne(permissionId); - return RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope); + const permission = Permissions.findOne(permissionId); + return Roles.isUserInRoles(userId, permission.roles, scope); }; -RocketChat.authz.hasAtLeastOnePermission = function(userId, permissions, scope) { - return hasPermission(userId, permissions, scope, atLeastOne); -}; +export const hasAtLeastOnePermission = (userId, permissions, scope) => _hasPermission(userId, permissions, scope, atLeastOne); diff --git a/packages/rocketchat-authorization/server/functions/hasRole.js b/packages/rocketchat-authorization/server/functions/hasRole.js index 60a3b0e56800..5a39c9a03dfb 100644 --- a/packages/rocketchat-authorization/server/functions/hasRole.js +++ b/packages/rocketchat-authorization/server/functions/hasRole.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; -RocketChat.authz.hasRole = function(userId, roleNames, scope) { +export const hasRole = (userId, roleNames, scope) => { roleNames = [].concat(roleNames); - return RocketChat.models.Roles.isUserInRoles(userId, roleNames, scope); + return Roles.isUserInRoles(userId, roleNames, scope); }; diff --git a/packages/rocketchat-authorization/server/functions/removeUserFromRoles.js b/packages/rocketchat-authorization/server/functions/removeUserFromRoles.js index 150ebbd61d00..f351441c7be7 100644 --- a/packages/rocketchat-authorization/server/functions/removeUserFromRoles.js +++ b/packages/rocketchat-authorization/server/functions/removeUserFromRoles.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users, Roles } from 'meteor/rocketchat:models'; +import { getRoles } from './getRoles'; import _ from 'underscore'; -RocketChat.authz.removeUserFromRoles = function(userId, roleNames, scope) { +export const removeUserFromRoles = (userId, roleNames, scope) => { if (!userId || !roleNames) { return false; } - const user = RocketChat.models.Users.findOneById(userId); + const user = Users.findOneById(userId); if (!user) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { @@ -16,7 +17,7 @@ RocketChat.authz.removeUserFromRoles = function(userId, roleNames, scope) { } roleNames = [].concat(roleNames); - const existingRoleNames = _.pluck(RocketChat.authz.getRoles(), '_id'); + const existingRoleNames = _.pluck(getRoles(), '_id'); const invalidRoleNames = _.difference(roleNames, existingRoleNames); if (!_.isEmpty(invalidRoleNames)) { @@ -25,7 +26,7 @@ RocketChat.authz.removeUserFromRoles = function(userId, roleNames, scope) { }); } - RocketChat.models.Roles.removeUserRoles(userId, roleNames, scope); + Roles.removeUserRoles(userId, roleNames, scope); return true; }; diff --git a/packages/rocketchat-authorization/server/index.js b/packages/rocketchat-authorization/server/index.js index 84f7f5739e3a..00a143d0ba69 100644 --- a/packages/rocketchat-authorization/server/index.js +++ b/packages/rocketchat-authorization/server/index.js @@ -1,16 +1,10 @@ -import './models/Base'; -import './models/Permissions'; -import './models/Roles'; -import './models/Subscriptions'; -import './models/Users'; -import './functions/addUserRoles'; -import './functions/canAccessRoom'; -import './functions/getRoles'; -import './functions/getUsersInRole'; -import './functions/hasPermission'; -import './functions/hasRole'; -import './functions/removeUserFromRoles'; -import './functions/removeUserFromRoles'; +import { addUserRoles } from './functions/addUserRoles'; +import { addRoomAccessValidator, canAccessRoom, roomAccessValidators } from './functions/canAccessRoom'; +import { getRoles } from './functions/getRoles'; +import { getUsersInRole } from './functions/getUsersInRole'; +import { hasAllPermission, hasAtLeastOnePermission, hasPermission } from './functions/hasPermission'; +import { hasRole } from './functions/hasRole'; +import { removeUserFromRoles } from './functions/removeUserFromRoles'; import './methods/addPermissionToRole'; import './methods/addUserToRole'; import './methods/deleteRole'; @@ -21,3 +15,17 @@ import './publications/permissions'; import './publications/roles'; import './publications/usersInRole'; import './startup'; + +export { + getRoles, + getUsersInRole, + hasAllPermission, + hasAtLeastOnePermission, + hasPermission, + hasRole, + removeUserFromRoles, + canAccessRoom, + addRoomAccessValidator, + roomAccessValidators, + addUserRoles, +}; diff --git a/packages/rocketchat-authorization/server/methods/addPermissionToRole.js b/packages/rocketchat-authorization/server/methods/addPermissionToRole.js index dd41746e94a6..312e6b5376e2 100644 --- a/packages/rocketchat-authorization/server/methods/addPermissionToRole.js +++ b/packages/rocketchat-authorization/server/methods/addPermissionToRole.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Permissions } from 'meteor/rocketchat:models'; +import { hasPermission } from '../functions/hasPermission'; Meteor.methods({ 'authorization:addPermissionToRole'(permission, role) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Adding permission is not allowed', { method: 'authorization:addPermissionToRole', action: 'Adding_permission', }); } - return RocketChat.models.Permissions.addRole(permission, role); + return Permissions.addRole(permission, role); }, }); diff --git a/packages/rocketchat-authorization/server/methods/addUserToRole.js b/packages/rocketchat-authorization/server/methods/addUserToRole.js index d76b18d916de..ce2693965761 100644 --- a/packages/rocketchat-authorization/server/methods/addUserToRole.js +++ b/packages/rocketchat-authorization/server/methods/addUserToRole.js @@ -1,10 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users, Roles } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { hasPermission } from '../functions/hasPermission'; import _ from 'underscore'; Meteor.methods({ 'authorization:addUserToRole'(roleName, username, scope) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { method: 'authorization:addUserToRole', action: 'Accessing_permissions', @@ -17,14 +20,14 @@ Meteor.methods({ }); } - if (roleName === 'admin' && !RocketChat.authz.hasPermission(Meteor.userId(), 'assign-admin-role')) { + if (roleName === 'admin' && !hasPermission(Meteor.userId(), 'assign-admin-role')) { throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { method: 'authorization:addUserToRole', action: 'Assign_admin', }); } - const user = RocketChat.models.Users.findOneByUsername(username, { + const user = Users.findOneByUsername(username, { fields: { _id: 1, }, @@ -36,10 +39,10 @@ Meteor.methods({ }); } - const add = RocketChat.models.Roles.addUserRoles(user._id, roleName, scope); + const add = Roles.addUserRoles(user._id, roleName, scope); - if (RocketChat.settings.get('UI_DisplayRoles')) { - RocketChat.Notifications.notifyLogged('roles-change', { + if (settings.get('UI_DisplayRoles')) { + Notifications.notifyLogged('roles-change', { type: 'added', _id: roleName, u: { diff --git a/packages/rocketchat-authorization/server/methods/deleteRole.js b/packages/rocketchat-authorization/server/methods/deleteRole.js index 2363cbabf29b..fa82a0c580bd 100644 --- a/packages/rocketchat-authorization/server/methods/deleteRole.js +++ b/packages/rocketchat-authorization/server/methods/deleteRole.js @@ -1,16 +1,17 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import * as Models from 'meteor/rocketchat:models'; +import { hasPermission } from '../functions/hasPermission'; Meteor.methods({ 'authorization:deleteRole'(roleName) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { method: 'authorization:deleteRole', action: 'Accessing_permissions', }); } - const role = RocketChat.models.Roles.findOne(roleName); + const role = Models.Roles.findOne(roleName); if (!role) { throw new Meteor.Error('error-invalid-role', 'Invalid role', { method: 'authorization:deleteRole', @@ -24,7 +25,7 @@ Meteor.methods({ } const roleScope = role.scope || 'Users'; - const model = RocketChat.models[roleScope]; + const model = Models[roleScope]; const existingUsers = model && model.findUsersInRoles && model.findUsersInRoles(roleName); if (existingUsers && existingUsers.count() > 0) { @@ -33,6 +34,6 @@ Meteor.methods({ }); } - return RocketChat.models.Roles.remove(role.name); + return Models.Roles.remove(role.name); }, }); diff --git a/packages/rocketchat-authorization/server/methods/removeRoleFromPermission.js b/packages/rocketchat-authorization/server/methods/removeRoleFromPermission.js index ef9c898279ae..f68c62d9a5b2 100644 --- a/packages/rocketchat-authorization/server/methods/removeRoleFromPermission.js +++ b/packages/rocketchat-authorization/server/methods/removeRoleFromPermission.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Permissions } from 'meteor/rocketchat:models'; +import { hasPermission } from '../functions/hasPermission'; Meteor.methods({ 'authorization:removeRoleFromPermission'(permission, role) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { method: 'authorization:removeRoleFromPermission', action: 'Accessing_permissions', }); } - return RocketChat.models.Permissions.removeRole(permission, role); + return Permissions.removeRole(permission, role); }, }); diff --git a/packages/rocketchat-authorization/server/methods/removeUserFromRole.js b/packages/rocketchat-authorization/server/methods/removeUserFromRole.js index 94a50b4a309d..012db582bbcb 100644 --- a/packages/rocketchat-authorization/server/methods/removeUserFromRole.js +++ b/packages/rocketchat-authorization/server/methods/removeUserFromRole.js @@ -1,10 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { hasPermission } from '../functions/hasPermission'; import _ from 'underscore'; Meteor.methods({ 'authorization:removeUserFromRole'(roleName, username, scope) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Access permissions is not allowed', { method: 'authorization:removeUserFromRole', action: 'Accessing_permissions', @@ -49,9 +52,9 @@ Meteor.methods({ } } - const remove = RocketChat.models.Roles.removeUserRoles(user._id, roleName, scope); - if (RocketChat.settings.get('UI_DisplayRoles')) { - RocketChat.Notifications.notifyLogged('roles-change', { + const remove = Roles.removeUserRoles(user._id, roleName, scope); + if (settings.get('UI_DisplayRoles')) { + Notifications.notifyLogged('roles-change', { type: 'removed', _id: roleName, u: { diff --git a/packages/rocketchat-authorization/server/methods/saveRole.js b/packages/rocketchat-authorization/server/methods/saveRole.js index 89f218850b95..7cf2cf659d91 100644 --- a/packages/rocketchat-authorization/server/methods/saveRole.js +++ b/packages/rocketchat-authorization/server/methods/saveRole.js @@ -1,9 +1,12 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { hasPermission } from '../functions/hasPermission'; Meteor.methods({ 'authorization:saveRole'(roleData) { - if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'access-permissions')) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { method: 'authorization:saveRole', action: 'Accessing_permissions', @@ -20,9 +23,9 @@ Meteor.methods({ roleData.scope = 'Users'; } - const update = RocketChat.models.Roles.createOrUpdate(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa); - if (RocketChat.settings.get('UI_DisplayRoles')) { - RocketChat.Notifications.notifyLogged('roles-change', { + const update = Roles.createOrUpdate(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa); + if (settings.get('UI_DisplayRoles')) { + Notifications.notifyLogged('roles-change', { type: 'changed', _id: roleData.name, }); diff --git a/packages/rocketchat-authorization/server/publications/permissions.js b/packages/rocketchat-authorization/server/publications/permissions.js index 04ba751dd7aa..dce5f09183b0 100644 --- a/packages/rocketchat-authorization/server/publications/permissions.js +++ b/packages/rocketchat-authorization/server/publications/permissions.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Permissions } from 'meteor/rocketchat:models'; +import { Notifications } from 'meteor/rocketchat:notifications'; Meteor.methods({ 'permissions/get'(updatedAt) { @@ -7,12 +8,12 @@ Meteor.methods({ // TODO: should we return this for non logged users? // TODO: we could cache this collection - const records = RocketChat.models.Permissions.find().fetch(); + const records = Permissions.find().fetch(); if (updatedAt instanceof Date) { return { update: records.filter((record) => record._updatedAt > updatedAt), - remove: RocketChat.models.Permissions.trashFindDeletedAfter(updatedAt, {}, { fields: { _id: 1, _deletedAt: 1 } }).fetch(), + remove: Permissions.trashFindDeletedAfter(updatedAt, {}, { fields: { _id: 1, _deletedAt: 1 } }).fetch(), }; } @@ -20,11 +21,11 @@ Meteor.methods({ }, }); -RocketChat.models.Permissions.on('change', ({ clientAction, id, data }) => { +Permissions.on('change', ({ clientAction, id, data }) => { switch (clientAction) { case 'updated': case 'inserted': - data = data || RocketChat.models.Permissions.findOneById(id); + data = data || Permissions.findOneById(id); break; case 'removed': @@ -32,5 +33,5 @@ RocketChat.models.Permissions.on('change', ({ clientAction, id, data }) => { break; } - RocketChat.Notifications.notifyLoggedInThisInstance('permissions-changed', clientAction, data); + Notifications.notifyLoggedInThisInstance('permissions-changed', clientAction, data); }); diff --git a/packages/rocketchat-authorization/server/publications/roles.js b/packages/rocketchat-authorization/server/publications/roles.js index f585c4b1d7cb..8bbd7abf8c91 100644 --- a/packages/rocketchat-authorization/server/publications/roles.js +++ b/packages/rocketchat-authorization/server/publications/roles.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles } from 'meteor/rocketchat:models'; Meteor.publish('roles', function() { if (!this.userId) { return this.ready(); } - return RocketChat.models.Roles.find(); + return Roles.find(); }); diff --git a/packages/rocketchat-authorization/server/publications/usersInRole.js b/packages/rocketchat-authorization/server/publications/usersInRole.js index c98efb7ab779..9bb6f251432b 100644 --- a/packages/rocketchat-authorization/server/publications/usersInRole.js +++ b/packages/rocketchat-authorization/server/publications/usersInRole.js @@ -1,12 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { hasPermission } from '../functions/hasPermission'; +import { getUsersInRole } from '../functions/getUsersInRole'; Meteor.publish('usersInRole', function(roleName, scope, limit = 50) { if (!this.userId) { return this.ready(); } - if (!RocketChat.authz.hasPermission(this.userId, 'access-permissions')) { + if (!hasPermission(this.userId, 'access-permissions')) { return this.error(new Meteor.Error('error-not-allowed', 'Not allowed', { publish: 'usersInRole', })); @@ -19,5 +20,5 @@ Meteor.publish('usersInRole', function(roleName, scope, limit = 50) { }, }; - return RocketChat.authz.getUsersInRole(roleName, scope, options); + return getUsersInRole(roleName, scope, options); }); diff --git a/packages/rocketchat-authorization/server/startup.js b/packages/rocketchat-authorization/server/startup.js index 75920aa63472..83e2a4ff3008 100644 --- a/packages/rocketchat-authorization/server/startup.js +++ b/packages/rocketchat-authorization/server/startup.js @@ -1,6 +1,6 @@ /* eslint no-multi-spaces: 0 */ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Roles, Permissions } from 'meteor/rocketchat:models'; Meteor.startup(function() { // Note: @@ -78,8 +78,8 @@ Meteor.startup(function() { ]; for (const permission of permissions) { - if (!RocketChat.models.Permissions.findOneById(permission._id)) { - RocketChat.models.Permissions.upsert(permission._id, { $set: permission }); + if (!Permissions.findOneById(permission._id)) { + Permissions.upsert(permission._id, { $set: permission }); } } @@ -95,6 +95,6 @@ Meteor.startup(function() { ]; for (const role of defaultRoles) { - RocketChat.models.Roles.upsert({ _id: role.name }, { $setOnInsert: { scope: role.scope, description: role.description || '', protected: true, mandatory2fa: false } }); + Roles.upsert({ _id: role.name }, { $setOnInsert: { scope: role.scope, description: role.description || '', protected: true, mandatory2fa: false } }); } }); diff --git a/packages/rocketchat-lib/client/lib/authorization.js b/packages/rocketchat-lib/client/lib/authorization.js new file mode 100644 index 000000000000..5431070179eb --- /dev/null +++ b/packages/rocketchat-lib/client/lib/authorization.js @@ -0,0 +1,6 @@ +import { hasAllPermission, hasAtLeastOnePermission, hasRole } from 'meteor/rocketchat:authorization'; + +RocketChat.authz.hasAllPermission = hasAllPermission; +RocketChat.authz.hasPermission = RocketChat.authz.hasAllPermission; +RocketChat.authz.hasAtLeastOnePermission = hasAtLeastOnePermission; +RocketChat.authz.hasRole = hasRole; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 07a2351488ec..b72a59ab60b6 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -40,12 +40,12 @@ Package.onUse(function(api) { api.use('rocketchat:logger'); api.use('rocketchat:mailer'); api.use('rocketchat:settings'); + api.use('rocketchat:authorization'); api.use('mizzao:timesync'); api.use('rocketchat:custom-oauth'); api.use('konecty:multiple-instances-status'); api.use('rocketchat:file'); api.use('rocketchat:push'); - api.use('rocketchat:authorization', { unordered: true }); api.use('rocketchat:push-notifications', { unordered: true }); api.use('templating', 'client'); @@ -128,6 +128,7 @@ Package.onUse(function(api) { api.addFiles('server/functions/updateMessage.js', 'server'); api.addFiles('server/functions/validateCustomFields.js', 'server'); api.addFiles('server/functions/Notifications.js', 'server'); + api.addFiles('server/functions/authorization.js', 'server'); // SERVER LIB api.addFiles('server/lib/configLogger.js', 'server'); @@ -242,6 +243,7 @@ Package.onUse(function(api) { api.addFiles('client/lib/userRoles.js', 'client'); api.addFiles('client/lib/Layout.js', 'client'); api.addFiles('client/lib/handleError.js', 'client'); + api.addFiles('client/lib/authorization.js', 'client'); // CLIENT LIB STARTUP api.addFiles('client/lib/startup/commands.js', 'client'); diff --git a/packages/rocketchat-lib/server/functions/authorization.js b/packages/rocketchat-lib/server/functions/authorization.js new file mode 100644 index 000000000000..e20a8958e9a5 --- /dev/null +++ b/packages/rocketchat-lib/server/functions/authorization.js @@ -0,0 +1,25 @@ +import { + addUserRoles, + roomAccessValidators, + canAccessRoom, + addRoomAccessValidator, + getRoles, + getUsersInRole, + hasAllPermission, + hasPermission, + hasAtLeastOnePermission, + hasRole, + removeUserFromRoles, +} from 'meteor/rocketchat:authorization'; + +RocketChat.authz.addUserRoles = addUserRoles; +RocketChat.authz.roomAccessValidators = roomAccessValidators; +RocketChat.authz.canAccessRoom = canAccessRoom; +RocketChat.authz.addRoomAccessValidator = addRoomAccessValidator; +RocketChat.authz.getRoles = getRoles; +RocketChat.authz.getUsersInRole = getUsersInRole; +RocketChat.authz.hasAllPermission = hasAllPermission; +RocketChat.authz.hasPermission = hasPermission; +RocketChat.authz.hasAtLeastOnePermission = hasAtLeastOnePermission; +RocketChat.authz.hasRole = hasRole; +RocketChat.authz.removeUserFromRoles = removeUserFromRoles; From c7700010c835bb75732dbfa129ad6f6a49a183ed Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 10:57:37 -0200 Subject: [PATCH 23/59] Move some functions from rocketchat:lib to rocketchat:utils --- .../rocketchat-lib/client/lib/roomTypes.js | 133 +-------- packages/rocketchat-lib/lib/RoomTypeConfig.js | 263 +---------------- .../rocketchat-lib/lib/RoomTypesCommon.js | 96 +------ .../lib/fileUploadRestrictions.js | 38 +-- packages/rocketchat-lib/package.js | 1 + .../server/functions/isDocker.js | 32 +-- .../rocketchat-lib/server/lib/roomTypes.js | 51 +--- packages/rocketchat-utils/client/index.js | 12 + .../rocketchat-utils/client/lib/roomTypes.js | 133 +++++++++ .../rocketchat-utils/lib/RoomTypeConfig.js | 270 ++++++++++++++++++ .../rocketchat-utils/lib/RoomTypesCommon.js | 93 ++++++ .../lib/fileUploadRestrictions.js | 37 +++ packages/rocketchat-utils/package.js | 1 + .../server/functions/isDocker.js | 31 ++ packages/rocketchat-utils/server/index.js | 14 + .../rocketchat-utils/server/lib/roomTypes.js | 50 ++++ 16 files changed, 661 insertions(+), 594 deletions(-) create mode 100644 packages/rocketchat-utils/client/lib/roomTypes.js create mode 100644 packages/rocketchat-utils/lib/RoomTypeConfig.js create mode 100644 packages/rocketchat-utils/lib/RoomTypesCommon.js create mode 100644 packages/rocketchat-utils/lib/fileUploadRestrictions.js create mode 100644 packages/rocketchat-utils/server/functions/isDocker.js create mode 100644 packages/rocketchat-utils/server/lib/roomTypes.js diff --git a/packages/rocketchat-lib/client/lib/roomTypes.js b/packages/rocketchat-lib/client/lib/roomTypes.js index 9201d9bba291..34dc06dc507f 100644 --- a/packages/rocketchat-lib/client/lib/roomTypes.js +++ b/packages/rocketchat-lib/client/lib/roomTypes.js @@ -1,132 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; -import _ from 'underscore'; +import { roomTypes } from 'meteor/rocketchat:utils'; -RocketChat.roomTypes = new class RocketChatRoomTypes extends RoomTypesCommon { - checkCondition(roomType) { - return roomType.condition == null || roomType.condition(); - } - getTypes() { - return _.sortBy(this.roomTypesOrder, 'order').map((type) => this.roomTypes[type.identifier]).filter((type) => !type.condition || type.condition()); - } - getIcon(roomType) { - return this.roomTypes[roomType] && this.roomTypes[roomType].icon; - } - getRoomName(roomType, roomData) { - return this.roomTypes[roomType] && this.roomTypes[roomType].roomName && this.roomTypes[roomType].roomName(roomData); - } - getSecondaryRoomName(roomType, roomData) { - return this.roomTypes[roomType] && typeof this.roomTypes[roomType].secondaryRoomName === 'function' && this.roomTypes[roomType].secondaryRoomName(roomData); - } - getIdentifiers(e) { - const except = [].concat(e); - const list = _.reject(this.roomTypesOrder, (t) => except.indexOf(t.identifier) !== -1); - return _.map(list, (t) => t.identifier); - } - getUserStatus(roomType, roomId) { - return this.roomTypes[roomType] && typeof this.roomTypes[roomType].getUserStatus === 'function' && this.roomTypes[roomType].getUserStatus(roomId); - } - findRoom(roomType, identifier, user) { - return this.roomTypes[roomType] && this.roomTypes[roomType].findRoom(identifier, user); - } - canSendMessage(roomId) { - return ChatSubscription.find({ - rid: roomId, - }).count() > 0; - } - readOnly(roomId, user) { - const fields = { - ro: 1, - }; - if (user) { - fields.muted = 1; - } - const room = ChatRoom.findOne({ - _id: roomId, - }, { - fields, - }); - if (!user) { - return room && room.ro; - } - const userOwner = RoomRoles.findOne({ - rid: roomId, - 'u._id': user._id, - roles: 'owner', - }, { - fields: { - _id: 1, - }, - }); - return room && (room.ro === true && Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1 && !userOwner); - } - archived(roomId) { - const fields = { - archived: 1, - }; - const room = ChatRoom.findOne({ - _id: roomId, - }, { - fields, - }); - return room && room.archived === true; - } - verifyCanSendMessage(roomId) { - const room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } }); - - if (!room || !room.t) { - return; - } - - const roomType = room.t; - if (this.roomTypes[roomType] && this.roomTypes[roomType].canSendMessage) { - return this.roomTypes[roomType].canSendMessage(roomId); - } - return this.canSendMessage(roomId); - } - verifyShowJoinLink(roomId) { - const room = ChatRoom.findOne({ - _id: roomId, - }, { - fields: { - t: 1, - }, - }); - if (!room || !room.t) { - return; - } - const roomType = room.t; - if (this.roomTypes[roomType] && !this.roomTypes[roomType].showJoinLink) { - return false; - } - return this.roomTypes[roomType].showJoinLink(roomId); - } - getNotSubscribedTpl(roomId) { - const room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } }); - if (!room || !room.t) { - return; - } - const roomType = room.t; - if (this.roomTypes[roomType] && !this.roomTypes[roomType].notSubscribedTpl) { - return false; - } - return this.roomTypes[roomType].notSubscribedTpl; - } - - openRouteLink(roomType, subData, queryParams) { - if (!this.roomTypes[roomType]) { - return false; - } - - let routeData = {}; - if (this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link) { - routeData = this.roomTypes[roomType].route.link(subData); - } else if (subData && subData.name) { - routeData = { - name: subData.name, - }; - } - - return FlowRouter.go(this.roomTypes[roomType].route.name, routeData, queryParams); - } -}; +RocketChat.roomTypes = roomTypes; diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index 2a16e5247384..c7c6cd384166 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -1,259 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; +import { RoomSettingsEnum, UiTextContext, RoomTypeRouteConfig, RoomTypeConfig } from 'meteor/rocketchat:utils'; -export const RoomSettingsEnum = { - NAME: 'roomName', - TOPIC: 'roomTopic', - ANNOUNCEMENT: 'roomAnnouncement', - DESCRIPTION: 'roomDescription', - READ_ONLY: 'readOnly', - REACT_WHEN_READ_ONLY: 'reactWhenReadOnly', - ARCHIVE_OR_UNARCHIVE: 'archiveOrUnarchive', - JOIN_CODE: 'joinCode', - BROADCAST: 'broadcast', - SYSTEM_MESSAGES: 'systemMessages', - E2E: 'encrypted', +export { + RoomSettingsEnum, + UiTextContext, + RoomTypeRouteConfig, + RoomTypeConfig, }; - -export const UiTextContext = { - CLOSE_WARNING: 'closeWarning', - HIDE_WARNING: 'hideWarning', - LEAVE_WARNING: 'leaveWarning', - NO_ROOMS_SUBSCRIBED: 'noRoomsSubscribed', -}; - -export class RoomTypeRouteConfig { - constructor({ name, path }) { - if (typeof name !== 'undefined' && (typeof name !== 'string' || name.length === 0)) { - throw new Error('The name must be a string.'); - } - - if (typeof path !== 'undefined' && (typeof path !== 'string' || path.length === 0)) { - throw new Error('The path must be a string.'); - } - - this._name = name; - this._path = path; - } - - get name() { - return this._name; - } - - get path() { - return this._path; - } -} - -export class RoomTypeConfig { - constructor({ - identifier = Random.id(), - order, - icon, - header, - label, - route, - }) { - if (typeof identifier !== 'string' || identifier.length === 0) { - throw new Error('The identifier must be a string.'); - } - - if (typeof order !== 'number') { - throw new Error('The order must be a number.'); - } - - if (typeof icon !== 'undefined' && (typeof icon !== 'string' || icon.length === 0)) { - throw new Error('The icon must be a string.'); - } - - if (typeof header !== 'undefined' && (typeof header !== 'string' || header.length === 0)) { - throw new Error('The header must be a string.'); - } - - if (typeof label !== 'undefined' && (typeof label !== 'string' || label.length === 0)) { - throw new Error('The label must be a string.'); - } - - if (typeof route !== 'undefined' && !(route instanceof RoomTypeRouteConfig)) { - throw new Error('Room\'s route is not a valid route configuration. Must be an instance of "RoomTypeRouteConfig".'); - } - - this._identifier = identifier; - this._order = order; - this._icon = icon; - this._header = header; - this._label = label; - this._route = route; - } - - /** - * The room type's internal identifier. - */ - get identifier() { - return this._identifier; - } - - /** - * The order of this room type for the display. - */ - get order() { - return this._order; - } - - /** - * Sets the order of this room type for the display. - * - * @param {number} order the number value for the order - */ - set order(order) { - if (typeof order !== 'number') { - throw new Error('The order must be a number.'); - } - - this._order = order; - } - - /** - * The icon class, css, to use as the visual aid. - */ - get icon() { - return this._icon; - } - - /** - * The header name of this type. - */ - get header() { - return this._header; - } - - /** - * The i18n label for this room type. - */ - get label() { - return this._label; - } - - /** - * The route config for this room type. - */ - get route() { - return this._route; - } - - /** - * Gets the room's name to display in the UI. - * - * @param {object} room - */ - getDisplayName(room) { - return room.name; - } - - allowRoomSettingChange(/* room, setting */) { - return true; - } - - /** - * Return a room's name - * - * @abstract - * @return {string} Room's name according to it's type - */ - roomName(/* room */) { - return ''; - } - - canBeCreated() { - return Meteor.isServer ? - RocketChat.authz.hasPermission(Meteor.userId(), `create-${ this._identifier }`) : - RocketChat.authz.hasPermission([`create-${ this._identifier }`]); - } - - canBeDeleted(room) { - return Meteor.isServer ? - RocketChat.authz.hasPermission(Meteor.userId(), `delete-${ room.t }`, room._id) : - RocketChat.authz.hasPermission(`delete-${ room.t }`, room._id); - } - - supportMembersList(/* room */) { - return true; - } - - isGroupChat() { - return false; - } - - canAddUser(/* userId, room */) { - return false; - } - - userDetailShowAll(/* room */) { - return true; - } - - userDetailShowAdmin(/* room */) { - return true; - } - - preventRenaming(/* room */) { - return false; - } - - includeInRoomSearch() { - return false; - } - - enableMembersListProfile() { - return false; - } - - /** - * Returns a text which can be used in generic UIs. - * @param context The role of the text in the UI-Element - * @return {string} A text or a translation key - the consumers of this method will pass the - * returned value to an internationalization library - */ - getUiText(/* context */) { - return ''; - } - - /** - * Returns the full object of message sender - * @param {string} senderId Sender's _id - * @return {object} Sender's object from db - */ - getMsgSender(senderId) { - return Meteor.isServer ? RocketChat.models.Users.findOneById(senderId) : {}; - } - - /** - * Returns details to use on notifications - * - * @param {object} room - * @param {object} user - * @param {string} notificationMessage - * @return {object} Notification details - */ - getNotificationDetails(room, user, notificationMessage) { - if (!Meteor.isServer) { - return {}; - } - - const title = `#${ this.roomName(room) }`; - - const text = `${ RocketChat.settings.get('UI_Use_Real_Name') ? user.name : user.username }: ${ notificationMessage }`; - - return { title, text }; - } - - /** - * Check if there is an user with the same id and loginToken - * @param {object} allowData - * @return {object} User's object from db - */ - canAccessUploadedFile(/* accessData */) { - return false; - } - -} diff --git a/packages/rocketchat-lib/lib/RoomTypesCommon.js b/packages/rocketchat-lib/lib/RoomTypesCommon.js index ee17d5bbb858..117dcf607bbc 100644 --- a/packages/rocketchat-lib/lib/RoomTypesCommon.js +++ b/packages/rocketchat-lib/lib/RoomTypesCommon.js @@ -1,93 +1,5 @@ -import { Meteor } from 'meteor/meteor'; -import { RoomTypeConfig } from './RoomTypeConfig'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import { RoomTypesCommon } from 'meteor/rocketchat:utils'; -export class RoomTypesCommon { - constructor() { - this.roomTypes = {}; - this.roomTypesOrder = []; - this.mainOrder = 1; - } - - /** - * Adds a room type to the application. - * - * @param {RoomTypeConfig} roomConfig - * @returns {void} - */ - add(roomConfig) { - if (!(roomConfig instanceof RoomTypeConfig)) { - throw new Error('Invalid Room Configuration object, it must extend "RoomTypeConfig"'); - } - - if (this.roomTypes[roomConfig.identifier]) { - return false; - } - - if (!roomConfig.order) { - roomConfig.order = this.mainOrder + 10; - this.mainOrder += 10; - } - - this.roomTypesOrder.push({ - identifier: roomConfig.identifier, - order: roomConfig.order, - }); - - this.roomTypes[roomConfig.identifier] = roomConfig; - - if (roomConfig.route && roomConfig.route.path && roomConfig.route.name && roomConfig.route.action) { - const routeConfig = { - name: roomConfig.route.name, - action: roomConfig.route.action, - }; - - if (Meteor.isClient) { - routeConfig.triggersExit = [roomExit]; - } - - return FlowRouter.route(roomConfig.route.path, routeConfig); - } - } - - hasCustomLink(roomType) { - return this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link != null; - } - - /** - * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) - * @param {object} subData the user's subscription data - */ - getRouteLink(roomType, subData) { - if (!this.roomTypes[roomType]) { - return false; - } - - let routeData = {}; - if (this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link) { - routeData = this.roomTypes[roomType].route.link(subData); - } else if (subData && subData.name) { - routeData = { - name: subData.name, - }; - } - - return FlowRouter.path(this.roomTypes[roomType].route.name, routeData); - } - - /** - * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) - * @param {RoomTypeConfig} roomConfig room's type configuration - */ - getConfig(roomType) { - return this.roomTypes[roomType]; - } - - getURL(...args) { - const path = this.getRouteLink(...args); - if (!path) { - return false; - } - return Meteor.absoluteUrl(path.replace(/^\//, '')); - } -} +export { + RoomTypesCommon, +}; diff --git a/packages/rocketchat-lib/lib/fileUploadRestrictions.js b/packages/rocketchat-lib/lib/fileUploadRestrictions.js index 763d11ca6916..e8051c803dd1 100644 --- a/packages/rocketchat-lib/lib/fileUploadRestrictions.js +++ b/packages/rocketchat-lib/lib/fileUploadRestrictions.js @@ -1,36 +1,4 @@ -import _ from 'underscore'; +import { fileUploadIsValidContentType, fileUploadMediaWhiteList } from 'meteor/rocketchat:utils'; -RocketChat.fileUploadMediaWhiteList = function() { - const mediaTypeWhiteList = RocketChat.settings.get('FileUpload_MediaTypeWhiteList'); - - if (!mediaTypeWhiteList || mediaTypeWhiteList === '*') { - return; - } - return _.map(mediaTypeWhiteList.split(','), function(item) { - return item.trim(); - }); -}; - -RocketChat.fileUploadIsValidContentType = function(type) { - const list = RocketChat.fileUploadMediaWhiteList(); - if (!list) { - return true; - } - - if (!type) { - return false; - } - - if (_.contains(list, type)) { - return true; - } else { - const wildCardGlob = '/*'; - const wildcards = _.filter(list, function(item) { - return item.indexOf(wildCardGlob) > 0; - }); - if (_.contains(wildcards, type.replace(/(\/.*)$/, wildCardGlob))) { - return true; - } - } - return false; -}; +RocketChat.fileUploadMediaWhiteList = fileUploadMediaWhiteList; +RocketChat.fileUploadIsValidContentType = fileUploadIsValidContentType; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index b72a59ab60b6..46b74340be48 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -45,6 +45,7 @@ Package.onUse(function(api) { api.use('rocketchat:custom-oauth'); api.use('konecty:multiple-instances-status'); api.use('rocketchat:file'); + api.use('rocketchat:file-upload'); api.use('rocketchat:push'); api.use('rocketchat:push-notifications', { unordered: true }); diff --git a/packages/rocketchat-lib/server/functions/isDocker.js b/packages/rocketchat-lib/server/functions/isDocker.js index 3ec908de64e9..eac079083cc7 100644 --- a/packages/rocketchat-lib/server/functions/isDocker.js +++ b/packages/rocketchat-lib/server/functions/isDocker.js @@ -1,31 +1,3 @@ -import fs from 'fs'; +import { isDocker } from 'meteor/rocketchat:utils'; -function hasDockerEnv() { - try { - fs.statSync('/.dockerenv'); - return true; - } catch (err) { - return false; - } -} - -function hasDockerCGroup() { - try { - return fs.readFileSync('/proc/self/cgroup', 'utf8').indexOf('docker') !== -1; - } catch (err) { - return false; - } -} - -function check() { - return hasDockerEnv() || hasDockerCGroup(); -} - -let isDocker; -RocketChat.isDocker = function() { - if (isDocker === undefined) { - isDocker = check(); - } - - return isDocker; -}; +RocketChat.isDocker = isDocker; diff --git a/packages/rocketchat-lib/server/lib/roomTypes.js b/packages/rocketchat-lib/server/lib/roomTypes.js index a1cdabe20977..34dc06dc507f 100644 --- a/packages/rocketchat-lib/server/lib/roomTypes.js +++ b/packages/rocketchat-lib/server/lib/roomTypes.js @@ -1,50 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; +import { roomTypes } from 'meteor/rocketchat:utils'; -RocketChat.roomTypes = new class roomTypesServer extends RoomTypesCommon { - /** - * Add a publish for a room type - * - * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) - * @param {function} callback function that will return the publish's data - */ - setPublish(roomType, callback) { - if (this.roomTypes[roomType] && this.roomTypes[roomType].publish != null) { - throw new Meteor.Error('route-publish-exists', 'Publish for the given type already exists'); - } - if (this.roomTypes[roomType] == null) { - this.roomTypes[roomType] = {}; - } - return this.roomTypes[roomType].publish = callback; - } - - setRoomFind(roomType, callback) { - if (this.roomTypes[roomType] && this.roomTypes[roomType].roomFind != null) { - throw new Meteor.Error('room-find-exists', 'Room find for the given type already exists'); - } - if (this.roomTypes[roomType] == null) { - this.roomTypes[roomType] = {}; - } - return this.roomTypes[roomType].roomFind = callback; - } - - getRoomFind(roomType) { - return this.roomTypes[roomType] && this.roomTypes[roomType].roomFind; - } - - getRoomName(roomType, roomData) { - return this.roomTypes[roomType] && this.roomTypes[roomType].roomName && this.roomTypes[roomType].roomName(roomData); - } - - /** - * Run the publish for a room type - * - * @param scope Meteor publish scope - * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) - * @param identifier identifier of the room - */ - runPublish(scope, roomType, identifier) { - return this.roomTypes[roomType] && this.roomTypes[roomType].publish && this.roomTypes[roomType].publish.call(scope, identifier); - } - -}; +RocketChat.roomTypes = roomTypes; diff --git a/packages/rocketchat-utils/client/index.js b/packages/rocketchat-utils/client/index.js index 4a27a89cbea5..ec51e35371ac 100644 --- a/packages/rocketchat-utils/client/index.js +++ b/packages/rocketchat-utils/client/index.js @@ -4,6 +4,10 @@ import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; import { Info } from '../rocketchat.info'; import { handleError } from './lib/handleError'; import { getUserPreference } from '../lib/getUserPreference'; +import { fileUploadMediaWhiteList, fileUploadIsValidContentType } from '../lib/fileUploadRestrictions'; +import { roomTypes } from './lib/roomTypes'; +import { RoomTypeRouteConfig, RoomTypeConfig, RoomSettingsEnum, UiTextContext } from '../lib/RoomTypeConfig'; +import { RoomTypesCommon } from '../lib/RoomTypesCommon'; export { t, @@ -14,4 +18,12 @@ export { Info, handleError, getUserPreference, + fileUploadIsValidContentType, + fileUploadMediaWhiteList, + roomTypes, + RoomTypeRouteConfig, + RoomTypesCommon, + RoomTypeConfig, + RoomSettingsEnum, + UiTextContext, }; diff --git a/packages/rocketchat-utils/client/lib/roomTypes.js b/packages/rocketchat-utils/client/lib/roomTypes.js new file mode 100644 index 000000000000..596da523f36d --- /dev/null +++ b/packages/rocketchat-utils/client/lib/roomTypes.js @@ -0,0 +1,133 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; +import { ChatRoom, ChatSubscription, RoomRoles } from 'meteor/rocketchat:models'; +import _ from 'underscore'; + +export const roomTypes = new class RocketChatRoomTypes extends RoomTypesCommon { + checkCondition(roomType) { + return roomType.condition == null || roomType.condition(); + } + getTypes() { + return _.sortBy(this.roomTypesOrder, 'order').map((type) => this.roomTypes[type.identifier]).filter((type) => !type.condition || type.condition()); + } + getIcon(roomType) { + return this.roomTypes[roomType] && this.roomTypes[roomType].icon; + } + getRoomName(roomType, roomData) { + return this.roomTypes[roomType] && this.roomTypes[roomType].roomName && this.roomTypes[roomType].roomName(roomData); + } + getSecondaryRoomName(roomType, roomData) { + return this.roomTypes[roomType] && typeof this.roomTypes[roomType].secondaryRoomName === 'function' && this.roomTypes[roomType].secondaryRoomName(roomData); + } + getIdentifiers(e) { + const except = [].concat(e); + const list = _.reject(this.roomTypesOrder, (t) => except.indexOf(t.identifier) !== -1); + return _.map(list, (t) => t.identifier); + } + getUserStatus(roomType, roomId) { + return this.roomTypes[roomType] && typeof this.roomTypes[roomType].getUserStatus === 'function' && this.roomTypes[roomType].getUserStatus(roomId); + } + findRoom(roomType, identifier, user) { + return this.roomTypes[roomType] && this.roomTypes[roomType].findRoom(identifier, user); + } + canSendMessage(roomId) { + return ChatSubscription.find({ + rid: roomId, + }).count() > 0; + } + readOnly(roomId, user) { + const fields = { + ro: 1, + }; + if (user) { + fields.muted = 1; + } + const room = ChatRoom.findOne({ + _id: roomId, + }, { + fields, + }); + if (!user) { + return room && room.ro; + } + const userOwner = RoomRoles.findOne({ + rid: roomId, + 'u._id': user._id, + roles: 'owner', + }, { + fields: { + _id: 1, + }, + }); + return room && (room.ro === true && Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1 && !userOwner); + } + archived(roomId) { + const fields = { + archived: 1, + }; + const room = ChatRoom.findOne({ + _id: roomId, + }, { + fields, + }); + return room && room.archived === true; + } + verifyCanSendMessage(roomId) { + const room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } }); + + if (!room || !room.t) { + return; + } + + const roomType = room.t; + if (this.roomTypes[roomType] && this.roomTypes[roomType].canSendMessage) { + return this.roomTypes[roomType].canSendMessage(roomId); + } + return this.canSendMessage(roomId); + } + verifyShowJoinLink(roomId) { + const room = ChatRoom.findOne({ + _id: roomId, + }, { + fields: { + t: 1, + }, + }); + if (!room || !room.t) { + return; + } + const roomType = room.t; + if (this.roomTypes[roomType] && !this.roomTypes[roomType].showJoinLink) { + return false; + } + return this.roomTypes[roomType].showJoinLink(roomId); + } + getNotSubscribedTpl(roomId) { + const room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } }); + if (!room || !room.t) { + return; + } + const roomType = room.t; + if (this.roomTypes[roomType] && !this.roomTypes[roomType].notSubscribedTpl) { + return false; + } + return this.roomTypes[roomType].notSubscribedTpl; + } + + openRouteLink(roomType, subData, queryParams) { + if (!this.roomTypes[roomType]) { + return false; + } + + let routeData = {}; + if (this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link) { + routeData = this.roomTypes[roomType].route.link(subData); + } else if (subData && subData.name) { + routeData = { + name: subData.name, + }; + } + + return FlowRouter.go(this.roomTypes[roomType].route.name, routeData, queryParams); + } +}; diff --git a/packages/rocketchat-utils/lib/RoomTypeConfig.js b/packages/rocketchat-utils/lib/RoomTypeConfig.js new file mode 100644 index 000000000000..e02f4997ef73 --- /dev/null +++ b/packages/rocketchat-utils/lib/RoomTypeConfig.js @@ -0,0 +1,270 @@ +import { Meteor } from 'meteor/meteor'; +import { Random } from 'meteor/random'; +import { Users } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; + +export const RoomSettingsEnum = { + NAME: 'roomName', + TOPIC: 'roomTopic', + ANNOUNCEMENT: 'roomAnnouncement', + DESCRIPTION: 'roomDescription', + READ_ONLY: 'readOnly', + REACT_WHEN_READ_ONLY: 'reactWhenReadOnly', + ARCHIVE_OR_UNARCHIVE: 'archiveOrUnarchive', + JOIN_CODE: 'joinCode', + BROADCAST: 'broadcast', + SYSTEM_MESSAGES: 'systemMessages', + E2E: 'encrypted', +}; + +export const UiTextContext = { + CLOSE_WARNING: 'closeWarning', + HIDE_WARNING: 'hideWarning', + LEAVE_WARNING: 'leaveWarning', + NO_ROOMS_SUBSCRIBED: 'noRoomsSubscribed', +}; + +export class RoomTypeRouteConfig { + constructor({ name, path }) { + if (typeof name !== 'undefined' && (typeof name !== 'string' || name.length === 0)) { + throw new Error('The name must be a string.'); + } + + if (typeof path !== 'undefined' && (typeof path !== 'string' || path.length === 0)) { + throw new Error('The path must be a string.'); + } + + this._name = name; + this._path = path; + } + + get name() { + return this._name; + } + + get path() { + return this._path; + } +} + +export class RoomTypeConfig { + constructor({ + identifier = Random.id(), + order, + icon, + header, + label, + route, + }) { + if (typeof identifier !== 'string' || identifier.length === 0) { + throw new Error('The identifier must be a string.'); + } + + if (typeof order !== 'number') { + throw new Error('The order must be a number.'); + } + + if (typeof icon !== 'undefined' && (typeof icon !== 'string' || icon.length === 0)) { + throw new Error('The icon must be a string.'); + } + + if (typeof header !== 'undefined' && (typeof header !== 'string' || header.length === 0)) { + throw new Error('The header must be a string.'); + } + + if (typeof label !== 'undefined' && (typeof label !== 'string' || label.length === 0)) { + throw new Error('The label must be a string.'); + } + + if (typeof route !== 'undefined' && !(route instanceof RoomTypeRouteConfig)) { + throw new Error('Room\'s route is not a valid route configuration. Must be an instance of "RoomTypeRouteConfig".'); + } + + this._identifier = identifier; + this._order = order; + this._icon = icon; + this._header = header; + this._label = label; + this._route = route; + } + + /** + * The room type's internal identifier. + */ + get identifier() { + return this._identifier; + } + + /** + * The order of this room type for the display. + */ + get order() { + return this._order; + } + + /** + * Sets the order of this room type for the display. + * + * @param {number} order the number value for the order + */ + set order(order) { + if (typeof order !== 'number') { + throw new Error('The order must be a number.'); + } + + this._order = order; + } + + /** + * The icon class, css, to use as the visual aid. + */ + get icon() { + return this._icon; + } + + /** + * The header name of this type. + */ + get header() { + return this._header; + } + + /** + * The i18n label for this room type. + */ + get label() { + return this._label; + } + + /** + * The route config for this room type. + */ + get route() { + return this._route; + } + + /** + * Gets the room's name to display in the UI. + * + * @param {object} room + */ + getDisplayName(room) { + return room.name; + } + + allowRoomSettingChange(/* room, setting */) { + return true; + } + + /** + * Return a room's name + * + * @abstract + * @return {string} Room's name according to it's type + */ + roomName(/* room */) { + return ''; + } + + async loadHasPermission() { + if (!this.hasPermission) { + const { hasPermission } = await import('meteor/rocketchat:authorization'); + this.hasPermission = hasPermission; + } + } + + async canBeCreated() { + await this.loadHasPermission(); + return Meteor.isServer ? + this.hasPermission(Meteor.userId(), `create-${ this._identifier }`) : + this.hasPermission([`create-${ this._identifier }`]); + } + + async canBeDeleted(room) { + await this.loadHasPermission(); + return Meteor.isServer ? + this.hasPermission(Meteor.userId(), `delete-${ room.t }`, room._id) : + this.hasPermission(`delete-${ room.t }`, room._id); + } + + supportMembersList(/* room */) { + return true; + } + + isGroupChat() { + return false; + } + + canAddUser(/* userId, room */) { + return false; + } + + userDetailShowAll(/* room */) { + return true; + } + + userDetailShowAdmin(/* room */) { + return true; + } + + preventRenaming(/* room */) { + return false; + } + + includeInRoomSearch() { + return false; + } + + enableMembersListProfile() { + return false; + } + + /** + * Returns a text which can be used in generic UIs. + * @param context The role of the text in the UI-Element + * @return {string} A text or a translation key - the consumers of this method will pass the + * returned value to an internationalization library + */ + getUiText(/* context */) { + return ''; + } + + /** + * Returns the full object of message sender + * @param {string} senderId Sender's _id + * @return {object} Sender's object from db + */ + getMsgSender(senderId) { + return Meteor.isServer ? Users.findOneById(senderId) : {}; + } + + /** + * Returns details to use on notifications + * + * @param {object} room + * @param {object} user + * @param {string} notificationMessage + * @return {object} Notification details + */ + getNotificationDetails(room, user, notificationMessage) { + if (!Meteor.isServer) { + return {}; + } + + const title = `#${ this.roomName(room) }`; + + const text = `${ settings.get('UI_Use_Real_Name') ? user.name : user.username }: ${ notificationMessage }`; + + return { title, text }; + } + + /** + * Check if there is an user with the same id and loginToken + * @param {object} allowData + * @return {object} User's object from db + */ + canAccessUploadedFile(/* accessData */) { + return false; + } + +} diff --git a/packages/rocketchat-utils/lib/RoomTypesCommon.js b/packages/rocketchat-utils/lib/RoomTypesCommon.js new file mode 100644 index 000000000000..ee17d5bbb858 --- /dev/null +++ b/packages/rocketchat-utils/lib/RoomTypesCommon.js @@ -0,0 +1,93 @@ +import { Meteor } from 'meteor/meteor'; +import { RoomTypeConfig } from './RoomTypeConfig'; +import { FlowRouter } from 'meteor/kadira:flow-router'; + +export class RoomTypesCommon { + constructor() { + this.roomTypes = {}; + this.roomTypesOrder = []; + this.mainOrder = 1; + } + + /** + * Adds a room type to the application. + * + * @param {RoomTypeConfig} roomConfig + * @returns {void} + */ + add(roomConfig) { + if (!(roomConfig instanceof RoomTypeConfig)) { + throw new Error('Invalid Room Configuration object, it must extend "RoomTypeConfig"'); + } + + if (this.roomTypes[roomConfig.identifier]) { + return false; + } + + if (!roomConfig.order) { + roomConfig.order = this.mainOrder + 10; + this.mainOrder += 10; + } + + this.roomTypesOrder.push({ + identifier: roomConfig.identifier, + order: roomConfig.order, + }); + + this.roomTypes[roomConfig.identifier] = roomConfig; + + if (roomConfig.route && roomConfig.route.path && roomConfig.route.name && roomConfig.route.action) { + const routeConfig = { + name: roomConfig.route.name, + action: roomConfig.route.action, + }; + + if (Meteor.isClient) { + routeConfig.triggersExit = [roomExit]; + } + + return FlowRouter.route(roomConfig.route.path, routeConfig); + } + } + + hasCustomLink(roomType) { + return this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link != null; + } + + /** + * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) + * @param {object} subData the user's subscription data + */ + getRouteLink(roomType, subData) { + if (!this.roomTypes[roomType]) { + return false; + } + + let routeData = {}; + if (this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link) { + routeData = this.roomTypes[roomType].route.link(subData); + } else if (subData && subData.name) { + routeData = { + name: subData.name, + }; + } + + return FlowRouter.path(this.roomTypes[roomType].route.name, routeData); + } + + /** + * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) + * @param {RoomTypeConfig} roomConfig room's type configuration + */ + getConfig(roomType) { + return this.roomTypes[roomType]; + } + + getURL(...args) { + const path = this.getRouteLink(...args); + if (!path) { + return false; + } + return Meteor.absoluteUrl(path.replace(/^\//, '')); + } +} diff --git a/packages/rocketchat-utils/lib/fileUploadRestrictions.js b/packages/rocketchat-utils/lib/fileUploadRestrictions.js new file mode 100644 index 000000000000..e37dc477ab77 --- /dev/null +++ b/packages/rocketchat-utils/lib/fileUploadRestrictions.js @@ -0,0 +1,37 @@ +import { settings } from 'meteor/rocketchat:settings'; +import _ from 'underscore'; + +export const fileUploadMediaWhiteList = function() { + const mediaTypeWhiteList = settings.get('FileUpload_MediaTypeWhiteList'); + + if (!mediaTypeWhiteList || mediaTypeWhiteList === '*') { + return; + } + return _.map(mediaTypeWhiteList.split(','), function(item) { + return item.trim(); + }); +}; + +export const fileUploadIsValidContentType = function(type) { + const list = fileUploadMediaWhiteList(); + if (!list) { + return true; + } + + if (!type) { + return false; + } + + if (_.contains(list, type)) { + return true; + } else { + const wildCardGlob = '/*'; + const wildcards = _.filter(list, function(item) { + return item.indexOf(wildCardGlob) > 0; + }); + if (_.contains(wildcards, type.replace(/(\/.*)$/, wildCardGlob))) { + return true; + } + } + return false; +}; diff --git a/packages/rocketchat-utils/package.js b/packages/rocketchat-utils/package.js index 59c5294f73fe..71c46c9a83c1 100644 --- a/packages/rocketchat-utils/package.js +++ b/packages/rocketchat-utils/package.js @@ -8,6 +8,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'tap:i18n', + 'kadira:flow-router', 'rocketchat:version', 'rocketchat:models', 'rocketchat:settings', diff --git a/packages/rocketchat-utils/server/functions/isDocker.js b/packages/rocketchat-utils/server/functions/isDocker.js new file mode 100644 index 000000000000..72da46c71f89 --- /dev/null +++ b/packages/rocketchat-utils/server/functions/isDocker.js @@ -0,0 +1,31 @@ +import fs from 'fs'; + +function hasDockerEnv() { + try { + fs.statSync('/.dockerenv'); + return true; + } catch (err) { + return false; + } +} + +function hasDockerCGroup() { + try { + return fs.readFileSync('/proc/self/cgroup', 'utf8').indexOf('docker') !== -1; + } catch (err) { + return false; + } +} + +function check() { + return hasDockerEnv() || hasDockerCGroup(); +} + +let _isDocker; +export const isDocker = function() { + if (_isDocker === undefined) { + _isDocker = check(); + } + + return _isDocker; +}; diff --git a/packages/rocketchat-utils/server/index.js b/packages/rocketchat-utils/server/index.js index d95324c1f3e9..43275f627d92 100644 --- a/packages/rocketchat-utils/server/index.js +++ b/packages/rocketchat-utils/server/index.js @@ -2,6 +2,11 @@ import { t, isRtl } from '../lib/tapi18n'; import { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; import { Info } from '../rocketchat.info'; import { getUserPreference } from '../lib/getUserPreference'; +import { fileUploadMediaWhiteList, fileUploadIsValidContentType } from '../lib/fileUploadRestrictions'; +import { roomTypes } from './lib/roomTypes'; +import { RoomTypeRouteConfig, RoomTypeConfig, RoomSettingsEnum, UiTextContext } from '../lib/RoomTypeConfig'; +import { RoomTypesCommon } from '../lib/RoomTypesCommon'; +import { isDocker } from './functions/isDocker'; export { t, @@ -9,4 +14,13 @@ export { getDefaultSubscriptionPref, Info, getUserPreference, + fileUploadIsValidContentType, + fileUploadMediaWhiteList, + roomTypes, + RoomTypeRouteConfig, + RoomTypesCommon, + RoomTypeConfig, + RoomSettingsEnum, + UiTextContext, + isDocker, }; diff --git a/packages/rocketchat-utils/server/lib/roomTypes.js b/packages/rocketchat-utils/server/lib/roomTypes.js new file mode 100644 index 000000000000..3e8c5a13d614 --- /dev/null +++ b/packages/rocketchat-utils/server/lib/roomTypes.js @@ -0,0 +1,50 @@ +import { Meteor } from 'meteor/meteor'; +import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; + +export const roomTypes = new class roomTypesServer extends RoomTypesCommon { + /** + * Add a publish for a room type + * + * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) + * @param {function} callback function that will return the publish's data + */ + setPublish(roomType, callback) { + if (this.roomTypes[roomType] && this.roomTypes[roomType].publish != null) { + throw new Meteor.Error('route-publish-exists', 'Publish for the given type already exists'); + } + if (this.roomTypes[roomType] == null) { + this.roomTypes[roomType] = {}; + } + return this.roomTypes[roomType].publish = callback; + } + + setRoomFind(roomType, callback) { + if (this.roomTypes[roomType] && this.roomTypes[roomType].roomFind != null) { + throw new Meteor.Error('room-find-exists', 'Room find for the given type already exists'); + } + if (this.roomTypes[roomType] == null) { + this.roomTypes[roomType] = {}; + } + return this.roomTypes[roomType].roomFind = callback; + } + + getRoomFind(roomType) { + return this.roomTypes[roomType] && this.roomTypes[roomType].roomFind; + } + + getRoomName(roomType, roomData) { + return this.roomTypes[roomType] && this.roomTypes[roomType].roomName && this.roomTypes[roomType].roomName(roomData); + } + + /** + * Run the publish for a room type + * + * @param scope Meteor publish scope + * @param {string} roomType room type (e.g.: c (for channels), d (for direct channels)) + * @param identifier identifier of the room + */ + runPublish(scope, roomType, identifier) { + return this.roomTypes[roomType] && this.roomTypes[roomType].publish && this.roomTypes[roomType].publish.call(scope, identifier); + } + +}; From 252ad4c99aafb5a322e7eecf2bc92ba629d295db Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 13:07:42 -0200 Subject: [PATCH 24/59] Add functions to settings package --- .../server/functions/settings.js | 330 ++++++++++++++++++ packages/rocketchat-settings/server/index.js | 2 +- 2 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 packages/rocketchat-settings/server/functions/settings.js diff --git a/packages/rocketchat-settings/server/functions/settings.js b/packages/rocketchat-settings/server/functions/settings.js new file mode 100644 index 000000000000..408e31c4450f --- /dev/null +++ b/packages/rocketchat-settings/server/functions/settings.js @@ -0,0 +1,330 @@ +import { Meteor } from 'meteor/meteor'; +import { settings } from '../../lib/settings'; +import _ from 'underscore'; + +let _Settings; + +const blockedSettings = {}; + +if (process.env.SETTINGS_BLOCKED) { + process.env.SETTINGS_BLOCKED.split(',').forEach((settingId) => blockedSettings[settingId] = 1); +} + +const hiddenSettings = {}; +if (process.env.SETTINGS_HIDDEN) { + process.env.SETTINGS_HIDDEN.split(',').forEach((settingId) => hiddenSettings[settingId] = 1); +} + +settings._sorter = {}; + + +/* +* Add a setting +* @param {String} _id +* @param {Mixed} value +* @param {Object} setting +*/ + +settings.add = async function(_id, value, options = {}) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (options == null) { + options = {}; + } + if (!_id || value == null) { + return false; + } + if (settings._sorter[options.group] == null) { + settings._sorter[options.group] = 0; + } + options.packageValue = value; + options.valueSource = 'packageValue'; + options.hidden = options.hidden || false; + options.blocked = options.blocked || false; + if (options.sorter == null) { + options.sorter = settings._sorter[options.group]++; + } + if (options.enableQuery != null) { + options.enableQuery = JSON.stringify(options.enableQuery); + } + if (options.i18nDefaultQuery != null) { + options.i18nDefaultQuery = JSON.stringify(options.i18nDefaultQuery); + } + if (typeof process !== 'undefined' && process.env && process.env[_id]) { + value = process.env[_id]; + if (value.toLowerCase() === 'true') { + value = true; + } else if (value.toLowerCase() === 'false') { + value = false; + } else if (options.type === 'int') { + value = parseInt(value); + } + options.processEnvValue = value; + options.valueSource = 'processEnvValue'; + } else if (Meteor.settings && typeof Meteor.settings[_id] !== 'undefined') { + if (Meteor.settings[_id] == null) { + return false; + } + + value = Meteor.settings[_id]; + options.meteorSettingsValue = value; + options.valueSource = 'meteorSettingsValue'; + } + if (options.i18nLabel == null) { + options.i18nLabel = _id; + } + if (options.i18nDescription == null) { + options.i18nDescription = `${ _id }_Description`; + } + if (blockedSettings[_id] != null) { + options.blocked = true; + } + if (hiddenSettings[_id] != null) { + options.hidden = true; + } + if (options.autocomplete == null) { + options.autocomplete = true; + } + if (typeof process !== 'undefined' && process.env && process.env[`OVERWRITE_SETTING_${ _id }`]) { + let value = process.env[`OVERWRITE_SETTING_${ _id }`]; + if (value.toLowerCase() === 'true') { + value = true; + } else if (value.toLowerCase() === 'false') { + value = false; + } else if (options.type === 'int') { + value = parseInt(value); + } + options.value = value; + options.processEnvValue = value; + options.valueSource = 'processEnvValue'; + } + const updateOperations = { + $set: options, + $setOnInsert: { + createdAt: new Date, + }, + }; + if (options.editor != null) { + updateOperations.$setOnInsert.editor = options.editor; + delete options.editor; + } + if (options.value == null) { + if (options.force === true) { + updateOperations.$set.value = options.packageValue; + } else { + updateOperations.$setOnInsert.value = value; + } + } + const query = _.extend({ + _id, + }, updateOperations.$set); + if (options.section == null) { + updateOperations.$unset = { + section: 1, + }; + query.section = { + $exists: false, + }; + } + const existantSetting = _Settings.db.findOne(query); + if (existantSetting != null) { + if (existantSetting.editor == null && updateOperations.$setOnInsert.editor != null) { + updateOperations.$set.editor = updateOperations.$setOnInsert.editor; + delete updateOperations.$setOnInsert.editor; + } + } else { + updateOperations.$set.ts = new Date; + } + return _Settings.upsert({ + _id, + }, updateOperations); +}; + + +/* +* Add a setting group +* @param {String} _id +*/ + +settings.addGroup = async function(_id, options = {}, cb) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (!_id) { + return false; + } + if (_.isFunction(options)) { + cb = options; + options = {}; + } + if (options.i18nLabel == null) { + options.i18nLabel = _id; + } + if (options.i18nDescription == null) { + options.i18nDescription = `${ _id }_Description`; + } + options.ts = new Date; + options.blocked = false; + options.hidden = false; + if (blockedSettings[_id] != null) { + options.blocked = true; + } + if (hiddenSettings[_id] != null) { + options.hidden = true; + } + _Settings.upsert({ + _id, + }, { + $set: options, + $setOnInsert: { + type: 'group', + createdAt: new Date, + }, + }); + if (cb != null) { + cb.call({ + add(id, value, options) { + if (options == null) { + options = {}; + } + options.group = _id; + return settings.add(id, value, options); + }, + section(section, cb) { + return cb.call({ + add(id, value, options) { + if (options == null) { + options = {}; + } + options.group = _id; + options.section = section; + return settings.add(id, value, options); + }, + }); + }, + }); + } +}; + + +/* +* Remove a setting by id +* @param {String} _id +*/ + +settings.removeById = async function(_id) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (!_id) { + return false; + } + return _Settings.removeById(_id); +}; + + +/* +* Update a setting by id +* @param {String} _id +*/ + +settings.updateById = async function(_id, value, editor) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (!_id || value == null) { + return false; + } + if (editor != null) { + return _Settings.updateValueAndEditorById(_id, value, editor); + } + return _Settings.updateValueById(_id, value); +}; + + +/* +* Update options of a setting by id +* @param {String} _id +*/ + +settings.updateOptionsById = async function(_id, options) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (!_id || options == null) { + return false; + } + return _Settings.updateOptionsById(_id, options); +}; + + +/* +* Update a setting by id +* @param {String} _id +*/ + +settings.clearById = async function(_id) { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + if (_id == null) { + return false; + } + return _Settings.updateValueById(_id, undefined); +}; + + +/* +* Update a setting by id +*/ + +settings.init = async function() { + if (!_Settings) { + const { Settings } = await import('meteor/rocketchat:models'); + _Settings = Settings; + } + settings.initialLoad = true; + _Settings.find().observe({ + added(record) { + Meteor.settings[record._id] = record.value; + if (record.env === true) { + process.env[record._id] = record.value; + } + return settings.load(record._id, record.value, settings.initialLoad); + }, + changed(record) { + Meteor.settings[record._id] = record.value; + if (record.env === true) { + process.env[record._id] = record.value; + } + return settings.load(record._id, record.value, settings.initialLoad); + }, + removed(record) { + delete Meteor.settings[record._id]; + if (record.env === true) { + delete process.env[record._id]; + } + return settings.load(record._id, undefined, settings.initialLoad); + }, + }); + settings.initialLoad = false; + settings.afterInitialLoad.forEach((fn) => fn(Meteor.settings)); +}; + +settings.afterInitialLoad = []; + +settings.onAfterInitialLoad = function(fn) { + settings.afterInitialLoad.push(fn); + if (settings.initialLoad === false) { + return fn(Meteor.settings); + } +}; + +export { settings }; diff --git a/packages/rocketchat-settings/server/index.js b/packages/rocketchat-settings/server/index.js index 8f93e01fb4f4..3ddef2f62407 100644 --- a/packages/rocketchat-settings/server/index.js +++ b/packages/rocketchat-settings/server/index.js @@ -1,4 +1,4 @@ -import { settings } from '../lib/settings'; +import { settings } from './functions/settings'; export { settings, From 0d94009e92daed710ac37449ac2fd908a0b03167 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 13:36:35 -0200 Subject: [PATCH 25/59] Convert rocketchat:file-upload to main module structure --- .../rocketchat-file-upload/client/index.js | 5 ++ .../client/lib/fileUploadHandler.js | 8 ++- .../globalFileRestrictions.js | 6 +- .../rocketchat-file-upload/lib/FileUpload.js | 20 ++++--- .../lib/FileUploadBase.js | 8 ++- packages/rocketchat-file-upload/package.js | 57 +++++++------------ .../server/config/AmazonS3.js | 24 ++++---- .../server/config/FileSystem.js | 7 ++- .../server/config/GoogleStorage.js | 15 ++--- .../server/config/GridFS.js | 2 +- .../server/config/Slingshot_DEPRECATED.js | 42 +++++++------- .../server/config/Webdav.js | 13 +++-- .../server/config/_configUploadStorage.js | 5 +- .../rocketchat-file-upload/server/index.js | 11 ++++ .../server/lib/FileUpload.js | 44 +++++++------- .../server/lib/proxy.js | 3 +- .../server/lib/requests.js | 4 +- .../server/methods/getS3FileUrl.js | 6 +- .../server/methods/sendFileMessage.js | 7 ++- .../server/startup/settings.js | 4 +- server/startup/migrations/v099.js | 1 + 21 files changed, 163 insertions(+), 129 deletions(-) create mode 100644 packages/rocketchat-file-upload/client/index.js create mode 100644 packages/rocketchat-file-upload/server/index.js diff --git a/packages/rocketchat-file-upload/client/index.js b/packages/rocketchat-file-upload/client/index.js new file mode 100644 index 000000000000..039df2d85775 --- /dev/null +++ b/packages/rocketchat-file-upload/client/index.js @@ -0,0 +1,5 @@ +import { fileUploadHandler } from './lib/fileUploadHandler'; + +export { + fileUploadHandler, +}; diff --git a/packages/rocketchat-file-upload/client/lib/fileUploadHandler.js b/packages/rocketchat-file-upload/client/lib/fileUploadHandler.js index 534ebeef82bb..f431a969f04b 100644 --- a/packages/rocketchat-file-upload/client/lib/fileUploadHandler.js +++ b/packages/rocketchat-file-upload/client/lib/fileUploadHandler.js @@ -3,9 +3,11 @@ import { Accounts } from 'meteor/accounts-base'; import { Tracker } from 'meteor/tracker'; import { UploadFS } from 'meteor/jalik:ufs'; import { FileUploadBase } from '../../lib/FileUploadBase'; +import { Uploads, Avatars } from 'meteor/rocketchat:models'; +import { FileUpload } from '../../lib/FileUpload'; new UploadFS.Store({ - collection: RocketChat.models.Uploads.model, + collection: Uploads.model, name: 'Uploads', filter: new UploadFS.Filter({ onCheck: FileUpload.validateFileUpload, @@ -13,14 +15,14 @@ new UploadFS.Store({ }); new UploadFS.Store({ - collection: RocketChat.models.Avatars.model, + collection: Avatars.model, name: 'Avatars', filter: new UploadFS.Filter({ onCheck: FileUpload.validateFileUpload, }), }); -fileUploadHandler = (directive, meta, file) => { +export const fileUploadHandler = (directive, meta, file) => { const store = UploadFS.getStore(directive); if (store) { diff --git a/packages/rocketchat-file-upload/globalFileRestrictions.js b/packages/rocketchat-file-upload/globalFileRestrictions.js index 0cda9fb97626..49e87093f93c 100644 --- a/packages/rocketchat-file-upload/globalFileRestrictions.js +++ b/packages/rocketchat-file-upload/globalFileRestrictions.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/tap:i18n'; import { Slingshot } from 'meteor/edgee:slingshot'; +import { settings } from 'meteor/rocketchat:settings'; +import { fileUploadIsValidContentType } from 'meteor/rocketchat:utils'; import filesize from 'filesize'; @@ -11,11 +13,11 @@ const slingShotConfig = { throw new Meteor.Error('login-required', 'Please login before posting files'); } - if (!RocketChat.fileUploadIsValidContentType(file.type)) { + if (!fileUploadIsValidContentType(file.type)) { throw new Meteor.Error(TAPi18n.__('error-invalid-file-type')); } - const maxFileSize = RocketChat.settings.get('FileUpload_MaxFileSize'); + const maxFileSize = settings.get('FileUpload_MaxFileSize'); if (maxFileSize > -1 && maxFileSize < file.size) { throw new Meteor.Error(TAPi18n.__('File_exceeds_allowed_size_of_bytes', { size: filesize(maxFileSize) })); diff --git a/packages/rocketchat-file-upload/lib/FileUpload.js b/packages/rocketchat-file-upload/lib/FileUpload.js index 50974ed99be8..82e628f626d3 100644 --- a/packages/rocketchat-file-upload/lib/FileUpload.js +++ b/packages/rocketchat-file-upload/lib/FileUpload.js @@ -1,21 +1,25 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; import { TAPi18n } from 'meteor/tap:i18n'; +import { Rooms, Settings } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; +import { fileUploadIsValidContentType } from 'meteor/rocketchat:utils'; +import { canAccessRoom } from 'meteor/rocketchat:authorization'; import filesize from 'filesize'; let maxFileSize = 0; -FileUpload = { +export const FileUpload = { validateFileUpload(file) { if (!Match.test(file.rid, String)) { return false; } // livechat users can upload files but they don't have an userId const user = file.userId ? Meteor.user() : null; - const room = RocketChat.models.Rooms.findOneById(file.rid); - const directMessageAllow = RocketChat.settings.get('FileUpload_Enabled_Direct'); - const fileUploadAllowed = RocketChat.settings.get('FileUpload_Enabled'); - if (RocketChat.authz.canAccessRoom(room, user, file) !== true) { + const room = Rooms.findOneById(file.rid); + const directMessageAllow = settings.get('FileUpload_Enabled_Direct'); + const fileUploadAllowed = settings.get('FileUpload_Enabled'); + if (canAccessRoom(room, user, file) !== true) { return false; } const language = user ? user.language : 'en'; @@ -37,7 +41,7 @@ FileUpload = { throw new Meteor.Error('error-file-too-large', reason); } - if (!RocketChat.fileUploadIsValidContentType(file.type)) { + if (!fileUploadIsValidContentType(file.type)) { const reason = TAPi18n.__('File_type_is_not_accepted', language); throw new Meteor.Error('error-invalid-file-type', reason); } @@ -46,10 +50,10 @@ FileUpload = { }, }; -RocketChat.settings.get('FileUpload_MaxFileSize', function(key, value) { +settings.get('FileUpload_MaxFileSize', function(key, value) { try { maxFileSize = parseInt(value); } catch (e) { - maxFileSize = RocketChat.models.Settings.findOneById('FileUpload_MaxFileSize').packageValue; + maxFileSize = Settings.findOneById('FileUpload_MaxFileSize').packageValue; } }); diff --git a/packages/rocketchat-file-upload/lib/FileUploadBase.js b/packages/rocketchat-file-upload/lib/FileUploadBase.js index 28e2d3cc5302..81ce6db6dd1b 100644 --- a/packages/rocketchat-file-upload/lib/FileUploadBase.js +++ b/packages/rocketchat-file-upload/lib/FileUploadBase.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { UploadFS } from 'meteor/jalik:ufs'; +import { canAccessRoom, hasPermission } from 'meteor/rocketchat:authorization'; +import { settings } from 'meteor/rocketchat:settings'; import _ from 'underscore'; UploadFS.config.defaultStorePermissions = new UploadFS.StorePermissions({ @@ -19,17 +21,17 @@ UploadFS.config.defaultStorePermissions = new UploadFS.StorePermissions({ return true; } - if (RocketChat.authz.canAccessRoom(null, null, doc)) { + if (canAccessRoom(null, null, doc)) { return true; } return false; }, update(userId, doc) { - return RocketChat.authz.hasPermission(Meteor.userId(), 'delete-message', doc.rid) || (RocketChat.settings.get('Message_AllowDeleting') && userId === doc.userId); + return hasPermission(Meteor.userId(), 'delete-message', doc.rid) || (settings.get('Message_AllowDeleting') && userId === doc.userId); }, remove(userId, doc) { - return RocketChat.authz.hasPermission(Meteor.userId(), 'delete-message', doc.rid) || (RocketChat.settings.get('Message_AllowDeleting') && userId === doc.userId); + return hasPermission(Meteor.userId(), 'delete-message', doc.rid) || (settings.get('Message_AllowDeleting') && userId === doc.userId); }, }); diff --git a/packages/rocketchat-file-upload/package.js b/packages/rocketchat-file-upload/package.js index 2a0ed2811e6d..3d2b1d8abae4 100644 --- a/packages/rocketchat-file-upload/package.js +++ b/packages/rocketchat-file-upload/package.js @@ -7,40 +7,25 @@ Package.describe({ }); Package.onUse(function(api) { - api.use('ecmascript'); - api.use('rocketchat:file'); - api.use('jalik:ufs'); - api.use('jalik:ufs-gridfs'); - api.use('jalik:ufs-local@0.2.5'); - api.use('edgee:slingshot'); - api.use('ostrio:cookies'); - api.use('rocketchat:lib'); - api.use('random'); - api.use('accounts-base'); - api.use('tracker'); - api.use('webapp'); - api.use('konecty:multiple-instances-status'); - api.use('rocketchat:e2e'); - - api.addFiles('globalFileRestrictions.js'); - - // commom lib - api.addFiles('lib/FileUpload.js'); - api.addFiles('lib/FileUploadBase.js'); - - api.addFiles('client/lib/fileUploadHandler.js', 'client'); - - api.addFiles('server/lib/FileUpload.js', 'server'); - api.addFiles('server/lib/proxy.js', 'server'); - api.addFiles('server/lib/requests.js', 'server'); - - api.addFiles('server/config/_configUploadStorage.js', 'server'); - - api.addFiles('server/methods/sendFileMessage.js', 'server'); - api.addFiles('server/methods/getS3FileUrl.js', 'server'); - - api.addFiles('server/startup/settings.js', 'server'); - - api.export('fileUploadHandler'); - api.export('FileUpload'); + api.use([ + 'ecmascript', + 'rocketchat:file', + 'jalik:ufs', + 'jalik:ufs-gridfs', + 'jalik:ufs-local@0.2.5', + 'edgee:slingshot', + 'ostrio:cookies', + 'rocketchat:models', + 'rocketchat:utils', + 'rocketchat:settings', + 'rocketchat:callbacks', + 'rocketchat:authorization', + 'random', + 'accounts-base', + 'tracker', + 'webapp', + 'konecty:multiple-instances-status', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); }); diff --git a/packages/rocketchat-file-upload/server/config/AmazonS3.js b/packages/rocketchat-file-upload/server/config/AmazonS3.js index 1b03831f2384..8bc7794587ef 100644 --- a/packages/rocketchat-file-upload/server/config/AmazonS3.js +++ b/packages/rocketchat-file-upload/server/config/AmazonS3.js @@ -1,5 +1,7 @@ import _ from 'underscore'; +import { settings } from 'meteor/rocketchat:settings'; import { FileUploadClass } from '../lib/FileUpload'; +import { FileUpload } from '../lib/FileUpload'; import '../../ufs/AmazonS3/server.js'; import http from 'http'; import https from 'https'; @@ -9,7 +11,7 @@ const get = function(file, req, res) { if (fileUrl) { const storeType = file.store.split(':').pop(); - if (RocketChat.settings.get(`FileUpload_S3_Proxy_${ storeType }`)) { + if (settings.get(`FileUpload_S3_Proxy_${ storeType }`)) { const request = /^https:/.test(fileUrl) ? https : http; request.get(fileUrl, (fileRes) => fileRes.pipe(res)); } else { @@ -56,16 +58,16 @@ const AmazonS3UserDataFiles = new FileUploadClass({ }); const configure = _.debounce(function() { - const Bucket = RocketChat.settings.get('FileUpload_S3_Bucket'); - const Acl = RocketChat.settings.get('FileUpload_S3_Acl'); - const AWSAccessKeyId = RocketChat.settings.get('FileUpload_S3_AWSAccessKeyId'); - const AWSSecretAccessKey = RocketChat.settings.get('FileUpload_S3_AWSSecretAccessKey'); - const URLExpiryTimeSpan = RocketChat.settings.get('FileUpload_S3_URLExpiryTimeSpan'); - const Region = RocketChat.settings.get('FileUpload_S3_Region'); - const SignatureVersion = RocketChat.settings.get('FileUpload_S3_SignatureVersion'); - const ForcePathStyle = RocketChat.settings.get('FileUpload_S3_ForcePathStyle'); + const Bucket = settings.get('FileUpload_S3_Bucket'); + const Acl = settings.get('FileUpload_S3_Acl'); + const AWSAccessKeyId = settings.get('FileUpload_S3_AWSAccessKeyId'); + const AWSSecretAccessKey = settings.get('FileUpload_S3_AWSSecretAccessKey'); + const URLExpiryTimeSpan = settings.get('FileUpload_S3_URLExpiryTimeSpan'); + const Region = settings.get('FileUpload_S3_Region'); + const SignatureVersion = settings.get('FileUpload_S3_SignatureVersion'); + const ForcePathStyle = settings.get('FileUpload_S3_ForcePathStyle'); // const CDN = RocketChat.settings.get('FileUpload_S3_CDN'); - const BucketURL = RocketChat.settings.get('FileUpload_S3_BucketURL'); + const BucketURL = settings.get('FileUpload_S3_BucketURL'); if (!Bucket) { return; @@ -101,4 +103,4 @@ const configure = _.debounce(function() { AmazonS3UserDataFiles.store = FileUpload.configureUploadsStore('AmazonS3', AmazonS3UserDataFiles.name, config); }, 500); -RocketChat.settings.get(/^FileUpload_S3_/, configure); +settings.get(/^FileUpload_S3_/, configure); diff --git a/packages/rocketchat-file-upload/server/config/FileSystem.js b/packages/rocketchat-file-upload/server/config/FileSystem.js index 36933d93ade3..e8a07c4f2a07 100644 --- a/packages/rocketchat-file-upload/server/config/FileSystem.js +++ b/packages/rocketchat-file-upload/server/config/FileSystem.js @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { UploadFS } from 'meteor/jalik:ufs'; +import { settings } from 'meteor/rocketchat:settings'; import _ from 'underscore'; import fs from 'fs'; -import { FileUploadClass } from '../lib/FileUpload'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; const FileSystemUploads = new FileUploadClass({ name: 'FileSystem:Uploads', @@ -98,7 +99,7 @@ const FileSystemUserDataFiles = new FileUploadClass({ const createFileSystemStore = _.debounce(function() { const options = { - path: RocketChat.settings.get('FileUpload_FileSystemPath'), // '/tmp/uploads/photos', + path: settings.get('FileUpload_FileSystemPath'), // '/tmp/uploads/photos', }; FileSystemUploads.store = FileUpload.configureUploadsStore('Local', FileSystemUploads.name, options); @@ -109,4 +110,4 @@ const createFileSystemStore = _.debounce(function() { UploadFS.getStores().fileSystem = UploadFS.getStores()[FileSystemUploads.name]; }, 500); -RocketChat.settings.get('FileUpload_FileSystemPath', createFileSystemStore); +settings.get('FileUpload_FileSystemPath', createFileSystemStore); diff --git a/packages/rocketchat-file-upload/server/config/GoogleStorage.js b/packages/rocketchat-file-upload/server/config/GoogleStorage.js index f0a20fc7db36..164c9161ebfb 100644 --- a/packages/rocketchat-file-upload/server/config/GoogleStorage.js +++ b/packages/rocketchat-file-upload/server/config/GoogleStorage.js @@ -1,5 +1,6 @@ import _ from 'underscore'; -import { FileUploadClass } from '../lib/FileUpload'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; +import { settings } from 'meteor/rocketchat:settings'; import '../../ufs/GoogleStorage/server.js'; import http from 'http'; import https from 'https'; @@ -12,7 +13,7 @@ const get = function(file, req, res) { if (fileUrl) { const storeType = file.store.split(':').pop(); - if (RocketChat.settings.get(`FileUpload_GoogleStorage_Proxy_${ storeType }`)) { + if (settings.get(`FileUpload_GoogleStorage_Proxy_${ storeType }`)) { const request = /^https:/.test(fileUrl) ? https : http; request.get(fileUrl, (fileRes) => fileRes.pipe(res)); } else { @@ -64,10 +65,10 @@ const GoogleCloudStorageUserDataFiles = new FileUploadClass({ }); const configure = _.debounce(function() { - const bucket = RocketChat.settings.get('FileUpload_GoogleStorage_Bucket'); - const accessId = RocketChat.settings.get('FileUpload_GoogleStorage_AccessId'); - const secret = RocketChat.settings.get('FileUpload_GoogleStorage_Secret'); - const URLExpiryTimeSpan = RocketChat.settings.get('FileUpload_S3_URLExpiryTimeSpan'); + const bucket = settings.get('FileUpload_GoogleStorage_Bucket'); + const accessId = settings.get('FileUpload_GoogleStorage_AccessId'); + const secret = settings.get('FileUpload_GoogleStorage_Secret'); + const URLExpiryTimeSpan = settings.get('FileUpload_S3_URLExpiryTimeSpan'); if (!bucket || !accessId || !secret) { return; @@ -89,4 +90,4 @@ const configure = _.debounce(function() { GoogleCloudStorageUserDataFiles.store = FileUpload.configureUploadsStore('GoogleStorage', GoogleCloudStorageUserDataFiles.name, config); }, 500); -RocketChat.settings.get(/^FileUpload_GoogleStorage_/, configure); +settings.get(/^FileUpload_GoogleStorage_/, configure); diff --git a/packages/rocketchat-file-upload/server/config/GridFS.js b/packages/rocketchat-file-upload/server/config/GridFS.js index 66ff1948414a..118ac20fe7b2 100644 --- a/packages/rocketchat-file-upload/server/config/GridFS.js +++ b/packages/rocketchat-file-upload/server/config/GridFS.js @@ -3,7 +3,7 @@ import stream from 'stream'; import zlib from 'zlib'; import util from 'util'; import { Logger } from 'meteor/rocketchat:logger'; -import { FileUploadClass } from '../lib/FileUpload'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; const logger = new Logger('FileUpload'); diff --git a/packages/rocketchat-file-upload/server/config/Slingshot_DEPRECATED.js b/packages/rocketchat-file-upload/server/config/Slingshot_DEPRECATED.js index bcb130c8fa67..e2cd5002d6c5 100644 --- a/packages/rocketchat-file-upload/server/config/Slingshot_DEPRECATED.js +++ b/packages/rocketchat-file-upload/server/config/Slingshot_DEPRECATED.js @@ -1,16 +1,18 @@ import _ from 'underscore'; import { Random } from 'meteor/random'; import { Slingshot } from 'meteor/edgee:slingshot'; +import { settings } from 'meteor/rocketchat:settings'; +import { Uploads } from 'meteor/rocketchat:models'; const configureSlingshot = _.debounce(() => { - const type = RocketChat.settings.get('FileUpload_Storage_Type'); - const bucket = RocketChat.settings.get('FileUpload_S3_Bucket'); - const acl = RocketChat.settings.get('FileUpload_S3_Acl'); - const accessKey = RocketChat.settings.get('FileUpload_S3_AWSAccessKeyId'); - const secretKey = RocketChat.settings.get('FileUpload_S3_AWSSecretAccessKey'); - const cdn = RocketChat.settings.get('FileUpload_S3_CDN'); - const region = RocketChat.settings.get('FileUpload_S3_Region'); - const bucketUrl = RocketChat.settings.get('FileUpload_S3_BucketURL'); + const type = settings.get('FileUpload_Storage_Type'); + const bucket = settings.get('FileUpload_S3_Bucket'); + const acl = settings.get('FileUpload_S3_Acl'); + const accessKey = settings.get('FileUpload_S3_AWSAccessKeyId'); + const secretKey = settings.get('FileUpload_S3_AWSSecretAccessKey'); + const cdn = settings.get('FileUpload_S3_CDN'); + const region = settings.get('FileUpload_S3_Region'); + const bucketUrl = settings.get('FileUpload_S3_BucketURL'); delete Slingshot._directives['rocketchat-uploads']; @@ -22,7 +24,7 @@ const configureSlingshot = _.debounce(() => { bucket, key(file, metaContext) { const id = Random.id(); - const path = `${ RocketChat.settings.get('uniqueID') }/uploads/${ metaContext.rid }/${ this.userId }/${ id }`; + const path = `${ settings.get('uniqueID') }/uploads/${ metaContext.rid }/${ this.userId }/${ id }`; const upload = { _id: id, @@ -32,7 +34,7 @@ const configureSlingshot = _.debounce(() => { }, }; - RocketChat.models.Uploads.insertFileInit(this.userId, 'AmazonS3:Uploads', file, upload); + Uploads.insertFileInit(this.userId, 'AmazonS3:Uploads', file, upload); return path; }, @@ -64,15 +66,15 @@ const configureSlingshot = _.debounce(() => { } }, 500); -RocketChat.settings.get('FileUpload_Storage_Type', configureSlingshot); -RocketChat.settings.get(/^FileUpload_S3_/, configureSlingshot); +settings.get('FileUpload_Storage_Type', configureSlingshot); +settings.get(/^FileUpload_S3_/, configureSlingshot); const createGoogleStorageDirective = _.debounce(() => { - const type = RocketChat.settings.get('FileUpload_Storage_Type'); - const bucket = RocketChat.settings.get('FileUpload_GoogleStorage_Bucket'); - const accessId = RocketChat.settings.get('FileUpload_GoogleStorage_AccessId'); - const secret = RocketChat.settings.get('FileUpload_GoogleStorage_Secret'); + const type = settings.get('FileUpload_Storage_Type'); + const bucket = settings.get('FileUpload_GoogleStorage_Bucket'); + const accessId = settings.get('FileUpload_GoogleStorage_AccessId'); + const secret = settings.get('FileUpload_GoogleStorage_Secret'); delete Slingshot._directives['rocketchat-uploads-gs']; @@ -87,7 +89,7 @@ const createGoogleStorageDirective = _.debounce(() => { GoogleSecretKey: secret, key(file, metaContext) { const id = Random.id(); - const path = `${ RocketChat.settings.get('uniqueID') }/uploads/${ metaContext.rid }/${ this.userId }/${ id }`; + const path = `${ settings.get('uniqueID') }/uploads/${ metaContext.rid }/${ this.userId }/${ id }`; const upload = { _id: id, @@ -97,7 +99,7 @@ const createGoogleStorageDirective = _.debounce(() => { }, }; - RocketChat.models.Uploads.insertFileInit(this.userId, 'GoogleCloudStorage:Uploads', file, upload); + Uploads.insertFileInit(this.userId, 'GoogleCloudStorage:Uploads', file, upload); return path; }, @@ -111,5 +113,5 @@ const createGoogleStorageDirective = _.debounce(() => { } }, 500); -RocketChat.settings.get('FileUpload_Storage_Type', createGoogleStorageDirective); -RocketChat.settings.get(/^FileUpload_GoogleStorage_/, createGoogleStorageDirective); +settings.get('FileUpload_Storage_Type', createGoogleStorageDirective); +settings.get(/^FileUpload_GoogleStorage_/, createGoogleStorageDirective); diff --git a/packages/rocketchat-file-upload/server/config/Webdav.js b/packages/rocketchat-file-upload/server/config/Webdav.js index bc29c284f360..84b304b813dc 100644 --- a/packages/rocketchat-file-upload/server/config/Webdav.js +++ b/packages/rocketchat-file-upload/server/config/Webdav.js @@ -1,5 +1,6 @@ import _ from 'underscore'; -import { FileUploadClass } from '../lib/FileUpload'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; +import { settings } from 'meteor/rocketchat:settings'; import '../../ufs/Webdav/server.js'; const get = function(file, req, res) { @@ -32,10 +33,10 @@ const WebdavUserDataFiles = new FileUploadClass({ }); const configure = _.debounce(function() { - const uploadFolderPath = RocketChat.settings.get('FileUpload_Webdav_Upload_Folder_Path'); - const server = RocketChat.settings.get('FileUpload_Webdav_Server_URL'); - const username = RocketChat.settings.get('FileUpload_Webdav_Username'); - const password = RocketChat.settings.get('FileUpload_Webdav_Password'); + const uploadFolderPath = settings.get('FileUpload_Webdav_Upload_Folder_Path'); + const server = settings.get('FileUpload_Webdav_Server_URL'); + const username = settings.get('FileUpload_Webdav_Username'); + const password = settings.get('FileUpload_Webdav_Password'); if (!server || !username || !password) { return; @@ -57,4 +58,4 @@ const configure = _.debounce(function() { WebdavUserDataFiles.store = FileUpload.configureUploadsStore('Webdav', WebdavUserDataFiles.name, config); }, 500); -RocketChat.settings.get(/^FileUpload_Webdav_/, configure); +settings.get(/^FileUpload_Webdav_/, configure); diff --git a/packages/rocketchat-file-upload/server/config/_configUploadStorage.js b/packages/rocketchat-file-upload/server/config/_configUploadStorage.js index b85f2c91a689..02b581effd1f 100644 --- a/packages/rocketchat-file-upload/server/config/_configUploadStorage.js +++ b/packages/rocketchat-file-upload/server/config/_configUploadStorage.js @@ -1,4 +1,5 @@ import { UploadFS } from 'meteor/jalik:ufs'; +import { settings } from 'meteor/rocketchat:settings'; import _ from 'underscore'; import './AmazonS3.js'; import './FileSystem.js'; @@ -8,7 +9,7 @@ import './Webdav.js'; import './Slingshot_DEPRECATED.js'; const configStore = _.debounce(() => { - const store = RocketChat.settings.get('FileUpload_Storage_Type'); + const store = settings.get('FileUpload_Storage_Type'); if (store) { console.log('Setting default file store to', store); @@ -18,4 +19,4 @@ const configStore = _.debounce(() => { } }, 1000); -RocketChat.settings.get(/^FileUpload_/, configStore); +settings.get(/^FileUpload_/, configStore); diff --git a/packages/rocketchat-file-upload/server/index.js b/packages/rocketchat-file-upload/server/index.js new file mode 100644 index 000000000000..995e47b22574 --- /dev/null +++ b/packages/rocketchat-file-upload/server/index.js @@ -0,0 +1,11 @@ +import { FileUpload } from './lib/FileUpload'; +import './lib/proxy'; +import './lib/requests'; +import './config/_configUploadStorage'; +import './methods/sendFileMessage'; +import './methods/getS3FileUrl'; +import './startup/settings'; + +export { + FileUpload, +}; diff --git a/packages/rocketchat-file-upload/server/lib/FileUpload.js b/packages/rocketchat-file-upload/server/lib/FileUpload.js index 4c4f3f0e318d..31a3cabf8b13 100644 --- a/packages/rocketchat-file-upload/server/lib/FileUpload.js +++ b/packages/rocketchat-file-upload/server/lib/FileUpload.js @@ -6,10 +6,14 @@ import Future from 'fibers/future'; import sharp from 'sharp'; import { Cookies } from 'meteor/ostrio:cookies'; import { UploadFS } from 'meteor/jalik:ufs'; +import { settings } from 'meteor/rocketchat:settings'; +import * as Models from 'meteor/rocketchat:models'; +import { FileUpload as _FileUpload } from '../../lib/FileUpload'; +import { roomTypes } from 'meteor/rocketchat:utils'; const cookie = new Cookies(); -Object.assign(FileUpload, { +export const FileUpload = Object.assign(_FileUpload, { handlers: {}, configureUploadsStore(store, name, options) { @@ -24,12 +28,12 @@ Object.assign(FileUpload, { defaultUploads() { return { - collection: RocketChat.models.Uploads.model, + collection: Models.Uploads.model, filter: new UploadFS.Filter({ onCheck: FileUpload.validateFileUpload, }), getPath(file) { - return `${ RocketChat.settings.get('uniqueID') }/uploads/${ file.rid }/${ file.userId }/${ file._id }`; + return `${ settings.get('uniqueID') }/uploads/${ file.rid }/${ file.userId }/${ file._id }`; }, onValidate: FileUpload.uploadsOnValidate, onRead(fileId, file, req, res) { @@ -46,12 +50,12 @@ Object.assign(FileUpload, { defaultAvatars() { return { - collection: RocketChat.models.Avatars.model, + collection: Models.Avatars.model, // filter: new UploadFS.Filter({ // onCheck: FileUpload.validateFileUpload // }), getPath(file) { - return `${ RocketChat.settings.get('uniqueID') }/avatars/${ file.userId }`; + return `${ settings.get('uniqueID') }/avatars/${ file.userId }`; }, onValidate: FileUpload.avatarsOnValidate, onFinishUpload: FileUpload.avatarsOnFinishUpload, @@ -60,9 +64,9 @@ Object.assign(FileUpload, { defaultUserDataFiles() { return { - collection: RocketChat.models.UserDataFiles.model, + collection: Models.UserDataFiles.model, getPath(file) { - return `${ RocketChat.settings.get('uniqueID') }/uploads/userData/${ file.userId }`; + return `${ settings.get('uniqueID') }/uploads/userData/${ file.userId }`; }, onValidate: FileUpload.uploadsOnValidate, onRead(fileId, file, req, res) { @@ -78,13 +82,13 @@ Object.assign(FileUpload, { }, avatarsOnValidate(file) { - if (RocketChat.settings.get('Accounts_AvatarResize') !== true) { + if (settings.get('Accounts_AvatarResize') !== true) { return; } const tempFilePath = UploadFS.getTempFilePath(file._id); - const height = RocketChat.settings.get('Accounts_AvatarSize'); + const height = settings.get('Accounts_AvatarSize'); const future = new Future(); const s = sharp(tempFilePath); @@ -123,7 +127,7 @@ Object.assign(FileUpload, { }, resizeImagePreview(file) { - file = RocketChat.models.Uploads.findOneById(file._id); + file = Models.Uploads.findOneById(file._id); file = FileUpload.addExtensionTo(file); const image = FileUpload.getStore('Uploads')._store.getReadStream(file._id, file); @@ -195,17 +199,17 @@ Object.assign(FileUpload, { avatarsOnFinishUpload(file) { // update file record to match user's username - const user = RocketChat.models.Users.findOneById(file.userId); - const oldAvatar = RocketChat.models.Avatars.findOneByName(user.username); + const user = Models.Users.findOneById(file.userId); + const oldAvatar = Models.Avatars.findOneByName(user.username); if (oldAvatar) { - RocketChat.models.Avatars.deleteFile(oldAvatar._id); + Models.Avatars.deleteFile(oldAvatar._id); } - RocketChat.models.Avatars.updateFileNameById(file._id, user.username); + Models.Avatars.updateFileNameById(file._id, user.username); // console.log('upload finished ->', file); }, requestCanAccessFiles({ headers = {}, query = {} }) { - if (!RocketChat.settings.get('FileUpload_ProtectFiles')) { + if (!settings.get('FileUpload_ProtectFiles')) { return true; } @@ -218,9 +222,9 @@ Object.assign(FileUpload, { rc_room_type = cookie.get('rc_room_type', headers.cookie); } - const isAuthorizedByCookies = rc_uid && rc_token && RocketChat.models.Users.findOneByIdAndLoginToken(rc_uid, rc_token); - const isAuthorizedByHeaders = headers['x-user-id'] && headers['x-auth-token'] && RocketChat.models.Users.findOneByIdAndLoginToken(headers['x-user-id'], headers['x-auth-token']); - const isAuthorizedByRoom = rc_room_type && RocketChat.roomTypes.getConfig(rc_room_type).canAccessUploadedFile({ rc_uid, rc_rid, rc_token }); + const isAuthorizedByCookies = rc_uid && rc_token && Models.Users.findOneByIdAndLoginToken(rc_uid, rc_token); + const isAuthorizedByHeaders = headers['x-user-id'] && headers['x-auth-token'] && Models.Users.findOneByIdAndLoginToken(headers['x-user-id'], headers['x-auth-token']); + const isAuthorizedByRoom = rc_room_type && roomTypes.getConfig(rc_room_type).canAccessUploadedFile({ rc_uid, rc_rid, rc_token }); return isAuthorizedByCookies || isAuthorizedByHeaders || isAuthorizedByRoom; }, addExtensionTo(file) { @@ -237,7 +241,7 @@ Object.assign(FileUpload, { }, getStore(modelName) { - const storageType = RocketChat.settings.get('FileUpload_Storage_Type'); + const storageType = settings.get('FileUpload_Storage_Type'); const handlerName = `${ storageType }:${ modelName }`; return this.getStoreByName(handlerName); @@ -306,7 +310,7 @@ export class FileUploadClass { } getModelFromName() { - return RocketChat.models[this.name.split(':')[1]]; + return Models[this.name.split(':')[1]]; } delete(fileId) { diff --git a/packages/rocketchat-file-upload/server/lib/proxy.js b/packages/rocketchat-file-upload/server/lib/proxy.js index 7f6c41ef1275..49e266dbea55 100644 --- a/packages/rocketchat-file-upload/server/lib/proxy.js +++ b/packages/rocketchat-file-upload/server/lib/proxy.js @@ -3,6 +3,7 @@ import { WebApp } from 'meteor/webapp'; import { UploadFS } from 'meteor/jalik:ufs'; import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; import { Logger } from 'meteor/rocketchat:logger'; +import { isDocker } from 'meteor/rocketchat:utils'; import http from 'http'; import URL from 'url'; @@ -68,7 +69,7 @@ WebApp.connectHandlers.stack.unshift({ return; } - if (instance.extraInformation.host === process.env.INSTANCE_IP && RocketChat.isDocker() === false) { + if (instance.extraInformation.host === process.env.INSTANCE_IP && isDocker() === false) { instance.extraInformation.host = 'localhost'; } diff --git a/packages/rocketchat-file-upload/server/lib/requests.js b/packages/rocketchat-file-upload/server/lib/requests.js index cac712ca25a1..2d6cce21d7e1 100644 --- a/packages/rocketchat-file-upload/server/lib/requests.js +++ b/packages/rocketchat-file-upload/server/lib/requests.js @@ -1,12 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; +import { Uploads } from 'meteor/rocketchat:models'; +import { FileUpload } from './FileUpload'; WebApp.connectHandlers.use('/file-upload/', function(req, res, next) { const match = /^\/([^\/]+)\/(.*)/.exec(req.url); if (match[1]) { - const file = RocketChat.models.Uploads.findOneById(match[1]); + const file = Uploads.findOneById(match[1]); if (file) { if (!Meteor.settings.public.sandstorm && !FileUpload.requestCanAccessFiles(req)) { diff --git a/packages/rocketchat-file-upload/server/methods/getS3FileUrl.js b/packages/rocketchat-file-upload/server/methods/getS3FileUrl.js index d104a60eca5c..5d9474c94f32 100644 --- a/packages/rocketchat-file-upload/server/methods/getS3FileUrl.js +++ b/packages/rocketchat-file-upload/server/methods/getS3FileUrl.js @@ -1,9 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { UploadFS } from 'meteor/jalik:ufs'; +import { settings } from 'meteor/rocketchat:settings'; +import { Uploads } from 'meteor/rocketchat:models'; let protectedFiles; -RocketChat.settings.get('FileUpload_ProtectFiles', function(key, value) { +settings.get('FileUpload_ProtectFiles', function(key, value) { protectedFiles = value; }); @@ -12,7 +14,7 @@ Meteor.methods({ if (protectedFiles && !Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' }); } - const file = RocketChat.models.Uploads.findOneById(fileId); + const file = Uploads.findOneById(fileId); return UploadFS.getStore('AmazonS3:Uploads').getRedirectURL(file); }, diff --git a/packages/rocketchat-file-upload/server/methods/sendFileMessage.js b/packages/rocketchat-file-upload/server/methods/sendFileMessage.js index 8cdf36cc0f08..13f938015ee5 100644 --- a/packages/rocketchat-file-upload/server/methods/sendFileMessage.js +++ b/packages/rocketchat-file-upload/server/methods/sendFileMessage.js @@ -1,6 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; +import { Uploads } from 'meteor/rocketchat:models'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { FileUpload } from '../lib/FileUpload'; import _ from 'underscore'; Meteor.methods({ @@ -23,7 +26,7 @@ Meteor.methods({ msg: Match.Optional(String), }); - RocketChat.models.Uploads.updateFileComplete(file._id, Meteor.userId(), _.omit(file, '_id')); + Uploads.updateFileComplete(file._id, Meteor.userId(), _.omit(file, '_id')); const fileUrl = `/file-upload/${ file._id }/${ encodeURI(file.name) }`; @@ -77,7 +80,7 @@ Meteor.methods({ msg = Meteor.call('sendMessage', msg); - Meteor.defer(() => RocketChat.callbacks.run('afterFileUpload', { user, room, message: msg })); + Meteor.defer(() => callbacks.run('afterFileUpload', { user, room, message: msg })); return msg; }, diff --git a/packages/rocketchat-file-upload/server/startup/settings.js b/packages/rocketchat-file-upload/server/startup/settings.js index 095b2acdc952..65dfa6cc6a63 100644 --- a/packages/rocketchat-file-upload/server/startup/settings.js +++ b/packages/rocketchat-file-upload/server/startup/settings.js @@ -1,4 +1,6 @@ -RocketChat.settings.addGroup('FileUpload', function() { +import { settings } from 'meteor/rocketchat:settings'; + +settings.addGroup('FileUpload', function() { this.add('FileUpload_Enabled', true, { type: 'boolean', public: true, diff --git a/server/startup/migrations/v099.js b/server/startup/migrations/v099.js index 001739053fa1..4718c52bb428 100644 --- a/server/startup/migrations/v099.js +++ b/server/startup/migrations/v099.js @@ -3,6 +3,7 @@ import { Match } from 'meteor/check'; import { Mongo } from 'meteor/mongo'; import { RocketChatFile } from 'meteor/rocketchat:file'; import { SystemLogger } from 'meteor/rocketchat:logger'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; import fs from 'fs'; import path from 'path'; From 61a71eb3b071f1393163fb8de5f0148e1199115c Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 13:39:29 -0200 Subject: [PATCH 26/59] Import FileUpload where it is being used --- packages/rocketchat-google-vision/package.js | 1 + packages/rocketchat-importer/package.js | 1 + .../rocketchat-lib/server/functions/cleanRoomHistory.js | 1 + packages/rocketchat-lib/server/functions/deleteMessage.js | 1 + packages/rocketchat-lib/server/functions/deleteUser.js | 1 + packages/rocketchat-lib/server/functions/setUserAvatar.js | 2 ++ packages/rocketchat-lib/server/functions/setUsername.js | 1 + packages/rocketchat-models/server/models/Messages.js | 8 ++++++-- packages/rocketchat-slackbridge/package.js | 1 + packages/rocketchat-ui-account/package.js | 1 + packages/rocketchat-ui-message/package.js | 1 + packages/rocketchat-user-data-download/package.js | 1 + packages/rocketchat-webdav/package.js | 1 + server/methods/resetAvatar.js | 1 + server/startup/initialData.js | 1 + server/startup/migrations/v002.js | 1 + 16 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-google-vision/package.js b/packages/rocketchat-google-vision/package.js index 210bb8a760b4..50cbf897c353 100644 --- a/packages/rocketchat-google-vision/package.js +++ b/packages/rocketchat-google-vision/package.js @@ -9,6 +9,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:lib', + 'rocketchat:file-upload', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-importer/package.js b/packages/rocketchat-importer/package.js index 47402525150d..136826523918 100644 --- a/packages/rocketchat-importer/package.js +++ b/packages/rocketchat-importer/package.js @@ -13,6 +13,7 @@ Package.onUse(function(api) { 'rocketchat:utils', 'rocketchat:lib', 'rocketchat:logger', + 'rocketchat:file-upload', ]); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-lib/server/functions/cleanRoomHistory.js b/packages/rocketchat-lib/server/functions/cleanRoomHistory.js index b68352325bd1..c668b32191e2 100644 --- a/packages/rocketchat-lib/server/functions/cleanRoomHistory.js +++ b/packages/rocketchat-lib/server/functions/cleanRoomHistory.js @@ -1,4 +1,5 @@ import { TAPi18n } from 'meteor/tap:i18n'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; RocketChat.cleanRoomHistory = function({ rid, latest = new Date(), oldest = new Date('0001-01-01T00:00:00Z'), inclusive = true, limit = 0, excludePinned = true, filesOnly = false, fromUsers = [] }) { const gt = inclusive ? '$gte' : '$gt'; diff --git a/packages/rocketchat-lib/server/functions/deleteMessage.js b/packages/rocketchat-lib/server/functions/deleteMessage.js index 0f04f99cc519..1f05cdd2975c 100644 --- a/packages/rocketchat-lib/server/functions/deleteMessage.js +++ b/packages/rocketchat-lib/server/functions/deleteMessage.js @@ -1,4 +1,5 @@ import { Meteor } from 'meteor/meteor'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; RocketChat.deleteMessage = function(message, user) { const keepHistory = RocketChat.settings.get('Message_KeepHistory'); diff --git a/packages/rocketchat-lib/server/functions/deleteUser.js b/packages/rocketchat-lib/server/functions/deleteUser.js index 40f05e4db3d0..363a49f1c9e0 100644 --- a/packages/rocketchat-lib/server/functions/deleteUser.js +++ b/packages/rocketchat-lib/server/functions/deleteUser.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/tap:i18n'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; RocketChat.deleteUser = function(userId) { const user = RocketChat.models.Users.findOneById(userId, { diff --git a/packages/rocketchat-lib/server/functions/setUserAvatar.js b/packages/rocketchat-lib/server/functions/setUserAvatar.js index 8e827ca1120b..df3b2e888cdb 100644 --- a/packages/rocketchat-lib/server/functions/setUserAvatar.js +++ b/packages/rocketchat-lib/server/functions/setUserAvatar.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { HTTP } from 'meteor/http'; import { RocketChatFile } from 'meteor/rocketchat:file'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; + RocketChat.setUserAvatar = function(user, dataURI, contentType, service) { let encoding; let image; diff --git a/packages/rocketchat-lib/server/functions/setUsername.js b/packages/rocketchat-lib/server/functions/setUsername.js index bada501f6649..a0d45b3f03d6 100644 --- a/packages/rocketchat-lib/server/functions/setUsername.js +++ b/packages/rocketchat-lib/server/functions/setUsername.js @@ -1,5 +1,6 @@ import s from 'underscore.string'; import { Accounts } from 'meteor/accounts-base'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; RocketChat._setUsername = function(userId, u) { const username = s.trim(u); diff --git a/packages/rocketchat-models/server/models/Messages.js b/packages/rocketchat-models/server/models/Messages.js index f7993db20d48..d56c4a255ba3 100644 --- a/packages/rocketchat-models/server/models/Messages.js +++ b/packages/rocketchat-models/server/models/Messages.js @@ -777,7 +777,11 @@ export class Messages extends Base { return this.remove(query); } - removeFilesByRoomId(roomId) { + async removeFilesByRoomId(roomId) { + if (!this.FileUpload) { + const { FileUpload } = await import('meteor/rocketchat:file-upload'); + this.FileUpload = FileUpload; + } this.find({ rid: roomId, 'file._id': { @@ -787,7 +791,7 @@ export class Messages extends Base { fields: { 'file._id': 1, }, - }).fetch().forEach((document) => FileUpload.getStore('Uploads').deleteById(document.file._id)); + }).fetch().forEach((document) => this.FileUpload.getStore('Uploads').deleteById(document.file._id)); } getMessageByFileId(fileID) { diff --git a/packages/rocketchat-slackbridge/package.js b/packages/rocketchat-slackbridge/package.js index 03dac8411e27..3faed900b643 100644 --- a/packages/rocketchat-slackbridge/package.js +++ b/packages/rocketchat-slackbridge/package.js @@ -11,6 +11,7 @@ Package.onUse(function(api) { 'ecmascript', 'rocketchat:lib', 'rocketchat:logger', + 'rocketchat:file-upload', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-ui-account/package.js b/packages/rocketchat-ui-account/package.js index 6fdd1c9011c2..082dece03b28 100644 --- a/packages/rocketchat-ui-account/package.js +++ b/packages/rocketchat-ui-account/package.js @@ -18,6 +18,7 @@ Package.onUse(function(api) { 'sha', 'rocketchat:utils', 'rocketchat:lazy-load', + 'rocketchat:file-upload', ]); api.mainModule('client/index.js', 'client'); }); diff --git a/packages/rocketchat-ui-message/package.js b/packages/rocketchat-ui-message/package.js index 591099cda9f3..6d12b0d35d4b 100644 --- a/packages/rocketchat-ui-message/package.js +++ b/packages/rocketchat-ui-message/package.js @@ -21,6 +21,7 @@ Package.onUse(function(api) { 'rocketchat:lib', 'rocketchat:ui-account', 'rocketchat:ui-vrecord', + 'rocketchat:file-upload', ]); api.addAssets('../../node_modules/pdfjs-dist/build/pdf.worker.js', 'client'); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-user-data-download/package.js b/packages/rocketchat-user-data-download/package.js index 897e19f47b9b..e10af05cc569 100644 --- a/packages/rocketchat-user-data-download/package.js +++ b/packages/rocketchat-user-data-download/package.js @@ -9,6 +9,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:file', + 'rocketchat:file-upload', 'rocketchat:lib', 'webapp', ]); diff --git a/packages/rocketchat-webdav/package.js b/packages/rocketchat-webdav/package.js index 61d2a21652fb..e0df4edb26ef 100644 --- a/packages/rocketchat-webdav/package.js +++ b/packages/rocketchat-webdav/package.js @@ -19,6 +19,7 @@ Package.onUse(function(api) { 'rocketchat:lib', 'rocketchat:api', 'rocketchat:grant', + 'rocketchat:file-upload', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/server/methods/resetAvatar.js b/server/methods/resetAvatar.js index aca806f46656..f4871d146676 100644 --- a/server/methods/resetAvatar.js +++ b/server/methods/resetAvatar.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; Meteor.methods({ resetAvatar() { diff --git a/server/startup/initialData.js b/server/startup/initialData.js index 4a11d4b114bd..308764ab4cf0 100644 --- a/server/startup/initialData.js +++ b/server/startup/initialData.js @@ -1,6 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { RocketChatFile } from 'meteor/rocketchat:file'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; import _ from 'underscore'; Meteor.startup(function() { diff --git a/server/startup/migrations/v002.js b/server/startup/migrations/v002.js index c9d8cdea99fa..8f7f69277231 100644 --- a/server/startup/migrations/v002.js +++ b/server/startup/migrations/v002.js @@ -1,4 +1,5 @@ import { RocketChatFile } from 'meteor/rocketchat:file'; +import { FileUpload } from 'meteor/rocketchat:file-upload'; RocketChat.Migrations.add({ version: 2, From 5a8d997e3690fd8f00d989a02bdcdd47d47fba27 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 13:39:51 -0200 Subject: [PATCH 27/59] Remove FileUpload and fileUploadHandler from globals eslintrc --- .eslintrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5b79d7417304..2017713d8d12 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,9 +20,7 @@ "device" : false, "DynamicCss" : false, "facebookConnectPlugin" : false, - "FileUpload" : false, "fileUpload" : false, - "fileUploadHandler" : false, "fireGlobalEvent" : false, "handleError" : false, "getAvatarUrlFromUsername" : false, From f6b7f82861560240f30aed150adb506bc52422b6 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:39:03 -0200 Subject: [PATCH 28/59] Move some functions to rocketchat:ui-utils --- packages/rocketchat-lib/client/lib/index.js | 3 +- packages/rocketchat-ui-utils/client/index.js | 29 ++ .../client/lib/AccountBox.js | 101 +++++ .../client/lib/ChannelActions.js | 13 +- .../client/lib/MessageAction.js | 361 ++++++++++++++++++ .../client/lib/RoomHistoryManager.js | 277 ++++++++++++++ .../client/lib/RoomManager.js | 331 ++++++++++++++++ .../rocketchat-ui-utils/client/lib/SideNav.js | 135 +++++++ .../client/lib/callMethod.js | 1 + .../client/lib/mainReady.js | 3 + .../rocketchat-ui-utils/client/lib/menu.js | 225 +++++++++++ .../client/lib/messageBox.js | 65 ++++ .../rocketchat-ui-utils/client/lib/modal.js | 2 +- .../client/lib/popover.html | 34 ++ .../rocketchat-ui-utils/client/lib/popover.js | 266 +++++++++++++ .../client/lib/readMessages.js | 216 +++++++++++ .../client/lib}/renderMessageBody.js | 4 +- packages/rocketchat-ui-utils/package.js | 8 + .../client/lib/RoomHistoryManager.js | 275 +------------ .../rocketchat-ui/client/lib/RoomManager.js | 327 +--------------- .../rocketchat-ui/client/lib/accountBox.js | 101 +---- packages/rocketchat-ui/client/lib/menu.js | 225 +---------- .../rocketchat-ui/client/lib/readMessages.js | 202 +--------- packages/rocketchat-ui/client/lib/sideNav.js | 133 +------ .../rocketchat-ui/client/views/app/popover.js | 262 +------------ packages/rocketchat-ui/package.js | 1 - 26 files changed, 2080 insertions(+), 1520 deletions(-) create mode 100644 packages/rocketchat-ui-utils/client/lib/AccountBox.js rename packages/{rocketchat-lib => rocketchat-ui-utils}/client/lib/ChannelActions.js (82%) create mode 100644 packages/rocketchat-ui-utils/client/lib/MessageAction.js create mode 100644 packages/rocketchat-ui-utils/client/lib/RoomHistoryManager.js create mode 100644 packages/rocketchat-ui-utils/client/lib/RoomManager.js create mode 100644 packages/rocketchat-ui-utils/client/lib/SideNav.js rename packages/{rocketchat-lib => rocketchat-ui-utils}/client/lib/callMethod.js (90%) create mode 100644 packages/rocketchat-ui-utils/client/lib/mainReady.js create mode 100644 packages/rocketchat-ui-utils/client/lib/menu.js create mode 100644 packages/rocketchat-ui-utils/client/lib/messageBox.js create mode 100644 packages/rocketchat-ui-utils/client/lib/popover.html create mode 100644 packages/rocketchat-ui-utils/client/lib/popover.js create mode 100644 packages/rocketchat-ui-utils/client/lib/readMessages.js rename packages/{rocketchat-ui-message/client => rocketchat-ui-utils/client/lib}/renderMessageBody.js (79%) diff --git a/packages/rocketchat-lib/client/lib/index.js b/packages/rocketchat-lib/client/lib/index.js index 4684347c02a1..1964294de6b5 100644 --- a/packages/rocketchat-lib/client/lib/index.js +++ b/packages/rocketchat-lib/client/lib/index.js @@ -9,8 +9,7 @@ import { RocketChatTabBar } from './RocketChatTabBar'; import { RocketChatAnnouncement } from './RocketChatAnnouncement'; import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig, UiTextContext } from '../../lib/RoomTypeConfig'; -import { hide, leave, erase } from './ChannelActions'; -import { call } from './callMethod'; +import { hide, leave, erase, call } from 'meteor/rocketchat:ui-utils'; import { LoginPresence } from './LoginPresence'; import * as DateFormat from './formatDate'; diff --git a/packages/rocketchat-ui-utils/client/index.js b/packages/rocketchat-ui-utils/client/index.js index d015902dc125..ba0212a9c146 100644 --- a/packages/rocketchat-ui-utils/client/index.js +++ b/packages/rocketchat-ui-utils/client/index.js @@ -1,7 +1,36 @@ import { AdminBox } from './lib/AdminBox'; import { modal } from './lib/modal'; +import { SideNav } from './lib/SideNav'; +import { AccountBox } from './lib/AccountBox'; +import { menu } from './lib/menu'; +import { call } from './lib/callMethod'; +import { erase, hide, leave } from './lib/ChannelActions'; +import { MessageAction } from './lib/MessageAction'; +import { messageBox } from './lib/messageBox'; +import { popover } from './lib/popover'; +import { readMessage } from './lib/readMessages'; +import { RoomManager } from './lib/RoomManager'; +import { upsertMessage, RoomHistoryManager } from './lib/RoomHistoryManager'; +import { mainReady } from './lib/mainReady'; +import { renderMessageBody } from './lib/renderMessageBody'; export { AdminBox, modal, + SideNav, + AccountBox, + menu, + call, + erase, + hide, + leave, + MessageAction, + messageBox, + popover, + readMessage, + RoomManager, + RoomHistoryManager, + mainReady, + renderMessageBody, + upsertMessage, }; diff --git a/packages/rocketchat-ui-utils/client/lib/AccountBox.js b/packages/rocketchat-ui-utils/client/lib/AccountBox.js new file mode 100644 index 000000000000..f10ab7f5bb4c --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/AccountBox.js @@ -0,0 +1,101 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { BlazeLayout } from 'meteor/kadira:blaze-layout'; +import { Session } from 'meteor/session'; +import { SideNav } from './SideNav'; +import _ from 'underscore'; + +export const AccountBox = (function() { + let status = 0; + const items = new ReactiveVar([]); + function setStatus(status) { + return Meteor.call('UserPresence:setDefaultStatus', status); + } + function open() { + if (SideNav.flexStatus()) { + SideNav.closeFlex(); + return; + } + status = 1; + } + function close() { + status = 0; + } + function toggle() { + if (status) { + return close(); + } else { + return open(); + } + } + function openFlex() { + status = 0; + } + + /* + * @param newOption: + * name: Button label + * icon: Button icon + * class: Class of the item + * permissions: Which permissions a user should have (all of them) to see this item + */ + function addItem(newItem) { + return Tracker.nonreactive(function() { + const actual = items.get(); + actual.push(newItem); + return items.set(actual); + }); + } + function checkCondition(item) { + return (item.condition == null) || item.condition(); + } + function getItems() { + return _.filter(items.get(), function(item) { + if (checkCondition(item)) { + return true; + } + }); + } + function addRoute(newRoute, router) { + if (router == null) { + router = FlowRouter; + } + const routeConfig = { + center: 'pageContainer', + pageTemplate: newRoute.pageTemplate, + }; + if (newRoute.i18nPageTitle != null) { + routeConfig.i18nPageTitle = newRoute.i18nPageTitle; + } + if (newRoute.pageTitle != null) { + routeConfig.pageTitle = newRoute.pageTitle; + } + return router.route(newRoute.path, { + name: newRoute.name, + action() { + Session.set('openedRoom'); + return BlazeLayout.render('main', routeConfig); + }, + triggersEnter: [ + function() { + if (newRoute.sideNav != null) { + SideNav.setFlex(newRoute.sideNav); + return SideNav.openFlex(); + } + }, + ], + }); + } + return { + setStatus, + toggle, + open, + close, + openFlex, + addRoute, + addItem, + getItems, + }; +}()); diff --git a/packages/rocketchat-lib/client/lib/ChannelActions.js b/packages/rocketchat-ui-utils/client/lib/ChannelActions.js similarity index 82% rename from packages/rocketchat-lib/client/lib/ChannelActions.js rename to packages/rocketchat-ui-utils/client/lib/ChannelActions.js index bbd791a2a8cf..3fa348c3100f 100644 --- a/packages/rocketchat-lib/client/lib/ChannelActions.js +++ b/packages/rocketchat-ui-utils/client/lib/ChannelActions.js @@ -1,11 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { call, UiTextContext } from 'meteor/rocketchat:lib'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; -import { t } from 'meteor/rocketchat:utils'; +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) { - const warnText = RocketChat.roomTypes.roomTypes[type].getUiText(UiTextContext.HIDE_WARNING); + const warnText = roomTypes.roomTypes[type].getUiText(UiTextContext.HIDE_WARNING); modal.open({ title: t('Are_you_sure'), @@ -48,8 +50,9 @@ const leaveRoom = async(rid) => { } }; -export function leave(type, rid, name) { - const warnText = RocketChat.roomTypes.roomTypes[type].getUiText(UiTextContext.LEAVE_WARNING); +export async function leave(type, rid, name) { + const { RoomManager } = await import('meteor/rocketchat:ui'); + const warnText = roomTypes.roomTypes[type].getUiText(UiTextContext.LEAVE_WARNING); modal.open({ title: t('Are_you_sure'), diff --git a/packages/rocketchat-ui-utils/client/lib/MessageAction.js b/packages/rocketchat-ui-utils/client/lib/MessageAction.js new file mode 100644 index 000000000000..5be8f3aa1750 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/MessageAction.js @@ -0,0 +1,361 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/tap:i18n'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { Session } from 'meteor/session'; +import { t, handleError, roomTypes } from 'meteor/rocketchat:utils'; +import { Messages, Rooms, Subscriptions } from 'meteor/rocketchat:models'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; +import { settings } from 'meteor/rocketchat:settings'; +import _ from 'underscore'; +import moment from 'moment'; +import toastr from 'toastr'; + +const call = (method, ...args) => new Promise((resolve, reject) => { + Meteor.call(method, ...args, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); +}); + +const success = function success(fn) { + return function(error, result) { + if (error) { + return handleError(error); + } + if (result) { + fn.call(this, result); + } + }; +}; + +export const MessageAction = new class { + /* + config expects the following keys (only id is mandatory): + id (mandatory) + icon: string + label: string + action: function(event, instance) + condition: function(message) + order: integer + group: string (message or menu) + */ + + constructor() { + this.buttons = new ReactiveVar({}); + } + + addButton(config) { + if (!config || !config.id) { + return false; + } + + if (!config.group) { + config.group = 'menu'; + } + + return Tracker.nonreactive(() => { + const btns = this.buttons.get(); + btns[config.id] = config; + return this.buttons.set(btns); + }); + } + + removeButton(id) { + return Tracker.nonreactive(() => { + const btns = this.buttons.get(); + delete btns[id]; + return this.buttons.set(btns); + }); + } + + updateButton(id, config) { + return Tracker.nonreactive(() => { + const btns = this.buttons.get(); + if (btns[id]) { + btns[id] = _.extend(btns[id], config); + return this.buttons.set(btns); + } + }); + } + + getButtonById(id) { + const allButtons = this.buttons.get(); + return allButtons[id]; + } + + getButtons(message, context, group) { + let allButtons = _.toArray(this.buttons.get()); + + if (group) { + allButtons = allButtons.filter((button) => button.group === group); + } + + if (message) { + allButtons = _.compact(_.map(allButtons, function(button) { + if (button.context == null || button.context.includes(context)) { + if (button.condition == null || button.condition(message, context)) { + return button; + } + } + })); + } + return _.sortBy(allButtons, 'order'); + } + + resetButtons() { + return this.buttons.set({}); + } + + async getPermaLink(msgId) { + if (!msgId) { + throw new Error('invalid-parameter'); + } + + const msg = Messages.findOne(msgId) || await call('getSingleMessage', msgId); + if (!msg) { + throw new Error('message-not-found'); + } + const roomData = Rooms.findOne({ + _id: msg.rid, + }); + + if (!roomData) { + throw new Error('room-not-found'); + } + + const subData = Subscriptions.findOne({ rid: roomData._id, 'u._id': Meteor.userId() }); + const roomURL = roomTypes.getURL(roomData.t, subData || roomData); + return `${ roomURL }?msg=${ msgId }`; + } +}; + +Meteor.startup(async function() { + const { chatMessages } = await import('meteor/rocketchat:ui'); + MessageAction.addButton({ + id: 'reply-message', + icon: 'message', + label: 'Reply', + context: ['message', 'message-mobile'], + action() { + const message = this._arguments[1]; + const { input } = chatMessages[message.rid]; + $(input) + .focus() + .data('mention-user', true) + .data('reply', message) + .trigger('dataChange'); + }, + condition(message) { + if (Subscriptions.findOne({ rid: message.rid }) == null) { + return false; + } + + return true; + }, + order: 1, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'edit-message', + icon: 'edit', + label: 'Edit', + context: ['message', 'message-mobile'], + action() { + const messageId = this._arguments[1]._id; + chatMessages[Session.get('openedRoom')].edit(document.getElementById(messageId)); + }, + condition(message) { + if (Subscriptions.findOne({ + rid: message.rid, + }) == null) { + return false; + } + const hasPermission = hasAtLeastOnePermission('edit-message', message.rid); + const isEditAllowed = settings.get('Message_AllowEditing'); + const editOwn = message.u && message.u._id === Meteor.userId(); + if (!(hasPermission || (isEditAllowed && editOwn))) { + return; + } + const blockEditInMinutes = settings.get('Message_AllowEditing_BlockEditInMinutes'); + if (blockEditInMinutes) { + let msgTs; + if (message.ts != null) { + msgTs = moment(message.ts); + } + let currentTsDiff; + if (msgTs != null) { + currentTsDiff = moment().diff(msgTs, 'minutes'); + } + return currentTsDiff < blockEditInMinutes; + } else { + return true; + } + }, + order: 2, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'delete-message', + icon: 'trash', + label: 'Delete', + context: ['message', 'message-mobile'], + color: 'alert', + action() { + const message = this._arguments[1]; + chatMessages[Session.get('openedRoom')].confirmDeleteMsg(message); + }, + condition(message) { + if (Subscriptions.findOne({ rid: message.rid }) == null) { + return false; + } + const forceDelete = hasAtLeastOnePermission('force-delete-message', message.rid); + const hasPermission = hasAtLeastOnePermission('delete-message', message.rid); + const isDeleteAllowed = settings.get('Message_AllowDeleting'); + const deleteOwn = message.u && message.u._id === Meteor.userId(); + if (!(hasPermission || (isDeleteAllowed && deleteOwn) || forceDelete)) { + return; + } + const blockDeleteInMinutes = settings.get('Message_AllowDeleting_BlockDeleteInMinutes'); + if (forceDelete) { + return true; + } + if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) { + let msgTs; + if (message.ts != null) { + msgTs = moment(message.ts); + } + let currentTsDiff; + if (msgTs != null) { + currentTsDiff = moment().diff(msgTs, 'minutes'); + } + return currentTsDiff < blockDeleteInMinutes; + } else { + return true; + } + }, + order: 3, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'permalink', + icon: 'permalink', + label: 'Permalink', + classes: 'clipboard', + context: ['message', 'message-mobile'], + async action(event) { + const message = this._arguments[1]; + const permalink = await MessageAction.getPermaLink(message._id); + if (Meteor.isCordova) { + cordova.plugins.clipboard.copy(permalink); + } else { + $(event.currentTarget).attr('data-clipboard-text', permalink); + } + toastr.success(TAPi18n.__('Copied')); + }, + condition(message) { + if (Subscriptions.findOne({ rid: message.rid }) == null) { + return false; + } + + return true; + }, + order: 4, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'copy', + icon: 'copy', + label: 'Copy', + classes: 'clipboard', + context: ['message', 'message-mobile'], + action(event) { + const message = this._arguments[1].msg; + if (Meteor.isCordova) { + cordova.plugins.clipboard.copy(message); + } else { + $(event.currentTarget).attr('data-clipboard-text', message); + } + toastr.success(TAPi18n.__('Copied')); + }, + condition(message) { + if (Subscriptions.findOne({ rid: message.rid }) == null) { + return false; + } + + return true; + }, + order: 5, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'quote-message', + icon: 'quote', + label: 'Quote', + context: ['message', 'message-mobile'], + action() { + const message = this._arguments[1]; + const { input } = chatMessages[message.rid]; + $(input) + .focus() + .data('mention-user', false) + .data('reply', message) + .trigger('dataChange'); + }, + condition(message) { + if (Subscriptions.findOne({ rid: message.rid }) == null) { + return false; + } + + return true; + }, + order: 6, + group: 'menu', + }); + + + MessageAction.addButton({ + id: 'ignore-user', + icon: 'ban', + label: t('Ignore'), + context: ['message', 'message-mobile'], + action() { + const [, { rid, u: { _id } }] = this._arguments; + Meteor.call('ignoreUser', { rid, userId:_id, ignore: true }, success(() => toastr.success(t('User_has_been_ignored')))); + }, + condition(message) { + const subscription = Subscriptions.findOne({ rid: message.rid }); + + return Meteor.userId() !== message.u._id && !(subscription && subscription.ignored && subscription.ignored.indexOf(message.u._id) > -1); + }, + order: 20, + group: 'menu', + }); + + MessageAction.addButton({ + id: 'unignore-user', + icon: 'ban', + label: t('Unignore'), + context: ['message', 'message-mobile'], + action() { + const [, { rid, u: { _id } }] = this._arguments; + Meteor.call('ignoreUser', { rid, userId:_id, ignore: false }, success(() => toastr.success(t('User_has_been_unignored')))); + + }, + condition(message) { + const subscription = Subscriptions.findOne({ rid: message.rid }); + return Meteor.userId() !== message.u._id && subscription && subscription.ignored && subscription.ignored.indexOf(message.u._id) > -1; + }, + order: 20, + group: 'menu', + }); + + +}); diff --git a/packages/rocketchat-ui-utils/client/lib/RoomHistoryManager.js b/packages/rocketchat-ui-utils/client/lib/RoomHistoryManager.js new file mode 100644 index 000000000000..f58ea4a34d87 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/RoomHistoryManager.js @@ -0,0 +1,277 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Blaze } from 'meteor/blaze'; +import { UserRoles, RoomRoles, ChatMessage, ChatSubscription, ChatRoom } from 'meteor/rocketchat:models'; +import _ from 'underscore'; +import { RoomManager } from './RoomManager'; +import { readMessage } from './readMessages'; + +export const upsertMessage = ({ msg, subscription }) => { + const userId = msg.u && msg.u._id; + + if (subscription && subscription.ignored && subscription.ignored.indexOf(userId) > -1) { + msg.ignored = true; + } + const roles = [ + (userId && UserRoles.findOne(userId, { fields: { roles: 1 } })) || {}, + (userId && RoomRoles.findOne({ rid: msg.rid, 'u._id': userId })) || {}, + ].map((e) => e.roles); + msg.roles = _.union.apply(_.union, roles); + if (msg.t === 'e2e' && !msg.file) { + msg.e2e = 'pending'; + } + + return ChatMessage.upsert({ _id: msg._id }, msg); +}; + +export const RoomHistoryManager = new class { + constructor() { + this.defaultLimit = 50; + this.histories = {}; + } + getRoom(rid) { + if ((this.histories[rid] == null)) { + this.histories[rid] = { + hasMore: new ReactiveVar(true), + hasMoreNext: new ReactiveVar(false), + isLoading: new ReactiveVar(false), + unreadNotLoaded: new ReactiveVar(0), + firstUnread: new ReactiveVar, + loaded: undefined, + }; + } + + return this.histories[rid]; + } + + getMore(rid, limit) { + let ts; + if (limit == null) { limit = this.defaultLimit; } + const room = this.getRoom(rid); + if (room.hasMore.curValue !== true) { + return; + } + + room.isLoading.set(true); + + // ScrollListener.setLoader true + const lastMessage = ChatMessage.findOne({ rid }, { sort: { ts: 1 } }); + // lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) + + if (lastMessage != null) { + ({ ts } = lastMessage); + } else { + ts = undefined; + } + + let ls = undefined; + let typeName = undefined; + + const subscription = ChatSubscription.findOne({ rid }); + if (subscription != null) { + ({ ls } = subscription); + typeName = subscription.t + subscription.name; + } else { + const curRoomDoc = ChatRoom.findOne({ _id: rid }); + typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); + } + + Meteor.call('loadHistory', rid, ts, limit, ls, function(err, result) { + if (err) { + return; + } + + let previousHeight; + const { messages = [] } = result; + room.unreadNotLoaded.set(result.unreadNotLoaded); + room.firstUnread.set(result.firstUnread); + + const wrapper = $('.messages-box .wrapper').get(0); + if (wrapper != null) { + previousHeight = wrapper.scrollHeight; + } + + messages.forEach((msg) => msg.t !== 'command' && upsertMessage({ msg, subscription })); + + if (wrapper) { + const heightDiff = wrapper.scrollHeight - previousHeight; + wrapper.scrollTop += heightDiff; + } + + Meteor.defer(() => { + readMessage.refreshUnreadMark(rid, true); + return RoomManager.updateMentionsMarksOfRoom(typeName); + }); + + room.isLoading.set(false); + if (room.loaded == null) { room.loaded = 0; } + room.loaded += messages.length; + if (messages.length < limit) { + return room.hasMore.set(false); + } + }); + } + + getMoreNext(rid, limit) { + if (limit == null) { limit = this.defaultLimit; } + const room = this.getRoom(rid); + if (room.hasMoreNext.curValue !== true) { + return; + } + + const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); + instance.atBottom = false; + + room.isLoading.set(true); + + const lastMessage = ChatMessage.findOne({ rid }, { sort: { ts: -1 } }); + + let typeName = undefined; + + const subscription = ChatSubscription.findOne({ rid }); + if (subscription != null) { + // const { ls } = subscription; + typeName = subscription.t + subscription.name; + } else { + const curRoomDoc = ChatRoom.findOne({ _id: rid }); + typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); + } + + const { ts } = lastMessage; + + if (ts) { + return Meteor.call('loadNextMessages', rid, ts, limit, function(err, result) { + for (const msg of Array.from((result != null ? result.messages : undefined) || [])) { + if (msg.t !== 'command') { + upsertMessage({ msg, subscription }); + } + } + + Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); + + room.isLoading.set(false); + if (room.loaded == null) { room.loaded = 0; } + + room.loaded += result.messages.length; + if (result.messages.length < limit) { + room.hasMoreNext.set(false); + } + }); + } + } + + getSurroundingMessages(message, limit) { + if (limit == null) { limit = this.defaultLimit; } + if (!(message != null ? message.rid : undefined)) { + return; + } + + const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); + + if (ChatMessage.findOne(message._id)) { + const wrapper = $('.messages-box .wrapper'); + const msgElement = $(`#${ message._id }`, wrapper); + if (msgElement.length === 0) { + return; + } + const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height() / 2); + wrapper.animate({ + scrollTop: pos, + }, 500); + msgElement.addClass('highlight'); + + setTimeout(function() { + const messages = wrapper[0]; + return instance.atBottom = messages.scrollTop >= (messages.scrollHeight - messages.clientHeight); + }); + + return setTimeout(() => msgElement.removeClass('highlight'), 500); + } else { + const room = this.getRoom(message.rid); + room.isLoading.set(true); + ChatMessage.remove({ rid: message.rid }); + + let typeName = undefined; + + const subscription = ChatSubscription.findOne({ rid: message.rid }); + if (subscription) { + // const { ls } = subscription; + typeName = subscription.t + subscription.name; + } else { + const curRoomDoc = ChatRoom.findOne({ _id: message.rid }); + typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); + } + + return Meteor.call('loadSurroundingMessages', message, limit, function(err, result) { + if (!result || !result.messages) { + return; + } + for (const msg of Array.from(result.messages)) { + if (msg.t !== 'command') { + upsertMessage({ msg, subscription }); + } + } + + Meteor.defer(function() { + readMessage.refreshUnreadMark(message.rid, true); + RoomManager.updateMentionsMarksOfRoom(typeName); + const wrapper = $('.messages-box .wrapper'); + const msgElement = $(`#${ message._id }`, wrapper); + const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height() / 2); + wrapper.animate({ + scrollTop: pos, + }, 500); + + msgElement.addClass('highlight'); + + setTimeout(function() { + room.isLoading.set(false); + const messages = wrapper[0]; + instance.atBottom = !result.moreAfter && (messages.scrollTop >= (messages.scrollHeight - messages.clientHeight)); + return 500; + }); + + return setTimeout(() => msgElement.removeClass('highlight'), 500); + }); + if (room.loaded == null) { room.loaded = 0; } + room.loaded += result.messages.length; + room.hasMore.set(result.moreBefore); + return room.hasMoreNext.set(result.moreAfter); + }); + } + } + + hasMore(rid) { + const room = this.getRoom(rid); + return room.hasMore.get(); + } + + hasMoreNext(rid) { + const room = this.getRoom(rid); + return room.hasMoreNext.get(); + } + + + getMoreIfIsEmpty(rid) { + const room = this.getRoom(rid); + + if (room.loaded === undefined) { + return this.getMore(rid); + } + } + + + isLoading(rid) { + const room = this.getRoom(rid); + return room.isLoading.get(); + } + + clear(rid) { + ChatMessage.remove({ rid }); + if (this.histories[rid] != null) { + this.histories[rid].hasMore.set(true); + this.histories[rid].isLoading.set(false); + return this.histories[rid].loaded = undefined; + } + } +}; diff --git a/packages/rocketchat-ui-utils/client/lib/RoomManager.js b/packages/rocketchat-ui-utils/client/lib/RoomManager.js new file mode 100644 index 000000000000..d124c87dde0e --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/RoomManager.js @@ -0,0 +1,331 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { Blaze } from 'meteor/blaze'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Template } from 'meteor/templating'; +import { roomTypes as _roomTypes } from 'meteor/rocketchat:utils'; +import { promises } from 'meteor/rocketchat:promises'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription, CachedCollectionManager } from 'meteor/rocketchat:models'; +import _ from 'underscore'; +import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; +import { mainReady } from './mainReady'; + +const maxRoomsOpen = parseInt(localStorage && localStorage.getItem('rc-maxRoomsOpen')) || 5 ; + +const onDeleteMessageStream = (msg) => ChatMessage.remove({ _id: msg._id }); +const onDeleteMessageBulkStream = ({ rid, ts, excludePinned, users }) => { + const query = { rid, ts }; + if (excludePinned) { + query.pinned = { $ne: true }; + } + if (users && users.length) { + query['u.username'] = { $in: users }; + } + ChatMessage.remove(query); +}; + +export const RoomManager = new function() { + const openedRooms = {}; + const msgStream = new Meteor.Streamer('room-messages'); + const onlineUsers = new ReactiveVar({}); + const Dep = new Tracker.Dependency(); + const Cls = class { + static initClass() { + this.prototype.openedRooms = openedRooms; + this.prototype.onlineUsers = onlineUsers; + this.prototype.computation = Tracker.autorun(() => { + Object.keys(openedRooms).forEach((typeName) => { + const record = openedRooms[typeName]; + if (record.active !== true || record.ready === true) { return; } + const ready = CachedChatRoom.ready.get() && mainReady.get(); + if (ready !== true) { return; } + const user = Meteor.user(); + + const type = typeName.substr(0, 1); + const name = typeName.substr(1); + + const room = Tracker.nonreactive(() => _roomTypes.findRoom(type, name, user)); + + if (room != null) { + openedRooms[typeName].rid = room._id; + RoomHistoryManager.getMoreIfIsEmpty(room._id); + + if (openedRooms[typeName].streamActive !== true) { + openedRooms[typeName].streamActive = true; + msgStream.on(openedRooms[typeName].rid, (msg) => + + promises.run('onClientMessageReceived', msg).then(function(msg) { + + // Should not send message to room if room has not loaded all the current messages + if (RoomHistoryManager.hasMoreNext(openedRooms[typeName].rid) === false) { + + // Do not load command messages into channel + if (msg.t !== 'command') { + const subscription = ChatSubscription.findOne({ rid: openedRooms[typeName].rid }); + upsertMessage({ msg, subscription }); + msg.room = { + type, + name, + }; + } + msg.name = room.name; + Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); + + callbacks.run('streamMessage', msg); + + return window.fireGlobalEvent('new-message', msg); + } + }) + ); + + Notifications.onRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define + Notifications.onRoom(openedRooms[typeName].rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define + } + } + Meteor.defer(() => { + record.ready = true; + Dep.changed(); + }); + }); + }); + } + + getOpenedRoomByRid(rid) { + return Object.keys(openedRooms).map((typeName) => openedRooms[typeName]).find((openedRoom) => openedRoom.rid === rid); + } + + getDomOfRoom(typeName, rid) { + const room = openedRooms[typeName]; + if ((room == null)) { + return; + } + + if ((room.dom == null) && (rid != null)) { + room.dom = document.createElement('div'); + room.dom.classList.add('room-container'); + const contentAsFunc = (content) => () => content; + + room.template = Blaze._TemplateWith({ _id: rid }, contentAsFunc(Template.room)); + Blaze.render(room.template, room.dom); // , nextNode, parentView + } + + return room.dom; + } + + close(typeName) { + if (openedRooms[typeName]) { + if (openedRooms[typeName].rid != null) { + msgStream.removeAllListeners(openedRooms[typeName].rid); + Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define + Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define + } + + openedRooms[typeName].ready = false; + openedRooms[typeName].active = false; + if (openedRooms[typeName].template != null) { + Blaze.remove(openedRooms[typeName].template); + } + delete openedRooms[typeName].dom; + delete openedRooms[typeName].template; + + const { rid } = openedRooms[typeName]; + delete openedRooms[typeName]; + + if (rid != null) { + return RoomHistoryManager.clear(rid); + } + } + } + + + closeOlderRooms() { + if (Object.keys(openedRooms).length <= maxRoomsOpen) { + return; + } + + const roomsToClose = _.sortBy(_.values(openedRooms), 'lastSeen').reverse().slice(maxRoomsOpen); + return Array.from(roomsToClose).map((roomToClose) => + this.close(roomToClose.typeName)); + } + + + closeAllRooms() { + Object.keys(openedRooms).forEach((key) => { + const openedRoom = openedRooms[key]; + this.close(openedRoom.typeName); + }); + } + + + open(typeName) { + if ((openedRooms[typeName] == null)) { + openedRooms[typeName] = { + typeName, + active: false, + ready: false, + unreadSince: new ReactiveVar(undefined), + }; + } + + openedRooms[typeName].lastSeen = new Date; + + if (openedRooms[typeName].ready) { + this.closeOlderRooms(); + } + + if (CachedChatSubscription.ready.get() === true) { + + if (openedRooms[typeName].active !== true) { + openedRooms[typeName].active = true; + if (this.computation) { + this.computation.invalidate(); + } + } + } + + return { + ready() { + Dep.depend(); + return openedRooms[typeName].ready; + }, + }; + } + + existsDomOfRoom(typeName) { + const room = openedRooms[typeName]; + return ((room != null ? room.dom : undefined) != null); + } + + updateUserStatus(user, status, utcOffset) { + const onlineUsersValue = onlineUsers.curValue; + + if (status === 'offline') { + delete onlineUsersValue[user.username]; + } else { + onlineUsersValue[user.username] = { + _id: user._id, + status, + utcOffset, + }; + } + + return onlineUsers.set(onlineUsersValue); + } + + updateMentionsMarksOfRoom(typeName) { + const dom = this.getDomOfRoom(typeName); + if ((dom == null)) { + return; + } + + const ticksBar = $(dom).find('.ticks-bar'); + $(dom).find('.ticks-bar > .tick').remove(); + + const scrollTop = $(dom).find('.messages-box > .wrapper').scrollTop() - 50; + const totalHeight = $(dom).find('.messages-box > .wrapper > ul').height() + 40; + + return $('.messages-box .mention-link-me').each(function(index, item) { + const topOffset = $(item).offset().top + scrollTop; + const percent = (100 / totalHeight) * topOffset; + if ($(item).hasClass('mention-link-all')) { + return ticksBar.append(`
`); + } else { + return ticksBar.append(`
`); + } + }); + } + }; + Cls.initClass(); + return new Cls; +}; + +const loadMissedMessages = function(rid) { + const lastMessage = ChatMessage.findOne({ rid, temp: { $exists: false } }, { sort: { ts: -1 }, limit: 1 }); + if (lastMessage == null) { + return; + } + const subscription = ChatSubscription.findOne({ rid }); + return Meteor.call('loadMissedMessages', rid, lastMessage.ts, (err, result) => { + if (result) { + return Array.from(result).map((item) => promises.run('onClientMessageReceived', item).then((msg) => upsertMessage({ msg, subscription }))); + } else { + return []; + } + }); +}; + +let connectionWasOnline = true; +Tracker.autorun(function() { + const { connected } = Meteor.connection.status(); + + if (connected === true && connectionWasOnline === false && RoomManager.openedRooms != null) { + Object.keys(RoomManager.openedRooms).forEach((key) => { + const value = RoomManager.openedRooms[key]; + if (value.rid != null) { + loadMissedMessages(value.rid); + } + }); + } + return connectionWasOnline = connected; +}); + +Meteor.startup(() => { + + // Reload rooms after login + let currentUsername = undefined; + Tracker.autorun(() => { + const user = Meteor.user(); + if ((currentUsername === undefined) && ((user != null ? user.username : undefined) != null)) { + currentUsername = user.username; + RoomManager.closeAllRooms(); + const { roomTypes } = _roomTypes; + // Reload only if the current route is a channel route + const roomType = Object.keys(roomTypes).find((key) => roomTypes[key].route && roomTypes[key].route.name === FlowRouter.current().route.name); + if (roomType) { + FlowRouter.reload(); + } + } + }); + + ChatMessage.find().observe({ + removed(record) { + if (RoomManager.getOpenedRoomByRid(record.rid) != null) { + const recordBefore = ChatMessage.findOne({ ts: { $lt: record.ts } }, { sort: { ts: -1 } }); + if (recordBefore != null) { + ChatMessage.update({ _id: recordBefore._id }, { $set: { tick: new Date } }); + } + + const recordAfter = ChatMessage.findOne({ ts: { $gt: record.ts } }, { sort: { ts: 1 } }); + if (recordAfter != null) { + return ChatMessage.update({ _id: recordAfter._id }, { $set: { tick: new Date } }); + } + } + }, + }); +}); + +Tracker.autorun(function() { + if (Meteor.userId()) { + return Notifications.onUser('message', function(msg) { + msg.u = + { username: 'rocket.cat' }; + msg.private = true; + + return ChatMessage.upsert({ _id: msg._id }, msg); + }); + } +}); + +callbacks.add('afterLogoutCleanUp', () => RoomManager.closeAllRooms(), callbacks.priority.MEDIUM, 'roommanager-after-logout-cleanup'); + +CachedCollectionManager.onLogin(() => { + Notifications.onUser('subscriptions-changed', (action, sub) => { + ChatMessage.update({ rid: sub.rid }, { $unset : { ignored : '' } }, { multi : true }); + if (sub && sub.ignored) { + ChatMessage.update({ rid: sub.rid, t: { $ne: 'command' }, 'u._id': { $in : sub.ignored } }, { $set: { ignored : true } }, { multi : true }); + } + }); +}); diff --git a/packages/rocketchat-ui-utils/client/lib/SideNav.js b/packages/rocketchat-ui-utils/client/lib/SideNav.js new file mode 100644 index 000000000000..6816c875b2fb --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/SideNav.js @@ -0,0 +1,135 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Session } from 'meteor/session'; +import { roomTypes } from 'meteor/rocketchat:utils'; +import { Subscriptions } from 'meteor/rocketchat:models'; +import { AccountBox } from './AccountBox'; + +export const SideNav = new class { + constructor() { + this.initiated = false; + this.sideNav = {}; + this.flexNav = {}; + this.animating = false; + this.openQueue = []; + } + + toggleFlex(status, callback) { + if (this.animating === true) { + return; + } + this.animating = true; + if (status === -1 || (status !== 1 && this.flexNav.opened)) { + this.flexNav.opened = false; + this.flexNav.addClass('animated-hidden'); + } else { + this.flexNav.opened = true; + setTimeout(() => this.flexNav.removeClass('animated-hidden'), 50); + } + return setTimeout(() => { + this.animating = false; + return typeof callback === 'function' && callback(); + }, 500); + } + closeFlex(callback = null) { + let subscription; + if (!roomTypes.getTypes().filter(function(i) { + return i.route; + }).map(function(i) { + return i.route.name; + }).includes(FlowRouter.current().route.name)) { + subscription = Subscriptions.findOne({ + rid: Session.get('openedRoom'), + }); + if (subscription != null) { + roomTypes.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); + } else { + FlowRouter.go('home'); + } + } + if (this.animating === true) { + return; + } + return this.toggleFlex(-1, callback); + } + flexStatus() { + return this.flexNav.opened; + } + setFlex(template, data) { + if (data == null) { + data = {}; + } + Session.set('flex-nav-template', template); + return Session.set('flex-nav-data', data); + } + getFlex() { + return { + template: Session.get('flex-nav-template'), + data: Session.get('flex-nav-data'), + }; + } + + toggleCurrent() { + if (this.flexNav && this.flexNav.opened) { + return this.closeFlex(); + } else { + return AccountBox.toggle(); + } + } + focusInput() { + const sideNavDivs = Array.from(this.sideNav[0].children).filter((el) => el.tagName === 'DIV' && !el.classList.contains('hidden')); + let highestZidx = 0; + let highestZidxElem; + sideNavDivs.forEach((el) => { + const zIndex = Number(window.getComputedStyle(el).zIndex); + if (zIndex > highestZidx) { + highestZidx = zIndex; + highestZidxElem = el; + } + }); + setTimeout(() => { + const ref = highestZidxElem.querySelector('input'); + return ref && ref.focus(); + }, 200); + } + validate() { + const invalid = []; + this.sideNav.find('input.required').each(function() { + if (!this.value.length) { + return invalid.push($(this).prev('label').html()); + } + }); + if (invalid.length) { + return invalid; + } + return false; + } + + openFlex(callback) { + if (!this.initiated) { + return this.openQueue.push({ + config: this.getFlex(), + callback, + }); + } + if (this.animating === true) { + return; + } + AccountBox.close(); + this.toggleFlex(1, callback); + return this.focusInput(); + } + + init() { + this.sideNav = $('.sidebar'); + this.flexNav = this.sideNav.find('.flex-nav'); + this.setFlex(''); + this.initiated = true; + if (this.openQueue.length > 0) { + this.openQueue.forEach((item) => { + this.setFlex(item.config.template, item.config.data); + return this.openFlex(item.callback); + }); + return this.openQueue = []; + } + } +}; diff --git a/packages/rocketchat-lib/client/lib/callMethod.js b/packages/rocketchat-ui-utils/client/lib/callMethod.js similarity index 90% rename from packages/rocketchat-lib/client/lib/callMethod.js rename to packages/rocketchat-ui-utils/client/lib/callMethod.js index 88af0fcc6539..44d418babe0e 100644 --- a/packages/rocketchat-lib/client/lib/callMethod.js +++ b/packages/rocketchat-ui-utils/client/lib/callMethod.js @@ -1,4 +1,5 @@ import { Meteor } from 'meteor/meteor'; +import { handleError } from 'meteor/rocketchat:utils'; /** * Wraps a Meteor method into a Promise. diff --git a/packages/rocketchat-ui-utils/client/lib/mainReady.js b/packages/rocketchat-ui-utils/client/lib/mainReady.js new file mode 100644 index 000000000000..b59b8cac677e --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/mainReady.js @@ -0,0 +1,3 @@ +import { ReactiveVar } from 'meteor/reactive-var'; + +export let mainReady = new ReactiveVar(false); // eslint-disable-line diff --git a/packages/rocketchat-ui-utils/client/lib/menu.js b/packages/rocketchat-ui-utils/client/lib/menu.js new file mode 100644 index 000000000000..287ccd03354a --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/menu.js @@ -0,0 +1,225 @@ +import { Session } from 'meteor/session'; +import _ from 'underscore'; +import EventEmitter from 'wolfy87-eventemitter'; +import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; +import { isRtl } from 'meteor/rocketchat:utils'; + +const sideNavW = 280; +const map = (x, in_min, in_max, out_min, out_max) => (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + +export const menu = new class extends EventEmitter { + constructor() { + super(); + this._open = false; + this.updateUnreadBars = _.throttle(() => { + if (this.list == null) { + return; + } + const listOffset = this.list.offset(); + const listHeight = this.list.height(); + let showTop = false; + let showBottom = false; + $('li.has-alert').each(function() { + if ($(this).offset().top < listOffset.top - $(this).height()) { + showTop = true; + } + if ($(this).offset().top > listOffset.top + listHeight) { + return showBottom = true; + } + }); + if (showTop === true) { + $('.top-unread-rooms').removeClass('hidden'); + } else { + $('.top-unread-rooms').addClass('hidden'); + } + if (showBottom === true) { + return $('.bottom-unread-rooms').removeClass('hidden'); + } else { + return $('.bottom-unread-rooms').addClass('hidden'); + } + }, 200); + this.sideNavW = sideNavW; + } + get isRtl() { + return isRtl(localStorage.getItem('userLanguage')); + } + touchstart(e) { + this.movestarted = false; + this.blockmove = false; + this.touchstartX = undefined; + this.touchstartY = undefined; + this.diff = 0; + if (e.target === this.sidebarWrap[0] || $(e.target).closest('.main-content').length > 0) { + this.closePopover(); + this.touchstartX = e.touches[0].clientX; + this.touchstartY = e.touches[0].clientY; + this.mainContent = $('.main-content'); + } + } + touchmove(e) { + if (this.touchstartX == null) { + return; + } + lazyloadtick(); + const [touch] = e.touches; + const diffX = touch.clientX - this.touchstartX; + const diffY = touch.clientY - this.touchstartY; + const absX = Math.abs(diffX); + const absY = Math.abs(diffY); + + if (!this.movestarted && absY > 5) { + this.blockmove = true; + } + if (this.blockmove) { + return; + } + this.sidebar.css('transition', 'none'); + this.sidebarWrap.css('transition', 'none'); + if (this.movestarted === true || absX > 5) { + this.movestarted = true; + if (this.isRtl) { + if (this.isOpen()) { + this.diff = -sideNavW + diffX; + } else { + this.diff = diffX; + } + if (this.diff < -sideNavW) { + this.diff = -sideNavW; + } + if (this.diff > 0) { + this.diff = 0; + } + } else { + if (this.isOpen()) { + this.diff = sideNavW + diffX; + } else { + this.diff = diffX; + } + if (this.diff > sideNavW) { + this.diff = sideNavW; + } + if (this.diff < 0) { + this.diff = 0; + } + } + // if (map((this.diff / sideNavW), 0, 1, -.1, .8) > 0) { + this.sidebar.css('box-shadow', '0 0 15px 1px rgba(0,0,0,.3)'); + // this.sidebarWrap.css('z-index', '9998'); + this.translate(this.diff); + // } + } + } + translate(diff, width = sideNavW) { + if (diff === undefined) { + diff = this.isRtl ? -1 * sideNavW : sideNavW; + } + this.sidebarWrap.css('width', '100%'); + this.wrapper.css('overflow', 'hidden'); + this.sidebarWrap.css('background-color', '#000'); + this.sidebarWrap.css('opacity', map((Math.abs(diff) / width), 0, 1, -.1, .8).toFixed(2)); + this.isRtl ? this.sidebar.css('transform', `translate3d(${ (sideNavW + diff).toFixed(3) }px, 0 , 0)`) : this.sidebar.css('transform', `translate3d(${ (diff - sideNavW).toFixed(3) }px, 0 , 0)`); + } + touchend() { + const [max, min] = [sideNavW * .76, sideNavW * .24]; + if (this.movestarted !== true) { + return; + } + this.movestarted = false; + if (this.isRtl) { + if (this.isOpen()) { + return this.diff >= -max ? this.close() : this.open(); + } else if (this.diff <= -min) { + return this.open(); + } + return this.close(); + } + if (this.isOpen()) { + if (this.diff >= max) { + return this.open(); + } + return this.close(); + } + if (this.diff >= min) { + return this.open(); + } + return this.close(); + } + init() { + this.sidebar = this.menu = $('.sidebar'); + this.sidebarWrap = $('.sidebar-wrap'); + this.wrapper = $('.messages-box > .wrapper'); + const ignore = (fn) => (event) => document.body.clientWidth <= 780 && fn(event); + + document.body.addEventListener('touchstart', ignore((e) => this.touchstart(e))); + document.body.addEventListener('touchmove', ignore((e) => this.touchmove(e))); + document.body.addEventListener('touchend', ignore((e) => this.touchend(e))); + this.sidebarWrap.on('click', ignore((e) => { + e.target === this.sidebarWrap[0] && this.isOpen() && this.emit('clickOut', e); + })); + this.on('close', () => { + this.sidebarWrap.css('width', ''); + // this.sidebarWrap.css('z-index', ''); + this.sidebarWrap.css('background-color', ''); + this.sidebar.css('transform', ''); + this.sidebar.css('box-shadow', ''); + this.sidebar.css('transition', ''); + this.sidebarWrap.css('transition', ''); + this.wrapper && this.wrapper.css('overflow', ''); + }); + this.on('open', ignore(() => { + this.sidebar.css('box-shadow', '0 0 15px 1px rgba(0,0,0,.3)'); + // this.sidebarWrap.css('z-index', '9998'); + this.translate(); + })); + this.mainContent = $('.main-content'); + + this.list = $('.rooms-list'); + this._open = false; + Session.set('isMenuOpen', this._open); + } + closePopover() { + return this.menu.find('[data-popover="anchor"]:checked').prop('checked', false).length > 0; + } + isOpen() { + return Session.get('isMenuOpen'); + } + open() { + this._open = true; + Session.set('isMenuOpen', this._open); + this.emit('open'); + } + + close() { + this._open = false; + Session.set('isMenuOpen', this._open); + this.emit('close'); + } + + toggle() { + return this.isOpen() ? this.close() : this.open(); + } +}; + + +let passClosePopover = false; + +menu.on('clickOut', function() { + if (!menu.closePopover()) { + passClosePopover = true; + menu.close(); + } +}); + +menu.on('close', function() { + if (!menu.sidebar) { + return; + } + + menu.sidebar.css('transition', ''); + menu.sidebarWrap.css('transition', ''); + if (passClosePopover) { + passClosePopover = false; + return; + } + menu.closePopover(); +}); diff --git a/packages/rocketchat-ui-utils/client/lib/messageBox.js b/packages/rocketchat-ui-utils/client/lib/messageBox.js new file mode 100644 index 000000000000..ba9a3a1170d7 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/messageBox.js @@ -0,0 +1,65 @@ +import EventEmitter from 'wolfy87-eventemitter'; + +export const messageBox = new EventEmitter; + +messageBox.actions = new class { + constructor() { + this.actions = {}; + } + + /* Add a action to messagebox + @param group + @param label + @param config + icon: icon class + action: action function + condition: condition to display the action + */ + + add(group, label, config) { + if (!group && !label && !config) { + return; + } + + if (!this.actions[group]) { + this.actions[group] = []; + } + + const actionExists = this.actions[group].find((action) => action.label === label); + + if (actionExists) { + return; + } + + this.actions[group].push({ ...config, label }); + } + remove(group, expression) { + if (!group || !this.actions[group]) { + return false; + } + return (this.actions[group] = this.actions[group].filter((action) => expression.test(action.id))); + } + get(group) { + if (!group) { + return Object.keys(this.actions).reduce((ret, key) => { + const actions = this.actions[key].filter((action) => !action.condition || action.condition()); + if (actions.length) { + ret[key] = actions; + } + return ret; + }, {}); + } + + return this.actions[group].filter((action) => !action.condition || action.condition()); + } + + getById(id) { + const messageActions = this.actions; + let actions = []; + Object.keys(messageActions).forEach(function(action) { + actions = actions.concat(messageActions[action]); + }); + + return actions.filter((action) => action.id === id); + } +}; diff --git a/packages/rocketchat-ui-utils/client/lib/modal.js b/packages/rocketchat-ui-utils/client/lib/modal.js index 9b8ea32cf070..1046165094f2 100644 --- a/packages/rocketchat-ui-utils/client/lib/modal.js +++ b/packages/rocketchat-ui-utils/client/lib/modal.js @@ -2,7 +2,7 @@ import './modal.html'; import { Meteor } from 'meteor/meteor'; import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; -import { t, getUserPreference } from 'meteor/rocketchat:utils'; +import { t, getUserPreference, handleError } from 'meteor/rocketchat:utils'; export const modal = { renderedModal: null, diff --git a/packages/rocketchat-ui-utils/client/lib/popover.html b/packages/rocketchat-ui-utils/client/lib/popover.html new file mode 100644 index 000000000000..9ebb7a82c547 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/popover.html @@ -0,0 +1,34 @@ + diff --git a/packages/rocketchat-ui-utils/client/lib/popover.js b/packages/rocketchat-ui-utils/client/lib/popover.js new file mode 100644 index 000000000000..e4ce3c2e4b5a --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/popover.js @@ -0,0 +1,266 @@ +import './popover.html'; +import { Meteor } from 'meteor/meteor'; +import { Blaze } from 'meteor/blaze'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Template } from 'meteor/templating'; +import { TAPi18n } from 'meteor/tap:i18n'; +import { isRtl, handleError } from 'meteor/rocketchat:utils'; +import { ChatSubscription } from 'meteor/rocketchat:models'; +import _ from 'underscore'; +import { hide, leave } from './ChannelActions'; +import { modal } from './modal'; +import { messageBox } from './messageBox'; +import { MessageAction } from './MessageAction'; +import { RoomManager } from './RoomManager'; + +export const popover = { + renderedPopover: null, + open({ currentTarget, ...config }) { + // Popover position must be computed as soon as possible, avoiding DOM changes over currentTarget + const data = { + targetRect: currentTarget && currentTarget.getBoundingClientRect && currentTarget.getBoundingClientRect(), + ...config, + }; + this.renderedPopover = Blaze.renderWithData(Template.popover, data, document.body); + }, + close() { + if (!this.renderedPopover) { + return false; + } + + Blaze.remove(this.renderedPopover); + + const { activeElement } = this.renderedPopover.dataVar.curValue; + if (activeElement) { + $(activeElement).removeClass('active'); + } + }, +}; + +Template.popover.helpers({ + hasAction() { + return !!this.action; + }, +}); + +Template.popover.onRendered(function() { + if (this.data.onRendered) { + this.data.onRendered(); + } + + $('.rc-popover').click(function(e) { + if (e.currentTarget === e.target) { + popover.close(); + } + }); + const { offsetVertical = 0, offsetHorizontal = 0 } = this.data; + const { activeElement } = this.data; + const popoverContent = this.firstNode.children[0]; + const position = _.throttle(() => { + + const direction = typeof this.data.direction === 'function' ? this.data.direction() : this.data.direction; + + const verticalDirection = /top/.test(direction) ? 'top' : 'bottom'; + const rtlDirection = isRtl() ^ /inverted/.test(direction) ? 'left' : 'right'; + const rightDirection = /right/.test(direction) ? 'right' : rtlDirection; + const horizontalDirection = /left/.test(direction) ? 'left' : rightDirection; + + const position = typeof this.data.position === 'function' ? this.data.position() : this.data.position; + const customCSSProperties = typeof this.data.customCSSProperties === 'function' ? this.data.customCSSProperties() : this.data.customCSSProperties; + + const mousePosition = typeof this.data.mousePosition === 'function' ? this.data.mousePosition() : this.data.mousePosition || { + x: this.data.targetRect[horizontalDirection === 'left' ? 'right' : 'left'], + y: this.data.targetRect[verticalDirection], + }; + const offsetWidth = offsetHorizontal * (horizontalDirection === 'left' ? 1 : -1); + const offsetHeight = offsetVertical * (verticalDirection === 'bottom' ? 1 : -1); + + if (position) { + popoverContent.style.top = `${ position.top }px`; + popoverContent.style.left = `${ position.left }px`; + } else { + const clientHeight = this.data.targetRect.height; + const popoverWidth = popoverContent.offsetWidth; + const popoverHeight = popoverContent.offsetHeight; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + + let top = mousePosition.y - clientHeight + offsetHeight; + + if (verticalDirection === 'top') { + top = mousePosition.y - popoverHeight + offsetHeight; + + if (top < 0) { + top = 10 + offsetHeight; + } + } + + if (top + popoverHeight > windowHeight) { + top = windowHeight - 10 - popoverHeight - offsetHeight; + } + + let left = mousePosition.x - popoverWidth + offsetWidth; + + if (horizontalDirection === 'right') { + left = mousePosition.x + offsetWidth; + } + + if (left + popoverWidth >= windowWidth) { + left = mousePosition.x - popoverWidth + offsetWidth; + } + + if (left <= 0) { + left = mousePosition.x + offsetWidth; + } + + popoverContent.style.top = `${ top }px`; + popoverContent.style.left = `${ left }px`; + } + + if (customCSSProperties) { + Object.keys(customCSSProperties).forEach(function(property) { + popoverContent.style[property] = customCSSProperties[property]; + }); + } + + const realTop = Number(popoverContent.style.top.replace('px', '')); + if (realTop + popoverContent.offsetHeight > window.innerHeight) { + popoverContent.style.overflow = 'scroll'; + popoverContent.style.bottom = 0; + popoverContent.className = 'rc-popover__content rc-popover__content-scroll'; + } + + if (activeElement) { + $(activeElement).addClass('active'); + } + + popoverContent.style.opacity = 1; + }, 50); + $(window).on('resize', position); + position(); + this.position = position; + + this.firstNode.style.visibility = 'visible'; +}); + +Template.popover.onDestroyed(function() { + if (this.data.onDestroyed) { + this.data.onDestroyed(); + } + $(window).off('resize', this.position); +}); + +Template.popover.events({ + 'click .js-action'(e, instance) { + !this.action || this.action.call(this, e, instance.data.data); + popover.close(); + }, + 'click .js-close'() { + popover.close(); + }, + 'click [data-type="messagebox-action"]'(event, t) { + const { id } = event.currentTarget.dataset; + const action = messageBox.actions.getById(id); + if ((action[0] != null ? action[0].action : undefined) != null) { + action[0].action({ rid: t.data.data.rid, messageBox: document.querySelector('.rc-message-box'), element: event.currentTarget, event }); + if (id !== 'audio-message') { + popover.close(); + } + } + }, + 'click [data-type="message-action"]'(e, t) { + const button = MessageAction.getButtonById(e.currentTarget.dataset.id); + if ((button != null ? button.action : undefined) != null) { + button.action.call(t.data.data, e, t.data.instance); + popover.close(); + return false; + } + + if (e.currentTarget.dataset.id === 'report-abuse') { + const message = t.data.data._arguments[1]; + modal.open({ + title: TAPi18n.__('Report_this_message_question_mark'), + text: message.msg, + inputPlaceholder: TAPi18n.__('Why_do_you_want_to_report_question_mark'), + type: 'input', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: TAPi18n.__('Report_exclamation_mark'), + cancelButtonText: TAPi18n.__('Cancel'), + closeOnConfirm: false, + html: false, + }, (inputValue) => { + if (inputValue === false) { + return false; + } + + if (inputValue === '') { + modal.showInputError(TAPi18n.__('You_need_to_write_something')); + return false; + } + + Meteor.call('reportMessage', message._id, inputValue); + + modal.open({ + title: TAPi18n.__('Report_sent'), + text: TAPi18n.__('Thank_you_exclamation_mark '), + type: 'success', + timer: 1000, + showConfirmButton: false, + }); + }); + popover.close(); + } + }, + 'click [data-type="sidebar-item"]'(e, instance) { + popover.close(); + const { rid, name, template } = instance.data.data; + const action = e.currentTarget.dataset.id; + + if (action === 'hide') { + hide(template, rid, name); + } + + if (action === 'leave') { + leave(template, rid, name); + } + + if (action === 'read') { + Meteor.call('readMessages', rid); + return false; + } + + if (action === 'unread') { + Meteor.call('unreadMessages', null, rid, function(error) { + if (error) { + return handleError(error); + } + + const subscription = ChatSubscription.findOne({ rid }); + if (subscription == null) { + return; + } + RoomManager.close(subscription.t + subscription.name); + + FlowRouter.go('home'); + }); + + return false; + } + + if (action === 'favorite') { + Meteor.call('toggleFavorite', rid, !$(e.currentTarget).hasClass('rc-popover__item--star-filled'), function(err) { + popover.close(); + if (err) { + handleError(err); + } + }); + + return false; + } + }, +}); + +Template.popover.helpers({ + isSafariIos: /iP(ad|hone|od).+Version\/[\d\.]+.*Safari/i.test(navigator.userAgent), +}); diff --git a/packages/rocketchat-ui-utils/client/lib/readMessages.js b/packages/rocketchat-ui-utils/client/lib/readMessages.js new file mode 100644 index 000000000000..5186d79cbb97 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/readMessages.js @@ -0,0 +1,216 @@ +import { Meteor } from 'meteor/meteor'; +import { Session } from 'meteor/session'; +import { ChatSubscription, ChatMessage } from 'meteor/rocketchat:models'; +import { RoomHistoryManager } from './RoomHistoryManager'; +import { RoomManager } from './RoomManager'; +import _ from 'underscore'; + +/* DEFINITIONS +- If window loses focus user needs to scroll or click/touch some place +- On hit ESC enable read, force read of current room and remove unread mark +- When user change room disable read until user interaction +- Only read if mark of *first-unread* is visible for user or if flag *force* was passed +- Always read the opened room +- The default method *read* has a delay of 2000ms to prevent multiple reads and to user be able to see the mark +*/ + +// Meteor.startup -> +// window.addEventListener 'focus', -> +// readMessage.refreshUnreadMark(undefined, true) + +export const readMessage = new class { + constructor() { + this.debug = false; + this.callbacks = []; + this.read = _.debounce((force) => this.readNow(force), 1000); + this.canReadMessage = false; + } + + readNow(force) { + if (force == null) { force = false; } + if (this.debug) { console.log('--------------'); } + if (this.debug) { console.log('readMessage -> readNow init process force:', force); } + + const self = this; + + self.refreshUnreadMark(); + + if ((force !== true) && (this.canReadMessage === false)) { + if (this.debug) { console.log('readMessage -> readNow canceled by canReadMessage: false'); } + return; + } + + const rid = Session.get('openedRoom'); + if (rid == null) { + if (this.debug) { console.log('readMessage -> readNow canceled, no rid informed'); } + return; + } + + if (force === true) { + if (this.debug) { console.log('readMessage -> readNow via force rid:', rid); } + return Meteor.call('readMessages', rid, function() { + RoomHistoryManager.getRoom(rid).unreadNotLoaded.set(0); + self.refreshUnreadMark(); + return self.fireRead(rid); + }); + } + + const subscription = ChatSubscription.findOne({ rid }); + if (subscription == null) { + if (this.debug) { console.log('readMessage -> readNow canceled, no subscription found for rid:', rid); } + return; + } + + if ((subscription.alert === false) && (subscription.unread === 0)) { + if (this.debug) { console.log('readMessage -> readNow canceled, alert', subscription.alert, 'and unread', subscription.unread); } + return; + } + + const room = RoomManager.getOpenedRoomByRid(rid); + if (room == null) { + if (this.debug) { console.log('readMessage -> readNow canceled, no room found for typeName:', subscription.t + subscription.name); } + return; + } + + // Only read messages if user saw the first unread message + const unreadMark = $('.message.first-unread'); + if (unreadMark.length > 0) { + const position = unreadMark.position(); + const visible = (position != null ? position.top : undefined) >= 0; + if (!visible && (room.unreadSince.get() != null)) { + if (this.debug) { console.log('readMessage -> readNow canceled, unread mark visible:', visible, 'unread since exists', (room.unreadSince.get() != null)); } + return; + } + // if unread mark is not visible and there is more more not loaded unread messages + } else if (RoomHistoryManager.getRoom(rid).unreadNotLoaded.get() > 0) { + return; + } + + if (this.debug) { console.log('readMessage -> readNow rid:', rid); } + Meteor.call('readMessages', rid, function() { + RoomHistoryManager.getRoom(rid).unreadNotLoaded.set(0); + self.refreshUnreadMark(); + return self.fireRead(rid); + }); + } + + disable() { + return this.canReadMessage = false; + } + + enable() { + return this.canReadMessage = document.hasFocus(); + } + + isEnable() { + return this.canReadMessage === true; + } + + onRead(cb) { + return this.callbacks.push(cb); + } + + fireRead(rid) { + return Array.from(this.callbacks).map((cb) => cb(rid)); + } + + refreshUnreadMark(rid, force) { + if (rid == null) { rid = Session.get('openedRoom'); } + if (rid == null) { + return; + } + + const subscription = ChatSubscription.findOne({ rid }, { reactive: false }); + if (subscription == null) { + return; + } + + const room = RoomManager.openedRooms[subscription.t + subscription.name]; + if (room == null) { + return; + } + + if (!subscription.alert && (subscription.unread === 0)) { + room.unreadSince.set(undefined); + return; + } + + if ((force == null) && (subscription.rid === Session.get('openedRoom')) && document.hasFocus()) { + return; + } + + + let lastReadRecord = ChatMessage.findOne({ + rid: subscription.rid, + ts: { + $lt: subscription.ls, + }, + }, { + sort: { + ts: -1, + }, + }); + + if ((lastReadRecord == null) && (RoomHistoryManager.getRoom(room.rid).unreadNotLoaded.get() === 0)) { + lastReadRecord = + { ts: new Date(0) }; + } + + if ((lastReadRecord != null) || (RoomHistoryManager.getRoom(room.rid).unreadNotLoaded.get() > 0)) { + room.unreadSince.set(subscription.ls); + } else { + room.unreadSince.set(undefined); + } + + if (lastReadRecord != null) { + const firstUnreadRecord = ChatMessage.findOne({ + rid: subscription.rid, + ts: { + $gt: lastReadRecord.ts, + }, + 'u._id': { + $ne: Meteor.userId(), + }, + }, { + sort: { + ts: 1, + }, + }); + + if (firstUnreadRecord != null) { + room.unreadFirstId = firstUnreadRecord._id; + $(room.dom).find(`.message.first-unread:not(#${ firstUnreadRecord._id })`).removeClass('first-unread'); + $(room.dom).find(`.message#${ firstUnreadRecord._id }`).addClass('first-unread'); + } + } + } +}; + + +Meteor.startup(function() { + $(window).on('blur', () => readMessage.disable()); + + $(window).on('focus', () => { + readMessage.enable(); + return readMessage.read(); + }); + + $(window).on('click', () => { + readMessage.enable(); + return readMessage.read(); + }); + + $(window).on('touchend', () => { + readMessage.enable(); + return readMessage.read(); + }); + + $(window).on('keyup', (e) => { + const key = e.which; + if (key === 27) { + readMessage.enable(); + readMessage.readNow(true); + return $('.message.first-unread').removeClass('first-unread'); + } + }); +}); diff --git a/packages/rocketchat-ui-message/client/renderMessageBody.js b/packages/rocketchat-ui-utils/client/lib/renderMessageBody.js similarity index 79% rename from packages/rocketchat-ui-message/client/renderMessageBody.js rename to packages/rocketchat-ui-utils/client/lib/renderMessageBody.js index cc2a1f261df9..8c3614735fc5 100644 --- a/packages/rocketchat-ui-message/client/renderMessageBody.js +++ b/packages/rocketchat-ui-utils/client/lib/renderMessageBody.js @@ -1,4 +1,4 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { callbacks } from 'meteor/rocketchat:callbacks'; import s from 'underscore.string'; export const renderMessageBody = (msg) => { @@ -8,7 +8,7 @@ export const renderMessageBody = (msg) => { msg.html = s.escapeHTML(msg.html); } - const message = RocketChat.callbacks.run('renderMessage', msg); + const message = callbacks.run('renderMessage', msg); if (message.tokens && message.tokens.length > 0) { // Unmounting tokens(LIFO) diff --git a/packages/rocketchat-ui-utils/package.js b/packages/rocketchat-ui-utils/package.js index 011cfebed1da..c98cabf93a0d 100644 --- a/packages/rocketchat-ui-utils/package.js +++ b/packages/rocketchat-ui-utils/package.js @@ -10,7 +10,15 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', + 'kadira:flow-router', + 'kadira:blaze-layout', 'rocketchat:utils', + 'rocketchat:promises', + 'rocketchat:notifications', + 'rocketchat:authorization', + 'rocketchat:streamer', + 'rocketchat:models', + 'rocketchat:lazy-load', ]); api.mainModule('client/index.js', 'client'); }); diff --git a/packages/rocketchat-ui/client/lib/RoomHistoryManager.js b/packages/rocketchat-ui/client/lib/RoomHistoryManager.js index e36aaa2d86e7..767167f86ad7 100644 --- a/packages/rocketchat-ui/client/lib/RoomHistoryManager.js +++ b/packages/rocketchat-ui/client/lib/RoomHistoryManager.js @@ -1,274 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Blaze } from 'meteor/blaze'; -import _ from 'underscore'; +import { upsertMessage, RoomHistoryManager as _RoomHistoryManager } from 'meteor/rocketchat:ui-utils'; -export const upsertMessage = ({ msg, subscription }) => { - const userId = msg.u && msg.u._id; - - if (subscription && subscription.ignored && subscription.ignored.indexOf(userId) > -1) { - msg.ignored = true; - } - const roles = [ - (userId && UserRoles.findOne(userId, { fields: { roles: 1 } })) || {}, - (userId && RoomRoles.findOne({ rid: msg.rid, 'u._id': userId })) || {}, - ].map((e) => e.roles); - msg.roles = _.union.apply(_.union, roles); - if (msg.t === 'e2e' && !msg.file) { - msg.e2e = 'pending'; - } - - return ChatMessage.upsert({ _id: msg._id }, msg); +export { + upsertMessage, }; -RoomHistoryManager = new class { - constructor() { - this.defaultLimit = 50; - this.histories = {}; - } - getRoom(rid) { - if ((this.histories[rid] == null)) { - this.histories[rid] = { - hasMore: new ReactiveVar(true), - hasMoreNext: new ReactiveVar(false), - isLoading: new ReactiveVar(false), - unreadNotLoaded: new ReactiveVar(0), - firstUnread: new ReactiveVar, - loaded: undefined, - }; - } - - return this.histories[rid]; - } - - getMore(rid, limit) { - let ts; - if (limit == null) { limit = this.defaultLimit; } - const room = this.getRoom(rid); - if (room.hasMore.curValue !== true) { - return; - } - - room.isLoading.set(true); - - // ScrollListener.setLoader true - const lastMessage = ChatMessage.findOne({ rid }, { sort: { ts: 1 } }); - // lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) - - if (lastMessage != null) { - ({ ts } = lastMessage); - } else { - ts = undefined; - } - - let ls = undefined; - let typeName = undefined; - - const subscription = ChatSubscription.findOne({ rid }); - if (subscription != null) { - ({ ls } = subscription); - typeName = subscription.t + subscription.name; - } else { - const curRoomDoc = ChatRoom.findOne({ _id: rid }); - typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); - } - - Meteor.call('loadHistory', rid, ts, limit, ls, function(err, result) { - if (err) { - return; - } - - let previousHeight; - const { messages = [] } = result; - room.unreadNotLoaded.set(result.unreadNotLoaded); - room.firstUnread.set(result.firstUnread); - - const wrapper = $('.messages-box .wrapper').get(0); - if (wrapper != null) { - previousHeight = wrapper.scrollHeight; - } - - messages.forEach((msg) => msg.t !== 'command' && upsertMessage({ msg, subscription })); - - if (wrapper) { - const heightDiff = wrapper.scrollHeight - previousHeight; - wrapper.scrollTop += heightDiff; - } - - Meteor.defer(() => { - readMessage.refreshUnreadMark(rid, true); - return RoomManager.updateMentionsMarksOfRoom(typeName); - }); - - room.isLoading.set(false); - if (room.loaded == null) { room.loaded = 0; } - room.loaded += messages.length; - if (messages.length < limit) { - return room.hasMore.set(false); - } - }); - } - - getMoreNext(rid, limit) { - if (limit == null) { limit = this.defaultLimit; } - const room = this.getRoom(rid); - if (room.hasMoreNext.curValue !== true) { - return; - } - - const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); - instance.atBottom = false; - - room.isLoading.set(true); - - const lastMessage = ChatMessage.findOne({ rid }, { sort: { ts: -1 } }); - - let typeName = undefined; - - const subscription = ChatSubscription.findOne({ rid }); - if (subscription != null) { - // const { ls } = subscription; - typeName = subscription.t + subscription.name; - } else { - const curRoomDoc = ChatRoom.findOne({ _id: rid }); - typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); - } - - const { ts } = lastMessage; - - if (ts) { - return Meteor.call('loadNextMessages', rid, ts, limit, function(err, result) { - for (const msg of Array.from((result != null ? result.messages : undefined) || [])) { - if (msg.t !== 'command') { - upsertMessage({ msg, subscription }); - } - } - - Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); - - room.isLoading.set(false); - if (room.loaded == null) { room.loaded = 0; } - - room.loaded += result.messages.length; - if (result.messages.length < limit) { - room.hasMoreNext.set(false); - } - }); - } - } - - getSurroundingMessages(message, limit) { - if (limit == null) { limit = this.defaultLimit; } - if (!(message != null ? message.rid : undefined)) { - return; - } - - const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); - - if (ChatMessage.findOne(message._id)) { - const wrapper = $('.messages-box .wrapper'); - const msgElement = $(`#${ message._id }`, wrapper); - if (msgElement.length === 0) { - return; - } - const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height() / 2); - wrapper.animate({ - scrollTop: pos, - }, 500); - msgElement.addClass('highlight'); - - setTimeout(function() { - const messages = wrapper[0]; - return instance.atBottom = messages.scrollTop >= (messages.scrollHeight - messages.clientHeight); - }); - - return setTimeout(() => msgElement.removeClass('highlight'), 500); - } else { - const room = this.getRoom(message.rid); - room.isLoading.set(true); - ChatMessage.remove({ rid: message.rid }); - - let typeName = undefined; - - const subscription = ChatSubscription.findOne({ rid: message.rid }); - if (subscription) { - // const { ls } = subscription; - typeName = subscription.t + subscription.name; - } else { - const curRoomDoc = ChatRoom.findOne({ _id: message.rid }); - typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); - } - - return Meteor.call('loadSurroundingMessages', message, limit, function(err, result) { - if (!result || !result.messages) { - return; - } - for (const msg of Array.from(result.messages)) { - if (msg.t !== 'command') { - upsertMessage({ msg, subscription }); - } - } - - Meteor.defer(function() { - readMessage.refreshUnreadMark(message.rid, true); - RoomManager.updateMentionsMarksOfRoom(typeName); - const wrapper = $('.messages-box .wrapper'); - const msgElement = $(`#${ message._id }`, wrapper); - const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height() / 2); - wrapper.animate({ - scrollTop: pos, - }, 500); - - msgElement.addClass('highlight'); - - setTimeout(function() { - room.isLoading.set(false); - const messages = wrapper[0]; - instance.atBottom = !result.moreAfter && (messages.scrollTop >= (messages.scrollHeight - messages.clientHeight)); - return 500; - }); - - return setTimeout(() => msgElement.removeClass('highlight'), 500); - }); - if (room.loaded == null) { room.loaded = 0; } - room.loaded += result.messages.length; - room.hasMore.set(result.moreBefore); - return room.hasMoreNext.set(result.moreAfter); - }); - } - } - - hasMore(rid) { - const room = this.getRoom(rid); - return room.hasMore.get(); - } - - hasMoreNext(rid) { - const room = this.getRoom(rid); - return room.hasMoreNext.get(); - } - - - getMoreIfIsEmpty(rid) { - const room = this.getRoom(rid); - - if (room.loaded === undefined) { - return this.getMore(rid); - } - } - - - isLoading(rid) { - const room = this.getRoom(rid); - return room.isLoading.get(); - } - - clear(rid) { - ChatMessage.remove({ rid }); - if (this.histories[rid] != null) { - this.histories[rid].hasMore.set(true); - this.histories[rid].isLoading.set(false); - return this.histories[rid].loaded = undefined; - } - } -}; +RoomHistoryManager = _RoomHistoryManager; diff --git a/packages/rocketchat-ui/client/lib/RoomManager.js b/packages/rocketchat-ui/client/lib/RoomManager.js index f5ed7d5f69ed..7b0f8dbd584d 100644 --- a/packages/rocketchat-ui/client/lib/RoomManager.js +++ b/packages/rocketchat-ui/client/lib/RoomManager.js @@ -1,326 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { Blaze } from 'meteor/blaze'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Template } from 'meteor/templating'; -import _ from 'underscore'; -import { upsertMessage } from './RoomHistoryManager'; -import { CachedChatRoom } from './collections'; +import { RoomManager as _RoomManager } from 'meteor/rocketchat:ui-utils'; -const maxRoomsOpen = parseInt(localStorage && localStorage.getItem('rc-maxRoomsOpen')) || 5 ; - -const onDeleteMessageStream = (msg) => ChatMessage.remove({ _id: msg._id }); -const onDeleteMessageBulkStream = ({ rid, ts, excludePinned, users }) => { - const query = { rid, ts }; - if (excludePinned) { - query.pinned = { $ne: true }; - } - if (users && users.length) { - query['u.username'] = { $in: users }; - } - ChatMessage.remove(query); -}; - -RoomManager = new function() { - const openedRooms = {}; - const msgStream = new Meteor.Streamer('room-messages'); - const onlineUsers = new ReactiveVar({}); - const Dep = new Tracker.Dependency(); - const Cls = class { - static initClass() { - this.prototype.openedRooms = openedRooms; - this.prototype.onlineUsers = onlineUsers; - this.prototype.computation = Tracker.autorun(() => { - Object.keys(openedRooms).forEach((typeName) => { - const record = openedRooms[typeName]; - if (record.active !== true || record.ready === true) { return; } - const ready = CachedChatRoom.ready.get() && RocketChat.mainReady.get(); - if (ready !== true) { return; } - const user = Meteor.user(); - - const type = typeName.substr(0, 1); - const name = typeName.substr(1); - - const room = Tracker.nonreactive(() => RocketChat.roomTypes.findRoom(type, name, user)); - - if (room != null) { - openedRooms[typeName].rid = room._id; - RoomHistoryManager.getMoreIfIsEmpty(room._id); - - if (openedRooms[typeName].streamActive !== true) { - openedRooms[typeName].streamActive = true; - msgStream.on(openedRooms[typeName].rid, (msg) => - - RocketChat.promises.run('onClientMessageReceived', msg).then(function(msg) { - - // Should not send message to room if room has not loaded all the current messages - if (RoomHistoryManager.hasMoreNext(openedRooms[typeName].rid) === false) { - - // Do not load command messages into channel - if (msg.t !== 'command') { - const subscription = ChatSubscription.findOne({ rid: openedRooms[typeName].rid }); - upsertMessage({ msg, subscription }); - msg.room = { - type, - name, - }; - } - msg.name = room.name; - Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); - - RocketChat.callbacks.run('streamMessage', msg); - - return window.fireGlobalEvent('new-message', msg); - } - }) - ); - - RocketChat.Notifications.onRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define - RocketChat.Notifications.onRoom(openedRooms[typeName].rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define - } - } - Meteor.defer(() => { - record.ready = true; - Dep.changed(); - }); - }); - }); - } - - getOpenedRoomByRid(rid) { - return Object.keys(openedRooms).map((typeName) => openedRooms[typeName]).find((openedRoom) => openedRoom.rid === rid); - } - - getDomOfRoom(typeName, rid) { - const room = openedRooms[typeName]; - if ((room == null)) { - return; - } - - if ((room.dom == null) && (rid != null)) { - room.dom = document.createElement('div'); - room.dom.classList.add('room-container'); - const contentAsFunc = (content) => () => content; - - room.template = Blaze._TemplateWith({ _id: rid }, contentAsFunc(Template.room)); - Blaze.render(room.template, room.dom); // , nextNode, parentView - } - - return room.dom; - } - - close(typeName) { - if (openedRooms[typeName]) { - if (openedRooms[typeName].rid != null) { - msgStream.removeAllListeners(openedRooms[typeName].rid); - RocketChat.Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define - RocketChat.Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define - } - - openedRooms[typeName].ready = false; - openedRooms[typeName].active = false; - if (openedRooms[typeName].template != null) { - Blaze.remove(openedRooms[typeName].template); - } - delete openedRooms[typeName].dom; - delete openedRooms[typeName].template; - - const { rid } = openedRooms[typeName]; - delete openedRooms[typeName]; - - if (rid != null) { - return RoomHistoryManager.clear(rid); - } - } - } - - - closeOlderRooms() { - if (Object.keys(openedRooms).length <= maxRoomsOpen) { - return; - } - - const roomsToClose = _.sortBy(_.values(openedRooms), 'lastSeen').reverse().slice(maxRoomsOpen); - return Array.from(roomsToClose).map((roomToClose) => - this.close(roomToClose.typeName)); - } - - - closeAllRooms() { - Object.keys(openedRooms).forEach((key) => { - const openedRoom = openedRooms[key]; - this.close(openedRoom.typeName); - }); - } - - - open(typeName) { - if ((openedRooms[typeName] == null)) { - openedRooms[typeName] = { - typeName, - active: false, - ready: false, - unreadSince: new ReactiveVar(undefined), - }; - } - - openedRooms[typeName].lastSeen = new Date; - - if (openedRooms[typeName].ready) { - this.closeOlderRooms(); - } - - if (CachedChatSubscription.ready.get() === true) { - - if (openedRooms[typeName].active !== true) { - openedRooms[typeName].active = true; - if (this.computation) { - this.computation.invalidate(); - } - } - } - - return { - ready() { - Dep.depend(); - return openedRooms[typeName].ready; - }, - }; - } - - existsDomOfRoom(typeName) { - const room = openedRooms[typeName]; - return ((room != null ? room.dom : undefined) != null); - } - - updateUserStatus(user, status, utcOffset) { - const onlineUsersValue = onlineUsers.curValue; - - if (status === 'offline') { - delete onlineUsersValue[user.username]; - } else { - onlineUsersValue[user.username] = { - _id: user._id, - status, - utcOffset, - }; - } - - return onlineUsers.set(onlineUsersValue); - } - - updateMentionsMarksOfRoom(typeName) { - const dom = this.getDomOfRoom(typeName); - if ((dom == null)) { - return; - } - - const ticksBar = $(dom).find('.ticks-bar'); - $(dom).find('.ticks-bar > .tick').remove(); - - const scrollTop = $(dom).find('.messages-box > .wrapper').scrollTop() - 50; - const totalHeight = $(dom).find('.messages-box > .wrapper > ul').height() + 40; - - return $('.messages-box .mention-link-me').each(function(index, item) { - const topOffset = $(item).offset().top + scrollTop; - const percent = (100 / totalHeight) * topOffset; - if ($(item).hasClass('mention-link-all')) { - return ticksBar.append(`
`); - } else { - return ticksBar.append(`
`); - } - }); - } - }; - Cls.initClass(); - return new Cls; -}; - -const loadMissedMessages = function(rid) { - const lastMessage = ChatMessage.findOne({ rid, temp: { $exists: false } }, { sort: { ts: -1 }, limit: 1 }); - if (lastMessage == null) { - return; - } - const subscription = ChatSubscription.findOne({ rid }); - return Meteor.call('loadMissedMessages', rid, lastMessage.ts, (err, result) => { - if (result) { - return Array.from(result).map((item) => RocketChat.promises.run('onClientMessageReceived', item).then((msg) => upsertMessage({ msg, subscription }))); - } else { - return []; - } - }); -}; - -let connectionWasOnline = true; -Tracker.autorun(function() { - const { connected } = Meteor.connection.status(); - - if (connected === true && connectionWasOnline === false && RoomManager.openedRooms != null) { - Object.keys(RoomManager.openedRooms).forEach((key) => { - const value = RoomManager.openedRooms[key]; - if (value.rid != null) { - loadMissedMessages(value.rid); - } - }); - } - return connectionWasOnline = connected; -}); - -Meteor.startup(() => { - - // Reload rooms after login - let currentUsername = undefined; - Tracker.autorun(() => { - const user = Meteor.user(); - if ((currentUsername === undefined) && ((user != null ? user.username : undefined) != null)) { - currentUsername = user.username; - RoomManager.closeAllRooms(); - const { roomTypes } = RocketChat.roomTypes; - // Reload only if the current route is a channel route - const roomType = Object.keys(roomTypes).find((key) => roomTypes[key].route && roomTypes[key].route.name === FlowRouter.current().route.name); - if (roomType) { - FlowRouter.reload(); - } - } - }); - - ChatMessage.find().observe({ - removed(record) { - if (RoomManager.getOpenedRoomByRid(record.rid) != null) { - const recordBefore = ChatMessage.findOne({ ts: { $lt: record.ts } }, { sort: { ts: -1 } }); - if (recordBefore != null) { - ChatMessage.update({ _id: recordBefore._id }, { $set: { tick: new Date } }); - } - - const recordAfter = ChatMessage.findOne({ ts: { $gt: record.ts } }, { sort: { ts: 1 } }); - if (recordAfter != null) { - return ChatMessage.update({ _id: recordAfter._id }, { $set: { tick: new Date } }); - } - } - }, - }); -}); - -Tracker.autorun(function() { - if (Meteor.userId()) { - return RocketChat.Notifications.onUser('message', function(msg) { - msg.u = - { username: 'rocket.cat' }; - msg.private = true; - - return ChatMessage.upsert({ _id: msg._id }, msg); - }); - } -}); - -RocketChat.callbacks.add('afterLogoutCleanUp', () => RoomManager.closeAllRooms(), RocketChat.callbacks.priority.MEDIUM, 'roommanager-after-logout-cleanup'); - -RocketChat.CachedCollectionManager.onLogin(() => { - RocketChat.Notifications.onUser('subscriptions-changed', (action, sub) => { - ChatMessage.update({ rid: sub.rid }, { $unset : { ignored : '' } }, { multi : true }); - if (sub && sub.ignored) { - ChatMessage.update({ rid: sub.rid, t: { $ne: 'command' }, 'u._id': { $in : sub.ignored } }, { $set: { ignored : true } }, { multi : true }); - } - }); -}); +RoomManager = _RoomManager; diff --git a/packages/rocketchat-ui/client/lib/accountBox.js b/packages/rocketchat-ui/client/lib/accountBox.js index 3c08a3b34d63..aa511de3b8d5 100644 --- a/packages/rocketchat-ui/client/lib/accountBox.js +++ b/packages/rocketchat-ui/client/lib/accountBox.js @@ -1,100 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { Session } from 'meteor/session'; -import _ from 'underscore'; +import { AccountBox as accountBox } from 'meteor/rocketchat:ui-utils'; -AccountBox = (function() { - let status = 0; - const items = new ReactiveVar([]); - function setStatus(status) { - return Meteor.call('UserPresence:setDefaultStatus', status); - } - function open() { - if (SideNav.flexStatus()) { - SideNav.closeFlex(); - return; - } - status = 1; - } - function close() { - status = 0; - } - function toggle() { - if (status) { - return close(); - } else { - return open(); - } - } - function openFlex() { - status = 0; - } - - /* - * @param newOption: - * name: Button label - * icon: Button icon - * class: Class of the item - * permissions: Which permissions a user should have (all of them) to see this item - */ - function addItem(newItem) { - return Tracker.nonreactive(function() { - const actual = items.get(); - actual.push(newItem); - return items.set(actual); - }); - } - function checkCondition(item) { - return (item.condition == null) || item.condition(); - } - function getItems() { - return _.filter(items.get(), function(item) { - if (checkCondition(item)) { - return true; - } - }); - } - function addRoute(newRoute, router) { - if (router == null) { - router = FlowRouter; - } - const routeConfig = { - center: 'pageContainer', - pageTemplate: newRoute.pageTemplate, - }; - if (newRoute.i18nPageTitle != null) { - routeConfig.i18nPageTitle = newRoute.i18nPageTitle; - } - if (newRoute.pageTitle != null) { - routeConfig.pageTitle = newRoute.pageTitle; - } - return router.route(newRoute.path, { - name: newRoute.name, - action() { - Session.set('openedRoom'); - return BlazeLayout.render('main', routeConfig); - }, - triggersEnter: [ - function() { - if (newRoute.sideNav != null) { - SideNav.setFlex(newRoute.sideNav); - return SideNav.openFlex(); - } - }, - ], - }); - } - return { - setStatus, - toggle, - open, - close, - openFlex, - addRoute, - addItem, - getItems, - }; -}()); +AccountBox = accountBox; diff --git a/packages/rocketchat-ui/client/lib/menu.js b/packages/rocketchat-ui/client/lib/menu.js index 467376571fb0..dd3899a7ef8e 100644 --- a/packages/rocketchat-ui/client/lib/menu.js +++ b/packages/rocketchat-ui/client/lib/menu.js @@ -1,11 +1,5 @@ -import { Session } from 'meteor/session'; import _ from 'underscore'; -import EventEmitter from 'wolfy87-eventemitter'; -import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; -import { isRtl } from 'meteor/rocketchat:utils'; - -const sideNavW = 280; -const map = (x, in_min, in_max, out_min, out_max) => (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +import { menu as _menu } from 'meteor/rocketchat:ui-utils'; window.addEventListener('resize', _.debounce((() => { let lastState = window.matchMedia('(min-width: 780px)').matches ? 'mini' : 'large'; @@ -19,222 +13,7 @@ window.addEventListener('resize', _.debounce((() => { }; })(), 100)); -this.menu = new class extends EventEmitter { - constructor() { - super(); - this._open = false; - this.updateUnreadBars = _.throttle(() => { - if (this.list == null) { - return; - } - const listOffset = this.list.offset(); - const listHeight = this.list.height(); - let showTop = false; - let showBottom = false; - $('li.has-alert').each(function() { - if ($(this).offset().top < listOffset.top - $(this).height()) { - showTop = true; - } - if ($(this).offset().top > listOffset.top + listHeight) { - return showBottom = true; - } - }); - if (showTop === true) { - $('.top-unread-rooms').removeClass('hidden'); - } else { - $('.top-unread-rooms').addClass('hidden'); - } - if (showBottom === true) { - return $('.bottom-unread-rooms').removeClass('hidden'); - } else { - return $('.bottom-unread-rooms').addClass('hidden'); - } - }, 200); - this.sideNavW = sideNavW; - } - get isRtl() { - return isRtl(localStorage.getItem('userLanguage')); - } - touchstart(e) { - this.movestarted = false; - this.blockmove = false; - this.touchstartX = undefined; - this.touchstartY = undefined; - this.diff = 0; - if (e.target === this.sidebarWrap[0] || $(e.target).closest('.main-content').length > 0) { - this.closePopover(); - this.touchstartX = e.touches[0].clientX; - this.touchstartY = e.touches[0].clientY; - this.mainContent = $('.main-content'); - } - } - touchmove(e) { - if (this.touchstartX == null) { - return; - } - lazyloadtick(); - const [touch] = e.touches; - const diffX = touch.clientX - this.touchstartX; - const diffY = touch.clientY - this.touchstartY; - const absX = Math.abs(diffX); - const absY = Math.abs(diffY); - - if (!this.movestarted && absY > 5) { - this.blockmove = true; - } - if (this.blockmove) { - return; - } - this.sidebar.css('transition', 'none'); - this.sidebarWrap.css('transition', 'none'); - if (this.movestarted === true || absX > 5) { - this.movestarted = true; - if (this.isRtl) { - if (this.isOpen()) { - this.diff = -sideNavW + diffX; - } else { - this.diff = diffX; - } - if (this.diff < -sideNavW) { - this.diff = -sideNavW; - } - if (this.diff > 0) { - this.diff = 0; - } - } else { - if (this.isOpen()) { - this.diff = sideNavW + diffX; - } else { - this.diff = diffX; - } - if (this.diff > sideNavW) { - this.diff = sideNavW; - } - if (this.diff < 0) { - this.diff = 0; - } - } - // if (map((this.diff / sideNavW), 0, 1, -.1, .8) > 0) { - this.sidebar.css('box-shadow', '0 0 15px 1px rgba(0,0,0,.3)'); - // this.sidebarWrap.css('z-index', '9998'); - this.translate(this.diff); - // } - } - } - translate(diff, width = sideNavW) { - if (diff === undefined) { - diff = this.isRtl ? -1 * sideNavW : sideNavW; - } - this.sidebarWrap.css('width', '100%'); - this.wrapper.css('overflow', 'hidden'); - this.sidebarWrap.css('background-color', '#000'); - this.sidebarWrap.css('opacity', map((Math.abs(diff) / width), 0, 1, -.1, .8).toFixed(2)); - this.isRtl ? this.sidebar.css('transform', `translate3d(${ (sideNavW + diff).toFixed(3) }px, 0 , 0)`) : this.sidebar.css('transform', `translate3d(${ (diff - sideNavW).toFixed(3) }px, 0 , 0)`); - } - touchend() { - const [max, min] = [sideNavW * .76, sideNavW * .24]; - if (this.movestarted !== true) { - return; - } - this.movestarted = false; - if (this.isRtl) { - if (this.isOpen()) { - return this.diff >= -max ? this.close() : this.open(); - } else if (this.diff <= -min) { - return this.open(); - } - return this.close(); - } - if (this.isOpen()) { - if (this.diff >= max) { - return this.open(); - } - return this.close(); - } - if (this.diff >= min) { - return this.open(); - } - return this.close(); - } - init() { - this.sidebar = this.menu = $('.sidebar'); - this.sidebarWrap = $('.sidebar-wrap'); - this.wrapper = $('.messages-box > .wrapper'); - const ignore = (fn) => (event) => document.body.clientWidth <= 780 && fn(event); - - document.body.addEventListener('touchstart', ignore((e) => this.touchstart(e))); - document.body.addEventListener('touchmove', ignore((e) => this.touchmove(e))); - document.body.addEventListener('touchend', ignore((e) => this.touchend(e))); - this.sidebarWrap.on('click', ignore((e) => { - e.target === this.sidebarWrap[0] && this.isOpen() && this.emit('clickOut', e); - })); - this.on('close', () => { - this.sidebarWrap.css('width', ''); - // this.sidebarWrap.css('z-index', ''); - this.sidebarWrap.css('background-color', ''); - this.sidebar.css('transform', ''); - this.sidebar.css('box-shadow', ''); - this.sidebar.css('transition', ''); - this.sidebarWrap.css('transition', ''); - this.wrapper && this.wrapper.css('overflow', ''); - }); - this.on('open', ignore(() => { - this.sidebar.css('box-shadow', '0 0 15px 1px rgba(0,0,0,.3)'); - // this.sidebarWrap.css('z-index', '9998'); - this.translate(); - })); - this.mainContent = $('.main-content'); - - this.list = $('.rooms-list'); - this._open = false; - Session.set('isMenuOpen', this._open); - } - closePopover() { - return this.menu.find('[data-popover="anchor"]:checked').prop('checked', false).length > 0; - } - isOpen() { - return Session.get('isMenuOpen'); - } - open() { - this._open = true; - Session.set('isMenuOpen', this._open); - this.emit('open'); - } - - close() { - this._open = false; - Session.set('isMenuOpen', this._open); - this.emit('close'); - } - - toggle() { - return this.isOpen() ? this.close() : this.open(); - } -}; - - -let passClosePopover = false; - -this.menu.on('clickOut', function() { - if (!this.closePopover()) { - passClosePopover = true; - this.close(); - } -}); - -this.menu.on('close', function() { - if (!this.sidebar) { - return; - } - - this.sidebar.css('transition', ''); - this.sidebarWrap.css('transition', ''); - if (passClosePopover) { - passClosePopover = false; - return; - } - this.closePopover(); -}); +this.menu = _menu; RocketChat.on('grid', () => { this.menu.close(); diff --git a/packages/rocketchat-ui/client/lib/readMessages.js b/packages/rocketchat-ui/client/lib/readMessages.js index c002f3754809..7479b935cd3e 100644 --- a/packages/rocketchat-ui/client/lib/readMessages.js +++ b/packages/rocketchat-ui/client/lib/readMessages.js @@ -1,7 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; -import _ from 'underscore'; - +import { readMessage as _readMessage } from 'meteor/rocketchat:ui-utils'; /* DEFINITIONS - If window loses focus user needs to scroll or click/touch some place - On hit ESC enable read, force read of current room and remove unread mark @@ -15,199 +12,4 @@ import _ from 'underscore'; // window.addEventListener 'focus', -> // readMessage.refreshUnreadMark(undefined, true) -readMessage = new class { - constructor() { - this.debug = false; - this.callbacks = []; - this.read = _.debounce((force) => this.readNow(force), 1000); - this.canReadMessage = false; - } - - readNow(force) { - if (force == null) { force = false; } - if (this.debug) { console.log('--------------'); } - if (this.debug) { console.log('readMessage -> readNow init process force:', force); } - - const self = this; - - self.refreshUnreadMark(); - - if ((force !== true) && (this.canReadMessage === false)) { - if (this.debug) { console.log('readMessage -> readNow canceled by canReadMessage: false'); } - return; - } - - const rid = Session.get('openedRoom'); - if (rid == null) { - if (this.debug) { console.log('readMessage -> readNow canceled, no rid informed'); } - return; - } - - if (force === true) { - if (this.debug) { console.log('readMessage -> readNow via force rid:', rid); } - return Meteor.call('readMessages', rid, function() { - RoomHistoryManager.getRoom(rid).unreadNotLoaded.set(0); - self.refreshUnreadMark(); - return self.fireRead(rid); - }); - } - - const subscription = ChatSubscription.findOne({ rid }); - if (subscription == null) { - if (this.debug) { console.log('readMessage -> readNow canceled, no subscription found for rid:', rid); } - return; - } - - if ((subscription.alert === false) && (subscription.unread === 0)) { - if (this.debug) { console.log('readMessage -> readNow canceled, alert', subscription.alert, 'and unread', subscription.unread); } - return; - } - - const room = RoomManager.getOpenedRoomByRid(rid); - if (room == null) { - if (this.debug) { console.log('readMessage -> readNow canceled, no room found for typeName:', subscription.t + subscription.name); } - return; - } - - // Only read messages if user saw the first unread message - const unreadMark = $('.message.first-unread'); - if (unreadMark.length > 0) { - const position = unreadMark.position(); - const visible = (position != null ? position.top : undefined) >= 0; - if (!visible && (room.unreadSince.get() != null)) { - if (this.debug) { console.log('readMessage -> readNow canceled, unread mark visible:', visible, 'unread since exists', (room.unreadSince.get() != null)); } - return; - } - // if unread mark is not visible and there is more more not loaded unread messages - } else if (RoomHistoryManager.getRoom(rid).unreadNotLoaded.get() > 0) { - return; - } - - if (this.debug) { console.log('readMessage -> readNow rid:', rid); } - Meteor.call('readMessages', rid, function() { - RoomHistoryManager.getRoom(rid).unreadNotLoaded.set(0); - self.refreshUnreadMark(); - return self.fireRead(rid); - }); - } - - disable() { - return this.canReadMessage = false; - } - - enable() { - return this.canReadMessage = document.hasFocus(); - } - - isEnable() { - return this.canReadMessage === true; - } - - onRead(cb) { - return this.callbacks.push(cb); - } - - fireRead(rid) { - return Array.from(this.callbacks).map((cb) => cb(rid)); - } - - refreshUnreadMark(rid, force) { - if (rid == null) { rid = Session.get('openedRoom'); } - if (rid == null) { - return; - } - - const subscription = ChatSubscription.findOne({ rid }, { reactive: false }); - if (subscription == null) { - return; - } - - const room = RoomManager.openedRooms[subscription.t + subscription.name]; - if (room == null) { - return; - } - - if (!subscription.alert && (subscription.unread === 0)) { - room.unreadSince.set(undefined); - return; - } - - if ((force == null) && (subscription.rid === Session.get('openedRoom')) && document.hasFocus()) { - return; - } - - - let lastReadRecord = ChatMessage.findOne({ - rid: subscription.rid, - ts: { - $lt: subscription.ls, - }, - }, { - sort: { - ts: -1, - }, - }); - - if ((lastReadRecord == null) && (RoomHistoryManager.getRoom(room.rid).unreadNotLoaded.get() === 0)) { - lastReadRecord = - { ts: new Date(0) }; - } - - if ((lastReadRecord != null) || (RoomHistoryManager.getRoom(room.rid).unreadNotLoaded.get() > 0)) { - room.unreadSince.set(subscription.ls); - } else { - room.unreadSince.set(undefined); - } - - if (lastReadRecord != null) { - const firstUnreadRecord = ChatMessage.findOne({ - rid: subscription.rid, - ts: { - $gt: lastReadRecord.ts, - }, - 'u._id': { - $ne: Meteor.userId(), - }, - }, { - sort: { - ts: 1, - }, - }); - - if (firstUnreadRecord != null) { - room.unreadFirstId = firstUnreadRecord._id; - $(room.dom).find(`.message.first-unread:not(#${ firstUnreadRecord._id })`).removeClass('first-unread'); - $(room.dom).find(`.message#${ firstUnreadRecord._id }`).addClass('first-unread'); - } - } - } -}; - - -Meteor.startup(function() { - $(window).on('blur', () => readMessage.disable()); - - $(window).on('focus', () => { - readMessage.enable(); - return readMessage.read(); - }); - - $(window).on('click', () => { - readMessage.enable(); - return readMessage.read(); - }); - - $(window).on('touchend', () => { - readMessage.enable(); - return readMessage.read(); - }); - - $(window).on('keyup', (e) => { - const key = e.which; - if (key === 27) { - readMessage.enable(); - readMessage.readNow(true); - return $('.message.first-unread').removeClass('first-unread'); - } - }); -}); +readMessage = _readMessage; diff --git a/packages/rocketchat-ui/client/lib/sideNav.js b/packages/rocketchat-ui/client/lib/sideNav.js index 3c1a283bae2b..62de09f000a9 100644 --- a/packages/rocketchat-ui/client/lib/sideNav.js +++ b/packages/rocketchat-ui/client/lib/sideNav.js @@ -1,132 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Session } from 'meteor/session'; +import { SideNav as _SideNav } from 'meteor/rocketchat:ui-utils'; -SideNav = new class { - constructor() { - this.initiated = false; - this.sideNav = {}; - this.flexNav = {}; - this.animating = false; - this.openQueue = []; - } - - toggleFlex(status, callback) { - if (this.animating === true) { - return; - } - this.animating = true; - if (status === -1 || (status !== 1 && this.flexNav.opened)) { - this.flexNav.opened = false; - this.flexNav.addClass('animated-hidden'); - } else { - this.flexNav.opened = true; - setTimeout(() => this.flexNav.removeClass('animated-hidden'), 50); - } - return setTimeout(() => { - this.animating = false; - return typeof callback === 'function' && callback(); - }, 500); - } - closeFlex(callback = null) { - let subscription; - if (!RocketChat.roomTypes.getTypes().filter(function(i) { - return i.route; - }).map(function(i) { - return i.route.name; - }).includes(FlowRouter.current().route.name)) { - subscription = RocketChat.models.Subscriptions.findOne({ - rid: Session.get('openedRoom'), - }); - if (subscription != null) { - RocketChat.roomTypes.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); - } else { - FlowRouter.go('home'); - } - } - if (this.animating === true) { - return; - } - return this.toggleFlex(-1, callback); - } - flexStatus() { - return this.flexNav.opened; - } - setFlex(template, data) { - if (data == null) { - data = {}; - } - Session.set('flex-nav-template', template); - return Session.set('flex-nav-data', data); - } - getFlex() { - return { - template: Session.get('flex-nav-template'), - data: Session.get('flex-nav-data'), - }; - } - - toggleCurrent() { - if (this.flexNav && this.flexNav.opened) { - return this.closeFlex(); - } else { - return AccountBox.toggle(); - } - } - focusInput() { - const sideNavDivs = Array.from(this.sideNav[0].children).filter((el) => el.tagName === 'DIV' && !el.classList.contains('hidden')); - let highestZidx = 0; - let highestZidxElem; - sideNavDivs.forEach((el) => { - const zIndex = Number(window.getComputedStyle(el).zIndex); - if (zIndex > highestZidx) { - highestZidx = zIndex; - highestZidxElem = el; - } - }); - setTimeout(() => { - const ref = highestZidxElem.querySelector('input'); - return ref && ref.focus(); - }, 200); - } - validate() { - const invalid = []; - this.sideNav.find('input.required').each(function() { - if (!this.value.length) { - return invalid.push($(this).prev('label').html()); - } - }); - if (invalid.length) { - return invalid; - } - return false; - } - - openFlex(callback) { - if (!this.initiated) { - return this.openQueue.push({ - config: this.getFlex(), - callback, - }); - } - if (this.animating === true) { - return; - } - AccountBox.close(); - this.toggleFlex(1, callback); - return this.focusInput(); - } - - init() { - this.sideNav = $('.sidebar'); - this.flexNav = this.sideNav.find('.flex-nav'); - this.setFlex(''); - this.initiated = true; - if (this.openQueue.length > 0) { - this.openQueue.forEach((item) => { - this.setFlex(item.config.template, item.config.data); - return this.openFlex(item.callback); - }); - return this.openQueue = []; - } - } -}; +SideNav = _SideNav; diff --git a/packages/rocketchat-ui/client/views/app/popover.js b/packages/rocketchat-ui/client/views/app/popover.js index 238e0d69eced..fb60060cc107 100644 --- a/packages/rocketchat-ui/client/views/app/popover.js +++ b/packages/rocketchat-ui/client/views/app/popover.js @@ -1,261 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Blaze } from 'meteor/blaze'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Template } from 'meteor/templating'; -import { TAPi18n } from 'meteor/tap:i18n'; -import { isRtl } from 'meteor/rocketchat:utils'; -import _ from 'underscore'; +import { popover as _popover } from 'meteor/rocketchat:ui-utils'; -import { hide, leave } from 'meteor/rocketchat:lib'; - -popover = { - renderedPopover: null, - open({ currentTarget, ...config }) { - // Popover position must be computed as soon as possible, avoiding DOM changes over currentTarget - const data = { - targetRect: currentTarget && currentTarget.getBoundingClientRect && currentTarget.getBoundingClientRect(), - ...config, - }; - this.renderedPopover = Blaze.renderWithData(Template.popover, data, document.body); - }, - close() { - if (!this.renderedPopover) { - return false; - } - - Blaze.remove(this.renderedPopover); - - const { activeElement } = this.renderedPopover.dataVar.curValue; - if (activeElement) { - $(activeElement).removeClass('active'); - } - }, -}; - -Template.popover.helpers({ - hasAction() { - return !!this.action; - }, -}); - -Template.popover.onRendered(function() { - if (this.data.onRendered) { - this.data.onRendered(); - } - - $('.rc-popover').click(function(e) { - if (e.currentTarget === e.target) { - popover.close(); - } - }); - const { offsetVertical = 0, offsetHorizontal = 0 } = this.data; - const { activeElement } = this.data; - const popoverContent = this.firstNode.children[0]; - const position = _.throttle(() => { - - const direction = typeof this.data.direction === 'function' ? this.data.direction() : this.data.direction; - - const verticalDirection = /top/.test(direction) ? 'top' : 'bottom'; - const rtlDirection = isRtl() ^ /inverted/.test(direction) ? 'left' : 'right'; - const rightDirection = /right/.test(direction) ? 'right' : rtlDirection; - const horizontalDirection = /left/.test(direction) ? 'left' : rightDirection; - - const position = typeof this.data.position === 'function' ? this.data.position() : this.data.position; - const customCSSProperties = typeof this.data.customCSSProperties === 'function' ? this.data.customCSSProperties() : this.data.customCSSProperties; - - const mousePosition = typeof this.data.mousePosition === 'function' ? this.data.mousePosition() : this.data.mousePosition || { - x: this.data.targetRect[horizontalDirection === 'left' ? 'right' : 'left'], - y: this.data.targetRect[verticalDirection], - }; - const offsetWidth = offsetHorizontal * (horizontalDirection === 'left' ? 1 : -1); - const offsetHeight = offsetVertical * (verticalDirection === 'bottom' ? 1 : -1); - - if (position) { - popoverContent.style.top = `${ position.top }px`; - popoverContent.style.left = `${ position.left }px`; - } else { - const clientHeight = this.data.targetRect.height; - const popoverWidth = popoverContent.offsetWidth; - const popoverHeight = popoverContent.offsetHeight; - const windowWidth = window.innerWidth; - const windowHeight = window.innerHeight; - - let top = mousePosition.y - clientHeight + offsetHeight; - - if (verticalDirection === 'top') { - top = mousePosition.y - popoverHeight + offsetHeight; - - if (top < 0) { - top = 10 + offsetHeight; - } - } - - if (top + popoverHeight > windowHeight) { - top = windowHeight - 10 - popoverHeight - offsetHeight; - } - - let left = mousePosition.x - popoverWidth + offsetWidth; - - if (horizontalDirection === 'right') { - left = mousePosition.x + offsetWidth; - } - - if (left + popoverWidth >= windowWidth) { - left = mousePosition.x - popoverWidth + offsetWidth; - } - - if (left <= 0) { - left = mousePosition.x + offsetWidth; - } - - popoverContent.style.top = `${ top }px`; - popoverContent.style.left = `${ left }px`; - } - - if (customCSSProperties) { - Object.keys(customCSSProperties).forEach(function(property) { - popoverContent.style[property] = customCSSProperties[property]; - }); - } - - const realTop = Number(popoverContent.style.top.replace('px', '')); - if (realTop + popoverContent.offsetHeight > window.innerHeight) { - popoverContent.style.overflow = 'scroll'; - popoverContent.style.bottom = 0; - popoverContent.className = 'rc-popover__content rc-popover__content-scroll'; - } - - if (activeElement) { - $(activeElement).addClass('active'); - } - - popoverContent.style.opacity = 1; - }, 50); - $(window).on('resize', position); - position(); - this.position = position; - - this.firstNode.style.visibility = 'visible'; -}); - -Template.popover.onDestroyed(function() { - if (this.data.onDestroyed) { - this.data.onDestroyed(); - } - $(window).off('resize', this.position); -}); - -Template.popover.events({ - 'click .js-action'(e, instance) { - !this.action || this.action.call(this, e, instance.data.data); - popover.close(); - }, - 'click .js-close'() { - popover.close(); - }, - 'click [data-type="messagebox-action"]'(event, t) { - const { id } = event.currentTarget.dataset; - const action = RocketChat.messageBox.actions.getById(id); - if ((action[0] != null ? action[0].action : undefined) != null) { - action[0].action({ rid: t.data.data.rid, messageBox: document.querySelector('.rc-message-box'), element: event.currentTarget, event }); - if (id !== 'audio-message') { - popover.close(); - } - } - }, - 'click [data-type="message-action"]'(e, t) { - const button = RocketChat.MessageAction.getButtonById(e.currentTarget.dataset.id); - if ((button != null ? button.action : undefined) != null) { - button.action.call(t.data.data, e, t.data.instance); - popover.close(); - return false; - } - - if (e.currentTarget.dataset.id === 'report-abuse') { - const message = t.data.data._arguments[1]; - modal.open({ - title: TAPi18n.__('Report_this_message_question_mark'), - text: message.msg, - inputPlaceholder: TAPi18n.__('Why_do_you_want_to_report_question_mark'), - type: 'input', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: TAPi18n.__('Report_exclamation_mark'), - cancelButtonText: TAPi18n.__('Cancel'), - closeOnConfirm: false, - html: false, - }, (inputValue) => { - if (inputValue === false) { - return false; - } - - if (inputValue === '') { - modal.showInputError(TAPi18n.__('You_need_to_write_something')); - return false; - } - - Meteor.call('reportMessage', message._id, inputValue); - - modal.open({ - title: TAPi18n.__('Report_sent'), - text: TAPi18n.__('Thank_you_exclamation_mark '), - type: 'success', - timer: 1000, - showConfirmButton: false, - }); - }); - popover.close(); - } - }, - 'click [data-type="sidebar-item"]'(e, instance) { - popover.close(); - const { rid, name, template } = instance.data.data; - const action = e.currentTarget.dataset.id; - - if (action === 'hide') { - hide(template, rid, name); - } - - if (action === 'leave') { - leave(template, rid, name); - } - - if (action === 'read') { - Meteor.call('readMessages', rid); - return false; - } - - if (action === 'unread') { - Meteor.call('unreadMessages', null, rid, function(error) { - if (error) { - return handleError(error); - } - - const subscription = ChatSubscription.findOne({ rid }); - if (subscription == null) { - return; - } - RoomManager.close(subscription.t + subscription.name); - - FlowRouter.go('home'); - }); - - return false; - } - - if (action === 'favorite') { - Meteor.call('toggleFavorite', rid, !$(e.currentTarget).hasClass('rc-popover__item--star-filled'), function(err) { - popover.close(); - if (err) { - handleError(err); - } - }); - - return false; - } - }, -}); - -Template.popover.helpers({ - isSafariIos: /iP(ad|hone|od).+Version\/[\d\.]+.*Safari/i.test(navigator.userAgent), -}); +popover = _popover; diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index f54f746e2bb7..d79deecdb349 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -105,7 +105,6 @@ Package.onUse(function(api) { api.addFiles('client/views/app/userSearch.html', 'client'); api.addFiles('client/views/app/videoCall/videoButtons.html', 'client'); api.addFiles('client/views/app/videoCall/videoCall.html', 'client'); - api.addFiles('client/views/app/popover.html', 'client'); api.addFiles('client/views/app/photoswipe.html', 'client'); api.addFiles('client/views/cmsPage.js', 'client'); From d64719a24a186acb5f13517c91ef2a9f884ec586 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:39:48 -0200 Subject: [PATCH 29/59] Remove directly dependency between rocketchat:authorization and rocketchat:ui-utils --- .../client/startup.js | 21 +++++++++++-------- .../client/views/permissionsRole.js | 13 ++++++++---- packages/rocketchat-authorization/package.js | 1 - 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/rocketchat-authorization/client/startup.js b/packages/rocketchat-authorization/client/startup.js index ef220b62edb2..e88296ba57e9 100644 --- a/packages/rocketchat-authorization/client/startup.js +++ b/packages/rocketchat-authorization/client/startup.js @@ -1,15 +1,18 @@ import { Meteor } from 'meteor/meteor'; -import { AdminBox } from 'meteor/rocketchat:ui-utils'; import { CachedCollectionManager } from 'meteor/rocketchat:models'; import { hasAllPermission } from './hasPermission'; -CachedCollectionManager.onLogin(() => Meteor.subscribe('roles')); +Meteor.startup(async() => { + const { AdminBox } = await import('meteor/rocketchat:ui-utils'); -AdminBox.addOption({ - href: 'admin-permissions', - i18nLabel: 'Permissions', - icon: 'lock', - permissionGranted() { - return hasAllPermission('access-permissions'); - }, + CachedCollectionManager.onLogin(() => Meteor.subscribe('roles')); + + AdminBox.addOption({ + href: 'admin-permissions', + i18nLabel: 'Permissions', + icon: 'lock', + permissionGranted() { + return hasAllPermission('access-permissions'); + }, + }); }); diff --git a/packages/rocketchat-authorization/client/views/permissionsRole.js b/packages/rocketchat-authorization/client/views/permissionsRole.js index f74b98ad97d0..64a08018d7ce 100644 --- a/packages/rocketchat-authorization/client/views/permissionsRole.js +++ b/packages/rocketchat-authorization/client/views/permissionsRole.js @@ -2,12 +2,13 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { modal } from 'meteor/rocketchat:ui-utils'; import { t, handleError } from 'meteor/rocketchat:utils'; import { Roles } from 'meteor/rocketchat:models'; import { hasAllPermission } from '../hasPermission'; import toastr from 'toastr'; +let _modal; + Template.permissionsRole.helpers({ role() { return Roles.findOne({ @@ -111,9 +112,13 @@ Template.permissionsRole.helpers({ }); Template.permissionsRole.events({ - 'click .remove-user'(e, instance) { + async 'click .remove-user'(e, instance) { + if (!_modal) { + const { modal } = await import('meteor/rocketchat:ui-utils'); + _modal = modal; + } e.preventDefault(); - modal.open({ + _modal.open({ title: t('Are_you_sure'), type: 'warning', showCancelButton: true, @@ -128,7 +133,7 @@ Template.permissionsRole.events({ return handleError(error); } - modal.open({ + _modal.open({ title: t('Removed'), text: t('User_removed'), type: 'success', diff --git a/packages/rocketchat-authorization/package.js b/packages/rocketchat-authorization/package.js index 8f871ec8e6e4..84c855815ab5 100644 --- a/packages/rocketchat-authorization/package.js +++ b/packages/rocketchat-authorization/package.js @@ -13,7 +13,6 @@ Package.onUse(function(api) { 'rocketchat:utils', 'rocketchat:models', 'rocketchat:notifications', - 'rocketchat:ui-utils', ]); api.use([ 'templating', From 093230043e22e2e9bc09888bd42c51f8587656fc Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:41:19 -0200 Subject: [PATCH 30/59] Remove dependency between lazy-load and lib --- packages/rocketchat-lazy-load/package.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rocketchat-lazy-load/package.js b/packages/rocketchat-lazy-load/package.js index 3fcecdc33684..e303c547e4f0 100644 --- a/packages/rocketchat-lazy-load/package.js +++ b/packages/rocketchat-lazy-load/package.js @@ -9,7 +9,6 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', - 'rocketchat:lib', ]); api.mainModule('client/index.js', 'client'); From 601008bce5768455f3a5dc7bcbe4e5f3c6d3eb02 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:45:12 -0200 Subject: [PATCH 31/59] Change imports of renderMessageBody from ui-message to ui-utils --- .../client/messageAttachment.js | 2 +- packages/rocketchat-message-attachments/package.js | 1 + packages/rocketchat-ui-message/client/index.js | 5 ----- packages/rocketchat-ui-message/client/message.js | 2 +- packages/rocketchat-ui-message/package.js | 2 ++ 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.js b/packages/rocketchat-message-attachments/client/messageAttachment.js index 44f0b7f77e9e..93b70ba62bf3 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.js +++ b/packages/rocketchat-message-attachments/client/messageAttachment.js @@ -3,7 +3,7 @@ import { DateFormat } from 'meteor/rocketchat:lib'; import { fixCordova } from 'meteor/rocketchat:lazy-load'; import { Template } from 'meteor/templating'; import { RocketChat } from 'meteor/rocketchat:lib'; -import { renderMessageBody } from 'meteor/rocketchat:ui-message'; +import { renderMessageBody } from 'meteor/rocketchat:ui-utils'; const colors = { good: '#35AC19', diff --git a/packages/rocketchat-message-attachments/package.js b/packages/rocketchat-message-attachments/package.js index 168bd37e33e8..956c10513ea2 100644 --- a/packages/rocketchat-message-attachments/package.js +++ b/packages/rocketchat-message-attachments/package.js @@ -13,6 +13,7 @@ Package.onUse(function(api) { 'rocketchat:lazy-load', 'rocketchat:e2e', 'rocketchat:ui-message', + 'rocketchat:ui-utils', ]); api.addFiles('client/stylesheets/messageAttachments.css', 'client'); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-ui-message/client/index.js b/packages/rocketchat-ui-message/client/index.js index 91541d4454d2..2ede1925b767 100644 --- a/packages/rocketchat-ui-message/client/index.js +++ b/packages/rocketchat-ui-message/client/index.js @@ -15,9 +15,4 @@ import './popup/messagePopup'; import './popup/messagePopupChannel'; import './popup/messagePopupConfig'; import './popup/messagePopupEmoji'; -import { renderMessageBody } from './renderMessageBody'; import './startup/messageBoxActions'; - -export { - renderMessageBody, -}; diff --git a/packages/rocketchat-ui-message/client/message.js b/packages/rocketchat-ui-message/client/message.js index 4a4690e00e82..38f4c7437c2f 100644 --- a/packages/rocketchat-ui-message/client/message.js +++ b/packages/rocketchat-ui-message/client/message.js @@ -7,7 +7,7 @@ import _ from 'underscore'; import moment from 'moment'; import { DateFormat } from 'meteor/rocketchat:lib'; import { renderEmoji } from 'meteor/rocketchat:emoji'; -import { renderMessageBody } from './renderMessageBody'; +import { renderMessageBody } from 'meteor/rocketchat:ui-utils'; import { RocketChat } from 'meteor/rocketchat:lib'; import { RoomRoles, UserRoles } from 'meteor/rocketchat:ui'; import { t } from 'meteor/rocketchat:utils'; diff --git a/packages/rocketchat-ui-message/package.js b/packages/rocketchat-ui-message/package.js index 6d12b0d35d4b..7bcbb2a0ffec 100644 --- a/packages/rocketchat-ui-message/package.js +++ b/packages/rocketchat-ui-message/package.js @@ -17,10 +17,12 @@ Package.onUse(function(api) { 'templating', 'tracker', 'rocketchat:utils', + 'rocketchat:ui-utils', 'rocketchat:emoji', 'rocketchat:lib', 'rocketchat:ui-account', 'rocketchat:ui-vrecord', + 'rocketchat:ui-sidenav', 'rocketchat:file-upload', ]); api.addAssets('../../node_modules/pdfjs-dist/build/pdf.worker.js', 'client'); From c4dd3aa645b8053c7dc7cf719f4165427d02240e Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:46:13 -0200 Subject: [PATCH 32/59] Add import of main ready from ui-utils --- packages/rocketchat-ui-master/client/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-ui-master/client/main.js b/packages/rocketchat-ui-master/client/main.js index 6bffc67cb76a..9c626f6a0753 100644 --- a/packages/rocketchat-ui-master/client/main.js +++ b/packages/rocketchat-ui-master/client/main.js @@ -6,6 +6,7 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { t } from 'meteor/rocketchat:utils'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; +import { mainReady } from 'meteor/rocketchat:ui-utils'; import Clipboard from 'clipboard'; import s from 'underscore.string'; @@ -123,7 +124,7 @@ Template.body.onRendered(function() { } }); -RocketChat.mainReady = new ReactiveVar(false); +RocketChat.mainReady = mainReady; Template.main.helpers({ removeSidenav() { const { modal } = this; From f82338eaba2de803b328e933c72a7c383e3830ef Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:47:24 -0200 Subject: [PATCH 33/59] Convert rocketchat-ui-sidenav to main module structure --- .../client/chatRoomItem.js | 20 ++++++---- .../client/createCombinedFlex.js | 10 +++-- .../rocketchat-ui-sidenav/client/index.js | 27 +++++++++++++ .../client/listChannelsFlex.js | 11 ++++-- .../client/listCombinedFlex.js | 11 ++++-- .../client/listPrivateGroupsFlex.js | 6 ++- .../rocketchat-ui-sidenav/client/roomList.js | 39 ++++++++++--------- .../rocketchat-ui-sidenav/client/sideNav.js | 22 ++++++----- .../client/sidebarHeader.js | 36 +++++++++-------- .../client/sidebarItem.js | 25 ++++++------ .../rocketchat-ui-sidenav/client/sortlist.js | 14 ++++--- .../rocketchat-ui-sidenav/client/toolbar.js | 11 ++++-- packages/rocketchat-ui-sidenav/package.js | 35 ++++------------- 13 files changed, 155 insertions(+), 112 deletions(-) create mode 100644 packages/rocketchat-ui-sidenav/client/index.js diff --git a/packages/rocketchat-ui-sidenav/client/chatRoomItem.js b/packages/rocketchat-ui-sidenav/client/chatRoomItem.js index ea0bbc0b813b..c48339fee60b 100644 --- a/packages/rocketchat-ui-sidenav/client/chatRoomItem.js +++ b/packages/rocketchat-ui-sidenav/client/chatRoomItem.js @@ -2,13 +2,17 @@ import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { t } from 'meteor/rocketchat:utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { roomTypes } from 'meteor/rocketchat:utils'; +import { Rooms } from 'meteor/rocketchat:models'; +import { callbacks } from 'meteor/rocketchat:callbacks'; Template.chatRoomItem.helpers({ roomData() { let { name } = this; if (this.fname) { - const realNameForDirectMessages = this.t === 'd' && RocketChat.settings.get('UI_Use_Real_Name'); - const realNameForChannel = this.t !== 'd' && RocketChat.settings.get('UI_Allow_room_names_with_special_chars'); + const realNameForDirectMessages = this.t === 'd' && settings.get('UI_Use_Real_Name'); + const realNameForChannel = this.t !== 'd' && settings.get('UI_Allow_room_names_with_special_chars'); if (realNameForDirectMessages || realNameForChannel) { name = this.fname; } @@ -26,7 +30,7 @@ Template.chatRoomItem.helpers({ this.alert = !this.hideUnreadStatus && this.alert; // && (!hasFocus || FlowRouter.getParam('_id') !== this.rid); - const icon = RocketChat.roomTypes.getIcon(this.t); + const icon = roomTypes.getIcon(this.t); const avatar = !icon; const roomData = { @@ -34,8 +38,8 @@ Template.chatRoomItem.helpers({ icon, avatar, username : this.name, - route: RocketChat.roomTypes.getRouteLink(this.t, this), - name: name || RocketChat.roomTypes.getRoomName(this.t, this), + route: roomTypes.getRouteLink(this.t, this), + name: name || roomTypes.getRoomName(this.t, this), unread, active, archivedClass, @@ -43,15 +47,15 @@ Template.chatRoomItem.helpers({ }; roomData.username = roomData.username || roomData.name; - if (!this.lastMessage && RocketChat.settings.get('Store_Last_Message')) { - const room = RocketChat.models.Rooms.findOne(this.rid || this._id, { fields: { lastMessage: 1 } }); + if (!this.lastMessage && settings.get('Store_Last_Message')) { + const room = Rooms.findOne(this.rid || this._id, { fields: { lastMessage: 1 } }); roomData.lastMessage = (room && room.lastMessage) || { msg: t('No_messages_yet') }; } return roomData; }, }); -RocketChat.callbacks.add('enter-room', (sub) => { +callbacks.add('enter-room', (sub) => { const items = $('.rooms-list .sidebar-item'); items.filter('.sidebar-item--active').removeClass('sidebar-item--active'); if (sub) { diff --git a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js index 18961020c182..094beb79465d 100644 --- a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js +++ b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js @@ -2,6 +2,10 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; +import { hasAllPermission } from 'meteor/rocketchat:authorization'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { SideNav } from 'meteor/rocketchat:ui-utils'; +import { handleError } from 'meteor/rocketchat:utils'; import _ from 'underscore'; Template.createCombinedFlex.helpers({ @@ -46,10 +50,10 @@ Template.createCombinedFlex.helpers({ }; }, privateSwitchDisabled() { - return RocketChat.authz.hasAllPermission(['create-c', 'create-p']) ? '' : 'disabled'; + return hasAllPermission(['create-c', 'create-p']) ? '' : 'disabled'; }, privateSwitchChecked() { - return RocketChat.authz.hasAllPermission('create-c') ? '' : 'checked'; + return hasAllPermission('create-c') ? '' : 'checked'; }, }); @@ -128,7 +132,7 @@ Template.createCombinedFlex.events({ SideNav.closeFlex(() => instance.clearForm()); if (!privateGroup) { - RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); + callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); } return FlowRouter.go(successRoute, { name: result.name }, FlowRouter.current().queryParams); diff --git a/packages/rocketchat-ui-sidenav/client/index.js b/packages/rocketchat-ui-sidenav/client/index.js new file mode 100644 index 000000000000..da2da78dfb6d --- /dev/null +++ b/packages/rocketchat-ui-sidenav/client/index.js @@ -0,0 +1,27 @@ +import './createCombinedFlex.html'; +import './chatRoomItem.html'; +import './listChannelsFlex.html'; +import './listCombinedFlex.html'; +import './listPrivateGroupsFlex.html'; +import './sidebarHeader.html'; +import './sidebarItem.html'; +import './sideNav.html'; +import './toolbar.html'; +import './roomList.html'; +import './sortlist.html'; +import './userStatus.html'; +import './createCombinedFlex'; +import './chatRoomItem'; +import './listChannelsFlex'; +import './listCombinedFlex'; +import './listPrivateGroupsFlex'; +import { toolbarSearch } from './sidebarHeader'; +import './sidebarItem'; +import './sideNav'; +import './roomList'; +import './sortlist'; +import './toolbar'; + +export { + toolbarSearch, +}; diff --git a/packages/rocketchat-ui-sidenav/client/listChannelsFlex.js b/packages/rocketchat-ui-sidenav/client/listChannelsFlex.js index db48950e7152..d845a027936d 100644 --- a/packages/rocketchat-ui-sidenav/client/listChannelsFlex.js +++ b/packages/rocketchat-ui-sidenav/client/listChannelsFlex.js @@ -1,6 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import { Subscriptions } from 'meteor/rocketchat:models'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; +import { SideNav } from 'meteor/rocketchat:ui-utils'; import _ from 'underscore'; import s from 'underscore.string'; @@ -21,10 +24,10 @@ Template.listChannelsFlex.helpers({ return Template.instance().show.get() === show; }, member() { - return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: true }); + return !!Subscriptions.findOne({ name: this.name, open: true }); }, hidden() { - return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: false }); + return !!Subscriptions.findOne({ name: this.name, open: false }); }, }); @@ -38,7 +41,7 @@ Template.listChannelsFlex.events({ }, 'click footer .create'() { - if (RocketChat.authz.hasAtLeastOnePermission('create-c')) { + if (hasAtLeastOnePermission('create-c')) { return SideNav.setFlex('createChannelFlex'); } }, @@ -102,7 +105,7 @@ Template.listChannelsFlex.onCreated(function() { break; } } - this.channelsList.set(RocketChat.models.Subscriptions.find({ + this.channelsList.set(Subscriptions.find({ name: new RegExp(s.trim(s.escapeRegExp(this.nameFilter.get())), 'i'), t: 'c', }, options).fetch() diff --git a/packages/rocketchat-ui-sidenav/client/listCombinedFlex.js b/packages/rocketchat-ui-sidenav/client/listCombinedFlex.js index e2e0003a19e3..da58fa20934c 100644 --- a/packages/rocketchat-ui-sidenav/client/listCombinedFlex.js +++ b/packages/rocketchat-ui-sidenav/client/listCombinedFlex.js @@ -1,6 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import { SideNav } from 'meteor/rocketchat:ui-utils'; +import { roomTypes } from 'meteor/rocketchat:utils'; +import { Subscriptions } from 'meteor/rocketchat:models'; import _ from 'underscore'; import s from 'underscore.string'; @@ -24,13 +27,13 @@ Template.listCombinedFlex.helpers({ return Template.instance().channelType.get() === type; }, member() { - return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: true }); + return !!Subscriptions.findOne({ name: this.name, open: true }); }, hidden() { - return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: false }); + return !!Subscriptions.findOne({ name: this.name, open: false }); }, roomIcon() { - return RocketChat.roomTypes.getIcon(this.t); + return roomTypes.getIcon(this.t); }, url() { return this.t === 'p' ? 'group' : 'channel'; @@ -122,7 +125,7 @@ Template.listCombinedFlex.onCreated(function() { break; } } - this.channelsList.set(RocketChat.models.Subscriptions.find({ + this.channelsList.set(Subscriptions.find({ name: new RegExp(s.trim(s.escapeRegExp(this.nameFilter.get())), 'i'), t: type, }, options).fetch() diff --git a/packages/rocketchat-ui-sidenav/client/listPrivateGroupsFlex.js b/packages/rocketchat-ui-sidenav/client/listPrivateGroupsFlex.js index e5052de26abf..6cefe6a5f638 100644 --- a/packages/rocketchat-ui-sidenav/client/listPrivateGroupsFlex.js +++ b/packages/rocketchat-ui-sidenav/client/listPrivateGroupsFlex.js @@ -2,6 +2,8 @@ import _ from 'underscore'; import s from 'underscore.string'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import { SideNav } from 'meteor/rocketchat:ui-utils'; +import { Subscriptions } from 'meteor/rocketchat:models'; Template.listPrivateGroupsFlex.helpers({ groups() { @@ -14,7 +16,7 @@ Template.listPrivateGroupsFlex.helpers({ return Template.instance().sort.get() === sort; }, hidden() { - return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: false }); + return !!Subscriptions.findOne({ name: this.name, open: false }); }, }); @@ -68,7 +70,7 @@ Template.listPrivateGroupsFlex.onCreated(function() { } } - this.groups.set(RocketChat.models.Subscriptions.find({ + this.groups.set(Subscriptions.find({ name: new RegExp(s.trim(s.escapeRegExp(this.nameFilter.get())), 'i'), t: 'p', archived: { $ne: true }, diff --git a/packages/rocketchat-ui-sidenav/client/roomList.js b/packages/rocketchat-ui-sidenav/client/roomList.js index 6b77ce7e35d0..137a73074302 100644 --- a/packages/rocketchat-ui-sidenav/client/roomList.js +++ b/packages/rocketchat-ui-sidenav/client/roomList.js @@ -1,6 +1,9 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat, UiTextContext } from 'meteor/rocketchat:lib'; +import { callbacks } from 'meteor/rocketchat:callbacks'; import { Template } from 'meteor/templating'; +import { ChatSubscription, Rooms, Users, Subscriptions } from 'meteor/rocketchat:models'; +import { UiTextContext, getUserPreference, roomTypes } from 'meteor/rocketchat:utils'; +import { settings } from 'meteor/rocketchat:settings'; Template.roomList.helpers({ rooms() { @@ -12,10 +15,10 @@ Template.roomList.helpers({ show unread */ if (this.anonymous) { - return RocketChat.models.Rooms.find({ t: 'c' }, { sort: { name: 1 } }); + return Rooms.find({ t: 'c' }, { sort: { name: 1 } }); } - const user = RocketChat.models.Users.findOne(Meteor.userId(), { + const user = Users.findOne(Meteor.userId(), { fields: { 'settings.preferences.sidebarSortby': 1, 'settings.preferences.sidebarShowFavorites': 1, @@ -24,7 +27,7 @@ Template.roomList.helpers({ }, }); - const sortBy = RocketChat.getUserPreference(user, 'sidebarSortby') || 'alphabetical'; + const sortBy = getUserPreference(user, 'sidebarSortby') || 'alphabetical'; const query = { open: true, }; @@ -34,7 +37,7 @@ Template.roomList.helpers({ if (sortBy === 'activity') { sort.lm = -1; } else { // alphabetical - sort[this.identifier === 'd' && RocketChat.settings.get('UI_Use_Real_Name') ? 'lowerCaseFName' : 'lowerCaseName'] = /descending/.test(sortBy) ? -1 : 1; + sort[this.identifier === 'd' && settings.get('UI_Use_Real_Name') ? 'lowerCaseFName' : 'lowerCaseName'] = /descending/.test(sortBy) ? -1 : 1; } if (this.identifier === 'unread') { @@ -44,7 +47,7 @@ Template.roomList.helpers({ return ChatSubscription.find(query, { sort }); } - const favoritesEnabled = !!(RocketChat.settings.get('Favorite_Rooms') && RocketChat.getUserPreference(user, 'sidebarShowFavorites')); + const favoritesEnabled = !!(settings.get('Favorite_Rooms') && getUserPreference(user, 'sidebarShowFavorites')); if (this.identifier === 'f') { query.f = favoritesEnabled; @@ -65,7 +68,7 @@ Template.roomList.helpers({ query.tokens = { $exists: true }; } - if (RocketChat.getUserPreference(user, 'sidebarShowUnread')) { + if (getUserPreference(user, 'sidebarShowUnread')) { query.$or = [ { alert: { $ne: true } }, { hideUnreadStatus: true }, @@ -101,11 +104,11 @@ Template.roomList.helpers({ noSubscriptionText() { const instance = Template.instance(); - return RocketChat.roomTypes.roomTypes[instance.data.identifier].getUiText(UiTextContext.NO_ROOMS_SUBSCRIBED) || 'No_channels_yet'; + return roomTypes.roomTypes[instance.data.identifier].getUiText(UiTextContext.NO_ROOMS_SUBSCRIBED) || 'No_channels_yet'; }, showRoomCounter() { - return RocketChat.getUserPreference(Meteor.userId(), 'roomCounterSidebar'); + return getUserPreference(Meteor.userId(), 'roomCounterSidebar'); }, }); @@ -119,7 +122,7 @@ const getLowerCaseNames = (room, nameDefault = '', fnameDefault = '') => { }; const mergeSubRoom = (subscription) => { - const room = RocketChat.models.Rooms.findOne(subscription.rid) || { _updatedAt: subscription.ts }; + const room = Rooms.findOne(subscription.rid) || { _updatedAt: subscription.ts }; subscription.lastMessage = room.lastMessage; subscription.lm = room._updatedAt; subscription.streamingOptions = room.streamingOptions; @@ -127,12 +130,12 @@ const mergeSubRoom = (subscription) => { }; const mergeRoomSub = (room) => { - const sub = RocketChat.models.Subscriptions.findOne({ rid: room._id }); + const sub = Subscriptions.findOne({ rid: room._id }); if (!sub) { return room; } - RocketChat.models.Subscriptions.update({ + Subscriptions.update({ rid: room._id, }, { $set: { @@ -146,10 +149,10 @@ const mergeRoomSub = (room) => { return room; }; -RocketChat.callbacks.add('cachedCollection-received-rooms', mergeRoomSub); -RocketChat.callbacks.add('cachedCollection-sync-rooms', mergeRoomSub); -RocketChat.callbacks.add('cachedCollection-loadFromServer-rooms', mergeRoomSub); +callbacks.add('cachedCollection-received-rooms', mergeRoomSub); +callbacks.add('cachedCollection-sync-rooms', mergeRoomSub); +callbacks.add('cachedCollection-loadFromServer-rooms', mergeRoomSub); -RocketChat.callbacks.add('cachedCollection-received-subscriptions', mergeSubRoom); -RocketChat.callbacks.add('cachedCollection-sync-subscriptions', mergeSubRoom); -RocketChat.callbacks.add('cachedCollection-loadFromServer-subscriptions', mergeSubRoom); +callbacks.add('cachedCollection-received-subscriptions', mergeSubRoom); +callbacks.add('cachedCollection-sync-subscriptions', mergeSubRoom); +callbacks.add('cachedCollection-loadFromServer-subscriptions', mergeSubRoom); diff --git a/packages/rocketchat-ui-sidenav/client/sideNav.js b/packages/rocketchat-ui-sidenav/client/sideNav.js index 15400b520be7..27cd024e7fab 100644 --- a/packages/rocketchat-ui-sidenav/client/sideNav.js +++ b/packages/rocketchat-ui-sidenav/client/sideNav.js @@ -3,6 +3,10 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; +import { SideNav, menu } from 'meteor/rocketchat:ui-utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { roomTypes, getUserPreference } from 'meteor/rocketchat:utils'; +import { Users } from 'meteor/rocketchat:models'; Template.sideNav.helpers({ flexTemplate() { @@ -14,11 +18,11 @@ Template.sideNav.helpers({ }, footer() { - return String(RocketChat.settings.get('Layout_Sidenav_Footer')).trim(); + return String(settings.get('Layout_Sidenav_Footer')).trim(); }, roomType() { - return RocketChat.roomTypes.getTypes().map((roomType) => ({ + return roomTypes.getTypes().map((roomType) => ({ template: roomType.customTemplate || 'roomList', data: { header: roomType.header, @@ -34,12 +38,12 @@ Template.sideNav.helpers({ }, sidebarViewMode() { - const viewMode = RocketChat.getUserPreference(Meteor.userId(), 'sidebarViewMode'); + const viewMode = getUserPreference(Meteor.userId(), 'sidebarViewMode'); return viewMode ? viewMode : 'condensed'; }, sidebarHideAvatar() { - return RocketChat.getUserPreference(Meteor.userId(), 'sidebarHideAvatar'); + return getUserPreference(Meteor.userId(), 'sidebarHideAvatar'); }, }); @@ -66,8 +70,8 @@ Template.sideNav.onRendered(function() { SideNav.init(); menu.init(); lazyloadtick(); - const first_channel_login = RocketChat.settings.get('First_Channel_After_Login'); - const room = RocketChat.roomTypes.findRoom('c', first_channel_login, Meteor.userId()); + const first_channel_login = settings.get('First_Channel_After_Login'); + const room = roomTypes.findRoom('c', first_channel_login, Meteor.userId()); if (room !== undefined && room._id !== '') { FlowRouter.go(`/channel/${ first_channel_login }`); } @@ -79,12 +83,12 @@ Template.sideNav.onCreated(function() { this.groupedByType = new ReactiveVar(false); this.autorun(() => { - const user = RocketChat.models.Users.findOne(Meteor.userId(), { + const user = Users.findOne(Meteor.userId(), { fields: { 'settings.preferences.sidebarGroupByType': 1, }, }); - const userPref = RocketChat.getUserPreference(user, 'sidebarGroupByType'); - this.groupedByType.set(userPref ? userPref : RocketChat.settings.get('UI_Group_Channels_By_Type')); + const userPref = getUserPreference(user, 'sidebarGroupByType'); + this.groupedByType.set(userPref ? userPref : settings.get('UI_Group_Channels_By_Type')); }); }); diff --git a/packages/rocketchat-ui-sidenav/client/sidebarHeader.js b/packages/rocketchat-ui-sidenav/client/sidebarHeader.js index bc5c6eec5535..f1b4e521ebec 100644 --- a/packages/rocketchat-ui-sidenav/client/sidebarHeader.js +++ b/packages/rocketchat-ui-sidenav/client/sidebarHeader.js @@ -2,12 +2,16 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { popover } from 'meteor/rocketchat:ui'; -import { t } from 'meteor/rocketchat:utils'; +import { popover } from 'meteor/rocketchat:ui-utils'; +import { t, getUserPreference, handleError } from 'meteor/rocketchat:utils'; +import { AccountBox, menu, SideNav } from 'meteor/rocketchat:ui-utils'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { settings } from 'meteor/rocketchat:settings'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; const setStatus = (status) => { AccountBox.setStatus(status); - RocketChat.callbacks.run('userStatusManuallySet', status); + callbacks.run('userStatusManuallySet', status); popover.close(); }; @@ -18,11 +22,11 @@ const viewModeIcon = { }; const extendedViewOption = (user) => { - if (RocketChat.settings.get('Store_Last_Message')) { + if (settings.get('Store_Last_Message')) { return { icon: viewModeIcon.extended, name: t('Extended'), - modifier: RocketChat.getUserPreference(user, 'sidebarViewMode') === 'extended' ? 'bold' : null, + modifier: getUserPreference(user, 'sidebarViewMode') === 'extended' ? 'bold' : null, action: () => { Meteor.call('saveUserPreferences', { sidebarViewMode: 'extended' }, function(error) { if (error) { @@ -39,7 +43,7 @@ const extendedViewOption = (user) => { const showToolbar = new ReactiveVar(false); const selectorSearch = '.toolbar__search .rc-input__element'; -toolbarSearch = { +export const toolbarSearch = { shortcut: false, clear() { const $inputMessage = $('.js-input-message'); @@ -81,9 +85,9 @@ const toolbarButtons = (user) => [{ }, { name: t('View_mode'), - icon: () => viewModeIcon[RocketChat.getUserPreference(user, 'sidebarViewMode') || 'condensed'], + icon: () => viewModeIcon[getUserPreference(user, 'sidebarViewMode') || 'condensed'], action: (e) => { - const hideAvatarSetting = RocketChat.getUserPreference(user, 'sidebarHideAvatar'); + const hideAvatarSetting = getUserPreference(user, 'sidebarHideAvatar'); const config = { columns: [ { @@ -94,7 +98,7 @@ const toolbarButtons = (user) => [{ { icon: viewModeIcon.medium, name: t('Medium'), - modifier: RocketChat.getUserPreference(user, 'sidebarViewMode') === 'medium' ? 'bold' : null, + modifier: getUserPreference(user, 'sidebarViewMode') === 'medium' ? 'bold' : null, action: () => { Meteor.call('saveUserPreferences', { sidebarViewMode: 'medium' }, function(error) { if (error) { @@ -106,7 +110,7 @@ const toolbarButtons = (user) => [{ { icon: viewModeIcon.condensed, name: t('Condensed'), - modifier: RocketChat.getUserPreference(user, 'sidebarViewMode') === 'condensed' ? 'bold' : null, + modifier: getUserPreference(user, 'sidebarViewMode') === 'condensed' ? 'bold' : null, action: () => { Meteor.call('saveUserPreferences', { sidebarViewMode: 'condensed' }, function(error) { if (error) { @@ -161,7 +165,7 @@ const toolbarButtons = (user) => [{ { name: t('Create_A_New_Channel'), icon: 'edit-rounded', - condition: () => RocketChat.authz.hasAtLeastOnePermission(['create-c', 'create-p']), + condition: () => hasAtLeastOnePermission(['create-c', 'create-p']), action: () => { menu.close(); FlowRouter.go('create-channel'); @@ -170,10 +174,10 @@ const toolbarButtons = (user) => [{ { name: t('Options'), icon: 'menu', - condition: () => AccountBox.getItems().length || RocketChat.authz.hasAtLeastOnePermission(['manage-emoji', 'manage-integrations', 'manage-oauth-apps', 'manage-own-integrations', 'manage-sounds', 'view-logs', 'view-privileged-setting', 'view-room-administration', 'view-statistics', 'view-user-administration']), + condition: () => AccountBox.getItems().length || hasAtLeastOnePermission(['manage-emoji', 'manage-integrations', 'manage-oauth-apps', 'manage-own-integrations', 'manage-sounds', 'view-logs', 'view-privileged-setting', 'view-room-administration', 'view-statistics', 'view-user-administration']), action: (e) => { let adminOption; - if (RocketChat.authz.hasAtLeastOnePermission(['manage-emoji', 'manage-integrations', 'manage-oauth-apps', 'manage-own-integrations', 'manage-sounds', 'view-logs', 'view-privileged-setting', 'view-room-administration', 'view-statistics', 'view-user-administration'])) { + if (hasAtLeastOnePermission(['manage-emoji', 'manage-integrations', 'manage-oauth-apps', 'manage-own-integrations', 'manage-sounds', 'view-logs', 'view-privileged-setting', 'view-room-administration', 'view-statistics', 'view-user-administration'])) { adminOption = { icon: 'customize', name: t('Administration'), @@ -237,7 +241,7 @@ Template.sidebarHeader.helpers({ myUserInfo() { const id = Meteor.userId(); - if (id == null && RocketChat.settings.get('Accounts_AllowAnonymousRead')) { + if (id == null && settings.get('Accounts_AllowAnonymousRead')) { return { username: 'anonymous', status: 'online', @@ -263,7 +267,7 @@ Template.sidebarHeader.events({ return this.action && this.action.apply(this, [e]); }, 'click .sidebar__header .avatar'(e) { - if (!(Meteor.userId() == null && RocketChat.settings.get('Accounts_AllowAnonymousRead'))) { + if (!(Meteor.userId() == null && settings.get('Accounts_AllowAnonymousRead'))) { const user = Meteor.user(); const config = { popoverClass: 'sidebar-header', @@ -320,7 +324,7 @@ Template.sidebarHeader.events({ id: 'logout', action: () => { Meteor.logout(() => { - RocketChat.callbacks.run('afterLogoutCleanUp', user); + callbacks.run('afterLogoutCleanUp', user); Meteor.call('logoutCleanUp', user); FlowRouter.go('home'); popover.close(); diff --git a/packages/rocketchat-ui-sidenav/client/sidebarItem.js b/packages/rocketchat-ui-sidenav/client/sidebarItem.js index 20cfc6f16002..f24964c4110a 100644 --- a/packages/rocketchat-ui-sidenav/client/sidebarItem.js +++ b/packages/rocketchat-ui-sidenav/client/sidebarItem.js @@ -2,10 +2,13 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { t } from 'meteor/rocketchat:utils'; +import { t, getUserPreference, roomTypes } from 'meteor/rocketchat:utils'; import moment from 'moment'; -import { renderMessageBody } from 'meteor/rocketchat:ui-message'; -import { popover } from 'meteor/rocketchat:ui'; +import { popover, renderMessageBody } from 'meteor/rocketchat:ui-utils'; +import { Users, ChatSubscription } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; +import { menu } from 'meteor/rocketchat:ui-utils'; Template.sidebarItem.helpers({ or(...args) { @@ -19,7 +22,7 @@ Template.sidebarItem.helpers({ return this.rid || this._id; }, isExtendedViewMode() { - return RocketChat.getUserPreference(Meteor.userId(), 'sidebarViewMode') === 'extended'; + return getUserPreference(Meteor.userId(), 'sidebarViewMode') === 'extended'; }, lastMessage() { return this.lastMessage && Template.instance().renderedMessage; @@ -58,7 +61,7 @@ function setLastMessageTs(instance, ts) { } Template.sidebarItem.onCreated(function() { - this.user = RocketChat.models.Users.findOne(Meteor.userId(), { fields: { username: 1 } }); + this.user = Users.findOne(Meteor.userId(), { fields: { username: 1 } }); this.lastMessageTs = new ReactiveVar(); this.timeAgoInterval; @@ -68,7 +71,7 @@ Template.sidebarItem.onCreated(function() { this.autorun(() => { const currentData = Template.currentData(); - if (!currentData.lastMessage || RocketChat.getUserPreference(Meteor.userId(), 'sidebarViewMode') !== 'extended') { + if (!currentData.lastMessage || getUserPreference(Meteor.userId(), 'sidebarViewMode') !== 'extended') { return clearInterval(this.timeAgoInterval); } @@ -82,7 +85,7 @@ Template.sidebarItem.onCreated(function() { return this.renderedMessage = '******'; } - const otherUser = RocketChat.settings.get('UI_Use_Real_Name') ? currentData.lastMessage.u.name || currentData.lastMessage.u.username : currentData.lastMessage.u.username; + const otherUser = settings.get('UI_Use_Real_Name') ? currentData.lastMessage.u.name || currentData.lastMessage.u.username : currentData.lastMessage.u.username; const renderedMessage = renderMessageBody(currentData.lastMessage).replace(//g, ' '); const sender = this.user._id === currentData.lastMessage.u._id ? t('You') : otherUser; @@ -117,13 +120,13 @@ Template.sidebarItem.events({ if (!roomData) { return false; } - if (roomData.t === 'c' && !RocketChat.authz.hasAtLeastOnePermission('leave-c')) { return false; } - if (roomData.t === 'p' && !RocketChat.authz.hasAtLeastOnePermission('leave-p')) { return false; } + if (roomData.t === 'c' && !hasAtLeastOnePermission('leave-c')) { return false; } + if (roomData.t === 'p' && !hasAtLeastOnePermission('leave-p')) { return false; } return !(((roomData.cl != null) && !roomData.cl) || (['d', 'l'].includes(roomData.t))); }; - const canFavorite = RocketChat.settings.get('Favorite_Rooms') && ChatSubscription.find({ rid: this.rid }).count() > 0; + const canFavorite = settings.get('Favorite_Rooms') && ChatSubscription.find({ rid: this.rid }).count() > 0; const isFavorite = () => { const sub = ChatSubscription.findOne({ rid: this.rid }, { fields: { f: 1 } }); if (((sub != null ? sub.f : undefined) != null) && sub.f) { @@ -209,7 +212,7 @@ Template.sidebarItemIcon.helpers({ } if (this.t === 'l') { - return RocketChat.roomTypes.getUserStatus('l', this.rid) || 'offline'; + return roomTypes.getUserStatus('l', this.rid) || 'offline'; } return false; diff --git a/packages/rocketchat-ui-sidenav/client/sortlist.js b/packages/rocketchat-ui-sidenav/client/sortlist.js index 899093c82405..9daf91c69b71 100644 --- a/packages/rocketchat-ui-sidenav/client/sortlist.js +++ b/packages/rocketchat-ui-sidenav/client/sortlist.js @@ -1,26 +1,28 @@ import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; -import { popover } from 'meteor/rocketchat:ui'; +import { popover } from 'meteor/rocketchat:ui-utils'; +import { getUserPreference } from 'meteor/rocketchat:utils'; +import { settings } from 'meteor/rocketchat:settings'; const checked = function(prop, field) { const userId = Meteor.userId(); if (prop === 'sidebarShowFavorites') { - return RocketChat.getUserPreference(userId, 'sidebarShowFavorites'); + return getUserPreference(userId, 'sidebarShowFavorites'); } if (prop === 'sidebarGroupByType') { - return RocketChat.getUserPreference(userId, 'sidebarGroupByType'); + return getUserPreference(userId, 'sidebarGroupByType'); } if (prop === 'sidebarShowUnread') { - return RocketChat.getUserPreference(userId, 'sidebarShowUnread'); + return getUserPreference(userId, 'sidebarShowUnread'); } if (prop === 'sidebarSortby') { - return (RocketChat.getUserPreference(userId, 'sidebarSortby') || 'alphabetical') === field; + return (getUserPreference(userId, 'sidebarSortby') || 'alphabetical') === field; } }; Template.sortlist.helpers({ favorite() { - return RocketChat.settings.get('Favorite_Rooms'); + return settings.get('Favorite_Rooms'); }, checked, bold(...props) { diff --git a/packages/rocketchat-ui-sidenav/client/toolbar.js b/packages/rocketchat-ui-sidenav/client/toolbar.js index 6bd7d26a8a92..70f0fdf4a937 100644 --- a/packages/rocketchat-ui-sidenav/client/toolbar.js +++ b/packages/rocketchat-ui-sidenav/client/toolbar.js @@ -5,6 +5,11 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/tap:i18n'; +import { Rooms, Subscriptions } from 'meteor/rocketchat:models'; +import { roomTypes } from 'meteor/rocketchat:utils'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; +import { menu } from 'meteor/rocketchat:ui-utils'; +import { toolbarSearch } from './sidebarHeader'; import _ from 'underscore'; let filterText = ''; @@ -88,7 +93,7 @@ Template.toolbar.helpers({ popupConfig() { const config = { cls: 'search-results-list', - collection: Meteor.userId() ? RocketChat.models.Subscriptions : RocketChat.models.Rooms, + collection: Meteor.userId() ? Subscriptions : Rooms, template: 'toolbarSearchList', sidebar: true, emptyTemplate: 'toolbarSearchListEmpty', @@ -161,7 +166,7 @@ Template.toolbar.helpers({ getValue(_id, collection, records) { const doc = _.findWhere(records, { _id }); - RocketChat.roomTypes.openRouteLink(doc.t, doc, FlowRouter.current().queryParams); + roomTypes.openRouteLink(doc.t, doc, FlowRouter.current().queryParams); menu.close(); }, }; @@ -190,7 +195,7 @@ Template.toolbar.events({ }, 'click [role="search"] button, touchend [role="search"] button'(e) { - if (RocketChat.authz.hasAtLeastOnePermission(['create-c', 'create-p'])) { + if (hasAtLeastOnePermission(['create-c', 'create-p'])) { // TODO: resolve this name menu/sidebar/sidebav/flex... menu.close(); FlowRouter.go('create-channel'); diff --git a/packages/rocketchat-ui-sidenav/package.js b/packages/rocketchat-ui-sidenav/package.js index d92210779e0a..66e3d0decee0 100644 --- a/packages/rocketchat-ui-sidenav/package.js +++ b/packages/rocketchat-ui-sidenav/package.js @@ -15,35 +15,14 @@ Package.onUse(function(api) { 'ecmascript', 'templating', 'rocketchat:lib', + 'rocketchat:callbacks', + 'rocketchat:authorization', + 'rocketchat:settings', 'rocketchat:utils', - 'rocketchat:ui', + 'rocketchat:ui-utils', + 'rocketchat:models', 'rocketchat:lazy-load', ]); - - api.addFiles('client/createCombinedFlex.html', 'client'); - api.addFiles('client/chatRoomItem.html', 'client'); - api.addFiles('client/listChannelsFlex.html', 'client'); - api.addFiles('client/listCombinedFlex.html', 'client'); - api.addFiles('client/listPrivateGroupsFlex.html', 'client'); - api.addFiles('client/sidebarHeader.html', 'client'); - api.addFiles('client/sidebarItem.html', 'client'); - api.addFiles('client/sideNav.html', 'client'); - api.addFiles('client/toolbar.html', 'client'); - api.addFiles('client/roomList.html', 'client'); - api.addFiles('client/sortlist.html', 'client'); - api.addFiles('client/userStatus.html', 'client'); - - api.addFiles('client/createCombinedFlex.js', 'client'); - api.addFiles('client/chatRoomItem.js', 'client'); - api.addFiles('client/listChannelsFlex.js', 'client'); - api.addFiles('client/listCombinedFlex.js', 'client'); - api.addFiles('client/listPrivateGroupsFlex.js', 'client'); - api.addFiles('client/sidebarHeader.js', 'client'); - api.addFiles('client/sidebarItem.js', 'client'); - api.addFiles('client/sideNav.js', 'client'); - api.addFiles('client/roomList.js', 'client'); - api.addFiles('client/sortlist.js', 'client'); - api.addFiles('client/toolbar.js', 'client'); - - api.export('toolbarSearch', 'client'); + api.mainModule('client/index.js', 'client'); }); + From 2ffadf05ff5b07c26294b8ca4dfa605a6b04d6f5 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:47:49 -0200 Subject: [PATCH 34/59] Add imports of toolbarSearch from ui-sidenav --- packages/rocketchat-ui-master/client/main.js | 2 +- packages/rocketchat-ui-master/package.js | 2 ++ packages/rocketchat-ui-message/client/popup/messagePopup.js | 1 + .../client/popup/messagePopupSlashCommandPreview.js | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-ui-master/client/main.js b/packages/rocketchat-ui-master/client/main.js index 9c626f6a0753..b486d859acb6 100644 --- a/packages/rocketchat-ui-master/client/main.js +++ b/packages/rocketchat-ui-master/client/main.js @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; -import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { t } from 'meteor/rocketchat:utils'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { mainReady } from 'meteor/rocketchat:ui-utils'; +import { toolbarSearch } from 'meteor/rocketchat:ui-sidenav'; import Clipboard from 'clipboard'; import s from 'underscore.string'; diff --git a/packages/rocketchat-ui-master/package.js b/packages/rocketchat-ui-master/package.js index 1b89acbef491..7f206de98658 100644 --- a/packages/rocketchat-ui-master/package.js +++ b/packages/rocketchat-ui-master/package.js @@ -18,6 +18,8 @@ Package.onUse(function(api) { 'reactive-var', 'rocketchat:lib', 'rocketchat:utils', + 'rocketchat:ui-utils', + 'rocketchat:ui-sidenav', 'meteorhacks:inject-initial', ]); diff --git a/packages/rocketchat-ui-message/client/popup/messagePopup.js b/packages/rocketchat-ui-message/client/popup/messagePopup.js index 4a109515b84d..bb4d660fe617 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopup.js +++ b/packages/rocketchat-ui-message/client/popup/messagePopup.js @@ -4,6 +4,7 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import { toolbarSearch } from 'meteor/rocketchat:ui-sidenav'; import _ from 'underscore'; import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; diff --git a/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommandPreview.js b/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommandPreview.js index d59fd6a382d0..63090414a00a 100644 --- a/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommandPreview.js +++ b/packages/rocketchat-ui-message/client/popup/messagePopupSlashCommandPreview.js @@ -3,6 +3,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { RocketChat } from 'meteor/rocketchat:lib'; +import { toolbarSearch } from 'meteor/rocketchat:ui-sidenav'; import _ from 'underscore'; const keys = { From 545d53418c0c9a1017ce55162d8e34e644ec370c Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 8 Jan 2019 18:48:10 -0200 Subject: [PATCH 35/59] Remove toolbarSearch from eslintrc globals --- .eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 2017713d8d12..cce120c59cf4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -51,7 +51,6 @@ "Settings" : false, "SideNav" : false, "TAPi18next" : false, - "toolbarSearch" : false, "TwitterConnect" : false, "updateAvatarOfUsername" : false, "UserRoles" : false, From c4320eabad6c514527ceed3c878a095bc9d629b3 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:29:42 -0200 Subject: [PATCH 36/59] Move CachedCollection to a specific package --- .meteor/packages | 3 ++- .meteor/versions | 1 + packages/rocketchat-models/client/index.js | 3 --- .../client/index.js | 1 + .../client/models/CachedCollection.js | 0 .../package.js | 19 +++++++++++++++++++ 6 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 packages/rocketchat-ui-cached-collection/client/index.js rename packages/{rocketchat-models => rocketchat-ui-cached-collection}/client/models/CachedCollection.js (100%) create mode 100644 packages/rocketchat-ui-cached-collection/package.js diff --git a/.meteor/packages b/.meteor/packages index 3e0d5e62b45d..5969ddcb02fe 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -207,4 +207,5 @@ rocketchat:metrics rocketchat:callbacks rocketchat:notifications rocketchat:promises -rocketchat:ui-utils \ No newline at end of file +rocketchat:ui-utils +rocketchat:ui-cached-collection \ No newline at end of file diff --git a/.meteor/versions b/.meteor/versions index 6cae6fc2e85b..650c3b9ead18 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -248,6 +248,7 @@ rocketchat:tooltip@0.0.1 rocketchat:ui@0.1.0 rocketchat:ui-account@0.1.0 rocketchat:ui-admin@0.1.0 +rocketchat:ui-cached-collection@0.0.1 rocketchat:ui-clean-history@0.0.1 rocketchat:ui-flextab@0.1.0 rocketchat:ui-login@0.1.0 diff --git a/packages/rocketchat-models/client/index.js b/packages/rocketchat-models/client/index.js index 8dc4e8f481b7..6f520bcef5fc 100644 --- a/packages/rocketchat-models/client/index.js +++ b/packages/rocketchat-models/client/index.js @@ -3,7 +3,6 @@ import { Base } from './models/_Base'; import Avatars from './models/Avatars'; import Uploads from './models/Uploads'; import UserDataFiles from './models/UserDataFiles'; -import { CachedCollection, CachedCollectionManager } from './models/CachedCollection'; import { Roles } from './models/Roles'; import { Subscriptions as subscriptions } from './models/Subscriptions'; import { Users as users } from './models/Users'; @@ -31,8 +30,6 @@ export { Avatars, Uploads, UserDataFiles, - CachedCollection, - CachedCollectionManager, Roles, Subscriptions, Users, diff --git a/packages/rocketchat-ui-cached-collection/client/index.js b/packages/rocketchat-ui-cached-collection/client/index.js new file mode 100644 index 000000000000..f95870e1a83f --- /dev/null +++ b/packages/rocketchat-ui-cached-collection/client/index.js @@ -0,0 +1 @@ +export { CachedCollection, CachedCollectionManager } from './models/CachedCollection'; diff --git a/packages/rocketchat-models/client/models/CachedCollection.js b/packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js similarity index 100% rename from packages/rocketchat-models/client/models/CachedCollection.js rename to packages/rocketchat-ui-cached-collection/client/models/CachedCollection.js diff --git a/packages/rocketchat-ui-cached-collection/package.js b/packages/rocketchat-ui-cached-collection/package.js new file mode 100644 index 000000000000..4f3dccc4a2d2 --- /dev/null +++ b/packages/rocketchat-ui-cached-collection/package.js @@ -0,0 +1,19 @@ +Package.describe({ + name: 'rocketchat:ui-cached-collection', + version: '0.0.1', + // Brief, one-line summary of the package. + summary: '', + // URL to the Git repository containing the source code for this package. + git: '', + // By default, Meteor will default to using README.md for documentation. + // To avoid submitting documentation, set this field to null. + documentation: 'README.md', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:callbacks', + ]); + api.mainModule('client/index.js', 'client'); +}); From 0ff6b3ec8d83366e5d9725ea93e722f9ea38a151 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:31:03 -0200 Subject: [PATCH 37/59] Change imports of CachedCollection to new package --- packages/rocketchat-authorization/client/startup.js | 2 +- packages/rocketchat-authorization/package.js | 1 + packages/rocketchat-lib/client/lib/cachedCollection.js | 2 +- packages/rocketchat-models/client/models/CachedChatRoom.js | 2 +- .../rocketchat-models/client/models/CachedChatSubscription.js | 2 +- packages/rocketchat-models/client/models/ChatPermissions.js | 2 +- packages/rocketchat-models/package.js | 1 + packages/rocketchat-ui-utils/client/lib/RoomManager.js | 3 ++- 8 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/rocketchat-authorization/client/startup.js b/packages/rocketchat-authorization/client/startup.js index e88296ba57e9..2c155c613f29 100644 --- a/packages/rocketchat-authorization/client/startup.js +++ b/packages/rocketchat-authorization/client/startup.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { CachedCollectionManager } from 'meteor/rocketchat:models'; +import { CachedCollectionManager } from 'meteor/rocketchat:ui-cached-collection'; import { hasAllPermission } from './hasPermission'; Meteor.startup(async() => { diff --git a/packages/rocketchat-authorization/package.js b/packages/rocketchat-authorization/package.js index 84c855815ab5..269a60ca64d3 100644 --- a/packages/rocketchat-authorization/package.js +++ b/packages/rocketchat-authorization/package.js @@ -13,6 +13,7 @@ Package.onUse(function(api) { 'rocketchat:utils', 'rocketchat:models', 'rocketchat:notifications', + 'rocketchat:ui-cached-collection', ]); api.use([ 'templating', diff --git a/packages/rocketchat-lib/client/lib/cachedCollection.js b/packages/rocketchat-lib/client/lib/cachedCollection.js index d41e8d115676..3f3a74f48ae2 100644 --- a/packages/rocketchat-lib/client/lib/cachedCollection.js +++ b/packages/rocketchat-lib/client/lib/cachedCollection.js @@ -1,4 +1,4 @@ -import { CachedCollection, CachedCollectionManager } from 'meteor/rocketchat:models'; +import { CachedCollection, CachedCollectionManager } from 'meteor/rocketchat:ui-cached-collection'; RocketChat.CachedCollectionManager = CachedCollectionManager; RocketChat.CachedCollection = CachedCollection; diff --git a/packages/rocketchat-models/client/models/CachedChatRoom.js b/packages/rocketchat-models/client/models/CachedChatRoom.js index 9ba03b2e373b..d6c41d0f2123 100644 --- a/packages/rocketchat-models/client/models/CachedChatRoom.js +++ b/packages/rocketchat-models/client/models/CachedChatRoom.js @@ -1,3 +1,3 @@ -import { CachedCollection } from './CachedCollection'; +import { CachedCollection } from 'meteor/rocketchat:ui-cached-collection'; export const CachedChatRoom = new CachedCollection({ name: 'rooms' }); diff --git a/packages/rocketchat-models/client/models/CachedChatSubscription.js b/packages/rocketchat-models/client/models/CachedChatSubscription.js index 8d49f28397c9..da8822906118 100644 --- a/packages/rocketchat-models/client/models/CachedChatSubscription.js +++ b/packages/rocketchat-models/client/models/CachedChatSubscription.js @@ -1,3 +1,3 @@ -import { CachedCollection } from './CachedCollection'; +import { CachedCollection } from 'meteor/rocketchat:ui-cached-collection'; export const CachedChatSubscription = new CachedCollection({ name: 'subscriptions' }); diff --git a/packages/rocketchat-models/client/models/ChatPermissions.js b/packages/rocketchat-models/client/models/ChatPermissions.js index 527996dd9d22..f87bb73ec3a5 100644 --- a/packages/rocketchat-models/client/models/ChatPermissions.js +++ b/packages/rocketchat-models/client/models/ChatPermissions.js @@ -1,4 +1,4 @@ -import { CachedCollection } from './CachedCollection'; +import { CachedCollection } from 'meteor/rocketchat:ui-cached-collection'; export const AuthzCachedCollection = new CachedCollection({ name: 'permissions', diff --git a/packages/rocketchat-models/package.js b/packages/rocketchat-models/package.js index 74c8e04810b0..f09b9482f4aa 100755 --- a/packages/rocketchat-models/package.js +++ b/packages/rocketchat-models/package.js @@ -10,6 +10,7 @@ Package.onUse(function(api) { 'ecmascript', 'rocketchat:settings', 'rocketchat:callbacks', + 'rocketchat:ui-cached-collection', 'konecty:multiple-instances-status', ]); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-ui-utils/client/lib/RoomManager.js b/packages/rocketchat-ui-utils/client/lib/RoomManager.js index d124c87dde0e..6c4bad817df8 100644 --- a/packages/rocketchat-ui-utils/client/lib/RoomManager.js +++ b/packages/rocketchat-ui-utils/client/lib/RoomManager.js @@ -8,7 +8,8 @@ import { roomTypes as _roomTypes } from 'meteor/rocketchat:utils'; import { promises } from 'meteor/rocketchat:promises'; import { callbacks } from 'meteor/rocketchat:callbacks'; import { Notifications } from 'meteor/rocketchat:notifications'; -import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription, CachedCollectionManager } from 'meteor/rocketchat:models'; +import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription } from 'meteor/rocketchat:models'; +import { CachedCollectionManager } from 'meteor/rocketchat:ui-cached-collection'; import _ from 'underscore'; import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; import { mainReady } from './mainReady'; From 803abf653dbf8858a3bb97e1246bc15ccd336b06 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:32:47 -0200 Subject: [PATCH 38/59] Move some functions to rocketchat:ui-utils --- .../client/iframe_client.js | 108 +---------------- packages/rocketchat-iframe-login/package.js | 1 + packages/rocketchat-lib/client/lib/Layout.js | 14 +-- packages/rocketchat-ui-utils/client/index.js | 7 ++ .../client/lib/IframeLogin.js | 112 ++++++++++++++++++ .../rocketchat-ui-utils/client/lib/Layout.js | 14 +++ .../client/lib/fireGlobalEvent.js | 20 ++++ .../rocketchat-ui/client/lib/fireEvent.js | 21 +--- 8 files changed, 159 insertions(+), 138 deletions(-) create mode 100644 packages/rocketchat-ui-utils/client/lib/IframeLogin.js create mode 100644 packages/rocketchat-ui-utils/client/lib/Layout.js create mode 100644 packages/rocketchat-ui-utils/client/lib/fireGlobalEvent.js diff --git a/packages/rocketchat-iframe-login/client/iframe_client.js b/packages/rocketchat-iframe-login/client/iframe_client.js index 19825a9ba837..9da0f7e7e81f 100644 --- a/packages/rocketchat-iframe-login/client/iframe_client.js +++ b/packages/rocketchat-iframe-login/client/iframe_client.js @@ -1,10 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import { RocketChat } from 'meteor/rocketchat:lib'; import { Accounts } from 'meteor/accounts-base'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { HTTP } from 'meteor/http'; +import { IframeLogin } from 'meteor/rocketchat:ui-utils'; import { OAuth } from 'meteor/oauth'; import _ from 'underscore'; @@ -14,109 +11,6 @@ Accounts._unstoreLoginToken = function(...args) { _unstoreLoginToken.apply(Accounts, args); }; -class IframeLogin { - constructor() { - this.enabled = false; - this.reactiveIframeUrl = new ReactiveVar(); - this.reactiveEnabled = new ReactiveVar(); - this.iframeUrl = undefined; - this.apiUrl = undefined; - this.apiMethod = undefined; - - Tracker.autorun((c) => { - this.enabled = RocketChat.settings.get('Accounts_iframe_enabled'); - this.reactiveEnabled.set(this.enabled); - - this.iframeUrl = RocketChat.settings.get('Accounts_iframe_url'); - this.apiUrl = RocketChat.settings.get('Accounts_Iframe_api_url'); - this.apiMethod = RocketChat.settings.get('Accounts_Iframe_api_method'); - - if (this.enabled === false) { - return c.stop(); - } - - if (this.enabled === true && this.iframeUrl && this.apiUrl && this.apiMethod) { - c.stop(); - if (!Accounts._storedLoginToken()) { - this.tryLogin(() => {}); - } - } - }); - } - - tryLogin(callback) { - if (!this.enabled) { - return; - } - - if (!this.iframeUrl || !this.apiUrl || !this.apiMethod) { - return; - } - - console.log('tryLogin'); - const options = { - beforeSend: (xhr) => { - xhr.withCredentials = true; - }, - }; - - let { iframeUrl } = this; - let separator = '?'; - if (iframeUrl.indexOf('?') > -1) { - separator = '&'; - } - - if (window.cordova) { - iframeUrl += `${ separator }client=cordova`; - } else if (navigator.userAgent.indexOf('Electron') > -1) { - iframeUrl += `${ separator }client=electron`; - } - - HTTP.call(this.apiMethod, this.apiUrl, options, (error, result) => { - console.log(error, result); - if (result && result.data && (result.data.token || result.data.loginToken)) { - this.loginWithToken(result.data, (error, result) => { - if (error) { - this.reactiveIframeUrl.set(iframeUrl); - } else { - this.reactiveIframeUrl.set(); - } - callback(error, result); - }); - } else { - this.reactiveIframeUrl.set(iframeUrl); - callback(error, result); - } - }); - } - - loginWithToken(tokenData, callback) { - if (!this.enabled) { - return; - } - - if (Match.test(tokenData, String)) { - tokenData = { - token: tokenData, - }; - } - - console.log('loginWithToken'); - - if (tokenData.loginToken) { - return Meteor.loginWithToken(tokenData.loginToken, callback); - } - - Accounts.callLoginMethod({ - methodArguments: [{ - iframe: true, - token: tokenData.token, - }], - userCallback: callback, - }); - } -} - RocketChat.iframeLogin = new IframeLogin(); const requestCredential = (serviceName, options = {}, callback) => { diff --git a/packages/rocketchat-iframe-login/package.js b/packages/rocketchat-iframe-login/package.js index 9814a9ad4b5b..9c4e35cc77d3 100644 --- a/packages/rocketchat-iframe-login/package.js +++ b/packages/rocketchat-iframe-login/package.js @@ -9,6 +9,7 @@ Package.onUse(function(api) { 'rocketchat:logger', 'kadira:flow-router', 'rocketchat:lib', + 'rocketchat:ui-utils', 'accounts-base', 'ecmascript', 'reactive-var', diff --git a/packages/rocketchat-lib/client/lib/Layout.js b/packages/rocketchat-lib/client/lib/Layout.js index 511f1d56adc9..3d84f446438b 100644 --- a/packages/rocketchat-lib/client/lib/Layout.js +++ b/packages/rocketchat-lib/client/lib/Layout.js @@ -1,14 +1,4 @@ -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Layout } from 'meteor/rocketchat:ui-utils'; -RocketChat.Layout = new (class RocketChatLayout { - constructor() { - Tracker.autorun(() => { - this.layout = FlowRouter.getQueryParam('layout'); - }); - } +RocketChat.Layout = Layout; - isEmbedded() { - return this.layout === 'embedded'; - } -}); diff --git a/packages/rocketchat-ui-utils/client/index.js b/packages/rocketchat-ui-utils/client/index.js index ba0212a9c146..aefac9a85055 100644 --- a/packages/rocketchat-ui-utils/client/index.js +++ b/packages/rocketchat-ui-utils/client/index.js @@ -13,6 +13,9 @@ import { RoomManager } from './lib/RoomManager'; import { upsertMessage, RoomHistoryManager } from './lib/RoomHistoryManager'; import { mainReady } from './lib/mainReady'; import { renderMessageBody } from './lib/renderMessageBody'; +import { Layout } from './lib/Layout'; +import { IframeLogin, iframeLogin } from './lib/IframeLogin'; +import { fireGlobalEvent } from './lib/fireGlobalEvent'; export { AdminBox, @@ -33,4 +36,8 @@ export { mainReady, renderMessageBody, upsertMessage, + Layout, + IframeLogin, + iframeLogin, + fireGlobalEvent, }; diff --git a/packages/rocketchat-ui-utils/client/lib/IframeLogin.js b/packages/rocketchat-ui-utils/client/lib/IframeLogin.js new file mode 100644 index 000000000000..ea04ac3669e5 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/IframeLogin.js @@ -0,0 +1,112 @@ +import { Meteor } from 'meteor/meteor'; +import { Match } from 'meteor/check'; +import { settings } from 'meteor/rocketchat:settings'; +import { Accounts } from 'meteor/accounts-base'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { HTTP } from 'meteor/http'; + +export class IframeLogin { + constructor() { + this.enabled = false; + this.reactiveIframeUrl = new ReactiveVar(); + this.reactiveEnabled = new ReactiveVar(); + this.iframeUrl = undefined; + this.apiUrl = undefined; + this.apiMethod = undefined; + + Tracker.autorun((c) => { + this.enabled = settings.get('Accounts_iframe_enabled'); + this.reactiveEnabled.set(this.enabled); + + this.iframeUrl = settings.get('Accounts_iframe_url'); + this.apiUrl = settings.get('Accounts_Iframe_api_url'); + this.apiMethod = settings.get('Accounts_Iframe_api_method'); + + if (this.enabled === false) { + return c.stop(); + } + + if (this.enabled === true && this.iframeUrl && this.apiUrl && this.apiMethod) { + c.stop(); + if (!Accounts._storedLoginToken()) { + this.tryLogin(() => {}); + } + } + }); + } + + tryLogin(callback) { + if (!this.enabled) { + return; + } + + if (!this.iframeUrl || !this.apiUrl || !this.apiMethod) { + return; + } + + console.log('tryLogin'); + const options = { + beforeSend: (xhr) => { + xhr.withCredentials = true; + }, + }; + + let { iframeUrl } = this; + let separator = '?'; + if (iframeUrl.indexOf('?') > -1) { + separator = '&'; + } + + if (window.cordova) { + iframeUrl += `${ separator }client=cordova`; + } else if (navigator.userAgent.indexOf('Electron') > -1) { + iframeUrl += `${ separator }client=electron`; + } + + HTTP.call(this.apiMethod, this.apiUrl, options, (error, result) => { + console.log(error, result); + if (result && result.data && (result.data.token || result.data.loginToken)) { + this.loginWithToken(result.data, (error, result) => { + if (error) { + this.reactiveIframeUrl.set(iframeUrl); + } else { + this.reactiveIframeUrl.set(); + } + callback(error, result); + }); + } else { + this.reactiveIframeUrl.set(iframeUrl); + callback(error, result); + } + }); + } + + loginWithToken(tokenData, callback) { + if (!this.enabled) { + return; + } + + if (Match.test(tokenData, String)) { + tokenData = { + token: tokenData, + }; + } + + console.log('loginWithToken'); + + if (tokenData.loginToken) { + return Meteor.loginWithToken(tokenData.loginToken, callback); + } + + Accounts.callLoginMethod({ + methodArguments: [{ + iframe: true, + token: tokenData.token, + }], + userCallback: callback, + }); + } +} + +export const iframeLogin = new IframeLogin(); diff --git a/packages/rocketchat-ui-utils/client/lib/Layout.js b/packages/rocketchat-ui-utils/client/lib/Layout.js new file mode 100644 index 000000000000..8e96e34ddf5a --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/Layout.js @@ -0,0 +1,14 @@ +import { Tracker } from 'meteor/tracker'; +import { FlowRouter } from 'meteor/kadira:flow-router'; + +export const Layout = new (class RocketChatLayout { + constructor() { + Tracker.autorun(() => { + this.layout = FlowRouter.getQueryParam('layout'); + }); + } + + isEmbedded() { + return this.layout === 'embedded'; + } +}); diff --git a/packages/rocketchat-ui-utils/client/lib/fireGlobalEvent.js b/packages/rocketchat-ui-utils/client/lib/fireGlobalEvent.js new file mode 100644 index 000000000000..5257d92e8508 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/fireGlobalEvent.js @@ -0,0 +1,20 @@ +import { settings } from 'meteor/rocketchat:settings'; +import { Tracker } from 'meteor/tracker'; + +export const fireGlobalEvent = function _fireGlobalEvent(eventName, params) { + window.dispatchEvent(new CustomEvent(eventName, { detail: params })); + + Tracker.autorun((computation) => { + const enabled = settings.get('Iframe_Integration_send_enable'); + if (enabled === undefined) { + return; + } + computation.stop(); + if (enabled) { + parent.postMessage({ + eventName, + data: params, + }, settings.get('Iframe_Integration_send_target_origin')); + } + }); +}; diff --git a/packages/rocketchat-ui/client/lib/fireEvent.js b/packages/rocketchat-ui/client/lib/fireEvent.js index c0d50a6b66e1..b68fac27a93f 100644 --- a/packages/rocketchat-ui/client/lib/fireEvent.js +++ b/packages/rocketchat-ui/client/lib/fireEvent.js @@ -1,20 +1,3 @@ -import { Tracker } from 'meteor/tracker'; - -fireGlobalEvent = function _fireGlobalEvent(eventName, params) { - window.dispatchEvent(new CustomEvent(eventName, { detail: params })); - - Tracker.autorun((computation) => { - const enabled = RocketChat.settings.get('Iframe_Integration_send_enable'); - if (enabled === undefined) { - return; - } - computation.stop(); - if (enabled) { - parent.postMessage({ - eventName, - data: params, - }, RocketChat.settings.get('Iframe_Integration_send_target_origin')); - } - }); -}; +import { fireGlobalEvent as _fireGlobalEvent } from 'meteor/rocketchat:ui-utils'; +fireGlobalEvent = _fireGlobalEvent; From 76ee0f011bc44adb7ded924f5c6c39860860532f Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:34:23 -0200 Subject: [PATCH 39/59] Remove directly dependency between tooltip and lib --- packages/rocketchat-lib/client/lib/tooltip.js | 3 +++ packages/rocketchat-lib/package.js | 2 ++ packages/rocketchat-tooltip/client/index.js | 5 +++-- packages/rocketchat-tooltip/client/rocketchat-tooltip.js | 3 +-- packages/rocketchat-tooltip/package.js | 3 --- packages/rocketchat-ui-utils/package.js | 1 + 6 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 packages/rocketchat-lib/client/lib/tooltip.js diff --git a/packages/rocketchat-lib/client/lib/tooltip.js b/packages/rocketchat-lib/client/lib/tooltip.js new file mode 100644 index 000000000000..15ed1c87ed50 --- /dev/null +++ b/packages/rocketchat-lib/client/lib/tooltip.js @@ -0,0 +1,3 @@ +import { tooltip } from 'meteor/rocketchat:tooltip'; + +RocketChat.tooltip = tooltip; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 46b74340be48..59ae61e52a97 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -32,6 +32,7 @@ Package.onUse(function(api) { api.use('rocketchat:notifications'); api.use('rocketchat:promises'); api.use('rocketchat:ui-utils'); + api.use('rocketchat:tooltip'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); @@ -245,6 +246,7 @@ Package.onUse(function(api) { api.addFiles('client/lib/Layout.js', 'client'); api.addFiles('client/lib/handleError.js', 'client'); api.addFiles('client/lib/authorization.js', 'client'); + api.addFiles('client/lib/tooltip.js', 'client'); // CLIENT LIB STARTUP api.addFiles('client/lib/startup/commands.js', 'client'); diff --git a/packages/rocketchat-tooltip/client/index.js b/packages/rocketchat-tooltip/client/index.js index e0a2d2f0e404..41cf931eaf81 100644 --- a/packages/rocketchat-tooltip/client/index.js +++ b/packages/rocketchat-tooltip/client/index.js @@ -1,3 +1,4 @@ import './rocketchat-tooltip.html'; -import './rocketchat-tooltip'; -import './init'; +import { tooltip } from './rocketchat-tooltip'; + +export { tooltip }; diff --git a/packages/rocketchat-tooltip/client/rocketchat-tooltip.js b/packages/rocketchat-tooltip/client/rocketchat-tooltip.js index eadb86af0fa6..ac902791736e 100644 --- a/packages/rocketchat-tooltip/client/rocketchat-tooltip.js +++ b/packages/rocketchat-tooltip/client/rocketchat-tooltip.js @@ -1,8 +1,7 @@ import { Template } from 'meteor/templating'; -import { RocketChat } from 'meteor/rocketchat:lib'; import { Blaze } from 'meteor/blaze'; -RocketChat.tooltip = { +export const tooltip = { source: null, initiated: false, opened: false, diff --git a/packages/rocketchat-tooltip/package.js b/packages/rocketchat-tooltip/package.js index 3e83c9549772..ee73ebebe3f5 100644 --- a/packages/rocketchat-tooltip/package.js +++ b/packages/rocketchat-tooltip/package.js @@ -10,9 +10,6 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', - 'rocketchat:lib', - 'rocketchat:theme', - 'rocketchat:ui-master', ]); api.addFiles('client/tooltip.css', 'client'); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-ui-utils/package.js b/packages/rocketchat-ui-utils/package.js index c98cabf93a0d..cf1daff6045c 100644 --- a/packages/rocketchat-ui-utils/package.js +++ b/packages/rocketchat-ui-utils/package.js @@ -9,6 +9,7 @@ Package.describe({ Package.onUse(function(api) { api.use([ 'ecmascript', + 'http', 'templating', 'kadira:flow-router', 'kadira:blaze-layout', From 6455fbcb58c8b9a627147029dbcadba337763b2c Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:35:11 -0200 Subject: [PATCH 40/59] Remove directly dependency between settings and metrics --- packages/rocketchat-metrics/package.js | 1 - .../rocketchat-metrics/server/lib/metrics.js | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/rocketchat-metrics/package.js b/packages/rocketchat-metrics/package.js index 21b91460874c..af04b090f290 100644 --- a/packages/rocketchat-metrics/package.js +++ b/packages/rocketchat-metrics/package.js @@ -8,7 +8,6 @@ Package.describe({ Package.onUse(function(api) { api.use([ 'ecmascript', - 'rocketchat:settings', 'rocketchat:migrations', 'rocketchat:version', ]); diff --git a/packages/rocketchat-metrics/server/lib/metrics.js b/packages/rocketchat-metrics/server/lib/metrics.js index 0e69cdffb0b2..052334bf3b09 100644 --- a/packages/rocketchat-metrics/server/lib/metrics.js +++ b/packages/rocketchat-metrics/server/lib/metrics.js @@ -1,5 +1,4 @@ import { Meteor } from 'meteor/meteor'; -import { settings } from 'meteor/rocketchat:settings'; import { Migrations } from 'meteor/rocketchat:migrations'; import client from 'prom-client'; import connect from 'connect'; @@ -77,12 +76,12 @@ metrics.totalPrivateGroupMessages = new client.Gauge({ name: 'rocketchat_private metrics.totalDirectMessages = new client.Gauge({ name: 'rocketchat_direct_messages_total', help: 'total of messages in direct rooms' }); metrics.totalLivechatMessages = new client.Gauge({ name: 'rocketchat_livechat_messages_total', help: 'total of messages in livechat rooms' }); -client.register.setDefaultLabels({ - uniqueId: settings.get('uniqueID'), - siteUrl: settings.get('Site_Url'), -}); - const setPrometheusData = async() => { + const { settings } = await import('meteor/rocketchat:settings'); + client.register.setDefaultLabels({ + uniqueId: settings.get('uniqueID'), + siteUrl: settings.get('Site_Url'), + }); const date = new Date(); if (!Info) { const Utils = await import('meteor/rocketchat:utils'); @@ -167,7 +166,8 @@ app.use('/', (req, res) => { const server = http.createServer(app); let timer; -const updatePrometheusConfig = () => { +const updatePrometheusConfig = async() => { + const { settings } = await import('meteor/rocketchat:settings'); const port = settings.get('Prometheus_Port'); const enabled = settings.get('Prometheus_Enabled'); if (port == null || enabled == null) { @@ -186,5 +186,8 @@ const updatePrometheusConfig = () => { } }; -settings.get('Prometheus_Enabled', updatePrometheusConfig); -settings.get('Prometheus_Port', updatePrometheusConfig); +Meteor.startup(async() => { + const { settings } = await import('meteor/rocketchat:settings'); + settings.get('Prometheus_Enabled', updatePrometheusConfig); + settings.get('Prometheus_Port', updatePrometheusConfig); +}); From 772e87c23adee83dfb7c1ca6a5c14f4f7a6b1b20 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:36:08 -0200 Subject: [PATCH 41/59] Move some settings client function from lib to settings --- .../rocketchat-lib/client/lib/settings.js | 47 ------------------- packages/rocketchat-settings/client/index.js | 2 +- .../client/lib/settings.js | 47 +++++++++++++++++++ packages/rocketchat-settings/package.js | 1 + 4 files changed, 49 insertions(+), 48 deletions(-) create mode 100644 packages/rocketchat-settings/client/lib/settings.js diff --git a/packages/rocketchat-lib/client/lib/settings.js b/packages/rocketchat-lib/client/lib/settings.js index cbf735772ad8..99b7e2a65ab9 100644 --- a/packages/rocketchat-lib/client/lib/settings.js +++ b/packages/rocketchat-lib/client/lib/settings.js @@ -1,54 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { ReactiveDict } from 'meteor/reactive-dict'; import { t } from 'meteor/rocketchat:utils'; -/* -* RocketChat.settings holds all packages settings -* @namespace RocketChat.settings -*/ - -RocketChat.settings.cachedCollection = new RocketChat.CachedCollection({ - name: 'public-settings', - eventType: 'onAll', - userRelated: false, - listenChangesForLoggedUsersOnly: true, -}); - -RocketChat.settings.collection = RocketChat.settings.cachedCollection.collection; - -RocketChat.settings.cachedCollection.init(); - -RocketChat.settings.dict = new ReactiveDict('settings'); - -RocketChat.settings.get = function(_id) { - return RocketChat.settings.dict.get(_id); -}; - -RocketChat.settings.init = function() { - let initialLoad = true; - RocketChat.settings.collection.find().observe({ - added(record) { - Meteor.settings[record._id] = record.value; - RocketChat.settings.dict.set(record._id, record.value); - RocketChat.settings.load(record._id, record.value, initialLoad); - }, - changed(record) { - Meteor.settings[record._id] = record.value; - RocketChat.settings.dict.set(record._id, record.value); - RocketChat.settings.load(record._id, record.value, initialLoad); - }, - removed(record) { - delete Meteor.settings[record._id]; - RocketChat.settings.dict.set(record._id, null); - RocketChat.settings.load(record._id, null, initialLoad); - }, - }); - initialLoad = false; -}; - -RocketChat.settings.init(); - Meteor.startup(function() { if (Meteor.isCordova === true) { return; diff --git a/packages/rocketchat-settings/client/index.js b/packages/rocketchat-settings/client/index.js index 8f93e01fb4f4..481b58fd8a8a 100644 --- a/packages/rocketchat-settings/client/index.js +++ b/packages/rocketchat-settings/client/index.js @@ -1,4 +1,4 @@ -import { settings } from '../lib/settings'; +import { settings } from './lib/settings'; export { settings, diff --git a/packages/rocketchat-settings/client/lib/settings.js b/packages/rocketchat-settings/client/lib/settings.js new file mode 100644 index 000000000000..7a216290f058 --- /dev/null +++ b/packages/rocketchat-settings/client/lib/settings.js @@ -0,0 +1,47 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveDict } from 'meteor/reactive-dict'; +import { CachedCollection } from 'meteor/rocketchat:ui-cached-collection'; +import { settings } from '../../lib/settings'; + +settings.cachedCollection = new CachedCollection({ + name: 'public-settings', + eventType: 'onAll', + userRelated: false, + listenChangesForLoggedUsersOnly: true, +}); + +settings.collection = settings.cachedCollection.collection; + +settings.cachedCollection.init(); + +settings.dict = new ReactiveDict('settings'); + +settings.get = function(_id) { + return settings.dict.get(_id); +}; + +settings.init = function() { + let initialLoad = true; + settings.collection.find().observe({ + added(record) { + Meteor.settings[record._id] = record.value; + settings.dict.set(record._id, record.value); + settings.load(record._id, record.value, initialLoad); + }, + changed(record) { + Meteor.settings[record._id] = record.value; + settings.dict.set(record._id, record.value); + settings.load(record._id, record.value, initialLoad); + }, + removed(record) { + delete Meteor.settings[record._id]; + settings.dict.set(record._id, null); + settings.load(record._id, null, initialLoad); + }, + }); + initialLoad = false; +}; + +settings.init(); + +export { settings }; diff --git a/packages/rocketchat-settings/package.js b/packages/rocketchat-settings/package.js index 9d63fec60647..227969519e91 100644 --- a/packages/rocketchat-settings/package.js +++ b/packages/rocketchat-settings/package.js @@ -8,6 +8,7 @@ Package.describe({ Package.onUse(function(api) { api.use([ 'ecmascript', + 'rocketchat:ui-cached-collection', ]); api.mainModule('client/index.js', 'client'); api.mainModule('server/index.js', 'server'); From 0f16f538c9f9d2455962715aa23ddb9b5c34deeb Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 9 Jan 2019 16:36:51 -0200 Subject: [PATCH 42/59] Convert rocketchat-ui-master to main module structure --- packages/rocketchat-tooltip/client/init.js | 6 -- packages/rocketchat-ui-master/client/index.js | 5 ++ packages/rocketchat-ui-master/client/main.js | 62 +++++++++++-------- packages/rocketchat-ui-master/package.js | 12 +--- packages/rocketchat-ui-master/server/index.js | 1 + .../rocketchat-ui-master/server/inject.js | 26 ++++---- 6 files changed, 58 insertions(+), 54 deletions(-) delete mode 100644 packages/rocketchat-tooltip/client/init.js create mode 100644 packages/rocketchat-ui-master/client/index.js create mode 100644 packages/rocketchat-ui-master/server/index.js diff --git a/packages/rocketchat-tooltip/client/init.js b/packages/rocketchat-tooltip/client/init.js deleted file mode 100644 index 77de42dc4079..000000000000 --- a/packages/rocketchat-tooltip/client/init.js +++ /dev/null @@ -1,6 +0,0 @@ -import { Template } from 'meteor/templating'; -import { RocketChat } from 'meteor/rocketchat:lib'; - -Template.main.onCreated(function() { - RocketChat.tooltip.init(); -}); diff --git a/packages/rocketchat-ui-master/client/index.js b/packages/rocketchat-ui-master/client/index.js new file mode 100644 index 000000000000..fd3af97d4fe6 --- /dev/null +++ b/packages/rocketchat-ui-master/client/index.js @@ -0,0 +1,5 @@ +import './main.html'; +import './loading.html'; +import './error.html'; +import './logoLayout.html'; +import './main'; diff --git a/packages/rocketchat-ui-master/client/main.js b/packages/rocketchat-ui-master/client/main.js index b486d859acb6..2cb707e57fc4 100644 --- a/packages/rocketchat-ui-master/client/main.js +++ b/packages/rocketchat-ui-master/client/main.js @@ -2,15 +2,20 @@ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import { t } from 'meteor/rocketchat:utils'; +import { t, getUserPreference } from 'meteor/rocketchat:utils'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { mainReady } from 'meteor/rocketchat:ui-utils'; +import { mainReady, Layout, iframeLogin, modal, popover, menu, fireGlobalEvent } from 'meteor/rocketchat:ui-utils'; import { toolbarSearch } from 'meteor/rocketchat:ui-sidenav'; +import { settings } from 'meteor/rocketchat:settings'; +import { CachedChatSubscription, Roles, ChatSubscription } from 'meteor/rocketchat:models'; +import { CachedCollectionManager } from 'meteor/rocketchat:ui-cached-collection'; +import { hasRole } from 'meteor/rocketchat:authorization'; +import { tooltip } from 'meteor/rocketchat:tooltip'; import Clipboard from 'clipboard'; import s from 'underscore.string'; -RocketChat.settings.collection.find({ _id:/theme-color-rc/i }, { fields:{ value: 1 } }).observe({ changed: () => { DynamicCss.run(true); } }); +settings.collection.find({ _id:/theme-color-rc/i }, { fields:{ value: 1 } }).observe({ changed: () => { DynamicCss.run(true); } }); Template.body.onRendered(function() { new Clipboard('.clipboard'); @@ -86,7 +91,7 @@ Template.body.onRendered(function() { if (link.origin === s.rtrim(Meteor.absoluteUrl(), '/') && /msg=([a-zA-Z0-9]+)/.test(link.search)) { e.preventDefault(); e.stopPropagation(); - if (RocketChat.Layout.isEmbedded()) { + if (Layout.isEmbedded()) { return fireGlobalEvent('click-message-link', { link: link.pathname + link.search, }); @@ -101,7 +106,7 @@ Template.body.onRendered(function() { const d = document; const script = 'script'; const l = 'dataLayer'; - const i = RocketChat.settings.get('GoogleTagManager_id'); + const i = settings.get('GoogleTagManager_id'); if (Match.test(i, String) && i.trim() !== '') { c.stop(); return (function(w, d, s, l, i) { @@ -124,17 +129,20 @@ Template.body.onRendered(function() { } }); -RocketChat.mainReady = mainReady; +Template.main.onCreated(function() { + tooltip.init(); +}); + Template.main.helpers({ removeSidenav() { const { modal } = this; return (modal || typeof modal === 'function' ? modal() : modal); // || RocketChat.Layout.isEmbedded(); }, siteName() { - return RocketChat.settings.get('Site_Name'); + return settings.get('Site_Name'); }, logged() { - if (Meteor.userId() != null || (RocketChat.settings.get('Accounts_AllowAnonymousRead') === true && Session.get('forceLogin') !== true)) { + if (Meteor.userId() != null || (settings.get('Accounts_AllowAnonymousRead') === true && Session.get('forceLogin') !== true)) { $('html').addClass('noscroll').removeClass('scroll'); return true; } else { @@ -143,26 +151,26 @@ Template.main.helpers({ } }, useIframe() { - const iframeEnabled = typeof RocketChat.iframeLogin !== 'undefined'; - return iframeEnabled && RocketChat.iframeLogin.reactiveEnabled.get(); + const iframeEnabled = typeof iframeLogin !== 'undefined'; + return iframeEnabled && iframeLogin.reactiveEnabled.get(); }, iframeUrl() { - const iframeEnabled = typeof RocketChat.iframeLogin !== 'undefined'; - return iframeEnabled && RocketChat.iframeLogin.reactiveIframeUrl.get(); + const iframeEnabled = typeof iframeLogin !== 'undefined'; + return iframeEnabled && iframeLogin.reactiveIframeUrl.get(); }, subsReady() { const routerReady = FlowRouter.subsReady('userData', 'activeUsers'); const subscriptionsReady = CachedChatSubscription.ready.get(); - const settingsReady = RocketChat.settings.cachedCollection.ready.get(); + const settingsReady = settings.cachedCollection.ready.get(); const ready = (Meteor.userId() == null) || (routerReady && subscriptionsReady && settingsReady); - RocketChat.CachedCollectionManager.syncEnabled = ready; + CachedCollectionManager.syncEnabled = ready; Meteor.defer(() => { - RocketChat.mainReady.set(ready); + mainReady.set(ready); }); return ready; }, hasUsername() { - return (Meteor.userId() != null && Meteor.user().username != null) || (Meteor.userId() == null && RocketChat.settings.get('Accounts_AllowAnonymousRead') === true); + return (Meteor.userId() != null && Meteor.user().username != null) || (Meteor.userId() == null && settings.get('Accounts_AllowAnonymousRead') === true); }, requirePasswordChange() { const user = Meteor.user(); @@ -176,31 +184,31 @@ Template.main.helpers({ return false; } - const mandatoryRole = RocketChat.models.Roles.findOne({ _id: { $in: user.roles }, mandatory2fa: true }); + const mandatoryRole = Roles.findOne({ _id: { $in: user.roles }, mandatory2fa: true }); return mandatoryRole !== undefined; }, CustomScriptLoggedOut() { - const script = RocketChat.settings.get('Custom_Script_Logged_Out') || ''; + const script = settings.get('Custom_Script_Logged_Out') || ''; if (script.trim()) { eval(script);//eslint-disable-line } }, CustomScriptLoggedIn() { - const script = RocketChat.settings.get('Custom_Script_Logged_In') || ''; + const script = settings.get('Custom_Script_Logged_In') || ''; if (script.trim()) { eval(script);//eslint-disable-line } }, embeddedVersion() { - if (RocketChat.Layout.isEmbedded()) { + if (Layout.isEmbedded()) { return 'embedded-view'; } }, showSetupWizard() { const userId = Meteor.userId(); - const Show_Setup_Wizard = RocketChat.settings.get('Show_Setup_Wizard'); + const Show_Setup_Wizard = settings.get('Show_Setup_Wizard'); - return (!userId && Show_Setup_Wizard === 'pending') || (userId && RocketChat.authz.hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress'); + return (!userId && Show_Setup_Wizard === 'pending') || (userId && hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress'); }, }); @@ -221,21 +229,21 @@ Template.main.onRendered(function() { }); return Tracker.autorun(function() { const userId = Meteor.userId(); - const Show_Setup_Wizard = RocketChat.settings.get('Show_Setup_Wizard'); + const Show_Setup_Wizard = settings.get('Show_Setup_Wizard'); - if ((!userId && Show_Setup_Wizard === 'pending') || (userId && RocketChat.authz.hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress')) { + if ((!userId && Show_Setup_Wizard === 'pending') || (userId && hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress')) { FlowRouter.go('setup-wizard'); } - if (RocketChat.getUserPreference(userId, 'hideUsernames')) { + if (getUserPreference(userId, 'hideUsernames')) { $(document.body).on('mouseleave', 'button.thumb', function() { - return RocketChat.tooltip.hide(); + return tooltip.hide(); }); return $(document.body).on('mouseenter', 'button.thumb', function(e) { const avatarElem = $(e.currentTarget); const username = avatarElem.attr('data-username'); if (username) { e.stopPropagation(); - return RocketChat.tooltip.showElement($('').text(username), avatarElem); + return tooltip.showElement($('').text(username), avatarElem); } }); } else { diff --git a/packages/rocketchat-ui-master/package.js b/packages/rocketchat-ui-master/package.js index 7f206de98658..4ac4f0471624 100644 --- a/packages/rocketchat-ui-master/package.js +++ b/packages/rocketchat-ui-master/package.js @@ -16,20 +16,14 @@ Package.onUse(function(api) { 'ecmascript', 'templating', 'reactive-var', - 'rocketchat:lib', 'rocketchat:utils', + 'rocketchat:tooltip', 'rocketchat:ui-utils', 'rocketchat:ui-sidenav', 'meteorhacks:inject-initial', ]); - - api.addFiles('client/main.html', 'client'); - api.addFiles('client/loading.html', 'client'); - api.addFiles('client/error.html', 'client'); - api.addFiles('client/logoLayout.html', 'client'); - api.addFiles('client/main.js', 'client'); - - api.addFiles('server/inject.js', 'server'); + 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/packages/rocketchat-ui-master/server/index.js b/packages/rocketchat-ui-master/server/index.js new file mode 100644 index 000000000000..38a8fbba742c --- /dev/null +++ b/packages/rocketchat-ui-master/server/index.js @@ -0,0 +1 @@ +import './inject'; diff --git a/packages/rocketchat-ui-master/server/inject.js b/packages/rocketchat-ui-master/server/inject.js index 1ef0717440fd..973e960b86e9 100644 --- a/packages/rocketchat-ui-master/server/inject.js +++ b/packages/rocketchat-ui-master/server/inject.js @@ -1,11 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Inject } from 'meteor/meteorhacks:inject-initial'; +import { Settings } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; import _ from 'underscore'; import s from 'underscore.string'; const renderDynamicCssList = _.debounce(Meteor.bindEnvironment(() => { // const variables = RocketChat.models.Settings.findOne({_id:'theme-custom-variables'}, {fields: { value: 1}}); - const colors = RocketChat.models.Settings.find({ _id:/theme-color-rc/i }, { fields: { value: 1, editor: 1 } }).fetch().filter((color) => color && color.value); + const colors = Settings.find({ _id:/theme-color-rc/i }, { fields: { value: 1, editor: 1 } }).fetch().filter((color) => color && color.value); if (!colors) { return; @@ -25,7 +27,7 @@ renderDynamicCssList(); // changed: renderDynamicCssList // }); -RocketChat.models.Settings.find({ _id:/theme-color-rc/i }, { fields: { value: 1 } }).observe({ +Settings.find({ _id:/theme-color-rc/i }, { fields: { value: 1 } }).observe({ changed: renderDynamicCssList, }); @@ -57,7 +59,7 @@ if (process.env.DISABLE_ANIMATION || process.env.TEST_MODE === 'true') { `); } -RocketChat.settings.get('Assets_SvgFavicon_Enable', (key, value) => { +settings.get('Assets_SvgFavicon_Enable', (key, value) => { const standardFavicons = ` `; @@ -71,13 +73,13 @@ RocketChat.settings.get('Assets_SvgFavicon_Enable', (key, value) => { } }); -RocketChat.settings.get('theme-color-sidebar-background', (key, value) => { +settings.get('theme-color-sidebar-background', (key, value) => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, `` + ``); }); -RocketChat.settings.get('Accounts_ForgetUserSessionOnWindowClose', (key, value) => { +settings.get('Accounts_ForgetUserSessionOnWindowClose', (key, value) => { if (value) { Inject.rawModHtml(key, (html) => { const script = ` @@ -94,7 +96,7 @@ RocketChat.settings.get('Accounts_ForgetUserSessionOnWindowClose', (key, value) } }); -RocketChat.settings.get('Site_Name', (key, value = 'Rocket.Chat') => { +settings.get('Site_Name', (key, value = 'Rocket.Chat') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, `${ escapedValue }` + @@ -102,34 +104,34 @@ RocketChat.settings.get('Site_Name', (key, value = 'Rocket.Chat') => { ``); }); -RocketChat.settings.get('Meta_language', (key, value = '') => { +settings.get('Meta_language', (key, value = '') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, `` + ``); }); -RocketChat.settings.get('Meta_robots', (key, value = '') => { +settings.get('Meta_robots', (key, value = '') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, ``); }); -RocketChat.settings.get('Meta_msvalidate01', (key, value = '') => { +settings.get('Meta_msvalidate01', (key, value = '') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, ``); }); -RocketChat.settings.get('Meta_google-site-verification', (key, value = '') => { +settings.get('Meta_google-site-verification', (key, value = '') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, ``); }); -RocketChat.settings.get('Meta_fb_app_id', (key, value = '') => { +settings.get('Meta_fb_app_id', (key, value = '') => { const escapedValue = s.escapeHTML(value); Inject.rawHead(key, ``); }); -RocketChat.settings.get('Meta_custom', (key, value = '') => { +settings.get('Meta_custom', (key, value = '') => { Inject.rawHead(key, value); }); From c652612632122232c0b40494159386605ff01477 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 12:49:14 -0200 Subject: [PATCH 43/59] Remove directly dependency between rocketchat:e2e and rocketchat:lib --- .../client/accountEncryption.js | 4 +- .../rocketchat-e2e/client/rocketchat.e2e.js | 37 +++++++++------- .../client/rocketchat.e2e.room.js | 13 +++--- packages/rocketchat-e2e/package.js | 8 +++- packages/rocketchat-e2e/server/index.js | 10 ++--- .../server/methods/fetchMyKeys.js | 4 +- .../methods/getUsersOfRoomWithoutKey.js | 6 +-- .../server/methods/requestSubscriptionKeys.js | 9 ++-- .../server/methods/resetUserE2EKey.js | 11 ++--- .../server/methods/setRoomKeyID.js | 4 +- .../methods/setUserPublicAndPivateKeys.js | 4 +- .../server/methods/updateGroupKey.js | 8 ++-- .../rocketchat-e2e/server/models/Rooms.js | 15 ------- .../server/models/Subscriptions.js | 40 ----------------- .../rocketchat-e2e/server/models/Users.js | 44 ------------------- packages/rocketchat-e2e/server/settings.js | 4 +- .../rocketchat-models/server/models/Rooms.js | 14 ++++++ .../server/models/Subscriptions.js | 39 ++++++++++++++++ .../rocketchat-models/server/models/Users.js | 43 ++++++++++++++++++ 19 files changed, 162 insertions(+), 155 deletions(-) delete mode 100644 packages/rocketchat-e2e/server/models/Rooms.js delete mode 100644 packages/rocketchat-e2e/server/models/Subscriptions.js delete mode 100644 packages/rocketchat-e2e/server/models/Users.js diff --git a/packages/rocketchat-e2e/client/accountEncryption.js b/packages/rocketchat-e2e/client/accountEncryption.js index 77a927bba701..961f197888f6 100644 --- a/packages/rocketchat-e2e/client/accountEncryption.js +++ b/packages/rocketchat-e2e/client/accountEncryption.js @@ -2,13 +2,13 @@ import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import toastr from 'toastr'; import s from 'underscore.string'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; import { t } from 'meteor/rocketchat:utils'; import { e2e } from 'meteor/rocketchat:e2e'; Template.accountEncryption.helpers({ isEnabled() { - return RocketChat.settings.get('E2E_Enable'); + return settings.get('E2E_Enable'); }, allowKeyChange() { return localStorage.getItem('public_key') && localStorage.getItem('private_key'); diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index 2ef8992bd0ab..d19a050f2afe 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -3,9 +3,12 @@ import { Random } from 'meteor/random'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; import { EJSON } from 'meteor/ejson'; - import { FlowRouter } from 'meteor/kadira:flow-router'; -import { RocketChat, call } from 'meteor/rocketchat:lib'; +import { Rooms, Subscriptions, Messages } from 'meteor/rocketchat:models'; +import { promises } from 'meteor/rocketchat:promises'; +import { settings } from 'meteor/rocketchat:settings'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { Layout, call } from 'meteor/rocketchat:ui-utils'; import { TAPi18n } from 'meteor/tap:i18n'; import { E2ERoom } from './rocketchat.e2e.room'; import { @@ -59,7 +62,7 @@ class E2E { return; } - const room = RocketChat.models.Rooms.findOne({ + const room = Rooms.findOne({ _id: roomId, }); @@ -72,7 +75,7 @@ class E2E { } if (!this.instancesByRoomId[roomId]) { - const subscription = RocketChat.models.Subscriptions.findOne({ + const subscription = Subscriptions.findOne({ rid: roomId, }); @@ -206,7 +209,7 @@ class E2E { } setupListeners() { - RocketChat.Notifications.onUser('e2ekeyRequest', async(roomId, keyId) => { + Notifications.onUser('e2ekeyRequest', async(roomId, keyId) => { const e2eRoom = await this.getInstanceByRoomId(roomId); if (!e2eRoom) { return; @@ -215,19 +218,19 @@ class E2E { e2eRoom.provideKeyToUser(keyId); }); - RocketChat.models.Subscriptions.after.update((userId, doc) => { + Subscriptions.after.update((userId, doc) => { this.decryptSubscription(doc); }); - RocketChat.models.Subscriptions.after.insert((userId, doc) => { + Subscriptions.after.insert((userId, doc) => { this.decryptSubscription(doc); }); - RocketChat.models.Messages.after.update((userId, doc) => { + Messages.after.update((userId, doc) => { this.decryptMessage(doc); }); - RocketChat.models.Messages.after.insert((userId, doc) => { + Messages.after.insert((userId, doc) => { this.decryptMessage(doc); }); } @@ -421,7 +424,7 @@ class E2E { return; } - RocketChat.models.Messages.direct.update({ _id: message._id }, { + Messages.direct.update({ _id: message._id }, { $set: { msg: data.text, e2e: 'done', @@ -434,7 +437,7 @@ class E2E { return; } - return await RocketChat.models.Messages.find({ t: 'e2e', e2e: 'pending' }).forEach(async(item) => { + return await Messages.find({ t: 'e2e', e2e: 'pending' }).forEach(async(item) => { await this.decryptMessage(item); }); } @@ -459,7 +462,7 @@ class E2E { return; } - RocketChat.models.Subscriptions.direct.update({ + Subscriptions.direct.update({ _id: subscription._id, }, { $set: { @@ -470,7 +473,7 @@ class E2E { } async decryptPendingSubscriptions() { - RocketChat.models.Subscriptions.find({ + Subscriptions.find({ 'lastMessage.t': 'e2e', 'lastMessage.e2e': { $ne: 'done', @@ -494,9 +497,9 @@ export const e2e = new E2E(); Meteor.startup(function() { Tracker.autorun(function() { if (Meteor.userId()) { - const adminEmbedded = RocketChat.Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); + const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); - if (!adminEmbedded && RocketChat.settings.get('E2E_Enable') && window.crypto) { + if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { e2e.startClient(); e2e.enabled.set(true); } else { @@ -506,7 +509,7 @@ Meteor.startup(function() { }); // Encrypt messages before sending - RocketChat.promises.add('onClientBeforeSendMessage', async function(message) { + promises.add('onClientBeforeSendMessage', async function(message) { if (!message.rid) { return Promise.resolve(message); } @@ -525,5 +528,5 @@ Meteor.startup(function() { message.e2e = 'pending'; return message; }); - }, RocketChat.promises.priority.HIGH); + }, promises.priority.HIGH); }); diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.room.js b/packages/rocketchat-e2e/client/rocketchat.e2e.room.js index ad0ac0a2aa1e..25402d777d29 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.room.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.room.js @@ -5,8 +5,9 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { EJSON } from 'meteor/ejson'; import { Random } from 'meteor/random'; import { TimeSync } from 'meteor/mizzao:timesync'; - -import { RocketChat, call } from 'meteor/rocketchat:lib'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { Rooms, Subscriptions } from 'meteor/rocketchat:models'; +import { call } from 'meteor/rocketchat:ui-utils'; import { e2e } from './rocketchat.e2e'; import { Deferred, @@ -38,7 +39,7 @@ export class E2ERoom { this._ready.set(true); this.establishing.set(false); - RocketChat.Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => { + Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => { this.provideKeyToUser(keyId); }); }); @@ -70,7 +71,7 @@ export class E2ERoom { // Fetch encrypted session key from subscription model let groupKey; try { - groupKey = RocketChat.models.Subscriptions.findOne({ rid: this.roomId }).E2EKey; + groupKey = Subscriptions.findOne({ rid: this.roomId }).E2EKey; } catch (error) { return console.error('E2E -> Error fetching group key: ', error); } @@ -81,7 +82,7 @@ export class E2ERoom { return true; } - const room = RocketChat.models.Rooms.findOne({ _id: this.roomId }); + const room = Rooms.findOne({ _id: this.roomId }); if (!room.e2eKeyId) { await this.createGroupKey(); @@ -92,7 +93,7 @@ export class E2ERoom { console.log('E2E -> Requesting room key'); // TODO: request group key - RocketChat.Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId); + Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId); } isSupportedRoomType(type) { diff --git a/packages/rocketchat-e2e/package.js b/packages/rocketchat-e2e/package.js index bb189c659e59..d7d9cf61e5f0 100644 --- a/packages/rocketchat-e2e/package.js +++ b/packages/rocketchat-e2e/package.js @@ -10,8 +10,14 @@ Package.onUse(function(api) { 'ecmascript', 'less', 'mizzao:timesync', - 'rocketchat:lib', 'rocketchat:utils', + 'rocketchat:models', + 'rocketchat:notifications', + 'rocketchat:authorization', + 'rocketchat:callbacks', + 'rocketchat:settings', + 'rocketchat:promises', + 'rocketchat:ui-utils', 'templating', 'sha', ]); diff --git a/packages/rocketchat-e2e/server/index.js b/packages/rocketchat-e2e/server/index.js index c7167c3f6fd3..9672bd606733 100644 --- a/packages/rocketchat-e2e/server/index.js +++ b/packages/rocketchat-e2e/server/index.js @@ -1,9 +1,7 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { Notifications } from 'meteor/rocketchat:notifications'; import './settings'; -import './models/Users'; -import './models/Rooms'; -import './models/Subscriptions'; import './methods/setUserPublicAndPivateKeys'; import './methods/getUsersOfRoomWithoutKey'; import './methods/updateGroupKey'; @@ -12,6 +10,6 @@ import './methods/fetchMyKeys'; import './methods/resetUserE2EKey'; import './methods/requestSubscriptionKeys'; -RocketChat.callbacks.add('afterJoinRoom', (user, room) => { - RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); +callbacks.add('afterJoinRoom', (user, room) => { + Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); }); diff --git a/packages/rocketchat-e2e/server/methods/fetchMyKeys.js b/packages/rocketchat-e2e/server/methods/fetchMyKeys.js index b0c584ceddb6..1caa6fc33630 100644 --- a/packages/rocketchat-e2e/server/methods/fetchMyKeys.js +++ b/packages/rocketchat-e2e/server/methods/fetchMyKeys.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; Meteor.methods({ 'e2e.fetchMyKeys'() { @@ -7,6 +7,6 @@ Meteor.methods({ if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.fetchMyKeys' }); } - return RocketChat.models.Users.fetchKeysByUserId(userId); + return Users.fetchKeysByUserId(userId); }, }); diff --git a/packages/rocketchat-e2e/server/methods/getUsersOfRoomWithoutKey.js b/packages/rocketchat-e2e/server/methods/getUsersOfRoomWithoutKey.js index 26cc598f975b..5700578a3bb8 100644 --- a/packages/rocketchat-e2e/server/methods/getUsersOfRoomWithoutKey.js +++ b/packages/rocketchat-e2e/server/methods/getUsersOfRoomWithoutKey.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Subscriptions, Users } from 'meteor/rocketchat:models'; Meteor.methods({ 'e2e.getUsersOfRoomWithoutKey'(rid) { @@ -13,11 +13,11 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' }); } - const subscriptions = RocketChat.models.Subscriptions.findByRidWithoutE2EKey(rid, { fields: { 'u._id': 1 } }).fetch(); + const subscriptions = Subscriptions.findByRidWithoutE2EKey(rid, { fields: { 'u._id': 1 } }).fetch(); const userIds = subscriptions.map((s) => s.u._id); const options = { fields: { 'e2e.public_key': 1 } }; - const users = RocketChat.models.Users.findByIdsWithPublicE2EKey(userIds, options).fetch(); + const users = Users.findByIdsWithPublicE2EKey(userIds, options).fetch(); return { users, diff --git a/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js b/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js index 50d15ce4814d..78fa00f97c7f 100644 --- a/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js +++ b/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Subscriptions, Rooms } from 'meteor/rocketchat:models'; +import { Notifications } from 'meteor/rocketchat:notifications'; Meteor.methods({ 'e2e.requestSubscriptionKeys'() { @@ -10,7 +11,7 @@ Meteor.methods({ } // Get all encrypted rooms that the user is subscribed to and has no E2E key yet - const subscriptions = RocketChat.models.Subscriptions.findByUserIdWithoutE2E(Meteor.userId()); + const subscriptions = Subscriptions.findByUserIdWithoutE2E(Meteor.userId()); const roomIds = subscriptions.map((subscription) => subscription.rid); // For all subscriptions without E2E key, get the rooms that have encryption enabled @@ -23,9 +24,9 @@ Meteor.methods({ }, }; - const rooms = RocketChat.models.Rooms.find(query); + const rooms = Rooms.find(query); rooms.forEach((room) => { - RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); + Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); }); return true; diff --git a/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js index 53599f16d284..07a853c9f86a 100644 --- a/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js +++ b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users, Subscrtipions } from 'meteor/rocketchat:models'; +import { hasPermission } from 'meteor/rocketchat:authorization'; Meteor.methods({ 'e2e.resetUserE2EKey'(userId) { @@ -9,17 +10,17 @@ Meteor.methods({ }); } - if (RocketChat.authz.hasPermission(Meteor.userId(), 'reset-other-user-e2e-key') !== true) { + if (hasPermission(Meteor.userId(), 'reset-other-user-e2e-key') !== true) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'resetUserE2EKey', }); } - RocketChat.models.Users.resetE2EKey(userId); - RocketChat.models.Subscriptions.resetUserE2EKey(userId); + Users.resetE2EKey(userId); + Subscriptions.resetUserE2EKey(userId); // Force the user to logout, so that the keys can be generated again - RocketChat.models.Users.removeResumeService(userId); + Users.removeResumeService(userId); return true; }, }); diff --git a/packages/rocketchat-e2e/server/methods/setRoomKeyID.js b/packages/rocketchat-e2e/server/methods/setRoomKeyID.js index d31aaf492e35..1634d16cf30e 100644 --- a/packages/rocketchat-e2e/server/methods/setRoomKeyID.js +++ b/packages/rocketchat-e2e/server/methods/setRoomKeyID.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Rooms } from 'meteor/rocketchat:models'; Meteor.methods({ 'e2e.setRoomKeyID'(rid, keyID) { @@ -17,6 +17,6 @@ Meteor.methods({ throw new Meteor.Error('error-room-e2e-key-already-exists', 'E2E Key ID already exists', { method: 'e2e.setRoomKeyID' }); } - return RocketChat.models.Rooms.setE2eKeyId(room._id, keyID); + return Rooms.setE2eKeyId(room._id, keyID); }, }); diff --git a/packages/rocketchat-e2e/server/methods/setUserPublicAndPivateKeys.js b/packages/rocketchat-e2e/server/methods/setUserPublicAndPivateKeys.js index 7e2bd1612121..71c993ef13fd 100644 --- a/packages/rocketchat-e2e/server/methods/setUserPublicAndPivateKeys.js +++ b/packages/rocketchat-e2e/server/methods/setUserPublicAndPivateKeys.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; Meteor.methods({ 'e2e.setUserPublicAndPivateKeys'({ public_key, private_key }) { @@ -9,6 +9,6 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.setUserPublicAndPivateKeys' }); } - return RocketChat.models.Users.setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key }); + return Users.setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key }); }, }); diff --git a/packages/rocketchat-e2e/server/methods/updateGroupKey.js b/packages/rocketchat-e2e/server/methods/updateGroupKey.js index 95acf162e2dc..2900a3194c7c 100644 --- a/packages/rocketchat-e2e/server/methods/updateGroupKey.js +++ b/packages/rocketchat-e2e/server/methods/updateGroupKey.js @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Subscriptions } from 'meteor/rocketchat:models'; Meteor.methods({ 'e2e.updateGroupKey'(rid, uid, key) { - const mySub = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId()); + const mySub = Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId()); if (mySub) { // I have a subscription to this room - const userSub = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, uid); + const userSub = Subscriptions.findOneByRoomIdAndUserId(rid, uid); if (userSub) { // uid also has subscription to this room - return RocketChat.models.Subscriptions.updateGroupE2EKey(userSub._id, key); + return Subscriptions.updateGroupE2EKey(userSub._id, key); } } }, diff --git a/packages/rocketchat-e2e/server/models/Rooms.js b/packages/rocketchat-e2e/server/models/Rooms.js deleted file mode 100644 index 549be9048000..000000000000 --- a/packages/rocketchat-e2e/server/models/Rooms.js +++ /dev/null @@ -1,15 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.models.Rooms.setE2eKeyId = function(_id, e2eKeyId, options) { - const query = { - _id, - }; - - const update = { - $set: { - e2eKeyId, - }, - }; - - return this.update(query, update, options); -}; diff --git a/packages/rocketchat-e2e/server/models/Subscriptions.js b/packages/rocketchat-e2e/server/models/Subscriptions.js deleted file mode 100644 index 2ab46b438e54..000000000000 --- a/packages/rocketchat-e2e/server/models/Subscriptions.js +++ /dev/null @@ -1,40 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.models.Subscriptions.updateGroupE2EKey = function(_id, key) { - const query = { _id }; - const update = { $set: { E2EKey: key } }; - this.update(query, update); - return this.findOne({ _id }); -}; - -RocketChat.models.Subscriptions.findByRidWithoutE2EKey = function(rid, options) { - const query = { - rid, - E2EKey: { - $exists: false, - }, - }; - - return this.find(query, options); -}; - -RocketChat.models.Subscriptions.resetUserE2EKey = function(userId) { - this.update({ 'u._id': userId }, { - $unset: { - E2EKey: '', - }, - }, { - multi: true, - }); -}; - -RocketChat.models.Subscriptions.findByUserIdWithoutE2E = function(userId, options) { - const query = { - 'u._id': userId, - E2EKey: { - $exists: false, - }, - }; - - return this.find(query, options); -}; diff --git a/packages/rocketchat-e2e/server/models/Users.js b/packages/rocketchat-e2e/server/models/Users.js deleted file mode 100644 index 3a099808b0d9..000000000000 --- a/packages/rocketchat-e2e/server/models/Users.js +++ /dev/null @@ -1,44 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.models.Users.setE2EPublicAndPivateKeysByUserId = function(userId, { public_key, private_key }) { - this.update({ _id: userId }, { - $set: { - 'e2e.public_key': public_key, - 'e2e.private_key': private_key, - }, - }); -}; - -RocketChat.models.Users.fetchKeysByUserId = function(userId) { - const user = this.findOne({ _id: userId }, { fields: { e2e: 1 } }); - - if (!user || !user.e2e || !user.e2e.public_key) { - return {}; - } - - return { - public_key: user.e2e.public_key, - private_key: user.e2e.private_key, - }; -}; - -RocketChat.models.Users.findByIdsWithPublicE2EKey = function(ids, options) { - const query = { - _id: { - $in: ids, - }, - 'e2e.public_key': { - $exists: 1, - }, - }; - - return this.find(query, options); -}; - -RocketChat.models.Users.resetE2EKey = function(userId) { - this.update({ _id: userId }, { - $unset: { - e2e: '', - }, - }); -}; diff --git a/packages/rocketchat-e2e/server/settings.js b/packages/rocketchat-e2e/server/settings.js index 46e18bd69fe9..dc89fbe90c84 100644 --- a/packages/rocketchat-e2e/server/settings.js +++ b/packages/rocketchat-e2e/server/settings.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; -RocketChat.settings.addGroup('E2E Encryption', function() { +settings.addGroup('E2E Encryption', function() { this.add('E2E_Enable', false, { type: 'boolean', i18nLabel: 'Enabled', diff --git a/packages/rocketchat-models/server/models/Rooms.js b/packages/rocketchat-models/server/models/Rooms.js index 3b0a31c908a5..c7951569c87a 100644 --- a/packages/rocketchat-models/server/models/Rooms.js +++ b/packages/rocketchat-models/server/models/Rooms.js @@ -26,6 +26,20 @@ export class Rooms extends Base { return this.findOne(query, options); } + setE2eKeyId(_id, e2eKeyId, options) { + const query = { + _id, + }; + + const update = { + $set: { + e2eKeyId, + }, + }; + + return this.update(query, update, options); + } + findOneByImportId(_id, options) { const query = { importIds: _id }; diff --git a/packages/rocketchat-models/server/models/Subscriptions.js b/packages/rocketchat-models/server/models/Subscriptions.js index 4dc17cf489f8..8e6da0dbd4d2 100644 --- a/packages/rocketchat-models/server/models/Subscriptions.js +++ b/packages/rocketchat-models/server/models/Subscriptions.js @@ -41,6 +41,45 @@ export class Subscriptions extends Base { return query; } + findByRidWithoutE2EKey(rid, options) { + const query = { + rid, + E2EKey: { + $exists: false, + }, + }; + + return this.find(query, options); + } + + resetUserE2EKey(userId) { + this.update({ 'u._id': userId }, { + $unset: { + E2EKey: '', + }, + }, { + multi: true, + }); + } + + findByUserIdWithoutE2E(userId, options) { + const query = { + 'u._id': userId, + E2EKey: { + $exists: false, + }, + }; + + return this.find(query, options); + } + + updateGroupE2EKey(_id, key) { + const query = { _id }; + const update = { $set: { E2EKey: key } }; + this.update(query, update); + return this.findOne({ _id }); + } + findUsersInRoles(roles, scope, options) { roles = [].concat(roles); diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js index f89431943978..b6724c7bfb3e 100644 --- a/packages/rocketchat-models/server/models/Users.js +++ b/packages/rocketchat-models/server/models/Users.js @@ -23,6 +23,49 @@ export class Users extends Base { return { _id: userId }; } + setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key }) { + this.update({ _id: userId }, { + $set: { + 'e2e.public_key': public_key, + 'e2e.private_key': private_key, + }, + }); + } + + fetchKeysByUserId(userId) { + const user = this.findOne({ _id: userId }, { fields: { e2e: 1 } }); + + if (!user || !user.e2e || !user.e2e.public_key) { + return {}; + } + + return { + public_key: user.e2e.public_key, + private_key: user.e2e.private_key, + }; + } + + findByIdsWithPublicE2EKey(ids, options) { + const query = { + _id: { + $in: ids, + }, + 'e2e.public_key': { + $exists: 1, + }, + }; + + return this.find(query, options); + } + + resetE2EKey(userId) { + this.update({ _id: userId }, { + $unset: { + e2e: '', + }, + }); + } + findUsersInRoles(roles, scope, options) { roles = [].concat(roles); From 85c72bdec6963e39ee46b4fc3affaabc9f8add5e Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 13:16:37 -0200 Subject: [PATCH 44/59] Fix wrong import and lint --- packages/rocketchat-e2e/server/methods/resetUserE2EKey.js | 2 +- packages/rocketchat-models/server/models/Rooms.js | 4 ++-- packages/rocketchat-models/server/models/Subscriptions.js | 4 ++-- packages/rocketchat-models/server/models/Users.js | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js index 07a853c9f86a..2a4a797ed124 100644 --- a/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js +++ b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { Users, Subscrtipions } from 'meteor/rocketchat:models'; +import { Users, Subscriptions } from 'meteor/rocketchat:models'; import { hasPermission } from 'meteor/rocketchat:authorization'; Meteor.methods({ diff --git a/packages/rocketchat-models/server/models/Rooms.js b/packages/rocketchat-models/server/models/Rooms.js index c7951569c87a..caf0da9b7eb6 100644 --- a/packages/rocketchat-models/server/models/Rooms.js +++ b/packages/rocketchat-models/server/models/Rooms.js @@ -30,13 +30,13 @@ export class Rooms extends Base { const query = { _id, }; - + const update = { $set: { e2eKeyId, }, }; - + return this.update(query, update, options); } diff --git a/packages/rocketchat-models/server/models/Subscriptions.js b/packages/rocketchat-models/server/models/Subscriptions.js index 8e6da0dbd4d2..e1359f2fd7c4 100644 --- a/packages/rocketchat-models/server/models/Subscriptions.js +++ b/packages/rocketchat-models/server/models/Subscriptions.js @@ -48,7 +48,7 @@ export class Subscriptions extends Base { $exists: false, }, }; - + return this.find(query, options); } @@ -69,7 +69,7 @@ export class Subscriptions extends Base { $exists: false, }, }; - + return this.find(query, options); } diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js index b6724c7bfb3e..ffc1182a36ae 100644 --- a/packages/rocketchat-models/server/models/Users.js +++ b/packages/rocketchat-models/server/models/Users.js @@ -34,11 +34,11 @@ export class Users extends Base { fetchKeysByUserId(userId) { const user = this.findOne({ _id: userId }, { fields: { e2e: 1 } }); - + if (!user || !user.e2e || !user.e2e.public_key) { return {}; } - + return { public_key: user.e2e.public_key, private_key: user.e2e.private_key, @@ -54,7 +54,7 @@ export class Users extends Base { $exists: 1, }, }; - + return this.find(query, options); } From c61d8346ee3700eea855d91195a1f92008cfbcd3 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 15:01:53 -0200 Subject: [PATCH 45/59] Convert rocketchat-webrtc to main module structure --- .../rocketchat-webrtc/client/WebRTCClass.js | 26 ++++++++++--------- packages/rocketchat-webrtc/client/index.js | 6 +++++ packages/rocketchat-webrtc/package.js | 22 +++++++--------- packages/rocketchat-webrtc/server/index.js | 1 + packages/rocketchat-webrtc/server/settings.js | 4 ++- 5 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 packages/rocketchat-webrtc/client/index.js create mode 100644 packages/rocketchat-webrtc/server/index.js diff --git a/packages/rocketchat-webrtc/client/WebRTCClass.js b/packages/rocketchat-webrtc/client/WebRTCClass.js index 9c07e70ba943..d8ea11827fa3 100644 --- a/packages/rocketchat-webrtc/client/WebRTCClass.js +++ b/packages/rocketchat-webrtc/client/WebRTCClass.js @@ -5,13 +5,15 @@ import { FlowRouter } from 'meteor/kadira:flow-router' ; import { TAPi18n } from 'meteor/tap:i18n'; import { t } from 'meteor/rocketchat:utils'; import { ChromeScreenShare } from './screenShare'; +import { Notifications } from 'meteor/rocketchat:notifications'; +import { settings } from 'meteor/rocketchat:settings'; class WebRTCTransportClass { constructor(webrtcInstance) { this.debug = false; this.webrtcInstance = webrtcInstance; this.callbacks = {}; - RocketChat.Notifications.onRoom(this.webrtcInstance.room, 'webrtc', (type, data) => { + Notifications.onRoom(this.webrtcInstance.room, 'webrtc', (type, data) => { const { onRemoteStatus } = this.callbacks; this.log('WebRTCTransportClass - onRoom', type, data); switch (type) { @@ -61,7 +63,7 @@ class WebRTCTransportClass { startCall(data) { this.log('WebRTCTransportClass - startCall', this.webrtcInstance.room, this.webrtcInstance.selfId); - RocketChat.Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'call', { + Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'call', { from: this.webrtcInstance.selfId, room: this.webrtcInstance.room, media: data.media, @@ -72,14 +74,14 @@ class WebRTCTransportClass { joinCall(data) { this.log('WebRTCTransportClass - joinCall', this.webrtcInstance.room, this.webrtcInstance.selfId); if (data.monitor === true) { - RocketChat.Notifications.notifyUser(data.to, 'webrtc', 'join', { + Notifications.notifyUser(data.to, 'webrtc', 'join', { from: this.webrtcInstance.selfId, room: this.webrtcInstance.room, media: data.media, monitor: data.monitor, }); } else { - RocketChat.Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'join', { + Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'join', { from: this.webrtcInstance.selfId, room: this.webrtcInstance.room, media: data.media, @@ -92,20 +94,20 @@ class WebRTCTransportClass { data.from = this.webrtcInstance.selfId; data.room = this.webrtcInstance.room; this.log('WebRTCTransportClass - sendCandidate', data); - RocketChat.Notifications.notifyUser(data.to, 'webrtc', 'candidate', data); + Notifications.notifyUser(data.to, 'webrtc', 'candidate', data); } sendDescription(data) { data.from = this.webrtcInstance.selfId; data.room = this.webrtcInstance.room; this.log('WebRTCTransportClass - sendDescription', data); - RocketChat.Notifications.notifyUser(data.to, 'webrtc', 'description', data); + Notifications.notifyUser(data.to, 'webrtc', 'description', data); } sendStatus(data) { this.log('WebRTCTransportClass - sendStatus', data, this.webrtcInstance.room); data.from = this.webrtcInstance.selfId; - RocketChat.Notifications.notifyRoom(this.webrtcInstance.room, 'webrtc', 'status', data); + Notifications.notifyRoom(this.webrtcInstance.room, 'webrtc', 'status', data); } onRemoteCall(fn) { @@ -165,7 +167,7 @@ class WebRTCClass { this.TransportClass = WebRTCTransportClass; this.selfId = selfId; this.room = room; - let servers = RocketChat.settings.get('WebRTC_Servers'); + let servers = settings.get('WebRTC_Servers'); if (servers && servers.trim() !== '') { servers = servers.replace(/\s/g, ''); servers = servers.split(','); @@ -966,13 +968,13 @@ const WebRTC = new class { let enabled = false; switch (subscription.t) { case 'd': - enabled = RocketChat.settings.get('WebRTC_Enable_Direct'); + enabled = settings.get('WebRTC_Enable_Direct'); break; case 'p': - enabled = RocketChat.settings.get('WebRTC_Enable_Private'); + enabled = settings.get('WebRTC_Enable_Private'); break; case 'c': - enabled = RocketChat.settings.get('WebRTC_Enable_Channel'); + enabled = settings.get('WebRTC_Enable_Channel'); } if (enabled === false) { return; @@ -987,7 +989,7 @@ const WebRTC = new class { Meteor.startup(function() { Tracker.autorun(function() { if (Meteor.userId()) { - RocketChat.Notifications.onUser('webrtc', (type, data) => { + Notifications.onUser('webrtc', (type, data) => { if (data.room == null) { return; } diff --git a/packages/rocketchat-webrtc/client/index.js b/packages/rocketchat-webrtc/client/index.js new file mode 100644 index 000000000000..19030daa8614 --- /dev/null +++ b/packages/rocketchat-webrtc/client/index.js @@ -0,0 +1,6 @@ +import './adapter'; +import { WebRTC } from './WebRTCClass'; + +export { + WebRTC, +}; diff --git a/packages/rocketchat-webrtc/package.js b/packages/rocketchat-webrtc/package.js index c82843b19bb8..611e7ca315a3 100644 --- a/packages/rocketchat-webrtc/package.js +++ b/packages/rocketchat-webrtc/package.js @@ -6,17 +6,13 @@ Package.describe({ }); Package.onUse(function(api) { - api.use('rocketchat:lib'); - api.use('rocketchat:utils'); - api.use('ecmascript'); - - api.use('templating', 'client'); - api.mainModule('client/WebRTCClass.js', 'client'); - api.addFiles('client/adapter.js', 'client'); - // api.addFiles('); - api.addFiles('client/screenShare.js', 'client'); - - api.addFiles('server/settings.js', 'server'); - - api.export('WebRTC', 'client'); + api.use([ + 'ecmascript', + 'rocketchat:utils', + 'rocketchat:notifications', + 'rocketchat:settings', + 'templating', + ]); + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); }); diff --git a/packages/rocketchat-webrtc/server/index.js b/packages/rocketchat-webrtc/server/index.js new file mode 100644 index 000000000000..97097791afdc --- /dev/null +++ b/packages/rocketchat-webrtc/server/index.js @@ -0,0 +1 @@ +import './settings'; diff --git a/packages/rocketchat-webrtc/server/settings.js b/packages/rocketchat-webrtc/server/settings.js index dc3b054cc35e..e20bb47617e0 100644 --- a/packages/rocketchat-webrtc/server/settings.js +++ b/packages/rocketchat-webrtc/server/settings.js @@ -1,4 +1,6 @@ -RocketChat.settings.addGroup('WebRTC', function() { +import { settings } from 'meteor/rocketchat:settings'; + +settings.addGroup('WebRTC', function() { this.add('WebRTC_Enable_Channel', false, { type: 'boolean', group: 'WebRTC', From 303613b2e997440f0a952e03be0b576d0b2f2563 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 15:31:57 -0200 Subject: [PATCH 46/59] Fix missing export --- packages/rocketchat-authorization/client/hasPermission.js | 2 ++ packages/rocketchat-authorization/client/index.js | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-authorization/client/hasPermission.js b/packages/rocketchat-authorization/client/hasPermission.js index 92cdd76d05a4..646ab46d7840 100644 --- a/packages/rocketchat-authorization/client/hasPermission.js +++ b/packages/rocketchat-authorization/client/hasPermission.js @@ -53,3 +53,5 @@ Template.registerHelper('hasPermission', function(permission, scope) { export const hasAllPermission = (permissions, scope) => _hasPermission(permissions, scope, all); export const hasAtLeastOnePermission = (permissions, scope) => _hasPermission(permissions, scope, atLeastOne); +export const hasPermission = hasAllPermission; + diff --git a/packages/rocketchat-authorization/client/index.js b/packages/rocketchat-authorization/client/index.js index 76bf9a87483d..a45f3463243a 100644 --- a/packages/rocketchat-authorization/client/index.js +++ b/packages/rocketchat-authorization/client/index.js @@ -1,4 +1,4 @@ -import { hasAllPermission, hasAtLeastOnePermission } from './hasPermission'; +import { hasAllPermission, hasAtLeastOnePermission, hasPermission } from './hasPermission'; import { hasRole } from './hasRole'; import './usersNameChanged'; import './requiresPermission.html'; @@ -13,4 +13,5 @@ export { hasAllPermission, hasAtLeastOnePermission, hasRole, + hasPermission, }; From 58e4389480403a0a54058d86b3d13d72180c5076 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 15:41:40 -0200 Subject: [PATCH 47/59] Remove directly dependency between rocketchat:emoji and lib --- .../rocketchat-emoji/client/emojiParser.js | 16 +-- .../rocketchat-emoji/client/emojiPicker.js | 101 +++++++++--------- packages/rocketchat-emoji/client/index.js | 6 +- .../rocketchat-emoji/client/keyboardFix.js | 6 +- .../client/lib/EmojiPicker.js | 23 ++-- .../client/lib/emojiRenderer.js | 18 ++-- packages/rocketchat-emoji/lib/rocketchat.js | 4 +- packages/rocketchat-emoji/package.js | 4 +- packages/rocketchat-emoji/server/index.js | 2 +- 9 files changed, 91 insertions(+), 89 deletions(-) diff --git a/packages/rocketchat-emoji/client/emojiParser.js b/packages/rocketchat-emoji/client/emojiParser.js index f17aa776e994..339ee4b7ea67 100644 --- a/packages/rocketchat-emoji/client/emojiParser.js +++ b/packages/rocketchat-emoji/client/emojiParser.js @@ -1,5 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { getUserPreference } from 'meteor/rocketchat:utils'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { emoji } from '../lib/rocketchat'; import { isSetNotNull } from './function-isSet'; import s from 'underscore.string'; @@ -7,9 +9,9 @@ import s from 'underscore.string'; * emojiParser is a function that will replace emojis * @param {Object} message - The message object */ -RocketChat.callbacks.add('renderMessage', (message) => { - if (isSetNotNull(() => RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')) && - !RocketChat.getUserPreference(Meteor.userId(), 'useEmojis')) { +callbacks.add('renderMessage', (message) => { + if (isSetNotNull(() => getUserPreference(Meteor.userId(), 'useEmojis')) && + !getUserPreference(Meteor.userId(), 'useEmojis')) { return message; } @@ -17,8 +19,8 @@ RocketChat.callbacks.add('renderMessage', (message) => { // ' to apostrophe (') for emojis such as :') message.html = message.html.replace(/'/g, '\''); - Object.keys(RocketChat.emoji.packages).forEach((emojiPackage) => { - message.html = RocketChat.emoji.packages[emojiPackage].render(message.html); + Object.keys(emoji.packages).forEach((emojiPackage) => { + message.html = emoji.packages[emojiPackage].render(message.html); }); const checkEmojiOnly = $(`
${ message.html }
`); @@ -50,4 +52,4 @@ RocketChat.callbacks.add('renderMessage', (message) => { } return message; -}, RocketChat.callbacks.priority.LOW, 'emoji'); +}, callbacks.priority.LOW, 'emoji'); diff --git a/packages/rocketchat-emoji/client/emojiPicker.js b/packages/rocketchat-emoji/client/emojiPicker.js index 342a2f08391e..73eadb48b957 100644 --- a/packages/rocketchat-emoji/client/emojiPicker.js +++ b/packages/rocketchat-emoji/client/emojiPicker.js @@ -1,8 +1,9 @@ import { ReactiveVar } from 'meteor/reactive-var'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { emoji } from '../lib/rocketchat'; import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/tap:i18n'; import { isSetNotNull } from './function-isSet'; +import { EmojiPicker } from './lib/EmojiPicker'; const emojiCategories = {}; /** @@ -11,10 +12,10 @@ const emojiCategories = {}; * @return {string} readable and translated */ function categoryName(category) { - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - if (RocketChat.emoji.packages[emojiPackage].emojiCategories.hasOwnProperty(category)) { - const categoryTag = RocketChat.emoji.packages[emojiPackage].emojiCategories[category]; + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + if (emoji.packages[emojiPackage].emojiCategories.hasOwnProperty(category)) { + const categoryTag = emoji.packages[emojiPackage].emojiCategories[category]; return TAPi18n.__(categoryTag); } } @@ -30,24 +31,24 @@ function getEmojisByCategory(category) { const t = Template.instance(); const actualTone = t.tone; let html = ''; - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - if (RocketChat.emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(category)) { - const total = RocketChat.emoji.packages[emojiPackage].emojisByCategory[category].length; + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + if (emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(category)) { + const total = emoji.packages[emojiPackage].emojisByCategory[category].length; for (let i = 0; i < total; i++) { - const emoji = RocketChat.emoji.packages[emojiPackage].emojisByCategory[category][i]; + const _emoji = emoji.packages[emojiPackage].emojisByCategory[category][i]; let tone = ''; - if (actualTone > 0 && RocketChat.emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { + if (actualTone > 0 && emoji.packages[emojiPackage].toneList.hasOwnProperty(_emoji)) { tone = `_tone${ actualTone }`; } // set correctPackage here to allow for recent emojis to work properly - if (isSetNotNull(() => RocketChat.emoji.list[`:${ emoji }:`].emojiPackage)) { - const correctPackage = RocketChat.emoji.list[`:${ emoji }:`].emojiPackage; - const image = RocketChat.emoji.packages[correctPackage].render(`:${ emoji }${ tone }:`); + if (isSetNotNull(() => emoji.list[`:${ _emoji }:`].emojiPackage)) { + const correctPackage = emoji.list[`:${ _emoji }:`].emojiPackage; + const image = emoji.packages[correctPackage].render(`:${ _emoji }${ tone }:`); - html += `
  • ${ image }
  • `; + html += `
  • ${ image }
  • `; } } } @@ -63,26 +64,26 @@ function getEmojisBySearchTerm(searchTerm) { const searchRegExp = new RegExp(RegExp.escape(searchTerm.replace(/:/g, '')), 'i'); - for (let emoji in RocketChat.emoji.list) { - if (!RocketChat.emoji.list.hasOwnProperty(emoji)) { + for (let _emoji in emoji.list) { + if (!emoji.list.hasOwnProperty(_emoji)) { continue; } - if (searchRegExp.test(emoji)) { - const emojiObject = RocketChat.emoji.list[emoji]; + if (searchRegExp.test(_emoji)) { + const emojiObject = emoji.list[_emoji]; const { emojiPackage } = emojiObject; let tone = ''; - emoji = emoji.replace(/:/g, ''); + _emoji = _emoji.replace(/:/g, ''); - if (actualTone > 0 && RocketChat.emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { + if (actualTone > 0 && emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { tone = `_tone${ actualTone }`; } let emojiFound = false; - for (const key in RocketChat.emoji.packages[emojiPackage].emojisByCategory) { - if (RocketChat.emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(key)) { - const contents = RocketChat.emoji.packages[emojiPackage].emojisByCategory[key]; + for (const key in emoji.packages[emojiPackage].emojisByCategory) { + if (emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(key)) { + const contents = emoji.packages[emojiPackage].emojisByCategory[key]; if (contents.indexOf(emoji) !== -1) { emojiFound = true; break; @@ -91,8 +92,8 @@ function getEmojisBySearchTerm(searchTerm) { } if (emojiFound) { - const image = RocketChat.emoji.packages[emojiPackage].render(`:${ emoji }${ tone }:`); - html += `
  • ${ image }
  • `; + const image = emoji.packages[emojiPackage].render(`:${ _emoji }${ tone }:`); + html += `
  • ${ image }
  • `; } } } @@ -103,10 +104,10 @@ function getEmojisBySearchTerm(searchTerm) { Template.emojiPicker.helpers({ category() { const categories = []; - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - for (const key in RocketChat.emoji.packages[emojiPackage].emojisByCategory) { - if (RocketChat.emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(key)) { + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + for (const key in emoji.packages[emojiPackage].emojisByCategory) { + if (emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(key)) { categories.push(key); } } @@ -116,10 +117,10 @@ Template.emojiPicker.helpers({ }, emojiByCategory(category) { let emojisByCategory = []; - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - if (RocketChat.emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(category)) { - emojisByCategory = emojisByCategory.concat(RocketChat.emoji.packages[emojiPackage].emojisByCategory[category]); + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + if (emoji.packages[emojiPackage].emojisByCategory.hasOwnProperty(category)) { + emojisByCategory = emojisByCategory.concat(emoji.packages[emojiPackage].emojisByCategory[category]); } } } @@ -212,19 +213,19 @@ Template.emojiPicker.events({ newTone = ''; } - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - if (RocketChat.emoji.packages[emojiPackage].hasOwnProperty('toneList')) { - for (const emoji in RocketChat.emoji.packages[emojiPackage].toneList) { - if (RocketChat.emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { - $(`.emoji-${ emoji }`).html(RocketChat.emoji.packages[emojiPackage].render(`:${ emoji }${ newTone }:`)); + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + if (emoji.packages[emojiPackage].hasOwnProperty('toneList')) { + for (const emoji in emoji.packages[emojiPackage].toneList) { + if (emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { + $(`.emoji-${ emoji }`).html(emoji.packages[emojiPackage].render(`:${ emoji }${ newTone }:`)); } } } } } - RocketChat.EmojiPicker.setTone(tone); + EmojiPicker.setTone(tone); instance.setCurrentTone(tone); @@ -233,13 +234,13 @@ Template.emojiPicker.events({ 'click .emoji-list li'(event, instance) { event.stopPropagation(); - const { emoji } = event.currentTarget.dataset; + const _emoji = event.currentTarget.dataset.emoji; const actualTone = instance.tone; let tone = ''; - for (const emojiPackage in RocketChat.emoji.packages) { - if (RocketChat.emoji.packages.hasOwnProperty(emojiPackage)) { - if (actualTone > 0 && RocketChat.emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { + for (const emojiPackage in emoji.packages) { + if (emoji.packages.hasOwnProperty(emojiPackage)) { + if (actualTone > 0 && emoji.packages[emojiPackage].toneList.hasOwnProperty(_emoji)) { tone = `_tone${ actualTone }`; } } @@ -251,7 +252,7 @@ Template.emojiPicker.events({ } instance.currentSearchTerm.set(''); - RocketChat.EmojiPicker.pickEmoji(emoji + tone); + EmojiPicker.pickEmoji(_emoji + tone); }, 'keyup .js-emojipicker-search, change .js-emojipicker-search'(event, instance) { const value = event.target.value.trim(); @@ -264,14 +265,14 @@ Template.emojiPicker.events({ }); Template.emojiPicker.onCreated(function() { - this.tone = RocketChat.EmojiPicker.getTone(); - const recent = RocketChat.EmojiPicker.getRecent(); + this.tone = EmojiPicker.getTone(); + const recent = EmojiPicker.getRecent(); this.recentNeedsUpdate = new ReactiveVar(false); this.currentCategory = new ReactiveVar(recent.length > 0 ? 'recent' : 'people'); this.currentSearchTerm = new ReactiveVar(''); - recent.forEach((emoji) => { - RocketChat.emoji.packages.base.emojisByCategory.recent.push(emoji); + recent.forEach((_emoji) => { + emoji.packages.base.emojisByCategory.recent.push(_emoji); }); this.setCurrentTone = (newTone) => { diff --git a/packages/rocketchat-emoji/client/index.js b/packages/rocketchat-emoji/client/index.js index e4b043a604ef..48c7310015b2 100644 --- a/packages/rocketchat-emoji/client/index.js +++ b/packages/rocketchat-emoji/client/index.js @@ -1,11 +1,13 @@ -import '../lib/rocketchat'; +import { emoji } from '../lib/rocketchat'; import './emojiParser'; import './emojiPicker.html'; import './emojiPicker'; -import './lib/EmojiPicker'; +import { EmojiPicker } from './lib/EmojiPicker'; import { renderEmoji } from './lib/emojiRenderer'; import './keyboardFix'; export { renderEmoji, + emoji, + EmojiPicker, }; diff --git a/packages/rocketchat-emoji/client/keyboardFix.js b/packages/rocketchat-emoji/client/keyboardFix.js index 7f65426c2d33..87f1ecbc069d 100644 --- a/packages/rocketchat-emoji/client/keyboardFix.js +++ b/packages/rocketchat-emoji/client/keyboardFix.js @@ -1,15 +1,15 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { EmojiPicker } from './lib/EmojiPicker'; if (Meteor.isCordova) { window.addEventListener('native.keyboardshow', function() { if ((typeof device !== 'undefined' && device !== null ? device.platform.toLowerCase() : false) !== 'android') { - RocketChat.EmojiPicker.setPosition(); + EmojiPicker.setPosition(); } }); window.addEventListener('native.keyboardhide', function() { if ((typeof device !== 'undefined' && device !== null ? device.platform.toLowerCase() : false) !== 'android') { - RocketChat.EmojiPicker.setPosition(); + EmojiPicker.setPosition(); } }); } diff --git a/packages/rocketchat-emoji/client/lib/EmojiPicker.js b/packages/rocketchat-emoji/client/lib/EmojiPicker.js index 6644789d3888..20d29c00f7e3 100644 --- a/packages/rocketchat-emoji/client/lib/EmojiPicker.js +++ b/packages/rocketchat-emoji/client/lib/EmojiPicker.js @@ -1,10 +1,9 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; - +import { emoji } from '../../lib/rocketchat'; import _ from 'underscore'; -RocketChat.EmojiPicker = { +export const EmojiPicker = { width: 365, height: 290, initiated: false, @@ -105,17 +104,17 @@ RocketChat.EmojiPicker = { this.close(); this.addRecent(emoji); }, - addRecent(emoji) { - const pos = this.recent.indexOf(emoji); + addRecent(_emoji) { + const pos = this.recent.indexOf(_emoji); if (pos !== -1) { this.recent.splice(pos, 1); } - this.recent.unshift(emoji); + this.recent.unshift(_emoji); window.localStorage.setItem('emoji.recent', this.recent); - RocketChat.emoji.packages.base.emojisByCategory.recent = this.recent; + emoji.packages.base.emojisByCategory.recent = this.recent; this.updateRecent(); }, updateRecent() { @@ -128,16 +127,16 @@ RocketChat.EmojiPicker = { }, refreshDynamicEmojiLists() { const dynamicEmojiLists = [ - RocketChat.emoji.packages.base.emojisByCategory.recent, - RocketChat.emoji.packages.emojiCustom.emojisByCategory.rocket, + emoji.packages.base.emojisByCategory.recent, + emoji.packages.emojiCustom.emojisByCategory.rocket, ]; dynamicEmojiLists.forEach((category) => { if (category) { for (let i = 0; i < category.length; i++) { - const emoji = category[i]; - if (!RocketChat.emoji.list[`:${ emoji }:`]) { - category = _.without(category, emoji); + const _emoji = category[i]; + if (!emoji.list[`:${ _emoji }:`]) { + category = _.without(category, _emoji); } } } diff --git a/packages/rocketchat-emoji/client/lib/emojiRenderer.js b/packages/rocketchat-emoji/client/lib/emojiRenderer.js index 15731c2fb704..4c8ded7a2454 100644 --- a/packages/rocketchat-emoji/client/lib/emojiRenderer.js +++ b/packages/rocketchat-emoji/client/lib/emojiRenderer.js @@ -1,13 +1,13 @@ import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { emoji } from '../../lib/rocketchat'; import { isSetNotNull } from '../function-isSet'; import { HTML } from 'meteor/htmljs'; -export const renderEmoji = function(emoji) { - if (isSetNotNull(() => RocketChat.emoji.list[emoji].emojiPackage)) { - const { emojiPackage } = RocketChat.emoji.list[emoji]; - return RocketChat.emoji.packages[emojiPackage].render(emoji); +export const renderEmoji = function(_emoji) { + if (isSetNotNull(() => emoji.list[_emoji].emojiPackage)) { + const { emojiPackage } = emoji.list[_emoji]; + return emoji.packages[emojiPackage].render(_emoji); } }; @@ -15,11 +15,11 @@ Blaze.registerHelper('renderEmoji', renderEmoji); Template.registerHelper('renderEmoji', new Template('renderEmoji', function() { const view = this; - const emoji = Blaze.getData(view); + const _emoji = Blaze.getData(view); - if (isSetNotNull(() => RocketChat.emoji.list[emoji].emojiPackage)) { - const { emojiPackage } = RocketChat.emoji.list[emoji]; - return new HTML.Raw(RocketChat.emoji.packages[emojiPackage].render(emoji)); + if (isSetNotNull(() => emoji.list[_emoji].emojiPackage)) { + const { emojiPackage } = emoji.list[_emoji]; + return new HTML.Raw(emoji.packages[emojiPackage].render(_emoji)); } return ''; diff --git a/packages/rocketchat-emoji/lib/rocketchat.js b/packages/rocketchat-emoji/lib/rocketchat.js index c0bcf7a3f002..1dc7063ac023 100644 --- a/packages/rocketchat-emoji/lib/rocketchat.js +++ b/packages/rocketchat-emoji/lib/rocketchat.js @@ -1,6 +1,4 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.emoji = { +export const emoji = { packages: { base: { emojiCategories: { recent: 'Frequently_Used' }, diff --git a/packages/rocketchat-emoji/package.js b/packages/rocketchat-emoji/package.js index 6fb43988aecb..d5c0a973640b 100644 --- a/packages/rocketchat-emoji/package.js +++ b/packages/rocketchat-emoji/package.js @@ -9,8 +9,8 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', - 'rocketchat:lib', - 'rocketchat:theme', + 'rocketchat:callbacks', + 'rocketchat:utils', 'htmljs', ]); diff --git a/packages/rocketchat-emoji/server/index.js b/packages/rocketchat-emoji/server/index.js index 8e716d1f414f..4bb73e75c17d 100644 --- a/packages/rocketchat-emoji/server/index.js +++ b/packages/rocketchat-emoji/server/index.js @@ -1 +1 @@ -import '../lib/rocketchat'; +export { emoji } from '../lib/rocketchat'; From b9a377dff8ee6986ebca6e1d93a6d124acedbd54 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 15:47:48 -0200 Subject: [PATCH 48/59] Add emoji dependencies inside RocketChat namespace --- packages/rocketchat-lib/client/lib/EmojiPicker.js | 3 +++ packages/rocketchat-lib/lib/emoji.js | 3 +++ packages/rocketchat-lib/package.js | 3 +++ packages/rocketchat-ui/package.js | 1 + 4 files changed, 10 insertions(+) create mode 100644 packages/rocketchat-lib/client/lib/EmojiPicker.js create mode 100644 packages/rocketchat-lib/lib/emoji.js diff --git a/packages/rocketchat-lib/client/lib/EmojiPicker.js b/packages/rocketchat-lib/client/lib/EmojiPicker.js new file mode 100644 index 000000000000..cea3e8899b4b --- /dev/null +++ b/packages/rocketchat-lib/client/lib/EmojiPicker.js @@ -0,0 +1,3 @@ +import { EmojiPicker } from 'meteor/rocketchat:emoji'; + +RocketChat.EmojiPicker = EmojiPicker; diff --git a/packages/rocketchat-lib/lib/emoji.js b/packages/rocketchat-lib/lib/emoji.js new file mode 100644 index 000000000000..ff6a9f7c668a --- /dev/null +++ b/packages/rocketchat-lib/lib/emoji.js @@ -0,0 +1,3 @@ +import { emoji } from 'meteor/rocketchat:emoji'; + +RocketChat.emoji = emoji; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 59ae61e52a97..ec587239e478 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -33,6 +33,7 @@ Package.onUse(function(api) { api.use('rocketchat:promises'); api.use('rocketchat:ui-utils'); api.use('rocketchat:tooltip'); + api.use('rocketchat:emoji'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); @@ -94,6 +95,7 @@ Package.onUse(function(api) { api.addFiles('lib/getUserNotificationPreference.js'); api.addFiles('lib/getUserPreference.js'); + api.addFiles('lib/emoji.js'); api.addFiles('server/lib/bugsnag.js', 'server'); api.addFiles('server/lib/metrics.js', 'server'); @@ -247,6 +249,7 @@ Package.onUse(function(api) { api.addFiles('client/lib/handleError.js', 'client'); api.addFiles('client/lib/authorization.js', 'client'); api.addFiles('client/lib/tooltip.js', 'client'); + api.addFiles('client/lib/EmojiPicker.js', 'client'); // CLIENT LIB STARTUP api.addFiles('client/lib/startup/commands.js', 'client'); diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index d79deecdb349..f2558cea2782 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -24,6 +24,7 @@ Package.onUse(function(api) { 'rocketchat:ui-master', 'rocketchat:push', 'rocketchat:utils', + 'rocketchat:emoji', 'rocketchat:ui-utils', 'rocketchat:models', 'raix:ui-dropped-event', From b40ea2d7e0c9f2c827f77733b15c5b6f3fb47e29 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 16:10:50 -0200 Subject: [PATCH 49/59] Merge branch 'develop' into globals/move-rocketchat-callbacks --- .meteor/packages | 1 + .meteor/versions | 1 + packages/rocketchat-api/server/api.js | 1 + .../client/admin/callback.html | 16 +++ .../rocketchat-cloud/client/admin/callback.js | 37 +++++ .../rocketchat-cloud/client/admin/cloud.html | 83 ++++++++++++ .../rocketchat-cloud/client/admin/cloud.js | 93 +++++++++++++ packages/rocketchat-cloud/client/index.js | 28 ++++ packages/rocketchat-cloud/package.js | 17 +++ .../server/functions/connectWorkspace.js | 70 ++++++++++ .../functions/finishOAuthAuthorization.js | 50 +++++++ .../functions/getOAuthAuthorizationUrl.js | 15 ++ .../server/functions/getRedirectUri.js | 3 + .../functions/getWorkspaceAccessTokens.js | 50 +++++++ .../server/functions/getWorkspaceLicense.js | 34 +++++ .../functions/retrieveRegistrationStatus.js | 18 +++ packages/rocketchat-cloud/server/index.js | 11 ++ packages/rocketchat-cloud/server/methods.js | 72 ++++++++++ .../rocketchat-e2e/client/rocketchat.e2e.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 19 ++- packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 3 +- .../rocketchat-lib/client/models/Avatars.js | 3 - .../rocketchat-lib/client/models/Uploads.js | 3 - .../client/models/UserDataFiles.js | 3 - .../rocketchat-lib/client/models/_Base.js | 3 - .../rocketchat-lib/client/models/index.js | 11 ++ packages/rocketchat-lib/package.js | 20 +-- .../lib/{metrics.js => metrics_import.js} | 0 .../rocketchat-lib/server/models/Avatars.js | 3 - .../server/models/ExportOperations.js | 3 - .../rocketchat-lib/server/models/Messages.js | 3 - .../rocketchat-lib/server/models/Reports.js | 3 - .../rocketchat-lib/server/models/Rooms.js | 4 - .../rocketchat-lib/server/models/Settings.js | 3 - .../server/models/Subscriptions.js | 4 - .../rocketchat-lib/server/models/Uploads.js | 3 - .../server/models/UserDataFiles.js | 3 - .../rocketchat-lib/server/models/Users.js | 4 - .../rocketchat-lib/server/models/_Base.js | 3 - .../rocketchat-lib/server/models/index.js | 25 ++++ .../rocketchat-lib/server/startup/settings.js | 128 ++++++++++++++++++ ...statsTracker.js => statsTracker_import.js} | 0 .../client/setupWizard.js | 8 ++ .../server/functions/get.js | 10 +- .../rocketchat-statistics/server/index.js | 2 +- .../{Statistics.js => Statistics_import.js} | 0 .../client/tabs/userInfo.html | 4 +- .../client/components/header/header.html | 4 +- packages/rocketchat-version-check/package.js | 1 + .../server/functions/getNewUpdates.js | 8 ++ server/lib/cordova.js | 8 ++ server/startup/cron.js | 9 ++ server/startup/migrations/v099.js | 10 +- server/startup/migrations/v137.js | 10 ++ tests/end-to-end/ui/00-login.js | 2 +- 55 files changed, 851 insertions(+), 81 deletions(-) create mode 100644 packages/rocketchat-cloud/client/admin/callback.html create mode 100644 packages/rocketchat-cloud/client/admin/callback.js create mode 100644 packages/rocketchat-cloud/client/admin/cloud.html create mode 100644 packages/rocketchat-cloud/client/admin/cloud.js create mode 100644 packages/rocketchat-cloud/client/index.js create mode 100644 packages/rocketchat-cloud/package.js create mode 100644 packages/rocketchat-cloud/server/functions/connectWorkspace.js create mode 100644 packages/rocketchat-cloud/server/functions/finishOAuthAuthorization.js create mode 100644 packages/rocketchat-cloud/server/functions/getOAuthAuthorizationUrl.js create mode 100644 packages/rocketchat-cloud/server/functions/getRedirectUri.js create mode 100644 packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js create mode 100644 packages/rocketchat-cloud/server/functions/getWorkspaceLicense.js create mode 100644 packages/rocketchat-cloud/server/functions/retrieveRegistrationStatus.js create mode 100644 packages/rocketchat-cloud/server/index.js create mode 100644 packages/rocketchat-cloud/server/methods.js delete mode 100644 packages/rocketchat-lib/client/models/Avatars.js delete mode 100644 packages/rocketchat-lib/client/models/Uploads.js delete mode 100644 packages/rocketchat-lib/client/models/UserDataFiles.js delete mode 100644 packages/rocketchat-lib/client/models/_Base.js create mode 100644 packages/rocketchat-lib/client/models/index.js rename packages/rocketchat-lib/server/lib/{metrics.js => metrics_import.js} (100%) delete mode 100644 packages/rocketchat-lib/server/models/Avatars.js delete mode 100644 packages/rocketchat-lib/server/models/ExportOperations.js delete mode 100644 packages/rocketchat-lib/server/models/Messages.js delete mode 100644 packages/rocketchat-lib/server/models/Reports.js delete mode 100644 packages/rocketchat-lib/server/models/Rooms.js delete mode 100644 packages/rocketchat-lib/server/models/Settings.js delete mode 100644 packages/rocketchat-lib/server/models/Subscriptions.js delete mode 100644 packages/rocketchat-lib/server/models/Uploads.js delete mode 100644 packages/rocketchat-lib/server/models/UserDataFiles.js delete mode 100644 packages/rocketchat-lib/server/models/Users.js delete mode 100644 packages/rocketchat-lib/server/models/_Base.js create mode 100644 packages/rocketchat-lib/server/models/index.js rename packages/rocketchat-lib/server/startup/{statsTracker.js => statsTracker_import.js} (100%) rename packages/rocketchat-statistics/server/models/{Statistics.js => Statistics_import.js} (100%) create mode 100644 server/startup/migrations/v137.js diff --git a/.meteor/packages b/.meteor/packages index aa8c0be5aaa7..f5643db382fe 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -51,6 +51,7 @@ rocketchat:bot-helpers rocketchat:cas rocketchat:channel-settings rocketchat:channel-settings-mail-messages +rocketchat:cloud rocketchat:colors rocketchat:crowd rocketchat:custom-oauth diff --git a/.meteor/versions b/.meteor/versions index 76cbc06a8557..6098cf93abe3 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -145,6 +145,7 @@ rocketchat:callbacks@0.0.1 rocketchat:cas@1.0.0 rocketchat:channel-settings@0.0.1 rocketchat:channel-settings-mail-messages@0.0.1 +rocketchat:cloud@0.0.1 rocketchat:colors@0.0.1 rocketchat:cors@0.0.1 rocketchat:crowd@1.0.0 diff --git a/packages/rocketchat-api/server/api.js b/packages/rocketchat-api/server/api.js index d28a95d95f49..ff10ec4a4b1c 100644 --- a/packages/rocketchat-api/server/api.js +++ b/packages/rocketchat-api/server/api.js @@ -386,6 +386,7 @@ const defaultOptionsEndpoint = function _defaultOptionsEndpoint() { if (RocketChat.settings.get('API_Enable_CORS') === true) { this.response.writeHead(200, { 'Access-Control-Allow-Origin': RocketChat.settings.get('API_CORS_Origin'), + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, HEAD, PATCH', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-User-Id, X-Auth-Token, x-visitor-token', }); } else { diff --git a/packages/rocketchat-cloud/client/admin/callback.html b/packages/rocketchat-cloud/client/admin/callback.html new file mode 100644 index 000000000000..5d6c9c432f4c --- /dev/null +++ b/packages/rocketchat-cloud/client/admin/callback.html @@ -0,0 +1,16 @@ + diff --git a/packages/rocketchat-cloud/client/admin/callback.js b/packages/rocketchat-cloud/client/admin/callback.js new file mode 100644 index 000000000000..adf4bac1d211 --- /dev/null +++ b/packages/rocketchat-cloud/client/admin/callback.js @@ -0,0 +1,37 @@ +import './callback.html'; + +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; + +import { FlowRouter } from 'meteor/kadira:flow-router'; + +import queryString from 'query-string'; + +Template.cloudCallback.onCreated(function() { + const instance = this; + + instance.loading = new ReactiveVar(true); + instance.callbackError = new ReactiveVar({ error: false }); + + const params = queryString.parse(location.search); + + if (params.error_code) { + instance.callbackError.set({ error: true, errorCode: params.error_code }); + } else { + Meteor.call('cloud:finishOAuthAuthorization', params.code, params.state, (error) => { + if (error) { + console.warn('cloud:finishOAuthAuthorization', error); + return; + } + + FlowRouter.go('/admin/cloud'); + }); + } +}); + +Template.cloudCallback.helpers({ + callbackError() { + return Template.instance().callbackError.get(); + }, +}); diff --git a/packages/rocketchat-cloud/client/admin/cloud.html b/packages/rocketchat-cloud/client/admin/cloud.html new file mode 100644 index 000000000000..c09e1281a77e --- /dev/null +++ b/packages/rocketchat-cloud/client/admin/cloud.html @@ -0,0 +1,83 @@ + diff --git a/packages/rocketchat-cloud/client/admin/cloud.js b/packages/rocketchat-cloud/client/admin/cloud.js new file mode 100644 index 000000000000..e68f0e890532 --- /dev/null +++ b/packages/rocketchat-cloud/client/admin/cloud.js @@ -0,0 +1,93 @@ +import './cloud.html'; + +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import { t } from 'meteor/rocketchat:utils'; + +import queryString from 'query-string'; +import toastr from 'toastr'; + +Template.cloud.onCreated(function() { + const instance = this; + instance.info = new ReactiveVar(); + instance.loading = new ReactiveVar(true); + + instance.loadRegStatus = function _loadRegStatus() { + Meteor.call('cloud:checkRegisterStatus', (error, info) => { + if (error) { + console.warn('cloud:checkRegisterStatus', error); + return; + } + + instance.info.set(info); + instance.loading.set(false); + }); + }; + + instance.connectWorkspace = function _connectWorkspace(token) { + Meteor.call('cloud:connectWorkspace', token, (error, success) => { + if (error) { + toastr.error(error); + instance.loadRegStatus(); + return; + } + + if (!success) { + toastr.error('Invalid token'); + instance.loadRegStatus(); + return; + } + + toastr.success(t('Connected')); + + instance.loadRegStatus(); + }); + }; + + const params = queryString.parse(location.search); + + if (params.token) { + instance.connectWorkspace(); + } else { + instance.loadRegStatus(); + } +}); + +Template.cloud.helpers({ + info() { + return Template.instance().info.get(); + }, +}); + +Template.cloud.events({ + 'click .update-email-btn'() { + const val = $('input[name=cloudEmail]').val(); + + Meteor.call('cloud:updateEmail', val, (error) => { + if (error) { + console.warn(error); + return; + } + + toastr.success(t('Saved')); + }); + }, + + 'click .login-btn'() { + Meteor.call('cloud:getOAuthAuthorizationUrl', (error, url) => { + if (error) { + console.warn(error); + return; + } + + window.location.href = url; + }); + }, + + 'click .connect-btn'(e, i) { + const token = $('input[name=cloudToken]').val(); + + i.connectWorkspace(token); + }, +}); diff --git a/packages/rocketchat-cloud/client/index.js b/packages/rocketchat-cloud/client/index.js new file mode 100644 index 000000000000..e5dddf8944bf --- /dev/null +++ b/packages/rocketchat-cloud/client/index.js @@ -0,0 +1,28 @@ +import './admin/callback'; +import './admin/cloud'; + +import { BlazeLayout } from 'meteor/kadira:blaze-layout'; +import { FlowRouter } from 'meteor/kadira:flow-router'; + +FlowRouter.route('/admin/cloud', { + name: 'cloud-config', + action() { + BlazeLayout.render('main', { center: 'cloud', old: true }); + }, +}); + +FlowRouter.route('/admin/cloud/oauth-callback', { + name: 'cloud-oauth-callback', + action() { + BlazeLayout.render('main', { center: 'cloudCallback', old: true }); + }, +}); + +RocketChat.AdminBox.addOption({ + icon: 'cloud-plus', + href: 'admin/cloud', + i18nLabel: 'Cloud', + permissionGranted() { + return RocketChat.authz.hasAtLeastOnePermission(['manage-cloud']); + }, +}); diff --git a/packages/rocketchat-cloud/package.js b/packages/rocketchat-cloud/package.js new file mode 100644 index 000000000000..52480e9e66d6 --- /dev/null +++ b/packages/rocketchat-cloud/package.js @@ -0,0 +1,17 @@ +Package.describe({ + name: 'rocketchat:cloud', + version: '0.0.1', + summary: 'Package which interacts with the Rocket.Chat Cloud offerings.', + git: '', +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'rocketchat:lib', + 'templating', + ]); + + api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); +}); diff --git a/packages/rocketchat-cloud/server/functions/connectWorkspace.js b/packages/rocketchat-cloud/server/functions/connectWorkspace.js new file mode 100644 index 000000000000..f34cecd22449 --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/connectWorkspace.js @@ -0,0 +1,70 @@ +import querystring from 'querystring'; +import { HTTP } from 'meteor/http'; + +import { getRedirectUri } from './getRedirectUri'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; + +export function connectWorkspace(token) { + const { registeredWithWizard } = retrieveRegistrationStatus(); + if (!registeredWithWizard) { + return false; + } + + const redirectUri = getRedirectUri(); + + const regInfo = { + email: RocketChat.settings.get('Organization_Email'), + client_name: RocketChat.settings.get('Site_Name'), + redirect_uris: [redirectUri], + }; + + const cloudUrl = RocketChat.settings.get('Cloud_Url'); + let result; + try { + result = HTTP.post(`${ cloudUrl }/api/oauth/clients`, { + headers: { + Authorization: `Bearer ${ token }`, + }, + data: regInfo, + }); + } catch (e) { + return false; + } + + const { data } = result; + + if (!data) { + return false; + } + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Id', data.workspaceId); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Name', data.client_name); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Client_Id', data.client_id); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Client_Secret', data.client_secret); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Client_Secret_Expires_At', data.client_secret_expires_at); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Registration_Client_Uri', data.registration_client_uri); + + // Now that we have the client id and secret, let's get the access token + let authTokenResult; + try { + authTokenResult = HTTP.post(`${ cloudUrl }/api/oauth/token`, { + data: {}, + query: querystring.stringify({ + client_id: data.client_id, + client_secret: data.client_secret, + grant_type: 'client_credentials', + redirect_uri: redirectUri, + }), + }); + } catch (e) { + return false; + } + + const expiresAt = new Date(); + expiresAt.setSeconds(expiresAt.getSeconds() + authTokenResult.data.expires_in); + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Access_Token', authTokenResult.data.access_token); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Access_Token_Expires_At', expiresAt); + + return true; +} diff --git a/packages/rocketchat-cloud/server/functions/finishOAuthAuthorization.js b/packages/rocketchat-cloud/server/functions/finishOAuthAuthorization.js new file mode 100644 index 000000000000..80e2a67e17ea --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/finishOAuthAuthorization.js @@ -0,0 +1,50 @@ +import querystring from 'querystring'; + +import { Meteor } from 'meteor/meteor'; +import { HTTP } from 'meteor/http'; + +import { getRedirectUri } from './getRedirectUri'; + +export function finishOAuthAuthorization(code, state) { + if (RocketChat.settings.get('Cloud_Workspace_Registration_State') !== state) { + throw new Meteor.Error('error-invalid-state', 'Invalid state provided', { method: 'cloud:finishOAuthAuthorization' }); + } + + const cloudUrl = RocketChat.settings.get('Cloud_Url'); + const clientId = RocketChat.settings.get('Cloud_Workspace_Client_Id'); + const clientSecret = RocketChat.settings.get('Cloud_Workspace_Client_Secret'); + + let result; + try { + result = HTTP.post(`${ cloudUrl }/api/oauth/token`, { + data: {}, + query: querystring.stringify({ + client_id: clientId, + client_secret: clientSecret, + grant_type: 'authorization_code', + code, + redirect_uri: getRedirectUri(), + }), + }); + } catch (e) { + return false; + } + + const expiresAt = new Date(); + expiresAt.setSeconds(expiresAt.getSeconds() + result.data.expires_in); + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Account_Associated', true); + RocketChat.models.Users.update({ _id: Meteor.userId() }, { + $set: { + 'services.cloud': { + accessToken: result.data.access_token, + expiresAt, + scope: result.data.scope, + tokenType: result.data.token_type, + refreshToken: result.data.refresh_token, + }, + }, + }); + + return true; +} diff --git a/packages/rocketchat-cloud/server/functions/getOAuthAuthorizationUrl.js b/packages/rocketchat-cloud/server/functions/getOAuthAuthorizationUrl.js new file mode 100644 index 000000000000..b3edc95090f9 --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/getOAuthAuthorizationUrl.js @@ -0,0 +1,15 @@ +import { Random } from 'meteor/random'; + +import { getRedirectUri } from './getRedirectUri'; + +export function getOAuthAuthorizationUrl() { + const state = Random.id(); + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Registration_State', state); + + const cloudUrl = RocketChat.settings.get('Cloud_Url'); + const client_id = RocketChat.settings.get('Cloud_Workspace_Client_Id'); + const redirectUri = getRedirectUri(); + + return `${ cloudUrl }/authorize?response_type=code&client_id=${ client_id }&redirect_uri=${ redirectUri }&scope=offline_access&state=${ state }`; +} diff --git a/packages/rocketchat-cloud/server/functions/getRedirectUri.js b/packages/rocketchat-cloud/server/functions/getRedirectUri.js new file mode 100644 index 000000000000..21a219791c9e --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/getRedirectUri.js @@ -0,0 +1,3 @@ +export function getRedirectUri() { + return `${ RocketChat.settings.get('Site_Url') }/admin/cloud/oauth-callback`.replace(/\/\/admin+/g, '/admin'); +} diff --git a/packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js b/packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js new file mode 100644 index 000000000000..d853ce5b6df3 --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/getWorkspaceAccessTokens.js @@ -0,0 +1,50 @@ +import querystring from 'querystring'; +import { HTTP } from 'meteor/http'; + +import { getRedirectUri } from './getRedirectUri'; + +export function getWorkspaceAccessToken() { + if (!RocketChat.settings.get('Register_Server')) { + return ''; + } + + const client_id = RocketChat.settings.get('Cloud_Workspace_Client_Id'); + if (!client_id) { + return ''; + } + + const expires = RocketChat.models.Settings.findOneById('Cloud_Workspace_Access_Token_Expires_At'); + const now = new Date(); + + if (now < expires.value) { + return RocketChat.settings.get('Cloud_Workspace_Access_Token'); + } + + const cloudUrl = RocketChat.settings.get('Cloud_Url'); + const client_secret = RocketChat.settings.get('Cloud_Workspace_Client_Secret'); + const redirectUri = getRedirectUri(); + + let authTokenResult; + try { + authTokenResult = HTTP.post(`${ cloudUrl }/api/oauth/token`, { + data: {}, + query: querystring.stringify({ + client_id, + client_secret, + grant_type: 'client_credentials', + redirect_uri: redirectUri, + }), + }); + } catch (e) { + return ''; + } + + const expiresAt = new Date(); + expiresAt.setSeconds(expiresAt.getSeconds() + authTokenResult.data.expires_in); + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Access_Token', authTokenResult.data.access_token); + RocketChat.models.Settings.updateValueById('Cloud_Workspace_Access_Token_Expires_At', expiresAt); + + + return authTokenResult.data.access_token; +} diff --git a/packages/rocketchat-cloud/server/functions/getWorkspaceLicense.js b/packages/rocketchat-cloud/server/functions/getWorkspaceLicense.js new file mode 100644 index 000000000000..d9278a65d519 --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/getWorkspaceLicense.js @@ -0,0 +1,34 @@ +import { HTTP } from 'meteor/http'; + +import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; + +export function getWorkspaceLicense() { + const token = getWorkspaceAccessToken(); + + if (!token) { + return { updated: false, license: '' }; + } + + + let licenseResult; + try { + licenseResult = HTTP.get(`${ RocketChat.settings.get('Cloud_Workspace_Registration_Client_Uri') }/license`, { + headers: { + Authorization: `Bearer ${ token }`, + }, + }); + } catch (e) { + return { updated: false, license: '' }; + } + + const remoteLicense = licenseResult.data; + const currentLicense = RocketChat.settings.get('Cloud_Workspace_License'); + + if (remoteLicense.updatedAt <= currentLicense._updatedAt) { + return { updated: false, license: '' }; + } + + RocketChat.models.Settings.updateValueById('Cloud_Workspace_License', remoteLicense.license); + + return { updated: true, license: remoteLicense.license }; +} diff --git a/packages/rocketchat-cloud/server/functions/retrieveRegistrationStatus.js b/packages/rocketchat-cloud/server/functions/retrieveRegistrationStatus.js new file mode 100644 index 000000000000..dd109853234e --- /dev/null +++ b/packages/rocketchat-cloud/server/functions/retrieveRegistrationStatus.js @@ -0,0 +1,18 @@ +export function retrieveRegistrationStatus() { + const info = { + registeredWithWizard: RocketChat.settings.get('Register_Server'), + workspaceConnected: (RocketChat.settings.get('Cloud_Workspace_Client_Id')) ? true : false, + userAssociated: (RocketChat.settings.get('Cloud_Workspace_Account_Associated')) ? true : false, + token: '', + email: '', + }; + + const firstUser = RocketChat.models.Users.getOldest({ emails: 1 }); + info.email = firstUser && firstUser.emails[0].address; + + if (RocketChat.settings.get('Organization_Email')) { + info.email = RocketChat.settings.get('Organization_Email'); + } + + return info; +} diff --git a/packages/rocketchat-cloud/server/index.js b/packages/rocketchat-cloud/server/index.js new file mode 100644 index 000000000000..c64f14941528 --- /dev/null +++ b/packages/rocketchat-cloud/server/index.js @@ -0,0 +1,11 @@ +import './methods'; +import { getWorkspaceAccessToken } from './functions/getWorkspaceAccessTokens'; + +if (RocketChat.models && RocketChat.models.Permissions) { + RocketChat.models.Permissions.createOrUpdate('manage-cloud', ['admin']); +} + +// Ensure the client/workspace access token is valid +getWorkspaceAccessToken(); + +export { getWorkspaceAccessToken }; diff --git a/packages/rocketchat-cloud/server/methods.js b/packages/rocketchat-cloud/server/methods.js new file mode 100644 index 000000000000..480274bd14ab --- /dev/null +++ b/packages/rocketchat-cloud/server/methods.js @@ -0,0 +1,72 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; + +import { retrieveRegistrationStatus } from './functions/retrieveRegistrationStatus'; +import { connectWorkspace } from './functions/connectWorkspace'; +import { getOAuthAuthorizationUrl } from './functions/getOAuthAuthorizationUrl'; +import { finishOAuthAuthorization } from './functions/finishOAuthAuthorization'; + +Meteor.methods({ + 'cloud:checkRegisterStatus'() { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:checkRegisterStatus' }); + } + + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'manage-cloud')) { + throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:checkRegisterStatus' }); + } + + return retrieveRegistrationStatus(); + }, + 'cloud:updateEmail'(email) { + check(email, String); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:updateEmail' }); + } + + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'manage-cloud')) { + throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:updateEmail' }); + } + + RocketChat.models.Settings.updateValueById('Organization_Email', email); + }, + 'cloud:connectWorkspace'(token) { + check(token, String); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:connectServer' }); + } + + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'manage-cloud')) { + throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:connectServer' }); + } + + return connectWorkspace(token); + }, + 'cloud:getOAuthAuthorizationUrl'() { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:connectServer' }); + } + + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'manage-cloud')) { + throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:connectServer' }); + } + + return getOAuthAuthorizationUrl(); + }, + 'cloud:finishOAuthAuthorization'(code, state) { + check(code, String); + check(state, String); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:finishOAuthAuthorization' }); + } + + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'manage-cloud')) { + throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:connectServer' }); + } + + return finishOAuthAuthorization(code, state); + }, +}); diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index 2ef8992bd0ab..5db2b5fc471b 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -347,7 +347,7 @@ class E2E { modal.open({ title: TAPi18n.__('Enter_E2E_password_to_decode_your_key'), type: 'input', - inputType: 'text', + inputType: 'password', html: true, text: `
    ${ TAPi18n.__('E2E_password_request_text') }
    `, showConfirmButton: true, diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 4c12e0c15b40..5b590d9f54de 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -576,6 +576,21 @@ "Closed": "Closed", "Closed_by_visitor": "Closed by visitor", "Closing_chat": "Closing chat", + "Cloud": "Cloud", + "Cloud_connect": "Rocket.Chat Cloud Connect", + "Cloud_what_is_it": "What is this?", + "Cloud_what_is_it_description": "Rocket.Chat Cloud Connect allows you to connect your self-hosted Rocket.Chat Workspace to our Cloud. Doing so enables you to manage your licenses, Billing and Support in Rocket.Chat Cloud.", + "Cloud_workspace_connected_plus_account": "Your workspace is now connected to the Rocket.Chat Cloud and an account is associated.", + "Cloud_workspace_connected_without_account": "Your workspace is now connected to the Rocket.Chat Cloud. If you would like, you can login to the Rocket.Chat Cloud and associate your workspace with your Cloud account.", + "Cloud_login_to_cloud": "Login to Rocket.Chat Cloud", + "Cloud_address_to_send_registration_to": "The address to send your Cloud registration email to.", + "Cloud_update_email": "Update Email", + "Cloud_manually_input_token": "Manually enter the token received from the Cloud Registration Email.", + "Cloud_registration_required": "Registration Required", + "Cloud_registration_required_description": "Looks like during setup you didn't chose to register your workspace.", + "Cloud_registration_requried_link_text": "Click here to register your workspace.", + "Cloud_error_in_authenticating": "Error received while authenticating", + "Cloud_error_code": "Code: ", "Collaborative": "Collaborative", "Collapse_Embedded_Media_By_Default": "Collapse Embedded Media by Default", "color": "Color", @@ -590,6 +605,7 @@ "Computer": "Computer", "Confirm_new_encryption_password": "Confirm new encryption password", "Confirm_password": "Confirm your password", + "Connect": "Connect", "Connection_Closed": "Connection closed", "Connection_Reset": "Connection reset", "Consulting": "Consulting", @@ -2691,6 +2707,7 @@ "to_see_more_details_on_how_to_integrate": "to see more details on how to integrate.", "To_users": "To Users", "Toggle_original_translated": "Toggle original/translated", + "Token": "Token", "Token_Access": "Token Access", "Token_Controlled_Access": "Token Controlled Access", "Token_required": "Token required", @@ -3029,4 +3046,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-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index d229e59d902f..cbd5555bb4e7 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -1153,7 +1153,6 @@ "Facebook_Page": "Página do Facebook", "False": "Não", "Favorite_Rooms": "Ativar Salas Favoritas", - "Favorite": "F", "Favorites": "Favoritos", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Esse recurso depende de \"Enviar histórico de navegação do visitante como uma mensagem\" para ser ativado.", "Features_Enabled": "Funcionalidades habilitadas", @@ -2880,4 +2879,4 @@ "Your_push_was_sent_to_s_devices": "Sua notificação foi enviada para %s dispositivos", "Your_server_link": "O link do seu servidor", "Your_workspace_is_ready": "O seu espaço de trabalho está pronto a usar 🎉" -} \ No newline at end of file +} diff --git a/packages/rocketchat-lib/client/models/Avatars.js b/packages/rocketchat-lib/client/models/Avatars.js deleted file mode 100644 index 455ca64a97d1..000000000000 --- a/packages/rocketchat-lib/client/models/Avatars.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Avatars } from 'meteor/rocketchat:models'; - -RocketChat.models.Avatars = Avatars; diff --git a/packages/rocketchat-lib/client/models/Uploads.js b/packages/rocketchat-lib/client/models/Uploads.js deleted file mode 100644 index 7dc92cf6f67b..000000000000 --- a/packages/rocketchat-lib/client/models/Uploads.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Uploads } from 'meteor/rocketchat:models'; - -RocketChat.models.Uploads = Uploads; diff --git a/packages/rocketchat-lib/client/models/UserDataFiles.js b/packages/rocketchat-lib/client/models/UserDataFiles.js deleted file mode 100644 index b3eff0ac6125..000000000000 --- a/packages/rocketchat-lib/client/models/UserDataFiles.js +++ /dev/null @@ -1,3 +0,0 @@ -import { UserDataFiles } from 'meteor/rocketchat:models'; - -RocketChat.models.UserDataFiles = UserDataFiles; diff --git a/packages/rocketchat-lib/client/models/_Base.js b/packages/rocketchat-lib/client/models/_Base.js deleted file mode 100644 index bfda6cf65cce..000000000000 --- a/packages/rocketchat-lib/client/models/_Base.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Base } from 'meteor/rocketchat:models'; - -RocketChat.models._Base = Base; diff --git a/packages/rocketchat-lib/client/models/index.js b/packages/rocketchat-lib/client/models/index.js new file mode 100644 index 000000000000..d9f46c728520 --- /dev/null +++ b/packages/rocketchat-lib/client/models/index.js @@ -0,0 +1,11 @@ +import { Avatars } from 'meteor/rocketchat:models'; +import { Base as _Base } from 'meteor/rocketchat:models'; +import { Uploads } from 'meteor/rocketchat:models'; +import { UserDataFiles } from 'meteor/rocketchat:models'; + +Object.assign(RocketChat.models, { + _Base, + Avatars, + Uploads, + UserDataFiles, +}); diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 6c0adfafa5ac..d005d99aa64a 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -90,7 +90,7 @@ Package.onUse(function(api) { api.addFiles('lib/getUserPreference.js'); api.addFiles('server/lib/bugsnag.js', 'server'); - api.addFiles('server/lib/metrics.js', 'server'); + api.addFiles('server/lib/metrics_import.js', 'server'); api.addFiles('server/lib/RateLimiter.js', 'server'); @@ -140,17 +140,7 @@ Package.onUse(function(api) { api.addFiles('server/lib/migrations.js', 'server'); // SERVER MODELS - api.addFiles('server/models/_Base.js', 'server'); - api.addFiles('server/models/Avatars.js', 'server'); - api.addFiles('server/models/Messages.js', 'server'); - api.addFiles('server/models/Reports.js', 'server'); - api.addFiles('server/models/Rooms.js', 'server'); - api.addFiles('server/models/Settings.js', 'server'); - api.addFiles('server/models/Subscriptions.js', 'server'); - api.addFiles('server/models/Uploads.js', 'server'); - api.addFiles('server/models/Users.js', 'server'); - api.addFiles('server/models/ExportOperations.js', 'server'); - api.addFiles('server/models/UserDataFiles.js', 'server'); + api.addFiles('server/models/index.js', 'server'); api.addFiles('server/oauth/oauth.js', 'server'); api.addFiles('server/oauth/facebook.js', 'server'); @@ -158,7 +148,7 @@ Package.onUse(function(api) { api.addFiles('server/oauth/google.js', 'server'); api.addFiles('server/oauth/proxy.js', 'server'); - api.addFiles('server/startup/statsTracker.js', 'server'); + api.addFiles('server/startup/statsTracker_import.js', 'server'); api.addFiles('server/startup/robots.js', 'server'); // SERVER PUBLICATIONS @@ -249,9 +239,7 @@ Package.onUse(function(api) { api.addFiles('client/CustomTranslations.js', 'client'); // CLIENT MODELS - api.addFiles('client/models/_Base.js', 'client'); - api.addFiles('client/models/Avatars.js', 'client'); - api.addFiles('client/models/Uploads.js', 'client'); + api.addFiles('client/models/index.js', 'client'); // CLIENT VIEWS api.addFiles('client/views/customFieldsForm.html', 'client'); diff --git a/packages/rocketchat-lib/server/lib/metrics.js b/packages/rocketchat-lib/server/lib/metrics_import.js similarity index 100% rename from packages/rocketchat-lib/server/lib/metrics.js rename to packages/rocketchat-lib/server/lib/metrics_import.js diff --git a/packages/rocketchat-lib/server/models/Avatars.js b/packages/rocketchat-lib/server/models/Avatars.js deleted file mode 100644 index 455ca64a97d1..000000000000 --- a/packages/rocketchat-lib/server/models/Avatars.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Avatars } from 'meteor/rocketchat:models'; - -RocketChat.models.Avatars = Avatars; diff --git a/packages/rocketchat-lib/server/models/ExportOperations.js b/packages/rocketchat-lib/server/models/ExportOperations.js deleted file mode 100644 index 5f2375cc281c..000000000000 --- a/packages/rocketchat-lib/server/models/ExportOperations.js +++ /dev/null @@ -1,3 +0,0 @@ -import { ExportOperations } from 'meteor/rocketchat:models'; - -RocketChat.models.ExportOperations = ExportOperations; diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js deleted file mode 100644 index 8a9de256ce0b..000000000000 --- a/packages/rocketchat-lib/server/models/Messages.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Messages } from 'meteor/rocketchat:models'; - -RocketChat.models.Messages = Messages; diff --git a/packages/rocketchat-lib/server/models/Reports.js b/packages/rocketchat-lib/server/models/Reports.js deleted file mode 100644 index ec1aac6ee255..000000000000 --- a/packages/rocketchat-lib/server/models/Reports.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Reports } from 'meteor/rocketchat:models'; - -RocketChat.models.Reports = Reports; diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js deleted file mode 100644 index d664591ad794..000000000000 --- a/packages/rocketchat-lib/server/models/Rooms.js +++ /dev/null @@ -1,4 +0,0 @@ -import { Rooms } from 'meteor/rocketchat:models'; - -RocketChat.models.Rooms = Rooms; - diff --git a/packages/rocketchat-lib/server/models/Settings.js b/packages/rocketchat-lib/server/models/Settings.js deleted file mode 100644 index 6fc6d0da7df2..000000000000 --- a/packages/rocketchat-lib/server/models/Settings.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Settings } from 'meteor/rocketchat:models'; - -RocketChat.models.Settings = Settings; diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js deleted file mode 100644 index 36931f09b5ca..000000000000 --- a/packages/rocketchat-lib/server/models/Subscriptions.js +++ /dev/null @@ -1,4 +0,0 @@ -import { Subscriptions } from 'meteor/rocketchat:models'; - -RocketChat.models.Subscriptions = Subscriptions; - diff --git a/packages/rocketchat-lib/server/models/Uploads.js b/packages/rocketchat-lib/server/models/Uploads.js deleted file mode 100644 index 7dc92cf6f67b..000000000000 --- a/packages/rocketchat-lib/server/models/Uploads.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Uploads } from 'meteor/rocketchat:models'; - -RocketChat.models.Uploads = Uploads; diff --git a/packages/rocketchat-lib/server/models/UserDataFiles.js b/packages/rocketchat-lib/server/models/UserDataFiles.js deleted file mode 100644 index b3eff0ac6125..000000000000 --- a/packages/rocketchat-lib/server/models/UserDataFiles.js +++ /dev/null @@ -1,3 +0,0 @@ -import { UserDataFiles } from 'meteor/rocketchat:models'; - -RocketChat.models.UserDataFiles = UserDataFiles; diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js deleted file mode 100644 index 8918c19aa575..000000000000 --- a/packages/rocketchat-lib/server/models/Users.js +++ /dev/null @@ -1,4 +0,0 @@ -import { Users } from 'meteor/rocketchat:models'; - -RocketChat.models.Users = Users; - diff --git a/packages/rocketchat-lib/server/models/_Base.js b/packages/rocketchat-lib/server/models/_Base.js deleted file mode 100644 index bfda6cf65cce..000000000000 --- a/packages/rocketchat-lib/server/models/_Base.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Base } from 'meteor/rocketchat:models'; - -RocketChat.models._Base = Base; diff --git a/packages/rocketchat-lib/server/models/index.js b/packages/rocketchat-lib/server/models/index.js new file mode 100644 index 000000000000..567fcf47b642 --- /dev/null +++ b/packages/rocketchat-lib/server/models/index.js @@ -0,0 +1,25 @@ +import { Avatars } from 'meteor/rocketchat:models'; +import { Base as _Base } from 'meteor/rocketchat:models'; +import { ExportOperations } from 'meteor/rocketchat:models'; +import { Messages } from 'meteor/rocketchat:models'; +import { Reports } from 'meteor/rocketchat:models'; +import { Rooms } from 'meteor/rocketchat:models'; +import { Settings } from 'meteor/rocketchat:models'; +import { Subscriptions } from 'meteor/rocketchat:models'; +import { Uploads } from 'meteor/rocketchat:models'; +import { UserDataFiles } from 'meteor/rocketchat:models'; +import { Users } from 'meteor/rocketchat:models'; + +Object.assign(RocketChat.models, { + _Base, + Avatars, + ExportOperations, + Messages, + Reports, + Rooms, + Settings, + Subscriptions, + Uploads, + UserDataFiles, + Users, +}); diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js index 7cb5f032b9dc..464314fbce11 100644 --- a/packages/rocketchat-lib/server/startup/settings.js +++ b/packages/rocketchat-lib/server/startup/settings.js @@ -2529,6 +2529,134 @@ RocketChat.settings.addGroup('Setup_Wizard', function() { this.add('Allow_Marketing_Emails', true, { type: 'boolean', }); + this.add('Register_Server', true, { + type: 'boolean', + }); + this.add('Organization_Email', '', { + type: 'string', + }); + }); + + this.section('Cloud_Info', function() { + this.add('Cloud_Url', 'https://cloud.rocket.chat', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Id', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Name', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Client_Id', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Client_Secret', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Client_Secret_Expires_At', '', { + type: 'int', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Registration_Client_Uri', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_License', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Access_Token', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Access_Token_Expires_At', new Date(), { + type: 'date', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Registration_State', '', { + type: 'string', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); + + this.add('Cloud_Workspace_Account_Associated', false, { + type: 'boolean', + hidden: true, + readonly: true, + enableQuery: { + _id: 'Register_Server', + value: true, + }, + }); }); }); diff --git a/packages/rocketchat-lib/server/startup/statsTracker.js b/packages/rocketchat-lib/server/startup/statsTracker_import.js similarity index 100% rename from packages/rocketchat-lib/server/startup/statsTracker.js rename to packages/rocketchat-lib/server/startup/statsTracker_import.js diff --git a/packages/rocketchat-setup-wizard/client/setupWizard.js b/packages/rocketchat-setup-wizard/client/setupWizard.js index 817868adda84..d79dd8288f50 100644 --- a/packages/rocketchat-setup-wizard/client/setupWizard.js +++ b/packages/rocketchat-setup-wizard/client/setupWizard.js @@ -74,6 +74,14 @@ const persistSettings = (state, callback) => { _id: 'Statistics_reporting', value: state.registerServer, }, + { + _id: 'Apps_Framework_enabled', + value: state.registerServer, + }, + { + _id: 'Register_Server', + value: state.registerServer, + }, { _id: 'Allow_Marketing_Emails', value: state.optIn, diff --git a/packages/rocketchat-statistics/server/functions/get.js b/packages/rocketchat-statistics/server/functions/get.js index cc6a70c2f146..0ecfa629d74f 100644 --- a/packages/rocketchat-statistics/server/functions/get.js +++ b/packages/rocketchat-statistics/server/functions/get.js @@ -32,10 +32,12 @@ RocketChat.statistics.get = function _getStatistics() { } }); - if (statistics.wizard.allowMarketingEmails) { - const firstUser = RocketChat.models.Users.getOldest({ name: 1, emails: 1 }); - statistics.wizard.contactName = firstUser && firstUser.name; - statistics.wizard.contactEmail = firstUser && firstUser.emails[0].address; + const firstUser = RocketChat.models.Users.getOldest({ name: 1, emails: 1 }); + statistics.wizard.contactName = firstUser && firstUser.name; + statistics.wizard.contactEmail = firstUser && firstUser.emails && firstUser.emails[0].address; + + if (RocketChat.settings.get('Organization_Email')) { + statistics.wizard.contactEmail = RocketChat.settings.get('Organization_Email'); } // Version diff --git a/packages/rocketchat-statistics/server/index.js b/packages/rocketchat-statistics/server/index.js index 91b887ae52af..b6c264b84f2b 100644 --- a/packages/rocketchat-statistics/server/index.js +++ b/packages/rocketchat-statistics/server/index.js @@ -1,5 +1,5 @@ import '../lib/rocketchat'; -import './models/Statistics'; +import './models/Statistics_import'; import './functions/get'; import './functions/save'; import './methods/getStatistics'; diff --git a/packages/rocketchat-statistics/server/models/Statistics.js b/packages/rocketchat-statistics/server/models/Statistics_import.js similarity index 100% rename from packages/rocketchat-statistics/server/models/Statistics.js rename to packages/rocketchat-statistics/server/models/Statistics_import.js diff --git a/packages/rocketchat-ui-flextab/client/tabs/userInfo.html b/packages/rocketchat-ui-flextab/client/tabs/userInfo.html index e5a8d827b723..578126cdc76a 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/userInfo.html +++ b/packages/rocketchat-ui-flextab/client/tabs/userInfo.html @@ -27,8 +27,8 @@

    {{_ "User_Info"}}

    {{#if username}}{{/if}} -
    -
    {{userStatus}}
    +
    +
    {{_ userStatus}}
    diff --git a/packages/rocketchat-ui/client/components/header/header.html b/packages/rocketchat-ui/client/components/header/header.html index 354d0d810d0b..39da3aba4b00 100644 --- a/packages/rocketchat-ui/client/components/header/header.html +++ b/packages/rocketchat-ui/client/components/header/header.html @@ -36,8 +36,8 @@ {{#if isDirect}} -
    -
    {{userStatus}}
    +
    +
    {{_ userStatus}}
    {{else}} {{#if roomTopic}}{{{RocketChatMarkdown roomTopic}}}{{/if}} diff --git a/packages/rocketchat-version-check/package.js b/packages/rocketchat-version-check/package.js index a88d0e9e712f..2f76c06b8c3e 100644 --- a/packages/rocketchat-version-check/package.js +++ b/packages/rocketchat-version-check/package.js @@ -11,6 +11,7 @@ Package.onUse(function(api) { 'ecmascript', 'rocketchat:lib', 'rocketchat:logger', + 'rocketchat:cloud', 'littledata:synced-cron', ]); diff --git a/packages/rocketchat-version-check/server/functions/getNewUpdates.js b/packages/rocketchat-version-check/server/functions/getNewUpdates.js index 8426537cbd55..09ff8715e68a 100644 --- a/packages/rocketchat-version-check/server/functions/getNewUpdates.js +++ b/packages/rocketchat-version-check/server/functions/getNewUpdates.js @@ -1,6 +1,7 @@ import os from 'os'; import { HTTP } from 'meteor/http'; import { RocketChat } from 'meteor/rocketchat:lib'; +import { getWorkspaceAccessToken } from 'meteor/rocketchat:cloud'; import { MongoInternals } from 'meteor/mongo'; // import checkUpdate from '../checkUpdate'; @@ -24,8 +25,15 @@ export default () => { deployPlatform: process.env.DEPLOY_PLATFORM || 'selfinstall', }; + const headers = {}; + const token = getWorkspaceAccessToken(); + if (token) { + headers.Authorization = `Bearer ${ token }`; + } + const result = HTTP.get('https://releases.rocket.chat/updates/check', { params: data, + headers, }); return result.data; diff --git a/server/lib/cordova.js b/server/lib/cordova.js index 2eb5a33a0c0a..8dc30c65864e 100644 --- a/server/lib/cordova.js +++ b/server/lib/cordova.js @@ -2,8 +2,10 @@ import { Meteor } from 'meteor/meteor'; import { HTTP } from 'meteor/http'; import { TAPi18n } from 'meteor/tap:i18n'; import { SystemLogger } from 'meteor/rocketchat:logger'; +import { getWorkspaceAccessToken } from 'meteor/rocketchat:cloud'; import { Push } from 'meteor/rocketchat:push'; + Meteor.methods({ // log() { // return console.log(...arguments); @@ -80,8 +82,14 @@ function sendPush(service, token, options, tries = 0) { token, options, }, + headers: {}, }; + const workspaceAccesstoken = getWorkspaceAccessToken(); + if (token) { + data.headers.Authorization = `Bearer ${ workspaceAccesstoken }`; + } + return HTTP.post(`${ RocketChat.settings.get('Push_gateway') }/push/${ service }/send`, data, function(error, response) { if (response && response.statusCode === 406) { console.log('removing push token', token); diff --git a/server/startup/cron.js b/server/startup/cron.js index a4b5130d6455..a5c93b6e1a5a 100644 --- a/server/startup/cron.js +++ b/server/startup/cron.js @@ -1,6 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { HTTP } from 'meteor/http'; import { Logger } from 'meteor/rocketchat:logger'; +import { getWorkspaceAccessToken } from 'meteor/rocketchat:cloud'; import { SyncedCron } from 'meteor/littledata:synced-cron'; const logger = new Logger('SyncedCron'); @@ -19,8 +20,16 @@ function generateStatistics() { if (RocketChat.settings.get('Statistics_reporting')) { try { + const headers = {}; + const token = getWorkspaceAccessToken(); + + if (token) { + headers.Authorization = `Bearer ${ token }`; + } + HTTP.post('https://collector.rocket.chat/', { data: statistics, + headers, }); } catch (error) { /* error*/ diff --git a/server/startup/migrations/v099.js b/server/startup/migrations/v099.js index 001739053fa1..cddb80db5fb0 100644 --- a/server/startup/migrations/v099.js +++ b/server/startup/migrations/v099.js @@ -201,11 +201,15 @@ RocketChat.Migrations.add({ }).then(() => { const avatarsFiles = new Mongo.Collection('avatars.files'); const avatarsChunks = new Mongo.Collection('avatars.chunks'); - avatarsFiles.rawCollection().drop(); - avatarsChunks.rawCollection().drop(); + try { + avatarsFiles.rawCollection().drop(); + avatarsChunks.rawCollection().drop(); + } catch (error) { + console.warn('Migration Error: avatars.files and avatars.chunks collections may not exist!'); + } RocketChat.models.Settings.remove({ _id: 'Accounts_AvatarStoreType' }); RocketChat.models.Settings.remove({ _id: 'Accounts_AvatarStorePath' }); - }); + }).catch((error) => console.error(error)); }, 1000); }); }, diff --git a/server/startup/migrations/v137.js b/server/startup/migrations/v137.js new file mode 100644 index 000000000000..7ebf4c7b7241 --- /dev/null +++ b/server/startup/migrations/v137.js @@ -0,0 +1,10 @@ +RocketChat.Migrations.add({ + version: 137, + up() { + const firstUser = RocketChat.models.Users.getOldest({ emails: 1 }); + const reportStats = RocketChat.settings.get('Statistics_reporting'); + + RocketChat.models.Settings.updateValueById('Organization_Email', firstUser && firstUser.emails && firstUser.emails[0].address); + RocketChat.models.Settings.updateValueById('Register_Server', reportStats); + }, +}); diff --git a/tests/end-to-end/ui/00-login.js b/tests/end-to-end/ui/00-login.js index 3511f01e9d5d..294b8c13d72a 100644 --- a/tests/end-to-end/ui/00-login.js +++ b/tests/end-to-end/ui/00-login.js @@ -141,7 +141,7 @@ describe('[Setup Wizard]', () => { describe('[Render - Final Step]', () => { it('it should render "Go to your workspace button', () => { - setupWizard.goToWorkspace.waitForVisible(15000); + setupWizard.goToWorkspace.waitForVisible(20000); setupWizard.goToWorkspace.isVisible().should.be.true; }); From d4c52bb2bbf0b7f11d172ea6fab226eb4635e80b Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 22:16:24 -0200 Subject: [PATCH 50/59] Move some functions to utils --- .../rocketchat-ui/getAvatarUrlFromUsername.js | 21 +----- packages/rocketchat-utils/client/index.js | 4 ++ .../lib/getAvatarUrlFromUsername.js | 20 ++++++ packages/rocketchat-utils/lib/slashCommand.js | 67 +++++++++++++++++++ packages/rocketchat-utils/server/index.js | 4 ++ 5 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 packages/rocketchat-utils/lib/getAvatarUrlFromUsername.js create mode 100644 packages/rocketchat-utils/lib/slashCommand.js diff --git a/packages/rocketchat-ui/getAvatarUrlFromUsername.js b/packages/rocketchat-ui/getAvatarUrlFromUsername.js index 1f09d9b6e7c9..0ff10ee8518b 100644 --- a/packages/rocketchat-ui/getAvatarUrlFromUsername.js +++ b/packages/rocketchat-ui/getAvatarUrlFromUsername.js @@ -1,20 +1,3 @@ -// TODO: remove global -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; +import { getAvatarUrlFromUsername as _getAvatarUrlFromUsername } from 'meteor/rocketchat:utils'; -getAvatarUrlFromUsername = function(username) { - const key = `avatar_random_${ username }`; - const random = typeof Session !== 'undefined' && typeof Session.keys[key] !== 'undefined' ? Session.keys[key] : 0; - if (username == null) { - return; - } - const cdnPrefix = (RocketChat.settings.get('CDN_PREFIX') || '').trim().replace(/\/$/, ''); - const pathPrefix = (__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '').trim().replace(/\/$/, ''); - let path = pathPrefix; - if (cdnPrefix) { - path = cdnPrefix + pathPrefix; - } else if (Meteor.isCordova) { - path = Meteor.absoluteUrl().replace(/\/$/, ''); - } - return `${ path }/avatar/${ encodeURIComponent(username) }?_dc=${ random }`; -}; +getAvatarUrlFromUsername = _getAvatarUrlFromUsername; diff --git a/packages/rocketchat-utils/client/index.js b/packages/rocketchat-utils/client/index.js index ec51e35371ac..6db21c18dd36 100644 --- a/packages/rocketchat-utils/client/index.js +++ b/packages/rocketchat-utils/client/index.js @@ -8,6 +8,8 @@ import { fileUploadMediaWhiteList, fileUploadIsValidContentType } from '../lib/f import { roomTypes } from './lib/roomTypes'; import { RoomTypeRouteConfig, RoomTypeConfig, RoomSettingsEnum, UiTextContext } from '../lib/RoomTypeConfig'; import { RoomTypesCommon } from '../lib/RoomTypesCommon'; +import { getAvatarUrlFromUsername } from '../lib/getAvatarUrlFromUsername'; +import { slashCommands } from '../lib/slashCommand'; export { t, @@ -26,4 +28,6 @@ export { RoomTypeConfig, RoomSettingsEnum, UiTextContext, + getAvatarUrlFromUsername, + slashCommands, }; diff --git a/packages/rocketchat-utils/lib/getAvatarUrlFromUsername.js b/packages/rocketchat-utils/lib/getAvatarUrlFromUsername.js new file mode 100644 index 000000000000..ff81d658065b --- /dev/null +++ b/packages/rocketchat-utils/lib/getAvatarUrlFromUsername.js @@ -0,0 +1,20 @@ +import { Meteor } from 'meteor/meteor'; +import { Session } from 'meteor/session'; +import { settings } from 'meteor/rocketchat:settings'; + +export const getAvatarUrlFromUsername = function(username) { + const key = `avatar_random_${ username }`; + const random = typeof Session !== 'undefined' && typeof Session.keys[key] !== 'undefined' ? Session.keys[key] : 0; + if (username == null) { + return; + } + const cdnPrefix = (settings.get('CDN_PREFIX') || '').trim().replace(/\/$/, ''); + const pathPrefix = (__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '').trim().replace(/\/$/, ''); + let path = pathPrefix; + if (cdnPrefix) { + path = cdnPrefix + pathPrefix; + } else if (Meteor.isCordova) { + path = Meteor.absoluteUrl().replace(/\/$/, ''); + } + return `${ path }/avatar/${ encodeURIComponent(username) }?_dc=${ random }`; +}; diff --git a/packages/rocketchat-utils/lib/slashCommand.js b/packages/rocketchat-utils/lib/slashCommand.js new file mode 100644 index 000000000000..4ef8f92bcbe5 --- /dev/null +++ b/packages/rocketchat-utils/lib/slashCommand.js @@ -0,0 +1,67 @@ +import { Meteor } from 'meteor/meteor'; + +export const slashCommands = { + commands: {}, +}; + +slashCommands.add = function _addingSlashCommand(command, callback, options = {}, result, providesPreview = false, previewer, previewCallback) { + slashCommands.commands[command] = { + command, + callback, + params: options.params, + description: options.description, + permission: options.permission, + clientOnly: options.clientOnly || false, + result, + providesPreview, + previewer, + previewCallback, + }; +}; + +slashCommands.run = function _runningSlashCommand(command, params, message) { + if (slashCommands.commands[command] && typeof slashCommands.commands[command].callback === 'function') { + if (!message || !message.rid) { + throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); + } + + return slashCommands.commands[command].callback(command, params, message); + } +}; + +slashCommands.getPreviews = function _gettingSlashCommandPreviews(command, params, message) { + if (slashCommands.commands[command] && typeof slashCommands.commands[command].previewer === 'function') { + if (!message || !message.rid) { + throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); + } + + // { i18nTitle, items: [{ id, type, value }] } + const previewInfo = slashCommands.commands[command].previewer(command, params, message); + + if (typeof previewInfo !== 'object' || !Array.isArray(previewInfo.items) || previewInfo.items.length === 0) { + return; + } + + // A limit of ten results, to save time and bandwidth + if (previewInfo.items.length >= 10) { + previewInfo.items = previewInfo.items.slice(0, 10); + } + + return previewInfo; + } +}; + +slashCommands.executePreview = function _executeSlashCommandPreview(command, params, message, preview) { + if (slashCommands.commands[command] && typeof slashCommands.commands[command].previewCallback === 'function') { + if (!message || !message.rid) { + throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); + } + + // { id, type, value } + if (!preview.id || !preview.type || !preview.value) { + throw new Meteor.Error('error-invalid-preview', 'Preview Item must have an id, type, and value.'); + } + + return slashCommands.commands[command].previewCallback(command, params, message, preview); + } +}; diff --git a/packages/rocketchat-utils/server/index.js b/packages/rocketchat-utils/server/index.js index 43275f627d92..d205242eb5a2 100644 --- a/packages/rocketchat-utils/server/index.js +++ b/packages/rocketchat-utils/server/index.js @@ -7,6 +7,8 @@ import { roomTypes } from './lib/roomTypes'; import { RoomTypeRouteConfig, RoomTypeConfig, RoomSettingsEnum, UiTextContext } from '../lib/RoomTypeConfig'; import { RoomTypesCommon } from '../lib/RoomTypesCommon'; import { isDocker } from './functions/isDocker'; +import { getAvatarUrlFromUsername } from '../lib/getAvatarUrlFromUsername'; +import { slashCommands } from '../lib/slashCommand'; export { t, @@ -23,4 +25,6 @@ export { RoomSettingsEnum, UiTextContext, isDocker, + getAvatarUrlFromUsername, + slashCommands, }; From cee8b745ba8e23c34333e2e85b15104bcd9eef7e Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 22:36:08 -0200 Subject: [PATCH 51/59] Fix lint --- packages/rocketchat-emoji/client/emojiPicker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rocketchat-emoji/client/emojiPicker.js b/packages/rocketchat-emoji/client/emojiPicker.js index 73eadb48b957..41a3c029ba44 100644 --- a/packages/rocketchat-emoji/client/emojiPicker.js +++ b/packages/rocketchat-emoji/client/emojiPicker.js @@ -216,9 +216,9 @@ Template.emojiPicker.events({ for (const emojiPackage in emoji.packages) { if (emoji.packages.hasOwnProperty(emojiPackage)) { if (emoji.packages[emojiPackage].hasOwnProperty('toneList')) { - for (const emoji in emoji.packages[emojiPackage].toneList) { - if (emoji.packages[emojiPackage].toneList.hasOwnProperty(emoji)) { - $(`.emoji-${ emoji }`).html(emoji.packages[emojiPackage].render(`:${ emoji }${ newTone }:`)); + for (const _emoji in emoji.packages[emojiPackage].toneList) { + if (emoji.packages[emojiPackage].toneList.hasOwnProperty(_emoji)) { + $(`.emoji-${ _emoji }`).html(emoji.packages[emojiPackage].render(`:${ _emoji }${ newTone }:`)); } } } From 31e876f15038094d64de6c685fc05e1ca84daeb3 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Thu, 10 Jan 2019 22:46:49 -0200 Subject: [PATCH 52/59] Move some ui functions to ui-utils --- .../client/lib/RocketChatTabBar.js | 76 +--- packages/rocketchat-lib/client/lib/TabBar.js | 84 +---- packages/rocketchat-lib/lib/MessageTypes.js | 21 +- packages/rocketchat-lib/lib/slashCommand.js | 67 +--- packages/rocketchat-ui-utils/client/index.js | 69 ++-- .../client/lib/RocketChatTabBar.js | 76 ++++ .../client/lib/RoomManager.js | 3 +- .../rocketchat-ui-utils/client/lib/TabBar.js | 83 +++++ .../client/lib}/alerts.html | 0 .../rocketchat-ui-utils/client/lib/alerts.js | 73 ++++ .../rocketchat-ui-utils/client/lib/avatar.js | 39 +++ .../client/lib/openedRoom.js | 1 + .../client/lib}/popout.html | 0 .../rocketchat-ui-utils/client/lib/popout.js | 211 ++++++++++++ .../lib/MessageProperties.js | 15 + .../rocketchat-ui-utils/lib/MessageTypes.js | 19 + packages/rocketchat-ui-utils/package.js | 2 + packages/rocketchat-ui-utils/server/index.js | 2 + packages/rocketchat-ui/client/lib/avatar.js | 38 +- .../rocketchat-ui/client/views/app/alerts.js | 73 +--- .../rocketchat-ui/client/views/app/popout.js | 210 +---------- packages/rocketchat-ui/package.js | 2 - tests/end-to-end/ui/00-login.js | 326 +++++++++--------- 23 files changed, 727 insertions(+), 763 deletions(-) create mode 100644 packages/rocketchat-ui-utils/client/lib/RocketChatTabBar.js create mode 100644 packages/rocketchat-ui-utils/client/lib/TabBar.js rename packages/{rocketchat-ui/client/views/app => rocketchat-ui-utils/client/lib}/alerts.html (100%) create mode 100644 packages/rocketchat-ui-utils/client/lib/alerts.js create mode 100644 packages/rocketchat-ui-utils/client/lib/avatar.js create mode 100644 packages/rocketchat-ui-utils/client/lib/openedRoom.js rename packages/{rocketchat-ui/client/views/app => rocketchat-ui-utils/client/lib}/popout.html (100%) create mode 100644 packages/rocketchat-ui-utils/client/lib/popout.js create mode 100644 packages/rocketchat-ui-utils/lib/MessageProperties.js create mode 100644 packages/rocketchat-ui-utils/lib/MessageTypes.js create mode 100644 packages/rocketchat-ui-utils/server/index.js diff --git a/packages/rocketchat-lib/client/lib/RocketChatTabBar.js b/packages/rocketchat-lib/client/lib/RocketChatTabBar.js index 9f6301d955e1..5956a4199343 100644 --- a/packages/rocketchat-lib/client/lib/RocketChatTabBar.js +++ b/packages/rocketchat-lib/client/lib/RocketChatTabBar.js @@ -1,75 +1 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; - -export class RocketChatTabBar { - constructor() { - this.template = new ReactiveVar(); - this.id = new ReactiveVar(); - this.group = new ReactiveVar(); - this.state = new ReactiveVar(); - this.data = new ReactiveVar(); - } - - getTemplate() { - return this.template.get(); - } - - getId() { - return this.id.get(); - } - - setTemplate(template) { - this.template.set(template); - } - - currentGroup() { - return this.group.get(); - } - - showGroup(group) { - this.group.set(group); - } - - setData(d) { - this.data.set(d); - } - - getData() { - return this.data.get(); - } - - getButtons() { - return RocketChat.TabBar.getButtons(); - } - - getState() { - return this.state.get(); - } - - open(button) { - this.state.set('opened'); - Tracker.afterFlush(() => { - $('.contextual-bar__container').scrollTop(0).find('input[type=text]:first').focus(); - }); - - if (!button) { - return; - } - if (typeof button !== 'object' || !button.id) { - button = RocketChat.TabBar.getButton(button); - } - $('.flex-tab, .contextual-bar').css('width', button.width ? `${ button.width }px` : ''); - this.template.set(button.template); - this.id.set(button.id); - return button; - } - - close() { - this.state.set(''); - - $('.flex-tab, .contextual-bar').css('width', ''); - - this.template.set(); - this.id.set(); - } -} +export { RocketChatTabBar } from 'meteor/rocketchat:ui-utils'; diff --git a/packages/rocketchat-lib/client/lib/TabBar.js b/packages/rocketchat-lib/client/lib/TabBar.js index 8e7db8dff6e9..6bbc9d5f09d0 100644 --- a/packages/rocketchat-lib/client/lib/TabBar.js +++ b/packages/rocketchat-lib/client/lib/TabBar.js @@ -1,83 +1,3 @@ -import _ from 'underscore'; -import { ReactiveVar } from 'meteor/reactive-var'; +import { TabBar } from 'meteor/rocketchat:ui-utils'; -RocketChat.TabBar = new (class TabBar { - get size() { - return this._size.get(); - } - set size(s) { - this._size.set(s); - } - constructor() { - this.buttons = new ReactiveVar({}); - this._size = new ReactiveVar(4); - this.extraGroups = {}; - } - - show() { - $('.flex-tab-bar').show(); - } - - hide() { - $('.flex-tab-bar').hide(); - } - - addButton(config) { - if (!config || !config.id) { - return false; - } - - const btns = this.buttons.curValue; - btns[config.id] = config; - - if (this.extraGroups[config.id]) { - btns[config.id].groups = _.union((btns[config.id].groups || []), this.extraGroups[config.id]); - } - - this.buttons.set(btns); - } - - removeButton(id) { - const btns = this.buttons.curValue; - delete btns[id]; - this.buttons.set(btns); - } - - updateButton(id, config) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id] = _.extend(btns[id], config); - this.buttons.set(btns); - } - } - - getButtons() { - const buttons = _.toArray(this.buttons.get()).filter((button) => !button.condition || button.condition()); - - return _.sortBy(buttons, 'order'); - } - - getButton(id) { - return _.findWhere(this.buttons.get(), { id }); - } - - addGroup(id, groups) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id].groups = _.union((btns[id].groups || []), groups); - this.buttons.set(btns); - } else { - this.extraGroups[id] = _.union((this.extraGroups[id] || []), groups); - } - } - - removeGroup(id, groups) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id].groups = _.difference((btns[id].groups || []), groups); - this.buttons.set(btns); - } else { - this.extraGroups[id] = _.difference((this.extraGroups[id] || []), groups); - } - } -}); +RocketChat.TabBar = TabBar; diff --git a/packages/rocketchat-lib/lib/MessageTypes.js b/packages/rocketchat-lib/lib/MessageTypes.js index 14902dc6b720..6e773f5d54ab 100644 --- a/packages/rocketchat-lib/lib/MessageTypes.js +++ b/packages/rocketchat-lib/lib/MessageTypes.js @@ -1,24 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { MessageTypes } from 'meteor/rocketchat:ui-utils'; -RocketChat.MessageTypes = new class { - constructor() { - this.types = {}; - } - - registerType(options) { - return this.types[options.id] = options; - } - - getType(message) { - return this.types[message && message.t]; - } - - isSystemMessage(message) { - const type = this.types[message && message.t]; - return type && type.system; - } - -}; +RocketChat.MessageTypes = MessageTypes; Meteor.startup(function() { RocketChat.MessageTypes.registerType({ diff --git a/packages/rocketchat-lib/lib/slashCommand.js b/packages/rocketchat-lib/lib/slashCommand.js index c502ccc65d97..3bab665a07fd 100644 --- a/packages/rocketchat-lib/lib/slashCommand.js +++ b/packages/rocketchat-lib/lib/slashCommand.js @@ -1,70 +1,7 @@ import { Meteor } from 'meteor/meteor'; +import { slashCommands } from 'meteor/rocketchat:utils'; -RocketChat.slashCommands = { - commands: {}, -}; - -RocketChat.slashCommands.add = function _addingSlashCommand(command, callback, options = {}, result, providesPreview = false, previewer, previewCallback) { - RocketChat.slashCommands.commands[command] = { - command, - callback, - params: options.params, - description: options.description, - permission: options.permission, - clientOnly: options.clientOnly || false, - result, - providesPreview, - previewer, - previewCallback, - }; -}; - -RocketChat.slashCommands.run = function _runningSlashCommand(command, params, message) { - if (RocketChat.slashCommands.commands[command] && typeof RocketChat.slashCommands.commands[command].callback === 'function') { - if (!message || !message.rid) { - throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); - } - - return RocketChat.slashCommands.commands[command].callback(command, params, message); - } -}; - -RocketChat.slashCommands.getPreviews = function _gettingSlashCommandPreviews(command, params, message) { - if (RocketChat.slashCommands.commands[command] && typeof RocketChat.slashCommands.commands[command].previewer === 'function') { - if (!message || !message.rid) { - throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); - } - - // { i18nTitle, items: [{ id, type, value }] } - const previewInfo = RocketChat.slashCommands.commands[command].previewer(command, params, message); - - if (typeof previewInfo !== 'object' || !Array.isArray(previewInfo.items) || previewInfo.items.length === 0) { - return; - } - - // A limit of ten results, to save time and bandwidth - if (previewInfo.items.length >= 10) { - previewInfo.items = previewInfo.items.slice(0, 10); - } - - return previewInfo; - } -}; - -RocketChat.slashCommands.executePreview = function _executeSlashCommandPreview(command, params, message, preview) { - if (RocketChat.slashCommands.commands[command] && typeof RocketChat.slashCommands.commands[command].previewCallback === 'function') { - if (!message || !message.rid) { - throw new Meteor.Error('invalid-command-usage', 'Executing a command requires at least a message with a room id.'); - } - - // { id, type, value } - if (!preview.id || !preview.type || !preview.value) { - throw new Meteor.Error('error-invalid-preview', 'Preview Item must have an id, type, and value.'); - } - - return RocketChat.slashCommands.commands[command].previewCallback(command, params, message, preview); - } -}; +RocketChat.slashCommands = slashCommands; Meteor.methods({ slashCommand(command) { diff --git a/packages/rocketchat-ui-utils/client/index.js b/packages/rocketchat-ui-utils/client/index.js index aefac9a85055..bfe0510a4c5c 100644 --- a/packages/rocketchat-ui-utils/client/index.js +++ b/packages/rocketchat-ui-utils/client/index.js @@ -1,43 +1,26 @@ -import { AdminBox } from './lib/AdminBox'; -import { modal } from './lib/modal'; -import { SideNav } from './lib/SideNav'; -import { AccountBox } from './lib/AccountBox'; -import { menu } from './lib/menu'; -import { call } from './lib/callMethod'; -import { erase, hide, leave } from './lib/ChannelActions'; -import { MessageAction } from './lib/MessageAction'; -import { messageBox } from './lib/messageBox'; -import { popover } from './lib/popover'; -import { readMessage } from './lib/readMessages'; -import { RoomManager } from './lib/RoomManager'; -import { upsertMessage, RoomHistoryManager } from './lib/RoomHistoryManager'; -import { mainReady } from './lib/mainReady'; -import { renderMessageBody } from './lib/renderMessageBody'; -import { Layout } from './lib/Layout'; -import { IframeLogin, iframeLogin } from './lib/IframeLogin'; -import { fireGlobalEvent } from './lib/fireGlobalEvent'; - -export { - AdminBox, - modal, - SideNav, - AccountBox, - menu, - call, - erase, - hide, - leave, - MessageAction, - messageBox, - popover, - readMessage, - RoomManager, - RoomHistoryManager, - mainReady, - renderMessageBody, - upsertMessage, - Layout, - IframeLogin, - iframeLogin, - fireGlobalEvent, -}; +export { AdminBox } from './lib/AdminBox'; +export { modal } from './lib/modal'; +export { SideNav } from './lib/SideNav'; +export { AccountBox } from './lib/AccountBox'; +export { menu } from './lib/menu'; +export { call } from './lib/callMethod'; +export { erase, hide, leave } from './lib/ChannelActions'; +export { MessageAction } from './lib/MessageAction'; +export { messageBox } from './lib/messageBox'; +export { popover } from './lib/popover'; +export { readMessage } from './lib/readMessages'; +export { RoomManager } from './lib/RoomManager'; +export { upsertMessage, RoomHistoryManager } from './lib/RoomHistoryManager'; +export { mainReady } from './lib/mainReady'; +export { renderMessageBody } from './lib/renderMessageBody'; +export { Layout } from './lib/Layout'; +export { IframeLogin, iframeLogin } from './lib/IframeLogin'; +export { fireGlobalEvent } from './lib/fireGlobalEvent'; +export { getAvatarAsPng, updateAvatarOfUsername } from './lib/avatar'; +export { TabBar } from './lib/TabBar'; +export { RocketChatTabBar } from './lib/RocketChatTabBar'; +export { openedRoom } from './lib/openedRoom'; +export { popout } from './lib/popout'; +export { messageProperties } from '../lib/MessageProperties'; +export { MessageTypes } from '../lib/MessageTypes'; +export { alerts } from './lib/alerts'; diff --git a/packages/rocketchat-ui-utils/client/lib/RocketChatTabBar.js b/packages/rocketchat-ui-utils/client/lib/RocketChatTabBar.js new file mode 100644 index 000000000000..d22b7c5e32a7 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/RocketChatTabBar.js @@ -0,0 +1,76 @@ +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { TabBar } from './TabBar'; + +export class RocketChatTabBar { + constructor() { + this.template = new ReactiveVar(); + this.id = new ReactiveVar(); + this.group = new ReactiveVar(); + this.state = new ReactiveVar(); + this.data = new ReactiveVar(); + } + + getTemplate() { + return this.template.get(); + } + + getId() { + return this.id.get(); + } + + setTemplate(template) { + this.template.set(template); + } + + currentGroup() { + return this.group.get(); + } + + showGroup(group) { + this.group.set(group); + } + + setData(d) { + this.data.set(d); + } + + getData() { + return this.data.get(); + } + + getButtons() { + return TabBar.getButtons(); + } + + getState() { + return this.state.get(); + } + + open(button) { + this.state.set('opened'); + Tracker.afterFlush(() => { + $('.contextual-bar__container').scrollTop(0).find('input[type=text]:first').focus(); + }); + + if (!button) { + return; + } + if (typeof button !== 'object' || !button.id) { + button = TabBar.getButton(button); + } + $('.flex-tab, .contextual-bar').css('width', button.width ? `${ button.width }px` : ''); + this.template.set(button.template); + this.id.set(button.id); + return button; + } + + close() { + this.state.set(''); + + $('.flex-tab, .contextual-bar').css('width', ''); + + this.template.set(); + this.id.set(); + } +} diff --git a/packages/rocketchat-ui-utils/client/lib/RoomManager.js b/packages/rocketchat-ui-utils/client/lib/RoomManager.js index 6c4bad817df8..daae1b96981b 100644 --- a/packages/rocketchat-ui-utils/client/lib/RoomManager.js +++ b/packages/rocketchat-ui-utils/client/lib/RoomManager.js @@ -5,6 +5,7 @@ import { Blaze } from 'meteor/blaze'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; import { roomTypes as _roomTypes } from 'meteor/rocketchat:utils'; +import { fireGlobalEvent } from 'meteor/rocketchat:ui-utils'; import { promises } from 'meteor/rocketchat:promises'; import { callbacks } from 'meteor/rocketchat:callbacks'; import { Notifications } from 'meteor/rocketchat:notifications'; @@ -77,7 +78,7 @@ export const RoomManager = new function() { callbacks.run('streamMessage', msg); - return window.fireGlobalEvent('new-message', msg); + return fireGlobalEvent('new-message', msg); } }) ); diff --git a/packages/rocketchat-ui-utils/client/lib/TabBar.js b/packages/rocketchat-ui-utils/client/lib/TabBar.js new file mode 100644 index 000000000000..9cf8908034e4 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/TabBar.js @@ -0,0 +1,83 @@ +import _ from 'underscore'; +import { ReactiveVar } from 'meteor/reactive-var'; + +export const TabBar = new (class TabBar { + get size() { + return this._size.get(); + } + set size(s) { + this._size.set(s); + } + constructor() { + this.buttons = new ReactiveVar({}); + this._size = new ReactiveVar(4); + this.extraGroups = {}; + } + + show() { + $('.flex-tab-bar').show(); + } + + hide() { + $('.flex-tab-bar').hide(); + } + + addButton(config) { + if (!config || !config.id) { + return false; + } + + const btns = this.buttons.curValue; + btns[config.id] = config; + + if (this.extraGroups[config.id]) { + btns[config.id].groups = _.union((btns[config.id].groups || []), this.extraGroups[config.id]); + } + + this.buttons.set(btns); + } + + removeButton(id) { + const btns = this.buttons.curValue; + delete btns[id]; + this.buttons.set(btns); + } + + updateButton(id, config) { + const btns = this.buttons.curValue; + if (btns[id]) { + btns[id] = _.extend(btns[id], config); + this.buttons.set(btns); + } + } + + getButtons() { + const buttons = _.toArray(this.buttons.get()).filter((button) => !button.condition || button.condition()); + + return _.sortBy(buttons, 'order'); + } + + getButton(id) { + return _.findWhere(this.buttons.get(), { id }); + } + + addGroup(id, groups) { + const btns = this.buttons.curValue; + if (btns[id]) { + btns[id].groups = _.union((btns[id].groups || []), groups); + this.buttons.set(btns); + } else { + this.extraGroups[id] = _.union((this.extraGroups[id] || []), groups); + } + } + + removeGroup(id, groups) { + const btns = this.buttons.curValue; + if (btns[id]) { + btns[id].groups = _.difference((btns[id].groups || []), groups); + this.buttons.set(btns); + } else { + this.extraGroups[id] = _.difference((this.extraGroups[id] || []), groups); + } + } +}); diff --git a/packages/rocketchat-ui/client/views/app/alerts.html b/packages/rocketchat-ui-utils/client/lib/alerts.html similarity index 100% rename from packages/rocketchat-ui/client/views/app/alerts.html rename to packages/rocketchat-ui-utils/client/lib/alerts.html diff --git a/packages/rocketchat-ui-utils/client/lib/alerts.js b/packages/rocketchat-ui-utils/client/lib/alerts.js new file mode 100644 index 000000000000..fb45088f0e70 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/alerts.js @@ -0,0 +1,73 @@ +import './alerts.html'; +import { Blaze } from 'meteor/blaze'; +import { Template } from 'meteor/templating'; + +export const alerts = { + renderedAlert: null, + open(config) { + this.close(false); + + config.closable = typeof(config.closable) === typeof(true) ? config.closable : true; + + if (config.timer) { + this.timer = setTimeout(() => this.close(), config.timer); + } + + this.renderedAlert = Blaze.renderWithData(Template.alerts, config, document.body, document.body.querySelector('#alert-anchor')); + }, + close(dismiss = true) { + if (this.timer) { + clearTimeout(this.timer); + delete this.timer; + } + if (!this.renderedAlert) { + return false; + } + + Blaze.remove(this.renderedAlert); + + const { activeElement } = this.renderedAlert.dataVar.curValue; + if (activeElement) { + $(activeElement).removeClass('active'); + } + + dismiss && this.renderedAlert.dataVar.curValue.onClose && this.renderedAlert.dataVar.curValue.onClose(); + }, +}; + +Template.alerts.helpers({ + hasAction() { + return Template.instance().data.action ? 'rc-alerts--has-action' : ''; + }, + modifiers() { + return (Template.instance().data.modifiers || []).map((mod) => `rc-alerts--${ mod }`).join(' '); + }, +}); + +Template.alerts.onRendered(function() { + if (this.data.onRendered) { + this.data.onRendered(); + } +}); + +Template.alerts.onDestroyed(function() { + if (this.data.onDestroyed) { + this.data.onDestroyed(); + } +}); + +Template.alerts.events({ + 'click .js-action'(e, instance) { + if (!this.action) { + return; + } + this.action.call(this, e, instance.data.data); + }, + 'click .js-close'() { + alerts.close(); + }, +}); + +Template.alerts.helpers({ + isSafariIos: /iP(ad|hone|od).+Version\/[\d\.]+.*Safari/i.test(navigator.userAgent), +}); diff --git a/packages/rocketchat-ui-utils/client/lib/avatar.js b/packages/rocketchat-ui-utils/client/lib/avatar.js new file mode 100644 index 000000000000..ec761e267b71 --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/avatar.js @@ -0,0 +1,39 @@ +import { Blaze } from 'meteor/blaze'; +import { Session } from 'meteor/session'; +import { getAvatarUrlFromUsername } from 'meteor/rocketchat:utils'; +import { RoomManager } from './RoomManager'; + +Blaze.registerHelper('avatarUrlFromUsername', getAvatarUrlFromUsername); + +export const getAvatarAsPng = function(username, cb) { + const image = new Image; + image.src = getAvatarUrlFromUsername(username); + image.onload = function() { + + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + try { + return cb(canvas.toDataURL('image/png')); + } catch (e) { + return cb(''); + } + }; + return image.onerror = function() { + return cb(''); + }; +}; + +export const updateAvatarOfUsername = function(username) { + const key = `avatar_random_${ username }`; + Session.set(key, Math.round(Math.random() * 1000)); + + Object.keys(RoomManager.openedRooms).forEach((key) => { + const room = RoomManager.openedRooms[key]; + const url = getAvatarUrlFromUsername(username); + $(room.dom).find(`.message[data-username='${ username }'] .avatar-image`).css('background-image', `url(${ url })`); + }); + return true; +}; diff --git a/packages/rocketchat-ui-utils/client/lib/openedRoom.js b/packages/rocketchat-ui-utils/client/lib/openedRoom.js new file mode 100644 index 000000000000..469e8f59523f --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/openedRoom.js @@ -0,0 +1 @@ +export let openedRoom; //eslint-disable-line diff --git a/packages/rocketchat-ui/client/views/app/popout.html b/packages/rocketchat-ui-utils/client/lib/popout.html similarity index 100% rename from packages/rocketchat-ui/client/views/app/popout.html rename to packages/rocketchat-ui-utils/client/lib/popout.html diff --git a/packages/rocketchat-ui-utils/client/lib/popout.js b/packages/rocketchat-ui-utils/client/lib/popout.js new file mode 100644 index 000000000000..019f43baebee --- /dev/null +++ b/packages/rocketchat-ui-utils/client/lib/popout.js @@ -0,0 +1,211 @@ +import './popout.html'; +import { Blaze } from 'meteor/blaze'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import { callbacks } from 'meteor/rocketchat:callbacks'; + +export const popout = { + context: null, + isAudioOnly: false, + showVideoControls: true, + showStreamControls: false, + x: 0, + y: 0, + open(config = {}, fn) { + this.close(); + this.context = Blaze.renderWithData(Template.popout, config, document.body); + this.fn = fn; + this.config = config; + this.onCloseCallback = config.onCloseCallback || null; + this.timer = null; + if (config.timer) { + this.timer = setTimeout(() => this.close(), config.timer); + } + if (config.data) { + this.isAudioOnly = config.data.isAudioOnly; + this.showVideoControls = config.data.showVideoControls; + this.showStreamControls = config.data.showStreamControls; + } + }, + close() { + if (this.context) { + Blaze.remove(this.context); + } + this.context = null; + this.fn = null; + if (this.timer) { + clearTimeout(this.timer); + } + if (typeof(this.onCloseCallback) === 'function') { + this.onCloseCallback(); + } + }, + dragstart(event) { + if (!event.target.classList.contains('dropzone-overlay')) { + const popoutElement = document.querySelector('.rc-popout-wrapper'); + setTimeout(function() { + popoutElement.style.display = 'none'; + }, 0); + } + }, + dragover(event) { + const e = event.originalEvent || event; + e.dataTransfer.dropEffect = 'move'; + e.preventDefault(); + }, + dragend(event) { + if (!event.target.classList.contains('dropzone-overlay')) { + const popoutElement = document.querySelector('.rc-popout-wrapper'); + popoutElement.style.display = 'initial'; + } + }, + drop(event) { + const e = event.originalEvent || event; + e.preventDefault(); + // do not mess with the position if we are dropping files in the dropzone + if (!event.target.classList.contains('dropzone-overlay')) { + const popoutElement = document.querySelector('.rc-popout-wrapper'); + const positionTop = e.clientY - popout.y; + const positionLeft = e.clientX - popout.x; + popoutElement.style.left = `${ positionLeft >= 0 ? positionLeft : 0 }px`; + popoutElement.style.top = `${ positionTop >= 0 ? positionTop : 0 }px`; + } + }, +}; + +Template.popout.helpers({ + state() { + return Template.instance().isMinimized.get() ? 'closed' : 'open'; + }, + isAudioOnly() { + return Template.instance().isAudioOnly.get(); + }, + isMuted() { + return Template.instance().isMuted.get(); + }, + isPlaying() { + return Template.instance().isPlaying.get(); + }, + showVideoControls() { + return Template.instance().showVideoControls.get(); + }, + showStreamControls() { + return Template.instance().showStreamControls.get(); + }, + getStreamStatus() { + return Template.instance().streamStatus.get(); + }, +}); + +Template.popout.onRendered(function() { + Template.instance().isMinimized.set(popout.isAudioOnly); + Template.instance().isAudioOnly.set(popout.isAudioOnly); + Template.instance().showVideoControls.set(popout.showVideoControls); + Template.instance().showStreamControls.set(popout.showStreamControls); + + + if (this.data.onRendered) { + this.data.onRendered(); + } +}); +Template.popout.onCreated(function() { + this.isMinimized = new ReactiveVar(popout.isAudioOnly); + this.isAudioOnly = new ReactiveVar(popout.isAudioOnly); + this.canOpenExternal = new ReactiveVar(popout.canOpenExternal); + this.showVideoControls = new ReactiveVar(popout.showVideoControls); + this.showStreamControls = new ReactiveVar(popout.showStreamControls); + + this.isMuted = new ReactiveVar(false); + this.isPlaying = new ReactiveVar(true); + this.streamStatus = new ReactiveVar('preparing'); + document.body.addEventListener('dragstart', popout.dragstart, true); + document.body.addEventListener('dragover', popout.dragover, true); + document.body.addEventListener('dragend', popout.dragend, true); + document.body.addEventListener('drop', popout.drop, true); +}); + +Template.popout.onDestroyed(function() { + popout.context = null; + document.body.removeEventListener('dragstart', popout.dragstart, true); + document.body.removeEventListener('dragover', popout.dragover, true); + document.body.removeEventListener('dragend', popout.dragend, true); + document.body.removeEventListener('drop', popout.drop, true); +}); + +Template.popout.events({ + 'click .js-action'(e, instance) { + !this.action || this.action.call(instance.data.data, e, instance); + e.stopPropagation(); + popout.close(); + }, + 'click .js-close'(e) { + e.stopPropagation(); + popout.close(); + }, + 'click .js-minimize'(e, i) { + e.stopPropagation(); + if (i.isMinimized.get()) { + i.isMinimized.set(false); + window.liveStreamPlayer.setSize(380, 214); + } else { + i.isMinimized.set(true); + window.liveStreamPlayer.setSize(0, 0); + } + }, + 'dragstart .rc-popout-wrapper'(event) { + const e = event.originalEvent || event; + const url = (this.data && this.data.streamingSource) || '.rc-popout-wrapper'; + popout.x = e.offsetX; + popout.y = e.offsetY; + e.dataTransfer.setData('application/x-moz-node', e.currentTarget); + e.dataTransfer.setData('text/plain', url); + e.dataTransfer.effectAllowed = 'move'; + }, + 'dragend .rc-popout-wrapper'(event) { + event.preventDefault(); + }, + 'click .rc-popout__controls--record'(e, i) { + e.preventDefault(); + if (i.streamStatus.get() === 'ready') { + document.querySelector('.streaming-popup').dispatchEvent(new Event('startStreaming')); + i.streamStatus.set('starting'); + } else if (i.streamStatus.get() === 'broadcasting') { + document.querySelector('.streaming-popup').dispatchEvent(new Event('stopStreaming')); + i.streamStatus.set('finished'); + setTimeout(() => popout && popout.close(), 2000); + } + }, + 'broadcastStreamReady .streaming-popup'(e, i) { + e.preventDefault(); + i.streamStatus.set('ready'); + }, + 'broadcastStream .streaming-popup'(e, i) { + e.preventDefault(); + i.streamStatus.set('broadcasting'); + }, + 'click .rc-popout__controls--play'(e, i) { + window.liveStreamPlayer.playVideo(); + i.isPlaying.set(true); + }, + 'click .rc-popout__controls--pause'(e, i) { + window.liveStreamPlayer.pauseVideo(); + i.isPlaying.set(false); + }, + 'click .rc-popout__controls--mute'(e, i) { + window.liveStreamPlayer.mute(); + i.isMuted.set(true); + }, + 'click .rc-popout__controls--unmute'(e, i) { + window.liveStreamPlayer.unMute(); + i.isMuted.set(false); + }, + 'playerStateChanged .rc-popout'(e, i) { + if (e.detail === window.YT.PlayerState.PLAYING) { + i.isPlaying.set(true); + } else if (e.detail === window.YT.PlayerState.PAUSED) { + i.isPlaying.set(false); + } + }, +}); + +callbacks.add('afterLogoutCleanUp', () => popout.close(), callbacks.priority.MEDIUM, 'popout-close-after-logout-cleanup'); diff --git a/packages/rocketchat-ui-utils/lib/MessageProperties.js b/packages/rocketchat-ui-utils/lib/MessageProperties.js new file mode 100644 index 000000000000..569a6d461833 --- /dev/null +++ b/packages/rocketchat-ui-utils/lib/MessageProperties.js @@ -0,0 +1,15 @@ +import GraphemeSplitter from 'grapheme-splitter'; + +const splitter = new GraphemeSplitter(); + +export const messageProperties = { + + length: ((message) => splitter.countGraphemes(message)), + + messageWithoutEmojiShortnames: ((message) => message.replace(/:\w+:/gm, (match) => { + if (RocketChat.emoji.list[match] !== undefined) { + return ' '; + } + return match; + })), +}; diff --git a/packages/rocketchat-ui-utils/lib/MessageTypes.js b/packages/rocketchat-ui-utils/lib/MessageTypes.js new file mode 100644 index 000000000000..8e4191e005ac --- /dev/null +++ b/packages/rocketchat-ui-utils/lib/MessageTypes.js @@ -0,0 +1,19 @@ +export const MessageTypes = new class { + constructor() { + this.types = {}; + } + + registerType(options) { + return this.types[options.id] = options; + } + + getType(message) { + return this.types[message && message.t]; + } + + isSystemMessage(message) { + const type = this.types[message && message.t]; + return type && type.system; + } + +}; diff --git a/packages/rocketchat-ui-utils/package.js b/packages/rocketchat-ui-utils/package.js index cf1daff6045c..aacd2e789d84 100644 --- a/packages/rocketchat-ui-utils/package.js +++ b/packages/rocketchat-ui-utils/package.js @@ -20,6 +20,8 @@ Package.onUse(function(api) { 'rocketchat:streamer', 'rocketchat:models', 'rocketchat:lazy-load', + 'rocketchat:emoji', ]); api.mainModule('client/index.js', 'client'); + api.mainModule('server/index.js', 'server'); }); diff --git a/packages/rocketchat-ui-utils/server/index.js b/packages/rocketchat-ui-utils/server/index.js new file mode 100644 index 000000000000..d10c21758175 --- /dev/null +++ b/packages/rocketchat-ui-utils/server/index.js @@ -0,0 +1,2 @@ +export { messageProperties } from '../lib/MessageProperties'; +export { MessageTypes } from '../lib/MessageTypes'; diff --git a/packages/rocketchat-ui/client/lib/avatar.js b/packages/rocketchat-ui/client/lib/avatar.js index b5920a8c1f8b..ce85dbff5d15 100644 --- a/packages/rocketchat-ui/client/lib/avatar.js +++ b/packages/rocketchat-ui/client/lib/avatar.js @@ -1,37 +1,7 @@ -import { Blaze } from 'meteor/blaze'; -import { Session } from 'meteor/session'; +import { getAvatarAsPng, updateAvatarOfUsername as _updateAvatarOfUsername } from 'meteor/rocketchat:ui-utils'; -Blaze.registerHelper('avatarUrlFromUsername', getAvatarUrlFromUsername); - -export const getAvatarAsPng = function(username, cb) { - const image = new Image; - image.src = getAvatarUrlFromUsername(username); - image.onload = function() { - - const canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - const context = canvas.getContext('2d'); - context.drawImage(image, 0, 0); - try { - return cb(canvas.toDataURL('image/png')); - } catch (e) { - return cb(''); - } - }; - return image.onerror = function() { - return cb(''); - }; +export { + getAvatarAsPng, }; -updateAvatarOfUsername = function(username) { - const key = `avatar_random_${ username }`; - Session.set(key, Math.round(Math.random() * 1000)); - - Object.keys(RoomManager.openedRooms).forEach((key) => { - const room = RoomManager.openedRooms[key]; - const url = getAvatarUrlFromUsername(username); - $(room.dom).find(`.message[data-username='${ username }'] .avatar-image`).css('background-image', `url(${ url })`); - }); - return true; -}; +updateAvatarOfUsername = _updateAvatarOfUsername; diff --git a/packages/rocketchat-ui/client/views/app/alerts.js b/packages/rocketchat-ui/client/views/app/alerts.js index 1ebe6e6609e4..d4d43c5f8c87 100644 --- a/packages/rocketchat-ui/client/views/app/alerts.js +++ b/packages/rocketchat-ui/client/views/app/alerts.js @@ -1,72 +1,3 @@ -import { Blaze } from 'meteor/blaze'; -import { Template } from 'meteor/templating'; +import { alerts as _alerts } from 'meteor/rocketchat:ui-utils'; -alerts = { - renderedAlert: null, - open(config) { - this.close(false); - - config.closable = typeof(config.closable) === typeof(true) ? config.closable : true; - - if (config.timer) { - this.timer = setTimeout(() => this.close(), config.timer); - } - - this.renderedAlert = Blaze.renderWithData(Template.alerts, config, document.body, document.body.querySelector('#alert-anchor')); - }, - close(dismiss = true) { - if (this.timer) { - clearTimeout(this.timer); - delete this.timer; - } - if (!this.renderedAlert) { - return false; - } - - Blaze.remove(this.renderedAlert); - - const { activeElement } = this.renderedAlert.dataVar.curValue; - if (activeElement) { - $(activeElement).removeClass('active'); - } - - dismiss && this.renderedAlert.dataVar.curValue.onClose && this.renderedAlert.dataVar.curValue.onClose(); - }, -}; - -Template.alerts.helpers({ - hasAction() { - return Template.instance().data.action ? 'rc-alerts--has-action' : ''; - }, - modifiers() { - return (Template.instance().data.modifiers || []).map((mod) => `rc-alerts--${ mod }`).join(' '); - }, -}); - -Template.alerts.onRendered(function() { - if (this.data.onRendered) { - this.data.onRendered(); - } -}); - -Template.alerts.onDestroyed(function() { - if (this.data.onDestroyed) { - this.data.onDestroyed(); - } -}); - -Template.alerts.events({ - 'click .js-action'(e, instance) { - if (!this.action) { - return; - } - this.action.call(this, e, instance.data.data); - }, - 'click .js-close'() { - alerts.close(); - }, -}); - -Template.alerts.helpers({ - isSafariIos: /iP(ad|hone|od).+Version\/[\d\.]+.*Safari/i.test(navigator.userAgent), -}); +alerts = _alerts; diff --git a/packages/rocketchat-ui/client/views/app/popout.js b/packages/rocketchat-ui/client/views/app/popout.js index a877a9acad30..fadd5bbbe959 100644 --- a/packages/rocketchat-ui/client/views/app/popout.js +++ b/packages/rocketchat-ui/client/views/app/popout.js @@ -1,209 +1,3 @@ -import { Blaze } from 'meteor/blaze'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Template } from 'meteor/templating'; +import { popout as _popout } from 'meteor/rocketchat:ui-utils'; -popout = { - context: null, - isAudioOnly: false, - showVideoControls: true, - showStreamControls: false, - x: 0, - y: 0, - open(config = {}, fn) { - this.close(); - this.context = Blaze.renderWithData(Template.popout, config, document.body); - this.fn = fn; - this.config = config; - this.onCloseCallback = config.onCloseCallback || null; - this.timer = null; - if (config.timer) { - this.timer = setTimeout(() => this.close(), config.timer); - } - if (config.data) { - this.isAudioOnly = config.data.isAudioOnly; - this.showVideoControls = config.data.showVideoControls; - this.showStreamControls = config.data.showStreamControls; - } - }, - close() { - if (this.context) { - Blaze.remove(this.context); - } - this.context = null; - this.fn = null; - if (this.timer) { - clearTimeout(this.timer); - } - if (typeof(this.onCloseCallback) === 'function') { - this.onCloseCallback(); - } - }, - dragstart(event) { - if (!event.target.classList.contains('dropzone-overlay')) { - const popoutElement = document.querySelector('.rc-popout-wrapper'); - setTimeout(function() { - popoutElement.style.display = 'none'; - }, 0); - } - }, - dragover(event) { - const e = event.originalEvent || event; - e.dataTransfer.dropEffect = 'move'; - e.preventDefault(); - }, - dragend(event) { - if (!event.target.classList.contains('dropzone-overlay')) { - const popoutElement = document.querySelector('.rc-popout-wrapper'); - popoutElement.style.display = 'initial'; - } - }, - drop(event) { - const e = event.originalEvent || event; - e.preventDefault(); - // do not mess with the position if we are dropping files in the dropzone - if (!event.target.classList.contains('dropzone-overlay')) { - const popoutElement = document.querySelector('.rc-popout-wrapper'); - const positionTop = e.clientY - popout.y; - const positionLeft = e.clientX - popout.x; - popoutElement.style.left = `${ positionLeft >= 0 ? positionLeft : 0 }px`; - popoutElement.style.top = `${ positionTop >= 0 ? positionTop : 0 }px`; - } - }, -}; - -Template.popout.helpers({ - state() { - return Template.instance().isMinimized.get() ? 'closed' : 'open'; - }, - isAudioOnly() { - return Template.instance().isAudioOnly.get(); - }, - isMuted() { - return Template.instance().isMuted.get(); - }, - isPlaying() { - return Template.instance().isPlaying.get(); - }, - showVideoControls() { - return Template.instance().showVideoControls.get(); - }, - showStreamControls() { - return Template.instance().showStreamControls.get(); - }, - getStreamStatus() { - return Template.instance().streamStatus.get(); - }, -}); - -Template.popout.onRendered(function() { - Template.instance().isMinimized.set(popout.isAudioOnly); - Template.instance().isAudioOnly.set(popout.isAudioOnly); - Template.instance().showVideoControls.set(popout.showVideoControls); - Template.instance().showStreamControls.set(popout.showStreamControls); - - - if (this.data.onRendered) { - this.data.onRendered(); - } -}); -Template.popout.onCreated(function() { - this.isMinimized = new ReactiveVar(popout.isAudioOnly); - this.isAudioOnly = new ReactiveVar(popout.isAudioOnly); - this.canOpenExternal = new ReactiveVar(popout.canOpenExternal); - this.showVideoControls = new ReactiveVar(popout.showVideoControls); - this.showStreamControls = new ReactiveVar(popout.showStreamControls); - - this.isMuted = new ReactiveVar(false); - this.isPlaying = new ReactiveVar(true); - this.streamStatus = new ReactiveVar('preparing'); - document.body.addEventListener('dragstart', popout.dragstart, true); - document.body.addEventListener('dragover', popout.dragover, true); - document.body.addEventListener('dragend', popout.dragend, true); - document.body.addEventListener('drop', popout.drop, true); -}); - -Template.popout.onDestroyed(function() { - popout.context = null; - document.body.removeEventListener('dragstart', popout.dragstart, true); - document.body.removeEventListener('dragover', popout.dragover, true); - document.body.removeEventListener('dragend', popout.dragend, true); - document.body.removeEventListener('drop', popout.drop, true); -}); - -Template.popout.events({ - 'click .js-action'(e, instance) { - !this.action || this.action.call(instance.data.data, e, instance); - e.stopPropagation(); - popout.close(); - }, - 'click .js-close'(e) { - e.stopPropagation(); - popout.close(); - }, - 'click .js-minimize'(e, i) { - e.stopPropagation(); - if (i.isMinimized.get()) { - i.isMinimized.set(false); - window.liveStreamPlayer.setSize(380, 214); - } else { - i.isMinimized.set(true); - window.liveStreamPlayer.setSize(0, 0); - } - }, - 'dragstart .rc-popout-wrapper'(event) { - const e = event.originalEvent || event; - const url = (this.data && this.data.streamingSource) || '.rc-popout-wrapper'; - popout.x = e.offsetX; - popout.y = e.offsetY; - e.dataTransfer.setData('application/x-moz-node', e.currentTarget); - e.dataTransfer.setData('text/plain', url); - e.dataTransfer.effectAllowed = 'move'; - }, - 'dragend .rc-popout-wrapper'(event) { - event.preventDefault(); - }, - 'click .rc-popout__controls--record'(e, i) { - e.preventDefault(); - if (i.streamStatus.get() === 'ready') { - document.querySelector('.streaming-popup').dispatchEvent(new Event('startStreaming')); - i.streamStatus.set('starting'); - } else if (i.streamStatus.get() === 'broadcasting') { - document.querySelector('.streaming-popup').dispatchEvent(new Event('stopStreaming')); - i.streamStatus.set('finished'); - setTimeout(() => popout && popout.close(), 2000); - } - }, - 'broadcastStreamReady .streaming-popup'(e, i) { - e.preventDefault(); - i.streamStatus.set('ready'); - }, - 'broadcastStream .streaming-popup'(e, i) { - e.preventDefault(); - i.streamStatus.set('broadcasting'); - }, - 'click .rc-popout__controls--play'(e, i) { - window.liveStreamPlayer.playVideo(); - i.isPlaying.set(true); - }, - 'click .rc-popout__controls--pause'(e, i) { - window.liveStreamPlayer.pauseVideo(); - i.isPlaying.set(false); - }, - 'click .rc-popout__controls--mute'(e, i) { - window.liveStreamPlayer.mute(); - i.isMuted.set(true); - }, - 'click .rc-popout__controls--unmute'(e, i) { - window.liveStreamPlayer.unMute(); - i.isMuted.set(false); - }, - 'playerStateChanged .rc-popout'(e, i) { - if (e.detail === window.YT.PlayerState.PLAYING) { - i.isPlaying.set(true); - } else if (e.detail === window.YT.PlayerState.PAUSED) { - i.isPlaying.set(false); - } - }, -}); - -RocketChat.callbacks.add('afterLogoutCleanUp', () => popout.close(), RocketChat.callbacks.priority.MEDIUM, 'popout-close-after-logout-cleanup'); +popout = _popout; diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index f2558cea2782..13bf72e0cf5b 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -97,8 +97,6 @@ Package.onUse(function(api) { api.addFiles('client/views/app/notAuthorized.html', 'client'); api.addFiles('client/views/app/pageContainer.html', 'client'); api.addFiles('client/views/app/pageSettingsContainer.html', 'client'); - api.addFiles('client/views/app/popout.html', 'client'); - api.addFiles('client/views/app/alerts.html', 'client'); api.addFiles('client/views/app/room.html', 'client'); api.addFiles('client/views/app/roomSearch.html', 'client'); diff --git a/tests/end-to-end/ui/00-login.js b/tests/end-to-end/ui/00-login.js index 294b8c13d72a..47c4ee929a3c 100644 --- a/tests/end-to-end/ui/00-login.js +++ b/tests/end-to-end/ui/00-login.js @@ -1,163 +1,163 @@ -import loginPage from '../../pageobjects/login.page'; -import setupWizard from '../../pageobjects/setup-wizard.page'; - -describe('[Login]', () => { - before(() => { - loginPage.open(); - // This Can Cause Timeouts erros if the server is slow so it should have a big wait - loginPage.emailOrUsernameField.waitForVisible(15000); - }); - - describe('[Render]', () => { - it('it should show email / username field', () => { - loginPage.emailOrUsernameField.isVisible().should.be.true; - }); - - it('it should show password field', () => { - loginPage.passwordField.isVisible().should.be.true; - }); - - it('it should show submit button', () => { - loginPage.submitButton.isVisible().should.be.true; - }); - - it('it should show register button', () => { - loginPage.registerButton.isVisible().should.be.true; - }); - - it('it should show forgot password button', () => { - loginPage.forgotPasswordButton.isVisible().should.be.true; - }); - - it('it should not show name field', () => { - loginPage.nameField.isVisible().should.be.false; - }); - - it('it should not show email field', () => { - loginPage.emailField.isVisible().should.be.false; - }); - - it('it should not show confirm password field', () => { - loginPage.confirmPasswordField.isVisible().should.be.false; - }); - - it('it should not show back to login button', () => { - loginPage.backToLoginButton.isVisible().should.be.false; - }); - }); - - describe('[Required Fields]', () => { - before(() => { - loginPage.submit(); - }); - - describe('email / username: ', () => { - it('it should be required', () => { - loginPage.emailOrUsernameField.getAttribute('class').should.contain('error'); - loginPage.emailOrUsernameInvalidText.getText().should.not.be.empty; - }); - }); - - describe('password: ', () => { - it('it should be required', () => { - loginPage.passwordField.getAttribute('class').should.contain('error'); - loginPage.passwordInvalidText.getText().should.not.be.empty; - }); - }); - }); -}); - -describe('[Setup Wizard]', () => { - before(() => { - setupWizard.login(); - setupWizard.organizationType.waitForVisible(15000); - }); - - describe('[Render - Step 1]', () => { - it('it should show organization type', () => { - setupWizard.organizationType.isVisible().should.be.true; - }); - - it('it should show organization name', () => { - setupWizard.organizationName.isVisible().should.be.true; - }); - - it('it should show industry', () => { - setupWizard.industry.isVisible().should.be.true; - }); - - it('it should show size', () => { - setupWizard.size.isVisible().should.be.true; - }); - - it('it should show country', () => { - setupWizard.country.isVisible().should.be.true; - }); - - it('it should show website', () => { - setupWizard.website.isVisible().should.be.true; - }); - - after(() => { - setupWizard.goNext(); - }); - }); - - describe('[Render - Step 2]', () => { - it('it should show site name', () => { - setupWizard.siteName.isVisible().should.be.true; - }); - - it('it should show language', () => { - setupWizard.language.isVisible().should.be.true; - }); - - it('it should server type', () => { - setupWizard.serverType.isVisible().should.be.true; - }); - - after(() => { - setupWizard.goNext(); - }); - }); - - describe('[Render - Step 3]', () => { - it('it should have option for registered server', () => { - setupWizard.registeredServer.isExisting().should.be.true; - }); - - it('it should have option for standalone server', () => { - setupWizard.standaloneServer.isExisting().should.be.true; - }); - - it('it should check option for registered server by default', () => { - setupWizard.registeredServer.isSelected().should.be.true; - }); - - after(() => { - setupWizard.goNext(); - }); - }); - - describe('[Render - Final Step]', () => { - it('it should render "Go to your workspace button', () => { - setupWizard.goToWorkspace.waitForVisible(20000); - setupWizard.goToWorkspace.isVisible().should.be.true; - }); - - after(() => { - setupWizard.goToHome(); - }); - }); - - after(() => { - browser.execute(function() { - const user = Meteor.user(); - Meteor.logout(() => { - RocketChat.callbacks.run('afterLogoutCleanUp', user); - Meteor.call('logoutCleanUp', user); - FlowRouter.go('home'); - }); - }); - }); -}); +// import loginPage from '../../pageobjects/login.page'; +// import setupWizard from '../../pageobjects/setup-wizard.page'; + +// describe('[Login]', () => { +// before(() => { +// loginPage.open(); +// // This Can Cause Timeouts erros if the server is slow so it should have a big wait +// loginPage.emailOrUsernameField.waitForVisible(15000); +// }); + +// describe('[Render]', () => { +// it('it should show email / username field', () => { +// loginPage.emailOrUsernameField.isVisible().should.be.true; +// }); + +// it('it should show password field', () => { +// loginPage.passwordField.isVisible().should.be.true; +// }); + +// it('it should show submit button', () => { +// loginPage.submitButton.isVisible().should.be.true; +// }); + +// it('it should show register button', () => { +// loginPage.registerButton.isVisible().should.be.true; +// }); + +// it('it should show forgot password button', () => { +// loginPage.forgotPasswordButton.isVisible().should.be.true; +// }); + +// it('it should not show name field', () => { +// loginPage.nameField.isVisible().should.be.false; +// }); + +// it('it should not show email field', () => { +// loginPage.emailField.isVisible().should.be.false; +// }); + +// it('it should not show confirm password field', () => { +// loginPage.confirmPasswordField.isVisible().should.be.false; +// }); + +// it('it should not show back to login button', () => { +// loginPage.backToLoginButton.isVisible().should.be.false; +// }); +// }); + +// describe('[Required Fields]', () => { +// before(() => { +// loginPage.submit(); +// }); + +// describe('email / username: ', () => { +// it('it should be required', () => { +// loginPage.emailOrUsernameField.getAttribute('class').should.contain('error'); +// loginPage.emailOrUsernameInvalidText.getText().should.not.be.empty; +// }); +// }); + +// describe('password: ', () => { +// it('it should be required', () => { +// loginPage.passwordField.getAttribute('class').should.contain('error'); +// loginPage.passwordInvalidText.getText().should.not.be.empty; +// }); +// }); +// }); +// }); + +// describe('[Setup Wizard]', () => { +// before(() => { +// setupWizard.login(); +// setupWizard.organizationType.waitForVisible(15000); +// }); + +// describe('[Render - Step 1]', () => { +// it('it should show organization type', () => { +// setupWizard.organizationType.isVisible().should.be.true; +// }); + +// it('it should show organization name', () => { +// setupWizard.organizationName.isVisible().should.be.true; +// }); + +// it('it should show industry', () => { +// setupWizard.industry.isVisible().should.be.true; +// }); + +// it('it should show size', () => { +// setupWizard.size.isVisible().should.be.true; +// }); + +// it('it should show country', () => { +// setupWizard.country.isVisible().should.be.true; +// }); + +// it('it should show website', () => { +// setupWizard.website.isVisible().should.be.true; +// }); + +// after(() => { +// setupWizard.goNext(); +// }); +// }); + +// describe('[Render - Step 2]', () => { +// it('it should show site name', () => { +// setupWizard.siteName.isVisible().should.be.true; +// }); + +// it('it should show language', () => { +// setupWizard.language.isVisible().should.be.true; +// }); + +// it('it should server type', () => { +// setupWizard.serverType.isVisible().should.be.true; +// }); + +// after(() => { +// setupWizard.goNext(); +// }); +// }); + +// describe('[Render - Step 3]', () => { +// it('it should have option for registered server', () => { +// setupWizard.registeredServer.isExisting().should.be.true; +// }); + +// it('it should have option for standalone server', () => { +// setupWizard.standaloneServer.isExisting().should.be.true; +// }); + +// it('it should check option for registered server by default', () => { +// setupWizard.registeredServer.isSelected().should.be.true; +// }); + +// after(() => { +// setupWizard.goNext(); +// }); +// }); + +// describe('[Render - Final Step]', () => { +// it('it should render "Go to your workspace button', () => { +// setupWizard.goToWorkspace.waitForVisible(20000); +// setupWizard.goToWorkspace.isVisible().should.be.true; +// }); + +// after(() => { +// setupWizard.goToHome(); +// }); +// }); + +// after(() => { +// browser.execute(function() { +// const user = Meteor.user(); +// Meteor.logout(() => { +// RocketChat.callbacks.run('afterLogoutCleanUp', user); +// Meteor.call('logoutCleanUp', user); +// FlowRouter.go('home'); +// }); +// }); +// }); +// }); From 06409583187b04a1ee008df133a10eb1d2a0c8b5 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 12:11:14 -0200 Subject: [PATCH 53/59] Fix import missed objects inside RocketChat namespace --- .../rocketchat-lib/client/MessageAction.js | 102 +----------------- packages/rocketchat-lib/lib/messageBox.js | 65 +---------- 2 files changed, 4 insertions(+), 163 deletions(-) diff --git a/packages/rocketchat-lib/client/MessageAction.js b/packages/rocketchat-lib/client/MessageAction.js index e898821a4951..fb35c7fe5733 100644 --- a/packages/rocketchat-lib/client/MessageAction.js +++ b/packages/rocketchat-lib/client/MessageAction.js @@ -4,6 +4,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; import { t } from 'meteor/rocketchat:utils'; +import { MessageAction } from 'meteor/rocketchat:ui-utils'; import _ from 'underscore'; import moment from 'moment'; import toastr from 'toastr'; @@ -28,106 +29,7 @@ const success = function success(fn) { }; }; -RocketChat.MessageAction = new class { - /* - config expects the following keys (only id is mandatory): - id (mandatory) - icon: string - label: string - action: function(event, instance) - condition: function(message) - order: integer - group: string (message or menu) - */ - - constructor() { - this.buttons = new ReactiveVar({}); - } - - addButton(config) { - if (!config || !config.id) { - return false; - } - - if (!config.group) { - config.group = 'menu'; - } - - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - btns[config.id] = config; - return this.buttons.set(btns); - }); - } - - removeButton(id) { - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - delete btns[id]; - return this.buttons.set(btns); - }); - } - - updateButton(id, config) { - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - if (btns[id]) { - btns[id] = _.extend(btns[id], config); - return this.buttons.set(btns); - } - }); - } - - getButtonById(id) { - const allButtons = this.buttons.get(); - return allButtons[id]; - } - - getButtons(message, context, group) { - let allButtons = _.toArray(this.buttons.get()); - - if (group) { - allButtons = allButtons.filter((button) => button.group === group); - } - - if (message) { - allButtons = _.compact(_.map(allButtons, function(button) { - if (button.context == null || button.context.includes(context)) { - if (button.condition == null || button.condition(message, context)) { - return button; - } - } - })); - } - return _.sortBy(allButtons, 'order'); - } - - resetButtons() { - return this.buttons.set({}); - } - - async getPermaLink(msgId) { - if (!msgId) { - throw new Error('invalid-parameter'); - } - - const msg = RocketChat.models.Messages.findOne(msgId) || await call('getSingleMessage', msgId); - if (!msg) { - throw new Error('message-not-found'); - } - const roomData = RocketChat.models.Rooms.findOne({ - _id: msg.rid, - }); - - if (!roomData) { - throw new Error('room-not-found'); - } - - const subData = RocketChat.models.Subscriptions.findOne({ rid: roomData._id, 'u._id': Meteor.userId() }); - const roomURL = RocketChat.roomTypes.getURL(roomData.t, subData || roomData); - return `${ roomURL }?msg=${ msgId }`; - } -}; +RocketChat.MessageAction = MessageAction; Meteor.startup(function() { RocketChat.MessageAction.addButton({ diff --git a/packages/rocketchat-lib/lib/messageBox.js b/packages/rocketchat-lib/lib/messageBox.js index 514fb1258ffb..63d58c8fc8a6 100644 --- a/packages/rocketchat-lib/lib/messageBox.js +++ b/packages/rocketchat-lib/lib/messageBox.js @@ -1,65 +1,4 @@ import EventEmitter from 'wolfy87-eventemitter'; +import { messageBox } from 'meteor/rocketchat:ui-utils'; -RocketChat.messageBox = new EventEmitter; - -RocketChat.messageBox.actions = new class { - constructor() { - this.actions = {}; - } - - /* Add a action to messagebox - @param group - @param label - @param config - icon: icon class - action: action function - condition: condition to display the action - */ - - add(group, label, config) { - if (!group && !label && !config) { - return; - } - - if (!this.actions[group]) { - this.actions[group] = []; - } - - const actionExists = this.actions[group].find((action) => action.label === label); - - if (actionExists) { - return; - } - - this.actions[group].push({ ...config, label }); - } - remove(group, expression) { - if (!group || !this.actions[group]) { - return false; - } - return (this.actions[group] = this.actions[group].filter((action) => expression.test(action.id))); - } - get(group) { - if (!group) { - return Object.keys(this.actions).reduce((ret, key) => { - const actions = this.actions[key].filter((action) => !action.condition || action.condition()); - if (actions.length) { - ret[key] = actions; - } - return ret; - }, {}); - } - - return this.actions[group].filter((action) => !action.condition || action.condition()); - } - - getById(id) { - const messageActions = this.actions; - let actions = []; - Object.keys(messageActions).forEach(function(action) { - actions = actions.concat(messageActions[action]); - }); - - return actions.filter((action) => action.id === id); - } -}; +RocketChat.messageBox = messageBox; From a575e541ab41667ef309f14d0f86fea20a6e4436 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 12:35:49 -0200 Subject: [PATCH 54/59] Fix lint --- packages/rocketchat-lib/client/MessageAction.js | 12 ------------ packages/rocketchat-lib/lib/messageBox.js | 1 - 2 files changed, 13 deletions(-) diff --git a/packages/rocketchat-lib/client/MessageAction.js b/packages/rocketchat-lib/client/MessageAction.js index fb35c7fe5733..07a95c4d7c36 100644 --- a/packages/rocketchat-lib/client/MessageAction.js +++ b/packages/rocketchat-lib/client/MessageAction.js @@ -1,23 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/tap:i18n'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; import { t } from 'meteor/rocketchat:utils'; import { MessageAction } from 'meteor/rocketchat:ui-utils'; -import _ from 'underscore'; import moment from 'moment'; import toastr from 'toastr'; -const call = (method, ...args) => new Promise((resolve, reject) => { - Meteor.call(method, ...args, function(err, data) { - if (err) { - return reject(err); - } - resolve(data); - }); -}); - const success = function success(fn) { return function(error, result) { if (error) { diff --git a/packages/rocketchat-lib/lib/messageBox.js b/packages/rocketchat-lib/lib/messageBox.js index 63d58c8fc8a6..b7c2c98ff35a 100644 --- a/packages/rocketchat-lib/lib/messageBox.js +++ b/packages/rocketchat-lib/lib/messageBox.js @@ -1,4 +1,3 @@ -import EventEmitter from 'wolfy87-eventemitter'; import { messageBox } from 'meteor/rocketchat:ui-utils'; RocketChat.messageBox = messageBox; From 7c788ca246fafabb5c709f31354306ac3f579fe2 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 13:31:41 -0200 Subject: [PATCH 55/59] Remove rocketchat:ui package dependency of RocketChat namespace --- .../rocketchat-lib/client/lib/ui-buttons.js | 7 + packages/rocketchat-lib/package.js | 2 + .../client/components/header/header.js | 29 +++-- .../client/components/popupList.js | 3 +- packages/rocketchat-ui/client/index.js | 1 + .../rocketchat-ui/client/lib/chatMessages.js | 53 ++++---- .../rocketchat-ui/client/lib/collections.js | 3 +- .../rocketchat-ui/client/lib/cordova/push.js | 3 +- .../client/lib/cordova/user-state.js | 1 + packages/rocketchat-ui/client/lib/esc.js | 8 +- .../rocketchat-ui/client/lib/fileUpload.js | 7 +- .../client/lib/iframeCommands.js | 9 +- packages/rocketchat-ui/client/lib/menu.js | 11 +- packages/rocketchat-ui/client/lib/modal.js | 110 ---------------- .../rocketchat-ui/client/lib/msgTyping.js | 10 +- .../rocketchat-ui/client/lib/notification.js | 18 ++- .../client/lib/recorderjs/recorder.js | 4 +- packages/rocketchat-ui/client/lib/rocket.js | 18 +-- .../rocketchat-ui/client/views/app/burger.js | 3 +- .../client/views/app/createChannel.js | 37 +++--- .../client/views/app/directory.js | 12 +- .../rocketchat-ui/client/views/app/home.js | 5 +- .../rocketchat-ui/client/views/app/room.js | 121 ++++++++++-------- .../client/views/app/roomSearch.js | 3 +- .../client/views/app/secretURL.js | 3 +- .../rocketchat-ui/client/views/cmsPage.js | 3 +- packages/rocketchat-ui/package.js | 4 +- 27 files changed, 224 insertions(+), 264 deletions(-) create mode 100644 packages/rocketchat-lib/client/lib/ui-buttons.js create mode 100644 packages/rocketchat-ui/client/index.js delete mode 100644 packages/rocketchat-ui/client/lib/modal.js diff --git a/packages/rocketchat-lib/client/lib/ui-buttons.js b/packages/rocketchat-lib/client/lib/ui-buttons.js new file mode 100644 index 000000000000..cf026fe7cfc3 --- /dev/null +++ b/packages/rocketchat-lib/client/lib/ui-buttons.js @@ -0,0 +1,7 @@ +import { Login, Button, animationSupport, animeBack, preLoadImgs } from 'meteor/rocketchat:ui'; + +RocketChat.Login = Login; +RocketChat.Button = Button; +RocketChat.animationSupport = animationSupport; +RocketChat.animeBack = animeBack; +RocketChat.preLoadImgs = preLoadImgs; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 8cc3c1c3dedf..9784e41bedd6 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -34,6 +34,7 @@ Package.onUse(function(api) { api.use('rocketchat:ui-utils'); api.use('rocketchat:tooltip'); api.use('rocketchat:emoji'); + api.use('rocketchat:ui'); api.use('rocketchat:accounts'); api.use('modules'); api.use('rocketchat:i18n'); @@ -240,6 +241,7 @@ Package.onUse(function(api) { api.addFiles('client/lib/authorization.js', 'client'); api.addFiles('client/lib/tooltip.js', 'client'); api.addFiles('client/lib/EmojiPicker.js', 'client'); + api.addFiles('client/lib/ui-buttons.js', 'client'); // CLIENT LIB STARTUP api.addFiles('client/lib/startup/commands.js', 'client'); diff --git a/packages/rocketchat-ui/client/components/header/header.js b/packages/rocketchat-ui/client/components/header/header.js index 03d82cffa020..cd9d5bfcfd17 100644 --- a/packages/rocketchat-ui/client/components/header/header.js +++ b/packages/rocketchat-ui/client/components/header/header.js @@ -1,11 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { t } from 'meteor/rocketchat:utils'; +import { t, roomTypes, handleError } from 'meteor/rocketchat:utils'; +import { TabBar, fireGlobalEvent } from 'meteor/rocketchat:ui-utils'; +import { ChatSubscription, Rooms } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; const isSubscribed = (_id) => ChatSubscription.find({ rid: _id }).count() > 0; -const favoritesEnabled = () => RocketChat.settings.get('Favorite_Rooms'); +const favoritesEnabled = () => settings.get('Favorite_Rooms'); Template.header.helpers({ back() { @@ -14,15 +17,15 @@ Template.header.helpers({ avatarBackground() { const roomData = Session.get(`roomData${ this._id }`); if (!roomData) { return ''; } - return RocketChat.roomTypes.getSecondaryRoomName(roomData.t, roomData) || RocketChat.roomTypes.getRoomName(roomData.t, roomData); + return roomTypes.getSecondaryRoomName(roomData.t, roomData) || roomTypes.getRoomName(roomData.t, roomData); }, buttons() { - return RocketChat.TabBar.getButtons(); + return TabBar.getButtons(); }, isTranslated() { const sub = ChatSubscription.findOne({ rid: this._id }, { fields: { autoTranslate: 1, autoTranslateLanguage: 1 } }); - return RocketChat.settings.get('AutoTranslate_Enabled') && ((sub != null ? sub.autoTranslate : undefined) === true) && (sub.autoTranslateLanguage != null); + return settings.get('AutoTranslate_Enabled') && ((sub != null ? sub.autoTranslate : undefined) === true) && (sub.autoTranslateLanguage != null); }, state() { @@ -38,21 +41,21 @@ Template.header.helpers({ }, isDirect() { - return RocketChat.models.Rooms.findOne(this._id).t === 'd'; + return Rooms.findOne(this._id).t === 'd'; }, roomName() { const roomData = Session.get(`roomData${ this._id }`); if (!roomData) { return ''; } - return RocketChat.roomTypes.getRoomName(roomData.t, roomData); + return roomTypes.getRoomName(roomData.t, roomData); }, secondaryName() { const roomData = Session.get(`roomData${ this._id }`); if (!roomData) { return ''; } - return RocketChat.roomTypes.getSecondaryRoomName(roomData.t, roomData); + return roomTypes.getSecondaryRoomName(roomData.t, roomData); }, roomTopic() { @@ -62,7 +65,7 @@ Template.header.helpers({ }, channelIcon() { - const roomType = RocketChat.models.Rooms.findOne(this._id).t; + const roomType = Rooms.findOne(this._id).t; switch (roomType) { case 'd': return 'at'; @@ -73,7 +76,7 @@ Template.header.helpers({ case 'l': return 'livechat'; default: - return RocketChat.roomTypes.getIcon(roomType); + return roomTypes.getIcon(roomType); } }, @@ -81,7 +84,7 @@ Template.header.helpers({ const roomData = Session.get(`roomData${ this._id }`); if (!(roomData != null ? roomData.t : undefined)) { return ''; } - return RocketChat.roomTypes.getIcon(roomData != null ? roomData.t : undefined); + return roomTypes.getIcon(roomData != null ? roomData.t : undefined); }, encryptedChannel() { @@ -91,7 +94,7 @@ Template.header.helpers({ userStatus() { const roomData = Session.get(`roomData${ this._id }`); - return RocketChat.roomTypes.getUserStatus(roomData.t, this._id) || t('offline'); + return roomTypes.getUserStatus(roomData.t, this._id) || t('offline'); }, showToggleFavorite() { @@ -146,5 +149,5 @@ Template.header.events({ }); Template.header.onCreated(function() { - this.currentChannel = (this.data && this.data._id && RocketChat.models.Rooms.findOne(this.data._id)) || undefined; + this.currentChannel = (this.data && this.data._id && Rooms.findOne(this.data._id)) || undefined; }); diff --git a/packages/rocketchat-ui/client/components/popupList.js b/packages/rocketchat-ui/client/components/popupList.js index 4fa3a349b56b..eefbe85cd570 100644 --- a/packages/rocketchat-ui/client/components/popupList.js +++ b/packages/rocketchat-ui/client/components/popupList.js @@ -1,4 +1,5 @@ import { Template } from 'meteor/templating'; +import { settings } from 'meteor/rocketchat:settings'; Template.popupList.helpers({ config() { @@ -34,6 +35,6 @@ Template.popupList_default.helpers({ Template.popupList_item_default.helpers({ showRealNames() { - return RocketChat.settings.get('UI_Use_Real_Name'); + return settings.get('UI_Use_Real_Name'); }, }); diff --git a/packages/rocketchat-ui/client/index.js b/packages/rocketchat-ui/client/index.js new file mode 100644 index 000000000000..0ad4e65c1a3a --- /dev/null +++ b/packages/rocketchat-ui/client/index.js @@ -0,0 +1 @@ +export { Button, Login, animationSupport, animeBack, preLoadImgs } from './lib/rocket'; diff --git a/packages/rocketchat-ui/client/lib/chatMessages.js b/packages/rocketchat-ui/client/lib/chatMessages.js index c46d8ca05389..94d53e427410 100644 --- a/packages/rocketchat-ui/client/lib/chatMessages.js +++ b/packages/rocketchat-ui/client/lib/chatMessages.js @@ -5,7 +5,14 @@ import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { TAPi18n } from 'meteor/tap:i18n'; -import { t } from 'meteor/rocketchat:utils'; +import { t, getUserPreference, slashCommands, handleError } from 'meteor/rocketchat:utils'; +import { MessageAction, messageProperties, MessageTypes, readMessage, modal } from 'meteor/rocketchat:ui-utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { promises } from 'meteor/rocketchat:promises'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; +import { Messages, Rooms, ChatMessage } from 'meteor/rocketchat:models'; +import { emoji } from 'meteor/rocketchat:emoji'; import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment'; @@ -16,7 +23,7 @@ let sendOnEnter = ''; Meteor.startup(() => { Tracker.autorun(function() { const user = Meteor.userId(); - sendOnEnter = RocketChat.getUserPreference(user, 'sendOnEnter'); + sendOnEnter = getUserPreference(user, 'sendOnEnter'); }); }); @@ -32,7 +39,7 @@ ChatMessages = class ChatMessages { init(node) { this.editing = {}; this.records = {}; - this.messageMaxSize = RocketChat.settings.get('Message_MaxAllowedSize'); + this.messageMaxSize = settings.get('Message_MaxAllowedSize'); this.wrapper = $(node).find('.wrapper'); this.input = this.input || $(node).find('.js-input-message').get(0); this.$input = $(this.input); @@ -120,14 +127,14 @@ ChatMessages = class ChatMessages { const message = this.getMessageById(element.getAttribute('id')); - const hasPermission = RocketChat.authz.hasAtLeastOnePermission('edit-message', message.rid); - const editAllowed = RocketChat.settings.get('Message_AllowEditing'); + const hasPermission = hasAtLeastOnePermission('edit-message', message.rid); + const editAllowed = settings.get('Message_AllowEditing'); const editOwn = message && message.u && message.u._id === Meteor.userId(); if (!hasPermission && (!editAllowed || !editOwn)) { return; } if (element.classList.contains('system')) { return; } - const blockEditInMinutes = RocketChat.settings.get('Message_AllowEditing_BlockEditInMinutes'); + const blockEditInMinutes = settings.get('Message_AllowEditing_BlockEditInMinutes'); if (blockEditInMinutes && blockEditInMinutes !== 0) { let currentTsDiff; let msgTs; @@ -211,8 +218,8 @@ ChatMessages = class ChatMessages { const mentionUser = $(input).data('mention-user') || false; if (reply !== undefined) { - msg = `[ ](${ await RocketChat.MessageAction.getPermaLink(reply._id) }) `; - const roomInfo = RocketChat.models.Rooms.findOne(reply.rid, { fields: { t: 1 } }); + msg = `[ ](${ await MessageAction.getPermaLink(reply._id) }) `; + const roomInfo = Rooms.findOne(reply.rid, { fields: { t: 1 } }); if (roomInfo.t !== 'd' && reply.u.username !== Meteor.user().username && mentionUser) { msg += `@${ reply.u.username } `; } @@ -225,7 +232,7 @@ ChatMessages = class ChatMessages { if (msg.slice(0, 2) === '+:') { const reaction = msg.slice(1).trim(); - if (RocketChat.emoji.list[reaction]) { + if (emoji.list[reaction]) { const lastMessage = ChatMessage.findOne({ rid }, { fields: { ts: 1 }, sort: { ts: -1 } }); Meteor.call('setReaction', reaction, lastMessage._id); input.value = ''; @@ -235,7 +242,7 @@ ChatMessages = class ChatMessages { } // Run to allow local encryption, and maybe other client specific actions to be run before send - const msgObject = await RocketChat.promises.run('onClientBeforeSendMessage', { _id: Random.id(), rid, msg }); + const msgObject = await promises.run('onClientBeforeSendMessage', { _id: Random.id(), rid, msg }); // checks for the final msgObject.msg size before actually sending the message if (this.isMessageTooLong(msgObject.msg)) { @@ -290,12 +297,12 @@ ChatMessages = class ChatMessages { const match = msgObject.msg.match(/^\/([^\s]+)(?:\s+(.*))?$/m); if (match) { let command; - if (RocketChat.slashCommands.commands[match[1]]) { - const commandOptions = RocketChat.slashCommands.commands[match[1]]; + if (slashCommands.commands[match[1]]) { + const commandOptions = slashCommands.commands[match[1]]; command = match[1]; const param = match[2] || ''; - if (!commandOptions.permission || RocketChat.authz.hasAtLeastOnePermission(commandOptions.permission, Session.get('openedRoom'))) { + if (!commandOptions.permission || hasAtLeastOnePermission(commandOptions.permission, Session.get('openedRoom'))) { if (commandOptions.clientOnly) { commandOptions.callback(command, param, msgObject); } else { @@ -306,14 +313,14 @@ ChatMessages = class ChatMessages { } } - if (!RocketChat.settings.get('Message_AllowUnrecognizedSlashCommand')) { + if (!settings.get('Message_AllowUnrecognizedSlashCommand')) { const invalidCommandMsg = { _id: Random.id(), rid: msgObject.rid, ts: new Date, msg: TAPi18n.__('No_such_command', { command: match[1] }), u: { - username: RocketChat.settings.get('InternalHubot_Username'), + username: settings.get('InternalHubot_Username'), }, private: true, }; @@ -328,7 +335,7 @@ ChatMessages = class ChatMessages { } confirmDeleteMsg(message, done = function() {}) { - if (RocketChat.MessageTypes.isSystemMessage(message)) { return; } + if (MessageTypes.isSystemMessage(message)) { return; } modal.open({ title: t('Are_you_sure'), text: t('You_will_not_be_able_to_recover'), @@ -358,8 +365,8 @@ ChatMessages = class ChatMessages { } deleteMsg(message) { - const forceDelete = RocketChat.authz.hasAtLeastOnePermission('force-delete-message', message.rid); - const blockDeleteInMinutes = RocketChat.settings.get('Message_AllowDeleting_BlockDeleteInMinutes'); + const forceDelete = hasAtLeastOnePermission('force-delete-message', message.rid); + const blockDeleteInMinutes = settings.get('Message_AllowDeleting_BlockDeleteInMinutes'); if (blockDeleteInMinutes && forceDelete === false) { let msgTs; if (message.ts != null) { msgTs = moment(message.ts); } @@ -460,7 +467,7 @@ ChatMessages = class ChatMessages { if (!msgId) { return; } - const message = RocketChat.models.Messages.findOne(msgId); + const message = Messages.findOne(msgId); if (message) { return this.$input.data('reply', message).trigger('dataChange'); } @@ -594,8 +601,8 @@ ChatMessages = class ChatMessages { } isMessageTooLong(message) { - const adjustedMessage = RocketChat.messageProperties.messageWithoutEmojiShortnames(message); - return RocketChat.messageProperties.length(adjustedMessage) > this.messageMaxSize && message; + const adjustedMessage = messageProperties.messageWithoutEmojiShortnames(message); + return messageProperties.length(adjustedMessage) > this.messageMaxSize && message; } isEmpty() { @@ -604,10 +611,10 @@ ChatMessages = class ChatMessages { }; -RocketChat.callbacks.add('afterLogoutCleanUp', () => { +callbacks.add('afterLogoutCleanUp', () => { Object.keys(localStorage).forEach((item) => { if (item.indexOf('messagebox_') === 0) { localStorage.removeItem(item); } }); -}, RocketChat.callbacks.priority.MEDIUM, 'chatMessages-after-logout-cleanup'); +}, callbacks.priority.MEDIUM, 'chatMessages-after-logout-cleanup'); diff --git a/packages/rocketchat-ui/client/lib/collections.js b/packages/rocketchat-ui/client/lib/collections.js index 4db4b857d13f..d6313d1ae677 100644 --- a/packages/rocketchat-ui/client/lib/collections.js +++ b/packages/rocketchat-ui/client/lib/collections.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; +import { settings } from 'meteor/rocketchat:settings'; import { ChatMessage as chatMessage, CachedChatRoom as cachedChatRoom, @@ -27,7 +28,7 @@ this.CachedUserList = cachedUserList; Meteor.startup(() => { Tracker.autorun(() => { - if (!Meteor.userId() && RocketChat.settings.get('Accounts_AllowAnonymousRead') === true) { + if (!Meteor.userId() && settings.get('Accounts_AllowAnonymousRead') === true) { CachedChatRoom.init(); CachedChatSubscription.ready.set(true); } diff --git a/packages/rocketchat-ui/client/lib/cordova/push.js b/packages/rocketchat-ui/client/lib/cordova/push.js index dbb098ab7bf4..813253be08f3 100644 --- a/packages/rocketchat-ui/client/lib/cordova/push.js +++ b/packages/rocketchat-ui/client/lib/cordova/push.js @@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Push } from 'meteor/rocketchat:push'; +import { settings } from 'meteor/rocketchat:settings'; if (Meteor.isCordova) { // Push.addListener 'token', (token) -> @@ -72,7 +73,7 @@ if (Meteor.isCordova) { Meteor.startup(() => Tracker.autorun(() => { - if (RocketChat.settings.get('Push_enable') === true) { + if (settings.get('Push_enable') === true) { Push.Configure({ android: { diff --git a/packages/rocketchat-ui/client/lib/cordova/user-state.js b/packages/rocketchat-ui/client/lib/cordova/user-state.js index a7dd9d0892f9..a32910f294f2 100644 --- a/packages/rocketchat-ui/client/lib/cordova/user-state.js +++ b/packages/rocketchat-ui/client/lib/cordova/user-state.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { UserPresence } from 'meteor/konecty:user-presence'; +import { readMessage } from 'meteor/rocketchat:ui-utils'; import _ from 'underscore'; let timer = undefined; diff --git a/packages/rocketchat-ui/client/lib/esc.js b/packages/rocketchat-ui/client/lib/esc.js index 4a4450ee5bfc..d93b2cbf78f7 100644 --- a/packages/rocketchat-ui/client/lib/esc.js +++ b/packages/rocketchat-ui/client/lib/esc.js @@ -1,3 +1,6 @@ +import { SideNav } from 'meteor/rocketchat:ui-utils'; +import { VideoRecorder } from './recorderjs/videoRecorder'; + const escapify = { init() { const that = this; @@ -6,7 +9,7 @@ const escapify = { if (keyName === 'Escape') { that.sideNavIcon(); that.flextTabButton(); - that.videoDialog(); + that.videoDialog().then(); that.sweetAlerts(); } }, false); @@ -36,7 +39,8 @@ const escapify = { } }, - videoDialog() { + async videoDialog() { + const { VRecDialog } = await import('meteor/rocketchat:ui-vrecord'); const vrecDialog = document.querySelector('.vrec-dialog'); if (vrecDialog && Number(window.getComputedStyle(vrecDialog).opacity) === 1) { VideoRecorder.stop(); diff --git a/packages/rocketchat-ui/client/lib/fileUpload.js b/packages/rocketchat-ui/client/lib/fileUpload.js index d3a30e794a1f..13cd13724239 100644 --- a/packages/rocketchat-ui/client/lib/fileUpload.js +++ b/packages/rocketchat-ui/client/lib/fileUpload.js @@ -4,7 +4,8 @@ import { Session } from 'meteor/session'; import s from 'underscore.string'; import { fileUploadHandler } from 'meteor/rocketchat:file-upload'; import { Handlebars } from 'meteor/ui'; -import { t } from 'meteor/rocketchat:utils'; +import { t, fileUploadIsValidContentType } from 'meteor/rocketchat:utils'; +import { modal } from 'meteor/rocketchat:ui-utils'; const readAsDataURL = (file, callback) => { const reader = new FileReader(); @@ -135,7 +136,7 @@ const getUploadPreview = async(file, preview) => { return getGenericUploadPreview(file, preview); }; -fileUpload = async(files) => { //eslint-disable-line +fileUpload = async(files) => { files = [].concat(files); const roomId = Session.get('openedRoom'); @@ -147,7 +148,7 @@ fileUpload = async(files) => { //eslint-disable-line return; } - if (!RocketChat.fileUploadIsValidContentType(file.file.type)) { + if (!fileUploadIsValidContentType(file.file.type)) { modal.open({ title: t('FileUpload_MediaType_NotAccepted'), text: file.file.type || `*.${ s.strRightBack(file.file.name, '.') }`, diff --git a/packages/rocketchat-ui/client/lib/iframeCommands.js b/packages/rocketchat-ui/client/lib/iframeCommands.js index 01c06886e5f3..56c49f0495d4 100644 --- a/packages/rocketchat-ui/client/lib/iframeCommands.js +++ b/packages/rocketchat-ui/client/lib/iframeCommands.js @@ -2,6 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { ServiceConfiguration } from 'meteor/service-configuration'; +import { AccountBox } from 'meteor/rocketchat:ui-utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { callbacks } from 'meteor/rocketchat:callbacks'; import s from 'underscore.string'; const commands = { @@ -52,7 +55,7 @@ const commands = { 'logout'() { const user = Meteor.user(); Meteor.logout(() => { - RocketChat.callbacks.run('afterLogoutCleanUp', user); + callbacks.run('afterLogoutCleanUp', user); Meteor.call('logoutCleanUp', user); return FlowRouter.go('home'); }); @@ -72,7 +75,7 @@ const commands = { }; window.addEventListener('message', (e) => { - if (RocketChat.settings.get('Iframe_Integration_receive_enable') !== true) { + if (settings.get('Iframe_Integration_receive_enable') !== true) { return; } @@ -80,7 +83,7 @@ window.addEventListener('message', (e) => { return; } - const origins = RocketChat.settings.get('Iframe_Integration_receive_origin'); + const origins = settings.get('Iframe_Integration_receive_origin'); if (origins !== '*' && origins.split(',').indexOf(e.origin) === -1) { return console.error('Origin not allowed', e.origin); diff --git a/packages/rocketchat-ui/client/lib/menu.js b/packages/rocketchat-ui/client/lib/menu.js index dd3899a7ef8e..8217a5c80f81 100644 --- a/packages/rocketchat-ui/client/lib/menu.js +++ b/packages/rocketchat-ui/client/lib/menu.js @@ -1,20 +1,23 @@ import _ from 'underscore'; import { menu as _menu } from 'meteor/rocketchat:ui-utils'; +import EventEmitter from 'wolfy87-eventemitter'; + +const emitter = new EventEmitter(); window.addEventListener('resize', _.debounce((() => { let lastState = window.matchMedia('(min-width: 780px)').matches ? 'mini' : 'large'; - RocketChat.emit('grid', lastState); + emitter.emit('grid', lastState); return () => { const futureState = window.matchMedia('(min-width: 780px)').matches ? 'mini' : 'large'; if (lastState !== futureState) { lastState = futureState; - RocketChat.emit('grid', lastState); + emitter.emit('grid', lastState); } }; })(), 100)); this.menu = _menu; -RocketChat.on('grid', () => { - this.menu.close(); +emitter.on('grid', () => { + _menu.close(); }); diff --git a/packages/rocketchat-ui/client/lib/modal.js b/packages/rocketchat-ui/client/lib/modal.js deleted file mode 100644 index f09195e9b0e5..000000000000 --- a/packages/rocketchat-ui/client/lib/modal.js +++ /dev/null @@ -1,110 +0,0 @@ -import { Blaze } from 'meteor/blaze'; - -this.Modal = (function() { - - const self = {}; - const win = $(window); - - // mistérios da vida c.483: Pq a self.$window diz ter 100% da janela via css mas na verdade ocupa menos de 100% da tela? - // isso impede que o retorno da janela ao normal quando não mais necessária a classe fluid. (comportamento dançante) - - function focus() { - if (self.$modal) { - const input = self.$modal.find('input[type=\'text\']'); - if (input.length) { return input.get(0).focus(); } - } - } - function check() { - if (self.$modal && self.$modal.length) { - if (win.height() < (self.$window.outerHeight() + (win.height() * 0.10))) { - if (!self.$modal.hasClass('fluid')) { - return self.$modal.addClass('fluid'); - } - } - } - } - function stopListening() { - if (self.interval) { return clearInterval(self.interval); } - } - function startListening() { - stopListening(); - return self.interval = setInterval(() => check(), 100); - } - - - function close() { - self.$modal.addClass('closed'); - win.unbind('keydown.modal'); - // acionar no on-complete da animação - return setTimeout(function() { - self.opened = 0; - stopListening(); - return self.$modal.removeClass('opened closed'); - }, 300); - } - function keydown(e) { - const k = e.which; - if (k === 27) { - e.preventDefault(); - e.stopImmediatePropagation(); - return close(); - } - } - function checkFooter() { - if (self.$footer && self.$footer.length) { - const buttons = self.$footer.find('button'); - return buttons.each(function() { - const btn = $(this); - if (btn.html().match(/fechar/ig)) { - return btn.click(function(e) { - e.preventDefault(); - return close(); - }); - } - }); - } - } - - function setContent(template, data) { - self.$main.empty(); - if (template) { - if (data) { - Blaze.renderWithData(template, data, self.$main.get(0)); - } else { - Blaze.render(template, self.$main.get(0)); - } - checkFooter(); - return check(); - } - } - - function open(template, params) { - params = params || {}; - RocketChat.animeBack(self.$modal, () => focus()); - self.opened = 1; - if (params.listening) { startListening(); } - if (template != null) { setContent(template, params.data); } - self.$modal.addClass('opened'); - self.$modal.removeClass('fluid'); - return setTimeout(() => focus(), 200); - } - - function init($modal, params) { - self.params = params || {}; - self.opened = 0; - self.initialized = 0; - self.$modal = $modal.length ? $modal : $('.rocket-modal'); - if (self.$modal.length) { - self.initialized = 0; - self.$window = self.$modal.find('.modal'); - self.$main = self.$modal.find('main'); - self.$close = self.$modal.find('header > .close'); - self.$footer = self.$modal.find('footer'); - self.$close.unbind('click').click(close); - win.unbind('resize.modal').bind('resize.modal', check); - return win.unbind('keydown.modal').bind('keydown.modal', (e) => keydown(e)); - } - } - - return { init, open, close, focus, setContent }; -}()); diff --git a/packages/rocketchat-ui/client/lib/msgTyping.js b/packages/rocketchat-ui/client/lib/msgTyping.js index 85dede240bcc..a79e0b8a7d8f 100644 --- a/packages/rocketchat-ui/client/lib/msgTyping.js +++ b/packages/rocketchat-ui/client/lib/msgTyping.js @@ -2,6 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { ReactiveVar } from 'meteor/reactive-var'; import { Session } from 'meteor/session'; +import { settings } from 'meteor/rocketchat:settings'; +import { Notifications } from 'meteor/rocketchat:notifications'; import _ from 'underscore'; export const MsgTyping = (function() { @@ -17,7 +19,7 @@ export const MsgTyping = (function() { if (!user) { return; } - if (RocketChat.settings.get('UI_Use_Real_Name')) { + if (settings.get('UI_Use_Real_Name')) { return user.name; } return user.username; @@ -28,7 +30,7 @@ export const MsgTyping = (function() { return; } usersTyping[room] = { users: {} }; - return RocketChat.Notifications.onRoom(room, 'typing', function(username, typing) { + return Notifications.onRoom(room, 'typing', function(username, typing) { const user = Meteor.users.findOne(Meteor.userId(), { fields: { name: 1, username: 1 } }); if (username === shownName(user)) { return; @@ -57,7 +59,7 @@ export const MsgTyping = (function() { timeouts[room] = null; } const user = Meteor.user(); - return RocketChat.Notifications.notifyRoom(room, 'typing', shownName(user), false); + return Notifications.notifyRoom(room, 'typing', shownName(user), false); }; const start = function(room) { if (!renew) { return; } @@ -67,7 +69,7 @@ export const MsgTyping = (function() { renew = false; selfTyping.set(true); const user = Meteor.user(); - RocketChat.Notifications.notifyRoom(room, 'typing', shownName(user), true); + Notifications.notifyRoom(room, 'typing', shownName(user), true); clearTimeout(timeouts[room]); return timeouts[room] = Meteor.setTimeout(() => stop(room), timeout); }; diff --git a/packages/rocketchat-ui/client/lib/notification.js b/packages/rocketchat-ui/client/lib/notification.js index c8e43ef20879..6a2428fdc6eb 100644 --- a/packages/rocketchat-ui/client/lib/notification.js +++ b/packages/rocketchat-ui/client/lib/notification.js @@ -8,6 +8,10 @@ import { Session } from 'meteor/session'; import _ from 'underscore'; import s from 'underscore.string'; import { e2e } from 'meteor/rocketchat:e2e'; +import { Users, ChatSubscription } from 'meteor/rocketchat:models'; +import { getUserPreference } from 'meteor/rocketchat:utils'; +import { getAvatarUrlFromUsername } from 'meteor/rocketchat:ui-utils'; +import { promises } from 'meteor/rocketchat:promises'; import { getAvatarAsPng } from './avatar'; KonchatNotification = { @@ -28,7 +32,7 @@ KonchatNotification = { notify(notification) { if (window.Notification && Notification.permission === 'granted') { const message = { rid: (notification.payload != null ? notification.payload.rid : undefined), msg: notification.text, notification: true }; - return RocketChat.promises.run('onClientMessageReceived', message).then(function(message) { + return promises.run('onClientMessageReceived', message).then(function(message) { const n = new Notification(notification.title, { icon: notification.icon || getAvatarUrlFromUsername(notification.payload.sender.username), body: s.stripTags(message.msg), @@ -37,7 +41,7 @@ KonchatNotification = { canReply: true, }); - const notificationDuration = notification.duration - 0 || RocketChat.getUserPreference(Meteor.userId(), 'desktopNotificationDuration') - 0; + const notificationDuration = notification.duration - 0 || getUserPreference(Meteor.userId(), 'desktopNotificationDuration') - 0; if (notificationDuration > 0) { setTimeout((() => n.close()), notificationDuration * 1000); } @@ -95,8 +99,8 @@ KonchatNotification = { newMessage(rid) { if (!Session.equals(`user_${ Meteor.user().username }_status`, 'busy')) { const userId = Meteor.userId(); - const newMessageNotification = RocketChat.getUserPreference(userId, 'newMessageNotification'); - const audioVolume = RocketChat.getUserPreference(userId, 'notificationsSoundVolume'); + const newMessageNotification = getUserPreference(userId, 'newMessageNotification'); + const audioVolume = getUserPreference(userId, 'notificationsSoundVolume'); const sub = ChatSubscription.findOne({ rid }, { fields: { audioNotificationValue: 1 } }); @@ -144,14 +148,14 @@ KonchatNotification = { Meteor.startup(() => { Tracker.autorun(function() { - const user = RocketChat.models.Users.findOne(Meteor.userId(), { + const user = Users.findOne(Meteor.userId(), { fields: { 'settings.preferences.newRoomNotification': 1, 'settings.preferences.notificationsSoundVolume': 1, }, }); - const newRoomNotification = RocketChat.getUserPreference(user, 'newRoomNotification'); - const audioVolume = RocketChat.getUserPreference(user, 'notificationsSoundVolume'); + const newRoomNotification = getUserPreference(user, 'newRoomNotification'); + const audioVolume = getUserPreference(user, 'notificationsSoundVolume'); if ((Session.get('newRoomSound') || []).length > 0) { Meteor.defer(function() { diff --git a/packages/rocketchat-ui/client/lib/recorderjs/recorder.js b/packages/rocketchat-ui/client/lib/recorderjs/recorder.js index 1dee04ea2745..83e8d6b0231b 100644 --- a/packages/rocketchat-ui/client/lib/recorderjs/recorder.js +++ b/packages/rocketchat-ui/client/lib/recorderjs/recorder.js @@ -1,3 +1,5 @@ +import { settings } from 'meteor/rocketchat:settings'; + (function(window){ var WORKER_PATH = 'mp3-realtime-worker.js'; @@ -6,7 +8,7 @@ var config = cfg || {}; var bufferLen = config.bufferLen || 4096; var numChannels = config.numChannels || 1; - var bitRate = RocketChat.settings.get('Message_Audio_bitRate'); + var bitRate = settings.get('Message_Audio_bitRate'); this.context = source.context; this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, diff --git a/packages/rocketchat-ui/client/lib/rocket.js b/packages/rocketchat-ui/client/lib/rocket.js index 87e2679835bc..80ce4b8ab664 100644 --- a/packages/rocketchat-ui/client/lib/rocket.js +++ b/packages/rocketchat-ui/client/lib/rocket.js @@ -1,4 +1,4 @@ -RocketChat.Login = (function() { +export const Login = (function() { function onClick(el) { const $el = $(el); if ($el.length) { @@ -29,7 +29,7 @@ RocketChat.Login = (function() { return { check, onClick, onBlur }; }()); -RocketChat.Button = (function() { +export const Button = (function() { let time = undefined; const loading = function(el) { const next = el.attr('data-loading-text'); @@ -50,7 +50,7 @@ RocketChat.Button = (function() { return { done, loading, reset }; }()); -RocketChat.animationSupport = function() { +export const animationSupport = function() { const animeEnd = { WebkitAnimation: 'webkitAnimationEnd', OAnimation: 'oAnimationEnd', @@ -65,9 +65,9 @@ RocketChat.animationSupport = function() { msTransition: 'MSTransitionEnd', transition: 'transitionend', }; - const prefixB = transEndEventNames[Modernizr.prefixed('transition')]; - const prefixA = animeEnd[Modernizr.prefixed('animation')]; - const support = Modernizr.cssanimations; + const prefixB = transEndEventNames[window.Modernizr.prefixed('transition')]; + const prefixA = animeEnd[window.Modernizr.prefixed('animation')]; + const support = window.Modernizr.cssanimations; return { support, animation: prefixA, @@ -75,13 +75,13 @@ RocketChat.animationSupport = function() { }; }; -RocketChat.animeBack = function(e, callback, type) { +export const animeBack = function(e, callback, type) { const el = $(e); if (!el.length > 0) { if (callback) { callback(el); } return; } - const s = RocketChat.animationSupport(); + const s = animationSupport(); const p = ((type ? s.animation : s.transition)); el.one(p, function(e) { @@ -91,7 +91,7 @@ RocketChat.animeBack = function(e, callback, type) { }; -RocketChat.preLoadImgs = function(urls, callback) { +export const preLoadImgs = function(urls, callback) { const preLoader = $('
    ').attr({ id: 'perverter-preloader' }); let ended = undefined; const l_ = function(x) { diff --git a/packages/rocketchat-ui/client/views/app/burger.js b/packages/rocketchat-ui/client/views/app/burger.js index a3c61d480528..6c461529e0a2 100644 --- a/packages/rocketchat-ui/client/views/app/burger.js +++ b/packages/rocketchat-ui/client/views/app/burger.js @@ -1,5 +1,6 @@ import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; +import { Layout } from 'meteor/rocketchat:ui-utils'; Template.burger.helpers({ unread() { @@ -11,6 +12,6 @@ Template.burger.helpers({ } }, embeddedVersion() { - return RocketChat.Layout.isEmbedded(); + return Layout.isEmbedded(); }, }); diff --git a/packages/rocketchat-ui/client/views/app/createChannel.js b/packages/rocketchat-ui/client/views/app/createChannel.js index 6a89273aabbe..6b84080ce5f6 100644 --- a/packages/rocketchat-ui/client/views/app/createChannel.js +++ b/packages/rocketchat-ui/client/views/app/createChannel.js @@ -5,7 +5,10 @@ import { Blaze } from 'meteor/blaze'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; import { AutoComplete } from 'meteor/mizzao:autocomplete'; -import { t } from 'meteor/rocketchat:utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { t, roomTypes } from 'meteor/rocketchat:utils'; +import { hasAllPermission } from 'meteor/rocketchat:authorization'; import _ from 'underscore'; const acEvents = { @@ -34,20 +37,20 @@ const acEvents = { }; const validateChannelName = (name) => { - if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { + if (settings.get('UI_Allow_room_names_with_special_chars')) { return true; } - const reg = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`); + const reg = new RegExp(`^${ settings.get('UTF8_Names_Validation') }$`); return name.length === 0 || reg.test(name); }; const filterNames = (old) => { - if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { + if (settings.get('UI_Allow_room_names_with_special_chars')) { return old; } - const reg = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`); + const reg = new RegExp(`^${ settings.get('UTF8_Names_Validation') }$`); return [...old.replace(' ', '').toLocaleLowerCase()].filter((f) => reg.test(f)).join(''); }; @@ -101,7 +104,7 @@ Template.createChannel.helpers({ return Template.instance().type.get() !== 'p' || Template.instance().broadcast.get(); }, e2eEnabled() { - return RocketChat.settings.get('E2E_Enable'); + return settings.get('E2E_Enable'); }, readOnly() { return Template.instance().readOnly.get(); @@ -110,7 +113,7 @@ Template.createChannel.helpers({ return t(Template.instance().readOnly.get() ? t('Only_authorized_users_can_write_new_messages') : t('All_users_in_the_channel_can_write_new_messages')); }, cantCreateBothTypes() { - return !RocketChat.authz.hasAllPermission(['create-c', 'create-p']); + return !hasAllPermission(['create-c', 'create-p']); }, roomTypeIsP() { return Template.instance().type.get() === 'p'; @@ -131,7 +134,7 @@ Template.createChannel.helpers({ return Template.instance().type.get() === 'p' ? 'lock' : 'hashtag'; }, tokenAccessEnabled() { - return RocketChat.settings.get('API_Tokenpass_URL') !== ''; + return settings.get('API_Tokenpass_URL') !== ''; }, tokenIsDisabled() { return Template.instance().type.get() !== 'p' ? 'disabled' : null; @@ -148,19 +151,19 @@ Template.createChannel.helpers({ }; }, roomTypesBeforeStandard() { - const orderLow = RocketChat.roomTypes.roomTypesOrder.filter((roomTypeOrder) => roomTypeOrder.identifier === 'c')[0].order; - return RocketChat.roomTypes.roomTypesOrder.filter( + const orderLow = roomTypes.roomTypesOrder.filter((roomTypeOrder) => roomTypeOrder.identifier === 'c')[0].order; + return roomTypes.roomTypesOrder.filter( (roomTypeOrder) => roomTypeOrder.order < orderLow ).map( - (roomTypeOrder) => RocketChat.roomTypes.roomTypes[roomTypeOrder.identifier] + (roomTypeOrder) => roomTypes.roomTypes[roomTypeOrder.identifier] ).filter((roomType) => roomType.creationTemplate); }, roomTypesAfterStandard() { - const orderHigh = RocketChat.roomTypes.roomTypesOrder.filter((roomTypeOrder) => roomTypeOrder.identifier === 'd')[0].order; - return RocketChat.roomTypes.roomTypesOrder.filter( + const orderHigh = roomTypes.roomTypesOrder.filter((roomTypeOrder) => roomTypeOrder.identifier === 'd')[0].order; + return roomTypes.roomTypesOrder.filter( (roomTypeOrder) => roomTypeOrder.order > orderHigh ).map( - (roomTypeOrder) => RocketChat.roomTypes.roomTypes[roomTypeOrder.identifier] + (roomTypeOrder) => roomTypes.roomTypes[roomTypeOrder.identifier] ).filter((roomType) => roomType.creationTemplate); }, }); @@ -247,7 +250,7 @@ Template.createChannel.events({ } if (!isPrivate) { - RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); + callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); } return FlowRouter.go(isPrivate ? 'group' : 'channel', { name: result.name }, FlowRouter.current().queryParams); @@ -280,7 +283,7 @@ Template.createChannel.onCreated(function() { this.extensions_validations = {}; this.extensions_submits = {}; this.name = new ReactiveVar(''); - this.type = new ReactiveVar(RocketChat.authz.hasAllPermission(['create-p']) ? 'p' : 'c'); + this.type = new ReactiveVar(hasAllPermission(['create-p']) ? 'p' : 'c'); this.readOnly = new ReactiveVar(false); this.broadcast = new ReactiveVar(false); this.encrypted = new ReactiveVar(false); @@ -355,7 +358,7 @@ Template.createChannel.onCreated(function() { Template.tokenpass.onCreated(function() { this.data.validations.tokenpass = (instance) => { - const result = (RocketChat.settings.get('API_Tokenpass_URL') !== '' && instance.tokensRequired.get() && instance.type.get() === 'p') && this.selectedTokens.get().length === 0; + const result = (settings.get('API_Tokenpass_URL') !== '' && instance.tokensRequired.get() && instance.type.get() === 'p') && this.selectedTokens.get().length === 0; this.invalid.set(result); return !result; }; diff --git a/packages/rocketchat-ui/client/views/app/directory.js b/packages/rocketchat-ui/client/views/app/directory.js index 7391748a3178..7cb91a5b3f4a 100644 --- a/packages/rocketchat-ui/client/views/app/directory.js +++ b/packages/rocketchat-ui/client/views/app/directory.js @@ -4,7 +4,9 @@ import { Tracker } from 'meteor/tracker'; import { Template } from 'meteor/templating'; import _ from 'underscore'; import { timeAgo } from './helpers'; -import { t } from 'meteor/rocketchat:utils'; +import { t, roomTypes } from 'meteor/rocketchat:utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { hasAtLeastOnePermission } from 'meteor/rocketchat:authorization'; function directorySearch(config, cb) { return Meteor.call('browseChannels', config, (err, result) => { @@ -38,7 +40,7 @@ Template.directory.helpers({ return Template.instance().searchText.get(); }, showLastMessage() { - return RocketChat.settings.get('Store_Last_Message'); + return settings.get('Store_Last_Message'); }, searchResults() { return Template.instance().results.get(); @@ -57,7 +59,7 @@ Template.directory.helpers({ return Template.instance().searchSortBy.get() === key; }, createChannelOrGroup() { - return RocketChat.authz.hasAtLeastOnePermission(['create-c', 'create-p']); + return hasAtLeastOnePermission(['create-c', 'create-p']); }, tabsData() { const { @@ -116,7 +118,7 @@ Template.directory.helpers({ type = 'd'; routeConfig = { name: item.username }; } - RocketChat.roomTypes.openRouteLink(type, routeConfig); + roomTypes.openRouteLink(type, routeConfig); }; }, isLoading() { @@ -203,7 +205,7 @@ Template.directory.onRendered(function() { }); Template.directory.onCreated(function() { - const viewType = RocketChat.settings.get('Accounts_Directory_DefaultView') || 'channels'; + const viewType = settings.get('Accounts_Directory_DefaultView') || 'channels'; this.searchType = new ReactiveVar(viewType); if (viewType === 'channels') { this.searchSortBy = new ReactiveVar('usersCount'); diff --git a/packages/rocketchat-ui/client/views/app/home.js b/packages/rocketchat-ui/client/views/app/home.js index 07f79aa9cce2..27e05263aa03 100644 --- a/packages/rocketchat-ui/client/views/app/home.js +++ b/packages/rocketchat-ui/client/views/app/home.js @@ -1,10 +1,11 @@ import { Template } from 'meteor/templating'; +import { settings } from 'meteor/rocketchat:settings'; Template.home.helpers({ title() { - return RocketChat.settings.get('Layout_Home_Title'); + return settings.get('Layout_Home_Title'); }, body() { - return RocketChat.settings.get('Layout_Home_Body'); + return settings.get('Layout_Home_Body'); }, }); diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 2116c2ea199f..522ef8ebec84 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -4,36 +4,53 @@ import { Random } from 'meteor/random'; import { Tracker } from 'meteor/tracker'; import { Blaze } from 'meteor/blaze'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import { RocketChatTabBar } from 'meteor/rocketchat:lib'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { t } from 'meteor/rocketchat:utils'; +import { t, roomTypes, getUserPreference, handleError } from 'meteor/rocketchat:utils'; import { WebRTC } from 'meteor/rocketchat:webrtc'; +import { ChatSubscription, ChatMessage, RoomRoles, Users, Subscriptions, Rooms } from 'meteor/rocketchat:models'; +import { + fireGlobalEvent, + RoomHistoryManager, + RoomManager, + readMessage, + popover, + modal, + Layout, + MessageAction, + RocketChatTabBar, + openedRoom, +} from 'meteor/rocketchat:ui-utils'; +import { settings } from 'meteor/rocketchat:settings'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { promises } from 'meteor/rocketchat:promises'; +import { hasAllPermission, hasRole } from 'meteor/rocketchat:authorization'; import _ from 'underscore'; import moment from 'moment'; import mime from 'mime-type/with-db'; import Clipboard from 'clipboard'; - import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; +import { ChatMessages } from '../../lib/chatMessages'; +import { fileUpload } from '../../lib/fileUpload'; chatMessages = {}; const isSubscribed = (_id) => ChatSubscription.find({ rid: _id }).count() > 0; -const favoritesEnabled = () => RocketChat.settings.get('Favorite_Rooms'); +const favoritesEnabled = () => settings.get('Favorite_Rooms'); -const userCanDrop = (_id) => !RocketChat.roomTypes.readOnly(_id, Meteor.user()); +const userCanDrop = (_id) => !roomTypes.readOnly(_id, Meteor.user()); const openProfileTab = (e, instance, username) => { const roomData = Session.get(`roomData${ Session.get('openedRoom') }`); - if (RocketChat.Layout.isEmbedded()) { + if (Layout.isEmbedded()) { fireGlobalEvent('click-user-card-message', { username }); e.preventDefault(); e.stopPropagation(); return; } - if (RocketChat.roomTypes.roomTypes[roomData.t].enableMembersListProfile()) { + if (roomTypes.roomTypes[roomData.t].enableMembersListProfile()) { instance.setUserDetail(username); } @@ -42,7 +59,7 @@ const openProfileTab = (e, instance, username) => { }; const openProfileTabOrOpenDM = (e, instance, username) => { - if (RocketChat.settings.get('UI_Click_Direct_Message')) { + if (settings.get('UI_Click_Direct_Message')) { Meteor.call('createDirectMessage', username, (error, result) => { if (error) { if (error.isClientSafe) { @@ -70,7 +87,7 @@ const mountPopover = (e, i, outerContext) => { const [, message] = outerContext._arguments; - let menuItems = RocketChat.MessageAction.getButtons(message, context, 'menu').map((item) => ({ + let menuItems = MessageAction.getButtons(message, context, 'menu').map((item) => ({ icon: item.icon, name: t(item.label), type: 'message-action', @@ -79,7 +96,7 @@ const mountPopover = (e, i, outerContext) => { })); if (window.matchMedia('(max-width: 500px)').matches) { - const messageItems = RocketChat.MessageAction.getButtons(message, context, 'message').map((item) => ({ + const messageItems = MessageAction.getButtons(message, context, 'message').map((item) => ({ icon: item.icon, name: t(item.label), type: 'message-action', @@ -136,23 +153,23 @@ const wipeFailedUploads = () => { }; function roomHasGlobalPurge(room) { - if (!RocketChat.settings.get('RetentionPolicy_Enabled')) { + if (!settings.get('RetentionPolicy_Enabled')) { return false; } switch (room.t) { case 'c': - return RocketChat.settings.get('RetentionPolicy_AppliesToChannels'); + return settings.get('RetentionPolicy_AppliesToChannels'); case 'p': - return RocketChat.settings.get('RetentionPolicy_AppliesToGroups'); + return settings.get('RetentionPolicy_AppliesToGroups'); case 'd': - return RocketChat.settings.get('RetentionPolicy_AppliesToDMs'); + return settings.get('RetentionPolicy_AppliesToDMs'); } return false; } function roomHasPurge(room) { - if (!room || !RocketChat.settings.get('RetentionPolicy_Enabled')) { + if (!room || !settings.get('RetentionPolicy_Enabled')) { return false; } @@ -172,7 +189,7 @@ function roomFilesOnly(room) { return room.retention.filesOnly; } - return RocketChat.settings.get('RetentionPolicy_FilesOnly'); + return settings.get('RetentionPolicy_FilesOnly'); } function roomExcludePinned(room) { @@ -184,7 +201,7 @@ function roomExcludePinned(room) { return room.retention.excludePinned; } - return RocketChat.settings.get('RetentionPolicy_ExcludePinned'); + return settings.get('RetentionPolicy_ExcludePinned'); } function roomMaxAge(room) { @@ -200,26 +217,26 @@ function roomMaxAge(room) { } if (room.t === 'c') { - return RocketChat.settings.get('RetentionPolicy_MaxAge_Channels'); + return settings.get('RetentionPolicy_MaxAge_Channels'); } if (room.t === 'p') { - return RocketChat.settings.get('RetentionPolicy_MaxAge_Groups'); + return settings.get('RetentionPolicy_MaxAge_Groups'); } if (room.t === 'd') { - return RocketChat.settings.get('RetentionPolicy_MaxAge_DMs'); + return settings.get('RetentionPolicy_MaxAge_DMs'); } } -RocketChat.callbacks.add('enter-room', wipeFailedUploads); +callbacks.add('enter-room', wipeFailedUploads); Template.room.helpers({ isTranslated() { const sub = ChatSubscription.findOne({ rid: this._id }, { fields: { autoTranslate: 1, autoTranslateLanguage: 1 } }); - RocketChat.settings.get('AutoTranslate_Enabled') && ((sub != null ? sub.autoTranslate : undefined) === true) && (sub.autoTranslateLanguage != null); + settings.get('AutoTranslate_Enabled') && ((sub != null ? sub.autoTranslate : undefined) === true) && (sub.autoTranslateLanguage != null); }, embeddedVersion() { - return RocketChat.Layout.isEmbedded(); + return Layout.isEmbedded(); }, subscribed() { @@ -228,7 +245,7 @@ Template.room.helpers({ messagesHistory() { const hideMessagesOfType = []; - RocketChat.settings.collection.find({ _id: /Message_HideType_.+/ }).forEach(function(record) { + settings.collection.find({ _id: /Message_HideType_.+/ }).forEach(function(record) { let types; const type = record._id.replace('Message_HideType_', ''); switch (type) { @@ -289,10 +306,10 @@ Template.room.helpers({ roomLeader() { const roles = RoomRoles.findOne({ rid: this._id, roles: 'leader', 'u._id': { $ne: Meteor.userId() } }); if (roles) { - const leader = RocketChat.models.Users.findOne({ _id: roles.u._id }, { fields: { status: 1 } }) || {}; + const leader = Users.findOne({ _id: roles.u._id }, { fields: { status: 1 } }) || {}; return { ...roles.u, - name: RocketChat.settings.get('UI_Use_Real_Name') ? (roles.u.name || roles.u.username) : roles.u.username, + name: settings.get('UI_Use_Real_Name') ? (roles.u.name || roles.u.username) : roles.u.username, status: leader.status || 'offline', statusDisplay: ((status) => status.charAt(0).toUpperCase() + status.slice(1))(leader.status || 'offline'), }; @@ -300,7 +317,7 @@ Template.room.helpers({ }, chatNowLink() { - return RocketChat.roomTypes.getRouteLink('d', { name: this.username }); + return roomTypes.getRouteLink('d', { name: this.username }); }, showAnnouncement() { @@ -337,7 +354,7 @@ Template.room.helpers({ const roomData = Session.get(`roomData${ this._id }`); if (!(roomData != null ? roomData.t : undefined)) { return ''; } - const roomIcon = RocketChat.roomTypes.getIcon(roomData != null ? roomData.t : undefined); + const roomIcon = roomTypes.getIcon(roomData != null ? roomData.t : undefined); // Remove this 'codegueira' on header redesign if (!roomIcon) { @@ -353,11 +370,11 @@ Template.room.helpers({ userStatus() { const roomData = Session.get(`roomData${ this._id }`); - return RocketChat.roomTypes.getUserStatus(roomData.t, this._id) || 'offline'; + return roomTypes.getUserStatus(roomData.t, this._id) || 'offline'; }, maxMessageLength() { - return RocketChat.settings.get('Message_MaxAllowedSize'); + return settings.get('Message_MaxAllowedSize'); }, unreadData() { @@ -401,7 +418,7 @@ Template.room.helpers({ }, adminClass() { - if (RocketChat.authz.hasRole(Meteor.userId(), 'admin')) { return 'admin'; } + if (hasRole(Meteor.userId(), 'admin')) { return 'admin'; } }, showToggleFavorite() { @@ -409,7 +426,7 @@ Template.room.helpers({ }, messageViewMode() { - const viewMode = RocketChat.getUserPreference(Meteor.userId(), 'messageViewMode'); + const viewMode = getUserPreference(Meteor.userId(), 'messageViewMode'); const modes = ['', 'cozy', 'compact']; return modes[viewMode] || modes[0]; }, @@ -419,11 +436,11 @@ Template.room.helpers({ }, hideUsername() { - return RocketChat.getUserPreference(Meteor.userId(), 'hideUsernames') ? 'hide-usernames' : undefined; + return getUserPreference(Meteor.userId(), 'hideUsernames') ? 'hide-usernames' : undefined; }, hideAvatar() { - return RocketChat.getUserPreference(Meteor.userId(), 'hideAvatars') ? 'hide-avatars' : undefined; + return getUserPreference(Meteor.userId(), 'hideAvatars') ? 'hide-avatars' : undefined; }, userCanDrop() { @@ -445,15 +462,15 @@ Template.room.helpers({ return true; } - if (RocketChat.settings.get('Accounts_AllowAnonymousRead') === true) { + if (settings.get('Accounts_AllowAnonymousRead') === true) { return true; } - if (RocketChat.authz.hasAllPermission('preview-c-room')) { + if (hasAllPermission('preview-c-room')) { return true; } - return (RocketChat.models.Subscriptions.findOne({ rid: this._id }) != null); + return (Subscriptions.findOne({ rid: this._id }) != null); }, hideLeaderHeader() { @@ -494,14 +511,14 @@ let lastScrollTop; Template.room.events({ 'click .js-reply-broadcast'() { const message = this._arguments[1]; - RocketChat.roomTypes.openRouteLink('d', { name: this._arguments[1].u.username }, { ...FlowRouter.current().queryParams, reply: message._id }); + roomTypes.openRouteLink('d', { name: this._arguments[1].u.username }, { ...FlowRouter.current().queryParams, reply: message._id }); }, 'click, touchend'(e, t) { Meteor.setTimeout(() => t.sendToBottomIfNecessaryDebounced(), 100); }, 'click .messages-container-main'() { - if (Template.instance().tabBar.getState() === 'opened' && RocketChat.getUserPreference(Meteor.userId(), 'hideFlexTab')) { + if (Template.instance().tabBar.getState() === 'opened' && getUserPreference(Meteor.userId(), 'hideFlexTab')) { Template.instance().tabBar.close(); } }, @@ -672,7 +689,7 @@ Template.room.events({ 'click .new-message'() { Template.instance().atBottom = true; - chatMessages[RocketChat.openedRoom].input.focus(); + chatMessages[openedRoom].input.focus(); }, 'click .message-actions__menu'(e, i) { let context = $(e.target).parents('.message').data('context'); @@ -681,7 +698,7 @@ Template.room.events({ } const [, message] = this._arguments; - const allItems = RocketChat.MessageAction.getButtons(message, context, 'menu').map((item) => ({ + const allItems = MessageAction.getButtons(message, context, 'menu').map((item) => ({ icon: item.icon, name: t(item.label), type: 'message-action', @@ -721,7 +738,7 @@ Template.room.events({ } const channel = $(e.currentTarget).data('channel'); if (channel != null) { - if (RocketChat.Layout.isEmbedded()) { + if (Layout.isEmbedded()) { fireGlobalEvent('click-mention-link', { path: FlowRouter.path('channel', { name: channel }), channel }); } @@ -831,7 +848,7 @@ Template.room.events({ const roomData = Session.get(`roomData${ this._id }`); if (!roomData) { return false; } if (roomData.announcementDetails != null && roomData.announcementDetails.callback != null) { - return RocketChat.callbacks.run(roomData.announcementDetails.callback, this._id); + return callbacks.run(roomData.announcementDetails.callback, this._id); } else { modal.open({ title: t('Announcement'), @@ -853,9 +870,9 @@ Template.room.events({ if (!msg) { return; } - RocketChat.promises.run('onClientBeforeSendMessage', msgObject).then((msgObject) => { - const chatMessages = window.chatMessages[rid]; - if (chatMessages && chatMessages.processSlashCommand(msgObject)) { + promises.run('onClientBeforeSendMessage', msgObject).then((msgObject) => { + const _chatMessages = chatMessages[rid]; + if (_chatMessages && _chatMessages.processSlashCommand(msgObject)) { return; } @@ -941,7 +958,7 @@ Template.room.onCreated(function() { this.hasTokenpass = new ReactiveVar(false); - if (RocketChat.settings.get('API_Tokenpass_URL') !== '') { + if (settings.get('API_Tokenpass_URL') !== '') { Meteor.call('getChannelTokenpass', this.data._id, (error, result) => { if (!error) { this.hasTokenpass.set(!!(result && result.tokens && result.tokens.length > 0)); @@ -990,10 +1007,10 @@ Template.room.onDestroyed(function() { Template.room.onRendered(function() { // $(this.find('.messages-box .wrapper')).perfectScrollbar(); const rid = Session.get('openedRoom'); - if (!window.chatMessages[rid]) { - window.chatMessages[rid] = new ChatMessages; + if (!chatMessages[rid]) { + chatMessages[rid] = new ChatMessages; } - window.chatMessages[rid].init(this.firstNode); + chatMessages[rid].init(this.firstNode); // ScrollListener.init() const wrapper = this.find('.wrapper'); @@ -1137,7 +1154,7 @@ Template.room.onRendered(function() { } }); } - RocketChat.callbacks.add('streamMessage', (msg) => { + callbacks.add('streamMessage', (msg) => { if (rid !== msg.rid || msg.editedAt) { return; } @@ -1146,7 +1163,7 @@ Template.room.onRendered(function() { } }); Tracker.autorun(function() { - const room = RocketChat.models.Rooms.findOne({ _id: template.data._id }); + const room = Rooms.findOne({ _id: template.data._id }); if (!room) { FlowRouter.go('home'); } diff --git a/packages/rocketchat-ui/client/views/app/roomSearch.js b/packages/rocketchat-ui/client/views/app/roomSearch.js index 26c451fbf364..96034a2bcf4f 100644 --- a/packages/rocketchat-ui/client/views/app/roomSearch.js +++ b/packages/rocketchat-ui/client/views/app/roomSearch.js @@ -1,4 +1,5 @@ import { Template } from 'meteor/templating'; +import { roomTypes } from 'meteor/rocketchat:utils'; Template.roomSearch.helpers({ roomIcon() { @@ -6,7 +7,7 @@ Template.roomSearch.helpers({ return 'icon-at'; } if (this.type === 'r') { - return RocketChat.roomTypes.getIcon(this.t); + return roomTypes.getIcon(this.t); } }, userStatus() { diff --git a/packages/rocketchat-ui/client/views/app/secretURL.js b/packages/rocketchat-ui/client/views/app/secretURL.js index f8b1f3fc7091..cc85227ba271 100644 --- a/packages/rocketchat-ui/client/views/app/secretURL.js +++ b/packages/rocketchat-ui/client/views/app/secretURL.js @@ -3,11 +3,12 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; +import { settings } from 'meteor/rocketchat:settings'; Template.secretURL.helpers({ registrationAllowed() { const { hashIsValid } = Template.instance(); - return RocketChat.settings.get('Accounts_RegistrationForm') === 'Secret URL' && hashIsValid && hashIsValid.get(); + return settings.get('Accounts_RegistrationForm') === 'Secret URL' && hashIsValid && hashIsValid.get(); }, ready() { const instance = Template.instance(); diff --git a/packages/rocketchat-ui/client/views/cmsPage.js b/packages/rocketchat-ui/client/views/cmsPage.js index 27d1d4b37a3a..17ee242341c8 100644 --- a/packages/rocketchat-ui/client/views/cmsPage.js +++ b/packages/rocketchat-ui/client/views/cmsPage.js @@ -3,13 +3,14 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { Tracker } from 'meteor/tracker'; +import { settings } from 'meteor/rocketchat:settings'; Template.cmsPage.onCreated(function() { this.page = new ReactiveVar(''); return Tracker.autorun(() => { const cmsPage = Session.get('cmsPage'); if (cmsPage != null) { - return this.page.set(RocketChat.settings.get(cmsPage)); + return this.page.set(settings.get(cmsPage)); } }); }); diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index 13bf72e0cf5b..74339d3006bb 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -20,7 +20,6 @@ Package.onUse(function(api) { 'reactive-var', 'ecmascript', 'templating', - 'rocketchat:lib', 'rocketchat:ui-master', 'rocketchat:push', 'rocketchat:utils', @@ -52,7 +51,6 @@ Package.onUse(function(api) { api.addFiles('client/lib/fireEvent.js', 'client'); api.addFiles('client/lib/iframeCommands.js', 'client'); api.addFiles('client/lib/menu.js', 'client'); - api.addFiles('client/lib/modal.js', 'client'); api.addFiles('client/lib/Modernizr.js', 'client'); api.addFiles('client/lib/msgTyping.js', 'client'); api.addFiles('client/lib/notification.js', 'client'); @@ -147,6 +145,8 @@ Package.onUse(function(api) { api.addFiles('client/components/contextualBar.html', 'client'); api.addFiles('client/components/contextualBar.js', 'client'); + api.mainModule('client/index.js', 'client'); + api.export('fileUpload'); api.export('modal', 'client'); api.export('popover', 'client'); From 27c8d79ed3c870d8db77ddf962286a495dd9304d Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 13:32:15 -0200 Subject: [PATCH 56/59] Remove lib dependency in rocketchat:ui-sidenav --- packages/rocketchat-ui-sidenav/package.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rocketchat-ui-sidenav/package.js b/packages/rocketchat-ui-sidenav/package.js index 66e3d0decee0..a82212b38e48 100644 --- a/packages/rocketchat-ui-sidenav/package.js +++ b/packages/rocketchat-ui-sidenav/package.js @@ -14,7 +14,6 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', - 'rocketchat:lib', 'rocketchat:callbacks', 'rocketchat:authorization', 'rocketchat:settings', From ad557d85bf1e89969876b269087cbc8a62e8d245 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 13:32:53 -0200 Subject: [PATCH 57/59] Remove dependency between lib and ui-vrecord --- packages/rocketchat-ui-vrecord/package.js | 3 ++- packages/rocketchat-ui-vrecord/server/settings.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/rocketchat-ui-vrecord/package.js b/packages/rocketchat-ui-vrecord/package.js index fca0a279381d..374758fdfc1a 100644 --- a/packages/rocketchat-ui-vrecord/package.js +++ b/packages/rocketchat-ui-vrecord/package.js @@ -11,7 +11,8 @@ Package.onUse(function(api) { 'ecmascript', 'templating', 'tracker', - 'rocketchat:lib', + 'rocketchat:settings', + 'rocketchat:ui', ]); api.addFiles('client/vrecord.css', 'client'); api.mainModule('server/index.js', 'server'); diff --git a/packages/rocketchat-ui-vrecord/server/settings.js b/packages/rocketchat-ui-vrecord/server/settings.js index 88f2f6a907a8..ab10627c3478 100644 --- a/packages/rocketchat-ui-vrecord/server/settings.js +++ b/packages/rocketchat-ui-vrecord/server/settings.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; -RocketChat.settings.addGroup('Message', function() { +settings.addGroup('Message', function() { this.add('Message_VideoRecorderEnabled', true, { type: 'boolean', public: true, From 9837311fc827780ff2cabeee863ae3d67c63db34 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 13:33:19 -0200 Subject: [PATCH 58/59] Add logger dependency in file-upload --- packages/rocketchat-file-upload/package.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-file-upload/package.js b/packages/rocketchat-file-upload/package.js index 3d2b1d8abae4..4d19339dd795 100644 --- a/packages/rocketchat-file-upload/package.js +++ b/packages/rocketchat-file-upload/package.js @@ -20,6 +20,7 @@ Package.onUse(function(api) { 'rocketchat:settings', 'rocketchat:callbacks', 'rocketchat:authorization', + 'rocketchat:logger', 'random', 'accounts-base', 'tracker', From 2fa35912725aaec0bb04e6a1bb8e36e2a2b13b64 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 11 Jan 2019 17:01:31 -0200 Subject: [PATCH 59/59] Revert commented test file --- tests/end-to-end/ui/00-login.js | 326 ++++++++++++++++---------------- 1 file changed, 163 insertions(+), 163 deletions(-) diff --git a/tests/end-to-end/ui/00-login.js b/tests/end-to-end/ui/00-login.js index 47c4ee929a3c..294b8c13d72a 100644 --- a/tests/end-to-end/ui/00-login.js +++ b/tests/end-to-end/ui/00-login.js @@ -1,163 +1,163 @@ -// import loginPage from '../../pageobjects/login.page'; -// import setupWizard from '../../pageobjects/setup-wizard.page'; - -// describe('[Login]', () => { -// before(() => { -// loginPage.open(); -// // This Can Cause Timeouts erros if the server is slow so it should have a big wait -// loginPage.emailOrUsernameField.waitForVisible(15000); -// }); - -// describe('[Render]', () => { -// it('it should show email / username field', () => { -// loginPage.emailOrUsernameField.isVisible().should.be.true; -// }); - -// it('it should show password field', () => { -// loginPage.passwordField.isVisible().should.be.true; -// }); - -// it('it should show submit button', () => { -// loginPage.submitButton.isVisible().should.be.true; -// }); - -// it('it should show register button', () => { -// loginPage.registerButton.isVisible().should.be.true; -// }); - -// it('it should show forgot password button', () => { -// loginPage.forgotPasswordButton.isVisible().should.be.true; -// }); - -// it('it should not show name field', () => { -// loginPage.nameField.isVisible().should.be.false; -// }); - -// it('it should not show email field', () => { -// loginPage.emailField.isVisible().should.be.false; -// }); - -// it('it should not show confirm password field', () => { -// loginPage.confirmPasswordField.isVisible().should.be.false; -// }); - -// it('it should not show back to login button', () => { -// loginPage.backToLoginButton.isVisible().should.be.false; -// }); -// }); - -// describe('[Required Fields]', () => { -// before(() => { -// loginPage.submit(); -// }); - -// describe('email / username: ', () => { -// it('it should be required', () => { -// loginPage.emailOrUsernameField.getAttribute('class').should.contain('error'); -// loginPage.emailOrUsernameInvalidText.getText().should.not.be.empty; -// }); -// }); - -// describe('password: ', () => { -// it('it should be required', () => { -// loginPage.passwordField.getAttribute('class').should.contain('error'); -// loginPage.passwordInvalidText.getText().should.not.be.empty; -// }); -// }); -// }); -// }); - -// describe('[Setup Wizard]', () => { -// before(() => { -// setupWizard.login(); -// setupWizard.organizationType.waitForVisible(15000); -// }); - -// describe('[Render - Step 1]', () => { -// it('it should show organization type', () => { -// setupWizard.organizationType.isVisible().should.be.true; -// }); - -// it('it should show organization name', () => { -// setupWizard.organizationName.isVisible().should.be.true; -// }); - -// it('it should show industry', () => { -// setupWizard.industry.isVisible().should.be.true; -// }); - -// it('it should show size', () => { -// setupWizard.size.isVisible().should.be.true; -// }); - -// it('it should show country', () => { -// setupWizard.country.isVisible().should.be.true; -// }); - -// it('it should show website', () => { -// setupWizard.website.isVisible().should.be.true; -// }); - -// after(() => { -// setupWizard.goNext(); -// }); -// }); - -// describe('[Render - Step 2]', () => { -// it('it should show site name', () => { -// setupWizard.siteName.isVisible().should.be.true; -// }); - -// it('it should show language', () => { -// setupWizard.language.isVisible().should.be.true; -// }); - -// it('it should server type', () => { -// setupWizard.serverType.isVisible().should.be.true; -// }); - -// after(() => { -// setupWizard.goNext(); -// }); -// }); - -// describe('[Render - Step 3]', () => { -// it('it should have option for registered server', () => { -// setupWizard.registeredServer.isExisting().should.be.true; -// }); - -// it('it should have option for standalone server', () => { -// setupWizard.standaloneServer.isExisting().should.be.true; -// }); - -// it('it should check option for registered server by default', () => { -// setupWizard.registeredServer.isSelected().should.be.true; -// }); - -// after(() => { -// setupWizard.goNext(); -// }); -// }); - -// describe('[Render - Final Step]', () => { -// it('it should render "Go to your workspace button', () => { -// setupWizard.goToWorkspace.waitForVisible(20000); -// setupWizard.goToWorkspace.isVisible().should.be.true; -// }); - -// after(() => { -// setupWizard.goToHome(); -// }); -// }); - -// after(() => { -// browser.execute(function() { -// const user = Meteor.user(); -// Meteor.logout(() => { -// RocketChat.callbacks.run('afterLogoutCleanUp', user); -// Meteor.call('logoutCleanUp', user); -// FlowRouter.go('home'); -// }); -// }); -// }); -// }); +import loginPage from '../../pageobjects/login.page'; +import setupWizard from '../../pageobjects/setup-wizard.page'; + +describe('[Login]', () => { + before(() => { + loginPage.open(); + // This Can Cause Timeouts erros if the server is slow so it should have a big wait + loginPage.emailOrUsernameField.waitForVisible(15000); + }); + + describe('[Render]', () => { + it('it should show email / username field', () => { + loginPage.emailOrUsernameField.isVisible().should.be.true; + }); + + it('it should show password field', () => { + loginPage.passwordField.isVisible().should.be.true; + }); + + it('it should show submit button', () => { + loginPage.submitButton.isVisible().should.be.true; + }); + + it('it should show register button', () => { + loginPage.registerButton.isVisible().should.be.true; + }); + + it('it should show forgot password button', () => { + loginPage.forgotPasswordButton.isVisible().should.be.true; + }); + + it('it should not show name field', () => { + loginPage.nameField.isVisible().should.be.false; + }); + + it('it should not show email field', () => { + loginPage.emailField.isVisible().should.be.false; + }); + + it('it should not show confirm password field', () => { + loginPage.confirmPasswordField.isVisible().should.be.false; + }); + + it('it should not show back to login button', () => { + loginPage.backToLoginButton.isVisible().should.be.false; + }); + }); + + describe('[Required Fields]', () => { + before(() => { + loginPage.submit(); + }); + + describe('email / username: ', () => { + it('it should be required', () => { + loginPage.emailOrUsernameField.getAttribute('class').should.contain('error'); + loginPage.emailOrUsernameInvalidText.getText().should.not.be.empty; + }); + }); + + describe('password: ', () => { + it('it should be required', () => { + loginPage.passwordField.getAttribute('class').should.contain('error'); + loginPage.passwordInvalidText.getText().should.not.be.empty; + }); + }); + }); +}); + +describe('[Setup Wizard]', () => { + before(() => { + setupWizard.login(); + setupWizard.organizationType.waitForVisible(15000); + }); + + describe('[Render - Step 1]', () => { + it('it should show organization type', () => { + setupWizard.organizationType.isVisible().should.be.true; + }); + + it('it should show organization name', () => { + setupWizard.organizationName.isVisible().should.be.true; + }); + + it('it should show industry', () => { + setupWizard.industry.isVisible().should.be.true; + }); + + it('it should show size', () => { + setupWizard.size.isVisible().should.be.true; + }); + + it('it should show country', () => { + setupWizard.country.isVisible().should.be.true; + }); + + it('it should show website', () => { + setupWizard.website.isVisible().should.be.true; + }); + + after(() => { + setupWizard.goNext(); + }); + }); + + describe('[Render - Step 2]', () => { + it('it should show site name', () => { + setupWizard.siteName.isVisible().should.be.true; + }); + + it('it should show language', () => { + setupWizard.language.isVisible().should.be.true; + }); + + it('it should server type', () => { + setupWizard.serverType.isVisible().should.be.true; + }); + + after(() => { + setupWizard.goNext(); + }); + }); + + describe('[Render - Step 3]', () => { + it('it should have option for registered server', () => { + setupWizard.registeredServer.isExisting().should.be.true; + }); + + it('it should have option for standalone server', () => { + setupWizard.standaloneServer.isExisting().should.be.true; + }); + + it('it should check option for registered server by default', () => { + setupWizard.registeredServer.isSelected().should.be.true; + }); + + after(() => { + setupWizard.goNext(); + }); + }); + + describe('[Render - Final Step]', () => { + it('it should render "Go to your workspace button', () => { + setupWizard.goToWorkspace.waitForVisible(20000); + setupWizard.goToWorkspace.isVisible().should.be.true; + }); + + after(() => { + setupWizard.goToHome(); + }); + }); + + after(() => { + browser.execute(function() { + const user = Meteor.user(); + Meteor.logout(() => { + RocketChat.callbacks.run('afterLogoutCleanUp', user); + Meteor.call('logoutCleanUp', user); + FlowRouter.go('home'); + }); + }); + }); +});