diff --git a/db/manifest b/db/manifest
index fb64e8498..0fc258a88 100755
--- a/db/manifest
+++ b/db/manifest
@@ -241,3 +241,4 @@ jam_track_available.sql
active_jam_track.sql
bpms_on_tap_in.sql
jamtracks_job.sql
+text_messages.sql
diff --git a/db/up/text_messages.sql b/db/up/text_messages.sql
new file mode 100644
index 000000000..4e8a10089
--- /dev/null
+++ b/db/up/text_messages.sql
@@ -0,0 +1,8 @@
+CREATE TABLE text_messages (
+ id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
+ source_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
+ target_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
+ message TEXT NOT NULL,
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index d89371770..084ae7d9e 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -197,6 +197,7 @@ require "jam_ruby/models/jam_company"
require "jam_ruby/models/user_sync"
require "jam_ruby/models/video_source"
require "jam_ruby/models/recorded_video"
+require "jam_ruby/models/text_message"
require "jam_ruby/jam_tracks_manager"
include Jampb
diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb
index 6dd93acc8..c28ae8de2 100644
--- a/ruby/lib/jam_ruby/models/active_music_session.rb
+++ b/ruby/lib/jam_ruby/models/active_music_session.rb
@@ -290,13 +290,8 @@ module JamRuby
}
)
- if (offset)
- query = query.offset(offset)
- end
-
- if (limit)
- query = query.limit(limit)
- end
+ query = query.offset(offset) if offset
+ query = query.limit(limit) if limit
if as_musician
query = query.where(
diff --git a/ruby/lib/jam_ruby/models/text_message.rb b/ruby/lib/jam_ruby/models/text_message.rb
new file mode 100644
index 000000000..64ef56e4c
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/text_message.rb
@@ -0,0 +1,51 @@
+include Devise::Models
+
+module JamRuby
+ class TextMessage < ActiveRecord::Base
+
+ self.primary_key = 'id'
+
+ default_scope order('created_at DESC')
+
+ attr_accessible :target_user_id, :source_user_id, :message
+
+ belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id"
+ belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id"
+
+ validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true
+
+ def self.index(target_user_id, source_user_id, options = {})
+ offset = options[:offset]
+ limit = options[:limit]
+
+ # if not specified, default offset to 0
+ offset ||= 0
+ offset = offset.to_i
+
+ # if not specified, default limit to 10
+ limit ||= 10
+ limit = limit.to_i
+
+ TextMessage
+ .offset(offset)
+ .limit(limit)
+ .where('(source_user_id = (?) AND target_user_id = (?)) OR (source_user_id = (?) AND target_user_id = (?))', source_user_id, target_user_id, target_user_id, source_user_id)
+
+ end
+
+ def self.create(message, target_user_id, source_user_id)
+ sanitized_text = Sanitize.fragment(message, elements: HtmlSanitize::SAFE)
+
+ # create new message
+ tm = TextMessage.new
+ tm.message = sanitized_text
+ tm.target_user_id = target_user_id
+ tm.source_user_id = source_user_id
+
+ # send notification
+ @notification = Notification.send_text_message(sanitized_text, source_user_id, User.find_by_id(target_user_id))
+
+ tm
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index a5ea8b827..cb6ff4b41 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -79,6 +79,9 @@ module JamRuby
# self.id = followable_id in follows table
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy
+ # text messages
+ has_many :text_messages, :class_name => "JamRuby:TextMessage", :foreign_key => "target_user_id"
+
# notifications
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id"
has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User"
@@ -1017,6 +1020,8 @@ module JamRuby
user.save
+ user.errors.add("recaptcha", "verification failed") if recaptcha_failed
+
if user.errors.any?
raise ActiveRecord::Rollback
else
@@ -1033,16 +1038,9 @@ module JamRuby
UserMailer.confirm_email(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver
end
end
-
- if recaptcha_failed
- user.errors.add "recaptcha", "verification failed"
- raise ActiveRecord::Rollback
- end
-
end
-
- return user
- end
+ user
+ end # def signup
# this is intended to be development-mode or test-mode only; VRFS-149
# it creates or updates one user per developer, so that we aren't in the business
diff --git a/ruby/spec/jam_ruby/models/text_message.spec.rb b/ruby/spec/jam_ruby/models/text_message.spec.rb
new file mode 100644
index 000000000..7b612b3ac
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/text_message.spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe TextMessage do
+
+ before do
+ TextMessage.delete_all
+ User.delete_all
+ @target_user = FactoryGirl.create(:user)
+ @source_user = FactoryGirl.create(:user)
+
+ @msg = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id)
+ end
+
+ describe "index" do
+
+ it "should retrieve conversation for both users" do
+ @msg.message = "Test message"
+ @msg.save!
+
+ messages = TextMessage.index(@target_user.id, @source_user.id)
+ messages.count.should == 1
+
+ messages = TextMessage.index(@source_user.id, @target_user.id)
+ messages.count.should == 1
+ end
+
+ it "should page records" do
+ 11.times do |n|
+ message = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id)
+ message.message = "Message #{n}"
+ message.save!
+ end
+
+ messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 0})
+ messages.count.should == 10
+
+ messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 10})
+ messages.count.should == 1
+ end
+
+ it "should not allow empty message" do
+ expect { @msg.save! }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/web/app/assets/javascripts/accounts_session_detail.js b/web/app/assets/javascripts/accounts_session_detail.js
index d75a5ea2e..82c028e52 100644
--- a/web/app/assets/javascripts/accounts_session_detail.js
+++ b/web/app/assets/javascripts/accounts_session_detail.js
@@ -339,7 +339,14 @@
if ("instrument_list" in pending_rsvp_request && pending_rsvp_request.instrument_list != null) {
$.each(pending_rsvp_request.instrument_list, function (index, instrument) {
- var instrumentId = context.JK.getInstrumentId(instrument.id);
+ var instrumentId;
+
+ if (instrument) {
+ instrumentId = context.JK.getInstrumentId(instrument.id);
+ }
+ else {
+ instrumentId = 'other';
+ }
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += ' ';
instrumentDesc.push(instrumentId);
@@ -378,7 +385,14 @@
$.each(sessionData.approved_rsvps, function(index, approved_rsvp) {
if ("instrument_list" in approved_rsvp) {
$.each(approved_rsvp.instrument_list, function(index, instrument) {
- var instrumentId = context.JK.getInstrumentId(instrument.id);
+ var instrumentId;
+
+ if (instrument) {
+ instrumentId = context.JK.getInstrumentId(instrument.id);
+ }
+ else {
+ instrumentId = 'other';
+ }
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += '
';
});
diff --git a/web/app/assets/javascripts/dialog/sessionStartDialog.js b/web/app/assets/javascripts/dialog/sessionStartDialog.js
new file mode 100644
index 000000000..971a5da65
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/sessionStartDialog.js
@@ -0,0 +1,130 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.SessionStartDialog = function(app, session) {
+ var logger = context.JK.logger;
+ var sessionUtils = context.JK.SessionUtils;
+ var $dialog = null;
+ var dialogId = 'session-start-dialog';
+ var $btnStartSession = null;
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+ }
+
+ function afterHide() {
+ }
+
+ function showDialog() {
+ return app.layout.showDialog(dialogId);
+ }
+
+ function events() {
+ $btnStartSession.unbind('click');
+ $btnStartSession.click(function(e) {
+ context.location = '/client#/session/' + session.id;
+ app.layout.closeDialog(dialogId);
+ });
+ }
+
+ function initializeSessionDetails() {
+ $dialog.find('#session-start-type-disp').html('Now!');
+ $dialog.find('#session-name-disp').html(session.name);
+ $dialog.find('#session-description-disp').html(session.description);
+
+ if (session.music_notations && session.music_notations.length > 0) {
+ $dialog.find('#session-notations-disp').html("Notations: " + session.music_notations.join(', '));
+ }
+
+ if (session.band) {
+ $dialog.find('#session-band-disp').html(band.name);
+ }
+ else {
+ $dialog.find('#session-band-disp').html('N/A');
+ }
+
+ $dialog.find('#session-language-disp').html(session.language_description);
+
+ var invitedFriends = session.invitations;
+
+ var sessionInvited = [];
+ $.each(invitedFriends, function(index, invitation) {
+ sessionInvited.push(invitation.receiver_name);
+ });
+
+ var sessionInvitedString = sessionInvited.join(', ');
+
+ if (session.musician_access && session.approval_required) {
+ if (session.open_rsvps) {
+ if (invitedFriends.length == 0)
+ sessionInvitedString = "Any interested JamKazam musicians that I approve";
+ else
+ sessionInvitedString += ", plus any interested JamKazam musicians that I approve";
+ }
+ else {
+ if (invitedFriends.length == 0) {
+ sessionInvitedString = "No open RSVPs";
+ }
+ else {
+ sessionInvitedString += " (No open RSVPs)";
+ }
+ }
+ }
+ else if (session.musician_access && !session.approval_required) {
+ if (invitedFriends.length == 0)
+ sessionInvitedString = "Any interested JamKazam musicians who want to join us";
+ else
+ sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
+ }
+
+ $dialog.find('#session-invited-disp').html(sessionInvitedString);
+
+ var instrumentsMe = [], instrumentsOthers = [];
+
+ $.each(session.approved_rsvps, function(index, rsvp) {
+ if (rsvp.id === context.JK.currentUserId) {
+ $.each(rsvp.instrument_list, function(index, instrument) {
+ instrumentsMe.push(instrument.desc);
+ });
+ }
+ else {
+ $.each(rsvp.instrument_list, function(index, instrument) {
+ instrumentsOthers.push(instrument.desc);
+ });
+ }
+ });
+
+ $dialog.find('#session-instruments-me-disp').html(instrumentsMe.join(', '));
+ $dialog.find('#session-instruments-rsvp-disp').html(instrumentsOthers.join(', '));
+
+ $dialog.find('#session-musician-access-disp').html('Musicians: ' + session.musician_access_description);
+ $dialog.find('#session-fans-access-disp').html('Fans: ' + session.fan_access_description);
+ $dialog.find('#session-policy-disp').html(session.legal_policy);
+ }
+
+ function initialize() {
+
+ var dialogBindings = {
+ 'beforeShow' : beforeShow,
+ 'afterShow' : afterShow,
+ 'afterHide': afterHide
+ };
+
+ app.bindDialog(dialogId, dialogBindings);
+
+ $dialog = $('[layout-id="' + dialogId + '"]');
+ $btnStartSession = $dialog.find('.btnStartSession');
+
+ initializeSessionDetails();
+ events();
+ }
+
+ this.initialize = initialize;
+ this.showDialog = showDialog;
+ }
+
+ return this;
+})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/dialog/textMessageDialog.js b/web/app/assets/javascripts/dialog/textMessageDialog.js
index 78107a7f9..62b3b68f9 100644
--- a/web/app/assets/javascripts/dialog/textMessageDialog.js
+++ b/web/app/assets/javascripts/dialog/textMessageDialog.js
@@ -163,6 +163,7 @@
}
$sendTextMessage.click(sendMessage);
+ // TODO: PULL FROM NEW TABLE
rest.getNotifications(buildParams())
.done(function (response) {
context._.each(response, function (textMessage) {
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index 230e6d23b..37951e687 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -1258,6 +1258,7 @@
});
}
+ // TODO: push into new table
function createTextMessage(options) {
var id = getId(options);
return $.ajax({
@@ -1269,6 +1270,17 @@
});
}
+ function getTextMessages(options) {
+ if(!options) options = {};
+ var id = getId(options);
+ return $.ajax({
+ type: "GET",
+ url: '/api/users/' + id + '/text_messages?' + $.param(options),
+ dataType: "json",
+ contentType: 'application/json'
+ });
+ }
+
function getNotifications(options) {
if(!options) options = {};
var id = getId(options);
diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js
index 41fa918c4..ca393b18d 100644
--- a/web/app/assets/javascripts/notificationPanel.js
+++ b/web/app/assets/javascripts/notificationPanel.js
@@ -220,7 +220,7 @@
function updateNotificationList(response) {
$.each(response, function(index, val) {
- if(val.description == 'TEXT_MESSAGE') {
+ if(val.description == context.JK.MessageType.TEXT_MESSAGE) {
val.formatted_msg = textMessageDialog.formatTextMessage(val.message.substring(0, 200), val.source_user_id, val.source_user.name, val.message.length > 200).html();
}
diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb
index ebbd6a9b3..969f9449d 100644
--- a/web/app/assets/javascripts/scheduled_session.js.erb
+++ b/web/app/assets/javascripts/scheduled_session.js.erb
@@ -200,14 +200,14 @@
createSessionSettings.startDate = createSessionSettings.startDate || (new Date().toDateString());
- $("#session-start-date").val(createSessionSettings.startDate);
+ $screen.find("#session-start-date").val(createSessionSettings.startDate);
toggleDate(true);
toggleStartTime();
toggleStepStatus();
sessionUtils.defaultTimezone($timezoneList);
if(firstTimeShown) {
firstTimeShown = false;
- $('#session-when-start-scheduled').iCheck('check');
+ $screen.find('#session-when-start-scheduled').iCheck('check');
}
}
@@ -217,7 +217,7 @@
function beforeShowStep3() {
rest.getBands(context.JK.currentUserId)
.done(function(result) {
- var options = $("#session-band-list");
+ var options = $screen.find("#session-band-list");
options.empty();
options.append($("").val('').text('No'));
$.each(result, function(idx, item) {
@@ -251,10 +251,10 @@
var sessionName = createSessionSettings.name;
sessionName += ' (' + createSessionSettings.genresValues[0] + ')';
- $('#session-name-disp').html(sessionName);
+ $screen.find('#session-name-disp').html(sessionName);
var sessionDescription = createSessionSettings.description;
- $('#session-description-disp').html(sessionDescription);
+ $screen.find('#session-description-disp').html(sessionDescription);
var sessionNotations = [];
for (var i = 0; i < createSessionSettings.notations.length; i++) {
@@ -262,16 +262,16 @@
sessionNotations.push(name);
}
if(sessionNotations.length > 0) {
- $('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
+ $screen.find('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
}
else {
- $('#session-notations-disp').html('');
+ $screen.find('#session-notations-disp').html('');
}
- $('#session-language-disp').html(createSessionSettings.language.label);
- $('#session-band-disp').html(createSessionSettings.band.label);
+ $screen.find('#session-language-disp').html(createSessionSettings.language.label);
+ $screen.find('#session-band-disp').html(createSessionSettings.band.label);
- var plusMusicians = $('#session-plus-musicians')[0].checked;
+ var plusMusicians = $screen.find('#session-plus-musicians')[0].checked;
var sessionInvited = [];
var invitedFriends = inviteMusiciansUtil.getInvitedFriendNames();
@@ -302,7 +302,7 @@
else
sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
}
- $('#session-invited-disp').html(sessionInvitedString);
+ $screen.find('#session-invited-disp').html(sessionInvitedString);
if (createSessionSettings.createType == '<%= MusicSession::CREATE_TYPE_START_SCHEDULED%>') {
var session = scheduledSessions[createSessionSettings.selectedSessionId];
@@ -315,7 +315,7 @@
});
}
});
- $('#session-instruments-me-disp').html(instruments_me.join(', '));
+ $screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
}
if (session.open_slots.length > 0) {
@@ -334,7 +334,7 @@
$.map(instruments_rsvp_arr, function(val, i) {
instruments_str_arr.push(i + ' (' + val.count + ') (' + val.level + ')');
})
- $('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
+ $screen.find('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
}
}
else {
@@ -342,7 +342,7 @@
$.each(getCreatorInstruments(), function(index, instrument) {
instruments_me.push(instrument.name);
});
- $('#session-instruments-me-disp').html(instruments_me.join(', '));
+ $screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
var instruments_rsvp = [];
var otherInstruments = instrumentRSVP.getSelectedInstruments();
@@ -353,13 +353,13 @@
$.each(otherInstruments, function(index, instrument) {
instruments_rsvp.push(instrument.name + ' (' + instrument.count + ') (' + proficiencyDescriptionMap[instrument.level] + ')');
});
- $('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
+ $screen.find('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
}
- $('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
- $('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
+ $screen.find('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
+ $screen.find('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
- $('#session-policy-disp').html(createSessionSettings.session_policy);
+ $screen.find('#session-policy-disp').html(createSessionSettings.session_policy);
}
function beforeMoveStep1() {
@@ -428,7 +428,7 @@
createSessionSettings.recurring_mode.value = 'once';
}
else {
- createSessionSettings.startDate = $('#session-start-date').val();
+ createSessionSettings.startDate = $screen.find('#session-start-date').val();
createSessionSettings.startTime = $startTimeList.val();
createSessionSettings.endTime = $endTimeList.val();
createSessionSettings.notations = [];
@@ -437,7 +437,7 @@
createSessionSettings.timezone.label = $timezoneList.get(0).options[$timezoneList.get(0).selectedIndex].text;
createSessionSettings.recurring_mode.label = $recurringModeList.get(0).options[$recurringModeList.get(0).selectedIndex].text;
createSessionSettings.recurring_mode.value = $recurringModeList.val();
- createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked;
+ createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked;
}
return true;
@@ -503,22 +503,22 @@
function beforeMoveStep2() {
var isValid = true;
- var name = $('#session-name').val();
+ var name = $screen.find('#session-name').val();
if (!name) {
$('#divSessionName .error-text').remove();
$('#divSessionName').addClass("error");
- $('#session-name').after("