diff --git a/db/manifest b/db/manifest index fb64e8498..e51930a82 100755 --- a/db/manifest +++ b/db/manifest @@ -241,3 +241,5 @@ jam_track_available.sql active_jam_track.sql bpms_on_tap_in.sql jamtracks_job.sql +text_messages.sql +text_message_migration.sql \ No newline at end of file diff --git a/db/up/text_message_migration.sql b/db/up/text_message_migration.sql new file mode 100644 index 000000000..c4603c307 --- /dev/null +++ b/db/up/text_message_migration.sql @@ -0,0 +1,5 @@ +insert into text_messages(source_user_id, target_user_id, message, created_at, updated_at) ( + select source_user_id, target_user_id, message, created_at, updated_at + from notifications + where description='TEXT_MESSAGE' +); \ No newline at end of file diff --git a/db/up/text_messages.sql b/db/up/text_messages.sql new file mode 100644 index 000000000..4e8a10089 --- /dev/null +++ b/db/up/text_messages.sql @@ -0,0 +1,8 @@ +CREATE TABLE text_messages ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + source_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE, + target_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE, + message TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index d89371770..084ae7d9e 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -197,6 +197,7 @@ require "jam_ruby/models/jam_company" require "jam_ruby/models/user_sync" require "jam_ruby/models/video_source" require "jam_ruby/models/recorded_video" +require "jam_ruby/models/text_message" require "jam_ruby/jam_tracks_manager" include Jampb diff --git a/ruby/lib/jam_ruby/amqp/amqp_connection_manager.rb b/ruby/lib/jam_ruby/amqp/amqp_connection_manager.rb index 644449b64..863bc1172 100644 --- a/ruby/lib/jam_ruby/amqp/amqp_connection_manager.rb +++ b/ruby/lib/jam_ruby/amqp/amqp_connection_manager.rb @@ -64,7 +64,7 @@ module JamRuby def on_recovery(conn, settings) @connected = true - @log.debug "reconnected #{conn} #{settings}" + @log.warn "reconnected #{conn} #{settings}" #puts "#channel before #{@channel}" #puts "recovered channel: #{@channel.reuse}" diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 6feeb3f66..439aefe3a 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -11,13 +11,13 @@

Getting Started Video
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running: -https://www.youtube.com/watch?v=VexH4834o9I +https://www.youtube.com/watch?v=DBo--aj_P1w

Other Great Tutorial Videos
There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians’ sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here: -https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos +https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles

diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb index ef28cd06c..90efc88ef 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -3,12 +3,12 @@ Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> -- We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are links to some resources that can help to get you up and running quickly. Getting Started Video -We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running: -https://www.youtube.com/watch?v=VexH4834o9I +We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running: +https://www.youtube.com/watch?v=DBo--aj_P1w Other Great Tutorial Videos There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians’ sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here: -https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos +https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles Knowledge Base Articles You can find Getting Started knowledge base articles on things like frequently asked questions (FAQ), minimum system requirements for your Windows or Mac computer, how to troubleshoot audio problems in sessions, and more here: diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 135238a02..bcf06b4c5 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -333,6 +333,8 @@ SQL connection = Connection.find_by_client_id_and_user_id!(client_id, user.id) connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources) + + JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id) # connection.music_session_id = music_session.id # connection.as_musician = as_musician # connection.joining_session = true diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb index 6dd93acc8..c28ae8de2 100644 --- a/ruby/lib/jam_ruby/models/active_music_session.rb +++ b/ruby/lib/jam_ruby/models/active_music_session.rb @@ -290,13 +290,8 @@ module JamRuby } ) - if (offset) - query = query.offset(offset) - end - - if (limit) - query = query.limit(limit) - end + query = query.offset(offset) if offset + query = query.limit(limit) if limit if as_musician query = query.where( diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 375e3806c..a29d0b2f9 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -426,14 +426,19 @@ module JamRuby # returns one user history per user, with instruments all crammed together, and with total duration def unique_user_histories + + # only get the active users if the session is in progress + user_filter = "music_sessions_user_history.session_removed_at is null" if self.session_removed_at.nil? + MusicSessionUserHistory - .joins(:user) - .select("STRING_AGG(instruments, '|') AS total_instruments, - SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration, - music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") - .group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") - .order("music_sessions_user_history.user_id") - .where(%Q{ music_sessions_user_history.music_session_id = '#{id}'}) + .joins(:user) + .select("STRING_AGG(instruments, '|') AS total_instruments, + SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration, + music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") + .group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") + .order("music_sessions_user_history.user_id") + .where(%Q{ music_sessions_user_history.music_session_id = '#{id}'}) + .where(user_filter) end def duration_minutes diff --git a/ruby/lib/jam_ruby/models/music_session_user_history.rb b/ruby/lib/jam_ruby/models/music_session_user_history.rb index f79e2e810..430c897a1 100644 --- a/ruby/lib/jam_ruby/models/music_session_user_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_user_history.rb @@ -56,6 +56,20 @@ module JamRuby (end_time - self.created_at) / 60.0 end + def self.join_music_session(user_id, session_id) + hist = self + .where(:user_id => user_id) + .where(:music_session_id => session_id) + .limit(1) + .first + hist.start_history if hist + end + + def start_history + self.session_removed_at = nil + self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent) + end + def self.removed_music_session(user_id, session_id) hist = self .where(:user_id => user_id) @@ -67,7 +81,6 @@ module JamRuby def end_history self.session_removed_at = Time.now if self.session_removed_at.nil? - self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent) end diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 5fa628f3a..e0ec6590b 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -42,7 +42,7 @@ module JamRuby # used for persisted notifications def formatted_msg # target_user, band, session, recording, invitation, join_request = nil - source_user, band = nil + source_user, band, session = nil unless self.source_user_id.nil? source_user = User.find(self.source_user_id) @@ -53,7 +53,12 @@ module JamRuby end unless self.session_id.nil? - session = MusicSession.find(self.session_id) + session = MusicSession.find_by_id(self.session_id) + + # remove all notifications related to this session if it's not found + if session.nil? + Notification.delete_all "(session_id = '#{session_id}')" + end end self.class.format_msg(self.description, {:user => source_user, :band => band, :session => session}) diff --git a/ruby/lib/jam_ruby/models/text_message.rb b/ruby/lib/jam_ruby/models/text_message.rb new file mode 100644 index 000000000..c4a3a3d07 --- /dev/null +++ b/ruby/lib/jam_ruby/models/text_message.rb @@ -0,0 +1,52 @@ +include Devise::Models + +module JamRuby + class TextMessage < ActiveRecord::Base + + self.primary_key = 'id' + + default_scope order('created_at DESC') + + attr_accessible :target_user_id, :source_user_id, :message + + belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id" + belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id" + + validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true + + def self.index(target_user_id, source_user_id, options = {}) + offset = options[:offset] + limit = options[:limit] + + # if not specified, default offset to 0 + offset ||= 0 + offset = offset.to_i + + # if not specified, default limit to 10 + limit ||= 10 + limit = limit.to_i + + TextMessage + .offset(offset) + .limit(limit) + .where('(source_user_id = (?) AND target_user_id = (?)) OR (source_user_id = (?) AND target_user_id = (?))', source_user_id, target_user_id, target_user_id, source_user_id) + + end + + def self.create(message, target_user_id, source_user_id) + sanitized_text = Sanitize.fragment(message, elements: HtmlSanitize::SAFE) + + # create new message + tm = TextMessage.new + tm.message = sanitized_text + tm.target_user_id = target_user_id + tm.source_user_id = source_user_id + tm.save + + # send notification + @notification = Notification.send_text_message(sanitized_text, User.find(source_user_id), User.find(target_user_id)) + + tm + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index a5ea8b827..c59021903 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -25,6 +25,10 @@ module JamRuby MOD_NO_SHOW = "no_show" + # MIN/MAX AUDIO LATENCY + MINIMUM_AUDIO_LATENCY = 2 + MAXIMUM_AUDIO_LATENCY = 10000 + devise :database_authenticatable, :recoverable, :rememberable acts_as_mappable @@ -79,6 +83,9 @@ module JamRuby # self.id = followable_id in follows table has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy + # text messages + has_many :text_messages, :class_name => "JamRuby:TextMessage", :foreign_key => "target_user_id" + # notifications has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id" has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User" @@ -163,7 +170,7 @@ module JamRuby validates :musician, :inclusion => {:in => [true, false]} validates :show_whats_next, :inclusion => {:in => [nil, true, false]} validates :mods, json: true - validates_numericality_of :last_jam_audio_latency, greater_than:0, :allow_nil => true + validates_numericality_of :last_jam_audio_latency, greater_than:MINIMUM_AUDIO_LATENCY, less_than:MAXIMUM_AUDIO_LATENCY, :allow_nil => true validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN] } # custom validators @@ -1017,6 +1024,8 @@ module JamRuby user.save + user.errors.add("recaptcha", "verification failed") if recaptcha_failed + if user.errors.any? raise ActiveRecord::Rollback else @@ -1033,16 +1042,9 @@ module JamRuby UserMailer.confirm_email(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver end end - - if recaptcha_failed - user.errors.add "recaptcha", "verification failed" - raise ActiveRecord::Rollback - end - end - - return user - end + user + end # def signup # this is intended to be development-mode or test-mode only; VRFS-149 # it creates or updates one user per developer, so that we aren't in the business @@ -1379,8 +1381,9 @@ module JamRuby end def update_audio_latency(connection, audio_latency) - if audio_latency > 2 - # updating the connection is best effort + # the backend sometimes gives tiny numbers, and sometimes very large numbers + if audio_latency > MINIMUM_AUDIO_LATENCY && audio_latency < MAXIMUM_AUDIO_LATENCY + # updating the connection is best effort; if it's not there that's OK if connection Connection.where(:id => connection.id).update_all(:last_jam_audio_latency => audio_latency) end diff --git a/ruby/lib/jam_ruby/resque/resque_hooks.rb b/ruby/lib/jam_ruby/resque/resque_hooks.rb index fcd344bf6..e58298569 100644 --- a/ruby/lib/jam_ruby/resque/resque_hooks.rb +++ b/ruby/lib/jam_ruby/resque/resque_hooks.rb @@ -11,7 +11,10 @@ def shutdown end Resque.before_first_fork do - JamWebEventMachine.start + current = Thread.current + Thread.new do + JamWebEventMachine.run_em(current) + end #ActiveRecord::Base.establish_connection config = { @@ -41,6 +44,9 @@ Resque.before_fork do # ActiveRecord::Base.connection.disconnect! #JamRuby::Stats.destroy! + + # reconnect between jobs + ActiveRecord::Base.connection_handler.verify_active_connections! end Resque.after_fork do diff --git a/ruby/spec/jam_ruby/models/text_message.spec.rb b/ruby/spec/jam_ruby/models/text_message.spec.rb new file mode 100644 index 000000000..7b612b3ac --- /dev/null +++ b/ruby/spec/jam_ruby/models/text_message.spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe TextMessage do + + before do + TextMessage.delete_all + User.delete_all + @target_user = FactoryGirl.create(:user) + @source_user = FactoryGirl.create(:user) + + @msg = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id) + end + + describe "index" do + + it "should retrieve conversation for both users" do + @msg.message = "Test message" + @msg.save! + + messages = TextMessage.index(@target_user.id, @source_user.id) + messages.count.should == 1 + + messages = TextMessage.index(@source_user.id, @target_user.id) + messages.count.should == 1 + end + + it "should page records" do + 11.times do |n| + message = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id) + message.message = "Message #{n}" + message.save! + end + + messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 0}) + messages.count.should == 10 + + messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 10}) + messages.count.should == 1 + end + + it "should not allow empty message" do + expect { @msg.save! }.to raise_error(ActiveRecord::RecordInvalid) + end + + end +end \ No newline at end of file diff --git a/ruby/spec/jam_ruby/models/user_spec.rb b/ruby/spec/jam_ruby/models/user_spec.rb index 4fd3d2040..ec8b8440d 100644 --- a/ruby/spec/jam_ruby/models/user_spec.rb +++ b/ruby/spec/jam_ruby/models/user_spec.rb @@ -489,14 +489,14 @@ describe User do describe "audio latency" do it "allow update" do - @user.last_jam_audio_latency = 1 + @user.last_jam_audio_latency = 5 @user.save! end - it "prevent negative or 0" do - @user.last_jam_audio_latency = 0 + it "prevent negative" do + @user.last_jam_audio_latency = -1 @user.save.should be_false - @user.errors[:last_jam_audio_latency].should == ['must be greater than 0'] + @user.errors[:last_jam_audio_latency].should == ['must be greater than 2'] end it "prevent non numerical" do @@ -504,6 +504,42 @@ describe User do @user.save.should be_false @user.errors[:last_jam_audio_latency].should == ['is not a number'] end + + it "prevent 2 (minimum)" do + @user.last_jam_audio_latency = User::MINIMUM_AUDIO_LATENCY + @user.save.should be_false + @user.errors[:last_jam_audio_latency].should == ['must be greater than 2'] + end + + it "prevent 10000 (maximum)" do + @user.last_jam_audio_latency = User::MAXIMUM_AUDIO_LATENCY + @user.save.should be_false + @user.errors[:last_jam_audio_latency].should == ['must be less than 10000'] + end + end + + describe "update_audio_latency" do + + before(:each) do + @user.last_jam_audio_latency.should be_nil + end + + it "ignores low latency" do + @user.update_audio_latency(nil, User::MINIMUM_AUDIO_LATENCY) + @user.last_jam_audio_latency.should be_nil + end + + it "ignores high latency" do + @user.last_jam_audio_latency.should be_nil + @user.update_audio_latency(nil, User::MAXIMUM_AUDIO_LATENCY) + @user.last_jam_audio_latency.should be_nil + end + + it "accepts normal latency" do + @user.last_jam_audio_latency.should be_nil + @user.update_audio_latency(nil, User::MINIMUM_AUDIO_LATENCY + 1) + @user.last_jam_audio_latency.should == User::MINIMUM_AUDIO_LATENCY + 1 + end end describe "html_sanitize" do diff --git a/web/Gemfile b/web/Gemfile index eda57ee0a..e5c3f1583 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -65,7 +65,6 @@ gem 'postgres-copy' #end gem 'geokit-rails' gem 'postgres_ext' -gem 'recaptcha', '0.3.6' gem 'resque' gem 'resque-retry' gem 'resque-failed-job-mailer' diff --git a/web/app/assets/javascripts/accounts_session_detail.js b/web/app/assets/javascripts/accounts_session_detail.js index d75a5ea2e..82c028e52 100644 --- a/web/app/assets/javascripts/accounts_session_detail.js +++ b/web/app/assets/javascripts/accounts_session_detail.js @@ -339,7 +339,14 @@ if ("instrument_list" in pending_rsvp_request && pending_rsvp_request.instrument_list != null) { $.each(pending_rsvp_request.instrument_list, function (index, instrument) { - var instrumentId = context.JK.getInstrumentId(instrument.id); + var instrumentId; + + if (instrument) { + instrumentId = context.JK.getInstrumentId(instrument.id); + } + else { + instrumentId = 'other'; + } var inst = context.JK.getInstrumentIcon24(instrumentId); instrumentLogoHtml += ' '; instrumentDesc.push(instrumentId); @@ -378,7 +385,14 @@ $.each(sessionData.approved_rsvps, function(index, approved_rsvp) { if ("instrument_list" in approved_rsvp) { $.each(approved_rsvp.instrument_list, function(index, instrument) { - var instrumentId = context.JK.getInstrumentId(instrument.id); + var instrumentId; + + if (instrument) { + instrumentId = context.JK.getInstrumentId(instrument.id); + } + else { + instrumentId = 'other'; + } var inst = context.JK.getInstrumentIcon24(instrumentId); instrumentLogoHtml += ' '; }); diff --git a/web/app/assets/javascripts/dialog/sessionStartDialog.js b/web/app/assets/javascripts/dialog/sessionStartDialog.js new file mode 100644 index 000000000..971a5da65 --- /dev/null +++ b/web/app/assets/javascripts/dialog/sessionStartDialog.js @@ -0,0 +1,130 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.SessionStartDialog = function(app, session) { + var logger = context.JK.logger; + var sessionUtils = context.JK.SessionUtils; + var $dialog = null; + var dialogId = 'session-start-dialog'; + var $btnStartSession = null; + + function beforeShow(data) { + } + + function afterShow(data) { + } + + function afterHide() { + } + + function showDialog() { + return app.layout.showDialog(dialogId); + } + + function events() { + $btnStartSession.unbind('click'); + $btnStartSession.click(function(e) { + context.location = '/client#/session/' + session.id; + app.layout.closeDialog(dialogId); + }); + } + + function initializeSessionDetails() { + $dialog.find('#session-start-type-disp').html('Now!'); + $dialog.find('#session-name-disp').html(session.name); + $dialog.find('#session-description-disp').html(session.description); + + if (session.music_notations && session.music_notations.length > 0) { + $dialog.find('#session-notations-disp').html("Notations: " + session.music_notations.join(', ')); + } + + if (session.band) { + $dialog.find('#session-band-disp').html(band.name); + } + else { + $dialog.find('#session-band-disp').html('N/A'); + } + + $dialog.find('#session-language-disp').html(session.language_description); + + var invitedFriends = session.invitations; + + var sessionInvited = []; + $.each(invitedFriends, function(index, invitation) { + sessionInvited.push(invitation.receiver_name); + }); + + var sessionInvitedString = sessionInvited.join(', '); + + if (session.musician_access && session.approval_required) { + if (session.open_rsvps) { + if (invitedFriends.length == 0) + sessionInvitedString = "Any interested JamKazam musicians that I approve"; + else + sessionInvitedString += ", plus any interested JamKazam musicians that I approve"; + } + else { + if (invitedFriends.length == 0) { + sessionInvitedString = "No open RSVPs"; + } + else { + sessionInvitedString += " (No open RSVPs)"; + } + } + } + else if (session.musician_access && !session.approval_required) { + if (invitedFriends.length == 0) + sessionInvitedString = "Any interested JamKazam musicians who want to join us"; + else + sessionInvitedString += ", plus any interested JamKazam musicians who want to join us"; + } + + $dialog.find('#session-invited-disp').html(sessionInvitedString); + + var instrumentsMe = [], instrumentsOthers = []; + + $.each(session.approved_rsvps, function(index, rsvp) { + if (rsvp.id === context.JK.currentUserId) { + $.each(rsvp.instrument_list, function(index, instrument) { + instrumentsMe.push(instrument.desc); + }); + } + else { + $.each(rsvp.instrument_list, function(index, instrument) { + instrumentsOthers.push(instrument.desc); + }); + } + }); + + $dialog.find('#session-instruments-me-disp').html(instrumentsMe.join(', ')); + $dialog.find('#session-instruments-rsvp-disp').html(instrumentsOthers.join(', ')); + + $dialog.find('#session-musician-access-disp').html('Musicians: ' + session.musician_access_description); + $dialog.find('#session-fans-access-disp').html('Fans: ' + session.fan_access_description); + $dialog.find('#session-policy-disp').html(session.legal_policy); + } + + function initialize() { + + var dialogBindings = { + 'beforeShow' : beforeShow, + 'afterShow' : afterShow, + 'afterHide': afterHide + }; + + app.bindDialog(dialogId, dialogBindings); + + $dialog = $('[layout-id="' + dialogId + '"]'); + $btnStartSession = $dialog.find('.btnStartSession'); + + initializeSessionDetails(); + events(); + } + + this.initialize = initialize; + this.showDialog = showDialog; + } + + return this; +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/textMessageDialog.js b/web/app/assets/javascripts/dialog/textMessageDialog.js index 78107a7f9..d83f0efb5 100644 --- a/web/app/assets/javascripts/dialog/textMessageDialog.js +++ b/web/app/assets/javascripts/dialog/textMessageDialog.js @@ -37,14 +37,14 @@ } function buildParams() { - return { type: 'TEXT_MESSAGE', receiver: otherId, offset: offset, limit: LIMIT}; + return { target_user_id: otherId, offset: offset, limit: LIMIT}; } function buildMessage() { var message = {}; message['message'] = $textBox.val(); - message['receiver'] = otherId; + message['target_user_id'] = otherId; return message; } @@ -66,17 +66,17 @@ $sendTextMessage.text('SENDING...') rest.createTextMessage(buildMessage()) - .done(function() { - $textBox.val(''); - renderMessage(msg, user.id, user.name, new Date().toISOString(), true); - }) - .fail(function(jqXHR) { - app.notifyServerError(jqXHR, 'Unable to Send Message'); - }) - .always(function() { - sendingMessage = false; - $sendTextMessage.text('SEND'); - }) + .done(function() { + $textBox.val(''); + renderMessage(msg, user.id, user.name, new Date().toISOString(), true); + }) + .fail(function(jqXHR) { + app.notifyServerError(jqXHR, 'Unable to Send Message'); + }) + .always(function() { + sendingMessage = false; + $sendTextMessage.text('SEND'); + }); } return false; @@ -163,7 +163,7 @@ } $sendTextMessage.click(sendMessage); - rest.getNotifications(buildParams()) + rest.getTextMessages(buildParams()) .done(function (response) { context._.each(response, function (textMessage) { renderMessage(textMessage.message, textMessage.source_user_id, userLookup[textMessage.source_user_id].name, textMessage.created_at); diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 2278fd333..c02cee31c 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1262,13 +1262,24 @@ var id = getId(options); return $.ajax({ type: "POST", - url: '/api/users/' + id + '/notifications', + url: '/api/text_messages', dataType: "json", contentType: 'application/json', data: JSON.stringify(options) }); } + function getTextMessages(options) { + if(!options) options = {}; + var id = getId(options); + return $.ajax({ + type: "GET", + url: '/api/text_messages?' + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + } + function getNotifications(options) { if(!options) options = {}; var id = getId(options); @@ -1597,6 +1608,7 @@ this.tweet = tweet; this.createFbInviteUrl = createFbInviteUrl; this.createTextMessage = createTextMessage; + this.getTextMessages = getTextMessages; this.getNotifications = getNotifications; this.createChatMessage = createChatMessage; this.getChatMessages = getChatMessages; diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 41fa918c4..fdf0ac3ee 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -207,20 +207,25 @@ isLoading = true; // retrieve pending notifications for this user rest.getNotifications(buildParams()) - .done(function(response) { - updateNotificationList(response); - isLoading = false; - }) - .fail(function() { - isLoading = false; - app.ajaxError(); - }) + .done(function(response) { + updateNotificationList(response); + isLoading = false; + }) + .fail(function() { + isLoading = false; + app.ajaxError(); + }) } function updateNotificationList(response) { $.each(response, function(index, val) { - if(val.description == 'TEXT_MESSAGE') { + // this means the session no longer exists + if (response.fan_access == null && response.musician_access == null) { + return; + } + + if(val.description == context.JK.MessageType.TEXT_MESSAGE) { val.formatted_msg = textMessageDialog.formatTextMessage(val.message.substring(0, 200), val.source_user_id, val.source_user.name, val.message.length > 200).html(); } diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb index ebbd6a9b3..969f9449d 100644 --- a/web/app/assets/javascripts/scheduled_session.js.erb +++ b/web/app/assets/javascripts/scheduled_session.js.erb @@ -200,14 +200,14 @@ createSessionSettings.startDate = createSessionSettings.startDate || (new Date().toDateString()); - $("#session-start-date").val(createSessionSettings.startDate); + $screen.find("#session-start-date").val(createSessionSettings.startDate); toggleDate(true); toggleStartTime(); toggleStepStatus(); sessionUtils.defaultTimezone($timezoneList); if(firstTimeShown) { firstTimeShown = false; - $('#session-when-start-scheduled').iCheck('check'); + $screen.find('#session-when-start-scheduled').iCheck('check'); } } @@ -217,7 +217,7 @@ function beforeShowStep3() { rest.getBands(context.JK.currentUserId) .done(function(result) { - var options = $("#session-band-list"); + var options = $screen.find("#session-band-list"); options.empty(); options.append($("

"); + $screen.find('#session-name').after(""); isValid = false; } else { $('#divSessionName').removeClass("error"); } - var description = $('#session-description').val(); + var description = $screen.find('#session-description').val(); if (!description) { $('#divSessionDescription .error-text').remove(); $('#divSessionDescription').addClass("error"); - $('#session-description').after(""); + $screen.find('#session-description').after(""); isValid = false; } else { @@ -558,13 +558,13 @@ else createSessionSettings.band.label = $bandList.get(0).options[$bandList.get(0).selectedIndex].text; - createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked; + createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked; return true; } function beforeMoveStep4() { var isValid = true; - var sessionPolicyChecked = $('#session-policy-confirm').is(':checked'); + var sessionPolicyChecked = $screen.find('#session-policy-confirm').is(':checked'); if (!sessionPolicyChecked) { $('#divSessionPolicy .error-text').remove(); $('#divSessionPolicy').addClass("error"); @@ -576,11 +576,11 @@ } createSessionSettings.session_policy = $('input[name="session-policy-type"][checked="checked"]').attr('policy-id'); - var $musicianAccess = $('#session-musician-access'); + var $musicianAccess = $screen.find('#session-musician-access'); createSessionSettings.musician_access.value = $musicianAccess.val(); createSessionSettings.musician_access.label = $musicianAccess.get(0).options[$musicianAccess.get(0).selectedIndex].text; - var $fansAccess = $('#session-fans-access'); + var $fansAccess = $screen.find('#session-fans-access'); createSessionSettings.fans_access.value = $fansAccess.val(); createSessionSettings.fans_access.label = $fansAccess.get(0).options[$fansAccess.get(0).selectedIndex].text; @@ -975,7 +975,7 @@ } function toggleDate(dontRebuildDropdowns) { - var selectedDate = new Date($('#session-start-date').val()); + var selectedDate = new Date($screen.find('#session-start-date').val()); var currentDate = new Date(); var startIndex = 0; @@ -1046,7 +1046,7 @@ radioClass: 'iradio_minimal', inheritClass: true }); - $("#session-start-date").datepicker({ + $screen.find("#session-start-date").datepicker({ dateFormat: "D d MM yy", onSelect: function() { toggleDate(); } } diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index c1c41357d..96d1dc55a 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -836,7 +836,6 @@ var metronomeTrackMixers = []; var adhocTrackMixers = []; - console.log("_renderLocalMediaTracks", localMediaMixers) function groupByType(mixers) { context._.each(mixers, function(mixer) { var mediaType = mixer.media_type; @@ -1254,6 +1253,11 @@ if (mixer && oppositeMixer) { myTrack = (mixer.group_id === ChannelGroupIds.AudioInputMusicGroup); + if(!myTrack) { + // it only makes sense to track 'audio established' for tracks that don't belong to you + sessionModel.setAudioEstablished(participant.client_id, true); + } + var gainPercent = percentFromMixerValue( mixer.range_low, mixer.range_high, mixer.volume_left); var muteClass = "enabled"; @@ -1342,6 +1346,10 @@ var muteMixer = mixerData.muteMixer; if (mixer && oppositeMixer) { + if(!myTrack) { + // it only makes sense to track 'audio established' for tracks that don't belong to you + sessionModel.setAudioEstablished(clientId, true); + } var participant = (sessionModel.getParticipant(clientId) || {name:'unknown'}).name; logger.debug("found mixer=" + mixer.id + ", participant=" + participant) usedMixers[mixer.id] = true; @@ -1369,6 +1377,13 @@ $('.disabled-track-overlay', $track).show(); $('.track-connection', $track).removeClass('red yellow green').addClass('red'); } + // if 5 seconds have gone by and no mixer, then we tell the server failed to establish audio + else if(lookingForMixersCount == 10) { + if(!myTrack) { + // it only makes sense to track 'audio established' for tracks that don't belong to you + sessionModel.setAudioEstablished(clientId, false); + } + } var participant = (sessionModel.getParticipant(clientId) || { user: {name: 'unknown'}}).user.name; logger.debug("still looking for mixer for participant=" + participant + ", clientId=" + clientId) } diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 7cc62dfd9..5a02eb429 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -19,7 +19,7 @@ var $musicianTemplate = $('#template-musician-info'); var showJoinLink = true; var showListenLink = true; - var showRsvpLink = true; + var MAX_MINUTES_SHOW_START = 15; // related to listen function stateChange(e, data) { @@ -88,7 +88,6 @@ var inSessionUsers = []; showJoinLink = session.musician_access; - console.log('session', session) showListenLink = session.fan_access && session.active_music_session && session.active_music_session.mount; // render musicians who are already in the session @@ -159,6 +158,17 @@ sessionVals.in_session_musicians = inSessionUsersHtml.length > 0 ? inSessionUsersHtml : 'N/A'; sessionVals.join_link_display_style = showJoinLink ? "block" : "none"; sessionVals.listen_link_display_style = showListenLink ? "inline-block" : "none"; + if (!session.fan_access) { + sessionVals.listen_link_text = ''; + } + else if (session.active_music_session && session.active_music_session.mount) { + sessionVals.listen_link_text = 'Listen'; + } + else { + sessionVals.listen_link_text = ''; + } + + var $row = $(context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals)); var $offsetParent = $(tbGroup).closest('.content'); @@ -359,11 +369,39 @@ var showRsvpLink = true; var noLinkText = ''; + $('.rsvp-link-text', $parentRow).hide(); - if (approvedRsvpId) { + function showStartSessionButton(scheduledStart) { + var now = new Date(); + var scheduledDate = new Date(scheduledStart); + var minutesFromStart = (scheduledDate.getTime() - now.getTime()) / (1000 * 60); + return minutesFromStart <= MAX_MINUTES_SHOW_START; + }; + + if (session.creator.id === context.JK.currentUserId) { showRsvpLink = false; - noLinkText = $('You have been confirmed for this session. Cancel'); + noLinkText = $('Start session now?'); noLinkText.find('a').click(function() { + ui.launchSessionStartDialog(session); + return false; + }); + } + else if (approvedRsvpId) { + showRsvpLink = false; + + if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) { + noLinkText = $('Start session now? | Cancel RSVP'); + noLinkText.find('a.start').click(function() { + ui.launchSessionStartDialog(session); + return false; + }); + } + else { + noLinkText = $('Cancel RSVP'); + } + + // wire cancel link + noLinkText.find('a.cancel').click(function() { ui.launchRsvpCancelDialog(session.id, approvedRsvpId) .one(EVENTS.RSVP_CANCELED, function() { rest.getSessionHistory(session.id) @@ -377,9 +415,20 @@ return false; }); } + else if (hasInvitation) { + showRsvpLink = false; + + if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) { + noLinkText = $('Start session now?'); + noLinkText.find('a').click(function() { + ui.launchSessionStartDialog(session); + return false; + }); + } + } else if (pendingRsvpId) { showRsvpLink = false; - noLinkText = $('You have RSVP\'ed to this session. Cancel'); + noLinkText = $('Cancel RSVP'); noLinkText.find('a').click(function() { ui.launchRsvpCancelDialog(session.id, pendingRsvpId) .one(EVENTS.RSVP_CANCELED, function() { @@ -406,6 +455,7 @@ if (showRsvpLink) { $('.rsvp-msg', $parentRow).hide(); $('.rsvp-link', $parentRow).show(); + $('.rsvp-link-text', $parentRow).show(); $('.rsvp-link', $parentRow).click(function(evt) { ui.launchRsvpSubmitDialog(session.id) diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 9b7f034fe..92d77d2bb 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -28,6 +28,7 @@ var backendMixerAlertThrottleTimer = null; // we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar var participantsEverSeen = {}; + var currentParticipants = {}; var $self = $(this); var sessionPageEnterDeferred = null; var sessionPageEnterTimeout = null; @@ -301,6 +302,7 @@ $(document).trigger(EVENTS.SESSION_ENDED, {session: {id: currentSessionId}}); } currentSessionId = null; + currentParticipants = {} } // you should only update currentSession with this function @@ -391,6 +393,16 @@ }; } + function ParticipantJoined(newSession, participant) { + client.ParticipantJoined(newSession, _toJamClientParticipant(participant)); + currentParticipants[participant.client_id] = {server:participant, client: {audio_established:null}} + } + + function ParticipantLeft(newSession, participant) { + client.ParticipantLeft(newSession, _toJamClientParticipant(participant)); + delete currentParticipants[participant.client_id] + } + function sendClientParticipantChanges(oldSession, newSession) { var joins = [], leaves = [], leaveJoins = []; // Will hold JamClientParticipants @@ -422,40 +434,41 @@ // if the participant is here now, and here before, there is still a chance we missed a // very fast leave/join. So check if joined_session_at is different if(oldParticipantIds[client_id].joined_session_at != participant.joined_session_at) { - leaveJoins.push(_toJamClientParticipant(participant)) + leaveJoins.push(participant) } } else { // new participant id that's not in old participant ids: Join - joins.push(_toJamClientParticipant(participant)); + joins.push(participant); } }); $.each(oldParticipantIds, function(client_id, participant) { if (!(client_id in newParticipantIds)) { // old participant id that's not in new participant ids: Leave - leaves.push(_toJamClientParticipant(participant)); + leaves.push(participant); } }); $.each(joins, function(i,v) { if (v.client_id != clientId) { - logger.debug("jamClient.ParticipantJoined", v.clientID) - client.ParticipantJoined(newSession, v); + logger.debug("jamClient.ParticipantJoined", v.client_id) + ParticipantJoined(newSession, v) } }); $.each(leaves, function(i,v) { if (v.client_id != clientId) { - logger.debug("jamClient.ParticipantLeft", v.clientID) - client.ParticipantLeft(newSession, v); + logger.debug("jamClient.ParticipantLeft", v.client_id) + ParticipantLeft(newSession, v) } }); $.each(leaveJoins, function(i,v) { if (v.client_id != clientId) { logger.debug("participant had a rapid leave/join") - logger.debug("jamClient.ParticipantLeft", v.clientID) - client.ParticipantLeft(newSession, v); - logger.debug("jamClient.ParticipantJoined", v.clientID) - client.ParticipantJoined(newSession, v); + logger.debug("jamClient.ParticipantLeft", v.client_id) + ParticipantLeft(newSession, v) + + logger.debug("jamClient.ParticipantJoined", v.client_id) + ParticipantJoined(newSession, v) } }); } @@ -754,6 +767,23 @@ this.getParticipant = function(clientId) { return participantsEverSeen[clientId] }; + + // call to report if the current user was able to establish audio with the specified clientID + this.setAudioEstablished = function(clientId, audioEstablished) { + var participant = currentParticipants[clientId] + if(participant) { + if(participant.client.audio_established === null) { + participant.client.audio_established = audioEstablished; + context.stats.write('client.audio_established.' + (audioEstablished ? 'success': 'failure'), {user_id: context.JK.currentUserId, name: context.JK.currentUserName, remote_user_id: participant.server.user.id, remote_name: participant.server.user.name, value: 1}) + } + else if(participant.client.audio_established === false && audioEstablished === true) { + // this means the frontend had declared this audio failed, but later says it works. + // this could mean our threshold of time to wait before audio is considered failed is too low, and needs tweaking + context.stats.write('client.audio_established.delayed', {user_id: context.JK.currentUserId, name: context.JK.currentUserName, remote_user_id: participant.server.user.id, remote_name: participant.server.user.name, value: 1}) + } + } + } + this.ensureEnded = function() { updateCurrentSession(null); } diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js index 167855b30..9213c43cb 100644 --- a/web/app/assets/javascripts/ui_helper.js +++ b/web/app/assets/javascripts/ui_helper.js @@ -53,6 +53,12 @@ return rsvpDialog.showDialog(); } + function launchSessionStartDialog(session) { + var sessionStartDialog = new JK.SessionStartDialog(JK.app, session); + sessionStartDialog.initialize(); + return sessionStartDialog.showDialog(); + } + this.addSessionLike = addSessionLike; this.addRecordingLike = addRecordingLike; this.launchCommentDialog = launchCommentDialog; @@ -60,6 +66,7 @@ this.launchRsvpSubmitDialog = launchRsvpSubmitDialog; this.launchRsvpCancelDialog = launchRsvpCancelDialog; this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog; + this.launchSessionStartDialog = launchSessionStartDialog; return this; }; diff --git a/web/app/assets/stylesheets/client/sessionList.css.scss b/web/app/assets/stylesheets/client/sessionList.css.scss index a1b65a214..16d208a59 100644 --- a/web/app/assets/stylesheets/client/sessionList.css.scss +++ b/web/app/assets/stylesheets/client/sessionList.css.scss @@ -127,6 +127,10 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks { vertical-align:top; } + .musician-groups td.nowrap { + white-space: nowrap; + } + a { color:#fff; text-decoration:none; diff --git a/web/app/assets/stylesheets/dialogs/base_dialog.css.scss b/web/app/assets/stylesheets/dialogs/base_dialog.css.scss index 4a183d9d3..049962e28 100644 --- a/web/app/assets/stylesheets/dialogs/base_dialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/base_dialog.css.scss @@ -70,8 +70,8 @@ min-height: 600px; } -.dialog-overlay-sm { - width: 600px; +.dialog-overlay-lg { + width: 800px; height: auto; position: fixed; left: 50%; @@ -90,6 +90,18 @@ color: #aaa; } +.dialog-overlay-sm { + width: 600px; + height: auto; + position: fixed; + left: 50%; + top: 20%; + margin-left: -300px; + background-color: #333; + border: 1px solid #ed3618; + z-index: 1000; +} + .dialog-overlay-sm .dialog-inner { width: 550px; height: auto; diff --git a/web/app/assets/stylesheets/dialogs/sessionStartDialog.css.scss b/web/app/assets/stylesheets/dialogs/sessionStartDialog.css.scss new file mode 100644 index 000000000..492db6b16 --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/sessionStartDialog.css.scss @@ -0,0 +1,17 @@ +.session-wrapper { + padding: 10px 35px 0 0px; + white-space: initial; + + h3 { + font-weight: bold; + color:#dedede; + } + + > div.session { + width: 50%; + + &.right { + font-size: 13px; + } + } +} \ No newline at end of file diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index e2d7dffe7..bd58c1f0f 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -34,8 +34,8 @@ class ApiJamTracksController < ApiController def download if @jam_track_right.valid? if (@jam_track_right && @jam_track_right.signed && @jam_track_right.url.present? &&@jam_track_right.url.file.exists?) - JamTrackRight.where(:id => @jam_track_right.id).update_all(:last_downloaded_at => Time.now) - @jam_track_right.update_download_count + @jam_track_right.update_download_count + @jam_track_right.last_downloaded_at = Time.now @jam_track_right.save! redirect_to @jam_track_right.sign_url else diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb index 695e3a4b4..6244e1e17 100644 --- a/web/app/controllers/api_recordings_controller.rb +++ b/web/app/controllers/api_recordings_controller.rb @@ -230,13 +230,13 @@ class ApiRecordingsController < ApiController # POST /api/recordings/:id/videos/:video_id/upload_sign def video_upload_sign length = params[:length] - @youtube_client.upload_sign(current_user, @recorded_video.url, length) + @youtube_client.sign_youtube_upload(current_user, @recorded_video.url, length) end # POST /api/recordings/:id/videos/:video_id/upload_complete def video_upload_start length = params[:length] - @youtube_client.get_upload_status(current_user, @recorded_video.url, length) + @youtube_client.youtube_upload_status(current_user, @recorded_video.url, length) end # POST /api/recordings/:id/videos/:video_id/upload_complete diff --git a/web/app/controllers/api_text_messages_controller.rb b/web/app/controllers/api_text_messages_controller.rb new file mode 100644 index 000000000..2cf919690 --- /dev/null +++ b/web/app/controllers/api_text_messages_controller.rb @@ -0,0 +1,18 @@ +require 'sanitize' +class ApiTextMessagesController < ApiController + + before_filter :api_signed_in_user + + respond_to :json + + def index + @text_messages = TextMessage.index(params[:target_user_id], current_user.id, {:offset => params[:offset]}) + respond_with @text_messages, responder: ApiResponder, :status => 200 + end + + def create + @text_message = TextMessage.create(params[:message], params[:target_user_id], current_user.id) + respond_with_model(@text_message, new: true) + end + +end \ No newline at end of file diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index 407b9a4c0..6cd34cabd 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- require 'builder' -require 'recaptcha/rails' class Slide attr_accessor :img_url, :header, :vid_url @@ -142,10 +141,6 @@ class UsersController < ApplicationController terms_of_service = params[:jam_ruby_user][:terms_of_service].nil? || params[:jam_ruby_user][:terms_of_service] == "0"? false : true musician = params[:jam_ruby_user][:musician] - if Rails.application.config.recaptcha_enable - recaptcha_failed = verify_recaptcha(:private_key=>Rails.application.config.recaptcha_private_key, :timeout=>10) - end - @user = UserManager.new.signup(remote_ip: request.remote_ip, first_name: params[:jam_ruby_user][:first_name], last_name: params[:jam_ruby_user][:last_name], @@ -157,7 +152,7 @@ class UsersController < ApplicationController birth_date: birth_date, location: location, musician: musician, - recaptcha_failed: recaptcha_failed, + recaptcha_response: params['g-recaptcha-response'], invited_user: @invited_user, fb_signup: @fb_signup, signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm", diff --git a/web/app/views/api_music_sessions/show_history.rabl b/web/app/views/api_music_sessions/show_history.rabl index a172d1afc..5d44c43f0 100644 --- a/web/app/views/api_music_sessions/show_history.rabl +++ b/web/app/views/api_music_sessions/show_history.rabl @@ -98,7 +98,9 @@ else node do |invitation| user_score(invitation.receiver.id).merge({ - receiver_avatar_url: invitation.receiver.resolved_photo_url, audio_latency: last_jam_audio_latency(invitation.receiver) + receiver_name: invitation.receiver.name, + receiver_avatar_url: invitation.receiver.resolved_photo_url, + audio_latency: last_jam_audio_latency(invitation.receiver) }) end } diff --git a/web/app/views/api_text_messages/index.rabl b/web/app/views/api_text_messages/index.rabl new file mode 100644 index 000000000..e38e85034 --- /dev/null +++ b/web/app/views/api_text_messages/index.rabl @@ -0,0 +1,11 @@ +collection @text_messages + +attributes :id, :source_user_id, :target_user_id, :message, :created_at + +node :source_user do |msg| + attributes :id, :name +end + +node :target_user do |msg| + attributes :id, :name +end \ No newline at end of file diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index 69c5c97c7..e78ec34b6 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -97,7 +97,7 @@ <%= image_tag "content/listen-icon.jpg", :size => "40x40" %>
- Listen? + {listen_link_text}? @@ -196,7 +196,7 @@ - + <%= image_tag "content/rsvp-icon.jpg", :size => "40x40" %> diff --git a/web/app/views/clients/_scheduledSession.html.erb b/web/app/views/clients/_scheduledSession.html.erb index 157b6afe0..04a0e2055 100644 --- a/web/app/views/clients/_scheduledSession.html.erb +++ b/web/app/views/clients/_scheduledSession.html.erb @@ -402,9 +402,9 @@

What instruments/parts do you need?

Me:
-
+
Others:
-

+

What access policies are in effect?

diff --git a/web/app/views/corps/features.html.erb b/web/app/views/corps/features.html.erb index 62e87c4b5..f06dfc52f 100644 --- a/web/app/views/corps/features.html.erb +++ b/web/app/views/corps/features.html.erb @@ -4,7 +4,7 @@

Features & Benefits

- Musicians can enjoy four major features of the new JamKazam service, as described below. Also, several Tutorial Videos are available to see how various features work in more detail. + Musicians can enjoy four major features of the new JamKazam service, as described below. Also, several Tutorial Videos are available to see how various features work in more detail.


diff --git a/web/app/views/corps/videos.html.erb b/web/app/views/corps/videos.html.erb index 95783a179..53c604383 100644 --- a/web/app/views/corps/videos.html.erb +++ b/web/app/views/corps/videos.html.erb @@ -23,5 +23,5 @@

- Also, several Tutorial Videos are available to see how various features work in more detail. + Also, several Tutorial Videos are available to see how various features work in more detail.

\ No newline at end of file diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml index b7b15976c..56ec88ccc 100644 --- a/web/app/views/dialogs/_dialogs.html.haml +++ b/web/app/views/dialogs/_dialogs.html.haml @@ -18,6 +18,7 @@ = render 'dialogs/rsvpSubmitDialog' = render 'dialogs/rsvpCancelDialog' = render 'dialogs/rsvpCreateSlotDialog' += render 'dialogs/sessionStartDialog' = render 'dialogs/sessionCancelDialog' = render 'dialogs/signinDialog' = render 'dialogs/signupDialog' diff --git a/web/app/views/dialogs/_sessionStartDialog.html.slim b/web/app/views/dialogs/_sessionStartDialog.html.slim new file mode 100644 index 000000000..5117beca0 --- /dev/null +++ b/web/app/views/dialogs/_sessionStartDialog.html.slim @@ -0,0 +1,44 @@ +.dialog.dialog-overlay-lg layout='dialog' layout-id='session-start-dialog' id='session-start-dialog' + .content-head + = image_tag "content/icon_add.png", {:width => 24, :height => 24, :class => 'content-icon' } + h1 Start Session + + .dialog-inner + .session-wrapper + .left.w55 + h3 When are you starting your session? + .mt5 id='session-start-type-disp' + h3.mb5.mt20 What are you playing? + em id='session-name-disp' + #session-description-disp.mt5 + #session-notations-disp.mt5 + + h3.mt20 Which band is playing? + #session-band-disp.mt5 + + h3.mt20 What language will be spoken? + #session-language-disp.mt5 + .right.w40 + h3 Who is invited? + #session-invited-disp.mt5 + + h3.mt20 What instruments/parts do you need? + .left.ib.w20.mt5 Me: + #session-instruments-me-disp.left.ib.w75.mt5 + .clearall.left.ib.w20.mt5 Others: + #session-instruments-rsvp-disp.left.ib.w75.mt5 + br clear='all' + + h3.mt20 What access policies are in effect? + #session-musician-access-disp.mt5 + #session-fans-access-disp + + h3.mt20 What legal policy is in effect? + #session-policy-disp.mt5 + .clearall + br + br + .buttons + .right + a.button-grey class='btnCancel' layout-action='cancel' CANCEL + a.button-orange class='btnStartSession' START SESSION \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index 4b81b4224..327ae86a1 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -295,6 +295,10 @@ SampleApp::Application.routes.draw do match '/users/:id/friends/:friend_id' => 'api_users#friend_show', :via => :get, :as => 'api_friend_detail' match '/users/:id/friends/:friend_id' => 'api_users#friend_destroy', :via => :delete + # text messages + match '/text_messages' => 'api_text_messages#index', :via => :get + match '/text_messages' => 'api_text_messages#create', :via => :post + # notifications match '/users/:id/notifications' => 'api_users#notification_index', :via => :get match '/users/:id/notifications/:notification_id' => 'api_users#notification_destroy', :via => :delete diff --git a/web/lib/you_tube_client.rb b/web/lib/google_client.rb similarity index 88% rename from web/lib/you_tube_client.rb rename to web/lib/google_client.rb index cba813844..c622a6e5f 100644 --- a/web/lib/you_tube_client.rb +++ b/web/lib/google_client.rb @@ -9,7 +9,7 @@ require 'socket' # Youtube OAuth and API functionality: module JamRuby - class YouTubeClient + class GoogleClient attr_accessor :client attr_accessor :api attr_accessor :request @@ -27,7 +27,7 @@ module JamRuby :application_version => '1.0.0' ) - youtube = client.discovered_api('youtube', 'v3') + #youtube = client.discovered_api('youtube', 'v3') end # Return a login URL that will show a web page with @@ -52,7 +52,7 @@ module JamRuby # https://developers.google.com/youtube/v3/docs/videos/insert # https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol - def upload_sign(user, filename, length) + def sign_youtube_upload(user, filename, length) raise ArgumentError, "Length is required and should be > 0" if length.to_i.zero? # Something like this: @@ -140,7 +140,7 @@ module JamRuby end # https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol#Check_Upload_Status - def get_upload_status(user, upload_url, length) + def youtube_upload_status(user, upload_url, length) auth = UserAuthorization.google_auth(user).first if auth.nil? || auth.token.nil? raise SecurityError, "No current google token found for user #{user}" @@ -187,21 +187,50 @@ module JamRuby end # @return true if file specified by URL uploaded, false otherwise - def verify_upload(user, upload_url, length) - status_hash=get_upload_status(user, upload_url, length) + def verify_youtube_upload(user, upload_url, length) + status_hash=youtube_upload_status(user, upload_url, length) (status_hash['status']>=200 && status_hash['status']<300) end # Set fully_uploaded if the upload can be verified. # @return true if verified; false otherwise: def complete_upload(recorded_video) - if (verify_upload(recorded_video.user, recorded_video.url, recorded_video.length)) + if (verify_youtube_upload(recorded_video.user, recorded_video.url, recorded_video.length)) recorded_video.update_attribute(:fully_uploaded, true) else false end end + def verify_recaptcha(recaptcha_response) + success = false + if !Rails.application.config.recaptcha_enable + success = true + else + Rails.logger.info "Login with: #{recaptcha_response}" + RestClient.get("https://www.google.com/recaptcha/api/siteverify", + params: { + secret: Rails.application.config.recaptcha_private_key, + response: recaptcha_response + } + ) do |response, request, result| + Rails.logger.info "response: #{response.inspect}" + case(response.code) + when 200..207 + json = JSON.parse(response.to_str) + if json['success'] + success = true + else + Rails.logger.info("Error verifying recaptcha: #{json['error-codes'].inspect}") + end + else + Rails.logger.info("Unexpected status from google_recaptcha: [#{response.code}] with headers: #{response.headers.inspect}") + end #case + end #do + end # if + success + end #def + # This will also sign in and prompt for login as necessary; # currently requires the server to be running at localhost:3000 def signin_flow() diff --git a/web/lib/user_manager.rb b/web/lib/user_manager.rb index a694af94c..5c36c6974 100644 --- a/web/lib/user_manager.rb +++ b/web/lib/user_manager.rb @@ -1,8 +1,10 @@ +require 'google_client' class UserManager < BaseManager def initialize(options={}) super(options) @log = Logging.logger[self] + @google_client = GoogleClient.new() end # Note that almost everything can be nil here. This is because when users sign up via social media, @@ -24,7 +26,8 @@ class UserManager < BaseManager fb_signup = options[:fb_signup] signup_confirm_url = options[:signup_confirm_url] affiliate_referral_id = options[:affiliate_referral_id] - recaptcha_failed = Rails.application.config.recaptcha_enable && options[:recaptcha_failed] + recaptcha_failed=(fb_signup) ? false : !@google_client.verify_recaptcha(options[:recaptcha_response]) + user = User.new # check if we have disabled open signup for this site. open == invited users can still get in diff --git a/web/spec/features/find_sessions_spec.rb b/web/spec/features/find_sessions_spec.rb index ba8f7c517..5853c13ce 100644 --- a/web/spec/features/find_sessions_spec.rb +++ b/web/spec/features/find_sessions_spec.rb @@ -192,6 +192,120 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr end end + describe "start session behavior" do + before(:each) do + + @music_session = FactoryGirl.create(:music_session, creator: user) + + @invited_user = FactoryGirl.create(:user) + FactoryGirl.create(:friendship, :user => @invited_user, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => @invited_user) + @invitation = FactoryGirl.create(:invitation, :sender => user, :receiver => @invited_user, :music_session => @music_session) + + @rsvp_user = FactoryGirl.create(:user) + @rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: @music_session) + @rsvp_request = FactoryGirl.create(:rsvp_request_for_slots, chosen: nil, user: @rsvp_user, slots: [@rsvp_slot]) + end + + it "should always show start session link for session creator" do + pending + fast_signin(user, Nav.find_session) + find("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + end + + it "should not show start session link for anyone who is not creator, invitee, or RSVP user" do + pending + random_user = FactoryGirl.create(:user) + fast_signin(random_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start") + fast_signout + end + + it "should show start session link for invited or RSVP users" do + pending + # make session date/time TBD + @music_session.scheduled_start = Time.now + 5.minutes + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + fast_signin(@rsvp_user, Nav.find_session) + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + + it "should not show start session link for invited or RSVP users when date/time is TBD" do + pending + # make session date/time TBD + @music_session.scheduled_start = nil + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + # "start session" should still be hidden + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + + it "should not show start session link for invited or RSVP users when more than 15 minutes remain to start time" do + pending + # make session date/time more than 15 min away + @music_session.scheduled_start = Time.now + 60.minutes + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + # "start session" should still be hidden + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + end + describe "rsvp behavior" do before(:each) do stub_const("APP_CONFIG", web_config) @@ -222,10 +336,9 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr it "RSVP text shows correctly" do music_session = FactoryGirl.create(:music_session, creator: user) + # session creator cannot cancel fast_signin(user, Nav.find_session) - - find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ") - + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") sign_out # create a slot so the session can be joined @@ -241,15 +354,15 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr # first state: an unconfirmed RSVP go_to_root fast_signin(finder, Nav.find_session) - find("#sessions-scheduled .rsvp-msg span.text", text: "You have RSVP'ed to this session. ") + find("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true rsvp_request.rsvp_requests_rsvp_slots[0].save! - # second state: a connfirmed RSVP + # second state: a confirmed RSVP go_to_root fast_signin(finder, Nav.find_session) - find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ") + find("#sessions-scheduled a.cancel", text: "Cancel RSVP") # need to now CANCEL, and check what it says: // VRFS-1891 diff --git a/web/spec/features/oauth_spec.rb b/web/spec/features/oauth_spec.rb index 024418400..a24e6cfae 100644 --- a/web/spec/features/oauth_spec.rb +++ b/web/spec/features/oauth_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'you_tube_client' +require 'google_client' describe "OAuth", :slow=>true, :js=>true, :type=>:feature, :capybara_feature=>true do @@ -15,7 +15,7 @@ describe "OAuth", :slow=>true, :js=>true, :type=>:feature, :capybara_feature=>tr end before(:each) do - @youtube_client = YouTubeClient.new() + @youtube_client = GoogleClient.new() end after(:each) do diff --git a/web/spec/features/session_start_dialog_spec.rb b/web/spec/features/session_start_dialog_spec.rb new file mode 100644 index 000000000..e69de29bb diff --git a/web/spec/features/youtube_spec.rb b/web/spec/features/youtube_spec.rb index 48d905df3..1fe81bf06 100644 --- a/web/spec/features/youtube_spec.rb +++ b/web/spec/features/youtube_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'you_tube_client' +require 'google_client' require 'rest_client' describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature => true do @@ -12,7 +12,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature @previous_run_server = Capybara.run_server Capybara.run_server = false @user=FactoryGirl.create(:user, :email => "jamkazamtest@gmail.com") - @youtube_client = YouTubeClient.new() + @youtube_client = GoogleClient.new() authorize_google_user(@youtube_client, @user, "stinkyblueberryjam") google_auth = UserAuthorization.google_auth(@user).first # Consider returning this from above now that it is reliable end @@ -25,7 +25,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature it "should retrieve upload url" do length = 3276 - upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length) + upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length) upload_hash.should_not be_nil upload_hash.length.should be >=1 upload_hash['method'].should eq("PUT") @@ -35,13 +35,13 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature upload_hash['Content-Length'].should eq(length) upload_hash['Content-Type'].should_not be_nil - @youtube_client.verify_upload(@user, upload_hash['url'], length).should be_false + @youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_false end it "upload url should allow uploading" do vid_path = Rails.root.join('spec', 'files', 'test_video.mp4') length = File.size?(vid_path) - upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length) + upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length) #puts upload_hash.inspect upload_hash.should_not be_nil upload_hash.length.should be >=1 @@ -54,8 +54,8 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature # Upload this file as the client would: RestClient.put(upload_hash['url'], File.read(vid_path)) - @youtube_client.verify_upload(@user, upload_hash['url'], length).should be_true - #@youtube_client.get_upload_status(@user, upload_hash['url'], length) + @youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_true + #@youtube_client.youtube_upload_status(@user, upload_hash['url'], length) end it "sets upload flag when complete" do @@ -67,7 +67,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature vid_path = Rails.root.join('spec', 'files', 'test_video.mp4') length = File.size?(vid_path) - upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length) + upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length) upload_hash.should_not be_nil upload_hash['url'].should_not be_nil RestClient.put(upload_hash['url'], File.read(vid_path)) @@ -82,7 +82,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature @recording.recorded_videos << recorded_video - @youtube_client.verify_upload(@user, upload_hash['url'], length).should be_true + @youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_true @youtube_client.complete_upload(recorded_video).should be_true recorded_video.fully_uploaded.should be_true end diff --git a/web/spec/managers/user_manager_spec.rb b/web/spec/managers/user_manager_spec.rb index 23368a208..275d90b69 100644 --- a/web/spec/managers/user_manager_spec.rb +++ b/web/spec/managers/user_manager_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe UserManager do before(:each) do + Rails.application.config.recaptcha_enable=false @user_manager = UserManager.new(:conn => @conn) UserMailer.deliveries.clear @location = { :country => "US", :state => "Arkansas", :city => "Little Rock" } @@ -662,12 +663,13 @@ describe UserManager do end # describe "without nocaptcha" describe "with nocaptcha" do - before(:all) do + before(:each) do @old_recaptcha=Rails.application.config.recaptcha_enable Rails.application.config.recaptcha_enable=true + UserMailer.deliveries.clear end - after(:all) do + after(:each) do Rails.application.config.recaptcha_enable=@old_recaptcha end @@ -682,11 +684,14 @@ describe UserManager do instruments: @instruments, musician: true, location: @loca, - recaptcha_failed: true, + recaptcha_response: nil, signup_confirm_url: "http://localhost:3000/confirm") user.errors.any?.should be_true + UserMailer.deliveries.should have(0).items + end + it "passes when facebook signup" do user = @user_manager.signup(remote_ip: "1.2.3.4", first_name: "bob", last_name: "smith", @@ -694,13 +699,14 @@ describe UserManager do password: "foobar", password_confirmation: "foobar", terms_of_service: true, - recaptcha_failed: false, + fb_signup: FactoryGirl.create(:facebook_signup), + recaptcha_response: nil, instruments: @instruments, musician: true, location: @loca, signup_confirm_url: "http://localhost:3000/confirm") user.errors.any?.should be_false - end # it "fails when nocaptcha fails" + end # it "passes when facebook signup" end # describe "with nocaptcha" end # test diff --git a/web/spec/testing_oauth.txt b/web/spec/testing_oauth.txt index db139b81f..2a6ad3874 100644 --- a/web/spec/testing_oauth.txt +++ b/web/spec/testing_oauth.txt @@ -4,7 +4,7 @@ To obtain an access token, one must actually log into google using a browser run Getting an access token for the purposes of automated testing is tricky, but possible using Capybara with a javascript-enabled driver. (Note, web/spec/support/utilities.rb utilizes the JK youtube client to perform the intricate bits): -1) Obtain the login URL. It's ugly, but we can get it from the YouTubeClient. It contains the callback URL, as well as a "hint" that will fill in the username for us. +1) Obtain the login URL. It's ugly, but we can get it from the GoogleClient. It contains the callback URL, as well as a "hint" that will fill in the username for us. 2) Start a web server on an enabled callback server, such as localhost:3000 3) Obtain the URL using a known test user 4) Visit the URL in a capybara test @@ -12,7 +12,7 @@ Getting an access token for the purposes of automated testing is tricky, but pos 4b) Click the login button 4c) The approve page should load. Wait for the approve button to be enabled. This is usually a second or two after the page loads, but not immediately. 4d) Click the approve button -5) After google approves, some javascript will redirect to our test web server, which contains a code. This is not the access_token, but a one-time code that can be exchanged for an access_token, again POSTing to google's auth server. You can see it in gory detail in YouTubeClient.exchange_for_token. +5) After google approves, some javascript will redirect to our test web server, which contains a code. This is not the access_token, but a one-time code that can be exchanged for an access_token, again POSTing to google's auth server. You can see it in gory detail in GoogleClient.exchange_for_token. 6) If all goes well, the test web server will call back the invoker with a real access token. 7) For testing purposes, stick the access token in the user.user_authorizations table for the user for which we are testing. diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index 459b59bec..cf00b389b 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -804,7 +804,7 @@ module JamWebsockets id = subscribe.id type = subscribe.type if id && id.length > 0 && type && type.length > 0 - register_subscription(client, type, id) + #register_subscription(client, type, id) else @log.error("handle_subscribe: empty data #{subscribe}") end @@ -814,7 +814,7 @@ module JamWebsockets id = unsubscribe.id type = unsubscribe.type if id && id.length > 0 && type && type.length > 0 - unregister_subscription(client, type, id) + #unregister_subscription(client, type, id) else @log.error("handle_subscribe: empty data #{unsubscribe}") end