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
@@ -1,101 +1,102
1 1 class AnnouncementsController < ApplicationController
2 2
3 3 before_filter :admin_authorization
4 4
5 5 in_place_edit_for :announcement, :published
6 6
7 7 # GET /announcements
8 8 # GET /announcements.xml
9 9 def index
10 10 @announcements = Announcement.find(:all,
11 11 :order => "created_at DESC")
12 12
13 13 respond_to do |format|
14 14 format.html # index.html.erb
15 15 format.xml { render :xml => @announcements }
16 16 end
17 17 end
18 18
19 19 # GET /announcements/1
20 20 # GET /announcements/1.xml
21 21 def show
22 22 @announcement = Announcement.find(params[:id])
23 23
24 24 respond_to do |format|
25 25 format.html # show.html.erb
26 26 format.xml { render :xml => @announcement }
27 27 end
28 28 end
29 29
30 30 # GET /announcements/new
31 31 # GET /announcements/new.xml
32 32 def new
33 33 @announcement = Announcement.new
34 34
35 35 respond_to do |format|
36 36 format.html # new.html.erb
37 37 format.xml { render :xml => @announcement }
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
@@ -1,119 +1,124
1 1 class ApplicationController < ActionController::Base
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
104 109 end
105 110 end
106 111
107 112 def verify_time_limit
108 113 return true if session[:user_id]==nil
109 114 user = User.find(session[:user_id], :include => :site)
110 115 return true if user==nil or user.site == nil
111 116 if user.contest_finished?
112 117 flash[:notice] = 'Error: the contest you are participating is over.'
113 118 redirect_to :back
114 119 return false
115 120 end
116 121 return true
117 122 end
118 123
119 124 end
@@ -1,274 +1,274
1 1 class ProblemsController < ApplicationController
2 2
3 3 before_filter :authenticate, :authorization
4 4
5 5 in_place_edit_for :problem, :name
6 6 in_place_edit_for :problem, :full_name
7 7 in_place_edit_for :problem, :full_score
8 8
9 9 def index
10 10 @problems = Problem.find(:all, :order => 'date_added DESC')
11 11 end
12 12
13 13 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 14 verify :method => :post, :only => [ :destroy,
15 15 :create, :quick_create,
16 16 :do_manage,
17 17 :do_import,
18 18 :update ],
19 19 :redirect_to => { :action => :index }
20 20
21 21 def show
22 22 @problem = Problem.find(params[:id])
23 23 end
24 24
25 25 def new
26 26 @problem = Problem.new
27 27 @description = nil
28 28 end
29 29
30 30 def create
31 31 @problem = Problem.new(params[:problem])
32 32 @description = Description.new(params[:description])
33 33 if @description.body!=''
34 34 if !@description.save
35 35 render :action => new and return
36 36 end
37 37 else
38 38 @description = nil
39 39 end
40 40 @problem.description = @description
41 41 if @problem.save
42 42 flash[:notice] = 'Problem was successfully created.'
43 43 redirect_to action: :index
44 44 else
45 45 render :action => 'new'
46 46 end
47 47 end
48 48
49 49 def quick_create
50 50 @problem = Problem.new(params[:problem])
51 51 @problem.full_name = @problem.name if @problem.full_name == ''
52 52 @problem.full_score = 100
53 53 @problem.available = false
54 54 @problem.test_allowed = true
55 55 @problem.output_only = false
56 56 @problem.date_added = Time.new
57 57 if @problem.save
58 58 flash[:notice] = 'Problem was successfully created.'
59 59 redirect_to action: :index
60 60 else
61 61 flash[:notice] = 'Error saving problem'
62 62 redirect_to action: :index
63 63 end
64 64 end
65 65
66 66 def edit
67 67 @problem = Problem.find(params[:id])
68 68 @description = @problem.description
69 69 end
70 70
71 71 def update
72 72 @problem = Problem.find(params[:id])
73 73 @description = @problem.description
74 74 if @description == nil and params[:description][:body]!=''
75 75 @description = Description.new(params[:description])
76 76 if !@description.save
77 77 flash[:notice] = 'Error saving description'
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
175 175 def do_manage
176 176 if params.has_key? 'change_date_added'
177 177 change_date_added
178 178 elsif params.has_key? 'add_to_contest'
179 179 add_to_contest
180 180 elsif params.has_key? 'enable_problem'
181 181 set_available(true)
182 182 elsif params.has_key? 'disable_problem'
183 183 set_available(false)
184 184 end
185 185 redirect_to :action => 'manage'
186 186 end
187 187
188 188 def import
189 189 @allow_test_pair_import = allow_test_pair_import?
190 190 end
191 191
192 192 def do_import
193 193 old_problem = Problem.find_by_name(params[:name])
194 194 if !allow_test_pair_import? and params.has_key? :import_to_db
195 195 params.delete :import_to_db
196 196 end
197 197 @problem, import_log = Problem.create_from_import_form_params(params,
198 198 old_problem)
199 199
200 200 if !@problem.errors.empty?
201 201 render :action => 'import' and return
202 202 end
203 203
204 204 if old_problem!=nil
205 205 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
206 206 end
207 207 @log = import_log
208 208 end
209 209
210 210 def remove_contest
211 211 problem = Problem.find(params[:id])
212 212 contest = Contest.find(params[:contest_id])
213 213 if problem!=nil and contest!=nil
214 214 problem.contests.delete(contest)
215 215 end
216 216 redirect_to :action => 'manage'
217 217 end
218 218
219 219 ##################################
220 220 protected
221 221
222 222 def allow_test_pair_import?
223 223 if defined? ALLOW_TEST_PAIR_IMPORT
224 224 return ALLOW_TEST_PAIR_IMPORT
225 225 else
226 226 return false
227 227 end
228 228 end
229 229
230 230 def change_date_added
231 231 problems = get_problems_from_params
232 232 year = params[:date_added][:year].to_i
233 233 month = params[:date_added][:month].to_i
234 234 day = params[:date_added][:day].to_i
235 235 date = Date.new(year,month,day)
236 236 problems.each do |p|
237 237 p.date_added = date
238 238 p.save
239 239 end
240 240 end
241 241
242 242 def add_to_contest
243 243 problems = get_problems_from_params
244 244 contest = Contest.find(params[:contest][:id])
245 245 if contest!=nil and contest.enabled
246 246 problems.each do |p|
247 247 p.contests << contest
248 248 end
249 249 end
250 250 end
251 251
252 252 def set_available(avail)
253 253 problems = get_problems_from_params
254 254 problems.each do |p|
255 255 p.available = avail
256 256 p.save
257 257 end
258 258 end
259 259
260 260 def get_problems_from_params
261 261 problems = []
262 262 params.keys.each do |k|
263 263 if k.index('prob-')==0
264 264 name, id, order = k.split('-')
265 265 problems << Problem.find(id)
266 266 end
267 267 end
268 268 problems
269 269 end
270 270
271 271 def get_problems_stat
272 272 end
273 273
274 274 end
@@ -1,543 +1,542
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
69 68 redirect_to action: 'list', page: params[:page]
70 69 end
71 70
72 71 def create_from_list
73 72 lines = params[:user_list]
74 73
75 74 note = []
76 75
77 76 lines.split("\n").each do |line|
78 77 items = line.chomp.split(',')
79 78 if items.length>=2
80 79 login = items[0]
81 80 full_name = items[1]
82 81
83 82 added_random_password = false
84 83 if items.length>=3
85 84 password = items[2].chomp(" ")
86 85 user_alias = (items.length>=4) ? items[3] : login
87 86 else
88 87 password = random_password
89 88 user_alias = (items.length>=4) ? items[3] : login
90 89 added_random_password = true
91 90 end
92 91
93 92 user = User.find_by_login(login)
94 93 if (user)
95 94 user.full_name = full_name
96 95 user.password = password
97 96 else
98 97 user = User.new({:login => login,
99 98 :full_name => full_name,
100 99 :password => password,
101 100 :password_confirmation => password,
102 101 :alias => user_alias})
103 102 end
104 103 user.activated = true
105 104 user.save
106 105
107 106 if added_random_password
108 107 note << "'#{login}' (+)"
109 108 else
110 109 note << login
111 110 end
112 111 end
113 112 end
114 113 flash[:notice] = 'User(s) ' + note.join(', ') +
115 114 ' were successfully created. ' +
116 115 '( (+) - created with random passwords.)'
117 116 redirect_to :action => 'list'
118 117 end
119 118
120 119 def edit
121 120 @user = User.find(params[:id])
122 121 end
123 122
124 123 def update
125 124 @user = User.find(params[:id])
126 125 if @user.update_attributes(params[:user])
127 126 flash[:notice] = 'User was successfully updated.'
128 127 redirect_to :action => 'show', :id => @user
129 128 else
130 129 render :action => 'edit'
131 130 end
132 131 end
133 132
134 133 def destroy
135 134 User.find(params[:id]).destroy
136 135 redirect_to :action => 'list'
137 136 end
138 137
139 138 def user_stat
140 139 if params[:commit] == 'download csv'
141 140 @problems = Problem.all
142 141 else
143 142 @problems = Problem.find_available_problems
144 143 end
145 144 @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
146 145 @scorearray = Array.new
147 146 @users.each do |u|
148 147 ustat = Array.new
149 148 ustat[0] = u
150 149 @problems.each do |p|
151 150 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
152 151 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
153 152 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
154 153 else
155 154 ustat << [0,false]
156 155 end
157 156 end
158 157 @scorearray << ustat
159 158 end
160 159 if params[:commit] == 'download csv' then
161 160 csv = gen_csv_from_scorearray(@scorearray,@problems)
162 161 send_data csv, filename: 'last_score.csv'
163 162 else
164 163 render template: 'user_admin/user_stat'
165 164 end
166 165 end
167 166
168 167 def user_stat_max
169 168 if params[:commit] == 'download csv'
170 169 @problems = Problem.all
171 170 else
172 171 @problems = Problem.find_available_problems
173 172 end
174 173 @users = User.find(:all, :include => [:contests, :contest_stat])
175 174 @scorearray = Array.new
176 175 #set up range from param
177 176 since_id = params.fetch(:since_id, 0).to_i
178 177 until_id = params.fetch(:until_id, 0).to_i
179 178 @users.each do |u|
180 179 ustat = Array.new
181 180 ustat[0] = u
182 181 @problems.each do |p|
183 182 max_points = 0
184 183 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
185 184 max_points = sub.points if sub and sub.points and (sub.points > max_points)
186 185 end
187 186 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
188 187 end
189 188 @scorearray << ustat
190 189 end
191 190
192 191 if params[:commit] == 'download csv' then
193 192 csv = gen_csv_from_scorearray(@scorearray,@problems)
194 193 send_data csv, filename: 'max_score.csv'
195 194 else
196 195 render template: 'user_admin/user_stat'
197 196 end
198 197 end
199 198
200 199 def import
201 200 if params[:file]==''
202 201 flash[:notice] = 'Error importing no file'
203 202 redirect_to :action => 'list' and return
204 203 end
205 204 import_from_file(params[:file])
206 205 end
207 206
208 207 def random_all_passwords
209 208 users = User.find(:all)
210 209 @prefix = params[:prefix] || ''
211 210 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
212 211 @changed = false
213 212 if request.request_method == 'POST'
214 213 @non_admin_users.each do |user|
215 214 password = random_password
216 215 user.password = password
217 216 user.password_confirmation = password
218 217 user.save
219 218 end
220 219 @changed = true
221 220 end
222 221 end
223 222
224 223 # contest management
225 224
226 225 def contests
227 226 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
228 227 @contests = Contest.enabled
229 228 end
230 229
231 230 def assign_from_list
232 231 contest_id = params[:users_contest_id]
233 232 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
234 233 contest = Contest.find(params[:new_contest][:id])
235 234 if !contest
236 235 flash[:notice] = 'Error: no contest'
237 236 redirect_to :action => 'contests', :id =>contest_id
238 237 end
239 238
240 239 note = []
241 240 users.each do |u|
242 241 u.contests = [contest]
243 242 note << u.login
244 243 end
245 244 flash[:notice] = 'User(s) ' + note.join(', ') +
246 245 " were successfully reassigned to #{contest.title}."
247 246 redirect_to :action => 'contests', :id =>contest.id
248 247 end
249 248
250 249 def add_to_contest
251 250 user = User.find(params[:id])
252 251 contest = Contest.find(params[:contest_id])
253 252 if user and contest
254 253 user.contests << contest
255 254 end
256 255 redirect_to :action => 'list'
257 256 end
258 257
259 258 def remove_from_contest
260 259 user = User.find(params[:id])
261 260 contest = Contest.find(params[:contest_id])
262 261 if user and contest
263 262 user.contests.delete(contest)
264 263 end
265 264 redirect_to :action => 'list'
266 265 end
267 266
268 267 def contest_management
269 268 end
270 269
271 270 def manage_contest
272 271 contest = Contest.find(params[:contest][:id])
273 272 if !contest
274 273 flash[:notice] = 'You did not choose the contest.'
275 274 redirect_to :action => 'contest_management' and return
276 275 end
277 276
278 277 operation = params[:operation]
279 278
280 279 if not ['add','remove','assign'].include? operation
281 280 flash[:notice] = 'You did not choose the operation to perform.'
282 281 redirect_to :action => 'contest_management' and return
283 282 end
284 283
285 284 lines = params[:login_list]
286 285 if !lines or lines.blank?
287 286 flash[:notice] = 'You entered an empty list.'
288 287 redirect_to :action => 'contest_management' and return
289 288 end
290 289
291 290 note = []
292 291 users = []
293 292 lines.split("\n").each do |line|
294 293 user = User.find_by_login(line.chomp)
295 294 if user
296 295 if operation=='add'
297 296 if ! user.contests.include? contest
298 297 user.contests << contest
299 298 end
300 299 elsif operation=='remove'
301 300 user.contests.delete(contest)
302 301 else
303 302 user.contests = [contest]
304 303 end
305 304
306 305 if params[:reset_timer]
307 306 user.contest_stat.forced_logout = true
308 307 user.contest_stat.reset_timer_and_save
309 308 end
310 309
311 310 if params[:notification_emails]
312 311 send_contest_update_notification_email(user, contest)
313 312 end
314 313
315 314 note << user.login
316 315 users << user
317 316 end
318 317 end
319 318
320 319 if params[:reset_timer]
321 320 logout_users(users)
322 321 end
323 322
324 323 flash[:notice] = 'User(s) ' + note.join(', ') +
325 324 ' were successfully modified. '
326 325 redirect_to :action => 'contest_management'
327 326 end
328 327
329 328 # admin management
330 329
331 330 def admin
332 331 @admins = User.find(:all).find_all {|user| user.admin? }
333 332 end
334 333
335 334 def grant_admin
336 335 login = params[:login]
337 336 user = User.find_by_login(login)
338 337 if user!=nil
339 338 admin_role = Role.find_by_name('admin')
340 339 user.roles << admin_role
341 340 else
342 341 flash[:notice] = 'Unknown user'
343 342 end
344 343 flash[:notice] = 'User added as admins'
345 344 redirect_to :action => 'admin'
346 345 end
347 346
348 347 def revoke_admin
349 348 user = User.find(params[:id])
350 349 if user==nil
351 350 flash[:notice] = 'Unknown user'
352 351 redirect_to :action => 'admin' and return
353 352 elsif user.login == 'root'
354 353 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
355 354 redirect_to :action => 'admin' and return
356 355 end
357 356
358 357 admin_role = Role.find_by_name('admin')
359 358 user.roles.delete(admin_role)
360 359 flash[:notice] = 'User permission revoked'
361 360 redirect_to :action => 'admin'
362 361 end
363 362
364 363 # mass mailing
365 364
366 365 def mass_mailing
367 366 end
368 367
369 368 def bulk_mail
370 369 lines = params[:login_list]
371 370 if !lines or lines.blank?
372 371 flash[:notice] = 'You entered an empty list.'
373 372 redirect_to :action => 'mass_mailing' and return
374 373 end
375 374
376 375 mail_subject = params[:subject]
377 376 if !mail_subject or mail_subject.blank?
378 377 flash[:notice] = 'You entered an empty mail subject.'
379 378 redirect_to :action => 'mass_mailing' and return
380 379 end
381 380
382 381 mail_body = params[:email_body]
383 382 if !mail_body or mail_body.blank?
384 383 flash[:notice] = 'You entered an empty mail body.'
385 384 redirect_to :action => 'mass_mailing' and return
386 385 end
387 386
388 387 note = []
389 388 users = []
390 389 lines.split("\n").each do |line|
391 390 user = User.find_by_login(line.chomp)
392 391 if user
393 392 send_mail(user.email, mail_subject, mail_body)
394 393 note << user.login
395 394 end
396 395 end
397 396
398 397 flash[:notice] = 'User(s) ' + note.join(', ') +
399 398 ' were successfully modified. '
400 399 redirect_to :action => 'mass_mailing'
401 400 end
402 401
403 402 protected
404 403
405 404 def random_password(length=5)
406 405 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
407 406 newpass = ""
408 407 length.times { newpass << chars[rand(chars.size-1)] }
409 408 return newpass
410 409 end
411 410
412 411 def import_from_file(f)
413 412 data_hash = YAML.load(f)
414 413 @import_log = ""
415 414
416 415 country_data = data_hash[:countries]
417 416 site_data = data_hash[:sites]
418 417 user_data = data_hash[:users]
419 418
420 419 # import country
421 420 countries = {}
422 421 country_data.each_pair do |id,country|
423 422 c = Country.find_by_name(country[:name])
424 423 if c!=nil
425 424 countries[id] = c
426 425 @import_log << "Found #{country[:name]}\n"
427 426 else
428 427 countries[id] = Country.new(:name => country[:name])
429 428 countries[id].save
430 429 @import_log << "Created #{country[:name]}\n"
431 430 end
432 431 end
433 432
434 433 # import sites
435 434 sites = {}
436 435 site_data.each_pair do |id,site|
437 436 s = Site.find_by_name(site[:name])
438 437 if s!=nil
439 438 @import_log << "Found #{site[:name]}\n"
440 439 else
441 440 s = Site.new(:name => site[:name])
442 441 @import_log << "Created #{site[:name]}\n"
443 442 end
444 443 s.password = site[:password]
445 444 s.country = countries[site[:country_id]]
446 445 s.save
447 446 sites[id] = s
448 447 end
449 448
450 449 # import users
451 450 user_data.each_pair do |id,user|
452 451 u = User.find_by_login(user[:login])
453 452 if u!=nil
454 453 @import_log << "Found #{user[:login]}\n"
455 454 else
456 455 u = User.new(:login => user[:login])
457 456 @import_log << "Created #{user[:login]}\n"
458 457 end
459 458 u.full_name = user[:name]
460 459 u.password = user[:password]
461 460 u.country = countries[user[:country_id]]
462 461 u.site = sites[user[:site_id]]
463 462 u.activated = true
464 463 u.email = "empty-#{u.login}@none.com"
465 464 if not u.save
466 465 @import_log << "Errors\n"
467 466 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
468 467 end
469 468 end
470 469
471 470 end
472 471
473 472 def logout_users(users)
474 473 users.each do |user|
475 474 contest_stat = user.contest_stat(true)
476 475 if contest_stat and !contest_stat.forced_logout
477 476 contest_stat.forced_logout = true
478 477 contest_stat.save
479 478 end
480 479 end
481 480 end
482 481
483 482 def send_contest_update_notification_email(user, contest)
484 483 contest_title_name = GraderConfiguration['contest.name']
485 484 contest_name = contest.name
486 485 mail_subject = t('contest.notification.email_subject', {
487 486 :contest_title_name => contest_title_name,
488 487 :contest_name => contest_name })
489 488 mail_body = t('contest.notification.email_body', {
490 489 :full_name => user.full_name,
491 490 :contest_title_name => contest_title_name,
492 491 :contest_name => contest.name,
493 492 })
494 493
495 494 logger.info mail_body
496 495 send_mail(user.email, mail_subject, mail_body)
497 496 end
498 497
499 498 def find_contest_and_user_from_contest_id(id)
500 499 if id!='none'
501 500 @contest = Contest.find(id)
502 501 else
503 502 @contest = nil
504 503 end
505 504 if @contest
506 505 @users = @contest.users
507 506 else
508 507 @users = User.find_users_with_no_contest
509 508 end
510 509 return [@contest, @users]
511 510 end
512 511
513 512 def gen_csv_from_scorearray(scorearray,problem)
514 513 CSV.generate do |csv|
515 514 #add header
516 515 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
517 516 problem.each { |p| header << p.name }
518 517 header += ['Total','Passed']
519 518 csv << header
520 519 #add data
521 520 scorearray.each do |sc|
522 521 total = num_passed = 0
523 522 row = Array.new
524 523 sc.each_index do |i|
525 524 if i == 0
526 525 row << sc[i].login
527 526 row << sc[i].full_name
528 527 row << sc[i].activated
529 528 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
530 529 row << sc[i].contests.collect {|c| c.name}.join(', ')
531 530 else
532 531 row << sc[i][0]
533 532 total += sc[i][0]
534 533 num_passed += 1 if sc[i][1]
535 534 end
536 535 end
537 536 row << total
538 537 row << num_passed
539 538 csv << row
540 539 end
541 540 end
542 541 end
543 542 end
@@ -1,195 +1,214
1 1 require 'net/smtp'
2 2
3 3 class UsersController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_filter :authenticate, :except => [:new,
8 8 :register,
9 9 :confirm,
10 10 :forget,
11 11 :retrieve_password]
12 12
13 13 before_filter :verify_online_registration, :only => [:new,
14 14 :register,
15 15 :forget,
16 16 :retrieve_password]
17 17 before_filter :authenticate, :profile_authorization, only: [:profile]
18 18
19 19 verify :method => :post, :only => [:chg_passwd],
20 20 :redirect_to => { :action => :index }
21 21
22 22 #in_place_edit_for :user, :alias_for_editing
23 23 #in_place_edit_for :user, :email_for_editing
24 24
25 25 def index
26 26 if !GraderConfiguration['system.user_setting_enabled']
27 27 redirect_to :controller => 'main', :action => 'list'
28 28 else
29 29 @user = User.find(session[:user_id])
30 30 end
31 31 end
32 32
33 33 def chg_passwd
34 34 user = User.find(session[:user_id])
35 35 user.password = params[:passwd]
36 36 user.password_confirmation = params[:passwd_verify]
37 37 if user.save
38 38 flash[:notice] = 'password changed'
39 39 else
40 40 flash[:notice] = 'Error: password changing failed'
41 41 end
42 42 redirect_to :action => 'index'
43 43 end
44 44
45 45 def new
46 46 @user = User.new
47 47 render :action => 'new', :layout => 'empty'
48 48 end
49 49
50 50 def register
51 51 if(params[:cancel])
52 52 redirect_to :controller => 'main', :action => 'login'
53 53 return
54 54 end
55 55 @user = User.new(params[:user])
56 56 @user.password_confirmation = @user.password = User.random_password
57 57 @user.activated = false
58 58 if (@user.valid?) and (@user.save)
59 59 if send_confirmation_email(@user)
60 60 render :action => 'new_splash', :layout => 'empty'
61 61 else
62 62 @admin_email = GraderConfiguration['system.admin_email']
63 63 render :action => 'email_error', :layout => 'empty'
64 64 end
65 65 else
66 66 @user.errors.add(:base,"Email cannot be blank") if @user.email==''
67 67 render :action => 'new', :layout => 'empty'
68 68 end
69 69 end
70 70
71 71 def confirm
72 72 login = params[:login]
73 73 key = params[:activation]
74 74 @user = User.find_by_login(login)
75 75 if (@user) and (@user.verify_activation_key(key))
76 76 if @user.valid? # check uniquenss of email
77 77 @user.activated = true
78 78 @user.save
79 79 @result = :successful
80 80 else
81 81 @result = :email_used
82 82 end
83 83 else
84 84 @result = :failed
85 85 end
86 86 render :action => 'confirm', :layout => 'empty'
87 87 end
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
@@ -1,182 +1,177
1 1 # Methods added to this helper will be available to all templates in the application.
2 2 module ApplicationHelper
3 3
4 4 def navbar_user_header
5 5 left_menu = ''
6 6 right_menu = ''
7 7 user = User.find(session[:user_id])
8 8
9 9 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
10 10 left_menu << add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
11 11 left_menu << add_menu("#{I18n.t 'menu.submissions'}", 'main', 'submission')
12 12 left_menu << add_menu("#{I18n.t 'menu.test'}", 'test', 'index')
13 13 end
14 14
15 15 if GraderConfiguration['right.user_hall_of_fame']
16 16 left_menu << add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
17 17 end
18 18
19 19 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
20 20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
21 21 if GraderConfiguration['system.user_setting_enabled']
22 22 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
23 23 end
24 24 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
25 25
26 26
27 27 result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
28 28 end
29 29
30 30 def add_menu(title, controller, action,html_option = {})
31 31 link_option = {controller: controller, action: action}
32 32 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
33 33 content_tag(:li, link_to(title,link_option),html_option)
34 34 end
35 35
36 36 def user_header
37 37 menu_items = ''
38 38 user = User.find(session[:user_id])
39 39
40 40 if (user!=nil) and (session[:admin])
41 41 # admin menu
42 42 menu_items << "<b>Administrative task:</b> "
43 43 append_to menu_items, '[Announcements]', 'announcements', 'index'
44 44 append_to menu_items, '[Msg console]', 'messages', 'console'
45 45 append_to menu_items, '[Problems]', 'problems', 'index'
46 46 append_to menu_items, '[Users]', 'user_admin', 'index'
47 47 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
48 48 append_to menu_items, '[Report]', 'report', 'multiple_login'
49 49 append_to menu_items, '[Graders]', 'graders', 'list'
50 50 append_to menu_items, '[Contests]', 'contest_management', 'index'
51 51 append_to menu_items, '[Sites]', 'sites', 'index'
52 52 append_to menu_items, '[System config]', 'configurations', 'index'
53 53 menu_items << "<br/>"
54 54 end
55 55
56 56 # main page
57 57 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
58 58 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
59 59
60 60 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
61 61 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
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/>
168 163 </td>
169 164 <td class="right-col">#{contest_name}</td>
170 165 </tr>
171 166 </table>
172 167 </div>
173 168 TITLEBAR
174 169 result.html_safe
175 170 end
176 171
177 172 def markdown(text)
178 173 markdown = RDiscount.new(text)
179 174 markdown.to_html.html_safe
180 175 end
181 176
182 177 end
@@ -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