diff --git a/app/controllers/public/chat_controller.rb b/app/controllers/public/chat_controller.rb index 2d835bc..a88051b 100644 --- a/app/controllers/public/chat_controller.rb +++ b/app/controllers/public/chat_controller.rb @@ -2,6 +2,7 @@ class ChatController < PublicController before_filter :login_required before_filter :check_environment_feature + before_filter :can_send_message, :only => :register_message def start_session login = user.jid @@ -54,6 +55,16 @@ class ChatController < PublicController end end + def avatars + profiles = environment.profiles.where(:identifier => params[:profiles]) + avatar_map = profiles.inject({}) do |result, profile| + result[profile.identifier] = profile_icon(profile, :minor) + result + end + + render_json avatar_map + end + def update_presence_status if request.xhr? current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {})) @@ -62,11 +73,17 @@ class ChatController < PublicController end def save_message - to = environment.profiles.find_by_identifier(params[:to]) - body = params[:body] - - ChatMessage.create!(:to => to, :from => user, :body => body) - render :text => 'ok' + if request.post? + to = environment.profiles.where(:identifier => params[:to]).first + body = params[:body] + + begin + ChatMessage.create!(:to => to, :from => user, :body => body) + return render_json({:status => 0}) + rescue Exception => exception + return render_json({:status => 3, :message => exception.to_s, :backtrace => exception.backtrace}) + end + end end def recent_messages @@ -90,8 +107,9 @@ class ChatController < PublicController end def recent_conversations - conversations_order = ActiveRecord::Base.connection.execute("select profiles.identifier from profiles inner join (select distinct r.id as id, MAX(r.created_at) as created_at from (select from_id, to_id, created_at, (case when from_id=#{user.id} then to_id else from_id end) as id from chat_messages where from_id=#{user.id} or to_id=#{user.id}) as r group by id order by created_at desc, id) as t on profiles.id=t.id order by t.created_at desc").entries.map {|e| e['identifier']} - render :json => {:order => conversations_order.reverse, :domain => environment.default_hostname.gsub('.','-')}.to_json + profiles = Profile.find_by_sql("select profiles.* from profiles inner join (select distinct r.id as id, MAX(r.created_at) as created_at from (select from_id, to_id, created_at, (case when from_id=#{user.id} then to_id else from_id end) as id from chat_messages where from_id=#{user.id} or to_id=#{user.id}) as r group by id order by created_at desc, id) as t on profiles.id=t.id order by t.created_at desc") + jids = profiles.map(&:jid).reverse + render :json => jids.to_json end #TODO Ideally this is done through roster table on ejabberd. @@ -108,4 +126,14 @@ class ChatController < PublicController end end + def can_send_message + return render_json({:status => 1, :message => 'Missing parameters!'}) if params[:from].nil? || params[:to].nil? || params[:message].nil? + return render_json({:status => 2, :message => 'You can not send message as another user!'}) if params[:from] != user.jid + # TODO Maybe register the jid in a table someday to avoid this below + return render_json({:status => 3, :messsage => 'You can not send messages to strangers!'}) if user.friends.where(:identifier => params[:to].split('@').first).blank? + end + + def render_json(result) + render :text => result.to_json + end end diff --git a/app/helpers/chat_helper.rb b/app/helpers/chat_helper.rb index a799852..584d3af 100644 --- a/app/helpers/chat_helper.rb +++ b/app/helpers/chat_helper.rb @@ -9,12 +9,12 @@ module ChatHelper avatar = profile_image(user, :portrait, :class => 'avatar') content_tag('span', link_to(avatar + content_tag('span', user.name) + ui_icon('ui-icon-triangle-1-s'), - '#', + '', :onclick => 'toggleMenu(this); return false', :class => icon_class + ' simplemenu-trigger' ) + content_tag('ul', - links.map{|link| content_tag('li', link_to(link[1], '#', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), + links.map{|link| content_tag('li', link_to(link[1], '', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), :style => 'display: none; z-index: 100', :class => 'simplemenu-submenu' ), diff --git a/app/models/chat_message.rb b/app/models/chat_message.rb index 6fecffb..af8caf8 100644 --- a/app/models/chat_message.rb +++ b/app/models/chat_message.rb @@ -3,5 +3,4 @@ class ChatMessage < ActiveRecord::Base belongs_to :to, :class_name => 'Profile' belongs_to :from, :class_name => 'Profile' - end diff --git a/app/views/blocks/profile_info_actions/_common.html.erb b/app/views/blocks/profile_info_actions/_common.html.erb new file mode 100644 index 0000000..ee8d800 --- /dev/null +++ b/app/views/blocks/profile_info_actions/_common.html.erb @@ -0,0 +1,2 @@ +
<%= ui_icon('ui-icon-alert') %> -<%= _('Could not connect to chat') %>, <%= _('try again') %>. +<%= _('Could not connect to chat') %>, <%= _('try again') %>.
diff --git a/app/views/shared/logged_in/xmpp_chat.html.erb b/app/views/shared/logged_in/xmpp_chat.html.erb index b0b0122..85a1c0a 100644 --- a/app/views/shared/logged_in/xmpp_chat.html.erb +++ b/app/views/shared/logged_in/xmpp_chat.html.erb @@ -7,13 +7,13 @@ var $own_name = '<%= user.name %>'; var $muc_domain = '<%= "conference.#{environment.default_hostname}" %>'; var $bosh_service = '//<%= environment.default_hostname %>/http-bind'; - var $user_unavailable_error = '<%= _("ooops! The message could not be sent because the user is not online") %>'; + var $user_unavailable_error = '<%= _("The user is not online now. He/She will receive these messages as soon as he/she gets online.") %>'; var $update_presence_status_every = <%= User.expires_chat_status_every.minutes %>; var $presence = '<%= current_user.last_chat_status %>'; -' + body + '
'); - } - else { - if (time==undefined) { - time = new Date().toISOString(); - } - var message_html = Jabber.template('.message') - .replace('%{message}', body) - .replace(/%{who}/g, who) - .replace('%{time}', time) - .replace('%{name}', name) - .replace('%{avatar}', getAvatar(identifier)); - offset_container.append(message_html); - $(".message span.time").timeago(); - } - if(offset == 0) history.scrollTo({top:'100%', left:'0%'}); - else history.scrollTo(offset_container.height()); - if (who != "self") { - if ($(tab_id).find('.history:visible').length == 0) { - count_unread_messages(jid_id); - } - document.alert_title = name; - } - } - }, - - show_status: function(presence) { - log('changing my status to ' + presence); - $('#buddy-list .user-status .simplemenu-trigger') - .removeClass('icon-menu-chat') - .removeClass('icon-menu-offline') - .removeClass('icon-menu-dnd') - .addClass('icon-menu-' + (presence || 'offline')); - $('#buddy-list #user-status img.avatar').replaceWith(getMyAvatar()); - $.get('/chat/update_presence_status', { status: {chat_status: presence, last_chat_status: presence} }); - }, - - send_availability_status: function(presence) { - log('send availability status ' + presence); - Jabber.connection.send($pres().c('show').t(presence).up()); - Jabber.show_status(presence); - }, - - enter_room: function(jid, push) { - if(push == undefined) - push = true - var jid_id = Jabber.jid_to_id(jid); - var conversation_id = Jabber.conversation_prefix + jid_id; - var button = $('#' + conversation_id + ' .join'); - button.hide(); - button.siblings('.leave').show(); - Jabber.connection.send( - $pres({to: jid + '/' + $own_name}).c('x', {xmlns: Strophe.NS.MUC}).c('history', {maxchars: 0}) - ); - Jabber.insert_or_update_group(jid, 'online'); + if (offset_container.find('.message:last').attr('data-who') == who) { + offset_container.find('.message:last .content').append('' + body + '
'); + } + else { + if (time==undefined) { + time = new Date().toISOString(); + } + var message_html = Jabber.template('.message') + .replace('%{message}', body) + .replace(/%{who}/g, who) + .replace('%{time}', time) + .replace('%{name}', name) + .replace('%{avatar}', getAvatar(identifier)); + offset_container.append(message_html); + $(".message span.time").timeago(); + } + if(offset == 0) history.scrollTo({top:'100%', left:'0%'}); + else history.scrollTo(offset_container.height()); + if (who != "self") { + if ($(tab_id).find('.history:visible').length == 0) { + count_unread_messages(jid_id); + } + document.alert_title = name; + } + } + }, + + show_status: function(presence) { + log('changing my status to ' + presence); + $('#buddy-list .user-status .simplemenu-trigger') + .removeClass('icon-menu-chat') + .removeClass('icon-menu-offline') + .removeClass('icon-menu-dnd') + .addClass('icon-menu-' + (presence || 'offline')); + $('#buddy-list #user-status img.avatar').replaceWith(getMyAvatar()); + $.get('/chat/update_presence_status', { status: {chat_status: presence, last_chat_status: presence} }); + }, + + send_availability_status: function(presence) { + log('send availability status ' + presence); + Jabber.connection.send($pres().c('show').t(presence).up()); + Jabber.show_status(presence); + }, + + enter_room: function(jid, push) { + if(push == undefined) + push = true + var jid_id = Jabber.jid_to_id(jid); + var conversation_id = Jabber.conversation_prefix + jid_id; + var button = $('#' + conversation_id + ' .join'); + button.hide(); + button.siblings('.leave').show(); + Jabber.connection.send( + $pres({to: jid + '/' + $own_name}).c('x', {xmlns: Strophe.NS.MUC}).c('history', {maxchars: 0}) + ); + Jabber.insert_or_update_group(jid, 'online'); + Jabber.update_chat_title(); + sort_conversations(); + if(push) + $.post('/chat/join', {room_id: jid}); + }, + + leave_room: function(jid, push) { + if(push == undefined) + push = true + var jid_id = Jabber.jid_to_id(jid); + var conversation_id = Jabber.conversation_prefix + jid_id; + var button = $('#' + conversation_id + ' .leave'); + button.hide(); + button.siblings('.join').show(); + Jabber.connection.send($pres({from: Jabber.connection.jid, to: jid + '/' + $own_name, type: 'unavailable'})) + Jabber.insert_or_update_group(jid, 'offline'); + sort_conversations(); + if(push) + $.post('/chat/leave', {room_id: jid}); + }, + + update_chat_title: function () { + var friends_online = $('#buddy-list #friends .buddy-list.online li').length; + $('#friends-online').text(friends_online); + var friends_offline = $('#buddy-list #friends .buddy-list.offline li').length; + $('#friends-offline').text(friends_offline); + var groups_online = $('#buddy-list #rooms .buddy-list li').length; + $('#groups-online').text(groups_online); + }, + + on_connect: function (status) { + switch (status) { + case Strophe.Status.CONNECTING: + log('connecting...'); + break; + case Strophe.Status.CONNFAIL: + log('failed to connect'); + setTimeout(function(){Jabber.connect()}, 10000); + break; + case Strophe.Status.DISCONNECTING: + log('disconnecting...'); + $('#buddy-list .toolbar').addClass('small-loading-dark'); + break; + case Strophe.Status.DISCONNECTED: + log('disconnected'); + $('#buddy-list ul.buddy-list, .occupants ul.occupant-list').html(''); Jabber.update_chat_title(); - sort_conversations(); - if(push) - $.post('/chat/join', {room_id: jid}); - }, - - leave_room: function(jid, push) { - if(push == undefined) - push = true + $('#buddy-list .toolbar').removeClass('small-loading-dark'); + $('textarea').prop('disabled', 'disabled'); + if(Jabber.presence_status != 'offline') + Jabber.connect(); + break; + case Strophe.Status.CONNECTED: + log('connected'); + case Strophe.Status.ATTACHED: + log('XMPP/BOSH session attached'); + $('#buddy-list .toolbar').removeClass('small-loading-dark'); + $('textarea').prop('disabled', ''); + break; + } + }, + + on_roster: function (iq) { + log('receiving roster'); + var profiles = []; + var contacts_to_insert = {}; + var groups_to_insert = []; + + $(iq).find('item').each(function () { + var jid = $(this).attr('jid'); + profiles.push(getIdentifier(jid)); + var name = $(this).attr('name') || jid; var jid_id = Jabber.jid_to_id(jid); - var conversation_id = Jabber.conversation_prefix + jid_id; - var button = $('#' + conversation_id + ' .leave'); - button.hide(); - button.siblings('.join').show(); - Jabber.connection.send($pres({from: Jabber.connection.jid, to: jid + '/' + $own_name, type: 'unavailable'})) - Jabber.insert_or_update_group(jid, 'offline'); - sort_conversations(); - if(push) - $.post('/chat/leave', {room_id: jid}); - }, - - update_chat_title: function () { - var friends_online = $('#buddy-list #friends .buddy-list.online li').length; - $('#friends-online').text(friends_online); - var friends_offline = $('#buddy-list #friends .buddy-list.offline li').length; - $('#friends-offline').text(friends_offline); - var groups_online = $('#buddy-list #rooms .buddy-list li').length; - $('#groups-online').text(groups_online); - }, - - on_connect: function (status) { - switch (status) { - case Strophe.Status.CONNECTING: - log('connecting...'); - break; - case Strophe.Status.CONNFAIL: - log('failed to connect'); - break; - case Strophe.Status.DISCONNECTING: - log('disconnecting...'); - $('#buddy-list .toolbar').addClass('small-loading-dark'); - break; - case Strophe.Status.DISCONNECTED: - log('disconnected'); - $('#buddy-list ul.buddy-list, .occupants ul.occupant-list').html(''); - Jabber.update_chat_title(); - $('#buddy-list .toolbar').removeClass('small-loading-dark'); - $('textarea').prop('disabled', 'disabled'); - break; - case Strophe.Status.CONNECTED: - log('connected'); - case Strophe.Status.ATTACHED: - log('XMPP/BOSH session attached'); - $('#buddy-list .toolbar').removeClass('small-loading-dark'); - $('textarea').prop('disabled', ''); - break; - } - }, - - on_roster: function (iq) { - log('receiving roster'); - $(iq).find('item').each(function () { - var jid = $(this).attr('jid'); - var name = $(this).attr('name') || jid; - var jid_id = Jabber.jid_to_id(jid); - Jabber.insert_or_update_contact(jid, name); - }); - //TODO Add groups through roster too... - $.ajax({ - url: '/chat/roster_groups', - dataType: 'json', - success: function(data){ - data.each(function(room){ - var jid_id = Jabber.jid_to_id(room.jid); - Jabber.jids[jid_id] = {jid: room.jid, name: room.name, type: 'groupchat'}; - //FIXME This must check on session if the user is inside the room... - Jabber.insert_or_update_group(room.jid, 'offline'); + contacts_to_insert[jid] = name; + }); + + //TODO Add groups through roster too... + $.ajax({ + url: '/chat/roster_groups', + dataType: 'json', + success: function(data){ + $(data).each(function(index, room){ + profiles.push(getIdentifier(room.jid)); + var jid_id = Jabber.jid_to_id(room.jid); + Jabber.jids[jid_id] = {jid: room.jid, name: room.name, type: 'groupchat'}; + //FIXME This must check on session if the user is inside the room... + groups_to_insert.push(room.jid); + + }); + $.getJSON('/chat/avatars', {profiles: profiles}, function(data) { + for(identifier in data) + Jabber.avatars[identifier] = data[identifier]; + + // Insert contacts + for(contact_jid in contacts_to_insert) + Jabber.insert_or_update_contact(contact_jid, contacts_to_insert[contact_jid]); + + // Insert groups + for (var i = 0; i < groups_to_insert.length; i++) + Jabber.insert_or_update_group(groups_to_insert[i], 'offline'); + + $.getJSON('/chat/recent_conversations', {}, function(data) { + Jabber.conversations_order = data; + sort_conversations(); }); - }, - error: function(data, textStatus, jqXHR){ - console.log(data); - }, - }); - sort_conversations(); - // set up presence handler and send initial presence - Jabber.connection.addHandler(Jabber.on_presence, null, "presence"); - Jabber.send_availability_status(Jabber.presence_status); - load_defaults(); - }, - - // NOTE: cause Noosfero store's rosters in database based on friendship relation between people - // these event never occurs cause jabber service (ejabberd) didn't know when a roster was changed - on_roster_changed: function (iq) { - log('roster changed'); - $(iq).find('item').each(function () { - var sub = $(this).attr('subscription'); - var jid = $(this).attr('jid'); - var name = $(this).attr('name') || jid; - if (sub == 'remove') { - // contact is being removed - Jabber.remove_contact(jid); - } else { - // contact is being added or modified - Jabber.insert_or_update_contact(jid, name); - } - }); - return true; - }, - - parse: function (stanza) { - var result = {}; - if (Strophe.isTagEqual(stanza, 'presence')) { - result.from = $(stanza).attr('from'); - result.type = $(stanza).attr('type'); - if (result.type == 'unavailable') { - result.show = 'offline'; - } else { - var show = $(stanza).find("show").text(); - if (show === "" || show == "chat") { - result.show = 'chat'; - } - else if (show == "dnd" || show == "xa") { - result.show = 'dnd'; - } - else { - result.show = 'away'; - } - } - if ($(stanza).find('x[xmlns="'+ Strophe.NS.MUC_USER +'"]').length > 0) { - result.is_from_room = true; - result.from_user = $(stanza).find('x item').attr('jid'); - if ($(stanza).find('x item').attr('affiliation') == 'owner') { - result.awaiting_configuration = ($(stanza).find('x status').attr('code') == '201'); - } - } + + // set up presence handler and send initial presence + Jabber.connection.addHandler(Jabber.on_presence, null, "presence"); + Jabber.send_availability_status(Jabber.presence_status); + load_defaults(); + }); + }, + error: function(data, textStatus, jqXHR){ + console.log(data); + }, + }); + + }, + + // NOTE: cause Noosfero store's rosters in database based on friendship relation between people + // these event never occurs cause jabber service (ejabberd) didn't know when a roster was changed + on_roster_changed: function (iq) { + log('roster changed'); + $(iq).find('item').each(function () { + var sub = $(this).attr('subscription'); + var jid = $(this).attr('jid'); + var name = $(this).attr('name') || jid; + if (sub == 'remove') { + // contact is being removed + Jabber.remove_contact(jid); + } else { + // contact is being added or modified + Jabber.insert_or_update_contact(jid, name); } - else if (Strophe.isTagEqual(stanza, 'message')) { - result.from = $(stanza).attr('from'); - result.body = $(stanza).find('body').text(); - if ($(stanza).find('error').length > 0) { - result.error = $(stanza).find('error text').text(); - if (!result.error && $(stanza).find('error').find('service-unavailable').length > 0) { - result.error = $user_unavailable_error; - } - } + }); + return true; + }, + + parse: function (stanza) { + var result = {}; + if (Strophe.isTagEqual(stanza, 'presence')) { + result.from = $(stanza).attr('from'); + result.type = $(stanza).attr('type'); + if (result.type == 'unavailable') { + result.show = 'offline'; + } else { + var show = $(stanza).find("show").text(); + if (show === "" || show == "chat") { + result.show = 'chat'; + } + else if (show == "dnd" || show == "xa") { + result.show = 'dnd'; + } + else { + result.show = 'away'; + } } - return result; - }, - - on_presence: function (presence) { - presence = Jabber.parse(presence); - if (presence.type != 'error') { - if (presence.is_from_room) { - log('receiving room presence from ' + presence.from + ' as ' + presence.show); - var name = Strophe.getResourceFromJid(presence.from); - if (presence.from_user) { - Jabber.insert_or_update_occupant(presence.from_user, name, presence.show, presence.from); - } - else { - log('ooops! user jid not found in presence stanza'); - } - if (presence.awaiting_configuration) { - log('sending instant room configuration to ' + Strophe.getBareJidFromJid(presence.from)); - Jabber.connection.sendIQ( - $iq({type: 'set', to: Strophe.getBareJidFromJid(presence.from)}) - .c('query', {xmlns: Strophe.NS.MUC_OWNER}) - .c('x', {xmlns: Strophe.NS.DATA_FORMS, type: 'submit'}) - ); - } - } - else { - log('receiving contact presence from ' + presence.from + ' as ' + presence.show); - var jid = Strophe.getBareJidFromJid(presence.from); - if (jid != Jabber.connection.jid) { - var name = Jabber.name_of(Jabber.jid_to_id(jid)); - Jabber.insert_or_update_contact(jid, name, presence.show); - Jabber.update_chat_title(); - } - else { - // why server sends presence from myself to me? - log('ignoring presence from myself'); - if(presence.show=='offline') { - console.log(Jabber.presence_status); - Jabber.send_availability_status(Jabber.presence_status); - } - } - } + if ($(stanza).find('x[xmlns="'+ Strophe.NS.MUC_USER +'"]').length > 0) { + result.is_from_room = true; + result.from_user = $(stanza).find('x item').attr('jid'); + if ($(stanza).find('x item').attr('affiliation') == 'owner') { + result.awaiting_configuration = ($(stanza).find('x status').attr('code') == '201'); + } } - return true; - }, - - on_private_message: function (message) { - message = Jabber.parse(message); - log('receiving message from ' + message.from); - var jid = Strophe.getBareJidFromJid(message.from); - var jid_id = Jabber.jid_to_id(jid); - var name = Jabber.name_of(jid_id); - create_conversation_tab(name, jid_id); - Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid)); - notifyMessage(message); - return true; - }, - - on_public_message: function (message) { - message = Jabber.parse(message); - log('receiving message from ' + message.from); - var name = Strophe.getResourceFromJid(message.from); - // is a message from the room itself - if (! name) { - Jabber.show_notice(Jabber.jid_to_id(message.from), message.body); - } - // is a message from another user, not mine - else if ($own_name != name) { - var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name]; - Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid)); + } + else if (Strophe.isTagEqual(stanza, 'message')) { + result.from = $(stanza).attr('from'); + result.body = $(stanza).find('body').text(); + if ($(stanza).find('error').length > 0) { + result.error = $(stanza).find('error text').text(); + if (!result.error && $(stanza).find('error').find('service-unavailable').length > 0) { + result.error = $user_unavailable_error; + } } - notifyMessage(message); - return true; - }, - - on_message_error: function (message) { - message = Jabber.parse(message) - var jid = Strophe.getBareJidFromJid(message.from); - log('Receiving error message from ' + jid); - var body = Jabber.template('.error-message').replace('%{text}', message.error); - Jabber.show_message(jid, Jabber.name_of(Jabber.jid_to_id(jid)), body, 'other', Strophe.getNodeFromJid(jid)); - return true; - }, - - on_muc_support: function(iq) { - if ($(iq).find('identity[category=conference]').length > 0 && $(iq).find('feature[var="'+ Strophe.NS.MUC +'"]').length > 0) { - var name = $(iq).find('identity[category=conference]').attr('name'); - log('muc support found with identity '+ name); - Jabber.muc_supported = true; + } + return result; + }, + + on_presence: function (presence) { + presence = Jabber.parse(presence); + if (presence.type != 'error') { + if (presence.is_from_room) { + log('receiving room presence from ' + presence.from + ' as ' + presence.show); + var name = Strophe.getResourceFromJid(presence.from); + if (presence.from_user) { + Jabber.insert_or_update_occupant(presence.from_user, name, presence.show, presence.from); + } + else { + log('ooops! user jid not found in presence stanza'); + } + if (presence.awaiting_configuration) { + log('sending instant room configuration to ' + Strophe.getBareJidFromJid(presence.from)); + Jabber.connection.sendIQ( + $iq({type: 'set', to: Strophe.getBareJidFromJid(presence.from)}) + .c('query', {xmlns: Strophe.NS.MUC_OWNER}) + .c('x', {xmlns: Strophe.NS.DATA_FORMS, type: 'submit'}) + ); + } } else { - log('muc support not found'); + log('receiving contact presence from ' + presence.from + ' as ' + presence.show); + var jid = Strophe.getBareJidFromJid(presence.from); + if (jid != Jabber.connection.jid) { + var jid_id = Jabber.jid_to_id(jid); + var name = Jabber.name_of(jid_id); + if(presence.show == 'chat') + Jabber.remove_notice(jid_id); + Jabber.insert_or_update_contact(jid, name, presence.show); + Jabber.update_chat_title(); + } + else { + // why server sends presence from myself to me? + log('ignoring presence from myself'); + if(presence.show=='offline') { + Jabber.send_availability_status(Jabber.presence_status); + } + } } - }, + } + return true; + }, - attach_connection: function(data) { - // create the connection and attach it - Jabber.connection = new Strophe.Connection(Jabber.bosh_service); - Jabber.connection.attach(data.jid, data.sid, data.rid, Jabber.on_connect); + on_private_message: function (message) { + message = Jabber.parse(message); + log('receiving message from ' + message.from); + var jid = Strophe.getBareJidFromJid(message.from); + var jid_id = Jabber.jid_to_id(jid); + var name = Jabber.name_of(jid_id); + create_conversation_tab(name, jid_id); + Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid)); + renew_conversation_order(jid); + notifyMessage(message); + return true; + }, + + on_public_message: function (message) { + message = Jabber.parse(message); + log('receiving message from ' + message.from); + var name = Strophe.getResourceFromJid(message.from); + // is a message from the room itself + if (! name) { + // FIXME Ignoring message from room for now. + // Jabber.show_notice(Jabber.jid_to_id(message.from), message.body); + } + // is a message from another user, not mine + else if ($own_name != name) { + var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name]; + Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid)); + renew_conversation_order(jid); + notifyMessage(message); + } + return true; + }, - // handle get roster list (buddy list) - Jabber.connection.sendIQ($iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}), Jabber.on_roster); + on_message_error: function (message) { + }, - // handle presence updates in roster list - Jabber.connection.addHandler(Jabber.on_roster_changed, 'jabber:iq:roster', 'iq', 'set'); + on_muc_support: function(iq) { + if ($(iq).find('identity[category=conference]').length > 0 && $(iq).find('feature[var="'+ Strophe.NS.MUC +'"]').length > 0) { + var name = $(iq).find('identity[category=conference]').attr('name'); + log('muc support found with identity '+ name); + Jabber.muc_supported = true; + } + else { + log('muc support not found'); + } + }, - // Handle messages - Jabber.connection.addHandler(Jabber.on_private_message, null, "message", "chat"); + attach_connection: function(data) { + // create the connection and attach it + Jabber.connection = new Strophe.Connection(Jabber.bosh_service); + Jabber.connection.attach(data.jid, data.sid, data.rid, Jabber.on_connect); - // Handle conference messages - Jabber.connection.addHandler(Jabber.on_public_message, null, "message", "groupchat"); + // handle get roster list (buddy list) + Jabber.connection.sendIQ($iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}), Jabber.on_roster); - // Handle message errors - Jabber.connection.addHandler(Jabber.on_message_error, null, "message", "error"); + // handle presence updates in roster list + Jabber.connection.addHandler(Jabber.on_roster_changed, 'jabber:iq:roster', 'iq', 'set'); - // discovering MUC support - Jabber.connection.sendIQ( - $iq({type: 'get', from: Jabber.connection.jid, to: Jabber.muc_domain}) - .c('query', {xmlns: Strophe.NS.DISCO_INFO}), - Jabber.on_muc_support - ); + // Handle messages + Jabber.connection.addHandler(Jabber.on_private_message, null, "message", "chat"); - // uncomment for extra debugging - //Strophe.log = function (lvl, msg) { log(msg); }; - }, + // Handle conference messages + Jabber.connection.addHandler(Jabber.on_public_message, null, "message", "groupchat"); - connect: function() { - if (Jabber.connection && Jabber.connection.connected) { - Jabber.send_availability_status(Jabber.presence_status); - } - else { - log('starting XMPP/BOSH session...'); - $('#buddy-list .toolbar').removeClass('small-loading-dark').addClass('small-loading-dark'); - $('.dialog-error').hide(); - $.ajax({ - url: '/chat/start_session', - dataType: 'json', - success: function(data) { - Jabber.attach_connection(data) - }, - error: function(error) { - $('#buddy-list .toolbar').removeClass('small-loading-dark'); - $('#buddy-list .dialog-error') - .html(error.responseText) - .show('highlight') - .unbind('click') - .click(function() { $(this).hide('highlight'); }); - } - }); - } - }, - - deliver_message: function(jid, body) { - var type = Jabber.type_of(Jabber.jid_to_id(jid)); - var message = $msg({to: jid, from: Jabber.connection.jid, "type": type}) - .c('body').t(body).up() - .c('active', {xmlns: Strophe.NS.CHAT_STATES}); - Jabber.connection.send(message); - Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); - move_conversation_to_the_top(jid); - }, - - is_a_room: function(jid_id) { - return Jabber.type_of(jid_id) == 'groupchat'; - }, - - show_notice: function(jid_id, msg) { - var tab_id = '#' + Jabber.conversation_prefix + jid_id; - var notice = $(tab_id).find('.history .notice'); - if (notice.length > 0) - notice.html(msg) - else - $(tab_id).find('.history').append("" + msg + ""); - } - }; - - $('#chat-connect').live('click', function() { - Jabber.presence_status = 'chat'; - Jabber.connect(); - }); - - $('#chat-disconnect').click(function() { - disconnect(); - }); - - $('#chat-busy').click(function() { - Jabber.presence_status = 'dnd'; - Jabber.connect(); - }); - - $('#chat-retry').live('click', function() { - Jabber.presence_status = Jabber.presence_status || 'chat'; - Jabber.connect(); - }); - - $('.conversation textarea').live('keydown', function(e) { - if (e.keyCode == 13) { - var jid = $(this).attr('data-to'); - var body = $(this).val(); - body = body.stripScripts(); - save_message(jid, body); - Jabber.deliver_message(jid, body); - $(this).val(''); - return false; - } - }); - - function save_message(jid, body) { - $.post('/chat/save_message', { - to: getIdentifier(jid), - body: body - }); - } + // Handle message errors + Jabber.connection.addHandler(Jabber.on_message_error, null, "message", "error"); - // open new conversation or change to already opened tab - $('#buddy-list .buddies li a').live('click', function() { - var jid_id = $(this).attr('id'); - var name = Jabber.name_of(jid_id); - var conversation = create_conversation_tab(name, jid_id); - - $('.conversation').hide(); - conversation.show(); - count_unread_messages(jid_id, true); - if(conversation.find('.chat-offset-container-0').length == 0) - recent_messages(Jabber.jid_of(jid_id)); - conversation.find('.conversation .input-div textarea.input').focus(); - $.post('/chat/tab', {tab_id: jid_id}); - }); - - // put name into text area when click in one occupant - $('.occupants .occupant-list li a').live('click', function() { - var jid_id = $(this).attr('data-id'); - var name = Jabber.name_of(jid_id); - var val = $('.conversation textarea:visible').val(); - $('.conversation textarea:visible').focus().val(val + name + ', '); - }); + // discovering MUC support + Jabber.connection.sendIQ( + $iq({type: 'get', from: Jabber.connection.jid, to: Jabber.muc_domain}) + .c('query', {xmlns: Strophe.NS.DISCO_INFO}), + Jabber.on_muc_support + ); - $('#chat .conversation .history').live('click', function() { - $('.conversation textarea:visible').focus(); - }); + // uncomment for extra debugging + //Strophe.log = function (lvl, msg) { log(msg); }; + }, - function toggle_chat_window() { - if(jQuery('#conversations .conversation').length == 0) jQuery('.buddies a').first().click(); - jQuery('#chat').toggleClass('opened'); - jQuery('#chat-label').toggleClass('opened'); - } + connect: function() { + if (Notification.permission !== "granted" && Notification.permission !== "denied") { + Notification.requestPermission(function (permission) { + if (!('permission' in Notification)) { + Notification.permission = permission; + } + }); + } - function load_conversation(jid) { - var jid_id = Jabber.jid_to_id(jid); - var name = Jabber.name_of(jid_id); - if (jid) { - if (Strophe.getDomainFromJid(jid) == Jabber.muc_domain) { - if (Jabber.muc_supported) { - log('opening groupchat with ' + jid); - Jabber.jids[jid_id] = {jid: jid, name: name, type: 'groupchat'}; - var conversation = create_conversation_tab(name, jid_id); - Jabber.enter_room(jid); - recent_messages(jid); - return conversation; - } - } - else { - log('opening chat with ' + jid); - Jabber.jids[jid_id] = {jid: jid, name: name, type: 'friendchat'}; - var conversation = create_conversation_tab(name, jid_id); - recent_messages(jid); - return conversation; - } + if (Jabber.connection && Jabber.connection.connected) { + Jabber.send_availability_status(Jabber.presence_status); } - } - - function open_conversation(jid) { - var conversation = load_conversation(jid); - var jid_id = $(this).attr('id'); - - $('.conversation').hide(); - conversation.show(); - count_unread_messages(jid_id, true); - if(conversation.find('.chat-offset-container-0').length == 0) - recent_messages(Jabber.jid_of(jid_id)); - conversation.find('.input').focus(); - $('#chat').addClass('opened'); - $('#chat-label').addClass('opened'); - $.post('/chat/tab', {tab_id: jid_id}); - } - - function create_conversation_tab(title, jid_id) { - var conversation_id = Jabber.conversation_prefix + jid_id; - var conversation = $('#' + conversation_id); - if (conversation.length > 0) { - return conversation; + else { + log('starting XMPP/BOSH session...'); + $('#buddy-list .toolbar').removeClass('small-loading-dark').addClass('small-loading-dark'); + $('.dialog-error').hide(); + $.ajax({ + url: '/chat/start_session', + dataType: 'json', + success: function(data) { + Jabber.attach_connection(data) + }, + error: function(error) { + $('#buddy-list .toolbar').removeClass('small-loading-dark'); + $('#buddy-list .dialog-error') + .html(error.responseText) + .show('highlight') + .unbind('click') + .click(function() { $(this).hide('highlight'); }); + } + }); } + }, - var jid = Jabber.jid_of(jid_id); - var identifier = getIdentifier(jid); + deliver_message: function(jid, body) { + var jid_id = Jabber.jid_to_id(jid); + var type = Jabber.type_of(jid_id); + var presence = Jabber.presence_of(jid_id); + var message = $msg({to: jid, from: Jabber.connection.jid, "type": type}) + .c('body').t(body).up() + .c('active', {xmlns: Strophe.NS.CHAT_STATES}); + Jabber.connection.send(message); + Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); + save_message(jid, body); + renew_conversation_order(jid); + move_conversation_to_the_top(jid); + if (presence == 'offline') + Jabber.show_notice(jid_id, $user_unavailable_error); + }, + + is_a_room: function(jid_id) { + return Jabber.type_of(jid_id) == 'groupchat'; + }, + + show_notice: function(jid_id, msg) { + var tab_id = '#' + Jabber.conversation_prefix + jid_id; + var history = $(tab_id).find('.history'); + var notice = $(tab_id).find('.history .notice'); + if (notice.length > 0) + notice.html(msg) + else + $(tab_id).find('.history').append("" + msg + ""); + history.scrollTo({top:'100%', left:'0%'}); + }, + + remove_notice: function(jid_id) { + var tab_id = '#' + Jabber.conversation_prefix + jid_id; + var notice = $(tab_id).find('.history .notice').remove(); + }, + }; - var panel = $('#chat-templates .conversation').clone().appendTo($conversations).attr('id', conversation_id); - panel.find('.chat-target .avatar').replaceWith(getAvatar(identifier)); - panel.find('.chat-target .other-name').html(title); - $('#chat .history').perfectScrollbar(); + $('#chat-connect').live('click', function() { + Jabber.presence_status = 'chat'; + Jabber.connect(); + $('#chat .simplemenu-submenu').hide(); + return false; + }); - panel.find('.history').scroll(function(){ - if($(this).scrollTop() == 0){ - var offset = panel.find('.message p').size(); - recent_messages(jid, offset); - } - }); + $('#chat-disconnect').click(function() { + disconnect(); + $('#chat .simplemenu-submenu').hide(); + return false; + }); + + $('#chat-busy').click(function() { + Jabber.presence_status = 'dnd'; + Jabber.connect(); + $('#chat .simplemenu-submenu').hide(); + return false; + }); - var textarea = panel.find('textarea'); - textarea.attr('name', panel.id); + $('#chat-retry').live('click', function() { + Jabber.presence_status = Jabber.presence_status || 'chat'; + Jabber.connect(); + return false; + }); + + $('.conversation textarea').live('keydown', function(e) { + if (e.keyCode == 13) { + var jid = $(this).attr('data-to'); + var body = $(this).val(); + body = $('