Save room features in separate model

As a namespacing mechanism to avoid clashes.
Fixes bug where two chats are shown as currently being active in the rooms list.
This commit is contained in:
JC Brand 2019-01-10 12:14:45 +01:00
parent 0464772bfe
commit a4d608dcdf
10 changed files with 173 additions and 151 deletions

View File

@ -3,6 +3,7 @@
## 4.0.7 (Unreleased) ## 4.0.7 (Unreleased)
- Bugfix: MUC commands were being ignored - Bugfix: MUC commands were being ignored
- Bugfix: Multiple rooms shown active in the rooms list
- UI: Always show the OMEMO lock icon (grayed out if not available). - UI: Always show the OMEMO lock icon (grayed out if not available).
- Use `publish-options` with `pubsub#access_model` set to `open` when publishing OMEMO public keys and devices - Use `publish-options` with `pubsub#access_model` set to `open` when publishing OMEMO public keys and devices
- Add a new `converse-pubsub` plugin, for generic PubSub operations - Add a new `converse-pubsub` plugin, for generic PubSub operations

124
dist/converse.js vendored
View File

@ -53857,11 +53857,12 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
return templates_chatroom_details_modal_html__WEBPACK_IMPORTED_MODULE_9___default()(_.extend(this.model.toJSON(), { return templates_chatroom_details_modal_html__WEBPACK_IMPORTED_MODULE_9___default()(_.extend(this.model.toJSON(), {
'_': _, '_': _,
'__': __, '__': __,
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
'features': this.model.features.toJSON(),
'num_occupants': this.model.occupants.length,
'topic': u.addHyperlinks(xss__WEBPACK_IMPORTED_MODULE_26___default.a.filterXSS(_.get(this.model.get('subject'), 'text'), { 'topic': u.addHyperlinks(xss__WEBPACK_IMPORTED_MODULE_26___default.a.filterXSS(_.get(this.model.get('subject'), 'text'), {
'whiteList': {} 'whiteList': {}
})), }))
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
'num_occupants': this.model.occupants.length
})); }));
} }
@ -55361,13 +55362,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
this.chatroomview = this.model.chatroomview; this.chatroomview = this.model.chatroomview;
this.chatroomview.model.on('change:open', this.renderInviteWidget, this); this.chatroomview.model.on('change:open', this.renderInviteWidget, this);
this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this); this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this);
this.chatroomview.model.on('change', () => { this.chatroomview.model.features.on('change', this.renderRoomFeatures, this);
if (_.intersection(_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES, Object.keys(this.chatroomview.model.changed)).length === 0) {
return;
}
this.renderRoomFeatures();
}, this);
this.render(); this.render();
this.model.fetch({ this.model.fetch({
'add': true, 'add': true,
@ -55409,15 +55404,18 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
}, },
renderRoomFeatures() { renderRoomFeatures() {
const picks = _.pick(this.chatroomview.model.attributes, _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES), const features = this.chatroomview.model.features,
iteratee = (a, v) => a || v, picks = _.pick(features.attributes, _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES),
el = this.el.querySelector('.chatroom-features'); iteratee = (a, v) => a || v;
if (_.reduce(_.values(picks), iteratee)) {
const el = this.el.querySelector('.chatroom-features');
el.innerHTML = templates_chatroom_features_html__WEBPACK_IMPORTED_MODULE_11___default()(_.extend(features.toJSON(), {
__
}));
this.setOccupantsHeight();
}
el.innerHTML = templates_chatroom_features_html__WEBPACK_IMPORTED_MODULE_11___default()(_.extend(this.chatroomview.model.toJSON(), {
'__': __,
'has_features': _.reduce(_.values(picks), iteratee)
}));
this.setOccupantsHeight();
return this; return this;
}, },
@ -56448,7 +56446,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
}, },
renderOMEMOToolbarButton() { renderOMEMOToolbarButton() {
if (this.model.get('membersonly') && this.model.get('nonanonymous')) { if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
this.__super__.renderOMEMOToolbarButton.apply(arguments); this.__super__.renderOMEMOToolbarButton.apply(arguments);
} else { } else {
const icon = this.el.querySelector('.toggle-omemo'); const icon = this.el.querySelector('.toggle-omemo');
@ -57265,7 +57263,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
} }
async function onOccupantAdded(chatroom, occupant) { async function onOccupantAdded(chatroom, occupant) {
if (occupant.isSelf() || !chatroom.get('nonanonymous') || !chatroom.get('membersonly')) { if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) {
return; return;
} }
@ -57290,7 +57288,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) { if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
await _converse.api.waitUntil('OMEMOInitialized'); await _converse.api.waitUntil('OMEMOInitialized');
supported = chatbox.get('nonanonymous') && chatbox.get('membersonly'); supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly');
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) { } else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid')); supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid'));
} }
@ -57303,8 +57301,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) { if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o)); chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o));
chatbox.on('change:nonanonymous', checkOMEMOSupported); chatbox.features.on('change', () => checkOMEMOSupported(chatbox));
chatbox.on('change:membersonly', checkOMEMOSupported);
} }
})); }));
@ -63515,7 +63512,7 @@ _converse.initialize = function (settings, callback) {
}; };
this.initSession = function () { this.initSession = function () {
const id = b64_sha1('converse.bosh-session'); const id = 'converse.bosh-session';
_converse.session = new Backbone.Model({ _converse.session = new Backbone.Model({
id id
}); });
@ -66188,7 +66185,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
_converse.ChatRoom = _converse.ChatBox.extend({ _converse.ChatRoom = _converse.ChatBox.extend({
defaults() { defaults() {
return _.assign(_.clone(_converse.ChatBox.prototype.defaults), _.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse)), { return _.assign(_.clone(_converse.ChatBox.prototype.defaults), {
// For group chats, we distinguish between generally unread // For group chats, we distinguish between generally unread
// messages and those ones that specifically mention the // messages and those ones that specifically mention the
// user. // user.
@ -66203,7 +66200,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
'name': '', 'name': '',
'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname,
'description': '', 'description': '',
'features_fetched': false,
'roomconfig': {}, 'roomconfig': {},
'type': _converse.CHATROOMS_TYPE, 'type': _converse.CHATROOMS_TYPE,
'message_type': 'groupchat' 'message_type': 'groupchat'
@ -66214,6 +66210,15 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
this.constructor.__super__.initialize.apply(this, arguments); this.constructor.__super__.initialize.apply(this, arguments);
this.on('change:connection_status', this.onConnectionStatusChanged, this); this.on('change:connection_status', this.onConnectionStatusChanged, this);
const storage = _converse.config.get('storage');
const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
this.features = new Backbone.Model(_.assign({
id
}, _.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse))));
this.features.browserStorage = new Backbone.BrowserStorage.session(id);
this.features.fetch();
this.occupants = new _converse.ChatRoomOccupants(); this.occupants = new _converse.ChatRoomOccupants();
this.occupants.browserStorage = new Backbone.BrowserStorage.session(b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`)); this.occupants.browserStorage = new Backbone.BrowserStorage.session(b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`));
this.occupants.chatroom = this; this.occupants.chatroom = this;
@ -66342,6 +66347,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
* (String) exit_msg: Optional message to indicate your * (String) exit_msg: Optional message to indicate your
* reason for leaving. * reason for leaving.
*/ */
this.features.destroy();
this.occupants.browserStorage._clear(); this.occupants.browserStorage._clear();
this.occupants.reset(); this.occupants.reset();
@ -66583,13 +66590,27 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
return this.getRoomFeatures(); return this.getRoomFeatures();
}, },
async getRoomIdentity() {
const _ref = await Promise.all([_converse.api.disco.getIdentity('conference', 'text', this.get('jid')), _converse.api.disco.getFields(this.get('jid'))]),
_ref2 = _slicedToArray(_ref, 2),
identity = _ref2[0],
fields = _ref2[1];
this.save({
'name': identity && identity.get('name'),
'description': _.get(fields.findWhere({
'var': "muc#roominfo_description"
}), 'attributes.value')
});
},
async getRoomFeatures() { async getRoomFeatures() {
// XXX: not sure whet the right place is to get the room identitiy
this.getRoomIdentity();
const features = await _converse.api.disco.getFeatures(this.get('jid')), const features = await _converse.api.disco.getFeatures(this.get('jid')),
fields = await _converse.api.disco.getFields(this.get('jid')),
identity = await _converse.api.disco.getIdentity('conference', 'text', this.get('jid')),
attrs = _.extend(_.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse)), { attrs = _.extend(_.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse)), {
'features_fetched': moment().format(), 'fetched': moment().format()
'name': identity && identity.get('name')
}); });
features.each(feature => { features.each(feature => {
@ -66605,10 +66626,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
attrs[fieldname.replace('muc_', '')] = true; attrs[fieldname.replace('muc_', '')] = true;
}); });
attrs.description = _.get(fields.findWhere({ this.features.save(attrs);
'var': "muc#roominfo_description"
}), 'attributes.value');
this.save(attrs);
}, },
requestMemberList(affiliation) { requestMemberList(affiliation) {
@ -67230,7 +67248,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
this.trigger('configurationNeeded'); this.trigger('configurationNeeded');
return; // We haven't yet entered the groupchat, so bail here. return; // We haven't yet entered the groupchat, so bail here.
} }
} else if (!this.get('features_fetched')) { } else if (!this.features.get('fetched')) {
// The features for this groupchat weren't fetched. // The features for this groupchat weren't fetched.
// That must mean it's a new groupchat without locking // That must mean it's a new groupchat without locking
// (in which case Prosody doesn't send a 201 status), // (in which case Prosody doesn't send a 201 status),
@ -92568,7 +92586,7 @@ __e(o.num_occupants) +
'</p>\n <p class="room-info"><strong>' + '</p>\n <p class="room-info"><strong>' +
__e(o.__('Features')) + __e(o.__('Features')) +
'</strong>:\n <div class="chatroom-features">\n <ul class="features-list">\n '; '</strong>:\n <div class="chatroom-features">\n <ul class="features-list">\n ';
if (o.passwordprotected) { ; if (o.features.passwordprotected) { ;
__p += '\n <li class="feature" ><span class="fa fa-lock"></span>' + __p += '\n <li class="feature" ><span class="fa fa-lock"></span>' +
__e( o.__('Password protected') ) + __e( o.__('Password protected') ) +
' - <em>' + ' - <em>' +
@ -92576,7 +92594,7 @@ __e( o.__('This groupchat requires a password before entry') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.unsecured) { ; if (o.features.unsecured) { ;
__p += '\n <li class="feature" ><span class="fa fa-unlock"></span>' + __p += '\n <li class="feature" ><span class="fa fa-unlock"></span>' +
__e( o.__('No password required') ) + __e( o.__('No password required') ) +
' - <em>' + ' - <em>' +
@ -92584,7 +92602,7 @@ __e( o.__('This groupchat does not require a password upon entry') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.hidden) { ; if (o.features.hidden) { ;
__p += '\n <li class="feature" ><span class="fa fa-eye-slash"></span>' + __p += '\n <li class="feature" ><span class="fa fa-eye-slash"></span>' +
__e( o.__('Hidden') ) + __e( o.__('Hidden') ) +
' - <em>' + ' - <em>' +
@ -92592,7 +92610,7 @@ __e( o.__('This groupchat is not publicly searchable') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.public_room) { ; if (o.features.public_room) { ;
__p += '\n <li class="feature" ><span class="fa fa-eye"></span>' + __p += '\n <li class="feature" ><span class="fa fa-eye"></span>' +
__e( o.__('Public') ) + __e( o.__('Public') ) +
' - <em>' + ' - <em>' +
@ -92600,7 +92618,7 @@ __e( o.__('This groupchat is publicly searchable') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.membersonly) { ; if (o.features.membersonly) { ;
__p += '\n <li class="feature" ><span class="fa fa-address-book"></span>' + __p += '\n <li class="feature" ><span class="fa fa-address-book"></span>' +
__e( o.__('Members only') ) + __e( o.__('Members only') ) +
' - <em>' + ' - <em>' +
@ -92608,7 +92626,7 @@ __e( o.__('This groupchat is restricted to members only') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.open) { ; if (o.features.open) { ;
__p += '\n <li class="feature" ><span class="fa fa-globe"></span>' + __p += '\n <li class="feature" ><span class="fa fa-globe"></span>' +
__e( o.__('Open') ) + __e( o.__('Open') ) +
' - <em>' + ' - <em>' +
@ -92616,7 +92634,7 @@ __e( o.__('Anyone can join this groupchat') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.persistent) { ; if (o.features.persistent) { ;
__p += '\n <li class="feature" ><span class="fa fa-save"></span>' + __p += '\n <li class="feature" ><span class="fa fa-save"></span>' +
__e( o.__('Persistent') ) + __e( o.__('Persistent') ) +
' - <em>' + ' - <em>' +
@ -92624,7 +92642,7 @@ __e( o.__('This groupchat persists even if it\'s unoccupied') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.temporary) { ; if (o.features.temporary) { ;
__p += '\n <li class="feature" ><span class="fa fa-snowflake-o"></span>' + __p += '\n <li class="feature" ><span class="fa fa-snowflake-o"></span>' +
__e( o.__('Temporary') ) + __e( o.__('Temporary') ) +
' - <em>' + ' - <em>' +
@ -92632,7 +92650,7 @@ __e( o.__('This groupchat will disappear once the last person leaves') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.nonanonymous) { ; if (o.features.nonanonymous) { ;
__p += '\n <li class="feature" ><span class="fa fa-id-card"></span>' + __p += '\n <li class="feature" ><span class="fa fa-id-card"></span>' +
__e( o.__('Not anonymous') ) + __e( o.__('Not anonymous') ) +
' - <em>' + ' - <em>' +
@ -92640,7 +92658,7 @@ __e( o.__('All other groupchat participants can see your XMPP username') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.semianonymous) { ; if (o.features.semianonymous) { ;
__p += '\n <li class="feature" ><span class="fa fa-user-secret"></span>' + __p += '\n <li class="feature" ><span class="fa fa-user-secret"></span>' +
__e( o.__('Semi-anonymous') ) + __e( o.__('Semi-anonymous') ) +
' - <em>' + ' - <em>' +
@ -92648,7 +92666,7 @@ __e( o.__('Only moderators can see your XMPP username') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.moderated) { ; if (o.features.moderated) { ;
__p += '\n <li class="feature" ><span class="fa fa-gavel"></span>' + __p += '\n <li class="feature" ><span class="fa fa-gavel"></span>' +
__e( o.__('Moderated') ) + __e( o.__('Moderated') ) +
' - <em>' + ' - <em>' +
@ -92656,7 +92674,7 @@ __e( o.__('Participants entering this groupchat need to request permission to wr
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.unmoderated) { ; if (o.features.unmoderated) { ;
__p += '\n <li class="feature" ><span class="fa fa-info-circle"></span>' + __p += '\n <li class="feature" ><span class="fa fa-info-circle"></span>' +
__e( o.__('Not moderated') ) + __e( o.__('Not moderated') ) +
' - <em>' + ' - <em>' +
@ -92664,7 +92682,7 @@ __e( o.__('Participants entering this groupchat can write right away') ) +
'</em></li>\n '; '</em></li>\n ';
} ; } ;
__p += '\n '; __p += '\n ';
if (o.mam_enabled) { ; if (o.features.mam_enabled) { ;
__p += '\n <li class="feature" ><span class="fa fa-database"></span>' + __p += '\n <li class="feature" ><span class="fa fa-database"></span>' +
__e( o.__('Message archiving') ) + __e( o.__('Message archiving') ) +
' - <em>' + ' - <em>' +
@ -92715,13 +92733,9 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
module.exports = function(o) { module.exports = function(o) {
var __t, __p = '', __e = _.escape, __j = Array.prototype.join; var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') } function print() { __p += __j.call(arguments, '') }
__p += '<!-- src/templates/chatroom_features.html -->\n'; __p += '<!-- src/templates/chatroom_features.html -->\n<p class="occupants-heading">' +
if (o.has_features) { ;
__p += '\n<p class="occupants-heading">' +
__e(o.__('Features')) + __e(o.__('Features')) +
'</p>\n'; '</p>\n<ul class="features-list">\n';
} ;
__p += '\n<ul class="features-list">\n';
if (o.passwordprotected) { ; if (o.passwordprotected) { ;
__p += '\n<li class="feature" title="' + __p += '\n<li class="feature" title="' +
__e( o.__('This groupchat requires a password before entry') ) + __e( o.__('This groupchat requires a password before entry') ) +
@ -92781,7 +92795,7 @@ __p += '\n';
if (o.temporary) { ; if (o.temporary) { ;
__p += '\n<li class="feature" title="' + __p += '\n<li class="feature" title="' +
__e( o.__('This groupchat will disappear once the last person leaves') ) + __e( o.__('This groupchat will disappear once the last person leaves') ) +
'"><span class="fa fa-snowflake-o"></span>' + '"><span class="fa fa-snowflake"></span>' +
__e( o.__('Temporary') ) + __e( o.__('Temporary') ) +
'</li>\n'; '</li>\n';
} ; } ;

View File

@ -2141,13 +2141,13 @@
let view = _converse.chatboxviews.get('coven@chat.shakespeare.lit'); let view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING)); await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
view = _converse.chatboxviews.get('coven@chat.shakespeare.lit'); view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
expect(view.model.get('features_fetched')).toBeTruthy(); expect(view.model.features.get('fetched')).toBeTruthy();
expect(view.model.get('passwordprotected')).toBe(true); expect(view.model.features.get('passwordprotected')).toBe(true);
expect(view.model.get('hidden')).toBe(true); expect(view.model.features.get('hidden')).toBe(true);
expect(view.model.get('temporary')).toBe(true); expect(view.model.features.get('temporary')).toBe(true);
expect(view.model.get('open')).toBe(true); expect(view.model.features.get('open')).toBe(true);
expect(view.model.get('unmoderated')).toBe(true); expect(view.model.features.get('unmoderated')).toBe(true);
expect(view.model.get('nonanonymous')).toBe(true); expect(view.model.features.get('nonanonymous')).toBe(true);
done(); done();
})); }));
@ -2172,19 +2172,19 @@
let features_list = chatroomview.el.querySelector('.features-list'); let features_list = chatroomview.el.querySelector('.features-list');
let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s); let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
expect(_.difference(["Password protected", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0); expect(_.difference(["Password protected", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0);
expect(chatroomview.model.get('hidden')).toBe(false); expect(chatroomview.model.features.get('hidden')).toBe(false);
expect(chatroomview.model.get('mam_enabled')).toBe(false); expect(chatroomview.model.features.get('mam_enabled')).toBe(false);
expect(chatroomview.model.get('membersonly')).toBe(false); expect(chatroomview.model.features.get('membersonly')).toBe(false);
expect(chatroomview.model.get('moderated')).toBe(false); expect(chatroomview.model.features.get('moderated')).toBe(false);
expect(chatroomview.model.get('nonanonymous')).toBe(true); expect(chatroomview.model.features.get('nonanonymous')).toBe(true);
expect(chatroomview.model.get('open')).toBe(true); expect(chatroomview.model.features.get('open')).toBe(true);
expect(chatroomview.model.get('passwordprotected')).toBe(true); expect(chatroomview.model.features.get('passwordprotected')).toBe(true);
expect(chatroomview.model.get('persistent')).toBe(false); expect(chatroomview.model.features.get('persistent')).toBe(false);
expect(chatroomview.model.get('publicroom')).toBe(true); expect(chatroomview.model.features.get('publicroom')).toBe(true);
expect(chatroomview.model.get('semianonymous')).toBe(false); expect(chatroomview.model.features.get('semianonymous')).toBe(false);
expect(chatroomview.model.get('temporary')).toBe(true); expect(chatroomview.model.features.get('temporary')).toBe(true);
expect(chatroomview.model.get('unmoderated')).toBe(true); expect(chatroomview.model.features.get('unmoderated')).toBe(true);
expect(chatroomview.model.get('unsecured')).toBe(false); expect(chatroomview.model.features.get('unsecured')).toBe(false);
expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('Room'); expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('Room');
chatroomview.el.querySelector('.configure-chatroom-button').click(); chatroomview.el.querySelector('.configure-chatroom-button').click();
@ -2304,7 +2304,7 @@
'muc_passwordprotected', 'muc_passwordprotected',
'muc_hidden', 'muc_hidden',
'muc_temporary', 'muc_temporary',
'muc_open', 'muc_membersonly',
'muc_unmoderated', 'muc_unmoderated',
'muc_nonanonymous' 'muc_nonanonymous'
]; ];
@ -2316,27 +2316,26 @@
.c('value').t('This is the description').up().up() .c('value').t('This is the description').up().up()
.c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'}) .c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'})
.c('value').t(0); .c('value').t(0);
_converse.connection._dataRecv(test_utils.createRequest(features_stanza)); _converse.connection._dataRecv(test_utils.createRequest(features_stanza));
spyOn(chatroomview.occupantsview, 'renderRoomFeatures').and.callThrough(); await test_utils.waitUntil(() => new Promise(success => chatroomview.model.features.on('change', success)));
await test_utils.waitUntil(() => chatroomview.occupantsview.renderRoomFeatures.calls.count());
features_list = chatroomview.el.querySelector('.features-list'); features_list = chatroomview.el.querySelector('.features-list');
features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s); features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
expect(_.difference(["Password protected", "Hidden", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0); expect(_.difference(["Password protected", "Hidden", "Members only", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0);
expect(chatroomview.model.get('hidden')).toBe(true); expect(chatroomview.model.features.get('hidden')).toBe(true);
expect(chatroomview.model.get('mam_enabled')).toBe(false); expect(chatroomview.model.features.get('mam_enabled')).toBe(false);
expect(chatroomview.model.get('membersonly')).toBe(false); expect(chatroomview.model.features.get('membersonly')).toBe(true);
expect(chatroomview.model.get('moderated')).toBe(false); expect(chatroomview.model.features.get('moderated')).toBe(false);
expect(chatroomview.model.get('nonanonymous')).toBe(true); expect(chatroomview.model.features.get('nonanonymous')).toBe(true);
expect(chatroomview.model.get('open')).toBe(true); expect(chatroomview.model.features.get('open')).toBe(false);
expect(chatroomview.model.get('passwordprotected')).toBe(true); expect(chatroomview.model.features.get('passwordprotected')).toBe(true);
expect(chatroomview.model.get('persistent')).toBe(false); expect(chatroomview.model.features.get('persistent')).toBe(false);
expect(chatroomview.model.get('publicroom')).toBe(false); expect(chatroomview.model.features.get('publicroom')).toBe(false);
expect(chatroomview.model.get('semianonymous')).toBe(false); expect(chatroomview.model.features.get('semianonymous')).toBe(false);
expect(chatroomview.model.get('temporary')).toBe(true); expect(chatroomview.model.features.get('temporary')).toBe(true);
expect(chatroomview.model.get('unmoderated')).toBe(true); expect(chatroomview.model.features.get('unmoderated')).toBe(true);
expect(chatroomview.model.get('unsecured')).toBe(false); expect(chatroomview.model.features.get('unsecured')).toBe(false);
expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('New room name'); expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('New room name');
done(); done();
})); }));
@ -3708,7 +3707,7 @@
.c('feature', {'var': 'muc_membersonly'}).up(); .c('feature', {'var': 'muc_membersonly'}).up();
_converse.connection._dataRecv(test_utils.createRequest(features_stanza)); _converse.connection._dataRecv(test_utils.createRequest(features_stanza));
await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING)); await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
expect(view.model.get('membersonly')).toBeTruthy(); expect(view.model.features.get('membersonly')).toBeTruthy();
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');

View File

@ -1254,13 +1254,13 @@
// Test that the button gets disabled when the room becomes // Test that the button gets disabled when the room becomes
// anonymous or semi-anonymous // anonymous or semi-anonymous
view.model.save({'nonanonymous': false, 'semianonymous': true}); view.model.features.save({'nonanonymous': false, 'semianonymous': true});
await test_utils.waitUntil(() => !view.model.get('omemo_supported')); await test_utils.waitUntil(() => !view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo'); toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(true); expect(_.isNull(toggle)).toBe(true);
expect(view.model.get('omemo_supported')).toBe(false); expect(view.model.get('omemo_supported')).toBe(false);
view.model.save({'nonanonymous': true, 'semianonymous': false}); view.model.features.save({'nonanonymous': true, 'semianonymous': false});
await test_utils.waitUntil(() => view.model.get('omemo_supported')); await test_utils.waitUntil(() => view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo'); toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(false); expect(_.isNull(toggle)).toBe(false);
@ -1269,12 +1269,12 @@
expect(u.hasClass('disabled', toggle)).toBe(false); expect(u.hasClass('disabled', toggle)).toBe(false);
// Test that the button gets disabled when the room becomes open // Test that the button gets disabled when the room becomes open
view.model.save({'membersonly': false, 'open': true}); view.model.features.save({'membersonly': false, 'open': true});
await test_utils.waitUntil(() => !view.model.get('omemo_supported')); await test_utils.waitUntil(() => !view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo'); toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(true); expect(_.isNull(toggle)).toBe(true);
view.model.save({'membersonly': true, 'open': false}); view.model.features.save({'membersonly': true, 'open': false});
await test_utils.waitUntil(() => view.model.get('omemo_supported')); await test_utils.waitUntil(() => view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo'); toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(false); expect(_.isNull(toggle)).toBe(false);

View File

@ -236,7 +236,7 @@ converse.plugins.add('converse-muc-views', {
function toggleRoomInfo (ev) { function toggleRoomInfo (ev) {
/* Show/hide extra information about a groupchat in a listing. */ /* Show/hide extra information about a groupchat in a listing. */
const parent_el = u.ancestor(ev.target, '.room-item'), const parent_el = u.ancestor(ev.target, '.room-item'),
div_el = parent_el.querySelector('div.room-info'); div_el = parent_el.querySelector('div.room-info');
if (div_el) { if (div_el) {
u.slideIn(div_el).then(u.removeElement) u.slideIn(div_el).then(u.removeElement)
parent_el.querySelector('a.room-info').classList.remove('selected'); parent_el.querySelector('a.room-info').classList.remove('selected');
@ -439,9 +439,10 @@ converse.plugins.add('converse-muc-views', {
this.model.toJSON(), { this.model.toJSON(), {
'_': _, '_': _,
'__': __, '__': __,
'topic': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}})),
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()), 'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
'num_occupants': this.model.occupants.length 'features': this.model.features.toJSON(),
'num_occupants': this.model.occupants.length,
'topic': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}}))
}) })
); );
} }
@ -1831,12 +1832,7 @@ converse.plugins.add('converse-muc-views', {
this.chatroomview = this.model.chatroomview; this.chatroomview = this.model.chatroomview;
this.chatroomview.model.on('change:open', this.renderInviteWidget, this); this.chatroomview.model.on('change:open', this.renderInviteWidget, this);
this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this); this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this);
this.chatroomview.model.on('change', () => { this.chatroomview.model.features.on('change', this.renderRoomFeatures, this);
if (_.intersection(converse.ROOM_FEATURES, Object.keys(this.chatroomview.model.changed)).length === 0) {
return;
}
this.renderRoomFeatures();
}, this);
this.render(); this.render();
this.model.fetch({ this.model.fetch({
@ -1882,16 +1878,15 @@ converse.plugins.add('converse-muc-views', {
}, },
renderRoomFeatures () { renderRoomFeatures () {
const picks = _.pick(this.chatroomview.model.attributes, converse.ROOM_FEATURES), const features = this.chatroomview.model.features,
iteratee = (a, v) => a || v, picks = _.pick(features.attributes, converse.ROOM_FEATURES),
el = this.el.querySelector('.chatroom-features'); iteratee = (a, v) => a || v;
el.innerHTML = tpl_chatroom_features( if (_.reduce(_.values(picks), iteratee)) {
_.extend(this.chatroomview.model.toJSON(), { const el = this.el.querySelector('.chatroom-features');
'__': __, el.innerHTML = tpl_chatroom_features(_.extend(features.toJSON(), {__}));
'has_features': _.reduce(_.values(picks), iteratee) this.setOccupantsHeight();
})); }
this.setOccupantsHeight();
return this; return this;
}, },

View File

@ -446,7 +446,7 @@ converse.plugins.add('converse-omemo', {
}, },
renderOMEMOToolbarButton () { renderOMEMOToolbarButton () {
if (this.model.get('membersonly') && this.model.get('nonanonymous')) { if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
this.__super__.renderOMEMOToolbarButton.apply(arguments); this.__super__.renderOMEMOToolbarButton.apply(arguments);
} else { } else {
const icon = this.el.querySelector('.toggle-omemo'); const icon = this.el.querySelector('.toggle-omemo');
@ -1137,7 +1137,7 @@ converse.plugins.add('converse-omemo', {
} }
async function onOccupantAdded (chatroom, occupant) { async function onOccupantAdded (chatroom, occupant) {
if (occupant.isSelf() || !chatroom.get('nonanonymous') || !chatroom.get('membersonly')) { if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) {
return; return;
} }
if (chatroom.get('omemo_active')) { if (chatroom.get('omemo_active')) {
@ -1157,7 +1157,7 @@ converse.plugins.add('converse-omemo', {
let supported; let supported;
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) { if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
await _converse.api.waitUntil('OMEMOInitialized'); await _converse.api.waitUntil('OMEMOInitialized');
supported = chatbox.get('nonanonymous') && chatbox.get('membersonly'); supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly');
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) { } else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid')); supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid'));
} }
@ -1169,8 +1169,7 @@ converse.plugins.add('converse-omemo', {
checkOMEMOSupported(chatbox); checkOMEMOSupported(chatbox);
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) { if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o)); chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o));
chatbox.on('change:nonanonymous', checkOMEMOSupported); chatbox.features.on('change', () => checkOMEMOSupported(chatbox));
chatbox.on('change:membersonly', checkOMEMOSupported);
} }
}) })
); );

View File

@ -795,7 +795,7 @@ _converse.initialize = function (settings, callback) {
this.initSession = function () { this.initSession = function () {
const id = b64_sha1('converse.bosh-session'); const id = 'converse.bosh-session';
_converse.session = new Backbone.Model({id}); _converse.session = new Backbone.Model({id});
_converse.session.browserStorage = new Backbone.BrowserStorage.session(id); _converse.session.browserStorage = new Backbone.BrowserStorage.session(id);
_converse.session.fetch(); _converse.session.fetch();

View File

@ -159,9 +159,7 @@ converse.plugins.add('converse-muc', {
defaults () { defaults () {
return _.assign( return _.assign(
_.clone(_converse.ChatBox.prototype.defaults), _.clone(_converse.ChatBox.prototype.defaults), {
_.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)),
{
// For group chats, we distinguish between generally unread // For group chats, we distinguish between generally unread
// messages and those ones that specifically mention the // messages and those ones that specifically mention the
// user. // user.
@ -177,7 +175,6 @@ converse.plugins.add('converse-muc', {
'name': '', 'name': '',
'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname,
'description': '', 'description': '',
'features_fetched': false,
'roomconfig': {}, 'roomconfig': {},
'type': _converse.CHATROOMS_TYPE, 'type': _converse.CHATROOMS_TYPE,
'message_type': 'groupchat' 'message_type': 'groupchat'
@ -189,6 +186,14 @@ converse.plugins.add('converse-muc', {
this.constructor.__super__.initialize.apply(this, arguments); this.constructor.__super__.initialize.apply(this, arguments);
this.on('change:connection_status', this.onConnectionStatusChanged, this); this.on('change:connection_status', this.onConnectionStatusChanged, this);
const storage = _converse.config.get('storage');
const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
this.features = new Backbone.Model(
_.assign({id}, _.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)))
);
this.features.browserStorage = new Backbone.BrowserStorage.session(id);
this.features.fetch();
this.occupants = new _converse.ChatRoomOccupants(); this.occupants = new _converse.ChatRoomOccupants();
this.occupants.browserStorage = new Backbone.BrowserStorage.session( this.occupants.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`) b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`)
@ -304,6 +309,7 @@ converse.plugins.add('converse-muc', {
* (String) exit_msg: Optional message to indicate your * (String) exit_msg: Optional message to indicate your
* reason for leaving. * reason for leaving.
*/ */
this.features.destroy();
this.occupants.browserStorage._clear(); this.occupants.browserStorage._clear();
this.occupants.reset(); this.occupants.reset();
if (_converse.disco_entities) { if (_converse.disco_entities) {
@ -496,14 +502,25 @@ converse.plugins.add('converse-muc', {
return this.getRoomFeatures(); return this.getRoomFeatures();
}, },
async getRoomIdentity () {
const [identity, fields] = await Promise.all([
_converse.api.disco.getIdentity('conference', 'text', this.get('jid')),
_converse.api.disco.getFields(this.get('jid'))
]);
this.save({
'name': identity && identity.get('name'),
'description': _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value')
});
},
async getRoomFeatures () { async getRoomFeatures () {
// XXX: not sure whet the right place is to get the room identitiy
this.getRoomIdentity();
const features = await _converse.api.disco.getFeatures(this.get('jid')), const features = await _converse.api.disco.getFeatures(this.get('jid')),
fields = await _converse.api.disco.getFields(this.get('jid')), attrs = _.extend(
identity = await _converse.api.disco.getIdentity('conference', 'text', this.get('jid')), _.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)),
attrs = _.extend(_.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)), { {'fetched': moment().format()}
'features_fetched': moment().format(), );
'name': identity && identity.get('name')
});
features.each(feature => { features.each(feature => {
const fieldname = feature.get('var'); const fieldname = feature.get('var');
@ -515,8 +532,7 @@ converse.plugins.add('converse-muc', {
} }
attrs[fieldname.replace('muc_', '')] = true; attrs[fieldname.replace('muc_', '')] = true;
}); });
attrs.description = _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value'); this.features.save(attrs);
this.save(attrs);
}, },
requestMemberList (affiliation) { requestMemberList (affiliation) {
@ -1053,7 +1069,7 @@ converse.plugins.add('converse-muc', {
this.trigger('configurationNeeded'); this.trigger('configurationNeeded');
return; // We haven't yet entered the groupchat, so bail here. return; // We haven't yet entered the groupchat, so bail here.
} }
} else if (!this.get('features_fetched')) { } else if (!this.features.get('fetched')) {
// The features for this groupchat weren't fetched. // The features for this groupchat weren't fetched.
// That must mean it's a new groupchat without locking // That must mean it's a new groupchat without locking
// (in which case Prosody doesn't send a 201 status), // (in which case Prosody doesn't send a 201 status),

View File

@ -18,43 +18,43 @@
<p class="room-info"><strong>{{{o.__('Features')}}}</strong>: <p class="room-info"><strong>{{{o.__('Features')}}}</strong>:
<div class="chatroom-features"> <div class="chatroom-features">
<ul class="features-list"> <ul class="features-list">
{[ if (o.passwordprotected) { ]} {[ if (o.features.passwordprotected) { ]}
<li class="feature" ><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}} - <em>{{{ o.__('This groupchat requires a password before entry') }}}</em></li> <li class="feature" ><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}} - <em>{{{ o.__('This groupchat requires a password before entry') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.unsecured) { ]} {[ if (o.features.unsecured) { ]}
<li class="feature" ><span class="fa fa-unlock"></span>{{{ o.__('No password required') }}} - <em>{{{ o.__('This groupchat does not require a password upon entry') }}}</em></li> <li class="feature" ><span class="fa fa-unlock"></span>{{{ o.__('No password required') }}} - <em>{{{ o.__('This groupchat does not require a password upon entry') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.hidden) { ]} {[ if (o.features.hidden) { ]}
<li class="feature" ><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}} - <em>{{{ o.__('This groupchat is not publicly searchable') }}}</em></li> <li class="feature" ><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}} - <em>{{{ o.__('This groupchat is not publicly searchable') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.public_room) { ]} {[ if (o.features.public_room) { ]}
<li class="feature" ><span class="fa fa-eye"></span>{{{ o.__('Public') }}} - <em>{{{ o.__('This groupchat is publicly searchable') }}}</em></li> <li class="feature" ><span class="fa fa-eye"></span>{{{ o.__('Public') }}} - <em>{{{ o.__('This groupchat is publicly searchable') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.membersonly) { ]} {[ if (o.features.membersonly) { ]}
<li class="feature" ><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}} - <em>{{{ o.__('This groupchat is restricted to members only') }}}</em></li> <li class="feature" ><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}} - <em>{{{ o.__('This groupchat is restricted to members only') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.open) { ]} {[ if (o.features.open) { ]}
<li class="feature" ><span class="fa fa-globe"></span>{{{ o.__('Open') }}} - <em>{{{ o.__('Anyone can join this groupchat') }}}</em></li> <li class="feature" ><span class="fa fa-globe"></span>{{{ o.__('Open') }}} - <em>{{{ o.__('Anyone can join this groupchat') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.persistent) { ]} {[ if (o.features.persistent) { ]}
<li class="feature" ><span class="fa fa-save"></span>{{{ o.__('Persistent') }}} - <em>{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}</em></li> <li class="feature" ><span class="fa fa-save"></span>{{{ o.__('Persistent') }}} - <em>{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.temporary) { ]} {[ if (o.features.temporary) { ]}
<li class="feature" ><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}} - <em>{{{ o.__('This groupchat will disappear once the last person leaves') }}}</em></li> <li class="feature" ><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}} - <em>{{{ o.__('This groupchat will disappear once the last person leaves') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.nonanonymous) { ]} {[ if (o.features.nonanonymous) { ]}
<li class="feature" ><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}} - <em>{{{ o.__('All other groupchat participants can see your XMPP username') }}}</em></li> <li class="feature" ><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}} - <em>{{{ o.__('All other groupchat participants can see your XMPP username') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.semianonymous) { ]} {[ if (o.features.semianonymous) { ]}
<li class="feature" ><span class="fa fa-user-secret"></span>{{{ o.__('Semi-anonymous') }}} - <em>{{{ o.__('Only moderators can see your XMPP username') }}}</em></li> <li class="feature" ><span class="fa fa-user-secret"></span>{{{ o.__('Semi-anonymous') }}} - <em>{{{ o.__('Only moderators can see your XMPP username') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.moderated) { ]} {[ if (o.features.moderated) { ]}
<li class="feature" ><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}} - <em>{{{ o.__('Participants entering this groupchat need to request permission to write') }}}</em></li> <li class="feature" ><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}} - <em>{{{ o.__('Participants entering this groupchat need to request permission to write') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.unmoderated) { ]} {[ if (o.features.unmoderated) { ]}
<li class="feature" ><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}} - <em>{{{ o.__('Participants entering this groupchat can write right away') }}}</em></li> <li class="feature" ><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}} - <em>{{{ o.__('Participants entering this groupchat can write right away') }}}</em></li>
{[ } ]} {[ } ]}
{[ if (o.mam_enabled) { ]} {[ if (o.features.mam_enabled) { ]}
<li class="feature" ><span class="fa fa-database"></span>{{{ o.__('Message archiving') }}} - <em>{{{ o.__('Messages are archived on the server') }}}</em></li> <li class="feature" ><span class="fa fa-database"></span>{{{ o.__('Message archiving') }}} - <em>{{{ o.__('Messages are archived on the server') }}}</em></li>
{[ } ]} {[ } ]}
</ul> </ul>

View File

@ -1,6 +1,4 @@
{[ if (o.has_features) { ]}
<p class="occupants-heading">{{{o.__('Features')}}}</p> <p class="occupants-heading">{{{o.__('Features')}}}</p>
{[ } ]}
<ul class="features-list"> <ul class="features-list">
{[ if (o.passwordprotected) { ]} {[ if (o.passwordprotected) { ]}
<li class="feature" title="{{{ o.__('This groupchat requires a password before entry') }}}"><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}}</li> <li class="feature" title="{{{ o.__('This groupchat requires a password before entry') }}}"><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}}</li>
@ -24,7 +22,7 @@
<li class="feature" title="{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}"><span class="fa fa-save"></span>{{{ o.__('Persistent') }}}</li> <li class="feature" title="{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}"><span class="fa fa-save"></span>{{{ o.__('Persistent') }}}</li>
{[ } ]} {[ } ]}
{[ if (o.temporary) { ]} {[ if (o.temporary) { ]}
<li class="feature" title="{{{ o.__('This groupchat will disappear once the last person leaves') }}}"><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}}</li> <li class="feature" title="{{{ o.__('This groupchat will disappear once the last person leaves') }}}"><span class="fa fa-snowflake"></span>{{{ o.__('Temporary') }}}</li>
{[ } ]} {[ } ]}
{[ if (o.nonanonymous) { ]} {[ if (o.nonanonymous) { ]}
<li class="feature" title="{{{ o.__('All other groupchat participants can see your XMPP username') }}}"><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}}</li> <li class="feature" title="{{{ o.__('All other groupchat participants can see your XMPP username') }}}"><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}}</li>