pull/6551/head
Guilherme Gazzo 9 years ago
parent 112f2e6187
commit fc6541692d
  1. 31
      packages/rocketchat-channel-settings/client/lib/ChannelSettings.coffee
  2. 28
      packages/rocketchat-channel-settings/client/lib/ChannelSettings.js
  3. 38
      packages/rocketchat-channel-settings/client/startup/messageTypes.coffee
  4. 16
      packages/rocketchat-channel-settings/client/startup/messageTypes.js
  5. 8
      packages/rocketchat-channel-settings/client/startup/tabBar.coffee
  6. 10
      packages/rocketchat-channel-settings/client/startup/tabBar.js
  7. 307
      packages/rocketchat-channel-settings/client/views/channelSettings.coffee
  8. 425
      packages/rocketchat-channel-settings/client/views/channelSettings.js
  9. 27
      packages/rocketchat-channel-settings/package.js
  10. 11
      packages/rocketchat-channel-settings/server/functions/saveRoomDescription.coffee
  11. 12
      packages/rocketchat-channel-settings/server/functions/saveRoomDescription.js
  12. 29
      packages/rocketchat-channel-settings/server/functions/saveRoomName.coffee
  13. 36
      packages/rocketchat-channel-settings/server/functions/saveRoomName.js
  14. 7
      packages/rocketchat-channel-settings/server/functions/saveRoomReadOnly.coffee
  15. 8
      packages/rocketchat-channel-settings/server/functions/saveRoomReadOnly.js
  16. 7
      packages/rocketchat-channel-settings/server/functions/saveRoomSystemMessages.coffee
  17. 8
      packages/rocketchat-channel-settings/server/functions/saveRoomSystemMessages.js
  18. 12
      packages/rocketchat-channel-settings/server/functions/saveRoomTopic.coffee
  19. 13
      packages/rocketchat-channel-settings/server/functions/saveRoomTopic.js
  20. 26
      packages/rocketchat-channel-settings/server/functions/saveRoomType.coffee
  21. 44
      packages/rocketchat-channel-settings/server/functions/saveRoomType.js
  22. 55
      packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee
  23. 95
      packages/rocketchat-channel-settings/server/methods/saveRoomSettings.js
  24. 5
      packages/rocketchat-channel-settings/server/models/Messages.coffee
  25. 7
      packages/rocketchat-channel-settings/server/models/Messages.js
  26. 55
      packages/rocketchat-channel-settings/server/models/Rooms.coffee
  27. 65
      packages/rocketchat-channel-settings/server/models/Rooms.js

@ -1,31 +0,0 @@
RocketChat.ChannelSettings = new class
options = new ReactiveVar {}
###
# Adds an option in Channel Settings
# @config (object)
# id: option id (required)
# template (string): template name to render (required)
# validation (function): if option should be displayed
###
addOption = (config) ->
unless config?.id
return false
Tracker.nonreactive ->
opts = options.get()
opts[config.id] = config
options.set opts
getOptions = (currentData, group) ->
allOptions = _.toArray options.get()
allowedOptions = _.compact _.map allOptions, (option) ->
if not option.validation? or option.validation()
option.data = Object.assign (option.data or {}), currentData
return option
allowedOptions = allowedOptions.filter (option) ->
!group or !option.group or option.group?.indexOf(group) > -1
return _.sortBy allowedOptions, 'order'
addOption: addOption
getOptions: getOptions

@ -0,0 +1,28 @@
RocketChat.ChannelSettings = new class {
constructor() {
this.options = new ReactiveVar({});
}
addOption(config) {
if (!(config && config.id)) {
return false;
}
return Tracker.nonreactive(() => {
const opts = this.options.get();
opts[config.id] = config;
return this.options.set(opts);
});
}
getOptions(currentData, group) {
const allOptions = _.toArray(this.options.get());
const allowedOptions = _.compact(_.map(allOptions, function(option) {
if ((option.validation == null) || option.validation()) {
option.data = Object.assign(option.data || {}, currentData);
return option;
}
})).filter(function(option) {
return !group || !option.group || option.group.includes(group);
});
return _.sortBy(allowedOptions, 'order');
}
};

@ -1,38 +0,0 @@
Meteor.startup ->
RocketChat.MessageTypes.registerType
id: 'room_changed_privacy'
system: true
message: 'room_changed_privacy'
data: (message) ->
return {
user_by: message.u?.username
room_type: message.msg
}
RocketChat.MessageTypes.registerType
id: 'room_changed_topic'
system: true
message: 'room_changed_topic'
data: (message) ->
return {
user_by: message.u?.username
room_topic: message.msg
}
RocketChat.MessageTypes.registerType
id: 'room_changed_announcement'
system: true
message: 'room_changed_announcement'
data: (message) ->
return {
user_by: message.u?.username
room_announcement: message.msg
}
RocketChat.MessageTypes.registerType
id: 'room_changed_description'
system: true
message: 'room_changed_description'
data: (message) ->
user_by: message.u?.username
room_description: message.msg

@ -0,0 +1,16 @@
function data(message) {
return {
user_by: message.u && message.u.username,
room_type: message.msg
};
}
Meteor.startup(function() {
['room_changed_privacy', 'room_changed_topic', 'room_changed_announcement', 'room_changed_description'].forEach(id => {
RocketChat.MessageTypes.registerType({
id,
system: true,
message: id,
data
});
});
});

@ -1,8 +0,0 @@
Meteor.startup ->
RocketChat.TabBar.addButton
groups: ['channel', 'group', 'direct']
id: 'channel-settings'
i18nTitle: 'Room_Info'
icon: 'icon-info-circled'
template: 'channelSettings'
order: 0

@ -0,0 +1,10 @@
Meteor.startup(() => {
RocketChat.TabBar.addButton({
groups: ['channel', 'group', 'direct'],
id: 'channel-settings',
i18nTitle: 'Room_Info',
icon: 'icon-info-circled',
template: 'channelSettings',
order: 0
});
});

@ -1,307 +0,0 @@
import toastr from 'toastr'
Template.channelSettings.helpers
toArray: (obj) ->
arr = []
for key, value of obj
arr.push
$key: key
$value: value
return arr
valueOf: (obj, key) ->
if key is 't'
if obj[key] is 'c'
return false
return true
return obj?[key]
showSetting: (setting, room) ->
if setting.showInDirect is false
return room.t isnt 'd'
return true
settings: ->
return Template.instance().settings
getRoom: ->
return ChatRoom.findOne(@rid)
editing: (field) ->
return Template.instance().editing.get() is field
isDisabled: (field, room) ->
return Template.instance().settings[field].disabled?(room) or Template.instance().settings[field].processing.get() or !RocketChat.authz.hasAllPermission('edit-room', room._id)
channelSettings: ->
return RocketChat.ChannelSettings.getOptions(Template.currentData(), 'room')
unscape: (value) ->
return s.unescapeHTML value
canDeleteRoom: ->
roomType = ChatRoom.findOne(@rid, { fields: { t: 1 }})?.t
return roomType? and RocketChat.authz.hasAtLeastOnePermission("delete-#{roomType}", @rid)
readOnly: ->
return ChatRoom.findOne(@rid, { fields: { ro: 1 }})?.ro
has: (v, key) ->
v?[key]?
readOnlyDescription: ->
readOnly = ChatRoom.findOne(@rid, { fields: { ro: 1 }})?.ro
if readOnly is true
return t('True')
else
return t('False')
Template.channelSettings.events
'click .delete': ->
swal {
title: t('Are_you_sure')
text: t('Delete_Room_Warning')
type: 'warning'
showCancelButton: true
confirmButtonColor: '#DD6B55'
confirmButtonText: t('Yes_delete_it')
cancelButtonText: t('Cancel')
closeOnConfirm: false
html: false
}, =>
swal.disableButtons()
Meteor.call 'eraseRoom', @rid, (error, result) ->
if error
handleError(error)
swal.enableButtons()
else
swal
title: t('Deleted')
text: t('Room_has_been_deleted')
type: 'success'
timer: 2000
showConfirmButton: false
'keydown input[type=text]': (e, t) ->
if e.keyCode is 13
e.preventDefault()
t.saveSetting()
'click [data-edit]': (e, t) ->
e.preventDefault()
if $(e.currentTarget).data('edit')
t.editing.set($(e.currentTarget).data('edit'))
setTimeout (-> t.$('input.editing').focus().select()), 100
'change [type="radio"]': (e, t) ->
t.editing.set($(e.currentTarget).attr('name'))
'change [type="checkbox"]': (e, t) ->
t.editing.set($(e.currentTarget).attr('name'))
t.saveSetting()
'click .cancel': (e, t) ->
e.preventDefault()
t.editing.set()
'click .save': (e, t) ->
e.preventDefault()
t.saveSetting()
Template.channelSettings.onCreated ->
@editing = new ReactiveVar
@settings =
name:
type: 'text'
label: 'Name'
canView: (room) => room.t isnt 'd'
canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id)
save: (value, room) ->
if not RocketChat.authz.hasAllPermission('edit-room', room._id) or room.t not in ['c', 'p']
return toastr.error t('error-not-allowed')
try
nameValidation = new RegExp '^' + RocketChat.settings.get('UTF8_Names_Validation') + '$'
catch
nameValidation = new RegExp '^[0-9a-zA-Z-_.]+$'
if not nameValidation.test value
return toastr.error t('error-invalid-room-name', { room_name: name: value })
RocketChat.callbacks.run 'roomNameChanged', { _id: room._id, name: value }
Meteor.call 'saveRoomSettings', room._id, 'roomName', value, (err, result) ->
return handleError err if err
toastr.success TAPi18n.__ 'Room_name_changed_successfully'
topic:
type: 'markdown'
label: 'Topic'
canView: (room) => true
canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id)
save: (value, room) ->
Meteor.call 'saveRoomSettings', room._id, 'roomTopic', value, (err, result) ->
return handleError err if err
toastr.success TAPi18n.__ 'Room_topic_changed_successfully'
RocketChat.callbacks.run 'roomTopicChanged', room
announcement:
type: 'markdown'
label: 'Announcement'
canView: (room) => true
canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id)
save: (value, room) ->
Meteor.call 'saveRoomSettings', room._id, 'roomAnnouncement', value, (err, result) ->
return handleError err if err
toastr.success TAPi18n.__ 'Room_announcement_changed_successfully'
RocketChat.callbacks.run 'roomAnnouncementChanged', room
description:
type: 'text'
label: 'Description'
canView: (room) => room.t isnt 'd'
canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id)
save: (value, room) ->
Meteor.call 'saveRoomSettings', room._id, 'roomDescription', value, (err, result) ->
return handleError err if err
toastr.success TAPi18n.__ 'Room_description_changed_successfully'
t:
type: 'boolean'
label: 'Private'
isToggle: true
processing: new ReactiveVar(false)
disabled: (room) =>
room.default and not RocketChat.authz.hasRole( Meteor.userId(), 'admin')
message: (room) =>
#if the user can change but the channel is default
if RocketChat.authz.hasAllPermission('edit-room', room._id) and room.default
# if you are an admin, even so you can change
unless RocketChat.authz.hasRole( Meteor.userId(), 'admin')
return 'Room_type_of_default_rooms_cant_be_changed'
canView: (room) ->
if room.t not in ['c', 'p']
return false
else if room.t is 'p' and not RocketChat.authz.hasAllPermission('create-c')
return false
else if room.t is 'c' and not RocketChat.authz.hasAllPermission('create-p')
return false
return true
canEdit: (room) => ( RocketChat.authz.hasAllPermission('edit-room', room._id) and not room.default) or RocketChat.authz.hasRole( Meteor.userId(), 'admin')
save: (value, room) ->
saveRoomSettings = =>
@processing.set(true)
value = if value then 'p' else 'c'
RocketChat.callbacks.run 'roomTypeChanged', room
Meteor.call 'saveRoomSettings', room._id, 'roomType', value, (err, result) =>
return handleError err if err
@processing.set(false)
toastr.success TAPi18n.__ 'Room_type_changed_successfully'
if room.default
if RocketChat.authz.hasRole Meteor.userId(), 'admin'
swal {
title: t('Room_default_change_to_private_will_be_default_no_more')
type: 'warning'
showCancelButton: true
confirmButtonColor: '#DD6B55'
confirmButtonText: t('Yes')
cancelButtonText: t('Cancel')
closeOnConfirm: true
html: false
}, (confirmed) =>
return saveRoomSettings() if confirmed
$(".channel-settings form [name='t']").prop('checked', !!room.type == 'p')
else
saveRoomSettings()
ro:
type: 'boolean'
label: 'Read_only'
isToggle: true
processing: new ReactiveVar(false)
canView: (room) => room.t isnt 'd'
canEdit: (room) => RocketChat.authz.hasAllPermission('set-readonly', room._id)
save: (value, room) ->
@processing.set(true)
Meteor.call 'saveRoomSettings', room._id, 'readOnly', value, (err, result) =>
return handleError err if err
@processing.set(false)
toastr.success TAPi18n.__ 'Read_only_changed_successfully'
reactWhenReadOnly:
type: 'boolean'
label: 'React_when_read_only'
isToggle: true
processing: new ReactiveVar(false)
canView: (room) => room.t isnt 'd' and room.ro
canEdit: (room) => RocketChat.authz.hasAllPermission('set-react-when-readonly', room._id)
save: (value, room) ->
@processing.set(true)
Meteor.call 'saveRoomSettings', room._id, 'reactWhenReadOnly', value, (err, result) =>
return handleError err if err
@processing.set(false)
toastr.success TAPi18n.__ 'React_when_read_only_changed_successfully'
archived:
type: 'boolean'
label: 'Room_archivation_state_true'
isToggle: true,
processing: new ReactiveVar(false)
canView: (room) => room.t isnt 'd'
canEdit: (room) => RocketChat.authz.hasAtLeastOnePermission(['archive-room', 'unarchive-room'], room._id)
save: (value, room) =>
swal {
title: t('Are_you_sure')
type: 'warning'
showCancelButton: true
confirmButtonColor: '#DD6B55'
confirmButtonText: if value then t('Yes_archive_it') else t('Yes_unarchive_it')
cancelButtonText: t('Cancel')
closeOnConfirm: false
html: false
}, (confirmed) =>
swal.disableButtons()
if (confirmed)
action = if value then 'archiveRoom' else 'unarchiveRoom'
Meteor.call action, room._id, (err, results) =>
if err
swal.enableButtons()
handleError err
swal
title: if value then t('Room_archived') else t('Room_has_been_archived')
text: if value then t('Room_has_been_archived') else t('Room_has_been_unarchived')
type: 'success'
timer: 2000
showConfirmButton: false
RocketChat.callbacks.run action, room
else
$(".channel-settings form [name='archived']").prop('checked', !!room.archived)
joinCode:
type: 'text'
label: 'Password'
canView: (room) => room.t is 'c' and RocketChat.authz.hasAllPermission('edit-room', room._id)
canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id)
save: (value, room) ->
Meteor.call 'saveRoomSettings', room._id, 'joinCode', value, (err, result) ->
return handleError err if err
toastr.success TAPi18n.__ 'Room_password_changed_successfully'
RocketChat.callbacks.run 'roomCodeChanged', room
@saveSetting = =>
room = ChatRoom.findOne @data?.rid
field = @editing.get()
if @settings[field].type is 'select'
value = @$(".channel-settings form [name=#{field}]:checked").val()
else if @settings[field].type is 'boolean'
value = @$(".channel-settings form [name=#{field}]").is(":checked")
else
value = @$(".channel-settings form [name=#{field}]").val()
if value isnt room[field]
@settings[field].save(value, room)
@editing.set()

@ -0,0 +1,425 @@
import toastr from 'toastr';
Template.channelSettings.helpers({
toArray(obj) {
return Object.keys(obj).map((key) => {
return {
$key: key,
$value: obj[key]
};
});
},
valueOf(obj, key) {
if (key === 't') {
if (obj[key] === 'c') {
return false;
}
return true;
}
return obj && obj[key];
},
showSetting(setting, room) {
if (setting.showInDirect === false) {
return room.t !== 'd';
}
return true;
},
settings() {
return Template.instance().settings;
},
getRoom() {
return ChatRoom.findOne(this.rid);
},
editing(field) {
return Template.instance().editing.get() === field;
},
isDisabled(field, room) {
const setting = Template.instance().settings[field];
return (typeof setting.disabled === 'function' && setting.disabled(room)) || setting.processing.get() || !RocketChat.authz.hasAllPermission('edit-room', room._id);
},
channelSettings() {
return RocketChat.ChannelSettings.getOptions(Template.currentData(), 'room');
},
unscape(value) {
return s.unescapeHTML(value);
},
canDeleteRoom() {
const room = ChatRoom.findOne(this.rid, {
fields: {
t: 1
}
});
const roomType = room && room.t;
return roomType && RocketChat.authz.hasAtLeastOnePermission(`delete-${ roomType }`, this.rid);
},
readOnly() {
const room = ChatRoom.findOne(this.rid, {
fields: {
ro: 1
}
});
return room && room.ro;
},
has(v, key) {
return !!(v && v[key]);
},
readOnlyDescription() {
const room = ChatRoom.findOne(this.rid, {
fields: {
ro: 1
}
});
return t(room && room.ro ? 'True' : 'False');
}
});
Template.channelSettings.events({
'click .delete'() {
return swal({
title: t('Are_you_sure'),
text: t('Delete_Room_Warning'),
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: t('Yes_delete_it'),
cancelButtonText: t('Cancel'),
closeOnConfirm: false,
html: false
}, () => {
swal.disableButtons();
Meteor.call('eraseRoom', this.rid, function(error) {
if (error) {
handleError(error);
return swal.enableButtons();
}
swal({
title: t('Deleted'),
text: t('Room_has_been_deleted'),
type: 'success',
timer: 2000,
showConfirmButton: false
});
});
});
},
'keydown input[type=text]'(e, t) {
if (e.keyCode === 13) {
e.preventDefault();
t.saveSetting();
}
},
'click [data-edit]'(e, t) {
e.preventDefault();
if ($(e.currentTarget).data('edit')) {
t.editing.set($(e.currentTarget).data('edit'));
return setTimeout((function() {
return t.$('input.editing').focus().select();
}), 100);
}
},
'change [type="radio"]'(e, t) {
return t.editing.set($(e.currentTarget).attr('name'));
},
'change [type="checkbox"]'(e, t) {
t.editing.set($(e.currentTarget).attr('name'));
return t.saveSetting();
},
'click .cancel'(e, t) {
e.preventDefault();
return t.editing.set();
},
'click .save'(e, t) {
e.preventDefault();
return t.saveSetting();
}
});
Template.channelSettings.onCreated(function() {
this.editing = new ReactiveVar;
this.settings = {
name: {
type: 'text',
label: 'Name',
canView(room) {
return room.t !== 'd';
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
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'));
}
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: {
name: value
}
}));
}
Meteor.call('saveRoomSettings', room._id, 'roomName', value, function(err) {
if (err) {
return handleError(err);
}
RocketChat.callbacks.run('roomNameChanged', {
_id: room._id,
name: value
});
return toastr.success(TAPi18n.__('Room_name_changed_successfully'));
});
}
},
topic: {
type: 'markdown',
label: 'Topic',
canView() {
return true;
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
save(value, room) {
return Meteor.call('saveRoomSettings', room._id, 'roomTopic', value, function(err) {
if (err) {
return handleError(err);
}
toastr.success(TAPi18n.__('Room_topic_changed_successfully'));
return RocketChat.callbacks.run('roomTopicChanged', room);
});
}
},
announcement: {
type: 'markdown',
label: 'Announcement',
canView() {
return true;
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
save(value, room) {
return Meteor.call('saveRoomSettings', room._id, 'roomAnnouncement', value, function(err) {
if (err) {
return handleError(err);
}
toastr.success(TAPi18n.__('Room_announcement_changed_successfully'));
return RocketChat.callbacks.run('roomAnnouncementChanged', room);
});
}
},
description: {
type: 'text',
label: 'Description',
canView(room) {
return room.t !== 'd';
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
save(value, room) {
return Meteor.call('saveRoomSettings', room._id, 'roomDescription', value, function(err) {
if (err) {
return handleError(err);
}
return toastr.success(TAPi18n.__('Room_description_changed_successfully'));
});
}
},
t: {
type: 'boolean',
label: 'Private',
isToggle: true,
processing: new ReactiveVar(false),
disabled(room) {
return room['default'] && !RocketChat.authz.hasRole(Meteor.userId(), 'admin');
},
message(room) {
if (RocketChat.authz.hasAllPermission('edit-room', room._id) && room['default']) {
if (!RocketChat.authz.hasRole(Meteor.userId(), 'admin')) {
return 'Room_type_of_default_rooms_cant_be_changed';
}
}
},
canView(room) {
let ref;
if ((ref = room.t) !== 'c' && ref !== 'p') {
return false;
} else if (room.t === 'p' && !RocketChat.authz.hasAllPermission('create-c')) {
return false;
} else if (room.t === 'c' && !RocketChat.authz.hasAllPermission('create-p')) {
return false;
}
return true;
},
canEdit(room) {
return (RocketChat.authz.hasAllPermission('edit-room', room._id) && !room['default']) || RocketChat.authz.hasRole(Meteor.userId(), 'admin');
},
save(value, room) {
const saveRoomSettings = () => {
this.processing.set(true);
value = value ? 'p' : 'c';
RocketChat.callbacks.run('roomTypeChanged', room);
return Meteor.call('saveRoomSettings', room._id, 'roomType', value, (err) => {
if (err) {
return handleError(err);
}
this.processing.set(false);
return toastr.success(TAPi18n.__('Room_type_changed_successfully'));
});
};
if (room['default']) {
if (RocketChat.authz.hasRole(Meteor.userId(), 'admin')) {
swal({
title: t('Room_default_change_to_private_will_be_default_no_more'),
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: t('Yes'),
cancelButtonText: t('Cancel'),
closeOnConfirm: true,
html: false
}, function(confirmed) {
if (confirmed) {
return saveRoomSettings();
}
});
}
return $('.channel-settings form [name=\'t\']').prop('checked', !!room.type === 'p');
} else {
return saveRoomSettings();
}
}
},
ro: {
type: 'boolean',
label: 'Read_only',
isToggle: true,
processing: new ReactiveVar(false),
canView(room) {
return room.t !== 'd';
},
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) => {
if (err) {
return handleError(err);
}
this.processing.set(false);
return toastr.success(TAPi18n.__('Read_only_changed_successfully'));
});
}
},
reactWhenReadOnly: {
type: 'boolean',
label: 'React_when_read_only',
isToggle: true,
processing: new ReactiveVar(false),
canView(room) {
return room.t !== 'd' && room.ro;
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('set-react-when-readonly', room._id);
},
save(value, room) {
this.processing.set(true);
return Meteor.call('saveRoomSettings', room._id, 'reactWhenReadOnly', value, (err) => {
if (err) {
return handleError(err);
}
this.processing.set(false);
return toastr.success(TAPi18n.__('React_when_read_only_changed_successfully'));
});
}
},
archived: {
type: 'boolean',
label: 'Room_archivation_state_true',
isToggle: true,
processing: new ReactiveVar(false),
canView(room) {
return room.t !== 'd';
},
canEdit(room) {
return RocketChat.authz.hasAtLeastOnePermission(['archive-room', 'unarchive-room'], room._id);
},
save(value, room) {
return swal({
title: t('Are_you_sure'),
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: value ? t('Yes_archive_it') : t('Yes_unarchive_it'),
cancelButtonText: t('Cancel'),
closeOnConfirm: false,
html: false
}, function(confirmed) {
let action;
swal.disableButtons();
if (confirmed) {
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'),
type: 'success',
timer: 2000,
showConfirmButton: false
});
return RocketChat.callbacks.run(action, room);
});
} else {
return $('.channel-settings form [name=\'archived\']').prop('checked', !!room.archived);
}
});
}
},
joinCode: {
type: 'text',
label: 'Password',
canView(room) {
return room.t === 'c' && RocketChat.authz.hasAllPermission('edit-room', room._id);
},
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
save(value, room) {
return Meteor.call('saveRoomSettings', room._id, 'joinCode', value, function(err) {
if (err) {
return handleError(err);
}
toastr.success(TAPi18n.__('Room_password_changed_successfully'));
return RocketChat.callbacks.run('roomCodeChanged', room);
});
}
}
};
return this.saveSetting = () => {
const room = ChatRoom.findOne(this.data && this.data.rid);
const field = this.editing.get();
let value;
if (this.settings[field].type === 'select') {
value = this.$(`.channel-settings form [name=${ field }]:checked`).val();
} else if (this.settings[field].type === 'boolean') {
value = this.$(`.channel-settings form [name=${ field }]`).is(':checked');
} else {
value = this.$(`.channel-settings form [name=${ field }]`).val();
}
if (value !== room[field]) {
this.settings[field].save(value, room);
}
return this.editing.set();
};
});

@ -7,7 +7,6 @@ Package.describe({
Package.onUse(function(api) {
api.use([
'coffeescript',
'ecmascript',
'reactive-var',
'tracker',
@ -17,27 +16,27 @@ Package.onUse(function(api) {
]);
api.addFiles([
'client/lib/ChannelSettings.coffee',
'client/startup/messageTypes.coffee',
'client/startup/tabBar.coffee',
'client/lib/ChannelSettings.js',
'client/startup/messageTypes.js',
'client/startup/tabBar.js',
'client/startup/trackSettingsChange.js',
'client/views/channelSettings.html',
'client/views/channelSettings.coffee',
'client/views/channelSettings.js',
'client/stylesheets/channel-settings.less'
], 'client');
api.addFiles([
'server/functions/saveReactWhenReadOnly.js',
'server/functions/saveRoomType.coffee',
'server/functions/saveRoomTopic.coffee',
'server/functions/saveRoomType.js',
'server/functions/saveRoomTopic.js',
'server/functions/saveRoomAnnouncement.js',
'server/functions/saveRoomName.coffee',
'server/functions/saveRoomReadOnly.coffee',
'server/functions/saveRoomDescription.coffee',
'server/functions/saveRoomSystemMessages.coffee',
'server/methods/saveRoomSettings.coffee',
'server/models/Messages.coffee',
'server/models/Rooms.coffee',
'server/functions/saveRoomName.js',
'server/functions/saveRoomReadOnly.js',
'server/functions/saveRoomDescription.js',
'server/functions/saveRoomSystemMessages.js',
'server/methods/saveRoomSettings.js',
'server/models/Messages.js',
'server/models/Rooms.js',
'server/startup.js'
], 'server');
});

@ -1,11 +0,0 @@
RocketChat.saveRoomDescription = (rid, roomDescription, user) ->
unless Match.test rid, String
throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomDescription' }
roomDescription = s.escapeHTML(roomDescription)
update = RocketChat.models.Rooms.setDescriptionById rid, roomDescription
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_description', rid, roomDescription, user
return update

@ -0,0 +1,12 @@
RocketChat.saveRoomDescription = function(rid, roomDescription, user) {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomDescription'
});
}
const escapedRoomDescription = s.escapeHTML(roomDescription);
const update = RocketChat.models.Rooms.setDescriptionById(rid, escapedRoomDescription);
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_description', rid, escapedRoomDescription, user);
return update;
};

@ -1,29 +0,0 @@
RocketChat.saveRoomName = (rid, name, user, sendMessage=true) ->
room = RocketChat.models.Rooms.findOneById rid
if room.t not in ['c', 'p']
throw new Meteor.Error 'error-not-allowed', 'Not allowed', { function: 'RocketChat.saveRoomName' }
try
nameValidation = new RegExp '^' + RocketChat.settings.get('UTF8_Names_Validation') + '$'
catch
nameValidation = new RegExp '^[0-9a-zA-Z-_.]+$'
if not nameValidation.test name
throw new Meteor.Error 'error-invalid-room-name', name + ' is not a valid room name. Use only letters, numbers, hyphens and underscores', { function: 'RocketChat.saveRoomName', room_name: name }
# name = _.slugify name
if name is room.name
return
# avoid duplicate names
if RocketChat.models.Rooms.findOneByName name
throw new Meteor.Error 'error-duplicate-channel-name', 'A channel with name \'' + name + '\' exists', { function: 'RocketChat.saveRoomName', channel_name: name }
update = RocketChat.models.Rooms.setNameById(rid, name) and RocketChat.models.Subscriptions.updateNameAndAlertByRoomId(rid, name)
if update and sendMessage
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser rid, name, user
return name

@ -0,0 +1,36 @@
RocketChat.saveRoomName = function(rid, name, user, sendMessage = true) {
debugger
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.saveRoomName'
});
}
let nameValidation;
try {
nameValidation = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`);
} catch (error) {
nameValidation = new RegExp('^[0-9a-zA-Z-_.]+$');
}
if (!nameValidation.test(name)) {
throw new Meteor.Error('error-invalid-room-name', `${ name } is not a valid room name. Use only letters, numbers, hyphens and underscores`, {
'function': 'RocketChat.saveRoomName',
room_name: name
});
}
if (name === room.name) {
return;
}
if (RocketChat.models.Rooms.findOneByName(name)) {
throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ name }' exists`, {
'function': 'RocketChat.saveRoomName',
channel_name: name
});
}
const update = RocketChat.models.Rooms.setNameById(rid, name) && RocketChat.models.Subscriptions.updateNameAndAlertByRoomId(rid, name);
if (update && sendMessage) {
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser(rid, name, user);
}
return name;
};

@ -1,7 +0,0 @@
RocketChat.saveRoomReadOnly = (rid, readOnly, user) ->
unless Match.test rid, String
throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomReadOnly' }
update = RocketChat.models.Rooms.setReadOnlyById rid, readOnly
return update

@ -0,0 +1,8 @@
RocketChat.saveRoomReadOnly = function(rid, readOnly) {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomReadOnly'
});
}
return RocketChat.models.Rooms.setReadOnlyById(rid, readOnly);
};

@ -1,7 +0,0 @@
RocketChat.saveRoomSystemMessages = (rid, systemMessages, user) ->
unless Match.test rid, String
throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomSystemMessages' }
update = RocketChat.models.Rooms.setSystemMessagesById rid, systemMessages
return update

@ -0,0 +1,8 @@
RocketChat.saveRoomSystemMessages = function(rid, systemMessages) {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomSystemMessages'
});
}
return RocketChat.models.Rooms.setSystemMessagesById(rid, systemMessages);
};

@ -1,12 +0,0 @@
RocketChat.saveRoomTopic = (rid, roomTopic, user, sendMessage=true) ->
unless Match.test rid, String
throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomTopic' }
roomTopic = s.escapeHTML(roomTopic)
update = RocketChat.models.Rooms.setTopicById(rid, roomTopic)
if update and sendMessage
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_topic', rid, roomTopic, user
return update

@ -0,0 +1,13 @@
RocketChat.saveRoomTopic = function(rid, roomTopic, user, sendMessage = true) {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomTopic'
});
}
roomTopic = s.escapeHTML(roomTopic);
const update = RocketChat.models.Rooms.setTopicById(rid, roomTopic);
if (update && sendMessage) {
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', rid, roomTopic, user);
}
return update;
};

@ -1,26 +0,0 @@
RocketChat.saveRoomType = (rid, roomType, user, sendMessage=true) ->
unless Match.test rid, String
throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomType' }
if roomType not in ['c', 'p']
throw new Meteor.Error 'error-invalid-room-type', 'error-invalid-room-type', { function: 'RocketChat.saveRoomType', type: roomType }
room = RocketChat.models.Rooms.findOneById(rid);
if not room?
throw new Meteor.Error 'error-invalid-room', 'error-invalid-room', { function: 'RocketChat.saveRoomType', _id: rid }
if room.t is 'd'
throw new Meteor.Error 'error-direct-room', 'Can\'t change type of direct rooms', { function: 'RocketChat.saveRoomType' }
result = RocketChat.models.Rooms.setTypeById(rid, roomType) and RocketChat.models.Subscriptions.updateTypeByRoomId(rid, roomType)
if result and sendMessage
if roomType is 'c'
message = TAPi18n.__('Channel', { lng: user?.language || RocketChat.settings.get('language') || 'en' })
else
message = TAPi18n.__('Private_Group', { lng: user?.language || RocketChat.settings.get('language') || 'en' })
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_privacy', rid, message, user
return result

@ -0,0 +1,44 @@
RocketChat.saveRoomType = function(rid, roomType, user, sendMessage = true) {
if (sendMessage == null) {
sendMessage = true;
}
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomType'
});
}
if (roomType !== 'c' && roomType !== 'p') {
throw new Meteor.Error('error-invalid-room-type', 'error-invalid-room-type', {
'function': 'RocketChat.saveRoomType',
type: roomType
});
}
const room = RocketChat.models.Rooms.findOneById(rid);
if (room == null) {
throw new Meteor.Error('error-invalid-room', 'error-invalid-room', {
'function': 'RocketChat.saveRoomType',
_id: rid
});
}
if (room.t === 'd') {
throw new Meteor.Error('error-direct-room', 'Can\'t change type of direct rooms', {
'function': 'RocketChat.saveRoomType'
});
}
let message;
const result = RocketChat.models.Rooms.setTypeById(rid, roomType) && RocketChat.models.Subscriptions.updateTypeByRoomId(rid, roomType);
if (result && sendMessage) {
if (roomType === 'c') {
message = TAPi18n.__('Channel', {
lng: user && user.language || RocketChat.settings.get('language') || 'en'
});
} else {
message = TAPi18n.__('Private_Group', {
lng: user && user.language || RocketChat.settings.get('language') || 'en'
});
}
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_privacy', rid, message, user);
}
return result;
};

@ -1,55 +0,0 @@
Meteor.methods
saveRoomSettings: (rid, setting, value) ->
if not Meteor.userId()
throw new Meteor.Error('error-invalid-user', "Invalid user", { function: 'RocketChat.saveRoomName' })
unless Match.test rid, String
throw new Meteor.Error 'error-invalid-room', 'Invalid room', { method: 'saveRoomSettings' }
if setting not in ['roomName', 'roomTopic', 'roomAnnouncement', 'roomDescription', 'roomType', 'readOnly', 'reactWhenReadOnly', 'systemMessages', 'default', 'joinCode']
throw new Meteor.Error 'error-invalid-settings', 'Invalid settings provided', { method: 'saveRoomSettings' }
unless RocketChat.authz.hasPermission(Meteor.userId(), 'edit-room', rid)
throw new Meteor.Error 'error-action-not-allowed', 'Editing room is not allowed', { method: 'saveRoomSettings', action: 'Editing_room' }
if setting is 'default' and not RocketChat.authz.hasPermission(@userId, 'view-room-administration')
throw new Meteor.Error 'error-action-not-allowed', 'Viewing room administration is not allowed', { method: 'saveRoomSettings', action: 'Viewing_room_administration' }
room = RocketChat.models.Rooms.findOneById rid
if room?
if setting is 'roomType' and value isnt room.t and value is 'c' and not RocketChat.authz.hasPermission(@userId, 'create-c')
throw new Meteor.Error 'error-action-not-allowed', 'Changing a private group to a public channel is not allowed', { method: 'saveRoomSettings', action: 'Change_Room_Type' }
if setting is 'roomType' and value isnt room.t and value is 'p' and not RocketChat.authz.hasPermission(@userId, 'create-p')
throw new Meteor.Error 'error-action-not-allowed', 'Changing a public channel to a private room is not allowed', { method: 'saveRoomSettings', action: 'Change_Room_Type' }
switch setting
when 'roomName'
name = RocketChat.saveRoomName rid, value, Meteor.user()
when 'roomTopic'
if value isnt room.topic
RocketChat.saveRoomTopic(rid, value, Meteor.user())
when 'roomAnnouncement'
if value isnt room.announcement
RocketChat.saveRoomAnnouncement(rid, value, Meteor.user())
when 'roomDescription'
if value isnt room.description
RocketChat.saveRoomDescription rid, value, Meteor.user()
when 'roomType'
if value isnt room.t
RocketChat.saveRoomType(rid, value, Meteor.user())
when 'readOnly'
if value isnt room.ro
RocketChat.saveRoomReadOnly rid, value, Meteor.user()
when 'reactWhenReadOnly'
if value isnt room.reactWhenReadOnly
RocketChat.saveReactWhenReadOnly rid, value, Meteor.user()
when 'systemMessages'
if value isnt room.sysMes
RocketChat.saveRoomSystemMessages rid, value, Meteor.user()
when 'joinCode'
RocketChat.models.Rooms.setJoinCodeById rid, String(value)
when 'default'
RocketChat.models.Rooms.saveDefaultById rid, value
return { result: true, rid: room._id }

@ -0,0 +1,95 @@
Meteor.methods({
saveRoomSettings(rid, setting, value) {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
'function': 'RocketChat.saveRoomName'
});
}
if (!Match.test(rid, String)) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'saveRoomSettings'
});
}
if (setting !== 'roomName' && setting !== 'roomTopic' && setting !== 'roomAnnouncement' && setting !== 'roomDescription' && setting !== 'roomType' && setting !== 'readOnly' && setting !== 'reactWhenReadOnly' && setting !== 'systemMessages' && setting !== 'default' && setting !== 'joinCode') {
throw new Meteor.Error('error-invalid-settings', 'Invalid settings provided', {
method: 'saveRoomSettings'
});
}
if (!RocketChat.authz.hasPermission(Meteor.userId(), 'edit-room', rid)) {
throw new Meteor.Error('error-action-not-allowed', 'Editing room is not allowed', {
method: 'saveRoomSettings',
action: 'Editing_room'
});
}
if (setting === 'default' && !RocketChat.authz.hasPermission(this.userId, 'view-room-administration')) {
throw new Meteor.Error('error-action-not-allowed', 'Viewing room administration is not allowed', {
method: 'saveRoomSettings',
action: 'Viewing_room_administration'
});
}
const room = RocketChat.models.Rooms.findOneById(rid);
if (room != null) {
if (setting === 'roomType' && value !== room.t && value === 'c' && !RocketChat.authz.hasPermission(this.userId, 'create-c')) {
throw new Meteor.Error('error-action-not-allowed', 'Changing a private group to a public channel is not allowed', {
method: 'saveRoomSettings',
action: 'Change_Room_Type'
});
}
if (setting === 'roomType' && value !== room.t && value === 'p' && !RocketChat.authz.hasPermission(this.userId, 'create-p')) {
throw new Meteor.Error('error-action-not-allowed', 'Changing a public channel to a private room is not allowed', {
method: 'saveRoomSettings',
action: 'Change_Room_Type'
});
}
switch (setting) {
case 'roomName':
name = RocketChat.saveRoomName(rid, value, Meteor.user());
break;
case 'roomTopic':
if (value !== room.topic) {
RocketChat.saveRoomTopic(rid, value, Meteor.user());
}
break;
case 'roomAnnouncement':
if (value !== room.announcement) {
RocketChat.saveRoomAnnouncement(rid, value, Meteor.user());
}
break;
case 'roomDescription':
if (value !== room.description) {
RocketChat.saveRoomDescription(rid, value, Meteor.user());
}
break;
case 'roomType':
if (value !== room.t) {
RocketChat.saveRoomType(rid, value, Meteor.user());
}
break;
case 'readOnly':
if (value !== room.ro) {
RocketChat.saveRoomReadOnly(rid, value, Meteor.user());
}
break;
case 'reactWhenReadOnly':
if (value !== room.reactWhenReadOnly) {
RocketChat.saveReactWhenReadOnly(rid, value, Meteor.user());
}
break;
case 'systemMessages':
if (value !== room.sysMes) {
RocketChat.saveRoomSystemMessages(rid, value, Meteor.user());
}
break;
case 'joinCode':
RocketChat.models.Rooms.setJoinCodeById(rid, String(value));
break;
case 'default':
RocketChat.models.Rooms.saveDefaultById(rid, value);
}
}
return {
result: true,
rid: room._id
};
}
});

@ -1,5 +0,0 @@
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser = (type, roomId, message, user, extraData) ->
return @createWithTypeRoomIdMessageAndUser type, roomId, message, user, extraData
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser = (roomId, roomName, user, extraData) ->
return @createWithTypeRoomIdMessageAndUser 'r', roomId, roomName, user, extraData

@ -0,0 +1,7 @@
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser = function(type, roomId, message, user, extraData) {
return this.createWithTypeRoomIdMessageAndUser(type, roomId, message, user, extraData);
};
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser = function(roomId, roomName, user, extraData) {
return this.createWithTypeRoomIdMessageAndUser('r', roomId, roomName, user, extraData);
};

@ -1,55 +0,0 @@
RocketChat.models.Rooms.setDescriptionById = (_id, description) ->
query =
_id: _id
update =
$set:
description: description
return @update query, update
RocketChat.models.Rooms.setReadOnlyById = (_id, readOnly) ->
query =
_id: _id
update =
$set:
ro: readOnly
if readOnly
# we want to mute all users without the post-readonly permission
RocketChat.models.Subscriptions.findByRoomId(_id).forEach (subscription) ->
if not subscription._user?
return
user = subscription._user
if RocketChat.authz.hasPermission(user._id, 'post-readonly') is false
# create a new array if necessary
update.$set.muted = [] if !update.$set.muted
update.$set.muted.push user.username
else
# remove the muted user array
update.$unset = {muted: ""}
return @update query, update
RocketChat.models.Rooms.setAllowReactingWhenReadOnlyById = (_id, allowReacting) ->
query =
_id: _id
update =
$set:
reactWhenReadOnly: allowReacting
return @update query, update
RocketChat.models.Rooms.setSystemMessagesById = (_id, systemMessages) ->
query =
_id: _id
update =
$set:
sysMes: systemMessages
return @update query, update

@ -0,0 +1,65 @@
RocketChat.models.Rooms.setDescriptionById = function(_id, description) {
const query = {
_id
};
const update = {
$set: {
description
}
};
return this.update(query, update);
};
RocketChat.models.Rooms.setReadOnlyById = function(_id, readOnly) {
const query = {
_id
};
const update = {
$set: {
ro: readOnly
}
};
if (readOnly) {
RocketChat.models.Subscriptions.findByRoomId(_id).forEach(function(subscription) {
if (subscription._user == null) {
return;
}
const user = subscription._user;
if (RocketChat.authz.hasPermission(user._id, 'post-readonly') === false) {
if (!update.$set.muted) {
update.$set.muted = [];
}
return update.$set.muted.push(user.username);
}
});
} else {
update.$unset = {
muted: ''
};
}
return this.update(query, update);
};
RocketChat.models.Rooms.setAllowReactingWhenReadOnlyById = function(_id, allowReacting) {
const query = {
_id
};
const update = {
$set: {
reactWhenReadOnly: allowReacting
}
};
return this.update(query, update);
};
RocketChat.models.Rooms.setSystemMessagesById = function(_id, systemMessages) {
const query = {
_id
};
const update = {
$set: {
sysMes: systemMessages
}
};
return this.update(query, update);
};
Loading…
Cancel
Save