class ApiUsersController < ApiController before_filter :api_signed_in_user, :except => [:create, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play] before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete, :liking_create, :liking_destroy, # likes :following_create, :following_show, :following_destroy, # followings :recording_update, :recording_destroy, # recordings :favorite_create, :favorite_destroy, # favorites :friend_request_index, :friend_request_show, :friend_request_create, :friend_request_update, # friend requests :friend_show, :friend_destroy, # friends :notification_index, :notification_destroy, # notifications :band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations :set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy, :share_session, :share_recording] respond_to :json def index @users = User.paginate(page: params[:page]) respond_with @users, responder: ApiResponder, :status => 200 end def show @user = User.includes([{:musician_instruments => :instrument}, {:band_musicians => :user}, :bands, :instruments]) .find(params[:id]) respond_with @user, responder: ApiResponder, :status => 200 end def update @user = User.find(params[:id]) @user.first_name = params[:first_name] if params.has_key?(:first_name) @user.last_name = params[:last_name] if params.has_key?(:last_name) @user.gender = params[:gender] if params.has_key?(:gender) @user.birth_date = Date.strptime(params[:birth_date], '%m-%d-%Y') if params.has_key?(:birth_date) @user.city = params[:city] if params.has_key?(:city) @user.state = params[:state] if params.has_key?(:state) @user.country = params[:country] if params.has_key?(:country) @user.musician = params[:musician] if params.has_key?(:musician) @user.update_instruments(params[:instruments].nil? ? [] : params[:instruments]) if params.has_key?(:instruments) @user.show_whats_next = params[:show_whats_next] if params.has_key?(:show_whats_next) @user.subscribe_email = params[:subscribe_email] if params.has_key?(:subscribe_email) @user.biography = params[:biography] if params.has_key?(:biography) @user.save if @user.errors.any? respond_with @user, :status => :unprocessable_entity else respond_with @user, responder: ApiResponder, :status => 200 end end # a user that is created administratively has an incomplete profile # when they first visit the confirmation page by clicking the link in their email. def complete signup_token = params[:signup_token] user = User.find_by_signup_token(signup_token) if user.nil? return end user.updating_password = true user.easy_save( params[:first_name], params[:last_name], nil, # email can't be edited at this phase. We need to get them into the site, and they can edit on profile page if they really want params[:password], params[:password_confirmation], true, # musician params[:gender], params[:birth_date], params[:isp], params[:city], params[:state], params[:country], params[:instruments], params[:photo_url]) if user.errors.any? render :json => user.errors.full_messages(), :status => :unprocessable_entity else # log the user in automatically user.signup_confirm sign_in(user) respond_with user, responder: ApiResponder, :status => 200 end end def delete @user.destroy respond_with responder: ApiResponder, :status => 204 end def signup_confirm @user = UserManager.new.signup_confirm(params[:signup_token]) unless @user.errors.any? respond_with @user, responder: ApiResponder, :location => api_user_detail_url(@user) else response.status = :unprocessable_entity respond_with @user, responder: ApiResponder end end def set_password @user.set_password(params[:old_password], params[:new_password], params[:new_password_confirm]) if @user.errors.any? response.status = :unprocessable_entity respond_with @user else sign_in(@user) respond_with @user, responder: ApiResponder, status: 200 end end def reset_password begin User.reset_password(params[:email], ApplicationHelper.base_uri(request)) rescue JamRuby::JamArgumentError render :json => { :message => ValidationMessages::EMAIL_NOT_FOUND }, :status => 403 end respond_with responder: ApiResponder, :status => 204 end def reset_password_token begin User.set_password_from_token(params[:email], params[:token], params[:new_password], params[:new_password_confirm]) rescue JamRuby::JamArgumentError # FIXME # There are some other errors that can happen here, besides just EMAIL_NOT_FOUND render :json => { :message => ValidationMessages::EMAIL_NOT_FOUND }, :status => 403 end set_remember_token(@user) respond_with responder: ApiResponder, :status => 204 end ###################### AUTHENTICATION ################### def auth_session_create @user = User.authenticate(params[:email], params[:password]) if @user.nil? render :json => { :success => false }, :status => 404 else sign_in @user render :json => { :success => true }, :status => 200 end end def auth_session_delete sign_out render :json => { :success => true }, :status => 200 end ###################### SESSION SETTINGS ################### def session_settings_show respond_with @user.my_session_settings, responder: ApiResponder end ###################### SESSION HISTORY ################### def session_history_index @session_history = @user.session_history(params[:id], params[:band_id], params[:genre]) end def session_user_history_index @session_user_history = @user.session_user_history(params[:id], params[:session_id]) end ###################### BANDS ######################## def band_index @bands = User.band_index(params[:id]) end ###################### LIKERS ######################## def liker_index # NOTE: liker_index.rabl template references the likers property @user = User.find(params[:id]) end ###################### LIKES ######################### def liking_index @user = User.find(params[:id]) end def liking_create @user = User.find(params[:id]) if !params[:user_id].nil? @user.create_user_liking(params[:user_id]) elsif !params[:band_id].nil? @user.create_band_liking(params[:band_id]) end respond_with @user, responder: ApiResponder, :location => api_user_liking_index_url(@user) end def liking_destroy User.delete_liking(params[:id], params[:likable_id]) respond_with responder: ApiResponder, :status => 204 end ###################### FOLLOWERS ######################## def follower_index # NOTE: follower_index.rabl template references the followers property @user = User.find(params[:id]) end ###################### FOLLOWINGS ####################### def following_index @user = User.find(params[:id]) end def following_create @user = User.find(params[:id]) if !params[:user_id].nil? @user.create_user_following(params[:user_id]) elsif !params[:band_id].nil? @user.create_band_following(params[:band_id]) end respond_with @user, responder: ApiResponder, :location => api_user_following_index_url(@user) end def following_destroy User.delete_following(params[:id], params[:followable_id]) respond_with responder: ApiResponder, :status => 204 end ###################### FAVORITES ######################## def favorite_index @user = User.find(params[:id]) end def favorite_create @favorite = UserFavorite.new() User.create_favorite(params[:id], params[:recording_id]) @user = User.find(params[:id]) respond_with @user, responder: ApiResponder, :location => api_favorite_index_url(@user) end def favorite_destroy User.delete_favorite(params[:id], params[:recording_id]) respond_with responder: ApiResponder, :status => 204 end ###################### FRIENDS ########################## def friend_request_index # get all outgoing and incoming friend requests @friend_requests = FriendRequest.where("(friend_id='#{params[:id]}' AND status is null) OR user_id='#{params[:id]}'") end def friend_request_show @friend_request = FriendRequest.find(params[:friend_request_id]) respond_with @friend_request, responder: ApiResponder, :status => 200 end def friend_request_create @friend_request = FriendRequest.save(nil, params[:id], params[:friend_id], nil, params[:message]) respond_with @friend_request, responder: ApiResponder, :status => 201, :location => api_friend_request_detail_url(@user, @friend_request) end def friend_request_update @friend_request = FriendRequest.save(params[:friend_request_id], params[:id], params[:friend_id], params[:status], nil) respond_with @friend_request, responder: ApiResponder, :status => 200 end def friend_index # NOTE: friend_index.rabl template references the friends property @user = User.find(params[:id]) end def friend_show @friend = Friendship.find_by_user_id_and_friend_id(params[:id], params[:friend_id]) end def friend_destroy if current_user.id != params[:id] && current_user.id != params[:friend_id] render :json => { :message => "You are not allowed to delete this friendship." }, :status => 403 end # clean up both records representing this "friendship" JamRuby::Friendship.delete_all "(user_id = '#{params[:id]}' AND friend_id = '#{params[:friend_id]}') OR (user_id = '#{params[:friend_id]}' AND friend_id = '#{params[:id]}')" respond_with responder: ApiResponder, :status => 204 end ###################### NOTIFICATIONS #################### def notification_index @notifications = @user.notifications respond_with @notifications, responder: ApiResponder, :status => 200 end def notification_destroy Notification.delete(params[:notification_id]) respond_with responder: ApiResponder, :status => 204 end ##################### BAND INVITATIONS ################## def band_invitation_index @invitations = @user.received_band_invitations respond_with @invitations, responder: ApiResponder, :status => 200 end def band_invitation_show begin @invitation = BandInvitation.find(params[:invitation_id]) respond_with @invitation, responder: ApiResponder, :status => 200 rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::BAND_INVITATION_NOT_FOUND }, :status => 404 end end def band_invitation_update begin @invitation = BandInvitation.save(params[:invitation_id], nil, nil, nil, params[:accepted]) respond_with @invitation, responder: ApiResponder, :status => 200 rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::BAND_INVITATION_NOT_FOUND }, :status => 404 end end ###################### ACCOUNT SETTINGS ################# def begin_update_email # begins email update by sending an email for the user to confirm their new email # NOTE: if you change confirm_email_link value below, you break outstanding email changes because links in user inboxes are broken confirm_email_link = confirm_email_url + "?token=" current_user.begin_update_email(params[:update_email], params[:current_password], confirm_email_link) if current_user.errors.any? respond_with current_user, status: :unprocessable_entity else respond_with current_user, responder: ApiResponder, status: 200 end end def finalize_update_email # used when the user goes to the confirmation link in their email @user = User.finalize_update_email(params[:token]) sign_in(@user) respond_with current_user, responder: ApiResponder, status: 200 end def isp_scoring data = request.body.read score = IspScoreBatch.new score.json_scoring_data = data if score.save render :text => 'scoring recorded' else render :text => "score invalid: #{score.errors.inspect}", status:422 end end ################# AVATAR ##################### def update_avatar original_fpfile = params[:original_fpfile] cropped_fpfile = params[:cropped_fpfile] cropped_large_fpfile = params[:cropped_large_fpfile] crop_selection = params[:crop_selection] # public bucket to allow images to be available to public @user.update_avatar(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, Rails.application.config.aws_bucket_public) if @user.errors.any? respond_with @user, status: :unprocessable_entity else respond_with @user, responder: ApiResponder, status: 200 end end def delete_avatar @user.delete_avatar(Rails.application.config.aws_bucket_public) if @user.errors.any? respond_with @user, status: :unprocessable_entity else respond_with @user, responder: ApiResponder, status: 204 end end def generate_filepicker_policy # generates a soon-expiring filepicker policy so that a user can only upload to their own folder in their bucket handle = params[:handle] call = 'pick,convert,store' policy = { :expiry => (DateTime.now + 5.minutes).to_i(), :call => call, #:path => 'avatars/' + @user.id + '/.*jpg' } # if the caller specifies a handle, add it to the hash unless handle.nil? start = handle.rindex('/') + 1 policy[:handle] = handle[start..-1] end policy = Base64.urlsafe_encode64( policy.to_json ) digest = OpenSSL::Digest::Digest.new('sha256') signature = OpenSSL::HMAC.hexdigest(digest, Rails.application.config.fp_secret, policy) render :json => { :signature => signature, :policy => policy }, :status => :ok end ###################### CRASH DUMPS ####################### # This is very similar to api_music_sessions#perf_upload # This should largely be moved into a library somewhere in jam-ruby. def crash_dump # example of using curl to access this API: # curl -L -T some_file -X PUT http://localhost:3000/api/users/dump.json?client_type=[MACOSX/Win32/JamBox]&client_version=[VERSION]&client_id=[CLIENT_ID]&session_id=[SESSION_ID]×tamp=[TIMESTAMP] # user_id is deduced if possible from the user's cookie. @dump = CrashDump.new @dump.client_type = params[:client_type] @dump.client_version = params[:client_version] @dump.client_id = params[:client_id] @dump.user_id = current_user @dump.session_id = params[:session_id] @dump.timestamp = params[:timestamp] unless @dump.save # There are at least some conditions on valid dumps (need client_type) response.status = :unprocessable_entity respond_with @dump return end # This part is the piece that really needs to be decomposed into a library... if Rails.application.config.storage_type == :fog s3 = AWS::S3.new(:access_key_id => Rails.application.config.aws_access_key_id, :secret_access_key => Rails.application.config.aws_secret_access_key) bucket = s3.buckets[Rails.application.config.aws_bucket] url = bucket.objects[@dump.uri].url_for(:write, :expires => Rails.application.config.crash_dump_data_signed_url_timeout, :'response_content_type' => 'application/octet-stream').to_s logger.debug("crash_dump can upload to url #{url}") redirect_to url else # we should store it here to aid in development, but we don't have to until someone wants the feature # so... just return 200 render :json => { :id => @dump.id }, :status => 200 end end # user progression tracking def downloaded_client @user = current_user @user.update_progression_field(:first_downloaded_client_at) if @user.errors.any? respond_with @user, :status => :unprocessable_entity return end render :json => {}, :status => 200 end # user progression tracking def qualified_gear @user = current_user if params[:success] @user.update_progression_field(:first_certified_gear_at) else @user.failed_qualification(params[:reason]) end if @user.errors.any? respond_with @user, :status => :unprocessable_entity return end render :json => {}, :status => 200 end # user progression tracking def social_promoted @user = current_user @user.update_progression_field(:first_social_promoted_at) if @user.errors.any? respond_with @user, :status => :unprocessable_entity return end render :json => {}, :status => 200 end # creates display-ready session data for sharing def share_session provider = params[:provider] music_session_id = params[:music_session] history = MusicSessionHistory.find(music_session_id) if provider == 'facebook' render json: { description: view_context.description_for_music_session_history(history), title: view_context.title_for_music_session_history(history, current_user), photo_url: view_context.facebook_image_for_music_session_history(history), caption: 'www.jamkazam.com' }, status: 200 elsif provider == 'twitter' render json: { message: view_context.title_for_music_session_history(history, current_user) }, status: 200 else render :json => { :errors => {:provider => ['not valid']} }, :status => 422 end end # creates display-ready recording data for sharing def share_recording provider = params[:provider] claimed_recording_id = params[:claimed_recording] claimed_recording = ClaimedRecording.find(claimed_recording_id) if provider == 'facebook' render json: { description: view_context.description_for_claimed_recording(claimed_recording), title: view_context.title_for_claimed_recording(claimed_recording, current_user), photo_url: view_context.facebook_image_for_claimed_recording(claimed_recording), caption: 'www.jamkazam.com' }, status: 200 elsif provider == 'twitter' render json: { message: view_context.title_for_claimed_recording(history, current_user) + " at " + request.host_with_port }, status: 200 else render :json => { :errors => {:provider => ['not valid']} }, :status => 422 end end def add_play if params[:id].blank? render :json => { :message => "Playable ID is required" }, :status => 400 return end play = PlayablePlay.new play.playable_id = params[:id] play.playable_type = params[:playable_type] play.player_id = params[:user_id] play.claimed_recording_id = params[:claimed_recording_id] play.ip_address = request.remote_ip play.save if play.errors.any? render :json => { :message => "Unexpected error occurred" }, :status => 500 return else render :json => {}, :status => 201 return end end ###################### RECORDINGS ####################### # def recording_index # @recordings = User.recording_index(current_user, params[:id]) # respond_with @recordings, responder: ApiResponder, :status => 200 # end # def recording_show # hide_private = false # # hide private recordings from anyone but the current user # if current_user.id != params[:id] # hide_private = true # end # @recording = Recording.find(params[:recording_id]) # if !@recording.public && hide_private # render :json => { :message => "You are not allowed to access this recording." }, :status => 403 # #respond_with "You are not allowed to access this recording.", responder: ApiResponder, :status => 403 # else # respond_with @recording, responder: ApiResponder, :status => 200 # end # end # def recording_create # @recording = Recording.save(params[:recording_id], # params[:public], # params[:description], # params[:genres], # current_user.id, # params[:id], # false) # @user = current_user # respond_with @recording, responder: ApiResponder, :status => 201, :location => api_recording_detail_url(@user, @recording) # end # def recording_update # @recording = Recording.save(params[:recording_id], # params[:public], # params[:description], # params[:genres], # current_user.id, # params[:id], # false) # respond_with @recording, responder: ApiResponder, :status => 200 # end # def recording_destroy # @recording = Recording.find(params[:recording_id]) # @recording.delete # respond_with responder: ApiResponder, :status => 204 # end end