Creating view to show confirmed matches, fixing bugs in permission logic and making proposal status updates ajax requests

This commit is contained in:
Absurdon 2017-10-07 12:28:06 +02:00
parent 548b15489c
commit 62bc78274a
7 changed files with 88 additions and 33 deletions

View file

@ -1,5 +1,5 @@
class ContestsController < ApplicationController class ContestsController < ApplicationController
before_filter :get_contest, only: [:show, :edit, :update, :destroy, :del_map, :scores, :recalc] before_filter :get_contest, only: [:show, :edit, :update, :destroy, :del_map, :scores, :recalc, :confirmed_matches]
def index def index
# @contests = Contest.all # @contests = Contest.all
@ -102,6 +102,10 @@ class ContestsController < ApplicationController
redirect_to contests_url redirect_to contests_url
end end
def confirmed_matches
@match_props = MatchProposal.confirmed_for_contest(@contest)
end
private private
def get_contest def get_contest

View file

@ -1,6 +1,7 @@
class MatchProposalsController < ApplicationController class MatchProposalsController < ApplicationController
before_filter :get_match before_filter :get_match
def index def index
raise AccessError unless @match.user_in_match?(cuser)
end end
def new def new
@ -25,25 +26,35 @@ class MatchProposalsController < ApplicationController
end end
def update def update
@proposal = MatchProposal.find(params[:id]) raise AccessError unless request.xhr? # Only respond to ajax requests
raise AccessError unless @proposal.can_update?(cuser, params[:match_proposal]) rjson = {}
@proposal.status = params[:match_proposal][:status] proposal = MatchProposal.find(params[:id])
if @proposal.save unless proposal
# TODO: rework messages rjson[:error] = {
# TODO: make it so only one proposal can be confirmed for a match at any given time code: 404,
action = case @proposal.status message: "No proposal with id #{params[:id]}"
when MatchProposal::STATUS_CONFIRMED }
"Confirmed Proposal for #{Time.use_zone(view_context.timezone_offset) { @proposal.proposed_time.strftime('%d %B %y %H:%M %Z') }}" render(json: rjson, status: :not_found) && return
when MatchProposal::STATUS_REJECTED end
"Rejected Proposal for #{Time.use_zone(view_context.timezone_offset) { @proposal.proposed_time.strftime('%d %B %y %H:%M %Z') }}" unless proposal.can_update?(cuser, params[:match_proposal])
else rjson[:error] = {
"Smthn went wrong" code: 403,
end message: "You are not allowed to update the state to #{MatchProposal.status_strings[params[:match_proposal][:status].to_i]}"
flash[:notice] = action }
else render(json: rjson, status: :forbidden) && return
flash[:notice] = "Error" end
proposal.status = params[:match_proposal][:status]
if proposal.save
rjson[:status] = MatchProposal.status_strings[proposal.status]
rjson[:message] = "Successfully updated status to #{MatchProposal.status_strings[proposal.status]}"
render(json: rjson, status: :success)
else
rjson[:error] = {
code: 500,
message: 'Something went wrong! Please try again.'
}
render(json: rjson, status: 500)
end end
redirect_to(match_proposals_path(@match))
end end
private private

View file

@ -19,8 +19,10 @@ class MatchProposal < ActiveRecord::Base
scope :of_match, ->(match) { where('match_id = ?', match.id) } scope :of_match, ->(match) { where('match_id = ?', match.id) }
scope :confirmed_for_match, ->(match) { where('match_id = ? AND status = ?', match.id, STATUS_CONFIRMED) } scope :confirmed_for_match, ->(match) { where('match_id = ? AND status = ?', match.id, STATUS_CONFIRMED) }
scope :confirmed_upcoming, ->{ where('status = ? AND proposed_time > UTC_TIMESTAMP()', STATUS_CONFIRMED) } scope :confirmed_upcoming, ->{ where('status = ? AND proposed_time > UTC_TIMESTAMP()', STATUS_CONFIRMED) }
scope :confirmed_for_contest,
->(contest){ includes(:match).where(matches:{contest_id: contest.id}, status: STATUS_CONFIRMED).all}
def status_strings def self.status_strings
{STATUS_PENDING => 'Pending', {STATUS_PENDING => 'Pending',
STATUS_REVOKED => 'Revoked', STATUS_REVOKED => 'Revoked',
STATUS_REJECTED => 'Rejected', STATUS_REJECTED => 'Rejected',
@ -47,7 +49,11 @@ class MatchProposal < ActiveRecord::Base
cuser && cuser.admin? cuser && cuser.admin?
end end
private def state_immutable?
status == STATUS_REJECTED ||
status == STATUS_DELAYED ||
status == STATUS_REVOKED
end
def status_change_allowed?(cuser, new_status) def status_change_allowed?(cuser, new_status)
case new_status case new_status
@ -58,17 +64,20 @@ class MatchProposal < ActiveRecord::Base
# only confirmed matches can be set to delayed # only confirmed matches can be set to delayed
# only admins can set matches to delayed and only if they are not playing in that match # only admins can set matches to delayed and only if they are not playing in that match
# matches can only be delayed if they are not to far in the future # matches can only be delayed if they are not to far in the future
return false unless self.status == STATUS_CONFIRMED && cuser.admin? && return self.status == STATUS_CONFIRMED && cuser.admin? &&
!self.match.user_in_match?(cuser) && self.proposed_time <= CONFIRMATION_LIMIT.minutes.from_now !self.match.user_in_match?(cuser) && self.proposed_time <= CONFIRMATION_LIMIT.minutes.from_now
when STATUS_REVOKED when STATUS_REVOKED
# unconfirmed can only be revoked by team making the proposal # unconfirmed can only be revoked by team making the proposal
# confirmed can only be revoked if soon enough before match time # confirmed can only be revoked if soon enough before match time
return false unless self.status == STATUS_PENDING && self.team == cuser.team || return self.status == STATUS_PENDING && self.team == cuser.team ||
self.status == STATUS_CONFIRMED && self.proposed_time > CONFIRMATION_LIMIT.minutes.from_now self.status == STATUS_CONFIRMED && self.proposed_time > CONFIRMATION_LIMIT.minutes.from_now
when STATUS_CONFIRMED, STATUS_REJECTED when STATUS_CONFIRMED, STATUS_REJECTED
# only team proposed to can reject or confirm and only if soon enough before match time # only team proposed to can reject or confirm and only if soon enough before match time
return false unless self.status == STATUS_PENDING && self.team != cuser.team && status_ok = self.status == STATUS_PENDING
self.proposed_time < CONFIRMATION_LIMIT.minutes.from_now team_ok = self.team != cuser.team
time_ok = CONFIRMATION_LIMIT.minutes.from_now < self.proposed_time
return status_ok && team_ok && time_ok
else else
# invalid status # invalid status
return false return false

View file

@ -0,0 +1,13 @@
<h1 class="title">Confirmed Matches for <%= @contest.name %></h1>
<table class="striped">
<tr>
<th>Match</th>
<th>Scheduled for</th>
</tr>
<% @match_props && @match_props.each do |mp| %>
<tr>
<th><%= link_to mp.match.contester1, contester_path(mp.match.contester1) %> VS <%= link_to mp.match.contester2, contester_path(mp.match.contester2) %></th>
<th><%= longertime(mp.proposed_time) %></th>
</tr>
<% end %>
</table>

View file

@ -1,7 +1,8 @@
<h1>Proposals</h1> <h1>Proposals</h1>
<h2><%= @match.contester1 %> VS <%= @match.contester2 %></h2>
<%= link_to 'Back', match_path(@match), class: 'button' %> <%= link_to 'Back', match_path(@match), class: 'button' %>
<% if @match.match_proposals.empty? %> <% if @match.match_proposals.empty? %>
<h4>There are no proposals yet</h4> <h4 style="clear: both;">There are no proposals yet</h4>
<% else %> <% else %>
<table id="proposals" class="striped"> <table id="proposals" class="striped">
<tr> <tr>
@ -16,13 +17,19 @@
<tr class="<%=cycle('even', 'odd') %>"> <tr class="<%=cycle('even', 'odd') %>">
<td><%= proposal.team.name %></td> <td><%= proposal.team.name %></td>
<td><%= longtime proposal.proposed_time %> </td> <td><%= longtime proposal.proposed_time %> </td>
<td><%= proposal.status_strings[proposal.status] %></td> <td><%= MatchProposal.status_strings[proposal.status] %></td>
<% if @match.can_make_proposal?(cuser) %> <% if @match.can_make_proposal?(cuser) %>
<td> <td>
<%= form_for proposal, url: match_proposal_path(@match, proposal) do |f| %> <% unless proposal.state_immutable? %>
<%= f.hidden_field :status, value: 0 %> <%= form_for proposal, url: match_proposal_path(@match, proposal) do |f| %>
<%= link_to_function icon('check'), "proposalStateSubmit(#{MatchProposal::STATUS_CONFIRMED},#{proposal.id})" %> <%= f.hidden_field :status, value: 0 %>
<%= link_to_function icon('times'), "proposalStateSubmit(#{MatchProposal::STATUS_REJECTED},#{proposal.id})" %> <%= link_to_function icon('check'), "proposalStateSubmit(#{MatchProposal::STATUS_CONFIRMED},#{proposal.id})", title: 'Confirm' %>
<%= link_to_function icon('times'), "proposalStateSubmit(#{MatchProposal::STATUS_REJECTED},#{proposal.id})", title: 'Reject' %>
<%= link_to_function icon('eraser'), "proposalStateSubmit(#{MatchProposal::STATUS_REVOKED},#{proposal.id})", title: 'Revoke' %>
<% if proposal.status_change_allowed?(cuser, MatchProposal::STATUS_DELAYED) %>
<%= link_to_function icon('hourglass'), "proposalStateSubmit(#{MatchProposal::STATUS_DELAYED},#{proposal.id})", title: 'Delay' %>
<% end %>
<% end %>
<% end %> <% end %>
</td> </td>
<% end %> <% end %>
@ -33,7 +40,16 @@
function proposalStateSubmit(newState, formID) { function proposalStateSubmit(newState, formID) {
var form = $('#edit_match_proposal_' + formID); var form = $('#edit_match_proposal_' + formID);
form.children("input[type='hidden']").val(newState); form.children("input[type='hidden']").val(newState);
form.submit(); $.post(form.attr('action'),form.serialize(), function(data) {
datajson = data.responseText;
tr = form.closest('tr');
tr.children('td').eq(1).html(datajson.status);
alert(datajson.message);
}, 'json')
.error(function (err) {
errjson = JSON.parse(err.responseText);
alert(errjson.error.message);
});
} }
</script> </script>
<% end %> <% end %>

View file

@ -116,7 +116,7 @@
<p><%= @match.report.html_safe %></p> <p><%= @match.report.html_safe %></p>
</div> </div>
<% end %> <% end %>
<% if cuser and (cuser.admin? or @match.of_user cuser) %> <% if cuser and (cuser.admin? or @match.user_in_match? cuser) %>
<div class="referee"> <div class="referee">
<%= link_to 'Proposals', match_proposals_path(@match), class: 'button' %> <%= link_to 'Proposals', match_proposals_path(@match), class: 'button' %>
</div> </div>

View file

@ -51,6 +51,8 @@ Ensl::Application.routes.draw do
resources :users resources :users
resources :locks resources :locks
resources :contesters resources :contesters
get "contests/:id/confirmedmatches" => "contests#confirmed_matches", as: :confirmed_matches
resources :contests resources :contests
resources :challenges resources :challenges
resources :servers resources :servers