Initial work on allowing in band registrations. XEP 077

This commit is contained in:
JC Brand 2014-11-16 12:47:30 +01:00
parent 35f203ff62
commit e9638e70c7
14 changed files with 313 additions and 179 deletions

View File

@ -179,13 +179,11 @@
'dnd': 2,
'online': 1
};
var INACTIVE = 'inactive';
var ACTIVE = 'active';
var COMPOSING = 'composing';
var PAUSED = 'paused';
var GONE = 'gone';
var HAS_CSPRNG = ((typeof crypto !== 'undefined') &&
((typeof crypto.randomBytes === 'function') ||
(typeof crypto.getRandomValues === 'function')
@ -195,7 +193,6 @@
(typeof OTR !== "undefined") &&
(typeof DSA !== "undefined")
);
var OPENED = 'opened';
var CLOSED = 'closed';
@ -440,7 +437,6 @@
};
this.onConnect = function (status, condition, reconnect) {
var $button, $form;
if ((status === Strophe.Status.CONNECTED) ||
(status === Strophe.Status.ATTACHED)) {
if ((typeof reconnect !== 'undefined') && (reconnect)) {
@ -1991,10 +1987,12 @@
var cfg = {'$parent': this.$el.find('.controlbox-panes'), 'model': this};
if (!this.loginpanel) {
this.loginpanel = new converse.LoginPanel(cfg);
this.registerpanel = new converse.RegisterPanel(cfg);
} else {
this.loginpanel.delegateEvents().initialize(cfg);
}
this.loginpanel.render();
this.registerpanel.render().$el.hide();
this.initDragResize();
},
@ -2420,72 +2418,15 @@
$stanza = $(stanza),
$fields = $stanza.find('field'),
title = $stanza.find('title').text(),
instructions = $stanza.find('instructions').text(),
i, j, options=[], $field, $options,
values=[], $values, value;
var input_types = {
'text-private': 'password',
'text-single': 'textline',
'fixed': 'label',
'boolean': 'checkbox',
'hidden': 'hidden',
'jid-multi': 'textarea',
'list-single': 'dropdown',
'list-multi': 'dropdown'
};
instructions = $stanza.find('instructions').text();
$form.find('span.spinner').remove();
$form.append($('<legend>').text(title));
if (instructions != title) {
$form.append($('<p>').text(instructions));
}
for (i=0; i<$fields.length; i++) {
$field = $($fields[i]);
if ($field.attr('type') == 'list-single' || $field.attr('type') == 'list-multi') {
values = [];
$values = $field.children('value');
for (j=0; j<$values.length; j++) {
values.push($($values[j]).text());
}
options = [];
$options = $field.children('option');
for (j=0; j<$options.length; j++) {
value = $($options[j]).find('value').text();
options.push(converse.templates.select_option({
value: value,
label: $($options[j]).attr('label'),
selected: (values.indexOf(value) >= 0)
}));
}
$form.append(converse.templates.form_select({
name: $field.attr('var'),
label: $field.attr('label'),
options: options.join(''),
multiple: ($field.attr('type') == 'list-multi')
}));
} else if ($field.attr('type') == 'fixed') {
$form.append($('<p>').text($field.find('value').text()));
} else if ($field.attr('type') == 'jid-multi') {
$form.append(converse.templates.form_textarea({
name: $field.attr('var'),
label: $field.attr('label') || '',
value: $field.find('value').text()
}));
} else if ($field.attr('type') == 'boolean') {
$form.append(converse.templates.form_checkbox({
name: $field.attr('var'),
type: input_types[$field.attr('type')],
label: $field.attr('label') || '',
checked: $field.find('value').text() === "1" && 'checked="1"' || ''
}));
} else {
$form.append(converse.templates.form_input({
name: $field.attr('var'),
type: input_types[$field.attr('type')],
label: $field.attr('label') || '',
value: $field.find('value').text()
}));
}
}
_.each($fields, function (field) {
$form.append(utils.xForm2webForm(field));
});
$form.append('<input type="submit" value="'+__('Save')+'"/>');
$form.append('<input type="button" value="'+__('Cancel')+'"/>');
$form.on('submit', $.proxy(this.saveConfiguration, this));
@ -3087,7 +3028,9 @@
this.model.each($.proxy(function (model) {
var id = model.get('id');
if (include_controlbox || id !== 'controlbox') {
this.get(id).close();
if (this.get(id)) { // Should always resolve, but shit happens
this.get(id).close();
}
}
}, this));
return this;
@ -4550,28 +4493,108 @@
}
});
this.LoginPanel = Backbone.View.extend({
this.RegisterPanel = Backbone.View.extend({
tagName: 'div',
id: "login-dialog",
id: "register",
className: 'controlbox-pane',
events: {
'submit form#converse-login': 'authenticate'
'submit form#converse-register': 'query'
},
connect: function ($form, jid, password) {
initialize: function (cfg) {
this.$parent = cfg.$parent;
this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
},
render: function () {
this.$parent.append(this.$el.html(
converse.templates.register_panel({
'label_domain': __('XMPP Provider'), // TODO: make this a dropdown of servers...
'label_register': __('Request Registration Form')
})
));
this.$tabs.append(converse.templates.register_tab({label_register: __('Register')}));
this.$el.find('input#jid').focus();
return this;
},
query: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
var $form = $(ev.target),
$domain_input = $form.find('input[name=domain]'),
domain = $domain_input.val(),
errors = false;
if (!domain) { errors = true; $domain_input.addClass('error'); }
if (errors) { return; } // TODO provide error messages
this.connect($form, domain);
return false;
},
connect: function ($form, domain) {
if ($form) {
$form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
}
var resource = Strophe.getResourceFromJid(jid);
if (!resource) {
jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
converse.connection.register.connect(domain, $.proxy(this.onRegistering, this));
},
giveFeedback: function (message, klass) {
$('.conn-feedback').attr('class', 'conn-feedback').text(message);
if (klass) {
$('.conn-feedback').addClass(klass);
}
converse.connection.connect(jid, password, converse.onConnect);
},
onRegistering: function (status) {
console.log('onRegistering');
if (status === Strophe.Status.CONNECTING) {
converse.giveFeedback(__('Connecting'));
} else if (status === Strophe.Status.CONNFAIL) {
converse.renderLoginPanel();
this.giveFeedback(__('Connection Failed'), 'error');
} else if (status === Strophe.Status.DISCONNECTING) {
if (!converse.connection.connected) {
converse.renderLoginPanel();
} else {
this.giveFeedback(__('Disconnecting'), 'error');
}
} else if (status == Strophe.Status.REGISTER) {
this.renderRegistrationForm();
}
},
renderRegistrationForm: function () {
var register = converse.connection.register,
$form= this.$el.find('form'),
$stanza = $(register.query),
$fields = $stanza.find('field');
$form.empty().append($('<p>').text(register.instructions));
_.each($fields, function (field) {
$form.append(utils.xForm2webForm(field));
});
$form.append('<input type="submit" value="'+__('Register')+'"/>');
$form.on('submit', $.proxy(this.register, this));
},
remove: function () {
// XXX ?
this.$tabs.empty();
this.$el.parent().empty();
}
});
this.LoginPanel = Backbone.View.extend({
tagName: 'div',
id: "login-dialog",
className: 'controlbox-pane',
events: {
'submit form#converse-login': 'authenticate'
},
initialize: function (cfg) {
cfg.$parent.html(this.$el.html(
converse.templates.login_panel({
'label_username': __('XMPP/Jabber Username:'),
'label_username': __('XMPP Username:'),
'label_password': __('Password:'),
'label_login': __('Log In')
})
@ -4616,6 +4639,18 @@
return false;
},
connect: function ($form, jid, password) {
if ($form) {
$form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
}
var resource = Strophe.getResourceFromJid(jid);
if (!resource) {
jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
}
converse.connection.connect(jid, password, converse.onConnect);
},
remove: function () {
this.$tabs.empty();
this.$el.parent().empty();

View File

@ -926,9 +926,6 @@ dl.add-converse-contact {
display: block;
clear: both;
}
#conversejs .chatroom-form label.label-ta {
height: auto;
}
#conversejs .chatroom-form label input,
#conversejs .chatroom-form label select {
float: right;
@ -1110,9 +1107,17 @@ dl.add-converse-contact {
width: 300px;
}
#conversejs .controlbox-pane {
padding: 0;
border-bottom-right-radius: 4px;
background-color: white;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border: 0;
font-size: 14px;
height: 289px;
height: calc(100% - 35px);
overflow-y: hidden;
padding: 0;
position: absolute;
width: 100%;
}
#conversejs .controlbox-pane dd {
margin-left: 0;
@ -1122,18 +1127,27 @@ dl.add-converse-contact {
#conversejs .controlbox-pane dd.odd {
background-color: #DCEAC5;
}
#conversejs form#converse-register,
#conversejs form#converse-login {
background: white;
padding: 2em 0.5em;
}
#conversejs form#converse-register input,
#conversejs form#converse-login input {
width: 100%;
width: 98%;
height: 30px;
}
#conversejs form#converse-register label,
#conversejs form#converse-login label {
margin-top: 0.5em;
}
#conversejs form#converse-login .login-submit {
#conversejs form#converse-register .login-submit,
#conversejs form#converse-login .login-submit,
#conversejs form#converse-register .submit,
#conversejs form#converse-login .submit {
width: 100%;
margin-top: 1em;
height: 30px;
}
#conversejs form.set-xmpp-status {
background: none;
@ -1201,21 +1215,6 @@ select#select-xmpp-status {
cursor: default;
color: #666666;
}
#conversejs div#users,
#conversejs div#chatrooms,
#conversejs div#login-dialog,
#conversejs div#settings {
border: 0;
font-size: 14px;
background-color: white;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
width: 100%;
height: 289px;
height: calc(100% - 35px);
overflow-y: hidden;
position: absolute;
}
#conversejs div#chatrooms {
overflow-y: auto;
}

View File

@ -16,6 +16,7 @@ h4,
h5,
h6 {
margin: 0 0 35px;
text-transform: uppercase;
font-family: "Montserrat", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 700;
letter-spacing: 1px;

View File

@ -12,14 +12,8 @@
<link type="text/css" rel="stylesheet" media="screen" href="components/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" media="screen" href="components/fontawesome/css/font-awesome.min.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/theme.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.min.css" />
<!-- Only for development: <script data-main="main" src="components/requirejs/require.js"></script> -->
<![if gte IE 9]>
<script src="builds/converse.website.min.js"></script>
<![endif]>
<!--[if lt IE 9]>
<script src="builds/converse.website-no-otr.min.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script data-main="main" src="components/requirejs/require.js"></script>
</head>
<body id="page-top" data-spy="scroll" data-target=".navbar-custom">

View File

@ -1247,9 +1247,17 @@ dl.add-converse-contact {
}
#conversejs .controlbox-pane {
padding: 0;
border-bottom-right-radius: 4px;
background-color: white;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border: 0;
font-size: 14px;
height: 289px;
height: ~"calc(100% - 35px)";
overflow-y: hidden;
padding: 0;
position: absolute;
width: 100%;
}
#conversejs .controlbox-pane dd {
@ -1262,21 +1270,30 @@ dl.add-converse-contact {
background-color: #DCEAC5;
}
#conversejs form#converse-register,
#conversejs form#converse-login {
background: white;
padding: 2em 0.5em;
}
#conversejs form#converse-register input,
#conversejs form#converse-login input {
width: 100%;
width: 98%;
height: 30px;
}
#conversejs form#converse-register label,
#conversejs form#converse-login label {
margin-top: 0.5em;
}
#conversejs form#converse-login .login-submit {
#conversejs form#converse-register .login-submit,
#conversejs form#converse-login .login-submit,
#conversejs form#converse-register .submit,
#conversejs form#converse-login .submit {
width: 100%;
margin-top: 1em;
height: 30px;
}
#conversejs form.set-xmpp-status {
@ -1357,22 +1374,6 @@ select#select-xmpp-status {
color: rgb(102,102,102);
}
#conversejs div#users,
#conversejs div#chatrooms,
#conversejs div#login-dialog,
#conversejs div#settings {
border: 0;
font-size: 14px;
background-color: white;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
width: 100%;
height: 289px;
height: ~"calc(100% - 35px)";
overflow-y: hidden;
position: absolute;
}
#conversejs div#chatrooms {
overflow-y: auto;
}

View File

@ -17,6 +17,7 @@ require.config({
"strophe": "components/strophe/strophe",
"strophe.disco": "components/strophejs-plugins/disco/strophe.disco",
"strophe.muc": "components/strophe.muc/index",
"strophe.register": "components/strophejs-plugins/register/strophe.register",
"strophe.roster": "src/strophe.roster",
"strophe.vcard": "components/strophejs-plugins/vcard/strophe.vcard",
"text": 'components/requirejs-text/text',
@ -93,6 +94,8 @@ require.config({
"pending_contacts": "src/templates/pending_contacts",
"requesting_contact": "src/templates/requesting_contact",
"requesting_contacts": "src/templates/requesting_contacts",
"register_panel": "src/templates/register_panel",
"register_tab": "src/templates/register_tab",
"room_description": "src/templates/room_description",
"room_item": "src/templates/room_item",
"room_panel": "src/templates/room_panel",
@ -143,6 +146,7 @@ require.config({
'strophe': { exports: 'Strophe' },
'strophe.disco': { deps: ['strophe'] },
'strophe.muc': { deps: ['strophe'] },
'strophe.register': { deps: ['strophe'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] }
}

View File

@ -31,16 +31,26 @@
<div class="chat-head controlbox-head">
<ul id="controlbox-tabs">
<li><a class="current" href="#login">Sign in</a></li>
<li><a href="#register">Register</a></li>
</ul>
<a class="close-chatbox-button icon-close"></a>
</div>
<div id="login-dialog">
<div id="login-dialog" class="controlbox-pane">
<form id="converse-login">
<label>XMPP/Jabber Username:</label><input type="text" id="jid">
<label>XMPP Username:</label><input type="text" id="jid" placeholder="name@server">
<label>Password:</label><input type="password" id="password">
<input class="login-submit" type="submit" value="Log In">
<input class="submit" type="submit" value="Log In">
<span class="conn-feedback"></span>
</form>
</div>
<div id="register" class="controlbox-pane" style="display: none">
<form id="converse-register">
<label>XMPP Username:</label><input type="text" id="jid" placeholder="name@server">
<label>Password:</label><input type="password" name="password">
<label>Confirm Password:</label><input type="password" name="password_confirm">
<input class="submit" type="submit" value="Register">
<span class="conn-feedback"></span>
</form>
<span class="conn-feedback"></span>
</div>
</div>
</div>
@ -55,6 +65,7 @@
</ul>
<a class="close-chatbox-button icon-close"></a>
</div>
<div id="users" class="controlbox-pane" style="display: block;">
<form class="set-xmpp-status" action="" method="post">
<span id="xmpp-status-holder">
@ -227,39 +238,41 @@
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
<div id="chatrooms" class="controlbox-pane" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
@ -272,6 +285,12 @@ $(document).ready(function () {
$('a[href=#users]').click(function (ev) {
switchTab(ev);
});
$('a[href=#register]').click(function (ev) {
switchTab(ev);
});
$('a[href=#login]').click(function (ev) {
switchTab(ev);
});
$("a.choose-xmpp-status").click(function (ev) {
ev.preventDefault();

View File

@ -10,6 +10,7 @@ define("converse-dependencies", [
"typeahead",
"strophe",
"strophe.muc",
"strophe.register",
"strophe.roster",
"strophe.vcard",
"strophe.disco"

View File

@ -12,6 +12,7 @@ define("converse-dependencies", [
"typeahead",
"strophe",
"strophe.muc",
"strophe.register",
"strophe.roster",
"strophe.vcard",
"strophe.disco"

View File

@ -29,6 +29,8 @@ define("converse-templates", [
"tpl!occupant",
"tpl!pending_contact",
"tpl!pending_contacts",
"tpl!register_panel",
"tpl!register_tab",
"tpl!requesting_contact",
"tpl!requesting_contacts",
"tpl!room_description",
@ -75,19 +77,21 @@ define("converse-templates", [
occupant: arguments[27],
pending_contact: arguments[28],
pending_contacts: arguments[29],
requesting_contact: arguments[30],
requesting_contacts: arguments[31],
room_description: arguments[32],
room_item: arguments[33],
room_panel: arguments[34],
roster: arguments[35],
roster_item: arguments[36],
select_option: arguments[37],
search_contact: arguments[38],
status_option: arguments[39],
toggle_chats: arguments[40],
toolbar: arguments[41],
trimmed_chat: arguments[42],
form_textarea: arguments[43]
register_panel: arguments[30],
register_tab: arguments[31],
requesting_contact: arguments[32],
requesting_contacts: arguments[33],
room_description: arguments[34],
room_item: arguments[35],
room_panel: arguments[36],
roster: arguments[37],
roster_item: arguments[38],
select_option: arguments[39],
search_contact: arguments[40],
status_option: arguments[41],
toggle_chats: arguments[42],
toolbar: arguments[43],
trimmed_chat: arguments[44],
form_textarea: arguments[45]
};
});

View File

@ -1,8 +1,8 @@
<form id="converse-login" method="post">
<label>{{label_username}}</label>
<input type="username" name="jid" placeholder="Username">
<input type="username" name="jid" placeholder="user@server">
<label>{{label_password}}</label>
<input type="password" name="password" placeholder="Password">
<input class="login-submit" type="submit" value="{{label_login}}">
<input type="password" name="password" placeholder="password">
<input class="submit" type="submit" value="{{label_login}}">
<span class="conn-feedback"></span>
</form>

View File

@ -0,0 +1,6 @@
<form id="converse-register">
<label>{{label_domain}}</label>
<input type="text" name="domain" placeholder="e.g. conversejs.org">
<input class="submit" type="submit" value="{{label_register}}">
<span class="conn-feedback"></span>
</form>

View File

@ -0,0 +1 @@
<li><a class="s" href="#register">{{label_register}}</a></li>

View File

@ -1,4 +1,17 @@
define(["jquery"], function ($) {
define(["jquery", "converse-templates"], function ($, templates) {
"use strict";
var XFORM_TYPE_MAP = {
'text-private': 'password',
'text-single': 'textline',
'fixed': 'label',
'boolean': 'checkbox',
'hidden': 'hidden',
'jid-multi': 'textarea',
'list-single': 'dropdown',
'list-multi': 'dropdown'
};
$.fn.hasScrollBar = function() {
if (!$.contains(document, this.get(0))) {
return false;
@ -52,6 +65,61 @@ define(["jquery"], function ($) {
* See actionInfoMessages
*/
return str;
},
xForm2webForm: function (field) {
/* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into a HTML DOM field.
*
* Parameters:
* (XMLElement) field - the field to convert
*/
var $field = $(field), options = [],
j, $options, $values, value, values;
if ($field.attr('type') == 'list-single' || $field.attr('type') == 'list-multi') {
values = [];
$values = $field.children('value');
for (j=0; j<$values.length; j++) {
values.push($($values[j]).text());
}
$options = $field.children('option');
for (j=0; j<$options.length; j++) {
value = $($options[j]).find('value').text();
options.push(templates.select_option({
value: value,
label: $($options[j]).attr('label'),
selected: (values.indexOf(value) >= 0)
}));
}
return templates.form_select({
name: $field.attr('var'),
label: $field.attr('label'),
options: options.join(''),
multiple: ($field.attr('type') == 'list-multi')
});
} else if ($field.attr('type') == 'fixed') {
return $('<p>').text($field.find('value').text());
} else if ($field.attr('type') == 'jid-multi') {
return templates.form_textarea({
name: $field.attr('var'),
label: $field.attr('label') || '',
value: $field.find('value').text()
});
} else if ($field.attr('type') == 'boolean') {
return templates.form_checkbox({
name: $field.attr('var'),
type: XFORM_TYPE_MAP[$field.attr('type')],
label: $field.attr('label') || '',
checked: $field.find('value').text() === "1" && 'checked="1"' || ''
});
} else {
return templates.form_input({
name: $field.attr('var'),
type: XFORM_TYPE_MAP[$field.attr('type')],
label: $field.attr('label') || '',
value: $field.find('value').text()
});
}
}
};
return utils;