diff --git a/admin/Gemfile b/admin/Gemfile index 74f6a2125..5ec8a3411 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -37,7 +37,6 @@ gem 'bootstrap-will_paginate', '0.0.6' gem 'carrierwave', '0.9.0' gem 'carrierwave_direct' gem 'uuidtools', '2.1.2' -gem 'bcrypt-ruby', '3.0.1' gem 'jquery-rails' # , '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet gem 'jquery-ui-rails' gem 'rails3-jquery-autocomplete' diff --git a/admin/app/views/admin/recordings/_form.html.haml b/admin/app/views/admin/recordings/_form.html.haml index 880f26823..2c1bb2ce0 100644 --- a/admin/app/views/admin/recordings/_form.html.haml +++ b/admin/app/views/admin/recordings/_form.html.haml @@ -26,6 +26,8 @@ = f.input :band, :as => :autocomplete, :url => autocomplete_band_name_admin_bands_path, :input_html => { :id => "jam_ruby_recording_band", :name => "", :id_element => "#jam_ruby_recording_band_id" } = f.input :band_id, :as => :hidden, :input_html => { :name => "jam_ruby_recording[band_id]" } + = f.input :duration, :hint => 'how long the recording is (in seconds)' + = f.semantic_fields_for :recorded_tracks do |recorded_track, index| = render 'recorded_track_fields', f: recorded_track .links diff --git a/db/manifest b/db/manifest index def0d818f..6d546452d 100755 --- a/db/manifest +++ b/db/manifest @@ -113,4 +113,5 @@ feed_use_recording.sql feed_autoincrement_primary_key.sql music_sessions_plays.sql plays_likes_counters.sql -add_upright_bass.sql \ No newline at end of file +add_upright_bass.sql +music_session_history_public.sql \ No newline at end of file diff --git a/db/up/music_session_history_public.sql b/db/up/music_session_history_public.sql new file mode 100644 index 000000000..cbff9b62a --- /dev/null +++ b/db/up/music_session_history_public.sql @@ -0,0 +1 @@ +ALTER TABLE music_sessions_history ADD COLUMN fan_access BOOLEAN NOT NULL; \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 6997821b9..973ed5efe 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -57,6 +57,7 @@ require "jam_ruby/models/feedback" require "jam_ruby/models/feedback_observer" require "jam_ruby/models/max_mind_geo" require "jam_ruby/models/max_mind_isp" +require "jam_ruby/models/band_genre" require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/user_observer" diff --git a/ruby/lib/jam_ruby/constants/limits.rb b/ruby/lib/jam_ruby/constants/limits.rb index 52e2036f1..a0932a389 100644 --- a/ruby/lib/jam_ruby/constants/limits.rb +++ b/ruby/lib/jam_ruby/constants/limits.rb @@ -1,5 +1,9 @@ module Limits + # session genres + MIN_GENRES_PER_SESSION = 1 + MAX_GENRES_PER_SESSION = 3 + # band genres MIN_GENRES_PER_BAND = 1 MAX_GENRES_PER_BAND = 3 diff --git a/ruby/lib/jam_ruby/constants/validation_messages.rb b/ruby/lib/jam_ruby/constants/validation_messages.rb index 8c2b8d3df..4a308798b 100644 --- a/ruby/lib/jam_ruby/constants/validation_messages.rb +++ b/ruby/lib/jam_ruby/constants/validation_messages.rb @@ -20,8 +20,13 @@ module ValidationMessages SESSION_NOT_FOUND = "Session not found." # genres - GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed." - GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + RECORDING_GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed." + BAND_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + SESSION_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + + RECORDING_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + BAND_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + SESSION_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." # instruments INSTRUMENT_LIMIT_EXCEEDED = "No more than 5 instruments are allowed." diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index 88d97bc14..ea14cabe5 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -5,13 +5,20 @@ module JamRuby :country, :original_fpfile_photo, :cropped_fpfile_photo, :cropped_large_fpfile_photo, :cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url - attr_accessor :updating_photo + attr_accessor :updating_photo, :skip_location_validation self.primary_key = 'id' before_save :stringify_photo_info , :if => :updating_photo + validates :biography, no_profanity: true, presence:true + validates :name, presence: true + validates :country, presence: true, :unless => :skip_location_validation + validates :state, presence: true, :unless => :skip_location_validation + validates :city, presence: true, :unless => :skip_location_validation + validate :validate_photo_info - validates :biography, no_profanity: true + validate :require_at_least_one_genre + validate :limit_max_genres before_save :check_lat_lng before_save :check_website_url @@ -21,7 +28,8 @@ module JamRuby has_many :users, :through => :band_musicians, :class_name => "JamRuby::User" # genres - has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" + has_many :band_genres, class_name: "JamRuby::BandGenre" + has_many :genres, class_name: "JamRuby::Genre", :through => :band_genres # recordings has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id" @@ -59,8 +67,7 @@ module JamRuby end def recent_history - recordings = ClaimedRecording.joins(:recording) - .where(:recordings => {:band_id => "#{self.id}"}) + recordings = Recording.where(:band_id => self.id) .order('created_at DESC') .limit(10) @@ -123,74 +130,52 @@ module JamRuby return recordings end + def self.build_band(user, params) + + id = params[:id] + + # ensure person creating this Band is a Musician + unless user.musician? + raise PermissionError, "must be a musician" + end + + band = id.blank? ? Band.new : Band.find(id) + + # ensure user updating Band details is a Band member + unless band.new_record? || band.users.exists?(user) + raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR + end + + band.name = params[:name] if params.has_key?(:name) + band.website = params[:website] if params.has_key?(:website) + band.biography = params[:biography] if params.has_key?(:biography) + band.city = params[:city] if params.has_key?(:city) + band.state = params[:state] if params.has_key?(:state) + band.country = params[:country] if params.has_key?(:country) + band.photo_url = params[:photo_url] if params.has_key?(:photo_url) + band.logo_url = params[:logo_url] if params.has_key?(:logo_url) + + if params.has_key?(:genres) && params[:genres] + # loop through each genre in the array and save to the db + genres = [] + params[:genres].each { |genre_id| genres << Genre.find(genre_id) } + band.genres = genres + end + + + band + end + # helper method for creating / updating a Band - def self.save(id, name, website, biography, city, state, country, genres, user_id, photo_url, logo_url) - user = User.find(user_id) + def self.save(user, params) + band = build_band(user, params) - # new band - if id.blank? - - # ensure person creating this Band is a Musician - unless user.musician? - raise JamRuby::PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR - end - - validate_genres(genres, false) - band = Band.new() - - # band update - else - validate_genres(genres, true) - band = Band.find(id) - - # ensure user updating Band details is a Band member - unless band.users.exists? user - raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR - end + if band.save + # add the creator as the admin + BandMusician.create(:band_id => band.id, :user_id => user.id, :admin => true) if params[:id].blank? end - # name - band.name = name unless name.nil? - - # website - band.website = website unless website.nil? - - # biography - band.biography = biography unless biography.nil? - - # city - band.city = city unless city.nil? - - # state - band.state = state unless state.nil? - - # country - band.country = country unless country.nil? - - # photo url - band.photo_url = photo_url unless photo_url.nil? - - # logo url - band.logo_url = logo_url unless logo_url.nil? - - # band.updated_at = Time.now.getutc - band.save! - band.reload - - # genres - unless genres.nil? - ActiveRecord::Base.transaction do - # delete all genres for this band first - band.genres.delete_all if id.present? - # loop through each genre in the array and save to the db - genres.each { |genre_id| band.genres << Genre.find(genre_id) } - end - end - - # add the creator as the admin - BandMusician.create(:band_id => band.id, :user_id => user_id, :admin => true) if id.blank? - - return band + band end def escape_filename(path) @@ -274,21 +259,16 @@ module JamRuby end private - def self.validate_genres(genres, is_nil_ok) - if is_nil_ok && genres.nil? - return + + def require_at_least_one_genre + if self.genres.size < Limits::MIN_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET) end + end - if genres.nil? - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET - else - if genres.size < Limits::MIN_GENRES_PER_BAND - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET - end - - if genres.size > Limits::MAX_GENRES_PER_BAND - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED - end + def limit_max_genres + if self.genres.size > Limits::MAX_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED) end end diff --git a/ruby/lib/jam_ruby/models/band_genre.rb b/ruby/lib/jam_ruby/models/band_genre.rb new file mode 100644 index 000000000..9bd4051d4 --- /dev/null +++ b/ruby/lib/jam_ruby/models/band_genre.rb @@ -0,0 +1,11 @@ +module JamRuby + class BandGenre < ActiveRecord::Base + + self.table_name = "bands_genres" + + self.primary_key = 'id' + + belongs_to :user, class_name: "JamRuby::User" + belongs_to :genre, class_name: "JamRuby::Genre" + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index fd930102f..43f31c430 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -7,10 +7,10 @@ module JamRuby FIXNUM_MAX = (2**(0.size * 8 -2) -1) SORT_TYPES = ['date', 'plays', 'likes'] - TIME_RANGES = ['today', 'week', 'month', 'all'] - TYPE_FILTERS = ['session', 'recording', 'all'] + TIME_RANGES = { "today" => 1 , "week" => 7, "month" => 30, "all" => 0} + TYPE_FILTERS = ['music_session_history', 'recording', 'all'] - def self.index(params = {}) + def self.index(user, params = {}) limit = params[:limit] limit ||= 20 limit = limit.to_i @@ -20,7 +20,7 @@ module JamRuby sort ||= 'date' raise "not valid sort #{sort}" unless SORT_TYPES.include?(sort) - start = params[:start] + start = params[:start].presence if sort == 'date' start ||= FIXNUM_MAX else @@ -30,18 +30,24 @@ module JamRuby time_range = params[:time_range] time_range ||= 'month' - raise "not valid time_range #{time_range}" unless TIME_RANGES.include?(time_range) + raise "not valid time_range #{time_range}" unless TIME_RANGES.has_key?(time_range) type_filter = params[:type] type_filter ||= 'all' raise "not valid type #{type_filter}" unless TYPE_FILTERS.include?(type_filter) - query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) + target_user = params[:user] + target_band = params[:band] + + #query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) + query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id") + .joins("LEFT OUTER JOIN music_sessions_history ON music_sessions_history.id = feeds.music_session_id") + .limit(limit) # handle sort if sort == 'date' - query = query.where("id < #{start}") - query = query.order('id DESC') + query = query.where("feeds.id < #{start}") + query = query.order('feeds.id DESC') elsif sort == 'plays' query = query.offset(start) query = query.order("COALESCE(recordings.play_count, music_sessions_history.play_count) DESC ") @@ -53,14 +59,71 @@ module JamRuby end # handle time range + days = TIME_RANGES[time_range] + if days > 0 + query = query.where("feeds.created_at > NOW() - '#{days} day'::INTERVAL") + end # handle type filters - if type_filter == 'session' - query = query.where('music_session_id is not NULL') + if type_filter == 'music_session_history' + query = query.where('feeds.music_session_id is not NULL') elsif type_filter == 'recording' - query = query.where('recording_id is not NULL') + query = query.where('feeds.recording_id is not NULL') end + + if target_user + + if target_user != user.id + require_public_recordings = "claimed_recordings.is_public = TRUE AND" + require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + end + + query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))") + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') + query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + + elsif target_band + + unless Band.find(target_band).users.include?(user) + require_public_recordings = "claimed_recordings.is_public = TRUE AND" + require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + end + + query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} recordings.band_id = '#{target_band}'") + query = query.where("music_sessions_history IS NULL OR #{require_public_sessions} music_sessions_history.band_id = '#{target_band}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') + #query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + else + query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.is_public = TRUE') + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND music_sessions_history.fan_access = TRUE") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.is_public = TRUE') + query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + end + + + + + if query.length == 0 [query, nil] elsif query.length < limit diff --git a/ruby/lib/jam_ruby/models/genre.rb b/ruby/lib/jam_ruby/models/genre.rb index 45776c59f..18fef77b8 100644 --- a/ruby/lib/jam_ruby/models/genre.rb +++ b/ruby/lib/jam_ruby/models/genre.rb @@ -4,7 +4,8 @@ module JamRuby self.primary_key = 'id' # bands - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_genres" + has_many :band_genres, class_name: "JamRuby::BandGenre" + has_many :bands, class_name: "JamRuby::Band", :through => :band_genres # genres has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 57ebcee50..c32e2650e 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -24,7 +24,7 @@ module JamRuby has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" - after_save :require_at_least_one_genre, :limit_max_genres + after_save :require_at_least_one_genre, :limit_max_genres, :sync_music_session_history after_destroy do |obj| JamRuby::MusicSessionHistory.removed_music_session(obj.id) @@ -264,18 +264,22 @@ module JamRuby def require_at_least_one_genre unless skip_genre_validation - if self.genres.count < Limits::MIN_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) + if self.genres.count < Limits::MIN_GENRES_PER_SESSION + errors.add(:genres, ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET) end end end def limit_max_genres unless skip_genre_validation - if self.genres.count > Limits::MAX_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) + if self.genres.count > Limits::MAX_GENRES_PER_SESSION + errors.add(:genres, ValidationMessages::SESSION_GENRE_LIMIT_EXCEEDED) end end end + + def sync_music_session_history + MusicSessionHistory.save(self) + end end end diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb index 29be1f4e1..852f73f3f 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -19,7 +19,7 @@ module JamRuby :class_name => 'JamRuby::MusicSession', :foreign_key => 'music_session_id') - has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id" + has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "session_id" has_many :plays, :class_name => "JamRuby::MusicSessionPlay", :foreign_key => "music_session_id" @@ -43,7 +43,6 @@ module JamRuby self.comments.size end - def tracks tracks = [] self.music_session_user_histories.each do |msuh| @@ -128,6 +127,7 @@ module JamRuby session_history.user_id = music_session.creator.id session_history.band_id = music_session.band.id unless music_session.band.nil? session_history.genres = music_session.genres.map { |g| g.id }.join SEPARATOR + session_history.fan_access = music_session.fan_access session_history.save! end diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 19ddd357b..8fc666e0f 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :is_downloadable, as: :admin + attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :is_downloadable, :duration, as: :admin has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User" diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 1c4f49700..7ff867717 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -275,8 +275,7 @@ module JamRuby end def recent_history - recordings = ClaimedRecording.joins(:recording) - .where(:recordings => {:owner_id => "#{self.id}"}) + recordings = Recording.where(:owner_id => self.id) .order('created_at DESC') .limit(10) diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 9868bf2e5..8f277cd51 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -14,12 +14,17 @@ FactoryGirl.define do musician true terms_of_service true + #u.association :musician_instrument, factory: :musician_instrument, user: u before(:create) do |user| user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user) end + factory :fan do + musician false + end + factory :admin do admin true end @@ -45,7 +50,7 @@ FactoryGirl.define do factory :music_session do after(:create) { |session| - MusicSessionHistory.save(session) + FactoryGirl.create(:music_session_user_history, :history => session.music_session_history, :user => session.creator) } factory :music_session_with_mount do @@ -61,6 +66,7 @@ FactoryGirl.define do music_session nil end + fan_access true music_session_id { music_session.id } description { music_session.description } user_id { music_session.user_id } @@ -101,6 +107,9 @@ FactoryGirl.define do city "Apex" state "NC" country "US" + before(:create) { |band| + band.genres << Genre.first + } end factory :genre, :class => JamRuby::Genre do diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb index dc093a0cb..99bc51a4e 100644 --- a/ruby/spec/jam_ruby/connection_manager_spec.rb +++ b/ruby/spec/jam_ruby/connection_manager_spec.rb @@ -23,7 +23,7 @@ describe ConnectionManager do description = "some session" @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, options[:musician_access], options[:approval_required], options[:fan_chat], options[:fan_access]]) do |result| session_id = result.getvalue(0, 0) - @conn.exec("INSERT INTO music_sessions_history (music_session_id, description, user_id) VALUES ($1, $2, $3)", [session_id, description, user_id]) + @conn.exec("INSERT INTO music_sessions_history (music_session_id, description, user_id, fan_access) VALUES ($1, $2, $3, $4)", [session_id, description, user_id, true]) return session_id end end diff --git a/ruby/spec/jam_ruby/models/band_location_spec.rb b/ruby/spec/jam_ruby/models/band_location_spec.rb index fae562a53..e70fd09fd 100644 --- a/ruby/spec/jam_ruby/models/band_location_spec.rb +++ b/ruby/spec/jam_ruby/models/band_location_spec.rb @@ -2,6 +2,11 @@ require 'spec_helper' describe Band do + before(:all) do + MaxMindIsp.delete_all + MaxMindGeo.delete_all + end + before do @geocode1 = FactoryGirl.create(:geocoder) @geocode2 = FactoryGirl.create(:geocoder) @@ -27,7 +32,8 @@ describe Band do describe "without location data" do it "should have nil lat/lng values without address" do - @band.update_attributes({ :city => nil, + @band.skip_location_validation = true + @band.update_attributes({ :city => nil, :state => nil, :country => nil, }) diff --git a/ruby/spec/jam_ruby/models/band_search_spec.rb b/ruby/spec/jam_ruby/models/band_search_spec.rb index 5d693ba5f..2f885138f 100644 --- a/ruby/spec/jam_ruby/models/band_search_spec.rb +++ b/ruby/spec/jam_ruby/models/band_search_spec.rb @@ -3,12 +3,24 @@ require 'spec_helper' describe User do let(:user) { FactoryGirl.create(:user) } + let(:band) { FactoryGirl.create(:band, name: "Example Band") } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } before(:each) do @geocode1 = FactoryGirl.create(:geocoder) @geocode2 = FactoryGirl.create(:geocoder) @user = FactoryGirl.create(:user) - @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band.touch + end @@ -16,43 +28,43 @@ describe User do ws = Search.band_search("Example Band").results ws.length.should == 1 band_result = ws[0] - band_result.name.should == @band.name - band_result.id.should == @band.id - band_result.location.should == @band.location + band_result.name.should == band.name + band_result.id.should == band.id + band_result.location.should == band.location end it "should allow search of one band with partial matches" do ws = Search.band_search("Ex").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exa").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exam").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Examp").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exampl").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Example").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Ba").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Ban").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id end it "should not match mid-word searchs" do @@ -67,9 +79,9 @@ describe User do ws = Search.band_search("Example Band").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + band_result.id.should == band.id - @band.destroy # delete doesn't work; you have to use destroy. + band.destroy # delete doesn't work; you have to use destroy. ws = Search.band_search("Example Band").results ws.length.should == 0 @@ -79,10 +91,10 @@ describe User do ws = Search.band_search("Example Band").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + band_result.id.should == band.id - @band.name = "bonus-stuff" - @band.save + band.name = "bonus-stuff" + band.save ws = Search.band_search("Example Band").results ws.length.should == 0 @@ -90,25 +102,25 @@ describe User do ws = Search.band_search("Bonus").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + band_result.id.should == band.id band_result.name.should == "bonus-stuff" end it "should tokenize correctly" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band2 = FactoryGirl.create(:band, name: 'Peach pit') ws = Search.band_search("pea").results ws.length.should == 1 user_result = ws[0] - user_result.id.should == @band2.id + user_result.id.should == band2.id end it "should not return anything with a 1 character search" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band2 = FactoryGirl.create(:band, name: 'Peach pit') ws = Search.band_search("pe").results ws.length.should == 1 user_result = ws[0] - user_result.id.should == @band2.id + user_result.id.should == band2.id ws = Search.band_search("p").results ws.length.should == 0 diff --git a/ruby/spec/jam_ruby/models/band_spec.rb b/ruby/spec/jam_ruby/models/band_spec.rb index 3f1c1c289..25f2b4bb1 100644 --- a/ruby/spec/jam_ruby/models/band_spec.rb +++ b/ruby/spec/jam_ruby/models/band_spec.rb @@ -2,7 +2,21 @@ require 'spec_helper' describe Band do - let(:band) { FactoryGirl.create(:band) } + let(:user) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + let(:fan) { FactoryGirl.create(:fan) } + let(:band) { FactoryGirl.create(:band) } + let(:new_band) { FactoryGirl.build(:band) } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } describe 'website update' do it 'should have http prefix on website url' do @@ -12,4 +26,65 @@ describe Band do end end + describe 'band validations' do + it "minimum genres" do + new_band.save.should be_false + new_band.errors[:genres].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET] + end + + it "maximum genres" do + new_band.genres = Genre.limit(4) + new_band.save.should be_false + new_band.errors[:genres].should == [ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED] + end + end + + describe "save" do + it "can succeed" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band.name.should == band_params[:name] + band.biography.should == band_params[:biography] + band.genres.should == [Genre.find(band_params[:genres][0])] + band.city.should == band_params[:city] + band.state.should == band_params[:state] + band.country.should == band_params[:country] + end + + it "ensures user is a musician" do + expect{ Band.save(fan, band_params) }.to raise_error("must be a musician") + end + + it "can update" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band_params[:id] = band.id + band_params[:name] = "changed name" + band = Band.save(user, band_params) + band.errors.any?.should be_false + Band.find(band.id).name.should == band_params[:name] + end + + it "stops non-members from updating" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band_params[:id] = band.id + band_params[:name] = "changed name" + expect{ Band.save(user2, band_params) }.to raise_error(ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR) + end + end + + describe "validate" do + it "can pass" do + band = Band.build_band(user, band_params) + band.valid?.should be_true + end + + it "can fail" do + band_params[:name] = nil + band = Band.build_band(user, band_params) + band.valid?.should be_false + band.errors[:name].should == ["can't be blank"] + end + end end diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index 13cb877b6..7ec9a5f79 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -6,16 +6,18 @@ describe Feed do let (:user2) { FactoryGirl.create(:user) } let (:user3) { FactoryGirl.create(:user) } let (:user4) { FactoryGirl.create(:user) } + let (:band) { FactoryGirl.create(:band) } it "no result" do - feeds, start = Feed.index() + feeds, start = Feed.index(user1) feeds.length.should == 0 end it "one claimed recording" do claimed_recording = FactoryGirl.create(:claimed_recording) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording MusicSessionHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording - feeds, start = Feed.index() + feeds, start = Feed.index(user1) feeds.length.should == 1 feeds[0].recording == claimed_recording.recording end @@ -25,27 +27,38 @@ describe Feed do second_track = FactoryGirl.create(:recorded_track, recording: recording) recording.recorded_tracks << second_track FactoryGirl.create(:claimed_recording, recording: recording, user: second_track.user) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording MusicSessionHistory.delete_all # verify the mess above only made one recording Recording.count.should == 1 - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 1 end it "one music session" do music_session = FactoryGirl.create(:music_session) - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 1 feeds[0].music_session_history == music_session.music_session_history end + it "does not return a recording with no claimed recordings" do + recording = FactoryGirl.create(:recording) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording + MusicSessionHistory.delete_all + + + feeds, start = Feed.index(user1) + feeds.length.should == 0 + end + describe "sorting" do it "sorts by index (date) DESC" do claimed_recording = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 2 feeds[0].recording.should == claimed_recording.recording feeds[1].music_session_history.should == claimed_recording.recording.music_session.music_session_history @@ -57,13 +70,13 @@ describe Feed do FactoryGirl.create(:recording_play, recording: claimed_recording1.recording, user:claimed_recording1.user) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 FactoryGirl.create(:recording_play, recording: claimed_recording2.recording, user:claimed_recording1.user) FactoryGirl.create(:recording_play, recording: claimed_recording2.recording, user:claimed_recording2.user) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording @@ -73,7 +86,7 @@ describe Feed do FactoryGirl.create(:music_session_play, music_session: claimed_recording1.recording.music_session.music_session_history, user: user3) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history feeds[1].recording.should == claimed_recording2.recording @@ -86,13 +99,13 @@ describe Feed do FactoryGirl.create(:recording_like, recording: claimed_recording1.recording, user:claimed_recording1.user) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 FactoryGirl.create(:recording_like, recording: claimed_recording2.recording, user:claimed_recording1.user) FactoryGirl.create(:recording_like, recording: claimed_recording2.recording, user:claimed_recording2.user) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording @@ -101,7 +114,7 @@ describe Feed do FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user2) FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user3) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history feeds[1].recording.should == claimed_recording2.recording @@ -114,7 +127,7 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'session') + feeds, start = Feed.index(user1, :type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end @@ -123,27 +136,78 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'session') + feeds, start = Feed.index(user1, :type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end end + + describe "time ranges" do + it "month" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 32.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(user1, :type => 'recording') + feeds.length.should == 0 + end + + it "day" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 25.hours.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'today') + feeds.length.should == 0 + end + + it "week" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 8.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'week') + feeds.length.should == 0 + end + + it "all" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 700.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'all') + feeds.length.should == 1 + end + end + describe "pagination" do it "supports date pagination" do claimed_recording = FactoryGirl.create(:claimed_recording) options = {limit: 1} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end @@ -154,17 +218,17 @@ describe Feed do FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user1) options = {limit: 1, sort: 'likes'} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording1.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end @@ -175,21 +239,221 @@ describe Feed do FactoryGirl.create(:music_session_play, music_session: claimed_recording1.recording.music_session.music_session_history, user: user1) options = {limit: 1, sort: 'plays'} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording1.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end - end + describe "public feed" do + it "only public" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user) + feeds.length.should == 1 + + claimed_recording1.recording.music_session.fan_access = false + claimed_recording1.recording.music_session.save! + + feeds, start = Feed.index(claimed_recording1.user) + feeds.length.should == 0 + end + end + + describe "band feeds" do + it "does show other band's stuff in this feed" do + other_band = FactoryGirl.create(:band) + music_session = FactoryGirl.create(:music_session, band: other_band) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.recording.band = other_band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + user1.bands << band + user1.save! + music_session = FactoryGirl.create(:music_session, band: band) + music_session.music_session_history.fan_access.should be_true + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + + feeds, start = Feed.index(user2, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + end + + it "shows private sessions to you, not to others" do + user1.bands << band + user1.save! + music_session = FactoryGirl.create(:music_session, band: band, fan_access: false) + music_session.music_session_history.fan_access.should be_false + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session_history.fan_access.should be_false + + + feeds, start = Feed.index(user2, band: band.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + end + + it "shows private recordings to you, not to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + claimed_recording1.user.bands << band + claimed_recording1.user.save! + + feeds, start = Feed.index(claimed_recording1.user, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 0 + end + end + + describe "user feeds" do + it "does not show stuff from other people" do + music_session = FactoryGirl.create(:music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user2) + + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 0 + end + + it "shows public sessions to you and to others" do + music_session = FactoryGirl.create(:music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + + feeds, start = Feed.index(user2, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + end + + + it "shows private sessions to you, not to others" do + music_session = FactoryGirl.create(:music_session, fan_access: false) + music_session.music_session_history.fan_access.should be_false + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session_history.fan_access.should be_false + + + feeds, start = Feed.index(user2, user: user1.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + end + + it "shows private recordings to you, not to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, user: claimed_recording1.user.id) + feeds.length.should == 0 + end + + it "shows band recordings to you even though you did not claim a recording" do + user1.bands << band + user1.save! + user2.bands << band + user2.save! + + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user2) + claimed_recording1.is_public = true + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1 .recording + + # make it private; should still be available + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1 .recording + + # take user1 out of the band; shouldn't be able to see it + user1.bands.delete_all + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 0 + end + end + + end diff --git a/ruby/spec/jam_ruby/models/music_session_history_spec.rb b/ruby/spec/jam_ruby/models/music_session_history_spec.rb index 183e9f6c4..0a66ec69b 100644 --- a/ruby/spec/jam_ruby/models/music_session_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_history_spec.rb @@ -4,18 +4,17 @@ describe MusicSessionHistory do let(:some_user) { FactoryGirl.create(:user) } let(:music_session) { FactoryGirl.create(:music_session_no_history) } - let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) } - let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) } - let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) } it "create" do - history.description.should eql(music_session.description) + music_session.music_session_history.description.should eql(music_session.description) end it "unique users" do user_history1.should_not be_nil user_history2.should_not be_nil - users = history.unique_users + users = music_session.music_session_history.unique_users users.length.should eql(2) diff --git a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb index b6c719151..3d7713b86 100644 --- a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb @@ -4,9 +4,8 @@ describe MusicSessionUserHistory do let(:some_user) { FactoryGirl.create(:user) } let(:music_session) { FactoryGirl.create(:music_session_no_history) } - let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) } - let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) } - let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) } describe "create" do it {user_history1.music_session_id.should == music_session.id } @@ -78,7 +77,7 @@ describe MusicSessionUserHistory do end it "two histories with same user within bounds of history1" do - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -99,7 +98,7 @@ describe MusicSessionUserHistory do it "two histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -120,7 +119,7 @@ describe MusicSessionUserHistory do it "two overlapping histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 8b747ead5..60f32513b 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -61,6 +61,7 @@ Spork.prefork do # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| + config.color_enabled = true config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true config.filter_run :focus diff --git a/web/Gemfile b/web/Gemfile index 195f731f8..5048d1b00 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -55,7 +55,6 @@ gem 'fog' gem 'haml-rails' gem 'unf' #optional fog dependency gem 'devise', '>= 1.1.2' -#gem 'thin' # the presence of this gem on mac seems to prevent normal startup of rails. gem 'postgres-copy' #group :libv8 do # gem 'libv8', "~> 3.11.8" diff --git a/web/app/assets/images/content/icon_instrument_piano24.png b/web/app/assets/images/content/icon_instrument_piano24.png new file mode 100644 index 000000000..6f5761df9 Binary files /dev/null and b/web/app/assets/images/content/icon_instrument_piano24.png differ diff --git a/web/app/assets/images/content/icon_instrument_piano45.png b/web/app/assets/images/content/icon_instrument_piano45.png new file mode 100644 index 000000000..5d44d4420 Binary files /dev/null and b/web/app/assets/images/content/icon_instrument_piano45.png differ diff --git a/web/app/assets/images/content/icon_instrument_upright_bass24.png b/web/app/assets/images/content/icon_instrument_upright_bass24.png new file mode 100644 index 000000000..c054f32ad Binary files /dev/null and b/web/app/assets/images/content/icon_instrument_upright_bass24.png differ diff --git a/web/app/assets/images/content/icon_instrument_upright_bass45.png b/web/app/assets/images/content/icon_instrument_upright_bass45.png new file mode 100644 index 000000000..7c2b8e1d1 Binary files /dev/null and b/web/app/assets/images/content/icon_instrument_upright_bass45.png differ diff --git a/web/app/assets/images/shared/icon_piano_256.png b/web/app/assets/images/shared/icon_piano_256.png new file mode 100644 index 000000000..b2f63670f Binary files /dev/null and b/web/app/assets/images/shared/icon_piano_256.png differ diff --git a/web/app/assets/images/shared/icon_upright_bass_256.png b/web/app/assets/images/shared/icon_upright_bass_256.png new file mode 100644 index 000000000..c5373a215 Binary files /dev/null and b/web/app/assets/images/shared/icon_upright_bass_256.png differ diff --git a/web/app/assets/javascripts/bandProfile.js b/web/app/assets/javascripts/bandProfile.js index cdb49ae8b..20ff38afc 100644 --- a/web/app/assets/javascripts/bandProfile.js +++ b/web/app/assets/javascripts/bandProfile.js @@ -16,9 +16,20 @@ } function afterShow(data) { - resetForm(); - events(); - renderActive(); + + // hide until we know if 'isMember' + $("#btn-follow-band").hide(); + $("#btn-edit-band-profile").hide(); + + resetForm(); + events(); + determineMembership() + .done(function() { + renderActive(); + }) + .fail(function(jqXHR) { + app.notifyServerError(jqXHR, "Unable to talk to server") + }) } function resetForm() { @@ -128,6 +139,16 @@ // refreshes the currently active tab function renderActive() { + + if (isMember) { + $("#btn-follow-band").hide(); + $("#btn-edit-band-profile").show(); + } + else { + $("#btn-follow-band").show(); + $("#btn-edit-band-profile").hide(); + } + if ($('#band-profile-about-link').hasClass('active')) { renderAbout(); } @@ -161,7 +182,6 @@ rest.getBand(bandId) .done(function(response) { band = response; - setIsMember(); if (band) { // name $('#band-profile-name').html(band.name); @@ -379,47 +399,37 @@ } } - // TODO: refactor // checks if person viewing the profile is also a band member - function setIsMember() { + function determineMembership() { var url = "/api/bands/" + bandId + "/musicians"; - $.ajax({ + return $.ajax({ type: "GET", dataType: "json", url: url, - async: false, processData:false, - success: function(response) { - $.each(response, function(index, val) { - if (val.id === context.JK.currentUserId) { - isMember = true; - } - }); - }, error: app.ajaxError - }); + }) + .done(function(response) { + isMember = false; + $.each(response, function(index, val) { + if (val.id === context.JK.currentUserId) { + isMember = true; + } + }); + }) } // events for main screen function events() { // wire up panel clicks - $('#band-profile-about-link').click(renderAbout); - $('#band-profile-history-link').click(renderHistory); - $('#band-profile-members-link').click(renderMembers); - $('#band-profile-social-link').click(renderSocial); + $('#band-profile-about-link').unbind('click').click(renderAbout); + $('#band-profile-history-link').unbind('click').click(renderHistory); + $('#band-profile-members-link').unbind('click').click(renderMembers); + $('#band-profile-social-link').unbind('click').click(renderSocial); - if (isMember) { - $("#btn-follow-band").hide(); - $("#btn-edit-band-profile").show(); - } - else { - $("#btn-follow-band").show(); - $("#btn-edit-band-profile").hide(); - } - - $("#btn-edit-band-profile").click(function() { - $('div[layout-id="band/setup"] .hdn-band-id').val(bandId); - context.location = "/client#/band/setup"; + $("#btn-edit-band-profile").unbind('click').click(function() { + //$('div[layout-id="band/setup"] .hdn-band-id').val(bandId); + context.location = "/client#/band/setup/" + bandId; return false; }); } diff --git a/web/app/assets/javascripts/band_setup.js b/web/app/assets/javascripts/band_setup.js index 79d25bcb1..875f31f41 100644 --- a/web/app/assets/javascripts/band_setup.js +++ b/web/app/assets/javascripts/band_setup.js @@ -21,45 +21,43 @@ var nilOptionText = 'n/a'; var bandId = ''; + function is_new_record() { + return bandId.length == 0; + } + + function removeErrors() { + $('#band-setup-form .error-text').remove() + $('#band-setup-form .error').removeClass("error") + + } + function resetForm() { + removeErrors(); + // name $("#band-name").val(''); - var $tdName = $("#tdBandName"); - $tdName.find('.error-text').remove(); - $tdName.removeClass("error"); // country $("#band-country").empty(); $("#band-country").val(''); - var $tdCountry = $("#tdBandCountry"); - $tdCountry.find('.error-text').remove(); - $tdCountry.removeClass("error"); // region $("#band-region").empty(); $("#band-region").val(''); - var $tdRegion = $("#tdBandRegion"); - $tdRegion.find('.error-text').remove(); - $tdRegion.removeClass("error"); // city $("#band-city").empty(); $("#band-city").val(''); - var $tdCity = $("#tdBandCity"); - $tdCity.find('.error-text').remove(); - $tdCity.removeClass("error"); // description $("#band-biography").val(''); - var $tdBiography = $("#tdBandBiography"); - $tdBiography.find('.error-text').remove(); - $tdBiography.removeClass("error"); + + // website + $('#band-website').val(''); resetGenres(); - $("#hdn-band-id").val(''); - $("#band-setup-step-1").show(); $("#band-setup-step-2").hide(); @@ -75,8 +73,6 @@ }); var $tdGenres = $("#tdBandGenres"); - $tdGenres.find('.error-text').remove(); - $tdGenres.removeClass("error"); } function getSelectedGenres() { @@ -90,96 +86,32 @@ } function validateGeneralInfo() { - var isValid = true; + removeErrors(); - // name - var $name = $("#band-name"); - var $tdName = $("#tdBandName"); - var name = $.trim($name.val()); - if (name.length === 0) { - $tdName.find('.error-text').remove(); - $tdName.addClass("error"); - $name.after(""); - isValid = false; - } - else { - $tdName.removeClass("error"); - } + var band = buildBand(); - // country - var $country = $("#band-country"); - var $tdCountry = $("#tdBandCountry"); - var country = $.trim($country.val()); - if (country.length === 0) { - $tdCountry.find('.error-text').remove(); - $tdCountry.addClass("error"); - $country.after(""); - isValid = false; - } - else { - $tdCountry.removeClass("error"); - } - - // region - var $region = $("#band-region"); - var $tdRegion = $("#tdBandRegion"); - var region = $.trim($region.val()); - if (region.length === 0) { - $tdRegion.find('.error-text').remove(); - $tdRegion.addClass("error"); - $region.after(""); - isValid = false; - } - else { - $tdRegion.removeClass("error"); - } - - // city - var $city = $("#band-city"); - var $tdCity = $("#tdBandCity"); - var city = $.trim($city.val()); - if (city.length === 0) { - $tdCity.find('.error-text').remove(); - $tdCity.addClass("error"); - $city.after(""); - isValid = false; - } - else { - $tdCity.removeClass("error"); - } - - // genres (no more than 3) - var $genres = $(".band-setup-genres"); - var $tdGenres = $("#tdBandGenres"); - var selectedGenres = getSelectedGenres(); - if (selectedGenres.length === 0 || selectedGenres.length > 3) { - $tdGenres.find('.error-text').remove(); - $tdGenres.addClass("error"); - $genres.after(""); - isValid = false; - } - else { - $tdGenres.removeClass("error"); - } - - // description - var $biography = $("#band-biography"); - var $tdBiography = $("#tdBandBiography"); - var biography = $.trim($biography.val()); - if (biography.length === 0) { - $tdBiography.find('.error-text').remove(); - $tdBiography.addClass("error"); - $biography.after(""); - isValid = false; - } - else { - $tdBiography.removeClass("error"); - } - - return isValid; + return rest.validateBand(band); } - function saveBand() { + function renderErrors(errors) { + var name = context.JK.format_errors("name", errors); + var country = context.JK.format_errors("country", errors); + var state = context.JK.format_errors("state", errors); + var city = context.JK.format_errors("city", errors); + var biography = context.JK.format_errors("biography", errors); + var genres = context.JK.format_errors("genres", errors); + var website = context.JK.format_errors("website", errors); + + if(name) $("#band-name").closest('div.field').addClass('error').end().after(name); + if(country) $("#band-country").closest('div.field').addClass('error').end().after(country); + if(state) $("#band-region").closest('div.field').addClass('error').end().after(state); + if(city) $("#band-city").closest('div.field').addClass('error').end().after(city); + if(genres) $(".band-setup-genres").closest('div.field').addClass('error').end().after(genres); + if(biography) $("#band-biography").closest('div.field').addClass('error').end().after(biography); + if(website) $("#band-website").closest('div.field').addClass('error').end().after(website); + } + + function buildBand() { var band = {}; band.name = $("#band-name").val(); band.website = $("#band-website").val(); @@ -188,8 +120,13 @@ band.state = $("#band-region").val(); band.country = $("#band-country").val(); band.genres = getSelectedGenres(); + return band; + } - if (bandId.length === 0) { + function saveBand() { + var band = buildBand() + + if (is_new_record()) { rest.createBand(band) .done(function (response) { createBandInvitations(response.id, function () { @@ -244,8 +181,7 @@ userNames = []; userIds = []; userPhotoUrls = []; - //bandId = "1158c8b6-4c92-47dc-82bf-1e390c4f9b2c"; - bandId = $("#hdn-band-id").val(); + bandId = data.id == 'new' ? '' : data.id; resetForm(); } @@ -253,10 +189,11 @@ friendSelectorDialog.setCallback(friendSelectorCallback); loadFriends(); - if (bandId.length > 0) { + if (!is_new_record()) { $("#band-setup-title").html("edit band"); $("#btn-band-setup-save").html("SAVE CHANGES"); $("#band-change-photo").html('Upload band photo.'); + $('#tdBandPhoto').css('visibility', 'visible'); // retrieve and initialize band profile data points loadBandDetails(); @@ -276,8 +213,7 @@ $("#band-setup-title").html("set up band"); $("#btn-band-setup-save").html("CREATE BAND"); - - $("#band-change-photo").html('Upload band photo (optional).'); + $('#tdBandPhoto').css('visibility', 'hidden'); // can't upload photo when going through initial setup } } @@ -479,8 +415,7 @@ function navigateToBandPhoto(evt) { evt.stopPropagation(); - $("#hdn-band-id").val(bandId); - context.location = '/client#/band/setup/photo'; + context.location = '/client#/band/setup/photo/' + bandId; return false; } @@ -499,14 +434,24 @@ $('#btn-band-setup-cancel').click(function () { resetForm(); - context.location = "/client#/home"; + window.history.go(-1); + return false; }); $('#btn-band-setup-next').click(function () { - if (validateGeneralInfo()) { - $("#band-setup-step-2").show(); - $("#band-setup-step-1").hide(); - } + validateGeneralInfo() + .done(function (response) { + $("#band-setup-step-2").show(); + $("#band-setup-step-1").hide(); + }) + .fail(function (jqXHR) { + if(jqXHR.status == 422) { + renderErrors(JSON.parse(jqXHR.responseText)) + } + else { + app.notifyServerError(jqXHR, "Unable to validate band") + } + }); }); $('#btn-band-setup-back').click(function () { diff --git a/web/app/assets/javascripts/band_setup_photo.js b/web/app/assets/javascripts/band_setup_photo.js index 193266131..b758d2737 100644 --- a/web/app/assets/javascripts/band_setup_photo.js +++ b/web/app/assets/javascripts/band_setup_photo.js @@ -18,11 +18,7 @@ var updatingBandPhoto = false; function beforeShow(data) { - bandId = $("#hdn-band-id").val(); - logger.debug("bandId=" + bandId); - if (!bandId) { - context.location = '/client#/home'; - } + bandId = data.id; } function afterShow(data) { @@ -143,8 +139,7 @@ function navToEditProfile() { resetForm(); - $("#hdn-band-id").val(bandId); - context.location = '/client#/band/setup'; + context.location = '/client#/band/setup/' + bandId; } function renderBandPhotoSpinner() { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index f80c0f037..5090066db 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -103,6 +103,17 @@ }); } + function validateBand(band) { + return $.ajax({ + type: "POST", + dataType: "json", + url: '/api/bands/validate', + contentType: 'application/json', + processData: false, + data: JSON.stringify(band) + }); + } + function getBand(bandId) { return $.ajax({ type: "GET", @@ -894,6 +905,7 @@ this.deleteBandPhoto = deleteBandPhoto; this.getBandPhotoFilepickerPolicy = getBandPhotoFilepickerPolicy; this.getBand = getBand; + this.validateBand = validateBand; this.createBandInvitation = createBandInvitation; this.updateBandInvitation = updateBandInvitation; this.removeBandMember = removeBandMember; diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index b80f7aa48..ef88e9f06 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -678,13 +678,12 @@ $('p', $notify).html( message.text); } - if (message.icon_url) { $('#avatar', $notify).attr('src', message.icon_url); - $('#avatar', $notify).show(); + $('#notify-avatar', $notify).show(); } else { - $('#avatar', $notify).hide(); + $('#notify-avatar', $notify).hide(); } if (message.detail) { diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js index d4e5ec5b3..ff15ff574 100644 --- a/web/app/assets/javascripts/profile.js +++ b/web/app/assets/javascripts/profile.js @@ -512,7 +512,7 @@ bandId: val.id, biography: val.biography, band_url: "/client#/bandProfile/" + val.id, - avatar_url: context.JK.resolveBandAvatarUrl(val.logo_url), + avatar_url: context.JK.resolveBandAvatarUrl(val.photo_url), name: val.name, location: val.location, genres: formatGenres(val.genres), @@ -594,7 +594,7 @@ } function configureBandFollowingButton(following, bandId) { - var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band'); + var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band-2'); $btnFollowBand.unbind("click"); if (following) { diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index 73d77dc65..b7f20f61e 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -86,15 +86,42 @@ 8: { "title": "Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, click here." }, // NETWORK_PING, 9: {"title": "", "message": ""}, // BITRATE_THROTTLE_WARN, 10: { "title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low,and this may impact your audio quality. For troubleshooting tips, click here." }, // BANDWIDTH_LOW - //IO related events + + // IO related events 11: { "title": "Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click here." }, // INPUT_IO_RATE 12: {"title": "", "message": ""}, // INPUT_IO_JTR, 13: { "title": "Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click here." }, // OUTPUT_IO_RATE 14: {"title": "", "message": ""}, // OUTPUT_IO_JTR, + // CPU load related 15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, click here." }, // CPU_LOAD 16: {"title": "", "message": ""}, // DECODE_VIOLATIONS, - 17: {"title": "", "message": ""} // LAST_THRESHOLD + 17: {"title": "", "message": ""}, // LAST_THRESHOLD + 18: {"title": "", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi + 19: {"title": "", "message": ""}, // NO_VALID_AUDIO_CONFIG, // alert the user to popup a config + 20: {"title": "", "message": ""}, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected + 21: {"title": "", "message": ""}, // RECORD_PLAYBACK_STATE, // record/playback events have occurred + 22: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_BACKGROUND, //this is auto check - do + 23: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_INTERACTIVE, //this is initiated by user + 24: {"title": "", "message": ""}, // STUN_EVENT, // system completed stun test... come get the result + 25: {"title": "", "message": ""}, // DEAD_USER_WARN_EVENT, //the backend is not receiving audio from this peer + 26: {"title": "", "message": ""}, // DEAD_USER_REMOVE_EVENT, //the backend is removing the user from session as no audio is coming from this peer + 27: {"title": "", "message": ""}, // WINDOW_CLOSE_BACKGROUND_MODE, //the user has closed the window and the client is now in background mode + 28: {"title": "", "message": ""}, // WINDOW_OPEN_FOREGROUND_MODE, //the user has opened the window and the client is now in forground mode/ + + 29: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_FAIL, //error of some sort - so can't broadcast + 30: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_ACTIVE, //active + 31: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_STOPPED, //stopped by server/user + 32: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_PINNED, //node pinned by user + 33: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user + + 34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message + 35: {"title": "", "message": ""}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable. + + //indicates problem with user computer stack or network itself (wifi, antivirus etc) + 36: {"title": "", "message": ""}, // LOCAL_NETWORK_LATENCY_HIGH, + 37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end + 38: {"title": "", "message": ""} // LAST_ALERT }; @@ -141,7 +168,26 @@ sessionModel.refreshCurrentSession(); // XXX: race condition possible here; other client may not have updated server yet }, 1000); } - } else { + } + else if (type === 24) { // STUN_EVENT + var testResults = context.jamClient.NetworkTestResult(); + + $.each(testResults, function(index, val) { + if (val.bStunFailed) { + // if true we could not reach a stun server + } + else if (val.bRemoteUdpBocked) { + // if true the user cannot communicate with peer via UDP, although they could do LAN based session + } + }); + } + else if (type === 25) { // DEAD_USER_WARN_EVENT + + } + else if (type === 26) { // DEAD_USER_REMOVE_EVENT + + } + else { context.setTimeout(function() { var alert = alert_type[type]; diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 7c3fd1efb..c275321ac 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -116,8 +116,8 @@ latency_text: latencyDescription, latency_style: latencyStyle, sortScore: latencyInfo.sortScore, - play_url: "TODO", - join_link_display_style: "block" // showJoinLink ? "block" : "none" + play_url: context.JK.root_url + "sessions/" + session.id, + join_link_display_style: showJoinLink ? "block" : "none" }; var row = context.JK.fillTemplate(rowTemplate, sessionVals); @@ -131,24 +131,24 @@ return false; // break } }); + if (!insertedEarly) { $(tbGroup).append(row); - - // wire up the Join Link to the T&Cs dialog - var $parentRow = $('tr[id=' + session.id + ']', tbGroup); - - - $('.join-link', $parentRow).click(function(evt) { - // If no FTUE, show that first. - if (!(context.jamClient.FTUEGetStatus())) { - app.afterFtue = function() { joinClick(session.id); }; - app.layout.showDialog('ftue'); - return; - } else { - joinClick(session.id); - } - }); } + + // wire up the Join Link to the T&Cs dialog + var $parentRow = $('tr[id=' + session.id + ']', tbGroup); + + $('.join-link', $parentRow).click(function(evt) { + // If no FTUE, show that first. + if (!(context.jamClient.FTUEGetStatus())) { + app.afterFtue = function() { joinClick(session.id); }; + app.layout.showDialog('ftue'); + return; + } else { + joinClick(session.id); + } + }); } function joinClick(sessionId) { diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 1d77bca84..b3e27859e 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -59,13 +59,13 @@ "mandolin":"mandolin", "oboe":"oboe", "other":"other", - "piano":"keyboard", + "piano":"piano", "saxophone":"saxophone", "trombone":"trombone", "trumpet":"trumpet", "tuba":"tuba", "ukulele":"ukelele", - "upright bass":"cello", + "upright bass":"upright_bass", "viola":"viola", "violin":"violin", "voice":"voice" diff --git a/web/app/assets/javascripts/web/recordings.js b/web/app/assets/javascripts/web/recordings.js index e942bcd5f..4644d5beb 100644 --- a/web/app/assets/javascripts/web/recordings.js +++ b/web/app/assets/javascripts/web/recordings.js @@ -53,12 +53,12 @@ shareDialog.showDialog(); }); - $("#txtRecordingComment", $scope).keypress(function(e) { - if (e.which === 13) { - addComment(); - $(this).val(''); - $(this).blur(); - } + $("#btnPostComment").click(function(e) { + if ($.trim($("#txtRecordingComment").val()).length > 0) { + addComment(); + $("#txtRecordingComment").val(''); + $("#txtRecordingComment").blur(); + } }); } else { diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js index 05e79e7db..7a95a05e8 100644 --- a/web/app/assets/javascripts/web/sessions.js +++ b/web/app/assets/javascripts/web/sessions.js @@ -43,12 +43,12 @@ shareDialog.showDialog(); }); - $("#txtSessionComment").keypress(function(e) { - if (e.which === 13) { - addComment(); - $(this).val(''); - $(this).blur(); - } + $("#btnPostComment").click(function(e) { + if ($.trim($("#txtSessionComment").val()).length > 0) { + addComment(); + $("#txtSessionComment").val(''); + $("#txtSessionComment").blur(); + } }); } else { diff --git a/web/app/assets/javascripts/web/signinDialog.js b/web/app/assets/javascripts/web/signinDialog.js index 5d1f4e989..9662a95e8 100644 --- a/web/app/assets/javascripts/web/signinDialog.js +++ b/web/app/assets/javascripts/web/signinDialog.js @@ -62,7 +62,11 @@ } function beforeShow() { - reset(); + reset(); + } + + function afterShow() { + $(dialogId + ' input[name=email]').focus(); } function afterHide() { @@ -73,6 +77,7 @@ var dialogBindings = { 'beforeShow' : beforeShow, + 'afterShow' : afterShow, 'afterHide': afterHide }; diff --git a/web/app/assets/stylesheets/client/band.css.scss b/web/app/assets/stylesheets/client/band.css.scss index 37f40c2bb..25f45e9a2 100644 --- a/web/app/assets/stylesheets/client/band.css.scss +++ b/web/app/assets/stylesheets/client/band.css.scss @@ -1,5 +1,6 @@ @import "client/common.css.scss"; + .band-setup-bio { height:90px; overflow:auto; @@ -21,7 +22,7 @@ .avatar-space { color: $color2; - margin-bottom: 20px; + margin: 20px; position:relative; min-height:300px; @@ -336,4 +337,12 @@ .easydropdown-wrapper { width:80%; } + + div.field { + margin: 10px; + } + + label { + margin-bottom:2px; + } } \ No newline at end of file diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss index 7942f386d..c2ba9e1be 100644 --- a/web/app/assets/stylesheets/web/main.css.scss +++ b/web/app/assets/stylesheets/web/main.css.scss @@ -159,6 +159,19 @@ body.web { font-weight:bold; color:#ED3618; } + + .button-orange { + margin: 0px 8px 0px 8px; + background-color: #ED3618; + border: solid 1px #F27861; + outline: solid 2px #ED3618; + padding: 3px 10px; + font-size: 12px; + font-weight: 300; + cursor: pointer; + color: #FC9; + text-decoration: none; + } } .landing-comment-scroller { diff --git a/web/app/assets/stylesheets/web/recordings.css.scss b/web/app/assets/stylesheets/web/recordings.css.scss index 45e591aa1..2e3ac9e24 100644 --- a/web/app/assets/stylesheets/web/recordings.css.scss +++ b/web/app/assets/stylesheets/web/recordings.css.scss @@ -45,7 +45,6 @@ .recording-slider { position:absolute; - left:25%; top:0px; } diff --git a/web/app/controllers/api_bands_controller.rb b/web/app/controllers/api_bands_controller.rb index fab1e96bc..e3fcfedbc 100644 --- a/web/app/controllers/api_bands_controller.rb +++ b/web/app/controllers/api_bands_controller.rb @@ -14,38 +14,26 @@ class ApiBandsController < ApiController def show @band = Band.find(params[:id]) + respond_with_model(@band) end def create - @band = Band.save(nil, - params[:name], - params[:website], - params[:biography], - params[:city], - params[:state], - params[:country], - params[:genres], - current_user.id, - params[:photo_url], - params[:logo_url]) - - respond_with @band, responder: ApiResponder, :status => 201, :location => api_band_detail_url(@band) + @band = Band.save(current_user, params) + + respond_with_model(@band, new: true, location: lambda { return api_band_detail_url(@band.id) }) end def update - @band = Band.save(params[:id], - params[:name], - params[:website], - params[:biography], - params[:city], - params[:state], - params[:country], - params[:genres], - current_user.id, - params[:photo_url], - params[:logo_url]) + @band = Band.save(current_user, params) - respond_with @band, responder: ApiResponder, :status => :ok + respond_with_model(@band) + end + + def validate + @band = Band.build_band(current_user, params) + @band.valid? + + respond_with_model(@band) end def musician_index diff --git a/web/app/controllers/api_controller.rb b/web/app/controllers/api_controller.rb index 2d1e54c5d..6c7ac8b58 100644 --- a/web/app/controllers/api_controller.rb +++ b/web/app/controllers/api_controller.rb @@ -28,7 +28,26 @@ class ApiController < ApplicationController end end + protected + + def respond_with_model(model, options = {}) + if model.errors.any? + response.status = :unprocessable_entity + respond_with model + else + status = options[:new] && options[:new] == true ? 201 : 200 + redirect_on_success = options[:location] + if redirect_on_success + location = redirect_on_success.call + raise "location must return something" unless location # development time error + respond_with model, responder: ApiResponder, status: status, location: location + else + respond_with model, responder: ApiResponder, status: status + end + end + end + def auth_user unless current_user.id == params[:id] raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR diff --git a/web/app/controllers/api_feeds_controller.rb b/web/app/controllers/api_feeds_controller.rb index a875395b3..db17218b1 100644 --- a/web/app/controllers/api_feeds_controller.rb +++ b/web/app/controllers/api_feeds_controller.rb @@ -3,11 +3,14 @@ class ApiFeedsController < ApiController respond_to :json def index - @feeds, @next = Feed.index(user: current_user, + @feeds, @next = Feed.index(current_user, start: params[:since], limit: params[:limit], sort: params[:sort], - time_range: params[:time_range]) + time_range: params[:time_range], + type: params[:type], + user: params[:user], + band: params[:band]) render "api_feeds/index", :layout => nil end diff --git a/web/app/views/api_bands/validate.rabl b/web/app/views/api_bands/validate.rabl new file mode 100644 index 000000000..fad461386 --- /dev/null +++ b/web/app/views/api_bands/validate.rabl @@ -0,0 +1,2 @@ +object @band + diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl index d3253d3d7..899cb9b52 100644 --- a/web/app/views/api_feeds/show.rabl +++ b/web/app/views/api_feeds/show.rabl @@ -7,7 +7,7 @@ glue :music_session_history do 'music_session_history' end - attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count + attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count, :fan_access child(:user => :creator) { @@ -67,7 +67,7 @@ glue :recording do child(:claimed_recordings => :claimed_recordings) { - attributes :id, :name, :description, :is_public, :is_downloadable, :genre_id + attributes :id, :name, :description, :is_public, :genre_id child(:user => :creator) { attributes :id, :first_name, :last_name, :photo_url diff --git a/web/app/views/clients/_band_setup.html.erb b/web/app/views/clients/_band_setup.html.erb index dd82bac8b..e3dd3ae13 100644 --- a/web/app/views/clients/_band_setup.html.erb +++ b/web/app/views/clients/_band_setup.html.erb @@ -1,5 +1,5 @@ -
+
<%= image_tag "content/icon_bands.png", :size => "19x19" %> @@ -13,7 +13,6 @@
-

@@ -21,44 +20,66 @@ - - - - - - - -

Step 1: General Information

+ <%= image_tag "shared/avatar_generic_band.png", {:id => "band-avatar", :align=>"absmiddle", :height => 88, :width => 88 } %>

Upload band photo.

+
Band Name:
+
+
+
+
Web Site:
+
+
+ +
Country:
+
+
+

+ +
State/Region:
-

+
+
+ + +
City:
+
+
+

+ +
Genres:
+
+
+
+
Description / Bio:
+
+
+ +
diff --git a/web/app/views/clients/_band_setup_photo.html.erb b/web/app/views/clients/_band_setup_photo.html.erb index 986821592..4f3cba9ce 100644 --- a/web/app/views/clients/_band_setup_photo.html.erb +++ b/web/app/views/clients/_band_setup_photo.html.erb @@ -1,5 +1,5 @@ -
+
@@ -12,9 +12,12 @@
- -
+
+ +
+ +
diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index c4ca01ee5..2765fa7a2 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -71,7 +71,7 @@
{latency_text}
- + <%= image_tag "content/icon_playbutton.png", :size => "20x20" %> diff --git a/web/app/views/clients/_notify.html.erb b/web/app/views/clients/_notify.html.erb index fb400a692..2683524db 100644 --- a/web/app/views/clients/_notify.html.erb +++ b/web/app/views/clients/_notify.html.erb @@ -1,7 +1,7 @@
diff --git a/web/app/views/music_sessions/show.html.erb b/web/app/views/music_sessions/show.html.erb index 74ae13253..96ae0b6a2 100644 --- a/web/app/views/music_sessions/show.html.erb +++ b/web/app/views/music_sessions/show.html.erb @@ -52,10 +52,13 @@
- <%= image_tag "content/icon_playbutton.png", {:width => 20, :height => 20, :alt => ""} %> + <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %> <% if @music_session.session_removed_at.blank? %>
SESSION IN PROGRESS
-
1:23
+
+ <% else %>
LIVE SESSION ENDED
<% end %> @@ -99,8 +102,38 @@ <% content_for :extra_js do %> <% end %> diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index 82ea6adb5..ceb08a75f 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -18,7 +18,7 @@ <% end %>
- <% unless @claimed_recording.recording.band.nil? %> + <% unless @claimed_recording.recording.band.blank? %>
<% unless @claimed_recording.recording.band.photo_url.blank? %> <%= image_tag "#{@claimed_recording.recording.band.photo_url}", {:alt => ""} %> @@ -51,17 +51,20 @@
<%= @claimed_recording.description %>

- <%= image_tag "content/icon_playbutton.png", {:width => 20, :height => 20, :alt => ""} %> + <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %>
0:00
<%= image_tag "content/slider_playcontrols.png", {:width => 5, :height => 16, :alt => ""} %>
-
4:59
+
-
1:23
+
0:00
+
-
<%= @claimed_recording.genre_id.capitalize %>
<%= @claimed_recording.recording.play_count %> @@ -101,7 +104,45 @@ $(function () { var showRecording = new JK.ShowRecording(JK.app); showRecording.initialize("<%= @claimed_recording.id %>", "<%= @claimed_recording.recording_id %>"); - }) + + // remainder of this code is related to playing/pausing the recording + var htmlAudio = $(".recording-controls").find('audio').get(0); + var durationInitialized = false; + + function formatTime(time) { + var minutes = Math.floor(time / 60); + var seconds = Math.floor(time % 60); + return minutes.toString() + ":" + (seconds > 9 ? seconds.toString() : '0' + seconds.toString()); + } + + // this calculates the original + $(htmlAudio).on('timeupdate', function() { + if (!durationInitialized) { + $("#recordingDuration").html(formatTime(htmlAudio.duration)); + durationInitialized = true; + } + var percentComplete = (htmlAudio.currentTime / htmlAudio.duration) * 100; + $(".recording-slider").css({'left': percentComplete + '%'}); + $(".recording-current").html(formatTime(htmlAudio.currentTime)); + + // reset icon to play and slider to far left when done + if (percentComplete === 100) { + $("#imgPlayPause").attr('src', '/assets/content/icon_playbutton.png'); + $(".recording-slider").css({'left': 0 + '%'}); + } + }); + + $("#btnPlayPause").click(function() { + if (htmlAudio.paused) { + htmlAudio.play(); + $("#imgPlayPause").attr('src', '/assets/content/icon_pausebutton.png'); + } + else { + htmlAudio.pause(); + $("#imgPlayPause").attr('src', '/assets/content/icon_playbutton.png'); + } + }); + }); <% end %> diff --git a/web/app/views/shared/_comments.html.erb b/web/app/views/shared/_comments.html.erb index 116c6bf51..c13f3559a 100644 --- a/web/app/views/shared/_comments.html.erb +++ b/web/app/views/shared/_comments.html.erb @@ -6,6 +6,9 @@
+
+ POST +

diff --git a/web/app/views/shared/_landing_sidebar.html.erb b/web/app/views/shared/_landing_sidebar.html.erb index c7ddd78d4..673acebf7 100644 --- a/web/app/views/shared/_landing_sidebar.html.erb +++ b/web/app/views/shared/_landing_sidebar.html.erb @@ -4,14 +4,15 @@ <% recent_history.each do |history_record| %> - <% if history_record.instance_of? ClaimedRecording %> + <% if history_record.instance_of? Recording %>
<%= history_record.created_at.strftime("%b #{history_record.created_at.day.ordinalize}") %>:
+
<%= history_record.claimed_recordings.first.description %>
<% elsif history_record.instance_of? MusicSessionHistory %>
@@ -31,9 +32,9 @@ Session Ended. Unavailable. <% end %>
+
<%= history_record.description %>
<% end %> -
<%= history_record.description %>
<% if history_record != recent_history.last %>

<% end %> diff --git a/web/app/views/users/_user_dropdown.html.erb b/web/app/views/users/_user_dropdown.html.erb index de58a65f5..6f2b785f4 100644 --- a/web/app/views/users/_user_dropdown.html.erb +++ b/web/app/views/users/_user_dropdown.html.erb @@ -21,7 +21,7 @@ <% if current_user && current_user.musician? %>
  • <%= link_to "Audio Gear", '/client#/account/audio' %>
  • -
  • <%= link_to "Band Setup", '/client#/band/setup' %>
  • +
  • <%= link_to "Band Setup", '/client#/band/setup/new' %>
  • <% end %>
  • <%= link_to "Invite Friends", '#' %>
      diff --git a/web/config/routes.rb b/web/config/routes.rb index a37c1f78f..dae5bba9f 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -225,6 +225,7 @@ SampleApp::Application.routes.draw do # bands match '/bands' => 'api_bands#index', :via => :get + match '/bands/validate' => 'api_bands#validate', :via => :post match '/bands/:id' => 'api_bands#show', :via => :get, :as => 'api_band_detail' match '/bands' => 'api_bands#create', :via => :post match '/bands/:id' => 'api_bands#update', :via => :post diff --git a/web/lib/music_session_manager.rb b/web/lib/music_session_manager.rb index b9d69fae7..3efc2cb90 100644 --- a/web/lib/music_session_manager.rb +++ b/web/lib/music_session_manager.rb @@ -43,9 +43,6 @@ MusicSessionManager < BaseManager # save session parameters for next session User.save_session_settings(user, music_session) - # save session history - MusicSessionHistory.save(music_session) - # auto-join this user into the newly created session as_musician = true connection = ConnectionManager.new.join_music_session(user, client_id, music_session, as_musician, tracks) @@ -96,12 +93,9 @@ MusicSessionManager < BaseManager update[:genres] = genre_array end - if music_session.update_attributes(update) - # save session history (only thing that could change is description) - MusicSessionHistory.save(music_session) - end + music_session.update_attributes(update) - return music_session + music_session end def participant_create(user, music_session_id, client_id, as_musician, tracks) diff --git a/web/lib/tasks/import_max_mind.rake b/web/lib/tasks/import_max_mind.rake index 08098a6e7..6d1d9044e 100644 --- a/web/lib/tasks/import_max_mind.rake +++ b/web/lib/tasks/import_max_mind.rake @@ -1,16 +1,16 @@ namespace :db do desc "Import a maxmind database; run like this: rake db:import_maxmind_geo file=" - task :import_maxmind_geo do + task import_maxmind_geo: :environment do MaxMindGeo.import_from_max_mind ENV['file'] end desc "Import a maxmind isp database; run like this: rake db:import_maxmind_isp file=" - task :import_maxmind_isp do + task import_maxmind_isp: :environment do MaxMindIsp.import_from_max_mind ENV['file'] end desc "Create a fake set of maxmind data" - task :phony_maxmind do + task phony_maxmind: :environment do MaxMindManager.active_record_transaction do |manager| manager.create_phony_database() end diff --git a/web/lib/tasks/sample_data.rake b/web/lib/tasks/sample_data.rake index e1c0ec1cd..622173630 100644 --- a/web/lib/tasks/sample_data.rake +++ b/web/lib/tasks/sample_data.rake @@ -31,6 +31,13 @@ namespace :db do DatabaseCleaner.clean end + task populate: :environment do + make_friends + make_bands + make_band_members + make_recording + end + task populate_friends: :environment do make_friends end @@ -48,44 +55,7 @@ namespace :db do end task populate_claimed_recording: :environment do - # need 4 users. - users = User.where(musician: true).limit(4) - raise "need at least 4 musicians in the database to create a recording" if users.length < 4 - - user1 = users[0] - user2 = users[1] - user3 = users[2] - user4 = users[3] - - recording = Recording.new - recording.name = 'sample data' - recording.owner = user1 - - make_recorded_track(recording, user1, 'bass guitar', 'f86949abc213a3ccdc9d266a2ee56453', 2579467, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-bass.ogg') - make_recorded_track(recording, user1, 'voice', '264cf4e0bf14d44109322a504d2e6d18', 2373055, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-vox.ogg') - make_recorded_track(recording, user2, 'electric guitar', '9f322e1991b8c04b00dc9055d6be933c', 2297867, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-guitar.ogg') - make_recorded_track(recording, user2, 'voice', '3c7dcb7c4c35c0bb313fc15ee3e6bfd5', 2244968, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-vox.ogg') - make_recorded_track(recording, user3, 'voice', '10ca4c6ef5b98b3489ae8da1c7fa9cfb', 2254275, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-matt-lead-vox.ogg') - make_recorded_track(recording, user4, 'drums', 'ea366f482fa969e1fd8530c13cb75716', 2386250, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum1.ogg') - make_recorded_track(recording, user4, 'drums', '4c693c6e99117719c6340eb68b0fe574', 2566463, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum2.ogg') - - make_claimed_recording(recording, user1, 'Poison', 'Classic song that you all know -- user1') - make_claimed_recording(recording, user2, 'Poison', 'Classic song that you all know -- user2') - make_claimed_recording(recording, user3, 'Poison', 'Classic song that you all know -- user3') - make_claimed_recording(recording, user4, 'Poison', 'Classic song that you all know -- user4') - - mix = Mix.new - mix.started_at = Time.now - mix.completed_at = Time.now - mix[:ogg_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.ogg' - mix.ogg_md5 = 'f1fee708264602e1705638e53f0ea667' - mix.ogg_length = 2500633 - mix[:mp3_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.mp3' - mix.mp3_md5 = 'df05abad96e5cb8439f7cd6e31b5c503' - mix.mp3_length = 3666137 - mix.completed = true - recording.mixes << mix - recording.save!(validate:false) + make_recording end desc "Fill database with music session sample data" @@ -257,3 +227,44 @@ def make_claimed_recording(recording, user, name, description) end + +def make_recording + # need 4 users. + users = User.where(musician: true).limit(4) + raise "need at least 4 musicians in the database to create a recording" if users.length < 4 + + user1 = users[0] + user2 = users[1] + user3 = users[2] + user4 = users[3] + + recording = Recording.new + recording.name = 'sample data' + recording.owner = user1 + + make_recorded_track(recording, user1, 'bass guitar', 'f86949abc213a3ccdc9d266a2ee56453', 2579467, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-bass.ogg') + make_recorded_track(recording, user1, 'voice', '264cf4e0bf14d44109322a504d2e6d18', 2373055, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-vox.ogg') + make_recorded_track(recording, user2, 'electric guitar', '9f322e1991b8c04b00dc9055d6be933c', 2297867, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-guitar.ogg') + make_recorded_track(recording, user2, 'voice', '3c7dcb7c4c35c0bb313fc15ee3e6bfd5', 2244968, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-vox.ogg') + make_recorded_track(recording, user3, 'voice', '10ca4c6ef5b98b3489ae8da1c7fa9cfb', 2254275, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-matt-lead-vox.ogg') + make_recorded_track(recording, user4, 'drums', 'ea366f482fa969e1fd8530c13cb75716', 2386250, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum1.ogg') + make_recorded_track(recording, user4, 'drums', '4c693c6e99117719c6340eb68b0fe574', 2566463, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum2.ogg') + + make_claimed_recording(recording, user1, 'Poison', 'Classic song that you all know -- user1') + make_claimed_recording(recording, user2, 'Poison', 'Classic song that you all know -- user2') + make_claimed_recording(recording, user3, 'Poison', 'Classic song that you all know -- user3') + make_claimed_recording(recording, user4, 'Poison', 'Classic song that you all know -- user4') + + mix = Mix.new + mix.started_at = Time.now + mix.completed_at = Time.now + mix[:ogg_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.ogg' + mix.ogg_md5 = 'f1fee708264602e1705638e53f0ea667' + mix.ogg_length = 2500633 + mix[:mp3_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.mp3' + mix.mp3_md5 = 'df05abad96e5cb8439f7cd6e31b5c503' + mix.mp3_length = 3666137 + mix.completed = true + recording.mixes << mix + recording.save!(validate:false) +end \ No newline at end of file diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb index 0044beec6..e3c906744 100644 --- a/web/spec/controllers/api_feeds_controller_spec.rb +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -4,11 +4,14 @@ describe ApiFeedsController do render_views let(:user) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + let(:band) { FactoryGirl.create(:band) } let(:music_session) {FactoryGirl.create(:music_session, creator: user) } let(:claimed_recording) {FactoryGirl.create(:claimed_recording) } before(:each) do MusicSession.delete_all + MusicSessionUserHistory.delete_all MusicSessionHistory.delete_all Recording.delete_all end @@ -23,6 +26,7 @@ describe ApiFeedsController do it "returns a recording" do claimed_recording.touch # artifact of factory of :claimed_recording that this gets created + MusicSessionUserHistory.delete_all MusicSessionHistory.delete_all get :index @@ -41,11 +45,48 @@ describe ApiFeedsController do json = JSON.parse(response.body, :symbolize_names => true) json[:entries].length.should == 1 - music_session = json[:entries][0] music_session[:type].should == 'music_session_history' end + describe "time range" do + + it "today and month find different results" do + music_session.music_session_history.touch + music_session.music_session_history.feed.created_at = 3.days.ago + music_session.music_session_history.feed.save! + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + + get :index, { time_range:'today' } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end + + describe "type filter" do + + it "can filter by type" do + claimed_recording.touch + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 2 + + get :index, { type: 'recording'} + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + json[:entries][0][:type].should == 'recording' + + get :index, { type: 'music_session_history'} + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + json[:entries][0][:type].should == 'music_session_history' + end + end + describe "pagination" do it "since parameter" do @@ -72,4 +113,65 @@ describe ApiFeedsController do _next.should be_nil end end + + describe "user targetting" do + + it "user viewing own profile" do + music_session.fan_access = false + music_session.save! + controller.current_user = music_session.creator + + get :index, { user: music_session.creator.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + end + + it "user viewing someone else's profile" do + music_session.fan_access = false + music_session.save! + controller.current_user = user2 + music_session.music_session_history.reload + music_session.music_session_history.fan_access.should be_false + + get :index, { user: music_session.creator.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end + + describe "band targetting" do + + it "user viewing own band" do + user.bands << band + user.save! + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + controller.current_user = user + + get :index, { band: band.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + end + + it "user viewing someone else's band" do + user.bands << band + user.save! + + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + controller.current_user = user2 + + get :index, { band: band.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end end diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 215db3715..1d5716708 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -16,6 +16,9 @@ FactoryGirl.define do terms_of_service true subscribe_email true + factory :fan do + musician false + end factory :admin do admin true @@ -33,20 +36,6 @@ FactoryGirl.define do end end - factory :fan, :class => JamRuby::User do - sequence(:first_name) { |n| "Person" } - sequence(:last_name) { |n| "#{n}" } - sequence(:email) { |n| "fan_#{n}@example.com"} - password "foobar" - password_confirmation "foobar" - email_confirmed true - musician false - city "Apex" - state "NC" - country "US" - terms_of_service true - end - factory :invited_user, :class => JamRuby::InvitedUser do sequence(:email) { |n| "user#{n}@someservice.com" } autofriend false @@ -63,12 +52,20 @@ FactoryGirl.define do association :creator, :factory => :user after(:create) { |session| - MusicSessionHistory.save(session) + FactoryGirl.create(:music_session_user_history, :history => session.music_session_history, :user => session.creator) } end factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do + ignore do + history nil + user nil + end + + music_session_id { history.music_session_id } + user_id { user.id } + sequence(:client_id) { |n| "Connection #{n}" } end @@ -92,6 +89,9 @@ FactoryGirl.define do city "Apex" state "NC" country "US" + before(:create) { |band| + band.genres << Genre.first + } end factory :join_request, :class => JamRuby::JoinRequest do diff --git a/web/spec/features/bands_spec.rb b/web/spec/features/bands_spec.rb index 4343af2ba..f61e65ff1 100644 --- a/web/spec/features/bands_spec.rb +++ b/web/spec/features/bands_spec.rb @@ -4,10 +4,18 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do subject { page } + + before(:all) do Capybara.javascript_driver = :poltergeist Capybara.current_driver = Capybara.javascript_driver Capybara.default_wait_time = 15 + + MaxMindIsp.delete_all # prove that city/state/country will remain nil if no maxmind data + MaxMindGeo.delete_all + MaxMindManager.active_record_transaction do |manager| + manager.create_phony_database() + end end let(:user) { FactoryGirl.create(:user) } @@ -15,9 +23,10 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do before(:each) do UserMailer.deliveries.clear + navigate_band_setup end - it "band setup is accessible through profile page" do + def navigate_band_setup sign_in_poltergeist(user) wait_until_curtain_gone find('div.homecard.profile').trigger(:click) @@ -26,6 +35,25 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do expect(page).to have_selector('#band-setup-title') end + it "have validation errors shown, but then can navigate past and eventually save" do + find('#btn-band-setup-next').trigger(:click) + find('#tdBandName .error-text li', text: "can't be blank") + find('#tdBandBiography .error-text li', text: "can't be blank") + find('#tdBandGenres .error-text li', text: "At least 1 genre is required.") + + within('#band-setup-form') do + fill_in "band-name", with: "The Band" + fill_in "band-biography", with: "Biography" + first('#band-genres input[type="checkbox"]').trigger(:click) + end + + find('#btn-band-setup-next').trigger(:click) + find('h2', text: 'Step 2: Add Band Members') + + find('#btn-band-setup-save').trigger(:click) + find('#band-profile-name', text: "The Band") + find('#band-profile-biography', text: "Biography") + end end diff --git a/web/spec/features/musician_search_spec.rb b/web/spec/features/musician_search_spec.rb index 4fe89d6c9..5f7a42b4c 100644 --- a/web/spec/features/musician_search_spec.rb +++ b/web/spec/features/musician_search_spec.rb @@ -28,6 +28,8 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature => end it "shows blank result set" do + pending "fails intermittently on build server" + wait_for_easydropdown('#musician_instrument') # get the 2nd option from the instruments list text = find('#musician_instrument', :visible => false).find(:xpath, 'option[2]', :visible => false).text diff --git a/web/spec/features/whats_next_spec.rb b/web/spec/features/whats_next_spec.rb index 1946e622e..3c31c8848 100644 --- a/web/spec/features/whats_next_spec.rb +++ b/web/spec/features/whats_next_spec.rb @@ -63,6 +63,7 @@ describe "Home Screen", :js => true, :type => :feature, :capybara_feature => tru # needed because we poke the server with an updateUser call, but their is no indication in the UI that it's done wait_for_ajax page.driver.headers = { 'User-Agent' => ' JamKazam ' } + sleep 1 visit "/client" wait_until_curtain_gone should_not have_selector('h1', text: 'what\'s next?') diff --git a/web/spec/requests/bands_api_spec.rb b/web/spec/requests/bands_api_spec.rb index 528ef656a..fcfe4cded 100644 --- a/web/spec/requests/bands_api_spec.rb +++ b/web/spec/requests/bands_api_spec.rb @@ -10,6 +10,16 @@ describe "Band API", :type => :api do let(:band) { FactoryGirl.create(:band) } let(:user) { FactoryGirl.create(:user) } let(:fan) { FactoryGirl.create(:fan) } + let(:band_params) { + { + name: "My Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['rock'] + } + } def login(email, password, http_code, success) # login as fan @@ -19,45 +29,33 @@ describe "Band API", :type => :api do end ################################## BANDS ################################## - def create_band(authenticated_user, name, website, biography, city, state, country, genres, photo_url, logo_url) - post "/api/bands.json", { :name => name, - :website => website, - :biography => biography, - :city => city, - :state => state, - :country => country, - :genres => genres, - :photo_url => photo_url, - :logo_url => logo_url - }.to_json, - "CONTENT_TYPE" => 'application/json' - return last_response + def create_band(authenticated_user, options={}) + options = band_params.merge(options) + post "/api/bands.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response end - def update_band(authenticated_user, band_id, name, website, biography, city, state, country, genres, photo_url, logo_url) - post "/api/bands/#{band_id}.json", { :name => name, - :website => website, - :biography => biography, - :city => city, - :state => state, - :country => country, - :genres => genres, - :photo_url => photo_url, - :logo_url => logo_url - }.to_json, - "CONTENT_TYPE" => 'application/json' - return last_response + def validate_band(authenticated_user, options={}) + options = band_params.merge(options) + post "/api/bands/validate.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response + end + + def update_band(authenticated_user, band_id, options={}) + options = band_params.merge(options) + post "/api/bands/#{band_id}.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response end def get_band(authenticated_user, band_id) get "/api/bands/#{band_id}.json", "CONTENT_TYPE" => 'application/json' - return last_response + last_response end ########################## RECORDINGS ######################### def create_band_recording(authenticated_user, band_id, description, public, genres) post "/api/bands/#{band_id}/recordings.json", { :description => description, :public => public, :genres => genres }.to_json, "CONTENT_TYPE" => 'application/json' - return last_response + last_response end def update_band_recording() @@ -98,9 +96,19 @@ describe "Band API", :type => :api do login(user.email, user.password, 200, true) end + it "should pass validation" do + last_response = validate_band(user) + last_response.status.should == 200 + end + + it "should fail validation" do + last_response = validate_band(user, name: nil) + last_response.status.should == 422 + end + it "should allow band creation" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", ["country"], "www.photos.com", "www.logos.com") + last_response = create_band(user) last_response.status.should == 201 new_band = JSON.parse(last_response.body) @@ -117,17 +125,17 @@ describe "Band API", :type => :api do end it "should prevent bands with less than 1 genre" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", nil, "www.photos.com", "www.logos.com") - last_response.status.should == 400 + last_response = create_band(user, genres: []) + last_response.status.should == 422 error_msg = JSON.parse(last_response.body) - error_msg["message"].should == ValidationMessages::GENRE_MINIMUM_NOT_MET + error_msg["errors"]["genres"].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET] end it "should prevent bands with more than 3 genres" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", ["african", "country", "ambient", "asian"], "www.photos.com", "www.logos.com") - last_response.status.should == 400 + last_response = create_band(user, genres: ["african", "country", "ambient", "asian"]) + last_response.status.should == 422 error_msg = JSON.parse(last_response.body) - error_msg["message"].should == ValidationMessages::GENRE_LIMIT_EXCEEDED + error_msg["errors"]["genres"].should == [ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED] end end @@ -135,9 +143,6 @@ describe "Band API", :type => :api do before(:each) do login(user.email, user.password, 200, true) - band.genres << Genre.find("hip hop") - #band.genres << Genre.find("african") - #band.genres << Genre.find("country") user.bands << band end @@ -145,7 +150,7 @@ describe "Band API", :type => :api do band.genres.size.should == 1 - last_response = update_band(user, band.id, "Brian's Band", "http://www.briansband.com", "Bio", "Apex", "NC", "US", ["african"], "www.photos.com", "www.logos.com") + last_response = update_band(user, band.id, name: "Brian's Band", website: "http://www.briansband.com", genres: ["african"]) last_response.status.should == 200 updated_band = JSON.parse(last_response.body) @@ -161,8 +166,8 @@ describe "Band API", :type => :api do band_details = JSON.parse(last_response.body) band_details["name"].should == "Brian's Band" band_details["website"].should == "http://www.briansband.com" - band_details["biography"].should == "Bio" - band_details["genres"].size.should == 1 + band_details["biography"].should == "Biography" + band_details["genres"].should == [{"id"=>"african", "description"=>"African"}] end it "should allow user to create recording for band A" do diff --git a/web/spec/requests/search_api_spec.rb b/web/spec/requests/search_api_spec.rb index a3475939f..29751d904 100644 --- a/web/spec/requests/search_api_spec.rb +++ b/web/spec/requests/search_api_spec.rb @@ -7,6 +7,16 @@ describe "Search API", :type => :api do describe "profile page" do let(:user) { FactoryGirl.create(:user) } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } before(:each) do post '/sessions', "session[email]" => user.email, "session[password]" => user.password @@ -22,8 +32,10 @@ describe "Search API", :type => :api do it "simple search" do @musician = FactoryGirl.create(:user, first_name: "Peach", last_name: "Nothing", email: "user@example.com", musician: true) @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Grovery", email: "fan@example.com", musician: false) - @band = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) - @band2 = Band.save(nil, "Peach", "www.bands2.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band_params[:name] = "Peach pit" + @band = Band.save(user, band_params) + band_params[:name] = "Peach" + @band2 = Band.save(user, band_params) get '/api/search.json?query=peach&search_text_type=bands' last_response.status.should == 200 diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index 8ac3f7d4d..40d775465 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -19,9 +19,12 @@ db_config = YAML::load(File.open('config/database.yml'))["test"] # initialize ActiveRecord's db connection\ SpecDb::recreate_database(db_config) ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) - require 'jam_ruby' +# uncomment this to see active record logs +# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) + + include JamRuby # put ActionMailer into test mode @@ -51,7 +54,6 @@ Spork.prefork do Capybara.default_wait_time = 10 if defined?(TEST_CONNECT_STATES) && TEST_CONNECT_STATES - require 'capybara/poltergeist' TEST_CONNECT_STATE_JS_CONSOLE_IO = File.open(TEST_CONNECT_STATE_JS_CONSOLE, 'w') Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: TEST_CONNECT_STATE_JS_CONSOLE_IO }) @@ -134,8 +136,8 @@ Spork.prefork do # dump response.body if an example fails if example.metadata[:type] == :controller && example.exception - puts "'#{determine_test_name(example.metadata)}' controller test failed." - puts "response.status = #{response.status}, response.body = " + response.body + puts "'#{determine_test_name(example.metadata)}' controller test failed." + puts "response.status = #{response.status}, response.body = " + response.body end end diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index a38b77ce7..7d44a27ea 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -56,8 +56,8 @@ end def sign_in_poltergeist(user) visit signin_path - fill_in "Email Address:", with: user.email - fill_in "Password:", with: user.password + fill_in "session_email", with: user.email + fill_in "session_password", with: user.password click_button "SIGN IN" if Capybara.javascript_driver == :poltergeist