From 882a8d276d04c544392bb936d23c526293a39b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sun, 24 Sep 2017 00:34:37 +0200 Subject: [PATCH 01/23] enable channel settings for custom room types --- .../client/views/channelSettings.js | 6 +++- .../server/functions/saveRoomName.js | 10 +++--- .../rocketchat-lib/lib/roomTypesCommon.js | 36 ++++++++++++++++++- .../client/rooms/adminRooms.js | 24 +++++++++---- server/methods/eraseRoom.js | 6 +++- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index d2ac089a722c..59e473bd3068 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -1,3 +1,4 @@ +/* globals RocketChat */ import toastr from 'toastr'; Template.channelSettings.helpers({ @@ -164,7 +165,10 @@ Template.channelSettings.onCreated(function() { save(value, room) { let nameValidation; if (!RocketChat.authz.hasAllPermission('edit-room', room._id) || (room.t !== 'c' && room.t !== 'p')) { - return toastr.error(t('error-not-allowed')); + // custom room types can explicitly enable/disable the channel settings support + if (!(RocketChat.roomTypes[room.t].allowChangeChannelSettings && RocketChat.roomTypes[room.t].allowChangeChannelSettings())) { + return toastr.error(t('error-not-allowed')); + } } if (!RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { try { diff --git a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js index 2696e1d5f079..6b5ddf4fb1de 100644 --- a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js +++ b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js @@ -1,10 +1,12 @@ - RocketChat.saveRoomName = function(rid, displayName, user, sendMessage = true) { const room = RocketChat.models.Rooms.findOneById(rid); if (room.t !== 'c' && room.t !== 'p') { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { - 'function': 'RocketChat.saveRoomdisplayName' - }); + // custom room types can explicitly enable/disable the channel settings support + if (!(RocketChat.roomTypes[room.t].allowChangeChannelSettings && RocketChat.roomTypes[room.t].allowChangeChannelSettings())) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { + 'function': 'RocketChat.saveRoomdisplayName' + }); + } } if (displayName === room.name) { return; diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index c0e669eff40d..94c5067e5aae 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -16,12 +16,21 @@ this.roomTypesCommon = class { name: route name action: route action function identifier: room type identifier - */ + Optional methods which can be redefined + getDisplayName(room): return room's name for the UI + allowChangeChannelSettings(room): Whether it's possible to use the common channel-settings-tab to change a room's settings + canBeDeleted(userId, room): Custom authorizations for whether a room can be deleted. If not implemented, `delete-{identifier}` will be checked for + */ add(identifier = Random.id(), order, config) { if (this.roomTypes[identifier] != null) { return false; } + + // apart from the structure, the config may optionally override default behaviour. + // In order to simplify implementation, default implementeations are added unless "redefined" + this._addDefaultImplementations(config); + if (order == null) { order = this.mainOrder + 10; this.mainOrder += 10; @@ -81,5 +90,30 @@ this.roomTypesCommon = class { } return FlowRouter.go(this.roomTypes[roomType].route.name, routeData, queryParams); } + + _addDefaultImplementations(config) { + if (!config.getDisplayName) { + config.getDisplayName = function(room) { + return room.name; + }; + } + + if (!config.allowChangeChannelSettings) { + config.allowChangeChannelSettings = function() { + return true; + }; + } + + if (!config.canBeDeleted) { + config.canBeDeleted = function(userId, room) { + /* this implementation is actually not necessary, since the generic authorisation check on delete-{identifier} + is already performed in /server/methods/eraseRoom.js. However, in order not to rely on that remaining as-is, + code the authorization check redundantly here. This shall also make consumption more easy and transparent + */ + RocketChat.authz.hasPermission(userId, `delete-${ room.t }`, room._id); + }; + } + } + }; export default this.roomTypesCommon; diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js index f535c4228813..f5fc6f7bf168 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js @@ -1,4 +1,4 @@ -/*globals RocketChatTabBar, AdminChatRoom */ +/*globals RocketChatTabBar, AdminChatRoom, RocketChat */ this.AdminChatRoom = new Mongo.Collection('rocketchat_room'); @@ -31,6 +31,10 @@ Template.adminRooms.helpers({ return this.name; } else if (this.t === 'd') { return this.usernames.join(' x '); + } else { + // custom room type + RocketChat.roomTypes[this.t].getDisplayName(this); + } }, type() { @@ -42,20 +46,26 @@ Template.adminRooms.helpers({ if (this.t === 'p') { return TAPi18n.__('Private_Groups'); } - }, + + // custom room type + return TAPi18n.__(RocketChat.roomTypes[this.t].label); + } + , 'default'() { if (this['default']) { return t('True'); } else { return t('False'); } - }, + } + , flexData() { return { tabBar: Template.instance().tabBar }; } -}); +}) +; Template.adminRooms.onCreated(function() { const instance = this; @@ -107,13 +117,13 @@ Template.adminRooms.onCreated(function() { filter = _.trim(filter); if (filter) { const filterReg = new RegExp(s.escapeRegExp(filter), 'i'); - query = { $or: [{ name: filterReg }, { t: 'd', usernames: filterReg } ]}; + query = {$or: [{name: filterReg}, {t: 'd', usernames: filterReg}]}; } if (types.length) { - query['t'] = { $in: types }; + query['t'] = {$in: types}; } const limit = instance.limit && instance.limit.get(); - return AdminChatRoom.find(query, { limit, sort: { 'default': -1, name: 1}}); + return AdminChatRoom.find(query, {limit, sort: {'default': -1, name: 1}}); }; this.getSearchTypes = function() { return _.map($('[name=room-type]:checked'), function(input) { diff --git a/server/methods/eraseRoom.js b/server/methods/eraseRoom.js index f6501542a292..1ff87da42913 100644 --- a/server/methods/eraseRoom.js +++ b/server/methods/eraseRoom.js @@ -1,3 +1,5 @@ +/* globals RocketChat */ + Meteor.methods({ eraseRoom(rid) { check(rid, String); @@ -17,7 +19,9 @@ Meteor.methods({ }); } - if (RocketChat.authz.hasPermission(fromId, `delete-${ room.t }`, rid)) { + if (RocketChat.authz.hasPermission(fromId, `delete-${ room.t }`, rid) + //support custom room types verifying deletion differently + || (RocketChat.roomTypes[room.t].canBeDeleted && RocketChat.roomTypes[room.t].canBeDeleted(fromId, room))) { RocketChat.models.Messages.removeByRoomId(rid); RocketChat.models.Subscriptions.removeByRoomId(rid); return RocketChat.models.Rooms.removeById(rid); From cf6ea72437dc85366acee2221d7ab8e9b9419f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sun, 24 Sep 2017 01:22:38 +0200 Subject: [PATCH 02/23] enable members list for custom room types --- .../rocketchat-lib/lib/roomTypesCommon.js | 42 +++++++++++++++++-- .../client/tabs/membersList.js | 13 +++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index 94c5067e5aae..fab92de4c653 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -18,9 +18,14 @@ this.roomTypesCommon = class { identifier: room type identifier Optional methods which can be redefined - getDisplayName(room): return room's name for the UI - allowChangeChannelSettings(room): Whether it's possible to use the common channel-settings-tab to change a room's settings - canBeDeleted(userId, room): Custom authorizations for whether a room can be deleted. If not implemented, `delete-{identifier}` will be checked for + getDisplayName(room): return room's name for the UI + allowChangeChannelSettings(room): Whether it's possible to use the common channel-settings-tab to change a room's settings + canBeDeleted(userId, room): Custom authorizations for whether a room can be deleted. If not implemented, `delete-{identifier}` will be checked for + supportMembersList(room): Whether the generic members list for managing a room's members shall be available + isGroupChat(): Whether the room type is a chat of a group of members + canAddUser(userId, room): Whether the given user is allowed to add users to the specified room + userDetailShowAll(room): Whether all room members' details be shown in the user info + userDetailShowAdmin(room): Whether admin-controls (change role etc.) shall be shown in the user info */ add(identifier = Random.id(), order, config) { if (this.roomTypes[identifier] != null) { @@ -113,6 +118,37 @@ this.roomTypesCommon = class { RocketChat.authz.hasPermission(userId, `delete-${ room.t }`, room._id); }; } + + if (!config.supportMembersList) { + config.supportMembersList = function() { + return true; + }; + } + + if (!config.isGroupChat) { + config.isGroupChat = function() { + return true; + }; + } + + if (!config.canAddUser) { + config.canAddUser = function() { + return true; + }; + } + + if (!config.userDetailShowAll) { + config.userDetailShowAll = function() { + return true; + }; + } + + if (!config.userDetailShowAdmin) { + config.userDetailShowAdmin = function() { + return true; + }; + } + } }; diff --git a/packages/rocketchat-ui-flextab/client/tabs/membersList.js b/packages/rocketchat-ui-flextab/client/tabs/membersList.js index de254edb32e6..ad9003980660 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/membersList.js +++ b/packages/rocketchat-ui-flextab/client/tabs/membersList.js @@ -6,7 +6,9 @@ Template.membersList.helpers({ }, isGroupChat() { - return ['c', 'p'].includes(ChatRoom.findOne(this.rid, { reactive: false }).t); + const room = ChatRoom.findOne(this.rid, { reactive: false }); + return ['c', 'p'].includes(room.t) + || (RocketChat.roomTypes[room.t].isGroupChat && RocketChat.roomTypes[room.t].isGroupChat()); }, isDirectChat() { @@ -89,7 +91,8 @@ Template.membersList.helpers({ switch (roomData.t) { case 'p': return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], this._id); case 'c': return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], this._id); - default: return false; + default: + return (RocketChat.roomTypes[roomData.t].canAddUser && RocketChat.roomTypes[roomData.t].canAddUser(roomData)) || false; } })(); }, @@ -136,8 +139,8 @@ Template.membersList.helpers({ tabBar: Template.currentData().tabBar, username: Template.instance().userDetail.get(), clear: Template.instance().clearUserDetail, - showAll: ['c', 'p'].includes(room != null ? room.t : undefined), - hideAdminControls: ['c', 'p', 'd'].includes(room != null ? room.t : undefined), + showAll: ['c', 'p'].includes(room != null ? room.t : undefined) || (RocketChat.roomTypes[room.t].userDetailShowAll && RocketChat.roomTypes[room.t].userDetailShowAll(room)), + hideAdminControls: ['c', 'p', 'd'].includes(room != null ? room.t : undefined) || !(RocketChat.roomTypes[room.t].userDetailShowAdmin && RocketChat.roomTypes[room.t].userDetailShowAdmin(room)), video: ['d'].includes(room != null ? room.t : undefined) }; }, @@ -163,7 +166,7 @@ Template.membersList.events({ const roomData = Session.get(`roomData${ template.data.rid }`); - if (['c', 'p'].includes(roomData.t)) { + if (['c', 'p'].includes(roomData.t) || (RocketChat.roomTypes[roomData.t].canAddUser && RocketChat.roomTypes[roomData.t].canAddUser(roomData))) { return Meteor.call('addUserToRoom', { rid: roomData._id, username: doc.username }, function(error) { if (error) { return handleError(error); From 0c53dd375ce00de20d9d1f5980643d39356df39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Mon, 25 Sep 2017 11:14:08 +0200 Subject: [PATCH 03/23] satisfy es-lint --- packages/rocketchat-ui-admin/client/rooms/adminRooms.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js index f5fc6f7bf168..ec6225640cca 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js @@ -49,16 +49,14 @@ Template.adminRooms.helpers({ // custom room type return TAPi18n.__(RocketChat.roomTypes[this.t].label); - } - , + }, 'default'() { if (this['default']) { return t('True'); } else { return t('False'); } - } - , + }, flexData() { return { tabBar: Template.instance().tabBar From 0e4f824a059d88f3e5f9ac29968a04712fb56316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Wed, 27 Sep 2017 23:20:31 +0200 Subject: [PATCH 04/23] added option to specify creation template for custom room types --- .../rocketchat-lib/client/lib/roomTypes.js | 4 ++- .../client/createCombinedFlex.html | 6 ++++ .../client/createCombinedFlex.js | 28 ++++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/rocketchat-lib/client/lib/roomTypes.js b/packages/rocketchat-lib/client/lib/roomTypes.js index b03ba4c964d9..92dad7df8105 100644 --- a/packages/rocketchat-lib/client/lib/roomTypes.js +++ b/packages/rocketchat-lib/client/lib/roomTypes.js @@ -110,5 +110,7 @@ RocketChat.roomTypes = new class extends roomTypesCommon { } return this.roomTypes[roomType].notSubscribedTpl; } - + getCreationTemplate(roomType) { + return this.roomTypes[roomType].creationTemplate; + } }; diff --git a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html index bce0f8330976..ef8e127876dc 100644 --- a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html +++ b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html @@ -1,4 +1,7 @@ diff --git a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js index 386af43b7801..feba662fe9ea 100644 --- a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js +++ b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js @@ -32,7 +32,7 @@ Template.createCombinedFlex.helpers({ exceptions: [Meteor.user().username].concat(Template.instance().selectedUsers.get()) }, selector(match) { - return { term: match }; + return {term: match}; }, sort: 'username' } @@ -44,6 +44,20 @@ Template.createCombinedFlex.helpers({ }, privateSwitchChecked() { return RocketChat.authz.hasAllPermission('create-c') ? '' : 'checked'; + }, + roomTypesBeforeStandard() { + return RocketChat.roomTypes.filter( + (roomType) => RocketChat.roomTypesOrder[roomType.identifier].order < RocketChat.roomTypesOrder['c'].order + ).map((roomType) => { + creationTemplate: roomType.getCreationTemplate(roomType); + }); + }, + roomTypesAfterStandard() { + return RocketChat.roomTypes.filter( + (roomType) => RocketChat.roomTypesOrder[roomType.identifier].order > RocketChat.roomTypesOrder['d'].order + ).map((roomType) => { + creationTemplate: roomType.getCreationTemplate(roomType); + }); } }); @@ -112,15 +126,15 @@ Template.createCombinedFlex.events({ return Meteor.call(createRoute, name, instance.selectedUsers.get(), readOnly, function(err, result) { if (err) { if (err.error === 'error-invalid-room-name') { - instance.error.set({ invalid: true }); + instance.error.set({invalid: true}); return; } if (err.error === 'error-duplicate-channel-name') { - instance.error.set({ duplicate: true }); + instance.error.set({duplicate: true}); return; } if (err.error === 'error-archived-duplicate-name') { - instance.error.set({ archivedduplicate: true }); + instance.error.set({archivedduplicate: true}); return; } else { return handleError(err); @@ -130,13 +144,13 @@ Template.createCombinedFlex.events({ SideNav.closeFlex(() => instance.clearForm()); if (!privateGroup) { - RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); + RocketChat.callbacks.run('aftercreateCombined', {_id: result.rid, name: result.name}); } - return FlowRouter.go(successRoute, { name: result.name }, FlowRouter.current().queryParams); + return FlowRouter.go(successRoute, {name: result.name}, FlowRouter.current().queryParams); }); } else { - return instance.error.set({ fields: err }); + return instance.error.set({fields: err}); } } }); From 929e84663d31b254d995bb37eda3cd5b60ace51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Thu, 28 Sep 2017 08:29:54 +0200 Subject: [PATCH 05/23] custom room types: prevent renaming --- .../server/functions/saveRoomName.js | 6 +++--- packages/rocketchat-lib/lib/roomTypesCommon.js | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js index 6b5ddf4fb1de..f3ce2014d6c5 100644 --- a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js +++ b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js @@ -1,10 +1,10 @@ RocketChat.saveRoomName = function(rid, displayName, user, sendMessage = true) { const room = RocketChat.models.Rooms.findOneById(rid); if (room.t !== 'c' && room.t !== 'p') { - // custom room types can explicitly enable/disable the channel settings support - if (!(RocketChat.roomTypes[room.t].allowChangeChannelSettings && RocketChat.roomTypes[room.t].allowChangeChannelSettings())) { + // custom room types can explicitly state they don't want to be renamed + if (!(RocketChat.roomTypes[room.t].preventRenaming && RocketChat.roomTypes[room.t].preventRenaming())) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { - 'function': 'RocketChat.saveRoomdisplayName' + 'function': 'RocketChat.saveRoomName' }); } } diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index fab92de4c653..0370aa6ce10c 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -26,6 +26,7 @@ this.roomTypesCommon = class { canAddUser(userId, room): Whether the given user is allowed to add users to the specified room userDetailShowAll(room): Whether all room members' details be shown in the user info userDetailShowAdmin(room): Whether admin-controls (change role etc.) shall be shown in the user info + preventRenaming(room): Whether it shall be impossible to rename the room */ add(identifier = Random.id(), order, config) { if (this.roomTypes[identifier] != null) { @@ -149,6 +150,11 @@ this.roomTypesCommon = class { }; } + if (!config.preventRenaming) { + config.preventRenaming = function() { + return false; + }; + } } }; From 3321156b3237b4253d1a33c30749d1dd951e1b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Thu, 28 Sep 2017 08:40:15 +0200 Subject: [PATCH 06/23] custom room types: include in global search --- .../rocketchat-lib/lib/roomTypesCommon.js | 7 ++++++ .../rocketchat-lib/server/models/Rooms.js | 6 +++-- server/publications/spotlight.js | 23 ++++++++++++++----- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index 0370aa6ce10c..602be723a8b9 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -27,6 +27,7 @@ this.roomTypesCommon = class { userDetailShowAll(room): Whether all room members' details be shown in the user info userDetailShowAdmin(room): Whether admin-controls (change role etc.) shall be shown in the user info preventRenaming(room): Whether it shall be impossible to rename the room + includeInRoomSearch(): Whether rooms of this type shall be included into the result list when searching for "rooms" */ add(identifier = Random.id(), order, config) { if (this.roomTypes[identifier] != null) { @@ -155,6 +156,12 @@ this.roomTypesCommon = class { return false; }; } + + if (!config.includeInRoomSearch) { + config.includeInRoomSearch = function() { + return true; + }; + } } }; diff --git a/packages/rocketchat-lib/server/models/Rooms.js b/packages/rocketchat-lib/server/models/Rooms.js index 6b1f9f7e21fc..a7161ca49b0c 100644 --- a/packages/rocketchat-lib/server/models/Rooms.js +++ b/packages/rocketchat-lib/server/models/Rooms.js @@ -257,9 +257,11 @@ class ModelRooms extends RocketChat.models._Base { return this._db.find(query, options); } - findByNameAndTypeNotContainingUsername(name, type, username, options) { + findByNameAndTypesNotContainingUsername(name, types, username, options) { const query = { - t: type, + t: { + $in: types + }, name, usernames: { $ne: username diff --git a/server/publications/spotlight.js b/server/publications/spotlight.js index 8f5b0cfa287b..b69edcbabe79 100644 --- a/server/publications/spotlight.js +++ b/server/publications/spotlight.js @@ -47,14 +47,25 @@ Meteor.methods({ username: 1 }).username; - result.rooms = RocketChat.models.Rooms.findByNameAndTypeNotContainingUsername(regex, 'c', username, roomOptions).fetch(); + let searchableRoomTypes = ['c']; + searchableRoomTypes = searchableRoomTypes.concat(RocketChat.roomTypes + .filter((roomType) => roomType.includeInRoomSearch && roomType.includeInRoomSearch()) + .map((roomType) => roomType.identifier) + ); + + result.rooms = RocketChat.models.Rooms.findByNameAndTypesNotContainingUsername(regex, searchableRoomTypes, username, roomOptions).fetch(); } } else if (type.users === true && rid) { - const subscriptions = RocketChat.models.Subscriptions.find({rid, 'u.username':{ - $regex: regex, - $nin:[...usernames, Meteor.user().username] - }}, {limit:userOptions.limit}).fetch().map(({u}) => u._id); - result.users = RocketChat.models.Users.find({_id:{$in:subscriptions}}, {fields:userOptions.fields, sort: userOptions.sort}).fetch(); + const subscriptions = RocketChat.models.Subscriptions.find({ + rid, 'u.username': { + $regex: regex, + $nin: [...usernames, Meteor.user().username] + } + }, {limit: userOptions.limit}).fetch().map(({u}) => u._id); + result.users = RocketChat.models.Users.find({_id: {$in: subscriptions}}, { + fields: userOptions.fields, + sort: userOptions.sort + }).fetch(); } return result; From 9d2381e2e5a1e128d223777eb35e2fae5b517d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Fri, 29 Sep 2017 10:04:27 +0200 Subject: [PATCH 07/23] Custom room type: handle not-provided creation template --- packages/rocketchat-lib/client/lib/roomTypes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/rocketchat-lib/client/lib/roomTypes.js b/packages/rocketchat-lib/client/lib/roomTypes.js index 92dad7df8105..8a551189de89 100644 --- a/packages/rocketchat-lib/client/lib/roomTypes.js +++ b/packages/rocketchat-lib/client/lib/roomTypes.js @@ -111,6 +111,9 @@ RocketChat.roomTypes = new class extends roomTypesCommon { return this.roomTypes[roomType].notSubscribedTpl; } getCreationTemplate(roomType) { + if (this.roomTypes[roomType] && !this.roomTypes[roomType].creationTemplate) { + return false; + } return this.roomTypes[roomType].creationTemplate; } }; From c62f9e8cd42bf1759f41aefe3aacfcad784211e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sat, 30 Sep 2017 15:22:38 +0200 Subject: [PATCH 08/23] Enable custom room type creation templates --- .../rocketchat-lib/client/lib/roomTypes.js | 6 ---- .../rocketchat-lib/lib/roomTypesCommon.js | 2 +- .../startup/defaultRoomTypes.js | 35 ++++++++++++++----- .../client/createCombinedFlex.html | 6 ---- .../client/createCombinedFlex.js | 28 ++++----------- .../client/views/app/createChannel.html | 6 ++++ .../client/views/app/createChannel.js | 34 ++++++++++++++---- server/publications/spotlight.js | 8 ++--- 8 files changed, 71 insertions(+), 54 deletions(-) diff --git a/packages/rocketchat-lib/client/lib/roomTypes.js b/packages/rocketchat-lib/client/lib/roomTypes.js index 8a551189de89..42193496d27a 100644 --- a/packages/rocketchat-lib/client/lib/roomTypes.js +++ b/packages/rocketchat-lib/client/lib/roomTypes.js @@ -110,10 +110,4 @@ RocketChat.roomTypes = new class extends roomTypesCommon { } return this.roomTypes[roomType].notSubscribedTpl; } - getCreationTemplate(roomType) { - if (this.roomTypes[roomType] && !this.roomTypes[roomType].creationTemplate) { - return false; - } - return this.roomTypes[roomType].creationTemplate; - } }; diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index 602be723a8b9..80d497a8e03f 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -117,7 +117,7 @@ this.roomTypesCommon = class { is already performed in /server/methods/eraseRoom.js. However, in order not to rely on that remaining as-is, code the authorization check redundantly here. This shall also make consumption more easy and transparent */ - RocketChat.authz.hasPermission(userId, `delete-${ room.t }`, room._id); + return RocketChat.authz.hasPermission(userId, `delete-${ room.t }`, room._id); }; } diff --git a/packages/rocketchat-lib/startup/defaultRoomTypes.js b/packages/rocketchat-lib/startup/defaultRoomTypes.js index 1c98e5eec705..b9f36d20d7fe 100644 --- a/packages/rocketchat-lib/startup/defaultRoomTypes.js +++ b/packages/rocketchat-lib/startup/defaultRoomTypes.js @@ -6,13 +6,19 @@ RocketChat.roomTypes.add('unread', 10, { const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; return preferences.roomsListExhibitionMode === 'unread'; }, - label: 'Unread' + label: 'Unread', + includeInRoomSearch() { + return false; + } }); RocketChat.roomTypes.add('f', 20, { header: 'favorite', icon: 'star', - label: 'Favorites' + label: 'Favorites', + includeInRoomSearch() { + return false; + } }); // activity @@ -22,7 +28,10 @@ RocketChat.roomTypes.add('activity', 30, { const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; return preferences.roomsListExhibitionMode === 'activity'; }, - label: 'Conversations' + label: 'Conversations', + includeInRoomSearch() { + return false; + } }); RocketChat.roomTypes.add('channels', 30, { @@ -67,7 +76,7 @@ RocketChat.roomTypes.add('c', 30, { }, showJoinLink(roomId) { - return !!ChatRoom.findOne({ _id: roomId, t: 'c' }); + return !!ChatRoom.findOne({_id: roomId, t: 'c'}); } }); @@ -102,6 +111,10 @@ RocketChat.roomTypes.add('p', 40, { const user = Meteor.user(); const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && RocketChat.authz.hasAllPermission('view-p-room'); + }, + + includeInRoomSearch() { + return false; } }); @@ -117,7 +130,7 @@ RocketChat.roomTypes.add('d', 50, { return openRoom('d', params.username); }, link(sub) { - return { username: sub.name }; + return {username: sub.name}; } }, @@ -134,7 +147,7 @@ RocketChat.roomTypes.add('d', 50, { }, roomName(roomData) { - const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1, fname: 1 } }); + const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1, fname: 1}}); if (!subscription) { return ''; } @@ -147,7 +160,7 @@ RocketChat.roomTypes.add('d', 50, { secondaryRoomName(roomData) { if (RocketChat.settings.get('UI_Use_Real_Name')) { - const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1 } }); + const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1}}); return subscription && subscription.name; } }, @@ -160,8 +173,14 @@ RocketChat.roomTypes.add('d', 50, { getUserStatus(roomId) { const subscription = RocketChat.models.Subscriptions.findOne({rid: roomId}); - if (subscription == null) { return; } + if (subscription == null) { + return; + } return Session.get(`user_${ subscription.name }_status`); + }, + + includeInRoomSearch() { + return false; } }); diff --git a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html index ef8e127876dc..bce0f8330976 100644 --- a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html +++ b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.html @@ -1,7 +1,4 @@ diff --git a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js index feba662fe9ea..386af43b7801 100644 --- a/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js +++ b/packages/rocketchat-ui-sidenav/client/createCombinedFlex.js @@ -32,7 +32,7 @@ Template.createCombinedFlex.helpers({ exceptions: [Meteor.user().username].concat(Template.instance().selectedUsers.get()) }, selector(match) { - return {term: match}; + return { term: match }; }, sort: 'username' } @@ -44,20 +44,6 @@ Template.createCombinedFlex.helpers({ }, privateSwitchChecked() { return RocketChat.authz.hasAllPermission('create-c') ? '' : 'checked'; - }, - roomTypesBeforeStandard() { - return RocketChat.roomTypes.filter( - (roomType) => RocketChat.roomTypesOrder[roomType.identifier].order < RocketChat.roomTypesOrder['c'].order - ).map((roomType) => { - creationTemplate: roomType.getCreationTemplate(roomType); - }); - }, - roomTypesAfterStandard() { - return RocketChat.roomTypes.filter( - (roomType) => RocketChat.roomTypesOrder[roomType.identifier].order > RocketChat.roomTypesOrder['d'].order - ).map((roomType) => { - creationTemplate: roomType.getCreationTemplate(roomType); - }); } }); @@ -126,15 +112,15 @@ Template.createCombinedFlex.events({ return Meteor.call(createRoute, name, instance.selectedUsers.get(), readOnly, function(err, result) { if (err) { if (err.error === 'error-invalid-room-name') { - instance.error.set({invalid: true}); + instance.error.set({ invalid: true }); return; } if (err.error === 'error-duplicate-channel-name') { - instance.error.set({duplicate: true}); + instance.error.set({ duplicate: true }); return; } if (err.error === 'error-archived-duplicate-name') { - instance.error.set({archivedduplicate: true}); + instance.error.set({ archivedduplicate: true }); return; } else { return handleError(err); @@ -144,13 +130,13 @@ Template.createCombinedFlex.events({ SideNav.closeFlex(() => instance.clearForm()); if (!privateGroup) { - RocketChat.callbacks.run('aftercreateCombined', {_id: result.rid, name: result.name}); + RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); } - return FlowRouter.go(successRoute, {name: result.name}, FlowRouter.current().queryParams); + return FlowRouter.go(successRoute, { name: result.name }, FlowRouter.current().queryParams); }); } else { - return instance.error.set({fields: err}); + return instance.error.set({ fields: err }); } } }); diff --git a/packages/rocketchat-ui/client/views/app/createChannel.html b/packages/rocketchat-ui/client/views/app/createChannel.html index 23b9e787f075..c0cfd7931900 100644 --- a/packages/rocketchat-ui/client/views/app/createChannel.html +++ b/packages/rocketchat-ui/client/views/app/createChannel.html @@ -1,5 +1,8 @@ diff --git a/packages/rocketchat-ui/client/views/app/createChannel.js b/packages/rocketchat-ui/client/views/app/createChannel.js index f8910a5f6fc4..890187cb3781 100644 --- a/packages/rocketchat-ui/client/views/app/createChannel.js +++ b/packages/rocketchat-ui/client/views/app/createChannel.js @@ -44,7 +44,7 @@ Template.createChannel.helpers({ autocomplete(key) { const instance = Template.instance(); const param = instance.ac[key]; - return typeof param === 'function' ? param.apply(instance.ac): param; + return typeof param === 'function' ? param.apply(instance.ac) : param; }, items() { return Template.instance().ac.filteredList(); @@ -96,6 +96,26 @@ Template.createChannel.helpers({ }, iconType() { return Template.instance().type.get() === 'p' ? 'lock' : 'hashtag'; + }, + roomTypesBeforeStandard() { + const orderLow = RocketChat.roomTypes.roomTypesOrder.filter((roomTypeOrder) => roomTypeOrder.identifier === 'c')[0].order; + return RocketChat.roomTypes.roomTypesOrder.filter( + (roomTypeOrder) => roomTypeOrder.order < orderLow + ).map( + (roomTypeOrder) => { + return RocketChat.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( + (roomTypeOrder) => roomTypeOrder.order > orderHigh + ).map( + (roomTypeOrder) => { + return RocketChat.roomTypes.roomTypes[roomTypeOrder.identifier]; + } + ).filter((roomType) => roomType.creationTemplate); } }); @@ -160,10 +180,10 @@ Template.createChannel.events({ } if (!isPrivate) { - RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); + RocketChat.callbacks.run('aftercreateCombined', {_id: result.rid, name: result.name}); } - return FlowRouter.go(isPrivate ? 'group' : 'channel', { name: result.name }, FlowRouter.current().queryParams); + return FlowRouter.go(isPrivate ? 'group' : 'channel', {name: result.name}, FlowRouter.current().queryParams); }); return false; } @@ -185,7 +205,7 @@ Template.createChannel.onRendered(function() { Template.createChannel.onCreated(function() { this.selectedUsers = new ReactiveVar([]); - const filter = {exceptions :[Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username))}; + const filter = {exceptions: [Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username))}; // this.onViewRead:??y(function() { Deps.autorun(() => { filter.exceptions = [Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username)); @@ -211,7 +231,7 @@ Template.createChannel.onCreated(function() { this.ac = new AutoComplete( { - selector:{ + selector: { item: '.rc-popup-list__item', container: '.rc-popup-list__list' }, @@ -220,7 +240,7 @@ Template.createChannel.onCreated(function() { inputDelay: 300, rules: [ { - // @TODO maybe change this 'collection' and/or template + // @TODO maybe change this 'collection' and/or template collection: 'UserAndRoom', subscription: 'userAutocomplete', field: 'username', @@ -228,7 +248,7 @@ Template.createChannel.onCreated(function() { filter, doNotChangeWidth: false, selector(match) { - return { term: match }; + return {term: match}; }, sort: 'username' } diff --git a/server/publications/spotlight.js b/server/publications/spotlight.js index b69edcbabe79..fd7500ac2494 100644 --- a/server/publications/spotlight.js +++ b/server/publications/spotlight.js @@ -47,11 +47,9 @@ Meteor.methods({ username: 1 }).username; - let searchableRoomTypes = ['c']; - searchableRoomTypes = searchableRoomTypes.concat(RocketChat.roomTypes - .filter((roomType) => roomType.includeInRoomSearch && roomType.includeInRoomSearch()) - .map((roomType) => roomType.identifier) - ); + const searchableRoomTypes = Object.entries(RocketChat.roomTypes.roomTypes) + .filter((roomType)=>roomType[1].includeInRoomSearch()) + .map((roomType)=>roomType[0]); result.rooms = RocketChat.models.Rooms.findByNameAndTypesNotContainingUsername(regex, searchableRoomTypes, username, roomOptions).fetch(); } From 65c4eecbfbea839e0b2a1f0c5c6a89f0c54b9719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sun, 1 Oct 2017 09:02:12 +0200 Subject: [PATCH 09/23] Provide wrapper for room creation for better extensibility --- .../client/views/app/createChannel.html | 154 +++++++++--------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/packages/rocketchat-ui/client/views/app/createChannel.html b/packages/rocketchat-ui/client/views/app/createChannel.html index c0cfd7931900..6bd261588ccb 100644 --- a/packages/rocketchat-ui/client/views/app/createChannel.html +++ b/packages/rocketchat-ui/client/views/app/createChannel.html @@ -1,90 +1,98 @@ From 06226f238f27dee6235282de8f13dd8215072367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sun, 1 Oct 2017 13:29:43 +0200 Subject: [PATCH 10/23] Replace hard-coded room types with "interface"-method-calls --- .../client/views/channelSettings.js | 2 +- .../server/functions/saveRoomName.js | 12 ++-- .../rocketchat-lib/lib/roomTypesCommon.js | 16 ++--- .../startup/defaultRoomTypes.js | 67 ++++++++++++++----- .../client/rooms/adminRooms.js | 31 ++------- .../client/tabs/membersList.js | 16 ++--- .../client/views/app/createChannel.js | 14 ++-- server/methods/eraseRoom.js | 5 +- 8 files changed, 83 insertions(+), 80 deletions(-) diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index 59e473bd3068..eec05b13013a 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -54,7 +54,7 @@ Template.channelSettings.helpers({ } }); const roomType = room && room.t; - return roomType && RocketChat.authz.hasAtLeastOnePermission(`delete-${ roomType }`, this.rid); + return roomType && RocketChat.roomTypes.roomTypes[room.t].canBeDeleted(room); }, readOnly() { const room = ChatRoom.findOne(this.rid, { diff --git a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js index f3ce2014d6c5..72d86581f3cb 100644 --- a/packages/rocketchat-channel-settings/server/functions/saveRoomName.js +++ b/packages/rocketchat-channel-settings/server/functions/saveRoomName.js @@ -1,12 +1,10 @@ + RocketChat.saveRoomName = function(rid, displayName, user, sendMessage = true) { const room = RocketChat.models.Rooms.findOneById(rid); - if (room.t !== 'c' && room.t !== 'p') { - // custom room types can explicitly state they don't want to be renamed - if (!(RocketChat.roomTypes[room.t].preventRenaming && RocketChat.roomTypes[room.t].preventRenaming())) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { - 'function': 'RocketChat.saveRoomName' - }); - } + if (RocketChat.roomTypes.roomTypes[room.t].preventRenaming()) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { + 'function': 'RocketChat.saveRoomdisplayName' + }); } if (displayName === room.name) { return; diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js index 80d497a8e03f..b64434676653 100644 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ b/packages/rocketchat-lib/lib/roomTypesCommon.js @@ -112,12 +112,10 @@ this.roomTypesCommon = class { } if (!config.canBeDeleted) { - config.canBeDeleted = function(userId, room) { - /* this implementation is actually not necessary, since the generic authorisation check on delete-{identifier} - is already performed in /server/methods/eraseRoom.js. However, in order not to rely on that remaining as-is, - code the authorization check redundantly here. This shall also make consumption more easy and transparent - */ - return RocketChat.authz.hasPermission(userId, `delete-${ room.t }`, room._id); + config.canBeDeleted = function(room) { + return Meteor.isServer ? + RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`delete-${ room.t }`], room._id) : + RocketChat.authz.hasAtLeastOnePermission([`delete-${ room.t }`], room._id); }; } @@ -129,13 +127,13 @@ this.roomTypesCommon = class { if (!config.isGroupChat) { config.isGroupChat = function() { - return true; + return false; }; } if (!config.canAddUser) { config.canAddUser = function() { - return true; + return false; }; } @@ -159,7 +157,7 @@ this.roomTypesCommon = class { if (!config.includeInRoomSearch) { config.includeInRoomSearch = function() { - return true; + return false; }; } } diff --git a/packages/rocketchat-lib/startup/defaultRoomTypes.js b/packages/rocketchat-lib/startup/defaultRoomTypes.js index b9f36d20d7fe..56a187843405 100644 --- a/packages/rocketchat-lib/startup/defaultRoomTypes.js +++ b/packages/rocketchat-lib/startup/defaultRoomTypes.js @@ -6,19 +6,13 @@ RocketChat.roomTypes.add('unread', 10, { const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; return preferences.roomsListExhibitionMode === 'unread'; }, - label: 'Unread', - includeInRoomSearch() { - return false; - } + label: 'Unread' }); RocketChat.roomTypes.add('f', 20, { header: 'favorite', icon: 'star', - label: 'Favorites', - includeInRoomSearch() { - return false; - } + label: 'Favorites' }); // activity @@ -28,10 +22,7 @@ RocketChat.roomTypes.add('activity', 30, { const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; return preferences.roomsListExhibitionMode === 'activity'; }, - label: 'Conversations', - includeInRoomSearch() { - return false; - } + label: 'Conversations' }); RocketChat.roomTypes.add('channels', 30, { @@ -77,6 +68,30 @@ RocketChat.roomTypes.add('c', 30, { showJoinLink(roomId) { return !!ChatRoom.findOne({_id: roomId, t: 'c'}); + }, + + includeInRoomSearch() { + return true; + }, + + isGroupChat() { + return true; + }, + + canAddUser(room) { + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id) + }, + + userDetailShowAll() { + return true; + }, + + userDetailShowAdmin() { + return true; + }, + + getDisplayName(room) { + return room.name; } }); @@ -113,8 +128,24 @@ RocketChat.roomTypes.add('p', 40, { return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && RocketChat.authz.hasAllPermission('view-p-room'); }, - includeInRoomSearch() { - return false; + isGroupChat() { + return true; + }, + + canAddUser(room) { + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id) + }, + + userDetailShowAll() { + return true; + }, + + userDetailShowAdmin() { + return true; + }, + + getDisplayName(room) { + return room.name; } }); @@ -180,7 +211,11 @@ RocketChat.roomTypes.add('d', 50, { return Session.get(`user_${ subscription.name }_status`); }, - includeInRoomSearch() { - return false; + userDetailShowAdmin() { + return true; + }, + + getDisplayName(room) { + return room.usernames.join(' x '); } }); diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js index ec6225640cca..3ae6c2619e22 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js @@ -27,28 +27,10 @@ Template.adminRooms.helpers({ return rooms && rooms.count(); }, name() { - if (this.t === 'c' || this.t === 'p') { - return this.name; - } else if (this.t === 'd') { - return this.usernames.join(' x '); - } else { - // custom room type - RocketChat.roomTypes[this.t].getDisplayName(this); - - } + RocketChat.roomTypes.roomTypes[this.t].getDisplayName(this); }, type() { - if (this.t === 'c') { - return TAPi18n.__('Channel'); - } else if (this.t === 'd') { - return TAPi18n.__('Direct_Messages'); - } - if (this.t === 'p') { - return TAPi18n.__('Private_Groups'); - } - - // custom room type - return TAPi18n.__(RocketChat.roomTypes[this.t].label); + return TAPi18n.__(RocketChat.roomTypes.roomTypes[this.t].label); }, 'default'() { if (this['default']) { @@ -62,8 +44,7 @@ Template.adminRooms.helpers({ tabBar: Template.instance().tabBar }; } -}) -; +}); Template.adminRooms.onCreated(function() { const instance = this; @@ -115,13 +96,13 @@ Template.adminRooms.onCreated(function() { filter = _.trim(filter); if (filter) { const filterReg = new RegExp(s.escapeRegExp(filter), 'i'); - query = {$or: [{name: filterReg}, {t: 'd', usernames: filterReg}]}; + query = { $or: [{ name: filterReg }, { t: 'd', usernames: filterReg } ]}; } if (types.length) { - query['t'] = {$in: types}; + query['t'] = { $in: types }; } const limit = instance.limit && instance.limit.get(); - return AdminChatRoom.find(query, {limit, sort: {'default': -1, name: 1}}); + return AdminChatRoom.find(query, { limit, sort: { 'default': -1, name: 1}}); }; this.getSearchTypes = function() { return _.map($('[name=room-type]:checked'), function(input) { diff --git a/packages/rocketchat-ui-flextab/client/tabs/membersList.js b/packages/rocketchat-ui-flextab/client/tabs/membersList.js index ad9003980660..e73422f4beca 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/membersList.js +++ b/packages/rocketchat-ui-flextab/client/tabs/membersList.js @@ -7,8 +7,7 @@ Template.membersList.helpers({ isGroupChat() { const room = ChatRoom.findOne(this.rid, { reactive: false }); - return ['c', 'p'].includes(room.t) - || (RocketChat.roomTypes[room.t].isGroupChat && RocketChat.roomTypes[room.t].isGroupChat()); + return RocketChat.roomTypes.roomTypes[room.t].isGroupChat(); }, isDirectChat() { @@ -88,12 +87,7 @@ Template.membersList.helpers({ const roomData = Session.get(`roomData${ this._id }`); if (!roomData) { return ''; } return (() => { - switch (roomData.t) { - case 'p': return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], this._id); - case 'c': return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], this._id); - default: - return (RocketChat.roomTypes[roomData.t].canAddUser && RocketChat.roomTypes[roomData.t].canAddUser(roomData)) || false; - } + return RocketChat.roomTypes.roomTypes[roomData.t].canAddUser(roomData); })(); }, @@ -139,8 +133,8 @@ Template.membersList.helpers({ tabBar: Template.currentData().tabBar, username: Template.instance().userDetail.get(), clear: Template.instance().clearUserDetail, - showAll: ['c', 'p'].includes(room != null ? room.t : undefined) || (RocketChat.roomTypes[room.t].userDetailShowAll && RocketChat.roomTypes[room.t].userDetailShowAll(room)), - hideAdminControls: ['c', 'p', 'd'].includes(room != null ? room.t : undefined) || !(RocketChat.roomTypes[room.t].userDetailShowAdmin && RocketChat.roomTypes[room.t].userDetailShowAdmin(room)), + showAll: RocketChat.roomTypes.roomTypes[room.t].userDetailShowAll(room) || false, + hideAdminControls: RocketChat.roomTypes.roomTypes[room.t].userDetailShowAdmin(room) || false, video: ['d'].includes(room != null ? room.t : undefined) }; }, @@ -166,7 +160,7 @@ Template.membersList.events({ const roomData = Session.get(`roomData${ template.data.rid }`); - if (['c', 'p'].includes(roomData.t) || (RocketChat.roomTypes[roomData.t].canAddUser && RocketChat.roomTypes[roomData.t].canAddUser(roomData))) { + if (RocketChat.roomTypes.roomTypes[roomData.t].canAddUser(roomData)) { return Meteor.call('addUserToRoom', { rid: roomData._id, username: doc.username }, function(error) { if (error) { return handleError(error); diff --git a/packages/rocketchat-ui/client/views/app/createChannel.js b/packages/rocketchat-ui/client/views/app/createChannel.js index 890187cb3781..c5be0ec5693a 100644 --- a/packages/rocketchat-ui/client/views/app/createChannel.js +++ b/packages/rocketchat-ui/client/views/app/createChannel.js @@ -44,7 +44,7 @@ Template.createChannel.helpers({ autocomplete(key) { const instance = Template.instance(); const param = instance.ac[key]; - return typeof param === 'function' ? param.apply(instance.ac) : param; + return typeof param === 'function' ? param.apply(instance.ac): param; }, items() { return Template.instance().ac.filteredList(); @@ -180,10 +180,10 @@ Template.createChannel.events({ } if (!isPrivate) { - RocketChat.callbacks.run('aftercreateCombined', {_id: result.rid, name: result.name}); + RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name }); } - return FlowRouter.go(isPrivate ? 'group' : 'channel', {name: result.name}, FlowRouter.current().queryParams); + return FlowRouter.go(isPrivate ? 'group' : 'channel', { name: result.name }, FlowRouter.current().queryParams); }); return false; } @@ -205,7 +205,7 @@ Template.createChannel.onRendered(function() { Template.createChannel.onCreated(function() { this.selectedUsers = new ReactiveVar([]); - const filter = {exceptions: [Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username))}; + const filter = {exceptions :[Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username))}; // this.onViewRead:??y(function() { Deps.autorun(() => { filter.exceptions = [Meteor.user().username].concat(this.selectedUsers.get().map(u => u.username)); @@ -231,7 +231,7 @@ Template.createChannel.onCreated(function() { this.ac = new AutoComplete( { - selector: { + selector:{ item: '.rc-popup-list__item', container: '.rc-popup-list__list' }, @@ -240,7 +240,7 @@ Template.createChannel.onCreated(function() { inputDelay: 300, rules: [ { - // @TODO maybe change this 'collection' and/or template + // @TODO maybe change this 'collection' and/or template collection: 'UserAndRoom', subscription: 'userAutocomplete', field: 'username', @@ -248,7 +248,7 @@ Template.createChannel.onCreated(function() { filter, doNotChangeWidth: false, selector(match) { - return {term: match}; + return { term: match }; }, sort: 'username' } diff --git a/server/methods/eraseRoom.js b/server/methods/eraseRoom.js index 1ff87da42913..a9ac97d94ed3 100644 --- a/server/methods/eraseRoom.js +++ b/server/methods/eraseRoom.js @@ -10,7 +10,6 @@ Meteor.methods({ }); } - const fromId = Meteor.userId(); const room = RocketChat.models.Rooms.findOneById(rid); if (!room) { @@ -19,9 +18,7 @@ Meteor.methods({ }); } - if (RocketChat.authz.hasPermission(fromId, `delete-${ room.t }`, rid) - //support custom room types verifying deletion differently - || (RocketChat.roomTypes[room.t].canBeDeleted && RocketChat.roomTypes[room.t].canBeDeleted(fromId, room))) { + if (RocketChat.roomTypes.roomTypes[room.t].canBeDeleted(room)) { RocketChat.models.Messages.removeByRoomId(rid); RocketChat.models.Subscriptions.removeByRoomId(rid); return RocketChat.models.Rooms.removeById(rid); From b2a425f8e05d63d09ead516157497f765452c251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Mon, 2 Oct 2017 09:51:47 +0200 Subject: [PATCH 11/23] thx, codacy --- packages/rocketchat-lib/startup/defaultRoomTypes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-lib/startup/defaultRoomTypes.js b/packages/rocketchat-lib/startup/defaultRoomTypes.js index 56a187843405..d5e0f368b855 100644 --- a/packages/rocketchat-lib/startup/defaultRoomTypes.js +++ b/packages/rocketchat-lib/startup/defaultRoomTypes.js @@ -79,7 +79,7 @@ RocketChat.roomTypes.add('c', 30, { }, canAddUser(room) { - return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id) + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id); }, userDetailShowAll() { @@ -133,7 +133,7 @@ RocketChat.roomTypes.add('p', 40, { }, canAddUser(room) { - return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id) + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id); }, userDetailShowAll() { From 37c36726c3380f93b23d9824e9f63f82366d2d6b Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Thu, 12 Oct 2017 10:57:24 -0500 Subject: [PATCH 12/23] Room definitions are more typed but extended classes have full control over their definitions beyound the expected. --- .../.npm/package/npm-shrinkwrap.json | 63 +++-- .../rocketchat-lib/client/lib/roomTypes.js | 5 +- packages/rocketchat-lib/lib/RoomTypeConfig.js | 172 +++++++++++++ .../rocketchat-lib/lib/RoomTypesCommon.js | 93 +++++++ .../rocketchat-lib/lib/roomTypes/channels.js | 17 ++ .../lib/roomTypes/conversation.js | 17 ++ .../rocketchat-lib/lib/roomTypes/direct.js | 82 ++++++ .../rocketchat-lib/lib/roomTypes/favorite.js | 13 + .../rocketchat-lib/lib/roomTypes/index.js | 17 ++ .../rocketchat-lib/lib/roomTypes/private.js | 59 +++++ .../rocketchat-lib/lib/roomTypes/public.js | 64 +++++ .../rocketchat-lib/lib/roomTypes/unread.js | 19 ++ .../rocketchat-lib/lib/roomTypesCommon.js | 166 ------------ packages/rocketchat-lib/package.js | 15 +- .../rocketchat-lib/server/lib/roomTypes.js | 26 +- .../startup/defaultRoomTypes.js | 238 ++---------------- packages/rocketchat-livechat/roomType.js | 64 +++-- 17 files changed, 670 insertions(+), 460 deletions(-) create mode 100644 packages/rocketchat-lib/lib/RoomTypeConfig.js create mode 100644 packages/rocketchat-lib/lib/RoomTypesCommon.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/channels.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/conversation.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/direct.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/favorite.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/index.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/private.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/public.js create mode 100644 packages/rocketchat-lib/lib/roomTypes/unread.js delete mode 100644 packages/rocketchat-lib/lib/roomTypesCommon.js diff --git a/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json b/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json index 3485f52f8fb5..2617915b67c1 100644 --- a/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json +++ b/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json @@ -265,14 +265,7 @@ "gcp-metadata": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.3.1.tgz", - "from": "gcp-metadata@>=0.3.0 <0.4.0", - "dependencies": { - "retry-request": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.0.0.tgz", - "from": "retry-request@>=3.0.0 <4.0.0" - } - } + "from": "gcp-metadata@>=0.3.0 <0.4.0" }, "gcs-resumable-upload": { "version": "0.8.2", @@ -347,13 +340,13 @@ "from": "graceful-fs@>=4.1.2 <5.0.0" }, "grpc": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.6.0.tgz", - "from": "grpc@>=1.3.1 <2.0.0", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.6.6.tgz", + "from": "grpc@>=1.6.0 <2.0.0", "dependencies": { "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "from": "abbrev@1" }, "ajv": { @@ -367,8 +360,8 @@ "from": "ansi-regex@^2.0.0" }, "aproba": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "from": "aproba@^1.0.3" }, "are-we-there-yet": { @@ -479,8 +472,8 @@ } }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "from": "debug@^2.2.0" }, "deep-extend": { @@ -583,7 +576,7 @@ "hawk": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "from": "hawk@~3.1.3" + "from": "hawk@3.1.3" }, "hoek": { "version": "2.16.3", @@ -668,13 +661,13 @@ } }, "mime-db": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", - "from": "mime-db@~1.29.0" + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "from": "mime-db@~1.30.0" }, "mime-types": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "from": "mime-types@~2.1.7" }, "minimatch": { @@ -705,9 +698,9 @@ "from": "ms@2.0.0" }, "node-pre-gyp": { - "version": "0.6.36", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", - "from": "node-pre-gyp@0.6.36" + "version": "0.6.38", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz", + "from": "node-pre-gyp@0.6.38" }, "nopt": { "version": "4.0.1", @@ -792,11 +785,11 @@ "request": { "version": "2.81.0", "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "from": "request@^2.81.0" + "from": "request@2.81.0" }, "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "from": "rimraf@^2.6.1" }, "safe-buffer": { @@ -872,8 +865,8 @@ "from": "tar-pack@^3.4.0" }, "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "from": "tough-cookie@~2.3.0" }, "tunnel-agent": { @@ -1123,7 +1116,7 @@ "nan": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "from": "nan@>=2.6.2 <3.0.0" + "from": "nan@>=2.7.0 <3.0.0" }, "node-forge": { "version": "0.7.1", @@ -1236,9 +1229,9 @@ "from": "request@>=2.79.0 <3.0.0" }, "retry-request": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-2.0.5.tgz", - "from": "retry-request@>=2.0.0 <3.0.0" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.0.1.tgz", + "from": "retry-request@>=3.0.0 <4.0.0" }, "rgb-hex": { "version": "1.0.0", diff --git a/packages/rocketchat-lib/client/lib/roomTypes.js b/packages/rocketchat-lib/client/lib/roomTypes.js index 42193496d27a..aeb7bb44f359 100644 --- a/packages/rocketchat-lib/client/lib/roomTypes.js +++ b/packages/rocketchat-lib/client/lib/roomTypes.js @@ -1,5 +1,6 @@ -import roomTypesCommon from '../../lib/roomTypesCommon'; -RocketChat.roomTypes = new class extends roomTypesCommon { +import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; + +RocketChat.roomTypes = new class extends RoomTypesCommon { checkCondition(roomType) { return roomType.condition == null || roomType.condition(); } diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js new file mode 100644 index 000000000000..b4c60335ca80 --- /dev/null +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -0,0 +1,172 @@ +RocketChat.definitions = RocketChat.definitions || {}; + +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; + } +} + +RocketChat.definitions.RoomTypeRouteConfig = RoomTypeRouteConfig; + +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; + } + + allowChangeChannelSettings(/* room, setting */) { + return true; + } + + canBeDeleted(userId, room) { + return Meteor.isServer ? + RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`delete-${ room.t }`], room._id) : + RocketChat.authz.hasAtLeastOnePermission([`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; + } +} + +RocketChat.definitions.RoomTypeConfig = RoomTypeConfig; diff --git a/packages/rocketchat-lib/lib/RoomTypesCommon.js b/packages/rocketchat-lib/lib/RoomTypesCommon.js new file mode 100644 index 000000000000..e7cead3d9dde --- /dev/null +++ b/packages/rocketchat-lib/lib/RoomTypesCommon.js @@ -0,0 +1,93 @@ +/* globals roomExit */ +import { RoomTypeConfig } from './RoomTypeConfig'; + +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); + } + + 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-lib/lib/roomTypes/channels.js b/packages/rocketchat-lib/lib/roomTypes/channels.js new file mode 100644 index 000000000000..cd39eeeb37bc --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/channels.js @@ -0,0 +1,17 @@ +import { RoomTypeConfig } from '../RoomTypeConfig'; + +export class ChannelsRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'channels', + order: 30, + label: 'Channels' + }); + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + return ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && preferences.mergeChannels; + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/conversation.js b/packages/rocketchat-lib/lib/roomTypes/conversation.js new file mode 100644 index 000000000000..1aaa9faa2374 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/conversation.js @@ -0,0 +1,17 @@ +import { RoomTypeConfig } from '../RoomTypeConfig'; + +export class ConversationRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'activity', + order: 30, + label: 'Conversations' + }); + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + return preferences.roomsListExhibitionMode === 'activity'; + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/direct.js b/packages/rocketchat-lib/lib/roomTypes/direct.js new file mode 100644 index 000000000000..3dc4d9972881 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/direct.js @@ -0,0 +1,82 @@ +/* globals openRoom */ +import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; + +export class DirectMessageRoomRoute extends RoomTypeRouteConfig { + constructor() { + super({ + name: 'direct', + path: '/direct/:username' + }); + } + + action(params) { + return openRoom('d', params.username); + } + + link(sub) { + return { username: sub.name }; + } +} + +export class DirectMessageRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'd', + order: 50, + label: 'Direct_Messages', + route: new DirectMessageRoomRoute() + }); + } + + findRoom(identifier) { + const query = { + t: 'd', + name: identifier + }; + + const subscription = ChatSubscription.findOne(query); + if (subscription && subscription.rid) { + return ChatRoom.findOne(subscription.rid); + } + } + + roomName(roomData) { + const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1, fname: 1 }}); + if (!subscription) { + return ''; + } + + if (RocketChat.settings.get('UI_Use_Real_Name') && subscription.fname) { + return subscription.fname; + } + + return subscription.name; + } + + secondaryRoomName(roomData) { + if (RocketChat.settings.get('UI_Use_Real_Name')) { + const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1 }}); + return subscription && subscription.name; + } + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + + return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && RocketChat.authz.hasAtLeastOnePermission(['view-d-room', 'view-joined-room']); + } + + getUserStatus(roomId) { + const subscription = RocketChat.models.Subscriptions.findOne({ rid: roomId }); + if (subscription == null) { + return; + } + + return Session.get(`user_${ subscription.name }_status`); + } + + getDisplayName(room) { + return room.usernames.join(' x '); + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/favorite.js b/packages/rocketchat-lib/lib/roomTypes/favorite.js new file mode 100644 index 000000000000..55e061547d75 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/favorite.js @@ -0,0 +1,13 @@ +import { RoomTypeConfig } from '../RoomTypeConfig'; + +export class FavoriteRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'f', + order: 20, + header: 'favorite', + icon: 'star', + label: 'Favorites' + }); + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/index.js b/packages/rocketchat-lib/lib/roomTypes/index.js new file mode 100644 index 000000000000..739421c89b38 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/index.js @@ -0,0 +1,17 @@ +import { ChannelsRoomType } from './channels'; +import { ConversationRoomType } from './conversation'; +import { DirectMessageRoomType } from './direct'; +import { FavoriteRoomType } from './favorite'; +import { PrivateRoomType } from './private'; +import { PublicRoomType } from './public'; +import { UnreadRoomType } from './unread'; + +export { + ChannelsRoomType, + ConversationRoomType, + DirectMessageRoomType, + FavoriteRoomType, + PrivateRoomType, + PublicRoomType, + UnreadRoomType +}; diff --git a/packages/rocketchat-lib/lib/roomTypes/private.js b/packages/rocketchat-lib/lib/roomTypes/private.js new file mode 100644 index 000000000000..2050f5ebe694 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/private.js @@ -0,0 +1,59 @@ +/* globals openRoom */ +import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; + +export class PrivateRoomRoute extends RoomTypeRouteConfig { + constructor() { + super({ + name: 'group', + path: '/group/:name' + }); + } + + action(params) { + return openRoom('p', params.name); + } +} + +export class PrivateRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'p', + order: 40, + icon: 'lock', + label: 'Private_Groups', + route: new PrivateRoomRoute() + }); + } + + findRoom(identifier) { + const query = { + t: 'p', + name: identifier + }; + + return ChatRoom.findOne(query); + } + + roomName(roomData) { + if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { + return roomData.fname || roomData.name; + } + + return roomData.name; + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + + return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && RocketChat.authz.hasAllPermission('view-p-room'); + } + + isGroupChat() { + return true; + } + + canAddUser(room) { + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id); + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/public.js b/packages/rocketchat-lib/lib/roomTypes/public.js new file mode 100644 index 000000000000..af2ce0567e2c --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/public.js @@ -0,0 +1,64 @@ +/* globals openRoom */ +import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; + +export class PublicRoomRoute extends RoomTypeRouteConfig { + constructor() { + super({ + name: 'channel', + path: '/channel/:name' + }); + } + + action(params) { + return openRoom('c', params.name); + } +} + +export class PublicRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'c', + order: 30, + icon: 'hashtag', + label: 'Channels', + route: new PublicRoomRoute() + }); + } + + findRoom(identifier) { + const query = { + t: 'c', + name: identifier + }; + return ChatRoom.findOne(query); + } + + roomName(roomData) { + if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { + return roomData.fname || roomData.name; + } + return roomData.name; + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && (RocketChat.authz.hasAtLeastOnePermission(['view-c-room', 'view-joined-room']) || RocketChat.settings.get('Accounts_AllowAnonymousRead') === true); + } + + showJoinLink(roomId) { + return !!ChatRoom.findOne({ _id: roomId, t: 'c' }); + } + + includeInRoomSearch() { + return true; + } + + isGroupChat() { + return true; + } + + canAddUser(room) { + return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id); + } +} diff --git a/packages/rocketchat-lib/lib/roomTypes/unread.js b/packages/rocketchat-lib/lib/roomTypes/unread.js new file mode 100644 index 000000000000..670f71b47a36 --- /dev/null +++ b/packages/rocketchat-lib/lib/roomTypes/unread.js @@ -0,0 +1,19 @@ +import { RoomTypeConfig } from '../RoomTypeConfig'; + +export class UnreadRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'unread', + order: 10, + label: 'Unread' + }); + + this.unread = true; + } + + condition() { + const user = Meteor.user(); + const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; + return preferences.roomsListExhibitionMode === 'unread'; + } +} diff --git a/packages/rocketchat-lib/lib/roomTypesCommon.js b/packages/rocketchat-lib/lib/roomTypesCommon.js deleted file mode 100644 index b64434676653..000000000000 --- a/packages/rocketchat-lib/lib/roomTypesCommon.js +++ /dev/null @@ -1,166 +0,0 @@ -/* globals roomExit*/ -this.roomTypesCommon = class { - constructor() { - this.roomTypes = {}; - this.roomTypesOrder = []; - this.mainOrder = 1; - } - - /* Adds a room type to app - @param identifier An identifier to the room type. If a real room, MUST BE the same of `db.rocketchat_room.t` field, if not, can be null - @param order Order number of the type - @param config - icon: icon class - label: i18n label - route: - name: route name - action: route action function - identifier: room type identifier - - Optional methods which can be redefined - getDisplayName(room): return room's name for the UI - allowChangeChannelSettings(room): Whether it's possible to use the common channel-settings-tab to change a room's settings - canBeDeleted(userId, room): Custom authorizations for whether a room can be deleted. If not implemented, `delete-{identifier}` will be checked for - supportMembersList(room): Whether the generic members list for managing a room's members shall be available - isGroupChat(): Whether the room type is a chat of a group of members - canAddUser(userId, room): Whether the given user is allowed to add users to the specified room - userDetailShowAll(room): Whether all room members' details be shown in the user info - userDetailShowAdmin(room): Whether admin-controls (change role etc.) shall be shown in the user info - preventRenaming(room): Whether it shall be impossible to rename the room - includeInRoomSearch(): Whether rooms of this type shall be included into the result list when searching for "rooms" - */ - add(identifier = Random.id(), order, config) { - if (this.roomTypes[identifier] != null) { - return false; - } - - // apart from the structure, the config may optionally override default behaviour. - // In order to simplify implementation, default implementeations are added unless "redefined" - this._addDefaultImplementations(config); - - if (order == null) { - order = this.mainOrder + 10; - this.mainOrder += 10; - } - this.roomTypesOrder.push({ - identifier, - order - }); - this.roomTypes[identifier] = {...config, identifier}; - if (config.route && config.route.path && config.route.name && config.route.action) { - const routeConfig = { - name: config.route.name, - action: config.route.action - }; - if (Meteor.isClient) { - routeConfig.triggersExit = [roomExit]; - } - return FlowRouter.route(config.route.path, routeConfig); - } - } - - hasCustomLink(roomType) { - return this.roomTypes[roomType] && this.roomTypes[roomType].route && this.roomTypes[roomType].route.link != null; - } - - /* - @param roomType: room type (e.g.: c (for channels), d (for direct channels)) - @param subData: the user's subscription data - */ - - getRouteLink(roomType, subData) { - if (this.roomTypes[roomType] == null) { - 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); - } - - openRouteLink(roomType, subData, queryParams) { - if (this.roomTypes[roomType] == null) { - 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); - } - - _addDefaultImplementations(config) { - if (!config.getDisplayName) { - config.getDisplayName = function(room) { - return room.name; - }; - } - - if (!config.allowChangeChannelSettings) { - config.allowChangeChannelSettings = function() { - return true; - }; - } - - if (!config.canBeDeleted) { - config.canBeDeleted = function(room) { - return Meteor.isServer ? - RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`delete-${ room.t }`], room._id) : - RocketChat.authz.hasAtLeastOnePermission([`delete-${ room.t }`], room._id); - }; - } - - if (!config.supportMembersList) { - config.supportMembersList = function() { - return true; - }; - } - - if (!config.isGroupChat) { - config.isGroupChat = function() { - return false; - }; - } - - if (!config.canAddUser) { - config.canAddUser = function() { - return false; - }; - } - - if (!config.userDetailShowAll) { - config.userDetailShowAll = function() { - return true; - }; - } - - if (!config.userDetailShowAdmin) { - config.userDetailShowAdmin = function() { - return true; - }; - } - - if (!config.preventRenaming) { - config.preventRenaming = function() { - return false; - }; - } - - if (!config.includeInRoomSearch) { - config.includeInRoomSearch = function() { - return false; - }; - } - } - -}; -export default this.roomTypesCommon; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 7be42499e08a..e35031b1b4e4 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -50,6 +50,19 @@ Package.onUse(function(api) { // DEBUGGER api.addFiles('server/lib/debug.js', 'server'); + // ROOM TYPES + api.addFiles('lib/RoomTypeConfig.js'); + api.addFiles([ + 'lib/roomTypes/channels.js', + 'lib/roomTypes/conversation.js', + 'lib/roomTypes/direct.js', + 'lib/roomTypes/favorite.js', + 'lib/roomTypes/index.js', + 'lib/roomTypes/private.js', + 'lib/roomTypes/public.js', + 'lib/roomTypes/unread.js' + ]); + // COMMON LIB api.addFiles('lib/getURL.js'); api.addFiles('lib/settings.js'); @@ -58,7 +71,7 @@ Package.onUse(function(api) { api.addFiles('lib/getValidRoomName.js'); api.addFiles('lib/placeholders.js'); api.addFiles('lib/promises.js'); - api.addFiles('lib/roomTypesCommon.js'); + api.addFiles('lib/RoomTypesCommon.js'); api.addFiles('lib/slashCommand.js'); api.addFiles('lib/Message.js'); api.addFiles('lib/messageBox.js'); diff --git a/packages/rocketchat-lib/server/lib/roomTypes.js b/packages/rocketchat-lib/server/lib/roomTypes.js index 9f238a89205d..68aec9b5ef6f 100644 --- a/packages/rocketchat-lib/server/lib/roomTypes.js +++ b/packages/rocketchat-lib/server/lib/roomTypes.js @@ -1,10 +1,12 @@ -/* globals roomTypesCommon*/ -RocketChat.roomTypes = new class roomTypesServer extends roomTypesCommon { - /* add a publish for a room type - @param roomType: room type (e.g.: c (for channels), d (for direct channels)) - @param callback: function that will return the publish's data - */ +import { RoomTypesCommon } from '../../lib/RoomTypesCommon'; +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'); @@ -24,17 +26,19 @@ RocketChat.roomTypes = new class roomTypesServer extends roomTypesCommon { } return this.roomTypes[roomType].roomFind = callback; } + getRoomFind(roomType) { return this.roomTypes[roomType] && this.roomTypes[roomType].roomFind; } - /* run the publish for a room type - @param scope: Meteor publish scope - @param roomType: room type (e.g.: c (for channels), d (for direct channels)) - @param identifier: identifier of the room + /** + * 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); } diff --git a/packages/rocketchat-lib/startup/defaultRoomTypes.js b/packages/rocketchat-lib/startup/defaultRoomTypes.js index d5e0f368b855..3bb90fc29299 100644 --- a/packages/rocketchat-lib/startup/defaultRoomTypes.js +++ b/packages/rocketchat-lib/startup/defaultRoomTypes.js @@ -1,221 +1,17 @@ -/* globals openRoom */ -RocketChat.roomTypes.add('unread', 10, { - unread: true, - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return preferences.roomsListExhibitionMode === 'unread'; - }, - label: 'Unread' -}); - -RocketChat.roomTypes.add('f', 20, { - header: 'favorite', - icon: 'star', - label: 'Favorites' -}); - -// activity -RocketChat.roomTypes.add('activity', 30, { - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return preferences.roomsListExhibitionMode === 'activity'; - }, - label: 'Conversations' -}); - -RocketChat.roomTypes.add('channels', 30, { - label: 'Channels', - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && preferences.mergeChannels; - } -}); -// public -RocketChat.roomTypes.add('c', 30, { - icon: 'hashtag', - label: 'Channels', - route: { - name: 'channel', - path: '/channel/:name', - action(params) { - return openRoom('c', params.name); - } - }, - - findRoom(identifier) { - const query = { - t: 'c', - name: identifier - }; - return ChatRoom.findOne(query); - }, - - roomName(roomData) { - if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { - return roomData.fname || roomData.name; - } - return roomData.name; - }, - - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && (RocketChat.authz.hasAtLeastOnePermission(['view-c-room', 'view-joined-room']) || RocketChat.settings.get('Accounts_AllowAnonymousRead') === true); - }, - - showJoinLink(roomId) { - return !!ChatRoom.findOne({_id: roomId, t: 'c'}); - }, - - includeInRoomSearch() { - return true; - }, - - isGroupChat() { - return true; - }, - - canAddUser(room) { - return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id); - }, - - userDetailShowAll() { - return true; - }, - - userDetailShowAdmin() { - return true; - }, - - getDisplayName(room) { - return room.name; - } -}); - -// private -RocketChat.roomTypes.add('p', 40, { - icon: 'lock', - label: 'Private_Groups', - route: { - name: 'group', - path: '/group/:name', - action(params) { - return openRoom('p', params.name); - } - }, - - findRoom(identifier) { - const query = { - t: 'p', - name: identifier - }; - return ChatRoom.findOne(query); - }, - - roomName(roomData) { - if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { - return roomData.fname || roomData.name; - } - return roomData.name; - }, - - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && !preferences.mergeChannels && RocketChat.authz.hasAllPermission('view-p-room'); - }, - - isGroupChat() { - return true; - }, - - canAddUser(room) { - return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id); - }, - - userDetailShowAll() { - return true; - }, - - userDetailShowAdmin() { - return true; - }, - - getDisplayName(room) { - return room.name; - } -}); - - -// direct -RocketChat.roomTypes.add('d', 50, { - icon: false, - label: 'Direct_Messages', - route: { - name: 'direct', - path: '/direct/:username', - action(params) { - return openRoom('d', params.username); - }, - link(sub) { - return {username: sub.name}; - } - }, - - findRoom(identifier) { - const query = { - t: 'd', - name: identifier - }; - - const subscription = ChatSubscription.findOne(query); - if (subscription && subscription.rid) { - return ChatRoom.findOne(subscription.rid); - } - }, - - roomName(roomData) { - const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1, fname: 1}}); - if (!subscription) { - return ''; - } - if (RocketChat.settings.get('UI_Use_Real_Name') && subscription.fname) { - return subscription.fname; - } - - return subscription.name; - }, - - secondaryRoomName(roomData) { - if (RocketChat.settings.get('UI_Use_Real_Name')) { - const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1}}); - return subscription && subscription.name; - } - }, - - condition() { - const user = Meteor.user(); - const preferences = (user && user.settings && user.settings.preferences && user.settings.preferences) || {}; - return !preferences.roomsListExhibitionMode || ['unread', 'category'].includes(preferences.roomsListExhibitionMode) && RocketChat.authz.hasAtLeastOnePermission(['view-d-room', 'view-joined-room']); - }, - - getUserStatus(roomId) { - const subscription = RocketChat.models.Subscriptions.findOne({rid: roomId}); - if (subscription == null) { - return; - } - - return Session.get(`user_${ subscription.name }_status`); - }, - - userDetailShowAdmin() { - return true; - }, - - getDisplayName(room) { - return room.usernames.join(' x '); - } -}); +import { + ChannelsRoomType, + ConversationRoomType, + DirectMessageRoomType, + FavoriteRoomType, + PrivateRoomType, + PublicRoomType, + UnreadRoomType +} from '../lib/roomTypes'; + +RocketChat.roomTypes.add(new UnreadRoomType()); +RocketChat.roomTypes.add(new FavoriteRoomType()); +RocketChat.roomTypes.add(new ConversationRoomType()); +RocketChat.roomTypes.add(new ChannelsRoomType()); +RocketChat.roomTypes.add(new PublicRoomType()); +RocketChat.roomTypes.add(new PrivateRoomType()); +RocketChat.roomTypes.add(new DirectMessageRoomType()); diff --git a/packages/rocketchat-livechat/roomType.js b/packages/rocketchat-livechat/roomType.js index 84e0ee616117..bae09bb07871 100644 --- a/packages/rocketchat-livechat/roomType.js +++ b/packages/rocketchat-livechat/roomType.js @@ -1,24 +1,42 @@ /* globals openRoom, LivechatInquiry */ -RocketChat.roomTypes.add('l', 5, { - icon: 'livechat', - label: 'Livechat', - route: { - name: 'live', - path: '/live/:code(\\d+)', - action(params/*, queryParams*/) { - openRoom('l', params.code); - }, - link(sub) { - return { - code: sub.code - }; - } - }, +class LivechatRoomRoute extends RocketChat.definitions.RoomTypeRouteConfig { + constructor() { + super({ + name: 'live', + path: '/live/:code(\\d+)' + }); + } + + action(params) { + openRoom('l', params.code); + } + + link(sub) { + return { + code: sub.code + }; + } +} + +class LivechatRoomType extends RocketChat.definitions.RoomTypeConfig { + constructor() { + super({ + identifier: 'l', + order: 5, + icon: 'livechat', + label: 'Livechat', + route: new LivechatRoomRoute() + }); + + this.notSubscribedTpl = { + template: 'livechatNotSubscribed' + }; + } findRoom(identifier) { return ChatRoom.findOne({ code: parseInt(identifier) }); - }, + } roomName(roomData) { if (!roomData.name) { @@ -26,16 +44,16 @@ RocketChat.roomTypes.add('l', 5, { } else { return roomData.name; } - }, + } condition() { return RocketChat.settings.get('Livechat_enabled') && RocketChat.authz.hasAllPermission('view-l-room'); - }, + } canSendMessage(roomId) { const room = ChatRoom.findOne({ _id: roomId }, { fields: { open: 1 } }); return room && room.open === true; - }, + } getUserStatus(roomId) { let guestName; @@ -51,9 +69,7 @@ RocketChat.roomTypes.add('l', 5, { if (guestName) { return Session.get(`user_${ guestName }_status`); } - }, - - notSubscribedTpl: { - template: 'livechatNotSubscribed' } -}); +} + +RocketChat.roomTypes.add(new LivechatRoomType()); From d4059ae2c930bf1a5903e412251161b0c9299291 Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Thu, 12 Oct 2017 16:12:12 -0500 Subject: [PATCH 13/23] Move the settings configuration over to an ENUM based. This allows for each different type of room to define which setting it wants to display instead of them being hard coded on the settings tab controller. --- .../client/views/channelSettings.js | 74 +++++++++++-------- .../client/admin/adminSounds.js | 3 +- .../admin/adminEmoji.js | 3 +- .../client/lib/RocketChatTabBar.js | 7 +- packages/rocketchat-lib/client/lib/index.js | 18 +++++ packages/rocketchat-lib/lib/RoomTypeConfig.js | 19 +++-- .../rocketchat-lib/lib/roomTypes/direct.js | 16 +++- .../rocketchat-lib/lib/roomTypes/private.js | 11 ++- .../rocketchat-lib/lib/roomTypes/public.js | 4 + packages/rocketchat-lib/package.js | 6 +- packages/rocketchat-lib/server/lib/index.js | 15 ++++ packages/rocketchat-livechat/roomType.js | 14 +++- .../client/rooms/adminRooms.js | 3 +- .../client/users/adminUsers.js | 3 +- .../rocketchat-ui/client/views/app/room.js | 4 +- 15 files changed, 149 insertions(+), 51 deletions(-) create mode 100644 packages/rocketchat-lib/client/lib/index.js create mode 100644 packages/rocketchat-lib/server/lib/index.js diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index eec05b13013a..b9b2a401467e 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -1,5 +1,5 @@ -/* globals RocketChat */ import toastr from 'toastr'; +import { RocketChat, RoomSettingsEnum } from 'meteor/rocketchat:lib'; Template.channelSettings.helpers({ toArray(obj) { @@ -22,12 +22,6 @@ Template.channelSettings.helpers({ } return obj && obj[key]; }, - showSetting(setting, room) { - if (setting.showInDirect === false) { - return room.t !== 'd'; - } - return true; - }, settings() { return Template.instance().settings; }, @@ -53,6 +47,7 @@ Template.channelSettings.helpers({ t: 1 } }); + const roomType = room && room.t; return roomType && RocketChat.roomTypes.roomTypes[room.t].canBeDeleted(room); }, @@ -62,6 +57,7 @@ Template.channelSettings.helpers({ ro: 1 } }); + return room && room.ro; }, has(v, key) { @@ -73,6 +69,7 @@ Template.channelSettings.helpers({ ro: 1 } }); + return t(room && room.ro ? 'True' : 'False'); } }); @@ -151,7 +148,7 @@ Template.channelSettings.onCreated(function() { type: 'text', label: 'Name', canView(room) { - return room.t !== 'd'; + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.NAME); }, canEdit(room) { return RocketChat.authz.hasAllPermission('edit-room', room._id); @@ -160,22 +157,18 @@ Template.channelSettings.onCreated(function() { if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { return room.fname || room.name; } + return room.name; }, save(value, room) { let nameValidation; - if (!RocketChat.authz.hasAllPermission('edit-room', room._id) || (room.t !== 'c' && room.t !== 'p')) { - // custom room types can explicitly enable/disable the channel settings support - if (!(RocketChat.roomTypes[room.t].allowChangeChannelSettings && RocketChat.roomTypes[room.t].allowChangeChannelSettings())) { - return toastr.error(t('error-not-allowed')); - } - } if (!RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) { try { nameValidation = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`); } catch (error1) { nameValidation = new RegExp('^[0-9a-zA-Z-_.]+$'); } + if (!nameValidation.test(value)) { return toastr.error(t('error-invalid-room-name', { room_name: { @@ -184,14 +177,19 @@ Template.channelSettings.onCreated(function() { })); } } - Meteor.call('saveRoomSettings', room._id, 'roomName', value, function(err) { + + this.processing.set(true); + Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.NAME, value, function(err) { if (err) { return handleError(err); } + + this.processing.set(false); RocketChat.callbacks.run('roomNameChanged', { _id: room._id, name: value }); + return toastr.success(TAPi18n.__('Room_name_changed_successfully')); }); } @@ -199,17 +197,20 @@ Template.channelSettings.onCreated(function() { topic: { type: 'markdown', label: 'Topic', - canView() { - return true; + canView(room) { + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.TOPIC); }, canEdit(room) { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - return Meteor.call('saveRoomSettings', room._id, 'roomTopic', value, function(err) { + this.processing.set(true); + return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.TOPIC, value, function(err) { if (err) { return handleError(err); } + + this.processing.set(false); toastr.success(TAPi18n.__('Room_topic_changed_successfully')); return RocketChat.callbacks.run('roomTopicChanged', room); }); @@ -218,17 +219,20 @@ Template.channelSettings.onCreated(function() { announcement: { type: 'markdown', label: 'Announcement', - canView() { - return true; + canView(room) { + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.ANNOUNCEMENT); }, canEdit(room) { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - return Meteor.call('saveRoomSettings', room._id, 'roomAnnouncement', value, function(err) { + this.processing.set(true); + return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.ANNOUNCEMENT, value, function(err) { if (err) { return handleError(err); } + + this.processing.set(false); toastr.success(TAPi18n.__('Room_announcement_changed_successfully')); return RocketChat.callbacks.run('roomAnnouncementChanged', room); }); @@ -238,16 +242,19 @@ Template.channelSettings.onCreated(function() { type: 'text', label: 'Description', canView(room) { - return room.t !== 'd'; + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.DESCRIPTION); }, canEdit(room) { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - return Meteor.call('saveRoomSettings', room._id, 'roomDescription', value, function(err) { + this.processing.set(true); + return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.DESCRIPTION, value, function(err) { if (err) { return handleError(err); } + + this.processing.set(false); return toastr.success(TAPi18n.__('Room_description_changed_successfully')); }); } @@ -268,7 +275,7 @@ Template.channelSettings.onCreated(function() { } }, canView(room) { - if (['c', 'p'].includes(room.t) === false) { + if (!['c', 'p'].includes(room.t)) { return false; } else if (room.t === 'p' && !RocketChat.authz.hasAllPermission('create-c')) { return false; @@ -322,17 +329,18 @@ Template.channelSettings.onCreated(function() { isToggle: true, processing: new ReactiveVar(false), canView(room) { - return room.t !== 'd'; + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.READ_ONLY); }, canEdit(room) { return RocketChat.authz.hasAllPermission('set-readonly', room._id); }, save(value, room) { this.processing.set(true); - return Meteor.call('saveRoomSettings', room._id, 'readOnly', value, (err) => { + return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.READ_ONLY, value, (err) => { if (err) { return handleError(err); } + this.processing.set(false); return toastr.success(TAPi18n.__('Read_only_changed_successfully')); }); @@ -344,7 +352,7 @@ Template.channelSettings.onCreated(function() { isToggle: true, processing: new ReactiveVar(false), canView(room) { - return room.t !== 'd' && room.ro; + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.REACT_WHEN_READ_ONLY) && room.ro; }, canEdit(room) { return RocketChat.authz.hasAllPermission('set-react-when-readonly', room._id); @@ -355,6 +363,7 @@ Template.channelSettings.onCreated(function() { if (err) { return handleError(err); } + this.processing.set(false); return toastr.success(TAPi18n.__('React_when_read_only_changed_successfully')); }); @@ -366,7 +375,7 @@ Template.channelSettings.onCreated(function() { isToggle: true, processing: new ReactiveVar(false), canView(room) { - return room.t !== 'd'; + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.ARCHIVE_OR_UNARCHIVE); }, canEdit(room) { return RocketChat.authz.hasAtLeastOnePermission(['archive-room', 'unarchive-room'], room._id); @@ -384,12 +393,14 @@ Template.channelSettings.onCreated(function() { }, function(confirmed) { swal.disableButtons(); if (confirmed) { + this.processing.set(true); const action = value ? 'archiveRoom' : 'unarchiveRoom'; return Meteor.call(action, room._id, function(err) { if (err) { swal.enableButtons(); handleError(err); } + swal({ title: value ? t('Room_archived') : t('Room_has_been_archived'), text: value ? t('Room_has_been_archived') : t('Room_has_been_unarchived'), @@ -397,6 +408,8 @@ Template.channelSettings.onCreated(function() { timer: 2000, showConfirmButton: false }); + + this.processing.set(false); return RocketChat.callbacks.run(action, room); }); } else { @@ -409,16 +422,19 @@ Template.channelSettings.onCreated(function() { type: 'text', label: 'Password', canView(room) { - return room.t === 'c' && RocketChat.authz.hasAllPermission('edit-room', room._id); + return RocketChat.roomTypes.roomTypes[room.t].allowRoomSettingChange(room, RoomSettingsEnum.JOIN_CODE) && RocketChat.authz.hasAllPermission('edit-room', room._id); }, canEdit(room) { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { + this.processing.set(true); return Meteor.call('saveRoomSettings', room._id, 'joinCode', value, function(err) { if (err) { return handleError(err); } + + this.processing.set(false); toastr.success(TAPi18n.__('Room_password_changed_successfully')); return RocketChat.callbacks.run('roomCodeChanged', room); }); diff --git a/packages/rocketchat-custom-sounds/client/admin/adminSounds.js b/packages/rocketchat-custom-sounds/client/admin/adminSounds.js index ec85ea2a7c45..186421281282 100644 --- a/packages/rocketchat-custom-sounds/client/admin/adminSounds.js +++ b/packages/rocketchat-custom-sounds/client/admin/adminSounds.js @@ -1,4 +1,5 @@ -/* globals RocketChatTabBar */ +import { RocketChatTabBar } from 'meteor/rocketchat:lib'; + Template.adminSounds.helpers({ isReady() { if (Template.instance().ready != null) { diff --git a/packages/rocketchat-emoji-custom/admin/adminEmoji.js b/packages/rocketchat-emoji-custom/admin/adminEmoji.js index 4b9f54d44e64..2caa2a0c33f6 100644 --- a/packages/rocketchat-emoji-custom/admin/adminEmoji.js +++ b/packages/rocketchat-emoji-custom/admin/adminEmoji.js @@ -1,4 +1,5 @@ -/* globals RocketChatTabBar */ +import { RocketChatTabBar } from 'meteor/rocketchat:lib'; + Template.adminEmoji.helpers({ isReady() { if (Template.instance().ready != null) { diff --git a/packages/rocketchat-lib/client/lib/RocketChatTabBar.js b/packages/rocketchat-lib/client/lib/RocketChatTabBar.js index 5395f5b3d474..bdae016d363b 100644 --- a/packages/rocketchat-lib/client/lib/RocketChatTabBar.js +++ b/packages/rocketchat-lib/client/lib/RocketChatTabBar.js @@ -1,7 +1,4 @@ -/* globals RocketChatTabBar */ -/* exported RocketChatTabBar */ - -RocketChatTabBar = class RocketChatTabBar { +export class RocketChatTabBar { constructor() { this.template = new ReactiveVar(); this.group = new ReactiveVar(); @@ -69,4 +66,4 @@ RocketChatTabBar = class RocketChatTabBar { this.template.set(); } -}; +} diff --git a/packages/rocketchat-lib/client/lib/index.js b/packages/rocketchat-lib/client/lib/index.js new file mode 100644 index 000000000000..deb1d177fb31 --- /dev/null +++ b/packages/rocketchat-lib/client/lib/index.js @@ -0,0 +1,18 @@ +/* + What is this file? Great question! To make Rocket.Chat more "modular" + and to make the "rocketchat:lib" package more of a core package + with the libraries, this index file contains the exported members + for the *client* pieces of code which does include the shared + library files. +*/ + +import { RocketChatTabBar } from './RocketChatTabBar'; +import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../../lib/RoomTypeConfig'; + + +export { + RocketChatTabBar, + RoomSettingsEnum, + RoomTypeConfig, + RoomTypeRouteConfig +}; diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index b4c60335ca80..436ab064f4b4 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -1,4 +1,13 @@ -RocketChat.definitions = RocketChat.definitions || {}; +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' +}; export class RoomTypeRouteConfig { constructor({ name, path }) { @@ -23,8 +32,6 @@ export class RoomTypeRouteConfig { } } -RocketChat.definitions.RoomTypeRouteConfig = RoomTypeRouteConfig; - export class RoomTypeConfig { constructor({ identifier = Random.id(), @@ -130,11 +137,11 @@ export class RoomTypeConfig { return room.name; } - allowChangeChannelSettings(/* room, setting */) { + allowRoomSettingChange(/* room, setting */) { return true; } - canBeDeleted(userId, room) { + canBeDeleted(room) { return Meteor.isServer ? RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`delete-${ room.t }`], room._id) : RocketChat.authz.hasAtLeastOnePermission([`delete-${ room.t }`], room._id); @@ -168,5 +175,3 @@ export class RoomTypeConfig { return false; } } - -RocketChat.definitions.RoomTypeConfig = RoomTypeConfig; diff --git a/packages/rocketchat-lib/lib/roomTypes/direct.js b/packages/rocketchat-lib/lib/roomTypes/direct.js index 3dc4d9972881..546c7b97e9f4 100644 --- a/packages/rocketchat-lib/lib/roomTypes/direct.js +++ b/packages/rocketchat-lib/lib/roomTypes/direct.js @@ -1,5 +1,5 @@ /* globals openRoom */ -import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; +import { RoomTypeConfig, RoomTypeRouteConfig, RoomSettingsEnum } from '../RoomTypeConfig'; export class DirectMessageRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -79,4 +79,18 @@ export class DirectMessageRoomType extends RoomTypeConfig { getDisplayName(room) { return room.usernames.join(' x '); } + + allowRoomSettingChange(room, setting) { + switch (setting) { + case RoomSettingsEnum.NAME: + case RoomSettingsEnum.DESCRIPTION: + case RoomSettingsEnum.READ_ONLY: + case RoomSettingsEnum.REACT_WHEN_READ_ONLY: + case RoomSettingsEnum.ARCHIVE_OR_UNARCHIVE: + case RoomSettingsEnum.JOIN_CODE: + return false; + default: + return true; + } + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/private.js b/packages/rocketchat-lib/lib/roomTypes/private.js index 2050f5ebe694..006d848d29e9 100644 --- a/packages/rocketchat-lib/lib/roomTypes/private.js +++ b/packages/rocketchat-lib/lib/roomTypes/private.js @@ -1,5 +1,5 @@ /* globals openRoom */ -import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; +import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; export class PrivateRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -56,4 +56,13 @@ export class PrivateRoomType extends RoomTypeConfig { canAddUser(room) { return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-p-room', 'add-user-to-joined-room'], room._id); } + + allowRoomSettingChange(room, setting) { + switch (setting) { + case RoomSettingsEnum.JOIN_CODE: + return false; + default: + return true; + } + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/public.js b/packages/rocketchat-lib/lib/roomTypes/public.js index af2ce0567e2c..c4acf55c0653 100644 --- a/packages/rocketchat-lib/lib/roomTypes/public.js +++ b/packages/rocketchat-lib/lib/roomTypes/public.js @@ -61,4 +61,8 @@ export class PublicRoomType extends RoomTypeConfig { canAddUser(room) { return RocketChat.authz.hasAtLeastOnePermission(['add-user-to-any-c-room', 'add-user-to-joined-room'], room._id); } + + allowRoomSettingChange() { + return true; + } } diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index e35031b1b4e4..a57ccc65dcf5 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -34,6 +34,7 @@ Package.onUse(function(api) { api.use('matb33:collection-hooks'); api.use('service-configuration'); api.use('check'); + api.use('modules'); api.use('rocketchat:i18n'); api.use('rocketchat:streamer'); api.use('rocketchat:version'); @@ -235,7 +236,10 @@ Package.onUse(function(api) { // EXPORT api.export('RocketChat'); - api.export('RocketChatTabBar'); + + // exports + api.mainModule('server/lib/index.js', 'server'); + api.mainModule('client/lib/index.js', 'client'); api.imply('tap:i18n'); }); diff --git a/packages/rocketchat-lib/server/lib/index.js b/packages/rocketchat-lib/server/lib/index.js new file mode 100644 index 000000000000..0a29fe0b32d2 --- /dev/null +++ b/packages/rocketchat-lib/server/lib/index.js @@ -0,0 +1,15 @@ +/* + What is this file? Great question! To make Rocket.Chat more "modular" + and to make the "rocketchat:lib" package more of a core package + with the libraries, this index file contains the exported members + for the *server* pieces of code which does include the shared + library files. +*/ + +import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../../lib/RoomTypeConfig'; + +export { + RoomSettingsEnum, + RoomTypeConfig, + RoomTypeRouteConfig +}; diff --git a/packages/rocketchat-livechat/roomType.js b/packages/rocketchat-livechat/roomType.js index bae09bb07871..5899aba990fa 100644 --- a/packages/rocketchat-livechat/roomType.js +++ b/packages/rocketchat-livechat/roomType.js @@ -1,6 +1,7 @@ /* globals openRoom, LivechatInquiry */ +import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from 'meteor/rocketchat:lib'; -class LivechatRoomRoute extends RocketChat.definitions.RoomTypeRouteConfig { +class LivechatRoomRoute extends RoomTypeRouteConfig { constructor() { super({ name: 'live', @@ -19,7 +20,7 @@ class LivechatRoomRoute extends RocketChat.definitions.RoomTypeRouteConfig { } } -class LivechatRoomType extends RocketChat.definitions.RoomTypeConfig { +class LivechatRoomType extends RoomTypeConfig { constructor() { super({ identifier: 'l', @@ -70,6 +71,15 @@ class LivechatRoomType extends RocketChat.definitions.RoomTypeConfig { return Session.get(`user_${ guestName }_status`); } } + + allowRoomSettingChange(room, setting) { + switch (setting) { + case RoomSettingsEnum.JOIN_CODE: + return false; + default: + return true; + } + } } RocketChat.roomTypes.add(new LivechatRoomType()); diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js index 3ae6c2619e22..f66550ea108a 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js @@ -1,4 +1,5 @@ -/*globals RocketChatTabBar, AdminChatRoom, RocketChat */ +/*globals AdminChatRoom, RocketChat */ +import { RocketChatTabBar } from 'meteor/rocketchat:lib'; this.AdminChatRoom = new Mongo.Collection('rocketchat_room'); diff --git a/packages/rocketchat-ui-admin/client/users/adminUsers.js b/packages/rocketchat-ui-admin/client/users/adminUsers.js index 500344c22e57..1a90ec6fafea 100644 --- a/packages/rocketchat-ui-admin/client/users/adminUsers.js +++ b/packages/rocketchat-ui-admin/client/users/adminUsers.js @@ -1,4 +1,5 @@ -/* globals RocketChatTabBar */ +import { RocketChatTabBar } from 'meteor/rocketchat:lib'; + Template.adminUsers.helpers({ isReady() { const instance = Template.instance(); diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 81bfc6b141db..3334cca15d33 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -1,4 +1,6 @@ -/* globals RocketChatTabBar , chatMessages, fileUpload , fireGlobalEvent , mobileMessageMenu , cordova , readMessage , RoomRoles, popover */ +/* globals chatMessages, fileUpload , fireGlobalEvent , mobileMessageMenu , cordova , readMessage , RoomRoles, popover */ +import { RocketChatTabBar } from 'meteor/rocketchat:lib'; + import moment from 'moment'; import mime from 'mime-type/with-db'; import Clipboard from 'clipboard'; From 17e7549b904c167bd8e12bcbe0e2217ed5aeb61a Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Thu, 12 Oct 2017 16:59:11 -0500 Subject: [PATCH 14/23] Fix the tests failing due to some items not being on all settings --- .../client/views/channelSettings.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.js b/packages/rocketchat-channel-settings/client/views/channelSettings.js index b9b2a401467e..492e705a5dae 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.js +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.js @@ -178,13 +178,11 @@ Template.channelSettings.onCreated(function() { } } - this.processing.set(true); Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.NAME, value, function(err) { if (err) { return handleError(err); } - this.processing.set(false); RocketChat.callbacks.run('roomNameChanged', { _id: room._id, name: value @@ -204,13 +202,11 @@ Template.channelSettings.onCreated(function() { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - this.processing.set(true); return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.TOPIC, value, function(err) { if (err) { return handleError(err); } - this.processing.set(false); toastr.success(TAPi18n.__('Room_topic_changed_successfully')); return RocketChat.callbacks.run('roomTopicChanged', room); }); @@ -226,13 +222,11 @@ Template.channelSettings.onCreated(function() { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - this.processing.set(true); return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.ANNOUNCEMENT, value, function(err) { if (err) { return handleError(err); } - this.processing.set(false); toastr.success(TAPi18n.__('Room_announcement_changed_successfully')); return RocketChat.callbacks.run('roomAnnouncementChanged', room); }); @@ -248,13 +242,11 @@ Template.channelSettings.onCreated(function() { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - this.processing.set(true); return Meteor.call('saveRoomSettings', room._id, RoomSettingsEnum.DESCRIPTION, value, function(err) { if (err) { return handleError(err); } - this.processing.set(false); return toastr.success(TAPi18n.__('Room_description_changed_successfully')); }); } @@ -393,7 +385,6 @@ Template.channelSettings.onCreated(function() { }, function(confirmed) { swal.disableButtons(); if (confirmed) { - this.processing.set(true); const action = value ? 'archiveRoom' : 'unarchiveRoom'; return Meteor.call(action, room._id, function(err) { if (err) { @@ -409,7 +400,6 @@ Template.channelSettings.onCreated(function() { showConfirmButton: false }); - this.processing.set(false); return RocketChat.callbacks.run(action, room); }); } else { @@ -428,13 +418,11 @@ Template.channelSettings.onCreated(function() { return RocketChat.authz.hasAllPermission('edit-room', room._id); }, save(value, room) { - this.processing.set(true); return Meteor.call('saveRoomSettings', room._id, 'joinCode', value, function(err) { if (err) { return handleError(err); } - this.processing.set(false); toastr.success(TAPi18n.__('Room_password_changed_successfully')); return RocketChat.callbacks.run('roomCodeChanged', room); }); From a7c04e12a33eee719fb6cc7894ee9ce6ed9c53b5 Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Fri, 13 Oct 2017 21:50:23 -0500 Subject: [PATCH 15/23] [DOC] Update the rocketchat:lib readme with information about providing custom room types --- packages/rocketchat-lib/README.md | 93 ++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/packages/rocketchat-lib/README.md b/packages/rocketchat-lib/README.md index 1836472b031e..bdcdc5b44e50 100644 --- a/packages/rocketchat-lib/README.md +++ b/packages/rocketchat-lib/README.md @@ -47,32 +47,78 @@ RocketChat.settings.addGroup('Settings_Group', function() { * `enableQuery` - Only enable this setting if the correspondent setting has the value specified * `alert` - Shows an alert message with the given text -#### roomTypes +#### Custom Room Types +Custom room types are now a regular and expected customization to Rocket.Chat. As a result of this, we have expanded +the capabilities which custom room types are given. Custom room types now have full control over the settings which +display on the room's setting tab. To achieve this, however, we had to forcefully break the previous behavior in order +to force the new behavior. If, after merging, you are getting the error `Error: Invalid Room Configuration object, it must extend "RoomTypeConfig"`, +then you will need to modify your custom room code to the class setup explained below. + +Implementing the new types requires two steps. First step is to implement a your own custom class to define the route configuration. +Then the second step would be to implement your own room class, which will contain all of the required methods and configuration +for usage in the clients. + +##### First Step: RoomTypeRouteConfig +If your room adds a custom route to the browser, then you will need to create a class which extends `RoomTypeRouteConfig`. +This class can be imported using es6 style imports `import { RoomTypeRouteConfig } from 'meteor/rocketchat:lib';`. There +are two fields which are required when constructing your route which is `{ name, path }` and both are strings. Then your +class must implement the `action(params, queryParams)` method. -You can create your own room type using (on the client): +```javascript +class LivechatRoomRoute extends RoomTypeRouteConfig { + constructor() { + super({ + name: 'live', + path: '/live/:code(\\d+)' + }); + } + + action(params) { + openRoom('l', params.code); + } + + link(sub) { + return { + code: sub.code + }; + } +} +``` + +##### Second Step: RoomTypeConfig +Next you need to create a class which extends `RoomTypeConfig`. This class can be imported using the es6 style import +such as `import { RoomTypeConfig } from 'meteor/rocketchat:lib';`. There are two required properties when constructing +your class which is `{ identifier, order }` with the `identifier` being a string and `order` being a number. There are +default implementations of the required methods, so unless you want to overwrite the default behavior, such as disallowing +certain settings, then you will need to implement that method and handle it. ```javascript -RocketChat.roomTypes.add('l', 5, { - template: 'livechat', - icon: 'icon-chat-empty', - route: { - name: 'live', - path: '/live/:name', - action(params, queryParams) { - Session.set('showUserInfo'); - openRoom('l', params.name); - }, - link(sub) { - return { name: sub.name } - } - }, - condition: () => { - return RocketChat.authz.hasAllPermission('view-l-room'); - } -}); +class LivechatRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'l', + order: 5, + icon: 'livechat', + label: 'Livechat', + route: new LivechatRoomRoute() //defined above, see the example + }); + } + + roomName(roomData) { + if (!roomData.name) { + return roomData.label; + } else { + return roomData.name; + } + } + + condition() { + return RocketChat.settings.get('Livechat_enabled') && RocketChat.authz.hasAllPermission('view-l-room'); + } +} ``` -You'll need publish information about the new room with (on the server): +Then for publishing of the data, you will need to provide the information about the new room with (on the server): ```javascript RocketChat.roomTypes.setPublish('l', (identifier) => { @@ -104,5 +150,10 @@ AccountBox.addItem({ ``` ### Functions +n/a + ### Methods +n/a + ### Publications +n/a From 685593a8d9bf0090d231aad817fce46ea626ffb2 Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Mon, 16 Oct 2017 11:19:10 -0500 Subject: [PATCH 16/23] Fix the room name's not being displayed in the admin rooms --- packages/rocketchat-ui-admin/client/rooms/adminRooms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js index f66550ea108a..22981a67e3b6 100644 --- a/packages/rocketchat-ui-admin/client/rooms/adminRooms.js +++ b/packages/rocketchat-ui-admin/client/rooms/adminRooms.js @@ -28,7 +28,7 @@ Template.adminRooms.helpers({ return rooms && rooms.count(); }, name() { - RocketChat.roomTypes.roomTypes[this.t].getDisplayName(this); + return RocketChat.roomTypes.roomTypes[this.t].getDisplayName(this); }, type() { return TAPi18n.__(RocketChat.roomTypes.roomTypes[this.t].label); From fa20b86696298e4405bf49843d71ef7dbfc84b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Tue, 17 Oct 2017 13:40:27 +0200 Subject: [PATCH 17/23] Add creation validation configuration, e. g. in order to check whether the user is authorized to create the room type --- packages/rocketchat-lib/lib/RoomTypeConfig.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index 436ab064f4b4..f191fa153a60 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -141,6 +141,12 @@ export class RoomTypeConfig { return true; } + canBeCreated() { + return Meteor.isServer ? + RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`create-${ this._identifier }`]) : + RocketChat.authz.hasAtLeastOnePermission([`create-${ this._identifier }`]); + } + canBeDeleted(room) { return Meteor.isServer ? RocketChat.authz.hasAtLeastOnePermission(Meteor.userId(), [`delete-${ room.t }`], room._id) : From b2b09a3475294ca0f02ffeff30d9d6518cccb86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Mon, 23 Oct 2017 16:44:47 +0200 Subject: [PATCH 18/23] Adapt warning-text for leaving a room. Fixes #92 --- packages/rocketchat-ui/client/views/app/popover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-ui/client/views/app/popover.js b/packages/rocketchat-ui/client/views/app/popover.js index 85180094ca71..48faa6c80236 100644 --- a/packages/rocketchat-ui/client/views/app/popover.js +++ b/packages/rocketchat-ui/client/views/app/popover.js @@ -181,7 +181,7 @@ Template.popover.events({ swal({ title: t('Are_you_sure'), - text: t(warnText, name), + text: warnText ? t(warnText, name) : '', type: 'warning', showCancelButton: true, confirmButtonColor: '#DD6B55', From 03e23cfd0dc81b572559c82e7006e6511ce1733f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Thu, 26 Oct 2017 07:51:15 +0200 Subject: [PATCH 19/23] Move profile in membersList to interface method --- packages/rocketchat-lib/lib/RoomTypeConfig.js | 4 ++++ packages/rocketchat-lib/lib/roomTypes/direct.js | 4 ++++ packages/rocketchat-lib/lib/roomTypes/private.js | 4 ++++ packages/rocketchat-lib/lib/roomTypes/public.js | 4 ++++ packages/rocketchat-ui/client/views/app/room.js | 2 +- 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index f191fa153a60..2c5abb62ec50 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -180,4 +180,8 @@ export class RoomTypeConfig { includeInRoomSearch() { return false; } + + enableMembersListProfile() { + return false; + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/direct.js b/packages/rocketchat-lib/lib/roomTypes/direct.js index 546c7b97e9f4..7ef3e8ff034b 100644 --- a/packages/rocketchat-lib/lib/roomTypes/direct.js +++ b/packages/rocketchat-lib/lib/roomTypes/direct.js @@ -93,4 +93,8 @@ export class DirectMessageRoomType extends RoomTypeConfig { return true; } } + + enableMembersListProfile() { + return true; + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/private.js b/packages/rocketchat-lib/lib/roomTypes/private.js index 006d848d29e9..a4dffc056476 100644 --- a/packages/rocketchat-lib/lib/roomTypes/private.js +++ b/packages/rocketchat-lib/lib/roomTypes/private.js @@ -65,4 +65,8 @@ export class PrivateRoomType extends RoomTypeConfig { return true; } } + + enableMembersListProfile() { + return true; + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/public.js b/packages/rocketchat-lib/lib/roomTypes/public.js index c4acf55c0653..ca93ff8bd2f3 100644 --- a/packages/rocketchat-lib/lib/roomTypes/public.js +++ b/packages/rocketchat-lib/lib/roomTypes/public.js @@ -65,4 +65,8 @@ export class PublicRoomType extends RoomTypeConfig { allowRoomSettingChange() { return true; } + + enableMembersListProfile() { + return true; + } } diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 3334cca15d33..31c56dbd8077 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -24,7 +24,7 @@ const openProfileTab = (e, instance, username) => { return; } - if (['c', 'p', 'd'].includes(roomData.t)) { + if (RocketChat.roomTypes.roomTypes[roomData.t].enableMembersListProfile()) { instance.setUserDetail(username); } From a480c207b9bc4e31cd1dad154e6910d260b154e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sat, 28 Oct 2017 13:22:08 +0200 Subject: [PATCH 20/23] Add interface method for UI-texts. Provide implementation for the confirmation dialogs on leave and hide --- .../.npm/package/npm-shrinkwrap.json | 52 ++++++++++--------- packages/rocketchat-lib/client/lib/index.js | 5 +- packages/rocketchat-lib/lib/RoomTypeConfig.js | 15 ++++++ .../rocketchat-lib/lib/roomTypes/direct.js | 21 ++++++-- .../rocketchat-lib/lib/roomTypes/private.js | 13 ++++- .../rocketchat-lib/lib/roomTypes/public.js | 15 +++++- packages/rocketchat-livechat/roomType.js | 19 +++++-- .../rocketchat-ui/client/views/app/popover.js | 18 ++----- 8 files changed, 105 insertions(+), 53 deletions(-) diff --git a/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json b/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json index 2617915b67c1..b2d682c623e3 100644 --- a/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json +++ b/packages/rocketchat-google-vision/.npm/package/npm-shrinkwrap.json @@ -1,8 +1,8 @@ { "dependencies": { "ajv": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", "from": "ajv@>=5.1.0 <6.0.0" }, "ansi-regex": { @@ -247,6 +247,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", "from": "fast-deep-equal@>=1.0.0 <2.0.0" }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "from": "fast-json-stable-stringify@>=2.0.0 <3.0.0" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -285,7 +290,14 @@ "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "from": "globby@>=6.1.0 <7.0.0" + "from": "globby@>=6.1.0 <7.0.0", + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "from": "pify@>=2.0.0 <3.0.0" + } + } }, "google-auth-library": { "version": "0.10.0", @@ -919,8 +931,8 @@ } }, "gtoken": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.2.tgz", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.3.tgz", "from": "gtoken@>=1.2.1 <2.0.0" }, "har-schema": { @@ -1023,21 +1035,11 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "from": "json-schema-traverse@>=0.3.0 <0.4.0" }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "from": "json-stable-stringify@>=1.0.1 <2.0.0" - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "from": "json-stringify-safe@>=5.0.1 <5.1.0" }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "from": "jsonify@>=0.0.0 <0.1.0" - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -1079,8 +1081,8 @@ "from": "long@>=3.0.0 <4.0.0" }, "make-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", "from": "make-dir@>=1.0.0 <2.0.0" }, "methmeth": { @@ -1091,7 +1093,7 @@ "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "from": "mime@>=1.2.11 <2.0.0" + "from": "mime@>=1.4.1 <2.0.0" }, "mime-db": { "version": "1.30.0", @@ -1164,9 +1166,9 @@ "from": "performance-now@>=2.1.0 <3.0.0" }, "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "from": "pify@>=2.3.0 <3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "from": "pify@>=3.0.0 <4.0.0" }, "pinkie": { "version": "2.0.4", @@ -1229,8 +1231,8 @@ "from": "request@>=2.79.0 <3.0.0" }, "retry-request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.0.1.tgz", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.1.0.tgz", "from": "retry-request@>=3.0.0 <4.0.0" }, "rgb-hex": { @@ -1249,8 +1251,8 @@ "from": "signal-exit@>=3.0.2 <4.0.0" }, "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "from": "sntp@>=2.0.0 <3.0.0" }, "split-array-stream": { diff --git a/packages/rocketchat-lib/client/lib/index.js b/packages/rocketchat-lib/client/lib/index.js index deb1d177fb31..908b583d61af 100644 --- a/packages/rocketchat-lib/client/lib/index.js +++ b/packages/rocketchat-lib/client/lib/index.js @@ -7,12 +7,13 @@ */ import { RocketChatTabBar } from './RocketChatTabBar'; -import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../../lib/RoomTypeConfig'; +import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig, UiTextContext } from '../../lib/RoomTypeConfig'; export { RocketChatTabBar, RoomSettingsEnum, RoomTypeConfig, - RoomTypeRouteConfig + RoomTypeRouteConfig, + UiTextContext }; diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index 2c5abb62ec50..34c419d5522e 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -9,6 +9,11 @@ export const RoomSettingsEnum = { JOIN_CODE: 'joinCode' }; +export const UiTextContext = { + HIDE_WARNING: 'hideWarning', + LEAVE_WARNING: 'leaveWarning' +}; + export class RoomTypeRouteConfig { constructor({ name, path }) { if (typeof name !== 'undefined' && (typeof name !== 'string' || name.length === 0)) { @@ -184,4 +189,14 @@ export class RoomTypeConfig { 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 ''; + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/direct.js b/packages/rocketchat-lib/lib/roomTypes/direct.js index 7ef3e8ff034b..c98a3f4b3aae 100644 --- a/packages/rocketchat-lib/lib/roomTypes/direct.js +++ b/packages/rocketchat-lib/lib/roomTypes/direct.js @@ -1,5 +1,5 @@ /* globals openRoom */ -import { RoomTypeConfig, RoomTypeRouteConfig, RoomSettingsEnum } from '../RoomTypeConfig'; +import {RoomTypeConfig, RoomTypeRouteConfig, RoomSettingsEnum, UiTextContext} from '../RoomTypeConfig'; export class DirectMessageRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -14,7 +14,7 @@ export class DirectMessageRoomRoute extends RoomTypeRouteConfig { } link(sub) { - return { username: sub.name }; + return {username: sub.name}; } } @@ -41,7 +41,7 @@ export class DirectMessageRoomType extends RoomTypeConfig { } roomName(roomData) { - const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1, fname: 1 }}); + const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1, fname: 1}}); if (!subscription) { return ''; } @@ -55,7 +55,7 @@ export class DirectMessageRoomType extends RoomTypeConfig { secondaryRoomName(roomData) { if (RocketChat.settings.get('UI_Use_Real_Name')) { - const subscription = ChatSubscription.findOne({ rid: roomData._id }, { fields: { name: 1 }}); + const subscription = ChatSubscription.findOne({rid: roomData._id}, {fields: {name: 1}}); return subscription && subscription.name; } } @@ -68,7 +68,7 @@ export class DirectMessageRoomType extends RoomTypeConfig { } getUserStatus(roomId) { - const subscription = RocketChat.models.Subscriptions.findOne({ rid: roomId }); + const subscription = RocketChat.models.Subscriptions.findOne({rid: roomId}); if (subscription == null) { return; } @@ -97,4 +97,15 @@ export class DirectMessageRoomType extends RoomTypeConfig { enableMembersListProfile() { return true; } + + getUiText(context) { + switch (context) { + case UiTextContext.HIDE_WARNING: + return 'Hide_Private_Warning'; + case UiTextContext.LEAVE_WARNING: + return 'Leave_Private_Warning'; + default: + return ''; + } + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/private.js b/packages/rocketchat-lib/lib/roomTypes/private.js index a4dffc056476..cc01d86fe93c 100644 --- a/packages/rocketchat-lib/lib/roomTypes/private.js +++ b/packages/rocketchat-lib/lib/roomTypes/private.js @@ -1,5 +1,5 @@ /* globals openRoom */ -import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; +import {RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig, UiTextContext} from '../RoomTypeConfig'; export class PrivateRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -69,4 +69,15 @@ export class PrivateRoomType extends RoomTypeConfig { enableMembersListProfile() { return true; } + + getUiText(context) { + switch (context) { + case UiTextContext.HIDE_WARNING: + return 'Hide_Group_Warning'; + case UiTextContext.LEAVE_WARNING: + return 'Leave_Group_Warning'; + default: + return ''; + } + } } diff --git a/packages/rocketchat-lib/lib/roomTypes/public.js b/packages/rocketchat-lib/lib/roomTypes/public.js index ca93ff8bd2f3..7d6425230bbc 100644 --- a/packages/rocketchat-lib/lib/roomTypes/public.js +++ b/packages/rocketchat-lib/lib/roomTypes/public.js @@ -1,5 +1,5 @@ /* globals openRoom */ -import { RoomTypeConfig, RoomTypeRouteConfig } from '../RoomTypeConfig'; +import {RoomTypeConfig, RoomTypeRouteConfig, UiTextContext} from '../RoomTypeConfig'; export class PublicRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -47,7 +47,7 @@ export class PublicRoomType extends RoomTypeConfig { } showJoinLink(roomId) { - return !!ChatRoom.findOne({ _id: roomId, t: 'c' }); + return !!ChatRoom.findOne({_id: roomId, t: 'c'}); } includeInRoomSearch() { @@ -69,4 +69,15 @@ export class PublicRoomType extends RoomTypeConfig { enableMembersListProfile() { return true; } + + getUiText(context) { + switch (context) { + case UiTextContext.HIDE_WARNING: + return 'Hide_Room_Warning'; + case UiTextContext.LEAVE_WARNING: + return 'Leave_Room_Warning'; + default: + return ''; + } + } } diff --git a/packages/rocketchat-livechat/roomType.js b/packages/rocketchat-livechat/roomType.js index 5899aba990fa..19d4d1650f17 100644 --- a/packages/rocketchat-livechat/roomType.js +++ b/packages/rocketchat-livechat/roomType.js @@ -1,5 +1,5 @@ /* globals openRoom, LivechatInquiry */ -import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from 'meteor/rocketchat:lib'; +import {RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig, UiTextContext} from 'meteor/rocketchat:lib'; class LivechatRoomRoute extends RoomTypeRouteConfig { constructor() { @@ -36,7 +36,7 @@ class LivechatRoomType extends RoomTypeConfig { } findRoom(identifier) { - return ChatRoom.findOne({ code: parseInt(identifier) }); + return ChatRoom.findOne({code: parseInt(identifier)}); } roomName(roomData) { @@ -52,7 +52,7 @@ class LivechatRoomType extends RoomTypeConfig { } canSendMessage(roomId) { - const room = ChatRoom.findOne({ _id: roomId }, { fields: { open: 1 } }); + const room = ChatRoom.findOne({_id: roomId}, {fields: {open: 1}}); return room && room.open === true; } @@ -63,7 +63,7 @@ class LivechatRoomType extends RoomTypeConfig { if (room) { guestName = room.v && room.v.username; } else { - const inquiry = LivechatInquiry.findOne({ rid: roomId }); + const inquiry = LivechatInquiry.findOne({rid: roomId}); guestName = inquiry && inquiry.v && inquiry.v.username; } @@ -80,6 +80,17 @@ class LivechatRoomType extends RoomTypeConfig { return true; } } + + getUiText(context) { + switch (context) { + case UiTextContext.HIDE_WARNING: + return 'Hide_Livechat_Warning'; + case UiTextContext.LEAVE_WARNING: + return 'Hide_Livechat_Warning'; + default: + return ''; + } + } } RocketChat.roomTypes.add(new LivechatRoomType()); diff --git a/packages/rocketchat-ui/client/views/app/popover.js b/packages/rocketchat-ui/client/views/app/popover.js index 7d5f6ad963ac..f4b3edeb32d9 100644 --- a/packages/rocketchat-ui/client/views/app/popover.js +++ b/packages/rocketchat-ui/client/views/app/popover.js @@ -1,5 +1,7 @@ /* globals popover isRtl */ +import {UiTextContext} from 'meteor/rocketchat:lib'; + this.popover = { renderedPopover: null, open(config) { @@ -176,13 +178,7 @@ Template.popover.events({ const { rid, name, template } = instance.data.data; if (e.currentTarget.dataset.id === 'hide') { - let warnText; - switch (template) { - case 'c': warnText = 'Hide_Room_Warning'; break; - case 'p': warnText = 'Hide_Group_Warning'; break; - case 'd': warnText = 'Hide_Private_Warning'; break; - case 'l': warnText = 'Hide_Livechat_Warning'; break; - } + const warnText = RocketChat.roomTypes.roomTypes[template].getUiText(UiTextContext.HIDE_WARNING); return swal({ title: t('Are_you_sure'), @@ -208,13 +204,7 @@ Template.popover.events({ }); }); } else { - let warnText; - switch (template) { - case 'c': warnText = 'Leave_Room_Warning'; break; - case 'p': warnText = 'Leave_Group_Warning'; break; - case 'd': warnText = 'Leave_Private_Warning'; break; - case 'l': warnText = 'Hide_Livechat_Warning'; break; - } + const warnText = RocketChat.roomTypes.roomTypes[template].getUiText(UiTextContext.LEAVE_WARNING); swal({ title: t('Are_you_sure'), From c5c0eceaec46902e9d2d6a6b234e653b349fc936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Sat, 28 Oct 2017 14:06:12 +0200 Subject: [PATCH 21/23] Added a closing text constant --- packages/rocketchat-lib/lib/RoomTypeConfig.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index 34c419d5522e..bf48ea4ed4fc 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -10,6 +10,7 @@ export const RoomSettingsEnum = { }; export const UiTextContext = { + CLOSE_WARNING: 'closeWarning', HIDE_WARNING: 'hideWarning', LEAVE_WARNING: 'leaveWarning' }; From 916afa56de0fa91e5af5ac6e16f1a52c17d82ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Fri, 3 Nov 2017 20:52:39 +0100 Subject: [PATCH 22/23] Allow room types to provide an own "no subscriptions yet" text (cherry picked from commit b463b84) --- packages/rocketchat-lib/lib/RoomTypeConfig.js | 3 +- .../client/roomList.html | 2 +- .../rocketchat-ui-sidenav/client/roomList.js | 31 ++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/rocketchat-lib/lib/RoomTypeConfig.js b/packages/rocketchat-lib/lib/RoomTypeConfig.js index bf48ea4ed4fc..43027c05dada 100644 --- a/packages/rocketchat-lib/lib/RoomTypeConfig.js +++ b/packages/rocketchat-lib/lib/RoomTypeConfig.js @@ -12,7 +12,8 @@ export const RoomSettingsEnum = { export const UiTextContext = { CLOSE_WARNING: 'closeWarning', HIDE_WARNING: 'hideWarning', - LEAVE_WARNING: 'leaveWarning' + LEAVE_WARNING: 'leaveWarning', + NO_ROOMS_SUBSCRIBED: 'noRoomsSubscribed' }; export class RoomTypeRouteConfig { diff --git a/packages/rocketchat-ui-sidenav/client/roomList.html b/packages/rocketchat-ui-sidenav/client/roomList.html index 4b4ef3ea92d2..10af65511c07 100644 --- a/packages/rocketchat-ui-sidenav/client/roomList.html +++ b/packages/rocketchat-ui-sidenav/client/roomList.html @@ -16,7 +16,7 @@

{{#each room in this}} {{> chatRoomItem room }} {{else}} -

{{_ "No_channels_yet" }}

+

{{_ noSubscriptionText }}

{{/each}} {{/if}} diff --git a/packages/rocketchat-ui-sidenav/client/roomList.js b/packages/rocketchat-ui-sidenav/client/roomList.js index c59bdb92c150..ed0b515a127a 100644 --- a/packages/rocketchat-ui-sidenav/client/roomList.js +++ b/packages/rocketchat-ui-sidenav/client/roomList.js @@ -1,16 +1,19 @@ +/* globals RocketChat */ +import {UiTextContext} from 'meteor/rocketchat:lib'; + Template.roomList.helpers({ rooms() { if (this.identifier === 'unread') { const query = { alert: true, open: true, - hideUnreadStatus: { $ne: true } + hideUnreadStatus: {$ne: true} }; - return ChatSubscription.find(query, { sort: { 't': 1, 'name': 1 }}); + return ChatSubscription.find(query, {sort: {'t': 1, 'name': 1}}); } if (this.anonymous) { - return RocketChat.models.Rooms.find({t: 'c'}, { sort: { name: 1 } }); + return RocketChat.models.Rooms.find({t: 'c'}, {sort: {name: 1}}); } @@ -19,7 +22,7 @@ Template.roomList.helpers({ const query = { open: true }; - const sort = { 't': 1, 'name': 1 }; + const sort = {'t': 1, 'name': 1}; if (this.identifier === 'f') { query.f = favoritesEnabled; } else { @@ -28,21 +31,21 @@ Template.roomList.helpers({ types = ['c', 'p', 'd']; } if (this.identifier === 'channels' || this.identifier === 'unread') { - types = [ 'c', 'p']; + types = ['c', 'p']; } const user = Meteor.user(); if (user && user.settings && user.settings.preferences && user.settings.preferences.roomsListExhibitionMode === 'unread') { query.$or = [ - { alert: { $ne: true } }, - { hideUnreadStatus: true } + {alert: {$ne: true}}, + {hideUnreadStatus: true} ]; } - query.t = { $in: types }; - query.f = { $ne: favoritesEnabled }; + query.t = {$in: types}; + query.f = {$ne: favoritesEnabled}; } if (this.identifier === 'activity') { const list = ChatSubscription.find(query).fetch().map(sub => { - const lm = RocketChat.models.Rooms.findOne(sub.rid, { fields: { _updatedAt: 1 }})._updatedAt; + const lm = RocketChat.models.Rooms.findOne(sub.rid, {fields: {_updatedAt: 1}})._updatedAt; return { lm: lm && lm.toISOString(), ...sub @@ -50,7 +53,7 @@ Template.roomList.helpers({ }); return _.sortBy(list, 'lm').reverse(); } - return ChatSubscription.find(query, { sort }); + return ChatSubscription.find(query, {sort}); }, isLivechat() { @@ -71,6 +74,12 @@ Template.roomList.helpers({ if (room.header || room.identifier) { return `type-${ room.header || room.identifier }`; } + }, + + noSubscriptionText() { + const instance = Template.instance(); + const roomType = (instance.data.header || instance.data.identifier); + return RocketChat.roomTypes.roomTypes[roomType].getUiText(UiTextContext.NO_ROOMS_SUBSCRIBED) || 'No_channels_yet'; } }); From 5f068ca4095d45c81206aa91d23da1df19590c08 Mon Sep 17 00:00:00 2001 From: Bradley Hilton Date: Mon, 4 Dec 2017 15:04:56 -0600 Subject: [PATCH 23/23] Fix the tokenpass room type not aligning --- .../rocketchat-tokenpass/client/roomType.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/rocketchat-tokenpass/client/roomType.js b/packages/rocketchat-tokenpass/client/roomType.js index dd551724da2a..dcb6f23634cb 100644 --- a/packages/rocketchat-tokenpass/client/roomType.js +++ b/packages/rocketchat-tokenpass/client/roomType.js @@ -1,9 +1,21 @@ -RocketChat.roomTypes.add('tokenpass', 1, { - customTemplate: 'tokenChannelsList', +import { RoomTypeConfig } from 'meteor/rocketchat:lib'; + +class TokenPassRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'tokenpass', + order: 1 + }); + + this.customTemplate = 'tokenChannelsList'; + } + condition() { const user = Meteor.user(); const hasTokenpass = !!(user && user.services && user.services.tokenpass); return hasTokenpass; } -}); +} + +RocketChat.roomTypes.add(new TokenPassRoomType());