From 34695c716bda5a1ff77c608467eed6275d0ea261 Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Tue, 4 Nov 2014 12:31:38 -0600 Subject: [PATCH 01/18] VRFS-1849 : Show google login link beside the "Upload to Youtube" link. --- web/app/assets/images/google_signin.png | Bin 0 -> 3093 bytes .../dialogs/recordingFinishedDialog.css.scss | 8 ++++++++ .../dialogs/_recordingFinishedDialog.html.haml | 7 +++++-- web/app/views/shared/_google_login.html.slim | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 web/app/assets/images/google_signin.png create mode 100644 web/app/views/shared/_google_login.html.slim diff --git a/web/app/assets/images/google_signin.png b/web/app/assets/images/google_signin.png new file mode 100644 index 0000000000000000000000000000000000000000..408190f1fcfa3655b55ac5bd62cf6121559b4765 GIT binary patch literal 3093 zcmZ8j2{cr18^3cK8mY`=7c~eIveZ9BWnz#pWZ#WsDO*{>KXSp3_K58P0AQctd4f3r zAn@?s2gMDq2jjgh0D$+4A>o{5=-?dL_r9Z*DD>0R1j+wERdqZz(s*xX{@UxG+o3o;qUM^TB7KGgs@^@|h|4&BPTdApnZOG!no=0DwZn zF@gXV4ikw2AOHcNu>k%7Z+97)d=?X~6A2=BB&uFAxV-A8k+;{mO}Ke>acvK*ykKT` z3j>Yk9fOiJuINy}znsScZhL4-AV328`lrc!)Z(RYv72ZtKJC<{#c>s6=x*{o06~Sc zn$aYFiGQ|5D#)xSZWt_HQ02fK;JYmnWhRKlBT*25!GcIUghC@gSdIe_Xe>y8;7}1# zcmS3$SOAvc$YGhI{&q(AN<-nrm90rW`R5Tdv9Jxwg%Bq*H{ILvIfe>NzSRXzPPIEk zj;b_{A!=c{@GxC|xbKOj+Oz1ZQXjU1O)0c}sT@C~Wn}-0@Z|R8AiL6&Mfz@@y^F6S zuC6^e!_K2+->;j#6Wf_bJ6;_Ud-c_}{2c~>Qu!M^{d-usB!8>!7eTf;RkCu+)4?yd zoZDs8qy6I0eTwS#4}JDOH*<;9&Ck5Krk){J+1RA*ygVAL9iG=-M4kdCDtzEppj4W{ zN2+>-#D<*1>k|DIOHB)5;s2VsR(rGU04MZw-Ba1>s6Dym%Iy8+i9r)l17I{M|?#Dm&qn+19GAoyc)tjm+}%KbmB<6X+e`P6%>}sILo=JLdbC16$k9F07fgN#F9oC~}7or`RI<5Sn^EWTSw=#~4?4Hh@#Ow#n5WF!4FzNlHXhV@UGp>i(Z}>9_kO;hQwJ z!P&LXrs8_({V(4)*-nKI*+DzL`u{QVQi{Az?G0czEZyn5%@ZrRc<7&Ip%EkNx(0}L zo#}wPLQl1r-W6Jvt`cDB#gv7{^pJ|lh0kj@AGtI>9Sc-Qyq@T73_bU5`eDze`p^Bq zvxF`v)NJQ9NHR!aJGtxBkd{wH^#Mm#=hO~+=Y;TsF@x=3v2u{c9+lw{F)t*p!Ir<*el^z#(!`B3(ivI01BU%T-V}vOAXERe{M8o#uTi z0)l&mMEmY`&-zlJ)4qeW?1}r zz3mqmR)JEnC0r{cgwS!%y;t@$elyzw)9fMh@{zF46GimHPS%4j#AK(KJMJ{9=jYo| z4K_#-Kelzu^fGPryqQ!hmOV79YaD-pd?#;pYBpZ9_-#v;T=k_yYH*@71I60q^J+Xt zet39N{@f|-`#CXlqITFCpqhGHI0<~dn^sA z)Xu!gw^SQ}Q_l0${*_dC}tjhg}dB){wgR*XqE*vr$e z6oRznij`{fa4ETyvu%~sid!SK?e3g7u9r2i?Dg99{4Jxg-NWd|zxP(2H#@|q3;9(A zT=fGL3&WfeCJZ3wNIs;BZ%~no#-MlXTX%;YNj=s`gQJnfT2bhfbp0S$7AF+;k ztaRz6LEUG%O8q<+r3V2l1Txy5#NNw4V8Ost?y|Y3;a!#J6VK1dqV}+o{6~ktRVDbGx5pcQcvYaA< zdp%4KnmC5fyxkqkw{;NF*L}#fxb!XgYpV_hY5kUFasT#F56On1#s+%+Rpz3(PeMf&KxR+E0DZq6m^_Vn8;19G|Ndzq z&B|5~Vf}J8)ghxMEBr1`Wp7y!jNkgskk06!c6wTTdRvcg%?AR6X}yQ5_XwL~4ow{h zHnJr!I9f#Ln7XOWlrYn#!Sr+~x3i=F{;0`tzM9K%4dT`t^#?Je)@~V%%dXWcyvHK@ zj%B6fDVe40Dft&jfX4SG&cAY!dD&2^&&;(P=sl11UV17Ow2@e$3t<$tzuO~Q z+?nMOwT|u9wdPc!G#;s;1g3>GtQj#W#x50ZdLl=IPX=sOiVfE3wz&*>S*Hd|J>Um#biO>`zy|{H2u0kmVaPWw1xxX5eoZC4n^U~iM z2GWO<Aft%$bRlSXW5Mx1i;b@?^X%il^ANEZYJk+vAbg_4bEH`NEOemj5U zI_}{rnl2O)$vAr(G$LBUFh*eLI*6*={BfAMZKWW4iYcAM%2I9XJfI~@NNGV;65mVI zUQV3799s75hjE@H1J8vkEIZPp(BHD*VX=64b*oNXrhog>f+Rz;)o5Z&1}Rbt1n^Q0gfZsj5d%q)xuPQkDt2>x8l@yxLbl0j2H8pgBxq}uF zkq&~kyZ8FoW-9kv@#!z=OX4BT0UoLJY})sWtTswOwYyzl^uE+i`=~y(#ZHUlhKnfl zo)r7mIL%y{arj-((d%VU=bq!Fzj9{9_Nl)Bh@?m(9y;`9 zj7-{-S(-uVihfn=ke$x6sYJdkJI9hOIaX4bUi_;0f;h|imqydHXL1wA!v`d;dZdS< zKXTRjoch2YwaV)anygSgt1md1X2pV=B$pyg*8_2_%N|#@)a;CKywsblQp+iiTd$~o z9H(vwc3js(D*aYX>*;B)Z*v7~N9Up@mLlouyp(E^uTMayJN=l4^^FAoKgD+a*c)9o z)Kxfl0BKtTb4i$_edpz@2o6Vsi6-14`0qge?}(BFSOh78$T&^UB2)3bj!{ z_J&uyumGl#$jK06ahdWxx=@eOR$uYiRUYY9N3Z)dzP#+S*W(gkjxv$8C~`nJKDE{4 zEAClkw9r_rQX|6v0Z6?m6qXQt2xyD!KN#LRc3e{ec$3_rME8m&TDtNi=@)#I8bxCt nz+`C>PUf*tKL7s{=c_3@2;eQsCm;W<41ga)JtCo8hZOy9mJg3A literal 0 HcmV?d00001 diff --git a/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss index 3eb894abe..3d6208565 100644 --- a/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss @@ -40,5 +40,13 @@ } clear: left; } + + .google_login_button { + + } + + .signed_in_to_google { + color: yellow; + } } diff --git a/web/app/views/dialogs/_recordingFinishedDialog.html.haml b/web/app/views/dialogs/_recordingFinishedDialog.html.haml index ae4c58afa..07f493cb3 100644 --- a/web/app/views/dialogs/_recordingFinishedDialog.html.haml +++ b/web/app/views/dialogs/_recordingFinishedDialog.html.haml @@ -27,8 +27,11 @@ %input{:checked => "checked", :name => "save_video", :type => "checkbox"}/ %label{:for => "save_video"} Save Video to Computer .field.left{:purpose => "upload_to_youtube"} - %input{:checked => "checked", :name => "upload_to_youtube", :type => "checkbox"}/ - %label{:for => "upload_to_youtube"} Upload Video to YouTube + %span + %input{:checked => "checked", :name => "upload_to_youtube", :type => "checkbox"}/ + %label{:for => "upload_to_youtube"} Upload Video to YouTube + %span + = render(:partial => "shared/google_login") .field.left{:purpose => "is_public"} %input{:checked => "checked", :name => "is_public", :type => "checkbox"}/ %label{:for => "is_public"} Public Recording diff --git a/web/app/views/shared/_google_login.html.slim b/web/app/views/shared/_google_login.html.slim new file mode 100644 index 000000000..676749773 --- /dev/null +++ b/web/app/views/shared/_google_login.html.slim @@ -0,0 +1,5 @@ +-google_auth = JamRuby::UserAuthorization.google_auth(current_user).first +-if google_auth + span.signed_in_to_google="(Signed in)" +-else + input.google_login_button type='image' onclick='window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");' src="../assets/google_signin.png" height="30px" From cbb1a71ffad4a019e1aacb1a2cd0bcacea3214ed Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Tue, 4 Nov 2014 13:28:04 -0600 Subject: [PATCH 02/18] Check current_user for nil, as this dialog is included at a higher level than originally expected. --- web/app/views/shared/_google_login.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/views/shared/_google_login.html.slim b/web/app/views/shared/_google_login.html.slim index 676749773..d5c0592fd 100644 --- a/web/app/views/shared/_google_login.html.slim +++ b/web/app/views/shared/_google_login.html.slim @@ -1,4 +1,4 @@ --google_auth = JamRuby::UserAuthorization.google_auth(current_user).first +-google_auth = (current_user.nil?) ? nil : JamRuby::UserAuthorization.google_auth(current_user).first -if google_auth span.signed_in_to_google="(Signed in)" -else From 5ee531d9442c51b4cd76f800892b847fb32c5a5e Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Tue, 4 Nov 2014 14:59:53 -0600 Subject: [PATCH 03/18] More resilient reference to image. --- web/app/views/shared/_google_login.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/views/shared/_google_login.html.slim b/web/app/views/shared/_google_login.html.slim index d5c0592fd..3402eb8b3 100644 --- a/web/app/views/shared/_google_login.html.slim +++ b/web/app/views/shared/_google_login.html.slim @@ -2,4 +2,4 @@ -if google_auth span.signed_in_to_google="(Signed in)" -else - input.google_login_button type='image' onclick='window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");' src="../assets/google_signin.png" height="30px" + input.google_login_button type='image' onclick='window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");' src="/assets/google_signin.png" height="30px" From f1b32336cdd32671ac3bc8a7f8b13df65d7db4d1 Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Tue, 4 Nov 2014 15:55:50 -0600 Subject: [PATCH 04/18] VRFS-1849 : Hide UI for youtube upload until the rest of feature implemented. --- .../_recordingFinishedDialog.html.haml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/web/app/views/dialogs/_recordingFinishedDialog.html.haml b/web/app/views/dialogs/_recordingFinishedDialog.html.haml index 07f493cb3..fc3d53eff 100644 --- a/web/app/views/dialogs/_recordingFinishedDialog.html.haml +++ b/web/app/views/dialogs/_recordingFinishedDialog.html.haml @@ -23,15 +23,16 @@ .field.w100.left{:purpose => "description"} %label{:for => "description"} Description: %textarea#claim-recording-description.w100{:name => "description"} - .field.left{:purpose => "save_video"} - %input{:checked => "checked", :name => "save_video", :type => "checkbox"}/ - %label{:for => "save_video"} Save Video to Computer - .field.left{:purpose => "upload_to_youtube"} - %span - %input{:checked => "checked", :name => "upload_to_youtube", :type => "checkbox"}/ - %label{:for => "upload_to_youtube"} Upload Video to YouTube - %span - = render(:partial => "shared/google_login") + / TODO VRFS-1849: Uncomment this when rest of feature developed: + / .field.left{:purpose => "save_video"} + / %input{:checked => "checked", :name => "save_video", :type => "checkbox"}/ + / %label{:for => "save_video"} Save Video to Computer + / .field.left{:purpose => "upload_to_youtube"} + / %span + / %input{:checked => "checked", :name => "upload_to_youtube", :type => "checkbox"}/ + / %label{:for => "upload_to_youtube"} Upload Video to YouTube + / %span + / = render(:partial => "shared/google_login") .field.left{:purpose => "is_public"} %input{:checked => "checked", :name => "is_public", :type => "checkbox"}/ %label{:for => "is_public"} Public Recording From ea601711faffb14832e9524c49cad8cd5d4388d6 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 5 Nov 2014 20:32:37 -0600 Subject: [PATCH 05/18] * make desk keys configurable --- db/up/deletable_recordings.sql | 2 ++ ruby/lib/jam_ruby/lib/desk_multipass.rb | 7 ++----- ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb | 0 ...g_dialog.html.haml => _edit_recording_dialog.html.slim} | 0 web/config/application.rb | 5 +++++ 5 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 db/up/deletable_recordings.sql create mode 100644 ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb rename web/app/views/dialogs/{_edit_recording_dialog.html.haml => _edit_recording_dialog.html.slim} (100%) diff --git a/db/up/deletable_recordings.sql b/db/up/deletable_recordings.sql new file mode 100644 index 000000000..842c4d4d8 --- /dev/null +++ b/db/up/deletable_recordings.sql @@ -0,0 +1,2 @@ +-- this is to make sure we don't delete any recordings for 7 days +UPDATE recordings SET updated_at = NOW(); diff --git a/ruby/lib/jam_ruby/lib/desk_multipass.rb b/ruby/lib/jam_ruby/lib/desk_multipass.rb index 560551473..2c20cd6b5 100644 --- a/ruby/lib/jam_ruby/lib/desk_multipass.rb +++ b/ruby/lib/jam_ruby/lib/desk_multipass.rb @@ -11,9 +11,6 @@ module JamRuby # https://github.com/assistly/multipass-examples/blob/master/ruby.rb class DeskMultipass - API_KEY = "453ddfc0bab00130a9c13bc9a68cf24c" - SITE_KEY = "jamkazam" - def initialize(user) @user = user generate_token_and_signature @@ -29,7 +26,7 @@ module JamRuby private def generate_token_and_signature - key = Digest::SHA1.digest(API_KEY + SITE_KEY)[0...16] + key = Digest::SHA1.digest(APP_CONFIG.desk_multipass_key + APP_CONFIG.desk_multipass_site)[0...16] # Generate a random 16 byte IV iv = OpenSSL::Random.random_bytes(16) @@ -48,7 +45,7 @@ module JamRuby prepended = iv + encrypted token = Base64.encode64(prepended) - signature = Base64.encode64(OpenSSL::HMAC.digest('sha1', API_KEY, token)) + signature = Base64.encode64(OpenSSL::HMAC.digest('sha1', APP_CONFIG.desk_multipass_key, token)) @token = CGI.escape(token) @signature = CGI.escape(signature) diff --git a/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb new file mode 100644 index 000000000..e69de29bb diff --git a/web/app/views/dialogs/_edit_recording_dialog.html.haml b/web/app/views/dialogs/_edit_recording_dialog.html.slim similarity index 100% rename from web/app/views/dialogs/_edit_recording_dialog.html.haml rename to web/app/views/dialogs/_edit_recording_dialog.html.slim diff --git a/web/config/application.rb b/web/config/application.rb index b15e13426..76b4242a0 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -152,6 +152,9 @@ if defined?(Bundler) config.multipass_callback_url = "http://jamkazam.desk.com/customer/authentication/multipass/callback" end + config.desk_multipass_key = "453ddfc0bab00130a9c13bc9a68cf24c" # found in https://jamkazam.desk.com/admin/channels/support-center/auth_settings + config.desk_multipass_site = "jamkazam" # found in https://jamkazam.desk.com/admin/channels/support-center/auth_settings + # perf_data configs config.perf_data_signed_url_timeout = 3600 * 24 # 1 day @@ -273,5 +276,7 @@ if defined?(Bundler) config.scoring_timeout_threshold = 5 # how many consequetive bad scores before you are put into the doghouse config.scoring_get_work_interval = 1000 # how much time between normal getwork requests config.scoring_get_work_backoff_interval = 60 * 1000 # how much time between failed getwork requests + + config.recordings_stale_time = 3 # num days of inactivity before we decide that a recording is no longer going to be claimed end end From 415805c4ebd7873cb9e4761ecbb9266bc139a270 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 5 Nov 2014 21:54:08 -0500 Subject: [PATCH 06/18] VRFS-2256 allow invited and approved RSVP users to join approval_required sessions without having to send a join request --- web/app/assets/javascripts/sessionList.js | 3 +-- web/app/assets/javascripts/session_utils.js | 16 +--------------- web/app/views/api_music_sessions/show.rabl | 4 ++++ .../views/api_music_sessions/show_history.rabl | 4 ++++ 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index a54ba7cec..2dae2c083 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -135,8 +135,7 @@ }); if (showJoinLink) { - // wire up the Join Link to the T&Cs dialog - + // wire up the Join Link to the T&Cs dialog $('.join-link', $parentRow).click(function(evt) { if(!context.JK.guardAgainstBrowser(app)) { return false; diff --git a/web/app/assets/javascripts/session_utils.js b/web/app/assets/javascripts/session_utils.js index 0c22b0108..eea0d79a9 100644 --- a/web/app/assets/javascripts/session_utils.js +++ b/web/app/assets/javascripts/session_utils.js @@ -138,22 +138,8 @@ return; } - if ("invitations" in session) { - var invitation; - // user has invitations for this session - for (var i=0; i < session.invitations.length; i++) { - invitation = session.invitations[i]; - // session contains an invitation for this user - if (invitation.receiver_id === context.JK.currentUserId) { - hasInvitation = true; - break; - } - } - } - if (session) { - // if user has an invitation, always open terms and allow joining regardless of settings - if (hasInvitation) { + if (session.can_join) { logger.debug("Found invitation for user " + context.JK.currentUserId + ", session " + sessionId); openJoinSessionTerms(sessionId); } diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index 98f642a7f..e4c264a7c 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -15,6 +15,10 @@ else attributes :id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat, :band_id, :user_id, :claimed_recording_initiator_id, :track_changes_counter, :max_score + node :can_join do |session| + session.can_join?(current_user, true) + end + node :genres do |item| [item.genre.description] # XXX: need to return single genre; not array end diff --git a/web/app/views/api_music_sessions/show_history.rabl b/web/app/views/api_music_sessions/show_history.rabl index e027c0874..c86e6e04f 100644 --- a/web/app/views/api_music_sessions/show_history.rabl +++ b/web/app/views/api_music_sessions/show_history.rabl @@ -21,6 +21,10 @@ else :language, :recurring_mode, :language_description, :scheduled_start_date, :access_description, :timezone, :timezone_id, :timezone_description, :musician_access_description, :fan_access_description, :session_removed_at, :legal_policy, :open_rsvps, :is_unstructured_rsvp? + node :can_join do |session| + session.can_join?(current_user, true) + end + node :share_url do |history| unless history.share_token.nil? share_token_url(history.share_token.token) From 164fe8db61ae084c4b1f186f43787f5ac12da1a9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 6 Nov 2014 11:26:13 -0600 Subject: [PATCH 07/18] * recording cleanup logic VRFS-2393 --- db/manifest | 1 + db/up/deletable_recordings.sql | 35 ++ ruby/lib/jam_ruby.rb | 1 + ruby/lib/jam_ruby/models/feed.rb | 2 +- ruby/lib/jam_ruby/models/mix.rb | 8 +- ruby/lib/jam_ruby/models/recorded_track.rb | 7 +- ruby/lib/jam_ruby/models/recording.rb | 80 +++- ruby/lib/jam_ruby/models/user_sync.rb | 25 ++ .../resque/scheduled/recordings_cleaner.rb | 32 ++ ruby/spec/factories.rb | 39 +- ruby/spec/jam_ruby/models/feed_spec.rb | 14 + ruby/spec/jam_ruby/models/recording_spec.rb | 410 ++++++++++++++++++ ruby/spec/jam_ruby/models/user_sync_spec.rb | 206 ++++++++- ruby/spec/support/utilities.rb | 4 + web/app/assets/javascripts/feedHelper.js | 11 + web/app/assets/javascripts/jam_rest.js | 10 + .../assets/javascripts/recordingManager.js | 6 +- .../assets/javascripts/sync_viewer.js.coffee | 86 +++- web/app/assets/javascripts/utils.js | 2 +- .../client/recordingManager.css.scss | 3 + .../dialogs/editRecordingDialog.css.scss | 18 + .../stylesheets/users/syncViewer.css.scss | 12 +- .../api_claimed_recordings_controller.rb | 2 +- .../controllers/api_recordings_controller.rb | 16 +- .../controllers/api_user_syncs_controller.rb | 5 + web/app/controllers/recordings_controller.rb | 4 +- .../views/api_claimed_recordings/show.rabl | 2 +- web/app/views/api_feeds/show.rabl | 2 +- web/app/views/api_recordings/show.rabl | 2 +- web/app/views/clients/_help.html.erb | 14 +- .../views/clients/_recordingManager.html.erb | 3 + .../clients/_sync_viewer_templates.html.slim | 6 +- .../dialogs/_edit_recording_dialog.html.slim | 34 +- web/app/views/recordings/show.html.erb | 4 +- .../users/_feed_recording_ajax.html.haml | 2 +- web/config/routes.rb | 2 + web/config/scheduler.yml | 7 +- .../api_user_syncs_controller_spec.rb | 6 + 38 files changed, 1055 insertions(+), 68 deletions(-) diff --git a/db/manifest b/db/manifest index c8652bebf..3625cb735 100755 --- a/db/manifest +++ b/db/manifest @@ -225,3 +225,4 @@ add_youtube_flag_to_claimed_recordings.sql add_session_create_type.sql user_syncs_and_quick_mix.sql user_syncs_fix_dup_tracks_2408.sql +deletable_recordings.sql diff --git a/db/up/deletable_recordings.sql b/db/up/deletable_recordings.sql index 842c4d4d8..648b45e19 100644 --- a/db/up/deletable_recordings.sql +++ b/db/up/deletable_recordings.sql @@ -1,2 +1,37 @@ -- this is to make sure we don't delete any recordings for 7 days UPDATE recordings SET updated_at = NOW(); + +ALTER TABLE recordings ADD COLUMN deleted BOOLEAN DEFAULT FALSE NOT NULL; + +DROP VIEW user_syncs; + +CREATE VIEW user_syncs AS + SELECT DISTINCT b.id AS recorded_track_id, + CAST(NULL as BIGINT) AS mix_id, + CAST(NULL as BIGINT) as quick_mix_id, + b.id AS unified_id, + a.user_id AS user_id, + b.fully_uploaded, + recordings.created_at AS created_at, + recordings.id AS recording_id + FROM recorded_tracks a INNER JOIN recordings ON a.recording_id = recordings.id AND duration IS NOT NULL AND all_discarded = FALSE AND deleted = FALSE INNER JOIN recorded_tracks b ON a.recording_id = b.recording_id + UNION ALL + SELECT CAST(NULL as BIGINT) AS recorded_track_id, + mixes.id AS mix_id, + CAST(NULL as BIGINT) AS quick_mix_id, + mixes.id AS unified_id, + claimed_recordings.user_id AS user_id, + NULL as fully_uploaded, + recordings.created_at AS created_at, + recordings.id AS recording_id + FROM mixes INNER JOIN recordings ON mixes.recording_id = recordings.id INNER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id WHERE claimed_recordings.discarded = FALSE AND deleted = FALSE + UNION ALL + SELECT CAST(NULL as BIGINT) AS recorded_track_id, + CAST(NULL as BIGINT) AS mix_id, + quick_mixes.id AS quick_mix_id, + quick_mixes.id AS unified_id, + quick_mixes.user_id, + quick_mixes.fully_uploaded, + recordings.created_at AS created_at, + recordings.id AS recording_id + FROM quick_mixes INNER JOIN recordings ON quick_mixes.recording_id = recordings.id AND duration IS NOT NULL AND all_discarded = FALSE AND deleted = FALSE; diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 649dc40e6..22f97a24f 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -54,6 +54,7 @@ require "jam_ruby/resque/scheduled/music_session_scheduler" require "jam_ruby/resque/scheduled/active_music_session_cleaner" require "jam_ruby/resque/scheduled/score_history_sweeper" require "jam_ruby/resque/scheduled/scheduled_music_session_cleaner" +require "jam_ruby/resque/scheduled/recordings_cleaner" require "jam_ruby/resque/google_analytics_event" require "jam_ruby/resque/batch_email_job" require "jam_ruby/mq_router" diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index 7fba1e705..71b10d054 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -35,7 +35,7 @@ module JamRuby query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id") .joins("LEFT OUTER JOIN music_sessions ON music_sessions.id = feeds.music_session_id") .limit(limit) - .where('recordings is NULL OR recordings.all_discarded = false') # remove any 'all_discarded recordings from the search results' + .where('recordings is NULL OR (recordings.all_discarded = false AND recordings.deleted = false)') # remove any 'all_discarded recordings from the search results' or 'deleted' # handle sort if sort == 'date' diff --git a/ruby/lib/jam_ruby/models/mix.rb b/ruby/lib/jam_ruby/models/mix.rb index 957e46ba4..8e8ab2ace 100644 --- a/ruby/lib/jam_ruby/models/mix.rb +++ b/ruby/lib/jam_ruby/models/mix.rb @@ -198,13 +198,15 @@ module JamRuby self.last_downloaded_at = Time.now end - private def delete_s3_files - s3_manager.delete(filename(type='ogg')) if self[:ogg_url] - s3_manager.delete(filename(type='mp3')) if self[:mp3_url] + s3_manager.delete(filename(type='ogg')) if self[:ogg_url] && s3_manager.exists?(filename(type='ogg')) + s3_manager.delete(filename(type='mp3')) if self[:mp3_url] && s3_manager.exists?(filename(type='mp3')) end + private + + def self.construct_filename(created_at, recording_id, id, type='ogg') raise "unknown ID" unless id diff --git a/ruby/lib/jam_ruby/models/recorded_track.rb b/ruby/lib/jam_ruby/models/recorded_track.rb index ce3299d8b..ccd5a2a3c 100644 --- a/ruby/lib/jam_ruby/models/recorded_track.rb +++ b/ruby/lib/jam_ruby/models/recorded_track.rb @@ -218,11 +218,12 @@ module JamRuby self.last_downloaded_at = Time.now end + def delete_s3_files + s3_manager.delete(self[:url]) if self[:url] && s3_manager.exists?(self[:url]) + end + private - def delete_s3_files - s3_manager.delete(self[:url]) if self[:url] - end def self.construct_filename(created_at, recording_id, client_track_id) raise "unknown ID" unless client_track_id diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 663622fff..34ccf78a7 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -1,12 +1,8 @@ module JamRuby class Recording < ActiveRecord::Base - self.primary_key = 'id' - - @@log = Logging.logger[Recording] - attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, as: :admin has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User" @@ -225,8 +221,6 @@ module JamRuby self end - - # Called when a user wants to "claim" a recording. To do this, the user must have been one of the tracks in the recording. def claim(user, name, description, genre, is_public, upload_to_youtube=false) upload_to_youtube = !!upload_to_youtube # Correct where nil is borking save @@ -255,6 +249,8 @@ module JamRuby def keep(user) recorded_tracks_for_user(user).update_all(:discard => false) + Recording.where(:id => id).update_all(:updated_at => Time.now) # updated updated_at for benefit of RecordingsCleaner + User.where(:id => user.id).update_all(:first_recording_at => Time.now ) unless user.first_recording_at end @@ -263,6 +259,8 @@ module JamRuby def discard(user) recorded_tracks_for_user(user).update_all(:discard => true) + Recording.where(:id => id).update_all(:updated_at => Time.now) # updated updated_at for benefit of RecordingsCleaner + # check if all recorded_tracks for this recording are discarded if recorded_tracks.where('discard = false or discard is NULL').length == 0 self.all_discarded = true # the feed won't pick this up; also background cleanup will find these and whack them later @@ -299,6 +297,8 @@ module JamRuby .order('recorded_tracks.id') .where('recorded_tracks.fully_uploaded = TRUE') .where('recorded_tracks.id > ?', since) + .where('all_discarded = false') + .where('deleted = false') .where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user).limit(limit).each do |recorded_track| downloads.push( { @@ -319,6 +319,8 @@ module JamRuby .order('mixes.id') .where('mixes.completed_at IS NOT NULL') .where('mixes.id > ?', since) + .where('all_discarded = false') + .where('deleted = false') .where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user) .limit(limit).each do |mix| downloads.push( @@ -454,6 +456,7 @@ module JamRuby .where("upload_failures <= #{APP_CONFIG.max_track_upload_failures}") \ .where("duration IS NOT NULL") \ .where('all_discarded = false') \ + .where('deleted = false') \ .order('recorded_items_all.id') \ .limit(limit) @@ -529,10 +532,71 @@ module JamRuby end # returns a ClaimedRecording that the user did not discard - def claim_for_user(user) + def claim_for_user(user, ignore_discarded = false) return nil unless user claim = claimed_recordings.find{|claimed_recording| claimed_recording.user == user } - claim unless claim && claim.discarded + if ignore_discarded + claim + else + claim unless claim && claim.discarded + end + end + + def self.when_will_be_discarded? + + recorded_track_votes = recorded_tracks.map(&:discard) + + discarded = 0 + recorded_track_votes.each do |discard_vote| + if discard_vote == nil || discard_vote == true + discarded = discarded + 1 + end + end + + if recorded_track_votes.length == discarded + # all tracks are discarded, figure out due time for deletion + # 3 days in seconds - amount of seconds since last updated + ((APP_CONFIG.recordings_stale_time * 3600 * 24) - (Time.now - updated_at).to_i).seconds.from_now + else + return nil + end + end + + # finds all discarded recordings that are sufficiently stale (i.e., abandoned by all those involved, and hasn't been mucked with in a while) + def self.discarded_and_stale + + # we count up all tracks for the Recording, and count up all discarded/not-voted-on tracks + # if they are equal, and if the recording is stale, let's return it. + Recording + .joins("INNER JOIN recorded_tracks ON recordings.id = recorded_tracks.recording_id") + .joins(%Q{ + LEFT OUTER JOIN + (SELECT id + FROM recorded_tracks WHERE discard IS NULL OR discard = TRUE) AS discard_info + ON recorded_tracks.id = discard_info.id + }) + .group("recordings.id") + .having('COUNT(recorded_tracks.id) = COUNT(discard_info.id)') + .where("NOW() - recordings.updated_at > '#{APP_CONFIG.recordings_stale_time} day'::INTERVAL") + .limit(1000) + end + + def mark_delete + + mixes.each do |mix| + mix.delete_s3_files + end + + quick_mixes.each do |quick_mix| + quick_mix.delete_s3_files + end + + recorded_tracks.each do |recorded_track| + recorded_track.delete_s3_files + end + + self.deleted = true + self.save(:validate => false) end private diff --git a/ruby/lib/jam_ruby/models/user_sync.rb b/ruby/lib/jam_ruby/models/user_sync.rb index 17925bcdf..03e4b938a 100644 --- a/ruby/lib/jam_ruby/models/user_sync.rb +++ b/ruby/lib/jam_ruby/models/user_sync.rb @@ -41,6 +41,31 @@ module JamRuby { query:query, next: offset + limit} end end + + + def self.deletables(params) + user_id = params[:user_id] + recording_ids = params[:recording_ids] + + limit = 1000 + + recording_ids = recording_ids.uniq + + raise "too many recording_ids" if recording_ids.length > limit + + found_recording_ids = + UserSync + .select('user_syncs.recording_id') + .joins("LEFT OUTER JOIN claimed_recordings ON claimed_recordings.user_id = user_syncs.user_id") + .where(%Q{ + ((claimed_recordings IS NULL OR claimed_recordings.discarded = TRUE) AND fully_uploaded = FALSE) OR (claimed_recordings IS NOT NULL AND claimed_recordings.discarded = FALSE) + }) + .where(user_id: user_id) + .paginate(:page => 1, :per_page => limit) + .group('user_syncs.recording_id').map(&:recording_id) + + recording_ids - found_recording_ids + end end end diff --git a/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb index e69de29bb..1e4b41769 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb @@ -0,0 +1,32 @@ +require 'json' +require 'resque' +require 'resque-retry' +require 'net/http' +require 'digest/md5' + +module JamRuby + + # periodically scheduled to find recordings to cleanup + class RecordingsCleaner + extend Resque::Plugins::LonelyJob + + @queue = :recordings_cleaner + + @@log = Logging.logger[RecordingsCleaner] + + def self.lock_timeout + # this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job + 1200 + end + + def self.perform + discarded_recordings = Recording.discarded_and_stale + + discarded_recordings.each do |recording| + @@log.debug("deleting recording #{recording.id}") + recording.mark_delete + end + end + end + +end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index b91b9dee1..264b44323 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -250,8 +250,6 @@ FactoryGirl.define do end factory :recorded_video, :class => JamRuby::RecordedVideo do - sequence(:client_id) { |n| "client_id-#{n}"} - sequence(:recording_id) { |n| "recording_id-#{n}"} sequence(:client_video_source_id) { |n| "client_video_source_id-#{n}"} fully_uploaded true length 1 @@ -283,14 +281,17 @@ FactoryGirl.define do association :genre, factory: :genre association :user, factory: :user - before(:create) { |claimed_recording| - claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user) unless claimed_recording.recording + before(:create) { |claimed_recording, evaluator| + claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user) unless evaluator.recording } end factory :mix, :class => JamRuby::Mix do + ignore do + autowire true + end started_at Time.now completed_at Time.now ogg_md5 'abc' @@ -301,10 +302,12 @@ FactoryGirl.define do sequence(:mp3_url) { |n| "recordings/mp3/#{n}" } completed true - before(:create) {|mix| - user = FactoryGirl.create(:user) - mix.recording = FactoryGirl.create(:recording_with_track, owner: user) - mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording) + before(:create) {|mix, evaluator| + if evaluator.autowire + user = FactoryGirl.create(:user) + mix.recording = FactoryGirl.create(:recording_with_track, owner: user) + mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording) + end } end @@ -330,6 +333,19 @@ FactoryGirl.define do mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording) end } + + + factory :quick_mix_completed do + started_at 1.minute.ago + completed_at Time.now + ogg_md5 'a' + ogg_length 1 + ogg_url 'recordings/ogg' + mp3_md5 'a' + mp3_length 1 + mp3_url 'recordings/mp3' + completed true + end end factory :invited_user, :class => JamRuby::InvitedUser do @@ -526,6 +542,13 @@ FactoryGirl.define do token_expires_at Time.now end + + factory :recording_comment, :class => JamRuby::RecordingComment do + sequence(:comment) { |n| "comment-#{n}" } + association :recording, factory: :recording + association :user, factory: :recording + end + factory :playable_play, :class => JamRuby::PlayablePlay do end diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index 16ae1b73a..550802c8b 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -464,5 +464,19 @@ describe Feed do end end + describe "deleted recording" do + it "should not show" do + claimed_recording = FactoryGirl.create(:claimed_recording) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording + MusicSession.delete_all # the factory makes a music_session while making the recording/claimed_recording + recording = claimed_recording.recording + recording.mark_delete + recording.reload + recording.deleted.should == true + feeds, next_page = Feed.index(user1) + feeds.length.should == 0 + end + end + end diff --git a/ruby/spec/jam_ruby/models/recording_spec.rb b/ruby/spec/jam_ruby/models/recording_spec.rb index c093d5950..01b1290e1 100644 --- a/ruby/spec/jam_ruby/models/recording_spec.rb +++ b/ruby/spec/jam_ruby/models/recording_spec.rb @@ -430,6 +430,19 @@ describe Recording do Recording.list_uploads(@user, 10, uploads["next"])["uploads"].length.should == 0 end + + it "should not consider deleted recordings" do + stub_const("APP_CONFIG", app_config) + @recording = Recording.start(@music_session, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @recording.claim(@user, "Recording", "Recording Description", @genre, true) + @recording.mark_delete + uploads = Recording.list_uploads(@user) + uploads["uploads"].length.should == 0 + end + it "should return a download only if claimed" do @recording = Recording.start(@music_session, @user) @recording.stop @@ -444,6 +457,21 @@ describe Recording do downloads["downloads"].length.should == 1 end + it "should not return a download if recording is deleted" do + @recording = Recording.start(@music_session, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @recording.claim(@user, "Recording", "Recording Description", @genre, true) + downloads = Recording.list_downloads(@user) + downloads["downloads"].length.should == 0 + @recorded_track = RecordedTrack.where(:recording_id => @recording.id)[0] + @recorded_track.update_attribute(:fully_uploaded, true) + @recording.mark_delete + downloads = Recording.list_downloads(@user) + downloads["downloads"].length.should == 0 + end + it "should mark first_recording_at" do @recording = Recording.start(@music_session, @user) @recording.stop @@ -638,6 +666,388 @@ describe Recording do uploads["uploads"].should have(0).items end + + describe "discarded_and_stale" do + let(:recording1) {FactoryGirl.create(:recording_with_track)} + let(:track1) {recording1.recorded_tracks[0]} + + it "no results if no recordings" do + Recording.discarded_and_stale.length.should == 0 + end + + describe "with one recording" do + before(:each) do + track1.discard.should be_nil + end + + describe "that has no votes" do + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + # and we should find it now... + stale = Recording.discarded_and_stale + stale.first.should eq(recording1) + end + end + + describe "that has discard vote" do + + before(:each) do + track1.discard = true + track1.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + # and we should find it now... + stale = Recording.discarded_and_stale + stale.first.should eq(recording1) + end + end + + describe "that has keep vote" do + + before(:each) do + track1.discard = false + track1.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "still not found it has an old updated_at time, because it was kept" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + # and still not find it + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + end + + describe "that has two tracks, no votes" do + + let(:track2) { FactoryGirl.create(:recorded_track, recording: recording1, user: recording1.owner) } + + before(:each) do + track1.discard = nil + track1.save! + + recording1.recorded_tracks << track2 + recording1.save! + + track2.discard = nil + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + # and we should find it now... + stale = Recording.discarded_and_stale + stale.first.should eq(recording1) + end + end + end + + describe "that has two tracks, two discard" do + + let(:track2) { FactoryGirl.create(:recorded_track, recording: recording1, user: recording1.owner) } + + before(:each) do + track1.discard = true + track1.save! + + recording1.recorded_tracks << track2 + recording1.save! + + track2.discard = true + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + # and we should find it now... + stale = Recording.discarded_and_stale + stale.first.should eq(recording1) + end + end + + describe "that has two tracks, one discard, one keep" do + + let(:track2) { FactoryGirl.create(:recorded_track, recording: recording1, user: recording1.owner) } + + before(:each) do + track1.discard = true + track1.save! + + recording1.recorded_tracks << track2 + recording1.save! + + track2.discard = false + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + end + + describe "that has two tracks, two keeps" do + + let(:track2) { FactoryGirl.create(:recorded_track, recording: recording1, user: recording1.owner) } + + before(:each) do + track1.discard = false + track1.save! + + recording1.recorded_tracks << track2 + recording1.save! + + track2.discard = false + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + end + + describe "two recordings" do + let(:recording2) {FactoryGirl.create(:recording_with_track)} + let(:track2) {recording2.recorded_tracks[0]} + + describe "both discard" do + + before(:each) do + track1.discard = true + track1.save! + + track2.discard = true + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 1 + stale.first.should eq(recording1) + + Recording.where(:id => recording2.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 2 + end + end + + describe "both keep" do + + before(:each) do + track1.discard = false + track1.save! + + track2.discard = false + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + + Recording.where(:id => recording2.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + end + + describe "one keep, one discard" do + + before(:each) do + track1.discard = false + track1.save! + + track2.discard = true + track2.save! + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + + Recording.where(:id => recording2.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 1 + stale.first.should eq(recording2) + end + end + + describe "two keeps, two discards" do + + let(:track1_2) { FactoryGirl.create(:recorded_track, recording: recording1, user: recording1.owner) } + let(:track2_2) { FactoryGirl.create(:recorded_track, recording: recording2, user: recording1.owner) } + + before(:each) do + track1.discard = false + track1.save! + recording1.recorded_tracks << track1_2 + recording1.save! + track1_2.discard = false + track1_2.save! + + track2.discard = true + track2.save! + recording2.recorded_tracks << track2_2 + recording2.save! + track2_2.discard = true + track2_2.save! + + end + + it "not found if it has recent updated_at time" do + stale = Recording.discarded_and_stale + stale.length.should == 0 + end + + it "found if it has an old updated_at time" do + # now age the recording + Recording.where(:id => recording1.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 0 + + Recording.where(:id => recording2.id).update_all(:updated_at => (APP_CONFIG.recordings_stale_time + 1).days.ago) + + stale = Recording.discarded_and_stale + stale.length.should == 1 + stale.first.should eq(recording2) + end + end + end + end + + describe "delete" do + let(:mix) {FactoryGirl.create(:mix)} + let(:recording) {mix.recording} + + before(:each) do + + end + + it "success" do + FactoryGirl.create(:quick_mix, user: recording.owner, recording:recording, autowire: false) + FactoryGirl.create(:recording_comment, recording: recording, user: recording.owner) + FactoryGirl.create(:recording_like, recording: recording, claimed_recording: recording.claimed_recordings.first, favorite:true) + FactoryGirl.create(:playable_play, playable_id: recording.id, playable_type: 'JamRuby::Recording') + FactoryGirl.create(:recorded_video, user: recording.owner, recording: recording) + recording.reload + recording.claimed_recordings.length.should > 0 + recording.mixes.length.should > 0 + recording.quick_mixes.length.should > 0 + recording.recorded_tracks.length.should > 0 + recording.comments.length.should > 0 + recording.likes.length.should > 0 + recording.plays.length.should > 0 + recording.recorded_videos.length.should > 0 + recording.feed.should_not be_nil + + claimed_recording = recording.claimed_recordings.first + quick_mix = recording.quick_mixes.first + track = recording.recorded_tracks.first + comment = recording.comments.first + like = recording.likes.first + play = recording.plays.first + feed = recording.feed + video = recording.recorded_videos.first + + recording.mark_delete + + ClaimedRecording.find_by_id(claimed_recording.id).should_not be_nil + Mix.find_by_id(mix.id).should_not be_nil + QuickMix.find_by_id(quick_mix.id).should_not be_nil + RecordedTrack.find_by_id(track.id).should_not be_nil + RecordingComment.find_by_id(comment.id).should_not be_nil + PlayablePlay.find_by_id(play.id).should_not be_nil + RecordingLiker.find_by_id(like.id).should_not be_nil + Feed.find_by_id(feed.id).should_not be_nil + RecordedVideo.find_by_id(video.id).should_not be_nil + end + end end diff --git a/ruby/spec/jam_ruby/models/user_sync_spec.rb b/ruby/spec/jam_ruby/models/user_sync_spec.rb index 349cf706e..83b9e4465 100644 --- a/ruby/spec/jam_ruby/models/user_sync_spec.rb +++ b/ruby/spec/jam_ruby/models/user_sync_spec.rb @@ -20,19 +20,25 @@ describe UserSync do data[:next].should be_nil end - it "one mix" do + it "one mix and quick mix" do mix = FactoryGirl.create(:mix) mix.recording.duration = 1 mix.recording.save! + quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id}) data[:next].should be_nil user_syncs = data[:query] - user_syncs.length.should == 2 + user_syncs.length.should == 3 user_syncs[0].recorded_track.should == mix.recording.recorded_tracks[0] user_syncs[0].mix.should be_nil + user_syncs[0].quick_mix.should be_nil user_syncs[1].mix.should == mix user_syncs[1].recorded_track.should be_nil + user_syncs[1].quick_mix.should be_nil + user_syncs[2].mix.should be_nil + user_syncs[2].recorded_track.should be_nil + user_syncs[2].quick_mix.should eq(quick_mix) end it "two mixes, one not belonging to querier" do @@ -267,8 +273,204 @@ describe UserSync do user_syncs = data[:query] user_syncs.length.should == 0 data[:query].total_entries.should == 2 + end + end + it "does not return deleted recordings" do + mix = FactoryGirl.create(:mix) + mix.recording.duration = 1 + mix.recording.save! + quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) + mix.recording.mark_delete + data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id}) + data[:next].should be_nil + user_syncs = data[:query] + user_syncs.length.should == 0 + end + + describe "deletable" do + describe "one mix and one quick mix" do + + let!(:mix) { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!; m} + let!(:quick_mix) { FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) } + + it "unknown id" do + recording_ids = ['1'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(recording_ids) + end + + it "unknown ids" do + recording_ids = ['1', '2', '3'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(recording_ids) + end + + it "valid recording id" do + recording_ids = [mix.recording.id] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq([]) + end + + it "valid recording id" do + recording_ids = [mix.recording.id] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq([]) + end + + it "valid recording_id mixed with unknown ids" do + recording_ids = [mix.recording.id, '1'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(['1']) + end + + end + + describe "two recordings" do + + let!(:mix) { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!; m} + let!(:quick_mix) { FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) } + + let!(:mix2) { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!; m} + let!(:quick_mix2) { FactoryGirl.create(:quick_mix_completed, recording:mix2.recording, user: mix2.recording.recorded_tracks[0].user, autowire:false) } + + before(:each) do + # fix up the user associated with the second mix/recording to be same as 1st + mix2.recording.owner = mix.recording.owner + mix2.recording.save! + mix2.recording.recorded_tracks[0].user = mix.recording.owner + mix2.recording.recorded_tracks[0].save! + end + + it "unknown id" do + recording_ids = ['1'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(recording_ids) + end + + it "unknown ids" do + recording_ids = ['1', '2', '3'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(recording_ids) + end + + it "valid recording id" do + recording_ids = [mix.recording.id, mix2.recording.id] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq([]) + end + + it "valid recording id" do + recording_ids = [mix.recording.id] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq([]) + end + + it "valid recording_id mixed with unknown ids" do + recording_ids = [mix.recording.id, '1'] + + result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids) + + result.should eq(['1']) + end + end + + it "resolved recordings" do + + # start with a recording with a fully uploaded recorded_track, and no claim + recording = FactoryGirl.create(:recording_with_track, owner: user1) + recording.duration = 1 + recording.save! + + recording_ids = [recording.id] + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq(recording_ids) + + # set the recorded_track to not fully uploaded, which should make it not deletable + recording.recorded_tracks[0].fully_uploaded = false + recording.recorded_tracks[0].save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq([]) + + # mark recording as deleted, which should make it deletable + recording.deleted = true + recording.save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq(recording_ids) + + # mark recording as fully discarded, which should make it deletable + recording.all_discarded = true + recording.deleted = false + recording.save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq(recording_ids) + + # claim the recording, and make the track not fully uploaded + recording.recorded_tracks[0].fully_uploaded = false + recording.all_discarded = false + claim = FactoryGirl.create(:claimed_recording, user: user1, recording: recording) + recording.save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq([]) + + # create a mix while still claiming the recording + mix = FactoryGirl.create(:mix, autowire:false, recording:recording) + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq([]) + + # now take away the claim, and make sure the track is fully uploaded + claim.discarded = true + claim.save! + # without a claimed recording, make the track fully uploaded, so as to not trigger 'need to upload' logic + recording.recorded_tracks[0].fully_uploaded = true + recording.recorded_tracks[0].save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq(recording_ids) + + # if we make a quick mix, but still have no claim, we still should still need to delete the recording + quick_mix = FactoryGirl.create(:quick_mix, autowire:false, user: user1, recording: recording, fully_uploaded: true) + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq(recording_ids) + + # make the quick_mix be not fully_uploaded, which should make it not be marked for deleting because we need to upload it + quick_mix.fully_uploaded = false + quick_mix.save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq([]) + + quick_mix.fully_uploaded = true + quick_mix.save! + claim.discarded = false + claim.save! + + result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids) + result.should eq([]) end end end diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index 0ed42f4d9..48a525fe0 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -138,6 +138,10 @@ def app_config 2 # don't put to 1; it'll break tests end + def recordings_stale_time + 7 + end + private def audiomixer_workspace_path diff --git a/web/app/assets/javascripts/feedHelper.js b/web/app/assets/javascripts/feedHelper.js index 328dcdace..9ae0b54db 100644 --- a/web/app/assets/javascripts/feedHelper.js +++ b/web/app/assets/javascripts/feedHelper.js @@ -23,6 +23,7 @@ var nextPage = 1; var $includeType = null; var didLoadAllFeeds = false, isLoading = false; + var $templateRecordingDiscardedSoon = null; function defaultQuery() { var query = { limit: feedBatchSize }; @@ -441,6 +442,15 @@ var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'})); var $controls = $feedItem.find('.recording-controls'); + var $titleText = $feedItem.find('.title .title-text'); + + // if this item will be discarded, tack on a * to the RECORDING NAME + var discardTime = feed['when_will_be_discarded?']; + if(discardTime) { + context.JK.helpBubble($titleText, 'recording-discarded-soon', {discardTime: discardTime}, {}); + $titleText.text($titleText.text() + '*'); + } + $controls.data('mix-state', feed.mix_info) // for recordingUtils helper methods $controls.data('server-info', feed.mix) // for recordingUtils helper methods $controls.data('view-context', 'feed') @@ -589,6 +599,7 @@ $includeDate.val(defaults.time_range) $includeType.val(defaults.type) + $templateRecordingDiscardedSoon = $('#template-help-recording-discarded-soon'); events(); } diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 5c714bde5..47368278c 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1064,6 +1064,15 @@ }); } + function deleteRecordingClaim(id) { + return $.ajax({ + type: "DELETE", + dataType: "json", + contentType: 'application/json', + url: "/api/recordings/" + id + "/claim" + }); + } + function claimRecording(options) { var recordingId = options["id"]; @@ -1337,6 +1346,7 @@ this.getClaimedRecording = getClaimedRecording; this.updateClaimedRecording = updateClaimedRecording; this.deleteClaimedRecording = deleteClaimedRecording; + this.deleteRecordingClaim = deleteRecordingClaim; this.claimRecording = claimRecording; this.startPlayClaimedRecording = startPlayClaimedRecording; this.stopPlayClaimedRecording = stopPlayClaimedRecording; diff --git a/web/app/assets/javascripts/recordingManager.js b/web/app/assets/javascripts/recordingManager.js index 30b10da4c..be2dacca6 100644 --- a/web/app/assets/javascripts/recordingManager.js +++ b/web/app/assets/javascripts/recordingManager.js @@ -25,6 +25,8 @@ var $uploadPercent = $('#recording-manager-upload .percent', $parentElement); var $convertCommand = $('#recording-manager-convert', $parentElement); var $convertPercent = $('#recording-manager-convert .percent', $parentElement); + var $deleteCommand = $('#recording-manager-delete', $parentElement); + var $deletePercent = $('#recording-manager-delete .percent', $parentElement); var $fileManager = $('#recording-manager-launcher', $parentElement); if($fileManager.length == 0) {throw "no file manager element"; } @@ -32,12 +34,14 @@ $downloadCommand.data('command-type', 'download') $uploadCommand.data('command-type', 'upload') $convertCommand.data('command-type', 'convert') + $deleteCommand.data('command-type', 'delete') // keys come from backend var lookup = { SyncDownload: { command: $downloadCommand, percent: $downloadPercent}, SyncUpload: { command: $uploadCommand, percent: $uploadPercent}, - SyncConvert: { command: $convertCommand, percent: $convertPercent} + SyncConvert: { command: $convertCommand, percent: $convertPercent}, + SyncDelete: { command: $deleteCommand, percent: $deletePercent} } var $self = $(this); diff --git a/web/app/assets/javascripts/sync_viewer.js.coffee b/web/app/assets/javascripts/sync_viewer.js.coffee index 4be93ba13..98f43ec18 100644 --- a/web/app/assets/javascripts/sync_viewer.js.coffee +++ b/web/app/assets/javascripts/sync_viewer.js.coffee @@ -14,7 +14,10 @@ context.JK.SyncViewer = class SyncViewer @downloadCommandId = null @downloadMetadata = null @uploadCommandId = null - @uploadMetadata = null; + @uploadMetadata = null + @cleanupCommandId = null + @cleanupMetadata = null + init: () => @root = $($('#template-sync-viewer').html()) @@ -330,12 +333,15 @@ context.JK.SyncViewer = class SyncViewer for clientInfo in recording.local_tracks $track = @list.find(".recorded-track[data-recording-id='#{recording.recording_id}'][data-client-track-id='#{clientInfo.client_track_id}']") $track.data('client-info', clientInfo) + $track.data('total-size', recording.size) $track = @list.find(".mix[data-recording-id='#{recording.recording_id}']") $track.data('client-info', recording.mix) + $track.data('total-size', recording.size) $track = @list.find(".stream-mix[data-recording-id='#{recording.recording_id}']") $track.data('client-info', recording.stream_mix) + $track.data('total-size', recording.size) displayStreamMixHover: ($streamMix) => $clientState = $streamMix.find('.client-state') @@ -470,10 +476,10 @@ context.JK.SyncViewer = class SyncViewer sendCommand: ($retry, cmd) => if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession() - context.JK.confirmBubble($retry, 'sync-viewer-paused', {}, {offsetParent: $retry.closest('.dialog')}) + context.JK.ackBubble($retry, 'sync-viewer-paused', {}, {offsetParent: $retry.closest('.dialog')}) else context.jamClient.OnTrySyncCommand(cmd) - context.JK.confirmBubble($retry, 'sync-viewer-retry', {}, {offsetParent: $retry.closest('.dialog')}) + context.JK.ackBubble($retry, 'sync-viewer-retry', {}, {offsetParent: $retry.closest('.dialog')}) retryDownloadRecordedTrack: (e) => @@ -564,7 +570,7 @@ context.JK.SyncViewer = class SyncViewer exportRecording: (e) => $export = $(e.target) if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession() - context.JK.confirmBubble($export, 'sync-viewer-paused', {}, {offsetParent: $export.closest('.dialog')}) + context.JK.ackBubble($export, 'sync-viewer-paused', {}, {offsetParent: $export.closest('.dialog')}) return recordingId = $export.closest('.details').attr('data-recording-id') @@ -573,7 +579,7 @@ context.JK.SyncViewer = class SyncViewer cmd = { type: 'export_recording', - action: 'export' + action: 'export', queue: 'upload', recording_id: recordingId} @@ -581,9 +587,50 @@ context.JK.SyncViewer = class SyncViewer context.jamClient.OnTrySyncCommand(cmd) return false; + deleteRecording: (e) => + $delete = $(e.target) + if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession() + context.JK.ackBubble($delete, 'sync-viewer-paused', {}, {offsetParent: $delete.closest('.dialog')}) + return + + $details = $delete.closest('.details') + recordingId = $details.attr('data-recording-id') + + if !recordingId? or recordingId == "" + throw "deleteRecording can't find data-recording-id" + + context.JK.Banner.showYesNo({ + title: "Confirm Deletion", + html: "Are you sure you want to delete this recording?", + yes: => + @rest.deleteRecordingClaim(recordingId).done((response)-> + cmd = + { type: 'recording_directory', + action: 'delete', + queue: 'cleanup', + recording_id: recordingId} + + context.JK.ackBubble($delete, 'command-enqueued', {}, {offsetParent: $delete.closest('.dialog')}) + + logger.debug("enqueueing delete #{recordingId}") + + context.jamClient.OnTrySyncCommand(cmd) + ) + .fail(@app.ajaxError) + }) + + return false; + + displaySize: (size) => + # size is in bytes. divide by million, and round to one decimal place + megs = Math.round(size * 10 / (1024 * 1024) ) / 10 + "#{megs}M" createRecordingWrapper: ($toWrap, recordingInfo) => + totalSize = $($toWrap.get(0)).data('total-size') recordingInfo.recording_landing_url = "/recordings/#{recordingInfo.id}" + recordingInfo.totalSize = this.displaySize(totalSize) + recordingInfo.claimedRecordingId = recordingInfo.my?.id $wrapperDetails = $(context._.template(@templateRecordingWrapperDetails.html(), recordingInfo, {variable: 'data'})) $wrapper = $('
') $toWrap.wrapAll($wrapper) @@ -595,6 +642,7 @@ context.JK.SyncViewer = class SyncViewer $wrapper.append(this.createMix('fake', recordingInfo)) $wrapper.find('a.export').click(this.exportRecording) + $wrapper.find('a.delete').click(this.deleteRecording) separateByRecording: () => $recordedTracks = @list.find('.sync') @@ -747,6 +795,10 @@ context.JK.SyncViewer = class SyncViewer this.renderUploadRecordedTrack(commandId, commandMetadata) else this.renderGeneric(commandId, 'upload', commandMetadata) + else if commandMetadata.queue == 'cleanup' + @cleanupCommandId = commandId + @cleanupMetadata = commandMetadata + renderSingleRecording: (userSyncs) => return if userSyncs.entries.length == 0 @@ -813,6 +865,9 @@ context.JK.SyncViewer = class SyncViewer recordingId = @uploadMetadata['recording_id'] this.updateSingleRecording(recordingId) if recordingId? + else if commandId == @cleanupCommandId + this.logResult(@cleanupMetadata, success, reason, false) + else @logger.error("unknown commandId in renderFinishCommand") @@ -849,7 +904,8 @@ context.JK.SyncViewer = class SyncViewer $matchingStreamMix = @list.find(".stream-mix.sync[data-recording-id='#{recordingId}']") if $matchingStreamMix.length > 0 this.updateProgressOnSync($matchingStreamMix, 'upload', percentage) - + else if commandId == @cleanupCommandId + # ignore else @logger.error("unknown commandId in renderFinishCommand") @@ -860,7 +916,7 @@ context.JK.SyncViewer = class SyncViewer commandType = data['commandType'] commandMetadata = data['commandMetadata'] - category = commandType == 'download' ? 'download' : 'upload' + category = commandMetadata.queue if category == 'download' && (@downloadCommandId != null && @downloadCommandId != commandId) @logger.warn("received command-start for download but previous command did not send stop") @@ -876,17 +932,20 @@ context.JK.SyncViewer = class SyncViewer commandId = data['commandId'] if commandId == @downloadCommandId - category = 'download' this.renderFinishCommand(commandId, data) @downloadCommandId = null @downloadMetadata = null; else if commandId == @uploadCommandId - category = 'upload' this.renderFinishCommand(commandId, data) @uploadCommandId = null - @uploadMetadata = null; + @uploadMetadata = null + else if commandId == @cleanupCommandId + this.renderFinishCommand(commandId, data) + @cleanupCommandId = null + @cleanupMetadata = null + else - @logger.warn("received command-stop for unknown command: #{commandId} #{@downloadCommandId} #{@uploadCommandId}" ) + @logger.warn("received command-stop for unknown command: #{commandId} #{@downloadCommandId} #{@uploadCommandId} #{@cleanupCommandId}" ) fileManagerCmdProgress: (e, data) => #console.log("fileManagerCmdProgress", data) @@ -899,6 +958,8 @@ context.JK.SyncViewer = class SyncViewer else if commandId == @uploadCommandId category = 'upload' this.renderPercentage(commandId, category, data.percentage) + else if commandId == @cleanupCommandId + # do nothing else @logger.warn("received command-percentage for unknown command") @@ -918,6 +979,8 @@ context.JK.SyncViewer = class SyncViewer return 'CLEANUP TRACK' else if metadata.type == 'stream_mix' && metadata.action == 'upload' return 'UPLOADING STREAM MIX' + else if metadata.type == 'recording_directory' && metadata.action == 'delete' + return 'DELETE RECORDING' else return "#{metadata.action} #{metadata.type}".toUpperCase() @@ -938,6 +1001,7 @@ context.JK.SyncViewer = class SyncViewer when 'no-match-in-queue' then 'restart JamKazam' when 'already-done' then 'ignored, already done' when 'failed-convert' then 'failed previously' + when 'minimum-protection-time' then 'too soon to delete' else reason displaySuccess = if success then 'yes' else 'no' diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index b7b7e1b26..b7641dbbc 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -149,7 +149,7 @@ * @param data (optional) data for your template, if applicable * @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips */ - context.JK.confirmBubble = function($element, templateName, data, options) { + context.JK.ackBubble = function($element, templateName, data, options) { if(!options) options = {}; options.spikeGirth = 0; options.spikeLength = 0; diff --git a/web/app/assets/stylesheets/client/recordingManager.css.scss b/web/app/assets/stylesheets/client/recordingManager.css.scss index 729e19b1e..d51d2001f 100644 --- a/web/app/assets/stylesheets/client/recordingManager.css.scss +++ b/web/app/assets/stylesheets/client/recordingManager.css.scss @@ -8,6 +8,9 @@ left: 17%; width: 50%; + #recording-manager-delete { + display:none; + } // if it's the native client, then show the File Manager span. if it's not (normal browser) hide it. // even if it's the native client, once a command is running, hide File Manager &.native-client { diff --git a/web/app/assets/stylesheets/dialogs/editRecordingDialog.css.scss b/web/app/assets/stylesheets/dialogs/editRecordingDialog.css.scss index 5a38282a4..b4d55c549 100644 --- a/web/app/assets/stylesheets/dialogs/editRecordingDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/editRecordingDialog.css.scss @@ -73,6 +73,24 @@ margin-top:4px; } } + label[for="keep"] { + display: inline; + float:right; + line-height: 26px; + padding-right: 5px; + vertical-align: middle; + } + select[name="keep"] { + float:right; + } + div[purpose="keep"] { + clear:both; + float:right; + .icheckbox_minimal { + float:right; + margin-top:4px; + } + } diff --git a/web/app/assets/stylesheets/users/syncViewer.css.scss b/web/app/assets/stylesheets/users/syncViewer.css.scss index 5ed5c471f..dcdae64e4 100644 --- a/web/app/assets/stylesheets/users/syncViewer.css.scss +++ b/web/app/assets/stylesheets/users/syncViewer.css.scss @@ -194,13 +194,23 @@ .export { float:right; - margin-right:3px; + margin-right:10px; font-size:12px; } .timeago { float:right; font-size:12px; } + .totalsize { + float:right; + margin-right:10px; + font-size:12px; + } + .delete { + float:right; + margin-right:10px; + font-size:12px; + } } .log-list { diff --git a/web/app/controllers/api_claimed_recordings_controller.rb b/web/app/controllers/api_claimed_recordings_controller.rb index 725209843..60acb0eb4 100644 --- a/web/app/controllers/api_claimed_recordings_controller.rb +++ b/web/app/controllers/api_claimed_recordings_controller.rb @@ -30,7 +30,7 @@ class ApiClaimedRecordingsController < ApiController raise PermissionError, 'only owner of claimed_recording can update it' end @claimed_recording.discard(current_user) - render :json => {}, :status => 200 + respond_with @claimed_recording end def download diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb index ce6cf9d42..f08789905 100644 --- a/web/app/controllers/api_recordings_controller.rb +++ b/web/app/controllers/api_recordings_controller.rb @@ -1,7 +1,7 @@ class ApiRecordingsController < ApiController before_filter :api_signed_in_user, :except => [ :add_like ] - before_filter :look_up_recording, :only => [ :show, :stop, :claim, :discard, :keep ] + before_filter :look_up_recording, :only => [ :show, :stop, :claim, :discard, :keep, :delete_claim ] before_filter :parse_filename, :only => [ :download, :upload_next_part, :upload_sign, :upload_part_complete, :upload_complete ] before_filter :lookup_stream_mix, :only => [ :upload_next_part_stream_mix, :upload_sign_stream_mix, :upload_part_complete_stream_mix, :upload_complete_stream_mix ] @@ -94,6 +94,20 @@ class ApiRecordingsController < ApiController end end + def delete_claim + claim = @recording.claim_for_user(current_user) + + if claim + claim.discard(current_user) + if claim.errors.any? + response.status = :unprocessable_entity + respond_with claim + end + end + + respond_with @recording + end + def add_comment if params[:id].blank? render :json => { :message => "Recording ID is required" }, :status => 400 diff --git a/web/app/controllers/api_user_syncs_controller.rb b/web/app/controllers/api_user_syncs_controller.rb index 32fbbe395..d511b34af 100644 --- a/web/app/controllers/api_user_syncs_controller.rb +++ b/web/app/controllers/api_user_syncs_controller.rb @@ -38,4 +38,9 @@ class ApiUserSyncsController < ApiController @next = data[:next] render "api_user_syncs/index", :layout => nil end + + def deletables + data = UserSync.deletables({user_id:current_user.id, recording_ids: params[:recording_ids]}) + render json: {recording_ids: data}, status: 200 + end end diff --git a/web/app/controllers/recordings_controller.rb b/web/app/controllers/recordings_controller.rb index 2f92c64c0..96bf1e1a1 100644 --- a/web/app/controllers/recordings_controller.rb +++ b/web/app/controllers/recordings_controller.rb @@ -5,8 +5,8 @@ class RecordingsController < ApplicationController def show @claimed_recording = ClaimedRecording.find_by_id(params[:id]) if @claimed_recording.nil? - recording = Recording.find(params[:id]) - @claimed_recording = recording.candidate_claimed_recording + recording = Recording.find_by_id(params[:id]) + @claimed_recording = recording.candidate_claimed_recording if recording end render :layout => "web" end diff --git a/web/app/views/api_claimed_recordings/show.rabl b/web/app/views/api_claimed_recordings/show.rabl index 6be19dee3..dbc1c7fd9 100644 --- a/web/app/views/api_claimed_recordings/show.rabl +++ b/web/app/views/api_claimed_recordings/show.rabl @@ -4,7 +4,7 @@ object @claimed_recording -attributes :id, :name, :description, :is_public, :genre_id +attributes :id, :name, :description, :is_public, :genre_id, :discarded node :share_url do |claimed_recording| unless claimed_recording.share_token.nil? diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl index 06a6df897..31edd3497 100644 --- a/web/app/views/api_feeds/show.rabl +++ b/web/app/views/api_feeds/show.rabl @@ -78,7 +78,7 @@ glue :recording do 'recording' end - attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?, :mix_state + attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?, :mix_state, :when_will_be_discarded? node do |recording| { diff --git a/web/app/views/api_recordings/show.rabl b/web/app/views/api_recordings/show.rabl index b1e52f16c..1fe574026 100644 --- a/web/app/views/api_recordings/show.rabl +++ b/web/app/views/api_recordings/show.rabl @@ -1,6 +1,6 @@ object @recording -attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count +attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :when_will_be_discarded? node :mix do |recording| if recording.mix diff --git a/web/app/views/clients/_help.html.erb b/web/app/views/clients/_help.html.erb index f857c3f08..38409096c 100644 --- a/web/app/views/clients/_help.html.erb +++ b/web/app/views/clients/_help.html.erb @@ -129,7 +129,7 @@ @@ -139,5 +139,17 @@ + + + + diff --git a/web/app/views/clients/_recordingManager.html.erb b/web/app/views/clients/_recordingManager.html.erb index 8553d47b7..b95c2f38b 100644 --- a/web/app/views/clients/_recordingManager.html.erb +++ b/web/app/views/clients/_recordingManager.html.erb @@ -11,4 +11,7 @@ downloading0 + + delete0 + \ No newline at end of file diff --git a/web/app/views/clients/_sync_viewer_templates.html.slim b/web/app/views/clients/_sync_viewer_templates.html.slim index 464e4007d..4c08f9ab9 100644 --- a/web/app/views/clients/_sync_viewer_templates.html.slim +++ b/web/app/views/clients/_sync_viewer_templates.html.slim @@ -88,12 +88,16 @@ script type="text/template" id='template-sync-viewer-no-syncs' | You have no recordings. script type="text/template" id="template-sync-viewer-recording-wrapper-details" - .details data-recording-id="{{data.id}}" + .details data-recording-id="{{data.id}}" data-claimed-recording-id="{{data.claimedRecordingId}}" a.session-detail-page href="{{data.recording_landing_url}}" rel="external" span.name | {{data.my ? data.my.name : 'Unknown Name'}} span.timeago | {{$.timeago(data.created_at)}} + span.totalsize + | SIZE: {{data.totalSize}} + a.delete href="#" + | DELETE a.export href="#" | EXPORT diff --git a/web/app/views/dialogs/_edit_recording_dialog.html.slim b/web/app/views/dialogs/_edit_recording_dialog.html.slim index a34457f2d..1b505af8e 100644 --- a/web/app/views/dialogs/_edit_recording_dialog.html.slim +++ b/web/app/views/dialogs/_edit_recording_dialog.html.slim @@ -1,24 +1,26 @@ -.dialog.configure-tracks{ layout: 'dialog', 'layout-id' => 'edit-recording', id: 'edit-recording-dialog'} +#edit-recording-dialog.dialog.configure-tracks layout='dialog' layout-id='edit-recording' .content-head = image_tag "content/icon_add.png", {:width => 19, :height => 19, :class => 'content-icon' } - %h1 Edit Recording + h1 Edit Recording .dialog-inner - %form + form .field - %label{for: 'name'} Recording name: - %input{type: 'text', name: 'name'} + label for='name' Recording name: + input type='text' name='name' .field - %label{for: 'description'} Description: - %textarea{name: 'description', rows: '4'} + label for='description' Description: + textarea name='description' rows='4' .field.genre-selector - %label{for: 'genre'} Genre: - %select{name:'genre'} - .field{purpose: 'is_public'} - %input{type: 'checkbox', name: 'is_public'} - %label{for: 'is_public'} Public Recording + label for='genre' Genre: + select name='genre' + .field purpose='is_public' + input type='checkbox' name='is_public' + label for='is_public' Public Recording .buttons - %a.button-grey.cancel-btn {'layout-action' => 'cancel'} CANCEL - %a.button-orange.delete-btn DELETE - %a.button-orange.save-btn UPDATE - %br{clear: 'all'} \ No newline at end of file + .left + a.button-grey.cancel-btn layout-action='cancel' CANCEL + .right + a.button-orange.delete-btn DELETE + a.button-orange.save-btn UPDATE + br clear='all' \ No newline at end of file diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index b8e2bf74a..6df8f601b 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -18,7 +18,7 @@ <% end %>
-<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %> +<% if !@claimed_recording.recording.deleted && (@claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user)) %>
<% unless @claimed_recording.recording.band.blank? %>
@@ -97,7 +97,7 @@ <% end %>
-<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %> +<% if !@claimed_recording.recording.deleted && (@claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user)) %> <% if signed_in? %> <% unless @claimed_recording.recording.band.nil? %> <%= render :partial => "shared/landing_sidebar", :locals => {:user => @claimed_recording.recording.band, :recent_history => @claimed_recording.recording.band.recent_history} %> diff --git a/web/app/views/users/_feed_recording_ajax.html.haml b/web/app/views/users/_feed_recording_ajax.html.haml index 9e45f8575..76c40a723 100644 --- a/web/app/views/users/_feed_recording_ajax.html.haml +++ b/web/app/views/users/_feed_recording_ajax.html.haml @@ -7,7 +7,7 @@ / type and artist .left.ml20.w15 .title - %a{:href => "/recordings/{{data.candidate_claimed_recording.id}}", :rel => "external", :hoveraction => "recording", :'recording-id' => '{{data.candidate_claimed_recording.id}}'} RECORDING + %a.title-text{:href => "/recordings/{{data.candidate_claimed_recording.id}}", :rel => "external", :hoveraction => "recording", :'recording-id' => '{{data.candidate_claimed_recording.id}}'} RECORDING %a.edit-recording-dialog{href: "#"} (edit) .artist %a.artist{:hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :profileaction => "{{data.feed_item.helpers.artist_hoveraction}}", :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'} diff --git a/web/config/routes.rb b/web/config/routes.rb index 5c4bf42c7..cea8bdf29 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -320,6 +320,7 @@ SampleApp::Application.routes.draw do # downloads/uploads match '/users/:id/syncs' => 'api_user_syncs#index', :via => :get match '/users/:id/syncs/:user_sync_id' => 'api_user_syncs#show', :via => :get + match '/users/:id/syncs/deletables' => 'api_user_syncs#deletables', :via => :post # bands @@ -398,6 +399,7 @@ SampleApp::Application.routes.draw do match '/recordings/:id' => 'api_recordings#show', :via => :get, :as => 'api_recordings_detail' match '/recordings/:id/stop' => 'api_recordings#stop', :via => :post, :as => 'api_recordings_stop' match '/recordings/:id/claim' => 'api_recordings#claim', :via => :post, :as => 'api_recordings_claim' + match '/recordings/:id/claim' => 'api_recordings#delete_claim', :via => :delete, :as => 'api_recordings_delete_claim' match '/recordings/:id/comments' => 'api_recordings#add_comment', :via => :post, :as => 'api_recordings_add_comment' match '/recordings/:id/likes' => 'api_recordings#add_like', :via => :post, :as => 'api_recordings_add_like' match '/recordings/:id/discard' => 'api_recordings#discard', :via => :post, :as => 'api_recordings_discard' diff --git a/web/config/scheduler.yml b/web/config/scheduler.yml index 441fe269d..4997a834e 100644 --- a/web/config/scheduler.yml +++ b/web/config/scheduler.yml @@ -57,4 +57,9 @@ ActiveMusicSessionCleaner: ScoreHistorySweeper: cron: 0 * * * * class: "JamRuby::ScoreHistorySweeper" - description: "Creates 'ScoreHistory' tables from Scores" \ No newline at end of file + description: "Creates 'ScoreHistory' tables from Scores" + +RecordingsCleaner: + cron: 0 * * * * + class: "JamRuby::RecordingsCleaner" + description: "Cleans up recordings that no one wants after 7 days" \ No newline at end of file diff --git a/web/spec/controllers/api_user_syncs_controller_spec.rb b/web/spec/controllers/api_user_syncs_controller_spec.rb index 5e88703a3..fb7c0a423 100644 --- a/web/spec/controllers/api_user_syncs_controller_spec.rb +++ b/web/spec/controllers/api_user_syncs_controller_spec.rb @@ -25,6 +25,12 @@ describe ApiUserSyncsController do json[:entries].length.should == 0 end + it "deletables" do + post :deletables, { :format => 'json', :id => user1.id, recording_ids: ['1'] } + json = JSON.parse(response.body, :symbolize_names => true) + json[:recording_ids].should eq(['1']) + end + describe "one recording with two users" do let!(:recording1) { recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1) From 7dc80c10ce6ed841067e1d55520696d19a0468c5 Mon Sep 17 00:00:00 2001 From: Steven Miers Date: Thu, 6 Nov 2014 13:16:58 -0600 Subject: [PATCH 08/18] VRFS-1849: Check for google authorization using AJAX and show/hide the google login button / "signed in" label as appropriate: --- .../dialogs/recordingFinishedDialog.css.scss | 2 +- web/app/controllers/sessions_controller.rb | 4 +++ web/app/views/layouts/client.html.erb | 1 + web/app/views/shared/_google_login.html.slim | 31 ++++++++++++++++--- web/config/routes.rb | 1 + 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss index 3d6208565..b8f27aede 100644 --- a/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/recordingFinishedDialog.css.scss @@ -42,7 +42,7 @@ } .google_login_button { - + vertical-align: middle; } .signed_in_to_google { diff --git a/web/app/controllers/sessions_controller.rb b/web/app/controllers/sessions_controller.rb index 7a2bb8ca7..6c772cd4a 100644 --- a/web/app/controllers/sessions_controller.rb +++ b/web/app/controllers/sessions_controller.rb @@ -150,6 +150,10 @@ class SessionsController < ApplicationController render 'oauth_complete', :layout => "landing" end + def has_google_auth + render :json => {has_google_auth: (!!current_user && !!UserAuthorization.google_auth(current_user).first)} + end + def redirect_after_signin(default) redirect_to(params['redirect-to'].blank? ? default : params['redirect-to']) end diff --git a/web/app/views/layouts/client.html.erb b/web/app/views/layouts/client.html.erb index 2565a4ec7..2c4238803 100644 --- a/web/app/views/layouts/client.html.erb +++ b/web/app/views/layouts/client.html.erb @@ -30,6 +30,7 @@ <% else %> <%= render "layouts/social_meta" %> <% end %> + <%= yield(:extra_js) %> <%= yield %> diff --git a/web/app/views/shared/_google_login.html.slim b/web/app/views/shared/_google_login.html.slim index 3402eb8b3..a190c6366 100644 --- a/web/app/views/shared/_google_login.html.slim +++ b/web/app/views/shared/_google_login.html.slim @@ -1,5 +1,26 @@ --google_auth = (current_user.nil?) ? nil : JamRuby::UserAuthorization.google_auth(current_user).first --if google_auth - span.signed_in_to_google="(Signed in)" --else - input.google_login_button type='image' onclick='window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");' src="/assets/google_signin.png" height="30px" +-content_for :extra_js do + javascript: + // Check for google authorization using AJAX and show/hide the + // google login button / "signed in" label as appropriate: + $(window).on('focus', function() { + $.ajax({ + type: "GET", + dataType: "json", + url: "/auth/has_google_auth" + }).success(function(data) { + if(data.has_google_auth) { + $("input.google_login_button").addClass("hidden") + $("span.signed_in_to_google").removeClass("hidden") + if (window._oauth_win) { + window._oauth_win.close() + } + } else { + $("span.signed_in_to_google").addClass("hidden") + $("input.google_login_button").removeClass("hidden") + } + }) + }); + +-google_auth = (current_user.nil?) ? nil : !!JamRuby::UserAuthorization.google_auth(current_user).first +span.signed_in_to_google class=((!google_auth) ? "hidden" : "") ="(Signed in)" +input.google_login_button class=((google_auth) ? "hidden" : "") type='image' onclick='window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");' src="/assets/google_signin.png" height="30px" diff --git a/web/config/routes.rb b/web/config/routes.rb index e42189fc6..6c35fb87c 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -30,6 +30,7 @@ SampleApp::Application.routes.draw do # oauth match '/auth/:provider/callback', :to => 'sessions#oauth_callback' match '/auth/failure', :to => 'sessions#failure' + match '/auth/has_google_auth', :to => 'sessions#has_google_auth' # session info page match '/sessions/:id/details' => 'music_sessions#session_info', :via => :get, :as => 'music_scheduled_session_detail' From f9d23f77fa38919e224a49554a502b13edad4a35 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 6 Nov 2014 20:43:47 -0500 Subject: [PATCH 09/18] VRFS-2426 one more try at latency badge on hover bubble --- web/app/assets/javascripts/hoverMusician.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/javascripts/hoverMusician.js b/web/app/assets/javascripts/hoverMusician.js index 20b44cbfc..7cac6517e 100644 --- a/web/app/assets/javascripts/hoverMusician.js +++ b/web/app/assets/javascripts/hoverMusician.js @@ -76,7 +76,7 @@ if (response.internet_score && response.internet_score.length > 0) { if (response.internet_score[0].score && !isNaN(response.internet_score[0].score)) { var internetScore = parseInt(response.internet_score[0].score); - fullScore = (response.internet_score + calculateAudioLatency(response.my_audio_latency) + calculateAudioLatency(response.last_jam_audio_latency)) / 2; + fullScore = (internetScore + calculateAudioLatency(response.my_audio_latency) + calculateAudioLatency(response.last_jam_audio_latency)) / 2; } } From a7cabbe92441a7ddce13a13b3908456e7254e35e Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 6 Nov 2014 21:15:04 -0500 Subject: [PATCH 10/18] VRFS-2311 fix bug --- web/app/assets/javascripts/accounts_session_detail.js | 8 ++++++-- web/app/assets/javascripts/session_utils.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/web/app/assets/javascripts/accounts_session_detail.js b/web/app/assets/javascripts/accounts_session_detail.js index 53eb195f7..a3debe85e 100644 --- a/web/app/assets/javascripts/accounts_session_detail.js +++ b/web/app/assets/javascripts/accounts_session_detail.js @@ -92,7 +92,9 @@ .done(function(openSlots) { if (openSlots) { if (openSlots.length === 0) { - ui.launchRsvpCreateSlotDialog(sessionData.id, instrumentIds.split('|'), userName); + ui.launchRsvpCreateSlotDialog(sessionData.id, instrumentIds.split('|'), userName, function() { + approve(rsvpId, params); + }); } else { var arrInstrumentIds = instrumentIds.split('|'); @@ -122,7 +124,9 @@ } } else { - ui.launchRsvpCreateSlotDialog(sessionData.id, instrumentIds.split('|'), userName); + ui.launchRsvpCreateSlotDialog(sessionData.id, instrumentIds.split('|'), userName, function() { + approve(rsvpId, params); + }); } }); } diff --git a/web/app/assets/javascripts/session_utils.js b/web/app/assets/javascripts/session_utils.js index eea0d79a9..2fd34dd94 100644 --- a/web/app/assets/javascripts/session_utils.js +++ b/web/app/assets/javascripts/session_utils.js @@ -140,7 +140,7 @@ if (session) { if (session.can_join) { - logger.debug("Found invitation for user " + context.JK.currentUserId + ", session " + sessionId); + logger.debug("Found invitation or approved RSVP for user " + context.JK.currentUserId + ", session " + sessionId); openJoinSessionTerms(sessionId); } else { From 7071177f3ff85d33e800a61b0c1c69e342fac61d Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 7 Nov 2014 12:39:45 -0500 Subject: [PATCH 11/18] VRFS-2297 show instrument tooltips in session and recording hover bubbles --- .gitignore | 1 + web/app/assets/javascripts/hoverRecording.js | 2 +- web/app/assets/javascripts/hoverSession.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 19a4b038a..19f07d121 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ HTML .DS_Store coverage dump.rdb +working.png diff --git a/web/app/assets/javascripts/hoverRecording.js b/web/app/assets/javascripts/hoverRecording.js index d58e72be5..5321df114 100644 --- a/web/app/assets/javascripts/hoverRecording.js +++ b/web/app/assets/javascripts/hoverRecording.js @@ -58,7 +58,7 @@ instrumentHtml = '
'; $.each(val.instrument_ids, function(index, val) { - instrumentHtml += '  '; + instrumentHtml += '  '; }); instrumentHtml += '
'; diff --git a/web/app/assets/javascripts/hoverSession.js b/web/app/assets/javascripts/hoverSession.js index 5ba742219..d64dfee71 100644 --- a/web/app/assets/javascripts/hoverSession.js +++ b/web/app/assets/javascripts/hoverSession.js @@ -25,7 +25,7 @@ instrumentHtml = '
'; var instruments = val.instruments.split("|"); $.each(instruments, function(index, instrument) { - instrumentHtml += ' '; + instrumentHtml += ' '; }); instrumentHtml += '
'; From c6bfd59999a98c6062ec3b178f1ef6f3ddceb878 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 7 Nov 2014 13:05:07 -0500 Subject: [PATCH 12/18] VRFS-2462 fixed session count on musician hover bubble --- ruby/lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 5216063db..a9c278566 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -306,7 +306,7 @@ module JamRuby end def session_count - self.music_sessions.size + MusicSession.where("user_id = ? AND started_at IS NOT NULL", self.id).size end # count up any session you are RSVP'ed to From 8cad33a303e39a461fca4a6f9726d50d522c40da Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 7 Nov 2014 16:12:36 -0600 Subject: [PATCH 13/18] * recording file/manager fixes and aging of recordings --- db/up/deletable_recordings.sql | 2 + ruby/lib/jam_ruby/models/user_sync.rb | 10 +++- ruby/lib/jam_ruby/resque/quick_mixer.rb | 3 +- ruby/spec/jam_ruby/models/user_sync_spec.rb | 29 ++++++++--- web/app/assets/javascripts/findBand.js | 2 +- .../assets/javascripts/sync_viewer.js.coffee | 20 ++++++-- web/app/views/clients/_help.html.erb | 8 +++- .../api_user_syncs_controller_spec.rb | 48 +++++-------------- 8 files changed, 70 insertions(+), 52 deletions(-) diff --git a/db/up/deletable_recordings.sql b/db/up/deletable_recordings.sql index 648b45e19..933f30611 100644 --- a/db/up/deletable_recordings.sql +++ b/db/up/deletable_recordings.sql @@ -1,8 +1,10 @@ + -- this is to make sure we don't delete any recordings for 7 days UPDATE recordings SET updated_at = NOW(); ALTER TABLE recordings ADD COLUMN deleted BOOLEAN DEFAULT FALSE NOT NULL; + DROP VIEW user_syncs; CREATE VIEW user_syncs AS diff --git a/ruby/lib/jam_ruby/models/user_sync.rb b/ruby/lib/jam_ruby/models/user_sync.rb index 03e4b938a..a84dadb6c 100644 --- a/ruby/lib/jam_ruby/models/user_sync.rb +++ b/ruby/lib/jam_ruby/models/user_sync.rb @@ -21,7 +21,15 @@ module JamRuby raise 'no user id specified' if user_id.blank? - query = UserSync.includes(recorded_track: [{recording: [:owner, {claimed_recordings: [:share_token]}, {recorded_tracks: [:user]}, {comments:[:user]}, :likes, :plays, :mixes]}, user: [], instrument:[]], mix: [], quick_mix:[]).where(user_id: user_id).paginate(:page => page, :per_page => limit).order('created_at DESC, unified_id') + query = UserSync + .includes(recorded_track: [{recording: [:owner, {claimed_recordings: [:share_token]}, {recorded_tracks: [:user]}, {comments:[:user]}, :likes, :plays, :mixes]}, user: [], instrument:[]], mix: [], quick_mix:[]) + .joins("LEFT OUTER JOIN claimed_recordings ON claimed_recordings.user_id = user_syncs.user_id AND claimed_recordings.recording_id = user_syncs.recording_id") + .where(user_id: user_id) + .where(%Q{ + ((claimed_recordings IS NULL OR claimed_recordings.discarded = TRUE) AND fully_uploaded = FALSE) OR (claimed_recordings IS NOT NULL AND claimed_recordings.discarded = FALSE) + }) + .paginate(:page => page, :per_page => limit) + .order('created_at DESC, unified_id') # allow selection of single user_sync, by ID diff --git a/ruby/lib/jam_ruby/resque/quick_mixer.rb b/ruby/lib/jam_ruby/resque/quick_mixer.rb index 1c3793c8f..7216610e4 100644 --- a/ruby/lib/jam_ruby/resque/quick_mixer.rb +++ b/ruby/lib/jam_ruby/resque/quick_mixer.rb @@ -65,10 +65,9 @@ module JamRuby cleanup_files - @@log.info("audiomixer job successful. mix_id #{quick_mix_id}") + @@log.info("quickmixer job successful. mix_id #{quick_mix_id}") rescue Exception => e - puts "EEOUOU #{e}" post_error(@quick_mix, e) raise end diff --git a/ruby/spec/jam_ruby/models/user_sync_spec.rb b/ruby/spec/jam_ruby/models/user_sync_spec.rb index 83b9e4465..8699d0e12 100644 --- a/ruby/spec/jam_ruby/models/user_sync_spec.rb +++ b/ruby/spec/jam_ruby/models/user_sync_spec.rb @@ -41,6 +41,22 @@ describe UserSync do user_syncs[2].quick_mix.should eq(quick_mix) end + # https://jamkazam.atlassian.net/browse/VRFS-2450 + it "no longer returned after fully uploaded and unclaimed" do + mix = FactoryGirl.create(:mix) + mix.recording.duration = 1 + mix.recording.save! + quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false, fully_uploaded: true) + mix.recording.recorded_tracks[0].fully_uploaded = true + mix.recording.recorded_tracks[0].save! + mix.recording.claimed_recordings[0].discarded = true + mix.recording.claimed_recordings[0].save! + + data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id}) + user_syncs = data[:query] + user_syncs.length.should == 0 + end + it "two mixes, one not belonging to querier" do mix1 = FactoryGirl.create(:mix) mix2 = FactoryGirl.create(:mix) @@ -71,8 +87,8 @@ describe UserSync do describe "one recording with two users" do let!(:recording1) { recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false) recording.save! recording.reload recording @@ -177,10 +193,10 @@ describe UserSync do describe "one recording with multi-track users" do let!(:recording1) { recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2) - recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false) + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false) recording.save! recording.reload recording @@ -228,6 +244,7 @@ describe UserSync do recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1) recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner) recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2) + claimed_recording = FactoryGirl.create(:claimed_recording, user: recording.owner, recording: recording, discarded:false) recording.save! recording.reload recording diff --git a/web/app/assets/javascripts/findBand.js b/web/app/assets/javascripts/findBand.js index fd3f1358d..df498dca7 100644 --- a/web/app/assets/javascripts/findBand.js +++ b/web/app/assets/javascripts/findBand.js @@ -204,7 +204,7 @@ // @FIXME -- this will need to be tweaked when we allow unfollowing $('div[data-band-id='+newFollowing.band_id+'] .search-m-follow').removeClass('button-orange').addClass('button-grey'); }, - error: app.ajaxError + error: app.ajaxError(arguments) }); } diff --git a/web/app/assets/javascripts/sync_viewer.js.coffee b/web/app/assets/javascripts/sync_viewer.js.coffee index 98f43ec18..3215359e2 100644 --- a/web/app/assets/javascripts/sync_viewer.js.coffee +++ b/web/app/assets/javascripts/sync_viewer.js.coffee @@ -603,16 +603,26 @@ context.JK.SyncViewer = class SyncViewer title: "Confirm Deletion", html: "Are you sure you want to delete this recording?", yes: => - @rest.deleteRecordingClaim(recordingId).done((response)-> + @rest.deleteRecordingClaim(recordingId).done((response)=> cmd = { type: 'recording_directory', action: 'delete', queue: 'cleanup', recording_id: recordingId} - context.JK.ackBubble($delete, 'command-enqueued', {}, {offsetParent: $delete.closest('.dialog')}) - - logger.debug("enqueueing delete #{recordingId}") + # now check if the sync is gone entirely, allowing us to delete it from the UI + @rest.getUserSync({user_sync_id: recordingId}).done((userSync) => + # the user sync is still here. tell user it'll be done as soon as they've uploaded their files + context.JK.ackBubble($delete, 'file-sync-delayed-deletion', {}, {offsetParent: $delete.closest('.dialog')}) + ) + .fail((xhr) => + if xhr.status == 404 + # the userSync is gone; remove from file manager dynamically + $recordingHolder = $details.closest('.recording-holder') + $recordingHolder.slideUp() + else + @app.ajaxError(arguments) + ) context.jamClient.OnTrySyncCommand(cmd) ) @@ -622,7 +632,7 @@ context.JK.SyncViewer = class SyncViewer return false; displaySize: (size) => - # size is in bytes. divide by million, and round to one decimal place + # size is in bytes. divide by million, anxosd round to one decimal place megs = Math.round(size * 10 / (1024 * 1024) ) / 10 "#{megs}M" diff --git a/web/app/views/clients/_help.html.erb b/web/app/views/clients/_help.html.erb index 38409096c..66ac73135 100644 --- a/web/app/views/clients/_help.html.erb +++ b/web/app/views/clients/_help.html.erb @@ -148,8 +148,12 @@ - + \ No newline at end of file diff --git a/web/spec/controllers/api_user_syncs_controller_spec.rb b/web/spec/controllers/api_user_syncs_controller_spec.rb index fb7c0a423..a1b039f2f 100644 --- a/web/spec/controllers/api_user_syncs_controller_spec.rb +++ b/web/spec/controllers/api_user_syncs_controller_spec.rb @@ -46,23 +46,12 @@ describe ApiUserSyncsController do } it "no claimed_recordings" do - # every is supposed to upload immediately, but no downloads until you try claim it. The assertions below validate this + + # if there are no claims, then no one wants the recording. no usersyncs get :index, { :format => 'json', :id => user1.id } json = JSON.parse(response.body, :symbolize_names => true) json[:next].should be_nil - json[:entries].length.should == 2 - - recorded_track1 = json[:entries][0] - recorded_track1[:upload][:should_upload].should be_true - recorded_track1[:upload][:too_many_upload_failures].should be_false - recorded_track1[:download][:should_download].should be_false - recorded_track1[:download][:too_many_downloads].should be_false - - recorded_track2 = json[:entries][1] - recorded_track2[:upload][:should_upload].should be_true - recorded_track2[:upload][:too_many_upload_failures].should be_false - recorded_track2[:download][:should_download].should be_false - recorded_track2[:download][:too_many_downloads].should be_false + json[:entries].length.should == 0 end it "recording isn't over" do @@ -79,6 +68,9 @@ describe ApiUserSyncsController do FactoryGirl.create(:claimed_recording, user: user1, recording: recording1, discarded:false) + recording1.recorded_tracks[0].fully_uploaded = false + recording1.recorded_tracks[0].save! + get :index, { :format => 'json', :id => user1.id } json = JSON.parse(response.body, :symbolize_names => true) json[:next].should be_nil @@ -100,7 +92,7 @@ describe ApiUserSyncsController do get :index, { :format => 'json', :id => user2.id } json = JSON.parse(response.body, :symbolize_names => true) json[:next].should be_nil - json[:entries].length.should == 2 + json[:entries].length.should == 1 recorded_track1 = json[:entries][0] recorded_track1[:upload][:should_upload].should be_true @@ -108,32 +100,17 @@ describe ApiUserSyncsController do recorded_track1[:download][:should_download].should be_false recorded_track1[:download][:too_many_downloads].should be_false - recorded_track2 = json[:entries][1] - recorded_track2[:upload][:should_upload].should be_true - recorded_track2[:upload][:too_many_upload_failures].should be_false - recorded_track2[:download][:should_download].should be_false - recorded_track2[:download][:too_many_downloads].should be_false end it "one user decides to discard the recording" do FactoryGirl.create(:claimed_recording, user: user1, recording: recording1, discarded:true) + FactoryGirl.create(:claimed_recording, user: user2, recording: recording1, discarded:false) + # it's a length of zero because recorded_tracks default to 'fully_uploaded = true'. so nothing to do for this user get :index, { :format => 'json', :id => user1.id } json = JSON.parse(response.body, :symbolize_names => true) json[:next].should be_nil - json[:entries].length.should == 2 - - recorded_track1 = json[:entries][0] - recorded_track1[:upload][:should_upload].should be_true - recorded_track1[:upload][:too_many_upload_failures].should be_false - recorded_track1[:download][:should_download].should be_false - recorded_track1[:download][:too_many_downloads].should be_false - - recorded_track2 = json[:entries][1] - recorded_track2[:upload][:should_upload].should be_true - recorded_track2[:upload][:too_many_upload_failures].should be_false - recorded_track2[:download][:should_download].should be_false - recorded_track2[:download][:too_many_downloads].should be_false + json[:entries].length.should == 0 controller.current_user = user2 get :index, { :format => 'json', :id => user2.id } @@ -141,16 +118,17 @@ describe ApiUserSyncsController do json[:next].should be_nil json[:entries].length.should == 2 + recorded_track1 = json[:entries][0] recorded_track1[:upload][:should_upload].should be_true recorded_track1[:upload][:too_many_upload_failures].should be_false - recorded_track1[:download][:should_download].should be_false + recorded_track1[:download][:should_download].should be_true recorded_track1[:download][:too_many_downloads].should be_false recorded_track2 = json[:entries][1] recorded_track2[:upload][:should_upload].should be_true recorded_track2[:upload][:too_many_upload_failures].should be_false - recorded_track2[:download][:should_download].should be_false + recorded_track2[:download][:should_download].should be_true recorded_track2[:download][:too_many_downloads].should be_false end From 9c30becc11ee013333e4ec18aaeceacec3f9b7fb Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 8 Nov 2014 21:32:04 -0500 Subject: [PATCH 14/18] VRFS-1712 fix issue with wrapping buttons on musician hover bubble --- web/app/assets/stylesheets/client/hoverBubble.css.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/stylesheets/client/hoverBubble.css.scss b/web/app/assets/stylesheets/client/hoverBubble.css.scss index b8d6ec315..fda3967c9 100644 --- a/web/app/assets/stylesheets/client/hoverBubble.css.scss +++ b/web/app/assets/stylesheets/client/hoverBubble.css.scss @@ -10,7 +10,7 @@ &.musician-bubble { - width:425px; + width:438px; } h2 { From 948eaecc23fe87d519feec2b9dd65a5507b0d045 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 9 Nov 2014 09:50:28 -0600 Subject: [PATCH 15/18] * VRFS-2468 - fix record others count --- web/app/helpers/recording_helper.rb | 17 ++++++++++++----- web/spec/helpers/recording_helper_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/web/app/helpers/recording_helper.rb b/web/app/helpers/recording_helper.rb index cbad1805c..c089bf1f3 100644 --- a/web/app/helpers/recording_helper.rb +++ b/web/app/helpers/recording_helper.rb @@ -23,22 +23,29 @@ module RecordingHelper if claimed_recording.recording.band "RECORDING: #{claimed_recording.recording.band.name}" else - unique_users = claimed_recording.recording.users + unique_users = claimed_recording.recording.users.uniq if sharer && unique_users.exists?(sharer) - "RECORDING: #{sharer.name}#{additional_member_count(unique_users)}" + "RECORDING: #{sharer.name}#{additional_member_count(unique_users, sharer)}" else - "RECORDING: #{claimed_recording.user.name}#{additional_member_count(unique_users)}" + "RECORDING: #{claimed_recording.user.name}#{additional_member_count(unique_users, claimed_recording.user)}" end end end - def additional_member_count(unique_users) + def additional_member_count(unique_users, target_user) length = unique_users.length if length < 2 "" else - " & #{length} OTHERS" + other_length = length - 1 + if other_length == 1 + other_user_in_array = unique_users - [target_user] + other_user = other_user_in_array[0] + " & #{other_user.name}" + else + " & #{length - 1} OTHERS" + end end end diff --git a/web/spec/helpers/recording_helper_spec.rb b/web/spec/helpers/recording_helper_spec.rb index 2b7603102..d102a72dd 100644 --- a/web/spec/helpers/recording_helper_spec.rb +++ b/web/spec/helpers/recording_helper_spec.rb @@ -79,6 +79,29 @@ describe MusicSessionHelper do result.start_with?("RECORDING").should be_true result.end_with?(@claimed_recording.user.name).should be_true end + + # regression: https://jamkazam.atlassian.net/browse/VRFS-2468 + it "correct number of others when multiple tracks for 2 users" do + second_user = FactoryGirl.create(:user) + track1 = FactoryGirl.create(:recorded_track, user: second_user, recording: @recording) + track2 = FactoryGirl.create(:recorded_track, user: second_user, recording: @recording) + result = helper.title_for_claimed_recording(@claimed_recording) + result.include?("& #{second_user.name}").should be_true + + end + + # regression: https://jamkazam.atlassian.net/browse/VRFS-2468 + it "correct number of others when multiple tracks for 3 users" do + second_user = FactoryGirl.create(:user) + track1 = FactoryGirl.create(:recorded_track, user: second_user, recording: @recording) + track2 = FactoryGirl.create(:recorded_track, user: second_user, recording: @recording) + third_user = FactoryGirl.create(:user) + track1 = FactoryGirl.create(:recorded_track, user: third_user, recording: @recording) + track2 = FactoryGirl.create(:recorded_track, user: third_user, recording: @recording) + result = helper.title_for_claimed_recording(@claimed_recording) + result.include?("& 2 OTHERS").should be_true + end + end describe "additional_member_count" do From 8a5a40db9cd860d8976bf342c01e2ab905a5b248 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 9 Nov 2014 10:52:25 -0600 Subject: [PATCH 16/18] * fix check for existence in array --- web/app/helpers/recording_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/helpers/recording_helper.rb b/web/app/helpers/recording_helper.rb index c089bf1f3..3d8a6eeec 100644 --- a/web/app/helpers/recording_helper.rb +++ b/web/app/helpers/recording_helper.rb @@ -24,7 +24,7 @@ module RecordingHelper "RECORDING: #{claimed_recording.recording.band.name}" else unique_users = claimed_recording.recording.users.uniq - if sharer && unique_users.exists?(sharer) + if sharer && unique_users.include?(sharer) "RECORDING: #{sharer.name}#{additional_member_count(unique_users, sharer)}" else "RECORDING: #{claimed_recording.user.name}#{additional_member_count(unique_users, claimed_recording.user)}" From f0b70b1bb7d5f824e3ac8c2a0d540e739d324bb5 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 9 Nov 2014 10:59:31 -0600 Subject: [PATCH 17/18] * fix issues with music session 'other count' and tests --- web/app/helpers/music_session_helper.rb | 15 +++++++++++---- web/spec/helpers/music_session_helper_spec.rb | 11 +++++++++-- web/spec/helpers/recording_helper_spec.rb | 11 +++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/web/app/helpers/music_session_helper.rb b/web/app/helpers/music_session_helper.rb index b550e70f3..03001dc0d 100644 --- a/web/app/helpers/music_session_helper.rb +++ b/web/app/helpers/music_session_helper.rb @@ -25,20 +25,27 @@ module MusicSessionHelper else unique_users = music_session.unique_users if sharer && unique_users.exists?(sharer) - "LIVE SESSION: #{sharer.name}#{additional_member_count(unique_users)}" + "LIVE SESSION: #{sharer.name}#{additional_member_count(unique_users, sharer)}" else - "LIVE SESSION: #{music_session.creator.name}#{additional_member_count(unique_users)}" + "LIVE SESSION: #{music_session.creator.name}#{additional_member_count(unique_users, music_session.creator)}" end end end - def additional_member_count(unique_users) + def additional_member_count(unique_users, target_user) length = unique_users.length if length < 2 "" else - " & #{length} OTHERS" + other_length = length - 1 + if other_length == 1 + other_user_in_array = unique_users - [target_user] + other_user = other_user_in_array[0] + " & #{other_user.name}" + else + " & #{length - 1} OTHERS" + end end end diff --git a/web/spec/helpers/music_session_helper_spec.rb b/web/spec/helpers/music_session_helper_spec.rb index b84b09e39..1a01a6426 100644 --- a/web/spec/helpers/music_session_helper_spec.rb +++ b/web/spec/helpers/music_session_helper_spec.rb @@ -60,11 +60,18 @@ describe MusicSessionHelper do describe "additional_member_count" do it "no unique users" do - helper.additional_member_count([]).should == "" + helper.additional_member_count([], @user).should == "" end it "has 2 users" do - helper.additional_member_count(['', '']).should == " & 2 OTHERS" + user2 = FactoryGirl.create(:user) + helper.additional_member_count([@user, user2], @user).should == " & #{user2.name}" + end + + it "has 3 users" do + user2 = FactoryGirl.create(:user) + user3 = FactoryGirl.create(:user) + helper.additional_member_count([@user, user2, user3], @user).should == " & 2 OTHERS" end end end diff --git a/web/spec/helpers/recording_helper_spec.rb b/web/spec/helpers/recording_helper_spec.rb index d102a72dd..efb4608ec 100644 --- a/web/spec/helpers/recording_helper_spec.rb +++ b/web/spec/helpers/recording_helper_spec.rb @@ -106,11 +106,18 @@ describe MusicSessionHelper do describe "additional_member_count" do it "no unique users" do - helper.additional_member_count([]).should == "" + helper.additional_member_count([], @user).should == "" end it "has 2 users" do - helper.additional_member_count(['', '']).should == " & 2 OTHERS" + user2 = FactoryGirl.create(:user) + helper.additional_member_count([@user, user2], @user).should == " & #{user2.name}" + end + + it "has 3 users" do + user2 = FactoryGirl.create(:user) + user3 = FactoryGirl.create(:user) + helper.additional_member_count([@user, user2, user3], @user).should == " & 2 OTHERS" end end end From 2c3fab9b3459a937bd2b998bb8abdcbc31ac013b Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 10 Nov 2014 14:59:30 +0000 Subject: [PATCH 18/18] VRFS-2362 replaced class selector with id selector --- web/app/assets/javascripts/scheduled_session.js.erb | 2 +- web/app/views/clients/_scheduledSession.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb index e113e4b86..1fc4d1156 100644 --- a/web/app/assets/javascripts/scheduled_session.js.erb +++ b/web/app/assets/javascripts/scheduled_session.js.erb @@ -1237,7 +1237,7 @@ $languageList = $screen.find('#session-language-list'); $sessionPlusMusiciansLabel = $screen.find('label[for="session-plus-musicians"]'); $editScheduledSessions = $screen.find('#edit_scheduled_sessions'); - $btnSelectFiles = $screen.find('.btn-select-files'); + $btnSelectFiles = $screen.find('#session-notation-file-selection'); $selectedFilenames = $screen.find('#selected-filenames'); $uploadSpinner = $screen.find('#file-upload-spinner'); $policyTypes = $screen.find('input[name="session-policy-type"]'); diff --git a/web/app/views/clients/_scheduledSession.html.erb b/web/app/views/clients/_scheduledSession.html.erb index 349ecc77f..89a98df01 100644 --- a/web/app/views/clients/_scheduledSession.html.erb +++ b/web/app/views/clients/_scheduledSession.html.erb @@ -204,7 +204,7 @@