diff --git a/.eslintrc.json b/.eslintrc.json
index 4397b6a8e..1723f892b 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -21,8 +21,9 @@
"rules": {
"lodash/prefer-lodash-method": [2, {
"ignoreMethods": [
- "keys", "find", "endsWith", "startsWith", "filter", "reduce", "isArray", "create",
- "map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase", "includes"
+ "every", "keys", "find", "endsWith", "startsWith", "filter", "reduce", "isArray",
+ "create", "map", "replace", "some", "toLower", "split", "trim", "forEach",
+ "toUpperCase", "includes"
]
}],
"lodash/prefer-invoke-map": "off",
diff --git a/CHANGES.md b/CHANGES.md
index 66ed74be5..2b947fe16 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@
- Take roster nickname into consideration when rendering messages and chat headings.
- Hide the textarea when a user is muted in a groupchat.
- Don't restore a BOSH session without knowing the JID
+- In the `/help` menu, only show allowed commands
- #1296: `embedded` view mode shows `chatbox-navback` arrow in header
- #1532: Converse reloads on enter pressed in the filter box
diff --git a/dist/converse.js b/dist/converse.js
index 8aed07e89..b67d0be4d 100644
--- a/dist/converse.js
+++ b/dist/converse.js
@@ -53468,6 +53468,10 @@ const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
$pres = _converse$env.$pres;
const u = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].env.utils;
const AFFILIATION_CHANGE_COMANDS = ['admin', 'ban', 'owner', 'member', 'revoke'];
+const OWNER_COMMANDS = ['owner'];
+const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
+const MODERATOR_COMMANDS = ['kick', 'mute', 'voice'];
+const VISITOR_COMMANDS = ['nick'];
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins.add('converse-muc-views', {
/* Dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
@@ -54385,26 +54389,40 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
return _converse.api.sendIQ(iq).then(onSuccess).catch(onError);
},
- verifyRoles(roles) {
- const me = this.model.occupants.findWhere({
- 'jid': _converse.bare_jid
- });
+ verifyRoles(roles, occupant) {
+ let show_error = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
+
+ if (!occupant) {
+ occupant = this.model.occupants.findWhere({
+ 'jid': _converse.bare_jid
+ });
+ }
+
+ if (!_.includes(roles, occupant.get('role'))) {
+ if (show_error) {
+ this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'));
+ }
- if (!_.includes(roles, me.get('role'))) {
- this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'));
return false;
}
return true;
},
- verifyAffiliations(affiliations) {
- const me = this.model.occupants.findWhere({
- 'jid': _converse.bare_jid
- });
+ verifyAffiliations(affiliations, occupant) {
+ let show_error = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
+
+ if (!occupant) {
+ occupant = this.model.occupants.findWhere({
+ 'jid': _converse.bare_jid
+ });
+ }
+
+ if (!_.includes(affiliations, occupant.get('affiliation'))) {
+ if (show_error) {
+ this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'));
+ }
- if (!_.includes(affiliations, me.get('affiliation'))) {
- this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'));
return false;
}
@@ -54454,63 +54472,105 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
switch (command) {
case 'admin':
- if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.model.setAffiliation('admin', [{
+ 'jid': args[0],
+ 'reason': args[1]
+ }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
break;
}
- this.model.setAffiliation('admin', [{
- 'jid': args[0],
- 'reason': args[1]
- }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
- break;
-
case 'ban':
- if (!this.verifyAffiliations(['owner', 'admin']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.model.setAffiliation('outcast', [{
+ 'jid': args[0],
+ 'reason': args[1]
+ }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
break;
}
- this.model.setAffiliation('outcast', [{
- 'jid': args[0],
- 'reason': args[1]
- }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
- break;
-
case 'deop':
- if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ // FIXME: /deop only applies to setting a moderators
+ // role to "participant" (which only admin/owner can
+ // do). Moderators can however set non-moderator's role
+ // to participant (e.g. visitor => participant).
+ // Currently we don't distinguish between these two
+ // cases.
+ if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.modifyRole(this.model.get('jid'), args[0], 'participant', args[1], undefined, this.onCommandError.bind(this));
break;
}
- this.modifyRole(this.model.get('jid'), args[0], 'participant', args[1], undefined, this.onCommandError.bind(this));
- break;
-
case 'destroy':
- if (!this.verifyAffiliations(['owner'])) {
+ {
+ if (!this.verifyAffiliations(['owner'])) {
+ break;
+ }
+
+ this.destroy(this.model.get('jid'), args[0]).then(() => this.close()).catch(e => this.onCommandError(e));
break;
}
- this.destroy(this.model.get('jid'), args[0]).then(() => this.close()).catch(e => this.onCommandError(e));
- break;
-
case 'help':
- this.showHelpMessages(_.filter([`/admin: ${__("Change user's affiliation to admin")}`, `/ban: ${__('Ban user from groupchat')}`, `/clear: ${__('Remove messages')}`, `/deop: ${__('Change user role to participant')}`, `/destroy: ${__('Remove this groupchat')}`, `/help: ${__('Show this menu')}`, `/kick: ${__('Kick user from groupchat')}`, `/me: ${__('Write in 3rd person')}`, `/member: ${__('Grant membership to a user')}`, `/mute: ${__("Remove user's ability to post messages")}`, `/nick: ${__('Change your nickname')}`, `/op: ${__('Grant moderator role to user')}`, `/owner: ${__('Grant ownership of this groupchat')}`, `/register: ${__("Register a nickname for this groupchat")}`, `/revoke: ${__("Revoke user's membership")}`, `/subject: ${__('Set groupchat subject')}`, `/topic: ${__('Set groupchat subject (alias for /subject)')}`, `/voice: ${__('Allow muted user to post messages')}`], line => _.every(disabled_commands, element => !line.startsWith(element + '<', 9))));
- break;
+ {
+ // FIXME: The availability of some of these commands
+ // depend on the MUCs configuration (e.g. whether it's
+ // moderated or not). We need to take that into
+ // consideration.
+ let allowed_commands = ['clear', 'help', 'me', 'nick', 'subject', 'topic', 'register'];
+ const occupant = this.model.occupants.findWhere({
+ 'jid': _converse.bare_jid
+ });
+
+ if (this.verifyAffiliations('owner', occupant, false)) {
+ allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS);
+ } else if (this.verifyAffiliations('admin', occupant, false)) {
+ allowed_commands = allowed_commands.concat(ADMIN_COMMANDS);
+ }
+
+ if (this.verifyRoles('moderator', occupant, false)) {
+ allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS);
+ } else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) {
+ allowed_commands = allowed_commands.concat(VISITOR_COMMANDS);
+ }
+
+ this.showHelpMessages([`${__("You can run the following commands")}`]);
+ this.showHelpMessages([`/admin: ${__("Change user's affiliation to admin")}`, `/ban: ${__('Ban user from groupchat')}`, `/clear: ${__('Clear the chat area')}`, `/deop: ${__('Change user role to participant')}`, `/destroy: ${__('Remove this groupchat')}`, `/help: ${__('Show this menu')}`, `/kick: ${__('Kick user from groupchat')}`, `/me: ${__('Write in 3rd person')}`, `/member: ${__('Grant membership to a user')}`, `/mute: ${__("Remove user's ability to post messages")}`, `/nick: ${__('Change your nickname')}`, `/op: ${__('Grant moderator role to user')}`, `/owner: ${__('Grant ownership of this groupchat')}`, `/register: ${__("Register your nickname")}`, `/revoke: ${__("Revoke user's membership")}`, `/subject: ${__('Set groupchat subject')}`, `/topic: ${__('Set groupchat subject (alias for /subject)')}`, `/voice: ${__('Allow muted user to post messages')}`].filter(line => disabled_commands.every(c => !line.startsWith(c + '<', 9))).filter(line => allowed_commands.some(c => line.startsWith(c + '<', 9))));
+ break;
+ }
case 'kick':
- if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.modifyRole(this.model.get('jid'), args[0], 'none', args[1], undefined, this.onCommandError.bind(this));
break;
}
- this.modifyRole(this.model.get('jid'), args[0], 'none', args[1], undefined, this.onCommandError.bind(this));
- break;
-
case 'mute':
- if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.modifyRole(this.model.get('jid'), args[0], 'visitor', args[1], undefined, this.onCommandError.bind(this));
break;
}
- this.modifyRole(this.model.get('jid'), args[0], 'visitor', args[1], undefined, this.onCommandError.bind(this));
- break;
-
case 'member':
{
if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
@@ -54536,18 +54596,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
}
case 'nick':
- if (!this.verifyRoles(['visitor', 'participant', 'moderator'])) {
+ {
+ if (!this.verifyRoles(['visitor', 'participant', 'moderator'])) {
+ break;
+ }
+
+ _converse.api.send($pres({
+ from: _converse.connection.jid,
+ to: this.model.getRoomJIDAndNick(match[2]),
+ id: _converse.connection.getUniqueId()
+ }).tree());
+
break;
}
- _converse.api.send($pres({
- from: _converse.connection.jid,
- to: this.model.getRoomJIDAndNick(match[2]),
- id: _converse.connection.getUniqueId()
- }).tree());
-
- break;
-
case 'owner':
if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
@@ -54560,34 +54622,40 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
break;
case 'op':
- if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.modifyRole(this.model.get('jid'), args[0], 'moderator', args[1], undefined, this.onCommandError.bind(this));
break;
}
- this.modifyRole(this.model.get('jid'), args[0], 'moderator', args[1], undefined, this.onCommandError.bind(this));
- break;
-
case 'register':
- if (args.length > 1) {
- this.showErrorMessage(__('Error: invalid number of arguments'));
- } else {
- this.model.registerNickname().then(err_msg => {
- if (err_msg) this.showErrorMessage(err_msg);
- });
- }
+ {
+ if (args.length > 1) {
+ this.showErrorMessage(__('Error: invalid number of arguments'));
+ } else {
+ this.model.registerNickname().then(err_msg => {
+ if (err_msg) this.showErrorMessage(err_msg);
+ });
+ }
- break;
-
- case 'revoke':
- if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
- this.model.setAffiliation('none', [{
- 'jid': args[0],
- 'reason': args[1]
- }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
- break;
+ case 'revoke':
+ {
+ if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.model.setAffiliation('none', [{
+ 'jid': args[0],
+ 'reason': args[1]
+ }]).then(() => this.model.occupants.fetchMembers(), err => this.onCommandError(err));
+ break;
+ }
case 'topic':
case 'subject':
@@ -54603,13 +54671,15 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
break;
case 'voice':
- if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ {
+ if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
+ break;
+ }
+
+ this.modifyRole(this.model.get('jid'), args[0], 'participant', args[1], undefined, this.onCommandError.bind(this));
break;
}
- this.modifyRole(this.model.get('jid'), args[0], 'participant', args[1], undefined, this.onCommandError.bind(this));
- break;
-
default:
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
}
diff --git a/spec/muc.js b/spec/muc.js
index d54c47b42..f724c3e0b 100644
--- a/spec/muc.js
+++ b/spec/muc.js
@@ -1781,9 +1781,7 @@
async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); // We need roster contacts, who can invite us
- spyOn(window, 'confirm').and.callFake(function () {
- return true;
- });
+ spyOn(window, 'confirm').and.callFake(() => true);
await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
const view = _converse.chatboxviews.get('lounge@localhost');
view.close(); // Hack, otherwise we have to mock stanzas.
@@ -2550,23 +2548,24 @@
null, ['rosterGroupsFetched'], {},
async function (done, _converse) {
+ spyOn(window, 'confirm').and.callFake(() => true);
await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
const view = _converse.chatboxviews.get('lounge@localhost');
- var textarea = view.el.querySelector('.chat-textarea');
- textarea.value = '/help This is the groupchat subject';
- view.keyPressed({
- target: textarea,
- preventDefault: _.noop,
- keyCode: 13
- });
+ const textarea = view.el.querySelector('.chat-textarea');
+ textarea.value = '/clear';
- const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
+ const enter = { 'target': textarea, 'preventDefault': _.noop, 'keyCode': 13 };
+ view.keyPressed(enter);
+ textarea.value = '/help';
+ view.keyPressed(enter);
+
+ let info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_messages.length).toBe(19);
expect(info_messages.pop().textContent).toBe('/voice: Allow muted user to post messages');
expect(info_messages.pop().textContent).toBe('/topic: Set groupchat subject (alias for /subject)');
expect(info_messages.pop().textContent).toBe('/subject: Set groupchat subject');
expect(info_messages.pop().textContent).toBe('/revoke: Revoke user\'s membership');
- expect(info_messages.pop().textContent).toBe('/register: Register a nickname for this groupchat');
+ expect(info_messages.pop().textContent).toBe('/register: Register your nickname');
expect(info_messages.pop().textContent).toBe('/owner: Grant ownership of this groupchat');
expect(info_messages.pop().textContent).toBe('/op: Grant moderator role to user');
expect(info_messages.pop().textContent).toBe('/nick: Change your nickname');
@@ -2577,9 +2576,44 @@
expect(info_messages.pop().textContent).toBe('/help: Show this menu');
expect(info_messages.pop().textContent).toBe('/destroy: Remove this groupchat');
expect(info_messages.pop().textContent).toBe('/deop: Change user role to participant');
- expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
+ expect(info_messages.pop().textContent).toBe('/clear: Clear the chat area');
expect(info_messages.pop().textContent).toBe('/ban: Ban user from groupchat');
expect(info_messages.pop().textContent).toBe('/admin: Change user\'s affiliation to admin');
+ expect(info_messages.pop().textContent).toBe('You can run the following commands');
+
+ const occupant = view.model.occupants.findWhere({'jid': _converse.bare_jid});
+ occupant.set('affiliation', 'admin');
+ textarea.value = '/clear';
+ view.keyPressed(enter);
+ textarea.value = '/help';
+ view.keyPressed(enter);
+ info_messages = sizzle('.chat-info', view.el).slice(1);
+ expect(info_messages.length).toBe(17);
+ let commands = info_messages.map(m => m.textContent.replace(/:.*$/, ''));
+ expect(commands).toEqual([
+ "/admin", "/ban", "/clear", "/deop", "/destroy",
+ "/help", "/kick", "/me", "/member", "/mute", "/nick",
+ "/op", "/register", "/revoke", "/subject", "/topic", "/voice"
+ ]);
+ occupant.set('affiliation', 'member');
+ textarea.value = '/clear';
+ view.keyPressed(enter);
+ textarea.value = '/help';
+ view.keyPressed(enter);
+ info_messages = sizzle('.chat-info', view.el).slice(1);
+ expect(info_messages.length).toBe(10);
+ commands = info_messages.map(m => m.textContent.replace(/:.*$/, ''));
+ expect(commands).toEqual(["/clear", "/help", "/kick", "/me", "/mute", "/nick", "/register", "/subject", "/topic", "/voice"]);
+
+ occupant.set('role', 'participant');
+ textarea.value = '/clear';
+ view.keyPressed(enter);
+ textarea.value = '/help';
+ view.keyPressed(enter);
+ info_messages = sizzle('.chat-info', view.el).slice(1);
+ expect(info_messages.length).toBe(7);
+ commands = info_messages.map(m => m.textContent.replace(/:.*$/, ''));
+ expect(commands).toEqual(["/clear", "/help", "/me", "/nick", "/register", "/subject", "/topic"]);
done();
}));
@@ -2599,11 +2633,11 @@
});
const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
- expect(info_messages.length).toBe(17);
+ expect(info_messages.length).toBe(18);
expect(info_messages.pop().textContent).toBe('/topic: Set groupchat subject (alias for /subject)');
expect(info_messages.pop().textContent).toBe('/subject: Set groupchat subject');
expect(info_messages.pop().textContent).toBe('/revoke: Revoke user\'s membership');
- expect(info_messages.pop().textContent).toBe('/register: Register a nickname for this groupchat');
+ expect(info_messages.pop().textContent).toBe('/register: Register your nickname');
expect(info_messages.pop().textContent).toBe('/owner: Grant ownership of this groupchat');
expect(info_messages.pop().textContent).toBe('/op: Grant moderator role to user');
expect(info_messages.pop().textContent).toBe('/nick: Change your nickname');
@@ -2613,9 +2647,10 @@
expect(info_messages.pop().textContent).toBe('/help: Show this menu');
expect(info_messages.pop().textContent).toBe('/destroy: Remove this groupchat');
expect(info_messages.pop().textContent).toBe('/deop: Change user role to participant');
- expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
+ expect(info_messages.pop().textContent).toBe('/clear: Clear the chat area');
expect(info_messages.pop().textContent).toBe('/ban: Ban user from groupchat');
expect(info_messages.pop().textContent).toBe('/admin: Change user\'s affiliation to admin');
+ expect(info_messages.pop().textContent).toBe('You can run the following commands');
done();
}));
diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js
index a14716d51..84bb5a006 100644
--- a/src/converse-muc-views.js
+++ b/src/converse-muc-views.js
@@ -41,6 +41,10 @@ import xss from "xss";
const { Backbone, Promise, Strophe, moment, f, sizzle, _, $build, $iq, $msg, $pres } = converse.env;
const u = converse.env.utils;
const AFFILIATION_CHANGE_COMANDS = ['admin', 'ban', 'owner', 'member', 'revoke'];
+const OWNER_COMMANDS = ['owner'];
+const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
+const MODERATOR_COMMANDS = ['kick', 'mute', 'voice'];
+const VISITOR_COMMANDS = ['nick'];
converse.plugins.add('converse-muc-views', {
/* Dependencies are other plugins which might be
@@ -882,19 +886,27 @@ converse.plugins.add('converse-muc-views', {
return _converse.api.sendIQ(iq).then(onSuccess).catch(onError);
},
- verifyRoles (roles) {
- const me = this.model.occupants.findWhere({'jid': _converse.bare_jid});
- if (!_.includes(roles, me.get('role'))) {
- this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'))
+ verifyRoles (roles, occupant, show_error=true) {
+ if (!occupant) {
+ occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
+ }
+ if (!_.includes(roles, occupant.get('role'))) {
+ if (show_error) {
+ this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'))
+ }
return false;
}
return true;
},
- verifyAffiliations (affiliations) {
- const me = this.model.occupants.findWhere({'jid': _converse.bare_jid});
- if (!_.includes(affiliations, me.get('affiliation'))) {
- this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'))
+ verifyAffiliations (affiliations, occupant, show_error=true) {
+ if (!occupant) {
+ occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
+ }
+ if (!_.includes(affiliations, occupant.get('affiliation'))) {
+ if (show_error) {
+ this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'))
+ }
return false;
}
return true;
@@ -938,7 +950,7 @@ converse.plugins.add('converse-muc-views', {
return false;
}
switch (command) {
- case 'admin':
+ case 'admin': {
if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -950,8 +962,9 @@ converse.plugins.add('converse-muc-views', {
(err) => this.onCommandError(err)
);
break;
- case 'ban':
- if (!this.verifyAffiliations(['owner', 'admin']) || !this.validateRoleChangeCommand(command, args)) {
+ }
+ case 'ban': {
+ if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
this.model.setAffiliation('outcast', [{
@@ -962,15 +975,23 @@ converse.plugins.add('converse-muc-views', {
(err) => this.onCommandError(err)
);
break;
- case 'deop':
+ }
+ case 'deop': {
+ // FIXME: /deop only applies to setting a moderators
+ // role to "participant" (which only admin/owner can
+ // do). Moderators can however set non-moderator's role
+ // to participant (e.g. visitor => participant).
+ // Currently we don't distinguish between these two
+ // cases.
if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
this.modifyRole(
- this.model.get('jid'), args[0], 'participant', args[1],
- undefined, this.onCommandError.bind(this));
+ this.model.get('jid'), args[0], 'participant', args[1],
+ undefined, this.onCommandError.bind(this));
break;
- case 'destroy':
+ }
+ case 'destroy': {
if (!this.verifyAffiliations(['owner'])) {
break;
}
@@ -978,11 +999,29 @@ converse.plugins.add('converse-muc-views', {
.then(() => this.close())
.catch(e => this.onCommandError(e));
break;
- case 'help':
- this.showHelpMessages(_.filter([
+ }
+ case 'help': {
+ // FIXME: The availability of some of these commands
+ // depend on the MUCs configuration (e.g. whether it's
+ // moderated or not). We need to take that into
+ // consideration.
+ let allowed_commands = ['clear', 'help', 'me', 'nick', 'subject', 'topic', 'register'];
+ const occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
+ if (this.verifyAffiliations('owner', occupant, false)) {
+ allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS);
+ } else if (this.verifyAffiliations('admin', occupant, false)) {
+ allowed_commands = allowed_commands.concat(ADMIN_COMMANDS);
+ }
+ if (this.verifyRoles('moderator', occupant, false)) {
+ allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS);
+ } else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) {
+ allowed_commands = allowed_commands.concat(VISITOR_COMMANDS);
+ }
+ this.showHelpMessages([`${__("You can run the following commands")}`]);
+ this.showHelpMessages([
`/admin: ${__("Change user's affiliation to admin")}`,
`/ban: ${__('Ban user from groupchat')}`,
- `/clear: ${__('Remove messages')}`,
+ `/clear: ${__('Clear the chat area')}`,
`/deop: ${__('Change user role to participant')}`,
`/destroy: ${__('Remove this groupchat')}`,
`/help: ${__('Show this menu')}`,
@@ -993,15 +1032,16 @@ converse.plugins.add('converse-muc-views', {
`/nick: ${__('Change your nickname')}`,
`/op: ${__('Grant moderator role to user')}`,
`/owner: ${__('Grant ownership of this groupchat')}`,
- `/register: ${__("Register a nickname for this groupchat")}`,
+ `/register: ${__("Register your nickname")}`,
`/revoke: ${__("Revoke user's membership")}`,
`/subject: ${__('Set groupchat subject')}`,
`/topic: ${__('Set groupchat subject (alias for /subject)')}`,
`/voice: ${__('Allow muted user to post messages')}`
- ], line => (_.every(disabled_commands, element => (!line.startsWith(element+'<', 9))))
- ));
+ ].filter(line => disabled_commands.every(c => (!line.startsWith(c+'<', 9))))
+ .filter(line => allowed_commands.some(c => line.startsWith(c+'<', 9)))
+ );
break;
- case 'kick':
+ } case 'kick': {
if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -1009,7 +1049,8 @@ converse.plugins.add('converse-muc-views', {
this.model.get('jid'), args[0], 'none', args[1],
undefined, this.onCommandError.bind(this));
break;
- case 'mute':
+ }
+ case 'mute': {
if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -1017,6 +1058,7 @@ converse.plugins.add('converse-muc-views', {
this.model.get('jid'), args[0], 'visitor', args[1],
undefined, this.onCommandError.bind(this));
break;
+ }
case 'member': {
if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
@@ -1034,7 +1076,8 @@ converse.plugins.add('converse-muc-views', {
.then(() => this.model.occupants.fetchMembers())
.catch(err => this.onCommandError(err));
break;
- } case 'nick':
+ }
+ case 'nick': {
if (!this.verifyRoles(['visitor', 'participant', 'moderator'])) {
break;
}
@@ -1044,6 +1087,7 @@ converse.plugins.add('converse-muc-views', {
id: _converse.connection.getUniqueId()
}).tree());
break;
+ }
case 'owner':
if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
@@ -1056,7 +1100,7 @@ converse.plugins.add('converse-muc-views', {
(err) => this.onCommandError(err)
);
break;
- case 'op':
+ case 'op': {
if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -1064,7 +1108,8 @@ converse.plugins.add('converse-muc-views', {
this.model.get('jid'), args[0], 'moderator', args[1],
undefined, this.onCommandError.bind(this));
break;
- case 'register':
+ }
+ case 'register': {
if (args.length > 1) {
this.showErrorMessage(__('Error: invalid number of arguments'))
} else {
@@ -1073,7 +1118,8 @@ converse.plugins.add('converse-muc-views', {
});
}
break;
- case 'revoke':
+ }
+ case 'revoke': {
if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -1085,6 +1131,7 @@ converse.plugins.add('converse-muc-views', {
(err) => this.onCommandError(err)
);
break;
+ }
case 'topic':
case 'subject':
// TODO: should be done via API call to _converse.api.rooms
@@ -1096,7 +1143,7 @@ converse.plugins.add('converse-muc-views', {
}).c("subject", {xmlns: "jabber:client"}).t(match[2] || "").tree()
);
break;
- case 'voice':
+ case 'voice': {
if (!this.verifyRoles(['moderator']) || !this.validateRoleChangeCommand(command, args)) {
break;
}
@@ -1104,6 +1151,7 @@ converse.plugins.add('converse-muc-views', {
this.model.get('jid'), args[0], 'participant', args[1],
undefined, this.onCommandError.bind(this));
break;
+ }
default:
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
}