Description:
master
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r801:33c0929c92ed - - 24 files changed: 201 inserted, 117 deleted

@@ -0,0 +1,8
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#group-enabled-#{@group.id}",button_on: @group.enabled }
3 + :plain
4 + r = $("#group-#{@group.id}");
5 + r.removeClass('success');
6 + r.removeClass('danger');
7 + r.addClass("#{@group.enabled? ? 'success' : 'danger'}");
8 +
@@ -0,0 +1,5
1 + class AddEnabledToGroup < ActiveRecord::Migration[5.2]
2 + def change
3 + add_column :groups, :enabled, :boolean, default: true
4 + end
5 + end
@@ -59,180 +59,181
59 59 #check if logged in
60 60 unless session[:user_id]
61 61 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 62 unauthorized_redirect('You need to login but you cannot log in at this time')
63 63 else
64 64 unauthorized_redirect('You need to login')
65 65 end
66 66 return false
67 67 end
68 68
69 69 # check if run in single user mode
70 70 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
71 71 if @current_user==nil || (!@current_user.admin?)
72 72 unauthorized_redirect('You cannot log in at this time')
73 73 return false
74 74 end
75 75 end
76 76
77 77 # check if the user is enabled
78 78 unless @current_user.enabled? || @current_user.admin?
79 79 unauthorized_redirect 'Your account is disabled'
80 80 return false
81 81 end
82 82
83 83 # check if user ip is allowed
84 84 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
85 85 unless is_request_ip_allowed?
86 86 unauthorized_redirect 'Your IP is not allowed to login at this time.'
87 87 return false
88 88 end
89 89 end
90 90
91 91 if GraderConfiguration.multicontests?
92 92 return true if @current_user.admin?
93 93 begin
94 94 if @current_user.contest_stat(true).forced_logout
95 95 flash[:notice] = 'You have been automatically logged out.'
96 96 redirect_to :controller => 'main', :action => 'index'
97 97 end
98 98 rescue
99 99 end
100 100 end
101 101 return true
102 102 end
103 103
104 104 #redirect to root (and also force logout)
105 105 #if the user use different ip from the previous connection
106 106 # only applicable when MULTIPLE_IP_LOGIN options is false only
107 107 def authenticate_by_ip_address
108 108 #this assume that we have already authenticate normally
109 109 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
110 110 user = User.find(session[:user_id])
111 111 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
112 112 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
113 113 redirect_to :controller => 'main', :action => 'login'
114 114 return false
115 115 end
116 116 unless user.last_ip
117 117 user.last_ip = request.remote_ip
118 118 user.save
119 119 end
120 120 end
121 121 return true
122 122 end
123 123
124 124 def authorization
125 125 return false unless check_valid_login
126 126 user = User.find(session[:user_id])
127 127 unless user.roles.detect { |role|
128 128 role.rights.detect{ |right|
129 129 right.controller == self.class.controller_name and
130 130 (right.action == 'all' || right.action == action_name)
131 131 }
132 132 }
133 133 flash[:notice] = 'You are not authorized to view the page you requested'
134 134 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
135 135 redirect_to :controller => 'main', :action => 'login'
136 136 return false
137 137 end
138 138 end
139 139
140 140 def verify_time_limit
141 141 return true if session[:user_id]==nil
142 142 user = User.find(session[:user_id], :include => :site)
143 143 return true if user==nil || user.site == nil
144 144 if user.contest_finished?
145 145 flash[:notice] = 'Error: the contest you are participating is over.'
146 146 redirect_to :back
147 147 return false
148 148 end
149 149 return true
150 150 end
151 151
152 152 def is_request_ip_allowed?
153 153 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
154 154 user_ip = IPAddr.new(request.remote_ip)
155 + allowed = GraderConfiguration[WHITELIST_IP_CONF_KEY] || ''
155 156
156 - GraderConfiguration[WHITELIST_IP_CONF_KEY].delete(' ').split(',').each do |ips|
157 + allowed.delete(' ').split(',').each do |ips|
157 158 allow_ips = IPAddr.new(ips)
158 159 if allow_ips.include?(user_ip)
159 160 return true
160 161 end
161 162 end
162 163 return false
163 164 end
164 165 return true
165 166 end
166 167
167 168 #function for datatable ajax query
168 169 #return record,total_count,filter_count
169 170 def process_query_record(record,
170 171 total_count: nil,
171 172 select: '',
172 173 global_search: [],
173 174 no_search: false,
174 175 force_order: '',
175 176 date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until',
176 177 hard_limit: nil)
177 178 arel_table = record.model.arel_table
178 179
179 180 if !no_search && params['search']
180 181 global_value = record.model.sanitize_sql(params['search']['value'].strip.downcase)
181 182 if !global_value.blank?
182 183 global_value.split.each do |value|
183 184 global_where = global_search.map{|f| "LOWER(#{f}) like '%#{value}%'"}.join(' OR ')
184 185 record = record.where(global_where)
185 186 end
186 187 end
187 188
188 189 params['columns'].each do |i, col|
189 190 if !col['search']['value'].blank?
190 191 record = record.where(arel_table[col['name']].lower.matches("%#{col['search']['value'].strip.downcase}%"))
191 192 end
192 193 end
193 194 end
194 195
195 196 if !date_filter.blank?
196 197 param_since = params[date_param_since]
197 198 param_until = params[date_param_until]
198 199 date_since = Time.zone.parse( param_since ) || Time.new(1,1,1) rescue Time.new(1,1,1)
199 200 date_until = Time.zone.parse( param_until ) || Time.zone.now() rescue Time.zone.now()
200 201 date_range = date_since..(date_until.end_of_day)
201 202 record = record.where(date_filter.to_sym => date_range)
202 203 end
203 204
204 205 if force_order.blank?
205 206 if params['order']
206 207 params['order'].each do |i, o|
207 208 colName = params['columns'][o['column']]['name']
208 209 colName = "#{record.model.table_name}.#{colName}" if colName.upcase == 'ID'
209 210 record = record.order("#{colName} #{o['dir'].casecmp('desc') != 0 ? 'ASC' : 'DESC'}") unless colName.blank?
210 211 end
211 212 end
212 213 else
213 214 record = record.order(force_order)
214 215 end
215 216
216 217 filterCount = record.count(record.model.primary_key)
217 218 # if .group() is used, filterCount might be like {id_1: count_1, id_2: count_2, ...}
218 219 # so we should count the result again..
219 220 if filterCount.is_a? Hash
220 221 filterCount = filterCount.count
221 222 end
222 223
223 224
224 225 record = record.offset(params['start'] || 0)
225 226 record = record.limit(hard_limit)
226 227 if (params['length'])
227 228 limit = params['length'].to_i
228 229 limit == hard_limit if (hard_limit && hard_limit < limit)
229 230 record = record.limit(limit)
230 231 end
231 232 if (!select.blank?)
232 233 record = record.select(select)
233 234 end
234 235
235 236 return record, total_count || record.model.count, filterCount
236 237 end
237 238
238 239 end
@@ -1,104 +1,110
1 1 class GroupsController < ApplicationController
2 2 before_action :set_group, only: [:show, :edit, :update, :destroy,
3 3 :add_user, :remove_user,:remove_all_user,
4 4 :add_problem, :remove_problem,:remove_all_problem,
5 + :toggle,
5 6 ]
6 7 before_action :admin_authorization
7 8
8 9 # GET /groups
9 10 def index
10 11 @groups = Group.all
11 12 end
12 13
13 14 # GET /groups/1
14 15 def show
15 16 end
16 17
17 18 # GET /groups/new
18 19 def new
19 20 @group = Group.new
20 21 end
21 22
22 23 # GET /groups/1/edit
23 24 def edit
24 25 end
25 26
26 27 # POST /groups
27 28 def create
28 29 @group = Group.new(group_params)
29 30
30 31 if @group.save
31 32 redirect_to @group, notice: 'Group was successfully created.'
32 33 else
33 34 render :new
34 35 end
35 36 end
36 37
37 38 # PATCH/PUT /groups/1
38 39 def update
39 40 if @group.update(group_params)
40 41 redirect_to @group, notice: 'Group was successfully updated.'
41 42 else
42 43 render :edit
43 44 end
44 45 end
45 46
46 47 # DELETE /groups/1
47 48 def destroy
48 49 @group.destroy
49 50 redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 51 end
51 52
53 + def toggle
54 + @group.enabled = @group.enabled? ? false : true
55 + @group.save
56 + end
57 +
52 58 def remove_user
53 59 user = User.find(params[:user_id])
54 60 @group.users.delete(user)
55 61 redirect_to group_path(@group), flash: {success: "User #{user.login} was removed from the group #{@group.name}"}
56 62 end
57 63
58 64 def remove_all_user
59 65 @group.users.clear
60 66 redirect_to group_path(@group), alert: 'All users removed'
61 67 end
62 68
63 69 def remove_all_problem
64 70 @group.problems.clear
65 71 redirect_to group_path(@group), alert: 'All problems removed'
66 72 end
67 73
68 74 def add_user
69 75 user = User.find(params[:user_id])
70 76 begin
71 77 @group.users << user
72 78 redirect_to group_path(@group), flash: { success: "User #{user.login} was add to the group #{@group.name}"}
73 79 rescue => e
74 80 redirect_to group_path(@group), alert: e.message
75 81 end
76 82 end
77 83
78 84 def remove_problem
79 85 problem = Problem.find(params[:problem_id])
80 86 @group.problems.delete(problem)
81 87 redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was removed from the group #{@group.name}" }
82 88 end
83 89
84 90 def add_problem
85 91 problem = Problem.find(params[:problem_id])
86 92 begin
87 93 @group.problems << problem
88 94 redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was add to the group #{@group.name}" }
89 95 rescue => e
90 96 redirect_to group_path(@group), alert: e.message
91 97 end
92 98 end
93 99
94 100 private
95 101 # Use callbacks to share common setup or constraints between actions.
96 102 def set_group
97 103 @group = Group.find(params[:id])
98 104 end
99 105
100 106 # Only allow a trusted parameter "white list" through.
101 107 def group_params
102 - params.require(:group).permit(:name, :description)
108 + params.require(:group).permit(:name, :description, :enabled)
103 109 end
104 110 end
@@ -95,193 +95,193
95 95
96 96 def source
97 97 submission = Submission.find(params[:id])
98 98 if ((submission.user_id == session[:user_id]) and
99 99 (submission.problem != nil) and
100 100 (submission.problem.available))
101 101 send_data(submission.source,
102 102 {:filename => submission.download_filename,
103 103 :type => 'text/plain'})
104 104 else
105 105 flash[:notice] = 'Error viewing source'
106 106 redirect_to :action => 'list'
107 107 end
108 108 end
109 109
110 110 def compiler_msg
111 111 @submission = Submission.find(params[:id])
112 112 if @submission.user_id == session[:user_id]
113 113 render :action => 'compiler_msg', :layout => 'empty'
114 114 else
115 115 flash[:notice] = 'Error viewing source'
116 116 redirect_to :action => 'list'
117 117 end
118 118 end
119 119
120 120 def result
121 121 if !GraderConfiguration.show_grading_result
122 122 redirect_to :action => 'list' and return
123 123 end
124 124 @user = User.find(session[:user_id])
125 125 @submission = Submission.find(params[:id])
126 126 if @submission.user!=@user
127 127 flash[:notice] = 'You are not allowed to view result of other users.'
128 128 redirect_to :action => 'list' and return
129 129 end
130 130 prepare_grading_result(@submission)
131 131 end
132 132
133 133 def load_output
134 134 if !GraderConfiguration.show_grading_result or params[:num]==nil
135 135 redirect_to :action => 'list' and return
136 136 end
137 137 @user = User.find(session[:user_id])
138 138 @submission = Submission.find(params[:id])
139 139 if @submission.user!=@user
140 140 flash[:notice] = 'You are not allowed to view result of other users.'
141 141 redirect_to :action => 'list' and return
142 142 end
143 143 case_num = params[:num].to_i
144 144 out_filename = output_filename(@user.login,
145 145 @submission.problem.name,
146 146 @submission.id,
147 147 case_num)
148 148 if !FileTest.exists?(out_filename)
149 149 flash[:notice] = 'Output not found.'
150 150 redirect_to :action => 'list' and return
151 151 end
152 152
153 153 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
154 154 response.headers['Content-Type'] = "application/force-download"
155 155 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
156 156 response.headers["X-Sendfile"] = out_filename
157 157 response.headers['Content-length'] = File.size(out_filename)
158 158 render :nothing => true
159 159 else
160 160 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
161 161 end
162 162 end
163 163
164 164 def error
165 165 @user = User.find(session[:user_id])
166 166 end
167 167
168 168 # announcement refreshing and hiding methods
169 169
170 170 def announcements
171 171 if params.has_key? 'recent'
172 172 prepare_announcements(params[:recent])
173 173 else
174 174 prepare_announcements
175 175 end
176 176 render(:partial => 'announcement',
177 177 :collection => @announcements,
178 178 :locals => {:announcement_effect => true})
179 179 end
180 180
181 181 def confirm_contest_start
182 182 user = User.find(session[:user_id])
183 183 if request.method == 'POST'
184 184 user.update_start_time
185 185 redirect_to :action => 'list'
186 186 else
187 187 @contests = user.contests
188 188 @user = user
189 189 end
190 190 end
191 -
191 +
192 192 protected
193 193
194 194 def prepare_announcements(recent=nil)
195 195 if GraderConfiguration.show_tasks_to?(@user)
196 196 @announcements = Announcement.published(true)
197 197 else
198 198 @announcements = Announcement.published
199 199 end
200 200 if recent!=nil
201 201 recent_id = recent.to_i
202 202 @announcements = @announcements.find_all { |a| a.id > recent_id }
203 203 end
204 204 end
205 205
206 206 def prepare_list_information
207 207 @user = User.find(session[:user_id])
208 208 if not GraderConfiguration.multicontests?
209 209 @problems = @user.available_problems
210 210 else
211 211 @contest_problems = @user.available_problems_group_by_contests
212 212 @problems = @user.available_problems
213 213 end
214 214 @prob_submissions = {}
215 215 @problems.each do |p|
216 216 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
217 217 if sub!=nil
218 218 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
219 219 else
220 220 @prob_submissions[p.id] = { :count => 0, :submission => nil }
221 221 end
222 222 end
223 223 prepare_announcements
224 224 end
225 225
226 226 def check_viewability
227 227 @user = User.find(session[:user_id])
228 228 if (!GraderConfiguration.show_tasks_to?(@user)) and
229 229 ((action_name=='submission') or (action_name=='submit'))
230 230 redirect_to :action => 'list' and return
231 231 end
232 232 end
233 233
234 234 def prepare_grading_result(submission)
235 235 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
236 236 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
237 237 else
238 238 # guess task info from problem.full_score
239 239 cases = submission.problem.full_score / 10
240 240 grading_info = {
241 241 'testruns' => cases,
242 242 'testcases' => cases
243 243 }
244 244 end
245 245 @test_runs = []
246 246 if grading_info['testruns'].is_a? Integer
247 247 trun_count = grading_info['testruns']
248 248 trun_count.times do |i|
249 249 @test_runs << [ read_grading_result(@user.login,
250 250 submission.problem.name,
251 251 submission.id,
252 252 i+1) ]
253 253 end
254 254 else
255 255 grading_info['testruns'].keys.sort.each do |num|
256 256 run = []
257 257 testrun = grading_info['testruns'][num]
258 258 testrun.each do |c|
259 259 run << read_grading_result(@user.login,
260 260 submission.problem.name,
261 261 submission.id,
262 262 c)
263 263 end
264 264 @test_runs << run
265 265 end
266 266 end
267 267 end
268 268
269 269 def grading_result_dir(user_name, problem_name, submission_id, case_num)
270 270 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
271 271 end
272 272
273 273 def output_filename(user_name, problem_name, submission_id, case_num)
274 274 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
275 275 return "#{dir}/output.txt"
276 276 end
277 277
278 278 def read_grading_result(user_name, problem_name, submission_id, case_num)
279 279 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
280 280 result_file_name = "#{dir}/result"
281 281 if !FileTest.exists?(result_file_name)
282 282 return {:num => case_num, :msg => 'program did not run'}
283 283 else
284 284 results = File.open(result_file_name).readlines
285 285 run_stat = extract_running_stat(results)
286 286 output_filename = "#{dir}/output.txt"
287 287 if FileTest.exists?(output_filename)
@@ -1,112 +1,111
1 1 class SubmissionsController < ApplicationController
2 2 before_action :check_valid_login
3 3 before_action :submission_authorization, only: [:show, :download, :edit]
4 4 before_action :admin_authorization, only: [:rejudge]
5 5
6 6 # GET /submissions
7 7 # GET /submissions.json
8 8 # Show problem selection and user's submission of that problem
9 9 def index
10 10 @user = @current_user
11 11 @problems = @user.available_problems
12 12
13 13 if params[:problem_id]==nil
14 14 @problem = nil
15 15 @submissions = nil
16 16 else
17 17 @problem = Problem.find_by_id(params[:problem_id])
18 18 if (@problem == nil) or (not @problem.available)
19 - redirect_to main_list_path
20 - flash[:notice] = 'Error: submissions for that problem are not viewable.'
19 + redirect_to list_main_path
20 + flash[:error] = 'Authorization error: You have no right to view submissions for this problem'
21 21 return
22 22 end
23 23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 24 end
25 25 end
26 26
27 27 # GET /submissions/1
28 28 # GET /submissions/1.json
29 29 def show
30 30 @submission = Submission.find(params[:id])
31 31
32 32 #log the viewing
33 33 user = User.find(session[:user_id])
34 34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35 35
36 36 @task = @submission.task
37 37 end
38 38
39 39 def download
40 40 @submission = Submission.find(params[:id])
41 41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 42 end
43 43
44 44 def compiler_msg
45 45 @submission = Submission.find(params[:id])
46 46 respond_to do |format|
47 47 format.js
48 48 end
49 49 end
50 50
51 51 #on-site new submission on specific problem
52 52 def direct_edit_problem
53 53 @problem = Problem.find(params[:problem_id])
54 54 unless @current_user.can_view_problem?(@problem)
55 55 unauthorized_redirect
56 56 return
57 57 end
58 58 @source = ''
59 59 if (params[:view_latest])
60 60 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
61 61 @source = @submission.source.to_s if @submission and @submission.source
62 62 end
63 63 render 'edit'
64 64 end
65 65
66 66 # GET /submissions/1/edit
67 67 def edit
68 68 @submission = Submission.find(params[:id])
69 69 @source = @submission.source.to_s
70 70 @problem = @submission.problem
71 71 @lang_id = @submission.language.id
72 72 end
73 73
74 74
75 75 def get_latest_submission_status
76 76 @problem = Problem.find(params[:pid])
77 77 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
78 78 respond_to do |format|
79 79 format.js
80 80 end
81 81 end
82 82
83 83 # GET /submissions/:id/rejudge
84 84 def rejudge
85 85 @submission = Submission.find(params[:id])
86 86 @task = @submission.task
87 87 @task.status_inqueue! if @task
88 88 respond_to do |format|
89 89 format.js
90 90 end
91 91 end
92 92
93 93 protected
94 94
95 95 def submission_authorization
96 96 #admin always has privileged
97 - if @current_user.admin?
98 - return true
99 - end
97 + return true if @current_user.admin?
98 + return true if @current_user.has_role?('TA') && (['show','download'].include? action_name)
100 99
101 100 sub = Submission.find(params[:id])
102 101 if @current_user.available_problems.include? sub.problem
103 102 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
104 103 end
105 104
106 105 #default to NO
107 106 unauthorized_redirect
108 107 return false
109 108 end
110 109
111 110
112 111 end
@@ -1,74 +1,76
1 1 class TasksController < ApplicationController
2 2
3 3 before_action :check_valid_login, :check_viewability
4 4
5 5 def index
6 6 redirect_to :action => 'list'
7 7 end
8 8
9 9 def list
10 10 @problems = @user.available_problems
11 11 end
12 12
13 13 # this has contest-wide access control
14 14 def view
15 15 base_name = params[:file]
16 16 base_filename = File.basename("#{base_name}.#{params[:ext]}")
17 17 filename = "#{Problem.download_file_basedir}/#{base_filename}"
18 18
19 19 if !FileTest.exists?(filename)
20 20 redirect_to :action => 'index' and return
21 21 end
22 22
23 23 send_file_to_user(filename, base_filename)
24 24 end
25 25
26 26 # this has problem-level access control
27 27 def download
28 28 problem = Problem.find(params[:id])
29 29 unless @current_user.can_view_problem? problem
30 - flash[:notice] = 'You are not authorized to access this file'
31 - redirect_to :action => 'index' and return
30 + flash[:error] = 'You are not authorized to access this file'
31 + redirect_to list_main_path
32 + return
32 33 end
33 34
34 35 base_name = params[:file]
35 36 base_filename = File.basename("#{base_name}.#{params[:ext]}")
36 37 filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}"
37 38
38 39 if !FileTest.exists?(filename)
39 40 flash[:notice] = 'File does not exists'
40 - redirect_to :action => 'index' and return
41 + redirect_to list_main_path
42 + return
41 43 end
42 44
43 45 send_file_to_user(filename, base_filename)
44 46 end
45 47
46 48 protected
47 49
48 50 def send_file_to_user(filename, base_filename)
49 51 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
50 52 response.headers['Content-Type'] = "application/force-download"
51 53 response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\""
52 54 response.headers["X-Sendfile"] = filename
53 55 response.headers['Content-length'] = File.size(filename)
54 56 render :nothing => true
55 57 else
56 58 if params[:ext]=='pdf'
57 59 content_type = 'application/pdf'
58 60 else
59 61 content_type = 'application/octet-stream'
60 62 end
61 63
62 64 send_file filename, :stream => false, :disposition => 'inline', :filename => base_filename, :type => content_type
63 65 end
64 66 end
65 67
66 68 def check_viewability
67 69 @user = User.find(session[:user_id])
68 70 if @user==nil or !GraderConfiguration.show_tasks_to?(@user)
69 71 redirect_to :controller => 'main', :action => 'list'
70 72 return false
71 73 end
72 74 end
73 75
74 76 end
@@ -1,187 +1,197
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_action :admin_authorization
8 8
9 9 def index
10 10 @user_count = User.count
11 11 @users = User.all
12 12 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
13 13 @contests = Contest.enabled
14 14 end
15 15
16 16 def active
17 17 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
18 18 @users = []
19 19 sessions.each do |session|
20 20 if session.data[:user_id]
21 21 @users << User.find(session.data[:user_id])
22 22 end
23 23 end
24 24 end
25 25
26 26 def show
27 27 @user = User.find(params[:id])
28 28 end
29 29
30 30 def new
31 31 @user = User.new
32 32 end
33 33
34 34 def create
35 35 @user = User.new(user_params)
36 36 @user.activated = true
37 37 if @user.save
38 38 flash[:notice] = 'User was successfully created.'
39 39 redirect_to :action => 'index'
40 40 else
41 41 render :action => 'new'
42 42 end
43 43 end
44 44
45 45 def clear_last_ip
46 46 @user = User.find(params[:id])
47 47 @user.last_ip = nil
48 48 @user.save
49 49 redirect_to action: 'index', page: params[:page]
50 50 end
51 51
52 52 def create_from_list
53 53 lines = params[:user_list]
54 54
55 55 note = []
56 56 error_note = []
57 57 error_msg = nil
58 58 ok_user = []
59 59
60 60 lines.split("\n").each do |line|
61 - items = line.chomp.split(',')
61 + #split with large limit, this will cause consecutive ',' to be result in a blank
62 + items = line.chomp.split(',',1000)
62 63 if items.length>=2
63 64 login = items[0]
64 65 full_name = items[1]
65 66 remark =''
66 67 user_alias = ''
67 68
68 69 added_random_password = false
69 - if items.length >= 3 and items[2].chomp(" ").length > 0;
70 - password = items[2].chomp(" ")
70 + added_password = false
71 + if items.length >= 3
72 + if items[2].chomp(" ").length > 0
73 + password = items[2].chomp(" ")
74 + added_password = true
75 + end
71 76 else
72 77 password = random_password
73 78 added_random_password=true;
74 79 end
75 80
76 81 if items.length>= 4 and items[3].chomp(" ").length > 0;
77 82 user_alias = items[3].chomp(" ")
78 83 else
79 84 user_alias = login
80 85 end
81 86
87 +
88 + has_remark = false
82 89 if items.length>=5
83 90 remark = items[4].strip;
91 + has_remark = true
84 92 end
85 93
86 94 user = User.find_by_login(login)
87 95 if (user)
88 96 user.full_name = full_name
89 - user.password = password
90 - user.remark = remark
97 + user.remark = remark if has_remark
98 + user.password = password if added_password || added_random_password
91 99 else
100 + #create a random password if none are given
101 + password = random_password unless password
92 102 user = User.new({:login => login,
93 103 :full_name => full_name,
94 104 :password => password,
95 105 :password_confirmation => password,
96 106 :alias => user_alias,
97 107 :remark => remark})
98 108 end
99 109 user.activated = true
100 110
101 111 if user.save
102 112 if added_random_password
103 113 note << "'#{login}' (+)"
104 114 else
105 115 note << login
106 116 end
107 117 ok_user << user
108 118 else
109 119 error_note << "'#{login}'"
110 120 error_msg = user.errors.full_messages.to_sentence unless error_msg
111 121 end
112 122
113 123 end
114 124 end
115 125
116 126 #add to group
117 127 if params[:add_to_group]
118 128 group = Group.where(id: params[:group_id]).first
119 129 if group
120 130 group.users << ok_user
121 131 end
122 132 end
123 133
124 134 # show flash
125 135 if note.size > 0
126 136 flash[:success] = 'User(s) ' + note.join(', ') +
127 137 ' were successfully created. ' +
128 138 '( (+) - created with random passwords.)'
129 139 end
130 140 if error_note.size > 0
131 141 flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ') + ". The error of the first failed one are: " + error_msg;
132 142 end
133 143 redirect_to :action => 'index'
134 144 end
135 145
136 146 def edit
137 147 @user = User.find(params[:id])
138 148 end
139 149
140 150 def update
141 151 @user = User.find(params[:id])
142 152 if @user.update_attributes(user_params)
143 153 flash[:notice] = 'User was successfully updated.'
144 154 redirect_to :action => 'show', :id => @user
145 155 else
146 156 render :action => 'edit'
147 157 end
148 158 end
149 159
150 160 def destroy
151 161 User.find(params[:id]).destroy
152 162 redirect_to :action => 'index'
153 163 end
154 164
155 165 def user_stat
156 166 if params[:commit] == 'download csv'
157 167 @problems = Problem.all
158 168 else
159 169 @problems = Problem.available_problems
160 170 end
161 171 @users = User.includes(:contests, :contest_stat).where(enabled: true)
162 172 @scorearray = Array.new
163 173 @users.each do |u|
164 174 ustat = Array.new
165 175 ustat[0] = u
166 176 @problems.each do |p|
167 177 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
168 178 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
169 179 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
170 180 else
171 181 ustat << [0,false]
172 182 end
173 183 end
174 184 @scorearray << ustat
175 185 end
176 186 if params[:commit] == 'download csv' then
177 187 csv = gen_csv_from_scorearray(@scorearray,@problems)
178 188 send_data csv, filename: 'last_score.csv'
179 189 else
180 190 render template: 'user_admin/user_stat'
181 191 end
182 192 end
183 193
184 194 def user_stat_max
185 195 if params[:commit] == 'download csv'
186 196 @problems = Problem.all
187 197 else
@@ -252,222 +262,219
252 262 flash[:notice] = 'Error: no contest'
253 263 redirect_to :action => 'contests', :id =>contest_id
254 264 end
255 265
256 266 note = []
257 267 users.each do |u|
258 268 u.contests = [contest]
259 269 note << u.login
260 270 end
261 271 flash[:notice] = 'User(s) ' + note.join(', ') +
262 272 " were successfully reassigned to #{contest.title}."
263 273 redirect_to :action => 'contests', :id =>contest.id
264 274 end
265 275
266 276 def add_to_contest
267 277 user = User.find(params[:id])
268 278 contest = Contest.find(params[:contest_id])
269 279 if user and contest
270 280 user.contests << contest
271 281 end
272 282 redirect_to :action => 'index'
273 283 end
274 284
275 285 def remove_from_contest
276 286 user = User.find(params[:id])
277 287 contest = Contest.find(params[:contest_id])
278 288 if user and contest
279 289 user.contests.delete(contest)
280 290 end
281 291 redirect_to :action => 'index'
282 292 end
283 293
284 294 def contest_management
285 295 end
286 296
287 297 def manage_contest
288 298 contest = Contest.find(params[:contest][:id])
289 299 if !contest
290 300 flash[:notice] = 'You did not choose the contest.'
291 301 redirect_to :action => 'contest_management' and return
292 302 end
293 303
294 304 operation = params[:operation]
295 305
296 306 if not ['add','remove','assign'].include? operation
297 307 flash[:notice] = 'You did not choose the operation to perform.'
298 308 redirect_to :action => 'contest_management' and return
299 309 end
300 310
301 311 lines = params[:login_list]
302 312 if !lines or lines.blank?
303 313 flash[:notice] = 'You entered an empty list.'
304 314 redirect_to :action => 'contest_management' and return
305 315 end
306 316
307 317 note = []
308 318 users = []
309 319 lines.split("\n").each do |line|
310 320 user = User.find_by_login(line.chomp)
311 321 if user
312 322 if operation=='add'
313 323 if ! user.contests.include? contest
314 324 user.contests << contest
315 325 end
316 326 elsif operation=='remove'
317 327 user.contests.delete(contest)
318 328 else
319 329 user.contests = [contest]
320 330 end
321 331
322 332 if params[:reset_timer]
323 333 user.contest_stat.forced_logout = true
324 334 user.contest_stat.reset_timer_and_save
325 335 end
326 336
327 337 if params[:notification_emails]
328 338 send_contest_update_notification_email(user, contest)
329 339 end
330 340
331 341 note << user.login
332 342 users << user
333 343 end
334 344 end
335 345
336 346 if params[:reset_timer]
337 347 logout_users(users)
338 348 end
339 349
340 350 flash[:notice] = 'User(s) ' + note.join(', ') +
341 351 ' were successfully modified. '
342 352 redirect_to :action => 'contest_management'
343 353 end
344 354
345 355 # admin management
346 356
347 357 def admin
348 - @admins = User.all.find_all {|user| user.admin? }
358 + @admins = Role.where(name: 'admin').take.users
359 + @tas = Role.where(name: 'ta').take.users
349 360 end
350 361
351 - def grant_admin
352 - login = params[:login]
353 - user = User.find_by_login(login)
354 - if user!=nil
355 - admin_role = Role.find_by_name('admin')
356 - user.roles << admin_role
357 - else
358 - flash[:notice] = 'Unknown user'
362 + def modify_role
363 + user = User.find_by_login(params[:login])
364 + role = Role.find_by_name(params[:role])
365 + unless user && role
366 + flash[:error] = 'Unknown user or role'
367 + redirect_to admin_user_admin_index_path
368 + return
359 369 end
360 - flash[:notice] = 'User added as admins'
361 - redirect_to :action => 'admin'
362 - end
363 -
364 - def revoke_admin
365 - user = User.find(params[:id])
366 - if user==nil
367 - flash[:notice] = 'Unknown user'
368 - redirect_to :action => 'admin' and return
369 - elsif user.login == 'root'
370 - flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
371 - redirect_to :action => 'admin' and return
370 + if params[:commit] == 'Grant'
371 + #grant role
372 + user.roles << role
373 + flash[:notice] = "User '#{user.login}' has been granted the role '#{role.name}'"
374 + else
375 + #revoke role
376 + if user.login == 'root' && role.name == 'admin'
377 + flash[:error] = 'You cannot revoke admisnistrator permission from root.'
378 + redirect_to admin_user_admin_index_path
379 + return
380 + end
381 + user.roles.delete(role)
382 + flash[:notice] = "The role '#{role.name}' has been revoked from User '#{user.login}'"
372 383 end
373 -
374 - admin_role = Role.find_by_name('admin')
375 - user.roles.delete(admin_role)
376 - flash[:notice] = 'User permission revoked'
377 - redirect_to :action => 'admin'
384 + redirect_to admin_user_admin_index_path
378 385 end
379 386
380 387 # mass mailing
381 388
382 389 def mass_mailing
383 390 end
384 391
385 392 def bulk_mail
386 393 lines = params[:login_list]
387 394 if !lines or lines.blank?
388 395 flash[:notice] = 'You entered an empty list.'
389 396 redirect_to :action => 'mass_mailing' and return
390 397 end
391 398
392 399 mail_subject = params[:subject]
393 400 if !mail_subject or mail_subject.blank?
394 401 flash[:notice] = 'You entered an empty mail subject.'
395 402 redirect_to :action => 'mass_mailing' and return
396 403 end
397 404
398 405 mail_body = params[:email_body]
399 406 if !mail_body or mail_body.blank?
400 407 flash[:notice] = 'You entered an empty mail body.'
401 408 redirect_to :action => 'mass_mailing' and return
402 409 end
403 410
404 411 note = []
405 412 users = []
406 413 lines.split("\n").each do |line|
407 414 user = User.find_by_login(line.chomp)
408 415 if user
409 416 send_mail(user.email, mail_subject, mail_body)
410 417 note << user.login
411 418 end
412 419 end
413 420
414 421 flash[:notice] = 'User(s) ' + note.join(', ') +
415 422 ' were successfully modified. '
416 423 redirect_to :action => 'mass_mailing'
417 424 end
418 425
419 426 #bulk manage
420 427 def bulk_manage
421 428
422 429 begin
423 430 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
424 431 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
425 432 rescue Exception
426 433 flash[:error] = 'Regular Expression is malformed'
427 434 @users = nil
428 435 end
429 436
430 437 if params[:commit]
431 438 @action = {}
432 439 @action[:set_enable] = params[:enabled]
433 440 @action[:enabled] = params[:enable] == "1"
434 441 @action[:gen_password] = params[:gen_password]
435 442 @action[:add_group] = params[:add_group]
436 443 @action[:group_name] = params[:group_name]
437 444 end
438 445
439 446 if params[:commit] == "Perform"
440 447 if @action[:set_enable]
441 448 @users.update_all(enabled: @action[:enabled])
442 449 end
443 450 if @action[:gen_password]
444 451 @users.each do |u|
445 452 password = random_password
446 453 u.password = password
447 454 u.password_confirmation = password
448 455 u.save
449 456 end
450 457 end
451 458 if @action[:add_group] and @action[:group_name]
452 459 @group = Group.find(@action[:group_name])
453 460 ok = []
454 461 failed = []
455 462 @users.each do |user|
456 463 begin
457 464 @group.users << user
458 465 ok << user.login
459 466 rescue => e
460 467 failed << user.login
461 468 end
462 469 end
463 470 flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
464 471 flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
465 472 end
466 473 end
467 474 end
468 475
469 476 protected
470 477
471 478 def random_password(length=5)
472 479 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
473 480 newpass = ""
@@ -20,205 +20,203
20 20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
21 21 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
22 22 if GraderConfiguration['system.user_setting_enabled']
23 23 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
24 24 end
25 25 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'}})
26 26
27 27
28 28 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')
29 29 end
30 30
31 31 def add_menu(title, controller, action, html_option = {})
32 32 link_option = {controller: controller, action: action}
33 33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
34 34 content_tag(:li, link_to(title,link_option),html_option)
35 35 end
36 36
37 37 def user_header
38 38 menu_items = ''
39 39 user = User.find(session[:user_id])
40 40
41 41 if (user!=nil) and (session[:admin])
42 42 # admin menu
43 43 menu_items << "<b>Administrative task:</b> "
44 44 append_to menu_items, '[Announcements]', 'announcements', 'index'
45 45 append_to menu_items, '[Msg console]', 'messages', 'console'
46 46 append_to menu_items, '[Problems]', 'problems', 'index'
47 47 append_to menu_items, '[Users]', 'user_admin', 'index'
48 48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
49 49 append_to menu_items, '[Report]', 'report', 'multiple_login'
50 50 append_to menu_items, '[Graders]', 'graders', 'list'
51 51 append_to menu_items, '[Contests]', 'contest_management', 'index'
52 52 append_to menu_items, '[Sites]', 'sites', 'index'
53 53 append_to menu_items, '[System config]', 'configurations', 'index'
54 54 menu_items << "<br/>"
55 55 end
56 56
57 57 # main page
58 58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
59 59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
60 60
61 61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
62 62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
63 63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
64 64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
65 65 end
66 66
67 67 if GraderConfiguration['right.user_hall_of_fame']
68 68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
69 69 end
70 70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
71 71
72 72 if GraderConfiguration['system.user_setting_enabled']
73 73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
74 74 end
75 75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
76 76
77 77 menu_items.html_safe
78 78 end
79 79
80 80 def append_to(option,label, controller, action)
81 81 option << ' ' if option!=''
82 82 option << link_to_unless_current(label,
83 83 :controller => controller,
84 84 :action => action)
85 85 end
86 86
87 87 def format_short_time(time)
88 88 now = Time.zone.now
89 89 st = ''
90 90 if (time.yday != now.yday) or (time.year != now.year)
91 91 st = time.strftime("%d/%m/%y ")
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 format_full_time_ago(time)
103 103 st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')'
104 104 end
105 105
106 106 def read_textfile(fname,max_size=2048)
107 107 begin
108 108 File.open(fname).read(max_size)
109 109 rescue
110 110 nil
111 111 end
112 112 end
113 113
114 114 def toggle_button(on,toggle_url,id, option={})
115 115 btn_size = option[:size] || 'btn-xs'
116 + btn_block = option[:block] || 'btn-block'
116 117 link_to (on ? "Yes" : "No"), toggle_url,
117 - {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
118 + {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
118 119 id: id,
119 120 data: {remote: true, method: 'get'}}
120 121 end
121 122
122 123 def get_ace_mode(language)
123 124 # return ace mode string from Language
124 125
125 126 case language.pretty_name
126 127 when 'Pascal'
127 128 'ace/mode/pascal'
128 129 when 'C++','C'
129 130 'ace/mode/c_cpp'
130 131 when 'Ruby'
131 132 'ace/mode/ruby'
132 133 when 'Python'
133 134 'ace/mode/python'
134 135 when 'Java'
135 136 'ace/mode/java'
136 137 else
137 138 'ace/mode/c_cpp'
138 139 end
139 140 end
140 141
141 142
142 143 def user_title_bar(user)
143 144 header = ''
144 145 time_left = ''
145 146
146 147 #
147 148 # if the contest is over
148 149 if GraderConfiguration.time_limit_mode?
149 150 if user.contest_finished?
150 151 header = <<CONTEST_OVER
151 152 <tr><td colspan="2" align="center">
152 153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
153 154 </td></tr>
154 155 CONTEST_OVER
155 156 end
156 157 if !user.contest_started?
157 158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
158 159 else
159 160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
160 161 " #{format_short_duration(user.contest_time_left)}"
161 162 end
162 163 end
163 164
164 165 #
165 166 # if the contest is in the anaysis mode
166 167 if GraderConfiguration.analysis_mode?
167 168 header = <<ANALYSISMODE
168 169 <tr><td colspan="2" align="center">
169 170 <span class="contest-over-msg">ANALYSIS MODE</span>
170 171 </td></tr>
171 172 ANALYSISMODE
172 173 end
173 174
174 175 contest_name = GraderConfiguration['contest.name']
175 176
176 177 #
177 178 # build real title bar
178 179 result = <<TITLEBAR
179 180 <div class="title">
180 181 <table>
181 182 #{header}
182 183 <tr>
183 184 <td class="left-col">
184 - #{user.full_name}<br/>
185 - #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
186 - #{time_left}
187 185 <br/>
188 186 </td>
189 187 <td class="right-col">#{contest_name}</td>
190 188 </tr>
191 189 </table>
192 190 </div>
193 191 TITLEBAR
194 192 result.html_safe
195 193 end
196 194
197 195 def markdown(text)
198 196 markdown = RDiscount.new(text)
199 197 markdown.to_html.html_safe
200 198 end
201 199
202 200
203 201 BOOTSTRAP_FLASH_MSG = {
204 202 success: 'alert-success',
205 203 error: 'alert-danger',
206 204 alert: 'alert-danger',
207 205 notice: 'alert-info'
208 206 }
209 207
210 208 def bootstrap_class_for(flash_type)
211 209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
212 210 end
213 211
214 212 def flash_messages
215 213 flash.each do |msg_type, message|
216 214 concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
217 215 concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
218 216 concat message
219 217 end)
220 218 end
221 219 nil
222 220 end
223 221
224 222 end
@@ -1,20 +1,14
1 1 module MainHelper
2 2
3 - def link_to_description_if_any(name, problem, options={})
3 + def link_to_description_if_any(name, problem)
4 4 if !problem.url.blank?
5 - return link_to name, problem.url, options
5 + return link_to name, problem.url
6 6 elsif !problem.description_filename.blank?
7 - #build a link to a problem (via task controller)
8 7 basename, ext = problem.description_filename.split('.')
9 - options[:controller] = 'tasks'
10 - options[:action] = 'download'
11 - options[:id] = problem.id
12 - options[:file] = basename
13 - options[:ext] = ext
14 - return link_to name, options
8 + return link_to name, download_task_path(problem.id,basename,ext), target: '_blank'
15 9 else
16 10 return ''
17 11 end
18 12 end
19 13
20 14 end
@@ -61,108 +61,108
61 61 if self.problem.output_only
62 62 return self.source_filename
63 63 else
64 64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 66 end
67 67 end
68 68
69 69 protected
70 70
71 71 def self.find_option_in_source(option, source)
72 72 if source==nil
73 73 return nil
74 74 end
75 75 i = 0
76 76 source.each_line do |s|
77 77 if s =~ option
78 78 words = s.split
79 79 return words[1]
80 80 end
81 81 i = i + 1
82 82 if i==10
83 83 return nil
84 84 end
85 85 end
86 86 return nil
87 87 end
88 88
89 89 def self.find_language_in_source(source, source_filename="")
90 90 langopt = find_option_in_source(/^LANG:/,source)
91 91 if langopt
92 92 return (Language.find_by_name(langopt) ||
93 93 Language.find_by_pretty_name(langopt))
94 94 else
95 95 if source_filename
96 96 return Language.find_by_extension(source_filename.split('.').last)
97 97 else
98 98 return nil
99 99 end
100 100 end
101 101 end
102 102
103 103 def self.find_problem_in_source(source, source_filename="")
104 104 prob_opt = find_option_in_source(/^TASK:/,source)
105 105 if problem = Problem.find_by_name(prob_opt)
106 106 return problem
107 107 else
108 108 if source_filename
109 109 return Problem.find_by_name(source_filename.split('.').first)
110 110 else
111 111 return nil
112 112 end
113 113 end
114 114 end
115 115
116 116 def assign_problem
117 117 if self.problem_id!=-1
118 118 begin
119 119 self.problem = Problem.find(self.problem_id)
120 120 rescue ActiveRecord::RecordNotFound
121 121 self.problem = nil
122 122 end
123 123 else
124 124 self.problem = Submission.find_problem_in_source(self.source,
125 125 self.source_filename)
126 126 end
127 127 end
128 128
129 129 def assign_language
130 130 if self.language == nil
131 131 self.language = Submission.find_language_in_source(self.source,
132 132 self.source_filename)
133 133 end
134 134 end
135 135
136 136 # validation codes
137 137 def must_specify_language
138 138 return if self.source==nil
139 139
140 140 # for output_only tasks
141 141 return if self.problem!=nil and self.problem.output_only
142 142
143 143 if self.language == nil
144 144 errors.add('source',"Cannot detect language. Did you submit a correct source file?")
145 145 end
146 146 end
147 147
148 148 def must_have_valid_problem
149 149 return if self.source==nil
150 150 if self.problem==nil
151 151 errors.add('problem',"must be specified.")
152 152 else
153 153 #admin always have right
154 154 return if self.user.admin?
155 155
156 156 #check if user has the right to submit the problem
157 - errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
157 + errors[:base] << "Authorization error: you have no right to submit to this problem" if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
158 158 end
159 159 end
160 160
161 161 # callbacks
162 162 def assign_latest_number_if_new_recond
163 163 return if !self.new_record?
164 164 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
165 165 self.number = (latest==nil) ? 1 : latest.number + 1;
166 166 end
167 167
168 168 end
@@ -1,376 +1,383
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 3 require 'net/https'
4 4 require 'net/http'
5 5 require 'json'
6 6
7 7 class User < ActiveRecord::Base
8 8
9 9 has_and_belongs_to_many :roles
10 10
11 11 #has_and_belongs_to_many :groups
12 12 has_many :groups_users, class_name: 'GroupUser'
13 13 has_many :groups, :through => :groups_users
14 14
15 15 has_many :test_requests, -> {order(submitted_at: :desc)}
16 16
17 17 has_many :messages, -> { order(created_at: :desc) },
18 18 :class_name => "Message",
19 19 :foreign_key => "sender_id"
20 20
21 21 has_many :replied_messages, -> { order(created_at: :desc) },
22 22 :class_name => "Message",
23 23 :foreign_key => "receiver_id"
24 24
25 25 has_many :logins
26 26
27 27 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
28 28
29 29 belongs_to :site
30 30 belongs_to :country
31 31
32 32 has_and_belongs_to_many :contests, -> { order(:name)}
33 33
34 34 scope :activated_users, -> {where activated: true}
35 35
36 36 validates_presence_of :login
37 37 validates_uniqueness_of :login
38 38 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
39 39 validates_length_of :login, :within => 3..30
40 40
41 41 validates_presence_of :full_name
42 42 validates_length_of :full_name, :minimum => 1
43 43
44 44 validates_presence_of :password, :if => :password_required?
45 45 validates_length_of :password, :within => 4..50, :if => :password_required?
46 46 validates_confirmation_of :password, :if => :password_required?
47 47
48 48 validates_format_of :email,
49 49 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
50 50 :if => :email_validation?
51 51 validate :uniqueness_of_email_from_activated_users,
52 52 :if => :email_validation?
53 53 validate :enough_time_interval_between_same_email_registrations,
54 54 :if => :email_validation?
55 55
56 56 # these are for ytopc
57 57 # disable for now
58 58 #validates_presence_of :province
59 59
60 60 attr_accessor :password
61 61
62 62 before_save :encrypt_new_password
63 63 before_save :assign_default_site
64 64 before_save :assign_default_contest
65 65
66 66 # this is for will_paginate
67 67 cattr_reader :per_page
68 68 @@per_page = 50
69 69
70 70 def self.authenticate(login, password)
71 71 user = find_by_login(login)
72 72 if user
73 73 return user if user.authenticated?(password)
74 74 end
75 75 end
76 76
77 77 def authenticated?(password)
78 78 if self.activated
79 79 hashed_password == User.encrypt(password,self.salt)
80 80 else
81 81 false
82 82 end
83 83 end
84 84
85 85 def admin?
86 - self.roles.where(name: 'admin').count > 0
86 + has_role?('admin')
87 + end
88 +
89 + def has_role?(role)
90 + self.roles.where(name: role).count > 0
87 91 end
88 92
89 93 def email_for_editing
90 94 if self.email==nil
91 95 "(unknown)"
92 96 elsif self.email==''
93 97 "(blank)"
94 98 else
95 99 self.email
96 100 end
97 101 end
98 102
99 103 def email_for_editing=(e)
100 104 self.email=e
101 105 end
102 106
103 107 def alias_for_editing
104 108 if self.alias==nil
105 109 "(unknown)"
106 110 elsif self.alias==''
107 111 "(blank)"
108 112 else
109 113 self.alias
110 114 end
111 115 end
112 116
113 117 def alias_for_editing=(e)
114 118 self.alias=e
115 119 end
116 120
117 121 def activation_key
118 122 if self.hashed_password==nil
119 123 encrypt_new_password
120 124 end
121 125 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
122 126 end
123 127
124 128 def verify_activation_key(key)
125 129 key == activation_key
126 130 end
127 131
128 132 def self.random_password(length=5)
129 133 chars = 'abcdefghjkmnopqrstuvwxyz'
130 134 password = ''
131 135 length.times { password << chars[rand(chars.length - 1)] }
132 136 password
133 137 end
134 138
135 139 def self.find_non_admin_with_prefix(prefix='')
136 140 users = User.all
137 141 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
138 142 end
139 143
140 144 # Contest information
141 145
142 146 def self.find_users_with_no_contest()
143 147 users = User.all
144 148 return users.find_all { |u| u.contests.length == 0 }
145 149 end
146 150
147 151
148 152 def contest_time_left
149 153 if GraderConfiguration.contest_mode?
150 154 return nil if site==nil
151 155 return site.time_left
152 156 elsif GraderConfiguration.indv_contest_mode?
153 157 time_limit = GraderConfiguration.contest_time_limit
154 158 if time_limit == nil
155 159 return nil
156 160 end
157 161 if contest_stat==nil or contest_stat.started_at==nil
158 162 return (Time.now.gmtime + time_limit) - Time.now.gmtime
159 163 else
160 164 finish_time = contest_stat.started_at + time_limit
161 165 current_time = Time.now.gmtime
162 166 if current_time > finish_time
163 167 return 0
164 168 else
165 169 return finish_time - current_time
166 170 end
167 171 end
168 172 else
169 173 return nil
170 174 end
171 175 end
172 176
173 177 def contest_finished?
174 178 if GraderConfiguration.contest_mode?
175 179 return false if site==nil
176 180 return site.finished?
177 181 elsif GraderConfiguration.indv_contest_mode?
178 182 return false if self.contest_stat==nil
179 183 return contest_time_left == 0
180 184 else
181 185 return false
182 186 end
183 187 end
184 188
185 189 def contest_started?
186 190 if GraderConfiguration.indv_contest_mode?
187 191 stat = self.contest_stat
188 192 return ((stat != nil) and (stat.started_at != nil))
189 193 elsif GraderConfiguration.contest_mode?
190 194 return true if site==nil
191 195 return site.started
192 196 else
193 197 return true
194 198 end
195 199 end
196 200
197 201 def update_start_time
198 202 stat = self.contest_stat
199 203 if stat.nil? or stat.started_at.nil?
200 204 stat ||= UserContestStat.new(:user => self)
201 205 stat.started_at = Time.now.gmtime
202 206 stat.save
203 207 end
204 208 end
205 209
206 210 def problem_in_user_contests?(problem)
207 211 problem_contests = problem.contests.all
208 212
209 213 if problem_contests.length == 0 # this is public contest
210 214 return true
211 215 end
212 216
213 217 contests.each do |contest|
214 218 if problem_contests.find {|c| c.id == contest.id }
215 219 return true
216 220 end
217 221 end
218 222 return false
219 223 end
220 224
221 225 def available_problems_group_by_contests
222 226 contest_problems = []
223 227 pin = {}
224 228 contests.enabled.each do |contest|
225 229 available_problems = contest.problems.available
226 230 contest_problems << {
227 231 :contest => contest,
228 232 :problems => available_problems
229 233 }
230 234 available_problems.each {|p| pin[p.id] = true}
231 235 end
232 236 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
233 237 contest_problems << {
234 238 :contest => nil,
235 239 :problems => other_avaiable_problems
236 240 }
237 241 return contest_problems
238 242 end
239 243
240 244 def solve_all_available_problems?
241 245 available_problems.each do |p|
242 246 u = self
243 247 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
244 248 return false if !p or !sub or sub.points < p.full_score
245 249 end
246 250 return true
247 251 end
248 252
249 253 #get a list of available problem
250 254 def available_problems
251 255 # first, we check if this is normal mode
252 256 if not GraderConfiguration.multicontests?
253 257
254 258 #if this is a normal mode
255 259 #we show problem based on problem_group, if the config said so
256 260 if GraderConfiguration.use_problem_group?
257 261 return available_problems_in_group
258 262 else
259 263 return Problem.available_problems
260 264 end
261 265 else
262 266 #this is multi contest mode
263 267 contest_problems = []
264 268 pin = {}
265 269 contests.enabled.each do |contest|
266 270 contest.problems.available.each do |problem|
267 271 if not pin.has_key? problem.id
268 272 contest_problems << problem
269 273 end
270 274 pin[problem.id] = true
271 275 end
272 276 end
273 277 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
274 278 return contest_problems + other_avaiable_problems
275 279 end
276 280 end
277 281
282 + # new feature, get list of available problem in all enabled group that the user belongs to
278 283 def available_problems_in_group
279 284 problem = []
280 - self.groups.each do |group|
285 + self.groups.where(enabled: true).each do |group|
281 286 group.problems.where(available: true).each { |p| problem << p }
282 287 end
283 288 problem.uniq!
284 289 if problem
285 290 problem.sort! do |a,b|
286 291 case
287 292 when a.date_added < b.date_added
288 293 1
289 294 when a.date_added > b.date_added
290 295 -1
291 296 else
292 297 a.name <=> b.name
293 298 end
294 299 end
295 300 return problem
296 301 else
297 302 return []
298 303 end
299 304 end
300 305
306 + #check if the user has the right to view that problem
307 + #this also consider group based problem policy
301 308 def can_view_problem?(problem)
302 309 return true if admin?
303 310 return available_problems.include? problem
304 311 end
305 312
306 313 def self.clear_last_login
307 314 User.update_all(:last_ip => nil)
308 315 end
309 316
310 317 protected
311 318 def encrypt_new_password
312 319 return if password.blank?
313 320 self.salt = (10+rand(90)).to_s
314 321 self.hashed_password = User.encrypt(self.password,self.salt)
315 322 end
316 323
317 324 def assign_default_site
318 325 # have to catch error when migrating (because self.site is not available).
319 326 begin
320 327 if self.site==nil
321 328 self.site = Site.find_by_name('default')
322 329 if self.site==nil
323 330 self.site = Site.find(1) # when 'default has be renamed'
324 331 end
325 332 end
326 333 rescue
327 334 end
328 335 end
329 336
330 337 def assign_default_contest
331 338 # have to catch error when migrating (because self.site is not available).
332 339 begin
333 340 if self.contests.length == 0
334 341 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
335 342 if default_contest
336 343 self.contests = [default_contest]
337 344 end
338 345 end
339 346 rescue
340 347 end
341 348 end
342 349
343 350 def password_required?
344 351 self.hashed_password.blank? || !self.password.blank?
345 352 end
346 353
347 354 def self.encrypt(string,salt)
348 355 Digest::SHA1.hexdigest(salt + string)
349 356 end
350 357
351 358 def uniqueness_of_email_from_activated_users
352 359 user = User.activated_users.find_by_email(self.email)
353 360 if user and (user.login != self.login)
354 361 self.errors.add(:base,"Email has already been taken")
355 362 end
356 363 end
357 364
358 365 def enough_time_interval_between_same_email_registrations
359 366 return if !self.new_record?
360 367 return if self.activated
361 368 open_user = User.find_by_email(self.email,
362 369 :order => 'created_at DESC')
363 370 if open_user and open_user.created_at and
364 371 (open_user.created_at > Time.now.gmtime - 5.minutes)
365 372 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
366 373 end
367 374 end
368 375
369 376 def email_validation?
370 377 begin
371 378 return VALIDATE_USER_EMAILS
372 379 rescue
373 380 return false
374 381 end
375 382 end
376 383 end
@@ -1,26 +1,26
1 1
2 2 %tr
3 3 %td{:align => "center"}
4 4 = submission.number
5 5 %td.text-right
6 6 = link_to "##{submission.id}", submission_path(submission.id)
7 7 %td
8 8 = l submission.submitted_at, format: :long
9 9 = "( #{time_ago_in_words(submission.submitted_at)} ago)"
10 10 %td
11 11 = submission.source_filename
12 12 = " (#{submission.language.pretty_name}) "
13 - = link_to('[load]',{:action => 'source', :id => submission.id})
13 + = link_to '[load]', download_submission_path(submission)
14 14 %td
15 15 - if submission.graded_at
16 16 = "Graded at #{format_short_time(submission.graded_at)}."
17 17 %br/
18 18 = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score']
19 19 = " ["
20 20 %tt
21 21 = submission.grader_comment
22 22 = "]"
23 23 %td
24 24 = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message }
25 25 %td
26 26 = link_to 'Edit', edit_submission_path(submission.id), class: 'btn btn-success'
@@ -1,82 +1,82
1 1 - content_for :head do
2 2 <meta http-equiv ="refresh" content="60"/>
3 3
4 4 %h1 Grader information
5 5
6 6 %p
7 7 = link_to 'Refresh', { :action => 'list' }, class: 'btn btn-info'
8 8
9 9 .panel.panel-primary
10 10 .panel-heading
11 11 Grader control:
12 12 .panel-body
13 - =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default', method: 'post'
14 - =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default', method: 'post'
15 - =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default', method: 'post'
16 - =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default', method: 'post'
13 + =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default'
14 + =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default'
15 + =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default'
16 + =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default'
17 17
18 18 .row
19 19 .col-md-6
20 20 - if @last_task
21 21 Last task:
22 22 = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task'
23 23
24 24 %br/
25 25
26 26 - if @last_test_request
27 27 Last test_request:
28 28 = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest'
29 29
30 30 %h2 Current graders
31 31
32 32 = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes}
33 33
34 34 %h2 Stalled graders
35 35
36 36 = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes}
37 37
38 38 %h2 Terminated graders
39 39
40 40 %p= link_to 'Clear data for terminated graders', { action: 'clear_terminated'}, class: 'btn btn-default', method: 'post'
41 41
42 42 = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes}
43 43 .col-md-6
44 44 %h2 Last 20 submissions
45 45 %table.table.table-striped.table-condensed
46 46 %thead
47 47 %th ID
48 48 %th User
49 49 %th Problem
50 50 %th Submitted
51 51 %th Graded
52 52 %th Result
53 53 %tbody
54 54 - @submission.each do |sub|
55 55 %tr.inactive
56 56 %td= link_to sub.id, submission_path(sub.id)
57 57 %td= ("" unless sub.user) || link_to(sub.try(:user).try(:full_name), stat_user_path(sub.user.id))
58 58 %td= ("" unless sub.problem) || link_to(sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id))
59 59 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
60 60 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
61 61 %td= sub.grader_comment
62 62 %h2 Ungraded submission
63 63 %table.table.table-striped.table-condensed
64 64 %thead
65 65 %th ID
66 66 %th User
67 67 %th Problem
68 68 %th Submitted
69 69 %th Graded
70 70 %th Result
71 71 %tbody
72 72 - @backlog_submission.each do |sub|
73 73 %tr.inactive
74 74 %td= link_to sub.id, submission_path(sub.id)
75 75 %td= ("" unless sub.user) || link_to( sub.try(:user).try(:full_name), stat_user_path(sub.user.id))
76 76 %td= ("" unless sub.problem) || link_to( sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id))
77 77 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
78 78 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
79 79 %td= sub.grader_comment
80 80
81 81
82 82
@@ -1,16 +1,27
1 1 = form_for @group do |f|
2 2 - if @group.errors.any?
3 3 #error_explanation
4 4 %h2= "#{pluralize(@group.errors.count, "error")} prohibited this group from being saved:"
5 5 %ul
6 6 - @group.errors.full_messages.each do |msg|
7 7 %li= msg
8 -
9 - .form-group.field
10 - = f.label :name
11 - = f.text_field :name, class: 'form-control'
12 - .form-group.field
13 - = f.label :description
14 - = f.text_field :description, class: 'form-control'
15 - .form-group.actions
16 - = f.submit 'Save', class: 'btn btn-primary'
8 + .row
9 + .col-md-6
10 + .form-group.field
11 + = f.label :name
12 + = f.text_field :name, class: 'form-control'
13 + .row
14 + .col-md-6
15 + .form-group.field
16 + = f.label :description
17 + = f.text_field :description, class: 'form-control'
18 + .row
19 + .col-md-6
20 + .checkbox
21 + = f.label :enabled do
22 + = f.check_box :enabled
23 + Enabled
24 + .row
25 + .col-md-6
26 + .form-group.actions
27 + = f.submit 'Save', class: 'btn btn-primary'
@@ -1,22 +1,24
1 1 %h1 Groups
2 2
3 3 %p
4 4 = link_to 'New Group', new_group_path, class: 'btn btn-primary'
5 5 %table.table.table-hover
6 6 %thead
7 7 %tr
8 8 %th Name
9 9 %th Description
10 + %th Enabled?
10 11 %th
11 12 %th
12 13
13 14 %tbody
14 15 - @groups.each do |group|
15 - %tr
16 + %tr{:class => "#{(group.enabled?) ? "success" : "danger"}", id: "group-#{group.id}"}
16 17 %td= group.name
17 18 %td= group.description
19 + %td= toggle_button(group.enabled?, toggle_group_path(group), "group-enabled-#{group.id}", size: ' ', block: ' ')
18 20 %td= link_to 'View', group, class: 'btn btn-default'
19 21 %td= link_to 'Destroy', group, :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger'
20 22
21 23 %br
22 24
@@ -1,96 +1,96
1 1 %header.navbar.navbar-default.navbar-fixed-top
2 2 %nav
3 3 .container-fluid
4 4 .navbar-header
5 5 %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
6 6 %span.sr-only Togggle Navigation
7 7 %span.icon-bar
8 8 %span.icon-bar
9 9 %span.icon-bar
10 10 %a.navbar-brand{href: list_main_path}
11 11 %span.glyphicon.glyphicon-home
12 12 MAIN
13 13 .collapse.navbar-collapse#navbar-collapse
14 14 %ul.nav.navbar-nav
15 15 / submission
16 16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
17 17 %li.dropdown
18 18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
19 19 = "#{I18n.t 'menu.submissions'}"
20 20 %span.caret
21 21 %ul.dropdown-menu
22 22 = add_menu("View", 'submissions', 'index')
23 23 = add_menu("Self Test", 'test', 'index')
24 24 / hall of fame
25 25 - if GraderConfiguration['right.user_hall_of_fame']
26 26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
27 27 / display MODE button (with countdown in contest mode)
28 28 - if GraderConfiguration.analysis_mode?
29 29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
30 30 - elsif GraderConfiguration.time_limit_mode?
31 31 - if @current_user.contest_finished?
32 32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
33 33 - elsif !@current_user.contest_started?
34 34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
35 35 - else
36 36 %div.navbar-btn.btn.btn-primary#countdown asdf
37 37 :javascript
38 38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
39 39 / admin section
40 40 - if (@current_user!=nil) and (session[:admin])
41 41 / management
42 42 %li.dropdown
43 43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
44 44 Manage
45 45 %span.caret
46 46 %ul.dropdown-menu
47 47 = add_menu( 'Announcements', 'announcements', 'index')
48 48 = add_menu( 'Problems', 'problems', 'index')
49 49 = add_menu( 'Tags', 'tags', 'index')
50 50 = add_menu( 'Users', 'user_admin', 'index')
51 51 = add_menu( 'User Groups', 'groups', 'index')
52 52 = add_menu( 'Graders', 'graders', 'list')
53 53 = add_menu( 'Message ', 'messages', 'console')
54 54 %li.divider{role: 'separator'}
55 55 = add_menu( 'System config', 'configurations', 'index')
56 56 %li.divider{role: 'separator'}
57 57 = add_menu( 'Sites', 'sites', 'index')
58 58 = add_menu( 'Contests', 'contest_management', 'index')
59 59 / report
60 60 %li.dropdown
61 61 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
62 62 Report
63 63 %span.caret
64 64 %ul.dropdown-menu
65 65 = add_menu( 'Current Score', 'report', 'current_score')
66 66 = add_menu( 'Score Report', 'report', 'max_score')
67 67 = add_menu( 'Submission Report', 'report', 'submission')
68 68 = add_menu( 'Login Report', 'report', 'login')
69 69 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
70 70 =link_to "#{ungraded} backlogs!",
71 - grader_list_path,
71 + graders_list_path,
72 72 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
73 73
74 74 %ul.nav.navbar-nav.navbar-right
75 75 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
76 76 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'index', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
77 77 - if GraderConfiguration['system.user_setting_enabled']
78 78 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog', id: 'user_profile')}".html_safe, 'users', 'profile', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
79 79 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
80 80
81 81 /
82 82 - if (@current_user!=nil) and (session[:admin])
83 83 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
84 84 .container-fluid
85 85 .collapse.navbar-collapse
86 86 %ul.nav.navbar-nav
87 87 = add_menu( '[Announcements]', 'announcements', 'index')
88 88 = add_menu( '[Msg console]', 'messages', 'console')
89 89 = add_menu( '[Problems]', 'problems', 'index')
90 90 = add_menu( '[Users]', 'user_admin', 'index')
91 91 = add_menu( '[Results]', 'user_admin', 'user_stat')
92 92 = add_menu( '[Report]', 'report', 'multiple_login')
93 93 = add_menu( '[Graders]', 'graders', 'list')
94 94 = add_menu( '[Contests]', 'contest_management', 'index')
95 95 = add_menu( '[Sites]', 'sites', 'index')
96 96 = add_menu( '[System config]', 'configurations', 'index')
@@ -1,47 +1,47
1 1 = user_title_bar(@user)
2 2
3 3 .announcementbox
4 4 %span{:class => 'title'}
5 5 =raw t 'help.how_to_submit'
6 6 .announcement
7 7 %p
8 8 =raw t 'help.must_specify_language'
9 9
10 10 %p
11 11 =raw t 'help.list_available_language'
12 12
13 13 %table{:border => '1'}
14 14 %tr
15 15 %th{:width => '150px'} C
16 16 %th{:width => '150px'} C++
17 17 %th{:width => '150px'} Pascal
18 18 %tr
19 19 %td=raw "<tt>/*<br/>LANG: C<br/>*/</tt>"
20 20 %td=raw "<tt>/*<br/>LANG: C++<br/>*/</tt>"
21 21 %td=raw "<tt>{<br/>LANG: Pascal<br/>}</tt>"
22 22
23 23 %p
24 24 =raw t 'help.accept_only_language_specified'
25 25
26 26 %p
27 27 =raw t 'help.specifying_task'
28 28
29 29 %p
30 30 =raw t 'help.example_cpp'
31 31
32 32 %table{:border => '1'}
33 33 %tr
34 34 %td{:width => '300px'}
35 35 %tt <tt>/*<br/>LANG: C++<br/>TASK: mobiles<br/>*/</tt>
36 36
37 37 %p
38 38 =raw t 'help.example_pas'
39 39
40 40 %table{:border => '1'}
41 41 %tr
42 42 %td{:width => '300px'}
43 43 %tt <tt>{<br/>LANG: Pascal<br/>TASK: mobiles<br/>}</tt>
44 44
45 45 %p
46 - = raw(t('help.ask_questions_at_messages',:message_link_name => (t 'menu.messages'),:url => url_for(:controller => 'messages', :action => 'list')))
46 + = raw(t('help.ask_questions_at_messages',:message_link_name => (t 'menu.messages'),url: messages_path ))
47 47
@@ -1,59 +1,63
1 1 :css
2 2 .fix-width {
3 3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
4 4 }
5 5
6 6 %h1 Problem stat: #{@problem.name}
7 7 %h2 Overview
8 8
9 + .row
10 + .col-md-2
11 + %strong Name:
12 + .col-md-10
13 + = @problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
14 + = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
15 + .row
16 + .col-md-2.strong
17 + %strong Submissions:
18 + .col-md-10
19 + = @submissions.count
20 + .row
21 + .col-md-2.strong
22 + %strong Solved/Attemped User
23 + .col-md-10
24 + #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
9 25
10 - %table.info
11 - %thead
12 - %tr.info-head
13 - %th Stat
14 - %th Value
15 - %tbody
16 - %tr{class: cycle('info-even','info-odd')}
17 - %td Submissions
18 - %td= @submissions.count
19 - %tr{class: cycle('info-even','info-odd')}
20 - %td Solved/Attempted User
21 - %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
22 26
23 27 %h2 Submissions Count
24 28 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
25 29
26 30 %h2 Submissions
27 31 - if @submissions and @submissions.count > 0
28 32 %table#main_table.table.table-condensed.table-striped
29 33 %thead
30 34 %tr
31 35 %th ID
32 36 %th Login
33 37 %th Name
34 38 %th Submitted_at
35 39 %th language
36 40 %th Points
37 41 %th comment
38 42 %th IP
39 43 %tbody
40 44 - row_odd,curr = true,''
41 45 - @submissions.each do |sub|
42 46 - next unless sub.user
43 47 - row_odd,curr = !row_odd, sub.user if curr != sub.user
44 48 %tr
45 49 %td= link_to sub.id, submission_path(sub)
46 50 %td= link_to sub.user.login, stat_user_path(sub.user)
47 51 %td= sub.user.full_name
48 52 %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago"
49 53 %td= sub.language.name
50 54 %td= sub.points
51 55 %td.fix-width= sub.grader_comment
52 56 %td= sub.ip_address
53 57 - else
54 58 No submission
55 59
56 60 :javascript
57 61 $("#main_table").DataTable({
58 62 paging: false
59 63 });
@@ -1,112 +1,113
1 1 %h1= "Submission: #{@submission.id}"
2 2
3 3 %textarea#data{style: "display:none;"}
4 4 :preserve
5 5 #{@submission.source}
6 6
7 7 //%div.highlight{:style => "border: 1px solid black;"}
8 8 //=@formatted_code.html_safe
9 9
10 10
11 11 .containter
12 12 .row
13 13 .col-md-7
14 14 %h2 Source Code
15 15 .col-md-5
16 16 %h2 Stat
17 17 .row
18 18 .col-md-7
19 19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
20 20 :javascript
21 21 e = ace.edit("editor")
22 22 e.setOptions({ maxLines: Infinity })
23 23 e.setValue($("#data").text())
24 24 e.gotoLine(1)
25 25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
26 26 e.setReadOnly(true)
27 27 .col-md-5
28 28 %table.table.table-striped
29 29 %tr
30 30 %td.text-right
31 31 %strong User
32 32 %td
33 33 - if @submission.user
34 34 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
35 35 = @submission.user.full_name
36 36 - else
37 37 = "(n/a)"
38 38 %tr
39 39 %td.text-right
40 40 %strong Task
41 41 %td
42 42 - if @submission.problem!=nil
43 43 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
44 44 = @submission.problem.full_name
45 + = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
45 46 - else
46 47 = "(n/a)"
47 48 %tr
48 49 %td.text-right
49 50 %strong Tries
50 51 %td= @submission.number
51 52 %tr
52 53 %td.text-right
53 54 %strong Language
54 55 %td= @submission.language.pretty_name
55 56 %tr
56 57 %td.text-right
57 58 %strong Submitted
58 59 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
59 60 %tr
60 61 %td.text-right
61 62 %strong Graded
62 63 - if @submission.graded_at
63 64 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
64 65 - else
65 66 %td -
66 67 %tr
67 68 %td.text-right
68 69 %strong Points
69 70 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
70 71 %tr
71 72 %td.text-right
72 73 %strong Comment
73 74 %td #{@submission.grader_comment}
74 75 %tr
75 76 %td.text-right
76 77 %strong Runtime (s)
77 78 %td #{@submission.max_runtime}
78 79 %tr
79 80 %td.text-right
80 81 %strong Memory (kb)
81 82 %td #{@submission.peak_memory}
82 83 %tr
83 84 %td.text-right
84 85 %strong Compiler result
85 86 %td
86 87 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
87 88 view
88 89 - if session[:admin]
89 90 %tr
90 91 %td.text-right
91 92 %strong IP
92 93 %td #{@submission.ip_address}
93 94 %tr
94 95 %td.text-right
95 96 %strong Grading Task Status
96 97 %td
97 98 = @task.status_str if @task
98 99 - if session[:admin]
99 100 = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
100 101
101 102
102 103 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
103 104 .modal-dialog.modal-lg{role:'document'}
104 105 .modal-content
105 106 .modal-header
106 107 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
107 108 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
108 109 %h4 Compiler message
109 110 .modal-body
110 111 %pre#compiler_msg= @submission.compiler_message
111 112 .modal-footer
112 113 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -1,25 +1,54
1 - %h1 Administrators
1 + %h1 Modify Role
2 + .row
3 + .col-md-6
4 + %h4 Administrators
5 + = form_tag modify_role_user_admin_index_path, method: 'post', class: 'form-inline' do
6 + = hidden_field_tag :role, 'admin'
7 + .form-group
8 + = label_tag :login, 'Grant admin role to:'
9 + = text_field_tag 'login',nil, class: 'form-control'
10 + .form-group
11 + = submit_tag 'Grant', class: 'btn btn-primary'
12 + %br
13 + %table.table.table-condense.table-hover.table-striped.table-bordered
14 + %thead{:class => 'info-head'}
15 + %th #
16 + %th Login
17 + %th Full name
18 + %th
19 + - @admins.each_with_index do |user, i|
20 + %tr
21 + %td= i+1
22 + %td= user.login
23 + %td= user.full_name
24 + %td
25 + - if user.login!='root'
26 + = link_to '[revoke]', modify_role_user_admin_index_path( login: user.login, role: 'admin', commit: 'revoke')
27 + .col-md-6
28 + %h4 Teacher Assistants (TA)
29 + = form_tag modify_role_user_admin_index_path, method: 'post', class: 'form-inline' do
30 + = hidden_field_tag :role, 'TA'
31 + .form-group
32 + = label_tag :login, 'Grant TA role to:'
33 + = text_field_tag 'login',nil, class: 'form-control'
34 + .form-group
35 + = submit_tag 'Grant', class: 'btn btn-primary'
36 + %br
37 + %table.table.table-condense.table-hover.table-striped.table-bordered
38 + %thead{:class => 'info-head'}
39 + %th #
40 + %th Login
41 + %th Full name
42 + %th
43 + - @tas.each_with_index do |user, i|
44 + %tr
45 + %td= i+1
46 + %td= user.login
47 + %td= user.full_name
48 + %td
49 + - if user.login!='root'
50 + = link_to '[revoke]', modify_role_user_admin_index_path( login: user.login, role: 'TA', commit: 'revoke')
2 51
3 - %table{:class => 'info'}
4 - %tr{:class => 'info-head'}
5 - %th #
6 - %th Login
7 - %th Full name
8 - %th
9 - - @admins.each_with_index do |user, i|
10 - %tr
11 - %td= i+1
12 - %td= user.login
13 - %td= user.full_name
14 - %td
15 - - if user.login!='root'
16 - = link_to '[revoke]', :action => 'revoke_admin', :id => user.id
17 - %hr
18 -
19 - = form_tag :action => 'grant_admin' do
20 - = label_tag :login, 'Grant admin permission to:'
21 - = text_field_tag 'login',nil, class: 'input-field'
22 - = submit_tag 'Grant', class: 'btn btn-primary'
23 52
24 53 %hr/
25 54 = link_to '[go back to index]', :action => 'index'
@@ -1,45 +1,54
1 1 .container-fluid
2 2 .row
3 3 .col-md-6
4 4 %h1 Adding list of users
5 5 .row
6 6 .col-md-6
7 7 .panel.panel-default
8 8 .panel-heading
9 9 .panel-title Info
10 10 .panel-body
11 11 %ul
12 12 %li
13 13 List of user information in this format:
14 14 %tt user_id,name(,passwd(,alias(,remark)))
15 15 %li
16 16 Note that
17 17 %tt passwd, alias
18 18 and
19 19 %tt remark
20 20 is optional.
21 21 %li
22 22 When
23 23 %tt passwd
24 24 or
25 25 %tt alias
26 26 is empty, the original value will be used instead.
27 27 %li
28 28 If the users with the same user_id already exists, existing information will be overwritten.
29 + Example:
30 + %ol
31 + %li
32 + %pre user1,Somchai Jaidee
33 + will create (or update) a user with login "user1" and setting the fullname to "Somchai Jaidee", also setting a random password.
34 + %li
35 + %pre user1,Somchai Jaidee,
36 + will create (or update) a user with login "user1" and and setting the fullname "Somchai Jaidee". No change is made to the password unless this is a new user. If this is a new user, a random password will be generated.
37 +
29 38
30 39 .row
31 40 .col-md-6
32 41 = form_tag :action => 'create_from_list' do
33 42 .form-group
34 43 = submit_tag 'Create following users',class: 'btn btn-success'
35 44 .form-group
36 45 .div.checkbox
37 46 %label
38 47 = check_box_tag :add_to_group
39 48 Also add these users to the following group
40 49 = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
41 50 .form-group
42 51 = text_area_tag 'user_list', nil, :rows => 50, :cols => 80
43 52 .col-md-6
44 53
45 54
@@ -1,211 +1,211
1 1 Rails.application.routes.draw do
2 2 resources :tags
3 3 get "sources/direct_edit"
4 4
5 5 root :to => 'main#login'
6 6
7 7 #logins
8 8 match 'login/login', to: 'login#login', via: [:get,:post]
9 9
10 10 resources :contests
11 11 resources :sites
12 12 resources :test
13 13
14 14 resources :messages do
15 15 member do
16 16 get 'hide'
17 17 post 'reply'
18 18 end
19 19 collection do
20 20 get 'console'
21 21 get 'list_all'
22 22 end
23 23 end
24 24
25 25 resources :announcements do
26 26 member do
27 27 get 'toggle','toggle_front'
28 28 end
29 29 end
30 30
31 31 resources :problems do
32 32 member do
33 33 get 'toggle'
34 34 get 'toggle_test'
35 35 get 'toggle_view_testcase'
36 36 get 'stat'
37 37 end
38 38 collection do
39 39 get 'turn_all_off'
40 40 get 'turn_all_on'
41 41 get 'import'
42 42 get 'manage'
43 43 get 'quick_create'
44 44 post 'do_manage'
45 45 post 'do_import'
46 46 end
47 47 end
48 48
49 49 resources :groups do
50 50 member do
51 51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 + get 'toggle'
57 58 end
58 59 collection do
59 60
60 61 end
61 62 end
62 63
63 64 resources :testcases, only: [] do
64 65 member do
65 66 get 'download_input'
66 67 get 'download_sol'
67 68 end
68 69 collection do
69 70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 71 end
71 72 end
72 73
73 74 resources :grader_configuration, controller: 'configurations' do
74 75 collection do
75 76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 77 end
77 78 end
78 79
79 80 resources :users do
80 81 member do
81 82 get 'toggle_activate', 'toggle_enable'
82 83 get 'stat'
83 84 end
84 85 collection do
85 86 get 'profile'
86 87 post 'chg_passwd'
87 88 end
88 89 end
89 90
90 91 resources :submissions do
91 92 member do
92 93 get 'download'
93 94 get 'compiler_msg'
94 95 get 'rejudge'
95 - get 'source'
96 96 end
97 97 collection do
98 98 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
99 99 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
100 100 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
101 101 end
102 102 end
103 103
104 104
105 105 #user admin
106 106 resources :user_admin do
107 107 collection do
108 108 match 'bulk_manage', via: [:get, :post]
109 109 get 'bulk_mail'
110 110 get 'user_stat'
111 111 get 'import'
112 112 get 'new_list'
113 113 get 'admin'
114 114 get 'active'
115 115 get 'mass_mailing'
116 - get 'revoke_admin'
117 - post 'grant_admin'
116 + match 'modify_role', via: [:get, :post]
118 117 match 'create_from_list', via: [:get, :post]
119 118 match 'random_all_passwords', via: [:get, :post]
120 119 end
121 120 member do
122 121 get 'clear_last_ip'
123 122 end
124 123 end
125 124
126 125 resources :contest_management, only: [:index] do
127 126 collection do
128 127 get 'user_stat'
129 128 get 'clear_stat'
130 129 get 'clear_all_stat'
131 130 get 'change_contest_mode'
132 131 end
133 132 end
134 133
135 134 #get 'user_admin', to: 'user_admin#index'
136 135 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 136 #post 'user_admin', to: 'user_admin#create'
138 137 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 138
140 139 #singular resource
141 140 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 141 #report
143 142 resource :report, only: [], controller: 'report' do
144 143 get 'login'
145 144 get 'multiple_login'
146 145 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 146 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 147 get 'max_score'
149 148 post 'show_max_score'
150 149 get 'stuck'
151 150 get 'cheat_report'
152 151 post 'cheat_report'
153 152 get 'cheat_scrutinize'
154 153 post 'cheat_scrutinize'
155 154 get 'submission'
156 155 post 'submission_query'
157 156 get 'login_stat'
158 157 post 'login_stat'
159 158 get 'login'
160 159 post 'login_summary_query'
161 160 post 'login_detail_query'
162 161 end
163 162 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
164 163 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
165 164 #get "report/login"
166 165 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
167 166 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
168 167
169 168 resource :main, only: [], controller: 'main' do
170 169 get 'login'
171 170 get 'logout'
172 171 get 'list'
173 172 get 'submission(/:id)', action: 'submission', as: 'main_submission'
174 173 get 'announcements'
175 174 get 'help'
176 175 post 'submit'
177 176 end
178 177 #main
179 178 #get "main/list"
180 179 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
181 180 #post 'main/submit', to: 'main#submit'
182 181 #get 'main/announcements', to: 'main#announcements'
183 182
184 183
185 184 #
186 185 get 'tasks/view/:file.:ext' => 'tasks#view'
187 - get 'tasks/download/:id/:file.:ext' => 'tasks#download'
186 + get 'tasks/download/:id/:file.:ext' => 'tasks#download', as: 'download_task'
188 187 get 'heartbeat/:id/edit' => 'heartbeat#edit'
189 188
190 189 #grader
191 - get 'graders/list', to: 'graders#list', as: 'grader_list'
190 + #get 'graders/list', to: 'graders#list', as: 'grader_list'
192 191 namespace :graders do
193 192 get 'task/:id/:type', action: 'task', as: 'task'
194 193 get 'view/:id/:type', action: 'view', as: 'view'
195 194 get 'clear/:id', action: 'clear', as: 'clear'
196 - get 'stop'
197 - get 'stop_all'
198 - get 'clear_all'
199 - get 'clear_terminated'
200 195 get 'start_grading'
201 196 get 'start_exam'
197 + get 'clear_all'
198 + get 'stop_all'
202 199
200 + get 'stop'
201 + get 'clear_terminated'
202 + get 'list'
203 203 end
204 204
205 205
206 206 # See how all your routes lay out with "rake routes"
207 207
208 208 # This is a legacy wild controller route that's not recommended for RESTful applications.
209 209 # Note: This route will make all actions in every controller accessible via GET requests.
210 210 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
211 211 end
@@ -132,157 +132,158
132 132 },
133 133
134 134 {
135 135 :key => 'system.user_setting_enabled',
136 136 :value_type => 'boolean',
137 137 :default_value => 'true',
138 138 :description => 'If this option is true, users can change their settings'
139 139 },
140 140
141 141 {
142 142 :key => 'system.user_setting_enabled',
143 143 :value_type => 'boolean',
144 144 :default_value => 'true',
145 145 :description => 'If this option is true, users can change their settings'
146 146 },
147 147
148 148 # If Configuration['contest.test_request.early_timeout'] is true
149 149 # the user will not be able to use test request at 30 minutes
150 150 # before the contest ends.
151 151 {
152 152 :key => 'contest.test_request.early_timeout',
153 153 :value_type => 'boolean',
154 154 :default_value => 'false'
155 155 },
156 156
157 157 {
158 158 :key => 'system.multicontests',
159 159 :value_type => 'boolean',
160 160 :default_value => 'false'
161 161 },
162 162
163 163 {
164 164 :key => 'contest.confirm_indv_contest_start',
165 165 :value_type => 'boolean',
166 166 :default_value => 'false'
167 167 },
168 168
169 169 {
170 170 :key => 'contest.default_contest_name',
171 171 :value_type => 'string',
172 172 :default_value => 'none',
173 173 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
174 174 },
175 175
176 176 {
177 177 :key => 'system.use_problem_group',
178 178 :value_type => 'boolean',
179 179 :default_value => 'false',
180 180 :description => "If true, available problem to the user will be only ones associated with the group of the user."
181 181 },
182 182
183 183
184 184 {
185 185 :key => 'right.whitelist_ignore',
186 186 :value_type => 'boolean',
187 187 :default_value => 'true',
188 188 :description => "If true, no IP check against whitelist_ip is perform. However, when false, non-admin user must have their ip in 'whitelist_ip' to be able to login."
189 189 },
190 190
191 191 {
192 192 :key => 'right.whitelist_ip',
193 193 :value_type => 'string',
194 194 :default_value => '0.0.0.0/0',
195 195 :description => "list of whitelist ip, given in comma separated CIDR notation. For example '192.168.90.0/23, 192.168.1.23/32'"
196 196 },
197 197
198 198 ]
199 199
200 200
201 201 def create_configuration_key(key,
202 202 value_type,
203 203 default_value,
204 204 description='')
205 205 conf = (GraderConfiguration.find_by_key(key) ||
206 206 GraderConfiguration.new(:key => key,
207 207 :value_type => value_type,
208 208 :value => default_value))
209 209 conf.description = description
210 210 conf.save
211 211 end
212 212
213 213 def seed_config
214 214 CONFIGURATIONS.each do |conf|
215 215 if conf.has_key? :description
216 216 desc = conf[:description]
217 217 else
218 218 desc = ''
219 219 end
220 220 create_configuration_key(conf[:key],
221 221 conf[:value_type],
222 222 conf[:default_value],
223 223 desc)
224 224 end
225 225 end
226 226
227 227 def seed_roles
228 + Role.find_or_create_by(name: 'TA')
228 229 return if Role.find_by_name('admin')
229 230
230 231 role = Role.create(:name => 'admin')
231 232 user_admin_right = Right.create(:name => 'user_admin',
232 233 :controller => 'user_admin',
233 234 :action => 'all')
234 235 problem_admin_right = Right.create(:name=> 'problem_admin',
235 236 :controller => 'problems',
236 237 :action => 'all')
237 238
238 239 graders_right = Right.create(:name => 'graders_admin',
239 240 :controller => 'graders',
240 241 :action => 'all')
241 242
242 243 role.rights << user_admin_right;
243 244 role.rights << problem_admin_right;
244 245 role.rights << graders_right;
245 246 role.save
246 247 end
247 248
248 249 def seed_root
249 250 return if User.find_by_login('root')
250 251
251 252 root = User.new(:login => 'root',
252 253 :full_name => 'Administrator',
253 254 :alias => 'root')
254 255 root.password = 'ioionrails';
255 256
256 257 class << root
257 258 public :encrypt_new_password
258 259 def valid?(context=nil)
259 260 true
260 261 end
261 262 end
262 263
263 264 root.encrypt_new_password
264 265
265 266 root.roles << Role.find_by_name('admin')
266 267
267 268 root.activated = true
268 269 root.save
269 270 end
270 271
271 272 def seed_users_and_roles
272 273 seed_roles
273 274 seed_root
274 275 end
275 276
276 277 def seed_more_languages
277 278 #Language.delete_all
278 279 Language.find_or_create_by( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
279 280 Language.find_or_create_by( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
280 281 Language.find_or_create_by( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
281 282 Language.find_or_create_by( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
282 283 Language.find_or_create_by( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
283 284 Language.find_or_create_by( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
284 285 end
285 286
286 287 seed_config
287 288 seed_users_and_roles
288 289 seed_more_languages
You need to be logged in to leave comments. Login now