Skip to content

Commit

Permalink
Allow spaces on room names
Browse files Browse the repository at this point in the history
Closes RocketChat#892
Closes RocketChat#7488

Fire global event for Load More during scroll
  • Loading branch information
Daniel Schreiber committed Jul 30, 2017
1 parent 485b623 commit fb50b3e
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ Meteor.startup(function() {
Tracker.nonreactive(() => {
if (msg.t === 'r') {
if (Session.get('openedRoom') === msg.rid) {
const type = FlowRouter.current().route.name === 'channel' ? 'c' : 'p';
RoomManager.close(type + FlowRouter.getParam('name'));
FlowRouter.go(FlowRouter.current().route.name, { name: msg.msg }, FlowRouter.current().queryParams);
const room = ChatRoom.findOne(msg.rid);
if (room.name !== FlowRouter.getParam('name')) {
RoomManager.close(room.t + FlowRouter.getParam('name'));
RocketChat.roomTypes.openRouteLink(room.t, room, FlowRouter.current().queryParams);
}
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Template.channelSettings.helpers({
}
return true;
}
if (this.$value.getValue) {
return this.$value.getValue(obj, key);
}
return obj && obj[key];
},
showSetting(setting, room) {
Expand Down Expand Up @@ -152,22 +155,30 @@ Template.channelSettings.onCreated(function() {
canEdit(room) {
return RocketChat.authz.hasAllPermission('edit-room', room._id);
},
getValue(room) {
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')) {
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
}
}));
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: {
name: value
}
}));
}
}
Meteor.call('saveRoomSettings', room._id, 'roomName', value, function(err) {
if (err) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@

RocketChat.saveRoomName = function(rid, name, user, sendMessage = true) {
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.saveRoomName'
'function': 'RocketChat.saveRoomdisplayName'
});
}
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) {
if (displayName === 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);

const slugifiedRoomName = RocketChat.getValidRoomName(displayName, rid);

const update = RocketChat.models.Rooms.setNameById(rid, slugifiedRoomName, displayName) && RocketChat.models.Subscriptions.updateNameAndAlertByRoomId(rid, slugifiedRoomName, displayName);

if (update && sendMessage) {
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser(rid, name, user);
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser(rid, displayName, user);
}
return name;
return displayName;
};
5 changes: 3 additions & 2 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@
"error-invalid-redirectUri": "Invalid redirectUri",
"error-invalid-role": "Invalid role",
"error-invalid-room": "Invalid room",
"error-invalid-room-name": "<strong>__room_name__</strong> is not a valid room name,<br/> use only letters, numbers, hyphens and underscores",
"error-invalid-room-name": "<strong>__room_name__</strong> is not a valid room name",
"error-invalid-room-type": "<strong>__type__</strong> is not a valid room type.",
"error-invalid-settings": "Invalid settings provided",
"error-invalid-subscription": "Invalid subscription",
Expand Down Expand Up @@ -766,7 +766,7 @@
"Invalid_name": "The name must not be empty",
"Invalid_notification_setting_s": "Invalid notification setting: %s",
"Invalid_pass": "The password must not be empty",
"Invalid_room_name": "<strong>%s</strong> is not a valid room name,<br/> use only letters, numbers, hyphens and underscores",
"Invalid_room_name": "<strong>%s</strong> is not a valid room name",
"Invalid_secret_URL_message": "The URL provided is invalid.",
"Invalid_setting_s": "Invalid setting: %s",
"Invalid_two_factor_code": "Invalid two factor code",
Expand Down Expand Up @@ -1557,6 +1557,7 @@
"Type_your_message": "Type your message",
"Type_your_name": "Type your name",
"Type_your_new_password": "Type your new password",
"UI_Allow_room_names_with_special_chars": "Allow special chars on room names",
"UI_DisplayRoles": "Display Roles",
"UI_Merge_Channels_Groups": "Merge private groups with channels",
"UI_Use_Name_Avatar": "Use full name initials to generate default avatar",
Expand Down
46 changes: 46 additions & 0 deletions packages/rocketchat-lib/lib/getValidRoomName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
RocketChat.getValidRoomName = function getValidRoomName(displayName, rid = '') {
let slugifiedName = displayName;

if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) {
const room = RocketChat.models.Rooms.findOneByDisplayName(displayName);
if (room && room._id !== rid) {
if (room.archived) {
throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ displayName }`, { function: 'RocketChat.getValidRoomName', channel_name: displayName });
} else {
throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ displayName }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: displayName });
}
}
slugifiedName = s.slugify(displayName);
}

let nameValidation;
try {
nameValidation = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`);
} catch (error) {
nameValidation = new RegExp('^[0-9a-zA-Z-_.]+$');
}
if (!nameValidation.test(slugifiedName)) {
throw new Meteor.Error('error-invalid-room-name', `${ slugifiedName } is not a valid room name.`, {
'function': 'RocketChat.getValidRoomName',
channel_name: slugifiedName
});
}

const room = RocketChat.models.Rooms.findOneByName(slugifiedName);
if (room && room._id !== rid) {
if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) {
let tmpName = slugifiedName;
let next = 0;
while (RocketChat.models.Rooms.findOneByNameAndNotId(tmpName, rid)) {
tmpName = `${ slugifiedName }-${ ++next }`;
}
slugifiedName = tmpName;
} else if (room.archived) {
throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ slugifiedName }`, { function: 'RocketChat.getValidRoomName', channel_name: slugifiedName });
} else {
throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ slugifiedName }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: slugifiedName });
}
}

return slugifiedName;
};
1 change: 1 addition & 0 deletions packages/rocketchat-lib/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Package.onUse(function(api) {
api.addFiles('lib/settings.js');
api.addFiles('lib/callbacks.js');
api.addFiles('lib/fileUploadRestrictions.js');
api.addFiles('lib/getValidRoomName.js');
api.addFiles('lib/placeholders.js');
api.addFiles('lib/promises.js');
api.addFiles('lib/roomTypesCommon.js');
Expand Down
29 changes: 6 additions & 23 deletions packages/rocketchat-lib/server/functions/createRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,18 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData
throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'RocketChat.createRoom' });
}

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-name', 'Invalid name', { function: 'RocketChat.createRoom' });
}
const slugifiedRoomName = RocketChat.getValidRoomName(name);

const now = new Date();
if (!_.contains(members, owner.username)) {
members.push(owner.username);
}

// avoid duplicate names
let room = RocketChat.models.Rooms.findOneByName(name);
if (room) {
if (room.archived) {
throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ name }`, { function: 'RocketChat.createRoom', room_name: name });
} else {
throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ name }' exists`, { function: 'RocketChat.createRoom', room_name: name });
}
}

if (type === 'c') {
RocketChat.callbacks.run('beforeCreateChannel', owner, {
t: 'c',
name,
name: slugifiedRoomName,
fname: name,
ts: now,
ro: readOnly === true,
sysMes: readOnly !== true,
Expand All @@ -60,7 +42,7 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData
sysMes: readOnly !== true
});

room = RocketChat.models.Rooms.createWithTypeNameUserAndUsernames(type, name, owner, members, extraData);
const room = RocketChat.models.Rooms.createWithTypeNameUserAndUsernames(type, slugifiedRoomName, name, owner, members, extraData);

for (const username of members) {
const member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1 }});
Expand Down Expand Up @@ -95,6 +77,7 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData
}

return {
rid: room._id
rid: room._id,
name: slugifiedRoomName
};
};
23 changes: 20 additions & 3 deletions packages/rocketchat-lib/server/models/Rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ class ModelRooms extends RocketChat.models._Base {
return this.findOne(query, options);
}

findOneByNameAndNotId(name, rid) {
const query = {
_id: { $ne: rid },
name
};

return this.findOne(query);
}

findOneByDisplayName(fname, options) {
const query = {fname};

return this.findOne(query, options);
}

findOneByNameAndType(name, type, options) {
const query = {
name,
Expand Down Expand Up @@ -490,12 +505,13 @@ class ModelRooms extends RocketChat.models._Base {
return this.update(query, update);
}

setNameById(_id, name) {
setNameById(_id, name, fname) {
const query = {_id};

const update = {
$set: {
name
name,
fname
}
};

Expand Down Expand Up @@ -719,9 +735,10 @@ class ModelRooms extends RocketChat.models._Base {
}

// INSERT
createWithTypeNameUserAndUsernames(type, name, user, usernames, extraData) {
createWithTypeNameUserAndUsernames(type, name, fname, user, usernames, extraData) {
const room = {
name,
fname,
t: type,
usernames,
msgs: 0,
Expand Down
4 changes: 3 additions & 1 deletion packages/rocketchat-lib/server/models/Subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,14 @@ class ModelSubscriptions extends RocketChat.models._Base {
return this.update(query, update);
}

updateNameAndAlertByRoomId(roomId, name) {
updateNameAndAlertByRoomId(roomId, name, fname) {
const query =
{rid: roomId};

const update = {
$set: {
name,
fname,
alert: true
}
};
Expand Down Expand Up @@ -540,6 +541,7 @@ class ModelSubscriptions extends RocketChat.models._Base {
ts: room.ts,
rid: room._id,
name: room.name,
fname: room.fname,
t: room.t,
u: {
_id: user._id,
Expand Down
4 changes: 4 additions & 0 deletions packages/rocketchat-lib/server/startup/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,10 @@ RocketChat.settings.addGroup('Layout', function() {
type: 'boolean',
'public': true
});
this.add('UI_Allow_room_names_with_special_chars', false, {
type: 'boolean',
public: true
});
});
});

Expand Down
6 changes: 6 additions & 0 deletions packages/rocketchat-lib/startup/defaultRoomTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ RocketChat.roomTypes.add('c', 10, {
},

roomName(roomData) {
if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) {
return roomData.fname || roomData.name;
}
return roomData.name;
},

Expand Down Expand Up @@ -114,6 +117,9 @@ RocketChat.roomTypes.add('p', 20, {
},

roomName(roomData) {
if (RocketChat.settings.get('UI_Allow_room_names_with_special_chars')) {
return roomData.fname || roomData.name;
}
return roomData.name;
},

Expand Down
4 changes: 3 additions & 1 deletion packages/rocketchat-ui-sidenav/client/chatRoomItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Template.chatRoomItem.helpers({
},

name() {
if (RocketChat.settings.get('UI_Use_Real_Name') && this.fname) {
const realNameForDirectMessages = RocketChat.settings.get('UI_Use_Real_Name') && this.t === 'd';
const realNameForChannel = RocketChat.settings.get('UI_Allow_room_names_with_special_chars') && this.t !== 'd';
if ((realNameForDirectMessages || realNameForChannel) && this.fname) {
return this.fname;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/rocketchat-ui-sidenav/client/createCombinedFlex.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Template.createCombinedFlex.events({
if (!err) {
return Meteor.call(createRoute, name, instance.selectedUsers.get(), readOnly, function(err, result) {
if (err) {
if (err.error === 'error-invalid-name') {
if (err.error === 'error-invalid-room-name') {
instance.error.set({ invalid: true });
return;
}
Expand All @@ -130,10 +130,10 @@ Template.createCombinedFlex.events({
SideNav.closeFlex(() => instance.clearForm());

if (!privateGroup) {
RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name });
RocketChat.callbacks.run('aftercreateCombined', { _id: result.rid, name: result.name });
}

return FlowRouter.go(successRoute, { name }, FlowRouter.current().queryParams);
return FlowRouter.go(successRoute, { name: result.name }, FlowRouter.current().queryParams);
});
} else {
return instance.error.set({ fields: err });
Expand Down
Loading

0 comments on commit fb50b3e

Please sign in to comment.