Description:
* DRY the toggle button via application_helper.rb#toggle_button and _toggle_button.js.haml * bootrapize the user_admin * now considering user.enabled * tidy up route
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r562:ede66b5a6449 - - 15 files changed: 171 inserted, 52 deleted

@@ -0,0 +1,7
1 + :plain
2 + var t = $("#{button_id}");
3 + t.removeClass('btn-default');
4 + t.removeClass('btn-success');
5 + t.removeClass('btn-warning');
6 + t.addClass("btn-#{button_on ? 'success' : 'default'}");
7 + t.text("#{button_on ? 'Yes' : 'No'}");
@@ -0,0 +1,86
1 + %h1 Listing users
2 + .submitbox
3 + %b Quick add
4 + = form_tag :action => 'create' do
5 + %table{:border => "0"}
6 + %tr
7 + %td
8 + %label{:for => "user_login"} Login
9 + %td
10 + %label{:for => "user_full_name"} Full name
11 + %td
12 + %label{:for => "user_password"} Password
13 + %td
14 + %label{:for => "user_password_confirmation"} Confirm
15 + %td
16 + %label{:for => "user_email"} Email
17 + %tr
18 + %td= text_field 'user', 'login', :size => 10
19 + %td= text_field 'user', 'full_name', :size => 30
20 + %td= password_field 'user', 'password', :size => 10
21 + %td= password_field 'user', 'password_confirmation', :size => 10
22 + %td= email_field 'user', 'email', :size => 15
23 + %td= submit_tag "Create"
24 + %br/
25 + %b Import from site management
26 + = form_tag({:action => 'import'}, :multipart => true) do
27 + File: #{file_field_tag 'file'} #{submit_tag 'Import'}
28 + %br/
29 + %b What else:
30 + = link_to 'New user', {:action => 'new'}, { class: 'btn btn-default btn-sm'}
31 + = link_to 'New list of users',{ :action => 'new_list'}, { class: 'btn btn-default btn-sm'}
32 + = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default btn-sm'}
33 + = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default btn-sm'}
34 + = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default btn-sm'}
35 + = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default btn-sm'}
36 + - if GraderConfiguration.multicontests?
37 + %br/
38 + %b Multi-contest:
39 + = link_to '[Manage bulk users in contests]', :action => 'contest_management'
40 + View users in:
41 + - @contests.each do |contest|
42 + = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
43 + = link_to "[no contest]", :action => 'contests', :id => 'none'
44 + Total #{@user_count} users |
45 + - if !@paginated
46 + Display all users.
47 + \#{link_to '[show in pages]', :action => 'index', :page => '1'}
48 + - else
49 + Display in pages.
50 + \#{link_to '[display all]', :action => 'index', :page => 'all'} |
51 + \#{will_paginate @users, :container => false}
52 +
53 +
54 + %table.table.table-hover.table-condense
55 + %thead
56 + %th Login
57 + %th Full name
58 + %th email
59 + %th Remark
60 + %th
61 + Activated
62 + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
63 + %th
64 + Enabled
65 + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
66 + %th Last IP
67 + %th
68 + %th
69 + %th
70 + %th
71 + - for user in @users
72 + %tr
73 + %td= link_to user.login, controller: :users, :action => 'profile', :id => user
74 + %td= user.full_name
75 + %td= user.email
76 + %td= user.remark
77 + %td= toggle_button(user.activated?, toggle_activate_user_url(user),"toggle_activate_user_#{user.id}")
78 + %td= toggle_button(user.enabled?, toggle_enable_user_url(user),"toggle_enable_user_#{user.id}")
79 + %td= user.last_ip
80 + %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
81 + %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
82 + %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
83 + %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block'
84 + %br/
85 + = link_to '[New user]', :action => 'new'
86 + = link_to '[New list of users]', :action => 'new_list'
@@ -1,14 +1,6
1 1 #js for announcement
2 2 $ ->
3 - $('.ajax-toggle').on 'click', (event) ->
4 - target = $(event.target)
5 - target.removeClass 'btn-default'
6 - target.removeClass 'btn-success'
7 - target.addClass 'btn-warning'
8 - target.text '...'
9 - return
10 -
11 3 $(document).ajaxError (event, jqxhr, settings, exception) ->
12 4 if jqxhr.status
13 5 alert 'We\'re sorry, but something went wrong (' + jqxhr.status + ')'
14 6 return
@@ -1,35 +1,41
1 1 $(document).on 'change', '.btn-file :file', ->
2 2 input = $(this)
3 3 numFiles = if input.get(0).files then input.get(0).files.length else 1
4 4 label = input.val().replace(/\\/g, '/').replace(/.*\//, '')
5 5 input.trigger 'fileselect', [
6 6 numFiles
7 7 label
8 8 ]
9 9 return
10 10
11 11
12 12 # document ready
13 13
14 14 $ ->
15 15 $(".select2").select2()
16 16 #$(".bootstrap-switch").bootstrapSwitch()
17 17 $(".bootstrap-toggle").bootstrapToggle()
18 18 $('.btn-file :file').on 'fileselect', (event, numFiles, label) ->
19 19 input = $(this).parents('.input-group').find(':text')
20 20 log = if numFiles > 1 then numFiles + ' files selected' else label
21 21 if input.length
22 22 input.val log
23 23 else
24 24 if log
25 25 alert log
26 26 return
27 27 $(".go-button").on 'click', (event) ->
28 28 link = $(this).attr("data-source")
29 29 url = $(link).val()
30 30 if url
31 31 window.location.href = url
32 32 return
33 - return
33 + $('.ajax-toggle').on 'click', (event) ->
34 + target = $(event.target)
35 + target.removeClass 'btn-default'
36 + target.removeClass 'btn-success'
37 + target.addClass 'btn-warning'
38 + target.text '...'
39 + return
34 40
35 -
41 + return
@@ -38,64 +38,65
38 38 end
39 39 end
40 40
41 41 # GET /announcements/1/edit
42 42 def edit
43 43 @announcement = Announcement.find(params[:id])
44 44 end
45 45
46 46 # POST /announcements
47 47 # POST /announcements.xml
48 48 def create
49 49 @announcement = Announcement.new(params[:announcement])
50 50
51 51 respond_to do |format|
52 52 if @announcement.save
53 53 flash[:notice] = 'Announcement was successfully created.'
54 54 format.html { redirect_to(@announcement) }
55 55 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
56 56 else
57 57 format.html { render :action => "new" }
58 58 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
59 59 end
60 60 end
61 61 end
62 62
63 63 # PUT /announcements/1
64 64 # PUT /announcements/1.xml
65 65 def update
66 66 @announcement = Announcement.find(params[:id])
67 67
68 68 respond_to do |format|
69 69 if @announcement.update_attributes(params[:announcement])
70 70 flash[:notice] = 'Announcement was successfully updated.'
71 71 format.html { redirect_to(@announcement) }
72 72 format.js {}
73 73 format.xml { head :ok }
74 74 else
75 75 format.html { render :action => "edit" }
76 76 format.js {}
77 77 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
78 78 end
79 79 end
80 80 end
81 81
82 82 def toggle
83 83 @announcement = Announcement.find(params[:id])
84 84 @announcement.update_attributes( published: !@announcement.published? )
85 85 respond_to do |format|
86 - format.js {}
86 + format.js { render partial: 'toggle_button',
87 + locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
87 88 end
88 89 end
89 90
90 91 # DELETE /announcements/1
91 92 # DELETE /announcements/1.xml
92 93 def destroy
93 94 @announcement = Announcement.find(params[:id])
94 95 @announcement.destroy
95 96
96 97 respond_to do |format|
97 98 format.html { redirect_to(announcements_url) }
98 99 format.xml { head :ok }
99 100 end
100 101 end
101 102 end
@@ -2,102 +2,107
2 2 protect_from_forgery
3 3
4 4 before_filter :current_user
5 5
6 6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
8 8
9 9 # Returns the current logged-in user (if any).
10 10 def current_user
11 11 return nil unless session[:user_id]
12 12 @current_user ||= User.find(session[:user_id])
13 13 end
14 14
15 15 def admin_authorization
16 16 return false unless authenticate
17 17 user = User.find(session[:user_id], :include => ['roles'])
18 18 unless user.admin?
19 19 flash[:notice] = 'You are not authorized to view the page you requested'
20 20 redirect_to :controller => 'main', :action => 'login' unless user.admin?
21 21 return false
22 22 end
23 23 return true
24 24 end
25 25
26 26 def authorization_by_roles(allowed_roles)
27 27 return false unless authenticate
28 28 user = User.find(session[:user_id])
29 29 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
30 30 flash[:notice] = 'You are not authorized to view the page you requested'
31 31 redirect_to :controller => 'main', :action => 'login'
32 32 return false
33 33 end
34 34 end
35 35
36 36 protected
37 37
38 38 def authenticate
39 39 unless session[:user_id]
40 40 flash[:notice] = 'You need to login'
41 41 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
42 42 flash[:notice] = 'You need to login but you cannot log in at this time'
43 43 end
44 44 redirect_to :controller => 'main', :action => 'login'
45 45 return false
46 46 end
47 47
48 48 # check if run in single user mode
49 49 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
50 - user = User.find(session[:user_id])
50 + user = User.find_by_id(session[:user_id])
51 51 if user==nil or (not user.admin?)
52 52 flash[:notice] = 'You cannot log in at this time'
53 53 redirect_to :controller => 'main', :action => 'login'
54 54 return false
55 55 end
56 + unless user.enabled?
57 + flash[:notice] = 'Your account is disabled'
58 + redirect_to :controller => 'main', :action => 'login'
59 + return false
60 + end
56 61 return true
57 62 end
58 63
59 64 if GraderConfiguration.multicontests?
60 65 user = User.find(session[:user_id])
61 66 return true if user.admin?
62 67 begin
63 68 if user.contest_stat(true).forced_logout
64 69 flash[:notice] = 'You have been automatically logged out.'
65 70 redirect_to :controller => 'main', :action => 'index'
66 71 end
67 72 rescue
68 73 end
69 74 end
70 75 return true
71 76 end
72 77
73 78 def authenticate_by_ip_address
74 79 #this assume that we have already authenticate normally
75 80 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
76 81 user = User.find(session[:user_id])
77 82 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
78 83 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
79 84 redirect_to :controller => 'main', :action => 'login'
80 85 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
81 86 return false
82 87 end
83 88 unless user.last_ip
84 89 user.last_ip = request.remote_ip
85 90 user.save
86 91 end
87 92 end
88 93 return true
89 94 end
90 95
91 96 def authorization
92 97 return false unless authenticate
93 98 user = User.find(session[:user_id])
94 99 unless user.roles.detect { |role|
95 100 role.rights.detect{ |right|
96 101 right.controller == self.class.controller_name and
97 102 (right.action == 'all' or right.action == action_name)
98 103 }
99 104 }
100 105 flash[:notice] = 'You are not authorized to view the page you requested'
101 106 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
102 107 redirect_to :controller => 'main', :action => 'login'
103 108 return false
@@ -78,97 +78,97
78 78 render :action => 'edit' and return
79 79 end
80 80 @problem.description = @description
81 81 elsif @description!=nil
82 82 if !@description.update_attributes(params[:description])
83 83 flash[:notice] = 'Error saving description'
84 84 render :action => 'edit' and return
85 85 end
86 86 end
87 87 if params[:file] and params[:file].content_type != 'application/pdf'
88 88 flash[:notice] = 'Error: Uploaded file is not PDF'
89 89 render :action => 'edit' and return
90 90 end
91 91 if @problem.update_attributes(params[:problem])
92 92 flash[:notice] = 'Problem was successfully updated.'
93 93 unless params[:file] == nil or params[:file] == ''
94 94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
95 95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
96 96 if not FileTest.exists? out_dirname
97 97 Dir.mkdir out_dirname
98 98 end
99 99
100 100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
101 101 if FileTest.exists? out_filename
102 102 File.delete out_filename
103 103 end
104 104
105 105 File.open(out_filename,"wb") do |file|
106 106 file.write(params[:file].read)
107 107 end
108 108 @problem.description_filename = "#{@problem.name}.pdf"
109 109 @problem.save
110 110 end
111 111 redirect_to :action => 'show', :id => @problem
112 112 else
113 113 render :action => 'edit'
114 114 end
115 115 end
116 116
117 117 def destroy
118 118 Problem.find(params[:id]).destroy
119 119 redirect_to action: :index
120 120 end
121 121
122 122 def toggle
123 123 @problem = Problem.find(params[:id])
124 124 @problem.update_attributes(available: !(@problem.available) )
125 125 respond_to do |format|
126 - format.js {}
126 + format.js { }
127 127 end
128 128 end
129 129
130 130 def turn_all_off
131 131 Problem.find(:all,
132 132 :conditions => "available = 1").each do |problem|
133 133 problem.available = false
134 134 problem.save
135 135 end
136 136 redirect_to action: :index
137 137 end
138 138
139 139 def turn_all_on
140 140 Problem.find(:all,
141 141 :conditions => "available = 0").each do |problem|
142 142 problem.available = true
143 143 problem.save
144 144 end
145 145 redirect_to action: :index
146 146 end
147 147
148 148 def stat
149 149 @problem = Problem.find(params[:id])
150 150 unless @problem.available or session[:admin]
151 151 redirect_to :controller => 'main', :action => 'list'
152 152 return
153 153 end
154 154 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
155 155
156 156 #stat summary
157 157 range =65
158 158 @histogram = { data: Array.new(range,0), summary: {} }
159 159 user = Hash.new(0)
160 160 @submissions.find_each do |sub|
161 161 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
162 162 @histogram[:data][d.to_i] += 1 if d < range
163 163 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
164 164 end
165 165 @histogram[:summary][:max] = [@histogram[:data].max,1].max
166 166
167 167 @summary = { attempt: user.count, solve: 0 }
168 168 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
169 169 end
170 170
171 171 def manage
172 172 @problems = Problem.find(:all, :order => 'date_added DESC')
173 173 end
174 174
@@ -1,68 +1,67
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_filter :admin_authorization
8 8
9 9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 10 verify :method => :post, :only => [ :destroy,
11 11 :create, :create_from_list,
12 12 :update,
13 13 :manage_contest,
14 14 :bulk_mail
15 15 ],
16 16 :redirect_to => { :action => :list }
17 17
18 18 def index
19 19 list
20 - render :action => 'list'
21 20 end
22 21
23 22 def list
24 23 @user_count = User.count
25 24 if params[:page] == 'all'
26 25 @users = User.all
27 26 @paginated = false
28 27 else
29 28 @users = User.paginate :page => params[:page]
30 29 @paginated = true
31 30 end
32 31 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
33 32 @contests = Contest.enabled
34 33 end
35 34
36 35 def active
37 36 sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
38 37 @users = []
39 38 sessions.each do |session|
40 39 if session.data[:user_id]
41 40 @users << User.find(session.data[:user_id])
42 41 end
43 42 end
44 43 end
45 44
46 45 def show
47 46 @user = User.find(params[:id])
48 47 end
49 48
50 49 def new
51 50 @user = User.new
52 51 end
53 52
54 53 def create
55 54 @user = User.new(params[:user])
56 55 @user.activated = true
57 56 if @user.save
58 57 flash[:notice] = 'User was successfully created.'
59 58 redirect_to :action => 'list'
60 59 else
61 60 render :action => 'new'
62 61 end
63 62 end
64 63
65 64 def clear_last_ip
66 65 @user = User.find(params[:id])
67 66 @user.last_ip = nil
68 67 @user.save
@@ -88,108 +88,127
88 88
89 89 def forget
90 90 render :action => 'forget', :layout => 'empty'
91 91 end
92 92
93 93 def retrieve_password
94 94 email = params[:email]
95 95 user = User.find_by_email(email)
96 96 if user
97 97 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
98 98 if last_updated_time > Time.now.gmtime - 5.minutes
99 99 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
100 100 else
101 101 user.password = user.password_confirmation = User.random_password
102 102 user.save
103 103 send_new_password_email(user)
104 104 flash[:notice] = 'New password has been mailed to you.'
105 105 end
106 106 else
107 107 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
108 108 end
109 109 redirect_to :action => 'forget'
110 110 end
111 111
112 112 def profile
113 113 @user = User.find(params[:id])
114 114 @submission = Submission.includes(:problem).where(user_id: params[:id])
115 115
116 116 range = 120
117 117 @histogram = { data: Array.new(range,0), summary: {} }
118 118 @summary = {count: 0, solve: 0, attempt: 0}
119 119 problem = Hash.new(0)
120 120
121 121 @submission.find_each do |sub|
122 122 #histogram
123 123 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
124 124 @histogram[:data][d.to_i] += 1 if d < range
125 125
126 126 @summary[:count] += 1
127 127 next unless sub.problem
128 128 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
129 129 end
130 130
131 131 @histogram[:summary][:max] = [@histogram[:data].max,1].max
132 132 @summary[:attempt] = problem.count
133 133 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
134 134 end
135 135
136 + def toggle_activate
137 + @user = User.find(params[:id])
138 + @user.update_attributes( activated: !@user.activated? )
139 + respond_to do |format|
140 + format.js { render partial: 'toggle_button',
141 + locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
142 + end
143 + end
144 +
145 + def toggle_enable
146 + @user = User.find(params[:id])
147 + @user.update_attributes( enabled: !@user.enabled? )
148 + respond_to do |format|
149 + format.js { render partial: 'toggle_button',
150 + locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
151 + end
152 + end
153 +
136 154 protected
137 155
138 156 def verify_online_registration
139 157 if !GraderConfiguration['system.online_registration']
140 158 redirect_to :controller => 'main', :action => 'login'
141 159 end
142 160 end
143 161
144 162 def send_confirmation_email(user)
145 163 contest_name = GraderConfiguration['contest.name']
146 164 activation_url = url_for(:action => 'confirm',
147 165 :login => user.login,
148 166 :activation => user.activation_key)
149 167 home_url = url_for(:controller => 'main', :action => 'index')
150 168 mail_subject = "[#{contest_name}] Confirmation"
151 169 mail_body = t('registration.email_body', {
152 170 :full_name => user.full_name,
153 171 :contest_name => contest_name,
154 172 :login => user.login,
155 173 :password => user.password,
156 174 :activation_url => activation_url,
157 175 :admin_email => GraderConfiguration['system.admin_email']
158 176 })
159 177
160 178 logger.info mail_body
161 179
162 180 send_mail(user.email, mail_subject, mail_body)
163 181 end
164 182
165 183 def send_new_password_email(user)
166 184 contest_name = GraderConfiguration['contest.name']
167 185 mail_subject = "[#{contest_name}] Password recovery"
168 186 mail_body = t('registration.password_retrieval.email_body', {
169 187 :full_name => user.full_name,
170 188 :contest_name => contest_name,
171 189 :login => user.login,
172 190 :password => user.password,
173 191 :admin_email => GraderConfiguration['system.admin_email']
174 192 })
175 193
176 194 logger.info mail_body
177 195
178 196 send_mail(user.email, mail_subject, mail_body)
179 197 end
180 198
181 199 # allow viewing of regular user profile only when options allow so
182 200 # only admins can view admins profile
183 201 def profile_authorization
184 202 #if view admins' profile, allow only admin
185 203 return false unless(params[:id])
186 204 user = User.find(params[:id])
187 205 return false unless user
188 206 return admin_authorization if user.admin?
189 207 return true if GraderConfiguration["right.user_view_submission"]
190 208
191 209 #finally, we allow only admin
192 210 admin_authorization
193 211 end
194 212
213 +
195 214 end
@@ -62,106 +62,101
62 62 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
63 63 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
64 64 end
65 65
66 66 if GraderConfiguration['right.user_hall_of_fame']
67 67 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
68 68 end
69 69 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
70 70
71 71 if GraderConfiguration['system.user_setting_enabled']
72 72 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
73 73 end
74 74 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
75 75
76 76 menu_items.html_safe
77 77 end
78 78
79 79 def append_to(option,label, controller, action)
80 80 option << ' ' if option!=''
81 81 option << link_to_unless_current(label,
82 82 :controller => controller,
83 83 :action => action)
84 84 end
85 85
86 86 def format_short_time(time)
87 87 now = Time.now.gmtime
88 88 st = ''
89 89 if (time.yday != now.yday) or
90 90 (time.year != now.year)
91 91 st = time.strftime("%x ")
92 92 end
93 93 st + time.strftime("%X")
94 94 end
95 95
96 96 def format_short_duration(duration)
97 97 return '' if duration==nil
98 98 d = duration.to_f
99 99 return Time.at(d).gmtime.strftime("%X")
100 100 end
101 101
102 102 def read_textfile(fname,max_size=2048)
103 103 begin
104 104 File.open(fname).read(max_size)
105 105 rescue
106 106 nil
107 107 end
108 108 end
109 109
110 - def problem_select(problems, options = {})
111 - prefix = options[:with_specific_in_header] ? [[(t 'main.specified_in_header'),'-1']] : []
112 - selected = options[:selected] || (options[:with_specific_in_header] ? -1 : nil)
113 - puts "selected = #{selected} hehe"
114 - html_options = {class: 'select2 form-control'}
115 - html_options[:id] = options[:id]if options[:id]
116 - select 'submission',
117 - 'problem_id', prefix + problems.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},
118 - (selected ? { selected: "#{selected}"} : {} ),
119 - html_options
110 + def toggle_button(on,toggle_url,id)
111 + link_to (on ? "Yes" : "No"), toggle_url,
112 + {class: "btn btn-block btn-xs btn-#{on ? 'success' : 'default'} ajax-toggle",
113 + id: id,
114 + data: {remote: true, method: 'get'}}
120 115 end
121 116
122 117 def user_title_bar(user)
123 118 header = ''
124 119 time_left = ''
125 120
126 121 #
127 122 # if the contest is over
128 123 if GraderConfiguration.time_limit_mode?
129 124 if user.contest_finished?
130 125 header = <<CONTEST_OVER
131 126 <tr><td colspan="2" align="center">
132 127 <span class="contest-over-msg">THE CONTEST IS OVER</span>
133 128 </td></tr>
134 129 CONTEST_OVER
135 130 end
136 131 if !user.contest_started?
137 132 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
138 133 else
139 134 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
140 135 " #{format_short_duration(user.contest_time_left)}"
141 136 end
142 137 end
143 138
144 139 #
145 140 # if the contest is in the anaysis mode
146 141 if GraderConfiguration.analysis_mode?
147 142 header = <<ANALYSISMODE
148 143 <tr><td colspan="2" align="center">
149 144 <span class="contest-over-msg">ANALYSIS MODE</span>
150 145 </td></tr>
151 146 ANALYSISMODE
152 147 end
153 148
154 149 contest_name = GraderConfiguration['contest.name']
155 150
156 151 #
157 152 # build real title bar
158 153 result = <<TITLEBAR
159 154 <div class="title">
160 155 <table>
161 156 #{header}
162 157 <tr>
163 158 <td class="left-col">
164 159 #{user.full_name}<br/>
165 160 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
166 161 #{time_left}
167 162 <br/>
@@ -1,37 +1,36
1 1 %h1 Listing announcements
2 2
3 3 = link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success'
4 4 %br
5 5 %br
6 6
7 7 %table.table.table-striped
8 8 %tr
9 9 %th Updated
10 10 %th Announcement
11 11 %th Author
12 12 %th Published
13 13 %th
14 14 %th
15 15 - for announcement in @announcements
16 16 %tr
17 17 - @announcement = announcement
18 18 %td= time_ago_in_words announcement.updated_at
19 19 %td
20 20 - if !announcement.title.blank?
21 21 %b Title:
22 22 = h announcement.title
23 23 %br/
24 24 - if !announcement.notes.blank?
25 25 %b
26 26 Notes: #{h announcement.notes}
27 27 %br/
28 28 = h announcement.body
29 29 %td= h announcement.author
30 - // %td= check_box_tag :published, 1, announcement.published, { class: 'bootstrap-toggle', id: "published-#{announcement.id}", data: {remote: true, method: 'PUT', url: url_for(controller: :announcements, action: :toggle, id: announcement), size: 'small', toggle: 'toggle' } }
31 - // <td><haml_loud> in_place_editor_field :announcement, :published, {}, :rows => 1 </haml_loud></td>
32 - %td= link_to (announcement.published? ? "Yes" : "No"), url_for(controller: :announcements, action: :toggle, id: announcement), { class: "btn btn-block btn-sm btn-#{(announcement.published? ? 'success' : 'default')} ajax-toggle", id: "published-#{announcement.id}", data: {remote: true, method: 'post' } }
30 + %td= toggle_button(announcement.published?, toggle_announcement_url(@announcement), "announcement_toggle_#{@announcement.id}")
31 + //%td= link_to (announcement.published? ? "Yes" : "No"), url_for(controller: :announcements, action: :toggle, id: announcement), { class: "btn btn-block btn-sm btn-#{(announcement.published? ? 'success' : 'default')} ajax-toggle", id: "published-#{announcement.id}", data: {remote: true, method: 'post' } }
33 32 %td= link_to 'Edit', edit_announcement_path(announcement), class: 'btn btn-block btn-sm btn-info'
34 33 %td= link_to 'Destroy', announcement, :confirm => 'Are you sure?', :method => :delete, class: "btn btn-block btn-sm btn-danger"
35 34 %br
36 35
37 36 = link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success'
@@ -1,46 +1,47
1 1 - content_for :head do
2 2 = stylesheet_link_tag 'problems'
3 3 %h1 Listing problems
4 4 %p
5 5 = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
6 6 = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
7 7 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
8 8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
9 9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
10 10 .submitbox
11 11 = form_tag :action => 'quick_create' do
12 12 %b Quick New:
13 13 %label{:for => "problem_name"} Name
14 14 = text_field 'problem', 'name'
15 15 |
16 16 %label{:for => "problem_full_name"} Full name
17 17 = text_field 'problem', 'full_name'
18 18 = submit_tag "Create"
19 - %table.table.table-condensed.table-hover
19 + %table.table.table-condense.table-hover
20 20 %thead
21 21 %th Name
22 22 %th Full name
23 23 %th Full score
24 24 %th Date added
25 25 %th Avail?
26 26 %th Test?
27 27 - if GraderConfiguration.multicontests?
28 28 %th Contests
29 29 - for problem in @problems
30 30 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
31 31 - @problem=problem
32 32 %td= in_place_editor_field :problem, :name, {}, :rows=>1
33 33 %td= in_place_editor_field :problem, :full_name, {}, :rows=>1
34 34 %td= in_place_editor_field :problem, :full_score, {}, :rows=>1
35 35 %td= problem.date_added
36 - %td{}= link_to (@problem.available? ? "Yes" : "No"), url_for(controller: :problems, action: :toggle, id: @problem), { class: "btn btn-block btn-sm btn-#{(@problem.available? ? 'success' : 'default')} ajax-toggle", id: "prob-#{@problem.id}-avail", data: {remote: true, method: 'post' } }
36 + %td= toggle_button(@problem.available?, url_for(controller: :problems, action: :toggle, id: @problem), "problem-avail-#{@problem.id}")
37 + //%td{}= link_to (@problem.available? ? "Yes" : "No"), url_for(controller: :problems, action: :toggle, id: @problem), { class: "btn btn-block btn-sm btn-#{(@problem.available? ? 'success' : 'default')} ajax-toggle", id: "problem-avail-#{@problem.id}", data: {remote: true, method: 'post' } }
37 38 %td= problem.test_allowed
38 39 - if GraderConfiguration.multicontests?
39 40 %td
40 41 = problem.contests.collect { |c| c.name }.join(', ')
41 - %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-primary btn-sm'
42 - %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-primary btn-sm'
43 - %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-primary btn-sm'
44 - %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-sm'
42 + %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
43 + %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
44 + %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
45 + %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block'
45 46 %br/
46 47 = link_to '[New problem]', :action => 'new'
@@ -1,12 +1,8
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#problem-avail-#{@problem.id}",button_on: @problem.available }
1 3 :plain
2 - b = $("#prob-#{@problem.id}-avail");
3 - b.removeClass('btn-default');
4 - b.removeClass('btn-success');
5 - b.removeClass('btn-warning');
6 - b.addClass("btn-#{@problem.available? ? 'success' : 'default'}");
7 - b.text("#{@problem.available? ? 'Yes' : 'No'}");
8 4 r = $("#prob-#{@problem.id}");
9 5 r.removeClass('success');
10 6 r.removeClass('danger');
11 7 r.addClass("#{@problem.available? ? 'success' : 'danger'}");
12 8
@@ -1,33 +1,54
1 1 CafeGrader::Application.routes.draw do
2 2 root :to => 'main#login'
3 3
4 - get "report/login"
5 4
6 5 resources :contests
7 6
8 - resources :announcements
9 - match 'announcements/toggle/:id' => 'announcements#toggle'
10 -
11 7 resources :sites
12 8
13 - resources :problem
9 + resources :announcements do
10 + member do
11 + get 'toggle'
12 + end
13 + end
14 +
15 +
16 + resources :problems do
17 + member do
18 + get 'toggle'
19 + end
20 + collection do
21 + get 'turn_all_off'
22 + get 'turn_all_on'
23 + get 'import'
24 + get 'manage'
25 + end
26 + end
14 27
15 28 resources :grader_configuration, controller: 'configurations'
16 29
30 + resources :users do
31 + member do
32 + get 'toggle_activate', 'toggle_enable'
33 + end
34 + end
35 +
36 +
17 37 match 'tasks/view/:file.:ext' => 'tasks#view'
18 38 match 'tasks/download/:id/:file.:ext' => 'tasks#download'
19 39 match 'heartbeat/:id/edit' => 'heartbeat#edit'
20 40
21 41 #main
22 42 get "main/list"
23 43 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
24 44
25 45 #report
26 46 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
47 + get "report/login"
27 48
28 49 # See how all your routes lay out with "rake routes"
29 50
30 51 # This is a legacy wild controller route that's not recommended for RESTful applications.
31 52 # Note: This route will make all actions in every controller accessible via GET requests.
32 53 match ':controller(/:action(/:id))(.:format)'
33 54 end
deleted file
You need to be logged in to leave comments. Login now