diff --git a/db/manifest b/db/manifest index 00a8e8bb2..f82a62034 100755 --- a/db/manifest +++ b/db/manifest @@ -121,3 +121,7 @@ scores_mod_connections.sql scores_create_schemas_and_extensions.sql scores_create_tables.sql remove_is_downloadable.sql +scores_mod_connections2.sql +track_download_counts.sql +scores_mod_users2.sql +user_bio.sql diff --git a/db/up/scores_mod_connections2.sql b/db/up/scores_mod_connections2.sql new file mode 100644 index 000000000..016652d85 --- /dev/null +++ b/db/up/scores_mod_connections2.sql @@ -0,0 +1,6 @@ +-- fix locidispid should be bigint + +ALTER TABLE connections DROP COLUMN locidispid; +ALTER TABLE connections ADD COLUMN locidispid BIGINT; +ALTER TABLE connections ALTER COLUMN locidispid SET NOT NULL; +CREATE INDEX connections_locidispid_ndx ON connections (locidispid); diff --git a/db/up/scores_mod_users2.sql b/db/up/scores_mod_users2.sql new file mode 100644 index 000000000..2dd4fe936 --- /dev/null +++ b/db/up/scores_mod_users2.sql @@ -0,0 +1,7 @@ +-- locidispid must be bigint + +ALTER TABLE users DROP COLUMN locidispid; +ALTER TABLE users ADD COLUMN locidispid BIGINT; +ALTER TABLE users ALTER COLUMN locidispid SET DEFAULT 0; +UPDATE users SET locidispid = 0; +ALTER TABLE users ALTER COLUMN locidispid SET NOT NULL; diff --git a/db/up/track_download_counts.sql b/db/up/track_download_counts.sql new file mode 100644 index 000000000..f08d001a9 --- /dev/null +++ b/db/up/track_download_counts.sql @@ -0,0 +1,5 @@ +ALTER TABLE recorded_tracks ADD COLUMN download_count INTEGER NOT NULL DEFAULT 0; +ALTER TABLE recorded_tracks ADD COLUMN last_downloaded_at TIMESTAMP; + +ALTER TABLE mixes ADD COLUMN download_count INTEGER NOT NULL DEFAULT 0; +ALTER TABLE mixes ADD COLUMN last_downloaded_at TIMESTAMP; \ No newline at end of file diff --git a/db/up/user_bio.sql b/db/up/user_bio.sql new file mode 100644 index 000000000..a83009a9b --- /dev/null +++ b/db/up/user_bio.sql @@ -0,0 +1 @@ +ALTER TABLE users ALTER COLUMN biography TYPE TEXT; \ No newline at end of file diff --git a/ruby/Gemfile b/ruby/Gemfile index 47bababdd..7ba18443c 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -33,6 +33,7 @@ gem 'carrierwave' gem 'aasm', '3.0.16' gem 'devise', '>= 1.1.2' gem 'postgres-copy' +gem 'geokit' gem 'geokit-rails' gem 'postgres_ext' gem 'resque' @@ -41,6 +42,7 @@ gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_ gem 'resque-lonely_job', '~> 1.0.0' gem 'oj' gem 'builder' +gem 'fog' group :test do gem 'simplecov', '~> 0.7.1' diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 41ffebc8b..4e840a9ee 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -11,6 +11,7 @@ require "action_mailer" require "devise" require "sendgrid" require "postgres-copy" +require "geokit" require "geokit-rails" require "postgres_ext" require 'builder' @@ -124,6 +125,9 @@ require "jam_ruby/models/recording_play" require "jam_ruby/models/feed" require "jam_ruby/models/jam_isp" require "jam_ruby/models/geo_ip_blocks" +require "jam_ruby/models/geo_ip_locations" +require "jam_ruby/models/score" +require "jam_ruby/models/get_work" include Jampb diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb index dcae48185..dfdd24eac 100644 --- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb @@ -135,7 +135,7 @@ @body = msg sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] - mail(:to => email, :subject => subject) do |format| + mail(:bcc => email, :subject => subject) do |format| format.text format.html end @@ -154,53 +154,54 @@ end end - def musician_session_join(email, msg) + def musician_session_join(email, msg, session_id) subject = "Someone you know is in a session on JamKazam" unique_args = {:type => "musician_session_join"} - @body = msg + @session_url = "#{APP_CONFIG.external_root_url}/sessions/#{session_id}" sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] - mail(:to => email, :subject => subject) do |format| + mail(:bcc => email, :subject => subject) do |format| format.text format.html end end - def band_session_join(email, msg) + def band_session_join(email, msg, session_id) subject = "A band that you follow has joined a session" unique_args = {:type => "band_session_join"} @body = msg + @session_url = "#{APP_CONFIG.external_root_url}/sessions/#{session_id}" sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] - mail(:to => email, :subject => subject) do |format| + mail(:bcc => email, :subject => subject) do |format| format.text format.html end end def musician_recording_saved(email, msg) - subject = msg + subject = "A musician has saved a new recording on JamKazam" unique_args = {:type => "musician_recording_saved"} @body = msg sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] - mail(:to => email, :subject => subject) do |format| + mail(:bcc => email, :subject => subject) do |format| format.text format.html end end def band_recording_saved(email, msg) - subject = msg + subject = "A band has saved a new recording on JamKazam" unique_args = {:type => "band_recording_saved"} @body = msg sendgrid_category "Notification" sendgrid_unique_args :type => unique_args[:type] - mail(:to => email, :subject => subject) do |format| + mail(:bcc => email, :subject => subject) do |format| format.text format.html end @@ -236,7 +237,7 @@ # @body = msg # sendgrid_category "Notification" # sendgrid_unique_args :type => unique_args[:type] - # mail(:to => email, :subject => subject) do |format| + # mail(:bcc => email, :subject => subject) do |format| # format.text # format.html # end diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.html.erb index 74a84c56b..276ae4fcc 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.html.erb @@ -1,3 +1,3 @@ <% provide(:title, 'New Band Session') %> -

<%= @body %>

\ No newline at end of file +

<%= @body %> Listen in.

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.text.erb index 2f21cf84a..88101c711 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/band_session_join.text.erb @@ -1 +1 @@ -<%= @body %> \ No newline at end of file +<%= @body %> Listen at <%= @session_url %>. \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.html.erb index ae36c8a1e..de2b7e43c 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.html.erb @@ -1,3 +1,3 @@ <% provide(:title, 'Musician in Session') %> -

<%= @body %>

\ No newline at end of file +

<%= @body %> Listen in.

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.text.erb index 2f21cf84a..88101c711 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/musician_session_join.text.erb @@ -1 +1 @@ -<%= @body %> \ No newline at end of file +<%= @body %> Listen at <%= @session_url %>. \ No newline at end of file diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 2320218b6..91368427c 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -56,27 +56,44 @@ module JamRuby end if ip_address - # todo turn ip_address string into a number, then fetch the locid and ispid and the other stuff... + # turn ip_address string into a number, then fetch the isp and block records and update location info addr = JamIsp.ip_to_num(ip_address) - puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============") + #puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============") isp = JamIsp.lookup(addr) + #puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============") if isp.nil? then ispid = 0 else ispid = isp.coid end - puts("============= JamIsp.lookup returns #{ispid} for #{addr} =============") block = GeoIpBlocks.lookup(addr) + #puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============") if block.nil? then locid = 0 else locid = block.locid end - puts("============= GeoIpBlocks.lookup returns #{locid} for #{addr} =============") - locidispid = 0 - latitude = 0.0 - longitude = 0.0 - countrycode = 'US' - region = 'TX' - city = 'Austin' + location = GeoIpLocations.lookup(locid) + if location.nil? + locidispid = 0 + latitude = 0.0 + longitude = 0.0 + countrycode = 'US' + region = 'TX' + city = 'Austin' + else + locidispid = locid*1000000+ispid + latitude = location.latitude + longitude = location.longitude + countrycode = location.countrycode + region = location.region + city = location.city + end - # todo stuff this stuff into the connection records + conn.ip_address = ip_address + conn.locidispid = locidispid + conn.latitude = latitude + conn.longitude = longitude + conn.countrycode = countrycode + conn.region = region + conn.city = city + conn.save!(validate: false) end sql =< :recording_id validate :user_belongs_to_recording + before_create :generate_share_token SHARE_TOKEN_LENGTH = 8 @@ -67,7 +68,6 @@ module JamRuby !ClaimedRecording.find_by_user_id_and_recording_id(some_user.id, recording_id).nil? end - def remove_non_alpha_num(token) token.gsub(/[^0-9A-Za-z]/, '') end diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index a090629d0..26bd1d240 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -102,15 +102,15 @@ module JamRuby errors.add(:music_session, ValidationMessages::CANT_JOIN_RECORDING_SESSION) end - unless user.admin? - num_sessions = Connection.where(:user_id => user_id) - .where(["(music_session_id IS NOT NULL) AND (aasm_state != ?)",EXPIRED_STATE.to_s]) - .count - if 0 < num_sessions - errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS) - return false; - end - end + # unless user.admin? + # num_sessions = Connection.where(:user_id => user_id) + # .where(["(music_session_id IS NOT NULL) AND (aasm_state != ?)",EXPIRED_STATE.to_s]) + # .count + # if 0 < num_sessions + # errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS) + # return false; + # end + # end return true end diff --git a/ruby/lib/jam_ruby/models/geo_ip_blocks.rb b/ruby/lib/jam_ruby/models/geo_ip_blocks.rb index 417d5bb95..931b8d28c 100644 --- a/ruby/lib/jam_ruby/models/geo_ip_blocks.rb +++ b/ruby/lib/jam_ruby/models/geo_ip_blocks.rb @@ -4,17 +4,14 @@ module JamRuby self.table_name = 'geoipblocks' def self.lookup(ipnum) - GeoIpBlocks.select(:locid) - .where('geom && ST_MakePoint(?, 0) AND ? BETWEEN beginip AND endip', ipnum, ipnum) + GeoIpBlocks.where('geom && ST_MakePoint(?, 0) AND ? BETWEEN beginip AND endip', ipnum, ipnum) .limit(1) .first end - def self.make_row(beginip, endip, locid) - c = ActiveRecord::Base.connection.raw_connection - c.prepare('blah', 'insert into geoipblocks (beginip, endip, locid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))') - c.exec_prepared('blah', [beginip, endip, locid]) - c.exec("deallocate blah") + def self.createx(beginip, endip, locid) + c = connection.raw_connection + c.exec_params('insert into geoipblocks (beginip, endip, locid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))', [beginip, endip, locid]) end end end diff --git a/ruby/lib/jam_ruby/models/geo_ip_locations.rb b/ruby/lib/jam_ruby/models/geo_ip_locations.rb new file mode 100644 index 000000000..0f8218353 --- /dev/null +++ b/ruby/lib/jam_ruby/models/geo_ip_locations.rb @@ -0,0 +1,18 @@ +module JamRuby + class GeoIpLocations < ActiveRecord::Base + + self.table_name = 'geoiplocations' + + def self.lookup(locid) + GeoIpLocations.where(locid: locid) + .limit(1) + .first + end + + def self.createx(locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode) + c = connection.raw_connection + c.exec_params('insert into geoiplocations (locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode, geog) values($1, $2, $3, $4, $5, $6, $7, $8, $9, ST_SetSRID(ST_MakePoint($7, $6), 4326)::geography)', + [locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode]) + end + end +end diff --git a/ruby/lib/jam_ruby/models/get_work.rb b/ruby/lib/jam_ruby/models/get_work.rb new file mode 100644 index 000000000..f82ca00f4 --- /dev/null +++ b/ruby/lib/jam_ruby/models/get_work.rb @@ -0,0 +1,22 @@ +module JamRuby + class GetWork < ActiveRecord::Base + + self.table_name = "connections" + + def self.get_work(mylocidispid) + list = self.get_work_list(mylocidispid) + return nil if list.nil? + return nil if list.length == 0 + return list[0] + end + + def self.get_work_list(mylocidispid) + r = GetWork.select(:client_id).find_by_sql("select get_work(#{mylocidispid}) as client_id") + #puts("r = #{r}") + a = r.map {|i| i.client_id} + #puts("a = #{a}") + a + #return ["blah1", "blah2", "blah3", "blah4", "blah5"] + end + end +end diff --git a/ruby/lib/jam_ruby/models/icecast_mount.rb b/ruby/lib/jam_ruby/models/icecast_mount.rb index bf3d5b393..cfa3424a2 100644 --- a/ruby/lib/jam_ruby/models/icecast_mount.rb +++ b/ruby/lib/jam_ruby/models/icecast_mount.rb @@ -110,7 +110,7 @@ module JamRuby def listener_add with_lock do - sourced_needs_changing_at = Time.now if listeners == 0 + self.sourced_needs_changing_at = Time.now if listeners == 0 # this is completely unsafe without that 'with_lock' statement above self.listeners = self.listeners + 1 @@ -126,7 +126,7 @@ module JamRuby end with_lock do - sourced_needs_changing_at = Time.now if listeners == 1 + self.sourced_needs_changing_at = Time.now if listeners == 1 # this is completely unsafe without that 'with_lock' statement above self.listeners = self.listeners - 1 diff --git a/ruby/lib/jam_ruby/models/jam_isp.rb b/ruby/lib/jam_ruby/models/jam_isp.rb index 4157b1ce3..680cd302a 100644 --- a/ruby/lib/jam_ruby/models/jam_isp.rb +++ b/ruby/lib/jam_ruby/models/jam_isp.rb @@ -18,11 +18,10 @@ module JamRuby .first end - def self.make_row(beginip, endip, coid) - c = ActiveRecord::Base.connection.raw_connection - c.prepare('blah', 'insert into jamisp (beginip, endip, coid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))') - c.exec_prepared('blah', [beginip, endip, coid]) - c.exec("deallocate blah") + def self.createx(beginip, endip, coid) + c = connection.raw_connection + c.exec_params('insert into jamisp (beginip, endip, coid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))', + [beginip, endip, coid]) end end end diff --git a/ruby/lib/jam_ruby/models/mix.rb b/ruby/lib/jam_ruby/models/mix.rb index 216145a2e..0d2282b0c 100644 --- a/ruby/lib/jam_ruby/models/mix.rb +++ b/ruby/lib/jam_ruby/models/mix.rb @@ -10,14 +10,24 @@ module JamRuby attr_accessible :ogg_url, :should_retry, as: :admin attr_accessor :is_skip_mount_uploader + attr_writer :current_user belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mixes, :foreign_key => 'recording_id' + validates :download_count, presence: true + validate :verify_download_count + skip_callback :save, :before, :store_picture!, if: :is_skip_mount_uploader mount_uploader :ogg_url, MixUploader + def verify_download_count + if (self.download_count < 0 || self.download_count > APP_CONFIG.max_audio_downloads) && !@current_user.admin + errors.add(:download_count, "must be less than or equal to 100") + end + end + before_validation do # this should be an activeadmin only path, because it's using the mount_uploader (whereas the client does something completely different) if !is_skip_mount_uploader && ogg_url.present? && ogg_url.respond_to?(:file) && ogg_url_changed? @@ -67,7 +77,6 @@ module JamRuby !ClaimedRecording.find_by_user_id_and_recording_id(some_user.id, recording_id).nil? end - def errored(reason, detail) self.error_reason = reason self.error_detail = detail @@ -148,6 +157,11 @@ module JamRuby Mix.construct_filename(self.created_at, self.recording_id, self.id, type) end + def update_download_count(count=1) + self.download_count = self.download_count + count + self.last_downloaded_at = Time.now + end + private def delete_s3_files diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb index f728493b5..ecc7ce8dc 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -43,19 +43,23 @@ module JamRuby self.comments.size end - def tracks + def grouped_tracks tracks = [] self.music_session_user_histories.each do |msuh| user = User.find(msuh.user_id) + t = Track.new + t.musician = user + t.instrument_ids = [] + # this treats each track as a "user", which has 1 or more instruments in the session unless msuh.instruments.blank? instruments = msuh.instruments.split(SEPARATOR) instruments.each do |instrument| - t = Track.new - t.musician = user - t.instrument_id = instrument - tracks << t + if !t.instrument_ids.include? instrument + t.instrument_ids << instrument + end end end + tracks << t end tracks end @@ -146,7 +150,7 @@ module JamRuby end def is_over? - !session_removed_at.nil? + music_session.nil? || !session_removed_at.nil? end def end_history @@ -173,8 +177,6 @@ module JamRuby hist.end_history if hist - puts "**************NOTIFICATION SESSION ENDED**************" - Notification.send_session_ended(session_id) end diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index ab6708ca4..978ee08f9 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -1,6 +1,8 @@ module JamRuby class Notification < ActiveRecord::Base + @@log = Logging.logger[Notification] + self.primary_key = 'id' default_scope order('created_at DESC') @@ -357,7 +359,6 @@ module JamRuby # publish to all users who have a notification for this session # TODO: do this in BULK or in async block notifications.each do |n| - puts "*************SENDING SESSION_ENDED TO #{n.target_user_id}***************" msg = @@message_factory.session_ended(n.target_user_id, session_id) @@mq_router.publish_to_user(n.target_user_id, msg) end @@ -505,7 +506,11 @@ module JamRuby # send email notifications unless offline_ff.empty? - UserMailer.musician_session_join(offline_ff.map! {|f| f.email}, notification_msg).deliver + begin + UserMailer.musician_session_join(offline_ff.map! {|f| f.email}, notification_msg, music_session.id).deliver if APP_CONFIG.send_join_session_email_notifications + rescue => e + @@log.error("unable to send email to offline participants #{e}") + end end end end @@ -547,7 +552,7 @@ module JamRuby # send email notifications unless offline_followers.empty? - UserMailer.band_session_join(offline_followers.map! {|f| f.email}, notification_msg).deliver + UserMailer.band_session_join(offline_followers.map! {|f| f.email}, notification_msg, music_session.id).deliver if APP_CONFIG.send_join_session_email_notifications end end end diff --git a/ruby/lib/jam_ruby/models/promotional.rb b/ruby/lib/jam_ruby/models/promotional.rb index 6f90c8616..660a768e6 100644 --- a/ruby/lib/jam_ruby/models/promotional.rb +++ b/ruby/lib/jam_ruby/models/promotional.rb @@ -81,6 +81,14 @@ class JamRuby::PromoLatest < JamRuby::Promotional attr_accessible :latest + def music_session_history + @music_session_history ||= MusicSessionHistory.find_by_id(latest_id) + end + + def recording + @recording ||= Recording.find_by_id(latest_id) + end + def self.latest_candidates recordings = Recording .where('music_session_id IS NOT NULL') diff --git a/ruby/lib/jam_ruby/models/recorded_track.rb b/ruby/lib/jam_ruby/models/recorded_track.rb index 21e15cf6e..ac1ba40d8 100644 --- a/ruby/lib/jam_ruby/models/recorded_track.rb +++ b/ruby/lib/jam_ruby/models/recorded_track.rb @@ -8,10 +8,13 @@ module JamRuby self.table_name = "recorded_tracks" self.primary_key = 'id' + default_scope order('user_id ASC') + attr_accessor :marking_complete attr_writer :is_skip_mount_uploader attr_accessible :discard, :user, :user_id, :instrument_id, :sound, :client_id, :track_id, :client_track_id, :url, as: :admin + attr_writer :current_user SOUND = %w(mono stereo) MAX_PART_FAILURES = 3 @@ -31,11 +34,13 @@ module JamRuby validates :length, length: {minimum: 1, maximum: 1024 * 1024 * 256 }, if: :upload_starting? # 256 megs max. is this reasonable? surely... validates :user, presence: true validates :instrument, presence: true + validates :download_count, presence: true before_destroy :delete_s3_files validate :validate_fully_uploaded validate :validate_part_complete validate :validate_too_many_upload_failures + validate :verify_download_count before_save :sanitize_active_admin skip_callback :save, :before, :store_picture!, if: :is_skip_mount_uploader? @@ -97,6 +102,12 @@ module JamRuby end end + def verify_download_count + if (self.download_count < 0 || self.download_count > APP_CONFIG.max_audio_downloads) && !@current_user.admin + errors.add(:download_count, "must be less than or equal to 100") + end + end + def sanitize_active_admin self.user_id = nil if self.user_id == '' end @@ -187,6 +198,11 @@ module JamRuby RecordedTrack.construct_filename(self.created_at, self.recording.id, self.client_track_id) end + def update_download_count(count=1) + self.download_count = self.download_count + count + self.last_downloaded_at = Time.now + end + private def delete_s3_files diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 313e64297..a2aa8f8b4 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -43,6 +43,35 @@ module JamRuby self.comments.size end + # this can probably be done more efficiently, but David needs this asap for a video + def grouped_tracks + tracks = [] + sorted_tracks = self.recorded_tracks.sort { |a,b| a.user.id <=> b.user.id } + + t = Track.new + t.instrument_ids = [] + sorted_tracks.each_with_index do |track, index| + if index > 0 + if sorted_tracks[index-1].user.id != sorted_tracks[index].user.id + t = Track.new + t.instrument_ids = [] + t.instrument_ids << track.instrument.id + t.musician = track.user + tracks << t + else + if !t.instrument_ids.include? track.instrument.id + t.instrument_ids << track.instrument.id + end + end + else + t.musician = track.user + t.instrument_ids << track.instrument.id + tracks << t + end + end + tracks + end + def not_already_recording if music_session && music_session.is_recording? errors.add(:music_session, ValidationMessages::ALREADY_BEING_RECORDED) @@ -309,6 +338,12 @@ module JamRuby save end + + # meant to be used as a way to 'pluck' a claimed_recording appropriate for user. + def candidate_claimed_recording + claimed_recordings.where(is_public: true).first + end + private def self.validate_user_is_band_member(user, band) unless band.users.exists? user diff --git a/ruby/lib/jam_ruby/models/score.rb b/ruby/lib/jam_ruby/models/score.rb new file mode 100644 index 000000000..570d409f4 --- /dev/null +++ b/ruby/lib/jam_ruby/models/score.rb @@ -0,0 +1,29 @@ +require 'ipaddr' + +module JamRuby + class Score < ActiveRecord::Base + + self.table_name = 'scores' + + attr_accessible :alocidispid, :anodeid, :aaddr, :blocidispid, :bnodeid, :baddr, :score, :score_dt, :scorer + + default_scope order('score_dt desc') + + def self.createx(alocidispid, anodeid, aaddr, blocidispid, bnodeid, baddr, score, score_dt) + score_dt = Time.new.utc if score_dt.nil? + Score.create(alocidispid: alocidispid, anodeid: anodeid, aaddr: aaddr, blocidispid: blocidispid, bnodeid: bnodeid, baddr: baddr, score: score, scorer: 0, score_dt: score_dt) + Score.create(alocidispid: blocidispid, anodeid: bnodeid, aaddr: baddr, blocidispid: alocidispid, bnodeid: anodeid, baddr: aaddr, score: score, scorer: 1, score_dt: score_dt) if alocidispid != blocidispid + end + + def self.deletex(alocidispid, blocidispid) + Score.where(alocidispid: alocidispid, blocidispid: blocidispid).delete_all + Score.where(alocidispid: blocidispid, blocidispid: alocidispid).delete_all if alocidispid != blocidispid + end + + def self.findx(alocidispid, blocidispid) + s = Score.where(alocidispid: alocidispid, blocidispid: blocidispid).first + return -1 if s.nil? + return s.score + end + end +end diff --git a/ruby/lib/jam_ruby/models/track.rb b/ruby/lib/jam_ruby/models/track.rb index 0a14457d7..da2cc8cc3 100644 --- a/ruby/lib/jam_ruby/models/track.rb +++ b/ruby/lib/jam_ruby/models/track.rb @@ -7,7 +7,7 @@ module JamRuby default_scope order('created_at ASC') - attr_accessor :musician + attr_accessor :musician, :instrument_ids SOUND = %w(mono stereo) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 7ff867717..949360c32 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -102,6 +102,7 @@ module JamRuby validates :first_name, presence: true, length: {maximum: 50}, no_profanity: true validates :last_name, presence: true, length: {maximum: 50}, no_profanity: true + validates :biography, length: {maximum: 4000}, no_profanity: true VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX} validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 69bb70a09..a0a6cdbfd 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -171,10 +171,28 @@ FactoryGirl.define do association :user, factory: :user before(:create) { |claimed_recording| - claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user) unless claimed_recording.recording } + + end + + factory :mix, :class => JamRuby::Mix do + started_at Time.now + completed_at Time.now + ogg_md5 'abc' + ogg_length 1 + sequence(:ogg_url) { |n| "recordings/ogg/#{n}" } + mp3_md5 'abc' + mp3_length 1 + sequence(:mp3_url) { |n| "recordings/mp3/#{n}" } + completed true + + before(:create) {|mix| + user = FactoryGirl.create(:user) + mix.recording = FactoryGirl.create(:recording_with_track, owner: user) + mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording) + } end factory :musician_instrument, :class => JamRuby::MusicianInstrument do diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb index 0c0211fc7..88672dec2 100644 --- a/ruby/spec/jam_ruby/connection_manager_spec.rb +++ b/ruby/spec/jam_ruby/connection_manager_spec.rb @@ -426,6 +426,7 @@ describe ConnectionManager do end it "join_music_session fails if user has music_session already active" do + pending user_id = create_user("test", "user11", "user11@jamkazam.com") user = User.find(user_id) diff --git a/ruby/spec/jam_ruby/models/geo_ip_blocks_spec.rb b/ruby/spec/jam_ruby/models/geo_ip_blocks_spec.rb index dd914143c..9c826a7c5 100644 --- a/ruby/spec/jam_ruby/models/geo_ip_blocks_spec.rb +++ b/ruby/spec/jam_ruby/models/geo_ip_blocks_spec.rb @@ -4,16 +4,22 @@ describe GeoIpBlocks do before do GeoIpBlocks.delete_all - GeoIpBlocks.make_row(0x00000000, 0xffffffff, 17192) + GeoIpBlocks.createx(0x01020300, 0x010203ff, 1) + GeoIpBlocks.createx(0x02030400, 0x020304ff, 2) end - it "count" do GeoIpBlocks.count.should == 1 end + after do + GeoIpBlocks.delete_all + GeoIpBlocks.createx(0x00000000, 0xffffffff, 17192) + end + + it "count" do GeoIpBlocks.count.should == 2 end let(:first) { GeoIpBlocks.lookup(0x01020304) } let(:second) { GeoIpBlocks.lookup(0x02030405) } - let(:seventh) { GeoIpBlocks.lookup(9999999999) } # bogus + let(:third) { GeoIpBlocks.lookup(9999999999) } # bogus - it "first.locid" do first.locid.should == 17192 end - it "second.locid" do second.locid.should == 17192 end - it "seventh" do seventh.should be_nil end + it "first.locid" do first.locid.should == 1 end + it "second.locid" do second.locid.should == 2 end + it "third" do third.should be_nil end end diff --git a/ruby/spec/jam_ruby/models/geo_ip_locations_spec.rb b/ruby/spec/jam_ruby/models/geo_ip_locations_spec.rb new file mode 100644 index 000000000..a9a7bc3fa --- /dev/null +++ b/ruby/spec/jam_ruby/models/geo_ip_locations_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe GeoIpLocations do + + before do + GeoIpLocations.delete_all + GeoIpLocations.createx(17192, 'US', 'TX', 'Austin', '78749', 30.2076, -97.8587, 635, '512') + GeoIpLocations.createx(48086, 'MX', '28', 'Matamoros', '', 25.8833, -97.5000, nil, '') + end + + it "count" do GeoIpLocations.count.should == 2 end + + let(:first) { GeoIpLocations.lookup(17192) } + let(:second) { GeoIpLocations.lookup(48086) } + let(:third) { GeoIpLocations.lookup(999999) } # bogus + + it "first" do + first.locid.should == 17192 + first.countrycode.should eql('US') + first.region.should eql('TX') + first.city.should eql('Austin') + first.latitude.should == 30.2076 + first.longitude.should == -97.8587 + end + + it "second" do + second.locid.should == 48086 + second.countrycode.should eql('MX') + second.region.should eql('28') + second.city.should eql('Matamoros') + second.latitude.should == 25.8833 + second.longitude.should == -97.5000 + end + + it "third" do third.should be_nil end +end diff --git a/ruby/spec/jam_ruby/models/get_work_spec.rb b/ruby/spec/jam_ruby/models/get_work_spec.rb new file mode 100644 index 000000000..015d1e6a3 --- /dev/null +++ b/ruby/spec/jam_ruby/models/get_work_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe GetWork do + + before(:each) do + + end + + it "get_work_1" do + x = GetWork.get_work(1) + puts x.inspect + x.should be_nil + end + + it "get_work_list_1" do + x = GetWork.get_work_list(1) + puts x.inspect + x.should eql([]) + end +end \ No newline at end of file diff --git a/ruby/spec/jam_ruby/models/jam_isp_spec.rb b/ruby/spec/jam_ruby/models/jam_isp_spec.rb index b68b309f7..4d88f23c5 100644 --- a/ruby/spec/jam_ruby/models/jam_isp_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_isp_spec.rb @@ -4,17 +4,17 @@ describe JamIsp do before do JamIsp.delete_all - JamIsp.make_row(0x01020300, 0x010203ff, 1) - JamIsp.make_row(0x02030400, 0x020304ff, 2) - JamIsp.make_row(0x03040500, 0x030405ff, 3) - JamIsp.make_row(0x04050600, 0x040506ff, 4) - JamIsp.make_row(0xc0A80100, 0xc0A801ff, 5) - JamIsp.make_row(0xfffefd00, 0xfffefdff, 6) + JamIsp.createx(0x01020300, 0x010203ff, 1) + JamIsp.createx(0x02030400, 0x020304ff, 2) + JamIsp.createx(0x03040500, 0x030405ff, 3) + JamIsp.createx(0x04050600, 0x040506ff, 4) + JamIsp.createx(0xc0A80100, 0xc0A801ff, 5) + JamIsp.createx(0xfffefd00, 0xfffefdff, 6) end after do JamIsp.delete_all - JamIsp.make_row(0x00000000, 0xffffffff, 1) + JamIsp.createx(0x00000000, 0xffffffff, 1) end it "count" do JamIsp.count.should == 6 end diff --git a/ruby/spec/jam_ruby/models/mix_spec.rb b/ruby/spec/jam_ruby/models/mix_spec.rb index 150b305e8..7296140a1 100755 --- a/ruby/spec/jam_ruby/models/mix_spec.rb +++ b/ruby/spec/jam_ruby/models/mix_spec.rb @@ -63,6 +63,16 @@ describe Mix do recordings.length.should == 0 end + + describe "download count" do + it "will fail if too high" do + mix = FactoryGirl.create(:mix) + mix.current_user = mix.recording.owner + mix.update_download_count(APP_CONFIG.max_audio_downloads + 1) + mix.save + mix.errors[:download_count].should == ["must be less than or equal to 100"] + end + end end diff --git a/ruby/spec/jam_ruby/models/score_spec.rb b/ruby/spec/jam_ruby/models/score_spec.rb new file mode 100644 index 000000000..84efb6127 --- /dev/null +++ b/ruby/spec/jam_ruby/models/score_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe Score do + + before do + Score.delete_all + Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil) + Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil) + Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600) + end + + it "count" do + Score.count.should == 6 + end + + it 'a to b' do + s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first + s.should_not be_nil + s.alocidispid.should == 1234 + s.anodeid.should eql('anodeid') + s.aaddr.should == 0x01020304 + s.blocidispid.should == 2345 + s.bnodeid.should eql('bnodeid') + s.baddr.should == 0x02030405 + s.score.should == 20 + s.scorer.should == 0 + s.score_dt.should_not be_nil + end + + it 'b to a' do + s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first + s.should_not be_nil + s.alocidispid.should == 2345 + s.anodeid.should eql('bnodeid') + s.aaddr.should == 0x02030405 + s.blocidispid.should == 1234 + s.bnodeid.should eql('anodeid') + s.baddr.should == 0x01020304 + s.score.should == 20 + s.scorer.should == 1 + s.score_dt.should_not be_nil + end + + it 'a to c' do + s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first + s.should_not be_nil + s.alocidispid.should == 1234 + s.anodeid.should eql('anodeid') + s.aaddr.should == 0x01020304 + s.blocidispid.should == 3456 + s.bnodeid.should eql('cnodeid') + s.baddr.should == 0x03040506 + s.score.should == 30 + s.scorer.should == 0 + s.score_dt.should_not be_nil + end + + it 'c to a' do + s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first + s.should_not be_nil + s.alocidispid.should == 3456 + s.anodeid.should eql('cnodeid') + s.aaddr.should == 0x03040506 + s.blocidispid.should == 1234 + s.bnodeid.should eql('anodeid') + s.baddr.should == 0x01020304 + s.score.should == 30 + s.scorer.should == 1 + s.score_dt.should_not be_nil + end + + it 'delete a to c' do + Score.deletex(1234, 3456) + Score.count.should == 2 + Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil + Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil + Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil + Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil + end + + it 'findx' do + Score.findx(1234, 1234).should == -1 + Score.findx(1234, 2345).should == 20 + Score.findx(1234, 3456).should == 30 + + Score.findx(2345, 1234).should == 20 + Score.findx(2345, 2345).should == -1 + Score.findx(2345, 3456).should == -1 + + Score.findx(3456, 1234).should == 30 + Score.findx(3456, 2345).should == -1 + Score.findx(3456, 3456).should == -1 + end + +end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 60f32513b..f1df45da8 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -13,6 +13,9 @@ SpecDb::recreate_database # initialize ActiveRecord's db connection ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) +# so jam_ruby models that use APP_CONFIG in metadata will load. this is later stubbed pre test run +APP_CONFIG = app_config + require 'jam_ruby' require 'factory_girl' require 'rubygems' diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index ef2e28292..564ec7b34 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -101,6 +101,14 @@ def app_config '315576000' end + def max_audio_downloads + 100 + end + + def send_join_session_email_notifications + true + end + private def audiomixer_workspace_path diff --git a/web/app/assets/javascripts/accounts_identity.js b/web/app/assets/javascripts/accounts_identity.js index 66a1d80a6..7d7ef1019 100644 --- a/web/app/assets/javascripts/accounts_identity.js +++ b/web/app/assets/javascripts/accounts_identity.js @@ -209,7 +209,7 @@ var password_confirmation_errors = context.JK.format_errors("password_confirmation", errors) if(current_password_errors != null) { - $('#account-edit-password-form #account-forgot-password').closest('div.field').addClass('error').end().after(current_password_errors); + $('#account-edit-password-form input[name=current_password]').closest('div.field').addClass('error').end().after(current_password_errors); } if(password_errors != null) { diff --git a/web/app/assets/javascripts/bandProfile.js b/web/app/assets/javascripts/bandProfile.js index 20ff38afc..7027661ea 100644 --- a/web/app/assets/javascripts/bandProfile.js +++ b/web/app/assets/javascripts/bandProfile.js @@ -47,55 +47,53 @@ /****************** MAIN PORTION OF SCREEN *****************/ function addFollowing(isBand, id) { - var newFollowing = {}; + var newFollowing = {}; - if (!isBand) { - newFollowing.user_id = id; - } - else { - newFollowing.band_id = id; - } + if (!isBand) { + newFollowing.user_id = id; + } + else { + newFollowing.band_id = id; + } - rest.addFollowing(newFollowing) - .done(function() { - if (isBand) { - var newCount = parseInt($("#band-profile-follower-stats").text()) + 1; - var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower"; - $('#band-profile-follower-stats').html(newCount + text); - configureBandFollowingButton(true); - } - else { - configureMemberFollowingButton(true, id); - } - }) - .fail(app.ajaxError); + rest.addFollowing(newFollowing) + .done(function() { + if (isBand) { + var newCount = parseInt($("#band-profile-follower-stats").text()) + 1; + var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower"; + $('#band-profile-follower-stats').html(newCount + text); + configureBandFollowingButton(true); + } + else { + configureMemberFollowingButton(true, id); + } + renderActive(); + }) + .fail(app.ajaxError); } function removeFollowing(isBand, id) { - var following = {}; - following.target_entity_id = id; - - rest.removeFollowing(following) - .done(function() { - renderActive(); // refresh stats - if (isBand) { - var newCount = parseInt($("#band-profile-follower-stats").text()) - 1; - var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower"; - $('#band-profile-follower-stats').html(newCount + text); - configureBandFollowingButton(false); - } - else { - configureMemberFollowingButton(false, id); - } - }) - .fail(app.ajaxError); + rest.removeFollowing(id) + .done(function() { + if (isBand) { + var newCount = parseInt($("#band-profile-follower-stats").text()) - 1; + var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower"; + $('#band-profile-follower-stats').html(newCount + text); + configureBandFollowingButton(false); + } + else { + configureMemberFollowingButton(false, id); + } + renderActive(); + }) + .fail(app.ajaxError); } function configureBandFollowingButton(following) { $('#btn-follow-band').unbind("click"); if (following) { - $('#btn-follow-band').text('STOP FOLLOWING'); + $('#btn-follow-band').text('UNFOLLOW'); $('#btn-follow-band').click(function() { removeFollowing(true, bandId); return false; @@ -121,7 +119,7 @@ $btnFollowMember.unbind("click"); if (following) { - $btnFollowMember.text('UN-FOLLOW'); + $btnFollowMember.text('UNFOLLOW'); $btnFollowMember.click(function() { removeFollowing(false, userId); return false; diff --git a/web/app/assets/javascripts/feed_item_recording.js b/web/app/assets/javascripts/feed_item_recording.js new file mode 100644 index 000000000..55187e8a6 --- /dev/null +++ b/web/app/assets/javascripts/feed_item_recording.js @@ -0,0 +1,60 @@ +(function(context, $) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.FeedItemRecording = function($parentElement, options){ + + var $feedItem = $parentElement; + var $description = $('.description', $feedItem) + var $musicians = $('.musician-detail', $feedItem) + + var toggledOpen = false; + if(!$feedItem.is('.feed-entry')) { + throw "$parentElement must be a .feed-entry" + } + + function toggleDetails() { + if(toggledOpen) { + $feedItem.css('height', $feedItem.height() + 'px') + $feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() { + $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height')); + + $musicians.hide(); + $description.css('height', $description.data('original-height')); + $description.dotdotdot(); + }); + } + else { + $description.trigger('destroy.dot'); + $description.data('original-height', $description.css('height')).css('height', 'auto'); + $musicians.show(); + $feedItem.animate({'max-height': '1000px'}); + } + + toggledOpen = !toggledOpen; + + return false; + } + + function events() { + $('.details', $feedItem).click(toggleDetails); + $('.details-arrow', $feedItem).click(toggleDetails); + } + + function initialize() { + $('.timeago', $feedItem).timeago(); + $('.dotdotdot', $feedItem).dotdotdot(); + context.JK.prettyPrintElements($('time.duration', $feedItem)); + context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem)); + + $feedItem.data('original-max-height', $feedItem.css('height')); + + events(); + } + + initialize(); + + return this; + } +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/feed_item_session.js b/web/app/assets/javascripts/feed_item_session.js new file mode 100644 index 000000000..329f154f2 --- /dev/null +++ b/web/app/assets/javascripts/feed_item_session.js @@ -0,0 +1,123 @@ +(function(context, $) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.FeedItemSessionTimer = null; + context.JK.FeedItemSession = function($parentElement, options){ + + var logger = context.JK.logger; + var rest = new context.JK.Rest(); + + var $feedItem = $parentElement; + var $description = $('.description', $feedItem) + var $musicians = $('.musician-detail', $feedItem) + var $controls = $('.session-controls', $feedItem); + var $status = $('.session-status', $feedItem); + var playing = false; + var toggledOpen = false; + var musicSessionId = $feedItem.attr('data-music-session'); + + if(!$feedItem.is('.feed-entry')) { + throw "$parentElement must be a .feed-entry" + } + + function startPlay() { + var img = $('.play-icon', $feedItem); + img.attr('src', '/assets/content/icon_pausebutton.png'); + $controls.trigger('play.listenBroadcast'); + playing = true; + } + + function stopPlay() { + var img = $('.play-icon', $feedItem); + img.attr('src', '/assets/content/icon_playbutton.png'); + $controls.trigger('pause.listenBroadcast'); + playing = false; + } + function togglePlay() { + if(playing) { + $status.text('SESSION IN PROGRESS'); + stopPlay(); + } + else { + startPlay(); + } + return false; + } + + function toggleDetails() { + if(toggledOpen) { + $feedItem.css('height', $feedItem.height() + 'px') + $feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() { + $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height')); + + $musicians.hide(); + $description.css('height', $description.data('original-height')); + $description.dotdotdot(); + }); + } + else { + $description.trigger('destroy.dot'); + $description.data('original-height', $description.css('height')).css('height', 'auto'); + $musicians.show(); + $feedItem.animate({'max-height': '1000px'}); + } + + toggledOpen = !toggledOpen; + + return false; + } + + function stateChange(e, data) { + if(data.displayText) $status.text(data.displayText); + + if(data.isEnd) stopPlay(); + + if(data.isSessionOver) { + $controls.removeClass('inprogress').addClass('ended') + } + } + + function events() { + $('.details', $feedItem).click(toggleDetails); + $('.details-arrow', $feedItem).click(toggleDetails); + $('.play-button', $feedItem).click(togglePlay); + + $controls.bind('statechange.listenBroadcast', stateChange); + } + + function initialize() { + $('.timeago', $feedItem).timeago(); + $('.dotdotdot', $feedItem).dotdotdot(); + $controls.listenBroadcast(); + + context.JK.prettyPrintElements($('time.duration', $feedItem)); + context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem)); + + $feedItem.data('original-max-height', $feedItem.css('height')); + + events(); + + + // this is a bit lame, because this is a singleton. + // the idea is, if there are any session widgets on the page, then we start ticking time + if(!context.JK.FeedItemSessionTimer) { + context.JK.FeedItemSessionTimer = setInterval(function() { + $.each($('.feed-entry.music-session-history-entry .inprogress .session-duration'), function(index, item) { + var $duration = $(item); + + var createdAt = new Date(Number($duration.attr('data-created-at')) * 1000) + var millisElapsed = (new Date()).getTime() - createdAt.getTime(); + console.log("createdAt, millis", createdAt, millisElapsed); + $duration.text(context.JK.prettyPrintSeconds( parseInt(millisElapsed / 1000))); + }); + }, 333); + } + } + + initialize(); + + return this; + } +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/findMusician.js b/web/app/assets/javascripts/findMusician.js index e96bb7329..f97ffd7da 100644 --- a/web/app/assets/javascripts/findMusician.js +++ b/web/app/assets/javascripts/findMusician.js @@ -108,9 +108,9 @@ for (var jj=0, ilen=mm['followings'].length; jj' + diff --git a/web/app/assets/javascripts/hoverBand.js b/web/app/assets/javascripts/hoverBand.js index fc6ca414c..2b98e8e62 100644 --- a/web/app/assets/javascripts/hoverBand.js +++ b/web/app/assets/javascripts/hoverBand.js @@ -49,20 +49,23 @@ }); var bandHtml = context.JK.fillTemplate(template, { - avatar_url: context.JK.resolveBandAvatarUrl(response.photo_url), - name: response.name, - location: response.location, - genres: genres.join(', '), - musicians: musicianHtml, - like_count: response.liker_count, - follower_count: response.follower_count, - recording_count: response.recording_count, - session_count: response.session_count, - biography: response.biography, - profile_url: "/client#/bandProfile/" + response.id + bandId: response.id, + avatar_url: context.JK.resolveBandAvatarUrl(response.photo_url), + name: response.name, + location: response.location, + genres: genres.join(', '), + musicians: musicianHtml, + like_count: response.liker_count, + follower_count: response.follower_count, + recording_count: response.recording_count, + session_count: response.session_count, + biography: response.biography, + followAction: response.is_following ? "removeBandFollowing" : "addBandFollowing", + profile_url: "/client#/bandProfile/" + response.id }); $(hoverSelector).append('

Band Detail

' + bandHtml); + configureActionButtons(response); }) .fail(function(xhr) { if(xhr.status >= 500) { @@ -77,6 +80,20 @@ }); }; + function configureActionButtons(band) { + var btnFollowSelector = "#btnFollow"; + + // if unauthenticated or authenticated user is viewing his own profile + if (!context.JK.currentUserId) { + $(btnFollowSelector, hoverSelector).hide(); + } + else { + if (band.is_following) { + $(btnFollowSelector, hoverSelector).html('UNFOLLOW'); + } + } + } + this.hideBubble = function() { $(hoverSelector).hide(); }; diff --git a/web/app/assets/javascripts/hoverFan.js b/web/app/assets/javascripts/hoverFan.js index b23342180..44d6ba075 100644 --- a/web/app/assets/javascripts/hoverFan.js +++ b/web/app/assets/javascripts/hoverFan.js @@ -56,12 +56,15 @@ location: response.location, friend_count: response.friend_count, follower_count: response.follower_count, + friendAction: response.is_friend ? "removeFanFriend" : (response.pending_friend_request ? "" : "sendFanFriendRequest"), + followAction: response.is_following ? "removeFanFollowing" : "addFanFollowing", biography: response.biography, followings: response.followings && response.followings.length > 0 ? followingHtml : "N/A", profile_url: "/client#/profile/" + response.id }); $(hoverSelector).append('

Fan Detail

' + fanHtml); + configureActionButtons(response); }) .fail(function(xhr) { if(xhr.status >= 500) { @@ -76,6 +79,33 @@ }); }; + function configureActionButtons(user) { + var btnFriendSelector = "#btnFriend"; + var btnFollowSelector = "#btnFollow"; + + if (!context.JK.currentUserId || context.JK.currentUserId === user.id) { + $(btnFriendSelector, hoverSelector).hide(); + $(btnFollowSelector, hoverSelector).hide(); + } + else { + if (user.is_friend) { + $(btnFriendSelector, hoverSelector).html('DISCONNECT'); + } + + if (user.is_following) { + $(btnFollowSelector, hoverSelector).html('UNFOLLOW'); + + $(btnFollowSelector, hoverSelector).click(function(evt) { + rest.removeFollowing(user.id); + }); + } + + if (user.pending_friend_request) { + $(btnFriendSelector, hoverSelector).hide(); + } + } + } + this.hideBubble = function() { $(hoverSelector).hide(); }; diff --git a/web/app/assets/javascripts/hoverMusician.js b/web/app/assets/javascripts/hoverMusician.js index 5a1e6d427..8e0fb930b 100644 --- a/web/app/assets/javascripts/hoverMusician.js +++ b/web/app/assets/javascripts/hoverMusician.js @@ -58,9 +58,15 @@ var sessionDisplayStyle = 'none'; var sessionId = ''; + var joinDisplayStyle = 'none'; if (response.sessions !== undefined && response.sessions.length > 0) { sessionDisplayStyle = 'block'; - sessionId = response.sessions[0].id; + var session = response.sessions[0]; + sessionId = session.id; + + if (context.jamClient && session.musician_access) { + joinDisplayStyle = 'inline'; + } } var musicianHtml = context.JK.fillTemplate(template, { @@ -74,13 +80,17 @@ recording_count: response.recording_count, session_count: response.session_count, session_display: sessionDisplayStyle, - session_id: sessionId, + join_display: joinDisplayStyle, + sessionId: sessionId, + friendAction: response.is_friend ? "removeMusicianFriend" : (response.pending_friend_request ? "" : "sendMusicianFriendRequest"), + followAction: response.is_following ? "removeMusicianFollowing" : "addMusicianFollowing", biography: response.biography, followings: response.followings && response.followings.length > 0 ? followingHtml : "N/A", profile_url: "/client#/profile/" + response.id }); $(hoverSelector).append('

Musician Detail

' + musicianHtml); + configureActionButtons(response); }) .fail(function(xhr) { if(xhr.status >= 500) { @@ -95,6 +105,28 @@ }); }; + function configureActionButtons(user) { + var btnFriendSelector = "#btnFriend"; + var btnFollowSelector = "#btnFollow"; + + // if unauthenticated or authenticated user is viewing his own profile + if (!context.JK.currentUserId || context.JK.currentUserId === user.id) { + $(btnFriendSelector, hoverSelector).hide(); + $(btnFollowSelector, hoverSelector).hide(); + } + else { + if (user.is_friend) { + $(btnFriendSelector, hoverSelector).html('DISCONNECT'); + } + if (user.is_following) { + $(btnFollowSelector, hoverSelector).html('UNFOLLOW'); + } + if (user.pending_friend_request) { + $(btnFriendSelector, hoverSelector).hide(); + } + } + } + this.hideBubble = function() { $(hoverSelector).hide(); }; diff --git a/web/app/assets/javascripts/hoverRecording.js b/web/app/assets/javascripts/hoverRecording.js index a3c43508a..6d32b8ffa 100644 --- a/web/app/assets/javascripts/hoverRecording.js +++ b/web/app/assets/javascripts/hoverRecording.js @@ -8,6 +8,36 @@ var instrumentLogoMap = context.JK.getInstrumentIconMap24(); var hoverSelector = "#recording-hover"; + function deDupTracks(recordedTracks) { + var tracks = []; + + // this is replicated in recording.rb model + var t = {}; + t.instrument_ids = [] + $.each(recordedTracks, function(index, track) { + if (index > 0) { + if (recordedTracks[index-1].user.id !== recordedTracks[index].user.id) { + t = {}; + t.instrument_ids = []; + t.instrument_ids.push(track.instrument_id); + t.user = track.user; + tracks.push(t); + } + else { + if ($.inArray(track.instrument_id, t.instrument_ids)) { + t.instrument_ids.push(track.instrument_id); + } + } + } + else { + t.user = track.user; + t.instrument_ids.push(track.instrument_id); + tracks.push(t); + } + }); + return tracks; + } + this.showBubble = function() { $(hoverSelector).css({left: position.left-100, top: position.top+20}); $(hoverSelector).fadeIn(500); @@ -18,9 +48,11 @@ var recording = response.recording; $(hoverSelector).html(''); + var deDupedTracks = deDupTracks(recording.recorded_tracks); + // musicians var musicianHtml = ''; - $.each(recording.recorded_tracks, function(index, val) { + $.each(deDupedTracks, function(index, val) { var instrumentHtml = ''; var musician = val.user; @@ -28,7 +60,9 @@ musicianHtml += '' + musician.name + ''; instrumentHtml = '
'; - instrumentHtml += ' '; + $.each(val.instrument_ids, function(index, val) { + instrumentHtml += '  '; + }) instrumentHtml += '
'; musicianHtml += instrumentHtml; @@ -44,7 +78,7 @@ name: claimedRecording.name, genre: claimedRecording.genre_id.toUpperCase(), created_at: context.JK.formatDateTime(recording.created_at), - description: response.description, + description: response.description ? response.description : "", play_count: recording.play_count, comment_count: recording.comment_count, like_count: recording.like_count, @@ -55,6 +89,7 @@ }); $(hoverSelector).append('

Recording Detail

' + recordingHtml); + toggleActionButtons(); }) .fail(function(xhr) { if(xhr.status >= 500) { @@ -69,6 +104,13 @@ }); }; + function toggleActionButtons() { + if (!context.JK.currentUserId) { + $("#btnLike", hoverSelector).hide(); + $("#btnShare", hoverSelector).hide(); + } + } + this.hideBubble = function() { $(hoverSelector).hide(); }; diff --git a/web/app/assets/javascripts/hoverSession.js b/web/app/assets/javascripts/hoverSession.js index 055d8cf17..373bd0cc6 100644 --- a/web/app/assets/javascripts/hoverSession.js +++ b/web/app/assets/javascripts/hoverSession.js @@ -50,6 +50,7 @@ }); $(hoverSelector).append('

Session Detail

' + sessionHtml); + toggleActionButtons(); }) .fail(function(xhr) { if(xhr.status >= 500) { @@ -64,6 +65,13 @@ }); }; + function toggleActionButtons() { + if (!context.JK.currentUserId) { + $("#btnLike", hoverSelector).hide(); + $("#btnShare", hoverSelector).hide(); + } + } + this.hideBubble = function() { $(hoverSelector).hide(); }; diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 5671ce513..4a8a3b564 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -451,7 +451,7 @@ }); } - function removeLike(options) { + function removeLike(likableId, options) { var id = getId(options); return $.ajax({ type: "DELETE", @@ -476,15 +476,13 @@ }); } - function removeFollowing(options) { + function removeFollowing(followableId, options) { var id = getId(options); - return $.ajax({ type: "DELETE", dataType: "json", contentType: 'application/json', - url: "/api/users/" + id + "/followings", - data: JSON.stringify(options), + url: "/api/users/" + id + "/followings/" + followableId, processData: false }); } diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 49311fecc..2d184efb6 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -198,12 +198,13 @@ * Generic error handler for Ajax calls. */ function ajaxError(jqXHR, textStatus, errorMessage) { - logger.error("Unexpected ajax error: " + textStatus); if (jqXHR.status == 404) { + logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage); app.notify({title: "Oops!", text: "What you were looking for is gone now."}); } else if (jqXHR.status = 422) { + logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText); // present a nicer message try { var text = "