diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index afd8c77c7..ec893c470 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -30,6 +30,7 @@ require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" require "jam_ruby/lib/profanity" require "jam_ruby/lib/em_helper.rb" +require "jam_ruby/lib/nav.rb" require "jam_ruby/resque/audiomixer" require "jam_ruby/resque/icecast_config_writer" require "jam_ruby/resque/resque_hooks" diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb index 4b196e569..0340fdf04 100644 --- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb @@ -89,10 +89,11 @@ end #################################### NOTIFICATION EMAILS #################################### - def friend_request(email, msg) + def friend_request(email, msg, friend_request_id) subject = "You have a new friend request on JamKazam" unique_args = {:type => "friend_request"} - + + @url = Nav.accept_friend_request_dialog(friend_request_id) @body = msg sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] @@ -239,7 +240,7 @@ unique_args = {:type => "text_message"} @note = message - @root_url = APP_CONFIG.external_root_url + @url = Nav.home(dialog: 'text-message', dialog_opts: {d1: sender_id}) @sender_id = sender_id @sender_name = sender_name @sender_photo_url = sender_photo_url diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.html.erb index ccfdaf529..81de23226 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.html.erb @@ -1,3 +1,5 @@ <% provide(:title, 'New JamKazam Friend Request') %> -
<%= @body %>
\ No newline at end of file +<%= @body %>
+ +To accept this friend request, click here.
\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.text.erb index 2f21cf84a..0f21a3033 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/friend_request.text.erb @@ -1 +1,3 @@ -<%= @body %> \ No newline at end of file +<%= @body %> + +To accept this friend request, click here: <%= @url %> \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.html.erb index 83329d84d..e18666bf3 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.html.erb @@ -4,5 +4,5 @@ <% content_for :note do %> <%= @note %> -To reply to this message, click here.
+To reply to this message, click here.
<% end %> \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.text.erb index 2df23f22b..d456e7191 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/text_message.text.erb @@ -1,3 +1,3 @@ <%= @sender_name %> says: <%= @note %> -To reply to this message, click here: <%= @root_url %>/client#/home/text-message/d1=<%= @sender_id %> \ No newline at end of file +To reply to this message, click here: <%= @url %> \ No newline at end of file diff --git a/ruby/lib/jam_ruby/lib/nav.rb b/ruby/lib/jam_ruby/lib/nav.rb new file mode 100644 index 000000000..56a7a09af --- /dev/null +++ b/ruby/lib/jam_ruby/lib/nav.rb @@ -0,0 +1,36 @@ +module JamRuby + + class Nav + + def self.home(options ={}) + "#{APP_CONFIG.external_root_url}/client#/home#{dialog(options)}" + end + + def self.accept_friend_request_dialog(friend_request_id) + Nav.home(dialog: 'accept-friend-request', dialog_opts: {d1: friend_request_id}) + end + + private + + def self.dialog(options) + dialog = '' + if options[:dialog] + dialog = "/#{options[:dialog]}" + + if options[:dialog_opts] + dialog = dialog + '/' + + options[:dialog_opts].each do|key, value| + dialog = dialog + ERB::Util.url_encode(key) + '=' + ERB::Util.url_encode(value) + '&' + end + + if options[:dialog_opts].length > 0 + dialog = dialog[0..-2] # trim off trailing '&' + end + end + end + + dialog + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/friend_request.rb b/ruby/lib/jam_ruby/models/friend_request.rb index fa15e36e8..b2fe2f5ba 100644 --- a/ruby/lib/jam_ruby/models/friend_request.rb +++ b/ruby/lib/jam_ruby/models/friend_request.rb @@ -5,8 +5,8 @@ module JamRuby STATUS = %w(accept block spam ignore) - belongs_to :user, :class_name => "JamRuby::User" - belongs_to :friend, :class_name => "JamRuby::User" + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => 'user_id' + belongs_to :friend, :class_name => "JamRuby::User", :foreign_key => 'friend_id' validates :user_id, :presence => true validates :friend_id, :presence => true diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index ae3450c79..37180190d 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -240,10 +240,10 @@ module JamRuby ) @@mq_router.publish_to_user(friend_id, msg) - else - UserMailer.friend_request(friend.email, notification_msg).deliver + UserMailer.friend_request(friend.email, notification_msg, friend_request_id).deliver end + notification end def send_friend_request_accepted(user_id, friend_id) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 6b83dc815..93f73d829 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -27,7 +27,8 @@ module JamRuby has_many :connections, :class_name => "JamRuby::Connection" # friend requests - has_many :friend_requests, :class_name => "JamRuby::FriendRequest" + has_many :sent_friend_requests, :class_name => "JamRuby::FriendRequest", :foreign_key => 'user_id' + has_many :received_friend_requests, :class_name => "JamRuby::FriendRequest", :foreign_key => 'friend_id' # instruments has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 25c4f4849..0c32a5dbb 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -107,6 +107,10 @@ FactoryGirl.define do end + factory :friend_request, :class => JamRuby::FriendRequest do + + end + factory :band_musician, :class => JamRuby::BandMusician do end diff --git a/ruby/spec/jam_ruby/models/notification_spec.rb b/ruby/spec/jam_ruby/models/notification_spec.rb index bff5e5b02..1e6d26751 100644 --- a/ruby/spec/jam_ruby/models/notification_spec.rb +++ b/ruby/spec/jam_ruby/models/notification_spec.rb @@ -7,22 +7,53 @@ describe Notification do end + def count_publish_to_user_calls + result = {count: 0} + MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| + result[:count] += 1 + result[:msg] = msg + end + result + end + + describe "send friend request" do + + let(:receiver) {FactoryGirl.create(:user)} + let(:sender) {FactoryGirl.create(:user)} + let(:friend_request) {FactoryGirl.create(:friend_request, user:sender, friend:receiver)} + + it "success when offline" do + calls = count_publish_to_user_calls + notification = Notification.send_friend_request(friend_request.id, sender.id, receiver.id) + + notification.errors.any?.should be_false + UserMailer.deliveries.length.should == 1 + calls[:count].should == 0 + end + + + it "success when online" do + receiver_connection = FactoryGirl.create(:connection, user: receiver) + calls = count_publish_to_user_calls + notification = Notification.send_friend_request(friend_request.id, sender.id, receiver.id) + + notification.errors.any?.should be_false + UserMailer.deliveries.length.should == 0 + calls[:count].should == 1 + end + end + describe "send_text_message" do it "success when offline" do receiver = FactoryGirl.create(:user) sender = FactoryGirl.create(:user) message = "Just a test message!" - - called_count = 0 - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, receiver) notification.errors.any?.should be_false UserMailer.deliveries.length.should == 1 - called_count.should == 0 + calls[:count].should == 0 end @@ -30,108 +61,77 @@ describe Notification do receiver = FactoryGirl.create(:user) receiver_connection = FactoryGirl.create(:connection, user: receiver) sender = FactoryGirl.create(:user) - message = "Just a test message!" - - called_count = 0 - saved_msg = nil - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - saved_msg = msg - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, receiver) notification.errors.any?.should be_false UserMailer.deliveries.length.should == 0 - called_count.should == 1 - saved_msg.text_message.msg.should == message - saved_msg.text_message.photo_url.should == '' - saved_msg.text_message.sender_name.should == sender.name - saved_msg.text_message.notification_id.should == notification.id - saved_msg.text_message.created_at = notification.created_date - saved_msg.text_message.clipped_msg.should be_false + calls[:count].should == 1 + calls[:msg].text_message.msg.should == message + calls[:msg].text_message.photo_url.should == '' + calls[:msg].text_message.sender_name.should == sender.name + calls[:msg].text_message.notification_id.should == notification.id + calls[:msg].text_message.created_at = notification.created_date + calls[:msg].text_message.clipped_msg.should be_false end it "success when online with long message" do receiver = FactoryGirl.create(:user) receiver_connection = FactoryGirl.create(:connection, user: receiver) sender = FactoryGirl.create(:user) - message = "0" * 203 # 200 is clip size - - called_count = 0 - saved_msg = nil - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - saved_msg = msg - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, receiver) notification.errors.any?.should be_false UserMailer.deliveries.length.should == 0 - called_count.should == 1 - saved_msg.text_message.msg.should == "0" * 200 - saved_msg.text_message.photo_url.should == '' - saved_msg.text_message.sender_name.should == sender.name - saved_msg.text_message.notification_id.should == notification.id - saved_msg.text_message.created_at = notification.created_date - saved_msg.text_message.clipped_msg.should be_true + calls[:count].should == 1 + calls[:msg].text_message.msg.should == "0" * 200 + calls[:msg].text_message.photo_url.should == '' + calls[:msg].text_message.sender_name.should == sender.name + calls[:msg].text_message.notification_id.should == notification.id + calls[:msg].text_message.created_at = notification.created_date + calls[:msg].text_message.clipped_msg.should be_true end it "fails with profanity" do receiver = FactoryGirl.create(:user) sender = FactoryGirl.create(:user) message = "ass" - - called_count = 0 - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, receiver) notification.errors.any?.should be_true notification.errors[:message].should == ['cannot contain profanity'] UserMailer.deliveries.length.should == 0 - called_count.should == 0 + calls[:count].should == 0 end it "fails when target is same as receiver" do receiver = FactoryGirl.create(:user) sender = FactoryGirl.create(:user) message = "yo" - - called_count = 0 - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, sender) notification.errors.any?.should be_true notification.errors[:target_user].should == [ValidationMessages::DIFFERENT_SOURCE_TARGET] UserMailer.deliveries.length.should == 0 - called_count.should == 0 + calls[:count].should == 0 end it "fails when there is no message" do receiver = FactoryGirl.create(:user) sender = FactoryGirl.create(:user) message = '' - - called_count = 0 - MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg| - called_count += 1 - end - + calls = count_publish_to_user_calls notification = Notification.send_text_message(message, sender, receiver) notification.errors.any?.should be_true notification.errors[:message].should == ['is too short (minimum is 1 characters)'] UserMailer.deliveries.length.should == 0 - called_count.should == 0 + calls[:count].should == 0 end end end diff --git a/ruby/spec/mailers/render_emails_spec.rb b/ruby/spec/mailers/render_emails_spec.rb index 3d80b32cc..9e505d2d1 100644 --- a/ruby/spec/mailers/render_emails_spec.rb +++ b/ruby/spec/mailers/render_emails_spec.rb @@ -34,9 +34,11 @@ describe "RenderMailers", :slow => true do describe "has sending user" do let(:user2) { FactoryGirl.create(:user) } - it { @filename="text_message"; UserMailer.text_message(user.email, user2.id, user2.name, user2.resolved_photo_url, 'Get online!!').deliver } - end + let(:friend_request) {FactoryGirl.create(:friend_request, user:user, friend: user2)} + it { @filename="text_message"; UserMailer.text_message(user.email, user2.id, user2.name, user2.resolved_photo_url, 'Get online!!').deliver } + it { @filename="friend_request"; UserMailer.friend_request(user.email, 'So and so has sent you a friend request.', friend_request.id).deliver} + end end describe "InvitedUserMailer emails" do diff --git a/web/app/assets/javascripts/acceptFriendRequestDialog.js b/web/app/assets/javascripts/acceptFriendRequestDialog.js new file mode 100644 index 000000000..4706105ca --- /dev/null +++ b/web/app/assets/javascripts/acceptFriendRequestDialog.js @@ -0,0 +1,197 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.AcceptFriendRequestDialog = function(app) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var $dialog = null; + var $dialogContents = null; + var $notFriendsTemplate = null; + var $alreadyFriendsTemplate = null; + var $genericErrorTemplate = null; + var $alreadyProcessedTemplate = null; + var $acceptBtn = null; + var $closeBtn = null; + var $cancelBtn = null; + var $actionBtns = null; + var friendRequestId = null; + var user = null; + var sending = false; + var friendRequest = null; + + function reset() { + sending = false; + friendRequest = null; + $dialogContents.empty(); + $actionBtns.hide(); + $actionBtns.find('a').hide(); + $acceptBtn.text('ACCEPT'); + } + + function buildShowRequest() { + return {friend_request_id: friendRequestId}; + } + + function buildAcceptRequest() { + var message = {}; + + message['friend_request_id'] = friendRequest.id; + message['status'] = 'accept'; + + return message; + } + + function acceptRequest(e) { + + if(!sending) { + sending = true; + + $acceptBtn.text('ACCEPTING...') + + rest.acceptFriendRequest(buildAcceptRequest()) + .done(function() { + app.layout.closeDialog('accept-friend-request') + }) + .fail(function(jqXHR) { + app.notifyServerError(jqXHR, 'Unable to Accept Friend Request'); + }) + .always(function() { + sending = false; + $acceptBtn.text('ACCEPT'); + }) + + } + return false; + } + + + function modifyResponseWithUIData() { + friendRequest.friend.user_type = friendRequest.friend.musician ? 'musician' : 'fan' + friendRequest.user.user_type = friendRequest.user.musician ? 'musician' : 'fan' + friendRequest.friend.photo_url = context.JK.resolveAvatarUrl(friendRequest.friend.photo_url); + friendRequest.user.photo_url = context.JK.resolveAvatarUrl(friendRequest.user.photo_url); + } + + function renderNoActionPossibleBtns() { + $closeBtn.show(); + $actionBtns.show(); + } + + function renderDefaultBtns() { + $cancelBtn.show(); + $acceptBtn.show(); + $actionBtns.show(); + } + function renderAlreadyFriends(options) { + return $(context._.template($alreadyFriendsTemplate.html(), options, { variable: 'data' })); + } + + function renderAlreadyProcessed(options) { + return $(context._.template($alreadyProcessedTemplate.html(), options, { variable: 'data' })); + } + + function renderNotFriends(options) { + return $(context._.template($notFriendsTemplate.html(), options, { variable: 'data' })); + } + + function renderGenericError(options) { + return $(context._.template($genericErrorTemplate.html(), options, { variable: 'data' })); + } + + function beforeShow(args) { + + app.layout.closeDialog('accept-friend-request') // ensure no others are showing. this is a singleton dialog + + app.user() + .done(function(userDetail) { + user = userDetail; + + friendRequestId = args.d1; + + if(!friendRequestId) throw "friend request must be specified in AcceptFriendRequestDialog" + + rest.getFriendRequest(buildShowRequest()) + .done(function(response) { + friendRequest = response; + modifyResponseWithUIData(); + var options = friendRequest; + + var contents = null; + + if(friendRequest.user_id == user.id) { + contents = renderGenericError({error_message: 'You can\'t become friends with yourself.'}) + renderNoActionPossibleBtns(); + } + else if(friendRequest.user.is_friend) { + // already friends + contents = renderAlreadyFriends(options); + renderNoActionPossibleBtns(); + } + else if(friendRequest.status) { + contents = renderAlreadyProcessed(options); + renderNoActionPossibleBtns(); + } + else { + contents = renderNotFriends(options); + renderDefaultBtns(); + } + + $dialogContents.append(contents); + context.JK.bindHoverEvents(contents); + }) + .fail(function(jqXHR) { + if(jqXHR.status == 403) { + var contents = renderGenericError({error_message: 'You do not have permission to access this information.'}) + $dialogContents.append(contents); + context.JK.bindHoverEvents(contents); + } + else if(jqXHR.status == 404) { + var contents = renderGenericError({error_message: 'This friend request no longer exists.'}) + $dialogContents.append(contents); + context.JK.bindHoverEvents(contents); + } + else { + app.notifyServerError(jqXHR, 'Unable to Load Friend Request'); + } + renderNoActionPossibleBtns(); + }) + }) + } + + function events() { + $acceptBtn.click(acceptRequest); + } + + function afterHide() { + reset(); + } + + function initialize() { + var dialogBindings = { + 'beforeShow' : beforeShow, + 'afterHide': afterHide + }; + + + app.bindDialog('accept-friend-request', dialogBindings); + + $dialog = $('#accept-friend-request-dialog'); + $dialogContents = $dialog.find('.dialog-inner'); + $notFriendsTemplate = $('#template-friend-request-not-friends'); + $alreadyFriendsTemplate = $('#template-friend-request-already-friends'); + $alreadyProcessedTemplate = $('#template-friend-request-already-processed') + $genericErrorTemplate = $('#template-friend-generic-error'); + $acceptBtn = $dialog.find('.btn-accept-friend-request'); + $cancelBtn = $dialog.find('.btn-cancel-dialog'); + $closeBtn = $dialog.find('.btn-close-dialog'); + $actionBtns = $dialog.find('.action-buttons'); + + events(); + } + + this.initialize = initialize; + } + + return this; +})(window,jQuery); diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index c6b6468b5..2b19b5d44 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -655,9 +655,24 @@ }); } + function getFriendRequest(options) { + var id = getId(options); + var friendRequestId = options["friend_request_id"]; + + var deferred = $.ajax({ + type: "GET", + dataType: "json", + contentType: 'application/json', + url: "/api/users/" + id + "/friend_requests/" + friendRequestId, + processData: false + }); + + return deferred; + } + function acceptFriendRequest(options) { var id = getId(options); - var friend_request_id = options["friend_request_id"]; + var friendRequestId = options["friend_request_id"]; var status = options["status"]; var friend_request = { status: status }; @@ -666,7 +681,7 @@ type: "POST", dataType: "json", contentType: 'application/json', - url: "/api/users/" + id + "/friend_requests/" + friend_request_id, + url: "/api/users/" + id + "/friend_requests/" + friendRequestId, data: JSON.stringify(friend_request), processData: false }); @@ -972,6 +987,7 @@ this.getFeeds = getFeeds; this.serverHealthCheck = serverHealthCheck; this.sendFriendRequest = sendFriendRequest; + this.getFriendRequest = getFriendRequest; this.acceptFriendRequest = acceptFriendRequest; this.signout = signout; this.userDownloadedClient = userDownloadedClient; diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 08f681978..ea0a39710 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -305,6 +305,10 @@ logger.debug("Unprocessable entity sent from server:", errors) this.notify({title: title, text: $errors, icon_url: "/assets/content/icon_alert_big.png"}) } + else if(jqXHR.status == 403) { + logger.debug("permission error sent from server:", jqXHR.responseText) + this.notify({title: 'Permission Error', text: 'You do not have permission to access this information', icon_url: "/assets/content/icon_alert_big.png"}) + } else { if (jqXHR.responseText.indexOf('') == 0 || jqXHR.responseText.indexOf('