VRFS-1442 band deletion; band member layout; invitations; band admin support

This commit is contained in:
Jonathan Kolyer 2014-12-03 02:42:26 +00:00
parent 220fd639e0
commit 9bdddc22ff
10 changed files with 174 additions and 45 deletions

View File

@ -26,30 +26,30 @@ module JamRuby
before_save :check_website_url
# musicians
has_many :band_musicians, :class_name => "JamRuby::BandMusician"
has_many :band_musicians, :class_name => "JamRuby::BandMusician", dependent: :destroy
has_many :users, :through => :band_musicians, :class_name => "JamRuby::User"
# genres
has_many :band_genres, class_name: "JamRuby::BandGenre"
has_many :band_genres, class_name: "JamRuby::BandGenre", dependent: :destroy
has_many :genres, class_name: "JamRuby::Genre", :through => :band_genres
# recordings
has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id"
has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id", dependent: :destroy
# self.id = likable_id in likes table
has_many :likers, :as => :likable, :class_name => "JamRuby::Like", :dependent => :destroy
has_many :likers, :as => :likable, :class_name => "JamRuby::Like", :dependent => :destroy, dependent: :destroy
# self.id = followable_id in follows table
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy, dependent: :destroy
# invitations
has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id"
has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id", dependent: :destroy
# music_sessions
has_many :music_sessions, :class_name => "JamRuby::MusicSession", foreign_key: :band_id, :inverse_of => :band
has_many :music_sessions, :class_name => "JamRuby::MusicSession", foreign_key: :band_id, :inverse_of => :band, dependent: :destroy
# events
has_many :event_sessions, :class_name => "JamRuby::EventSession"
has_many :event_sessions, :class_name => "JamRuby::EventSession", dependent: :destroy
include Geokit::ActsAsMappable::Glue unless defined?(acts_as_mappable)
acts_as_mappable
@ -114,11 +114,16 @@ module JamRuby
end
def self.musician_index(band_id)
@musicians = User.joins(:band_musicians).where(:bands_musicians => {:band_id => "#{band_id}"})
@musicians = User
.select("users.*, bands_musicians.admin AS band_admin")
.joins(:band_musicians)
.where(:bands_musicians => {:band_id => "#{band_id}"})
end
def self.pending_musicians(band_id)
@musicians = User.joins(:received_band_invitations)
@musicians = User
.select("users.*, band_invitations.id AS invitation_id")
.joins(:received_band_invitations)
.where(:band_invitations => {:band_id => "#{band_id}"})
.where(:band_invitations => {:accepted => nil})
end
@ -285,6 +290,12 @@ module JamRuby
Band.connection.execute("UPDATE bands SET lat = geo.latitude, lng = geo.longitude FROM geoiplocations#{table_suffix} as geo WHERE bands.city = geo.city AND bands.state = geo.region AND bands.country = geo.countrycode")
end
def self.is_member?(band_id, user_id)
BandMusician.where(band_id: band_id, user_id: user_id)
.limit(1)
.present?
end
private
def require_at_least_one_genre

View File

@ -7,11 +7,13 @@ module JamRuby
BAND_INVITATION_FAN_RECIPIENT_ERROR = "A Band invitation can only be sent to a Musician."
validates_uniqueness_of :user_id, scope: :band_id
belongs_to :receiver, :inverse_of => :received_band_invitations, :foreign_key => "user_id", :class_name => "JamRuby::User"
belongs_to :sender, :inverse_of => :sent_band_invitations, :foreign_key => "creator_id", :class_name => "JamRuby::User"
belongs_to :band, :inverse_of => :invitations, :foreign_key => "band_id", :class_name => "JamRuby::Band"
def self.save(id, band_id, user_id, creator_id, accepted)
def self.save(id, band_id, user_id, creator_id, accepted, resend=false)
band_invitation = BandInvitation.new()
@ -39,8 +41,17 @@ module JamRuby
# only the accepted flag can be updated after initial creation
else
band_invitation = BandInvitation.find(id)
band_invitation.accepted = accepted
band_invitation.save
if resend
Notification.send_band_invitation(
band_invitation.band,
band_invitation,
band_invitation.sender,
band_invitation.receiver
)
else
band_invitation.accepted = accepted
band_invitation.save
end
end
# accept logic:
@ -69,4 +80,4 @@ module JamRuby
band_invitation
end
end
end
end

View File

@ -8,6 +8,7 @@
var rest = context.JK.Rest();
var bandId;
var isMember = false;
var isAdmin = false;
var band = {};
var instrument_logo_map = context.JK.getInstrumentIconMap24();
@ -146,7 +147,8 @@
$("#btn-edit-band-profile").show();
$("#btn-edit-band-info").show();
$("#btn-edit-band-members").show();
$("#btn-edit-band-delete").show();
if (isAdmin)
$("#btn-edit-band-delete").show();
}
else {
$("#btn-follow-band").show();
@ -369,6 +371,8 @@
var memberHtml = context.JK.fillTemplate(template, {
userId: musician.id,
band_admin: bandAdmin,
is_pending: isPending,
invitation_id: isPending ? musician.invitation_id : '',
profile_url: "/client#/profile/" + musician.id,
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
name: musician.name,
@ -391,11 +395,25 @@
// var friend = isFriend(musician.id);
// configureMemberFriendButton(friend, musician.id);
});
if (isPending) {
$('div[pending-member=true] .btn-reinvite-member').each(function() {
var btn = $(this);
btn.show();
btn.unbind('click');
btn.click(function() {
var inviteid = $(this).closest('.band-profile-members').attr('invitation-id');
rest.resendBandInvitation(bandId, inviteid)
.done(function (response) {
app.notifyAlert('Band Invitation', 'Your invitation has been re-sent');
}).fail(app.ajaxError);
});
});
}
}
function configureRemoveMemberButton(userId, isPending, bandAdmin) {
var $divMember = $('div[user-id=' + userId + ']', '#band-profile-members');
var $btnRemoveMember = $divMember.find('#btn-remove-member');
var $btnRemoveMember = $divMember.find('.btn-remove-member');
if (isMember && !isPending && !bandAdmin) {
$btnRemoveMember.show();
$btnRemoveMember.unbind("click");
@ -411,6 +429,13 @@
rest.removeBandMember(bandId, userId)
.done(function() {
$divMember.remove();
if (userId == context.JK.currentUserId) {
$('#btn-edit-band-profile').hide();
$('#btn-edit-band-info').hide();
$('#btn-edit-band-members').hide();
$('.btn-remove-member').each(function(idx) { $(this).hide(); });
$('.btn-reinvite-member').each(function(idx) { $(this).hide(); });
}
})
.fail(app.ajaxError);
});
@ -435,10 +460,13 @@
error: app.ajaxError
})
.done(function(response) {
isMember = false;
isAdmin = isMember = false;
$.each(response, function(index, val) {
if (val.id === context.JK.currentUserId) {
isMember = true;
if (val.band_admin) {
isAdmin = true;
}
}
});
})
@ -464,6 +492,23 @@
context.location = "/client#/band/setup/" + bandId + '/step2';
return false;
});
$("#btn-edit-band-delete").unbind('click').click(function() {
var confirmDialog = new context.JK.ConfirmDialog(app,
"DELETE",
"Are you sure you want to delete this band? This is a permanent action which cannot be undone.",
"Delete Band",
function() {
app.layout.closeDialog('confirm');
rest.deleteBand(bandId)
.done(function() {
context.location = "/client#/profile/"+context.JK.currentUserId;
})
.fail(app.ajaxError);
});
confirmDialog.initialize();
context.JK.app.layout.showDialog('confirm');
return false;
});
}
function initialize() {

View File

@ -127,6 +127,10 @@
return band;
}
function showProfile(band_id) {
context.location = "/client#/bandProfile/" + band_id;
}
function saveBand() {
if (isSaving) return;
isSaving = true;
@ -137,9 +141,12 @@
rest.createBand(band)
.done(function (response) {
isSaving = false;
createBandInvitations(response.id, function () {
context.location = "/client#/bandProfile/" + response.id;
});
if (0 < $('#selected-friends-band .invitation').length) {
createBandInvitations(response.id, function () {
showProfile(response.id);
});
} else
showProfile(response.id);
})
.fail(function (jqXHR) {
isSaving = false;
@ -154,7 +161,7 @@
.done(function (response) {
isSaving = false;
createBandInvitations(band.id, function () {
context.location = "/client#/bandProfile/" + band.id;
showProfile(band.id);
});
}).fail(function (jqXHR) {
isSaving = false;
@ -172,10 +179,13 @@
});
} else if (step2) {
isSaving = false;
createBandInvitations(bandId, function () {
app.notifyAlert('Band Members', 'Your invitations have been sent');
context.location = "/client#/bandProfile/" + bandId;
});
if (0 < $('#selected-friends-band .invitation').length) {
createBandInvitations(bandId, function () {
app.notifyAlert('Band Members', 'Your invitations have been sent');
showProfile(bandId);
});
} else
showProfile(bandId);
}
}
}
@ -488,8 +498,7 @@
$("#band-setup-step-1").show();
$("#band-setup-step-2").hide();
} else {
resetForm();
window.history.go(-1);
showProfile(bandId);
return false;
}
});

View File

@ -350,6 +350,17 @@
return deferred;
}
function deleteBand(bandId) {
var url = "/api/bands/" + bandId;
return $.ajax({
type: "DELETE",
dataType: "json",
url: url,
contentType: 'application/json',
processData:false
});
}
function updateBand(band) {
return $.ajax({
type: "POST",
@ -396,6 +407,17 @@
return deferred;
}
function resendBandInvitation(bandId, invitationId) {
return $.ajax({
type: "POST",
dataType: "json",
url: '/api/bands/' + bandId + "/invitations/" + invitationId,
contentType: 'application/json',
processData: false,
data: JSON.stringify({"resend": true})
})
}
function removeBandMember(bandId, userId) {
var url = "/api/bands/" + bandId + "/musicians/" + userId;
return $.ajax({
@ -1456,6 +1478,7 @@
this.updateBand = updateBand;
this.updateBandPhoto = updateBandPhoto;
this.deleteBandPhoto = deleteBandPhoto;
this.deleteBand = deleteBand;
this.getBandPhotoFilepickerPolicy = getBandPhotoFilepickerPolicy;
this.getBand = getBand;
this.validateBand = validateBand;
@ -1486,6 +1509,7 @@
this.updateBillingInfo = updateBillingInfo;
this.placeOrder = placeOrder;
this.searchMusicians = searchMusicians;
this.resendBandInvitation = resendBandInvitation;
return this;
};

View File

@ -5,6 +5,7 @@ class ApiBandsController < ApiController
:recording_create, :recording_update, :recording_destroy,
:invitation_index, :invitation_show, :invitation_create, :invitation_destroy,
:update_photo, :delete_photo, :generate_filepicker_policy]
before_filter :auth_band_admin, :only => [:delete]
respond_to :json
@ -17,6 +18,11 @@ class ApiBandsController < ApiController
respond_with_model(@band)
end
def delete
@band.try(:destroy)
respond_with @band, responder => ApiResponder
end
def create
@band = Band.save(current_user, params)
@ -148,21 +154,32 @@ class ApiBandsController < ApiController
end
def invitation_create
@invitation = BandInvitation.save(nil,
params[:id],
params[:user_id],
current_user.id,
params[:accepted])
respond_with @invitation, responder: ApiResponder, :status => 201, :location => api_band_invitation_detail_url(@band, @invitation)
unless Band.is_member?(params[:id], params[:user_id])
@invitation = BandInvitation.save(nil,
params[:id],
params[:user_id],
current_user.id,
params[:accepted])
end
respond_with @invitation, responder: ApiResponder, :status => 201
# respond_with @invitation, responder: ApiResponder, :status => 201, :location => api_band_invitation_detail_url(@band, @invitation)
end
def invitation_update
@invitation = BandInvitation.save(params[:invitation_id],
nil,
nil,
nil,
params[:accepted])
if params[:resend]
@invitation = BandInvitation.save(params[:invitation_id],
nil,
nil,
nil,
false,
true)
else
@invitation = BandInvitation.save(params[:invitation_id],
nil,
nil,
nil,
params[:accepted])
end
end
def invitation_destroy
@ -236,4 +253,11 @@ class ApiBandsController < ApiController
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
def auth_band_admin
uid = current_user.id
@band = Band.find(params[:id])
unless @band.band_musicians.detect { |bm| bm.user_id == uid && bm.admin? }
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
end

View File

@ -3,7 +3,11 @@ collection @musicians
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography
node :band_admin do |musician|
't' == musician.band_admin ? true : false
musician.respond_to?(:band_admin) && 't' == musician.band_admin ? true : false
end
node :invitation_id do |musician|
musician.respond_to?(:invitation_id) ? musician.invitation_id : nil
end
node :instruments do |musician|

View File

@ -89,7 +89,7 @@
</div>
<script type="text/template" id="template-band-profile-members">
<div user-id="{userId}" class="band-profile-members" band-admin="{band_admin}">
<div user-id="{userId}" class="band-profile-members" band-admin="{band_admin}" pending-member="{is_pending}" invitation-id="{invitation_id}">
<div class="left" style="width:63px;">
<div class="avatar-small">
<img src="{avatar_url}" />
@ -117,9 +117,10 @@
</div>
<div class="result-list-button-wrapper">
<a class="button-orange smallbutton" href="{profile_url}">PROFILE</a>
<a id="btn-follow-member" class="button-orange smallbutton">FOLLOW</a>
<a id="btn-remove-member" class="button-orange smallbutton">REMOVE MEMBER</a>
<a id="btn-friend-member" style="display:none;" class="button-orange smallbutton">CONNECT</a>
<a class="btn-follow-member button-orange smallbutton">FOLLOW</a>
<a class="btn-remove-member button-orange smallbutton">REMOVE MEMBER</a>
<a style="display:none;" class="btn-reinvite-member button-orange smallbutton">RESEND INVITATION</a>
<a style="display:none;" class="btn-friend-member button-orange smallbutton">CONNECT</a>
</div>
</div>
</div>

View File

@ -209,7 +209,6 @@
<span class="profile-band-link-member-false"><a id="btn-follow-band-2" class="button-orange smallbutton">FOLLOW</a></span>
<span class="profile-band-link-member-true"><a href="{band_edit_url}" class="button-orange smallbutton">EDIT BAND</a></span>
<span class="profile-band-link-member-true"><a href="{band_member_url}" class="button-orange smallbutton">INVITE MEMBERS</a></span>
<span class="profile-band-link-delete"><a href="{band_delete_url}" class="button-orange smallbutton">DELETE</a></span>
</div>
</div>
</div>

View File

@ -345,6 +345,7 @@ SampleApp::Application.routes.draw do
match '/bands/:id' => 'api_bands#show', :via => :get, :as => 'api_band_detail'
match '/bands' => 'api_bands#create', :via => :post
match '/bands/:id' => 'api_bands#update', :via => :post
match '/bands/:id' => 'api_bands#delete', :via => :delete
# photo
match '/bands/:id/photo' => 'api_bands#update_photo', :via => :post