diff --git a/db/manifest b/db/manifest index d750aab4f..dcb427176 100755 --- a/db/manifest +++ b/db/manifest @@ -106,4 +106,5 @@ track_connection_id_not_null.sql recordings_all_discarded.sql recordings_via_admin_web.sql relax_band_model_varchar.sql -add_piano.sql \ No newline at end of file +add_piano.sql +feed.sql \ No newline at end of file diff --git a/db/up/feed.sql b/db/up/feed.sql new file mode 100644 index 000000000..b7bb81333 --- /dev/null +++ b/db/up/feed.sql @@ -0,0 +1,9 @@ +ALTER TABLE music_sessions_history ADD PRIMARY KEY (id); + +CREATE TABLE feeds ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + claimed_recording_id VARCHAR(64) UNIQUE REFERENCES claimed_recordings(id) ON DELETE CASCADE, + music_session_id VARCHAR(64) UNIQUE REFERENCES music_sessions_history(id) ON DELETE CASCADE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 2cc0844f4..dbdd16ce6 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -123,6 +123,7 @@ require "jam_ruby/models/icecast_template_socket" require "jam_ruby/models/icecast_server_group" require "jam_ruby/models/icecast_mount_template" require "jam_ruby/models/facebook_signup" +require "jam_ruby/models/feed" include Jampb diff --git a/ruby/lib/jam_ruby/models/claimed_recording.rb b/ruby/lib/jam_ruby/models/claimed_recording.rb index ff9438bc4..7fce422b9 100644 --- a/ruby/lib/jam_ruby/models/claimed_recording.rb +++ b/ruby/lib/jam_ruby/models/claimed_recording.rb @@ -9,6 +9,7 @@ module JamRuby has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack" has_many :playing_sessions, :class_name => "JamRuby::MusicSession" has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id' + has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :claimed_recording, :foreign_key => 'claimed_recording_id', :dependent => :destroy validates :name, no_profanity: true, length: {minimum: 3, maximum: 64}, presence: true @@ -20,11 +21,17 @@ module JamRuby validates_uniqueness_of :user_id, :scope => :recording_id validate :user_belongs_to_recording - - before_create :generate_share_token + before_create :add_to_feed + before_create :generate_share_token SHARE_TOKEN_LENGTH = 8 + + def add_to_feed + feed = Feed.new + feed.claimed_recording = self + end + def user_belongs_to_recording if user && recording && !recording.users.exists?(user) errors.add(:user, ValidationMessages::NOT_PART_OF_RECORDING) diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb new file mode 100644 index 000000000..0277d49eb --- /dev/null +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -0,0 +1,12 @@ + +module JamRuby + class Feed < ActiveRecord::Base + + belongs_to :claimed_recording, class_name: "JamRuby::ClaimedRecording", inverse_of: :feed, foreign_key: 'claimed_recording_id' + belongs_to :music_session_history, class_name: "JamRuby::MusicSessionHistory", inverse_of: :feed, foreign_key: 'music_session_id' + + def self.index(params = {}) + Feed.includes(:claimed_recording).includes(:music_session_history).order('created_at DESC').limit(20) + 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 49e8bc420..b80c93a64 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -23,13 +23,21 @@ module JamRuby has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "music_session_id" has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id' + has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :music_session_history, :foreign_key => 'music_session_id', :dependent => :destroy + before_create :generate_share_token + before_create :add_to_feed SHARE_TOKEN_LENGTH = 8 SEPARATOR = '|' + def add_to_feed + feed = Feed.new + feed.music_session_history = self + end + def comment_count self.comments.size end diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb new file mode 100644 index 000000000..825a8a6c6 --- /dev/null +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Feed do + + it "no result" do + Feed.index().length.should == 0 + end + + it "one claimed recording" do + claimed_recording = FactoryGirl.create(:claimed_recording) + feeds = Feed.index() + feeds.length.should == 2 # the factory makes a music_session while making the recording/claimed_recording + feeds[0].music_session_history == claimed_recording.recording.music_session.music_session_history + feeds[1].claimed_recording == claimed_recording + end + + it "one music session" do + music_session = FactoryGirl.create(:music_session) + feeds = Feed.index() + feeds.length.should == 1 + feeds[0].music_session_history == music_session.music_session_history + end + +end diff --git a/web/app/assets/javascripts/band_setup.js b/web/app/assets/javascripts/band_setup.js index 0bfb9d0ba..79d25bcb1 100644 --- a/web/app/assets/javascripts/band_setup.js +++ b/web/app/assets/javascripts/band_setup.js @@ -256,6 +256,7 @@ if (bandId.length > 0) { $("#band-setup-title").html("edit band"); $("#btn-band-setup-save").html("SAVE CHANGES"); + $("#band-change-photo").html('Upload band photo.'); // retrieve and initialize band profile data points loadBandDetails(); @@ -276,8 +277,7 @@ $("#band-setup-title").html("set up band"); $("#btn-band-setup-save").html("CREATE BAND"); - $("#band-change-photo").unbind('click'); - $("#band-change-photo").html('Set up band and then add photo.'); + $("#band-change-photo").html('Upload band photo (optional).'); } } @@ -477,6 +477,13 @@ } } + function navigateToBandPhoto(evt) { + evt.stopPropagation(); + $("#hdn-band-id").val(bandId); + context.location = '/client#/band/setup/photo'; + return false; + } + function removeInvitation(evt) { delete selectedFriendIds[$(evt.currentTarget).parent().attr('user-id')]; $(evt.currentTarget).closest('.invitation').remove(); @@ -526,12 +533,8 @@ return false; }); - $('#band-change-photo').click(function (evt) { - evt.stopPropagation(); - $("#hdn-band-id").val(bandId); - context.location = '/client#/band/setup/photo'; - return false; - }); + $('#band-change-photo').click(navigateToBandPhoto); + $('#band-setup .avatar-profile').click(navigateToBandPhoto); $('div[layout-id="band/setup"] .btn-email-invitation').click(function () { invitationDialog.showEmailDialog(); diff --git a/web/app/controllers/api_feeds_controller.rb b/web/app/controllers/api_feeds_controller.rb new file mode 100644 index 000000000..d4c0b9583 --- /dev/null +++ b/web/app/controllers/api_feeds_controller.rb @@ -0,0 +1,7 @@ +class ApiFeedsController < ApiController + + def index + @feeds = Feed.index(current_user) + end + +end \ No newline at end of file diff --git a/web/app/views/api_feeds/index.rabl b/web/app/views/api_feeds/index.rabl new file mode 100644 index 000000000..9833c0b63 --- /dev/null +++ b/web/app/views/api_feeds/index.rabl @@ -0,0 +1,3 @@ +object @feeds + +extends "api_feeds/show" \ No newline at end of file diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl new file mode 100644 index 000000000..a25285531 --- /dev/null +++ b/web/app/views/api_feeds/show.rabl @@ -0,0 +1,80 @@ +object @feed + + +glue :music_session_history do + + node :type do |i| + 'music_session_history' + end + + attributes :id, :description, :genres, :created_at, :session_removed_at + + child(:music_session => :music_session) do + # only show mount info if fan_access is public. Eventually we'll also need to show this in other scenarios, like if invited + child({:mount => :mount}, :if => lambda { |music_session| music_session.fan_access}) { + attributes :id, :name, :sourced, :listeners, :bitrate, :subtype, :url + node(:mime_type) { |mount| mount.resolve_string(:mime_type) } + node(:bitrate) { |mount| mount.resolve_string(:bitrate) } + node(:subtype) { |mount| mount.resolve_string(:subtype) } + } + end +end + +glue :claimed_recording do + + node :type do |i| + 'claimed_recording' + end + + attributes :id, :name, :description, :is_public, :is_downloadable, :genre_id + + node :share_url do |claimed_recording| + unless claimed_recording.share_token.nil? + share_token_url(claimed_recording.share_token.token) + end + end + + child(:recording => :recording) { + attributes :id, :created_at, :duration, :comment_count, :like_count, :play_count + + child(:band => :band) { + attributes :id, :name, :location, :photo_url + } + + child(:owner => :owner) { + attributes :id, :name, :location, :photo_url + } + + child(:mixes => :mixes) { + attributes :id, :is_completed + + node :mp3_url do |mix| + mix[:url] + end + + node :ogg_url do |mix| + mix[:url] + end + } + + child(:recorded_tracks => :recorded_tracks) { + attributes :id, :fully_uploaded, :client_track_id, :client_id, :instrument_id + + node :url do |recorded_track| + recorded_track[:url] + end + + child(:user => :user) { + attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :photo_url + } + } + + child(:comments => :comments) { + attributes :comment, :created_at + + child(:user => :creator) { + attributes :id, :first_name, :last_name, :photo_url + } + } + } +end diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index 1962111fb..851b57fe8 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -85,9 +85,9 @@ node(:claimed_recording, :if => lambda { |music_session| music_session.users.exi } end -# only show mount info if fan_acces is public. Eventually we'll also need to show this in other scenarios, like if invited +# only show mount info if fan_access is public. Eventually we'll also need to show this in other scenarios, like if invited child({:mount => :mount}, :if => lambda { |music_session| music_session.fan_access}) { - attributes :id, :name, :sourced, :listeners, :bitrate, :subtype + attributes :id, :name, :sourced, :listeners, :bitrate, :subtype, :url node(:mime_type) { |mount| mount.resolve_string(:mime_type) } node(:bitrate) { |mount| mount.resolve_string(:bitrate) } node(:subtype) { |mount| mount.resolve_string(:subtype) } diff --git a/web/app/views/clients/_band_setup.html.erb b/web/app/views/clients/_band_setup.html.erb index 2fcb3514e..dd82bac8b 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" %> @@ -26,7 +26,7 @@ <%= image_tag "shared/avatar_generic_band.png", {:id => "band-avatar", :align=>"absmiddle", :height => 88, :width => 88 } %>

- Upload Band Photo

+ Upload band photo.

diff --git a/web/config/routes.rb b/web/config/routes.rb index be1cdb259..e5355da6a 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -344,7 +344,11 @@ SampleApp::Application.routes.draw do match '/icecast/listener_add' => 'api_icecast#listener_add', :via => :post match '/icecast/listener_remove' => 'api_icecast#listener_remove', :via => :post + # tweet on behalf of client match '/twitter/tweet' => 'api_twitters#tweet', :via => :post + + # feed + match '/feeds' => 'api_feeds#index', :via => :get end end diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb new file mode 100644 index 000000000..723dc2c92 --- /dev/null +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe ApiFeedsController do + render_views + + let(:user) { FactoryGirl.create(:user) } + let(:music_session) {FactoryGirl.create(:music_session, creator: user) } + let(:claimed_recording) {FactoryGirl.create(:claimed_recording) } + + before(:each) do + MusicSession.delete_all + MusicSessionHistory.delete_all + Recording.delete_all + end + + it "returns nothing" do + get :index + json = JSON.parse(response.body) + json.length.should == 0 + end + + + it "returns a recording" do + claimed_recording.touch + # artifact of factory of :claimed_recording that this gets created + MusicSessionHistory.delete_all + + get :index + json = JSON.parse(response.body, {:symbolize_names => true}) + json.length.should == 1 + + claimed_recording = json[0] + claimed_recording[:type] == 'claimed_recording' + end + + it "returns a music session" do + music_session.touch + # artifact of factory of :claimed_recording that this gets created + + get :index + json = JSON.parse(response.body, {:symbolize_names => true}) + json.length.should == 1 + + music_session = json[0] + music_session[:type] == 'music_session_history' + end + +end