diff --git a/admin/app/admin/teachers.rb b/admin/app/admin/teachers.rb index ef4d10fbe..c79cd5bfb 100644 --- a/admin/app/admin/teachers.rb +++ b/admin/app/admin/teachers.rb @@ -18,8 +18,9 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do end filter :teacher_full_name_or_user_email_cont, label: 'Name', as: :string + filter :user_phantom, label: 'Phantom', as: :boolean, selected: 'false' - #filter :by_search_user_name, label: "Name", as: :string + #filter :by_search_user_name, label: "Name", as: :string index do column "Name" do |teacher| diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb index b67a619ab..1a258e30b 100644 --- a/ruby/lib/jam_ruby/models/chat_message.rb +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -3,6 +3,7 @@ module JamRuby include HtmlSanitize html_sanitize strict: [:message] + CHANNEL_SESSION = 'session' CHANNEL_LESSON = 'lesson' self.table_name = 'chat_messages' self.primary_key = 'id' @@ -40,6 +41,9 @@ module JamRuby chat_msg.music_notation = music_notation chat_msg.claimed_recording = recording + if purpose == 'Notation File' || purpose == 'Audio File' || purpose == 'JamKazam Recording' + chat_msg.ignore_message_checks = true + end if lesson_session chat_msg.ignore_message_checks = true @@ -65,7 +69,10 @@ module JamRuby if chat_msg.save ChatMessage.send_chat_msg music_session, chat_msg, source_user, client_id, channel, lesson_session, purpose, target_user, music_notation, recording + else + puts "Chat Message Save Error #{chat_msg.errors.inspect}" end + chat_msg end @@ -106,6 +113,7 @@ module JamRuby end def send_chat_msg(music_session, chat_msg, user, client_id, channel, lesson_session, purpose, target_user, music_notation, claimed_recording) + music_session_id = music_session.id if music_session lesson_session_id = lesson_session.id if lesson_session diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 6ef4783f4..fb7723942 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -2142,6 +2142,16 @@ }) } + function attachRecordingToSession(data) { + return $.ajax({ + type: "POST", + url: '/api/sessions/' + data.id + '/attach_recording', + dataType: "json", + contentType: 'application/json', + data: JSON.stringify(data) + }) + } + function bookLesson(data) { return $.ajax({ @@ -2998,6 +3008,7 @@ this.portOverCarts = portOverCarts; this.bookLesson = bookLesson; this.attachRecordingToLesson = attachRecordingToLesson; + this.attachRecordingToSession = attachRecordingToSession; this.getTestDrivePackageChoice = getTestDrivePackageChoice; this.getLessonBooking = getLessonBooking; this.getUnprocessedLesson = getUnprocessedLesson; diff --git a/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee index 27b77eec6..a3dd09ffa 100644 --- a/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee @@ -10,15 +10,21 @@ context = window parsed = @parseId(args.d1) - window.ChatActions.initializeLesson.trigger(parsed.id) + if parsed.type == 'session' + window.ChatActions.activateChannel('session') + @setState({id: parsed.id, type: parsed.type, lesson_session: null}) - @setState({id: parsed.id, type: parsed.type, lesson_session: null}) + else if parsed.type == 'lesson' + window.ChatActions.initializeLesson.trigger(parsed.id) - if parsed.type == "lesson" - rest.getLesson({id: parsed.id}).done((response) => @getLessonDone(response)).fail((jqXHR) => @getLessonFail(jqXHR)) + @setState({id: parsed.id, type: parsed.type, lesson_session: null}) + + if parsed.type == "lesson" + rest.getLesson({id: parsed.id}).done((response) => @getLessonDone(response)).fail((jqXHR) => @getLessonFail(jqXHR)) else context.JK.Banner.showAlert('unknown chat type', "chat type was #{parsed.type}") + getLessonDone: (lesson_session) -> @postProcessLesson(lesson_session) @setState({lesson_session: lesson_session}) @@ -27,9 +33,10 @@ context = window @app.ajaxError(jqXHR) afterHide: () -> - if !@session?.isLesson + if !@session window.ChatActions.activateChannel('global') + parseId:(id) -> if !id? {id: null, type: null} @@ -67,6 +74,10 @@ context = window title = "#{this.state.lesson_session.music_session.scheduled_start_date} lesson chat" lessonSessionId = this.state.lesson_session.id other = this.state.lesson_session.other + else + title = 'Session Chat' + other = null + lessonSessionId = null `
diff --git a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee index 901be0624..1dbf0a639 100644 --- a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee @@ -1,6 +1,7 @@ context = window rest = new context.JK.Rest() ChatActions = @ChatActions +SessionStore = @SessionStore @ChatWindow = React.createClass({ @@ -257,11 +258,11 @@ ChatActions = @ChatActions lessonId = data.options.id if data.lessonAction == 'attach-recording' - window.AttachmentActions.startAttachRecording(lessonId) + window.AttachmentActions.startAttachRecording.trigger(lessonId, SessionStore.id()) else if data.lessonAction == 'attach-notation' - window.AttachmentActions.startAttachNotation(lessonId) + window.AttachmentActions.startAttachNotation.trigger(lessonId, SessionStore.id()) else if data.lessonAction == 'attach-audio' - window.AttachmentActions.startAttachAudio(lessonId) + window.AttachmentActions.startAttachAudio.trigger(lessonId, SessionStore.id()) pasteIntoInput: (el, text) -> el.focus(); diff --git a/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee index 331ace894..f556a4384 100644 --- a/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/JamClassScreen.js.jsx.coffee @@ -95,11 +95,11 @@ LessonTimerActions = context.LessonTimerActions else if data.lessonAction == 'reschedule' @rescheduleLesson(lesson) else if data.lessonAction == 'attach-recording' - window.AttachmentActions.startAttachRecording(lesson.id) + window.AttachmentActions.startAttachRecording.trigger(lesson.id) else if data.lessonAction == 'attach-notation' - window.AttachmentActions.startAttachNotation(lesson.id) + window.AttachmentActions.startAttachNotation.trigger(lesson.id) else if data.lessonAction == 'attach-audio' - window.AttachmentActions.startAttachAudio(lesson.id) + window.AttachmentActions.startAttachAudio.trigger(lesson.id) else if data.lessonAction == 'start-5-min' rest.lessonStartTime({id: lessonId, minutes: 5}).done((response) => (@app.layout.notify({ title: 'Start Time Set', diff --git a/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee index bc824b703..0988cd3ec 100644 --- a/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee @@ -7,11 +7,14 @@ RecordingActions = @RecordingActions getInitialState: () -> {} - openLessonChat: () -> - this.props.app.layout.showDialog('chat-dialog', {d1: 'lesson_' + this.props.lessonId}) + openChat: () -> + if this.props.isLesson + this.props.app.layout.showDialog('chat-dialog', {d1: 'lesson_' + this.props.lessonId}) + else + this.props.app.layout.showDialog('chat-dialog', {d1: 'session_' + this.props.sessionId}) render: () -> - ` + ` FILES ` diff --git a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee index c029f7428..bf935b202 100644 --- a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee @@ -20,8 +20,7 @@ SessionActions = @SessionActions if gon.global.video_available != 'none' videoBtn = `` - if this.state.isLesson - filesBtn = `` + filesBtn = `` `
@@ -80,7 +79,7 @@ SessionActions = @SessionActions return { freezeInteraction: true }; onSessionStoreChanged: (session) -> - this.setState({isLesson: session.isLesson, lessonId: session.lessonId}) + this.setState({isLesson: session.isLesson, lessonId: session.lessonId, sessionId: session.id}) onAllowLeaveSession: () -> @allowLeave = true diff --git a/web/app/assets/javascripts/react-components/stores/AttachmentStore.js.coffee b/web/app/assets/javascripts/react-components/stores/AttachmentStore.js.coffee index 86c103c53..87d041b01 100644 --- a/web/app/assets/javascripts/react-components/stores/AttachmentStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/AttachmentStore.js.coffee @@ -23,42 +23,54 @@ AttachmentActions = @AttachmentActions recordingsSelected: (recordings) -> logger.debug("recording selected", recordings) - options = {id: @lessonId} - options.recordings = recordings - rest.attachRecordingToLesson(options).done((response) => @attachedRecordingsToLesson(response)).fail((jqXHR) => @attachedRecordingsFail(jqXHR)) + if @lessonId + options = {id: @lessonId} + options.recordings = recordings + rest.attachRecordingToLesson(options).done((response) => @attachedRecordingsToLesson(response)).fail((jqXHR) => @attachedRecordingsFail(jqXHR)) + else if @sessionId + options = {id: @sessionId} + options.recordings = recordings + rest.attachRecordingToSession(options).done((response) => @attachedRecordingsToSession(response)).fail((jqXHR) => @attachedRecordingsFail(jqXHR)) attachedRecordingsToLesson: (response) -> context.JK.Banner.showNotice('Recording Attached', 'Your recording has been associated with this lesson, and can be accessed from the Messages window for this lesson.') + attachedRecordingsToSession: (response) -> + context.JK.Banner.showNotice('Recording Attached', 'Your recording has been associated with this session.') + attachedRecordingsFail: (jqXHR) -> @app.ajaxError(jqXHR) - onStartAttachRecording: (lessonId) -> + onStartAttachRecording: (lessonId, sessionId = null) -> if @uploading logger.warn("rejecting startAttachRecording attempt as currently busy") return @lessonId = lessonId + @sessionId = sessionId @ui.launchRecordingSelectorDialog([], (recordings) => @recordingsSelected(recordings) ) @changed() - onStartAttachNotation: (lessonId) -> + onStartAttachNotation: (lessonId, sessionId = null) -> if @uploading logger.warn("rejecting onStartAttachNotation attempt as currently busy") return @lessonId = lessonId + @sessionId = sessionId + logger.debug("notation upload started for lesson: " + lessonId) @triggerNotation() @changed() - onStartAttachAudio: (lessonId) -> + onStartAttachAudio: (lessonId, sessionId = null) -> if @uploading logger.warn("rejecting onStartAttachAudio attempt as currently busy") return @lessonId = lessonId + @sessionId = sessionId logger.debug("audio upload started for lesson: " + lessonId) @triggerAudio() @@ -104,7 +116,11 @@ AttachmentActions = @AttachmentActions return - formData.append('lesson_session_id', @lessonId); + if @lessonId + formData.append('lesson_session_id', @lessonId) + else if @sessionId + formData.append('session_id', @sessionId) + formData.append('attachment_type', 'notation') @app.layout.showDialog('music-notation-upload-dialog') @@ -168,7 +184,11 @@ AttachmentActions = @AttachmentActions return - formData.append('lesson_session_id', @lessonId); + if @lessonId + formData.append('lesson_session_id', @lessonId) + else if @sessionId + formData.append('session_id', @sessionId) + formData.append('attachment_type', 'audio') @app.layout.showDialog('music-notation-upload-dialog') diff --git a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee index bf6bbb719..95c6fa0de 100644 --- a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee @@ -158,8 +158,13 @@ SessionStore = context.SessionStore effectiveChannel = msg.channel - if msg.channel == 'lesson' - effectiveChannel = msg.lesson_session_id + if msg.channel == 'lesson' || msg.channel == 'session' + + if msg.channel == 'session' + 'session' + else + effectiveChannel = msg.lesson_session_id + if msg.attachment_type? console.log("attachment type seen") @@ -199,7 +204,8 @@ SessionStore = context.SessionStore @channel = channel if @channel != 'lesson' @channelType = null - @fetchHistory() + if @channel == 'lesson' + @fetchHistory() @changed() onSendMsg: (msg, done, fail, target_user = null, channel = null) -> diff --git a/web/app/assets/stylesheets/client/lessonSessionActions.scss b/web/app/assets/stylesheets/client/lessonSessionActions.scss index e5e4c40d9..f6d1c14be 100644 --- a/web/app/assets/stylesheets/client/lessonSessionActions.scss +++ b/web/app/assets/stylesheets/client/lessonSessionActions.scss @@ -8,6 +8,13 @@ li { border-bottom:0 !important; } + input[type="file"] { + background: none; + border: none; + color: blue; + text-decoration: underline; + cursor: pointer; + } &.not-card-ok .bt-content{ height:20px !important; width:90px !important; diff --git a/web/app/assets/stylesheets/client/react-components/JamClassScreen.scss b/web/app/assets/stylesheets/client/react-components/JamClassScreen.scss index 95e570ee9..0727d3cf1 100644 --- a/web/app/assets/stylesheets/client/react-components/JamClassScreen.scss +++ b/web/app/assets/stylesheets/client/react-components/JamClassScreen.scss @@ -167,6 +167,7 @@ text-decoration: none !important; color:#fc0; + .arrow-down { float:none; margin-left:5px; @@ -223,4 +224,17 @@ display: block; text-align: center; } + .attachment-fields { + input { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + filter: alpha(opacity=0); + opacity: 0; + cursor: inherit; + display: block; + } + } } \ No newline at end of file diff --git a/web/app/controllers/api_music_notations_controller.rb b/web/app/controllers/api_music_notations_controller.rb index fd5b05fad..7ebd12932 100644 --- a/web/app/controllers/api_music_notations_controller.rb +++ b/web/app/controllers/api_music_notations_controller.rb @@ -11,16 +11,17 @@ class ApiMusicNotationsController < ApiController lesson_session = LessonSession.find_by_id(params[:lesson_session_id]) if lesson_session - session_id = lesson_session.music_session.id + session = lesson_session.music_session else - session_id = params[:session_id] + session = MusicSession.find(params[:session_id]) end params[:files].each do |file| - music_notation = MusicNotation.create(session_id, params[:attachment_type], file, current_user) + music_notation = MusicNotation.create(session.id, params[:attachment_type], file, current_user) @music_notations.push music_notation + if lesson_session if lesson_session.student.id == current_user.id me = lesson_session.student @@ -29,22 +30,32 @@ class ApiMusicNotationsController < ApiController me = lesson_session.teacher other = lesson_session.student end - - if !music_notation.errors.any? - # if no error and it's a lesson, then make a chat about it - - if params[:attachment_type] == MusicNotation::TYPE_NOTATION - purpose = "Notation File" - else - purpose = "Audio File" - end - - msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, lesson_session, purpose, music_notation) - - UserMailer.lesson_attachment(me, other, lesson_session, music_notation) - end + else + me = current_user end + + if !music_notation.errors.any? + # if no error and it's a lesson, then make a chat about it + + if params[:attachment_type] == MusicNotation::TYPE_NOTATION + purpose = "Notation File" + else + purpose = "Audio File" + end + + if lesson_session + msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, lesson_session, purpose, music_notation) + UserMailer.lesson_attachment(me, other, lesson_session, music_notation) + else + if session.active_music_session + msg = ChatMessage.create(me, session.active_music_session, '', ChatMessage::CHANNEL_SESSION, nil, nil, nil, purpose, music_notation) + end + end + + end + + end if params[:files] respond_with @music_notations, responder: ApiResponder, :status => 201 @@ -54,7 +65,7 @@ class ApiMusicNotationsController < ApiController @music_notation = MusicNotation.find(params[:id]) unless @music_notation.music_session.can_join?(current_user, true) - render :text => "Permission denied", status:403 + render :text => "Permission denied", status: 403 return end @@ -69,7 +80,7 @@ class ApiMusicNotationsController < ApiController @music_notation = MusicNotation.find(params[:id]) unless @music_notation.music_session.can_join?(current_user, true) - render :text => "Permission denied", status:403 + render :text => "Permission denied", status: 403 return end diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index a4d255149..37a282119 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -4,7 +4,7 @@ class ApiMusicSessionsController < ApiController # have to be signed in currently to see this screen before_filter :api_signed_in_user, :except => [ :add_like, :show, :show_history, :add_session_info_comment ] - before_filter :lookup_session, only: [:show, :update, :delete, :claimed_recording_start, :claimed_recording_stop, :track_sync, :jam_track_open, :jam_track_close, :backing_track_open, :backing_track_close, :metronome_open, :metronome_close] + before_filter :lookup_session, only: [:show, :update, :delete, :claimed_recording_start, :claimed_recording_stop, :track_sync, :jam_track_open, :jam_track_close, :backing_track_open, :backing_track_close, :metronome_open, :metronome_close, :attach_recording] before_filter :lookup_perm_session, only: [:get_livestream, :create_livestream, :livestream_transition] skip_before_filter :api_signed_in_user, only: [:perf_upload] around_filter :transactions_filter, only:[:sms_index, :ams_index] @@ -196,6 +196,25 @@ class ApiMusicSessionsController < ApiController end end + def attach_recording + + me = current_user + + recordings = params[:recordings] + recordings.each do |recording_data| + #recording = Recording.find(recording_data[:id]) + claimed_recording = ClaimedRecording.find_by_id(recording_data[:id]) + if claimed_recording.nil? || claimed_recording.user != current_user + raise JamPermissionError, 'only owner of claimed_recording can associated it with a lesson' + end + + msg = ChatMessage.create(me, @music_session, '', ChatMessage::CHANNEL_SESSION, nil, nil, nil, 'JamKazam Recording', nil, claimed_recording) + end + + render :json => {}, :status => 200 + + end + def session_update begin @music_session = MusicSession.find(params[:id]) diff --git a/web/config/routes.rb b/web/config/routes.rb index 77516b8cb..29c2c4019 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -270,6 +270,8 @@ Rails.application.routes.draw do match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_update', :via => :post match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_show', :via => :get, :as => 'api_session_track_detail' match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_destroy', :via => :delete + match '/sessions/:id/attach_recording' => 'api_music_sessions#attach_recording', :via => :post + # Music notations match '/music_notations' => 'api_music_notations#create', :via => :post